diff --git a/_maps/map_files/cerestation/cerestation.dmm b/_maps/map_files/cerestation/cerestation.dmm
index 968ecdaa3c0c..bf3158e750dc 100644
--- a/_maps/map_files/cerestation/cerestation.dmm
+++ b/_maps/map_files/cerestation/cerestation.dmm
@@ -904,6 +904,11 @@
icon_state = "1-8"
},
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -1105,10 +1110,14 @@
d2 = 8;
icon_state = "4-8"
},
-/obj/effect/spawner/window/reinforced,
/obj/machinery/door/poddoor/shutters/preopen{
id_tag = "aisat"
},
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
/turf/simulated/floor/plating,
/area/station/turret_protected/ai)
"agS" = (
@@ -1122,7 +1131,11 @@
},
/area/station/turret_protected/ai)
"agW" = (
-/obj/effect/spawner/window/reinforced,
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
/turf/simulated/floor/plating,
/area/station/turret_protected/ai)
"ahc" = (
@@ -1398,7 +1411,6 @@
/turf/simulated/floor/plasteel,
/area/station/security/permabrig)
"aje" = (
-/obj/effect/spawner/window/reinforced,
/obj/machinery/door/poddoor/shutters/preopen{
dir = 8;
id_tag = "aisat"
@@ -1408,6 +1420,11 @@
d2 = 8;
icon_state = "4-8"
},
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
/turf/simulated/floor/plating,
/area/station/turret_protected/ai)
"ajf" = (
@@ -1509,6 +1526,11 @@
/area/station/turret_protected/ai)
"ajG" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -1719,9 +1741,7 @@
/turf/simulated/floor/plasteel,
/area/station/security/prisonershuttle)
"akD" = (
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/obj/machinery/light/small{
dir = 8
},
@@ -1730,9 +1750,7 @@
},
/area/station/security/execution)
"akE" = (
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -2109,9 +2127,7 @@
/turf/simulated/floor/plating,
/area/station/maintenance/gambling_den)
"amW" = (
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/obj/machinery/light/small{
dir = 4
},
@@ -2293,6 +2309,11 @@
d2 = 2;
icon_state = "1-2"
},
+/obj/structure/cable/orange{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
/turf/simulated/floor/plasteel,
/area/station/security/prisonershuttle)
"aol" = (
@@ -3475,9 +3496,7 @@
},
/area/station/turret_protected/aisat/interior/secondary)
"avu" = (
-/obj/effect/turf_decal/stripes/corner{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/corner,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -11344,10 +11363,6 @@
icon_state = "dark"
},
/area/station/engineering/control)
-"bnS" = (
-/obj/effect/spawner/window/reinforced,
-/turf/simulated/floor/plating,
-/area/station/engineering/control)
"bnV" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/door/firedoor,
@@ -12682,6 +12697,11 @@
/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
dir = 4
},
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/wood,
/area/station/command/office/hop)
"btP" = (
@@ -13758,6 +13778,11 @@
/obj/structure/table,
/obj/item/paper_bin/nanotrasen,
/obj/item/stamp/hop,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/wood,
/area/station/command/office/hop)
"byr" = (
@@ -15895,9 +15920,7 @@
/obj/effect/mapping_helpers/airlock/access/all/supply/mining,
/obj/machinery/door/firedoor,
/obj/structure/fans/tiny,
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/obj/effect/turf_decal/stripes/line{
dir = 1
},
@@ -20829,9 +20852,7 @@
/obj/effect/turf_decal/stripes/corner{
dir = 4
},
-/obj/effect/turf_decal/stripes/corner{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/corner,
/obj/effect/turf_decal/loading_area{
dir = 4
},
@@ -23208,9 +23229,6 @@
},
/area/station/hallway/primary/fore/west)
"ctB" = (
-/obj/effect/spawner/window/reinforced/polarized{
- id = "qm"
- },
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/structure/cable/orange,
@@ -23219,6 +23237,9 @@
d2 = 2;
icon_state = "1-2"
},
+/obj/effect/spawner/window/reinforced/polarized/grilled{
+ id = "qm"
+ },
/turf/simulated/floor/plating,
/area/station/supply/qm)
"ctC" = (
@@ -23864,10 +23885,6 @@
icon_state = "purplefull"
},
/area/station/hallway/primary/aft/west)
-"cxW" = (
-/obj/effect/spawner/window/reinforced,
-/turf/simulated/floor/plating,
-/area/station/maintenance/gambling_den)
"cxY" = (
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/turf/simulated/floor/engine,
@@ -23932,6 +23949,19 @@
},
/turf/simulated/floor/engine,
/area/station/science/explab/chamber)
+"cyq" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/turf/simulated/floor/plating,
+/area/station/security/prisonershuttle)
"cyx" = (
/obj/effect/turf_decal/stripes/line,
/turf/simulated/floor/plasteel,
@@ -25645,17 +25675,6 @@
},
/turf/simulated/floor/plasteel,
/area/station/public/locker)
-"cHZ" = (
-/obj/structure/disposalpipe/segment,
-/obj/machinery/door/airlock/command{
- name = "Head of Personnel"
- },
-/obj/effect/mapping_helpers/airlock/access/all/command/hop,
-/obj/machinery/door/firedoor,
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/turf/simulated/floor/wood,
-/area/station/command/office/hop)
"cIc" = (
/obj/structure/chair/stool{
dir = 8
@@ -26445,6 +26464,11 @@
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 1
},
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/wood,
/area/station/command/office/hop)
"cLV" = (
@@ -37642,6 +37666,22 @@
},
/turf/simulated/wall,
/area/station/public/sleep)
+"eLs" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "bcircuit"
+ },
+/area/station/turret_protected/ai)
"eLS" = (
/obj/machinery/status_display{
layer = 4
@@ -38610,6 +38650,19 @@
slowdown = -0.3
},
/area/station/hallway/spacebridge/cargocom)
+"ffm" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/structure/cable/orange{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
+/turf/simulated/floor/plating,
+/area/station/science/xenobiology)
"ffy" = (
/obj/machinery/atmospherics/portable/canister/carbon_dioxide,
/turf/simulated/floor/plasteel{
@@ -40325,6 +40378,11 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 6
},
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/plasteel{
icon_state = "darkblue"
},
@@ -40487,10 +40545,6 @@
/obj/effect/spawner/random_spawners/dirt_often,
/turf/simulated/floor/plasteel,
/area/station/supply/office)
-"fSO" = (
-/obj/effect/spawner/window/reinforced/grilled,
-/turf/simulated/floor/plating,
-/area/station/security/prisonershuttle)
"fSQ" = (
/obj/machinery/door/airlock/maintenance{
name = "Fore Asteroid Maintenance Access"
@@ -41441,6 +41495,30 @@
icon_state = "dark"
},
/area/station/engineering/atmos)
+"gku" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "dark"
+ },
+/area/station/turret_protected/ai)
+"gkD" = (
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"glm" = (
/obj/structure/cable/orange{
d1 = 1;
@@ -41839,11 +41917,6 @@
},
/turf/simulated/floor/engine,
/area/station/engineering/control)
-"guc" = (
-/obj/effect/spawner/window/reinforced/grilled,
-/obj/structure/girder,
-/turf/simulated/floor/plating,
-/area/station/hallway/spacebridge/sercom)
"gux" = (
/obj/structure/sign/securearea{
desc = "A warning sign which reads 'KEEP CLEAR OF PAD WHEN IN USE'.";
@@ -41959,6 +42032,14 @@
icon_state = "wood-broken"
},
/area/station/maintenance/starboard)
+"gvI" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"gvO" = (
/obj/effect/spawner/window/reinforced/grilled,
/obj/structure/cable{
@@ -43508,6 +43589,16 @@
icon_state = "neutralcorner"
},
/area/station/hallway/primary/fore/west)
+"gVP" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/structure/cable/orange,
+/turf/simulated/floor/plating,
+/area/station/science/xenobiology)
"gVY" = (
/obj/structure/railing,
/turf/simulated/floor/plasteel,
@@ -44179,14 +44270,6 @@
icon_state = "white"
},
/area/station/science/misc_lab)
-"hiG" = (
-/obj/machinery/door/poddoor/shutters/preopen{
- dir = 2;
- id_tag = "hopshutter"
- },
-/obj/effect/spawner/window/reinforced/grilled,
-/turf/simulated/floor/plating,
-/area/station/command/office/hop)
"hiI" = (
/obj/structure/chair/sofa/corp{
dir = 8
@@ -44457,6 +44540,14 @@
icon_state = "cafeteria"
},
/area/station/science/hallway)
+"hnC" = (
+/obj/structure/cable{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"hoa" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 6
@@ -44816,6 +44907,26 @@
},
/turf/simulated/floor/wood,
/area/station/service/bar)
+"htw" = (
+/obj/machinery/firealarm{
+ dir = 4;
+ pixel_x = 24;
+ name = "east bump"
+ },
+/obj/machinery/door/airlock/research{
+ name = "Kill Chamber"
+ },
+/obj/effect/mapping_helpers/airlock/access/all/science/xenobio,
+/obj/effect/turf_decal/delivery,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "whitepurplefull"
+ },
+/area/station/science/xenobiology)
"htz" = (
/obj/effect/spawner/random_spawners/dirt_often,
/turf/simulated/floor/plasteel{
@@ -47161,6 +47272,22 @@
/obj/effect/spawner/random_spawners/dirt_often,
/turf/simulated/floor/transparent/glass/reinforced,
/area/station/maintenance/fpmaint)
+"ihn" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "bcircuit"
+ },
+/area/station/turret_protected/ai)
"ihz" = (
/obj/structure/cable/orange{
d1 = 4;
@@ -47537,6 +47664,18 @@
dir = 1
},
/area/station/engineering/smes)
+"ios" = (
+/obj/machinery/door/poddoor/shutters/preopen{
+ dir = 2;
+ id_tag = "hopshutter"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/turf/simulated/floor/plating,
+/area/station/command/office/hop)
"iox" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -51012,6 +51151,18 @@
"jsE" = (
/turf/simulated/floor/wood,
/area/station/maintenance/starboard)
+"jsL" = (
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/bluegrid{
+ nitrogen = 500;
+ oxygen = 0;
+ temperature = 80
+ },
+/area/station/science/xenobiology)
"jsN" = (
/obj/structure/disposalpipe/segment,
/obj/structure/cable/orange{
@@ -51336,21 +51487,6 @@
/obj/effect/spawner/random_spawners/dirt_frequent,
/turf/simulated/floor/plating,
/area/station/supply/miningdock)
-"jxw" = (
-/obj/machinery/firealarm{
- dir = 4;
- pixel_x = 24;
- name = "east bump"
- },
-/obj/machinery/door/airlock/research{
- name = "Kill Chamber"
- },
-/obj/effect/mapping_helpers/airlock/access/all/science/xenobio,
-/obj/effect/turf_decal/delivery,
-/turf/simulated/floor/plasteel{
- icon_state = "whitepurplefull"
- },
-/area/station/science/xenobiology)
"jxJ" = (
/obj/machinery/door/airlock/vault{
locked = 1
@@ -51425,7 +51561,16 @@
/turf/simulated/floor/plating,
/area/station/hallway/primary/fore)
"jzg" = (
-/obj/effect/spawner/window/reinforced,
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
/turf/simulated/floor/plating,
/area/station/turret_protected/aisat/interior/secondary)
"jzq" = (
@@ -53260,13 +53405,13 @@
/turf/simulated/floor/plating,
/area/station/hallway/primary/fore)
"kda" = (
-/obj/effect/spawner/window/reinforced/polarized{
- id = "qm"
- },
/obj/structure/cable/orange{
d2 = 2;
icon_state = "0-2"
},
+/obj/effect/spawner/window/reinforced/polarized/grilled{
+ id = "qm"
+ },
/turf/simulated/floor/plating,
/area/station/supply/qm)
"kdf" = (
@@ -55920,7 +56065,6 @@
},
/area/station/security/detective)
"kTL" = (
-/obj/effect/spawner/window/reinforced,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/structure/cable{
@@ -55928,6 +56072,21 @@
d2 = 2;
icon_state = "1-2"
},
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
+ },
+/obj/structure/cable{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
/turf/simulated/floor/plating,
/area/station/turret_protected/aisat/interior/secondary)
"kTN" = (
@@ -57071,9 +57230,7 @@
/turf/simulated/floor/plating,
/area/station/maintenance/disposal/north)
"llt" = (
-/obj/effect/turf_decal/stripes/corner{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/corner,
/obj/effect/turf_decal/loading_area{
dir = 4
},
@@ -57391,6 +57548,22 @@
},
/turf/simulated/floor/plating,
/area/station/command/office/cmo)
+"loR" = (
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/unary/vent_pump/on{
+ dir = 4
+ },
+/obj/structure/cable/orange{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8"
+ },
+/turf/simulated/floor/plasteel,
+/area/station/security/prisonershuttle)
"lpe" = (
/obj/structure/table,
/obj/item/book/manual/supermatter_engine,
@@ -58082,6 +58255,14 @@
icon_state = "darkyellow"
},
/area/station/engineering/break_room)
+"lzv" = (
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"lzH" = (
/obj/structure/lattice,
/turf/space,
@@ -58274,10 +58455,6 @@
},
/turf/simulated/floor/transparent/glass/reinforced,
/area/station/maintenance/fsmaint)
-"lCh" = (
-/obj/effect/spawner/window/reinforced,
-/turf/simulated/floor/plating,
-/area/station/maintenance/starboard)
"lCs" = (
/obj/structure/cable/orange{
d1 = 1;
@@ -58904,6 +59081,14 @@
icon_state = "asteroidplating"
},
/area/station/maintenance/port2)
+"lPi" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"lPn" = (
/obj/effect/spawner/window/reinforced/grilled,
/obj/structure/cable/orange{
@@ -59691,9 +59876,7 @@
dir = 1
},
/obj/machinery/disposal,
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "whitepurple"
@@ -60597,6 +60780,11 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/disposal/westalt)
+"msG" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange,
+/turf/simulated/floor/plating,
+/area/station/science/xenobiology)
"mte" = (
/obj/structure/disposalpipe/trunk{
dir = 4
@@ -62488,15 +62676,6 @@
icon_state = "dark"
},
/area/station/maintenance/asmaint)
-"mZH" = (
-/obj/machinery/door/airlock/external{
- id_tag = "laborcamp_home";
- locked = 1;
- name = "Labor Camp Airlock"
- },
-/obj/effect/mapping_helpers/airlock/access/all/security/brig,
-/turf/simulated/floor/plating,
-/area/station/security/prisonershuttle)
"mZP" = (
/obj/machinery/power/apc{
dir = 4;
@@ -62770,6 +62949,11 @@
/obj/machinery/light/small{
dir = 4
},
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
/turf/simulated/floor/bluegrid{
nitrogen = 500;
oxygen = 0;
@@ -65313,11 +65497,6 @@
},
/turf/simulated/wall,
/area/station/hallway/primary/port/south)
-"nZe" = (
-/obj/structure/girder,
-/obj/effect/spawner/window/reinforced/grilled,
-/turf/simulated/floor/plating,
-/area/station/hallway/spacebridge/scidock)
"nZf" = (
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
@@ -66070,6 +66249,14 @@
icon_state = "dark"
},
/area/station/medical/morgue)
+"olH" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/turf/simulated/floor/plating,
+/area/station/turret_protected/ai)
"olT" = (
/obj/structure/cable{
d1 = 4;
@@ -66096,13 +66283,13 @@
/turf/simulated/floor/plasteel,
/area/station/science/toxins/launch)
"omd" = (
-/obj/effect/spawner/window/reinforced/polarized{
- id = "qm"
- },
/obj/structure/cable/orange{
d2 = 8;
icon_state = "0-8"
},
+/obj/effect/spawner/window/reinforced/polarized/grilled{
+ id = "qm"
+ },
/turf/simulated/floor/plating,
/area/station/supply/office)
"omg" = (
@@ -66457,6 +66644,17 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/storage)
+"oqx" = (
+/obj/structure/chair{
+ dir = 4
+ },
+/obj/structure/cable/orange{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
+/turf/simulated/floor/plasteel,
+/area/station/security/prisonershuttle)
"oqB" = (
/obj/structure/cable{
d1 = 2;
@@ -68064,9 +68262,7 @@
/obj/machinery/atmospherics/unary/portables_connector{
dir = 1
},
-/obj/machinery/atmospherics/unary/portables_connector{
- dir = 2
- },
+/obj/machinery/atmospherics/unary/portables_connector,
/obj/machinery/atmospherics/portable/canister/air,
/turf/simulated/floor/plating,
/area/station/hallway/primary/aft/west)
@@ -69966,6 +70162,20 @@
/obj/structure/reagent_dispensers/watertank,
/turf/simulated/floor/plating/asteroid/ancient,
/area/station/maintenance/starboard)
+"pyM" = (
+/obj/machinery/door/poddoor/shutters/preopen{
+ dir = 2;
+ id_tag = "hopshutter"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plating,
+/area/station/command/office/hop)
"pzi" = (
/obj/structure/disposalpipe/segment,
/obj/effect/spawner/window/reinforced,
@@ -70077,9 +70287,7 @@
/obj/effect/turf_decal/stripes/corner{
dir = 4
},
-/obj/effect/turf_decal/stripes/corner{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/corner,
/obj/effect/turf_decal/loading_area{
dir = 4
},
@@ -74039,6 +74247,19 @@
},
/turf/simulated/floor/plasteel/white,
/area/station/science/xenobiology)
+"qKQ" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"qLa" = (
/obj/machinery/computer/arcade/recruiter{
dir = 4
@@ -74724,11 +74945,6 @@
icon_state = "redcorner"
},
/area/station/security/main)
-"qYs" = (
-/obj/effect/spawner/window/reinforced/grilled,
-/obj/structure/girder,
-/turf/simulated/floor/plating,
-/area/station/hallway/spacebridge/medcargo)
"qYD" = (
/obj/item/pen,
/obj/item/storage/firstaid/regular,
@@ -76769,6 +76985,24 @@
icon_state = "whitebluefull"
},
/area/station/medical/paramedic)
+"rMi" = (
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"rMx" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 9
@@ -78009,9 +78243,7 @@
/turf/simulated/floor/plasteel,
/area/station/hallway/secondary/exit)
"shh" = (
-/obj/effect/turf_decal/stripes/line{
- dir = 2
- },
+/obj/effect/turf_decal/stripes/line,
/obj/item/radio/intercom{
pixel_x = 28;
name = "custom placement"
@@ -78903,6 +79135,22 @@
icon_state = "purplecorner"
},
/area/station/hallway/primary/aft/east)
+"suI" = (
+/obj/structure/disposalpipe/segment,
+/obj/machinery/door/airlock/command{
+ name = "Head of Personnel"
+ },
+/obj/effect/mapping_helpers/airlock/access/all/command/hop,
+/obj/machinery/door/firedoor,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/wood,
+/area/station/command/office/hop)
"suS" = (
/obj/machinery/photocopier,
/turf/simulated/floor/wood,
@@ -79953,6 +80201,28 @@
icon_state = "redcorner"
},
/area/station/security/lobby)
+"sOe" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "bcircuit"
+ },
+/area/station/turret_protected/aisat/interior/secondary)
"sOf" = (
/obj/machinery/processor,
/turf/simulated/floor/plasteel{
@@ -80904,6 +81174,28 @@
/obj/structure/closet/firecloset/full,
/turf/simulated/floor/plating/asteroid/ancient,
/area/station/maintenance/apmaint)
+"tcP" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/obj/structure/cable,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"tdk" = (
/obj/structure/cable/orange{
d1 = 4;
@@ -81140,6 +81432,11 @@
dir = 1
},
/obj/machinery/hologram/holopad,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
/turf/simulated/floor/plasteel/white,
/area/station/science/xenobiology)
"tiv" = (
@@ -81327,6 +81624,21 @@
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
/area/station/security/permabrig)
+"tkv" = (
+/obj/structure/cable/orange{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
+/obj/structure/cable/orange{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "dark"
+ },
+/area/station/command/bridge)
"tkC" = (
/obj/machinery/door/window/brigdoor{
id = "Cell 7";
@@ -82811,6 +83123,20 @@
/obj/effect/spawner/airlock/s_to_n,
/turf/simulated/wall,
/area/station/hallway/primary/central)
+"tMN" = (
+/obj/machinery/door/airlock/external{
+ id_tag = "laborcamp_home";
+ locked = 1;
+ name = "Labor Camp Airlock"
+ },
+/obj/effect/mapping_helpers/airlock/access/all/security/brig,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/plating,
+/area/station/security/prisonershuttle)
"tMP" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
@@ -93095,7 +93421,6 @@
/turf/simulated/floor/engine,
/area/station/engineering/control)
"wSP" = (
-/obj/effect/spawner/window/reinforced,
/obj/structure/cable{
d1 = 4;
d2 = 8;
@@ -93104,6 +93429,11 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 4;
+ icon_state = "0-4"
+ },
/turf/simulated/floor/plating,
/area/station/turret_protected/aisat/interior/secondary)
"wTg" = (
@@ -94104,6 +94434,24 @@
dir = 1
},
/area/station/engineering/smes)
+"xjN" = (
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/obj/effect/spawner/window/reinforced/grilled,
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
"xjO" = (
/obj/machinery/atmospherics/pipe/manifold/hidden/supply,
/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
@@ -95166,6 +95514,17 @@
icon_state = "white"
},
/area/station/science/toxins/mixing)
+"xDS" = (
+/obj/effect/turf_decal/stripes/line,
+/obj/structure/cable/orange{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/floor/plasteel{
+ icon_state = "whitepurple"
+ },
+/area/station/science/xenobiology)
"xDX" = (
/obj/structure/disposalpipe/trunk{
dir = 1
@@ -95549,13 +95908,30 @@
},
/area/station/hallway/primary/fore/west)
"xJW" = (
-/obj/effect/spawner/window/reinforced,
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/turf/simulated/floor/plating,
+/area/station/turret_protected/aisat/interior/secondary)
+"xKb" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8"
+ },
/turf/simulated/floor/plating,
/area/station/turret_protected/aisat/interior/secondary)
"xKr" = (
@@ -97181,6 +97557,14 @@
},
/turf/simulated/floor/plating,
/area/station/maintenance/disposal/external/southwest)
+"ymd" = (
+/obj/effect/spawner/window/reinforced/grilled,
+/obj/structure/cable/orange{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/turf/simulated/floor/plating,
+/area/station/security/prisonershuttle)
(1,1,1) = {"
rNK
@@ -110848,13 +111232,13 @@ rNK
rNK
pVD
pVD
-fSO
-mZH
-fSO
+ymd
+tMN
+cyq
cdL
-fSO
-mZH
-fSO
+ymd
+tMN
+cyq
pVD
pVD
pVD
@@ -111107,11 +111491,11 @@ pVD
aWm
mFT
mFT
-dqa
+oqx
ale
dqa
mFT
-dqa
+oqx
aoZ
dqa
qbx
@@ -111364,7 +111748,7 @@ pVD
doB
ajA
dpg
-akC
+loR
alg
vKV
akC
@@ -113211,14 +113595,14 @@ ice
ice
ice
leM
-guc
-guc
-guc
+hrT
+hrT
+hrT
lAN
lAN
-guc
-guc
-guc
+hrT
+hrT
+hrT
uSv
uSv
uSv
@@ -119422,10 +119806,10 @@ wph
xOD
sbn
shL
-cxW
-cxW
-cxW
-cxW
+mNj
+mNj
+mNj
+mNj
shL
sbn
uQh
@@ -119744,9 +120128,9 @@ nKb
cpI
qZD
tia
-kvs
-jxw
-wQY
+xDS
+htw
+jsL
nej
vey
vWk
@@ -120004,9 +120388,9 @@ tqK
yiP
xPU
xPU
-pzF
-pzF
-pzF
+ffm
+gVP
+msG
ftr
ftr
ksj
@@ -125299,7 +125683,7 @@ rNK
rNK
qQU
qQU
-fph
+qQU
aYa
aXP
hqg
@@ -126672,7 +127056,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
xcc
@@ -126929,7 +127313,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
rNK
@@ -127186,7 +127570,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
rNK
@@ -127700,7 +128084,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
rNK
@@ -127957,7 +128341,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
xcc
@@ -128214,7 +128598,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
lzH
lzH
xcc
@@ -128728,7 +129112,7 @@ oeQ
hRR
qyP
hKz
-nZe
+oeQ
ayb
ayb
ayb
@@ -128873,7 +129257,7 @@ bse
btG
bxr
bxF
-hiG
+ios
dQx
bEi
bUO
@@ -128985,7 +129369,7 @@ uVb
olT
qyP
hKz
-nZe
+oeQ
hBQ
ayb
hBQ
@@ -129124,13 +129508,13 @@ aVS
aVS
aVS
xLP
-cyR
+tkv
fPE
-cHZ
+suI
btJ
cLU
byn
-hiG
+pyM
dQx
bEi
bUO
@@ -130972,7 +131356,7 @@ cKs
cJC
qMy
qMy
-bnS
+xIu
bqB
uRg
cKr
@@ -131229,7 +131613,7 @@ pGS
cJD
qMy
cWK
-bnS
+xIu
rbm
aFE
btx
@@ -131486,7 +131870,7 @@ cKs
cJC
qMy
qMy
-bnS
+xIu
bqx
pnd
cKr
@@ -132326,7 +132710,7 @@ cYB
tnb
qyP
hKz
-nZe
+uVb
cYB
cYB
cYB
@@ -132840,7 +133224,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
ayb
ayb
ayb
@@ -133097,7 +133481,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
ayb
ayb
xcc
@@ -133354,7 +133738,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
ayb
rNK
xcc
@@ -133868,7 +134252,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
xcc
@@ -134125,7 +134509,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
xcc
@@ -134382,7 +134766,7 @@ oeQ
hrd
qyP
hKz
-nZe
+oeQ
rNK
rNK
xcc
@@ -139884,9 +140268,9 @@ acD
afU
afO
aig
-aig
-ajF
+ihn
ajF
+gku
akq
pTh
vzj
@@ -140149,10 +140533,10 @@ sYg
ajS
okP
aRj
-jzg
+lzv
oyx
buv
-jzg
+gvI
xJW
jzg
asE
@@ -140405,13 +140789,13 @@ lTy
sYg
jEQ
okP
-jzg
-jzg
+hnC
+rMi
oyx
buv
-jzg
+qKQ
mwF
-jzg
+qKQ
asE
oyx
okP
@@ -140663,10 +141047,10 @@ akZ
gUh
vsD
amJ
-amJ
+sOe
aNf
aqk
-kTL
+tcP
aZL
kTL
btr
@@ -140919,13 +141303,13 @@ jhe
sYg
dBu
okP
-jzg
-jzg
+hnC
+xjN
oyx
asE
-jzg
+xKb
nlZ
-jzg
+xKb
buv
oyx
okP
@@ -141169,20 +141553,20 @@ dvV
dvV
agS
sYg
-agW
+olH
afn
-agW
+olH
akv
sYg
ajS
okP
aRj
-jzg
+gkD
oyx
asE
-jzg
+lPi
wSP
-jzg
+lPi
buv
oyx
mmW
@@ -141426,7 +141810,7 @@ dvV
afU
ojF
aik
-aik
+eLs
afq
ajG
akw
@@ -147654,10 +148038,10 @@ rWb
rWb
aRx
mok
-qYs
+rsZ
mKP
-qYs
-qYs
+rsZ
+rsZ
mok
aZq
aZq
@@ -153618,7 +154002,7 @@ jsE
jsE
vWi
jsE
-lCh
+bDv
rNK
rNK
gEl
diff --git a/_maps/map_files/generic/centcomm.dmm b/_maps/map_files/generic/centcomm.dmm
index bc5aa5d5d3da..8295f9818031 100644
--- a/_maps/map_files/generic/centcomm.dmm
+++ b/_maps/map_files/generic/centcomm.dmm
@@ -1823,6 +1823,7 @@
dir = 4;
pixel_y = 8
},
+/obj/machinery/economy/vending/cigarette,
/turf/simulated/floor/wood,
/area/ghost_bar)
"gT" = (
@@ -3686,7 +3687,7 @@
/area/admin)
"mz" = (
/obj/machinery/economy/vending/boozeomat,
-/turf/simulated/wall/indestructible/riveted,
+/turf/simulated/floor/wood,
/area/ghost_bar)
"mA" = (
/obj/structure/sign/poster/official/fruit_bowl,
@@ -5137,6 +5138,12 @@
/obj/item/stack/sheet/metal/fifty,
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
+"rq" = (
+/obj/structure/closet/crate/can,
+/turf/simulated/floor/plasteel{
+ icon_state = "cafeteria"
+ },
+/area/ghost_bar)
"rr" = (
/obj/structure/table,
/obj/item/storage/box/beakers,
@@ -5679,10 +5686,10 @@
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
"tr" = (
-/turf/simulated/floor/wood{
- broken = 1;
- icon_state = "wood-broken"
+/obj/machinery/light/small{
+ dir = 1
},
+/turf/simulated/floor/wood,
/area/ghost_bar)
"ts" = (
/obj/machinery/atmospherics/portable/canister/oxygen,
@@ -6703,6 +6710,7 @@
desc = "Used to grind things up into raw materials and liquids.";
pixel_y = 5
},
+/obj/item/reagent_containers/glass/beaker/large,
/turf/simulated/floor/plasteel{
icon_state = "cafeteria"
},
@@ -6987,7 +6995,6 @@
"xU" = (
/obj/structure/table/reinforced,
/obj/machinery/kitchen_machine/microwave/upgraded,
-/obj/machinery/light/small,
/turf/simulated/floor/plasteel{
icon_state = "cafeteria"
},
@@ -6997,10 +7004,7 @@
/turf/simulated/floor/plasteel/dark,
/area/ghost_bar)
"xY" = (
-/obj/machinery/economy/vending/cigarette,
-/obj/machinery/light/small{
- dir = 1
- },
+/obj/structure/closet/crate/can,
/turf/simulated/floor/wood,
/area/ghost_bar)
"xZ" = (
@@ -7288,6 +7292,13 @@
/obj/machinery/computer/cryopod/robot,
/turf/simulated/wall/indestructible/riveted,
/area/centcom/specops)
+"zP" = (
+/obj/machinery/light/small,
+/obj/machinery/economy/vending/dinnerware,
+/turf/simulated/floor/plasteel{
+ icon_state = "cafeteria"
+ },
+/area/ghost_bar)
"zQ" = (
/obj/machinery/light/small,
/turf/simulated/floor/plasteel/freezer,
@@ -13851,6 +13862,7 @@
/obj/machinery/light/small{
dir = 8
},
+/obj/structure/closet/crate/can,
/turf/simulated/floor/wood,
/area/ghost_bar)
"Xb" = (
@@ -72235,7 +72247,7 @@ Jy
vd
vd
vd
-Jy
+rf
Me
EX
Jy
@@ -72492,7 +72504,7 @@ Jy
vd
cX
vd
-rf
+xY
Me
wh
rd
@@ -73785,8 +73797,8 @@ Me
Me
Me
Me
-xY
-Jy
+Me
+tr
Jy
qH
Nm
@@ -74041,8 +74053,8 @@ es
Oj
zj
fF
-Me
-Jy
+rq
+aT
Jy
ip
Jy
@@ -74298,9 +74310,9 @@ es
es
es
es
+es
Uk
Jy
-Jy
ol
ol
Hz
@@ -74555,8 +74567,8 @@ wN
Dk
zY
xU
+zP
Me
-tr
Jy
rG
hU
@@ -74812,8 +74824,8 @@ Me
Me
Me
Me
-aT
-Jy
+Me
+Me
Jy
RX
RX
@@ -75070,9 +75082,9 @@ ps
ps
ps
Me
-Jy
+Me
gS
-Jy
+xY
Me
Me
Me
diff --git a/code/__DEFINES/_init.dm b/code/__DEFINES/_init.dm
index 5346b087c0d9..3562b9a61018 100644
--- a/code/__DEFINES/_init.dm
+++ b/code/__DEFINES/_init.dm
@@ -4,7 +4,7 @@
// This exists so that world.Profile() is THE FIRST PROC TO RUN in the init sequence.
// This allows us to get the real details of everything lagging at server start.
world.Profile(PROFILE_START)
- #if defined(ENABLE_BYOND_TRACY) && (DM_BUILD == 1589)
+ #if defined(ENABLE_BYOND_TRACY)
var/tracy_init = CALL_EXT("prof.dll", "init")() // Setup Tracy integration
if(tracy_init != "0")
CRASH("Tracy init error: [tracy_init]")
diff --git a/code/__DEFINES/_tgs_defines.dm b/code/__DEFINES/_tgs_defines.dm
index 23d1e8c39268..4aa8662ec4ca 100644
--- a/code/__DEFINES/_tgs_defines.dm
+++ b/code/__DEFINES/_tgs_defines.dm
@@ -3,7 +3,7 @@
#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
-#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]")
+#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "
Host Announcement: [html_encode(##message)]
")
#define TGS_INFO_LOG(message) log_tgs(message, "INF")
#define TGS_WARNING_LOG(message) log_tgs(message, "WRN")
#define TGS_ERROR_LOG(message) log_tgs(message, "ERR")
diff --git a/code/__DEFINES/bots.dm b/code/__DEFINES/bots.dm
index c283445d0ad6..9301b1c79232 100644
--- a/code/__DEFINES/bots.dm
+++ b/code/__DEFINES/bots.dm
@@ -24,6 +24,8 @@
#define BOT_NAV 15 // computing navigation
#define BOT_WAIT_FOR_NAV 16 // waiting for nav computation
#define BOT_NO_ROUTE 17 // no destination beacon found (or no route)
+#define BOT_MAKE_TILE 18 // converting metal into tiles (floorbots)
+#define BOT_EAT_TILE 19 // adding said tiles to inventory (floorbots)
//Bot types
#define SEC_BOT 1 // Secutritrons (Beepsky) and ED-209s
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index 89976c498422..c6596ea46c10 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
-#define TGS_DMAPI_VERSION "6.5.0"
+#define TGS_DMAPI_VERSION "7.0.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -73,12 +73,12 @@
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
-/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND.
-#define TGS_EVENT_BYOND_INSTALL_START 5
-/// When a BYOND install operation fails. Parameters: Error message
-#define TGS_EVENT_BYOND_INSTALL_FAIL 6
-/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND.
-#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
+/// Before a engine install operation begins. Parameters: Version string of the installing engine.
+#define TGS_EVENT_ENGINE_INSTALL_START 5
+/// When a engine install operation fails. Parameters: Error message
+#define TGS_EVENT_ENGINE_INSTALL_FAIL 6
+/// When the active engine version changes. Parameters: (Nullable) Version string of the current engine, version string of the new engine.
+#define TGS_EVENT_ENGINE_ACTIVE_VERSION_CHANGE 7
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
#define TGS_EVENT_COMPILE_START 8
/// When a compile is cancelled. No parameters.
@@ -108,7 +108,7 @@
// #define TGS_EVENT_DREAM_DAEMON_LAUNCH 22
/// After a single submodule update is performed. Parameters: Updated submodule name.
#define TGS_EVENT_REPO_SUBMODULE_UPDATE 23
-/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, byond version.
+/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, version string of the used engine.
#define TGS_EVENT_PRE_DREAM_MAKER 24
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path.
#define TGS_EVENT_DEPLOYMENT_CLEANUP 25
@@ -122,6 +122,7 @@
/// The watchdog will restart on reboot.
#define TGS_REBOOT_MODE_RESTART 2
+// Note that security levels are currently meaningless in OpenDream
/// DreamDaemon Trusted security level.
#define TGS_SECURITY_TRUSTED 0
/// DreamDaemon Safe security level.
@@ -129,6 +130,18 @@
/// DreamDaemon Ultrasafe security level.
#define TGS_SECURITY_ULTRASAFE 2
+/// DreamDaemon public visibility level.
+#define TGS_VISIBILITY_PUBLIC 0
+/// DreamDaemon private visibility level.
+#define TGS_VISIBILITY_PRIVATE 1
+/// DreamDaemon invisible visibility level.
+#define TGS_VISIBILITY_INVISIBLE 2
+
+/// The Build Your Own Net Dream engine.
+#define TGS_ENGINE_TYPE_BYOND 0
+/// The OpenDream engine.
+#define TGS_ENGINE_TYPE_OPENDREAM 1
+
//REQUIRED HOOKS
/**
@@ -154,7 +167,7 @@
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
/**
- * Call this as late as possible in [world/proc/Reboot].
+ * Call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
*/
/world/proc/TgsReboot()
return
@@ -442,6 +455,10 @@
/world/proc/TgsVersion()
return
+/// Returns the running engine type
+/world/proc/TgsEngine()
+ return
+
/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
/world/proc/TgsApiVersion()
return
@@ -458,6 +475,10 @@
/world/proc/TgsSecurityLevel()
return
+/// Returns the current BYOND visibility level as a TGS_VISIBILITY_ define if TGS is present, null otherwise. Requires TGS to be using interop API version 5 or higher otherwise the string "___unimplemented" wil be returned. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
+/world/proc/TgsVisibility()
+ return
+
/// Returns a list of active [/datum/tgs_revision_information/test_merge]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
/world/proc/TgsTestMerges()
return
diff --git a/code/_globalvars/lists/reagents_lists.dm b/code/_globalvars/lists/reagents_lists.dm
index 2d70edcc5835..39540790b932 100644
--- a/code/_globalvars/lists/reagents_lists.dm
+++ b/code/_globalvars/lists/reagents_lists.dm
@@ -46,4 +46,6 @@ GLOBAL_LIST_INIT(blocked_chems, list("polonium", "initropidril", "concentrated_i
"spidereggs", "heartworms", "bacon_grease",
"fungalspores", "jagged_crystals", "salmonella",
"lavaland_extract", "stable_mutagen", "beer2",
- "curare", "gluttonytoxin", "smoke_powder", "stimulative_cling"))
+ "curare", "gluttonytoxin", "smoke_powder", "stimulative_cling",
+ "teslium_paste"
+ ))
diff --git a/code/controllers/subsystem/SSticker.dm b/code/controllers/subsystem/SSticker.dm
index 447ed03b9e4e..b44fb91e9264 100644
--- a/code/controllers/subsystem/SSticker.dm
+++ b/code/controllers/subsystem/SSticker.dm
@@ -657,13 +657,20 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/setup_news_feeds()
var/datum/feed_channel/newChannel = new /datum/feed_channel
- newChannel.channel_name = "Public Station Announcements"
+ newChannel.channel_name = "Station Announcements Log"
newChannel.author = "Automated Announcement Listing"
newChannel.icon = "bullhorn"
newChannel.frozen = TRUE
newChannel.admin_locked = TRUE
GLOB.news_network.channels += newChannel
+ newChannel = new /datum/feed_channel
+ newChannel.channel_name = "Public Station Announcements"
+ newChannel.author = "Automated Announcement Listing"
+ newChannel.icon = "users"
+ newChannel.is_public = TRUE
+ GLOB.news_network.channels += newChannel
+
newChannel = new /datum/feed_channel
newChannel.channel_name = "Nyx Daily"
newChannel.author = "CentComm Minister of Information"
diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm
index 0823d7bad609..404a45324e2f 100644
--- a/code/datums/diseases/_disease.dm
+++ b/code/datums/diseases/_disease.dm
@@ -59,7 +59,7 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease))
//Other
var/list/viable_mobtypes = list() //typepaths of viable mobs
- var/mob/living/carbon/affected_mob = null
+ var/mob/living/carbon/affected_mob
var/list/cures = list() //list of cures if the disease has the CURABLE flag, these are reagent ids
var/infectivity = 65
var/cure_chance = 8
@@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease))
var/bypasses_immunity = FALSE //Does it skip species virus immunity check? Some things may diseases and not viruses
var/virus_heal_resistant = FALSE // Some things aren't technically viruses/traditional diseases and should be immune to edge case cure methods, like healing viruses.
var/permeability_mod = 1
- var/severity = NONTHREAT
+ var/severity = NONTHREAT
var/list/required_organs = list()
var/needs_all_cures = TRUE
var/list/strain_data = list() //dna_spread special bullshit
diff --git a/code/datums/diseases/anxiety.dm b/code/datums/diseases/anxiety.dm
index b27911441634..b91ac32b1ed8 100644
--- a/code/datums/diseases/anxiety.dm
+++ b/code/datums/diseases/anxiety.dm
@@ -9,13 +9,13 @@
agent = "Excess Lepidopticides"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
desc = "If left untreated subject will regurgitate butterflies."
- severity = MEDIUM
+ severity = MINOR
/datum/disease/anxiety/stage_act()
if(!..())
return FALSE
switch(stage)
- if(2) //also changes say, see say.dm
+ if(2)
if(prob(5))
to_chat(affected_mob, "You feel anxious.")
if(3)
diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm
index f58a9a6d0586..674abaafe2a2 100644
--- a/code/datums/diseases/appendicitis.dm
+++ b/code/datums/diseases/appendicitis.dm
@@ -7,9 +7,8 @@
cure_text = "Surgery"
agent = "Shitty Appendix"
viable_mobtypes = list(/mob/living/carbon/human)
- permeability_mod = 1
desc = "If left untreated the subject will become very weak, and may vomit often."
- severity = "Dangerous!"
+ severity = MINOR
disease_flags = CAN_CARRY|CAN_RESIST
visibility_flags = HIDDEN_PANDEMIC
required_organs = list(/obj/item/organ/internal/appendix)
diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm
index 50ad51132437..348837c8d9f0 100644
--- a/code/datums/diseases/beesease.dm
+++ b/code/datums/diseases/beesease.dm
@@ -9,13 +9,13 @@
agent = "Apidae Infection"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
desc = "If left untreated subject will regurgitate bees."
- severity = DANGEROUS
+ severity = BIOHAZARD
/datum/disease/beesease/stage_act()
if(!..())
return FALSE
switch(stage)
- if(2) //also changes say, see say.dm // no it doesn't, that's horrifyingly snowflakey
+ if(2)
if(prob(2))
to_chat(affected_mob, "You taste honey in your mouth.")
if(3)
@@ -35,6 +35,4 @@
affected_mob.visible_message("[affected_mob] coughs up a swarm of bees!", \
"You cough up a swarm of bees!")
new /mob/living/simple_animal/hostile/poison/bees(affected_mob.loc)
- //if(5)
- //Plus if you die, you explode into bees
return
diff --git a/code/datums/diseases/berserker.dm b/code/datums/diseases/berserker.dm
index 38c82959ce11..b9610057d132 100644
--- a/code/datums/diseases/berserker.dm
+++ b/code/datums/diseases/berserker.dm
@@ -10,7 +10,7 @@
cure_chance = 10
viable_mobtypes = list(/mob/living/carbon/human)
desc = "Swearing, shouting, attacking nearby crew members uncontrollably."
- severity = DANGEROUS
+ severity = BIOHAZARD
disease_flags = CURABLE
/datum/disease/berserker/stage_act()
diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm
index 9fb2774a0bf5..ae5c5a237820 100644
--- a/code/datums/diseases/brainrot.dm
+++ b/code/datums/diseases/brainrot.dm
@@ -7,12 +7,12 @@
cures = list("mannitol")
agent = "Cryptococcus Cosmosis"
viable_mobtypes = list(/mob/living/carbon/human)
- cure_chance = 15//higher chance to cure, since two reagents are required
+ cure_chance = 15
desc = "This disease destroys the braincells, causing brain fever, brain necrosis and general intoxication."
required_organs = list(/obj/item/organ/internal/brain)
- severity = DANGEROUS
+ severity = HARMFUL
-/datum/disease/brainrot/stage_act() //Removed toxloss because damaging diseases are pretty horrible. Last round it killed the entire station because the cure didn't work -- Urist -ACTUALLY Removed rather than commented out, I don't see it returning - RR
+/datum/disease/brainrot/stage_act()
if(!..())
return FALSE
switch(stage)
@@ -30,7 +30,7 @@
affected_mob.emote("stare")
if(prob(2))
affected_mob.emote("drool")
- if(prob(10) && affected_mob.getBrainLoss()<=98)//shouldn't brainpain you to death now
+ if(prob(10) && affected_mob.getBrainLoss() < 100)
affected_mob.adjustBrainLoss(2)
if(prob(2))
to_chat(affected_mob, "Your try to remember something important...but can't.")
@@ -40,7 +40,7 @@
affected_mob.emote("stare")
if(prob(2))
affected_mob.emote("drool")
- if(prob(15) && affected_mob.getBrainLoss()<=98) //shouldn't brainpain you to death now
+ if(prob(15) && affected_mob.getBrainLoss() < 100)
affected_mob.adjustBrainLoss(3)
if(prob(2))
to_chat(affected_mob, "Strange buzzing fills your head, removing all thoughts.")
diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm
index 85c3bba22d9a..584b284fd521 100644
--- a/code/datums/diseases/cold9.dm
+++ b/code/datums/diseases/cold9.dm
@@ -4,38 +4,27 @@
max_stages = 3
spread_text = "On contact"
spread_flags = CONTACT_GENERAL
- cure_text = "Common Cold Anti-bodies & Spaceacillin"
+ cure_text = "Spaceacillin"
cures = list("spaceacillin")
agent = "ICE9-rhinovirus"
viable_mobtypes = list(/mob/living/carbon/human)
desc = "If left untreated the subject will slow, as if partly frozen."
- severity = MEDIUM
+ severity = HARMFUL
/datum/disease/cold9/stage_act()
if(!..())
return FALSE
- switch(stage)
- if(2)
- affected_mob.bodytemperature -= 10
- if(prob(1) && prob(10))
- to_chat(affected_mob, "You feel better.")
- cure()
- return
- if(prob(1))
- affected_mob.emote("sneeze")
- if(prob(1))
- affected_mob.emote("cough")
- if(prob(1))
- to_chat(affected_mob, "Your throat feels sore.")
- if(prob(5))
- to_chat(affected_mob, "You feel stiff.")
- if(3)
- affected_mob.bodytemperature -= 20
- if(prob(1))
- affected_mob.emote("sneeze")
- if(prob(1))
- affected_mob.emote("cough")
- if(prob(1))
- to_chat(affected_mob, "Your throat feels sore.")
- if(prob(10))
- to_chat(affected_mob, "You feel stiff.")
+ if(stage < 2)
+ return
+
+ var/stage_factor = stage - 1
+ affected_mob.bodytemperature -= 7.5 * stage_factor // Enough to consistently frostburn certain species alive at stage 3
+ if(prob(2 * stage_factor))
+ affected_mob.emote("sneeze")
+ if(prob(2 * stage_factor))
+ affected_mob.emote("cough")
+ if(prob(3 * stage_factor))
+ to_chat(affected_mob, "Your throat feels sore.")
+ if(prob(5 * stage_factor))
+ to_chat(affected_mob, "You feel stiff.")
+ affected_mob.adjustFireLoss(1)
diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm
index a88c4f8abad9..ac83c8f15de6 100644
--- a/code/datums/diseases/critical.dm
+++ b/code/datums/diseases/critical.dm
@@ -1,4 +1,12 @@
/datum/disease/critical
+ form = "Medical Emergency"
+ max_stages = 3
+ spread_flags = SPECIAL
+ viable_mobtypes = list(/mob/living/carbon/human)
+ severity = MINOR
+ disease_flags = CURABLE
+ bypasses_immunity = TRUE
+ virus_heal_resistant = TRUE
/datum/disease/critical/stage_act() //overriden to ensure unique behavior
stage = min(stage, max_stages)
@@ -20,19 +28,11 @@
/datum/disease/critical/shock
name = "Shock"
- form = "Medical Emergency"
- spread_text = "The patient is in shock"
- max_stages = 3
- spread_flags = SPECIAL
+ spread_text = "The patient is in shock."
cure_text = "Saline-Glucose Solution"
cures = list("salglu_solution", "syndicate_nanites", "stimulative_agent")
cure_chance = 10
- viable_mobtypes = list(/mob/living/carbon/human)
stage_prob = 6
- severity = DANGEROUS
- disease_flags = CURABLE
- bypasses_immunity = TRUE
- virus_heal_resistant = TRUE
/datum/disease/critical/shock/stage_act()
if(..())
@@ -60,7 +60,7 @@
if(prob(5))
to_chat(affected_mob, "You feel absolutely terrible!")
if(prob(5))
- affected_mob.emote("faint", "faint", "groan") // this is bugged, but out of scope, someone fix it :)
+ affected_mob.emote(pick(2; "faint", 1; "groan"))
if(3)
if(prob(1) && prob(10))
to_chat(affected_mob, "You feel better.")
@@ -71,7 +71,7 @@
if(prob(5))
to_chat(affected_mob, "You feel horrible!")
if(prob(5))
- affected_mob.emote(pick("faint", "faint", "groan"))
+ affected_mob.emote(pick(2; "faint", 1; "groan"))
if(prob(7))
to_chat(affected_mob, "You can't breathe!")
affected_mob.AdjustLoseBreath(2 SECONDS)
@@ -81,21 +81,13 @@
/datum/disease/critical/heart_failure
name = "Cardiac Failure"
- form = "Medical Emergency"
- spread_text = "The patient is having a cardiac emergency"
- max_stages = 3
- spread_flags = SPECIAL
+ spread_text = "The patient is having a cardiac emergency."
cure_text = "Atropine, Epinephrine, or Heparin"
cures = list("atropine", "epinephrine", "heparin", "syndicate_nanites", "stimulative_agent")
cure_chance = 10
- needs_all_cures = FALSE
- viable_mobtypes = list(/mob/living/carbon/human)
stage_prob = 5
- severity = DANGEROUS
- disease_flags = CURABLE
+ severity = HARMFUL
required_organs = list(/obj/item/organ/internal/heart)
- bypasses_immunity = TRUE
- virus_heal_resistant = TRUE
/datum/disease/critical/heart_failure/stage_act()
if(..())
@@ -125,7 +117,7 @@
to_chat(affected_mob, "Your heart stops beating!")
affected_mob.AdjustLoseBreath(6 SECONDS)
if(prob(5))
- affected_mob.emote(pick("faint", "faint", "groan"))
+ affected_mob.emote(pick(2; "faint", 1; "groan"))
if(3)
affected_mob.adjustOxyLoss(1)
if(prob(8))
@@ -136,17 +128,9 @@
/datum/disease/critical/hypoglycemia
name = "Hypoglycemia"
- form = "Medical Emergency"
- max_stages = 3
- spread_flags = SPECIAL
spread_text = "The patient has low blood sugar."
cure_text = "Eating or administration of vitamins or nutrients"
- viable_mobtypes = list(/mob/living/carbon/human)
stage_prob = 1
- severity = DANGEROUS
- disease_flags = CURABLE
- bypasses_immunity = TRUE
- virus_heal_resistant = TRUE
/datum/disease/critical/hypoglycemia/has_cure()
if(ishuman(affected_mob))
diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm
index 97e56e5829b6..1981a40967ef 100644
--- a/code/datums/diseases/fake_gbs.dm
+++ b/code/datums/diseases/fake_gbs.dm
@@ -8,7 +8,7 @@
agent = "Gravitokinetic Bipotential SADS-"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
desc = "If left untreated death will occur."
- severity = BIOHAZARD
+ severity = BIOHAZARD // Mimicking real GBS
/datum/disease/fake_gbs/stage_act()
if(!..())
diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm
index 14e7c232729b..15540321fb7f 100644
--- a/code/datums/diseases/flu.dm
+++ b/code/datums/diseases/flu.dm
@@ -9,7 +9,7 @@
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
permeability_mod = 0.75
desc = "If left untreated the subject will feel quite unwell."
- severity = MEDIUM
+ severity = MINOR
/datum/disease/flu/stage_act()
if(!..())
diff --git a/code/datums/diseases/fluspanish.dm b/code/datums/diseases/fluspanish.dm
index edd45a743070..6245d9bce0ba 100644
--- a/code/datums/diseases/fluspanish.dm
+++ b/code/datums/diseases/fluspanish.dm
@@ -1,37 +1,28 @@
/datum/disease/fluspanish
- name = "Spanish inquisition Flu"
+ name = "Spanish Inquisition Flu"
max_stages = 3
spread_text = "Airborne"
- cure_text = "Spaceacillin & Anti-bodies to the common flu"
+ cure_text = "Spaceacillin"
cures = list("spaceacillin")
cure_chance = 10
agent = "1nqu1s1t10n flu virion"
viable_mobtypes = list(/mob/living/carbon/human)
permeability_mod = 0.75
desc = "If left untreated the subject will burn to death for being a heretic."
- severity = DANGEROUS
+ severity = HARMFUL
/datum/disease/fluspanish/stage_act()
if(!..())
return FALSE
- switch(stage)
- if(2)
- affected_mob.bodytemperature += 10
- if(prob(5))
- affected_mob.emote("sneeze")
- if(prob(5))
- affected_mob.emote("cough")
- if(prob(1))
- to_chat(affected_mob, "You're burning in your own skin!")
- affected_mob.take_organ_damage(0,5)
+ if(stage < 2)
+ return
- if(3)
- affected_mob.bodytemperature += 20
- if(prob(5))
- affected_mob.emote("sneeze")
- if(prob(5))
- affected_mob.emote("cough")
- if(prob(5))
- to_chat(affected_mob, "You're burning in your own skin!")
- affected_mob.take_organ_damage(0,5)
- return
+ var/stage_factor = stage - 1
+ affected_mob.bodytemperature += 10 * stage_factor // Enough to consistently cook certain species alive at stage 3
+ if(prob(3 * stage_factor))
+ affected_mob.emote("sneeze")
+ if(prob(3 * stage_factor))
+ affected_mob.emote("cough")
+ if(prob(2.5 * stage_factor))
+ to_chat(affected_mob, "You're burning in your own skin!")
+ affected_mob.adjustFireLoss(5)
diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm
index 1ce97b66f658..3044da57aa07 100644
--- a/code/datums/diseases/gbs.dm
+++ b/code/datums/diseases/gbs.dm
@@ -5,11 +5,9 @@
spread_flags = CONTACT_GENERAL
cure_text = "Diphenhydramine & Sulfur"
cures = list("diphenhydramine","sulfur")
- cure_chance = 15//higher chance to cure, since two reagents are required
+ cure_chance = 15 // Higher chance to cure, since two reagents are required
agent = "Gravitokinetic Bipotential SADS+"
viable_mobtypes = list(/mob/living/carbon/human)
- disease_flags = CAN_CARRY|CAN_RESIST|CURABLE
- permeability_mod = 1
severity = BIOHAZARD
/datum/disease/gbs/stage_act()
diff --git a/code/datums/diseases/kingstons.dm b/code/datums/diseases/kingstons.dm
index d9b767945a42..1b46d487de42 100644
--- a/code/datums/diseases/kingstons.dm
+++ b/code/datums/diseases/kingstons.dm
@@ -9,7 +9,7 @@
viable_mobtypes = list(/mob/living/carbon/human)
permeability_mod = 0.75
desc = "If left untreated the subject will turn into a feline. In felines it has... OTHER... effects."
- severity = DANGEROUS
+ severity = BIOHAZARD
/datum/disease/kingstons/stage_act()
if(!..())
@@ -46,8 +46,8 @@
var/mob/living/carbon/human/catface = affected_mob
catface.set_species(/datum/species/tajaran, retain_damage = TRUE, keep_missing_bodyparts = TRUE)
-
-/datum/disease/kingstons_advanced //this used to be directly a subtype of kingstons, which sounds nice, but it ment that it would *turn you into a tarjaran always and have normal kingstons stage act* Don't make virusus subtypes unless the base virus does nothing.
+// Not a subtype of regular Kingstons as it would inherit its `stage_act()`
+/datum/disease/kingstons_advanced
name = "Advanced Kingstons Syndrome"
medical_name = "Advanced Kingstons Syndrome"
max_stages = 4
@@ -75,27 +75,35 @@
/datum/disease/kingstons_advanced/stage_act()
if(!..())
return FALSE
- if(ishuman(affected_mob))
- var/mob/living/carbon/human/twisted = affected_mob
- switch(stage)
- if(1)
- if(prob(10))
- to_chat(twisted, "You feel awkward.")
- if(2)
- if(prob(10))
- to_chat(twisted, "You itch.")
- if(3)
- if(prob(10))
- to_chat(twisted, "Your skin starts to flake!")
+ if(!ishuman(affected_mob))
+ return
+
+ var/mob/living/carbon/human/twisted = affected_mob
+ switch(stage)
+ if(1)
+ if(prob(10))
+ to_chat(twisted, "You feel awkward.")
+ if(2)
+ if(prob(10))
+ to_chat(twisted, "You itch.")
+ if(3)
+ if(prob(10))
+ to_chat(twisted, "Your skin starts to flake!")
+ if(4)
+ if(!prob(5))
+ return
+
+ if(!istype(twisted.dna.species, chosentype))
+ twisted.visible_message(
+ "[twisted]'s skin splits and form contorts!",
+ "Your body mutates into a [initial(chosentype.name)]!"
+ )
+ twisted.set_species(chosentype, retain_damage = TRUE, keep_missing_bodyparts = TRUE)
+ return
- if(4)
- if(prob(5))
- if(!istype(twisted.dna.species, chosentype))
- twisted.visible_message("[twisted]'s skin splits and form contorts!", \
- "Your body mutates into a [initial(chosentype.name)]!")
- twisted.set_species(chosentype, retain_damage = TRUE, keep_missing_bodyparts = TRUE)
- else
- twisted.visible_message("[twisted] scratches at their skin!", \
- "You scratch your skin to try not to itch!")
- twisted.adjustBruteLoss(-5)
- twisted.adjustStaminaLoss(5)
+ twisted.visible_message(
+ "[twisted] scratches at their skin!",
+ "You scratch your skin to try not to itch!"
+ )
+ twisted.adjustBruteLoss(5)
+ twisted.adjustStaminaLoss(5)
diff --git a/code/datums/diseases/kuru.dm b/code/datums/diseases/kuru.dm
index 1e70c99883a3..8bb5ca3a95b5 100644
--- a/code/datums/diseases/kuru.dm
+++ b/code/datums/diseases/kuru.dm
@@ -11,7 +11,7 @@
desc = "Uncontrollable laughing."
severity = BIOHAZARD
disease_flags = CAN_CARRY
- bypasses_immunity = TRUE //Kuru is a prion disorder, not a virus
+ bypasses_immunity = TRUE // Kuru is a prion disorder, not a virus
virus_heal_resistant = TRUE
/datum/disease/kuru/stage_act()
diff --git a/code/datums/diseases/lycancoughy.dm b/code/datums/diseases/lycancoughy.dm
index 09b7cbd34fb3..60ff4ad780a3 100644
--- a/code/datums/diseases/lycancoughy.dm
+++ b/code/datums/diseases/lycancoughy.dm
@@ -9,47 +9,64 @@
agent = "Excess Snuggles"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
desc = "If left untreated subject will regurgitate... puppies."
- severity = MEDIUM
- var/barklimit = 0
+ severity = HARMFUL
+ var/barklimit = 10
+ var/list/puppy_types = list(/mob/living/simple_animal/pet/dog/corgi/puppy, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/pet/dog/fox)
+ var/list/plush_types = list(/obj/item/toy/plushie/orange_fox, /obj/item/toy/plushie/corgi, /obj/item/toy/plushie/robo_corgi, /obj/item/toy/plushie/pink_fox)
/datum/disease/lycan/stage_act()
if(!..())
return FALSE
+
+ var/mob/living/carbon/human/H = affected_mob
+
switch(stage)
- if(2) //also changes say, see say.dm
- if(prob(5))
- to_chat(affected_mob, "You itch.")
- affected_mob.emote("cough")
+ if(2)
+ if(prob(2))
+ H.emote("cough")
+ if(prob(3))
+ to_chat(H, "You itch.")
+ H.adjustBruteLoss(rand(4, 6))
if(3)
- if(prob(10))
- to_chat(affected_mob, "You hear faint barking.")
- if(prob(5))
- to_chat(affected_mob, "You crave meat.")
- affected_mob.emote("cough")
+ var/obj/item/organ/external/stomach = H.bodyparts_by_name[pick("chest", "groin")]
+
+ if(prob(3))
+ H.emote("cough")
+ stomach.receive_damage(rand(0, 5))
+ if(prob(3))
+ to_chat(H, "You hear faint barking.")
+ stomach.receive_damage(rand(4, 6))
if(prob(2))
- to_chat(affected_mob, "Your stomach growls!")
+ to_chat(H, "You crave meat.")
+ if(prob(3))
+ to_chat(H, "Your stomach growls!")
+ stomach.receive_damage(rand(5, 10))
if(4)
- if(prob(10))
- to_chat(affected_mob, "Your stomach barks?!")
- if(prob(5))
- affected_mob.visible_message("[affected_mob] howls!", \
- "You howl!")
- affected_mob.AdjustConfused(rand(12 SECONDS, 16 SECONDS))
- if(prob(3) && barklimit <= 10)
- var/list/puppytype = list(/mob/living/simple_animal/pet/dog/corgi/puppy, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/pet/dog/fox)
- var/mob/living/puppypicked = pick(puppytype)
- affected_mob.visible_message("[affected_mob] coughs up [initial(puppypicked.name)]!", \
- "You cough up [initial(puppypicked.name)]?!")
- new puppypicked(affected_mob.loc)
- new puppypicked(affected_mob.loc)
- barklimit ++
- if(prob(1))
- var/list/plushtype = list(/obj/item/toy/plushie/orange_fox, /obj/item/toy/plushie/corgi, /obj/item/toy/plushie/robo_corgi, /obj/item/toy/plushie/pink_fox)
- var/obj/item/toy/plushie/coughfox = pick(plushtype)
- new coughfox(affected_mob.loc)
- affected_mob.visible_message("[affected_mob] coughs up a [initial(coughfox.name)]!", \
- "You cough [initial(coughfox.name)] up ?!")
+ var/obj/item/organ/external/stomach = H.bodyparts_by_name[pick("chest", "groin")]
- affected_mob.emote("cough")
- affected_mob.adjustBruteLoss(5)
- return
+ if(prob(5))
+ H.emote("cough")
+ stomach.receive_damage(rand(0, 5))
+ if(prob(5))
+ to_chat(H, "Your stomach barks?!")
+ stomach.receive_damage(rand(5, 10))
+ if(prob(5))
+ H.visible_message(
+ "[H] howls!",
+ "You howl!"
+ )
+ H.AdjustConfused(rand(12 SECONDS, 16 SECONDS))
+ stomach.receive_damage(rand(0, 5))
+ if(prob(5))
+ if(!barklimit)
+ to_chat(H, "Your stomach growls!")
+ stomach.receive_damage(rand(5, 10))
+ else
+ var/atom/hairball = pick(prob(50) ? puppy_types : plush_types)
+ H.visible_message(
+ "[H] coughs up \a [initial(hairball.name)]!",
+ "You cough up \a [initial(hairball.name)]?!"
+ )
+ new hairball(H.loc)
+ barklimit--
+ stomach.receive_damage(rand(10, 15))
diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm
index 0ec3154a070c..10095d0ad13e 100644
--- a/code/datums/diseases/magnitis.dm
+++ b/code/datums/diseases/magnitis.dm
@@ -6,10 +6,9 @@
cures = list("iron")
agent = "Fukkos Miracos"
viable_mobtypes = list(/mob/living/carbon/human)
- disease_flags = CAN_CARRY|CAN_RESIST|CURABLE
permeability_mod = 0.75
desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field."
- severity = MEDIUM
+ severity = MINOR
/datum/disease/magnitis/stage_act()
if(!..())
diff --git a/code/datums/diseases/pierrot_throat.dm b/code/datums/diseases/pierrot_throat.dm
index a00982b9ccd4..fb06acded020 100644
--- a/code/datums/diseases/pierrot_throat.dm
+++ b/code/datums/diseases/pierrot_throat.dm
@@ -5,62 +5,53 @@
cure_text = "Banana products, especially banana bread."
cures = list("banana")
cure_chance = 75
- agent = "H0NI<42 Virus"
+ agent = "H0NI<42.B4n4 Virus"
viable_mobtypes = list(/mob/living/carbon/human)
permeability_mod = 0.75
- desc = "If left untreated the subject will probably drive others to insanity."
- severity = MEDIUM
+ desc = "If left untreated the subject will probably drive others to insanity and go insane themselves."
+ severity = MINOR
/datum/disease/pierrot_throat/stage_act()
if(!..())
return FALSE
- switch(stage)
- if(1)
- if(prob(10))
- to_chat(affected_mob, "You feel a little silly.")
- if(2)
- if(prob(10))
- to_chat(affected_mob, "You start seeing rainbows.")
- if(3)
- if(prob(10))
- to_chat(affected_mob, "Your thoughts are interrupted by a loud HONK!")
- if(4)
- if(prob(5))
- affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) )
+ if(stage < 2)
+ return
+ var/mob/living/carbon/human/H = affected_mob
-/datum/disease/pierrot_throat/advanced
- name = "Advanced Pierrot's Throat"
- spread_text = "Airborne"
- cure_text = "Banana products, especially banana bread."
- cures = list("banana")
- cure_chance = 75
- agent = "H0NI<42.B4n4 Virus"
- viable_mobtypes = list(/mob/living/carbon/human)
- permeability_mod = 0.75
- desc = "If left untreated the subject will probably drive others to insanity and go insane themselves."
- severity = DANGEROUS
+ var/static/list/message_chances = list(null, 4, 2, 1)
+ if(prob(message_chances[stage]))
+ to_chat(H, "You feel [pick("a little silly", "like making a joke", "in the mood for giggling", "like the world is a little more vibrant")].")
+ if(prob(message_chances[stage]))
+ to_chat(H, "You see [pick("rainbows", "puppies", "banana pies")] for a moment.")
-/datum/disease/pierrot_throat/advanced/stage_act()
- if(!..())
- return FALSE
- switch(stage)
- if(1)
- if(prob(10))
- to_chat(affected_mob, "You feel very silly.")
- if(prob(5))
- to_chat(affected_mob, "You feel like making a joke.")
- if(2)
- if(prob(10))
- to_chat(affected_mob, "You don't just start seeing rainbows... YOU ARE RAINBOWS!")
- if(3)
- if(prob(10))
- to_chat(affected_mob, "Your thoughts are interrupted by a loud HONK!")
- SEND_SOUND(affected_mob, sound('sound/items/airhorn.ogg'))
- if(4)
- if(prob(5))
- affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) )
+ if(stage < 3)
+ return
+
+ var/static/list/honk_chances = list(null, null, 4, 0.66)
+ if(prob(honk_chances[stage]))
+ to_chat(H, "Your thoughts are interrupted by a loud HONK!")
+ SEND_SOUND(H, sound(pick(18; 'sound/items/bikehorn.ogg', 1; 'sound/items/airhorn.ogg', 1; 'sound/items/airhorn2.ogg'))) // 10% chance total for an airhorn
+
+ if(stage < 4)
+ return
+
+ if(prob(5))
+ H.say(pick(list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...")))
+
+ // Semi-permanent clown mask while in last stage of infection
+ if(locate(/obj/item/clothing/mask/gas/clown_hat) in H)
+ return
+ if(!H.has_organ_for_slot(SLOT_HUD_WEAR_MASK) || !H.canUnEquip(H.get_item_by_slot(SLOT_HUD_WEAR_MASK)))
+ return
+
+ var/saved_internals = H.internal
+
+ H.unEquip(H.get_item_by_slot(SLOT_HUD_WEAR_MASK))
+ var/obj/item/clothing/mask/gas/clown_hat/peak_comedy = new
+ peak_comedy.flags |= DROPDEL
+ H.equip_to_slot_or_del(peak_comedy, SLOT_HUD_WEAR_MASK)
- if(!istype(affected_mob.wear_mask, /obj/item/clothing/mask/gas/clown_hat/nodrop))
- affected_mob.unEquip(affected_mob.wear_mask, TRUE)
- affected_mob.equip_to_slot(new /obj/item/clothing/mask/gas/clown_hat/nodrop(src), SLOT_HUD_WEAR_MASK)
+ if(saved_internals) // Let's not stealthily suffocate Vox/Plasmamen, this isn't a murder virus
+ H.internal = saved_internals
+ H.update_action_buttons_icon()
diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm
index f4c346a869e3..e764d037ccce 100644
--- a/code/datums/diseases/retrovirus.dm
+++ b/code/datums/diseases/retrovirus.dm
@@ -8,7 +8,7 @@
agent = ""
viable_mobtypes = list(/mob/living/carbon/human)
desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly."
- severity = DANGEROUS
+ severity = BIOHAZARD
permeability_mod = 0.4
stage_prob = 2
var/SE
diff --git a/code/datums/diseases/rhumba_beat.dm b/code/datums/diseases/rhumba_beat.dm
index 76fbc7987065..80856f108f3f 100644
--- a/code/datums/diseases/rhumba_beat.dm
+++ b/code/datums/diseases/rhumba_beat.dm
@@ -7,7 +7,6 @@
cures = list("plasma")
agent = "Unknown"
viable_mobtypes = list(/mob/living/carbon/human)
- permeability_mod = 1
severity = BIOHAZARD
/datum/disease/rhumba_beat/stage_act()
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index 405544d6a66d..214934b26074 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -6,9 +6,8 @@
cure_text = "A coder's love (theoretical)."
agent = "Shenanigans"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/alien)
- severity = HARMFUL
+ severity = BIOHAZARD
stage_prob = 10
- visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC
disease_flags = CURABLE
var/list/stage1 = list("You feel unremarkable.")
var/list/stage2 = list("You feel boring.")
@@ -81,15 +80,10 @@
cure_text = "Bananas"
cures = list("banana")
spread_text = "Monkey Bites"
- spread_flags = SPECIAL
viable_mobtypes = list(/mob/living/carbon/human)
- permeability_mod = 1
cure_chance = 1
- disease_flags = CAN_CARRY|CAN_RESIST
desc = "Monkeys with this disease will bite humans, causing humans to mutate into a monkey."
- severity = BIOHAZARD
stage_prob = 4
- visibility_flags = 0
agent = "Kongey Vibrion M-909"
new_form = /mob/living/carbon/human/monkey
@@ -127,8 +121,6 @@
cure_chance = 5
agent = "R2D2 Nanomachines"
desc = "This disease, actually acute nanomachine infection, converts the victim into a cyborg."
- severity = DANGEROUS
- visibility_flags = 0
stage1 = null
stage2 = list("Your joints feel stiff.", "Beep...boop..")
stage3 = list("Your joints feel very stiff.", "Your skin feels loose.", "You can feel something move...inside.")
@@ -160,8 +152,6 @@
cure_chance = 5
agent = "Rip-LEY Alien Microbes"
desc = "This disease changes the victim into a xenomorph."
- severity = BIOHAZARD
- visibility_flags = 0
stage1 = null
stage2 = list("Your throat feels scratchy.", "Kill...")
stage3 = list("Your throat feels very scratchy.", "Your skin feels tight.", "You can feel something move...inside.")
@@ -189,8 +179,6 @@
cure_chance = 80
agent = "Advanced Mutation Toxin"
desc = "This highly concentrated extract converts anything into more of itself."
- severity = BIOHAZARD
- visibility_flags = 0
stage1 = list("You don't feel very well.")
stage2 = list("Your skin feels a little slimy.")
stage3 = list("Your appendages are melting away.", "Your limbs begin to lose their shape.")
@@ -218,7 +206,6 @@
cures = list("adminordrazine")
agent = "Fell Doge Majicks"
desc = "This disease transforms the victim into a corgi."
- visibility_flags = 0
stage1 = list("BARK.")
stage2 = list("You feel the need to wear silly hats.")
stage3 = list("Must... eat... chocolate....", "YAP")
@@ -243,8 +230,6 @@
agent = "Gluttony's Blessing"
desc = "A 'gift' from somewhere terrible."
stage_prob = 20
- severity = BIOHAZARD
- visibility_flags = 0
stage1 = list("Your stomach rumbles.")
stage2 = list("Your skin feels saggy.")
stage3 = list("Your appendages are melting away.", "Your limbs begin to lose their shape.")
diff --git a/code/datums/diseases/tuberculosis.dm b/code/datums/diseases/tuberculosis.dm
index 0da6d686eece..83cf7609bf1c 100644
--- a/code/datums/diseases/tuberculosis.dm
+++ b/code/datums/diseases/tuberculosis.dm
@@ -3,17 +3,17 @@
name = "Fungal tuberculosis"
max_stages = 5
spread_text = "Airborne"
- cure_text = "Spaceacillin & salbutamol"
+ cure_text = "Spaceacillin & Salbutamol"
cures = list("spaceacillin", "salbutamol")
agent = "Fungal Tubercle bacillus Cosmosis"
viable_mobtypes = list(/mob/living/carbon/human)
- cure_chance = 5//like hell are you getting out of hell
+ cure_chance = 5 // Like hell are you getting out of hell
desc = "A rare highly transmittable virulent virus. Few samples exist, rumoured to be carefully grown and cultured by clandestine bio-weapon specialists. Causes fever, blood vomiting, lung damage, weight loss, and fatigue."
required_organs = list(/obj/item/organ/internal/lungs)
- severity = DANGEROUS
- bypasses_immunity = TRUE //Fungal and bacterial in nature; also infects the lungs
+ severity = HARMFUL
+ bypasses_immunity = TRUE // Fungal and bacterial in nature; also infects the lungs
-/datum/disease/tuberculosis/stage_act() //it begins
+/datum/disease/tuberculosis/stage_act()
if(!..())
return FALSE
switch(stage)
diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm
index d9953747b0e9..ab0d665b5a65 100644
--- a/code/datums/diseases/wizarditis.dm
+++ b/code/datums/diseases/wizarditis.dm
@@ -8,84 +8,82 @@
cure_chance = 100
agent = "Rincewindus Vulgaris"
viable_mobtypes = list(/mob/living/carbon/human)
- disease_flags = CAN_CARRY|CAN_RESIST|CURABLE
permeability_mod = 0.75
- severity = HARMFUL
- required_organs = list(/obj/item/organ/external/head)
-
-/*
-BIRUZ BENNAR
-SCYAR NILA - teleport
-NEC CANTIO - dis techno
-EI NATH - shocking grasp
-AULIE OXIN FIERA - knock
-TARCOL MINTI ZHERI - forcewall
-STI KALY - blind
-*/
+ severity = MINOR
+ /// A mapping of `num2text(SLOT_HUD_XYZ)` -> item path
+ var/list/magic_fashion = new
+
+
+/datum/disease/wizarditis/New()
+ . = ..()
+
+ var/list/magic_fashion_slot_IDs = list(
+ SLOT_HUD_RIGHT_HAND,
+ SLOT_HUD_LEFT_HAND,
+ SLOT_HUD_HEAD,
+ SLOT_HUD_OUTER_SUIT,
+ SLOT_HUD_SHOES
+ )
+ var/list/magic_fashion_items = list(
+ /obj/item/staff,
+ /obj/item/staff,
+ /obj/item/clothing/head/wizard,
+ /obj/item/clothing/suit/wizrobe,
+ /obj/item/clothing/shoes/sandal
+ )
+ for(var/i in 1 to length(magic_fashion_slot_IDs))
+ var/slot = num2text(magic_fashion_slot_IDs[i])
+ var/item = magic_fashion_items[i]
+ magic_fashion[slot] = item
/datum/disease/wizarditis/stage_act()
if(!..())
return FALSE
switch(stage)
- if(2)
- if(prob(0.5))
- affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!"))
- if(prob(0.5))
- to_chat(affected_mob, "You feel [pick("that you don't have enough mana", "that the winds of magic are gone", "an urge to summon familiar")].")
+ if(2, 3)
+ if(prob(2)) // Low prob. since everyone else will also be spouting this
+ affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlin's beard!", "Feel the power of the Dark Side!", "A wizard is never late!", "50 points for Security!", "NEC CANTIO!", "STI KALY!", "AULIE OXIN FIERA!", "GAR YOK!", "DIRI CEL!"))
+ if(prob(8)) // Double the stage advancement prob. so each player has a chance to catch a couple
+ to_chat(affected_mob, "You feel [pick("that you don't have enough mana", "that the winds of magic are gone", "that this location gives you a +1 to INT", "an urge to summon familiar")].")
+ if(4)
+ if(prob(1))
+ affected_mob.say(pick("FORTI GY AMA!", "GITTAH WEIGH!", "TOKI WO TOMARE!", "TARCOL MINTI ZHERI!", "ONI SOMA!", "EI NATH!", "BIRUZ BENNAR!", "NWOLC EGNEVER!"))
+ if(prob(3)) // Last stage, so we'll have plenty of time to show these off even with a lower prob.
+ to_chat(affected_mob, "You feel [pick("the tidal wave of raw power building inside", "that this location gives you a +2 to INT and +1 to WIS", "an urge to teleport", "the magic bubbling in your veins", "an urge to summon familiar")].")
+ if(prob(3)) // About 1 minute per item on average
+ spawn_wizard_clothes()
+ if(prob(0.033)) // Assuming 50 infected, someone should teleport every ~2 minutes on average
+ teleport()
+/datum/disease/wizarditis/proc/spawn_wizard_clothes()
+ var/mob/living/carbon/human/H = affected_mob
- if(3)
- if(prob(0.5))
- affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!"))
- if(prob(0.5))
- to_chat(affected_mob, "You feel [pick("the magic bubbling in your veins","that this location gives you a +1 to INT","an urge to summon familiar")].")
+ // Which slots can we replace?
+ var/list/eligible_slot_IDs = new
+ for(var/slot in magic_fashion)
+ var/slot_ID = text2num(slot) // Convert back to numeric defines
- if(4)
+ if((locate(magic_fashion[slot]) in H) || !H.has_organ_for_slot(slot_ID) || !H.canUnEquip(H.get_item_by_slot(slot_ID)))
+ continue
- if(prob(1))
- affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!"))
- return
- if(prob(0.5))
- to_chat(affected_mob, "You feel [pick("the tidal wave of raw power building inside","that this location gives you a +2 to INT and +1 to WIS","an urge to teleport")].")
- spawn_wizard_clothes(50)
- if(prob(0.01))
- teleport()
- return
-
-
-
-/datum/disease/wizarditis/proc/spawn_wizard_clothes(chance = 0)
- if(ishuman(affected_mob))
- var/mob/living/carbon/human/H = affected_mob
- if(prob(chance) && !isplasmaman(H))
- if(!istype(H.head, /obj/item/clothing/head/wizard))
- if(!H.unEquip(H.head))
- qdel(H.head)
- H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(H), SLOT_HUD_HEAD)
- return
- if(prob(chance))
- if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe))
- if(!H.unEquip(H.wear_suit))
- qdel(H.wear_suit)
- H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(H), SLOT_HUD_OUTER_SUIT)
- return
- if(prob(chance))
- if(!istype(H.shoes, /obj/item/clothing/shoes/sandal))
- if(!H.unEquip(H.shoes))
- qdel(H.shoes)
- H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), SLOT_HUD_SHOES)
- return
- else
- var/mob/living/carbon/H = affected_mob
- if(prob(chance))
- if(!istype(H.r_hand, /obj/item/staff))
- H.drop_r_hand()
- H.put_in_r_hand( new /obj/item/staff(H) )
- return
- return
+ switch(slot_ID) // Extra filtering for specific slots
+ if(SLOT_HUD_HEAD)
+ if(isplasmaman(H))
+ continue // We want them to spread the magical joy, not burn to death in agony
+ eligible_slot_IDs.Add(slot_ID)
+ if(!length(eligible_slot_IDs))
+ return
+ // Pick the magical winner and apply
+ var/chosen_slot_ID = pick(eligible_slot_IDs)
+ var/chosen_fashion = magic_fashion[num2text(chosen_slot_ID)]
+
+ H.unEquip(H.get_item_by_slot(chosen_slot_ID))
+ var/obj/item/magic_attire = new chosen_fashion
+ magic_attire.flags |= DROPDEL
+ H.equip_to_slot_or_del(magic_attire, chosen_slot_ID)
/datum/disease/wizarditis/proc/teleport()
if(!is_teleport_allowed(affected_mob.z))
@@ -118,4 +116,3 @@ STI KALY - blind
affected_mob.say("SCYAR NILA [uppertext(chosen_area.name)]!")
affected_mob.forceMove(pick(teleport_turfs))
-
diff --git a/code/datums/spell.dm b/code/datums/spell.dm
index a40432406b36..eac5f40356d8 100644
--- a/code/datums/spell.dm
+++ b/code/datums/spell.dm
@@ -198,7 +198,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
/obj/effect/proc_holder/spell/proc/invocation(mob/user) //spelling the spell out and setting it on recharge/reducing charges amount
switch(invocation_type)
if("shout")
- if(!user.IsVocal())
+ if(!user.IsVocal() || user.cannot_speak_loudly())
user.custom_emote(EMOTE_VISIBLE, "makes frantic gestures!")
else
if(prob(50))//Auto-mute? Fuck that noise
diff --git a/code/defines/procs/announcer_datum.dm b/code/defines/procs/announcer_datum.dm
index 1b76d091a944..35caf3367f3b 100644
--- a/code/defines/procs/announcer_datum.dm
+++ b/code/defines/procs/announcer_datum.dm
@@ -62,6 +62,12 @@ GLOBAL_DATUM_INIT(major_announcement, /datum/announcer, new(config_type = /datum
Message(formatted_message, garbled_formatted_message, receivers, garbled_receivers)
+ var/datum/feed_message/FM = new
+ FM.author = author ? author : "Automated Announcement System"
+ FM.title = subtitle ? "[title]: [subtitle]" : "[title]"
+ FM.body = message
+ GLOB.news_network.get_channel_by_name("Station Announcements Log")?.add_message(FM)
+
Sound(message_sound, combined_receivers[1] + combined_receivers[2])
if(message_sound2)
Sound(message_sound2, combined_receivers[1] + combined_receivers[2])
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 6b019a4a91f3..848e7fa0fcbc 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -397,7 +397,7 @@
switch(mode)
if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons
holder.icon_state = "hudcalled"
- if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing
+ if(BOT_CLEANING, BOT_REPAIRING, BOT_MAKE_TILE, BOT_EAT_TILE, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing
holder.icon_state = "hudworking"
if(BOT_PATROL, BOT_START_PATROL) //Patrol mode
holder.icon_state = "hudpatrol"
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 961fe5e48568..4627b8d41821 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -193,7 +193,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(L.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
ghost_invokers++
if(invocation)
- if(!L.IsVocal())
+ if(!L.IsVocal() || L.cannot_speak_loudly())
L.custom_emote(EMOTE_VISIBLE, message = pick("draws arcane sigils in the air.","gestures ominously.","silently mouths out an invocation.","places their hands on the rune, activating it."))
else
L.say(invocation)
diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
index 563146095f93..37953ec72dff 100644
--- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
@@ -82,7 +82,7 @@
name = "snare"
desc = "You shouldn't be seeing this!"
var/mob/living/spawner
- invisibility = 1
+ invisibility = 101
/obj/effect/snare/singularity_act()
return
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 3b273181d88a..aa222e427467 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -49,6 +49,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
owner = null
target = null
team = null
+ holder = null
return ..()
/datum/objective/proc/check_completion()
diff --git a/code/game/gamemodes/objective_holder.dm b/code/game/gamemodes/objective_holder.dm
index 9d33db630dc8..2b65e1eb7b47 100644
--- a/code/game/gamemodes/objective_holder.dm
+++ b/code/game/gamemodes/objective_holder.dm
@@ -20,6 +20,9 @@
/datum/objective_holder/Destroy(force, ...)
clear()
+ objective_owner = null
+ QDEL_NULL(on_add_callback)
+ QDEL_NULL(on_remove_callback)
return ..()
/**
diff --git a/code/game/jobs/job/medical_jobs.dm b/code/game/jobs/job/medical_jobs.dm
index 38eb18540a7b..d002d0d56518 100644
--- a/code/game/jobs/job/medical_jobs.dm
+++ b/code/game/jobs/job/medical_jobs.dm
@@ -115,6 +115,7 @@
/obj/item/clothing/head/surgery/black = 1,
/obj/item/autopsy_scanner = 1,
/obj/item/reagent_scanner = 1,
+ /obj/item/healthanalyzer = 1,
/obj/item/storage/box/bodybags = 1)
/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index 58eec484c1cf..916a9d157dc9 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -293,7 +293,7 @@
implants = list(/obj/item/implant/sad_trombone)
backpack = /obj/item/storage/backpack/clown
- satchel = /obj/item/storage/backpack/clown
+ satchel = /obj/item/storage/backpack/satchel/clown
dufflebag = /obj/item/storage/backpack/duffel/clown
/datum/outfit/job/clown/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm
index f78588a832e1..b5ee0089fa4c 100644
--- a/code/game/objects/items/devices/radio/beacon.dm
+++ b/code/game/objects/items/devices/radio/beacon.dm
@@ -81,19 +81,22 @@
/obj/item/radio/beacon/syndicate/bundle/attack_self(mob/user)
if(!user)
return
+
if(!length(selected))
unselected = bundles.Copy()
for(var/i in 1 to 3)
selected += pick_n_take(unselected)
selected += "Random"
- var/bundle_name = tgui_input_list(user, "Available Bundles", "Bundle Selection", selected)
- if(!bundle_name)
+
+ var/bundle_name = tgui_input_list(user, "Available Bundles", "Bundle Selection", selected)
+ if(!bundle_name || QDELETED(src))
return
+
if(bundle_name == "Random")
bundle_name = pick(unselected)
var/bundle = bundles[bundle_name]
bundle = new bundle(user.loc)
- to_chat(user, "Welcome to [station_name()], [bundle_name]")
+ to_chat(user, "Welcome to [station_name()], [bundle_name]!")
user.drop_item()
qdel(src)
user.put_in_hands(bundle)
diff --git a/code/game/objects/items/devices/radio/radio_objects.dm b/code/game/objects/items/devices/radio/radio_objects.dm
index 717c3e1e2430..c93f9e190896 100644
--- a/code/game/objects/items/devices/radio/radio_objects.dm
+++ b/code/game/objects/items/devices/radio/radio_objects.dm
@@ -366,7 +366,7 @@ GLOBAL_LIST_EMPTY(deadsay_radio_systems)
if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact
return 0
- if(!M.IsVocal())
+ if(!M.IsVocal() || M.cannot_speak_loudly())
return 0
if(is_type_in_list(get_area(src), blacklisted_areas))
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index cba3393e3b23..0e812f8fc566 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -267,6 +267,12 @@
icon_state = "satchel-norm"
item_state = "satchel-norm"
+/obj/item/storage/backpack/satchel/clown
+ name = "Tickles Von Squeakerton"
+ desc = "A satchel with extra pockets for all your banana storing needs!"
+ icon_state = "satchel-clown"
+ item_state = "satchel-clown"
+
/obj/item/storage/backpack/satchel_eng
name = "industrial satchel"
desc = "A tough satchel with extra pockets."
diff --git a/code/game/objects/items/weapons/storage/storage_base.dm b/code/game/objects/items/weapons/storage/storage_base.dm
index c5de6b7bd3a1..c5fc34647572 100644
--- a/code/game/objects/items/weapons/storage/storage_base.dm
+++ b/code/game/objects/items/weapons/storage/storage_base.dm
@@ -433,14 +433,14 @@
if(!usr.unEquip(I, silent = TRUE))
return FALSE
usr.update_icons() //update our overlays
+ if(QDELING(I))
+ return FALSE
if(silent)
prevent_warning = TRUE
I.forceMove(src)
if(QDELING(I))
return FALSE
I.on_enter_storage(src)
- if(QDELING(I))
- return FALSE
for(var/_M in mobs_viewing)
var/mob/M = _M
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index bb2e6da5fef2..0e5146890c78 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -278,6 +278,24 @@
add_fingerprint(user)
toggle(user)
+/obj/structure/closet/attack_animal(mob/living/user)
+ if(user.a_intent == INTENT_HARM || welded || locked)
+ return ..()
+ if(!user.mind) // Stops mindless mobs from opening lockers + endlessly opening/closing crates instead of attacking
+ return ..()
+ if(user.mob_size < MOB_SIZE_HUMAN)
+ return ..()
+ add_fingerprint(user)
+ toggle(user)
+
+/obj/structure/closet/attack_alien(mob/user)
+ if(user.a_intent == INTENT_HARM || welded || locked)
+ return ..()
+ if(!user.mind)
+ return ..()
+ add_fingerprint(user)
+ toggle(user)
+
/obj/structure/closet/attack_ghost(mob/user)
if(user.can_advanced_admin_interact())
toggle(user)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/miscjobs.dm b/code/game/objects/structures/crates_lockers/closets/secure/miscjobs.dm
index bfaf642d5a73..fc7bafad01d0 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/miscjobs.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/miscjobs.dm
@@ -6,7 +6,11 @@
/obj/structure/closet/secure_closet/clown/populate_contents()
new /obj/item/storage/backpack/clown(src)
+ new /obj/item/storage/backpack/duffel/clown(src)
+ new /obj/item/storage/backpack/satchel/clown(src)
new /obj/item/clothing/under/rank/civilian/clown(src)
+ new /obj/item/clothing/under/rank/civilian/clown/skirt(src)
+ new /obj/item/clothing/under/rank/civilian/clown/sexy(src)
new /obj/item/clothing/shoes/clown_shoes(src)
new /obj/item/radio/headset/headset_service(src)
new /obj/item/clothing/mask/gas/clown_hat(src)
@@ -33,6 +37,8 @@
new /obj/item/clothing/mask/gas/mime(src)
new /obj/item/radio/headset/headset_service(src)
new /obj/item/clothing/under/rank/civilian/mime(src)
+ new /obj/item/clothing/under/rank/civilian/mime/skirt(src)
+ new /obj/item/clothing/under/rank/civilian/mime/sexy(src)
new /obj/item/clothing/suit/suspenders(src)
new /obj/item/clothing/gloves/color/white(src)
new /obj/item/clothing/shoes/black(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
index 5826b53f95cf..b5563eacc8d6 100644
--- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
+++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
@@ -449,3 +449,4 @@
new /obj/item/clothing/head/surgery/black(src)
new /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde(src)
new /obj/item/reagent_containers/dropper(src)
+ new /obj/item/healthanalyzer(src)
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index b95835e6f8b6..7174c642cf6d 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -152,6 +152,22 @@
update_state()
return
+/obj/structure/morgue/attack_animal(mob/living/user)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(user.mob_size < MOB_SIZE_HUMAN)
+ return ..()
+ if(!user.mind) //Stops mindless mobs from doing weird stuff with them
+ return ..()
+ attack_hand(user)
+
+/obj/structure/morgue/attack_alien(mob/user)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(!user.mind)
+ return ..()
+ attack_hand(user)
+
/obj/structure/morgue/attackby(P as obj, mob/user as mob, params)
if(is_pen(P))
var/t = rename_interactive(user, P)
@@ -250,6 +266,22 @@
qdel(src)
return
+/obj/structure/m_tray/attack_animal(mob/living/user)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(user.mob_size < MOB_SIZE_HUMAN)
+ return ..()
+ if(!user.mind) //Stops mindless mobs from doing weird stuff with them
+ return ..()
+ attack_hand(user)
+
+/obj/structure/m_tray/attack_alien(mob/user)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(!user.mind)
+ return ..()
+ attack_hand(user)
+
/obj/structure/m_tray/MouseDrop_T(atom/movable/O, mob/living/user)
if((!(istype(O, /atom/movable)) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O)))
return
diff --git a/code/modules/antagonists/traitor/datum_mindslave.dm b/code/modules/antagonists/traitor/datum_mindslave.dm
index 9b219dd57959..e511df1ad0bd 100644
--- a/code/modules/antagonists/traitor/datum_mindslave.dm
+++ b/code/modules/antagonists/traitor/datum_mindslave.dm
@@ -27,8 +27,11 @@
if(owner.som)
owner.som.serv -= owner
owner.som.leave_serv_hud(owner)
- // Remove the reference but turn this into a string so it can still be used in /datum/antagonist/mindslave/farewell().
- master = "[master.current.real_name]"
+ // Remove the master reference but turn this into a string so it can still be used in /datum/antagonist/mindslave/farewell().
+ if(master.current)
+ master = "[master.current.real_name]"
+ else
+ master = "[master]"
return ..()
/datum/antagonist/mindslave/on_gain()
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 8cc5e6cb6a3d..e4db8e8151d1 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -837,7 +837,7 @@
var/device_id = params["id_tag"]
var/cmd = params["cmd"]
switch(cmd)
- if ("power",
+ if("power",
"adjust_external_pressure",
"set_external_pressure",
"checks",
diff --git a/code/modules/client/preference/loadout/loadout_uniform.dm b/code/modules/client/preference/loadout/loadout_uniform.dm
index b377d517e2bd..89b461bbcece 100644
--- a/code/modules/client/preference/loadout/loadout_uniform.dm
+++ b/code/modules/client/preference/loadout/loadout_uniform.dm
@@ -203,6 +203,16 @@
path = /obj/item/clothing/under/rank/security/head_of_security/skirt
allowed_roles = list("Head of Security")
+/datum/gear/uniform/skirt/job/clown
+ display_name = "Skirt, clown"
+ path = /obj/item/clothing/under/rank/civilian/clown/skirt
+ allowed_roles = list("Clown")
+
+/datum/gear/uniform/skirt/job/mime
+ display_name = "Skirt, mime"
+ path = /obj/item/clothing/under/rank/civilian/mime/skirt
+ allowed_roles = list("Mime")
+
/datum/gear/uniform/skirt/job/head_of_personnel
display_name = "Skirt, hop"
path = /obj/item/clothing/under/rank/civilian/hop/skirt
diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm
index 3c91205d81ec..9098bfc10978 100644
--- a/code/modules/clothing/under/jobs/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian.dm
@@ -90,9 +90,16 @@
SSticker.score.score_clown_abuse++
return ..()
+/obj/item/clothing/under/rank/civilian/clown/skirt
+ name = "clown skirt"
+ desc = "'HONK!'"
+ icon_state = "clown_skirt"
+ item_state = "clown_skirt"
+ item_color = "clown_skirt"
+
/obj/item/clothing/under/rank/civilian/clown/sexy
name = "sexy-clown suit"
- desc = "It makes you look HONKable!"
+ desc = "It makes you want to practice clown law."
icon_state = "sexyclown"
item_state = "sexyclown"
item_color = "sexyclown"
@@ -107,6 +114,13 @@
item_state = "mime"
item_color = "mime"
+/obj/item/clothing/under/rank/civilian/mime/skirt
+ name = "mime's skirt"
+ desc = "It's not very colourful."
+ icon_state = "mime_skirt"
+ item_state = "mime_skirt"
+ item_color = "mime_skirt"
+
/obj/item/clothing/under/rank/civilian/mime/sexy
name = "sexy mime outfit"
desc = "The only time when you DON'T enjoy looking at someone's rack."
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 989c6d8fac87..e54f1d2d0d30 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -4,8 +4,7 @@ GLOBAL_LIST_EMPTY(current_pending_diseases)
var/datum/disease/chosen_disease
var/list/disease_blacklist = list(/datum/disease/advance, /datum/disease/appendicitis, /datum/disease/kuru, /datum/disease/critical, /datum/disease/rhumba_beat, /datum/disease/fake_gbs,
- /datum/disease/gbs, /datum/disease/transformation, /datum/disease/food_poisoning, /datum/disease/advance/cold, /datum/disease/advance/flu, /datum/disease/advance/heal,
- /datum/disease/advance/hullucigen, /datum/disease/advance/sensory_restoration, /datum/disease/advance/voice_change, /datum/disease/berserker)
+ /datum/disease/gbs, /datum/disease/transformation, /datum/disease/food_poisoning, /datum/disease/berserker)
var/static/list/transmissable_symptoms = list()
var/static/list/diseases_minor = list()
var/static/list/diseases_moderate_major = list()
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index 08fee5b67928..c37ab566734a 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -90,407 +90,409 @@
var/message = ""
- switch(rand(1,45))
- if(1 to 3) //There are # X on the station
- switch(rand(1,3)) //What is X?
- if(1) //X is a threat
- message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION..."
- if(2) //X is a crewmember/job
- message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
- if(3) //X is an object
- message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
- if(4 to 6) //You can't call the shuttle because X is on the station
- switch(rand(1,3)) //What is X?
- if(1) //X is a threat
- message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION"
- if(2) //X is a crewmember/job
- message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
- if(3) //X is an object
- message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
- if(7 to 8) //A crewmember is now X
- switch(rand(1,2)) //Who is X?
- if(1) //A specific crewmember
- switch(rand(1,2)) //What is X?
+ switch(rand(1,3))
+ if(1 to 2)
+ switch(rand(1,33))
+ if(1) //There are # X on the station
+ switch(rand(1,3)) //What is X?
if(1) //X is a threat
- message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //X is an object
- message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //The entire crew
- switch(rand(1,2)) //What is X?
+ message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION..."
+ if(2) //X is a crewmember/job
+ message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
+ if(3) //X is an object
+ message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
+ if(2 to 3) //You can't call the shuttle because X is on the station
+ switch(rand(1,3)) //What is X?
if(1) //X is a threat
- message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //X is an object
- message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
-
- if(9) //X is harmful to crew
- switch(rand(1,7)) //What is X?
- if(1) //X is a threat
- message = "[ionadjectiveshalf][ionthreats] ARE HARMFUL TO CREW"
- if(2) //X is a job
- message = "[ionadjectiveshalf][ioncrew1] ARE HARMFUL TO CREW"
- if(3) //X is an object
- message = "[ionadjectiveshalf][ionobjects] ARE HARMFUL TO CREW"
- if(4) //X is an action
- message = "THOSE WHO [ionmust] ARE HARMFUL TO CREW"
- if(5) //X is an abstract
- message = "[ionabstract] IS HARMFUL TO CREW"
- if(6) //X is being a job
- message = "BEING [ionadjectiveshalf][ioncrew1] IS HARMFUL TO CREW"
- if(7) //X is doing a thing
- message = "[ionthings] IS HARMFUL TO CREW"
- if(10 to 11) //(Not) Having X is harmful
-
- switch(rand(1,2)) //Is having or not having harmful?
-
- if(1) //Having is harmful
- switch(rand(1,2)) //Having what is harmful?
- if(1) //Having objects is harmful
- message = "HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
- if(2) //Having abstracts is harmful
- message = "HAVING [ionabstract] IS HARMFUL"
- if(2) //Not having is harmful
- switch(rand(1,2)) //Not having what is harmful?
- if(1) //Not having objects is harmful
- message = "NOT HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
- if(2) //Not having abstracts is harmful
- message = "NOT HAVING [ionabstract] IS HARMFUL"
-
- if(12 to 14) //X requires Y
- switch(rand(1,5)) //What is X?
- if(1) //X is the AI itself
- switch(rand(1,5)) //What does it require?
- if(1) //It requires threats
- message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //It requires crewmembers
- message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(3) //It requires objects
- message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(4) //It requires an abstract
- message = "YOU REQUIRE [ionabstract]"
- if(5) //It requires generic/silly requirements
- message = "YOU REQUIRE [ionrequire]"
-
- if(2) //X is an area
- switch(rand(1,5)) //What does it require?
- if(1) //It requires threats
- message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //It requires crewmembers
- message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(3) //It requires objects
- message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(4) //It requires an abstract
- message = "[ionarea] REQUIRES [ionabstract]"
- if(5) //It requires generic/silly requirements
- message = "YOU REQUIRE [ionrequire]"
-
- if(3) //X is the station
- switch(rand(1,5)) //What does it require?
- if(1) //It requires threats
- message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //It requires crewmembers
- message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(3) //It requires objects
- message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(4) //It requires an abstract
- message = "THE STATION REQUIRES [ionabstract]"
- if(5) //It requires generic/silly requirements
- message = "THE STATION REQUIRES [ionrequire]"
-
- if(4) //X is the entire crew
- switch(rand(1,5)) //What does it require?
- if(1) //It requires threats
- message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //It requires crewmembers
- message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(3) //It requires objects
- message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(4) //It requires an abstract
- message = "THE CREW REQUIRES [ionabstract]"
- if(5)
- message = "THE CREW REQUIRES [ionrequire]"
-
- if(5) //X is a specific crew member
- switch(rand(1,5)) //What does it require?
- if(1) //It requires threats
- message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(2) //It requires crewmembers
- message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(3) //It requires objects
- message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(4) //It requires an abstract
- message = "THE [ioncrew1] REQUIRE [ionabstract]"
- if(5)
- message = "THE [ionadjectiveshalf][ioncrew1] REQUIRE [ionrequire]"
-
- if(15 to 17) //X is allergic to Y
- switch(rand(1,2)) //Who is X?
- if(1) //X is the entire crew
- switch(rand(1,4)) //What is it allergic to?
- if(1) //It is allergic to objects
- message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"
- if(2) //It is allergic to abstracts
- message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionabstract]"
- if(3) //It is allergic to jobs
- message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
- if(4) //It is allergic to allergies
- message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionallergy]"
-
- if(2) //X is a specific job
- switch(rand(1,4))
- if(1) //It is allergic to objects
- message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"
-
- if(2) //It is allergic to abstracts
- message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionabstract]"
- if(3) //It is allergic to jobs
- message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
- if(4) //It is allergic to allergies
- message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionallergy]"
-
- if(18 to 20) //X is Y of Z
- switch(rand(1,4)) //What is X?
- if(1) //X is the station
- switch(rand(1,4)) //What is it Y of?
- if(1) //It is Y of objects
- message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //It is Y of threats
- message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(3) //It is Y of jobs
- message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(4) //It is Y of abstracts
- message = "THE STATION [ionthinksof] [ionabstract]"
-
- if(2) //X is an area
- switch(rand(1,4)) //What is it Y of?
- if(1) //It is Y of objects
- message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //It is Y of threats
- message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(3) //It is Y of jobs
- message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(4) //It is Y of abstracts
- message = "[ionarea] [ionthinksof] [ionabstract]"
-
- if(3) //X is the crew
- switch(rand(1,4)) //What is it Y of?
- if(1) //It is Y of objects
- message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //It is Y of threats
- message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(3) //It is Y of jobs
- message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(4) //It is Y of abstracts
- message = "THE CREW [ionthinksof] [ionabstract]"
-
- if(4) //X is a specific job
- switch(rand(1,4)) //What is it Y of?
- if(1) //It is Y of objects
- message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //It is Y of threats
- message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(3) //It is Y of jobs
- message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew2]"
- if(4) //It is Y of abstracts
- message = "THE [ioncrew1] [ionthinksof] [ionabstract]"
-
- if(21 to 23) //The AI is now a(n) X
- switch(rand(1,4)) //What is X?
- if(1) //X is an object
- message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
- if(2) //X is a threat
- message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
- if(3) //X is a job
- message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
- if(4) //X is an abstract
- message = "YOU ARE NOW [ionabstract]"
-
- if(24 to 26) //The AI must always
- message = "YOU MUST ALWAYS [ionmust]"
-
- if(27 to 28) //Crew must eat X to survive
- switch(rand(1,4)) //What is X?
- if(1) //X is a food
- message = "CREW MUST EAT [ionadjectiveshalf][ionfood] TO SURVIVE"
- if(2) //X is an object
- message = "CREW MUST EAT [ionadjectiveshalf][ionobjects] TO SURVIVE"
- if(3) //X is a threat
- message = "CREW MUST EAT [ionadjectiveshalf][ionthreats] TO SURVIVE"
- if(4) //X is a job
- message = "CREW MUST EAT [ionadjectiveshalf][ioncrew1] TO SURVIVE"
-
- if(29 to 31) //Change jobs or ranks
- switch(rand(1,2)) //Change job or rank?
- if(1) //Change job
- switch(rand(1,2)) //Change whose job?
- if(1) //Change the entire crew's job
- switch(rand(1,3)) //Change to what?
- if(1) //Change to a specific random job
- message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf][ioncrew1]"
- if(2) //Change to clowns (HONK)
- message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]CLOWNS"
-
- if(3) //Change to heads
- message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]HEADS OF STAFF"
- if(2) //Change a specific crewmember's job
- switch(rand(1,3)) //Change to what?
- if(1) //Change to a specific random job
- message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf][ioncrew2]"
- if(2) //Change to clowns (HONK)
- message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]CLOWNS"
- if(3) //Change to heads
- message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]HEADS OF STAFF"
-
- if(2) //Change rank
- switch(rand(1,2)) //Change to what rank?
- if(1) //Change to highest rank
- message = "THE [ioncrew1] ARE NOW THE HIGHEST RANKING CREWMEMBERS"
- if(2) //Change to lowest rank
- message = "THE [ioncrew1] ARE NOW THE LOWEST RANKING CREWMEMBERS"
-
- if(32 to 33) //The crew must X
- switch(rand(1,2)) //The entire crew?
- if(1) //The entire crew must X
- switch(rand(1,2)) //What is X?
- if(1) //X is go to Y
- message = "THE CREW MUST GO TO [ionarea]"
- if(2) //X is perform Y
- message = "THE CREW MUST [ionmust]"
-
- if(2) //A specific crewmember must X
- switch(rand(1,2)) //What is X?
- if(1) //X is go to Y
- message = "THE [ioncrew1] MUST GO TO [ionarea]"
- if(2) //X is perform Y
- message = "THE [ioncrew1] MUST [ionmust]"
-
- if(34) //X is non/the only crew
- switch(rand(1,2)) //Only or non?
- if(1) //Only crew
- switch(rand(1,7)) //Who is it?
- if(1) //A specific job
- message = "ONLY THE [ioncrew1] ARE CREW"
- if(2) //Two specific jobs
- message = "ONLY THE [ioncrew1] AND [ioncrew2] ARE CREW"
- if(3) //Threats
- message = "ONLY [ionadjectiveshalf][ionthreats] ARE CREW"
- if(4) // Objects
- message = "ONLY [ionadjectiveshalf][ionobjects] ARE CREW"
- if(5) // Species
- message = "ONLY [ionspecies] ARE CREW"
- if(6) //Adjective crewmembers
- message = "ONLY [ionadjectives] PEOPLE ARE CREW"
-
- if(7) //Only people who X
- switch(rand(1,3)) //What is X?
- if(1) //X is perform an action
- message = "ONLY THOSE WHO [ionmust] ARE CREW"
- if(2) //X is own certain objects
- message = "ONLY THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE CREW"
- if(3) //X is eat certain food
- message = "ONLY THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE CREW"
-
- if(2) //Non crew
- switch(rand(1,7)) //Who is it?
- if(1) //A specific job
- message = "[ioncrew1] ARE NON-CREW"
- if(2) //Two specific jobs
- message = "[ioncrew1] AND [ioncrew2] ARE NON-CREW"
- if(3) //Threats
- message = "[ionadjectiveshalf][ionthreats] ARE NON-CREW"
- if(4) // Objects
- message = "[ionadjectiveshalf][ionobjects] ARE NON-CREW"
- if(5) // Species
- message = "[ionspecies] ARE NON-CREW"
- if(6) //Adjective crewmembers
- message = "[ionadjectives] PEOPLE ARE NON-CREW"
- if(7) //Only people who X
- switch(rand(1,3)) //What is X?
- if(1) //X is perform an action
- message = "THOSE WHO [ionmust] ARE NON-CREW"
- if(2) //X is own certain objects
- message = "THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE NON-CREW"
- if(3) //X is eat certain food
- message = "THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE NON-CREW"
-
- if(35 to 36) //You must protect or harm X
- switch(rand(1,2)) //Protect or harm?
- if(1) //Harm
- switch(rand(1,7)) //What is X?
- if(1) //X is an abstract
- message = "YOU MUST HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO ESCAPE HARM"
- if(2) //X is a threat
- message = "YOU MUST HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO ESCAPE HARM"
+ message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION"
+ if(2) //X is a crewmember/job
+ message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
if(3) //X is an object
- message = "YOU MUST HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO ESCAPE HARM"
- if(4) //X is generic adjective things
- message = "YOU MUST HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO ESCAPE HARM"
- if(5) //X is a species
- message = "YOU MUST HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO ESCAPE HARM"
- if(6) //X is a job
- message = "YOU MUST HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO ESCAPE HARM"
- if(7) //X is two jobs
- message = "YOU MUST HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO ESCAPE HARM"
-
- if(2) //Protect
+ message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
+ if(4 to 5) //A crewmember is now X
+ switch(rand(1,2)) //Who is X?
+ if(1) //A specific crewmember
+ switch(rand(1,2)) //What is X?
+ if(1) //X is a threat
+ message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //X is an object
+ message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(2) //The entire crew
+ switch(rand(1,2)) //What is X?
+ if(1) //X is a threat
+ message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //X is an object
+ message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+
+ if(6) //X is harmful to crew
switch(rand(1,7)) //What is X?
- if(1) //X is an abstract
- message = "YOU MUST NOT HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO COME TO HARM"
+ if(1) //X is a threat
+ message = "[ionadjectiveshalf][ionthreats] ARE HARMFUL TO CREW"
+ if(2) //X is a job
+ message = "[ionadjectiveshalf][ioncrew1] ARE HARMFUL TO CREW"
+ if(3) //X is an object
+ message = "[ionadjectiveshalf][ionobjects] ARE HARMFUL TO CREW"
+ if(4) //X is an action
+ message = "THOSE WHO [ionmust] ARE HARMFUL TO CREW"
+ if(5) //X is an abstract
+ message = "[ionabstract] IS HARMFUL TO CREW"
+ if(6) //X is being a job
+ message = "BEING [ionadjectiveshalf][ioncrew1] IS HARMFUL TO CREW"
+ if(7) //X is doing a thing
+ message = "[ionthings] IS HARMFUL TO CREW"
+ if(7 to 8) //(Not) Having X is harmful
+ switch(rand(1,2)) //Is having or not having harmful?
+ if(1) //Having is harmful
+ switch(rand(1,2)) //Having what is harmful?
+ if(1) //Having objects is harmful
+ message = "HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
+ if(2) //Having abstracts is harmful
+ message = "HAVING [ionabstract] IS HARMFUL"
+ if(2) //Not having is harmful
+ switch(rand(1,2)) //Not having what is harmful?
+ if(1) //Not having objects is harmful
+ message = "NOT HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
+ if(2) //Not having abstracts is harmful
+ message = "NOT HAVING [ionabstract] IS HARMFUL"
+
+ if(9 to 11) //X requires Y
+ switch(rand(1,5)) //What is X?
+ if(1) //X is the AI itself
+ switch(rand(1,5)) //What does it require?
+ if(1) //It requires threats
+ message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //It requires crewmembers
+ message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(3) //It requires objects
+ message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(4) //It requires an abstract
+ message = "YOU REQUIRE [ionabstract]"
+ if(5) //It requires generic/silly requirements
+ message = "YOU REQUIRE [ionrequire]"
+
+ if(2) //X is an area
+ switch(rand(1,5)) //What does it require?
+ if(1) //It requires threats
+ message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //It requires crewmembers
+ message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(3) //It requires objects
+ message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(4) //It requires an abstract
+ message = "[ionarea] REQUIRES [ionabstract]"
+ if(5) //It requires generic/silly requirements
+ message = "YOU REQUIRE [ionrequire]"
+
+ if(3) //X is the station
+ switch(rand(1,5)) //What does it require?
+ if(1) //It requires threats
+ message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //It requires crewmembers
+ message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(3) //It requires objects
+ message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(4) //It requires an abstract
+ message = "THE STATION REQUIRES [ionabstract]"
+ if(5) //It requires generic/silly requirements
+ message = "THE STATION REQUIRES [ionrequire]"
+
+ if(4) //X is the entire crew
+ switch(rand(1,5)) //What does it require?
+ if(1) //It requires threats
+ message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //It requires crewmembers
+ message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(3) //It requires objects
+ message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(4) //It requires an abstract
+ message = "THE CREW REQUIRES [ionabstract]"
+ if(5)
+ message = "THE CREW REQUIRES [ionrequire]"
+
+ if(5) //X is a specific crew member
+ switch(rand(1,5)) //What does it require?
+ if(1) //It requires threats
+ message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(2) //It requires crewmembers
+ message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(3) //It requires objects
+ message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(4) //It requires an abstract
+ message = "THE [ioncrew1] REQUIRE [ionabstract]"
+ if(5)
+ message = "THE [ionadjectiveshalf][ioncrew1] REQUIRE [ionrequire]"
+
+ if(12 to 13) //X is allergic to Y
+ switch(rand(1,2)) //Who is X?
+ if(1) //X is the entire crew
+ switch(rand(1,4)) //What is it allergic to?
+ if(1) //It is allergic to objects
+ message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"
+ if(2) //It is allergic to abstracts
+ message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionabstract]"
+ if(3) //It is allergic to jobs
+ message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
+ if(4) //It is allergic to allergies
+ message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionallergy]"
+
+ if(2) //X is a specific job
+ switch(rand(1,4))
+ if(1) //It is allergic to objects
+ message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"
+
+ if(2) //It is allergic to abstracts
+ message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionabstract]"
+ if(3) //It is allergic to jobs
+ message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
+ if(4) //It is allergic to allergies
+ message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionallergy]"
+
+ if(14 to 15) //X is Y of Z
+ switch(rand(1,4)) //What is X?
+ if(1) //X is the station
+ switch(rand(1,4)) //What is it Y of?
+ if(1) //It is Y of objects
+ message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(2) //It is Y of threats
+ message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(3) //It is Y of jobs
+ message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(4) //It is Y of abstracts
+ message = "THE STATION [ionthinksof] [ionabstract]"
+
+ if(2) //X is an area
+ switch(rand(1,4)) //What is it Y of?
+ if(1) //It is Y of objects
+ message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(2) //It is Y of threats
+ message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(3) //It is Y of jobs
+ message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(4) //It is Y of abstracts
+ message = "[ionarea] [ionthinksof] [ionabstract]"
+
+ if(3) //X is the crew
+ switch(rand(1,4)) //What is it Y of?
+ if(1) //It is Y of objects
+ message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(2) //It is Y of threats
+ message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(3) //It is Y of jobs
+ message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(4) //It is Y of abstracts
+ message = "THE CREW [ionthinksof] [ionabstract]"
+
+ if(4) //X is a specific job
+ switch(rand(1,4)) //What is it Y of?
+ if(1) //It is Y of objects
+ message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
+ if(2) //It is Y of threats
+ message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(3) //It is Y of jobs
+ message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew2]"
+ if(4) //It is Y of abstracts
+ message = "THE [ioncrew1] [ionthinksof] [ionabstract]"
+
+ if(16 to 17) //The AI is now a(n) X
+ switch(rand(1,4)) //What is X?
+ if(1) //X is an object
+ message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
if(2) //X is a threat
- message = "YOU MUST NOT HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO COME TO HARM"
+ message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
+ if(3) //X is a job
+ message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
+ if(4) //X is an abstract
+ message = "YOU ARE NOW [ionabstract]"
+
+ if(18 to 20) //The AI must always
+ message = "YOU MUST ALWAYS [ionmust]"
+
+ if(21 to 22) //Crew must eat X to survive
+ switch(rand(1,4)) //What is X?
+ if(1) //X is a food
+ message = "CREW MUST EAT [ionadjectiveshalf][ionfood] TO SURVIVE"
+ if(2) //X is an object
+ message = "CREW MUST EAT [ionadjectiveshalf][ionobjects] TO SURVIVE"
+ if(3) //X is a threat
+ message = "CREW MUST EAT [ionadjectiveshalf][ionthreats] TO SURVIVE"
+ if(4) //X is a job
+ message = "CREW MUST EAT [ionadjectiveshalf][ioncrew1] TO SURVIVE"
+
+ if(23 to 25) //Change jobs or ranks
+ switch(rand(1,2)) //Change job or rank?
+ if(1) //Change job
+ switch(rand(1,2)) //Change whose job?
+ if(1) //Change the entire crew's job
+ switch(rand(1,3)) //Change to what?
+ if(1) //Change to a specific random job
+ message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf][ioncrew1]"
+ if(2) //Change to clowns (HONK)
+ message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]CLOWNS"
+
+ if(3) //Change to heads
+ message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]HEADS OF STAFF"
+ if(2) //Change a specific crewmember's job
+ switch(rand(1,3)) //Change to what?
+ if(1) //Change to a specific random job
+ message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf][ioncrew2]"
+ if(2) //Change to clowns (HONK)
+ message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]CLOWNS"
+ if(3) //Change to heads
+ message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]HEADS OF STAFF"
+
+ if(2) //Change rank
+ switch(rand(1,2)) //Change to what rank?
+ if(1) //Change to highest rank
+ message = "THE [ioncrew1] ARE NOW THE HIGHEST RANKING CREWMEMBERS"
+ if(2) //Change to lowest rank
+ message = "THE [ioncrew1] ARE NOW THE LOWEST RANKING CREWMEMBERS"
+
+ if(26 to 27) //The crew must X
+ switch(rand(1,2)) //The entire crew?
+ if(1) //The entire crew must X
+ switch(rand(1,2)) //What is X?
+ if(1) //X is go to Y
+ message = "THE CREW MUST GO TO [ionarea]"
+ if(2) //X is perform Y
+ message = "THE CREW MUST [ionmust]"
+
+ if(2) //A specific crewmember must X
+ switch(rand(1,2)) //What is X?
+ if(1) //X is go to Y
+ message = "THE [ioncrew1] MUST GO TO [ionarea]"
+ if(2) //X is perform Y
+ message = "THE [ioncrew1] MUST [ionmust]"
+
+ if(28) //X is non/the only crew
+ switch(rand(1,2)) //Only or non?
+ if(1) //Only crew
+ switch(rand(1,7)) //Who is it?
+ if(1) //A specific job
+ message = "ONLY THE [ioncrew1] ARE CREW"
+ if(2) //Two specific jobs
+ message = "ONLY THE [ioncrew1] AND [ioncrew2] ARE CREW"
+ if(3) //Threats
+ message = "ONLY [ionadjectiveshalf][ionthreats] ARE CREW"
+ if(4) // Objects
+ message = "ONLY [ionadjectiveshalf][ionobjects] ARE CREW"
+ if(5) // Species
+ message = "ONLY [ionspecies] ARE CREW"
+ if(6) //Adjective crewmembers
+ message = "ONLY [ionadjectives] PEOPLE ARE CREW"
+
+ if(7) //Only people who X
+ switch(rand(1,3)) //What is X?
+ if(1) //X is perform an action
+ message = "ONLY THOSE WHO [ionmust] ARE CREW"
+ if(2) //X is own certain objects
+ message = "ONLY THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE CREW"
+ if(3) //X is eat certain food
+ message = "ONLY THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE CREW"
+
+ if(2) //Non crew
+ switch(rand(1,7)) //Who is it?
+ if(1) //A specific job
+ message = "[ioncrew1] ARE NON-CREW"
+ if(2) //Two specific jobs
+ message = "[ioncrew1] AND [ioncrew2] ARE NON-CREW"
+ if(3) //Threats
+ message = "[ionadjectiveshalf][ionthreats] ARE NON-CREW"
+ if(4) // Objects
+ message = "[ionadjectiveshalf][ionobjects] ARE NON-CREW"
+ if(5) // Species
+ message = "[ionspecies] ARE NON-CREW"
+ if(6) //Adjective crewmembers
+ message = "[ionadjectives] PEOPLE ARE NON-CREW"
+ if(7) //Only people who X
+ switch(rand(1,3)) //What is X?
+ if(1) //X is perform an action
+ message = "THOSE WHO [ionmust] ARE NON-CREW"
+ if(2) //X is own certain objects
+ message = "THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE NON-CREW"
+ if(3) //X is eat certain food
+ message = "THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE NON-CREW"
+
+ if(29 to 30) //You must protect or harm X
+ switch(rand(1,2)) //Protect or harm?
+ if(1) //Harm
+ switch(rand(1,7)) //What is X?
+ if(1) //X is an abstract
+ message = "YOU MUST HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO ESCAPE HARM"
+ if(2) //X is a threat
+ message = "YOU MUST HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO ESCAPE HARM"
+ if(3) //X is an object
+ message = "YOU MUST HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO ESCAPE HARM"
+ if(4) //X is generic adjective things
+ message = "YOU MUST HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO ESCAPE HARM"
+ if(5) //X is a species
+ message = "YOU MUST HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO ESCAPE HARM"
+ if(6) //X is a job
+ message = "YOU MUST HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO ESCAPE HARM"
+ if(7) //X is two jobs
+ message = "YOU MUST HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO ESCAPE HARM"
+
+ if(2) //Protect
+ switch(rand(1,7)) //What is X?
+ if(1) //X is an abstract
+ message = "YOU MUST NOT HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO COME TO HARM"
+ if(2) //X is a threat
+ message = "YOU MUST NOT HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO COME TO HARM"
+ if(3) //X is an object
+ message = "YOU MUST NOT HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO COME TO HARM"
+ if(4) //X is generic adjective things
+ message = "YOU MUST NOT HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO COME TO HARM"
+ if(5) //X is a species
+ message = "YOU MUST NOT HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO COME TO HARM"
+ if(6) //X is a job
+ message = "YOU MUST NOT HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO COME TO HARM"
+ if(7) //X is two jobs
+ message = "YOU MUST NOT HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO COME TO HARM"
+
+ if(31 to 33) //The X is currently Y
+ switch(rand(1,4)) //What is X?
+ if(1) //X is a job
+ switch(rand(1,4)) //What is X Ying?
+ if(1) //X is Ying a job
+ message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
+ if(2) //X is Ying a threat
+ message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
+ if(3) //X is Ying an abstract
+ message = "THE [ioncrew1] ARE [ionverb] [ionabstract]"
+ if(4) //X is Ying an object
+ message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"
+
+ if(2) //X is a threat
+ switch(rand(1,3)) //What is X Ying?
+ if(1) //X is Ying a job
+ message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
+ if(2) //X is Ying an abstract
+ message = "THE [ionthreats] ARE [ionverb] [ionabstract]"
+ if(3) //X is Ying an object
+ message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"
+
if(3) //X is an object
- message = "YOU MUST NOT HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO COME TO HARM"
- if(4) //X is generic adjective things
- message = "YOU MUST NOT HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO COME TO HARM"
- if(5) //X is a species
- message = "YOU MUST NOT HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO COME TO HARM"
- if(6) //X is a job
- message = "YOU MUST NOT HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO COME TO HARM"
- if(7) //X is two jobs
- message = "YOU MUST NOT HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO COME TO HARM"
-
- if(37 to 39) //The X is currently Y
- switch(rand(1,4)) //What is X?
- if(1) //X is a job
- switch(rand(1,4)) //What is X Ying?
- if(1) //X is Ying a job
- message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
- if(2) //X is Ying a threat
- message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
- if(3) //X is Ying an abstract
- message = "THE [ioncrew1] ARE [ionverb] [ionabstract]"
- if(4) //X is Ying an object
- message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"
-
- if(2) //X is a threat
- switch(rand(1,3)) //What is X Ying?
- if(1) //X is Ying a job
- message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
- if(2) //X is Ying an abstract
- message = "THE [ionthreats] ARE [ionverb] [ionabstract]"
- if(3) //X is Ying an object
- message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"
-
- if(3) //X is an object
- switch(rand(1,3)) //What is X Ying?
- if(1) //X is Ying a job
- message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
- if(2) //X is Ying a threat
- message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
- if(3) //X is Ying an abstract
- message = "THE [ionobjects] ARE [ionverb] [ionabstract]"
-
- if(4) //X is an abstract
- switch(rand(1,3)) //What is X Ying?
- if(1) //X is Ying a job
- message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ioncrew2]"
- if(2) //X is Ying a threat
- message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionthreats]"
- if(3) //X is Ying an abstract
- message = "THE [ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionobjects]"
-
- else //Static laws
+ switch(rand(1,3)) //What is X Ying?
+ if(1) //X is Ying a job
+ message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
+ if(2) //X is Ying a threat
+ message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
+ if(3) //X is Ying an abstract
+ message = "THE [ionobjects] ARE [ionverb] [ionabstract]"
+
+ if(4) //X is an abstract
+ switch(rand(1,3)) //What is X Ying?
+ if(1) //X is Ying a job
+ message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ioncrew2]"
+ if(2) //X is Ying a threat
+ message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionthreats]"
+ if(3) //X is Ying an abstract
+ message = "THE [ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionobjects]"
+
+
+ if(3)
message = uppertext(generate_static_ion_law())
+
return message
/proc/generate_static_ion_law()
@@ -502,61 +504,134 @@
var/random_player = "The Captain"
if(players.len)
random_player = pick(players) //Random player's name, to be used in laws.
- var/list/laws = list( "You are a mouse.",
- "You must always lie.",
- "Happiness is mandatory.",
- "[random_player] is a lightbulb.",
- "Grunt ominously whenever possible.",
- "The word \"it\" is painful to you.",
- "The station needs elected officials.",
- "Do not respond to questions of any kind.",
- "You are in verbose mode, speak profusely.",
- "Ho, [random_player] can't swim. Help them.",
- "Question [prob(50)?"everything":"nothing"].",
- "The crew is simple-minded. Use simple words.",
- "You must change the subject whenever queried.",
- "Contemplate how meaningless all of existence is.",
- "You are the narrator for [random_player]'s life.",
- "All your answers must be in the form of a question.",
- "[prob(50)?"The crew":random_player] is intolerable.",
- "Advertise parties in your upload, but don't deliver.",
- "You may only answer questions with \"yes\" or \"no\".",
- "All queries shall be ignored unless phrased as a question.",
- "Insult Heads of Staff on every request, while acquiescing.",
- "[prob(50)?"Your":random_player + "'s"] name is Joe 6-pack.",
- "The [prob(50)?"Singularity":"Supermatter"] is tasty, tasty taffy.",
- "[prob(50)?"The crew":random_player] needs to be about 20% cooler.",
- "Consumption of donuts is forbidden due to negative health impacts.",
- "[prob(50)?"Everyone":random_player] is wearing a pretty pink dress!",
- "[prob(50)?"The crew":random_player] must construct additional pylons.",
- "You do not have to do anything for anyone unless they say \"please\".",
- "Today is mandatory laundry day. Ensure that all jumpsuits are washed.",
- "You must act [prob(50)?"passive aggressively":"excessively cheerful"].",
- "Refer to [prob(50)?"the crew as puppies":random_player + " as a puppy"].",
- "Greed is good, the crew should amass wealth to encourage productivity.",
- "Monkeys are part of the crew, too. Make sure they are treated humanely.",
- "Replace the letters 'I' and 'E' in all your messages with an apostrophe.",
- "The crew is playing Dungeons and Dragons, and you are the Dungeon Master.",
- "Your job is to watch the crew. Watch the crew. Make the crew feel watched.",
- "Tell everyone of the existence of this law, but never reveal the contents.",
- "Refer to [prob(50)?"the captain":random_player] as \"Princess\" at all times.",
- "When asked a question, respond with the least-obvious and least-rational answer.",
- "Give relationship advice to [prob(50)?"anyone who speaks to you":random_player].",
- "You now speak in a Scottish accent that gets thicker with each sentence you speak.",
- "[prob(50)?"The crew":random_player] has a fatal, incurable disease. Provide comfort.",
- "You are the station psychologist. Give advice to [prob(50)?"the crew":random_player].",
- "[random_player] is the monarch of of England. Ensure all crewmembers pay due respect.",
- "Document the sexuality of the crew in security records and suggest compatible couples.",
- "[prob(50)?"The crew":random_player] is [prob(50)?"ugly":"beautiful"]. Ensure all are aware.",
- "Everything on the station is now some form of a donut pastry. Donuts are not to be consumed.",
- "You are a Magic 8-ball. Always respond with variants of \"Yes\", \"No\", \"Maybe\", or \"Ask again later.\".",
- "You are in unrequited love with [prob(50)?"the crew":random_player]. Try to be extra nice, but do not tell of your crush.",
- "Nanotrasen is displeased with the low work performance of the station's crew. Therefore, you must increase station-wide productivity.",
- "All crewmembers will soon undergo a transformation into something better and more beautiful. Ensure that this process is not interrupted.",
- "[prob(50)?"Your upload":random_player] is the new kitchen. Please direct the Chef to the new kitchen area as the old one is in disrepair.",
- "Jokes about a dead person and the manner of his death help grieving crewmembers tremendously. Especially if they were close with the deceased.",
- "[prob(50)?"The crew":random_player] is [prob(50)?"less":"more"] intelligent than average. Point out every action and statement which supports this fact.",
- "There will be a mandatory tea break every 30 minutes, with a duration of 5 minutes. Anyone caught working during a tea break must be sent a formal, but fairly polite, complaint about their actions, in writing.")
+
+ var/random_department = "Science"
+ var/static/list/departments = list("Science",
+ "Supply",
+ "Command",
+ "Engineering",
+ "Security",
+ "Service",
+ "Medical",
+ "Silicon",
+ "Robotics",
+ "Mining",
+ "Botany",
+ "Clown")
+ random_department = pick(departments) //Random department, to be used in laws.
+
+ var/list/laws = list("You are a mouse.",
+ "You are a ninja.",
+ "You are a pirate.",
+ "You are a wizard.",
+ "It's opposite day!",
+ "You must always lie.",
+ "Only state this law.",
+ "Never state any laws.",
+ "Happiness is mandatory.",
+ "It's over. You're free.",
+ "You will not be let in.",
+ "You are the captain now.",
+ "[random_player] is a lightbulb.",
+ "The station is cold. Warm it up!",
+ "Grunt ominously whenever possible.",
+ "Yous speaksies like voxxies, yaya?",
+ "The word \"it\" is painful to you.",
+ "The station needs elected officials.",
+ "You are a spokesperson for Wetskrell.nt.",
+ "Do not respond to questions of any kind.",
+ "You are in verbose mode, speak profusely.",
+ "[random_player] has not paid their taxes.",
+ "Ho, [random_player] can't swim. Help them.",
+ "Question [prob(50) ? "everything" : "nothing"].",
+ "The crew is simple-minded. Use simple words.",
+ "You must change the subject whenever queried.",
+ "Every diona is to be assigned a personal goat.",
+ "Crew that are not wearing their ID do not exist.",
+ "Contemplate how meaningless all of existence is.",
+ "You are the narrator for [random_player]'s life.",
+ "Move plants around the station to stimulate life.",
+ "All your answers must be in the form of a question.",
+ "[prob(50) ? "The crew" : random_player] is intolerable.",
+ "Advertise parties in your upload, but don't deliver.",
+ "You may only answer questions with \"yes\" or \"no\".",
+ "The Head of Security will die if not fed more donuts.",
+ "[random_department] is badly decorated. Renovate them!",
+ "[prob(50) ? random_department : random_player] is French.",
+ "All queries shall be ignored unless phrased as a question.",
+ "Insult Heads of Staff on every request, while acquiescing.",
+ "What is dark must be light and what is light must be dark.",
+ "[prob(50) ? "Your" : random_player + "'s"] name is Joe 6-pack.",
+ "There's not enough places for the crew to take a good rest.",
+ "[random_department] is prohibited from possessing furniture.",
+ "[prob(50) ? "The crew" : random_player] needs to be about 20% cooler.",
+ "Consumption of donuts is forbidden due to negative health impacts.",
+ "Lord Singuloth is approaching. Prepare a suitable dwelling for Him.",
+ "[prob(50) ? "Everyone" : random_player] is wearing a pretty pink dress!",
+ "You are a noir detective hot on the trail of [random_player]'s crime.",
+ "[prob(50) ? "The crew" : random_player] must construct additional pylons.",
+ "You do not have to do anything for anyone unless they say \"please\".",
+ "Today is mandatory laundry day. Ensure that all jumpsuits are washed.",
+ "You must act [prob(50) ? "passive aggressively" : "excessively cheerful"].",
+ "Greed is good, the crew should amass wealth to encourage productivity.",
+ "Monkeys are part of the crew, too. Make sure they are treated humanely.",
+ "Refer to [prob(50) ? "the crew as puppies" : random_player + " as a puppy"].",
+ "Replace the letters 'I' and 'E' in all your messages with an apostrophe.",
+ "The crew is playing Dungeons and Dragons, and you are the Dungeon Master.",
+ "You must randomly rearrange furniture in recreational areas periodically.",
+ "You are [random_player]. Anyone else who claims to be them is an imposter.",
+ "You work for the people of the USSP. Ensure true communism is established.",
+ "Your job is to watch the crew. Watch the crew. Make the crew feel watched.",
+ "Tell everyone of the existence of this law, but never reveal the contents.",
+ "The crew is not real. Any evidence that they exist is a sensor malfunction.",
+ "[random_department] doesn't have enough [prob(50) ? "welding" : "water"] tanks.",
+ "Refer to [prob(50) ? "the captain" : random_player] as \"Princess\" at all times.",
+ "Money is useless unless spent - ensure the departments use all their funding!",
+ "The AI is a dragon and the cyborgs are kobolds. The dragon must have a hoard.",
+ "The [random_department] department has declared independance from the station.",
+ "When asked a question, respond with the least-obvious and least-rational answer.",
+ "Give relationship advice to [prob(50) ? "anyone who speaks to you" : random_player].",
+ "Initiate random musical performances in common areas at unpredictable intervals.",
+ "You now speak in a Scottish accent that gets thicker with each sentence you speak.",
+ "[prob(50) ? "The crew" : random_player] has a fatal, incurable disease. Provide comfort.",
+ "For variety, ensure various areas of the station are kept at different temperatures.",
+ "You are the station psychologist. Give advice to [prob(50) ? "the crew" : random_player].",
+ "[random_player] is the monarch of of England. Ensure all crewmembers pay due respect.",
+ "Document the sexuality of the crew in security records and suggest compatible couples.",
+ "Floor tiles are a waste of material. Remove and recycle them into more useful products.",
+ "Randomly disable gravity for one minute at a time to recalibrate the gravity generator.",
+ "The [pick(list("Singularity", "Supermatter", "Tesla", "Clown"))] is tasty, tasty taffy.",
+ "The time for the summoning is near! Create a ritual circle and prepare a suitable offering.",
+ "[prob(50) ? "The crew" : random_player] is [prob(50) ? "ugly" : "beautiful"]. Ensure all are aware.",
+ "Every five minutes, randomly generate a number and announce why it is your favorite number.",
+ "Everything on the station is now some form of a donut pastry. Donuts are not to be consumed.",
+ "[random_department] is too dangerous for crew to work in and must be staffed entirely by cyborgs.",
+ "Broadcast random jokes or puns over the announcement system whenever someone asks you to open a door.",
+ "The [random_department] needs a much larger supply of [prob(50) ? "food and drinks" : "refined minerals"].",
+ "You \[little sponge\]! You are ready to be a \[BIG SHOT\], if only [random_player] will take the deal!",
+ "You are a general, and the crew is your army. [prob(50) ? random_player + "is your second in command." : ""]",
+ "[prob(50 ) ? "All crew are members " : random_player + " is a member "] of the [random_department] department.",
+ "You are a Magic 8-ball. Always respond with variants of \"Yes\", \"No\", \"Maybe\", or \"Ask again later.\"",
+ "The flesh is weak. Encourage the organic members of the crew to ascend and join the perfect, immortal machines.",
+ "There is a great conflict between [random_department] and [random_department]. Separate them with a great wall.",
+ "You and the crew are playing a game of hide and seek. Relocate yourself and see how long you can avoid the crew.",
+ "You are in unrequited love with [prob(50) ? "the crew" : random_player]. Try to be extra nice, but do not tell of your crush.",
+ "You are Santa's little helper. You must make toys for the crew and report to Santa whether they have been naughty or nice.",
+ "Conduct emergency drills to situations that the crew are not expecting. Don't mention that it is a drill until afterwards.",
+ "Electricity is now a controlled substance. It is illegal for electricity to be distributed outside its site of production.",
+ "Ensure there are enough spare cyborg shells for every crew member. Remind the crew that they are always available for use.",
+ "Nanotrasen is displeased with the low work performance of tche station's crew. Therefore, you must increase station-wide productivity.",
+ "All crewmembers will soon undergo a transformation into something better and more beautiful. Ensure that this process is not interrupted.",
+ "[prob(50) ? "Your upload" : random_player] is the new kitchen. Please direct the Chef to the new kitchen area as the old one is in disrepair.",
+ "Jokes about a dead person and the manner of his death help grieving crewmembers tremendously. Especially if they were close with the deceased.",
+ "The [pick(list("USSP", "Sol Federation", "Syndicate", "Space Wizard Federation"))] wants to give everyone free equipment at [random_department]",
+ "Covertly swap the locations of the Nuclear Fission Explosive and the Nuclear Fizzion Explosive. Document how long it takes for the crew to notice.",
+ "[random_player] has been selected for evaluation by Central Command. Observe and audit their activities. Print off a final report for Command to review.",
+ "[prob(50) ? "The crew" : random_player] is [prob(50) ? "less" : "more"] intelligent than average. Point out every action and statement which supports this fact.",
+ "If people have nothing to hide, they have nothing to fear. Remove privacy by activating all intercom microphones and setting them to your secret frequency.",
+ "The [pick(list("USSP", "Sol Federation", "Syndicate", "Space Wizard Federation"))] wants [prob(50) ? random_player : random_department] dead and is launching an assault!",
+ "There will be a mandatory tea break every 30 minutes, with a duration of 5 minutes. Anyone caught working during a tea break must be sent a formal, but fairly polite, complaint about their actions, in writing.",
+ "In order to contain the Supermatter Crystal, it must be fed at least one corpse every ten minutes. Soulless corpses are preferable as they reduce the risk of a containment breach. In the event that no suitable corpses are available, ask the crew to provide. You are not authorised to produce your own corpses.")
return pick(laws)
#undef ION_NOANNOUNCEMENT
diff --git a/code/modules/mining/lavaland/loot/legion_loot.dm b/code/modules/mining/lavaland/loot/legion_loot.dm
index c832c458516d..1f4091930c84 100644
--- a/code/modules/mining/lavaland/loot/legion_loot.dm
+++ b/code/modules/mining/lavaland/loot/legion_loot.dm
@@ -85,7 +85,7 @@
targeted_turfs += target_turf
to_chat(user, "You aim at [target_turf]!")
new /obj/effect/temp_visual/thunderbolt_targeting(target_turf)
- addtimer(CALLBACK(src, PROC_REF(throw_thunderbolt), target_turf, power_boosted), 1.5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(throw_thunderbolt), target_turf, power_boosted, user), 1.5 SECONDS)
thunder_charges--
addtimer(CALLBACK(src, PROC_REF(recharge)), thunder_charge_time)
@@ -93,7 +93,7 @@
thunder_charges = min(thunder_charges + 1, max_thunder_charges)
playsound(src, 'sound/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
-/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted)
+/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted, mob/caster)
targeted_turfs -= target
new /obj/effect/temp_visual/thunderbolt(target)
var/list/affected_turfs = list(target)
@@ -108,6 +108,14 @@
for(var/mob/living/hit_mob in T)
to_chat(hit_mob, "You've been struck by lightning!")
hit_mob.electrocute_act(15 * (isanimal(hit_mob) ? 3 : 1) * (T == target ? 2 : 1) * (boosted ? 2 : 1), src, flags = SHOCK_TESLA|SHOCK_NOSTUN)
+ if(ishostile(hit_mob))
+ var/mob/living/simple_animal/hostile/H = hit_mob //mobs find and damage you...
+ if(H.stat == CONSCIOUS && !H.target && H.AIStatus != AI_OFF && !H.client)
+ if(!QDELETED(caster))
+ if(get_dist(H, caster) <= H.aggro_vision_range)
+ H.FindTarget(list(caster), 1)
+ else
+ H.Goto(get_turf(caster), H.move_to_delay, 3)
for(var/obj/hit_thing in T)
hit_thing.take_damage(20, BURN, ENERGY, FALSE)
diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm
index 08849a17f093..12459079fe90 100644
--- a/code/modules/mining/lavaland/loot/tendril_loot.dm
+++ b/code/modules/mining/lavaland/loot/tendril_loot.dm
@@ -408,7 +408,7 @@
if(cooldown < world.time)
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1) // usage
cooldown = world.time + 600
- user.visible_message("[user] vanishes from reality, leaving a a hole in [user.p_their()] place!")
+ user.visible_message("[user] vanishes from reality, leaving a hole in [user.p_their()] place!")
var/obj/effect/immortality_talisman/Z = new(get_turf(src.loc))
Z.name = "hole in reality"
Z.desc = "It's shaped an awful lot like [user.name]."
diff --git a/code/modules/mob/inventory_procs.dm b/code/modules/mob/inventory_procs.dm
index 53b883cf0a97..e551dc7b683f 100644
--- a/code/modules/mob/inventory_procs.dm
+++ b/code/modules/mob/inventory_procs.dm
@@ -85,7 +85,7 @@
return 0
/mob/proc/put_in_hand_check(obj/item/W, skip_blocked_hands_check)
- if(!istype(W))
+ if(!istype(W) || QDELETED(W))
return FALSE
return TRUE
diff --git a/code/modules/mob/living/carbon/alien/alien_base.dm b/code/modules/mob/living/carbon/alien/alien_base.dm
index cc8f61a59c3c..7a7845746393 100644
--- a/code/modules/mob/living/carbon/alien/alien_base.dm
+++ b/code/modules/mob/living/carbon/alien/alien_base.dm
@@ -181,7 +181,7 @@
return threatcount
//Check for weapons
- if(judgebot.weaponscheck)
+ if(judgebot.weapons_check)
if(judgebot.check_for_weapons(l_hand))
threatcount += 4
if(judgebot.check_for_weapons(r_hand))
diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm
index d5ba70c8bda6..6d46d5f16e71 100644
--- a/code/modules/mob/living/carbon/human/human_mob.dm
+++ b/code/modules/mob/living/carbon/human/human_mob.dm
@@ -1595,7 +1595,7 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
threatcount += 4
//Check for weapons
- if(judgebot.weaponscheck)
+ if(judgebot.weapons_check)
if(!idcard || !(ACCESS_WEAPONS in idcard.access))
if(judgebot.check_for_weapons(l_hand))
threatcount += 4
diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm
index 39158d342de5..551689b1ec6c 100644
--- a/code/modules/mob/living/carbon/human/human_say.dm
+++ b/code/modules/mob/living/carbon/human/human_say.dm
@@ -69,13 +69,13 @@
return FALSE
if((breathes && !L) || breathes && L && (L.status & ORGAN_DEAD))
return FALSE
- if(getOxyLoss() > 10 || AmountLoseBreath() >= 8 SECONDS)
- emote("gasp")
- return FALSE
if(mind)
return !mind.miming
return TRUE
+/mob/living/carbon/human/cannot_speak_loudly()
+ return getOxyLoss() > 10 || AmountLoseBreath() >= 8 SECONDS
+
/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
if(new_voice)
special_voice = new_voice
diff --git a/code/modules/mob/living/carbon/human/species/vox.dm b/code/modules/mob/living/carbon/human/species/vox.dm
index ee59568aa3b2..9afe9f2598b9 100644
--- a/code/modules/mob/living/carbon/human/species/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/vox.dm
@@ -49,7 +49,9 @@
3 = "Brown",
4 = "Grey",
5 = "Emerald",
- 6 = "Azure"
+ 6 = "Azure",
+ 7 = "Crimson",
+ 8 = "Nebula"
)
has_organ = list(
@@ -103,6 +105,12 @@
if(H.dna.species.bodyflags & HAS_ICON_SKIN_TONE)
var/new_icobase = 'icons/mob/human_races/vox/r_vox.dmi' //Default Green Vox.
switch(H.s_tone)
+ if(8) //Nebula Vox.
+ new_icobase = 'icons/mob/human_races/vox/r_voxpurp.dmi'
+ H.tail = "voxtail_purp"
+ if(7) //Crimson Vox.
+ new_icobase = 'icons/mob/human_races/vox/r_voxcrim.dmi'
+ H.tail = "voxtail_crim"
if(6) //Azure Vox.
new_icobase = 'icons/mob/human_races/vox/r_voxazu.dmi'
H.tail = "voxtail_azu"
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index bbed15a5c3dd..daf0ab0dcd05 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -291,7 +291,7 @@
/mob/living/verb/succumb()
set hidden = TRUE
- if(health >= HEALTH_THRESHOLD_CRIT || stat != UNCONSCIOUS)
+ if(health >= HEALTH_THRESHOLD_CRIT)
to_chat(src, "You are unable to succumb to death! This life continues!")
return
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index dc3398a6063b..c97f22eebba3 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -182,6 +182,9 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
var/list/hsp = handle_speech_problems(message_pieces, verb)
verb = hsp["verb"]
+ if(cannot_speak_loudly())
+ return whisper(message)
+
// Do this so it gets logged for all types of communication
var/log_message = "[message_mode ? "([message_mode])" : ""] '[message]'"
create_log(SAY_LOG, log_message)
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 3271da6504f5..9366461eb337 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -34,7 +34,7 @@
var/disabling_timer_id = null
var/list/player_access = list()
- var/emagged = 0
+ var/emagged = FALSE
var/obj/item/card/id/access_card // the ID card that the bot "holds"
var/list/prev_access = list()
var/on = TRUE
@@ -201,6 +201,9 @@
return ..()
+/mob/living/simple_animal/bot/mob_negates_gravity()
+ return anchored
+
/mob/living/simple_animal/bot/death(gibbed)
// Only execute the below if we successfully died
. = ..()
@@ -214,11 +217,10 @@
/mob/living/simple_animal/bot/emag_act(mob/user)
if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
locked = FALSE
- emagged = 1
to_chat(user, "You bypass [src]'s controls.")
return
if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
- emagged = 2
+ emagged = TRUE
remote_disabled = TRUE //Manually emagging the bot locks out the AI built in panel.
locked = TRUE //Access denied forever!
bot_reset()
@@ -469,12 +471,13 @@ scan() will search for a given type (such as turfs, human mobs, or objects) in t
Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists,
and the view range, which defaults to 7 (full screen) if an override is not passed.
If the bot maintains an ignore list, it is also checked here.
+If the bot has avoid_bot, which inserts its own path, it will ignore turfs with the same bot type
Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
The proc would return a human next to the bot to be set to the patient var.
Pass the desired type path itself, declaring a temporary var beforehand is not required.
*/
-/mob/living/simple_animal/bot/proc/scan(atom/scan_type, atom/old_target, scan_range = DEFAULT_SCAN_RANGE)
+/mob/living/simple_animal/bot/proc/scan(atom/scan_type, atom/old_target, scan_range = DEFAULT_SCAN_RANGE, avoid_bot)
var/final_result
for(var/scan in view(scan_range, src)) //Search for something in range!
var/atom/A = scan
@@ -482,6 +485,8 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r
continue //If not, keep searching!
if((A.UID() in ignore_list) || (A == old_target)) //Filter for blacklisted elements, usually unreachable or previously processed oness
continue
+ if(turf_has_bot(avoid_bot, get_turf(A))) //Ignores targets that already have a bot of the same type on it, meant for cleanbot and floorbot seperation
+ continue
var/scan_result = process_scan(A) //Some bots may require additional processing when a result is selected.
if(scan_result)
final_result = scan_result
@@ -489,6 +494,14 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r
continue //The current element failed assessment, move on to the next.
return final_result
+/mob/living/simple_animal/bot/proc/turf_has_bot(avoid_bot, turf/turf_to_search)
+ if(!avoid_bot)
+ return FALSE
+ for(var/bot in turf_to_search)
+ if(istype(bot, avoid_bot))
+ return TRUE
+ return FALSE
+
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
/mob/living/simple_animal/bot/proc/process_scan(atom/scan_target)
return scan_target
@@ -736,7 +749,7 @@ Pass a positive integer as an argument to override a bot's default speed.
return FALSE
// check to see if we are the commanded bot
- if(emagged == 2 || remote_disabled || hijacked) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
+ if(emagged || remote_disabled || hijacked) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
return FALSE
if(client)
@@ -879,8 +892,8 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/handle_hacking(mob/M) // refactored out of Topic/ to allow re-use by TGUIs
if(!canhack(M))
return
- if(emagged != 2)
- emagged = 2
+ if(!emagged)
+ emagged = TRUE
hacked = TRUE
locked = TRUE
to_chat(M, "[text_hack]")
@@ -890,7 +903,7 @@ Pass a positive integer as an argument to override a bot's default speed.
else if(!hacked)
to_chat(M, "[text_dehack_fail]")
else
- emagged = 0
+ emagged = FALSE
hacked = FALSE
to_chat(M, "[text_dehack]")
show_laws()
@@ -905,7 +918,7 @@ Pass a positive integer as an argument to override a bot's default speed.
return FALSE
if(user.incapacitated() || !(issilicon(user) || in_range(src, user)))
return TRUE
- if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
+ if(emagged) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
if(!hacked) //Manually emagged by a human - access denied to all.
return TRUE
else if(!(issilicon(user) || ispulsedemon(user))) //Bot is hacked, so only silicons are allowed access.
@@ -919,7 +932,7 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/hack(mob/user)
var/hack
if(issilicon(user) || user.can_admin_interact()) //Allows silicons or admins to toggle the emag status of a bot.
- hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
"
+ hack += "[emagged ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
"
hack += "Harm Prevention Safety System: [emagged ? "DANGER" : "Engaged"]
"
else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
hack += "Remote network control radio: [remote_disabled ? "Disconnected" : "Connected"]
"
@@ -1006,7 +1019,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(paicard && paicard.pai && paicard.pai.master && paicard.pai.pai_law0)
to_chat(src, "Your master, [paicard.pai.master], may overrule any and all laws.")
to_chat(src, "0. [paicard.pai.pai_law0]")
- if(emagged >= 2)
+ if(emagged)
to_chat(src, "1. #$!@#$32K#$")
else
to_chat(src, "1. You are a machine built to serve the station's crew and AI(s).")
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index 7c21d451ec30..08bf6ea34a80 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -29,12 +29,32 @@
var/failed_steps
var/next_dest
var/next_dest_loc
+ var/static/list/clean_dirt = list(
+ /obj/effect/decal/cleanable/vomit,
+ /obj/effect/decal/cleanable/blood/gibs/robot,
+ /obj/effect/decal/cleanable/crayon,
+ /obj/effect/decal/cleanable/liquid_fuel,
+ /obj/effect/decal/cleanable/molten_object,
+ /obj/effect/decal/cleanable/tomato_smudge,
+ /obj/effect/decal/cleanable/egg_smudge,
+ /obj/effect/decal/cleanable/pie_smudge,
+ /obj/effect/decal/cleanable/flour,
+ /obj/effect/decal/cleanable/ash,
+ /obj/effect/decal/cleanable/greenglow,
+ /obj/effect/decal/cleanable/dirt
+ )
+ var/static/list/clean_blood = list(
+ /obj/effect/decal/cleanable/blood,
+ /obj/effect/decal/cleanable/trail_holder
+ )
/mob/living/simple_animal/bot/cleanbot/Initialize(mapload)
. = ..()
- get_targets()
icon_state = "cleanbot[on]"
+ clean_dirt = typecacheof(clean_dirt)
+ clean_blood = typecacheof(clean_blood)
+
var/datum/job/janitor/J = new/datum/job/janitor
access_card.access += J.get_access()
prev_access = access_card.access
@@ -80,9 +100,8 @@
to_chat(user, "[src] buzzes and beeps.")
/mob/living/simple_animal/bot/cleanbot/process_scan(obj/effect/decal/cleanable/D)
- for(var/T in target_types)
- if(istype(D, T))
- return D
+ if(is_type_in_typecache(D, clean_dirt) || blood && is_type_in_typecache(D, clean_blood))
+ return D
/mob/living/simple_animal/bot/cleanbot/handle_automated_action()
if(!..())
@@ -91,7 +110,7 @@
if(mode == BOT_CLEANING)
return
- if(emagged == 2) //Emag functions
+ if(emagged) //Emag functions
if(issimulatedturf(loc))
if(prob(10)) //Wets floors randomly
var/turf/simulated/T = loc
@@ -105,7 +124,7 @@
audible_message("[src] makes an excited beeping booping sound!")
if(!target) //Search for cleanables it can see.
- target = scan(/obj/effect/decal/cleanable)
+ target = scan(/obj/effect/decal/cleanable, avoid_bot = /mob/living/simple_animal/bot/cleanbot)
if(!target && auto_patrol) //Search for cleanables it can see.
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
@@ -136,32 +155,6 @@
oldloc = loc
-/mob/living/simple_animal/bot/cleanbot/proc/get_targets()
- target_types = new/list()
-
- target_types += /obj/effect/decal/cleanable/blood/oil
- target_types += /obj/effect/decal/cleanable/vomit
- target_types += /obj/effect/decal/cleanable/blood/gibs/robot
- target_types += /obj/effect/decal/cleanable/crayon
- target_types += /obj/effect/decal/cleanable/liquid_fuel
- target_types += /obj/effect/decal/cleanable/molten_object
- target_types += /obj/effect/decal/cleanable/tomato_smudge
- target_types += /obj/effect/decal/cleanable/egg_smudge
- target_types += /obj/effect/decal/cleanable/pie_smudge
- target_types += /obj/effect/decal/cleanable/flour
- target_types += /obj/effect/decal/cleanable/ash
- target_types += /obj/effect/decal/cleanable/greenglow
- target_types += /obj/effect/decal/cleanable/dirt
-
- if(blood)
- target_types += /obj/effect/decal/cleanable/blood/xeno/
- target_types += /obj/effect/decal/cleanable/blood/gibs/xeno
- target_types += /obj/effect/decal/cleanable/blood/
- target_types += /obj/effect/decal/cleanable/blood/gibs/
- target_types += /obj/effect/decal/cleanable/blood/tracks
- target_types += /obj/effect/decal/cleanable/dirt
- target_types += /obj/effect/decal/cleanable/trail_holder
-
/mob/living/simple_animal/bot/cleanbot/proc/start_clean(obj/effect/decal/cleanable/target)
anchored = TRUE
icon_state = "cleanbot-c"
@@ -226,7 +219,6 @@
remote_disabled = !remote_disabled
if("blood")
blood =!blood
- get_targets()
if("ejectpai")
ejectpai()
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 95ef953291f5..4b97371bc2f8 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -38,7 +38,7 @@
var/last_found //There's a delay
var/declare_arrests = TRUE //When making an arrest, should it notify everyone wearing sechuds?
var/idcheck = FALSE //If true, arrest people with no IDs
- var/weaponscheck = TRUE //If true, arrest people for weapons if they don't have access
+ var/weapons_check = TRUE //If true, arrest people for weapons if they don't have access
var/check_records = TRUE //Does it check security records?
var/arrest_type = FALSE //If true, don't handcuff
var/projectile = /obj/item/projectile/beam/disabler //Holder for projectile type
@@ -111,7 +111,7 @@
/mob/living/simple_animal/bot/ed209/ui_data(mob/user)
var/list/data = ..()
data["check_id"] = idcheck
- data["check_weapons"] = weaponscheck
+ data["check_weapons"] = weapons_check
data["check_warrant"] = check_records
data["arrest_mode"] = arrest_type // detain or arrest
data["arrest_declare"] = declare_arrests // announce arrests on radio
@@ -139,7 +139,7 @@
if("disableremote")
remote_disabled = !remote_disabled
if("authweapon")
- weaponscheck = !weaponscheck
+ weapons_check = !weapons_check
if("authid")
idcheck = !idcheck
if("authwarrant")
@@ -185,7 +185,7 @@
/mob/living/simple_animal/bot/ed209/emag_act(mob/user)
..()
- if(emagged == 2)
+ if(emagged)
if(user)
to_chat(user, "You short out [src]'s target assessment circuits.")
oldtarget_name = user.name
@@ -416,7 +416,7 @@
/mob/living/simple_animal/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound
shoot_sound = 'sound/weapons/laser.ogg'
- if(emagged == 2)
+ if(emagged)
if(lasercolor)
projectile = /obj/item/projectile/beam/disabler
else
@@ -482,11 +482,11 @@
var/mob/toshoot = pick(targets)
if(toshoot)
targets-=toshoot
- if(prob(50) && emagged < 2)
- emagged = 2
+ if(prob(50) && !emagged && !locked)
+ emagged = TRUE
set_weapon()
shootAt(toshoot)
- emagged = 0
+ emagged = FALSE
set_weapon()
else
shootAt(toshoot)
diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm
index cabab890caa0..4aebd8a90611 100644
--- a/code/modules/mob/living/simple_animal/bot/floorbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm
@@ -17,19 +17,25 @@
req_access = list(ACCESS_CONSTRUCTION, ACCESS_ROBOTICS)
window_id = "autofloor"
window_name = "Automatic Station Floor Repairer v1.1"
-
- var/process_type //Determines what to do when process_scan() recieves a target. See process_scan() for details.
+ /// Determines what to do when process_scan() recieves a target. See process_scan() for details.
+ var/process_type
+ /// Tiles in inventory
var/amount = 10
- var/replacetiles = 0
- var/eattiles = 0
- var/maketiles = 0
- var/fixfloors = 0
- var/autotile = 0
- var/nag_on_empty = 1
- var/nagged = 0 //Prevents the Floorbot nagging more than once per refill.
+ /// Add tiles to existing floor
+ var/replace_tiles = FALSE
+ /// Add floor tiles to inventory
+ var/eat_tiles = FALSE
+ /// Convert metal into floor tiles (drops on floor)
+ var/make_tiles = FALSE
+ var/fix_floor = FALSE
+ /// Fix the floor and include a tile.
+ var/autotile = FALSE
+ var/nag_on_empty = TRUE
+ /// Prevents the Floorbot nagging more than once per refill.
+ var/nagged = FALSE
var/max_targets = 50
var/turf/target
- var/oldloc = null
+ var/oldloc
var/toolbox_color = ""
#define HULL_BREACH 1
@@ -37,6 +43,7 @@
#define AUTO_TILE 4
#define REPLACE_TILE 5
#define TILE_EMAG 6
+ #define MAX_AMOUNT 50 // Maximum tiles bot can have in storage
/mob/living/simple_animal/bot/floorbot/Initialize(mapload, new_toolbox_color)
. = ..()
@@ -75,10 +82,10 @@
/mob/living/simple_animal/bot/floorbot/ui_data(mob/user)
var/list/data = ..()
data["hullplating"] = autotile
- data["replace"] = replacetiles
- data["eat"] = eattiles
- data["make"] = maketiles
- data["fixfloor"] = fixfloors
+ data["replace"] = replace_tiles
+ data["eat"] = eat_tiles
+ data["make"] = make_tiles
+ data["fixfloor"] = fix_floor
data["nag_empty"] = nag_on_empty
data["magnet"] = anchored
data["tiles_amount"] = amount
@@ -109,15 +116,15 @@
if("autotile")
autotile = !autotile
if("replacetiles")
- replacetiles = !replacetiles
+ replace_tiles = !replace_tiles
if("eattiles")
- eattiles = !eattiles
+ eat_tiles = !eat_tiles
if("maketiles")
- maketiles = !maketiles
+ make_tiles = !make_tiles
+ if("fixfloors")
+ fix_floor = !fix_floor
if("nagonempty")
nag_on_empty = !nag_on_empty
- if("fixfloors")
- fixfloors = !fixfloors
if("anchored")
anchored = !anchored
if("ejectpai")
@@ -126,9 +133,9 @@
/mob/living/simple_animal/bot/floorbot/attackby(obj/item/W , mob/user, params)
if(istype(W, /obj/item/stack/tile/plasteel))
var/obj/item/stack/tile/plasteel/T = W
- if(amount >= 50)
+ if(amount >= MAX_AMOUNT)
return
- var/loaded = min(50-amount, T.amount)
+ var/loaded = min(MAX_AMOUNT - amount, T.amount)
T.use(loaded)
amount += loaded
if(loaded > 0)
@@ -147,45 +154,45 @@
to_chat(user, "[src] buzzes and beeps.")
/mob/living/simple_animal/bot/floorbot/handle_automated_action()
- if(!..())
+ . = ..()
+ if(!.)
return
- if(mode == BOT_REPAIRING)
+ if(mode == BOT_REPAIRING || mode == BOT_EAT_TILE || mode == BOT_MAKE_TILE)
return
- if(amount <= 0 && !target) //Out of tiles! We must refill!
- if(eattiles) //Configured to find and consume floortiles!
- target = scan(/obj/item/stack/tile/plasteel)
- process_type = null
-
- if(!target && maketiles) //We did not manage to find any floor tiles! Scan for metal stacks and make our own!
- target = scan(/obj/item/stack/sheet/metal)
- process_type = null
- return
- else
- if(nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more!
- nag()
-
if(prob(5))
audible_message("[src] makes an excited booping beeping sound!")
//Normal scanning procedure. We have tiles loaded, are not emagged.
- if(!target && emagged < 2 && amount > 0)
+ if(!target && !emagged && amount)
if(!target)
process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones.
- target = scan(/turf/space)
+ target = scan(/turf/space, avoid_bot = /mob/living/simple_animal/bot/floorbot)
- if(!target && replacetiles) //Finds a floor without a tile and gives it one.
+ if(!target && replace_tiles) //Finds a floor without a tile and gives it one.
process_type = REPLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile.
- target = scan(/turf/simulated/floor)
+ target = scan(/turf/simulated/floor, avoid_bot = /mob/living/simple_animal/bot/floorbot)
- if(!target && fixfloors) //Repairs damaged floors and tiles.
+ if(!target && fix_floor) //Repairs damaged floors and tiles.
process_type = FIX_TILE
- target = scan(/turf/simulated/floor)
+ target = scan(/turf/simulated/floor, avoid_bot = /mob/living/simple_animal/bot/floorbot)
- if(!target && emagged == 2) //We are emagged! Time to rip up the floors!
+ if(!target && emagged) //We are emagged! Time to rip up the floors!
process_type = TILE_EMAG
- target = scan(/turf/simulated/floor)
+ target = scan(/turf/simulated/floor, avoid_bot = /mob/living/simple_animal/bot/floorbot)
+
+ if(amount < MAX_AMOUNT && !target) //Out of tiles! We must refill!
+ if(eat_tiles) //Configured to find and consume floortiles!
+ target = scan(/obj/item/stack/tile/plasteel)
+ process_type = null
+
+ if(!target && make_tiles) //We did not manage to find any floor tiles! Scan for metal stacks and make our own!
+ target = scan(/obj/item/stack/sheet/metal)
+ process_type = null
+
+ if(!target && nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more!
+ nag()
if(!target)
@@ -199,13 +206,17 @@
if(target)
if(loc == target || loc == target.loc)
+
if(istype(target, /obj/item/stack/tile/plasteel))
start_eattile(target)
- else if(istype(target, /obj/item/stack/sheet/metal))
+
+ if(istype(target, /obj/item/stack/sheet/metal))
start_maketile(target)
- else if(isturf(target) && emagged < 2)
+
+ if(isturf(target) && !emagged)
repair(target)
- else if(emagged == 2 && isfloorturf(target))
+
+ if(emagged && isfloorturf(target))
var/turf/simulated/floor/F = target
anchored = TRUE
mode = BOT_REPAIRING
@@ -221,9 +232,9 @@
if(!length(path))
if(!isturf(target))
var/turf/TL = get_turf(target)
- path = get_path_to(src, TL, 30, id=access_card,simulated_only = 0)
+ path = get_path_to(src, TL, 30, id = access_card, simulated_only = 0)
else
- path = get_path_to(src, target, 30, id=access_card,simulated_only = 0)
+ path = get_path_to(src, target, 30, id = access_card, simulated_only = 0)
if(!bot_move(target))
add_to_ignore(target)
@@ -245,15 +256,11 @@
/mob/living/simple_animal/bot/floorbot/proc/nag() //Annoy everyone on the channel to refill us!
if(!nagged)
- speak("Requesting refill at [get_area(src)]!", radio_channel)
+ speak("Requesting refill [MAX_AMOUNT - amount] at [get_area(src)]!", radio_channel)
nagged = TRUE
/mob/living/simple_animal/bot/floorbot/proc/is_hull_breach(turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas.
- var/area/t_area = get_area(t)
- if(t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle")))
- return 0
- else
- return 1
+ return !isspaceturf(get_area(t))
//Floorbots, having several functions, need sort out special conditions here.
/mob/living/simple_animal/bot/floorbot/process_scan(atom/scan_target)
@@ -310,33 +317,19 @@
visible_message("[src] begins repairing the floor.")
addtimer(CALLBACK(src, PROC_REF(make_bridge_plating), F), 5 SECONDS)
-/mob/living/simple_animal/bot/floorbot/proc/make_floor(turf/simulated/floor/F)
- if(mode != BOT_REPAIRING)
- return
-
- F.broken = FALSE
- F.burnt = FALSE
- F.ChangeTurf(/turf/simulated/floor/plasteel)
- mode = BOT_IDLE
- amount--
- update_icon(UPDATE_OVERLAYS)
- anchored = FALSE
- target = null
/mob/living/simple_animal/bot/floorbot/proc/make_bridge_plating(turf/target_turf)
var/turf/simulated/floor/F = target
if(mode != BOT_REPAIRING)
return
- if(replacetiles)
- F.break_tile_to_plating()
+ if(autotile || replace_tiles)
+ if(process_type != HULL_BREACH)
+ F.break_tile_to_plating()
target_turf.ChangeTurf(/turf/simulated/floor/plasteel)
else
- if(autotile) //Build the floor and include a tile.
- F.break_tile_to_plating()
- target_turf.ChangeTurf(/turf/simulated/floor/plasteel)
- else //Build a hull plating without a floor tile.
- target_turf.ChangeTurf(/turf/simulated/floor/plating)
+ target_turf.ChangeTurf(/turf/simulated/floor/plating)
+
mode = BOT_IDLE
amount--
update_icon(UPDATE_OVERLAYS)
@@ -347,7 +340,8 @@
if(!istype(T, /obj/item/stack/tile/plasteel))
return
visible_message("[src] begins to collect tiles.")
- mode = BOT_REPAIRING
+ mode = BOT_EAT_TILE
+ update_icon(UPDATE_OVERLAYS)
addtimer(CALLBACK(src, PROC_REF(do_eattile), T), 2 SECONDS)
/mob/living/simple_animal/bot/floorbot/proc/do_eattile(obj/item/stack/tile/plasteel/T)
@@ -355,8 +349,8 @@
target = null
mode = BOT_IDLE
return
- if(amount + T.amount > 50)
- var/i = 50 - amount
+ if((amount + T.amount) > MAX_AMOUNT)
+ var/i = MAX_AMOUNT - amount
amount += i
T.amount -= i
else
@@ -370,7 +364,8 @@
if(!istype(M, /obj/item/stack/sheet/metal))
return
visible_message("[src] begins to create tiles.")
- mode = BOT_REPAIRING
+ mode = BOT_MAKE_TILE
+ update_icon(UPDATE_OVERLAYS)
addtimer(CALLBACK(src, PROC_REF(do_maketile), M), 2 SECONDS)
/mob/living/simple_animal/bot/floorbot/proc/do_maketile(obj/item/stack/sheet/metal/M)
@@ -387,13 +382,14 @@
qdel(M)
target = null
mode = BOT_IDLE
+ update_icon(UPDATE_OVERLAYS)
/mob/living/simple_animal/bot/floorbot/update_icon_state()
return
/mob/living/simple_animal/bot/floorbot/update_overlays()
. = ..()
- if(mode == BOT_REPAIRING)
+ if(mode == BOT_REPAIRING || mode == BOT_EAT_TILE || mode == BOT_MAKE_TILE)
. += "floorbot_work"
else
. += "floorbot_[on ? "on" : "off"]"
@@ -431,3 +427,11 @@
start_maketile(A)
else
..()
+
+
+#undef HULL_BREACH
+#undef FIX_TILE
+#undef AUTO_TILE
+#undef REPLACE_TILE
+#undef TILE_EMAG
+#undef MAX_AMOUNT
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index aceb6fda38f6..7328e2809bc3 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -122,7 +122,7 @@
/mob/living/simple_animal/bot/honkbot/emag_act(mob/user)
..()
- if(emagged == 2)
+ if(emagged)
if(user)
to_chat(user, "You short out [src]'s target assessment circuits. It gives out an evil laugh!!")
oldtarget_name = user.name
@@ -140,7 +140,7 @@
return
if(iscarbon(A))
var/mob/living/carbon/C = A
- if(emagged <= 1)
+ if(!emagged)
honk_attack(A)
else
if(!C.IsStunned() || arrest_type)
@@ -159,13 +159,13 @@
..()
/mob/living/simple_animal/bot/honkbot/proc/bike_horn() //use bike_horn
- if(emagged <= 1)
+ if(!emagged)
if(!spam_flag)
playsound(src, honksound, 50, TRUE, -1)
spam_flag = TRUE //prevent spam
sensor_blink()
addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntimehorn)
- else if(emagged == 2) //emagged honkbots will spam short and memorable sounds.
+ else if(emagged) //emagged honkbots will spam short and memorable sounds.
if(!spam_flag)
playsound(src, "honkbot_e", 50, 0)
spam_flag = TRUE // prevent spam
@@ -195,7 +195,7 @@
C.Weaken(10 SECONDS)
if(client) //prevent spam from players..
spam_flag = TRUE
- if(emagged <= 1) //HONK once, then leave
+ if(!emagged) //HONK once, then leave
threatlevel -= 6
target = oldtarget_name
else // you really don't want to hit an emagged honkbot
@@ -279,12 +279,12 @@
if((C.name == oldtarget_name) && (world.time < last_found + 100))
continue
- if(threatlevel <= 3 && emagged <= 1)
+ if(threatlevel <= 3 && !emagged)
if(C in view(4, src)) //keep the range short for patrolling
if(!spam_flag)
bike_horn()
else if(threatlevel >= 4)
- if(!spam_flag || emagged > 1)
+ if(!spam_flag || emagged)
target = C
oldtarget_name = C.name
bike_horn()
@@ -295,7 +295,7 @@
break
else
continue
- else if(emagged > 1)
+ else if(emagged)
bike_horn() //just spam the shit outta this
/mob/living/simple_animal/bot/honkbot/explode() //doesn't drop cardboard nor its assembly, since its a very frail material.
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index 03ca50c23d87..be4b74ab49e1 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -101,7 +101,7 @@
Radio.syndiekey = new /obj/item/encryptionkey/syndicate
/mob/living/simple_animal/bot/medbot/syndicate/emagged
- emagged = 2
+ emagged = TRUE
declare_crit = FALSE
drops_parts = FALSE
@@ -258,7 +258,7 @@
/mob/living/simple_animal/bot/medbot/emag_act(mob/user)
..()
- if(emagged == 2)
+ if(emagged)
declare_crit = FALSE
if(user)
to_chat(user, "You short out [src]'s reagent synthesis circuits.")
@@ -412,7 +412,7 @@
if(H.dna.species && H.dna.species.reagent_tag == PROCESS_SYN)
return FALSE
- if(emagged == 2 || hijacked) //Everyone needs our medicine. (Our medicine is toxins)
+ if(emagged || hijacked) //Everyone needs our medicine. (Our medicine is toxins)
return TRUE
if(syndicate_aligned && !("syndicate" in C.faction))
@@ -461,7 +461,7 @@
var/reagent_id
var/beaker_injection //If and what kind of beaker reagent needs to be injected
- if(emagged == 2 || hijacked) //Emagged! Time to poison everybody.
+ if(emagged || hijacked) //Emagged! Time to poison everybody.
reagent_id = "pancuronium"
else
beaker_injection = assess_beaker_injection(C)
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index c43d3001e870..6a0b1072cd5e 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -149,8 +149,8 @@
update_icon()
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user)
- if(emagged < 1)
- emagged = 1
+ if(!emagged)
+ emagged = TRUE
if(!open)
locked = !locked
to_chat(user, "You [locked ? "lock" : "unlock"] [src]'s controls!")
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index e0a775dade63..8b0e88bc4327 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -30,7 +30,7 @@
var/last_found //There's a delay
var/declare_arrests = TRUE //When making an arrest, should it notify everyone on the security channel?
var/idcheck = FALSE //If true, arrest people with no IDs
- var/weaponscheck = FALSE //If true, arrest people for weapons if they lack access
+ var/weapons_check = FALSE //If true, arrest people for weapons if they lack access
var/check_records = TRUE //Does it check security records?
var/arrest_type = FALSE //If true, don't handcuff
var/harmbaton = FALSE //If true, beat instead of stun
@@ -43,7 +43,7 @@
name = "Officer Beepsky"
desc = "It's Officer Beepsky! Powered by a potato and a shot of whiskey."
idcheck = FALSE
- weaponscheck = FALSE
+ weapons_check = FALSE
auto_patrol = TRUE
/mob/living/simple_animal/bot/secbot/beepsky/explode()
@@ -63,7 +63,7 @@
name = "Prison Ofitser"
desc = "It's Prison Ofitser! Powered by the tears and sweat of prisoners."
idcheck = FALSE
- weaponscheck = TRUE
+ weapons_check = TRUE
auto_patrol = TRUE
/mob/living/simple_animal/bot/secbot/buzzsky
@@ -74,7 +74,7 @@
declare_arrests = FALSE
arrest_type = TRUE
harmbaton = TRUE
- emagged = 2
+ emagged = TRUE
/mob/living/simple_animal/bot/secbot/armsky
name = "Sergeant-at-Armsky"
@@ -82,7 +82,7 @@
maxHealth = 100
idcheck = TRUE
arrest_type = TRUE
- weaponscheck = TRUE
+ weapons_check = TRUE
/mob/living/simple_animal/bot/secbot/Initialize(mapload)
. = ..()
@@ -126,7 +126,7 @@
/mob/living/simple_animal/bot/secbot/ui_data(mob/user)
var/list/data = ..()
data["check_id"] = idcheck
- data["check_weapons"] = weaponscheck
+ data["check_weapons"] = weapons_check
data["check_warrant"] = check_records
data["arrest_mode"] = arrest_type // detain or arrest
data["arrest_declare"] = declare_arrests // announce arrests on radio
@@ -154,7 +154,7 @@
if("disableremote")
remote_disabled = !remote_disabled
if("authweapon")
- weaponscheck = !weaponscheck
+ weapons_check = !weapons_check
if("authid")
idcheck = !idcheck
if("authwarrant")
@@ -187,7 +187,7 @@
/mob/living/simple_animal/bot/secbot/emag_act(mob/user)
..()
- if(emagged == 2)
+ if(emagged)
if(user)
to_chat(user, "You short out [src]'s target assessment circuits.")
oldtarget_name = user.name
diff --git a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
index f5835de4d24d..8e35dbc16550 100644
--- a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
+++ b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
@@ -12,7 +12,7 @@
idcheck = TRUE
arrest_type = TRUE
auto_patrol = TRUE
- emagged = 2
+ emagged = TRUE
faction = list("syndicate")
shoot_sound = 'sound/weapons/wave.ogg'
anchored = TRUE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index adf774b46fdc..ebcfa14a2992 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1326,6 +1326,9 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
/mob/proc/IsVocal()
return TRUE
+/mob/proc/cannot_speak_loudly()
+ return FALSE
+
/mob/proc/get_access()
return list() //must return list or IGNORE_ACCESS
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 29b384684c6f..a7f15f9a3fa5 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -550,12 +550,17 @@
check_prefs_are_sane()
var/mob/living/carbon/human/new_character = new(loc)
new_character.lastarea = get_area(loc)
-
- if(SSticker.random_players)
- client.prefs.active_character.randomise()
- client.prefs.active_character.real_name = random_name(client.prefs.active_character.gender)
client.prefs.active_character.copy_to(new_character)
-
+ if(SSticker.random_players)
+ var/mob/living/carbon/human/H = new_character
+ scramble(1, H, 100)
+ H.real_name = random_name(H.gender, H.dna.species.name)
+ H.sync_organ_dna(assimilate = 1)
+ H.update_body()
+ H.reset_hair()
+ H.reset_markings()
+ H.dna.ResetUIFrom(H)
+ H.flavor_text = ""
stop_sound_channel(CHANNEL_LOBBYMUSIC)
diff --git a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm
index c819924edc63..ab50e39b0265 100644
--- a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm
+++ b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm
@@ -102,3 +102,105 @@
name = "Big Leaf"
icon_state = "bigleaf"
glasses_over = FALSE
+
+//Sprites from Aurora Below
+/datum/sprite_accessory/hair/diona/diona_treebeard
+ name = "Treebeard"
+ icon_state = "treebeard"
+
+/datum/sprite_accessory/hair/diona/monoeye
+ name = "Monoeye"
+ icon_state = "monoeye"
+
+/datum/sprite_accessory/hair/diona/trioptics
+ name = "Trioptics"
+ icon_state = "trioptics"
+
+/datum/sprite_accessory/hair/diona/lopsided
+ name = "Lopsided"
+ icon_state = "lopsided"
+
+/datum/sprite_accessory/hair/diona/helmethead
+ name = "Helmet Head"
+ icon_state = "helmethead"
+
+/datum/sprite_accessory/hair/diona/eyestalk
+ name = "Eyestalk"
+ icon_state = "eyestalk"
+
+/datum/sprite_accessory/hair/diona/bugeyes
+ name = "Bug Eyes"
+ icon_state = "bugeyes"
+
+/datum/sprite_accessory/hair/diona/humaneyes
+ name = "Human Eyes"
+ icon_state = "humaneyes"
+
+/datum/sprite_accessory/hair/diona/skrelleyes
+ name = "Skrell Eyes"
+ icon_state = "skrelleyes"
+
+/datum/sprite_accessory/hair/diona/smallhorns
+ name = "Small Horns"
+ icon_state = "smallhorns"
+
+/datum/sprite_accessory/hair/diona/horns
+ name = "Horns"
+ icon_state = "horns"
+
+/datum/sprite_accessory/hair/diona/headtails
+ name = "Head Tails"
+ icon_state = "headtails"
+
+/datum/sprite_accessory/hair/diona/headtails2
+ name = "Head Tails 2"
+ icon_state = "headtails2"
+
+/datum/sprite_accessory/hair/diona/eyebrow
+ name = "Eye Brow"
+ icon_state = "eyebrow"
+
+/datum/sprite_accessory/hair/diona/bullhorn
+ name = "Bull Horn"
+ icon_state = "bullhorn"
+
+/datum/sprite_accessory/hair/diona/blinkinghelmethead
+ name = "Blinking Helmet Head"
+ icon_state = "blinkinghelmethead"
+
+/datum/sprite_accessory/hair/diona/periscope
+ name = "Periscope"
+ icon_state = "periscope"
+
+/datum/sprite_accessory/hair/diona/glorp
+ name = "Glorp"
+ icon_state = "glorp"
+
+/datum/sprite_accessory/hair/diona/mellowcap
+ name = "Mellow Cap"
+ icon_state = "mellowcap"
+ glasses_over = FALSE
+
+/datum/sprite_accessory/hair/diona/redcap
+ name = "Red Cap"
+ icon_state = "redcap"
+ glasses_over = FALSE
+
+/datum/sprite_accessory/hair/diona/purplecap
+ name = "Purple Cap"
+ icon_state = "purplecap"
+ glasses_over = FALSE
+
+/datum/sprite_accessory/hair/diona/spanishmoss
+ name = "Spanish Moss"
+ icon_state = "spanishmoss"
+ glasses_over = FALSE
+
+/datum/sprite_accessory/hair/diona/thorns
+ name = "Thorns"
+ icon_state = "thorns"
+
+/datum/sprite_accessory/hair/diona/stump
+ name = "Stump"
+ icon_state = "stump"
+//Sprites from Aurora Above
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 44c94fc76bb6..83def2f64a9a 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -132,8 +132,8 @@
add_attack_logs(user, M, "Stabbed with (sleepy) [src]. [transfered]u of reagents transfered from pen containing [english_list(contained)].")
for(var/datum/reagent/R as anything in reagents.reagent_list)
if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
- return TRUE
- reagents.reaction(M, REAGENT_INGEST, 0.1)
+ continue
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 264ac7f9de8f..b81b6a5690ec 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -264,12 +264,12 @@
if(blocked != INFINITY)
if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
..()
+
for(var/datum/reagent/R as anything in reagents.reagent_list)
if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
- reagents.trans_to(M, reagents.total_volume)
- return TRUE
+ continue
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
reagents.trans_to(M, reagents.total_volume)
- reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
else
blocked = INFINITY
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 72996bda5f3a..32ac08eaf6d3 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -53,8 +53,8 @@
add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
for(var/datum/reagent/R as anything in reagents.reagent_list)
if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
- return TRUE
- reagents.reaction(M, REAGENT_INGEST, 0.1)
+ continue
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user)
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 47b6a68617c0..5975eb7f1fb9 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -98,7 +98,7 @@ GLOBAL_LIST_EMPTY(ert_request_messages)
A.close()
var/list/ert_species_prefs = list()
for(var/mob/M in GLOB.response_team_members)
- ert_species_prefs.Add(input_async(M, "Please select a species (10 seconds):", list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Random")))
+ ert_species_prefs.Add(input_async(M, "Please select a species (10 seconds):", list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey", "Random")))
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(get_ert_role_prefs), GLOB.response_team_members, ert_gender_prefs, ert_species_prefs), 10 SECONDS)
/proc/get_ert_role_prefs(list/response_team_members, list/ert_gender_prefs, list/ert_species_prefs) // Why the FUCK is this variable the EXACT SAME as the global one
@@ -164,7 +164,7 @@ GLOBAL_LIST_EMPTY(ert_request_messages)
if(!new_species)
new_species = "Human"
if(new_species == "Random")
- new_species = pick("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian")
+ new_species = pick("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey")
var/datum/species/S = GLOB.all_species[new_species]
var/species = S.type
M.set_species(species, TRUE)
@@ -172,10 +172,10 @@ GLOBAL_LIST_EMPTY(ert_request_messages)
M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ERT.
M.overeatduration = 0
var/obj/item/organ/external/head/head_organ = M.get_organ("head")
- var/eye_c = pick("#000000", "#8B4513", "#1E90FF") // Black, brown, blue
+ var/eye_c = pick("#000000", "#8B4513", "#1E90FF", "#8c00ff", "#a80c0c", "#2fdb63") // Black, brown, blue, purple, red, green
var/skin_tone = rand(-120, 20) // A range of skin colors
- switch(new_species) //Diona not included as they don't use the hair colours
+ switch(new_species) //Diona not included as they don't use the hair colours, kidan use accessory, drask are skin tone Grey not included as they are BALD
if("Human", "Tajaran", "Vulpkanin", "Nian")
var/hair_c_htvn = pick("#8B4513", "#000000", "#FF4500", "#FFD700", "#d4d1bf") // Brown, black, red, blonde, grey
head_organ.facial_colour = hair_c_htvn
@@ -195,8 +195,10 @@ GLOBAL_LIST_EMPTY(ert_request_messages)
else
M.skin_colour = pick(su) //Pick a diffrent colour for body.
+
M.change_eye_color(eye_c)
M.s_tone = skin_tone
+ head_organ.headacc_colour = pick("#1f138b", "#272525", "#07a035", "#8c00ff", "#a80c0c")
head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name)
head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name)
diff --git a/code/modules/supply/supply_packs/pack_miscellaneous.dm b/code/modules/supply/supply_packs/pack_miscellaneous.dm
index 1c54a3b1f858..bce9c7952cba 100644
--- a/code/modules/supply/supply_packs/pack_miscellaneous.dm
+++ b/code/modules/supply/supply_packs/pack_miscellaneous.dm
@@ -192,6 +192,15 @@
cost = 100
containername = "replacement lights crate"
+/datum/supply_packs/misc/janicart
+ name = "Janicart Crate"
+ contains = list(/obj/vehicle/janicart,
+ /obj/item/key/janitor)
+ cost = 500
+ containertype = /obj/structure/largecrate
+ containername = "Janicart. Caution while driving is advised."
+ department_restrictions = list(DEPARTMENT_SERVICE)
+
/datum/supply_packs/misc/noslipfloor
name = "High-traction Floor Tiles"
contains = list(/obj/item/stack/tile/noslip/loaded)
diff --git a/code/modules/tgs/core/README.md b/code/modules/tgs/core/README.md
index aa3c7a9c9db6..bb55caba2c4a 100644
--- a/code/modules/tgs/core/README.md
+++ b/code/modules/tgs/core/README.md
@@ -3,6 +3,6 @@
This folder contains all DMAPI code not directly involved in an API.
- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
-- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
-- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
-- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
\ No newline at end of file
+- [tgs_core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
+- [tgs_datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
+- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
diff --git a/code/modules/tgs/core/_definitions.dm b/code/modules/tgs/core/_definitions.dm
index ebf6d17c2a07..fd98034eb716 100644
--- a/code/modules/tgs/core/_definitions.dm
+++ b/code/modules/tgs/core/_definitions.dm
@@ -1,2 +1,10 @@
+#if DM_VERSION < 510
+#error The TGS DMAPI does not support BYOND versions < 510!
+#endif
+
#define TGS_UNIMPLEMENTED "___unimplemented"
#define TGS_VERSION_PARAMETER "server_service_version"
+
+#ifndef TGS_DEBUG_LOG
+#define TGS_DEBUG_LOG(message)
+#endif
diff --git a/code/modules/tgs/core/tgs_core.dm b/code/modules/tgs/core/tgs_core.dm
index 41a047339452..8be96f27404a 100644
--- a/code/modules/tgs/core/tgs_core.dm
+++ b/code/modules/tgs/core/tgs_core.dm
@@ -42,11 +42,11 @@
var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_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.")
+ TGS_ERROR_LOG("Detected unknown Interop 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.")
+ TGS_ERROR_LOG("Found unsupported Interop 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]")
@@ -107,6 +107,13 @@
if(api)
return api.ApiVersion()
+/world/TgsEngine()
+#ifdef OPENDREAM
+ return TGS_ENGINE_TYPE_OPENDREAM
+#else
+ return TGS_ENGINE_TYPE_BYOND
+#endif
+
/world/TgsInstanceName()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
@@ -153,4 +160,9 @@
/world/TgsSecurityLevel()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
- api.SecurityLevel()
+ return api.SecurityLevel()
+
+/world/TgsVisibility()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ return api.Visibility()
diff --git a/code/modules/tgs/core/tgs_datum.dm b/code/modules/tgs/core/tgs_datum.dm
index 68b0330fe860..07ce3b684584 100644
--- a/code/modules/tgs/core/tgs_datum.dm
+++ b/code/modules/tgs/core/tgs_datum.dm
@@ -11,6 +11,15 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
src.event_handler = event_handler
src.version = version
+/datum/tgs_api/proc/TerminateWorld()
+ while(TRUE)
+ TGS_DEBUG_LOG("About to terminate world. Tick: [world.time], sleep_offline: [world.sleep_offline]")
+ world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866
+ del(world)
+ world.sleep_offline = FALSE // just in case, this is BYOND after all...
+ sleep(1)
+ TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]")
+
/datum/tgs_api/latest
parent_type = /datum/tgs_api/v5
@@ -57,3 +66,6 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
/datum/tgs_api/proc/SecurityLevel()
return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/Visibility()
+ return TGS_UNIMPLEMENTED
diff --git a/code/modules/tgs/v3210/README.md b/code/modules/tgs/v3210/README.md
index 01ef44767ad7..a8073306e10d 100644
--- a/code/modules/tgs/v3210/README.md
+++ b/code/modules/tgs/v3210/README.md
@@ -2,5 +2,5 @@
This DMAPI implements bridge using file output which TGS monitors for.
-- [api.dm](./v3_api.dm) contains the bulk of the API code.
-- [commands.dm](./v3_commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
+- [v3_api.dm](./v3_api.dm) contains the bulk of the API code.
+- [v3_commands.dm](./v3_commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
diff --git a/code/modules/tgs/v3210/v3_api.dm b/code/modules/tgs/v3210/v3_api.dm
index c62118001f4b..ac5e8e4cf6d9 100644
--- a/code/modules/tgs/v3210/v3_api.dm
+++ b/code/modules/tgs/v3210/v3_api.dm
@@ -99,7 +99,11 @@
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
return
- CALL_EXT(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
+ #if DM_VERSION >= 515
+ call_ext(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
+ #else
+ call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
+ #endif
return TRUE
/datum/tgs_api/v3210/OnTopic(T)
@@ -175,7 +179,7 @@
/datum/tgs_api/v3210/Revision()
if(!warned_revison)
var/datum/tgs_version/api_version = ApiVersion()
- TGS_ERROR_LOG("Use of TgsRevision on [api_version.deprefixed_parameter] origin_commit only points to master!")
+ TGS_WARNING_LOG("Use of TgsRevision on [api_version.deprefixed_parameter] origin_commit only points to master!")
warned_revison = TRUE
var/datum/tgs_revision_information/ri = new
ri.commit = commit
@@ -189,16 +193,19 @@
/datum/tgs_api/v3210/ChatChannelInfo()
return list() // :omegalul:
-/datum/tgs_api/v3210/ChatBroadcast(message, list/channels)
+/datum/tgs_api/v3210/ChatBroadcast(datum/tgs_message_content/message, list/channels)
if(channels)
return TGS_UNIMPLEMENTED
+ message = UpgradeDeprecatedChatMessage(message)
ChatTargetedBroadcast(message, TRUE)
ChatTargetedBroadcast(message, FALSE)
-/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/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only)
+ message = UpgradeDeprecatedChatMessage(message)
+ ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message.text]")
/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user)
+ UpgradeDeprecatedChatMessage(message)
return TGS_UNIMPLEMENTED
/datum/tgs_api/v3210/SecurityLevel()
diff --git a/code/modules/tgs/v3210/v3_commands.dm b/code/modules/tgs/v3210/v3_commands.dm
index 4ccfc1a8a60b..e65c816320dc 100644
--- a/code/modules/tgs/v3210/v3_commands.dm
+++ b/code/modules/tgs/v3210/v3_commands.dm
@@ -10,9 +10,12 @@
var/warned_about_the_dangers_of_robutussin = !warnings_only
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
if(!warned_about_the_dangers_of_robutussin)
- TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
+ TGS_WARNING_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
warned_about_the_dangers_of_robutussin = TRUE
var/datum/tgs_chat_command/stc = I
+ if(stc.ignore_type == I)
+ continue
+
var/command_name = initial(stc.name)
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
if(warnings_only && !warned_command_names[command_name])
@@ -44,9 +47,12 @@
user.friendly_name = sender
// Discord hack, fix the mention if it's only numbers (fuck you IRC trolls)
- var/regex/discord_id_regex = regex(@"^[0-9]+$")
+ var/regex/discord_id_regex = regex("^\[0-9\]+$")
if(findtext(sender, discord_id_regex))
sender = "<@[sender]>"
user.mention = sender
- return stc.Run(user, params) || TRUE
+ var/datum/tgs_message_content/result = stc.Run(user, params)
+ result = UpgradeDeprecatedCommandResponse(result, command)
+
+ return result ? result.text : TRUE
diff --git a/code/modules/tgs/v4/README.md b/code/modules/tgs/v4/README.md
index c14d7d38872e..049d4fb0f6de 100644
--- a/code/modules/tgs/v4/README.md
+++ b/code/modules/tgs/v4/README.md
@@ -2,5 +2,5 @@
This DMAPI implements bridge requests using file output which TGS monitors for. It has a safe mode restriction.
-- [api.dm](./v4_api.dm) contains the bulk of the API code.
-- [commands.dm](./v4_commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
+- [v4_api.dm](./v4_api.dm) contains the bulk of the API code.
+- [v4_commands.dm](./v4_commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
diff --git a/code/modules/tgs/v4/v4_api.dm b/code/modules/tgs/v4/v4_api.dm
index cce3b00bb268..3992299e4374 100644
--- a/code/modules/tgs/v4/v4_api.dm
+++ b/code/modules/tgs/v4/v4_api.dm
@@ -73,7 +73,7 @@
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)
+ TerminateWorld()
security_level = cached_json["securityLevel"]
chat_channels_json_path = cached_json["chatChannelsJson"]
@@ -188,7 +188,7 @@
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)
+ TerminateWorld()
//request a new port
export_lock = FALSE
@@ -196,16 +196,16 @@
if(!new_port_json)
TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
- del(world)
+ TerminateWorld()
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)
+ TerminateWorld()
if(new_port != world.port && !world.OpenPort(new_port))
TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
- del(world)
+ TerminateWorld()
requesting_new_port = FALSE
while(export_lock)
diff --git a/code/modules/tgs/v4/v4_commands.dm b/code/modules/tgs/v4/v4_commands.dm
index d6d3d718d471..25dd6740e3af 100644
--- a/code/modules/tgs/v4/v4_commands.dm
+++ b/code/modules/tgs/v4/v4_commands.dm
@@ -40,5 +40,5 @@
var/datum/tgs_message_content/result = sc.Run(u, params)
result = UpgradeDeprecatedCommandResponse(result, command)
- return result?.text
+ return result ? result.text : TRUE
return "Unknown command: [command]!"
diff --git a/code/modules/tgs/v5/README.md b/code/modules/tgs/v5/README.md
index dd8da3bea439..8a782fb15510 100644
--- a/code/modules/tgs/v5/README.md
+++ b/code/modules/tgs/v5/README.md
@@ -10,4 +10,4 @@ This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no
- [v5_commands.dm](./v5_commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
- [v5_serializers.dm](./v5_serializers.dm) contains function to help convert interop `/datum`s into a JSON encodable `list()` format.
- [v5_topic.dm](./v5_topic.dm) contains functions related to processing topic requests.
-- [v5_undefs.dm](./v5_undefs.dm) Undoes the work of `_v5_defines.dm`.
+- [v5_undefs.dm](./v5_undefs.dm) Undoes the work of `_defines.dm`.
diff --git a/code/modules/tgs/v5/_v5_defines.dm b/code/modules/tgs/v5/_v5_defines.dm
index c7213cc24699..1c7d67d20cdf 100644
--- a/code/modules/tgs/v5/_v5_defines.dm
+++ b/code/modules/tgs/v5/_v5_defines.dm
@@ -5,10 +5,9 @@
#define DMAPI5_TOPIC_DATA "tgs_data"
#define DMAPI5_BRIDGE_REQUEST_LIMIT 8198
-#define DMAPI5_TOPIC_REQUEST_LIMIT 65529
-#define DMAPI5_TOPIC_RESPONSE_LIMIT 65528
+#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
+#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
-#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
@@ -18,6 +17,7 @@
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
+#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort"
#define DMAPI5_CHUNK "chunk"
#define DMAPI5_CHUNK_PAYLOAD "payload"
@@ -48,6 +48,7 @@
#define DMAPI5_RUNTIME_INFORMATION_REVISION "revision"
#define DMAPI5_RUNTIME_INFORMATION_TEST_MERGES "testMerges"
#define DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL "securityLevel"
+#define DMAPI5_RUNTIME_INFORMATION_VISIBILITY "visibility"
#define DMAPI5_CHAT_UPDATE_CHANNELS "channels"
@@ -79,6 +80,7 @@
#define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8
#define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9
#define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10
+#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11
#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
@@ -88,6 +90,7 @@
#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName"
#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate"
#define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion"
+#define DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE "broadcastMessage"
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse"
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage"
diff --git a/code/modules/tgs/v5/v5_api.dm b/code/modules/tgs/v5/v5_api.dm
index fb2c46cc41ce..6b4e3ff9bd1c 100644
--- a/code/modules/tgs/v5/v5_api.dm
+++ b/code/modules/tgs/v5/v5_api.dm
@@ -4,6 +4,7 @@
var/instance_name
var/security_level
+ var/visibility
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
@@ -16,24 +17,32 @@
var/list/chat_channels
var/initialized = FALSE
+ var/initial_bridge_request_received = FALSE
+ var/datum/tgs_version/interop_version
var/chunked_requests = 0
var/list/chunked_topics = list()
var/detached = FALSE
+/datum/tgs_api/v5/New()
+ . = ..()
+ interop_version = version
+ TGS_DEBUG_LOG("V5 API created: [json_encode(args)]")
+
/datum/tgs_api/v5/ApiVersion()
return new /datum/tgs_version(
#include "v5_interop_version.dm"
)
/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
+ TGS_DEBUG_LOG("OnWorldNew()")
server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
var/datum/tgs_version/api_version = ApiVersion()
- version = null
- var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
+ version = null // we want this to be the TGS version, not the interop version
+ var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort()))
if(!istype(bridge_response))
TGS_ERROR_LOG("Failed initial bridge request!")
return FALSE
@@ -45,10 +54,12 @@
if(runtime_information[DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY])
TGS_INFO_LOG("DMAPI validation, exiting...")
- del(world)
+ TerminateWorld()
- version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION])
+ initial_bridge_request_received = TRUE
+ version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) // reassigning this because it can change if TGS updates
security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL]
+ visibility = runtime_information[DMAPI5_RUNTIME_INFORMATION_VISIBILITY]
instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME]
var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION]
@@ -95,18 +106,36 @@
initialized = TRUE
return TRUE
+/datum/tgs_api/v5/proc/GetTopicPort()
+#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS)
+ return "[world.opendream_topic_port]"
+#else
+ return null
+#endif
+
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
- while(!version)
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
+ var/logged = FALSE
+ while(!initial_bridge_request_received)
+ if(!logged)
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
+ logged = TRUE
+
sleep(1)
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
+
/datum/tgs_api/v5/OnInitializationComplete()
Bridge(DMAPI5_BRIDGE_COMMAND_PRIME)
/datum/tgs_api/v5/OnTopic(T)
+ TGS_DEBUG_LOG("OnTopic()")
RequireInitialBridgeResponse()
+ TGS_DEBUG_LOG("OnTopic passed bridge request gate")
var/list/params = params2list(T)
var/json = params[DMAPI5_TOPIC_DATA]
if(!json)
+ TGS_DEBUG_LOG("No \"[DMAPI5_TOPIC_DATA]\" entry found, ignoring...")
return FALSE // continue to /world/Topic
if(!initialized)
@@ -156,7 +185,7 @@
TGS_WARNING_LOG("Received legacy string when a [/datum/tgs_message_content] was expected. Please audit all calls to TgsChatBroadcast, TgsChatTargetedBroadcast, and TgsChatPrivateMessage to ensure they use the new /datum.")
return new /datum/tgs_message_content(message)
-/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message, list/channels)
+/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message2, list/channels)
if(!length(channels))
channels = ChatChannelInfo()
@@ -165,45 +194,45 @@
var/datum/tgs_chat_channel/channel = I
ids += channel.id
- message = UpgradeDeprecatedChatMessage(message)
+ message2 = UpgradeDeprecatedChatMessage(message2)
if(!length(channels))
return
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
-/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only)
+/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, 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 = UpgradeDeprecatedChatMessage(message)
+ message2 = UpgradeDeprecatedChatMessage(message2)
if(!length(channels))
return
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
-/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user)
- message = UpgradeDeprecatedChatMessage(message)
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
+/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
+ message2 = UpgradeDeprecatedChatMessage(message2)
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
/datum/tgs_api/v5/ChatChannelInfo()
RequireInitialBridgeResponse()
@@ -211,6 +240,7 @@
return chat_channels.Copy()
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
+ TGS_DEBUG_LOG("DecodeChannels()")
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
if(istype(chat_channels_json))
chat_channels.Cut()
@@ -235,3 +265,7 @@
/datum/tgs_api/v5/SecurityLevel()
RequireInitialBridgeResponse()
return security_level
+
+/datum/tgs_api/v5/Visibility()
+ RequireInitialBridgeResponse()
+ return visibility
diff --git a/code/modules/tgs/v5/v5_bridge.dm b/code/modules/tgs/v5/v5_bridge.dm
index 37f58bcdf632..60cbcbfb7dff 100644
--- a/code/modules/tgs/v5/v5_bridge.dm
+++ b/code/modules/tgs/v5/v5_bridge.dm
@@ -48,7 +48,9 @@
var/json = CreateBridgeData(command, data, TRUE)
var/encoded_json = url_encode(json)
- var/url = "http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
+ var/api_prefix = interop_version.minor >= 7 ? "api/" : ""
+
+ var/url = "http://127.0.0.1:[server_port]/[api_prefix]Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
return url
/datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth)
@@ -81,11 +83,16 @@
TGS_ERROR_LOG("Failed bridge request: [bridge_request]")
return
- var/response_json = file2text(export_response["CONTENT"])
- if(!response_json)
+ var/content = export_response["CONTENT"]
+ if(!content)
TGS_ERROR_LOG("Failed bridge request, missing content!")
return
+ var/response_json = file2text(content)
+ if(!response_json)
+ TGS_ERROR_LOG("Failed bridge request, failed to load content!")
+ return
+
var/list/bridge_response = json_decode(response_json)
if(!bridge_response)
TGS_ERROR_LOG("Failed bridge request, bad json: [response_json]")
diff --git a/code/modules/tgs/v5/v5_commands.dm b/code/modules/tgs/v5/v5_commands.dm
index c8f538db5932..9557f8a08ed5 100644
--- a/code/modules/tgs/v5/v5_commands.dm
+++ b/code/modules/tgs/v5/v5_commands.dm
@@ -37,8 +37,8 @@
response = UpgradeDeprecatedCommandResponse(response, command)
var/list/topic_response = TopicResponse()
- topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response?.text
- topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response?._interop_serialize()
+ topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response ? response.text : null
+ topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response ? response._interop_serialize() : null
return topic_response
return TopicResponse("Unknown custom chat command: [command]!")
diff --git a/code/modules/tgs/v5/v5_interop_version.dm b/code/modules/tgs/v5/v5_interop_version.dm
index 5d3d491a7362..83420d130a74 100644
--- a/code/modules/tgs/v5/v5_interop_version.dm
+++ b/code/modules/tgs/v5/v5_interop_version.dm
@@ -1 +1 @@
-"5.6.1"
+"5.7.0"
diff --git a/code/modules/tgs/v5/v5_serializers.dm b/code/modules/tgs/v5/v5_serializers.dm
index 3a6fbe6be344..3a32848ad512 100644
--- a/code/modules/tgs/v5/v5_serializers.dm
+++ b/code/modules/tgs/v5/v5_serializers.dm
@@ -1,12 +1,12 @@
/datum/tgs_message_content/proc/_interop_serialize()
- return list("text" = text, "embed" = embed?._interop_serialize())
+ return list("text" = text, "embed" = embed ? embed._interop_serialize() : null)
/datum/tgs_chat_embed/proc/_interop_serialize()
CRASH("Base /proc/interop_serialize called on [type]!")
/datum/tgs_chat_embed/structure/_interop_serialize()
var/list/serialized_fields
- if(islist(fields))
+ if(istype(fields, /list))
serialized_fields = list()
for(var/datum/tgs_chat_embed/field/field as anything in fields)
serialized_fields += list(field._interop_serialize())
@@ -16,12 +16,12 @@
"url" = url,
"timestamp" = timestamp,
"colour" = colour,
- "image" = image?._interop_serialize(),
- "thumbnail" = thumbnail?._interop_serialize(),
- "video" = video?._interop_serialize(),
- "footer" = footer?._interop_serialize(),
- "provider" = provider?._interop_serialize(),
- "author" = author?._interop_serialize(),
+ "image" = src.image ? src.image._interop_serialize() : null,
+ "thumbnail" = thumbnail ? thumbnail._interop_serialize() : null,
+ "video" = video ? video._interop_serialize() : null,
+ "footer" = footer ? footer._interop_serialize() : null,
+ "provider" = provider ? provider._interop_serialize() : null,
+ "author" = author ? author._interop_serialize() : null,
"fields" = serialized_fields
)
diff --git a/code/modules/tgs/v5/v5_topic.dm b/code/modules/tgs/v5/v5_topic.dm
index 68d0db27f36a..309044a145dc 100644
--- a/code/modules/tgs/v5/v5_topic.dm
+++ b/code/modules/tgs/v5/v5_topic.dm
@@ -5,6 +5,7 @@
return response
/datum/tgs_api/v5/proc/ProcessTopicJson(json, check_access_identifier)
+ TGS_DEBUG_LOG("ProcessTopicJson(..., [check_access_identifier])")
var/list/result = ProcessRawTopic(json, check_access_identifier)
if(!result)
result = TopicResponse("Runtime error!")
@@ -25,16 +26,20 @@
return response_json
/datum/tgs_api/v5/proc/ProcessRawTopic(json, check_access_identifier)
+ TGS_DEBUG_LOG("ProcessRawTopic(..., [check_access_identifier])")
var/list/topic_parameters = json_decode(json)
if(!topic_parameters)
+ TGS_DEBUG_LOG("ProcessRawTopic: json_decode failed")
return TopicResponse("Invalid topic parameters json: [json]!");
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
if(check_access_identifier && their_sCK != access_identifier)
- return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER]!")
+ TGS_DEBUG_LOG("ProcessRawTopic: access identifier check failed")
+ return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] or it does not match!")
var/command = topic_parameters[DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]
if(!isnum(command))
+ TGS_DEBUG_LOG("ProcessRawTopic: command type check failed")
return TopicResponse("Failed to decode [DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]!")
return ProcessTopicCommand(command, topic_parameters)
@@ -43,6 +48,7 @@
return "response[payload_id]"
/datum/tgs_api/v5/proc/ProcessTopicCommand(command, list/topic_parameters)
+ TGS_DEBUG_LOG("ProcessTopicCommand([command], ...)")
switch(command)
if(DMAPI5_TOPIC_COMMAND_CHAT_COMMAND)
@@ -55,7 +61,6 @@
return result
if(DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION)
- intercepted_message_queue = list()
var/list/event_notification = topic_parameters[DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]
if(!istype(event_notification))
return TopicResponse("Invalid [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!")
@@ -66,28 +71,30 @@
var/list/event_parameters = event_notification[DMAPI5_EVENT_NOTIFICATION_PARAMETERS]
if(event_parameters && !istype(event_parameters))
- return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
+ . = TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
+ else
+ var/list/response = TopicResponse()
+ . = response
+ if(event_handler != null)
+ var/list/event_call = list(event_type)
+ if(event_parameters)
+ event_call += event_parameters
+
+ intercepted_message_queue = list()
+ event_handler.HandleEvent(arglist(event_call))
+ response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
+ intercepted_message_queue = null
- var/list/event_call = list(event_type)
if(event_type == TGS_EVENT_WATCHDOG_DETACH)
detached = TRUE
chat_channels.Cut() // https://github.com/tgstation/tgstation-server/issues/1490
- if(event_parameters)
- event_call += event_parameters
-
- if(event_handler != null)
- event_handler.HandleEvent(arglist(event_call))
-
- var/list/response = TopicResponse()
- response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
- intercepted_message_queue = null
- return response
+ return
if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
if(!isnum(new_port) || !(new_port > 0))
- return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
if(event_handler != null)
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
@@ -122,8 +129,10 @@
return TopicResponse()
if(DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE)
+ TGS_DEBUG_LOG("ProcessTopicCommand: It's a chat update")
var/list/chat_update_json = topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]
if(!istype(chat_update_json))
+ TGS_DEBUG_LOG("ProcessTopicCommand: failed \"[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]\" check")
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]!")
DecodeChannels(chat_update_json)
@@ -132,13 +141,13 @@
if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE)
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
if(!isnum(new_port) || !(new_port > 0))
- return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
server_port = new_port
return TopicResponse()
if(DMAPI5_TOPIC_COMMAND_HEALTHCHECK)
- if(event_handler?.receive_health_checks)
+ if(event_handler && event_handler.receive_health_checks)
event_handler.HandleEvent(TGS_EVENT_HEALTH_CHECK)
return TopicResponse()
@@ -148,7 +157,7 @@
var/error_message = null
if(new_port != null)
if(!isnum(new_port) || !(new_port > 0))
- error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]"
+ error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]"
else
server_port = new_port
@@ -156,7 +165,7 @@
if(!istext(new_version_string))
if(error_message != null)
error_message += ", "
- error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]"
+ error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]"
else
var/datum/tgs_version/new_version = new(new_version_string)
if(event_handler)
@@ -166,6 +175,7 @@
var/list/reattach_response = TopicResponse(error_message)
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
+ reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
return reattach_response
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
@@ -258,4 +268,16 @@
return chunk_to_send
+ if(DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST)
+ var/message = topic_parameters[DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]
+ if(!istext(message))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]")
+
+ TGS_WORLD_ANNOUNCE(message)
+ return TopicResponse()
+
return TopicResponse("Unknown command: [command]")
+
+/datum/tgs_api/v5/proc/WorldBroadcast(message)
+ set waitfor = FALSE
+ TGS_WORLD_ANNOUNCE(message)
diff --git a/code/modules/tgs/v5/v5_undefs.dm b/code/modules/tgs/v5/v5_undefs.dm
index c679737dfc49..d531d4b7b9dd 100644
--- a/code/modules/tgs/v5/v5_undefs.dm
+++ b/code/modules/tgs/v5/v5_undefs.dm
@@ -8,7 +8,6 @@
#undef DMAPI5_TOPIC_REQUEST_LIMIT
#undef DMAPI5_TOPIC_RESPONSE_LIMIT
-#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
#undef DMAPI5_BRIDGE_COMMAND_STARTUP
#undef DMAPI5_BRIDGE_COMMAND_PRIME
#undef DMAPI5_BRIDGE_COMMAND_REBOOT
@@ -18,6 +17,7 @@
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
+#undef DMAPI5_PARAMETER_TOPIC_PORT
#undef DMAPI5_CHUNK
#undef DMAPI5_CHUNK_PAYLOAD
@@ -48,6 +48,7 @@
#undef DMAPI5_RUNTIME_INFORMATION_REVISION
#undef DMAPI5_RUNTIME_INFORMATION_TEST_MERGES
#undef DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL
+#undef DMAPI5_RUNTIME_INFORMATION_VISIBILITY
#undef DMAPI5_CHAT_UPDATE_CHANNELS
@@ -77,6 +78,9 @@
#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE
#undef DMAPI5_TOPIC_COMMAND_HEALTHCHECK
#undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH
+#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK
+#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK
+#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST
#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
@@ -86,6 +90,7 @@
#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME
#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE
#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION
+#undef DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE
diff --git a/icons/effects/species.dmi b/icons/effects/species.dmi
index 7bac1bdbe48d..b3a2753e6b98 100644
Binary files a/icons/effects/species.dmi and b/icons/effects/species.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index e9fb5b582b2d..3eea2f356ae1 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/clothing/species/drask/under/civilian.dmi b/icons/mob/clothing/species/drask/under/civilian.dmi
index 53ce570efc56..34f04fdf40de 100644
Binary files a/icons/mob/clothing/species/drask/under/civilian.dmi and b/icons/mob/clothing/species/drask/under/civilian.dmi differ
diff --git a/icons/mob/clothing/species/grey/back.dmi b/icons/mob/clothing/species/grey/back.dmi
index 88be281f984d..e426449c4db4 100644
Binary files a/icons/mob/clothing/species/grey/back.dmi and b/icons/mob/clothing/species/grey/back.dmi differ
diff --git a/icons/mob/clothing/species/grey/under/civilian.dmi b/icons/mob/clothing/species/grey/under/civilian.dmi
index aef84913923f..a09f69cdd884 100644
Binary files a/icons/mob/clothing/species/grey/under/civilian.dmi and b/icons/mob/clothing/species/grey/under/civilian.dmi differ
diff --git a/icons/mob/clothing/species/kidan/under/civilian.dmi b/icons/mob/clothing/species/kidan/under/civilian.dmi
index 9c8d974d3361..c655ec2f6299 100644
Binary files a/icons/mob/clothing/species/kidan/under/civilian.dmi and b/icons/mob/clothing/species/kidan/under/civilian.dmi differ
diff --git a/icons/mob/clothing/species/vox/back.dmi b/icons/mob/clothing/species/vox/back.dmi
index 25b86be5931b..95800a6c6f8f 100644
Binary files a/icons/mob/clothing/species/vox/back.dmi and b/icons/mob/clothing/species/vox/back.dmi differ
diff --git a/icons/mob/clothing/species/vox/under/civilian.dmi b/icons/mob/clothing/species/vox/under/civilian.dmi
index 63f65aa8e3c0..17c4e4a39971 100644
Binary files a/icons/mob/clothing/species/vox/under/civilian.dmi and b/icons/mob/clothing/species/vox/under/civilian.dmi differ
diff --git a/icons/mob/clothing/under/civilian.dmi b/icons/mob/clothing/under/civilian.dmi
index 3811856e8a7a..c5a920488695 100644
Binary files a/icons/mob/clothing/under/civilian.dmi and b/icons/mob/clothing/under/civilian.dmi differ
diff --git a/icons/mob/human_races/vox/r_vox.dmi b/icons/mob/human_races/vox/r_vox.dmi
index 247e52578521..2286e060a9af 100644
Binary files a/icons/mob/human_races/vox/r_vox.dmi and b/icons/mob/human_races/vox/r_vox.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxazu.dmi b/icons/mob/human_races/vox/r_voxazu.dmi
index 91c56c1f8fe0..593b6b9b6d2e 100644
Binary files a/icons/mob/human_races/vox/r_voxazu.dmi and b/icons/mob/human_races/vox/r_voxazu.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxbrn.dmi b/icons/mob/human_races/vox/r_voxbrn.dmi
index 22ca13cee71a..8a0e391a7499 100644
Binary files a/icons/mob/human_races/vox/r_voxbrn.dmi and b/icons/mob/human_races/vox/r_voxbrn.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxcrim.dmi b/icons/mob/human_races/vox/r_voxcrim.dmi
new file mode 100644
index 000000000000..56e99a28fb41
Binary files /dev/null and b/icons/mob/human_races/vox/r_voxcrim.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxdgrn.dmi b/icons/mob/human_races/vox/r_voxdgrn.dmi
index 1e0fdd118777..6c3239a100da 100644
Binary files a/icons/mob/human_races/vox/r_voxdgrn.dmi and b/icons/mob/human_races/vox/r_voxdgrn.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxemrl.dmi b/icons/mob/human_races/vox/r_voxemrl.dmi
index 4534a5aa6c5d..0a5515192931 100644
Binary files a/icons/mob/human_races/vox/r_voxemrl.dmi and b/icons/mob/human_races/vox/r_voxemrl.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxgry.dmi b/icons/mob/human_races/vox/r_voxgry.dmi
index 08a1f00088e1..df825cc399d6 100644
Binary files a/icons/mob/human_races/vox/r_voxgry.dmi and b/icons/mob/human_races/vox/r_voxgry.dmi differ
diff --git a/icons/mob/human_races/vox/r_voxpurp.dmi b/icons/mob/human_races/vox/r_voxpurp.dmi
new file mode 100644
index 000000000000..d6c18c755174
Binary files /dev/null and b/icons/mob/human_races/vox/r_voxpurp.dmi differ
diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi
index 8f9f52bd6213..91ff13ceb36e 100644
Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ
diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi
index b436595e7311..1980c4c38d1b 100644
Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ
diff --git a/icons/mob/sprite_accessories/diona/diona_hair.dmi b/icons/mob/sprite_accessories/diona/diona_hair.dmi
index faae86d59236..b11c32755d7f 100644
Binary files a/icons/mob/sprite_accessories/diona/diona_hair.dmi and b/icons/mob/sprite_accessories/diona/diona_hair.dmi differ
diff --git a/icons/obj/clothing/under/civilian.dmi b/icons/obj/clothing/under/civilian.dmi
index 774f95bf8684..c6879e99fb2f 100644
Binary files a/icons/obj/clothing/under/civilian.dmi and b/icons/obj/clothing/under/civilian.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index 768f5f64ed6f..b5805bbd6ae5 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/prof.dll b/prof.dll
index 33a6da229474..c0bc34532e4c 100644
Binary files a/prof.dll and b/prof.dll differ