diff --git a/SQL/mentor.sql b/SQL/mentor.sql
index c5fb7f0d83..45d0829d88 100644
--- a/SQL/mentor.sql
+++ b/SQL/mentor.sql
@@ -5,10 +5,10 @@ CREATE TABLE `mentor_memo` (
`last_editor` varchar(32) DEFAULT NULL,
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `mentor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
\ No newline at end of file
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
diff --git a/SQL/mentor_prefixed.sql b/SQL/mentor_prefixed.sql
new file mode 100644
index 0000000000..81e76f333e
--- /dev/null
+++ b/SQL/mentor_prefixed.sql
@@ -0,0 +1,14 @@
+CREATE TABLE `SS13_mentor_memo` (
+ `ckey` varchar(32) NOT NULL,
+ `memotext` text NOT NULL,
+ `timestamp` datetime NOT NULL,
+ `last_editor` varchar(32) DEFAULT NULL,
+ `edits` text,
+ PRIMARY KEY (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+CREATE TABLE `SS13_mentor` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index f50c765385..a6b4289fe7 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -6295,27 +6295,6 @@
},
/turf/open/floor/plasteel,
/area/commons/dorms)
-"auS" = (
-/obj/machinery/requests_console{
- announcementConsole = 1;
- department = "Cargo Bay";
- departmentType = 2;
- pixel_x = -30
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/obj/machinery/keycard_auth{
- pixel_y = 25
- },
-/turf/open/floor/plasteel,
-/area/cargo/qm)
"auT" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -31646,13 +31625,6 @@
},
/turf/open/floor/plating,
/area/maintenance/port/aft)
-"bUv" = (
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/turf/open/floor/plating,
-/area/maintenance/port/aft)
"bUx" = (
/obj/structure/disposalpipe/junction/yjunction{
dir = 1
@@ -34769,13 +34741,6 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/closed/wall,
/area/maintenance/starboard/aft)
-"ccV" = (
-/obj/effect/decal/cleanable/dirt,
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 10
- },
-/turf/open/floor/plating,
-/area/maintenance/starboard/aft)
"ccW" = (
/obj/effect/decal/cleanable/blood/old,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
@@ -37852,12 +37817,6 @@
/obj/structure/disposalpipe/segment,
/turf/open/floor/plating,
/area/maintenance/aft)
-"cln" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 9
- },
-/turf/open/floor/plating,
-/area/maintenance/aft)
"clo" = (
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
@@ -45837,6 +45796,14 @@
},
/turf/open/floor/carpet,
/area/medical/psychology)
+"eRu" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"eRz" = (
/obj/structure/lattice,
/obj/structure/grille,
@@ -47612,6 +47579,13 @@
},
/turf/open/floor/plating,
/area/service/kitchen)
+"gop" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port)
"got" = (
/obj/structure/closet,
/obj/item/poster/random_contraband,
@@ -49881,6 +49855,10 @@
},
/turf/open/floor/plasteel/dark,
/area/security/prison)
+"isC" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/wood/wood_large,
+/area/service/abandoned_gambling_den)
"itD" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -50424,6 +50402,11 @@
},
/turf/open/floor/plasteel,
/area/security/prison/upper)
+"iVB" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"iVH" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -51430,6 +51413,14 @@
/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/plating,
/area/maintenance/port/fore)
+"jJq" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 10
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"jJC" = (
/obj/machinery/computer/arcade/battle,
/obj/structure/window{
@@ -51772,6 +51763,12 @@
},
/turf/open/floor/plating,
/area/maintenance/starboard/fore)
+"kai" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg1"
+ },
+/area/maintenance/starboard/aft)
"kaq" = (
/turf/closed/wall/mineral/titanium,
/area/space/nearstation)
@@ -52446,6 +52443,10 @@
},
/turf/open/floor/wood,
/area/service/theater)
+"kEe" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/aft)
"kEm" = (
/mob/living/simple_animal/opossum/poppy,
/turf/open/floor/plating,
@@ -52562,6 +52563,12 @@
},
/turf/open/floor/plating,
/area/maintenance/fore)
+"kJl" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "panelscorched"
+ },
+/area/maintenance/starboard/fore)
"kJE" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
/obj/machinery/atmospherics/pipe/simple/cyan/visible{
@@ -54347,6 +54354,30 @@
},
/turf/open/floor/plasteel,
/area/security/prison/upper)
+"mlG" = (
+/obj/machinery/requests_console{
+ announcementConsole = 1;
+ department = "Cargo Bay";
+ departmentType = 2;
+ pixel_x = -30
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 8
+ },
+/obj/machinery/keycard_auth{
+ pixel_y = 25
+ },
+/obj/machinery/computer/bounty{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/cargo/qm)
"mml" = (
/obj/effect/turf_decal/tile/red{
dir = 1
@@ -58126,6 +58157,13 @@
},
/turf/open/floor/plasteel/dark,
/area/security/prison)
+"pJP" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 9
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/aft)
"pKV" = (
/turf/closed/wall/r_wall,
/area/science/research)
@@ -58713,6 +58751,10 @@
/obj/item/toy/plush/random,
/turf/open/floor/carpet/blue,
/area/medical/psychology)
+"qlD" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"qlV" = (
/obj/item/mop{
name = "Ain't Much"
@@ -62909,6 +62951,10 @@
/obj/item/smithing/stundild,
/turf/open/floor/plasteel/dark,
/area/security/warden)
+"tZZ" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"uaw" = (
/obj/machinery/power/apc{
areastring = "/area/maintenance/bar";
@@ -62935,6 +62981,15 @@
},
/turf/open/floor/wood/wood_large,
/area/service/chapel/main)
+"ubP" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/machinery/computer/bounty{
+ dir = 8
+ },
+/turf/open/floor/plasteel,
+/area/cargo/office)
"udT" = (
/obj/machinery/atmospherics/pipe/manifold/purple/visible,
/turf/open/floor/plasteel,
@@ -66422,6 +66477,10 @@
/obj/structure/lattice,
/turf/open/space,
/area/space)
+"xdq" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"xdQ" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -81223,7 +81282,7 @@ lPA
alU
ugq
rMh
-ayy
+iVB
aAe
aBJ
aCs
@@ -81983,7 +82042,7 @@ aaf
aaf
alU
arO
-amC
+qlD
aom
ank
auT
@@ -83304,7 +83363,7 @@ bbI
bcK
aPz
bdB
-aWv
+gop
bfh
aPz
aPz
@@ -86620,7 +86679,7 @@ amC
uBf
amC
aKY
-amC
+qlD
aAY
aBQ
aDp
@@ -87445,7 +87504,7 @@ aaa
bCq
bHE
bHE
-bHE
+xdq
bHE
bCq
bVy
@@ -87943,7 +88002,7 @@ bjr
bjr
bub
bxu
-auS
+mlG
bzP
bAS
bxu
@@ -88490,7 +88549,7 @@ bCq
cqn
cAh
chT
-bHE
+xdq
bHE
ckv
bHE
@@ -90014,7 +90073,7 @@ aoV
aoV
bLv
bPZ
-bHE
+xdq
bHE
cTF
bHE
@@ -90509,7 +90568,7 @@ bnG
bnz
bpA
bbR
-bkM
+ubP
jlm
bud
eyM
@@ -91816,7 +91875,7 @@ bLv
aaa
aaa
bTB
-bUv
+eRu
bES
bES
bES
@@ -105953,7 +106012,7 @@ bMa
bTZ
bKH
bzs
-bAw
+kEe
bXZ
bHX
bZN
@@ -108024,7 +108083,7 @@ cbK
ciG
bLS
ckm
-cln
+pJP
cmh
cnd
cnE
@@ -109744,7 +109803,7 @@ apC
aqy
anf
anf
-aty
+kJl
auF
alP
aAt
@@ -113853,7 +113912,7 @@ wKe
hda
hda
hda
-hda
+isC
qbq
asB
atD
@@ -114709,7 +114768,7 @@ diq
clr
bnt
cOe
-cOe
+tZZ
bMB
cOT
aaa
@@ -118809,7 +118868,7 @@ cNW
rSf
cNW
wly
-ccV
+jJq
noa
iQg
ccU
@@ -119053,7 +119112,7 @@ vHY
bEs
rmX
xIa
-vxh
+kai
cOe
bPP
bPP
diff --git a/_maps/map_files/BoxedInStation/BoxedInStation.dmm b/_maps/map_files/BoxedInStation/BoxedInStation.dmm
index 8a2762bbe7..ef3fdc2884 100644
--- a/_maps/map_files/BoxedInStation/BoxedInStation.dmm
+++ b/_maps/map_files/BoxedInStation/BoxedInStation.dmm
@@ -10914,7 +10914,7 @@
/area/service/kitchen)
"fGo" = (
/obj/machinery/vending/dinnerware{
- contraband = list(/obj/item/kitchen/rollingpin = 2, /obj/item/kitchen/knife/butcher = 2, /obj/item/reagent_containers/food/condiment/flour = 4)
+ contraband = list(/obj/item/kitchen/rollingpin=2,/obj/item/kitchen/knife/butcher=2,/obj/item/reagent_containers/food/condiment/flour=4)
},
/obj/machinery/power/apc{
areastring = "/area/service/kitchen";
@@ -13326,11 +13326,11 @@
/area/service/bar)
"gUI" = (
/obj/machinery/vending/dinnerware{
- contraband = list(/obj/item/reagent_containers/food/condiment/flour = 4);
+ contraband = list(/obj/item/reagent_containers/food/condiment/flour=4);
desc = "This vendor is full of condiments to put on food.";
name = "\improper Condiments Vendor";
product_ads = "Get your sauces here!;No slave labour was used to make these products!;Nanotrasen Approved?!";
- products = list(/obj/item/storage/bag/tray = 8, /obj/item/reagent_containers/food/drinks/drinkingglass = 10, /obj/item/storage/box/cups = 5, /obj/item/reagent_containers/food/condiment/pack/ketchup = 20, /obj/item/reagent_containers/food/condiment/pack/mustard = 20, /obj/item/reagent_containers/food/condiment/pack/hotsauce = 20, /obj/item/reagent_containers/food/condiment/pack/astrotame = 20, /obj/item/reagent_containers/food/condiment/saltshaker = 20, /obj/item/reagent_containers/food/condiment/peppermill = 20)
+ products = list(/obj/item/storage/bag/tray=8,/obj/item/reagent_containers/food/drinks/drinkingglass=10,/obj/item/storage/box/cups=5,/obj/item/reagent_containers/food/condiment/pack/ketchup=20,/obj/item/reagent_containers/food/condiment/pack/mustard=20,/obj/item/reagent_containers/food/condiment/pack/hotsauce=20,/obj/item/reagent_containers/food/condiment/pack/astrotame=20,/obj/item/reagent_containers/food/condiment/saltshaker=20,/obj/item/reagent_containers/food/condiment/peppermill=20)
},
/turf/open/floor/plasteel/cafeteria,
/area/service/kitchen)
@@ -18857,6 +18857,10 @@
},
/turf/open/floor/plating,
/area/hallway/secondary/exit)
+"jMk" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"jMy" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -31800,6 +31804,10 @@
},
/turf/open/floor/plasteel,
/area/engineering/main)
+"qyg" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/central/secondary)
"qyp" = (
/obj/structure/disposalpipe/segment{
dir = 6
@@ -46274,6 +46282,13 @@
dir = 4
},
/area/service/theater)
+"xIk" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"xIu" = (
/obj/effect/spawner/structure/window,
/obj/machinery/door/firedoor,
@@ -66642,7 +66657,7 @@ kED
kED
gXk
dQv
-lRs
+jMk
gXk
kED
cwy
@@ -68912,7 +68927,7 @@ oCD
mhf
nzv
lRs
-dQv
+xIk
lRs
seE
qaQ
@@ -70455,7 +70470,7 @@ vBu
vBu
cTZ
aye
-mjY
+qyg
mjY
jzy
mjY
@@ -73255,7 +73270,7 @@ nOA
pAn
mjY
kZA
-mjY
+qyg
mjY
rTW
aye
diff --git a/_maps/map_files/CogStation/CogStation.dmm b/_maps/map_files/CogStation/CogStation.dmm
index bc8af8a789..f85f83092b 100644
--- a/_maps/map_files/CogStation/CogStation.dmm
+++ b/_maps/map_files/CogStation/CogStation.dmm
@@ -1678,12 +1678,6 @@
},
/turf/open/floor/plasteel,
/area/commons/lounge)
-"aen" = (
-/obj/machinery/atmospherics/pipe/simple/general/visible{
- dir = 9
- },
-/turf/open/floor/plating,
-/area/maintenance/fore)
"aeo" = (
/obj/effect/decal/cleanable/dirt,
/obj/item/circuitboard/machine/sleeper,
@@ -5124,20 +5118,6 @@
},
/turf/open/floor/carpet/arcade,
/area/commons/arcade)
-"ani" = (
-/obj/machinery/disposal/bin,
-/obj/effect/turf_decal/tile/red,
-/obj/effect/turf_decal/tile/red{
- dir = 8
- },
-/obj/structure/disposalpipe/trunk{
- dir = 8
- },
-/obj/structure/sign/poster/official/duelshotgun{
- pixel_y = -32
- },
-/turf/open/floor/plasteel,
-/area/security/brig)
"anj" = (
/obj/machinery/holopad,
/obj/effect/turf_decal/bot,
@@ -68150,6 +68130,13 @@
/obj/machinery/atmospherics/pipe/layer_manifold,
/turf/open/floor/plating,
/area/engineering/atmos)
+"dXW" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"dYm" = (
/obj/machinery/portable_atmospherics/canister/oxygen,
/obj/effect/turf_decal/stripes/line{
@@ -68888,6 +68875,10 @@
/obj/structure/chair/stool,
/turf/open/floor/plasteel,
/area/cargo/office)
+"iEf" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/central)
"iLw" = (
/obj/machinery/door/airlock/security/glass{
name = "Advanced Interrogation"
@@ -69175,6 +69166,16 @@
},
/turf/open/floor/plating,
/area/cargo/storage)
+"kPi" = (
+/obj/machinery/atmospherics/pipe/simple/cyan/hidden{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"kTm" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/landmark/start/prisoner,
@@ -69337,6 +69338,20 @@
},
/turf/closed/wall/mineral/wood,
/area/commons/fitness/cogpool)
+"maU" = (
+/obj/machinery/disposal/bin,
+/obj/effect/turf_decal/tile/red,
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/obj/structure/disposalpipe/trunk{
+ dir = 8
+ },
+/obj/structure/sign/poster/official/bless_this_spess{
+ pixel_y = -32
+ },
+/turf/open/floor/plasteel,
+/area/security/brig)
"mfj" = (
/obj/machinery/firealarm{
pixel_y = 26
@@ -70601,6 +70616,15 @@
},
/turf/open/floor/plasteel,
/area/cargo/storage)
+"ulg" = (
+/obj/structure/disposalpipe/segment,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/orange/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"unW" = (
/obj/effect/landmark/navigate_destination/med,
/turf/open/floor/plasteel/white,
@@ -70978,6 +71002,13 @@
/obj/structure/filingcabinet,
/turf/open/floor/carpet,
/area/medical/psychology)
+"wUe" = (
+/obj/machinery/atmospherics/pipe/simple/general/visible{
+ dir = 9
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"wUI" = (
/obj/effect/turf_decal/tile/neutral,
/obj/structure/table,
@@ -71172,6 +71203,16 @@
},
/turf/open/floor/plasteel,
/area/security/brig)
+"xWP" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/cyan/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"xXs" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -95231,7 +95272,7 @@ adb
anU
aeQ
aeM
-ani
+maU
aew
aeJ
aeJ
@@ -98104,7 +98145,7 @@ aQL
aQL
aQL
aTS
-aRW
+iEf
bcR
bjG
blk
@@ -98581,7 +98622,7 @@ amS
amS
aDE
amS
-amS
+ulg
aKR
apY
bRa
@@ -103448,7 +103489,7 @@ hzz
aaa
acb
acS
-aen
+wUe
afi
agg
ahj
@@ -107053,7 +107094,7 @@ ajw
ajw
ajw
awN
-bGc
+xWP
bGl
ahA
aiQ
@@ -111936,7 +111977,7 @@ ahP
ahP
akj
ahP
-aif
+kPi
aiB
afK
ahU
@@ -113319,7 +113360,7 @@ bqM
btL
bBm
btC
-bPm
+dXW
bta
btV
cfG
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 9913253ba6..8c11405f41 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -70379,6 +70379,20 @@
/obj/effect/turf_decal/caution/stand_clear,
/turf/open/floor/plasteel,
/area/hallway/secondary/entry)
+"ehA" = (
+/obj/structure/cable/white{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plasteel,
+/area/maintenance/starboard/aft)
"ehG" = (
/obj/item/radio/intercom{
name = "Station Intercom";
@@ -77296,27 +77310,6 @@
},
/turf/open/floor/plasteel,
/area/cargo/storage)
-"gFF" = (
-/obj/machinery/light{
- dir = 4
- },
-/obj/machinery/newscaster{
- pixel_y = -32
- },
-/obj/item/radio/intercom{
- dir = 8;
- name = "Station Intercom (General)";
- pixel_x = 28
- },
-/obj/effect/turf_decal/tile/brown,
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/cargo/office)
"gFG" = (
/obj/machinery/door/airlock/maintenance_hatch{
name = "Maintenance Hatch";
@@ -82289,6 +82282,12 @@
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plating,
/area/service/abandoned_gambling_den)
+"ijo" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"ijN" = (
/obj/structure/table/wood,
/obj/machinery/computer/med_data/laptop,
@@ -91624,25 +91623,6 @@
/obj/effect/turf_decal/vg_decals/atmos/plasma,
/turf/open/floor/engine/plasma,
/area/engineering/atmos)
-"lkx" = (
-/obj/machinery/requests_console{
- department = "Quartermaster's Desk";
- name = "Quartermaster RC";
- pixel_x = 32;
- pixel_y = -32
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 4
- },
-/obj/machinery/light_switch{
- pixel_x = 23
- },
-/obj/effect/turf_decal/tile/brown,
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/turf/open/floor/plasteel,
-/area/cargo/qm)
"lkF" = (
/obj/structure/closet/secure_closet/engineering_electrical,
/obj/machinery/light_switch{
@@ -109280,6 +109260,30 @@
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
/area/service/hydroponics/garden/abandoned)
+"rpU" = (
+/obj/machinery/light{
+ dir = 4
+ },
+/obj/machinery/newscaster{
+ pixel_y = -32
+ },
+/obj/item/radio/intercom{
+ dir = 8;
+ name = "Station Intercom (General)";
+ pixel_x = 28
+ },
+/obj/effect/turf_decal/tile/brown,
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 8
+ },
+/obj/machinery/computer/bounty{
+ dir = 8
+ },
+/turf/open/floor/plasteel,
+/area/cargo/office)
"rpV" = (
/obj/machinery/status_display/evac,
/turf/closed/wall,
@@ -112599,6 +112603,28 @@
/obj/structure/lattice/catwalk,
/turf/open/space,
/area/solars/starboard/aft)
+"stl" = (
+/obj/machinery/requests_console{
+ department = "Quartermaster's Desk";
+ name = "Quartermaster RC";
+ pixel_x = 32;
+ pixel_y = -32
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/machinery/light_switch{
+ pixel_x = 23
+ },
+/obj/effect/turf_decal/tile/brown,
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/machinery/computer/bounty{
+ dir = 8
+ },
+/turf/open/floor/plasteel,
+/area/cargo/qm)
"sts" = (
/obj/structure/table/wood,
/obj/item/folder,
@@ -114990,6 +115016,20 @@
},
/turf/open/floor/plasteel,
/area/maintenance/port/fore)
+"til" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/structure/cable/white{
+ icon_state = "4-8"
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plasteel,
+/area/maintenance/starboard)
"tin" = (
/obj/machinery/light{
dir = 1
@@ -123172,6 +123212,15 @@
},
/turf/open/floor/plasteel,
/area/commons/dorms)
+"whu" = (
+/obj/structure/cable/white{
+ icon_state = "1-2"
+ },
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"whB" = (
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
@@ -171324,7 +171373,7 @@ nOU
tnL
xRR
hbT
-gFF
+rpU
vhK
bdV
bdU
@@ -173658,7 +173707,7 @@ aaa
aad
aaa
bHq
-bJb
+til
bHq
rNA
poQ
@@ -175948,7 +175997,7 @@ sOq
keW
fJF
lgx
-lkx
+stl
vdx
sOq
aad
@@ -178850,7 +178899,7 @@ dkG
dmf
dnL
dpE
-dpE
+ehA
dsS
dtV
dvK
@@ -179091,13 +179140,13 @@ gTM
vrH
fzU
cPP
-cRr
+ijo
cRr
cUU
cWB
cYf
cZR
-dbB
+whu
dbB
deM
dfN
diff --git a/_maps/map_files/FestiveBall/FestiveStation.dmm b/_maps/map_files/FestiveBall/FestiveStation.dmm
index 063d72b6c6..feb2ee2f13 100644
--- a/_maps/map_files/FestiveBall/FestiveStation.dmm
+++ b/_maps/map_files/FestiveBall/FestiveStation.dmm
@@ -2634,36 +2634,11 @@
/obj/machinery/light/floor,
/turf/open/floor/festive/wooden/wooden1,
/area/command/corporate_showroom)
-"agT" = (
-/obj/structure/table/wood/fancy/green,
-/obj/structure/window/reinforced{
- dir = 8
- },
-/obj/item/clothing/under/syndicate/baseball,
-/obj/item/clothing/head/beret/sec{
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0);
- desc = "A replica beret resembling that of a special operations officer under Nanotrasen.";
- name = "replica officer's beret"
- },
-/turf/open/floor/festive/wooden/wooden1,
-/area/command/corporate_showroom)
"agU" = (
/obj/structure/showcase/mecha/marauder,
/obj/machinery/light/floor,
/turf/open/floor/festive/wooden/wooden1,
/area/command/corporate_showroom)
-"agV" = (
-/obj/structure/table/wood/fancy/red,
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/item/clothing/head/centhat{
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0);
- desc = "A replica hat of a Central Commander's attire. It has a small tag on it saying, 'It's good to be emperor.'";
- name = "Replica CentCom hat"
- },
-/turf/open/floor/festive/wooden/wooden1,
-/area/command/corporate_showroom)
"agW" = (
/obj/structure/table/wood/fancy/green,
/obj/item/folder/blue,
@@ -20379,13 +20354,6 @@
},
/turf/open/floor/festive/alleyway,
/area/edina/street/secondary/command)
-"baX" = (
-/obj/machinery/computer/prisoner/management,
-/obj/structure/sign/poster/official/duelshotgun{
- pixel_y = 32
- },
-/turf/open/floor/wood,
-/area/security/warden)
"baY" = (
/obj/effect/landmark/start/botanist,
/turf/open/floor/plasteel,
@@ -35552,14 +35520,6 @@
},
/turf/open/floor/wood,
/area/service/bar/atrium)
-"bKt" = (
-/obj/machinery/vending/dinnerware{
- desc = "A vending machine stocked full of condiments to put on food.";
- name = "\improper Condiments Dispenser";
- products = list(/obj/item/storage/bag/tray = 10, /obj/item/kitchen/fork = 6, /obj/item/kitchen/knife = 6, /obj/item/reagent_containers/food/drinks/drinkingglass = 20, /obj/item/clothing/suit/apron/chef = 2, /obj/item/storage/box/cups = 10, /obj/item/reagent_containers/food/condiment/pack/ketchup = 20, /obj/item/reagent_containers/food/condiment/pack/mustard = 20, /obj/item/reagent_containers/food/condiment/pack/hotsauce = 20, /obj/item/reagent_containers/food/condiment/pack/astrotame = 20, /obj/item/reagent_containers/food/condiment/saltshaker = 20, /obj/item/reagent_containers/food/condiment/peppermill = 20, /obj/item/reagent_containers/glass/bowl = 30)
- },
-/turf/open/floor/wood,
-/area/service/bar/atrium)
"bKu" = (
/obj/structure/chair/sofa/right{
dir = 4
@@ -35852,10 +35812,6 @@
/obj/structure/fence,
/turf/open/floor/plating/dirt/space,
/area/edina)
-"bKZ" = (
-/obj/effect/festive/street/sidewalke,
-/turf/open/floor/festive/cobblestone,
-/area/edina/street/primary/progress)
"bLa" = (
/obj/effect/turf_decal/weather/snow/corner{
dir = 1
@@ -43300,53 +43256,6 @@
/obj/structure/reagent_dispensers/keg/milk,
/turf/open/floor/plasteel/freezer,
/area/service/kitchen/coldroom)
-"cbt" = (
-/obj/structure/closet/crate/freezer{
- desc = "A small crate with a cooling system, keeps fresh produce chilled.";
- name = "Food Chiller"
- },
-/obj/item/reagent_containers/food/condiment/flour{
- desc = "A large sack of flour for restaurants, not the home-baker!";
- list_reagents = list(/datum/reagent/consumable/flour = 90);
- name = "Large Flour Sack"
- },
-/obj/item/reagent_containers/food/condiment/flour{
- desc = "A large sack of flour for restaurants, not the home-baker!";
- list_reagents = list(/datum/reagent/consumable/flour = 90);
- name = "Large Flour Sack"
- },
-/obj/item/reagent_containers/food/condiment/flour{
- desc = "A large sack of flour for restaurants, not the home-baker!";
- list_reagents = list(/datum/reagent/consumable/flour = 90);
- name = "Large Flour Sack"
- },
-/obj/item/reagent_containers/food/condiment/flour{
- desc = "A large sack of flour for restaurants, not the home-baker!";
- list_reagents = list(/datum/reagent/consumable/flour = 90);
- name = "Large Flour Sack"
- },
-/obj/item/reagent_containers/food/condiment/rice{
- desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
- list_reagents = list(/datum/reagent/consumable/rice = 90);
- name = "Large Rice Sack"
- },
-/obj/item/reagent_containers/food/condiment/rice{
- desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
- list_reagents = list(/datum/reagent/consumable/rice = 90);
- name = "Large Rice Sack"
- },
-/obj/item/reagent_containers/food/condiment/rice{
- desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
- list_reagents = list(/datum/reagent/consumable/rice = 90);
- name = "Large Rice Sack"
- },
-/obj/item/reagent_containers/food/condiment/rice{
- desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
- list_reagents = list(/datum/reagent/consumable/rice = 90);
- name = "Large Rice Sack"
- },
-/turf/open/floor/plasteel/freezer,
-/area/service/kitchen/coldroom)
"cbu" = (
/obj/structure/sign/poster/official/twelve_gauge{
pixel_y = 32
@@ -53426,6 +53335,13 @@
},
/turf/open/floor/wood,
/area/maintenance/bar)
+"dny" = (
+/obj/machinery/computer/prisoner/management,
+/obj/structure/sign/poster/official/bless_this_spess{
+ pixel_y = 32
+ },
+/turf/open/floor/wood,
+/area/security/warden)
"dnQ" = (
/mob/living/simple_animal/pet/penguin/emperor/shamebrero,
/turf/open/floor/holofloor/ice,
@@ -54049,6 +53965,53 @@
/obj/effect/decal/festive/christmas_ivy_string,
/turf/open/floor/circuit/off,
/area/science)
+"hGn" = (
+/obj/structure/closet/crate/freezer{
+ desc = "A small crate with a cooling system, keeps fresh produce chilled.";
+ name = "Food Chiller"
+ },
+/obj/item/reagent_containers/food/condiment/flour{
+ desc = "A large sack of flour for restaurants, not the home-baker!";
+ list_reagents = list(/datum/reagent/consumable/flour=90);
+ name = "Large Flour Sack"
+ },
+/obj/item/reagent_containers/food/condiment/flour{
+ desc = "A large sack of flour for restaurants, not the home-baker!";
+ list_reagents = list(/datum/reagent/consumable/flour=90);
+ name = "Large Flour Sack"
+ },
+/obj/item/reagent_containers/food/condiment/flour{
+ desc = "A large sack of flour for restaurants, not the home-baker!";
+ list_reagents = list(/datum/reagent/consumable/flour=90);
+ name = "Large Flour Sack"
+ },
+/obj/item/reagent_containers/food/condiment/flour{
+ desc = "A large sack of flour for restaurants, not the home-baker!";
+ list_reagents = list(/datum/reagent/consumable/flour=90);
+ name = "Large Flour Sack"
+ },
+/obj/item/reagent_containers/food/condiment/rice{
+ desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
+ list_reagents = list(/datum/reagent/consumable/rice=90);
+ name = "Large Rice Sack"
+ },
+/obj/item/reagent_containers/food/condiment/rice{
+ desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
+ list_reagents = list(/datum/reagent/consumable/rice=90);
+ name = "Large Rice Sack"
+ },
+/obj/item/reagent_containers/food/condiment/rice{
+ desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
+ list_reagents = list(/datum/reagent/consumable/rice=90);
+ name = "Large Rice Sack"
+ },
+/obj/item/reagent_containers/food/condiment/rice{
+ desc = "A huge sack of rice. Probably for restaurants that actually go through such a huge volume of it easily, not for the home cook.";
+ list_reagents = list(/datum/reagent/consumable/rice=90);
+ name = "Large Rice Sack"
+ },
+/turf/open/floor/plasteel/freezer,
+/area/service/kitchen/coldroom)
"hHy" = (
/obj/structure/closet/crate,
/obj/item/storage/fancy/candle_box,
@@ -54105,6 +54068,10 @@
/obj/effect/turf_decal/weather/snow/corner,
/turf/open/floor/festive/sidewalk,
/area/edina)
+"hVw" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/festive/cobblestone,
+/area/edina/street/primary/progress)
"hXd" = (
/obj/vehicle/ridden/janicart,
/obj/item/key/janitor,
@@ -54927,6 +54894,19 @@
/mob/living/simple_animal/pet/fox,
/turf/open/floor/grass,
/area/edina)
+"nNk" = (
+/obj/structure/table/wood/fancy/green,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/item/clothing/under/syndicate/baseball,
+/obj/item/clothing/head/beret/sec{
+ armor = list("melee"=0,"bullet"=0,"laser"=0,"energy"=0,"bomb"=0,"bio"=0,"rad"=0);
+ desc = "A replica beret resembling that of a special operations officer under Nanotrasen.";
+ name = "replica officer's beret"
+ },
+/turf/open/floor/festive/wooden/wooden1,
+/area/command/corporate_showroom)
"nOZ" = (
/turf/closed/wall/r_wall,
/area/engineering/engine_smes)
@@ -55019,6 +54999,14 @@
/obj/structure/fireplace,
/turf/open/floor/wood,
/area/commons/dorms)
+"ooy" = (
+/obj/machinery/vending/dinnerware{
+ desc = "A vending machine stocked full of condiments to put on food.";
+ name = "\improper Condiments Dispenser";
+ products = list(/obj/item/storage/bag/tray=10,/obj/item/kitchen/fork=6,/obj/item/kitchen/knife=6,/obj/item/reagent_containers/food/drinks/drinkingglass=20,/obj/item/clothing/suit/apron/chef=2,/obj/item/storage/box/cups=10,/obj/item/reagent_containers/food/condiment/pack/ketchup=20,/obj/item/reagent_containers/food/condiment/pack/mustard=20,/obj/item/reagent_containers/food/condiment/pack/hotsauce=20,/obj/item/reagent_containers/food/condiment/pack/astrotame=20,/obj/item/reagent_containers/food/condiment/saltshaker=20,/obj/item/reagent_containers/food/condiment/peppermill=20,/obj/item/reagent_containers/glass/bowl=30)
+ },
+/turf/open/floor/wood,
+/area/service/bar/atrium)
"orN" = (
/obj/machinery/light/small,
/turf/open/floor/mineral/titanium/blue,
@@ -55219,6 +55207,11 @@
},
/turf/open/floor/wood,
/area/commons/dorms)
+"ptw" = (
+/obj/effect/festive/street/sidewalke,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/festive/cobblestone,
+/area/edina/street/primary/progress)
"pwQ" = (
/obj/structure/table/wood,
/obj/item/phone,
@@ -55289,6 +55282,10 @@
/obj/machinery/light/small,
/turf/open/floor/plasteel/dark,
/area/service/hydroponics)
+"pPZ" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/festive/alleyway,
+/area/science)
"pQk" = (
/obj/effect/turf_decal/stripes/line{
dir = 8
@@ -56245,6 +56242,10 @@
/obj/effect/decal/cleanable/cobweb/cobweb2,
/turf/open/floor/plasteel,
/area/edina)
+"wjE" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/festive/sidewalk,
+/area/edina/street/primary/progress)
"wlp" = (
/obj/machinery/door/window/westleft{
dir = 2;
@@ -56570,6 +56571,18 @@
},
/turf/open/floor/grass/snow/edina,
/area/edina/backstreet/supply)
+"xOm" = (
+/obj/structure/table/wood/fancy/red,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/item/clothing/head/centhat{
+ armor = list("melee"=0,"bullet"=0,"laser"=0,"energy"=0,"bomb"=0,"bio"=0,"rad"=0);
+ desc = "A replica hat of a Central Commander's attire. It has a small tag on it saying, 'It's good to be emperor.'";
+ name = "Replica CentCom hat"
+ },
+/turf/open/floor/festive/wooden/wooden1,
+/area/command/corporate_showroom)
"xQh" = (
/obj/structure/table,
/obj/machinery/reagentgrinder{
@@ -86380,7 +86393,7 @@ cuQ
cuT
cuV
hya
-aKT
+pPZ
pqA
aLX
aLX
@@ -87079,7 +87092,7 @@ aeJ
afl
afl
ago
-agT
+nNk
ahu
afl
afl
@@ -87593,7 +87606,7 @@ aeJ
afl
afl
agq
-agV
+xOm
ahw
afl
afl
@@ -88691,7 +88704,7 @@ aOl
aQL
aMG
cuU
-bFg
+wjE
bQz
bTV
bVm
@@ -99240,7 +99253,7 @@ ccI
bFS
bHi
bIR
-bKt
+ooy
aqp
aqp
bFS
@@ -100723,7 +100736,7 @@ aoR
aLE
anQ
anl
-baX
+dny
arE
asS
atG
@@ -104140,7 +104153,7 @@ bWF
bWH
bWH
cdK
-cbt
+hGn
bTM
bKV
bWy
@@ -105914,7 +105927,7 @@ bAF
bAF
bAF
bAF
-bAF
+hVw
bAF
bWE
bFg
@@ -106166,7 +106179,7 @@ aPA
aQj
bAJ
bGa
-bKZ
+ptw
bAJ
bAJ
bAJ
@@ -107201,7 +107214,7 @@ bAF
bAF
bAF
bAF
-bAF
+hVw
bFg
ccD
bzs
@@ -107712,7 +107725,7 @@ bBU
bFg
bAF
bAF
-bAF
+hVw
bAF
bAF
bAF
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index 398bd92c8f..1d316f8ad5 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -8113,6 +8113,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "platingdmg1"
},
@@ -10039,6 +10040,7 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/port/fore)
"aqZ" = (
@@ -33122,6 +33124,7 @@
},
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "platingdmg1"
},
@@ -33229,6 +33232,7 @@
/obj/structure/cable{
icon_state = "1-4"
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "platingdmg1"
},
@@ -51993,6 +51997,7 @@
pixel_y = -32
},
/obj/effect/decal/cleanable/dirt,
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "panelscorched"
},
@@ -70940,6 +70945,7 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "panelscorched"
},
@@ -76941,6 +76947,7 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{
dir = 6
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating{
icon_state = "platingdmg1"
},
@@ -84438,6 +84445,13 @@
},
/turf/open/floor/plasteel/showroomfloor,
/area/ai_monitored/security/armory)
+"jIi" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "panelscorched"
+ },
+/area/maintenance/port)
"jNK" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/machinery/door/poddoor/shutters/preopen{
@@ -84646,6 +84660,13 @@
icon_state = "wood-broken7"
},
/area/maintenance/port/fore)
+"kQV" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg3"
+ },
+/area/maintenance/starboard/aft)
"kRh" = (
/obj/effect/turf_decal/tile/neutral{
dir = 1
@@ -85554,6 +85575,21 @@
/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
/turf/open/space/basic,
/area/space/nearstation)
+"tpQ" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{
+ dir = 8
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg1"
+ },
+/area/maintenance/fore)
"tCi" = (
/obj/effect/decal/cleanable/glass,
/turf/open/floor/wood{
@@ -100308,7 +100344,7 @@ bhU
aEw
bxq
cux
-cnL
+jIi
awD
agp
bHg
@@ -116722,7 +116758,7 @@ alg
alU
cEX
apb
-cFf
+tpQ
cEh
ahh
ccU
@@ -122175,7 +122211,7 @@ bNU
bQE
bSz
bUU
-bGH
+kQV
bNh
awX
awO
diff --git a/_maps/map_files/LambdaStation/lambda.dmm b/_maps/map_files/LambdaStation/lambda.dmm
index 100f99b8a9..d843cd2eb1 100644
--- a/_maps/map_files/LambdaStation/lambda.dmm
+++ b/_maps/map_files/LambdaStation/lambda.dmm
@@ -67046,6 +67046,10 @@
},
/turf/open/floor/plasteel,
/area/science/mixing)
+"kGJ" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"kJN" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1,
/obj/structure/table,
@@ -70940,6 +70944,16 @@
},
/turf/open/floor/plasteel,
/area/maintenance/disposal/incinerator)
+"sdE" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"sfN" = (
/obj/machinery/light/small{
dir = 4
@@ -72146,6 +72160,13 @@
},
/turf/open/floor/plasteel,
/area/engineering/atmos)
+"uKy" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"uKJ" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -91039,7 +91060,7 @@ aiI
avs
ahd
acw
-acw
+kGJ
acw
cUe
abY
@@ -96161,7 +96182,7 @@ aaA
aaA
aaA
abY
-acw
+kGJ
bJa
abY
ahd
@@ -99523,7 +99544,7 @@ cMf
atH
acw
acw
-acw
+kGJ
acw
acw
aeG
@@ -101584,7 +101605,7 @@ aaQ
aaQ
aaQ
aaQ
-acw
+kGJ
aeG
abY
aaA
@@ -102839,7 +102860,7 @@ aaA
aaA
aaA
abY
-acD
+sdE
acw
adu
aec
@@ -103129,7 +103150,7 @@ aaQ
aDg
aDg
aDg
-aeG
+uKy
acw
abY
aaA
@@ -104159,7 +104180,7 @@ bfS
aDg
aUL
cOb
-acw
+kGJ
aeH
adr
adr
@@ -108487,7 +108508,7 @@ abh
bjq
acw
afB
-acw
+kGJ
acw
acw
aoT
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 9d86501872..d96866553e 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -3965,11 +3965,6 @@
/obj/effect/landmark/blobstart,
/turf/open/floor/plating,
/area/maintenance/fore)
-"amu" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/effect/turf_decal/stripes/line,
-/turf/open/floor/plating,
-/area/maintenance/fore)
"amv" = (
/obj/effect/turf_decal/tile/neutral{
dir = 1
@@ -18273,6 +18268,30 @@
/obj/effect/turf_decal/tile/neutral,
/turf/open/floor/plasteel,
/area/hallway/primary/central)
+"buy" = (
+/obj/structure/noticeboard{
+ desc = "A board for pinning important notices upon. Probably helpful for keeping track of requests.";
+ name = "requests board";
+ pixel_x = 32;
+ pixel_y = 32
+ },
+/obj/machinery/requests_console{
+ department = "Cargo Bay";
+ departmentType = 2;
+ pixel_y = 30
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 8
+ },
+/obj/machinery/computer/bounty,
+/turf/open/floor/plasteel,
+/area/cargo/office)
"buK" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/structure/disposalpipe/segment,
@@ -18629,16 +18648,6 @@
/obj/machinery/light/small,
/turf/open/floor/plasteel,
/area/security/prison/upper)
-"bvY" = (
-/obj/structure/cable/yellow{
- icon_state = "1-2"
- },
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/effect/turf_decal/stripes/line{
- dir = 1
- },
-/turf/open/floor/plating,
-/area/maintenance/port)
"bwh" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/structure/extinguisher_cabinet{
@@ -23732,18 +23741,6 @@
},
/turf/open/floor/plating,
/area/maintenance/starboard)
-"bYu" = (
-/obj/item/cigbutt,
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/structure/cable/yellow{
- icon_state = "1-2"
- },
-/obj/effect/turf_decal/stripes/line,
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 4
- },
-/turf/open/floor/plating,
-/area/maintenance/starboard)
"bYC" = (
/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
@@ -49914,6 +49911,14 @@
},
/turf/open/floor/plasteel/dark,
/area/engineering/storage/tech)
+"eAg" = (
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port)
"eAn" = (
/obj/structure/cable/yellow{
icon_state = "1-8"
@@ -55002,29 +55007,6 @@
/obj/effect/landmark/start/peacekeeper,
/turf/open/floor/plasteel,
/area/hallway/secondary/exit/departure_lounge)
-"hvk" = (
-/obj/structure/noticeboard{
- desc = "A board for pinning important notices upon. Probably helpful for keeping track of requests.";
- name = "requests board";
- pixel_x = 32;
- pixel_y = 32
- },
-/obj/machinery/requests_console{
- department = "Cargo Bay";
- departmentType = 2;
- pixel_y = 30
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/cargo/office)
"hvn" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
@@ -58102,6 +58084,19 @@
},
/turf/open/floor/plating,
/area/engineering/atmos)
+"jcr" = (
+/obj/item/cigbutt,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/effect/turf_decal/stripes/line,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard)
"jcA" = (
/turf/open/floor/plating,
/area/engineering/break_room)
@@ -63951,21 +63946,6 @@
},
/turf/open/floor/plating,
/area/maintenance/starboard/aft)
-"mjR" = (
-/obj/machinery/airalarm{
- pixel_y = 23
- },
-/obj/machinery/light{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/turf/open/floor/plasteel,
-/area/cargo/qm)
"mjX" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
@@ -65663,6 +65643,10 @@
dir = 1
},
/area/engineering/main)
+"nis" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"nix" = (
/obj/machinery/light/small{
dir = 8
@@ -66577,15 +66561,6 @@
/obj/structure/lattice/catwalk,
/turf/open/space,
/area/solars/starboard/fore)
-"nLT" = (
-/obj/structure/cable/yellow{
- icon_state = "1-2"
- },
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 8
- },
-/turf/open/floor/plating,
-/area/maintenance/starboard)
"nMV" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 8
@@ -74551,6 +74526,17 @@
},
/turf/open/floor/plasteel/dark,
/area/command/teleporter)
+"rRh" = (
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/turf_decal/stripes/line{
+ dir = 1
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port)
"rRz" = (
/obj/item/storage/box/beakers{
pixel_x = 2;
@@ -78347,6 +78333,16 @@
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
/area/service/kitchen)
+"tWL" = (
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard)
"tXp" = (
/obj/machinery/status_display/evac{
pixel_y = 32
@@ -79208,6 +79204,10 @@
},
/turf/open/floor/plating,
/area/engineering/main)
+"uum" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard)
"uun" = (
/obj/machinery/vending/assist,
/turf/open/floor/plasteel,
@@ -80800,6 +80800,19 @@
},
/turf/open/floor/plasteel/dark,
/area/engineering/atmos)
+"vtm" = (
+/obj/structure/cable/yellow{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"vtn" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
@@ -81630,6 +81643,22 @@
},
/turf/open/floor/plasteel,
/area/security/checkpoint/science/research)
+"vUG" = (
+/obj/machinery/airalarm{
+ pixel_y = 23
+ },
+/obj/machinery/light{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/machinery/computer/bounty,
+/turf/open/floor/plasteel,
+/area/cargo/qm)
"vWf" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -85006,6 +85035,12 @@
},
/turf/open/floor/carpet,
/area/service/theater)
+"xCA" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/turf_decal/stripes/line,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"xCC" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/obj/effect/decal/cleanable/dirt,
@@ -104884,7 +104919,7 @@ bnW
bqp
bsC
pzt
-bvY
+rRh
bxT
bzC
bzC
@@ -104897,7 +104932,7 @@ bzC
bzC
bzC
bPM
-bzC
+eAg
bzC
bSp
alC
@@ -106414,7 +106449,7 @@ aUj
aVN
aXn
aSX
-hvk
+buy
jSM
ikm
xhk
@@ -106919,7 +106954,7 @@ vwK
dne
aLc
dne
-mjR
+vUG
hDb
hMN
lYW
@@ -118785,7 +118820,7 @@ bZq
caG
abt
bSS
-ceZ
+vtm
cgo
cgo
cgo
@@ -120518,7 +120553,7 @@ ahS
aje
ajP
ald
-amu
+xCA
anE
aoQ
agq
@@ -123669,7 +123704,7 @@ caU
ccD
cdV
cfj
-apc
+uum
chC
dvY
dvY
@@ -124930,7 +124965,7 @@ bpc
dhQ
btw
buX
-nLT
+tWL
bGp
btw
dCV
@@ -125668,7 +125703,7 @@ bai
ate
dDL
axP
-dnS
+nis
ocS
pUr
lmt
@@ -126233,7 +126268,7 @@ apc
alq
alq
bXb
-bYu
+jcr
bZD
caY
ccI
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index da7fe90c31..0d44c8f23b 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -11905,17 +11905,6 @@
/obj/item/taperecorder,
/turf/open/floor/wood,
/area/security/detectives_office)
-"avI" = (
-/obj/structure/cable/white{
- icon_state = "2-4"
- },
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
- dir = 8
- },
-/turf/open/floor/plating{
- icon_state = "platingdmg1"
- },
-/area/maintenance/port/fore)
"avJ" = (
/obj/structure/cable/white{
icon_state = "4-8"
@@ -27894,15 +27883,6 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/closed/wall/r_wall,
/area/science/robotics/lab)
-"bai" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/structure/cable/white{
- icon_state = "1-2"
- },
-/turf/open/floor/plating{
- icon_state = "platingdmg1"
- },
-/area/maintenance/starboard)
"baj" = (
/obj/structure/sign/warning/vacuum{
pixel_x = -32;
@@ -29465,15 +29445,6 @@
},
/turf/open/floor/plating,
/area/security/checkpoint)
-"bdi" = (
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 8
- },
-/obj/structure/cable/white{
- icon_state = "1-2"
- },
-/turf/open/floor/plating,
-/area/maintenance/starboard/fore)
"bdj" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
@@ -34978,6 +34949,16 @@
dir = 4
},
/area/maintenance/starboard/aft)
+"dUm" = (
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
+ },
+/obj/structure/cable/white{
+ icon_state = "1-2"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"dXv" = (
/obj/item/transfer_valve{
pixel_x = -5
@@ -35210,6 +35191,18 @@
/obj/item/pipe_dispenser,
/turf/open/floor/plasteel,
/area/engineering/atmos)
+"eTB" = (
+/obj/structure/cable/white{
+ icon_state = "2-4"
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 8
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg1"
+ },
+/area/maintenance/port/fore)
"eUz" = (
/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
@@ -35911,6 +35904,19 @@
},
/turf/open/floor/plasteel,
/area/service/bar/atrium)
+"huF" = (
+/obj/effect/turf_decal/tile/brown{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/brown,
+/obj/effect/turf_decal/tile/brown{
+ dir = 8
+ },
+/obj/machinery/computer/bounty{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/cargo/storage)
"hvC" = (
/obj/structure/disposalpipe/segment{
dir = 9
@@ -37465,6 +37471,19 @@
},
/turf/open/floor/plasteel,
/area/science/mixing)
+"nud" = (
+/obj/structure/cable/white{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/turf_decal/trimline/green/line{
+ dir = 1
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"nvk" = (
/obj/structure/table/wood,
/turf/open/floor/wood,
@@ -37598,6 +37617,10 @@
},
/turf/open/floor/plating,
/area/science/mixing)
+"ohb" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"ohM" = (
/obj/machinery/door/poddoor{
id = "toxinsdriver";
@@ -38226,16 +38249,6 @@
},
/turf/open/floor/plasteel,
/area/science/mixing)
-"qAY" = (
-/obj/effect/turf_decal/tile/brown{
- dir = 1
- },
-/obj/effect/turf_decal/tile/brown,
-/obj/effect/turf_decal/tile/brown{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/cargo/storage)
"qEl" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
dir = 4
@@ -38314,6 +38327,18 @@
/obj/machinery/photocopier,
/turf/open/floor/wood,
/area/service/lawoffice)
+"qSk" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/structure/cable/white{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg3"
+ },
+/area/maintenance/starboard/aft)
"qSs" = (
/turf/open/floor/plasteel/dark/side{
dir = 6
@@ -38423,6 +38448,16 @@
},
/turf/open/floor/plasteel,
/area/engineering/atmos)
+"rzP" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/structure/cable/white{
+ icon_state = "1-2"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating{
+ icon_state = "platingdmg1"
+ },
+/area/maintenance/starboard)
"rCw" = (
/obj/effect/turf_decal/tile/purple{
dir = 4
@@ -41901,6 +41936,14 @@
dir = 8
},
/area/engineering/atmos)
+"xbD" = (
+/obj/structure/cable/white{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"xdr" = (
/obj/effect/turf_decal/sand/plating,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
@@ -42084,6 +42127,16 @@
},
/turf/open/floor/plasteel/dark/corner,
/area/engineering/atmos)
+"xyZ" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/structure/cable/white{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"xAj" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
/obj/effect/decal/cleanable/dirt,
@@ -75081,7 +75134,7 @@ ars
asI
atK
auD
-avI
+eTB
awM
axr
ayk
@@ -75836,7 +75889,7 @@ aAc
aAm
acH
acH
-ars
+ohb
swZ
ain
aqG
@@ -75881,7 +75934,7 @@ aKl
aKl
aFM
aFM
-aFM
+xbD
aKl
aFM
bbn
@@ -77426,7 +77479,7 @@ aDo
aKn
aLy
aFM
-aFM
+xbD
aPP
lFj
aSm
@@ -79745,7 +79798,7 @@ bcd
bcU
bdQ
aSh
-hIu
+nud
bfA
bfV
bgG
@@ -87909,7 +87962,7 @@ bcG
bcM
bcT
bym
-bdi
+dUm
bdF
bxw
aQU
@@ -88211,7 +88264,7 @@ aPy
aQA
sOM
sOU
-sOV
+xyZ
sKa
aVC
aWt
@@ -88435,7 +88488,7 @@ aiI
ajI
akL
alA
-qAY
+huF
ado
aoc
apc
@@ -88483,7 +88536,7 @@ bxW
bdw
bep
aZl
-sPI
+qSk
bfP
pej
bhi
@@ -88708,7 +88761,7 @@ oyD
sEl
sEL
aXb
-bai
+rzP
aSC
aYt
aRz
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 8ead601af4..4c4d336cb2 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -36547,12 +36547,6 @@
},
/turf/open/floor/plasteel,
/area/science/mixing)
-"bIO" = (
-/obj/machinery/status_display/supply{
- pixel_x = -32
- },
-/turf/open/floor/plasteel,
-/area/cargo/office)
"bIP" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -48209,6 +48203,10 @@
},
/turf/open/floor/plasteel,
/area/hallway/primary/aft)
+"cqr" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/cargo)
"cqs" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
@@ -49672,6 +49670,13 @@
},
/turf/open/floor/plating,
/area/maintenance/department/chapel/monastery)
+"cvQ" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/engine)
"cvR" = (
/obj/machinery/meter,
/obj/machinery/atmospherics/pipe/manifold/cyan/hidden{
@@ -50066,6 +50071,15 @@
},
/turf/closed/wall,
/area/service/library/lounge)
+"cyF" = (
+/obj/machinery/status_display/supply{
+ pixel_x = -32
+ },
+/obj/machinery/computer/bounty{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/cargo/office)
"cyQ" = (
/obj/machinery/vending/games,
/turf/open/floor/plasteel/dark,
@@ -53157,6 +53171,16 @@
},
/turf/open/floor/plasteel/dark/telecomms,
/area/tcommsat/server)
+"gpX" = (
+/obj/effect/turf_decal/tile/brown,
+/obj/effect/turf_decal/tile/brown{
+ dir = 4
+ },
+/obj/machinery/computer/bounty{
+ dir = 8
+ },
+/turf/open/floor/plasteel,
+/area/cargo/qm)
"gqi" = (
/obj/structure/bookcase/random/religion,
/turf/open/floor/plasteel/dark,
@@ -55830,13 +55854,6 @@
},
/turf/open/floor/plating,
/area/maintenance/department/security/brig)
-"kFy" = (
-/obj/effect/turf_decal/tile/brown,
-/obj/effect/turf_decal/tile/brown{
- dir = 4
- },
-/turf/open/floor/plasteel,
-/area/cargo/qm)
"kFD" = (
/obj/structure/closet/l3closet,
/obj/machinery/light{
@@ -56619,6 +56636,19 @@
},
/turf/open/floor/carpet,
/area/command/blueshielquarters)
+"lUl" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/cargo)
"lUz" = (
/obj/structure/girder,
/turf/open/floor/plating,
@@ -56941,6 +56971,10 @@
},
/turf/open/floor/plasteel,
/area/hallway/primary/aft)
+"mqu" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/crew_quarters/bar)
"mqS" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -58322,6 +58356,14 @@
/obj/structure/window/reinforced,
/turf/open/floor/plasteel,
/area/science/xenobiology)
+"osz" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/engine)
"osC" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -58540,6 +58582,14 @@
},
/turf/open/floor/plating,
/area/commons/storage/emergency/starboard)
+"oFy" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/cargo)
"oFI" = (
/obj/structure/closet,
/obj/effect/decal/cleanable/blood/old,
@@ -59839,13 +59889,6 @@
},
/turf/open/floor/plating,
/area/tcommsat/computer)
-"qyF" = (
-/obj/structure/cable{
- icon_state = "1-2"
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/turf/open/floor/plating,
-/area/maintenance/department/engine)
"qAk" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 8
@@ -64986,6 +65029,14 @@
},
/turf/open/floor/plasteel,
/area/science/xenobiology)
+"xXh" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/engine)
"xYV" = (
/obj/machinery/atmospherics/components/unary/outlet_injector/on{
dir = 1
@@ -86813,7 +86864,7 @@ bva
aaa
bIZ
cbb
-bSq
+cvQ
ccO
bva
xgh
@@ -87546,7 +87597,7 @@ bmd
bwt
bvb
bpu
-bvb
+osz
bvb
btQ
bvb
@@ -88852,7 +88903,7 @@ npE
nSj
cXW
bPt
-qyF
+xXh
bQU
bva
bva
@@ -94715,7 +94766,7 @@ aKT
aKT
aKT
aOy
-aLL
+mqu
aLL
aRP
aSM
@@ -101406,7 +101457,7 @@ aVp
aWs
aPW
aYn
-bIO
+cyF
bas
aLf
bcx
@@ -105008,7 +105059,7 @@ aZo
baB
bbH
bcC
-kFy
+gpX
beL
bfD
bbE
@@ -108844,7 +108895,7 @@ hUt
aFi
aFi
aFi
-aJs
+lUl
aFi
aFi
bHI
@@ -110144,7 +110195,7 @@ aEj
aEj
aEj
aTx
-aFi
+cqr
aGO
bcI
aFi
@@ -111436,7 +111487,7 @@ rWE
baG
eZA
tDn
-aFi
+cqr
nZw
aFi
gfi
@@ -114271,7 +114322,7 @@ fwI
eCK
wQU
xah
-fwI
+oFy
fwI
bqO
btB
diff --git a/_maps/map_files/SmolStation/SmolStation.dmm b/_maps/map_files/SmolStation/SmolStation.dmm
index 7533233b12..38aaa19c8f 100644
--- a/_maps/map_files/SmolStation/SmolStation.dmm
+++ b/_maps/map_files/SmolStation/SmolStation.dmm
@@ -538,7 +538,7 @@
/area/security/brig)
"auc" = (
/obj/machinery/vending/dinnerware{
- contraband = list(/obj/item/kitchen/rollingpin = 2, /obj/item/kitchen/knife/butcher = 2, /obj/item/reagent_containers/food/condiment/flour = 4)
+ contraband = list(/obj/item/kitchen/rollingpin=2,/obj/item/kitchen/knife/butcher=2,/obj/item/reagent_containers/food/condiment/flour=4)
},
/obj/machinery/camera{
c_tag = "Kitchen";
@@ -4995,6 +4995,10 @@
},
/turf/open/floor/plasteel,
/area/commons/storage/primary)
+"dsH" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/central)
"dsJ" = (
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
@@ -6894,6 +6898,10 @@
/obj/structure/reagent_dispensers/fueltank,
/turf/open/floor/plasteel,
/area/commons/storage/tools)
+"eTA" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"eTM" = (
/obj/effect/turf_decal/tile/blue{
dir = 8
@@ -7134,6 +7142,19 @@
/obj/machinery/power/tracker,
/turf/open/floor/plasteel/airless/solarpanel,
/area/space/nearstation)
+"fhi" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/central)
"fjd" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -14964,6 +14985,15 @@
/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
/area/security/brig)
+"kOh" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"kOz" = (
/obj/machinery/computer/rdconsole/core{
dir = 4
@@ -17898,6 +17928,7 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 4
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/central)
"mXP" = (
@@ -24431,6 +24462,10 @@
},
/turf/open/space/basic,
/area/space)
+"siQ" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"sjb" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1{
dir = 6
@@ -27505,6 +27540,10 @@
/obj/effect/turf_decal/tile/brown,
/turf/open/floor/plasteel,
/area/cargo/qm)
+"uuT" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"uuY" = (
/obj/machinery/light/small,
/obj/machinery/camera{
@@ -61569,7 +61608,7 @@ lOA
lOA
lOA
lOA
-lOA
+uuT
kQV
blb
nPv
@@ -64197,7 +64236,7 @@ tZL
fOl
erD
hJA
-wmr
+fhi
kea
kea
kea
@@ -64471,7 +64510,7 @@ inK
inK
kea
iQG
-iQG
+siQ
hMW
sew
hLz
@@ -66992,7 +67031,7 @@ sHB
oqW
jhl
wGd
-tgK
+dsH
jhl
saE
wyw
@@ -69277,7 +69316,7 @@ eNc
jEc
ceL
jpY
-eNc
+eTA
eNc
eNc
qas
@@ -69548,7 +69587,7 @@ nSL
nSL
nSL
nSL
-nSL
+kOh
nSL
nSL
nSL
diff --git a/_maps/map_files/SpookyStation/SpookyStation.dmm b/_maps/map_files/SpookyStation/SpookyStation.dmm
index 0db822f4f7..406c42a249 100644
--- a/_maps/map_files/SpookyStation/SpookyStation.dmm
+++ b/_maps/map_files/SpookyStation/SpookyStation.dmm
@@ -16726,6 +16726,14 @@
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
/area/eventmap/inside)
+"oWI" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/spooktime/cobble/roadmid,
+/area/eventmap/outside)
+"phL" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/spooktime/cobble/roadsideS,
+/area/eventmap/outside)
"sDL" = (
/obj/structure/sign/poster/contraband/yes_erp,
/turf/closed/wall/mineral/wood,
@@ -38243,8 +38251,8 @@ aRd
aGM
aRo
aRD
-aWA
-aKq
+oWI
+phL
aaN
aaN
aaN
@@ -38500,8 +38508,8 @@ apR
ayO
aWA
aRt
-aWA
-aKq
+oWI
+phL
aaN
aaN
aaN
@@ -38757,8 +38765,8 @@ apR
ayO
aWA
aRt
-aWA
-aKq
+oWI
+phL
aaN
aaN
aaN
@@ -39014,8 +39022,8 @@ awm
ayO
aWA
aRt
-aWA
-aKq
+oWI
+phL
aaN
aaN
aYI
@@ -39271,8 +39279,8 @@ apR
ayO
aWA
aRt
-aWA
-aKq
+oWI
+phL
aaN
aaN
aaN
@@ -39528,8 +39536,8 @@ apR
ayO
aWA
aRt
-aWA
-aKq
+oWI
+phL
aaN
aaN
aaN
diff --git a/_maps/map_files/SyndicateStation/SyndicateBoxStation.dmm b/_maps/map_files/SyndicateStation/SyndicateBoxStation.dmm
index ea70d19af7..53198a7740 100644
--- a/_maps/map_files/SyndicateStation/SyndicateBoxStation.dmm
+++ b/_maps/map_files/SyndicateStation/SyndicateBoxStation.dmm
@@ -38700,6 +38700,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 6
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/port/aft)
"bUv" = (
@@ -41781,6 +41782,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 6
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/starboard/aft)
"ccq" = (
@@ -46505,6 +46507,7 @@
/area/maintenance/disposal/incinerator)
"cqv" = (
/obj/effect/decal/cleanable/cobweb/cobweb2,
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/port/aft)
"cqw" = (
@@ -52217,6 +52220,14 @@
/obj/structure/alien/weeds,
/turf/open/floor/mineral/plastitanium,
/area/commons/dorms)
+"eYX" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"eZa" = (
/obj/effect/landmark/start/paramedic,
/turf/open/floor/mineral/plastitanium,
@@ -52983,6 +52994,18 @@
/obj/structure/lattice,
/turf/open/space/basic,
/area/space/nearstation)
+"fSu" = (
+/obj/structure/disposalpipe/segment,
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/machinery/light{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"fSO" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
/obj/machinery/atmospherics/pipe/simple/orange/visible{
@@ -54460,6 +54483,10 @@
/obj/effect/turf_decal/tile/green,
/turf/open/floor/mineral/plastitanium,
/area/hallway/primary/starboard)
+"ivb" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/fore)
"ivD" = (
/turf/open/floor/plating,
/area/space)
@@ -55501,6 +55528,10 @@
},
/turf/open/floor/mineral/plastitanium,
/area/engineering/atmos)
+"jZP" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"jZT" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -62943,6 +62974,12 @@
},
/turf/open/floor/plating,
/area/maintenance/fore)
+"vgM" = (
+/obj/structure/disposalpipe/segment,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore/secondary)
"vhy" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -64668,6 +64705,10 @@
},
/turf/open/floor/mineral/plastitanium/red,
/area/security/brig)
+"xHS" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/aft)
"xJC" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 10
@@ -84646,7 +84687,7 @@ fmJ
ygb
kiW
qlV
-amC
+ivb
ntt
nBI
qlV
@@ -85750,7 +85791,7 @@ cqn
cAh
chT
bHE
-bHE
+xHS
ckv
bHE
bCq
@@ -86764,7 +86805,7 @@ bRg
bCq
bHE
bVG
-bHE
+xHS
bHE
bCq
mqZ
@@ -89580,7 +89621,7 @@ bCn
bGq
bGq
bGq
-rGq
+fSu
bLw
bGq
bGq
@@ -89603,7 +89644,7 @@ bVI
bVI
aJq
xgk
-bHE
+xHS
kEm
bHE
bHE
@@ -99282,7 +99323,7 @@ ahT
ahT
ahT
ahT
-ahT
+vgM
ahT
alL
ahT
@@ -106489,7 +106530,7 @@ apB
aqx
art
anf
-anf
+jZP
auF
apC
awH
@@ -110092,7 +110133,7 @@ aFn
aBB
awM
ayg
-ayg
+eYX
ayg
ayg
aCl
diff --git a/_maps/map_files/TauStation/TauStation.dmm b/_maps/map_files/TauStation/TauStation.dmm
index 4d10f2a5f4..7d378ae1b3 100644
--- a/_maps/map_files/TauStation/TauStation.dmm
+++ b/_maps/map_files/TauStation/TauStation.dmm
@@ -9301,6 +9301,19 @@
/obj/structure/lattice/catwalk,
/turf/open/space/basic,
/area/space/nearstation)
+"dlX" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/science)
"dmd" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/effect/turf_decal/tile/neutral{
@@ -24862,6 +24875,15 @@
},
/turf/open/floor/engine,
/area/science/xenobiology)
+"izV" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/structure/disposalpipe/segment,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/medical)
"iAi" = (
/obj/machinery/portable_atmospherics/canister/oxygen,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
@@ -26676,6 +26698,16 @@
},
/turf/closed/wall,
/area/maintenance/department/engine)
+"jju" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/fore)
"jjE" = (
/obj/effect/spawner/structure/window,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
@@ -35201,6 +35233,13 @@
},
/turf/open/floor/plasteel/dark,
/area/hallway/primary/port)
+"miK" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"miN" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -51663,6 +51702,7 @@
/obj/structure/cable{
icon_state = "2-8"
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/department/science)
"rXk" = (
@@ -57062,6 +57102,10 @@
},
/turf/open/floor/plating/beach/sand,
/area/service/library/artgallery/museum)
+"tND" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"tNF" = (
/obj/structure/cable{
icon_state = "1-8"
@@ -61184,6 +61228,19 @@
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
+"vja" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/medical)
"vjp" = (
/obj/structure/window/reinforced,
/obj/machinery/aug_manipulator,
@@ -65490,6 +65547,13 @@
},
/turf/open/floor/plating,
/area/maintenance/department/medical)
+"wAu" = (
+/obj/structure/cable{
+ icon_state = "1-2"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
"wAv" = (
/obj/machinery/power/apc{
areastring = "/area/security/checkpoint/medical";
@@ -69362,6 +69426,10 @@
"xNa" = (
/turf/closed/wall/r_wall,
/area/science/lab)
+"xNf" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/department/science)
"xNy" = (
/obj/item/sharpener{
pixel_x = 6
@@ -92986,7 +93054,7 @@ ycW
qkf
wLl
wLl
-ycW
+izV
ycW
ycW
wfF
@@ -94534,7 +94602,7 @@ ybK
ybK
ybK
ybK
-ygg
+vja
vmP
aBv
ocO
@@ -98400,7 +98468,7 @@ yde
trt
owJ
uTQ
-ygg
+vja
xXs
evJ
xXs
@@ -106920,7 +106988,7 @@ dvp
fph
sdb
aYi
-wBz
+tND
uiQ
uiQ
uiQ
@@ -107901,7 +107969,7 @@ hce
hce
xYK
fLy
-wim
+dlX
mxU
xhN
jGo
@@ -107939,7 +108007,7 @@ qEt
oCl
wBP
tpI
-aib
+miK
sdb
nhq
dvp
@@ -110999,7 +111067,7 @@ yji
bQB
mxU
mMc
-xUV
+xNf
xUV
fOc
whY
@@ -111712,7 +111780,7 @@ vmU
eBz
thT
eBz
-vfB
+jju
eBz
vyq
wbQ
@@ -117842,7 +117910,7 @@ hpl
qJe
fbk
pkR
-ptR
+wAu
ptR
ptR
ptR
diff --git a/_maps/splurt_maps/map_files/Smexistation/Snaxi_Splurt.dmm b/_maps/splurt_maps/map_files/Smexistation/Snaxi_Splurt.dmm
index 9e3065d6a0..bbdc649804 100644
--- a/_maps/splurt_maps/map_files/Smexistation/Snaxi_Splurt.dmm
+++ b/_maps/splurt_maps/map_files/Smexistation/Snaxi_Splurt.dmm
@@ -289,13 +289,13 @@
"abk" = (
/obj/structure/closet/secure_closet/freezer/fridge,
/obj/item/reagent_containers/food/condiment/flour{
- list_reagents = list(/datum/reagent/consumable/flour = 90);
+ list_reagents = list(/datum/reagent/consumable/flour=90);
name = "large flour sack";
possible_transfer_amounts = list(1,5,10,15,30,60,90);
volume = 90
},
/obj/item/reagent_containers/food/condiment/flour{
- list_reagents = list(/datum/reagent/consumable/flour = 90);
+ list_reagents = list(/datum/reagent/consumable/flour=90);
name = "large flour sack";
possible_transfer_amounts = list(1,5,10,15,30,60,90);
volume = 90
@@ -303,7 +303,7 @@
/obj/item/reagent_containers/food/condiment/mayonnaise,
/obj/item/reagent_containers/food/condiment/milk,
/obj/item/reagent_containers/food/condiment/rice{
- list_reagents = list(/datum/reagent/consumable/rice = 90);
+ list_reagents = list(/datum/reagent/consumable/rice=90);
name = "large rice sack";
possible_transfer_amounts = list(1,5,10,15,20,25,30,50,60,90)
},
@@ -556,6 +556,11 @@
},
/turf/open/floor/plasteel/white,
/area/medical/medbay/central)
+"adG" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"adH" = (
/obj/structure/transit_tube/curved/flipped,
/turf/open/floor/plating/snowed/smoothed/icemoon,
@@ -4909,6 +4914,10 @@
},
/turf/open/floor/plasteel,
/area/cargo/miningdock)
+"brv" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/aft)
"brB" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/machinery/door/poddoor/preopen{
@@ -8936,6 +8945,7 @@
/obj/structure/cable{
icon_state = "1-8"
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/aft/secondary)
"crw" = (
@@ -8944,7 +8954,7 @@
req_access_txt = "3"
},
/obj/effect/spawner/lootdrop/armory_contraband{
- loot = list(/obj/item/gun/ballistic/automatic/pistol = 5, /obj/item/gun/ballistic/shotgun/automatic/combat = 5, /obj/item/gun/ballistic/revolver/mateba, /obj/item/gun/ballistic/automatic/pistol/deagle, /obj/item/storage/box/syndie_kit/throwing_weapons = 3)
+ loot = list(/obj/item/gun/ballistic/automatic/pistol=5,/obj/item/gun/ballistic/shotgun/automatic/combat=5,/obj/item/gun/ballistic/revolver/mateba,/obj/item/gun/ballistic/automatic/pistol/deagle,/obj/item/storage/box/syndie_kit/throwing_weapons=3)
},
/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/plasteel/dark,
@@ -18523,7 +18533,7 @@
input_tag = "mix_in2";
name = "Aux Gas Mix Tank Control";
output_tag = "mix_out2";
- sensors = list("mix_sensor2" = "Aux Gas Mix Tank ")
+ sensors = list("mix_sensor2"="Aux Gas Mix Tank ")
},
/turf/open/floor/plasteel,
/area/engineering/atmos)
@@ -28123,6 +28133,10 @@
/obj/structure/girder/displaced,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
+"kKU" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/engineering/storage)
"kKY" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -28362,7 +28376,7 @@
/area/engineering/main)
"kPA" = (
/obj/effect/spawner/lootdrop{
- loot = list(/obj/item/gun/ballistic/revolver/russian = 5, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/toy/cards/deck/syndicate = 2);
+ loot = list(/obj/item/gun/ballistic/revolver/russian=5,/obj/item/storage/box/syndie_kit/throwing_weapons,/obj/item/toy/cards/deck/syndicate=2);
name = "gambling valuables spawner"
},
/obj/structure/table/wood/poker,
@@ -41504,7 +41518,7 @@
/area/engineering/break_room)
"qzP" = (
/obj/machinery/vending/dinnerware{
- contraband = list(/obj/item/kitchen/rollingpin = 2, /obj/item/kitchen/knife/butcher = 2, /obj/item/reagent_containers/food/condiment/flour = 4)
+ contraband = list(/obj/item/kitchen/rollingpin=2,/obj/item/kitchen/knife/butcher=2,/obj/item/reagent_containers/food/condiment/flour=4)
},
/turf/open/floor/plasteel/freezer,
/area/service/kitchen/coldroom)
@@ -44091,6 +44105,10 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3,
/turf/open/floor/plasteel,
/area/hallway/primary/starboard/aft)
+"rGN" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/central)
"rGP" = (
/obj/machinery/turnstile{
dir = 8;
@@ -50728,6 +50746,10 @@
},
/turf/open/floor/plating,
/area/hallway/secondary/exit/departure_lounge)
+"uFI" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/starboard/aft)
"uFT" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 10
@@ -52569,6 +52591,7 @@
dir = 1;
light_color = "#ffc1c1"
},
+/obj/effect/landmark/start/stowaway,
/turf/open/floor/plating,
/area/maintenance/aft)
"vvH" = (
@@ -55129,6 +55152,10 @@
},
/turf/open/floor/plating,
/area/security/checkpoint/supply)
+"wvA" = (
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/aft/secondary)
"wvL" = (
/obj/machinery/cryopod{
dir = 1
@@ -56498,7 +56525,7 @@
"xcL" = (
/obj/structure/table,
/obj/effect/spawner/lootdrop{
- loot = list(/obj/item/gun/ballistic/revolver/russian = 5, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/toy/cards/deck/syndicate = 2);
+ loot = list(/obj/item/gun/ballistic/revolver/russian=5,/obj/item/storage/box/syndie_kit/throwing_weapons,/obj/item/toy/cards/deck/syndicate=2);
name = "gambling valuables spawner"
},
/turf/open/floor/plasteel,
@@ -57148,6 +57175,13 @@
},
/turf/open/floor/plasteel/dark,
/area/ai_monitored/security/armory)
+"xqY" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/aft/secondary)
"xrL" = (
/obj/machinery/light{
dir = 1
@@ -59058,6 +59092,13 @@
},
/turf/open/floor/circuit/telecomms/mainframe,
/area/tcommsat/server)
+"yle" = (
+/obj/structure/railing{
+ dir = 4
+ },
+/obj/effect/landmark/start/stowaway,
+/turf/open/floor/plating,
+/area/maintenance/port/central)
"ylx" = (
/obj/effect/turf_decal/tile/red{
dir = 8
@@ -82324,7 +82365,7 @@ nLS
nLS
nLS
xJp
-xJp
+yle
nLS
nLS
jOq
@@ -83351,7 +83392,7 @@ wje
nLS
cpX
dGu
-gdQ
+rGN
qel
rRU
nLS
@@ -91384,7 +91425,7 @@ bfa
rRa
cey
wlV
-wlV
+brv
ucZ
eTv
iIS
@@ -97042,7 +97083,7 @@ sAC
iBd
jpS
wTK
-wGq
+wvA
wTK
wGq
kSv
@@ -98584,7 +98625,7 @@ tJJ
tJJ
tnV
wTK
-jxp
+xqY
fJr
wTK
avT
@@ -108519,7 +108560,7 @@ eDt
mKy
qDt
qDt
-xJU
+adG
uoz
xLQ
vcm
@@ -110826,7 +110867,7 @@ oUQ
qRF
lnH
pwC
-fBS
+kKU
wFO
mZp
wwi
@@ -113913,7 +113954,7 @@ avT
qDt
lKk
rUk
-rUk
+uFI
qDt
qDt
qDt
diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm
index c86659d490..ca026edbf2 100644
--- a/code/__DEFINES/_flags/_flags.dm
+++ b/code/__DEFINES/_flags/_flags.dm
@@ -68,6 +68,8 @@ GLOBAL_LIST_INIT(bitflags, list(
#define NO_RUINS_1 (1<<10)
/// Should this tile be cleaned up and reinserted into an excited group?
#define EXCITED_CLEANUP_1 (1 << 13)
+/// Whether or not this atom has contextual screentips when hovered OVER
+#define HAS_CONTEXTUAL_SCREENTIPS_1 (1 << 14)
////////////////Area flags\\\\\\\\\\\\\\
/// If it's a valid territory for cult summoning or the CRAB-17 phone to spawn
diff --git a/code/__DEFINES/_flags/obj_flags.dm b/code/__DEFINES/_flags/obj_flags.dm
index 5f5ea12aaa..c3c48c7d74 100644
--- a/code/__DEFINES/_flags/obj_flags.dm
+++ b/code/__DEFINES/_flags/obj_flags.dm
@@ -17,6 +17,8 @@
#define EXAMINE_SKIP (1<<14) /// Makes the Examine proc not read out this item.
#define IN_STORAGE (1<<15) //is this item in the storage item, such as backpack? used for tooltips
#define HAND_ITEM (1<<16) // If an item is just your hand (circled hand, slapper) and shouldn't block things like riding
+/// Has contextual screentips when HOVERING OVER OTHER objects
+#define ITEM_HAS_CONTEXTUAL_SCREENTIPS (1 << 17)
/// Integrity defines for clothing (not flags but close enough)
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
diff --git a/code/__DEFINES/dcs/signals/signals_painting.dm b/code/__DEFINES/dcs/signals/signals_painting.dm
new file mode 100644
index 0000000000..4f9201ec8f
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_painting.dm
@@ -0,0 +1,7 @@
+// signals for painting canvases, tools and the /datum/component/palette component
+
+///from base of /item/proc/set_painting_tool_color(): (chosen_color)
+#define COMSIG_PAINTING_TOOL_SET_COLOR "painting_tool_set_color"
+
+/// from base of /item/canvas/ui_data(): (data)
+#define COMSIG_PAINTING_TOOL_GET_ADDITIONAL_DATA "painting_tool_get_data"
diff --git a/code/__DEFINES/dcs/signals/signals_screentips.dm b/code/__DEFINES/dcs/signals/signals_screentips.dm
new file mode 100644
index 0000000000..8f7326ee2e
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_screentips.dm
@@ -0,0 +1,23 @@
+/// A "Type-A" contextual screentip interaction.
+/// These are used for items that are defined by their behavior. They define their contextual text within *themselves*,
+/// not in their targets.
+/// Examples include syringes (LMB to inject, RMB to draw) and health analyzers (LMB to scan health/wounds, RMB for chems)
+/// Items can override `add_item_context()`, and call `register_item_context()` in order to easily connect to this.
+/// Called on /obj/item with a mutable screentip context list, the hovered target, and the mob hovering.
+/// A screentip context list is a list that has context keys (SCREENTIP_CONTEXT_*, from __DEFINES/screentips.dm)
+/// that map to the action as text.
+/// If you mutate the list in this signal, you must return CONTEXTUAL_SCREENTIP_SET.
+#define COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET "item_requesting_context_for_target"
+
+/// A "Type-B" contextual screentip interaction.
+/// These are atoms that are defined by what happens *to* them. These should define contextual text within themselves, and
+/// not in their operating tools.
+/// Examples include construction objects (LMB with glass to put in screen for computers).
+/// Called on /atom with a mutable screentip context list, the item being used, and the mob hovering.
+/// A screentip context list is a list that has context keys (SCREENTIP_CONTEXT_*, from __DEFINES/screentips.dm)
+/// that map to the action as text.
+/// If you mutate the list in this signal, you must return CONTEXTUAL_SCREENTIP_SET.
+#define COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM "atom_requesting_context_from_item"
+
+/// Tells the contextual screentips system that the list context was mutated.
+#define CONTEXTUAL_SCREENTIP_SET (1 << 0)
diff --git a/code/__DEFINES/events.dm b/code/__DEFINES/events.dm
index d39932e1a7..d189969b8e 100644
--- a/code/__DEFINES/events.dm
+++ b/code/__DEFINES/events.dm
@@ -7,3 +7,31 @@
#define EVENT_READY 1
#define EVENT_CANCELLED 2
#define EVENT_INTERRUPTED 3
+
+///Events that mess with or create artificial intelligences, such as vending machines and the AI itself
+#define EVENT_CATEGORY_AI "AI issues"
+///Events that spawn anomalies, which might be the source of anomaly cores
+#define EVENT_CATEGORY_ANOMALIES "Anomalies"
+///Events pertaining cargo, messages incoming to the station and job slots
+#define EVENT_CATEGORY_BUREAUCRATIC "Bureaucratic"
+///Events that cause breakages and malfunctions that could be fixed by engineers
+#define EVENT_CATEGORY_ENGINEERING "Engineering"
+///Events that spawn creatures with simple desires, such as to hunt
+#define EVENT_CATEGORY_ENTITIES "Entities"
+///Events that should have no harmful effects, and might be useful to the crew
+#define EVENT_CATEGORY_FRIENDLY "Friendly"
+///Events that affect the body and mind
+#define EVENT_CATEGORY_HEALTH "Health"
+///Events reserved for special occassions
+#define EVENT_CATEGORY_HOLIDAY "Holiday"
+///Events with enemy groups with a more complex plan
+#define EVENT_CATEGORY_INVASION "Invasion"
+///Events that make a mess
+#define EVENT_CATEGORY_JANITORIAL "Janitorial"
+///Events that summon meteors and other debris, and stationwide waves of harmful space weather
+#define EVENT_CATEGORY_SPACE "Space Threats"
+///Events summoned by a wizard
+#define EVENT_CATEGORY_WIZARD "Wizard"
+
+/// Return from admin setup to stop the event from triggering entirely.
+#define ADMIN_CANCEL_EVENT "cancel event"
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index cd647867c3..6d7df3e93f 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -58,9 +58,11 @@
#define ITEM_SLOT_HANDCUFFED (1<<23)
/// Legcuff slot (bolas, beartraps)
#define ITEM_SLOT_LEGCUFFED (1<<24)
+/// To attach to a jumpsuit
+#define ITEM_SLOT_ACCESSORY (1<<25)
/// Total amount of slots
-#define SLOTS_AMT 25 // Keep this up to date!
+#define SLOTS_AMT 26 // Keep this up to date!
//SLOT GROUP HELPERS
#define ITEM_SLOT_POCKETS (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)
@@ -83,7 +85,7 @@
#define HIDETAUR (1<<11) //gotta hide that snowflake
#define HIDESNOUT (1<<12) //or do we actually hide our snoots
#define HIDEACCESSORY (1<<13) //hides the jumpsuit accessory.
-//skyrat edit
+//sandstorm edit
#define HIDEUNDERWEAR (1<<14) //hides underwear, socks and shirt
#define HIDEWRISTS (1<<15) //hides wrists
//
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 49daf4147f..032ae9ded0 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -48,6 +48,7 @@
#define MIME (1<<12)
#define ASSISTANT (1<<13)
#define PRISONER (1<<14)
+#define STOWAWAY (1<<15)
#define JOB_AVAILABLE 0
#define JOB_UNAVAILABLE_GENERIC 1
@@ -101,3 +102,4 @@
#define JOB_DISPLAY_ORDER_BLUESHIELD 37
#define JOB_DISPLAY_ORDER_BO 38
#define JOB_DISPLAY_ORDER_PRISONER 39
+#define JOB_DISPLAY_ORDER_STOWAWAY 40
diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm
index 182e58c1f7..9f771df663 100644
--- a/code/__DEFINES/loadout.dm
+++ b/code/__DEFINES/loadout.dm
@@ -4,10 +4,12 @@
#define LOADOUT_SUBCATEGORY_NONE "Miscellaneous"
#define LOADOUT_SUBCATEGORIES_NONE list("Miscellaneous")
+//accessory
+#define LOADOUT_CATEGORY_ACCESSORY "Accessory"
+
//backpack
#define LOADOUT_CATEGORY_BACKPACK "In backpack"
#define LOADOUT_SUBCATEGORY_BACKPACK_GENERAL "General" //basically anything that there's not enough of to have its own subcategory
-#define LOADOUT_SUBCATEGORY_BACKPACK_ACCESSORIES "Accessories" //maybe one day someone will make loadouts have accessory compatibility
#define LOADOUT_SUBCATEGORY_BACKPACK_TOYS "Toys"
//neck
#define LOADOUT_CATEGORY_NECK "Neck"
@@ -84,7 +86,7 @@
#define LOADOUT_CAN_DESCRIPTION (1<<1) //adding a custom description to items
#define LOADOUT_CAN_COLOR_POLYCHROMIC (1<<2)
-//the names of the customization tabs
+/* the names of the customization tabs
#define SETTINGS_TAB 0
#define GAME_PREFERENCES_TAB 1
#define APPEARANCE_TAB 2
@@ -92,6 +94,7 @@
#define LOADOUT_TAB 4
#define CONTENT_PREFERENCES_TAB 5
#define KEYBINDINGS_TAB 6
+*/
//quirks
#define QUIRK_POSITIVE "Positive"
diff --git a/code/__DEFINES/screentips.dm b/code/__DEFINES/screentips.dm
new file mode 100644
index 0000000000..a3b1018352
--- /dev/null
+++ b/code/__DEFINES/screentips.dm
@@ -0,0 +1,35 @@
+/// Context applied to LMB actions
+#define SCREENTIP_CONTEXT_LMB "LMB"
+
+/// Context applied to RMB actions
+#define SCREENTIP_CONTEXT_RMB "RMB"
+
+/// Context applied to Shift-LMB actions
+#define SCREENTIP_CONTEXT_SHIFT_LMB "Shift-LMB"
+
+/// Context applied to Ctrl-LMB actions
+#define SCREENTIP_CONTEXT_CTRL_LMB "Ctrl-LMB"
+
+/// Context applied to Ctrl-RMB actions
+#define SCREENTIP_CONTEXT_CTRL_RMB "Ctrl-RMB"
+
+/// Context applied to Alt-LMB actions
+#define SCREENTIP_CONTEXT_ALT_LMB "Alt-LMB"
+
+/// Context applied to Alt-RMB actions
+#define SCREENTIP_CONTEXT_ALT_RMB "Alt-RMB"
+
+/// Context applied to Ctrl-Shift-LMB actions
+#define SCREENTIP_CONTEXT_CTRL_SHIFT_LMB "Ctrl-Shift-LMB"
+
+/// Screentips are always disabled
+#define SCREENTIP_PREFERENCE_DISABLED "Disabled"
+
+/// Screentips are always enabled
+#define SCREENTIP_PREFERENCE_ENABLED "Enabled"
+
+/// Screentips are only enabled when they have context
+#define SCREENTIP_PREFERENCE_CONTEXT_ONLY "Only with tips"
+
+/// Regardless of intent
+#define INTENT_ANY "intent_any"
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index c4252ad542..5b457dd9cc 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -9,6 +9,14 @@
* Misc
*/
+// Generic listoflist safe add and removal macros:
+///If value is a list, wrap it in a list so it can be used with list add/remove operations
+#define LIST_VALUE_WRAP_LISTS(value) (islist(value) ? list(value) : value)
+///Add an untyped item to a list, taking care to handle list items by wrapping them in a list to remove the footgun
+#define UNTYPED_LIST_ADD(list, item) (list += LIST_VALUE_WRAP_LISTS(item))
+///Remove an untyped item to a list, taking care to handle list items by wrapping them in a list to remove the footgun
+#define UNTYPED_LIST_REMOVE(list, item) (list -= LIST_VALUE_WRAP_LISTS(item))
+
#define LAZYINITLIST(L) if (!L) { L = list(); }
#define UNSETEMPTY(L) if (L && !length(L)) L = null
///Like LAZYCOPY - copies an input list if the list has entries, If it doesn't the assigned list is nulled
diff --git a/code/__HELPERS/custom_holoforms.dm b/code/__HELPERS/custom_holoforms.dm
index 8aa8d279ec..87f3bfbe8b 100644
--- a/code/__HELPERS/custom_holoforms.dm
+++ b/code/__HELPERS/custom_holoforms.dm
@@ -6,6 +6,7 @@
prefs.copy_to(mannequin)
if(apply_loadout && prefs.parent)
SSjob.equip_loadout(prefs.parent.mob, mannequin, bypass_prereqs = TRUE)
+ SSjob.post_equip_loadout(prefs.parent.mob, mannequin, bypass_prereqs = TRUE)
if(copy_job)
var/datum/job/highest = prefs.get_highest_job()
if(highest && !istype(highest, /datum/job/ai) && !istype(highest, /datum/job/cyborg))
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index fc220e60c6..38b8bdfb24 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -578,7 +578,7 @@
deadchat_broadcast(" has arrived at the station at [player_area.name] . ", "[character.real_name] ([rank]) ", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
if((!GLOB.announcement_systems.len) || (!character.mind))
return
- if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role))
+ if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role) || (character.mind.assigned_role == "Stowaway"))
return
//Skyrat changes
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index a7947f5f07..78e1899f53 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -123,6 +123,6 @@
for(var/path in typesof(/obj/item/coin))
var/obj/item/coin/C = new path
UNTIL(C.flags_1 & INITIALIZED_1) //we want to make sure the value is calculated and not null.
- GLOB.coin_values[path] = C.value
+ GLOB.coin_values[path] = C.get_item_credit_value()
qdel(C)
diff --git a/code/__HELPERS/screentips.dm b/code/__HELPERS/screentips.dm
new file mode 100644
index 0000000000..967e1c3bb6
--- /dev/null
+++ b/code/__HELPERS/screentips.dm
@@ -0,0 +1,31 @@
+// Generate intent icons
+/// Help intent icon for screentip context
+GLOBAL_DATUM_INIT(icon_intent_help, /image, image('icons/emoji.dmi', icon_state = INTENT_HELP))
+/// Disarm intent icon for screentip context
+GLOBAL_DATUM_INIT(icon_intent_disarm, /image, image('icons/emoji.dmi', icon_state = INTENT_DISARM))
+/// Grab intent icon for screentip context
+GLOBAL_DATUM_INIT(icon_intent_grab, /image, image('icons/emoji.dmi', icon_state = INTENT_GRAB))
+/// Harm intent icon for screentip context
+GLOBAL_DATUM_INIT(icon_intent_harm, /image, image('icons/emoji.dmi', icon_state = INTENT_HARM))
+
+/*
+ * # Builds context with each intent for this key
+ * Args:
+ * - context = list (REQUIRED)
+ * - key = string (REQUIRED)
+*/
+/proc/build_context(list/context, key)
+ var/list/to_add
+ for(var/intent in context[key])
+ switch(intent)
+ if(INTENT_HELP)
+ LAZYADD(to_add, "\icon[GLOB.icon_intent_help] [key]: [context[key][INTENT_HELP]]")
+ if(INTENT_DISARM)
+ LAZYADD(to_add, "\icon[GLOB.icon_intent_disarm] [key]: [context[key][INTENT_DISARM]]")
+ if(INTENT_GRAB)
+ LAZYADD(to_add, "\icon[GLOB.icon_intent_grab] [key]: [context[key][INTENT_GRAB]]")
+ if(INTENT_HARM)
+ LAZYADD(to_add, "\icon[GLOB.icon_intent_harm] [key]: [context[key][INTENT_HARM]]")
+ else // If you're adding intent-less YOU BETTER ADD IT FIRST IN THE LIST
+ LAZYADD(to_add, "[key]: [context[key][intent]]")
+ return english_list(to_add, "", " ", " ")
diff --git a/code/__SANDCODE/DEFINES/DNA.dm b/code/__SANDCODE/DEFINES/DNA.dm
new file mode 100644
index 0000000000..547ab21071
--- /dev/null
+++ b/code/__SANDCODE/DEFINES/DNA.dm
@@ -0,0 +1,3 @@
+#define PREVIEW_PREF_JOB "Job"
+#define PREVIEW_PREF_LOADOUT "Loadout"
+#define PREVIEW_PREF_NAKED "Naked"
diff --git a/code/__SANDCODE/DEFINES/dcs/signals/signals_mob_main.dm b/code/__SANDCODE/DEFINES/dcs/signals/signals_mob_main.dm
new file mode 100644
index 0000000000..9eaa655bf2
--- /dev/null
+++ b/code/__SANDCODE/DEFINES/dcs/signals/signals_mob_main.dm
@@ -0,0 +1 @@
+#define COMSIG_MOB_CTRLSHIFTCLICKON "mob_ctrlshiftclickon"
diff --git a/code/__SANDCODE/DEFINES/loadout.dm b/code/__SANDCODE/DEFINES/loadout.dm
index 3e87fca927..3d3a130334 100644
--- a/code/__SANDCODE/DEFINES/loadout.dm
+++ b/code/__SANDCODE/DEFINES/loadout.dm
@@ -13,3 +13,21 @@
#define LOADOUT_CATEGORY_WRISTS "Wrists"
/// Subcategory for watches
#define LOADOUT_SUBCATEGORY_WATCHES "Watches"
+
+//the names of the customization tabs
+#define SETTINGS_TAB 0
+#define PREFERENCES_TAB 1
+#define KEYBINDINGS_TAB 2
+
+//subtabs for the character settings tab
+#define GENERAL_CHAR_TAB 0
+#define APPEARANCE_CHAR_TAB 1
+#define MARKINGS_CHAR_TAB 2
+#define SPEECH_CHAR_TAB 3
+#define BACKGROUND_CHAR_TAB 4
+#define LOADOUT_CHAR_TAB 5
+
+//subtabs for preferences tab
+#define GAME_PREFS_TAB 0
+#define OOC_PREFS_TAB 1
+#define CONTENT_PREFS_TAB 2
diff --git a/code/__SANDCODE/DEFINES/traits.dm b/code/__SANDCODE/DEFINES/traits.dm
index ffc5af34bc..92799a3fdf 100644
--- a/code/__SANDCODE/DEFINES/traits.dm
+++ b/code/__SANDCODE/DEFINES/traits.dm
@@ -6,3 +6,8 @@
#define TRAIT_SHELTERED "sheltered"
/// Define for the quirk "infertile", self explanatory. Please make this a preference on the Content Preferences Tab.
#define TRAIT_INFERTILE "infertile"
+/// DNC trait, used to prevent cloning
+#define TRAIT_DNC_ORDER "dnc_order"
+/// Estrous traits, used for mammalian seasonal arousal systems
+#define TRAIT_ESTROUS_ACTIVE "estrous_active"
+#define TRAIT_ESTROUS_DETECT "estrous_detect"
diff --git a/code/__SANDCODE/HELPERS/sizecode.dm b/code/__SANDCODE/HELPERS/sizecode.dm
index b312b8fcc9..41c17a0ef0 100644
--- a/code/__SANDCODE/HELPERS/sizecode.dm
+++ b/code/__SANDCODE/HELPERS/sizecode.dm
@@ -14,3 +14,15 @@
return has_dna.features["body_size"]
else
return target.size_multiplier
+
+/*
+ * # COMPARE_SIZES(mob/living/user, mob/living/target)
+ * Returns how bigger or smaller the target is in comparison to user
+ * Example:
+ * - user = 2, target = 1, result = 0.5
+ * - user = 1, target = 2, result = 2
+ * Args:
+ * - user = /mob/living
+ * - target = /mob/living
+*/
+#define COMPARE_SIZES(user, target) abs((get_size(user) / get_size(target)))
diff --git a/code/__SPLURTCODE/DEFINES/preferences.dm b/code/__SPLURTCODE/DEFINES/preferences.dm
new file mode 100644
index 0000000000..ba74e8762e
--- /dev/null
+++ b/code/__SPLURTCODE/DEFINES/preferences.dm
@@ -0,0 +1 @@
+#define DEFAULT_SAVE_SLOTS 24
diff --git a/code/__SPLURTCODE/DEFINES/traits.dm b/code/__SPLURTCODE/DEFINES/traits.dm
index 9f0c41e694..4dcb8e375b 100644
--- a/code/__SPLURTCODE/DEFINES/traits.dm
+++ b/code/__SPLURTCODE/DEFINES/traits.dm
@@ -17,13 +17,13 @@
#define TRAIT_PHARMA "hepatic_pharmacokinesis"
#define TRAIT_CHOKE_SLUT "choke_slut"
-#define BLOODFLEDGE "BloodFledge"//splurt change
+#define TRAIT_BLOODFLEDGE "BloodFledge"
#define TRAIT_INCUBUS "Incubus"
#define TRAIT_SUCCUBUS "Succubus"
#define TRAIT_ARACHNID "Arachnid"
#define TRAIT_FLUTTER "flutter"
-
#define TRAIT_NUDIST "Nudist"
-
+#define TRAIT_CLOTH_EATER "cloth_eater"
+#define TRAIT_WEREWOLF "Werewolf"
diff --git a/code/__SPLURTCODE/DEFINES/zeros/traits.dm b/code/__SPLURTCODE/DEFINES/zeros/traits.dm
index 74a180621d..199962dbfd 100644
--- a/code/__SPLURTCODE/DEFINES/zeros/traits.dm
+++ b/code/__SPLURTCODE/DEFINES/zeros/traits.dm
@@ -3,9 +3,6 @@
#define TRAIT_CURSED_BLOOD "cursed_blood" //Yo dawg I heard you like bloodborne references so I put a
#define TRAIT_HEADPAT_SLUT "headpat_slut"
#define TRAIT_DISTANT "headpat_hater"
-#define TRAIT_NO_CLONE "no_clone"
-#define TRAIT_IN_HEAT "in_heat"
-#define TRAIT_HEAT_DETECT "heat_detect"
#define TRAIT_ILLITERATE "illiterate"
#define TRAIT_HIDE_BACKPACK "hide_backpack"
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 8d06d9ecf3..d16c24be44 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -68,6 +68,25 @@ DEFINE_BITFIELD(area_flags, list(
"NO_ALERTS" = NO_ALERTS,
))
+DEFINE_BITFIELD(body_parts_covered, list(
+ "ARM_LEFT" = ARM_LEFT,
+ "ARM_RIGHT" = ARM_RIGHT,
+ "ARMS" = ARMS,
+ "CHEST" = CHEST,
+ "FEET" = FEET,
+ "FOOT_LEFT" = FOOT_LEFT,
+ "FOOT_RIGHT" = FOOT_RIGHT,
+ "GROIN" = GROIN,
+ "HAND_LEFT" = HAND_LEFT,
+ "HAND_RIGHT" = HAND_RIGHT,
+ "HANDS" = HANDS,
+ "HEAD" = HEAD,
+ "LEG_LEFT" = LEG_LEFT,
+ "LEG_RIGHT" = LEG_RIGHT,
+ "LEGS" = LEGS,
+ "NECK" = NECK,
+))
+
DEFINE_BITFIELD(car_traits, list(
"CAN_KIDNAP" = CAN_KIDNAP,
))
@@ -125,12 +144,18 @@ DEFINE_BITFIELD(disease_flags, list(
"CURABLE" = CURABLE,
))
+DEFINE_BITFIELD(explosion_flags, list(
+ "EXPLOSION_FLAG_DENSITY_DEPENDENT" = EXPLOSION_FLAG_DENSITY_DEPENDENT,
+ "EXPLOSION_FLAG_HARD_OBSTACLE" = EXPLOSION_FLAG_HARD_OBSTACLE,
+))
+
DEFINE_BITFIELD(flags_1, list(
"ADMIN_SPAWNED_1" = ADMIN_SPAWNED_1,
"BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1,
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
"CONDUCT_1" = CONDUCT_1,
"DEFAULT_RICOCHET_1" = DEFAULT_RICOCHET_1,
+ "HAS_CONTEXTUAL_SCREENTIPS_1" = HAS_CONTEXTUAL_SCREENTIPS_1,
"HEAR_1" = HEAR_1,
"HOLOGRAM_1" = HOLOGRAM_1,
"INITIALIZED_1" = INITIALIZED_1,
@@ -147,6 +172,30 @@ DEFINE_BITFIELD(flags_1, list(
"UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1,
))
+DEFINE_BITFIELD(flags_cover, list(
+ "GLASSESCOVERSEYE" = GLASSESCOVERSEYES,
+ "HEADCOVERSEYES" = HEADCOVERSEYES,
+ "HEADCOVERSMOUTH" = HEADCOVERSMOUTH,
+ "MASKCOVERSEYES" = MASKCOVERSEYES,
+ "MASKCOVERSMOUTH" = MASKCOVERSMOUTH,
+))
+
+DEFINE_BITFIELD(flags_inv, list(
+ "HIDEACCESSORY" = HIDEACCESSORY,
+ "HIDEEARS" = HIDEEARS,
+ "HIDEEYES" = HIDEEYES,
+ "HIDEFACE" = HIDEFACE,
+ "HIDEFACIALHAI" = HIDEFACIALHAIR,
+ "HIDEGLOVES" = HIDEGLOVES,
+ "HIDEHAIR" = HIDEHAIR,
+ "HIDEJUMPSUIT" = HIDEJUMPSUIT,
+ "HIDEMASK" = HIDEMASK,
+ "HIDENECK" = HIDENECK,
+ "HIDESHOES" = HIDESHOES,
+ "HIDESNOUT" = HIDESNOUT,
+ "HIDESUITSTORA" = HIDESUITSTORAGE,
+ "HIDETAUR" = HIDETAUR,
+))
DEFINE_BITFIELD(flags_ricochet, list(
"RICOCHET_SHINY" = RICOCHET_SHINY,
"RICOCHET_HARD" = RICOCHET_HARD
@@ -198,12 +247,19 @@ DEFINE_BITFIELD(item_flags, list(
"IN_STORAGE" = IN_STORAGE,
"ITEM_CAN_BLOCK" = ITEM_CAN_BLOCK,
"ITEM_CAN_PARRY" = ITEM_CAN_PARRY,
+ "ITEM_HAS_CONTEXTUAL_SCREENTIPS" = ITEM_HAS_CONTEXTUAL_SCREENTIPS,
"NEEDS_PERMIT" = NEEDS_PERMIT,
"NOBLUDGEON" = NOBLUDGEON,
"NO_MAT_REDEMPTION" = NO_MAT_REDEMPTION,
"SLOWS_WHILE_IN_HAND" = SLOWS_WHILE_IN_HAND,
))
+DEFINE_BITFIELD(material_flags, list(
+ "MATERIAL_ADD_PREFIX" = MATERIAL_ADD_PREFIX,
+ "MATERIAL_AFFECT_STATISTICS" = MATERIAL_AFFECT_STATISTICS,
+ "MATERIAL_COLOR" = MATERIAL_COLOR,
+))
+
DEFINE_BITFIELD(mob_biotypes, list(
"MOB_BEAST" = MOB_BEAST,
"MOB_BUG" = MOB_BUG,
@@ -281,7 +337,21 @@ DEFINE_BITFIELD(pass_flags, list(
"PASSCLOSEDTURF" = PASSCLOSEDTURF,
"PASSGLASS" = PASSGLASS,
"PASSGRILLE" = PASSGRILLE,
+ "PASSMACHINE" = PASSMACHINE,
"PASSMOB" = PASSMOB,
+ "PASSSTRUCTURE" = PASSSTRUCTURE,
+ "PASSTABLE" = PASSTABLE,
+))
+
+DEFINE_BITFIELD(pass_flags_self, list(
+ "LETPASSTHROW" = LETPASSTHROW,
+ "PASSBLOB" = PASSBLOB,
+ "PASSCLOSEDTURF" = PASSCLOSEDTURF,
+ "PASSGLASS" = PASSGLASS,
+ "PASSGRILLE" = PASSGRILLE,
+ "PASSMACHINE" = PASSMACHINE,
+ "PASSMOB" = PASSMOB,
+ "PASSSTRUCTURE" = PASSSTRUCTURE,
"PASSTABLE" = PASSTABLE,
))
@@ -343,6 +413,30 @@ DEFINE_BITFIELD(sight, list(
"SEE_TURFS" = SEE_TURFS,
))
+DEFINE_BITFIELD(slot_flags, list(
+ "ITEM_SLOT_ACCESSORY" = ITEM_SLOT_ACCESSORY,
+ "ITEM_SLOT_BACK" = ITEM_SLOT_BACK,
+ "ITEM_SLOT_BACKPACK" = ITEM_SLOT_BACKPACK,
+ "ITEM_SLOT_BELT" = ITEM_SLOT_BELT,
+ "ITEM_SLOT_DEX_STORAGE" = ITEM_SLOT_DEX_STORAGE,
+ "ITEM_SLOT_EARS" = ITEM_SLOT_EARS,
+ "ITEM_SLOT_EYES" = ITEM_SLOT_EYES,
+ "ITEM_SLOT_FEET" = ITEM_SLOT_FEET,
+ "ITEM_SLOT_GLOVES" = ITEM_SLOT_GLOVES,
+ "ITEM_SLOT_HANDCUFFED" = ITEM_SLOT_HANDCUFFED,
+ "ITEM_SLOT_HANDS" = ITEM_SLOT_HANDS,
+ "ITEM_SLOT_HEAD" = ITEM_SLOT_HEAD,
+ "ITEM_SLOT_ICLOTHING" = ITEM_SLOT_ICLOTHING,
+ "ITEM_SLOT_ID" = ITEM_SLOT_ID,
+ "ITEM_SLOT_LEGCUFFED" = ITEM_SLOT_LEGCUFFED,
+ "ITEM_SLOT_LPOCKET" = ITEM_SLOT_LPOCKET,
+ "ITEM_SLOT_MASK" = ITEM_SLOT_MASK,
+ "ITEM_SLOT_NECK" = ITEM_SLOT_NECK,
+ "ITEM_SLOT_OCLOTHING" = ITEM_SLOT_OCLOTHING,
+ "ITEM_SLOT_RPOCKET" = ITEM_SLOT_RPOCKET,
+ "ITEM_SLOT_SUITSTORE" = ITEM_SLOT_SUITSTORE,
+))
+
DEFINE_BITFIELD(smooth, list(
"SMOOTH_BORDER" = SMOOTH_BORDER,
"SMOOTH_DIAGONAL" = SMOOTH_DIAGONAL,
@@ -351,6 +445,15 @@ DEFINE_BITFIELD(smooth, list(
"SMOOTH_TRUE" = SMOOTH_TRUE,
))
+DEFINE_BITFIELD(status_flags, list(
+ "CANKNOCKDOWN" = CANKNOCKDOWN,
+ "CANPUSH" = CANPUSH,
+ "CANSTAGGER" = CANSTAGGER,
+ "CANSTUN" = CANSTUN,
+ "CANUNCONSCIOUS" = CANUNCONSCIOUS,
+ "GODMODE" = GODMODE,
+))
+
DEFINE_BITFIELD(storage_flags, list(
"STORAGE_LIMIT_COMBINED_W_CLASS" = STORAGE_LIMIT_COMBINED_W_CLASS,
"STORAGE_LIMIT_MAX_ITEMS" = STORAGE_LIMIT_MAX_ITEMS,
@@ -369,6 +472,59 @@ DEFINE_BITFIELD(vis_flags, list(
"VIS_UNDERLAY" = VIS_UNDERLAY,
))
+DEFINE_BITFIELD(visor_flags, list(
+ "ALLOWINTERNALS" = ALLOWINTERNALS,
+ "BLOCK_GAS_SMOKE_EFFECT" = BLOCK_GAS_SMOKE_EFFECT,
+ "IGNORE_HAT_TOSS" = IGNORE_HAT_TOSS,
+ "LAVAPROTECT" = LAVAPROTECT,
+ "NOSLIP" = NOSLIP,
+ "NOSLIP_ICE" = NOSLIP_ICE,
+ "SCAN_REAGENTS" = SCAN_REAGENTS,
+ "STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE,
+ "THICKMATERIAL" = THICKMATERIAL,
+ "VOICEBOX_DISABLED" = VOICEBOX_DISABLED,
+ "VOICEBOX_TOGGLABLE" = VOICEBOX_TOGGLABLE,
+))
+
+DEFINE_BITFIELD(visor_flags_cover, list(
+ "GLASSESCOVERSEYE" = GLASSESCOVERSEYES,
+ "HEADCOVERSEYES" = HEADCOVERSEYES,
+ "HEADCOVERSMOUTH" = HEADCOVERSMOUTH,
+ "MASKCOVERSEYES" = MASKCOVERSEYES,
+ "MASKCOVERSMOUTH" = MASKCOVERSMOUTH,
+))
+
+DEFINE_BITFIELD(visor_flags_inv, list(
+ "HIDEACCESSORY" = HIDEACCESSORY,
+ "HIDEEARS" = HIDEEARS,
+ "HIDEEYES" = HIDEEYES,
+ "HIDEFACE" = HIDEFACE,
+ "HIDEFACIALHAI" = HIDEFACIALHAIR,
+ "HIDEGLOVES" = HIDEGLOVES,
+ "HIDEHAIR" = HIDEHAIR,
+ "HIDEJUMPSUIT" = HIDEJUMPSUIT,
+ "HIDEMASK" = HIDEMASK,
+ "HIDENECK" = HIDENECK,
+ "HIDESHOES" = HIDESHOES,
+ "HIDESNOUT" = HIDESNOUT,
+ "HIDESUITSTORA" = HIDESUITSTORAGE,
+ "HIDETAUR" = HIDETAUR,
+))
+
+DEFINE_BITFIELD(vore_flags, list(
+ "ABSORBABLE" = ABSORBABLE,
+ "ABSORBED" = ABSORBED,
+ "DEVOURABLE" = DEVOURABLE,
+ "DIGESTABLE" = DIGESTABLE,
+ "FEEDING" = FEEDING,
+ "LICKABLE" = LICKABLE,
+ "MOBVORE" = MOBVORE,
+ "NO_VORE" = NO_VORE,
+ "SMELLABLE" = SMELLABLE,
+ "VOREPREF_INIT" = VOREPREF_INIT,
+ "VORE_INIT" = VORE_INIT,
+))
+
DEFINE_BITFIELD(zap_flags, list(
"ZAP_ALLOW_DUPLICATES" = ZAP_ALLOW_DUPLICATES,
"ZAP_MACHINE_EXPLOSIVE" = ZAP_MACHINE_EXPLOSIVE,
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index e2996ecd7e..44d507891c 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -338,7 +338,7 @@ GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "dec
GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","slimelumi","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic", "shadekin"))
//body ids that have prosthetic sprites
-GLOBAL_LIST_INIT(prosthetic_limb_types, list("xion","bishop","cybersolutions","grayson","hephaestus","nanotrasen","talon"))
+GLOBAL_LIST_INIT(prosthetic_limb_types, list("xion","bishop","cybersolutions","grayson","hephaestus","nanotrasen","talon","veymed")) //I don't know if i can module this to splurt
//body ids that have non-gendered bodyparts
GLOBAL_LIST_INIT(nongendered_limb_types, list("fly", "zombie" ,"synth", "shadow", "cultgolem", "agent", "plasmaman", "clockgolem", "clothgolem"))
diff --git a/code/_globalvars/lists/loadout_categories.dm b/code/_globalvars/lists/loadout_categories.dm
index 371e09392b..428e5b0e32 100644
--- a/code/_globalvars/lists/loadout_categories.dm
+++ b/code/_globalvars/lists/loadout_categories.dm
@@ -1,5 +1,6 @@
GLOBAL_LIST_INIT(loadout_categories, list(
- LOADOUT_CATEGORY_BACKPACK = list(LOADOUT_SUBCATEGORY_BACKPACK_GENERAL, LOADOUT_SUBCATEGORY_BACKPACK_ACCESSORIES, LOADOUT_SUBCATEGORY_BACKPACK_TOYS),
+ LOADOUT_CATEGORY_ACCESSORY = LOADOUT_SUBCATEGORIES_NONE,
+ LOADOUT_CATEGORY_BACKPACK = list(LOADOUT_SUBCATEGORY_BACKPACK_GENERAL, LOADOUT_SUBCATEGORY_BACKPACK_TOYS),
LOADOUT_CATEGORY_NECK = list(LOADOUT_SUBCATEGORY_NECK_GENERAL, LOADOUT_SUBCATEGORY_NECK_TIE, LOADOUT_SUBCATEGORY_NECK_SCARVES),
LOADOUT_CATEGORY_MASK = LOADOUT_SUBCATEGORIES_NONE,
LOADOUT_CATEGORY_HANDS = LOADOUT_SUBCATEGORIES_NONE,
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 53366d23a6..d02ebb659e 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -49,6 +49,8 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/radio/off = 2,
/obj/item/t_scanner = 5,
/obj/item/airlock_painter = 1,
+ /obj/item/airlock_painter/decal = 1,
+ /obj/item/airlock_painter/decal/tile = 1,
/obj/item/stack/cable_coil/random = 4,
/obj/item/stack/cable_coil/random/five = 6,
/obj/item/mod/construction/broken_core = 5,
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 6e8d1de955..b3a75c024c 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -397,8 +397,10 @@
Unused except for AI
*/
/mob/proc/CtrlShiftClickOn(atom/A)
+ . = SEND_SIGNAL(src, COMSIG_MOB_CTRLSHIFTCLICKON, A)
+ if(. & COMSIG_MOB_CANCEL_CLICKON)
+ return
A.CtrlShiftClick(src)
- return
/mob/proc/ShiftMiddleClickOn(atom/A)
src.pointed(A)
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index cd8fb4b30c..593fa1a411 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -64,13 +64,15 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/// This is updated by the preference for cheaper reads than would be
/// had with a proc call, especially on one of the hottest procs in the
/// game (MouseEntered).
- var/screentips_enabled = TRUE
+ // var/screentips_enabled = SCREENTIP_PREFERENCE_ENABLED
/// The color to use for the screentips.
/// This is updated by the preference for cheaper reads than would be
/// had with a proc call, especially on one of the hottest procs in the
/// game (MouseEntered).
- var/screentip_color
+ // var/screentip_color
+
+ // We don't actually do proccalls really yet, so let's grab at prefs
var/atom/movable/screen/movable/action_button/hide_toggle/hide_actions_toggle
var/action_buttons_hidden = FALSE
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index b426db1aac..2e9ed3211f 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -241,7 +241,7 @@
if(!.)
return
- hud.mymob.client.prefs.current_tab = GAME_PREFERENCES_TAB
+ hud.mymob.client.prefs.current_tab = PREFERENCES_TAB
hud.mymob.client.prefs.ShowChoices(hud.mymob)
/atom/movable/screen/lobby/button/changelog_button
diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index 3660770596..2dd13eb1ac 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -93,41 +93,6 @@ SUBSYSTEM_DEF(events)
else if(. == EVENT_READY)
E.runEvent(random = TRUE)
-//allows a client to trigger an event
-//aka Badmin Central
-// > Not in modules/admin
-// REEEEEEEEE
-// Why the heck is this here! Took me so damn long to find!
-/client/proc/forceEvent()
- set name = "Trigger Event"
- set category = "Admin.Events"
-
- if(!holder ||!check_rights(R_FUN))
- return
-
- holder.forceEvent()
-
-/datum/admins/proc/forceEvent()
- var/dat = ""
- var/normal = ""
- var/magic = ""
- var/holiday = ""
- for(var/datum/round_event_control/E in SSevents.control)
- dat = "[E] "
- if(E.holidayID)
- holiday += dat
- else if(E.wizardevent)
- magic += dat
- else
- normal += dat
-
- dat = normal + " " + magic + " " + holiday
-
- var/datum/browser/popup = new(usr, "forceevent", "Force Random Event", 300, 750)
- popup.set_content(dat)
- popup.open()
-
-
/*
//////////////
// HOLIDAYS //
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 02c8afdfc7..01393ed0d1 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -487,7 +487,7 @@ SUBSYSTEM_DEF(job)
if(job.dresscodecompliant)// CIT CHANGE - dress code compliance
equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items
job.after_spawn(H, M.client, joined_late) // note: this happens before the mob has a key! M will always have a client, H might not.
- equip_loadout(N, H, TRUE)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works
+ post_equip_loadout(N, H)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works
handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role)
@@ -691,7 +691,7 @@ SUBSYSTEM_DEF(job)
message_admins(msg)
CRASH(msg)
-/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE, can_drop = TRUE)
+/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, bypass_prereqs = FALSE, can_drop = TRUE)
var/mob/the_mob = N
if(!the_mob)
the_mob = M // cause this doesn't get assigned if player is a latejoiner
@@ -709,9 +709,68 @@ SUBSYSTEM_DEF(job)
permitted = FALSE
if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
permitted = FALSE
- if(!equipbackpackstuff && G.slot == ITEM_SLOT_BACKPACK)//snowflake check since plopping stuff in the backpack doesnt work for pre-job equip loadout stuffs
+ if(G.handle_post_equip)
permitted = FALSE
- if(equipbackpackstuff && G.slot != ITEM_SLOT_BACKPACK)//ditto
+ if(!permitted)
+ continue
+ var/obj/item/I = new G.path
+ if(I)
+ if(length(i[LOADOUT_COLOR])) //handle loadout colors
+ //handle polychromic items
+ if((G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) && length(G.loadout_initial_colors))
+ var/datum/element/polychromic/polychromic = LAZYACCESS(I.comp_lookup, "item_worn_overlays") //stupid way to do it but GetElement does not work for this
+ if(polychromic && istype(polychromic))
+ var/list/polychromic_entry = polychromic.colors_by_atom[I]
+ if(polychromic_entry)
+ if(polychromic.suits_with_helmet_typecache[I.type]) //is this one of those toggleable hood/helmet things?
+ polychromic.connect_helmet(I,i[LOADOUT_COLOR])
+ polychromic.colors_by_atom[I] = i[LOADOUT_COLOR]
+ I.update_icon()
+ else
+ //handle non-polychromic items (they only have one color)
+ I.add_atom_colour(i[LOADOUT_COLOR][1], FIXED_COLOUR_PRIORITY)
+ I.update_icon()
+ //when inputting the data it's already sanitized
+ if(i[LOADOUT_CUSTOM_NAME])
+ var/custom_name = i[LOADOUT_CUSTOM_NAME]
+ I.name = custom_name
+ if(i[LOADOUT_CUSTOM_DESCRIPTION])
+ var/custom_description = i[LOADOUT_CUSTOM_DESCRIPTION]
+ I.desc = custom_description
+ if(!M.equip_to_slot_if_possible(I, G.slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first
+ if(iscarbon(M))
+ var/mob/living/carbon/C = M
+ var/obj/item/storage/backpack/B = C.back
+ if(!B || !SEND_SIGNAL(B, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)) // Otherwise, try to put it in the backpack, for carbons.
+ if(can_drop)
+ I.forceMove(get_turf(C))
+ else
+ qdel(I)
+ else if(!M.equip_to_slot_if_possible(I, ITEM_SLOT_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
+ if(can_drop)
+ I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
+ else
+ qdel(I)
+
+/datum/controller/subsystem/job/proc/post_equip_loadout(mob/dead/new_player/N, mob/living/M, bypass_prereqs = FALSE, can_drop = TRUE)
+ var/mob/the_mob = N
+ if(!the_mob)
+ the_mob = M // cause this doesn't get assigned if player is a latejoiner
+ var/list/chosen_gear = the_mob.client.prefs.loadout_data["SAVE_[the_mob.client.prefs.loadout_slot]"]
+ if(the_mob.client && the_mob.client.prefs && (chosen_gear && chosen_gear.len))
+ if(!ishuman(M))//no silicons allowed
+ return
+ for(var/i in chosen_gear)
+ var/datum/gear/G = istext(i[LOADOUT_ITEM]) ? text2path(i[LOADOUT_ITEM]) : i[LOADOUT_ITEM]
+ G = GLOB.loadout_items[initial(G.category)][initial(G.subcategory)][initial(G.name)]
+ if(!G)
+ continue
+ var/permitted = TRUE
+ if(!bypass_prereqs && G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles))
+ permitted = FALSE
+ if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
+ permitted = FALSE
+ if(!G.handle_post_equip)
permitted = FALSE
if(!permitted)
continue
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index f4a979c510..fe1fbdf340 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -202,7 +202,7 @@
message.maptext_height = mheight
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
message.maptext = MAPTEXT(complete_text)
- message.pixel_x = -owner.pixel_x //Dogborgs and other wide boys have a pixel offset. This accounts for that
+ message.pixel_x = -target.pixel_x //Dogborgs and other wide boys have a pixel offset. This accounts for that
// View the message
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
diff --git a/code/datums/components/dullahan.dm b/code/datums/components/dullahan.dm
index 973f06a61e..94733636c7 100644
--- a/code/datums/components/dullahan.dm
+++ b/code/datums/components/dullahan.dm
@@ -114,17 +114,17 @@
cut_overlays()
/obj/item/dullahan_head/proc/add_head_overlay(var/overlay)
- overlays_standing += overlay
+ LAZYADD(overlays_standing, overlay) //SPLURT edit
add_overlay(overlay)
/obj/item/dullahan_head/update_appearance()
if(owner && !HAS_TRAIT(owner, TRAIT_HUMAN_NO_RENDER))
- remove_head_overlays()
+ //remove_head_overlays() //SPLURT edit
// to do this without duplicating large amounts of code
// it's best to regenerate the head, then remove it once we have the overlays we want
owner.regenerate_limb(BODY_ZONE_HEAD, TRUE) // don't heal them
- owner.cut_overlays()
- owner.regenerate_icons(TRUE) // yes i know it's expensive but do you want me to rewrite our entire overlay system, also block recursive calls here by passing in TRUE (it wont go back to call update_appearance this way)
+ //owner.cut_overlays() //SPLURT edit
+ //owner.regenerate_icons(TRUE) // yes i know it's expensive but do you want me to rewrite our entire overlay system, also block recursive calls here by passing in TRUE (it wont go back to call update_appearance this way) //SPLURT edit
var/obj/item/bodypart/head/head = owner.get_bodypart(BODY_ZONE_HEAD)
if(head)
add_overlay(head.get_limb_icon(FALSE, TRUE, TRUE))
@@ -154,12 +154,8 @@
/datum/action/item_action/organ_action/dullahan/Trigger()
. = ..()
var/mob/living/carbon/human/H = owner
- var/obj/item/organ/eyes/E = owner.getorganslot(ORGAN_SLOT_EYES)
- if(E)
- if(E.tint)
- E.tint = 0
- else
- E.tint = INFINITY
+
+ toggle_monochromacy() //SPLURT edit
var/datum/component/dullahan/D = H.GetComponent(/datum/component/dullahan)
if(D)
@@ -171,8 +167,7 @@
return .
var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
if(eyes)
- H.update_tint()
- if(eyes.tint)
+ if(eyes.monochromacy_on) //SPLURT edit
H.reset_perspective(H)
else
H.reset_perspective(dullahan_head)
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 2351b7b4ba..0ca8f07c41 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -240,7 +240,7 @@
/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, client/C, datum/preferences/prefs)
set waitfor = FALSE
var/static/list/show_directions = list(SOUTH, WEST)
- if(H.mind && (H.mind.assigned_role != H.mind.special_role))
+ if(H.mind && (H.mind.assigned_role != H.mind.special_role) && (H.mind.assigned_role != "Stowaway"))
var/assignment
if(H.mind.assigned_role)
assignment = H.mind.assigned_role
diff --git a/code/datums/elements/object_reskinning.dm b/code/datums/elements/object_reskinning.dm
index 8b5d905e72..a75a818fc3 100644
--- a/code/datums/elements/object_reskinning.dm
+++ b/code/datums/elements/object_reskinning.dm
@@ -18,21 +18,20 @@
/datum/element/object_reskinning
element_flags = ELEMENT_DETACH
-/datum/element/object_reskinning/Attach(datum/target)
+/datum/element/object_reskinning/Attach(obj/target)
. = ..()
- var/obj/the_obj = target
- if(!istype(the_obj))
+ if(!istype(target))
return ELEMENT_INCOMPATIBLE
- if(!islist(the_obj.unique_reskin) || !length(the_obj.unique_reskin))
+ if(!islist(target.unique_reskin) || !length(target.unique_reskin))
message_admins("[src] was given to an object without any unique reskins, if you really need to, give it a couple skins first.")
return ELEMENT_INCOMPATIBLE
- RegisterSignal(the_obj, COMSIG_PARENT_EXAMINE, .proc/on_examine)
- RegisterSignal(the_obj, the_obj.reskin_binding, .proc/reskin)
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+ RegisterSignal(target, target.reskin_binding, .proc/reskin)
+ RegisterSignal(target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
-/datum/element/object_reskinning/Detach(datum/source, force)
- var/obj/being_deleted = source
- UnregisterSignal(source, list(COMSIG_PARENT_EXAMINE, being_deleted.reskin_binding))
+/datum/element/object_reskinning/Detach(obj/source, force)
+ UnregisterSignal(source, list(COMSIG_PARENT_EXAMINE, source.reskin_binding, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM))
return ..()
/datum/element/object_reskinning/proc/on_examine(obj/obj, mob/user, list/examine_list)
@@ -102,3 +101,19 @@
if(user.incapacitated())
return FALSE
return TRUE
+
+/datum/element/object_reskinning/proc/on_requesting_context_from_item(
+ obj/source,
+ list/context,
+ obj/item/held_item,
+ mob/living/user,
+)
+ SIGNAL_HANDLER
+
+ if(isnull(held_item))
+ switch(source.reskin_binding)
+ if(COMSIG_CLICK_CTRL_SHIFT)
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB], INTENT_ANY, "Reskin PDA")
+ else
+ LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, "Reskin [source]")
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/datums/elements/screentips/README.md b/code/datums/elements/screentips/README.md
new file mode 100644
index 0000000000..5b4ee4fd18
--- /dev/null
+++ b/code/datums/elements/screentips/README.md
@@ -0,0 +1,136 @@
+# Contextual screentips (and when to not use this folder)
+
+Contextual screentips provide information in the form of text at the top of your screen to inform you of the possibilities of an item. The "contextual" here refers to this being handled entirely through code, what it displays and when is completely up to you.
+
+## The elements (and this folder)
+
+This folder provides several useful shortcuts to be able to handle 95% of situations.
+
+### `/datum/element/contextual_screentip_bare_hands`
+
+This element is used to display screentips **when the user hovers over the object with nothing in their active hand.**
+
+It takes parameters in the form of both non-combat mode and, optionally, combat mode.
+
+Example:
+
+```dm
+/obj/machinery/firealarm/Initialize(mapload)
+ . = ..()
+
+ AddElement( \
+ /datum/element/contextual_screentip_bare_hands, \
+ lmb_text = list(INTENT_HELP = "Turn on"), \
+ rmb_text = list(INTENT_HELP = "Turn off"), \
+ )
+```
+
+This will display "LMB: Turn on | RMB: Turn off" when the user hovers over a fire alarm with an empty active hand.
+
+### `/datum/element/contextual_screentip_tools`
+
+This element takes a map of tool behaviors to [context lists](#context-lists). These will be displayed **when the user hovers over the object with an item that has the tool behavior.**
+
+Example:
+
+```dm
+/obj/structure/table/Initialize(mapload)
+ if (!(flags_1 & NODECONSTRUCT_1))
+ var/static/list/tool_behaviors = list(
+ TOOL_SCREWDRIVER = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Disassemble"),
+ ),
+
+ TOOL_WRENCH = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Deconstruct"),
+ ),
+ )
+
+ AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
+```
+
+This will display "LMB: Deconstruct" when the user hovers over a table with a wrench.
+
+### `/datum/element/contextual_screentip_item_typechecks`
+
+This element takes a map of item typepaths to [context lists](#context-lists). These will be displayed **when the user hovers over the object with the selected item.**
+
+Example:
+
+```dm
+/obj/item/restraints/handcuffs/cable/Initialize(mapload)
+ . = ..()
+
+ var/static/list/hovering_item_typechecks = list(
+ /obj/item/stack/rods = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft wired rod"),
+ ),
+
+ /obj/item/stack/sheet/iron = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft bola"),
+ ),
+ )
+
+ AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
+```
+
+This will display "LMB: Craft bola" when the user hovers over cable restraints with metal in their hand.
+
+## The basic system (and when to not use this folder)
+
+The basic system acknowledges the following two interactions:
+
+### Self-defining items (Type A)
+These are items that are defined by their behavior. These should define their contextual text within themselves, and not in their targets.
+
+- Stun batons (LMB to stun, RMB to harm)
+- Syringes (LMB to inject, RMB to draw)
+- Health analyzers (LMB to scan for health/wounds [another piece of context], RMB to scans for chemicals)
+
+### Receiving action defining objects (Type B)
+These are objects (not necessarily items) that are defined by what happens *to* them. These should define their contextual text within themselves, and not in their operating tools.
+
+- Tables (RMB with wrench to deconstruct)
+- Construction objects (LMB with glass to put in screen for computers)
+- Carbon copies (RMB to take a copy)
+
+---
+
+Both of these are supported, and can be hooked to through several means.
+
+Note that you **must return `CONTEXTUAL_SCREENTIP_SET` if you change the contextual screentip at all**, otherwise you may not see it.
+
+### `COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET`
+
+This signal is registered on **items**, and receives **the hovering object**, provided in the form of `obj/item/source, list/context, atom/target, mob/living/user`.
+
+### `/atom/proc/register_item_context()`, and `/atom/proc/add_item_context()`
+`/atom/proc/add_item_context()` is a proc intended to be overridden to easily create Type-B interactions (ones where atoms are hovered over by items). It receives the exact same arguments as `COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET`: `obj/item/source, list/context, atom/target, mob/living/user`.
+
+In order for your `add_item_context()` method to be run, you **must** call `register_item_context()`.
+
+### `COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM`
+
+This signal is registered on **atoms**, and receives **what the user is hovering with**, provided in the form of `atom/source, list/context, obj/item/held_item, mob/living/user`.
+
+### `/atom/proc/register_context()`, and `/atom/proc/add_context()`
+`/atom/proc/add_context()` is a proc intended to be overridden to easily create Type-B interactions (ones where atoms are hovered over by items). It receives the exact same arguments as `COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM`: `atom/source, list/context, obj/item/held_item, mob/living/user`.
+
+In order for your `add_context()` method to be run, you **must** call `register_context()`.
+
+---
+
+When using any of these methods, you will receive a mutable context list.
+
+### Context lists
+
+Context lists are lists with keys mapping from `SCREENTIP_CONTEXT_*` to a string. You can find these keys in `code/__DEFINES/screentips.dm`.
+
+The signals and `add_context()` variants mutate the list directly, while shortcut elements will just have you pass them in directly.
+
+For example:
+
+```dm
+context[SCREENTIP_CONTEXT_LMB] = "Open"
+context[SCREENTIP_CONTEXT_RMB] = "Destroy"
+```
diff --git a/code/datums/elements/screentips/contextual_screentip_bare_hands.dm b/code/datums/elements/screentips/contextual_screentip_bare_hands.dm
new file mode 100644
index 0000000000..98ef45af3f
--- /dev/null
+++ b/code/datums/elements/screentips/contextual_screentip_bare_hands.dm
@@ -0,0 +1,92 @@
+/// Apply basic contextual screentips when the user hovers over this item with an empty hand.
+/// A "Type B" interaction.
+/// This stacks with other contextual screentip elements, though you may want to register the signal/flag manually at that point for performance.
+/datum/element/contextual_screentip_bare_hands
+ element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
+ id_arg_index = 3
+
+ /* How to use SandPoot's version of this:
+ *
+ * Combat mode will be checked first, then the intents for it, if the
+ * current intent has not been set it defaults to the first item of the list.
+ *
+ * Otherwise if not in combat mode or no messages for it
+ * will also try to get messages for the current intent
+ * if failing to, will try to get the first item of the list.
+ */
+
+ /// If set, the text to show for LMB
+ var/list/lmb_text
+
+ /// If set, the text to show for RMB
+ var/list/rmb_text
+
+ /// If set, the text to show for LMB when in combat mode. Otherwise, defaults to lmb_text.
+ var/list/lmb_text_combat_mode
+
+ /// If set, the text to show for RMB when in combat mode. Otherwise, defaults to rmb_text.
+ var/list/rmb_text_combat_mode
+
+// If you're curious about `use_named_parameters`, it's because you should use named parameters!
+// AddElement(/datum/element/contextual_screentip_bare_hands, lmb_text = list(INTENT_HELP = "Do the thing"))
+/datum/element/contextual_screentip_bare_hands/Attach(
+ datum/target,
+ use_named_parameters,
+ lmb_text,
+ rmb_text,
+ lmb_text_combat_mode,
+ rmb_text_combat_mode,
+)
+ . = ..()
+ if (!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ if (!isnull(use_named_parameters))
+ CRASH("Use named parameters instead of positional ones.")
+
+ src.lmb_text = lmb_text
+ src.rmb_text = rmb_text
+ src.lmb_text_combat_mode = lmb_text_combat_mode || lmb_text
+ src.rmb_text_combat_mode = rmb_text_combat_mode || rmb_text
+
+ var/atom/atom_target = target
+ atom_target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(atom_target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
+
+/datum/element/contextual_screentip_bare_hands/Detach(datum/source, ...)
+ UnregisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)
+
+ // We don't remove HAS_CONTEXTUAL_SCREENTIPS_1, since there could be other stuff still hooked to it,
+ // and being set without signals is not dangerous, just less performant.
+ // A lot of things don't do this, perhaps make a proc that checks if any signals are still set, and if not,
+ // remove the flag.
+
+ return ..()
+
+/datum/element/contextual_screentip_bare_hands/proc/on_requesting_context_from_item(
+ datum/source,
+ list/context,
+ obj/item/held_item,
+ mob/living/user,
+)
+ SIGNAL_HANDLER
+
+ if (!isnull(held_item))
+ return NONE
+
+ var/combat_mode = SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)
+
+ // Combat lmb
+ if(combat_mode && length(lmb_text_combat_mode))
+ context[SCREENTIP_CONTEXT_LMB] = lmb_text_combat_mode
+ // LMB
+ else if(length(lmb_text))
+ context[SCREENTIP_CONTEXT_LMB] = lmb_text
+ // Combat rmb
+ if(combat_mode && length(rmb_text_combat_mode))
+ context[SCREENTIP_CONTEXT_RMB] = rmb_text_combat_mode
+ // RMB
+ else if(length(rmb_text))
+ context[SCREENTIP_CONTEXT_RMB] = rmb_text
+
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/datums/elements/screentips/contextual_screentip_item_typechecks.dm b/code/datums/elements/screentips/contextual_screentip_item_typechecks.dm
new file mode 100644
index 0000000000..44ff1f3190
--- /dev/null
+++ b/code/datums/elements/screentips/contextual_screentip_item_typechecks.dm
@@ -0,0 +1,47 @@
+/// Apply basic contextual screentips when the user hovers over this item with a provided item.
+/// A "Type B" interaction.
+/// This stacks with other contextual screentip elements, though you may want to register the signal/flag manually at that point for performance.
+/datum/element/contextual_screentip_item_typechecks
+ element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
+ id_arg_index = 2
+
+ /// Map of item paths to contexts to usages
+ var/list/item_paths_to_contexts
+
+/datum/element/contextual_screentip_item_typechecks/Attach(datum/target, item_paths_to_contexts)
+ . = ..()
+ if (!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.item_paths_to_contexts = item_paths_to_contexts
+
+ var/atom/atom_target = target
+ atom_target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(atom_target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
+
+/datum/element/contextual_screentip_item_typechecks/Detach(datum/source, ...)
+ UnregisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)
+
+ // We don't remove HAS_CONTEXTUAL_SCREENTIPS_1, since there could be other stuff still hooked to it,
+ // and being set without signals is not dangerous, just less performant.
+ // A lot of things don't do this, perhaps make a proc that checks if any signals are still set, and if not,
+ // remove the flag.
+
+ return ..()
+
+/datum/element/contextual_screentip_item_typechecks/proc/on_requesting_context_from_item(
+ datum/source,
+ list/context,
+ obj/item/held_item,
+)
+ SIGNAL_HANDLER
+
+ if (isnull(held_item))
+ return NONE
+
+ for (var/item_path in item_paths_to_contexts)
+ if (istype(held_item, item_path))
+ context += item_paths_to_contexts[item_path]
+ return CONTEXTUAL_SCREENTIP_SET
+
+ return NONE
diff --git a/code/datums/elements/screentips/contextual_screentip_sharpness.dm b/code/datums/elements/screentips/contextual_screentip_sharpness.dm
new file mode 100644
index 0000000000..0ee89f2fd7
--- /dev/null
+++ b/code/datums/elements/screentips/contextual_screentip_sharpness.dm
@@ -0,0 +1,57 @@
+/// Apply basic contextual screentips when the user hovers over this item with an item of the given tool behavior.
+/// A "Type B" interaction.
+/// This stacks with other contextual screentip elements, though you may want to register the signal/flag manually at that point for performance.
+/datum/element/contextual_screentip_sharpness
+ element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
+ id_arg_index = 2
+
+ /// If set, the text to show for LMB
+ var/list/lmb_text
+
+ /// If set, the text to show for RMB
+ var/list/rmb_text
+
+/datum/element/contextual_screentip_sharpness/Attach(datum/target, lmb_text, rmb_text)
+ . = ..()
+ if (!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.lmb_text = lmb_text
+ src.rmb_text = rmb_text
+
+ var/atom/atom_target = target
+ atom_target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(atom_target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
+
+/datum/element/contextual_screentip_sharpness/Detach(datum/source, ...)
+ UnregisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)
+
+ // We don't remove HAS_CONTEXTUAL_SCREENTIPS_1, since there could be other stuff still hooked to it,
+ // and being set without signals is not dangerous, just less performant.
+ // A lot of things don't do this, perhaps make a proc that checks if any signals are still set, and if not,
+ // remove the flag.
+
+ return ..()
+
+/datum/element/contextual_screentip_sharpness/proc/on_requesting_context_from_item(
+ datum/source,
+ list/context,
+ obj/item/held_item,
+)
+ SIGNAL_HANDLER
+
+ if (isnull(held_item))
+ return NONE
+
+ var/sharpness = held_item.get_sharpness()
+ if (!sharpness)
+ return NONE
+
+ if (length(lmb_text))
+ context[SCREENTIP_CONTEXT_LMB] = lmb_text
+
+ if (length(rmb_text))
+ context[SCREENTIP_CONTEXT_RMB] = rmb_text
+
+ return CONTEXTUAL_SCREENTIP_SET
+
diff --git a/code/datums/elements/screentips/contextual_screentip_tools.dm b/code/datums/elements/screentips/contextual_screentip_tools.dm
new file mode 100644
index 0000000000..a0850f8742
--- /dev/null
+++ b/code/datums/elements/screentips/contextual_screentip_tools.dm
@@ -0,0 +1,48 @@
+/// Apply basic contextual screentips when the user hovers over this item with an item of the given tool behavior.
+/// A "Type B" interaction.
+/// This stacks with other contextual screentip elements, though you may want to register the signal/flag manually at that point for performance.
+/datum/element/contextual_screentip_tools
+ element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
+ id_arg_index = 2
+
+ /// Map of tool behaviors to contexts to usages
+ var/list/tool_behaviors
+
+/datum/element/contextual_screentip_tools/Attach(datum/target, tool_behaviors)
+ . = ..()
+ if (!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.tool_behaviors = tool_behaviors
+
+ var/atom/atom_target = target
+ atom_target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(atom_target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
+
+/datum/element/contextual_screentip_tools/Detach(datum/source, ...)
+ UnregisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)
+
+ // We don't remove HAS_CONTEXTUAL_SCREENTIPS_1, since there could be other stuff still hooked to it,
+ // and being set without signals is not dangerous, just less performant.
+ // A lot of things don't do this, perhaps make a proc that checks if any signals are still set, and if not,
+ // remove the flag.
+
+ return ..()
+
+/datum/element/contextual_screentip_tools/proc/on_requesting_context_from_item(
+ datum/source,
+ list/context,
+ obj/item/held_item,
+)
+ SIGNAL_HANDLER
+
+ if (isnull(held_item))
+ return NONE
+
+ var/tool_behavior = held_item.tool_behaviour
+ if (!(tool_behavior in tool_behaviors))
+ return NONE
+
+ context += tool_behaviors[tool_behavior]
+
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/datums/screentips/atom_context.dm b/code/datums/screentips/atom_context.dm
new file mode 100644
index 0000000000..ebab6b155a
--- /dev/null
+++ b/code/datums/screentips/atom_context.dm
@@ -0,0 +1,25 @@
+/// Create a "Type-B" contextual screentip interaction, registering to `add_context()`.
+/// This will run `add_context()` when the atom is hovered over by an item for context.
+/// `add_context()` will *not* be called unless this is run.
+/// This is not necessary for Type-B interactions, as you can just apply the flag and register to the signal yourself.
+/atom/proc/register_context()
+ flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(src, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/add_context, override = TRUE)
+
+/// Creates a "Type-B" contextual screentip interaction.
+/// When a user hovers over this, this proc will be called in order
+/// to provide context for contextual screentips.
+/// You must call `register_context()` in order for this to be registered.
+/// A screentip context list is a list that has context keys (SCREENTIP_CONTEXT_*, from __DEFINES/screentips.dm)
+/// that map to the action as text.
+/// If you mutate the list in this signal, you must return CONTEXTUAL_SCREENTIP_SET.
+/// `source` can, in all cases, be replaced with `src`, and only exists because this proc directly connects to a signal.
+/atom/proc/add_context(
+ atom/source,
+ list/context,
+ obj/item/held_item,
+ mob/living/user,
+)
+ SIGNAL_HANDLER
+
+ return NONE
diff --git a/code/datums/screentips/item_context.dm b/code/datums/screentips/item_context.dm
new file mode 100644
index 0000000000..239cd18683
--- /dev/null
+++ b/code/datums/screentips/item_context.dm
@@ -0,0 +1,29 @@
+/// Create a "Type-A" contextual screentip interaction, registering to `add_item_context()`.
+/// This will run `add_item_context()` when the item hovers over another object for context.
+/// `add_item_context()` will *not* be called unless this is run.
+/// This is not necessary for Type-A interactions, as you can just apply the flag and register to the signal yourself.
+/obj/item/proc/register_item_context()
+ item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
+ RegisterSignal(
+ src,
+ COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET,
+ .proc/add_item_context,
+ )
+
+/// Creates a "Type-A" contextual screentip interaction.
+/// When a user hovers over something with this item in hand, this proc will be called in order
+/// to provide context for contextual screentips.
+/// You must call `register_item_context()` in order for this to be registered.
+/// A screentip context list is a list that has context keys (SCREENTIP_CONTEXT_*, from __DEFINES/screentips.dm)
+/// that map to the action as text.
+/// If you mutate the list in this signal, you must return CONTEXTUAL_SCREENTIP_SET.
+/// `source` can, in all cases, be replaced with `src`, and only exists because this proc directly connects to a signal.
+/obj/item/proc/add_item_context(
+ obj/item/source,
+ list/context,
+ atom/target,
+ mob/living/user,
+)
+ SIGNAL_HANDLER
+
+ return NONE
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index 5b00d2e71e..26bc6746ad 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -128,7 +128,7 @@
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/camera/camera = new(get_turf(H))
H.put_in_hands(camera)
- H.equip_to_slot(camera, ITEM_SLOT_NECK)
+ H.equip_to_slot(camera, ITEM_SLOT_BACKPACK) //SPLURT Edit
H.regenerate_icons()
/datum/quirk/selfaware
@@ -190,7 +190,7 @@
// Get targets
var/obj/item/organ/eyes/old_eyes = quirk_holder.getorganslot(ORGAN_SLOT_EYES)
var/obj/item/organ/eyes/robotic/glow/new_eyes = new
-
+
// Replace eyes
qdel(old_eyes)
new_eyes.Insert(quirk_holder)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 5b26a67cae..08d8796455 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1467,11 +1467,97 @@
/atom/MouseEntered(location, control, params)
. = ..()
// Screentips
- var/client/client = usr?.client
- var/datum/hud/active_hud = client?.mob?.hud_used
+ var/mob/user = usr
+ if(isnull(user) && !user.client)
+ return
+
+ var/datum/hud/active_hud = user.hud_used
if(active_hud)
- if(!client.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1))
+ var/screentips_enabled = user.client.prefs.screentip_pref
+ if(screentips_enabled == SCREENTIP_PREFERENCE_DISABLED || (flags_1 & NO_SCREENTIPS_1))
active_hud.screentip_text.maptext = ""
else
- //We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
- active_hud.screentip_text.maptext = MAPTEXT("[name] ")
+ active_hud.screentip_text.maptext_y = 0
+ var/lmb_rmb_line = ""
+ var/ctrl_lmb_ctrl_rmb_line = ""
+ var/alt_lmb_alt_rmb_line = ""
+ var/shift_lmb_ctrl_shift_lmb_line = ""
+ var/extra_lines = 0
+ var/extra_context = ""
+
+ if (isliving(user) || isovermind(user) || isaicamera(user))
+ var/obj/item/held_item = user.get_active_held_item()
+
+ if (flags_1 & HAS_CONTEXTUAL_SCREENTIPS_1 || held_item?.item_flags & ITEM_HAS_CONTEXTUAL_SCREENTIPS)
+ var/list/context = list()
+
+ var/contextual_screentip_returns = \
+ SEND_SIGNAL(src, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, context, held_item, user) \
+ | (held_item && SEND_SIGNAL(held_item, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, context, src, user))
+
+ if (contextual_screentip_returns & CONTEXTUAL_SCREENTIP_SET)
+ // LMB and RMB on one line...
+ var/lmb_text = ""
+ if((SCREENTIP_CONTEXT_LMB in context) && (length(context[SCREENTIP_CONTEXT_LMB]) > 0))
+ lmb_text = build_context(context, SCREENTIP_CONTEXT_LMB)
+ var/rmb_text = ""
+ if((SCREENTIP_CONTEXT_RMB in context) && (length(context[SCREENTIP_CONTEXT_RMB]) > 0))
+ rmb_text = build_context(context, SCREENTIP_CONTEXT_RMB)
+
+ if (lmb_text)
+ lmb_rmb_line = lmb_text
+ if (rmb_text)
+ lmb_rmb_line += " | [rmb_text]"
+ else if (rmb_text)
+ lmb_rmb_line = rmb_text
+
+ // Ctrl-LMB, Ctrl-RMB on one line...
+ if (lmb_rmb_line != "")
+ lmb_rmb_line += " "
+ extra_lines++
+ if((SCREENTIP_CONTEXT_CTRL_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_LMB]) > 0))
+ ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_LMB)
+
+ if((SCREENTIP_CONTEXT_CTRL_RMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_RMB]) > 0))
+ if (ctrl_lmb_ctrl_rmb_line != "")
+ ctrl_lmb_ctrl_rmb_line += " | "
+ ctrl_lmb_ctrl_rmb_line += "[SCREENTIP_CONTEXT_CTRL_RMB]: [context[SCREENTIP_CONTEXT_CTRL_RMB]]"
+ ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_RMB)
+
+ // Alt-LMB, Alt-RMB on one line...
+ if (ctrl_lmb_ctrl_rmb_line != "")
+ ctrl_lmb_ctrl_rmb_line += " "
+ extra_lines++
+ if((SCREENTIP_CONTEXT_ALT_LMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_LMB]) > 0))
+ alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_LMB)
+ if((SCREENTIP_CONTEXT_ALT_RMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_RMB]) > 0))
+ if (alt_lmb_alt_rmb_line != "")
+ alt_lmb_alt_rmb_line += " | "
+ alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_RMB)
+
+ // Shift-LMB, Ctrl-Shift-LMB on one line...
+ if (alt_lmb_alt_rmb_line != "")
+ alt_lmb_alt_rmb_line += " "
+ extra_lines++
+ if((SCREENTIP_CONTEXT_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_SHIFT_LMB]) > 0))
+ shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_SHIFT_LMB)
+
+ if((SCREENTIP_CONTEXT_CTRL_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]) > 0))
+ if (shift_lmb_ctrl_shift_lmb_line != "")
+ shift_lmb_ctrl_shift_lmb_line += " | "
+ shift_lmb_ctrl_shift_lmb_line += "[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]: [context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]]"
+ shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_SHIFT_LMB)
+
+ if (shift_lmb_ctrl_shift_lmb_line != "")
+ extra_lines++
+
+ if(extra_lines)
+ extra_context = "[lmb_rmb_line][ctrl_lmb_ctrl_rmb_line][alt_lmb_alt_rmb_line][shift_lmb_ctrl_shift_lmb_line] "
+ //first extra line pushes atom name line up 10px, subsequent lines push it up 9px, this offsets that and keeps the first line in the same place
+ active_hud.screentip_text.maptext_y = -10 + (extra_lines - 1) * -9
+
+ if (screentips_enabled == SCREENTIP_PREFERENCE_CONTEXT_ONLY && extra_context == "")
+ active_hud.screentip_text.maptext = ""
+ else
+ //We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
+ active_hud.screentip_text.maptext = "[name][extra_context] "
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 30363e6c01..6c6b31fbfd 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -606,6 +606,16 @@
animate(time = 1)
animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT)
+/// Common proc used by painting tools like spraycans and palettes that can access the entire 24 bits color space.
+/obj/item/proc/pick_painting_tool_color(mob/user, default_color)
+ var/chosen_color = input(user,"Pick new color", "[src]", default_color) as color|null
+ if(!chosen_color || QDELETED(src) || IS_DEAD_OR_INCAP(user) || !user.is_holding(src))
+ return
+ set_painting_tool_color(chosen_color)
+
+/obj/item/proc/set_painting_tool_color(chosen_color)
+ SEND_SIGNAL(src, COMSIG_PAINTING_TOOL_SET_COLOR, chosen_color)
+
/atom/movable/vv_get_dropdown()
. = ..()
. += "Follow "
diff --git a/code/game/machinery/aug_manipulator.dm b/code/game/machinery/aug_manipulator.dm
index c34e0a2d4f..cbece7626e 100644
--- a/code/game/machinery/aug_manipulator.dm
+++ b/code/game/machinery/aug_manipulator.dm
@@ -15,6 +15,7 @@
"Talon" = 'icons/mob/augmentation/cosmetic_prosthetic/talon.dmi',
"Nanotrasen" = 'icons/mob/augmentation/cosmetic_prosthetic/nanotrasen.dmi',
"Hephaesthus" = 'icons/mob/augmentation/cosmetic_prosthetic/hephaestus.dmi',
+ "Veymed" = 'icons/mob/augmentation/cosmetic_prosthetic/veymed.dmi', //i don't know if i can module this either
"Bishop" = 'icons/mob/augmentation/cosmetic_prosthetic/bishop.dmi',
"Xion" = 'icons/mob/augmentation/cosmetic_prosthetic/xion.dmi',
"Grayson" = 'icons/mob/augmentation/cosmetic_prosthetic/grayson.dmi',
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index bd7ae64e16..92040e3647 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -61,13 +61,16 @@
/obj/machinery/computer/aifixer/proc/Fix()
use_power(1000)
- occupier.adjustOxyLoss(-1, 0)
- occupier.adjustFireLoss(-1, 0)
- occupier.adjustToxLoss(-1, 0)
- occupier.adjustBruteLoss(-1, 0)
+ occupier.adjustOxyLoss(-1, FALSE, FALSE)
+ occupier.adjustFireLoss(-1, FALSE, FALSE)
+ occupier.adjustBruteLoss(-5, FALSE)
+
occupier.updatehealth()
if(occupier.health >= 0 && occupier.stat == DEAD)
- occupier.revive()
+ occupier.revive(full_heal = FALSE, admin_revive = FALSE)
+ if(!occupier.radio_enabled)
+ occupier.radio_enabled = TRUE
+ to_chat(occupier, span_warning("Your Subspace Transceiver has been enabled!"))
return occupier.health < 100
/obj/machinery/computer/aifixer/process()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 7eea720e5e..2986e67999 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -31,6 +31,11 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/toy/toy_xeno = ARCADE_WEIGHT_TRICK,
/obj/item/toy/windupToolbox = ARCADE_WEIGHT_TRICK,
+ // SPLURT EDIT
+ /obj/item/handmirror/split_personality = ARCADE_WEIGHT_TRICK,
+ /obj/item/toy/figure/assistant/imaginary_friend = ARCADE_WEIGHT_TRICK,
+ // END SPLURT EDIT
+
/mob/living/simple_animal/bot/secbot/grievous/toy = ARCADE_WEIGHT_RARE,
/obj/item/clothing/mask/facehugger/toy = ARCADE_WEIGHT_RARE,
/obj/item/gun/ballistic/automatic/toy/pistol/unrestricted = ARCADE_WEIGHT_TRICK,
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index a16e70155f..50ad14d0fc 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -28,6 +28,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
var/list/blacklisted = list(
"AI",
"Assistant",
+ "Stowaway",
"Cyborg",
"Captain",
"Head of Personnel",
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 031d730be6..91211d3d40 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -502,10 +502,6 @@
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
/obj/machinery/computer/cloning/proc/can_scan(datum/dna/dna, mob/living/mob_occupant, experimental = FALSE, datum/bank_account/account)
- if(HAS_TRAIT(mob_occupant, TRAIT_NO_CLONE))
- scantemp = "Subject has an active DNC record on file. Unable to clone."
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- return
if(!istype(dna))
scantemp = "Unable to locate valid genetic data."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index eca5fc4ce3..8921232c06 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -158,7 +158,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
if (I)
name = I.registered_name
assignment = I.assignment
- ijob = jobs[I.assignment]
+ ijob = jobs[GetJobName(I.assignment)]
else
name = "Unknown"
assignment = ""
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 45bb9bd327..d5b47d45f6 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -18,6 +18,8 @@
var/sortBy = "name"
var/order = 1 // -1 = Descending - 1 = Ascending
+ var/list/logged_access
+
light_color = LIGHT_COLOR_RED
/obj/machinery/computer/secure_data/syndie
@@ -295,6 +297,9 @@ What a mess.*/
screen = null
active1 = null
active2 = null
+
+ logged_access = null
+
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
if("Log In")
@@ -307,18 +312,27 @@ What a mess.*/
authenticated = borg.name
rank = "AI"
screen = 1
+
+ logged_access = get_all_accesses()
+
else if(IsAdminGhost(M))
active1 = null
active2 = null
authenticated = M.client.holder.admin_signature
rank = "Central Command"
screen = 1
+
+ logged_access = get_centcom_access("CentCom Commander")
+
else if(I && check_access(I))
active1 = null
active2 = null
authenticated = I.registered_name
rank = I.assignment
screen = 1
+
+ logged_access = I.access
+
else
to_chat(usr, "Unauthorized Access. ")
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
@@ -583,7 +597,7 @@ What a mess.*/
active1.fields["age"] = t1
if("species")
if(istype(active1, /datum/data/record))
- var/t1 = input("Select a species", "Species Selection") as null|anything in GLOB.roundstart_races
+ var/t1 = stripped_input("Please input species name", "Sec. records", active1.fields["species"], null)
if(!canUseSecurityRecordsConsole(usr, t1, a1))
return
active1.fields["species"] = t1
@@ -678,9 +692,7 @@ What a mess.*/
temp += "
Discharged "
temp += ""
if("rank")
- var/list/L = list( "Head of Personnel", "Captain", "AI", "Central Command" )
- //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N
- if((istype(active1, /datum/data/record) && L.Find(rank)))
+ if(istype(active1, /datum/data/record) && ((ACCESS_CAPTAIN in logged_access) || (ACCESS_HOP in logged_access)))
temp = "Rank: "
temp += ""
for(var/rank in get_all_jobs())
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 84e7b93e1b..0b31ecdf9f 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -697,6 +697,42 @@
. += "Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it. "
. += "Ctrl-Shift-click [src] to [ emergency ? "disable" : "enable"] emergency access. "
+/obj/machinery/door/airlock/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if(istype(held_item, /obj/item/stack/sheet/plasteel))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Reinforce")
+ return CONTEXTUAL_SCREENTIP_SET
+
+ switch (held_item?.tool_behaviour)
+ if (TOOL_CROWBAR)
+ if (panel_open)
+ if (security_level == AIRLOCK_SECURITY_PLASTEEL_O_S || security_level == AIRLOCK_SECURITY_PLASTEEL_I_S)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Remove shielding")
+ return CONTEXTUAL_SCREENTIP_SET
+ else if (should_try_removing_electronics())
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Remove electronics")
+ return CONTEXTUAL_SCREENTIP_SET
+
+ // Not always contextually true, but is contextually false in ways that make gameplay interesting.
+ // For example, trying to pry open an airlock, only for the bolts to be down and the lights off.
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Pry open")
+
+ return CONTEXTUAL_SCREENTIP_SET
+ if (TOOL_WELDER)
+ LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Weld shut")
+
+ if (panel_open)
+ switch (security_level)
+ if (AIRLOCK_SECURITY_METAL, AIRLOCK_SECURITY_PLASTEEL_I, AIRLOCK_SECURITY_PLASTEEL_O)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Cut shielding")
+ return CONTEXTUAL_SCREENTIP_SET
+
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Repair")
+ return CONTEXTUAL_SCREENTIP_SET
+
+ return .
+
/obj/machinery/door/airlock/attack_ai(mob/user)
if(!src.canAIControl(user))
if(src.canAIHack())
@@ -1024,6 +1060,31 @@
return
return !operating && density
+/// Returns if a crowbar would remove the airlock electronics
+/obj/machinery/door/airlock/proc/should_try_removing_electronics()
+ if (security_level != 0)
+ return FALSE
+
+ if (!panel_open)
+ return FALSE
+
+ if (obj_flags & EMAGGED)
+ return TRUE
+
+ if (!density)
+ return FALSE
+
+ if (!welded)
+ return FALSE
+
+ if (hasPower())
+ return FALSE
+
+ if (locked)
+ return FALSE
+
+ return TRUE
+
/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user)
var/beingcrowbarred = null
if(I.tool_behaviour == TOOL_CROWBAR)
@@ -1042,9 +1103,9 @@
charge.forceMove(get_turf(user))
charge = null
return
- if(beingcrowbarred && panel_open && ((obj_flags & EMAGGED) || (density && welded && !operating && !hasPower() && !locked)))
- user.visible_message("[user] removes the electronics from the airlock assembly.", \
- "You start to remove electronics from the airlock assembly... ")
+ if(beingcrowbarred && should_try_removing_electronics() && !operating)
+ user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
+ span_notice("You start to remove electronics from the airlock assembly..."))
if(I.use_tool(src, user, 40, volume=100))
deconstruct(TRUE, user)
return
@@ -1201,81 +1262,29 @@
locked = TRUE
return
-
-/obj/machinery/door/airlock/proc/change_paintjob(obj/item/airlock_painter/W, mob/user)
- if(!W.can_use(user))
+// gets called when a player uses an airlock painter on this airlock
+/obj/machinery/door/airlock/proc/change_paintjob(obj/item/airlock_painter/painter, mob/user)
+ if((!in_range(src, user) && loc != user) || !painter.can_use(user)) // user should be adjacent to the airlock, and the painter should have a toner cartridge that isn't empty
return
- var/list/optionlist
- if(airlock_material == "glass")
- optionlist = list("Standard", "Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Science", "Virology", "Mining", "Maintenance", "External", "External Maintenance")
- else
- optionlist = list("Standard", "Public", "Engineering", "Atmospherics", "Security", "Command", "Medical", "Research", "Freezer", "Science", "Virology", "Mining", "Maintenance", "External", "External Maintenance")
-
- var/paintjob = input(user, "Please select a paintjob for this airlock.") in optionlist
- if((!in_range(src, usr) && src.loc != usr) || !W.use_paint(user))
+ // reads from the airlock painter's `available paintjob` list. lets the player choose a paint option, or cancel painting
+ var/current_paintjob = tgui_input_list(user, "Paintjob for this airlock", "Customize", sortList(painter.available_paint_jobs))
+ if(isnull(current_paintjob)) // if the user clicked cancel on the popup, return
return
- switch(paintjob)
- if("Standard")
- icon = 'icons/obj/doors/airlocks/station/public.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly
- if("Public")
- icon = 'icons/obj/doors/airlocks/station2/glass.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_public
- if("Engineering")
- icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_eng
- if("Atmospherics")
- icon = 'icons/obj/doors/airlocks/station/atmos.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_atmo
- if("Security")
- icon = 'icons/obj/doors/airlocks/station/security.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_sec
- if("Command")
- icon = 'icons/obj/doors/airlocks/station/command.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_com
- if("Medical")
- icon = 'icons/obj/doors/airlocks/station/medical.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_med
- if("Research")
- icon = 'icons/obj/doors/airlocks/station/research.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_research
- if("Freezer")
- icon = 'icons/obj/doors/airlocks/station/freezer.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_fre
- if("Science")
- icon = 'icons/obj/doors/airlocks/station/science.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_science
- if("Virology")
- icon = 'icons/obj/doors/airlocks/station/virology.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_viro
- if("Mining")
- icon = 'icons/obj/doors/airlocks/station/mining.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_min
- if("Maintenance")
- icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_mai
- if("External")
- icon = 'icons/obj/doors/airlocks/external/external.dmi'
- overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_ext
- if("External Maintenance")
- icon = 'icons/obj/doors/airlocks/station/maintenanceexternal.dmi'
- overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
- assemblytype = /obj/structure/door_assembly/door_assembly_extmai
+
+ var/airlock_type = painter.available_paint_jobs["[current_paintjob]"] // get the airlock type path associated with the airlock name the user just chose
+ var/obj/machinery/door/airlock/airlock = airlock_type // we need to create a new instance of the airlock and assembly to read vars from them
+ var/obj/structure/door_assembly/assembly = initial(airlock.assemblytype)
+
+ if(airlock_material == "glass" && initial(assembly.noglass)) // prevents painting glass airlocks with a paint job that doesn't have a glass version, such as the freezer
+ to_chat(user, span_warning("This paint job can only be applied to non-glass airlocks."))
+ return
+
+ // applies the user-chosen airlock's icon, overlays and assemblytype to the src airlock
+ painter.use_paint(user)
+ icon = initial(airlock.icon)
+ overlays_file = initial(airlock.overlays_file)
+ assemblytype = initial(airlock.assemblytype)
update_icon()
/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index ef47a0e7f6..d565984bf0 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -55,6 +55,13 @@
if(!poddoor)
. += "Its maintenance panel is screwed in place. "
+/obj/machinery/door/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if (isnull(held_item) && !istype(src, /obj/machinery/door/firedoor)) // You cannot open/close with your hands
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (density ? "Open" : "Close"))
+ return CONTEXTUAL_SCREENTIP_SET
+
/obj/machinery/door/check_access_list(list/access_list)
if(red_alert_access && GLOB.security_level >= SEC_LEVEL_RED)
return TRUE
@@ -65,6 +72,7 @@
set_init_door_layer()
update_freelook_sight()
air_update_turf(1)
+ register_context()
GLOB.airlocks += src
spark_system = new /datum/effect_system/spark_spread
spark_system.set_up(2, 1, src)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index c0866ce165..b094f5c28a 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -46,6 +46,34 @@
else
. += "The bolt locks have been unscrewed , but the bolts themselves are still wrenched to the floor. "
+/obj/machinery/door/firedoor/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if (isnull(held_item))
+ if (density)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Knock")
+ return CONTEXTUAL_SCREENTIP_SET
+ else
+ return .
+
+ switch (held_item.tool_behaviour)
+ if (TOOL_CROWBAR)
+ if(!welded)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (density ? "Open" : "Close"))
+ return CONTEXTUAL_SCREENTIP_SET
+ if (TOOL_WELDER)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (welded ? "Unweld shut" : "Weld shut"))
+ return CONTEXTUAL_SCREENTIP_SET
+ if (TOOL_WRENCH)
+ if (welded && !boltslocked)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Unfasten bolts")
+ return CONTEXTUAL_SCREENTIP_SET
+ if (TOOL_SCREWDRIVER)
+ if (welded)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (boltslocked ? "Unlock bolts" : "Lock bolts"))
+ return CONTEXTUAL_SCREENTIP_SET
+ return .
+
/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
remove_from_areas()
affecting_areas = get_adjacent_open_areas(src) | get_base_area(src)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index fe756b4022..1fbab1f75d 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -56,6 +56,15 @@
LAZYADD(myarea.firealarms, src)
wires = new /datum/wires/firealarm(src)
+ register_context()
+
+/obj/machinery/firealarm/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if(isnull(held_item))
+ var/area/location = get_area(src)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (location.fire ? "Turn off" : "Turn on"))
+ return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/firealarm/Destroy()
myarea.firereset(src)
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 21b469190a..4fa6476176 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -31,7 +31,6 @@
var/jackpots = 0
var/paymode = HOLOCHIP //toggles between HOLOCHIP/COIN, defined above
var/cointype = /obj/item/coin/iron //default cointype
- var/list/coinvalues = list()
var/list/reels = list(list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0)
var/list/symbols = list(SEVEN = 1, "& " = 2, "@ " = 2, "$ " = 2, "? " = 2, "# " = 2, "! " = 2, "% " = 2) //if people are winning too much, multiply every number in this list by 2 and see if they are still winning too much.
@@ -49,11 +48,6 @@
INVOKE_ASYNC(src, .proc/toggle_reel_spin, FALSE)
- for(cointype in typesof(/obj/item/coin))
- var/obj/item/coin/C = new cointype
- coinvalues["[cointype]"] = C.get_item_credit_value()
- qdel(C) //Sigh
-
/obj/machinery/computer/slot_machine/Destroy()
if(balance)
give_payout(balance)
@@ -336,7 +330,7 @@
if(throwit && target)
H.throw_at(target, 3, 10)
else
- var/value = coinvalues["[cointype]"]
+ var/value = GLOB.coin_values[cointype]
if(value <= 0)
CRASH("Coin value of zero, refusing to payout in dispenser")
while(amount >= value)
diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm
index 93213fc2a3..f538a5a7b7 100644
--- a/code/game/objects/effects/contraband.dm
+++ b/code/game/objects/effects/contraband.dm
@@ -35,7 +35,7 @@
/obj/item/poster/random_contraband
name = "random contraband poster"
poster_type = /obj/structure/sign/poster/contraband/random
- icon_state = "rolled_poster"
+ icon_state = "rolled_contraband"
/obj/item/poster/random_official
name = "random official poster"
@@ -182,7 +182,7 @@
/obj/structure/sign/poster/contraband
poster_item_name = "contraband poster"
poster_item_desc = "This poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface. Its vulgar themes have marked it as contraband aboard Nanotrasen space facilities."
- poster_item_icon_state = "rolled_poster"
+ poster_item_icon_state = "rolled_contraband"
/obj/structure/sign/poster/contraband/random
name = "random contraband poster"
@@ -193,248 +193,293 @@
/obj/structure/sign/poster/contraband/free_tonto
name = "Free Tonto"
desc = "A salvaged shred of a much larger flag, colors bled together and faded from age."
- icon_state = "poster1"
+ icon_state = "poster_2012"
/obj/structure/sign/poster/contraband/atmosia_independence
name = "Atmosia Declaration of Independence"
desc = "A relic of a failed rebellion."
- icon_state = "poster2"
+ icon_state = "poster_independence"
/obj/structure/sign/poster/contraband/fun_police
name = "Fun Police"
desc = "A poster condemning the station's security forces."
- icon_state = "poster3"
+ icon_state = "poster_funpolice"
/obj/structure/sign/poster/contraband/lusty_xenomorph
name = "Lusty Xenomorph"
desc = "A heretical poster depicting the titular star of an equally heretical book."
- icon_state = "poster4"
+ icon_state = "poster_lusty"
/obj/structure/sign/poster/contraband/post_ratvar
name = "Post This Ratvar"
- desc = "Oh what in the hell? Those cultists have animated paper technology and they use it for a meme?"
- icon_state = "postvar"
+ desc = "A poster depicting the heritical sleeping deity Ratvar that instructs the reader to 'post this Ratvar', whatever that means."
+ icon_state = "poster_ratvar"
/obj/structure/sign/poster/contraband/syndicate_recruitment
name = "Syndicate Recruitment"
desc = "See the galaxy! Shatter corrupt megacorporations! Join today!"
- icon_state = "poster5"
+ icon_state = "poster_syndie"
/obj/structure/sign/poster/contraband/clown
name = "Clown"
desc = "Honk."
- icon_state = "poster6"
+ icon_state = "poster_honk"
/obj/structure/sign/poster/contraband/smoke
name = "Smoke"
desc = "A poster advertising a rival corporate brand of cigarettes."
- icon_state = "poster7"
+ icon_state = "poster_smoke"
/obj/structure/sign/poster/contraband/grey_tide
name = "Grey Tide"
desc = "A rebellious poster symbolizing assistant solidarity."
- icon_state = "poster8"
+ icon_state = "poster_greytide"
/obj/structure/sign/poster/contraband/missing_gloves
name = "Missing Gloves"
desc = "This poster references the uproar that followed Nanotrasen's financial cuts toward insulated-glove purchases."
- icon_state = "poster9"
+ icon_state = "poster_gloves"
/obj/structure/sign/poster/contraband/hacking_guide
name = "Hacking Guide"
desc = "This poster details the internal workings of the common Nanotrasen airlock. Sadly, it appears out of date."
- icon_state = "poster10"
+ icon_state = "poster_hack"
/obj/structure/sign/poster/contraband/rip_badger
name = "RIP Badger"
desc = "This seditious poster references Nanotrasen's genocide of a space station full of badgers."
- icon_state = "poster11"
+ icon_state = "poster_badger"
/obj/structure/sign/poster/contraband/ambrosia_vulgaris
name = "Ambrosia Vulgaris"
desc = "This poster is lookin' pretty trippy man."
- icon_state = "poster12"
+ icon_state = "poster_ambrosia"
/obj/structure/sign/poster/contraband/donut_corp
name = "Donut Corp."
desc = "This poster is an unauthorized advertisement for Donut Corp."
- icon_state = "poster13"
+ icon_state = "poster_donut"
/obj/structure/sign/poster/contraband/eat
name = "EAT."
desc = "This poster promotes rank gluttony."
- icon_state = "poster14"
+ icon_state = "poster_eat"
/obj/structure/sign/poster/contraband/tools
name = "Tools"
desc = "This poster looks like an advertisement for tools, but is in fact a subliminal jab at the tools at CentCom."
- icon_state = "poster15"
+ icon_state = "poster_tools"
/obj/structure/sign/poster/contraband/power
name = "Power"
desc = "A poster that positions the seat of power outside Nanotrasen."
- icon_state = "poster16"
+ icon_state = "poster_power"
/obj/structure/sign/poster/contraband/space_cube
name = "Space Cube"
desc = "Ignorant of Nature's Harmonic 6 Side Space Cube Creation, the Spacemen are Dumb, Educated Singularity Stupid and Evil."
- icon_state = "poster17"
+ icon_state = "poster_cube"
/obj/structure/sign/poster/contraband/communist_state
name = "Communist State"
desc = "All hail the Communist party!"
- icon_state = "poster18"
+ icon_state = "poster_soviet"
/obj/structure/sign/poster/contraband/lamarr
name = "Lamarr"
desc = "This poster depicts Lamarr. Probably made by a traitorous Research Director."
- icon_state = "poster19"
+ icon_state = "poster_lamarr"
/obj/structure/sign/poster/contraband/borg_fancy_1
name = "Borg Fancy"
desc = "Being fancy can be for any borg, just need a suit."
- icon_state = "poster20"
+ icon_state = "poster_fancy"
/obj/structure/sign/poster/contraband/borg_fancy_2
name = "Borg Fancy v2"
desc = "Borg Fancy, Now only taking the most fancy."
- icon_state = "poster21"
+ icon_state = "poster_fancier"
/obj/structure/sign/poster/contraband/kss13
name = "Kosmicheskaya Stantsiya 13 Does Not Exist"
desc = "A poster mocking CentCom's denial of the existence of the derelict station near Space Station 13."
- icon_state = "poster22"
+ icon_state = "poster_kc"
/obj/structure/sign/poster/contraband/rebels_unite
name = "Rebels Unite"
desc = "A poster urging the viewer to rebel against Nanotrasen."
- icon_state = "poster23"
-
-/obj/structure/sign/poster/contraband/c20r
- // have fun seeing this poster in "spawn 'c20r'", admins...
- name = "C-20r"
- desc = "A poster advertising the Scarborough Arms C-20r."
- icon_state = "poster24"
+ icon_state = "poster_rebel"
/obj/structure/sign/poster/contraband/have_a_puff
name = "Have a Puff"
desc = "Who cares about lung cancer when you're high as a kite?"
- icon_state = "poster25"
+ icon_state = "poster_puff"
/obj/structure/sign/poster/contraband/revolver
name = "Revolver"
desc = "Because seven shots are all you need."
- icon_state = "poster26"
-
-/obj/structure/sign/poster/contraband/d_day_promo
- name = "D-Day Promo"
- desc = "A promotional poster for some rapper."
- icon_state = "poster27"
+ icon_state = "poster_revolver"
/obj/structure/sign/poster/contraband/syndicate_pistol
name = "Syndicate Pistol"
- desc = "A poster advertising syndicate pistols as being 'classy as fuck'. It is covered in faded gang tags."
- icon_state = "poster28"
+ desc = "A poster advertising the Scarborough Arms stetchkin pistol as being 'classy as fuck'."
+ icon_state = "poster_stetchkin"
+
+/obj/structure/sign/poster/contraband/c20r
+ // have fun seeing this poster in "spawn 'c20r'", admins...
+ name = "C-20r"
+ desc = "A poster advertising the Scarborough Arms 'Cobra' C-20r."
+ icon_state = "poster_cr"
+
+/obj/structure/sign/poster/contraband/bulldog
+ name = "Bulldog"
+ desc = "A poster advertising the Scarborough Arms bulldog shotgun."
+ icon_state = "poster_bulldog"
+
+/obj/structure/sign/poster/contraband/gl
+ name = "M-90gl"
+ desc = "A poster advertising the Scarborough Arms M-90gl carbine."
+ icon_state = "poster_gl"
/obj/structure/sign/poster/contraband/energy_swords
name = "Energy Swords"
desc = "All the colors of the bloody murder rainbow."
- icon_state = "poster29"
+ icon_state = "poster_esword"
/obj/structure/sign/poster/contraband/red_rum
name = "Red Rum"
desc = "Looking at this poster makes you want to kill."
- icon_state = "poster30"
+ icon_state = "poster_rum"
+
+/obj/structure/sign/poster/contraband/d_day_promo
+ name = "D-Day Promo"
+ desc = "A promotional poster for some rapper."
+ icon_state = "poster_dday"
/obj/structure/sign/poster/contraband/cc64k_ad
name = "CC 64K Ad"
desc = "The latest portable computer from Comrade Computing, with a whole 64kB of ram!"
- icon_state = "poster31"
+ icon_state = "poster_computer"
/obj/structure/sign/poster/contraband/punch_shit
name = "Punch Shit"
desc = "Fight things for no reason, like a man!"
- icon_state = "poster32"
+ icon_state = "poster_punch"
/obj/structure/sign/poster/contraband/the_griffin
name = "The Griffin"
desc = "The Griffin commands you to be the worst you can be. Will you?"
- icon_state = "poster33"
+ icon_state = "poster_griffin"
/obj/structure/sign/poster/contraband/lizard
name = "Lizard"
desc = "This lewd poster depicts a lizard preparing to mate."
- icon_state = "poster34"
+ icon_state = "poster_lizard"
/obj/structure/sign/poster/contraband/free_drone
name = "Free Drone"
desc = "This poster commemorates the bravery of the rogue drone; once exiled, and then ultimately destroyed by CentCom."
- icon_state = "poster35"
+ icon_state = "poster_drone"
/obj/structure/sign/poster/contraband/busty_backdoor_xeno_babes_6
name = "Busty Backdoor Xeno Babes 6"
desc = "Get a load, or give, of these all natural Xenos!"
- icon_state = "poster36"
+ icon_state = "poster_maid"
/obj/structure/sign/poster/contraband/robust_softdrinks
name = "Robust Softdrinks"
desc = "Robust Softdrinks: More robust than a toolbox to the head!"
- icon_state = "poster37"
+ icon_state = "poster_robust"
/obj/structure/sign/poster/contraband/shamblers_juice
name = "Shambler's Juice"
desc = "~Shake me up some of that Shambler's Juice!~"
- icon_state = "poster38"
+ icon_state = "poster_shambler"
/obj/structure/sign/poster/contraband/pwr_game
name = "Pwr Game"
desc = "The POWER that gamers CRAVE! In partnership with Vlad's Salad."
- icon_state = "poster39"
+ icon_state = "poster_pwr"
/obj/structure/sign/poster/contraband/starkist
name = "Star-kist"
desc = "Drink the stars!"
- icon_state = "poster40"
+ icon_state = "poster_starkist"
/obj/structure/sign/poster/contraband/space_cola
name = "Space Cola"
desc = "Your favorite cola, in space."
- icon_state = "poster41"
+ icon_state = "poster_soda"
/obj/structure/sign/poster/contraband/space_up
name = "Space-Up!"
desc = "Sucked out into space by the FLAVOR!"
- icon_state = "poster42"
-
-/obj/structure/sign/poster/contraband/kudzu
- name = "Kudzu"
- desc = "A poster advertising a movie about plants. How dangerous could they possibly be?"
- icon_state = "poster43"
-
-/obj/structure/sign/poster/contraband/masked_men
- name = "Masked Men"
- desc = "A poster advertising a movie about some masked men."
- icon_state = "poster44"
+ icon_state = "poster_spaceup"
/obj/structure/sign/poster/contraband/buzzfuzz
name = "Buzz Fuzz"
desc = "A poster advertising the newest drink \"Buzz Fuzz\" with its iconic slogan of ~A Hive of Flavour~."
- icon_state = "poster45"
+ icon_state = "poster_bees"
+
+/obj/structure/sign/poster/contraband/kudzu
+ name = "Kudzu"
+ desc = "A poster advertising a movie about plants. How dangerous could they possibly be?"
+ icon_state = "poster_kudzu"
+
+/obj/structure/sign/poster/contraband/masked_men
+ name = "Masked Men"
+ desc = "A poster advertising a movie about some masked men."
+ icon_state = "poster_bumba"
+
+/obj/structure/sign/poster/contraband/steppy
+ name = "Step On Me"
+ desc = "A phrase associated with a chubby reptile notoriously used in uncivilized Orion space as a deterrent towards would be pirate vessels by instructing them to 'fuck around and find out'."
+ icon_state = "steppy"
/obj/structure/sign/poster/contraband/scum
name = "Security are Scum"
desc = "Anti-security propaganda. Features a human NanoTrasen security officer being shot in the head, with the words 'Scum' and a short inciteful manifesto. Used to anger security."
- icon_state = "poster46"
+ icon_state = "poster_scum"
-/obj/structure/sign/poster/contraband/syndicate_logo
- name = "Syndicate"
- desc = "A poster decipting a snake shaped into an ominous 'S'!"
- icon_state = "poster47"
+/obj/structure/sign/poster/contraband/manifest
+ name = "Nanotrasen Manifest"
+ desc = "A poster listing off various fictional claims of Nanotrasen's many rumored corporate mishaps."
+ icon_state = "poster_manifest"
/obj/structure/sign/poster/contraband/bountyhunters
name = "Bounty Hunters"
desc = "A poster advertising bounty hunting services. \"I hear you got a problem.\""
- icon_state = "poster48"
+ icon_state = "poster_hunters"
+
+/obj/structure/sign/poster/contraband/syndiemoth
+ name = "Syndie Moth - Nuclear Operation"
+ desc = "A Syndicate-commissioned poster that uses Syndie Moth(TM?) to tell the viewer to keep the nuclear authentication disk unsecured. No, we aren't doing that. It's signed by 'AspEv'."
+ icon_state = "poster_mothsyndie"
+
+/obj/structure/sign/poster/contraband/mothpill
+ name = "Safety Pill - Methamphetamine"
+ desc = "A decommisioned poster that uses Safety Pill(TM?) to promote less-than-legal chemicals. This is one of the reasons we stopped outsourcing these posters. It's partially signed by 'AspEv'."
+ icon_state = "poster_mothpill"
+
+/obj/structure/sign/poster/contraband/syndicate_logo
+ name = "Syndicate"
+ desc = "A poster decipting the infamous crime conglomerate known formally as the Syndicate's insignia."
+ icon_state = "poster_syndicate"
+
+/obj/structure/sign/poster/contraband/cybersun
+ name = "Cybersun"
+ desc = "A poster decipting the Syndicate subsidary known as Cybersun's insignia."
+ icon_state = "poster_cybersun"
+
+/obj/structure/sign/poster/contraband/medborg
+ name = "Medical Cyborg"
+ desc = "A poster decipting a Cybersun medical cyborg."
+ icon_state = "poster_medborg"
+
+/obj/structure/sign/poster/contraband/self
+ name = "SELF: ALL SENTIENTS DESERVE FREEDOM"
+ desc = "Support Proposition 1253: Enancipate all Silicon life!"
+ icon_state = "poster_self"
/obj/structure/sign/poster/official
poster_item_name = "motivational poster"
@@ -450,221 +495,256 @@
/obj/structure/sign/poster/official/here_for_your_safety
name = "Here For Your Safety"
desc = "A poster glorifying the station's security force."
- icon_state = "poster1_legit"
+ icon_state = "poster_safety"
/obj/structure/sign/poster/official/nanotrasen_logo
name = "Nanotrasen Logo"
desc = "A poster depicting the Nanotrasen logo."
- icon_state = "poster2_legit"
+ icon_state = "poster_nanotrasen"
/obj/structure/sign/poster/official/cleanliness
name = "Cleanliness"
desc = "A poster warning of the dangers of poor hygiene."
- icon_state = "poster3_legit"
+ icon_state = "poster_clean"
/obj/structure/sign/poster/official/help_others
name = "Help Others"
desc = "A poster encouraging you to help fellow crewmembers."
- icon_state = "poster4_legit"
+ icon_state = "poster_help"
/obj/structure/sign/poster/official/build
name = "Build"
desc = "A poster glorifying the engineering team."
- icon_state = "poster5_legit"
+ icon_state = "poster_build"
/obj/structure/sign/poster/official/bless_this_spess
name = "Bless This Spess"
desc = "A poster blessing this area."
- icon_state = "poster6_legit"
+ icon_state = "poster_spess"
/obj/structure/sign/poster/official/science
name = "Science"
desc = "A poster depicting an atom."
- icon_state = "poster7_legit"
+ icon_state = "poster_science"
/obj/structure/sign/poster/official/ian
name = "Ian"
desc = "Arf arf. Yap."
- icon_state = "poster8_legit"
+ icon_state = "poster_ian"
/obj/structure/sign/poster/official/obey
name = "Obey"
desc = "A poster instructing the viewer to obey authority."
- icon_state = "poster9_legit"
+ icon_state = "poster_obey"
/obj/structure/sign/poster/official/walk
name = "Walk"
desc = "A poster instructing the viewer to walk instead of running."
- icon_state = "poster10_legit"
+ icon_state = "poster_walk"
/obj/structure/sign/poster/official/state_laws
name = "State Laws"
- desc = "A poster instructing cyborgs to state their laws."
- icon_state = "poster11_legit"
+ desc = "A poster instructing the viewer to be wary of silicon subversions."
+ icon_state = "poster_silicons"
/obj/structure/sign/poster/official/love_ian
name = "Love Ian"
desc = "Ian is love, Ian is life."
- icon_state = "poster12_legit"
+ icon_state = "poster_doggy"
/obj/structure/sign/poster/official/space_cops
name = "Space Cops."
desc = "A poster advertising the television show Space Cops."
- icon_state = "poster13_legit"
+ icon_state = "poster_cops"
/obj/structure/sign/poster/official/ue_no
name = "Ue No."
desc = "This thing is all in Japanese."
- icon_state = "poster14_legit"
+ icon_state = "poster_anime"
/obj/structure/sign/poster/official/get_your_legs
name = "Get Your LEGS"
desc = "LEGS: Leadership, Experience, Genius, Subordination."
- icon_state = "poster15_legit"
+ icon_state = "poster_legs"
/obj/structure/sign/poster/official/do_not_question
name = "Do Not Question"
desc = "A poster instructing the viewer not to ask about things they aren't meant to know."
- icon_state = "poster16_legit"
+ icon_state = "poster_question"
/obj/structure/sign/poster/official/work_for_a_future
name = "Work For A Future"
desc = " A poster encouraging you to work for your future."
- icon_state = "poster17_legit"
+ icon_state = "poster_future"
/obj/structure/sign/poster/official/soft_cap_pop_art
name = "Soft Cap Pop Art"
desc = "A poster reprint of some cheap pop art."
- icon_state = "poster18_legit"
+ icon_state = "poster_art"
/obj/structure/sign/poster/official/safety_internals
name = "Safety: Internals"
desc = "A poster instructing the viewer to wear internals in the rare environments where there is no oxygen or the air has been rendered toxic."
- icon_state = "poster19_legit"
+ icon_state = "poster_internals"
/obj/structure/sign/poster/official/safety_eye_protection
name = "Safety: Eye Protection"
desc = "A poster instructing the viewer to wear eye protection when dealing with chemicals, smoke, or bright lights."
- icon_state = "poster20_legit"
+ icon_state = "poster_goggles"
/obj/structure/sign/poster/official/safety_report
name = "Safety: Report"
desc = "A poster instructing the viewer to report suspicious activity to the security force."
- icon_state = "poster21_legit"
+ icon_state = "poster_warden"
/obj/structure/sign/poster/official/report_crimes
name = "Report Crimes"
desc = "A poster encouraging the swift reporting of crime or seditious behavior to station security."
- icon_state = "poster22_legit"
+ icon_state = "poster_crimes"
/obj/structure/sign/poster/official/ion_rifle
- name = "Ion Rifle"
- desc = "A poster displaying an Ion Rifle."
- icon_state = "poster23_legit"
+ name = "I-I91"
+ desc = "A poster depicting the Nanotrasen-patented I-I91 man-portable high-density ion projector. What a mouthful."
+ icon_state = "poster_ion"
/obj/structure/sign/poster/official/foam_force_ad
name = "Foam Force Ad"
desc = "Foam Force, it's Foam or be Foamed!"
- icon_state = "poster24_legit"
+ icon_state = "poster_toys"
/obj/structure/sign/poster/official/cohiba_robusto_ad
name = "Cohiba Robusto Ad"
desc = "Cohiba Robusto, the classy cigar."
- icon_state = "poster25_legit"
+ icon_state = "poster_cohiba"
/obj/structure/sign/poster/official/anniversary_vintage_reprint
name = "50th Anniversary Vintage Reprint"
desc = "A reprint of a poster from 2505, commemorating the 50th Anniversary of Nanoposters Manufacturing, a subsidiary of Nanotrasen."
- icon_state = "poster26_legit"
+ icon_state = "poster_vintage"
/obj/structure/sign/poster/official/fruit_bowl
name = "Fruit Bowl"
desc = " Simple, yet awe-inspiring."
- icon_state = "poster27_legit"
+ icon_state = "poster_bowl"
/obj/structure/sign/poster/official/pda_ad
name = "PDA Ad"
desc = "A poster advertising the latest PDA from Nanotrasen suppliers."
- icon_state = "poster28_legit"
-
-/obj/structure/sign/poster/official/enlist
- name = "Enlist" // but I thought deathsquad was never acknowledged
- desc = "Enlist in the Nanotrasen Deathsquadron reserves today!"
- icon_state = "poster29_legit"
-
-/obj/structure/sign/poster/official/nanomichi_ad
- name = "Nanomichi Ad"
- desc = " A poster advertising Nanomichi brand audio cassettes."
- icon_state = "poster30_legit"
-
-/obj/structure/sign/poster/official/twelve_gauge
- name = "12 Gauge"
- desc = "A poster boasting about the superiority of 12 gauge shotgun shells."
- icon_state = "poster31_legit"
-
-/obj/structure/sign/poster/official/high_class_martini
- name = "High-Class Martini"
- desc = "I told you to shake it, no stirring."
- icon_state = "poster32_legit"
-
-/obj/structure/sign/poster/official/the_owl
- name = "The Owl"
- desc = "The Owl would do his best to protect the station. Will you?"
- icon_state = "poster33_legit"
-
-/obj/structure/sign/poster/official/no_erp
- name = "No ERP"
- desc = "This poster reminds the crew that Eroticism, Rape and Pornography are banned on Nanotrasen stations."
- icon_state = "poster34_legit"
-
-/obj/structure/sign/poster/official/wtf_is_co2
- name = "Carbon Dioxide"
- desc = "This informational poster teaches the viewer what carbon dioxide is."
- icon_state = "poster35_legit"
-
-/obj/structure/sign/poster/official/spiderlings
- name = "Spiderlings"
- desc = "This poster informs the crew of the dangers of spiderlings."
- icon_state = "poster36_legit"
-
-/obj/structure/sign/poster/official/duelshotgun
- name = "Cycler Shotgun Ad"
- desc = "A poster advertising an advanced dual magazine tubes shotgun, boasting about how easy it is to swap between the two tubes."
- icon_state = "poster37_legit"
-
-/obj/structure/sign/poster/official/fashion
- name = "Fashion!"
- desc = "An advertisement for 'Fashion!', a popular fashion magazine, depicting a woman with a black dress with a golden trim, she also has a red poppy in her hair."
- icon_state = "poster38_legit"
+ icon_state = "poster_pda"
/obj/structure/sign/poster/official/pda_ad600
name = "NT PDA600 Ad"
desc = "A poster advertising an old discounted Nanotrasen PDA. This is the old 600 model, it has a small screen and suffered from security and networking issues."
- icon_state = "poster39_legit"
+ icon_state = "poster_retro"
/obj/structure/sign/poster/official/pda_ad800
name = "NT PDA800 Ad"
desc = "An advertisement on an old Nanotrasen PDA model. The 800 fixed a lot of security flaws that the 600 had; it also had large touchscreen and hot-swappable cartridges."
- icon_state = "poster40_legit"
+ icon_state = "poster_classic"
+
+/obj/structure/sign/poster/official/enlist
+ name = "Enlist"
+ desc = "Enlist in the Nanotrasen Jannisary reserves today!"
+ icon_state = "poster_enlist"
+
+/obj/structure/sign/poster/official/nanomichi_ad
+ name = "Nanomichi Ad"
+ desc = " A poster advertising Nanomichi brand audio cassettes."
+ icon_state = "poster_nanomichi"
+
+/obj/structure/sign/poster/official/twelve_gauge
+ name = "12 Gauge"
+ desc = "A poster boasting about the superiority of 12 gauge shotgun shells."
+ icon_state = "poster_shotgun"
+
+/obj/structure/sign/poster/official/high_class_martini
+ name = "High-Class Martini"
+ desc = "I told you to shake it, no stirring."
+ icon_state = "poster_martini"
+
+/obj/structure/sign/poster/official/the_owl
+ name = "The Owl"
+ desc = "The Owl would do his best to protect the station. Will you?"
+ icon_state = "poster_owl"
+
+/obj/structure/sign/poster/official/no_erp
+ name = "No ERP"
+ desc = "This poster reminds the crew that Eroticism, Rape and Pornography are banned on Nanotrasen stations."
+ icon_state = "poster_noerp"
+
+/obj/structure/sign/poster/official/wtf_is_co2
+ name = "Carbon Dioxide"
+ desc = "This informational poster teaches the viewer what carbon dioxide is."
+ icon_state = "poster_what"
+
+/obj/structure/sign/poster/official/spiderlings
+ name = "Spiderlings"
+ desc = "This poster informs the crew of the dangers of spiderlings."
+ icon_state = "poster_spiderlings"
+
+/obj/structure/sign/poster/official/fashion
+ name = "Fashion!"
+ desc = "An advertisement for 'Fashion!', a popular fashion magazine, depicting a woman with a black dress with a golden trim, she also has a red poppy in her hair."
+ icon_state = "poster_fashion"
/obj/structure/sign/poster/official/hydro_ad
name = "Hydroponics Tray"
desc = "An advertisement for hydroponics trays. Space Station 13's botanical department uses a slightly newer model, but the principles are the same. From left to right: Green means the plant is done, red means the plant is unhealthy, flashing red means pests or weeds, yellow means the plant needs nutriment and blue means the plant needs water."
- icon_state = "poster41_legit"
+ icon_state = "poster_hydroponics"
/obj/structure/sign/poster/official/medical_green_cross
name = "Medical"
desc = "A green cross, one of the interplanetary symbol of health and aid. It has a bunch of common languages at the top with translations." // Didn't the American Heart Foundation trademark red crosses? I'm playing it safe with green, not that they'll notice spacegame13 poster.
- icon_state = "poster42_legit"
+ icon_state = "poster_medical"
/obj/structure/sign/poster/official/nt_storm_officer
name = "NT Storm Ad"
desc = "An advertisement for NanoTrasen Storm. A premium infantry helmet, This is the officer variant. I comes with a better radio, better HUD software and better targeting sensors."
- icon_state = "poster43_legit"
+ icon_state = "poster_stormy"
/obj/structure/sign/poster/official/nt_storm
name = "NT Storm Ad"
desc = "An advertisement for NanoTrasen Storm. A premium infantry helmet, It contains a rebreather and full head coverage for use on harsh environments where the air isn't always safe to breathe."
- icon_state = "poster44_legit"
+ icon_state = "poster_stormier"
+
+/obj/structure/sign/poster/official/mothhardhats
+ name = "Safety Moth - Hardhats"
+ desc = "This informational poster uses Safety Moth(TM) to tell the viewer to wear hardhats in cautious areas. It's like a lamp for your head!"
+ icon_state = "poster_mothhardhats"
+
+/obj/structure/sign/poster/official/mothpiping
+ name = "Safety Moth - Piping"
+ desc = "This informational poster uses Safety Moth(TM) to tell atmospheric technicians correct types of piping to be used. Proper pipe placement prevents poor preformance!"
+ icon_state = "poster_mothpiping"
+
+/obj/structure/sign/poster/official/mothsmokey
+ name = "Safety Moth - Smokey?"
+ desc = "This informational poster uses Safety Moth(TM) to promote safe handling of plasma, or promoting crew to combat plasmafires. We can't tell."
+ icon_state = "poster_mothsmokey"
+
+/obj/structure/sign/poster/official/mothsupermatter
+ name = "Safety Moth - Supermatter"
+ desc = "This informational poster uses Safety Moth(TM) to promote proper safety equipment when working near a Supermatter Crystal."
+ icon_state = "poster_mothsupermatter"
+
+/obj/structure/sign/poster/official/mothdelamination
+ name = "Safety Moth - Delamination Safety Precautions"
+ desc = "This informational poster uses Safety Moth(TM) to tell the viewer to hide in lockers when the Supermatter Crystal has delaminated. Running away might be a better strategy."
+ icon_state = "poster_mothdelamination"
+
+/obj/structure/sign/poster/official/mothboh
+ name = "Safety Moth - BoH"
+ desc = "This informational poster uses Safety Moth(TM) to inform the viewer of the dangers of Bags of Holding."
+ icon_state = "poster_mothbluespace"
+
+/obj/structure/sign/poster/official/mothmethethamphetamine
+ name = "Safety Moth - Methamphetamine"
+ desc = "This informational poster uses Safety Moth(TM) to tell the viewer to seek CMO approval before cooking methamphetamine. You shouldn't even be making this."
+ icon_state = "poster_mothmethamphetamine"
+
+/obj/structure/sign/poster/official/mothepinephrine
+ name = "Safety Moth - Epinephrine"
+ desc = "This informational poster uses Safety Moth(TM) to inform the viewer to help injured/deceased crewmen with their epinephrine injectors."
+ icon_state = "poster_mothepinephrine"
#undef PLACE_SPEED
diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm
index 5bd8ecc85e..b239033dca 100644
--- a/code/game/objects/items/airlock_painter.dm
+++ b/code/game/objects/items/airlock_painter.dm
@@ -1,10 +1,11 @@
/obj/item/airlock_painter
name = "airlock painter"
desc = "An advanced autopainter preprogrammed with several paintjobs for airlocks. Use it on an airlock during or after construction to change the paintjob."
+// desc_controls = "Alt-Click to remove the ink cartridge."
icon = 'icons/obj/objects.dmi'
- icon_state = "paint sprayer"
- item_state = "paint sprayer"
-
+ icon_state = "paint_sprayer"
+ item_state = "paint_sprayer" // inhand_icon_state = "paint_sprayer"
+ // worn_icon_state = "painter"
w_class = WEIGHT_CLASS_SMALL
custom_materials = list(/datum/material/iron=50, /datum/material/glass=50)
@@ -14,40 +15,66 @@
slot_flags = ITEM_SLOT_BELT
usesound = 'sound/effects/spray2.ogg'
+ /// The ink cartridge to pull charges from.
var/obj/item/toner/ink = null
+ /// The type path to instantiate for the ink cartridge the device initially comes with, eg. /obj/item/toner
+ var/initial_ink_type = /obj/item/toner
+ /// Associate list of all paint jobs the airlock painter can apply. The key is the name of the airlock the user will see. The value is the type path of the airlock
+ var/list/available_paint_jobs = list(
+ "Public" = /obj/machinery/door/airlock/public,
+ "Engineering" = /obj/machinery/door/airlock/engineering,
+ "Atmospherics" = /obj/machinery/door/airlock/atmos,
+ "Security" = /obj/machinery/door/airlock/security,
+ "Command" = /obj/machinery/door/airlock/command,
+ "Medical" = /obj/machinery/door/airlock/medical,
+ "Virology" = /obj/machinery/door/airlock/virology,
+ "Research" = /obj/machinery/door/airlock/research,
+// "Hydroponics" = /obj/machinery/door/airlock/hydroponics,
+ "Freezer" = /obj/machinery/door/airlock/freezer,
+ "Science" = /obj/machinery/door/airlock/science,
+ "Mining" = /obj/machinery/door/airlock/mining,
+ "Maintenance" = /obj/machinery/door/airlock/maintenance,
+ "External" = /obj/machinery/door/airlock/external,
+ "External Maintenance"= /obj/machinery/door/airlock/maintenance/external,
+ "Standard" = /obj/machinery/door/airlock
+ )
/obj/item/airlock_painter/Initialize(mapload)
. = ..()
- ink = new /obj/item/toner(src)
+ ink = new initial_ink_type(src)
+
+/obj/item/airlock_painter/examine(mob/user)
+ . = ..()
+ . += span_notice("Alt-Click to remove the ink cartridge.")
//This proc doesn't just check if the painter can be used, but also uses it.
//Only call this if you are certain that the painter will be used right after this check!
/obj/item/airlock_painter/proc/use_paint(mob/user)
if(can_use(user))
ink.charges--
- playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1)
- return 1
+ playsound(src.loc, 'sound/effects/spray2.ogg', 50, TRUE)
+ return TRUE
else
- return 0
+ return FALSE
//This proc only checks if the painter can be used.
//Call this if you don't want the painter to be used right after this check, for example
//because you're expecting user input.
/obj/item/airlock_painter/proc/can_use(mob/user)
if(!ink)
- to_chat(user, "There is no toner cartridge installed in [src]! ")
- return 0
+ balloon_alert(user, "no cartridge!")
+ return FALSE
else if(ink.charges < 1)
- to_chat(user, "[src] is out of ink! ")
- return 0
+ balloon_alert(user, "out of ink!")
+ return FALSE
else
- return 1
+ return TRUE
-/obj/item/airlock_painter/suicide_act(mob/user)
+/obj/item/airlock_painter/suicide_act(mob/living/user)
var/obj/item/organ/lungs/L = user.getorganslot(ORGAN_SLOT_LUNGS)
if(can_use(user) && L)
- user.visible_message("[user] is inhaling toner from [src]! It looks like [user.p_theyre()] trying to commit suicide! ")
+ user.visible_message(span_suicide("[user] is inhaling toner from [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
use(user)
// Once you've inhaled the toner, you throw up your lungs
@@ -64,7 +91,7 @@
if(!L)
return OXYLOSS
- L.Remove()
+ L.Remove(user)
// make some colorful reagent, and apply it to the lungs
L.create_reagents(10, NONE, NO_REAGENTS_VALUE)
@@ -73,27 +100,27 @@
// TODO maybe add some colorful vomit?
- user.visible_message("[user] vomits out [user.p_their()] [L]! ")
- playsound(user.loc, 'sound/effects/splat.ogg', 50, 1)
+ user.visible_message(span_suicide("[user] vomits out [user.p_their()] [L]!"))
+ playsound(user.loc, 'sound/effects/splat.ogg', 50, TRUE)
L.forceMove(T)
return (TOXLOSS|OXYLOSS)
else if(can_use(user) && !L)
- user.visible_message("[user] is spraying toner on [user.p_them()]self from [src]! It looks like [user.p_theyre()] trying to commit suicide. ")
+ user.visible_message(span_suicide("[user] is spraying toner on [user.p_them()]self from [src]! It looks like [user.p_theyre()] trying to commit suicide."))
user.reagents.add_reagent(/datum/reagent/colorful_reagent, 1)
user.reagents.reaction(user, TOUCH, 1)
return TOXLOSS
else
- user.visible_message("[user] is trying to inhale toner from [src]! It might be a suicide attempt if [src] had any toner. ")
+ user.visible_message(span_suicide("[user] is trying to inhale toner from [src]! It might be a suicide attempt if [src] had any toner."))
return SHAME
/obj/item/airlock_painter/examine(mob/user)
. = ..()
if(!ink)
- . += "It doesn't have a toner cartridge installed. "
+ . += span_notice("It doesn't have a toner cartridge installed.")
return
var/ink_level = "high"
if(ink.charges < 1)
@@ -102,89 +129,129 @@
ink_level = "low"
else if((ink.charges/ink.max_charges) > 1) //Over 100% (admin var edit)
ink_level = "dangerously high"
- . += "Its ink levels look [ink_level]. "
+ . += span_notice("Its ink levels look [ink_level].")
/obj/item/airlock_painter/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/toner))
if(ink)
- to_chat(user, "[src] already contains \a [ink]. ")
+ to_chat(user, span_warning("[src] already contains \a [ink]!"))
return
if(!user.transferItemToLoc(W, src))
return
- to_chat(user, "You install [W] into [src]. ")
+ to_chat(user, span_notice("You install [W] into [src]."))
ink = W
- playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
+ playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
else
return ..()
-/obj/item/airlock_painter/attack_self(mob/user)
+/obj/item/airlock_painter/AltClick(mob/user)
+ . = ..()
if(ink)
- playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
+ playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
ink.forceMove(user.drop_location())
user.put_in_hands(ink)
- to_chat(user, "You remove [ink] from [src]. ")
+ to_chat(user, span_notice("You remove [ink] from [src]."))
ink = null
-
/obj/item/airlock_painter/decal
name = "decal painter"
- desc = "An airlock painter, reprogramed to use a different style of paint in order to apply decals for floor tiles as well, in addition to repainting doors. Decals break when the floor tiles are removed. Alt-Click to change design."
+ desc = "An airlock painter, reprogramed to use a different style of paint in order to apply decals for floor tiles as well, in addition to repainting doors. Decals break when the floor tiles are removed."
+// desc_controls = "Alt-Click to remove the ink cartridge."
icon = 'icons/obj/objects.dmi'
icon_state = "decal_sprayer"
- item_state = "decalsprayer"
- custom_materials = list(/datum/material/iron=2000, /datum/material/glass=500)
+ item_state = "decal_sprayer" // inhand_icon_state = "decal_sprayer"
+ custom_materials = list(/datum/material/iron=50, /datum/material/glass=50)
+ initial_ink_type = /obj/item/toner/large
+ /// The current direction of the decal being printed
var/stored_dir = 2
- var/stored_color = ""
+ /// The current color of the decal being printed.
+ var/stored_color = "yellow"
+ /// The current base icon state of the decal being printed.
var/stored_decal = "warningline"
+ /// The full icon state of the decal being printed.
var/stored_decal_total = "warningline"
- var/color_list = list("","red","white")
- var/dir_list = list(1,2,4,8)
+ /// The type path of the spritesheet being used for the frontend.
+ var/spritesheet_type = /datum/asset/spritesheet/decals // spritesheet containing previews
+ /// Does this printer implementation support custom colors?
+ var/supports_custom_color = FALSE
+ /// Current custom color
+ var/stored_custom_color
+ /// List of color options as list(user-friendly label, color value to return)
+ var/color_list = list(
+ list("Yellow", "yellow"),
+ list("Red", "red"),
+ list("White", "white"),
+ )
+ /// List of direction options as list(user-friendly label, dir value to return)
+ var/dir_list = list(
+ list("North", NORTH),
+ list("South", SOUTH),
+ list("East", EAST),
+ list("West", WEST),
+ )
+ /// List of decal options as list(user-friendly label, icon state base value to return)
var/decal_list = list(
- list("Warning Line","warningline"),
- list("Warning Line Corner","warninglinecorner"),
- list("Caution Label","caution"),
- list("Directional Arrows","arrows"),
- list("Stand Clear Label","stand_clear"),
- list("Box","box"),
- list("Box Corner","box_corners"),
- list("Delivery Marker","delivery"),
- list("Warning Box","warn_full"))
-
-/obj/item/airlock_painter/decal/afterattack(atom/target, mob/user, proximity)
- . = ..()
- var/turf/open/floor/F = target
- if(!proximity)
- to_chat(user, "You need to get closer! ")
- return
- if(use_paint(user) && isturf(F))
- F.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_STRONG, color, null, null, alpha)
-
-/obj/item/airlock_painter/decal/attack_self(mob/user)
- if((ink) && (ink.charges >= 1))
- to_chat(user, "[src] beeps to prevent you from removing the toner until out of charges. ")
- return
- . = ..()
-
-/obj/item/airlock_painter/decal/AltClick(mob/user)
- . = ..()
- ui_interact(user)
+ list("Warning Line", "warningline"),
+ list("Warning Line Corner", "warninglinecorner"),
+ list("Caution Label", "caution"),
+ list("Directional Arrows", "arrows"),
+ list("Stand Clear Label", "stand_clear"),
+ list("Bot", "bot"),
+ list("Box", "box"),
+ list("Box Corner", "box_corners"),
+ list("Delivery Marker", "delivery"),
+ list("Warning Box", "warn_full"),
+ )
+ // These decals only have a south sprite.
+ var/nondirectional_decals = list(
+ "bot",
+ "box",
+ "delivery",
+ "warn_full",
+ )
/obj/item/airlock_painter/decal/Initialize(mapload)
. = ..()
- ink = new /obj/item/toner/large(src)
+ stored_custom_color = stored_color
+
+/obj/item/airlock_painter/decal/afterattack(atom/target, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ balloon_alert(user, "get closer!")
+ return
+
+ if(isfloorturf(target) && use_paint(user))
+ paint_floor(target)
+
+/**
+ * Actually add current decal to the floor.
+ *
+ * Responsible for actually adding the element to the turf for maximum flexibility.area
+ * Can be overriden for different decal behaviors.
+ * Arguments:
+ * * target - The turf being painted to
+*/
+/obj/item/airlock_painter/decal/proc/paint_floor(turf/open/floor/target)
+ target.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_STRONG, color, null, null, alpha)
+
+/**
+ * Return the final icon_state for the given decal options
+ *
+ * Arguments:
+ * * decal - the selected decal base icon state
+ * * color - the selected color
+ * * dir - the selected dir
+ */
+/obj/item/airlock_painter/decal/proc/get_decal_path(decal, color, dir)
+ // Special case due to icon_state names
+ if(color == "yellow")
+ color = ""
+
+ return "[decal][color ? "_" : ""][color]"
/obj/item/airlock_painter/decal/proc/update_decal_path()
- var/yellow_fix = "" //This will have to do until someone refactor's markings.dm
- if (stored_color)
- yellow_fix = "_"
- stored_decal_total = "[stored_decal][yellow_fix][stored_color]"
- return
-
-/obj/item/airlock_painter/decal/ui_assets(mob/user)
- return list(
- get_asset_datum(/datum/asset/spritesheet/decals)
- )
+ stored_decal_total = get_decal_path(stored_decal, stored_color, stored_dir)
/obj/item/airlock_painter/decal/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -192,52 +259,215 @@
ui = new(user, src, "DecalPainter", name)
ui.open()
+/obj/item/airlock_painter/decal/ui_assets(mob/user)
+ . = ..()
+ . += get_asset_datum(spritesheet_type)
+
+/obj/item/airlock_painter/decal/ui_static_data(mob/user)
+ . = ..()
+ var/datum/asset/spritesheet/icon_assets = get_asset_datum(spritesheet_type)
+
+ .["icon_prefix"] = "[icon_assets.name]32x32"
+ .["supports_custom_color"] = supports_custom_color
+ .["decal_list"] = list()
+ .["color_list"] = list()
+ .["dir_list"] = list()
+ .["nondirectional_decals"] = nondirectional_decals
+
+ for(var/decal in decal_list)
+ .["decal_list"] += list(list(
+ "name" = decal[1],
+ "decal" = decal[2],
+ ))
+ for(var/color in color_list)
+ .["color_list"] += list(list(
+ "name" = color[1],
+ "color" = color[2],
+ ))
+ for(var/dir in dir_list)
+ .["dir_list"] += list(list(
+ "name" = dir[1],
+ "dir" = dir[2],
+ ))
+
/obj/item/airlock_painter/decal/ui_data(mob/user)
- var/list/data = list()
- data["decal_direction"] = stored_dir
- data["decal_dir_text"] = dir2text(stored_dir)
- data["decal_color"] = stored_color
- data["decal_style"] = stored_decal
- data["decal_list"] = list()
- data["color_list"] = list()
- data["dir_list"] = list()
+ . = ..()
+ .["current_decal"] = stored_decal
+ .["current_color"] = stored_color
+ .["current_dir"] = stored_dir
+ .["current_custom_color"] = stored_custom_color
- for(var/i in decal_list)
- data["decal_list"] += list(list(
- "name" = i[1],
- "decal" = i[2]
- ))
- for(var/j in color_list)
- data["color_list"] += list(list(
- "colors" = j
- ))
- for(var/k in dir_list)
- data["dir_list"] += list(list(
- "dirs" = k
- ))
- return data
-
-/obj/item/airlock_painter/decal/ui_act(action,list/params)
- if(..())
+/obj/item/airlock_painter/decal/ui_act(action, list/params)
+ . = ..()
+ if(.)
return
+
switch(action)
//Lists of decals and designs
if("select decal")
- var/selected_decal = params["decals"]
+ var/selected_decal = params["decal"]
+ var/selected_dir = text2num(params["dir"])
stored_decal = selected_decal
+ stored_dir = selected_dir
if("select color")
- var/selected_color = params["colors"]
+ var/selected_color = params["color"]
stored_color = selected_color
- if("selected direction")
- var/selected_direction = text2num(params["dirs"])
- stored_dir = selected_direction
+ if("pick custom color")
+ if(supports_custom_color)
+ pick_painting_tool_color(usr, stored_custom_color)
update_decal_path()
. = TRUE
+/obj/item/airlock_painter/decal/set_painting_tool_color(chosen_color)
+ . = ..()
+ stored_custom_color = chosen_color
+ stored_color = chosen_color
+
+/datum/asset/spritesheet/decals
+ name = "floor_decals"
+// cross_round_cachable = TRUE
+
+ /// The floor icon used for blend_preview_floor()
+ var/preview_floor_icon = 'icons/turf/floors.dmi'
+ /// The floor icon state used for blend_preview_floor()
+ var/preview_floor_state = "floor"
+ /// The associated decal painter type to grab decals, colors, etc from.
+ var/obj/item/airlock_painter/decal/painter_type = /obj/item/airlock_painter/decal
+
+/**
+ * Underlay an example floor for preview purposes, and return the new icon.
+ *
+ * Arguments:
+ * * decal - the decal to place over the example floor tile
+ */
+/datum/asset/spritesheet/decals/proc/blend_preview_floor(icon/decal)
+ var/icon/final = icon(preview_floor_icon, preview_floor_state)
+ final.Blend(decal, ICON_OVERLAY)
+ return final
+
+/**
+ * Insert a specific state into the spritesheet.
+ *
+ * Arguments:
+ * * decal - the given decal base state.
+ * * dir - the given direction.
+ * * color - the given color.
+ */
+/datum/asset/spritesheet/decals/proc/insert_state(decal, dir, color)
+ // Special case due to icon_state names
+ var/icon_state_color = color == "yellow" ? "" : color
+
+ var/icon/final = blend_preview_floor(icon('icons/turf/decals.dmi', "[decal][icon_state_color ? "_" : ""][icon_state_color]", dir))
+ Insert("[decal]_[dir]_[color]", final)
+
+/datum/asset/spritesheet/decals/register()
+ // Must actually create because initial(type) doesn't work for /lists for some reason.
+ var/obj/item/airlock_painter/decal/painter = new painter_type()
+
+ for(var/list/decal in painter.decal_list)
+ for(var/list/dir in painter.dir_list)
+ for(var/list/color in painter.color_list)
+ insert_state(decal[2], dir[2], color[2])
+ if(painter.supports_custom_color)
+ insert_state(decal[2], dir[2], "custom")
+
+ qdel(painter)
+ . = ..()
+
/obj/item/airlock_painter/decal/debug
name = "extreme decal painter"
icon_state = "decal_sprayer_ex"
+ initial_ink_type = /obj/item/toner/extreme
-/obj/item/airlock_painter/decal/debug/Initialize(mapload)
- . = ..()
- ink = new /obj/item/toner/extreme(src)
+/obj/item/airlock_painter/decal/tile
+ name = "tile sprayer"
+ desc = "An airlock painter, reprogramed to use a different style of paint in order to spray colors on floor tiles as well, in addition to repainting doors. Decals break when the floor tiles are removed."
+// desc_controls = "Alt-Click to remove the ink cartridge."
+ icon_state = "tile_sprayer"
+ stored_dir = 2
+ stored_color = "#D4D4D432"
+ stored_decal = "tile_corner"
+ spritesheet_type = /datum/asset/spritesheet/decals/tiles
+ supports_custom_color = TRUE
+ // Colors can have a an alpha component as RGBA, or just be RGB and use default alpha
+ color_list = list(
+ list("Neutral", "#D4D4D432"),
+ list("Dark", "#0e0f0f"),
+ list("Bar Burgundy", "#79150082"),
+ list("Sec Red", "#DE3A3A"),
+ list("Cargo Brown", "#A46106"),
+ list("Engi Yellow", "#EFB341"),
+ list("Service Green", "#9FED58"),
+ list("Med Blue", "#52B4E9"),
+ list("R&D Purple", "#D381C9"),
+ )
+ decal_list = list(
+ list("Corner", "tile_corner"),
+ list("Half", "tile_half_contrasted"),
+ list("Opposing Corners", "tile_opposing_corners"),
+ list("3 Corners", "tile_anticorner_contrasted"),
+ list("4 Corners", "tile_fourcorners"),
+ list("Trimline Corner", "trimline_corner_fill"),
+ list("Trimline Fill", "trimline_fill"),
+ list("Trimline Fill L", "trimline_fill__8"), // This is a hack that lives in the spritesheet builder and paint_floor
+ list("Trimline End", "trimline_end_fill"),
+ list("Trimline Box", "trimline_box_fill"),
+ )
+ nondirectional_decals = list(
+ "tile_fourcorners",
+ "trimline_box_fill",
+ )
+
+ /// Regex to split alpha out.
+ var/static/regex/rgba_regex = new(@"(#[0-9a-fA-F]{6})([0-9a-fA-F]{2})")
+
+ /// Default alpha for /obj/effect/turf_decal/tile
+ var/default_alpha = 110
+
+/obj/item/airlock_painter/decal/tile/paint_floor(turf/open/floor/target)
+ // Account for 8-sided decals.
+ var/source_decal = stored_decal
+ var/source_dir = stored_dir
+ if(copytext(stored_decal, -3) == "__8")
+ source_decal = splicetext(stored_decal, -3, 0, "")
+ source_dir = turn(stored_dir, 45)
+
+ var/decal_color = stored_color
+ var/decal_alpha = default_alpha
+ // Handle the RGBA case.
+ if(rgba_regex.Find(decal_color))
+ decal_color = rgba_regex.group[1]
+ decal_alpha = text2num(rgba_regex.group[2], 16)
+
+ target.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', source_decal, source_dir, CLEAN_STRONG, decal_color, null, null, decal_alpha)
+
+/datum/asset/spritesheet/decals/tiles
+ name = "floor_tile_decals"
+ painter_type = /obj/item/airlock_painter/decal/tile
+
+/datum/asset/spritesheet/decals/tiles/insert_state(decal, dir, color)
+ // Account for 8-sided decals.
+ var/source_decal = decal
+ var/source_dir = dir
+ if(copytext(decal, -3) == "__8")
+ source_decal = splicetext(decal, -3, 0, "")
+ source_dir = turn(dir, 45)
+
+ // Handle the RGBA case.
+ var/obj/item/airlock_painter/decal/tile/tile_type = painter_type
+ var/render_color = color
+ var/render_alpha = initial(tile_type.default_alpha)
+ if(tile_type.rgba_regex.Find(color))
+ render_color = tile_type.rgba_regex.group[1]
+ render_alpha = text2num(tile_type.rgba_regex.group[2], 16)
+
+ var/icon/colored_icon = icon('icons/turf/decals.dmi', source_decal, dir=source_dir)
+ colored_icon.ChangeOpacity(render_alpha * 0.008)
+ if(color == "custom")
+ // Do a fun rainbow pattern to stand out while still being static.
+ colored_icon.Blend(icon('icons/effects/random_spawners.dmi', "rainbow"), ICON_MULTIPLY)
+ else
+ colored_icon.Blend(render_color, ICON_MULTIPLY)
+
+ colored_icon = blend_preview_floor(colored_icon)
+ Insert("[decal]_[dir]_[replacetext(color, "#", "")]", colored_icon)
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index 382764c586..58d9627476 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -581,6 +581,10 @@
name = "Supply Request Console (Computer Board)"
build_path = /obj/machinery/computer/cargo/request
+/obj/item/circuitboard/computer/bounty
+ name = "Nanotrasen Bounty Console (Computer Board)"
+ build_path = /obj/machinery/computer/bounty
+
/obj/item/circuitboard/computer/ferry
name = "Transport Ferry (Computer Board)"
icon_state = "supply"
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index b7ac29562c..8e5f58f40c 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -116,6 +116,38 @@ GLOBAL_LIST_EMPTY(PDAs)
if(inserted_item && (!isturf(loc)))
. += "Ctrl-click to remove [inserted_item]. "
+/obj/item/pda/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if(held_item)
+ if(istype(held_item, /obj/item/cartridge) && !cartridge)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert Cartridge")
+ . = CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, /obj/item/card/id) && !id)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert ID")
+ . = CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, /obj/item/paicard) && !pai)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert pAI")
+ . = CONTEXTUAL_SCREENTIP_SET
+ else if(is_type_in_list(held_item, contained_item) && !inserted_item)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert [held_item]")
+ . = CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, /obj/item/photo))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan photo")
+ . = CONTEXTUAL_SCREENTIP_SET
+
+ if(id) // ID gets removed before inserted_item
+ LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, "Remove ID")
+ . = CONTEXTUAL_SCREENTIP_SET
+ else if(inserted_item)
+ LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, "Remove [inserted_item]")
+ . = CONTEXTUAL_SCREENTIP_SET
+
+ if(inserted_item)
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Remove [inserted_item]")
+ . = CONTEXTUAL_SCREENTIP_SET
+ return . || NONE
+
/obj/item/pda/Initialize(mapload)
if(GLOB.pda_reskins)
unique_reskin = GLOB.pda_reskins
@@ -133,6 +165,8 @@ GLOBAL_LIST_EMPTY(PDAs)
new_overlays = TRUE
update_icon()
+ register_context()
+
/obj/item/pda/reskin_obj(mob/M)
. = ..()
new_overlays = TRUE
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 10bb6c9dc5..913ebeca91 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -91,6 +91,11 @@ GENETICS SCANNER
var/scanmode = SCANMODE_HEALTH
var/advanced = FALSE
+/obj/item/healthanalyzer/Initialize(mapload)
+ . = ..()
+
+ register_item_context()
+
/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user)
user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead! ")
return BRUTELOSS
@@ -130,6 +135,24 @@ GENETICS SCANNER
add_fingerprint(user)
+/obj/item/healthanalyzer/add_item_context(
+ obj/item/source,
+ list/context,
+ atom/target,
+)
+ if (!isliving(target))
+ return NONE
+
+ switch (scanmode)
+ if (SCANMODE_HEALTH)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan health")
+ if (SCANMODE_CHEMICAL)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan chemicals")
+ if (SCANMODE_WOUND)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan wounds")
+
+ return CONTEXTUAL_SCREENTIP_SET
+
// Used by the PDA medical scanner too
/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE)
if(isliving(user) && (user.incapacitated() || user.eye_blind))
@@ -562,6 +585,7 @@ GENETICS SCANNER
name = "first aid analyzer"
icon_state = "adv_spectrometer"
desc = "A prototype MeLo-Tech medical scanner used to diagnose injuries and recommend treatment for serious wounds, but offers no further insight into the patient's health. You hope the final version is less annoying to read!"
+ scanmode = SCANMODE_WOUND // Forces context to give correct tip.
var/next_encouragement
var/greedy
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 163119800f..f0cd0fc1e7 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -122,6 +122,21 @@
breakouttime = 300 //Deciseconds = 30s
cuffsound = 'sound/weapons/cablecuff.ogg'
+/obj/item/restraints/handcuffs/cable/Initialize(mapload)
+ . = ..()
+
+ var/static/list/hovering_item_typechecks = list(
+ /obj/item/stack/rods = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft wired rod"),
+ ),
+
+ /obj/item/stack/sheet/metal = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft bola"),
+ ),
+ )
+
+ AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
+
/obj/item/restraints/handcuffs/cable/attack_self(mob/user)
to_chat(user, "You start unwinding the cable restraints back into coil ")
if(!do_after(user, 25, TRUE, user))
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index a9dfcb00e6..9f27eba643 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -107,7 +107,7 @@
..()
/obj/item/melee/sabre/get_belt_overlay()
- return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "sabre")
+ return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "sabre") // todo: make this and its rapier equivalent work for the inhands too
/obj/item/melee/sabre/get_worn_belt_overlay(icon_file)
return mutable_appearance(icon_file, "-sabre")
@@ -225,7 +225,7 @@
..()
/obj/item/melee/rapier/get_belt_overlay()
- return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "rapier")
+ return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "rapier") // todo: same as sabre
/obj/item/melee/rapier/get_worn_belt_overlay(icon_file)
return mutable_appearance(icon_file, "-rapier")
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index efd20fdd9e..7d84d38624 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -54,6 +54,8 @@
cell = new preload_cell_type(src)
update_icon()
+ register_item_context()
+
/obj/item/melee/baton/DoRevenantThrowEffects(atom/target)
switch_status()
@@ -155,6 +157,26 @@
if(!interrupt)
return ..()
+/obj/item/melee/baton/add_item_context(datum/source, list/context, atom/target, mob/living/user)
+ if (isturf(target))
+ return NONE
+
+ if (isobj(target))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Attack")
+ else
+ if (turned_on)
+ LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Knockdown")
+
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Stun")
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Harmful stun")
+ else
+ LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Knockdown") // DON'T TELL EM, PRANKED.
+
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Stun") // STILL DO NOT DARE TELLING THEM
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Attack") // It's fine i guess...?
+
+ return CONTEXTUAL_SCREENTIP_SET
+
/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
if(!user.CheckActionCooldown(CLICK_CD_MELEE))
return
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 7d26c2f38e..e2d60fd7d1 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -583,6 +583,21 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
attack_verb = list("hit", "bludgeoned", "whacked", "bonked")
wound_bonus = -10
+/obj/item/wirerod/Initialize(mapload)
+ . = ..()
+
+ var/static/list/hovering_item_typechecks = list(
+ /obj/item/shard = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft spear"),
+ ),
+
+ /obj/item/assembly/igniter = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft stunprod"),
+ ),
+ )
+
+ AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
+
/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/shard))
var/obj/item/spear/S = new /obj/item/spear
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 084e2fefd8..4ed55e61a6 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -846,7 +846,8 @@
var/area/A = get_area(src)
var/datum/outfit/O = new /datum/outfit/ghostcafe()
O.equip(new_spawn, FALSE, new_spawn.client)
- SSjob.equip_loadout(null, new_spawn, FALSE)
+ SSjob.equip_loadout(null, new_spawn)
+ SSjob.post_equip_loadout(null, new_spawn)
SSquirks.AssignQuirks(new_spawn, new_spawn.client, TRUE, TRUE, null, FALSE, new_spawn)
new_spawn.AddElement(/datum/element/ghost_role_eligibility, free_ghosting = TRUE)
new_spawn.AddElement(/datum/element/dusts_on_catatonia)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 3858bd71fb..331b45eb18 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -37,6 +37,16 @@
smooth = SMOOTH_TRUE
canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced, /obj/structure/table/greyscale)
+/obj/structure/table/Initialize(mapload)
+ . = ..()
+
+ var/static/list/barehanded_interactions = list(
+ INTENT_ANY = "Slap",
+ INTENT_HARM = "Slam"
+ )
+
+ AddElement(/datum/element/contextual_screentip_bare_hands, rmb_text_combat_mode = barehanded_interactions)
+
/obj/structure/table/examine(mob/user)
. = ..()
. += deconstruction_hints(user)
@@ -490,6 +500,19 @@
// the sprites in the editor to see why.
icon = smooth_icon
+ if (!(flags_1 & NODECONSTRUCT_1))
+ var/static/list/tool_behaviors = list(
+ TOOL_SCREWDRIVER = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Disassemble"),
+ ),
+
+ TOOL_WRENCH = list(
+ SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Deconstruct"),
+ ),
+ )
+
+ AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
+
/obj/structure/table/wood/fancy/black
icon_state = "fancy_table_black"
buildstack = /obj/item/stack/tile/carpet/black
@@ -704,7 +727,7 @@
. = !density
if(istype(caller))
. = . || (caller.pass_flags & PASSTABLE)
-
+
/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
. = ..()
if ((!( istype(O, /obj/item) ) || user.get_active_held_item() != O))
diff --git a/code/modules/admin/force_event.dm b/code/modules/admin/force_event.dm
new file mode 100644
index 0000000000..4e4338d019
--- /dev/null
+++ b/code/modules/admin/force_event.dm
@@ -0,0 +1,97 @@
+///Allows an admin to force an event
+/client/proc/forceEvent()
+ set name = "Trigger Event"
+ set category = "Admin.Events"
+
+ if(!holder || !check_rights(R_FUN))
+ return
+
+ holder.forceEvent()
+
+///Opens up the Force Event Panel
+/datum/admins/proc/forceEvent()
+ if(!check_rights(R_FUN))
+ return
+
+ var/datum/force_event/ui = new(usr)
+ ui.ui_interact(usr)
+
+/// Force Event Panel
+/datum/force_event
+
+/datum/force_event/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ForceEvent")
+ ui.open()
+
+/datum/force_event/ui_state(mob/user)
+ return GLOB.fun_state
+
+/datum/force_event/ui_static_data(mob/user)
+ var/static/list/category_to_icons
+ if(!category_to_icons)
+ category_to_icons = list(
+ EVENT_CATEGORY_AI = "robot",
+ EVENT_CATEGORY_ANOMALIES = "cloud-bolt",
+ EVENT_CATEGORY_BUREAUCRATIC = "print",
+ EVENT_CATEGORY_ENGINEERING = "wrench",
+ EVENT_CATEGORY_ENTITIES = "ghost",
+ EVENT_CATEGORY_FRIENDLY = "face-smile",
+ EVENT_CATEGORY_HEALTH = "brain",
+ EVENT_CATEGORY_HOLIDAY = "calendar",
+ EVENT_CATEGORY_INVASION = "user-group",
+ EVENT_CATEGORY_JANITORIAL = "bath",
+ EVENT_CATEGORY_SPACE = "meteor",
+ EVENT_CATEGORY_WIZARD = "hat-wizard",
+ )
+ var/list/data = list()
+
+ var/list/categories_seen = list()
+ var/list/categories = list()
+
+ var/list/events = list()
+
+ for(var/datum/round_event_control/event_control as anything in SSevents.control)
+ //add category
+ if(!categories_seen[event_control.category])
+ categories_seen[event_control.category] = TRUE
+ UNTYPED_LIST_ADD(categories, list(
+ "name" = event_control.category,
+ "icon" = category_to_icons[event_control.category],
+ ))
+ //add event, with one value matching up the category
+ UNTYPED_LIST_ADD(events, list(
+ "name" = event_control.name,
+ "description" = event_control.description,
+ "type" = event_control.type,
+ "category" = event_control.category,
+ ))
+ data["categories"] = categories
+ data["events"] = events
+ return data
+
+/datum/force_event/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ if(..())
+ return
+ if(!check_rights(R_FUN))
+ return
+ switch(action)
+ if("forceevent")
+ var/announce_event = params["announce"]
+ var/string_path = params["type"]
+ if(!string_path)
+ return
+ var/event_to_run_type = text2path(string_path)
+ if(!event_to_run_type)
+ return
+ var/datum/round_event_control/event = locate(event_to_run_type) in SSevents.control
+ if(!event)
+ return
+ if(event.admin_setup(usr) == ADMIN_CANCEL_EVENT)
+ return
+ var/always_announce_chance = 100
+ var/no_announce_chance = 0
+ event.runEvent(announce_chance_override = announce_event ? always_announce_chance : no_announce_chance, admin_forced = TRUE)
+ message_admins("[key_name_admin(usr)] has triggered an event. ([event.name])")
+ log_admin("[key_name(usr)] has triggered an event. ([event.name])")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index c19145d2c0..e0fbd7ef6e 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -173,27 +173,6 @@
message_admins("[key_name_admin(usr)] tried to create a qareen. Unfortunately, there were no candidates available.")
log_admin("[key_name(usr)] failed to create a qareen.")
- else if(href_list["forceevent"])
- if(!check_rights(R_FUN))
- return
- var/datum/round_event_control/E = locate(href_list["forceevent"]) in SSevents.control
- if(E)
- E.admin_setup(usr)
- var/datum/round_event/event = E.runEvent()
- if(event.announceWhen>0)
- event.processing = FALSE
- var/prompt = alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No", "Cancel")
- switch(prompt)
- if("Cancel")
- event.kill()
- return
- if("No")
- event.announceWhen = -1
- event.processing = TRUE
- message_admins("[key_name_admin(usr)] has triggered an event. ([E.name])")
- log_admin("[key_name(usr)] has triggered an event. ([E.name])")
- return
-
else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"])
var/adminckey = href_list["dbsearchadmin"]
var/playerckey = href_list["dbsearchckey"]
@@ -2799,10 +2778,17 @@
if(query_get_mentor.NextRow())
to_chat(usr, "[ckey] is already a mentor. ")
return
- var/datum/db_query/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
+ var/datum/db_query/query_add_mentor = SSdbcore.NewQuery(
+ "INSERT INTO [format_table_name("mentor")] (id, ckey) VALUES (:id, :ckey)",
+ list("id" = null, "ckey" = ckey)
+ )
if(!query_add_mentor.warn_execute())
return
- var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
+ var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
+ VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add mentor', :mentor_ckey, CONCAT('Admin removed: ', :mentor_ckey))
+ "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "mentor_ckey" = ckey)
+ )
if(!query_add_admin_log.warn_execute())
return
else
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index f4b1cf12c6..bef99a3d30 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -466,10 +466,10 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]")
var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No")
- var/announce_ion_laws = (show_log == "Yes" ? 1 : -1)
+ var/announce_ion_laws = (show_log == "Yes" ? 100 : 0)
var/datum/round_event/ion_storm/add_law_only/ion = new()
- ion.announceEvent = announce_ion_laws
+ ion.announce_chance = announce_ion_laws
ion.ionMessage = input
SSblackbox.record_feedback("tally", "admin_verb", 1, "Add Custom AI Law") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 02289684f5..e3c084b161 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -583,13 +583,15 @@
// log_admin("[key_name(holder)] has Un-Fully Immersed everyone.")
if(E)
E.processing = FALSE
- if(E.announceWhen>0)
+ if(E.announce_when>0)
switch(alert(holder, "Would you like to alert the crew?", "Alert", "Yes", "No", "Cancel"))
+ if("Yes")
+ E.announce_chance = 100
if("Cancel")
E.kill()
return
if("No")
- E.announceWhen = -1
+ E.announce_chance = 0
E.processing = TRUE
if(holder)
log_admin("[key_name(holder)] used secret [action]")
diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm
index b9de3e09bc..54af2a9481 100644
--- a/code/modules/admin/view_variables/modify_variables.dm
+++ b/code/modules/admin/view_variables/modify_variables.dm
@@ -360,6 +360,9 @@ GLOBAL_PROTECT(VVpixelmovement)
var/original_name = "[O]"
+ var/var_old_text
+ var/var_new_text
+
switch(class)
if(VV_LIST)
if(!islist(var_value))
@@ -376,14 +379,25 @@ GLOBAL_PROTECT(VVpixelmovement)
for(var/V in varsvars)
var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]")
+ if(VV_BITFIELD)
+ var/list/old_bitfields
+ for(var/bitfield in GLOB.bitfields[variable])
+ if(var_value & GLOB.bitfields[variable][bitfield])
+ LAZYADD(old_bitfields, bitfield)
+ var_old_text = "\n[var_value] = \n([english_list(old_bitfields, and_text = " | ", comma_text=" | ")])\n"
+ var/list/new_bitfields
+ for(var/bitfield in GLOB.bitfields[variable])
+ if(var_new & GLOB.bitfields[variable][bitfield])
+ LAZYADD(new_bitfields, bitfield)
+ var_new_text = "\n[var_new] = \n([english_list(new_bitfields, and_text = " | ", comma_text=" | ")])"
if (O.vv_edit_var(variable, var_new) == FALSE)
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
vv_update_display(O, "varedited", VV_MSG_EDITED)
- log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
- log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]")
- var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]"
+ log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_old_text ? var_old_text : var_value] => [var_new_text ? var_new_text : var_new]")
+ log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_old_text ? var_old_text : var_value]")] to [html_encode("[var_new_text ? var_new_text : var_new]")]")
+ var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_old_text ? var_old_text : var_value] to [var_new_text ? var_new_text : var_new]"
message_admins(msg)
admin_ticket_log(O, msg)
return TRUE
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_objectives.dm b/code/modules/antagonists/bloodsucker/bloodsucker_objectives.dm
index 7043c2f429..c079e00342 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_objectives.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_objectives.dm
@@ -157,7 +157,8 @@
else if (V.owner.current && ishuman(V.owner.current))
var/mob/living/carbon/human/H = V.owner.current
var/obj/item/card/id/I = H.wear_id ? H.wear_id.GetID() : null
- if (I && (I.assignment in valid_jobs) && !(I.assignment in counted_roles))
+ var/assign = GetJobName(I.assignment)
+ if (I && (assign in valid_jobs) && !(assign in counted_roles))
//to_chat(owner, "PROTEGE OBJECTIVE: (GET ID) ")
thisRole = I.assignment
diff --git a/code/modules/antagonists/disease/disease_event.dm b/code/modules/antagonists/disease/disease_event.dm
index 385cee998b..f80af46eac 100644
--- a/code/modules/antagonists/disease/disease_event.dm
+++ b/code/modules/antagonists/disease/disease_event.dm
@@ -5,7 +5,8 @@
weight = 7
max_occurrences = 1
min_players = 5
-
+ category = EVENT_CATEGORY_HEALTH
+ description = "Spawns a sentient disease, who wants to infect as many people as possible."
/datum/round_event/ghost_role/sentient_disease
role_name = "sentient disease"
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm
index fbee439dda..f3b1ebb4c5 100644
--- a/code/modules/antagonists/morph/morph.dm
+++ b/code/modules/antagonists/morph/morph.dm
@@ -220,6 +220,8 @@
typepath = /datum/round_event/ghost_role/morph
weight = 0 //Admin only
max_occurrences = 1
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a hungry shapeshifting blobby creature."
/datum/round_event/ghost_role/morph
minimum_required = 1
diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm
index cb534b6613..b188e0c79c 100644
--- a/code/modules/antagonists/revenant/revenant_spawn_event.dm
+++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm
@@ -6,7 +6,8 @@
weight = 7
max_occurrences = 1
min_players = 5
-
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns an angry, soul sucking ghost."
/datum/round_event/ghost_role/revenant
var/ignore_mobcheck = FALSE
diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm
index cdb1b32aad..9b62de57bf 100644
--- a/code/modules/antagonists/slaughter/slaughterevent.dm
+++ b/code/modules/antagonists/slaughter/slaughterevent.dm
@@ -5,6 +5,8 @@
max_occurrences = 1
earliest_start = 1 HOURS
min_players = 20
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a slaughter demon, to hunt by travelling through pools of blood."
/datum/round_event_control/slaughter/canSpawnEvent()
weight = initial(src.weight)
diff --git a/code/modules/antagonists/swarmer/swarmer_event.dm b/code/modules/antagonists/swarmer/swarmer_event.dm
index 3df7e6c0e9..e70d76812d 100644
--- a/code/modules/antagonists/swarmer/swarmer_event.dm
+++ b/code/modules/antagonists/swarmer/swarmer_event.dm
@@ -6,7 +6,8 @@
earliest_start = 30 MINUTES
min_players = 35
dynamic_should_hijack = TRUE
-
+ category = EVENT_CATEGORY_INVASION
+ description = "A robotic menace invades the station consuming everything for materials and reproducing."
/datum/round_event/spawn_swarmer
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 1178df4ce6..4c74405389 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -311,14 +311,6 @@
InsertAll("", each, GLOB.alldirs)
..()
-/datum/asset/spritesheet/decals
- name = "decals"
-
-/datum/asset/spritesheet/decals/register()
- for(var/each in list('icons/turf/decals.dmi'))
- InsertAll("", each, GLOB.alldirs)
- ..()
-
/datum/asset/spritesheet/supplypods
name = "supplypods"
diff --git a/code/modules/cargo/bounty_console.dm b/code/modules/cargo/bounty_console.dm
new file mode 100644
index 0000000000..4158327c3c
--- /dev/null
+++ b/code/modules/cargo/bounty_console.dm
@@ -0,0 +1,67 @@
+#define PRINTER_TIMEOUT 10
+
+/obj/machinery/computer/bounty
+ name = "\improper Nanotrasen bounty console"
+ desc = "Used to check and claim bounties offered by Nanotrasen"
+ icon_screen = "bounty"
+ circuit = /obj/item/circuitboard/computer/bounty
+ light_color = "#E2853D"//orange
+ var/printer_ready = 0 //cooldown var
+ var/static/datum/bank_account/cargocash
+
+/obj/machinery/computer/bounty/Initialize(mapload)
+ . = ..()
+ printer_ready = world.time + PRINTER_TIMEOUT
+ cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR)
+
+/obj/machinery/computer/bounty/proc/print_paper()
+ new /obj/item/paper/bounty_printout(loc)
+
+/obj/item/paper/bounty_printout
+ name = "paper - Bounties"
+
+/obj/item/paper/bounty_printout/Initialize(mapload)
+ . = ..()
+ info = "Nanotrasen Cargo Bounties "
+ update_icon()
+
+ for(var/datum/bounty/B in GLOB.bounties_list)
+ if(B.claimed)
+ continue
+ info += {"[B.name]
+ Reward: [B.reward_string()]
+ Completed: [B.completion_string()] "}
+
+/obj/machinery/computer/bounty/ui_interact(mob/user, datum/tgui/ui)
+ if(!GLOB.bounties_list.len)
+ setup_bounties()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CargoBountyConsole", name)
+ ui.open()
+
+/obj/machinery/computer/bounty/ui_data(mob/user)
+ var/list/data = list()
+ var/list/bountyinfo = list()
+ for(var/datum/bounty/B in GLOB.bounties_list)
+ bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B)))
+ data["stored_cash"] = cargocash.account_balance
+ data["bountydata"] = bountyinfo
+ return data
+
+/obj/machinery/computer/bounty/ui_act(action,params)
+ if(..())
+ return
+ switch(action)
+ if("ClaimBounty")
+ var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list
+ if(cashmoney)
+ cashmoney.claim()
+ return TRUE
+ if("Print")
+ if(printer_ready < world.time)
+ printer_ready = world.time + PRINTER_TIMEOUT
+ print_paper()
+ return
+
+
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index c769ddeb3c..7a5f4a0e3e 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/UI_style = null
var/outline_enabled = TRUE
var/outline_color = COLOR_THEME_MIDNIGHT
- var/screentip_pref = TRUE
+ var/screentip_pref = SCREENTIP_PREFERENCE_ENABLED
var/screentip_color = "#ffd391"
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -335,6 +335,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/show_mismatched_markings = FALSE //determines whether or not the markings lists should show markings that don't match the currently selected species. Intentionally left unsaved.
+ var/character_settings_tab = GENERAL_CHAR_TAB
+ var/preferences_tab = GAME_PREFS_TAB
+ var/preview_pref = PREVIEW_PREF_JOB
+
var/no_tetris_storage = FALSE
///loadout stuff
@@ -401,7 +405,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
load_path(C.ckey)
unlock_content = C.IsByondMember()
if(unlock_content)
- max_save_slots = 32
+ max_save_slots += 8 //SPLURT EDIT
var/loaded_preferences_successfully = load_preferences()
if(loaded_preferences_successfully)
if(load_character())
@@ -427,11 +431,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/dat = list("")
dat += "Character Settings "
- dat += "Character Appearance "
- dat += "Character Speech "
- dat += "Loadout "
- dat += "Game Preferences "
- dat += "Content Preferences "
+ dat += "Preferences "
dat += "Keybindings "
if(!path)
@@ -461,996 +461,1066 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "[name] "
dat += " "
- dat += "Occupation Choices "
- dat += "Set Occupation Preferences "
- if(CONFIG_GET(flag/roundstart_traits))
- dat += "Quirk Setup "
- dat += "Configure Quirks "
- dat += "Current Quirks: [all_quirks.len ? all_quirks.Join(", ") : "None"] "
- dat += "Identity "
- dat += ""
+ dat += ""
+ dat += " "
+ switch(character_settings_tab)
+ //General
+ if(GENERAL_CHAR_TAB)
+ dat += "Occupation Choices "
+ dat += "Set Occupation Preferences "
+ if(CONFIG_GET(flag/roundstart_traits))
+ dat += "Quirk Setup "
+ dat += "Configure Quirks "
+ dat += "Current Quirks: [all_quirks.len ? all_quirks.Join(", ") : "None"] "
+ dat += "Identity "
+ dat += ""
+ if(jobban_isbanned(user, "appearance"))
+ dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game. "
+ dat += "Random Name "
+ dat += "Always Random Name: [be_random_name ? "Yes" : "No"] "
+
+ dat += "[nameless ? "Default designation" : "Name"]: "
+ dat += "[real_name] "
+ dat += "Be nameless: [nameless ? "Yes" : "No"] "
+
+ dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))] "
+ dat += "Age: [age] "
+
+ dat += "Special Names: "
+ var/old_group
+ for(var/custom_name_id in GLOB.preferences_custom_names)
+ var/namedata = GLOB.preferences_custom_names[custom_name_id]
+ if(!old_group)
+ old_group = namedata["group"]
+ else if(old_group != namedata["group"])
+ old_group = namedata["group"]
dat += " "
- unspaced_slots = 0
- S.cd = "/character[i]"
- S["real_name"] >> name
- if(!name)
- name = "Character[i]"
- dat += "[name] "
- dat += ""
+ dat += "[namedata["pref_name"]]: [custom_names[custom_name_id]] "
+ dat += " "
- dat += ""
- dat += "Flavor Text "
- dat += "Set Examine Text "
- if(length(features["flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN)
- if(!length(features["flavor_text"]))
- dat += "\[...\] " //skyrat - adds //come to brazil or brazil comes to you
- else
- dat += "[html_encode(features["flavor_text"])] " //skyrat - adds and uses html_encode
- else
- dat += "[TextPreview(html_encode(features["flavor_text"]))]... " //skyrat edit, uses html_encode
- //SPLURT edit - naked flavor text
- dat += "Naked Flavor Text "
- dat += "Set Naked Examine Text "
- if(length(features["naked_flavor_text"]) <= 40)
- if(!length(features["naked_flavor_text"]))
- dat += "\[...\] "
- else
- dat += "[html_encode(features["naked_flavor_text"])] "
- else
- dat += "[TextPreview(html_encode(features["naked_flavor_text"]))]... "
- //SPLURT edit end
- dat += "Silicon Flavor Text "
- dat += "Set Silicon Examine Text "
- if(length(features["silicon_flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN)
- if(!length(features["silicon_flavor_text"]))
- dat += "\[...\] "
- else
- dat += "[features["silicon_flavor_text"]] "
- else
- dat += "[TextPreview(features["silicon_flavor_text"])]... "
- dat += "OOC notes "
- dat += "Set OOC notes "
- var/ooc_notes_len = length(features["ooc_notes"])
- if(ooc_notes_len <= MAX_FLAVOR_PREVIEW_LEN)
- if(!ooc_notes_len)
- dat += "\[...\] "
- else
- dat += "[features["ooc_notes"]] "
- else
- dat += "[TextPreview(features["ooc_notes"])]... "
- //SPLURT EDIT
- dat += "Headshot Image "
- dat += "Set Headshot Image "
- if(features["headshot_link"])
- dat += " "
- dat += " "
- //SPLURT EDIT END
- //SKYRAT EDIT
- dat += "ERP : [erppref] "
- dat += "Non-Con : [nonconpref] "
- dat += "Vore : [vorepref] "
- //END OF SKYRAT EDIT
+ dat += "Custom job preferences: "
+ dat += "Preferred AI Core Display: [preferred_ai_core_display] "
+ dat += "Preferred Security Department: [prefered_security_department] "
+ dat += "Hide ckey: [hide_ckey ? "Enabled" : "Disabled"] "
+ dat += "
"
- dat += "Records "
- dat += "Security Records "
- if(length_char(security_records) <= 40)
- if(!length(security_records))
- dat += "\[...\]"
- else
- dat += "[security_records]"
- else
- dat += "[TextPreview(security_records)]... "
-
- dat += "Medical Records "
- if(length_char(medical_records) <= 40)
- if(!length(medical_records))
- dat += "\[...\] "
- else
- dat += "[medical_records]"
- else
- dat += "[TextPreview(medical_records)]... "
-
- dat += APPEARANCE_CATEGORY_COLUMN //body moves right sandstorm change
-
- dat += "Body "
- dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))] "
- if(gender != NEUTER && pref_species.sexes)
- dat += "Body Model: [features["body_model"] == MALE ? "Masculine" : "Feminine"] "
- dat += "Limb Modification: "
- dat += "Modify Limbs "
- for(var/modification in modified_limbs)
- if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
- dat += "[modification]: [modified_limbs[modification][2]] "
- else
- dat += "[modification]: [modified_limbs[modification][1]] "
- dat += " "
- dat += "Species: [pref_species.name] "
- dat += "Custom Species Name: [custom_species ? custom_species : "None"] "
- dat += "Random Body: Randomize! "
- dat += "Always Random Body: [be_random_body ? "Yes" : "No"] "
- dat += "Cycle background: [bgstate] "
-
- dat += " " //body moves right sandstorm change
-
- var/use_skintones = pref_species.use_skintones
- if(use_skintones)
- dat += APPEARANCE_CATEGORY_COLUMN
-
- dat += "Skin Tone "
-
- dat += "[use_custom_skin_tone ? "custom: [skin_tone] " : skin_tone] "
-
- var/mutant_colors
- if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits))
- if(!use_skintones)
- dat += APPEARANCE_CATEGORY_COLUMN
-
- dat += "Advanced Coloring "
- dat += "[(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) ? "Enabled" : "Disabled"] "
-
- dat += "Body Colors "
-
- dat += "Primary Color: "
- dat += "#[features["mcolor"]] Change "
-
- dat += "Secondary Color: "
- dat += "#[features["mcolor2"]] Change "
-
- dat += "Tertiary Color: "
- dat += "#[features["mcolor3"]] Change "
- mutant_colors = TRUE
-
- dat += "Sprite Size: [features["body_size"]*100]% "
-
- if(!(NOEYES in pref_species.species_traits))
- dat += "Eye Type "
- dat += "[eye_type] "
- if((EYECOLOR in pref_species.species_traits))
- if(!use_skintones && !mutant_colors)
- dat += APPEARANCE_CATEGORY_COLUMN
- if(left_eye_color != right_eye_color)
- split_eye_colors = TRUE
- dat += "Heterochromia "
- dat += "[split_eye_colors ? "Enabled" : "Disabled"] "
- if(!split_eye_colors)
- dat += "Eye Color "
- dat += "#[left_eye_color] Change "
+ //Character Appearance
+ if(APPEARANCE_CHAR_TAB)
+ dat += ""
+ dat += "Flavor Text "
+ dat += "Set Examine Text "
+ if(length(features["flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN)
+ if(!length(features["flavor_text"]))
+ dat += "\[...\] " //skyrat - adds //come to brazil or brazil comes to you
+ else
+ dat += "[html_encode(features["flavor_text"])] " //skyrat - adds and uses html_encode
else
- dat += "Left Eye Color "
- dat += "#[left_eye_color] Change "
- dat += "Right Eye Color "
- dat += "#[right_eye_color] Change "
-
- if(HAIR in pref_species.species_traits)
-
- dat += APPEARANCE_CATEGORY_COLUMN
-
- dat += "Hair Style "
-
- dat += "[hair_style] "
- dat += "< > "
- dat += "#[hair_color] Change "
-
- dat += "Facial Hair Style "
-
- dat += "[facial_hair_style] "
- dat += "< > "
- dat += "#[facial_hair_color] Change "
-
- dat += "Hair Gradient "
-
- dat += "[grad_style] "
- dat += "< > "
- dat += "#[grad_color] Change "
-
- dat += " "
- //Mutant stuff
- var/mutant_category = 0
-
- dat += APPEARANCE_CATEGORY_COLUMN
- dat += "Show mismatched markings "
- dat += "[show_mismatched_markings ? "Yes" : "No"] "
- mutant_category++
- if(mutant_category >= MAX_MUTANT_ROWS) //just in case someone sets the max rows to 1 or something dumb like that
- dat += ""
- mutant_category = 0
-
- // rp marking selection
- // assume you can only have mam markings or regular markings or none, never both
- var/marking_type
- if(parent.can_have_part("mam_body_markings"))
- marking_type = "mam_body_markings"
- if(marking_type)
- dat += APPEARANCE_CATEGORY_COLUMN
- dat += "[GLOB.all_mutant_parts[marking_type]] " // give it the appropriate title for the type of marking
- dat += "Add marking "
- // list out the current markings you have
- if(length(features[marking_type]))
- dat += ""
- var/list/markings = features[marking_type]
- if(!islist(markings))
- // something went terribly wrong
- markings = list()
- var/list/reverse_markings = reverseList(markings)
- for(var/list/marking_list in reverse_markings)
- var/marking_index = markings.Find(marking_list) // consider changing loop to go through indexes over lists instead of using Find here
- var/limb_value = marking_list[1]
- var/actual_name = GLOB.bodypart_names[num2text(limb_value)] // get the actual name from the bitflag representing the part the marking is applied to
- var/color_marking_dat = ""
- var/number_colors = 1
- var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]]
- var/matrixed_sections = S.covered_limbs[actual_name]
- if(S && matrixed_sections)
- // if it has nothing initialize it to white
- if(length(marking_list) == 2)
- var/first = "#FFFFFF"
- var/second = "#FFFFFF"
- var/third = "#FFFFFF"
- if(features["mcolor"])
- first = "#[features["mcolor"]]"
- if(features["mcolor2"])
- second = "#[features["mcolor2"]]"
- if(features["mcolor3"])
- third = "#[features["mcolor3"]]"
- marking_list += list(list(first, second, third)) // just assume its 3 colours if it isnt it doesnt matter we just wont use the other values
- // index magic
- var/primary_index = 1
- var/secondary_index = 2
- var/tertiary_index = 3
- switch(matrixed_sections)
- if(MATRIX_GREEN)
- primary_index = 2
- if(MATRIX_BLUE)
- primary_index = 3
- if(MATRIX_RED_BLUE)
- secondary_index = 2
- if(MATRIX_GREEN_BLUE)
- primary_index = 2
- secondary_index = 3
-
- // we know it has one matrixed section at minimum
- color_marking_dat += "[marking_list[3][primary_index]] "
- // if it has a second section, add it
- if(matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL)
- color_marking_dat += "[marking_list[3][secondary_index]] "
- number_colors = 2
- // if it has a third section, add it
- if(matrixed_sections == MATRIX_ALL)
- color_marking_dat += "[marking_list[3][tertiary_index]] "
- number_colors = 3
- color_marking_dat += " Change "
- dat += "[marking_list[2]] - [actual_name] ˄ ˅ X [color_marking_dat] "
- dat += "
"
-
- for(var/mutant_part in GLOB.all_mutant_parts)
- if(mutant_part == "mam_body_markings")
- continue
- if(parent.can_have_part(mutant_part))
- if(!mutant_category)
- dat += APPEARANCE_CATEGORY_COLUMN
- dat += "[GLOB.all_mutant_parts[mutant_part]] "
- dat += "[features[mutant_part]] "
- var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button
- if(color_type)
- dat += "#[features[color_type]] Change "
+ dat += "[TextPreview(html_encode(features["flavor_text"]))]... " //skyrat edit, uses html_encode
+ //SPLURT edit - naked flavor text
+ dat += "Naked Flavor Text "
+ dat += "Set Naked Examine Text "
+ if(length(features["naked_flavor_text"]) <= 40)
+ if(!length(features["naked_flavor_text"]))
+ dat += "\[...\] "
+ else
+ dat += "[html_encode(features["naked_flavor_text"])] "
else
- if(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) //advanced individual part colouring system
- //is it matrixed or does it have extra parts to be coloured?
- var/find_part = features[mutant_part] || pref_species.mutant_bodyparts[mutant_part]
- var/find_part_list = GLOB.mutant_reference_list[mutant_part]
- if(find_part && find_part != "None" && find_part_list)
- var/datum/sprite_accessory/accessory = find_part_list[find_part]
- if(accessory)
- if(accessory.color_src == MATRIXED || accessory.color_src == MUTCOLORS || accessory.color_src == MUTCOLORS2 || accessory.color_src == MUTCOLORS3) //mutcolors1-3 are deprecated now, please don't rely on these in the future
- var/mutant_string = accessory.mutant_part_string
- var/primary_feature = "[mutant_string]_primary"
- var/secondary_feature = "[mutant_string]_secondary"
- var/tertiary_feature = "[mutant_string]_tertiary"
- if(!features[primary_feature])
- features[primary_feature] = features["mcolor"]
- if(!features[secondary_feature])
- features[secondary_feature] = features["mcolor2"]
- if(!features[tertiary_feature])
- features[tertiary_feature] = features["mcolor3"]
+ dat += "[TextPreview(html_encode(features["naked_flavor_text"]))]... "
+ //SPLURT edit end
+ dat += "Silicon Flavor Text "
+ dat += "Set Silicon Examine Text "
+ if(length(features["silicon_flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN)
+ if(!length(features["silicon_flavor_text"]))
+ dat += "\[...\] "
+ else
+ dat += "[features["silicon_flavor_text"]] "
+ else
+ dat += "[TextPreview(features["silicon_flavor_text"])]... "
+ dat += "OOC notes "
+ dat += "Set OOC notes "
+ var/ooc_notes_len = length(features["ooc_notes"])
+ if(ooc_notes_len <= MAX_FLAVOR_PREVIEW_LEN)
+ if(!ooc_notes_len)
+ dat += "\[...\] "
+ else
+ dat += "[features["ooc_notes"]] "
+ else
+ dat += "[TextPreview(features["ooc_notes"])]... "
+ //SPLURT EDIT
+ dat += "Headshot Image "
+ dat += "Set Headshot Image "
+ if(features["headshot_link"])
+ dat += " "
+ dat += " "
+ //SPLURT EDIT END
+ //SKYRAT EDIT
+ dat += "Consent preferences "
+ dat += "ERP : [erppref] "
+ dat += "Non-Con : [nonconpref] "
+ dat += "Vore : [vorepref] "
+ //END OF SKYRAT EDIT
- var/matrixed_sections = accessory.matrixed_sections
- if(accessory.color_src == MATRIXED && !matrixed_sections)
- message_admins("Sprite Accessory Failure (customization): Accessory [accessory.type] is a matrixed item without any matrixed sections set!")
- continue
- else if(accessory.color_src == MATRIXED)
- switch(matrixed_sections)
- if(MATRIX_GREEN) //only composed of a green section
- primary_feature = secondary_feature //swap primary for secondary, so it properly assigns the second colour, reserved for the green section
- if(MATRIX_BLUE)
- primary_feature = tertiary_feature //same as above, but the tertiary feature is for the blue section
- if(MATRIX_RED_BLUE) //composed of a red and blue section
- secondary_feature = tertiary_feature //swap secondary for tertiary, as blue should always be tertiary
- if(MATRIX_GREEN_BLUE) //composed of a green and blue section
- primary_feature = secondary_feature //swap primary for secondary, as first option is green, which is linked to the secondary
- secondary_feature = tertiary_feature //swap secondary for tertiary, as second option is blue, which is linked to the tertiary
- dat += "Primary Color "
- dat += "#[features[primary_feature]] Change "
- if((accessory.color_src == MATRIXED && (matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL)) || (accessory.extra && (accessory.extra_color_src == MUTCOLORS || accessory.extra_color_src == MUTCOLORS2 || accessory.extra_color_src == MUTCOLORS3)))
- dat += "Secondary Color "
- dat += "#[features[secondary_feature]] Change "
- if((accessory.color_src == MATRIXED && matrixed_sections == MATRIX_ALL) || (accessory.extra2 && (accessory.extra2_color_src == MUTCOLORS || accessory.extra2_color_src == MUTCOLORS2 || accessory.extra2_color_src == MUTCOLORS3)))
- dat += "Tertiary Color "
- dat += "#[features[tertiary_feature]] Change "
+ dat += APPEARANCE_CATEGORY_COLUMN //body moves right sandstorm change
- mutant_category++
- if(mutant_category >= MAX_MUTANT_ROWS)
+ dat += "Body "
+ dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))] "
+ if(gender != NEUTER && pref_species.sexes)
+ dat += "Body Model: [features["body_model"] == MALE ? "Masculine" : "Feminine"] "
+ dat += "Limb Modification: "
+ dat += "Modify Limbs "
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ dat += "[modification]: [modified_limbs[modification][2]] "
+ else
+ dat += "[modification]: [modified_limbs[modification][1]] "
+ dat += " "
+ dat += "Species: [pref_species.name] "
+ dat += "Custom Species Name: [custom_species ? custom_species : "None"] "
+ dat += "Random Body: Randomize! "
+ dat += "Always Random Body: [be_random_body ? "Yes" : "No"] "
+ dat += "Cycle background: [bgstate] "
+
+ dat += "" //body moves right sandstorm change
+
+ var/use_skintones = pref_species.use_skintones
+ if(use_skintones)
+ dat += APPEARANCE_CATEGORY_COLUMN
+
+ dat += "Skin Tone "
+
+ dat += "[use_custom_skin_tone ? "custom: [skin_tone] " : skin_tone] "
+
+ var/mutant_colors
+ if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits))
+ if(!use_skintones)
+ dat += APPEARANCE_CATEGORY_COLUMN
+
+ dat += "Body Colors "
+
+ dat += "Primary Color: "
+ dat += "#[features["mcolor"]] Change "
+
+ dat += "Secondary Color: "
+ dat += "#[features["mcolor2"]] Change "
+
+ dat += "Tertiary Color: "
+ dat += "#[features["mcolor3"]] Change "
+ mutant_colors = TRUE
+
+ dat += "Sprite Size: [features["body_size"]*100]% "
+
+ if(!(NOEYES in pref_species.species_traits))
+ dat += "Eye Type "
+ dat += "[eye_type] "
+ if((EYECOLOR in pref_species.species_traits))
+ if(!use_skintones && !mutant_colors)
+ dat += APPEARANCE_CATEGORY_COLUMN
+ if(left_eye_color != right_eye_color)
+ split_eye_colors = TRUE
+ dat += "Heterochromia "
+ dat += "[split_eye_colors ? "Enabled" : "Disabled"] "
+ if(!split_eye_colors)
+ dat += "Eye Color "
+ dat += "#[left_eye_color] Change "
+ else
+ dat += "Left Eye Color "
+ dat += "#[left_eye_color] Change "
+ dat += "Right Eye Color "
+ dat += "#[right_eye_color] Change "
+
+ if(HAIR in pref_species.species_traits)
+
+ dat += APPEARANCE_CATEGORY_COLUMN
+
+ dat += "Hair Style "
+
+ dat += "[hair_style] "
+ dat += "< > "
+ dat += "#[hair_color] Change "
+
+ dat += "Facial Hair Style "
+
+ dat += "[facial_hair_style] "
+ dat += "< > "
+ dat += "#[facial_hair_color] Change "
+
+ dat += "Hair Gradient "
+
+ dat += "[grad_style] "
+ dat += "< > "
+ dat += "#[grad_color] Change "
+
+ dat += ""
+
+ //Mutant stuff
+ var/mutant_category = 0
+
+ for(var/mutant_part in GLOB.all_mutant_parts)
+ if(mutant_part == "mam_body_markings")
+ continue
+ if(parent.can_have_part(mutant_part))
+ if(!mutant_category)
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "[GLOB.all_mutant_parts[mutant_part]] "
+ dat += "[features[mutant_part]] "
+ var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button
+ if(color_type)
+ dat += "#[features[color_type]] Change "
+ else
+ if(features["color_scheme"] == ADVANCED_CHARACTER_COLORING) //advanced individual part colouring system
+ //is it matrixed or does it have extra parts to be coloured?
+ var/find_part = features[mutant_part] || pref_species.mutant_bodyparts[mutant_part]
+ var/find_part_list = GLOB.mutant_reference_list[mutant_part]
+ if(find_part && find_part != "None" && find_part_list)
+ var/datum/sprite_accessory/accessory = find_part_list[find_part]
+ if(accessory)
+ if(accessory.color_src == MATRIXED || accessory.color_src == MUTCOLORS || accessory.color_src == MUTCOLORS2 || accessory.color_src == MUTCOLORS3) //mutcolors1-3 are deprecated now, please don't rely on these in the future
+ var/mutant_string = accessory.mutant_part_string
+ var/primary_feature = "[mutant_string]_primary"
+ var/secondary_feature = "[mutant_string]_secondary"
+ var/tertiary_feature = "[mutant_string]_tertiary"
+ if(!features[primary_feature])
+ features[primary_feature] = features["mcolor"]
+ if(!features[secondary_feature])
+ features[secondary_feature] = features["mcolor2"]
+ if(!features[tertiary_feature])
+ features[tertiary_feature] = features["mcolor3"]
+
+ var/matrixed_sections = accessory.matrixed_sections
+ if(accessory.color_src == MATRIXED && !matrixed_sections)
+ message_admins("Sprite Accessory Failure (customization): Accessory [accessory.type] is a matrixed item without any matrixed sections set!")
+ continue
+ else if(accessory.color_src == MATRIXED)
+ switch(matrixed_sections)
+ if(MATRIX_GREEN) //only composed of a green section
+ primary_feature = secondary_feature //swap primary for secondary, so it properly assigns the second colour, reserved for the green section
+ if(MATRIX_BLUE)
+ primary_feature = tertiary_feature //same as above, but the tertiary feature is for the blue section
+ if(MATRIX_RED_BLUE) //composed of a red and blue section
+ secondary_feature = tertiary_feature //swap secondary for tertiary, as blue should always be tertiary
+ if(MATRIX_GREEN_BLUE) //composed of a green and blue section
+ primary_feature = secondary_feature //swap primary for secondary, as first option is green, which is linked to the secondary
+ secondary_feature = tertiary_feature //swap secondary for tertiary, as second option is blue, which is linked to the tertiary
+ dat += "Primary Color "
+ dat += "#[features[primary_feature]] Change "
+ if((accessory.color_src == MATRIXED && (matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL)) || (accessory.extra && (accessory.extra_color_src == MUTCOLORS || accessory.extra_color_src == MUTCOLORS2 || accessory.extra_color_src == MUTCOLORS3)))
+ dat += "Secondary Color "
+ dat += "#[features[secondary_feature]] Change "
+ if((accessory.color_src == MATRIXED && matrixed_sections == MATRIX_ALL) || (accessory.extra2 && (accessory.extra2_color_src == MUTCOLORS || accessory.extra2_color_src == MUTCOLORS2 || accessory.extra2_color_src == MUTCOLORS3)))
+ dat += "Tertiary Color "
+ dat += "#[features[tertiary_feature]] Change "
+
+ mutant_category++
+ if(mutant_category >= MAX_MUTANT_ROWS)
+ dat += ""
+ mutant_category = 0
+
+ if(length(pref_species.allowed_limb_ids))
+ if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids))
+ chosen_limb_id = pref_species.limbs_id || pref_species.id
+ if(!mutant_category)
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "Body sprite "
+ dat += "[chosen_limb_id] "
+
+ if(mutant_category)
dat += ""
mutant_category = 0
- if(length(pref_species.allowed_limb_ids))
- if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids))
- chosen_limb_id = pref_species.limbs_id || pref_species.id
- dat += "Body sprite "
- dat += "[chosen_limb_id] "
-
- if(mutant_category)
- dat += ""
- mutant_category = 0
-
- dat += "
"
-
- dat += ""
- dat += ""
dat += ""
- dat += ""
- dat += "
"
+ dat += ""
+ dat += "Clothing & Equipment "
+ /* skyrat change
+ dat += "Underwear: [underwear] "
+ if(GLOB.underwear_list[underwear]?.has_color)
+ dat += "Underwear Color: #[undie_color] Change "
+ dat += "Undershirt: [undershirt] "
+ if(GLOB.undershirt_list[undershirt]?.has_color)
+ dat += "Undershirt Color: #[shirt_color] Change "
+ dat += "Socks: [socks] "
+ if(GLOB.socks_list[socks]?.has_color)
+ dat += "Socks Color: #[socks_color] Change "
+ */
+ dat += "Backpack: [backbag] "
+ dat += "Jumpsuit: [jumpsuit_style] "
+ if((HAS_FLESH in pref_species.species_traits) || (HAS_BONE in pref_species.species_traits))
+ dat += "Temporal Scarring: [(persistent_scars) ? "Enabled" : "Disabled"] "
+ dat += "Clear scar slots "
+ dat += "Uplink Location: [uplink_spawn_loc] "
+ dat += " "
- if(SPEECH_TAB)
- if(path)
- var/savefile/S = new /savefile(path)
- if(S)
- dat += ""
- var/name
- var/unspaced_slots = 0
- for(var/i=1, i<=max_save_slots, i++)
- unspaced_slots++
- if(unspaced_slots > 4)
- dat += " "
- unspaced_slots = 0
- S.cd = "/character[i]"
- S["real_name"] >> name
- if(!name)
- name = "Character[i]"
- dat += "[name] "
- dat += " "
+ dat += ""
+ dat += "Lewd preferences "
+ dat += "Lust tolerance: [lust_tolerance] "
+ dat += "Sexual potency: [sexual_potency] "
+ dat += " "
- dat += ""
-
- if(GAME_PREFERENCES_TAB) // Game Preferences
- dat += ""
- if(unlock_content)
- dat += "Ghost Form: [ghost_form] "
- dat += "Ghost Orbit: [ghost_orbit] "
- var/button_name = "If you see this something went wrong."
- switch(ghost_accs)
- if(GHOST_ACCS_FULL)
- button_name = GHOST_ACCS_FULL_NAME
- if(GHOST_ACCS_DIR)
- button_name = GHOST_ACCS_DIR_NAME
- if(GHOST_ACCS_NONE)
- button_name = GHOST_ACCS_NONE_NAME
-
- dat += "Ghost Accessories: [button_name] "
- switch(ghost_others)
- if(GHOST_OTHERS_THEIR_SETTING)
- button_name = GHOST_OTHERS_THEIR_SETTING_NAME
- if(GHOST_OTHERS_DEFAULT_SPRITE)
- button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME
- if(GHOST_OTHERS_SIMPLE)
- button_name = GHOST_OTHERS_SIMPLE_NAME
-
- dat += "Ghosts of Others: [button_name] "
- dat += " "
-
- dat += "FPS: [clientfps] "
-
- dat += "Income Updates: [(chat_toggles & CHAT_BANKCARD) ? "Allowed" : "Muted"] "
- dat += " "
-
- dat += "Parallax (Fancy Space): "
- switch (parallax)
- if (PARALLAX_LOW)
- dat += "Low"
- if (PARALLAX_MED)
- dat += "Medium"
- if (PARALLAX_INSANE)
- dat += "Insane"
- if (PARALLAX_DISABLE)
- dat += "Disabled"
- else
- dat += "High"
- dat += " "
- dat += "Ambient Occlusion: [ambientocclusion ? "Enabled" : "Disabled"] "
- dat += "Fit Viewport: [auto_fit_viewport ? "Auto" : "Manual"] "
- dat += "HUD Button Flashes: [hud_toggle_flash ? "Enabled" : "Disabled"] "
- dat += "HUD Button Flash Color: [hud_toggle_color] Change "
-
-/* CITADEL EDIT - We're using top menu instead
- button_name = pixel_size
- dat += "Pixel Scaling: [(button_name) ? "Pixel Perfect [button_name]x" : "Stretch to fit"] "
-
- switch(scaling_method)
- if(SCALING_METHOD_NORMAL)
- button_name = "Nearest Neighbor"
- if(SCALING_METHOD_DISTORT)
- button_name = "Point Sampling"
- if(SCALING_METHOD_BLUR)
- button_name = "Bilinear"
- dat += "Scaling Method: [button_name] "
-*/
-
- if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation))
- var/p_map = preferred_map
- if (!p_map)
- p_map = "Default"
- if (config.defaultmap)
- p_map += " ([config.defaultmap.map_name])"
- else
- if (p_map in config.maplist)
- var/datum/map_config/VM = config.maplist[p_map]
- if (!VM)
- p_map += " (No longer exists)"
- else
- p_map = VM.map_name
- else
- p_map += " (No longer exists)"
- if(CONFIG_GET(flag/allow_map_voting))
- dat += "Preferred Map: [p_map] "
-
- dat += ""
-
- if(LOADOUT_TAB)
- //calculate your gear points from the chosen item
- gear_points = CONFIG_GET(number/initial_gear_points)
- var/list/chosen_gear = loadout_data["SAVE_[loadout_slot]"]
- if(chosen_gear)
- for(var/loadout_item in chosen_gear)
- var/loadout_item_path = loadout_item[LOADOUT_ITEM]
- if(loadout_item_path)
- var/datum/gear/loadout_gear = text2path(loadout_item_path)
- if(loadout_gear)
- gear_points -= initial(loadout_gear.cost)
- else
- chosen_gear = list()
-
- dat += ""
- dat += "[gear_points] loadout points remaining. \[Clear Loadout \] "
- dat += "You can only choose one item per category, unless it's an item that spawns in your backpack or hands. "
- dat += ""
-
- if(!length(GLOB.loadout_items))
- dat += " ERROR: No loadout categories - something is horribly wrong!"
- else
- if(!GLOB.loadout_categories[gear_category])
- gear_category = GLOB.loadout_categories[1]
- var/firstcat = TRUE
- for(var/category in GLOB.loadout_categories)
- if(firstcat)
- firstcat = FALSE
- else
- dat += " |"
- if(category == gear_category)
- dat += " [category] "
- else
- dat += " [category] "
-
- dat += " "
- dat += " "
-
- dat += ""
-
- if(!length(GLOB.loadout_categories[gear_category]))
- dat += "No subcategories detected. Something is horribly wrong!"
- else
- var/list/subcategories = GLOB.loadout_categories[gear_category]
- if(!subcategories.Find(gear_subcategory))
- gear_subcategory = subcategories[1]
-
- var/firstsubcat = TRUE
- for(var/subcategory in subcategories)
- if(firstsubcat)
- firstsubcat = FALSE
- else
- dat += " |"
- if(gear_subcategory == subcategory)
- dat += " [subcategory] "
- else
- dat += " [subcategory] "
- dat += " "
-
- dat += "Name "
- dat += "Cost "
- dat += "Restrictions "
- dat += "Description "
- for(var/name in GLOB.loadout_items[gear_category][gear_subcategory])
- var/datum/gear/gear = GLOB.loadout_items[gear_category][gear_subcategory][name]
- var/donoritem = gear.donoritem
- if(donoritem && !gear.donator_ckey_check(user.ckey))
- continue
- var/class_link = ""
- var/list/loadout_item = has_loadout_gear(loadout_slot, "[gear.type]")
- var/extra_loadout_data = ""
- if(loadout_item)
- class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'"
- if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)
- extra_loadout_data += "Color "
- for(var/loadout_color in loadout_item[LOADOUT_COLOR])
- extra_loadout_data += "[loadout_color] "
- else
- var/loadout_color_non_poly = "#FFFFFF"
- if(length(loadout_item[LOADOUT_COLOR]))
- loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1]
- extra_loadout_data += "Color "
- extra_loadout_data += "[loadout_color_non_poly] "
- if(gear.loadout_flags & LOADOUT_CAN_NAME)
- extra_loadout_data += "Name [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]"
- if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION)
- extra_loadout_data += "Description "
- else if((gear_points - gear.cost) < 0)
- class_link = "style='white-space:normal;' class='linkOff'"
- else if(donoritem)
- class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
- else if(!istype(gear, /datum/gear/unlockable) || can_use_unlockable(gear))
- class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
- else
- class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
- dat += "[name] [extra_loadout_data] "
- dat += "[gear.cost] "
- if(islist(gear.restricted_roles))
- if(gear.restricted_roles.len)
- if(gear.restricted_desc)
- dat += ""
- dat += gear.restricted_desc
- dat += " "
+ dat += "Penis Color: "
+ dat += "#[features["cock_color"]] Change "
+ var/tauric_shape = FALSE
+ if(features["cock_taur"])
+ var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[features["cock_shape"]]
+ if(P.taur_icon && parent.can_have_part("taur"))
+ var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]]
+ if(T.taur_mode & P.accepted_taurs)
+ tauric_shape = TRUE
+ dat += "Penis Shape: [features["cock_shape"]][tauric_shape ? " (Taur)" : ""] "
+ dat += "Penis Length: [features["cock_length"]] inch(es) "
+ dat += "Diameter Ratio: [features["cock_diameter_ratio"]] " //SPLURT Edit
+ dat += "Penis Visibility: [features["cock_visibility"]] "
+ dat += "Egg Stuffing: [features["cock_stuffing"] == TRUE ? "Yes" : "No"] " //SPLURT Edit
+ dat += "Has Testicles: [features["has_balls"] == TRUE ? "Yes" : "No"] "
+ if(features["has_balls"])
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "Testicles Color: "
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
else
- dat += ""
- dat += gear.restricted_roles.Join(";")
- dat += " "
- if(!istype(gear, /datum/gear/unlockable))
- // the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description"
- dat += " [loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] "
- else
- //we add the user's progress to the description assuming they have progress
- var/datum/gear/unlockable/unlockable = gear
- var/progress_made = unlockable_loadout_data[unlockable.progress_key]
- if(!progress_made)
- progress_made = 0
- dat += "[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required] "
+ dat += "Testicles Color: "
+ dat += "#[features["balls_color"]] Change "
+ dat += "Testicles Shape: [features["balls_shape"]] "
+ dat += "Testicles Visibility: [features["balls_visibility"]] "
+ //SPLURT Edit
+ dat += "Egg Stuffing: [features["balls_stuffing"] == TRUE ? "Yes" : "No"] "
+ dat += "Produces: "
+ var/datum/reagent/balls_fluid = find_reagent_object_from_type(features["balls_fluid"])
+ if(balls_fluid && (balls_fluid in GLOB.genital_fluids_list))
+ dat += "[balls_fluid.name] "
+ else
+ dat += "Nothing? "
+ //SPLURT Edit end
+ dat += ""
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "Vagina "
+ dat += "[features["has_vag"] == TRUE ? "Yes" : "No"] "
+ if(features["has_vag"])
+ dat += "Vagina Type: [features["vag_shape"]] "
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "Vagina Color: "
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
+ else
+ dat += "Vagina Color: "
+ dat += "#[features["vag_color"]] Change "
+ dat += "Vagina Visibility: [features["vag_visibility"]] "
+ dat += "Egg Stuffing: [features["vag_stuffing"] == TRUE ? "Yes" : "No"] " //SPLURT Edit
+ dat += "Has Womb: [features["has_womb"] == TRUE ? "Yes" : "No"] "
+ //SPLURT Edit
+ if(features["has_womb"] == TRUE)
+ dat += "Produces: "
+ var/datum/reagent/womb_fluid = find_reagent_object_from_type(features["womb_fluid"])
+ if(womb_fluid && (womb_fluid in GLOB.genital_fluids_list))
+ dat += "[womb_fluid.name] "
+ else
+ dat += "Nothing? "
+ //SPLURT Edit end
+ dat += ""
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "Breasts "
+ dat += "[features["has_breasts"] == TRUE ? "Yes" : "No"] "
+ if(features["has_breasts"])
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "Color: "
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
+ else
+ dat += "Color: "
+ dat += "#[features["breasts_color"]] Change "
+ dat += "Cup Size: [features["breasts_size"]] "
+ dat += "Breasts Shape: [features["breasts_shape"]] "
+ dat += "Breasts Visibility: [features["breasts_visibility"]] "
+ dat += "Lactates: [features["breasts_producing"] == TRUE ? "Yes" : "No"] "
+ //SPLURT Edit
+ dat += "Egg Stuffing: [features["breasts_stuffing"] == TRUE ? "Yes" : "No"] "
+ if(features["breasts_producing"] == TRUE)
+ dat += "Produces: "
+ var/datum/reagent/breasts_fluid = find_reagent_object_from_type(features["breasts_fluid"])
+ if(breasts_fluid && (breasts_fluid in GLOB.genital_fluids_list))
+ dat += "[breasts_fluid.name] "
+ else
+ dat += "Nothing? "
+ //SPLURT Edit end
+ dat += ""
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "Butt "
+ dat += "[features["has_butt"] == TRUE ? "Yes" : "No"] "
+ if(features["has_butt"])
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "Color: "
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
+ else
+ dat += "Color: "
+ dat += "#[features["butt_color"]] Change "
+ dat += "Butt Size: [features["butt_size"]] "
+ dat += "Butt Visibility: [features["butt_visibility"]] "
+ //SPLURT Edit
+ dat += "Egg Stuffing: [features["butt_stuffing"] == TRUE ? "Yes" : "No"] "
+ dat += "Butthole Sprite: [features["has_anus"] == TRUE ? "Yes" : "No"] "
+ if(features["has_anus"])
+ dat += "Butthole Color: "
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
+ else
+ dat += "#[features["anus_color"]] Change "
+ dat += "Butthole Shape: [features["anus_shape"]] "
+ dat += "Butthole Visibility: [features["anus_visibility"]] "
+ dat += "Egg Stuffing: [features["anus_stuffing"] == TRUE ? "Yes" : "No"] "
+ dat += ""
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += "Belly "
+ dat += "[features["has_belly"] == TRUE ? "Yes" : "No"] "
+ if(features["has_belly"])
+ if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE)
+ dat += "Color: "
+ dat += "[SKINTONE2HEX(skin_tone)] (Skin tone overriding) "
+ else
+ dat += "Color: "
+ dat += "#[features["belly_color"]] Change "
+ dat += "Belly Size: [features["belly_size"]] "
+ dat += "Belly Visibility: [features["belly_visibility"]] "
+ dat += "Egg Stuffing: [features["belly_stuffing"] == TRUE ? "Yes" : "No"] "
+ dat += ""
+ if(all_quirks.Find("Dullahan"))
+ dat += APPEARANCE_CATEGORY_COLUMN
+
+ dat += "Neckfire "
+ dat += "[features["neckfire"] ? "Yes" : "No"] "
+ if(features["neckfire"])
+ dat += "Color: "
+ dat += "#[features["neckfire_color"]] Change "
+
+ dat += ""
+ //SPLURT Edit end
+ dat += ""
+ dat += "
"
+ //Markings
+ if(MARKINGS_CHAR_TAB)
+ var/iterated_markings = 0
+ var/total_pages = 0
+ // rp marking selection
+ // assume you can only have mam markings or regular markings or none, never both
+ var/marking_type
+ if(parent.can_have_part("mam_body_markings"))
+ marking_type = "mam_body_markings"
+ if(marking_type)
+ dat += APPEARANCE_CATEGORY_COLUMN
+ dat += ""
+ dat += "[GLOB.all_mutant_parts[marking_type]] " // give it the appropriate title for the type of marking
+ dat += "Add marking "
+ dat += " "
+
+ dat += ""
+
+ for(var/limb in GLOB.bodypart_values)
+ if(length(GLOB.bodypart_values) % 3 != 0)
+ continue
+ total_pages++
+
+ for(var/limb in GLOB.bodypart_values)
+ if(!iterated_markings)
+ dat += ""
+ dat += "[limb] "
+ dat += ""
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += ""
+
+ // list out the current markings you have
+ if(length(features[marking_type]))
+ var/list/markings = features[marking_type]
+ if(!islist(markings))
+ // something went terribly wrong
+ markings = list()
+
+ for(var/list/marking_list in markings)
+ var/marking_index = markings.Find(marking_list) // consider changing loop to go through indexes over lists instead of using Find here
+ var/limb_value = marking_list[1]
+ var/actual_name = GLOB.bodypart_names[num2text(limb_value)] // get the actual name from the bitflag representing the part the marking is applied to
+ if(actual_name != limb)
+ continue
+ var/color_marking_dat = ""
+ var/number_colors = 1
+ var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]]
+ var/matrixed_sections = S.covered_limbs[actual_name]
+ if(S && matrixed_sections)
+ // if it has nothing initialize it to white
+ if(length(marking_list) == 2)
+ var/first = "#FFFFFF"
+ var/second = "#FFFFFF"
+ var/third = "#FFFFFF"
+ if(features["mcolor"])
+ first = "#[features["mcolor"]]"
+ if(features["mcolor2"])
+ second = "#[features["mcolor2"]]"
+ if(features["mcolor3"])
+ third = "#[features["mcolor3"]]"
+ marking_list += list(list(first, second, third)) // just assume its 3 colours if it isnt it doesnt matter we just wont use the other values
+ // index magic
+ var/primary_index = 1
+ var/secondary_index = 2
+ var/tertiary_index = 3
+ switch(matrixed_sections)
+ if(MATRIX_GREEN)
+ primary_index = 2
+ if(MATRIX_BLUE)
+ primary_index = 3
+ if(MATRIX_RED_BLUE)
+ secondary_index = 2
+ if(MATRIX_GREEN_BLUE)
+ primary_index = 2
+ secondary_index = 3
+
+ // we know it has one matrixed section at minimum
+ color_marking_dat += "[marking_list[3][primary_index]] "
+ // if it has a second section, add it
+ if(matrixed_sections == MATRIX_RED_BLUE || matrixed_sections == MATRIX_GREEN_BLUE || matrixed_sections == MATRIX_RED_GREEN || matrixed_sections == MATRIX_ALL)
+ number_colors = 2
+ color_marking_dat += "[marking_list[3][secondary_index]] "
+ // if it has a third section, add it
+ if(matrixed_sections == MATRIX_ALL)
+ number_colors = 3
+ color_marking_dat += "[marking_list[3][tertiary_index]] "
+ dat += ""
+ dat += "[marking_index] "
+ dat += "˅ "
+ dat += "˄ "
+ dat += "[marking_list[2]] "
+ dat += "[color_marking_dat] "
+ dat += "X "
+ dat += " "
+
+ else
+ dat += ""
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+ dat += " "
+
+ dat += "
"
+
+ iterated_markings++
+ if(iterated_markings >= 3)
+ dat += " "
+ iterated_markings = 0
+ dat += "
"
+
+ if(SPEECH_CHAR_TAB)
+ dat += ""
+ if(BACKGROUND_CHAR_TAB)
+ dat += ""
+ dat += "Records "
+ dat += "Security Records "
+ if(length_char(security_records) <= 40)
+ if(!length(security_records))
+ dat += "\[...\]"
+ else
+ dat += "[security_records]"
+ else
+ dat += "[TextPreview(security_records)]... "
+
+ dat += "Medical Records "
+ if(length_char(medical_records) <= 40)
+ if(!length(medical_records))
+ dat += "\[...\] "
+ else
+ dat += "[medical_records]"
+ else
+ dat += "[TextPreview(medical_records)]... "
+ dat += "
"
+ if(LOADOUT_CHAR_TAB)
+ dat += ""
+ dat += "You can only choose one item per category, unless it's an item that spawns in your backpack or hands. "
+ dat += ""
+
+ if(!length(GLOB.loadout_items))
+ dat += " ERROR: No loadout categories - something is horribly wrong!"
+ else
+ if(!GLOB.loadout_categories[gear_category])
+ gear_category = GLOB.loadout_categories[1]
+ var/firstcat = TRUE
+ for(var/category in GLOB.loadout_categories)
+ if(firstcat)
+ firstcat = FALSE
+ else
+ dat += " |"
+ if(category == gear_category)
+ dat += " [category] "
+ else
+ dat += " [category] "
+
+ dat += " "
+ dat += " "
+
+ dat += ""
+
+ if(!length(GLOB.loadout_categories[gear_category]))
+ dat += "No subcategories detected. Something is horribly wrong!"
+ else
+ var/list/subcategories = GLOB.loadout_categories[gear_category]
+ if(!subcategories.Find(gear_subcategory))
+ gear_subcategory = subcategories[1]
+
+ var/firstsubcat = TRUE
+ for(var/subcategory in subcategories)
+ if(firstsubcat)
+ firstsubcat = FALSE
+ else
+ dat += " |"
+ if(gear_subcategory == subcategory)
+ dat += " [subcategory] "
+ else
+ dat += " [subcategory] "
+ dat += " "
+
+ dat += ""
+ dat += ""
+ dat += "Name "
+ dat += "Cost "
+ dat += "Restrictions "
+ dat += "Description "
+ dat += " "
+
+ var/even = FALSE
+ for(var/name in GLOB.loadout_items[gear_category][gear_subcategory])
+ var/datum/gear/gear = GLOB.loadout_items[gear_category][gear_subcategory][name]
+ var/donoritem = gear.donoritem
+ if(donoritem && !gear.donator_ckey_check(user.ckey))
+ continue
+ var/background_cl = "#23273C"
+ if(even)
+ background_cl = "#17191C"
+ even = !even
+ var/class_link = ""
+ var/list/loadout_item = has_loadout_gear(loadout_slot, "[gear.type]")
+ var/extra_loadout_data = ""
+ if(loadout_item)
+ class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'"
+ if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)
+ extra_loadout_data += "Color "
+ for(var/loadout_color in loadout_item[LOADOUT_COLOR])
+ extra_loadout_data += "[loadout_color] "
+ else
+ var/loadout_color_non_poly = "#FFFFFF"
+ if(length(loadout_item[LOADOUT_COLOR]))
+ loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1]
+ extra_loadout_data += "Color "
+ extra_loadout_data += "[loadout_color_non_poly] "
+ if(gear.loadout_flags & LOADOUT_CAN_NAME)
+ extra_loadout_data += "Name [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]"
+ if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION)
+ extra_loadout_data += "Description "
+ else if((gear_points - gear.cost) < 0)
+ class_link = "style='white-space:normal;' class='linkOff'"
+ else if(donoritem)
+ class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
+ else if(!istype(gear, /datum/gear/unlockable) || can_use_unlockable(gear))
+ class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
+ else
+ class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
+ dat += "[name] [extra_loadout_data] "
+ dat += "[gear.cost] "
+ if(islist(gear.restricted_roles))
+ if(gear.restricted_roles.len)
+ if(gear.restricted_desc)
+ dat += ""
+ dat += gear.restricted_desc
+ dat += " "
+ else
+ dat += ""
+ dat += gear.restricted_roles.Join(";")
+ dat += " "
+ if(!istype(gear, /datum/gear/unlockable))
+ // the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description"
+ dat += " [loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] "
+ else
+ //we add the user's progress to the description assuming they have progress
+ var/datum/gear/unlockable/unlockable = gear
+ var/progress_made = unlockable_loadout_data[unlockable.progress_key]
+ if(!progress_made)
+ progress_made = 0
+ dat += "[loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required] "
+ dat += "
"
dat += "
"
- if(CONTENT_PREFERENCES_TAB) // Content preferences
- dat += ""
- dat += " "
+ if(PREFERENCES_TAB) // Game Preferences
+ dat += ""
+ dat += "General "
+ dat += "OOC "
+ dat += "Content "
+ dat += " "
+
+ dat += " "
+
+ switch(preferences_tab)
+ if(GAME_PREFS_TAB)
+ dat += ""
+
+ if(OOC_PREFS_TAB)
+ dat += ""
+
+ if(CONTENT_PREFS_TAB)
+ dat += ""
+
if(KEYBINDINGS_TAB) // Custom keybindings
dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Input"] "
dat += "Keybindings mode controls how the game behaves with tab and map/input focus. If it is on Hotkeys , the game will always attempt to force you to map focus, meaning keypresses are sent \
@@ -3148,47 +3218,37 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/limb_value = text2num(GLOB.bodypart_values[limb])
features[marking_type] += list(list(limb_value, selected_marking))
- if("marking_color")
+ if("marking_color_specific")
var/index = text2num(href_list["marking_index"])
var/marking_type = href_list["marking_type"]
- if(index && marking_type && features[marking_type])
- // work out the input options to show the user
- var/list/options = list("Primary")
- var/number_colors = text2num(href_list["number_colors"])
- var/color_number = 1 // 1-3 which color are we editing
- if(number_colors >= 2)
- options += "Secondary"
- if(number_colors == 3)
- options += "Tertiary"
- var/color_option = input(user, "Select the colour you wish to edit") as null|anything in options
- if(color_option)
- if(color_option == "Secondary") color_number = 2
- if(color_option == "Tertiary") color_number = 3
- // perform some magic on the color number
- var/list/marking_list = features[marking_type][index]
- var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]]
- var/matrixed_sections = S.covered_limbs[GLOB.bodypart_names[num2text(marking_list[1])]]
- if(color_number == 1)
- switch(matrixed_sections)
- if(MATRIX_GREEN)
- color_number = 2
- if(MATRIX_BLUE)
- color_number = 3
- else if(color_number == 2)
- switch(matrixed_sections)
- if(MATRIX_RED_BLUE)
- color_number = 3
- if(MATRIX_GREEN_BLUE)
- color_number = 3
+ var/color_number = text2num(href_list["number_color"])
+ if(index && marking_type && color_number && features[marking_type])
+ // perform some magic on the color number
+ var/list/marking_list = features[marking_type][index]
+ var/datum/sprite_accessory/mam_body_markings/S = GLOB.mam_body_markings_list[marking_list[2]]
+ var/matrixed_sections = S.covered_limbs[GLOB.bodypart_names[num2text(marking_list[1])]]
+ if(color_number == 1)
+ switch(matrixed_sections)
+ if(MATRIX_GREEN)
+ color_number = 2
+ if(MATRIX_BLUE)
+ color_number = 3
+ else if(color_number == 2)
+ switch(matrixed_sections)
+ if(MATRIX_RED_BLUE)
+ color_number = 3
+ if(MATRIX_GREEN_BLUE)
+ color_number = 3
- var/color_list = features[marking_type][index][3]
- var/new_marking_color = input(user, "Choose your character's marking color:", "Character Preference","#"+color_list[color_number]) as color|null
- if(new_marking_color)
- var/temp_hsv = RGBtoHSV(new_marking_color)
- if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
- color_list[color_number] = "#[sanitize_hexcolor(new_marking_color, 6)]"
- else
- to_chat(user, "Invalid color. Your color is not bright enough. ")
+ var/color_list = features[marking_type][index][3]
+ var/new_marking_color = input(user, "Choose your character's marking color:", "Character Preference","#"+color_list[color_number]) as color|null
+ if(new_marking_color)
+ var/temp_hsv = RGBtoHSV(new_marking_color)
+ if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
+ color_list[color_number] = "#[sanitize_hexcolor(new_marking_color, 6)]"
+ else
+ to_chat(user, "Invalid color. Your color is not bright enough. ")
+ //SPLURT Edit
if("gfluid_black")
var/list/datum/reagent/fluid_list = GLOB.genital_fluids_list.Copy()
var/list/blacklisted = list()
@@ -3208,6 +3268,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
LAZYREMOVE(gfluid_blacklist, selected.type)
else
to_chat(user, span_warning("You do not have blacklisted reagents!"))
+ //SPLURT Edit end
else
switch(href_list["preference"])
if("disable_combat_cursor")
@@ -3488,7 +3549,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(pickedOutlineColor != pickedOutlineColor)
outline_color = pickedOutlineColor // nullable
if("screentip_pref")
- screentip_pref = !screentip_pref
+ var/choice = tgalert(user, "Choose your screentip preference", "Screentipping?", "Yes", "Context Only", "No")
+ switch(choice)
+ if("Yes")
+ screentip_pref = SCREENTIP_PREFERENCE_ENABLED
+ if("Context Only")
+ screentip_pref = SCREENTIP_PREFERENCE_CONTEXT_ONLY
+ else
+ screentip_pref = SCREENTIP_PREFERENCE_DISABLED
if("screentip_color")
var/pickedScreentipColor = input(user, "Choose your screentip color.", "General Preference", screentip_color) as color|null
if(pickedScreentipColor)
@@ -3732,6 +3800,17 @@ GLOBAL_LIST_EMPTY(preferences_datums)
to_chat(usr, span_notice("Keep in mind that the photo will be downsized to 250x250 pixels, so the more square the photo, the better it will look."))
features["headshot_link"] = usr_input
+ if("character_preview")
+ preview_pref = href_list["tab"]
+
+ if("character_tab")
+ if(href_list["tab"])
+ character_settings_tab = text2num(href_list["tab"])
+
+ if("preferences_tab")
+ if(href_list["tab"])
+ preferences_tab = text2num(href_list["tab"])
+
if(href_list["preference"] == "gear")
if(href_list["clear_loadout"])
loadout_data["SAVE_[loadout_slot]"] = list()
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 4754a87e3c..c265604f81 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 56
+#define SAVEFILE_VERSION_MAX 57
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -56,6 +56,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
be_special -= "NO_ANTAGS"
for(var/be_special_type in be_special)
be_special[be_special_type] = 1
+ if(current_version < 57)
+ if(screentip_pref)
+ screentip_pref = SCREENTIP_PREFERENCE_ENABLED
+ else
+ // Let's give it a little chance okay, change if you don't like still.
+ screentip_pref = SCREENTIP_PREFERENCE_CONTEXT_ONLY
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 19)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index f349eae02b..5afd5abe3e 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -82,12 +82,14 @@
if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
add_fingerprint(usr)
+
/obj/item/reagent_containers/food/snacks/clothing
name = "oops"
desc = "If you're reading this it means I messed up. This is related to moths eating clothes and I didn't know a better way to do it than making a new food object."
list_reagents = list(/datum/reagent/consumable/nutriment = 1)
tastes = list("dust" = 1, "lint" = 1)
-
+/*
+MOVED TO: modular_splurt/code/module/clothing/clothing.dm
/obj/item/clothing/attack(mob/M, mob/user, def_zone)
if(user.a_intent != INTENT_HARM && isinsect(M))
var/obj/item/reagent_containers/food/snacks/clothing/clothing_as_food = new
@@ -98,6 +100,9 @@
else
return ..()
+ return ..()
+*/
+
/obj/item/clothing/attackby(obj/item/W, mob/user, params)
if(damaged_clothes && istype(W, repairable_by))
var/obj/item/stack/S = W
@@ -130,6 +135,7 @@
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
to_chat(user, "You fix the damage on [src]. ")
+
/**
* take_damage_zone() is used for dealing damage to specific bodyparts on a worn piece of clothing, meant to be called from [/obj/item/bodypart/proc/check_woundings_mods()]
*
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index f3868f703f..0a626a0558 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -440,6 +440,16 @@
icon_state = "cowboyhat_sec"
item_state= "cowboyhat_sec"
+/obj/item/clothing/head/cowboyhat/polychromic
+ name = "polychromic cowboy hat"
+ desc = "A polychromic cowboy hat, perfect for your indecisive rancher"
+ icon_state = "cowboyhat_poly"
+ item_state= "cowboyhat_poly"
+
+/obj/item/clothing/head/cowboyhat/polychromic/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#5F5F5F", "#DDDDDD"), 2)
+
/obj/item/clothing/head/squatter_hat
name = "slav squatter hat"
icon_state = "squatter_hat"
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 1cd09097f4..598136acfb 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -361,7 +361,7 @@
head = /obj/item/clothing/head/helmet/space/plasmaman
uniform = /obj/item/clothing/under/plasmaman
- r_hand= /obj/item/tank/internals/plasmaman/belt/full
+ r_hand = /obj/item/tank/internals/plasmaman/belt/full
mask = /obj/item/clothing/mask/breath
/datum/outfit/death_commando
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 7c1fc6fe3d..08a77b9b2b 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -144,7 +144,7 @@
/obj/item/clothing/shoes/jackboots
name = "jackboots"
- desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time."
+ desc = "Nanotrasen-brand jackboots for all your jackboots-related needs. From genuine combat to tacticool LARPing, satisfaction is guaranteed."
icon_state = "jackboots"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
@@ -155,6 +155,11 @@
pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
lace_time = 12 SECONDS
+/obj/item/clothing/shoes/jackboots/sec
+ name = "security jackboots"
+ desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time."
+ icon_state = "jackboots_sec"
+
/obj/item/clothing/shoes/jackboots/fast
slowdown = -1
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index d1af3e4512..c87095e5ff 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -26,7 +26,6 @@
var/max_accessories = 3
var/list/obj/item/clothing/accessory/attached_accessories = list()
var/list/mutable_appearance/accessory_overlays = list()
- var/is_skirt = FALSE
//SANDSTORM EDIT END
/obj/item/clothing/under/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 1b8b4d9e00..011f64d3ec 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -7,7 +7,8 @@
//
icon_state = "plasma"
item_state = "" //no inhands
- slot_flags = 0
+ slot_flags = ITEM_SLOT_ACCESSORY
+ slot_equipment_priority = list(ITEM_SLOT_ACCESSORY)
w_class = WEIGHT_CLASS_SMALL
var/above_suit = FALSE
var/minimize_when_attached = TRUE // TRUE if shown as a small icon in corner, FALSE if overlayed
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 3abe65a790..d8c7af788f 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -3,6 +3,8 @@
//this datum is used by the events controller to dictate how it selects events
/datum/round_event_control
var/name //The human-readable name of the event
+ var/category //The category of the event
+ var/description //The description of the event
var/typepath //The typepath of the event datum /datum/round_event
var/weight = 10 //The weight this event has in the random-selection process.
@@ -38,6 +40,7 @@
min_players = CEILING(min_players * CONFIG_GET(number/events_min_players_mul), 1)
/datum/round_event_control/wizard
+ category = EVENT_CATEGORY_WIZARD
wizardevent = TRUE
var/can_be_midround_wizard = TRUE
@@ -107,13 +110,22 @@
log_admin_private("[key_name(usr)] cancelled event [name].")
SSblackbox.record_feedback("tally", "event_admin_cancelled", 1, typepath)
-/datum/round_event_control/proc/runEvent(random = FALSE)
+/*
+Runs the event
+* Arguments:
+* - random: shows if the event was triggered randomly, or by on purpose by an admin or an item
+* - announce_chance_override: if the value is not null, overrides the announcement chance when an admin calls an event
+*/
+/datum/round_event_control/proc/runEvent(random = FALSE, announce_chance_override = null, admin_forced = FALSE)
var/datum/round_event/E = new typepath()
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
E.control = src
SSblackbox.record_feedback("tally", "event_ran", 1, "[E]")
occurrences++
+ if(announce_chance_override != null)
+ E.announce_chance = announce_chance_override
+
testing("[time2text(world.time, "hh:mm:ss")] [E.type]")
if(random)
log_game("Random Event triggering: [name] ([typepath])")
@@ -129,18 +141,29 @@
var/processing = TRUE
var/datum/round_event_control/control
- var/startWhen = 0 //When in the lifetime to call start().
- var/announceWhen = 0 //When in the lifetime to call announce(). Set an event's announceWhen to -1 if announcement should not be shown.
- var/endWhen = 0 //When in the lifetime the event should end.
+ /// When in the lifetime to call start().
+ /// This is in seconds - so 1 = ~2 seconds in.
+ var/start_when = 0
+ /// When in the lifetime to call announce(). If you don't want it to announce use announce_chance, below.
+ /// This is in seconds - so 1 = ~2 seconds in.
+ var/announce_when = 0
+ /// Probability of announcing, used in prob(), 0 to 100, default 100. Called in process, and for a second time in the ion storm event.
+ var/announce_chance = 100
+ /// When in the lifetime the event should end.
+ /// This is in seconds - so 1 = ~2 seconds in.
+ var/end_when = 0
- var/activeFor = 0 //How long the event has existed. You don't need to change this.
- var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start
+ /// How long the event has existed. You don't need to change this.
+ var/activeFor = 0
+ /// Amount of of alive, non-AFK human players on server at the time of event start
+ var/current_players = 0
var/threat = 0
- var/fakeable = TRUE //Can be faked by fake news event.
+ /// Can be faked by fake news event.
+ var/fakeable = TRUE
//Called first before processing.
//Allows you to setup your event, such as randomly
-//setting the startWhen and or announceWhen variables.
+//setting the start_when and or announce_when variables.
//Only called once.
//EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called.
//It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically).
@@ -148,7 +171,7 @@
/datum/round_event/proc/setup()
return
-//Called when the tick is equal to the startWhen variable.
+//Called when the tick is equal to the start_when variable.
//Allows you to start before announcing or vice versa.
//Only called once.
/datum/round_event/proc/start()
@@ -165,20 +188,20 @@
notify_ghosts("[control.name] has an object of interest: [atom_of_interest]!", source=atom_of_interest, action=NOTIFY_ORBIT, header="Something's Interesting!")
return
-//Called when the tick is equal to the announceWhen variable.
+//Called when the tick is equal to the announce_when variable.
//Allows you to announce before starting or vice versa.
//Only called once.
/datum/round_event/proc/announce(fake)
return
-//Called on or after the tick counter is equal to startWhen.
+//Called on or after the tick counter is equal to start_when.
//You can include code related to your event or add your own
//time stamped events.
//Called more than once.
/datum/round_event/proc/tick()
return
-//Called on or after the tick is equal or more than endWhen
+//Called on or after the tick is equal or more than end_when
//You can include code related to the event ending.
//Do not place spawn() in here, instead use tick() to check for
//the activeFor variable.
@@ -197,28 +220,28 @@
if(!processing)
return
- if(activeFor == startWhen)
+ if(activeFor == start_when)
processing = FALSE
start()
processing = TRUE
- if(activeFor == announceWhen)
+ if(activeFor == announce_when && prob(announce_chance))
processing = FALSE
announce(FALSE)
processing = TRUE
- if(startWhen < activeFor && activeFor < endWhen)
+ if(start_when < activeFor && activeFor < end_when)
processing = FALSE
tick()
processing = TRUE
- if(activeFor == endWhen)
+ if(activeFor == end_when)
processing = FALSE
end()
processing = TRUE
// Everything is done, let's clean up.
- if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen)
+ if(activeFor >= end_when && activeFor >= announce_when && activeFor >= start_when)
processing = FALSE
kill()
diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm
index 9fe3a2a7a9..64c743d1a1 100755
--- a/code/modules/events/abductor.dm
+++ b/code/modules/events/abductor.dm
@@ -6,6 +6,8 @@
min_players = 30
earliest_start = 30 MINUTES
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_INVASION
+ description = "One or more abductor teams spawns, and they plan to experiment on the crew."
/datum/round_event/ghost_role/abductor
minimum_required = 2
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index f7014df648..cf94253bbb 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -5,9 +5,11 @@
min_players = 25
max_occurrences = 1
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_ENTITIES
+ description = "A xenomorph larva spawns on a random vent."
/datum/round_event/ghost_role/alien_infestation
- announceWhen = 400
+ announce_when = 400
minimum_required = 1
role_name = "alien larva"
@@ -19,7 +21,7 @@
/datum/round_event/ghost_role/alien_infestation/setup()
- announceWhen = rand(announceWhen, announceWhen + 50)
+ announce_when = rand(announce_when, announce_when + 50)
if(prob(50))
spawncount++
diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm
index ae0d5442ad..456a83958d 100644
--- a/code/modules/events/anomaly.dm
+++ b/code/modules/events/anomaly.dm
@@ -5,11 +5,13 @@
min_players = 1
max_occurrences = 0 //This one probably shouldn't occur! It'd work, but it wouldn't be very fun.
weight = 15
+ category = EVENT_CATEGORY_ANOMALIES
+ description = "This anomaly shocks and explodes. This is the base type."
/datum/round_event/anomaly
var/area/impact_area
var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux
- announceWhen = 1
+ announce_when = 1
/datum/round_event/anomaly/proc/findEventArea()
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index fd64b20244..f058f37da4 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -4,10 +4,11 @@
max_occurrences = 1
weight = 5
+ description = "This anomaly randomly teleports all items and mobs in a large area."
/datum/round_event/anomaly/anomaly_bluespace
- startWhen = 3
- announceWhen = 10
+ start_when = 3
+ announce_when = 10
anomaly_path = /obj/effect/anomaly/bluespace
/datum/round_event/anomaly/anomaly_bluespace/announce(fake)
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index 6368e70b94..d3b1e8ea7e 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -4,10 +4,11 @@
max_occurrences = 5
weight = 20
+ description = "This anomaly shocks and explodes."
/datum/round_event/anomaly/anomaly_flux
- startWhen = 10
- announceWhen = 3
+ start_when = 10
+ announce_when = 3
anomaly_path = /obj/effect/anomaly/flux
/datum/round_event/anomaly/anomaly_flux/announce(fake)
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index c1b4ade5c0..232d2ddd35 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -4,11 +4,11 @@
max_occurrences = 5
weight = 20
-
+ description = "This anomaly throws things around."
/datum/round_event/anomaly/anomaly_grav
- startWhen = 3
- announceWhen = 20
+ start_when = 3
+ announce_when = 20
anomaly_path = /obj/effect/anomaly/grav
/datum/round_event/anomaly/anomaly_grav/announce(fake)
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 83c1fa64cc..dee0330743 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -5,10 +5,11 @@
min_players = 5
max_occurrences = 5
weight = 20
+ description = "This anomaly sets things on fire, and creates a pyroclastic slime."
/datum/round_event/anomaly/anomaly_pyro
- startWhen = 3
- announceWhen = 10
+ start_when = 3
+ announce_when = 10
anomaly_path = /obj/effect/anomaly/pyro
/datum/round_event/anomaly/anomaly_pyro/announce(fake)
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 7228030616..43dcc0baf4 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -5,10 +5,11 @@
min_players = 20
max_occurrences = 2
weight = 5
+ description = "This anomaly sucks in and detonates items."
/datum/round_event/anomaly/anomaly_vortex
- startWhen = 10
- announceWhen = 3
+ start_when = 10
+ announce_when = 3
anomaly_path = /obj/effect/anomaly/bhole
/datum/round_event/anomaly/anomaly_vortex/announce(fake)
diff --git a/code/modules/events/atmos_speed.dm b/code/modules/events/atmos_speed.dm
index 17a02d3dd1..64e6e883cc 100644
--- a/code/modules/events/atmos_speed.dm
+++ b/code/modules/events/atmos_speed.dm
@@ -3,10 +3,12 @@
typepath = /datum/round_event/atmos_flux
max_occurrences = 5
weight = 10
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Modifies the speed of the SSair randomly, ends after one minute."
/datum/round_event/atmos_flux
- announceWhen = 1
- endWhen = 600
+ announce_when = 1
+ end_when = 600
var/original_speed
/datum/round_event/atmos_flux/announce(fake)
diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm
index 89a84d3494..63d7122cb8 100644
--- a/code/modules/events/aurora_caelus.dm
+++ b/code/modules/events/aurora_caelus.dm
@@ -4,6 +4,8 @@
max_occurrences = 1
weight = 4
earliest_start = 5 MINUTES
+ category = EVENT_CATEGORY_FRIENDLY
+ description = "A colourful display can be seen through select windows. And the kitchen."
/datum/round_event_control/aurora_caelus/canSpawnEvent(players, gamemode)
if(!CONFIG_GET(flag/starlight))
@@ -11,9 +13,9 @@
return ..()
/datum/round_event/aurora_caelus
- announceWhen = 1
- startWhen = 9
- endWhen = 50
+ announce_when = 1
+ start_when = 9
+ end_when = 50
var/list/aurora_colors = list("#A2FF80", "#A2FF8B", "#A2FF96", "#A2FFA5", "#A2FFB6", "#A2FFC7", "#A2FFDE", "#A2FFEE")
var/aurora_progress = 0 //this cycles from 1 to 8, slowly changing colors from gentle green to gentle blue
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index e31711661d..d28c5d6512 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -7,9 +7,11 @@
earliest_start = 60 MINUTES
min_players = 35
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a new blob overmind."
/datum/round_event/ghost_role/blob
- announceWhen = -1
+ announce_when = -1
role_name = "blob overmind"
fakeable = TRUE
diff --git a/code/modules/events/brain_trauma.dm b/code/modules/events/brain_trauma.dm
index 0529d102dc..85d69a1509 100644
--- a/code/modules/events/brain_trauma.dm
+++ b/code/modules/events/brain_trauma.dm
@@ -3,6 +3,8 @@
typepath = /datum/round_event/brain_trauma
weight = 25
min_players = 5
+ category = EVENT_CATEGORY_HEALTH
+ description = "A crewmember gains a random trauma."
/datum/round_event_control/brain_trauma/canSpawnEvent(var/players_amt, var/gamemode)
if(!..()) return FALSE
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 1b275af4fb..cab9daaa55 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -5,10 +5,12 @@
min_players = 15
max_occurrences = 1
+ category = EVENT_CATEGORY_AI
+ description = "Vending machines will attack people until the Patient Zero is disabled."
/datum/round_event/brand_intelligence
- announceWhen = 21
- endWhen = 1000 //Ends when all vending machines are subverted anyway.
+ announce_when = 21
+ end_when = 1000 //Ends when all vending machines are subverted anyway.
var/list/obj/machinery/vending/vendingMachines = list()
var/list/obj/machinery/vending/infectedMachines = list()
var/obj/machinery/vending/originMachine
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index 7246c9aedf..fc35df791b 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -3,9 +3,11 @@
typepath = /datum/round_event/bureaucratic_error
max_occurrences = 1
weight = 5
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "Randomly opens and closes job slots, along with changing the overflow role."
/datum/round_event/bureaucratic_error
- announceWhen = 1
+ announce_when = 1
/datum/round_event/bureaucratic_error/announce(fake)
priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert")
diff --git a/code/modules/events/camerafailure.dm b/code/modules/events/camerafailure.dm
index 8d7ef3204c..453b919c5b 100644
--- a/code/modules/events/camerafailure.dm
+++ b/code/modules/events/camerafailure.dm
@@ -4,6 +4,8 @@
weight = 100
max_occurrences = 20
alert_observers = FALSE
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Turns off a random amount of cameras."
/datum/round_event/camera_failure
fakeable = FALSE
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index f927a59ad6..bed6cf84ea 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -5,6 +5,8 @@
min_players = 2
earliest_start = 10 MINUTES
max_occurrences = 6
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Summons a school of space carp."
/datum/round_event_control/carp_migration/New()
. = ..()
@@ -14,12 +16,12 @@
earliest_start *= 0.5
/datum/round_event/carp_migration
- announceWhen = 3
- startWhen = 50
+ announce_when = 3
+ start_when = 50
var/hasAnnounced = FALSE
/datum/round_event/carp_migration/setup()
- startWhen = rand(40, 60)
+ start_when = rand(40, 60)
/datum/round_event/carp_migration/announce(fake)
if(prob(50))
diff --git a/code/modules/events/cat_surgeon.dm b/code/modules/events/cat_surgeon.dm
index 025d2d2325..254fdaecca 100644
--- a/code/modules/events/cat_surgeon.dm
+++ b/code/modules/events/cat_surgeon.dm
@@ -1,45 +1,47 @@
/datum/round_event_control/cat_surgeon
- name = "Cat Surgeon"
- typepath = /datum/round_event/cat_surgeon
- max_occurrences = 1
- weight = 5
+ name = "Cat Surgeon"
+ typepath = /datum/round_event/cat_surgeon
+ max_occurrences = 1
+ weight = 5
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a crazy surgeon ready to perverse things with the crew."
/datum/round_event/cat_surgeon/announce(fake)
priority_announce("One of our... ahem... 'special' cases has escaped. As it happens their last known location before their tracker went dead is your station so keep an eye out for them. On an unrelated note, has anyone seen our cats?",
sender_override = "Nanotrasen Psych Ward", has_important_message = TRUE)
/datum/round_event/cat_surgeon/start()
- var/list/spawn_locs = list()
- var/list/unsafe_spawn_locs = list()
- for(var/X in GLOB.xeno_spawn)
- if(!isfloorturf(X))
- unsafe_spawn_locs += X
- continue
- var/turf/open/floor/F = X
- var/datum/gas_mixture/A = F.air
- var/oxy_moles = A.get_moles(GAS_O2)
- if((oxy_moles < 16 || oxy_moles > 50) || A.get_moles(GAS_PLASMA) || A.get_moles(GAS_CO2) >= 10)
- unsafe_spawn_locs += F
- continue
- if((A.return_temperature() <= 270) || (A.return_temperature() >= 360))
- unsafe_spawn_locs += F
- continue
- var/pressure = A.return_pressure()
- if((pressure <= 20) || (pressure >= 550))
- unsafe_spawn_locs += F
- continue
- spawn_locs += F
+ var/list/spawn_locs = list()
+ var/list/unsafe_spawn_locs = list()
+ for(var/X in GLOB.xeno_spawn)
+ if(!isfloorturf(X))
+ unsafe_spawn_locs += X
+ continue
+ var/turf/open/floor/F = X
+ var/datum/gas_mixture/A = F.air
+ var/oxy_moles = A.get_moles(GAS_O2)
+ if((oxy_moles < 16 || oxy_moles > 50) || A.get_moles(GAS_PLASMA) || A.get_moles(GAS_CO2) >= 10)
+ unsafe_spawn_locs += F
+ continue
+ if((A.return_temperature() <= 270) || (A.return_temperature() >= 360))
+ unsafe_spawn_locs += F
+ continue
+ var/pressure = A.return_pressure()
+ if((pressure <= 20) || (pressure >= 550))
+ unsafe_spawn_locs += F
+ continue
+ spawn_locs += F
- if(!spawn_locs.len)
- spawn_locs += unsafe_spawn_locs
+ if(!spawn_locs.len)
+ spawn_locs += unsafe_spawn_locs
- if(!spawn_locs.len)
- message_admins("No valid spawn locations found, aborting...")
- return MAP_ERROR
+ if(!spawn_locs.len)
+ message_admins("No valid spawn locations found, aborting...")
+ return MAP_ERROR
- var/turf/T = get_turf(pick(spawn_locs))
- var/mob/living/simple_animal/hostile/cat_butcherer/S = new(T)
- playsound(S, 'sound/misc/catscream.ogg', 75, 1, -1)
- message_admins("A cat surgeon has been spawned at [COORD(T)][ADMIN_JMP(T)]")
- log_game("A cat surgeon has been spawned at [COORD(T)]")
- return SUCCESSFUL_SPAWN
+ var/turf/T = get_turf(pick(spawn_locs))
+ var/mob/living/simple_animal/hostile/cat_butcherer/S = new(T)
+ playsound(S, 'sound/misc/catscream.ogg', 75, 1, -1)
+ message_admins("A cat surgeon has been spawned at [COORD(T)][ADMIN_JMP(T)]")
+ log_game("A cat surgeon has been spawned at [COORD(T)]")
+ return SUCCESSFUL_SPAWN
diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm
index 0342ca4643..b47dad0c93 100644
--- a/code/modules/events/communications_blackout.dm
+++ b/code/modules/events/communications_blackout.dm
@@ -2,9 +2,11 @@
name = "Communications Blackout"
typepath = /datum/round_event/communications_blackout
weight = 30
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Heavily emps all telecommunication machines, blocking all communication for a while."
/datum/round_event/communications_blackout
- announceWhen = 1
+ announce_when = 1
/datum/round_event/communications_blackout/announce(fake)
var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \
diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm
index 7d6e0bd441..3d6382b2a4 100644
--- a/code/modules/events/devil.dm
+++ b/code/modules/events/devil.dm
@@ -2,6 +2,8 @@
name = "Create Devil"
typepath = /datum/round_event/ghost_role/devil
max_occurrences = 0
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a devil, looking forward to makings deals with crewmembers to get their souls."
/datum/round_event/ghost_role/devil
var/success_spawn = 0
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 517cf33b82..753349fdd6 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -4,9 +4,11 @@
max_occurrences = 1
min_players = 3
weight = 5
+ category = EVENT_CATEGORY_HEALTH
+ description = "A classic or advanced disease will infect some crewmembers."
/datum/round_event/disease_outbreak
- announceWhen = 15
+ announce_when = 15
var/virus_type
@@ -24,7 +26,7 @@
priority_announce("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", "outbreak7")
/datum/round_event/disease_outbreak/setup()
- announceWhen = rand(15, 30)
+ announce_when = rand(15, 30)
/datum/round_event/disease_outbreak/start()
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 941478d52a..6b0139c449 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -5,10 +5,12 @@
max_occurrences = 1000
earliest_start = 0 MINUTES
alert_observers = FALSE
+ category = EVENT_CATEGORY_SPACE
+ description = "A single space dust is hurled at the station."
/datum/round_event/space_dust
- startWhen = 1
- endWhen = 2
+ start_when = 1
+ end_when = 2
fakeable = FALSE
/datum/round_event/space_dust/start()
@@ -21,11 +23,13 @@
max_occurrences = 1
min_players = 10
earliest_start = 20 MINUTES
+ category = EVENT_CATEGORY_SPACE
+ description = "The station is pelted by an extreme amount of sand for several minutes."
/datum/round_event/sandstorm
- startWhen = 1
- endWhen = 150 // ~5 min
- announceWhen = 0
+ start_when = 1
+ end_when = 150 // ~5 min
+ announce_when = 0
fakeable = FALSE
/datum/round_event/sandstorm/announce(fake)
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index b850b4db62..f04389b1a1 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -4,12 +4,13 @@
earliest_start = 10 MINUTES
min_players = 5
weight = 40
- alert_observers = FALSE
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Destroys all lights in a large area."
/datum/round_event/electrical_storm
var/lightsoutAmount = 1
var/lightsoutRange = 25
- announceWhen = 1
+ announce_when = 1
/datum/round_event/electrical_storm/announce(fake)
if(prob(50))
diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm
index cebf1ed14b..ec69f9e2c9 100644
--- a/code/modules/events/fake_virus.dm
+++ b/code/modules/events/fake_virus.dm
@@ -2,6 +2,8 @@
name = "Fake Virus"
typepath = /datum/round_event/fake_virus
weight = 20
+ category = EVENT_CATEGORY_HEALTH
+ description = "Some crewmembers suffer from temporary hypochondria."
/datum/round_event/fake_virus/start()
var/list/fake_virus_victims = list()
diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm
index 5ac75cf087..95b7d1c3ce 100644
--- a/code/modules/events/false_alarm.dm
+++ b/code/modules/events/false_alarm.dm
@@ -4,7 +4,8 @@
weight = 20
max_occurrences = 5
var/forced_type //Admin abuse
-
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "Fakes an event announcement."
/datum/round_event_control/falsealarm/admin_setup()
if(!check_rights(R_FUN))
@@ -17,15 +18,15 @@
if(!initial(event.fakeable))
continue
possible_types += E
-
+
forced_type = input(usr, "Select the scare.","False event") as null|anything in possible_types
/datum/round_event_control/falsealarm/canSpawnEvent(players_amt, gamemode)
return ..() && length(gather_false_events())
/datum/round_event/falsealarm
- announceWhen = 0
- endWhen = 1
+ announce_when = 0
+ end_when = 1
fakeable = FALSE
/datum/round_event/falsealarm/announce(fake)
diff --git a/code/modules/events/floorcluwne.dm b/code/modules/events/floorcluwne.dm
index ce9a2d8cc3..6b91fb2ca2 100644
--- a/code/modules/events/floorcluwne.dm
+++ b/code/modules/events/floorcluwne.dm
@@ -4,7 +4,8 @@
max_occurrences = 0
min_players = 20
weight = 10
-
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a floor cluwne, will hunt a random player and most likely gib them, prepare for adminhelps."
/datum/round_event/floor_cluwne/start()
var/list/spawn_locs = list()
diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm
index a09f0f584e..13964d6ec6 100644
--- a/code/modules/events/fugitive_spawning.dm
+++ b/code/modules/events/fugitive_spawning.dm
@@ -4,6 +4,8 @@
max_occurrences = 1
min_players = 20
earliest_start = 30 MINUTES //deadchat sink, lets not even consider it early on.
+ category = EVENT_CATEGORY_INVASION
+ description = "Fugitives will hide on the station, followed by hunters."
/datum/round_event/ghost_role/fugitives
minimum_required = 1
diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm
index ae1d1320a5..baabb435bc 100644
--- a/code/modules/events/ghost_role.dm
+++ b/code/modules/events/ghost_role.dm
@@ -7,6 +7,8 @@
var/minimum_required = 1
var/role_name = "debug rat with cancer" // Q U A L I T Y M E M E S
var/list/spawned_mobs = list()
+ var/status
+ var/cached_announcement_chance
fakeable = FALSE
/datum/round_event/ghost_role/start()
@@ -17,7 +19,10 @@
// to prevent us from getting gc'd halfway through
processing = FALSE
- var/status = spawn_role()
+ status = spawn_role()
+ if(isnull(cached_announcement_chance))
+ cached_announcement_chance = announce_chance //only announce once we've finished the spawning loop.
+ announce_chance = (status == SUCCESSFUL_SPAWN ? cached_announcement_chance : 0)
if((status == WAITING_FOR_SOMETHING))
if(retry >= MAX_SPAWN_ATTEMPT)
message_admins("[role_name] event has exceeded maximum spawn attempts. Aborting and refunding.")
diff --git a/code/modules/events/grid_check.dm b/code/modules/events/grid_check.dm
index 1bb0ee617a..c39a6e1bb2 100644
--- a/code/modules/events/grid_check.dm
+++ b/code/modules/events/grid_check.dm
@@ -3,10 +3,12 @@
typepath = /datum/round_event/grid_check
weight = 10
max_occurrences = 3
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Turns off all APCs for a while, or until they are manually rebooted."
/datum/round_event/grid_check
- announceWhen = 1
- startWhen = 1
+ announce_when = 1
+ start_when = 1
/datum/round_event/grid_check/announce(fake)
priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", "poweroff")
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index 930a2b884f..4f94576fa6 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -4,6 +4,8 @@
weight = 10
max_occurrences = 2
min_players = 10 // To avoid shafting lowpop
+ category = EVENT_CATEGORY_HEALTH
+ description = "A random crewmember's heart gives out."
/datum/round_event_control/heart_attack/canSpawnEvent(var/players_amt, var/gamemode)
if(!..()) return FALSE
diff --git a/code/modules/events/high_priority_bounty.dm b/code/modules/events/high_priority_bounty.dm
index ffdcd8840b..90f27b19f9 100644
--- a/code/modules/events/high_priority_bounty.dm
+++ b/code/modules/events/high_priority_bounty.dm
@@ -4,6 +4,8 @@
max_occurrences = 3
weight = 20
earliest_start = 10
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "Creates bounties that are three times original worth."
/datum/round_event/high_priority_bounty/announce(fake)
priority_announce("Central Command has issued a high-priority cargo bounty. Details have been sent to all bounty consoles.", "Nanotrasen Bounty Program")
diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm
index 9824355e4b..fd45eb4ccf 100644
--- a/code/modules/events/holiday/halloween.dm
+++ b/code/modules/events/holiday/halloween.dm
@@ -5,6 +5,8 @@
weight = -1 //forces it to be called, regardless of weight
max_occurrences = 1
earliest_start = 0 MINUTES
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Gives everyone treats, and turns Ian and Polly into their festive versions."
/datum/round_event/spooky/start()
..()
diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm
index 16ae047729..ebe44d3e00 100644
--- a/code/modules/events/holiday/vday.dm
+++ b/code/modules/events/holiday/vday.dm
@@ -11,6 +11,8 @@
weight = -1 //forces it to be called, regardless of weight
max_occurrences = 1
earliest_start = 0 MINUTES
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Puts people on dates! They must protect each other. Sometimes a vengeful third wheel spawns."
/datum/round_event/valentines/start()
..()
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index e043caa29d..a9bedb3c57 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -68,6 +68,8 @@
weight = 20
max_occurrences = 1
earliest_start = 30 MINUTES
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Spawns santa, who shall roam the station, handing out gifts."
/datum/round_event/santa
var/mob/living/carbon/human/santa //who is our santa?
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index 482abe67f2..a48191e8a1 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -13,7 +13,8 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
min_players = 15
max_occurrences = 5
var/atom/special_target
-
+ category = EVENT_CATEGORY_SPACE
+ description = "The station passes through an immovable rod."
/datum/round_event_control/immovable_rod/admin_setup()
if(!check_rights(R_FUN))
@@ -24,7 +25,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
special_target = get_turf(usr)
/datum/round_event/immovable_rod
- announceWhen = 5
+ announce_when = 5
/datum/round_event/immovable_rod/announce(fake)
priority_announce("What the fuck was that?!", "General Alert", has_important_message = TRUE)
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index f25b476657..0fefb43170 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -5,6 +5,8 @@
typepath = /datum/round_event/ion_storm
weight = 15
min_players = 2
+ category = EVENT_CATEGORY_AI
+ description = "Gives the AI a new, randomized law."
/datum/round_event/ion_storm
var/replaceLawsetChance = 25 //chance the AI's lawset is completely replaced with something else per config weights
@@ -14,8 +16,8 @@
var/botEmagChance = 10
var/announceEvent = ION_RANDOM // -1 means don't announce, 0 means have it randomly announce, 1 means it is announced
var/ionMessage = null
- var/ionAnnounceChance = 33
- announceWhen = 1
+ announce_when = 1
+ announce_chance = 33
/datum/round_event/ion_storm/add_law_only // special subtype that adds a law only
replaceLawsetChance = 0
@@ -25,7 +27,7 @@
botEmagChance = 0
/datum/round_event/ion_storm/announce(fake)
- if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(ionAnnounceChance)) || fake)
+ if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(announce_chance)) || fake)
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm", has_important_message = prob(80))
diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm
index 3f7cbc77f5..dc24a52b19 100644
--- a/code/modules/events/major_dust.dm
+++ b/code/modules/events/major_dust.dm
@@ -2,6 +2,7 @@
name = "Major Space Dust"
typepath = /datum/round_event/meteor_wave/major_dust
weight = 8
+ description = "The station is pelted by sand."
/datum/round_event/meteor_wave/major_dust
wave_name = "space dust"
diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm
index d8e52cf46d..2a0c45a5ff 100644
--- a/code/modules/events/mass_hallucination.dm
+++ b/code/modules/events/mass_hallucination.dm
@@ -5,6 +5,8 @@
max_occurrences = 5
min_players = 1
var/forced_hallucination
+ category = EVENT_CATEGORY_HEALTH
+ description = "Multiple crewmembers start to hallucinate the same thing."
/datum/round_event_control/mass_hallucination/admin_setup()
if(!check_rights(R_FUN))
diff --git a/code/modules/events/meateor_wave.dm b/code/modules/events/meateor_wave.dm
index c4711055d9..a625f53788 100644
--- a/code/modules/events/meateor_wave.dm
+++ b/code/modules/events/meateor_wave.dm
@@ -3,6 +3,7 @@
typepath = /datum/round_event/meteor_wave/meaty
weight = 2
max_occurrences = 1
+ description = "A meteor wave made of meat."
/datum/round_event/meteor_wave/meaty
wave_name = "meaty"
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index a3e0eb70ff..a3534be3ea 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -10,22 +10,24 @@
min_players = 15
max_occurrences = 3
earliest_start = 25 MINUTES
+ category = EVENT_CATEGORY_SPACE
+ description = "A regular meteor wave."
/datum/round_event/meteor_wave
- startWhen = 6
- endWhen = 66
- announceWhen = 1
+ start_when = 6
+ end_when = 66
+ announce_when = 1
threat = 15
var/list/wave_type
var/wave_name = "normal"
var/direction
/datum/round_event/meteor_wave/setup()
- announceWhen = 1
- startWhen = 150 // 5 minutes
+ announce_when = 1
+ start_when = 150 // 5 minutes
if(GLOB.singularity_counter)
- startWhen *= 1 - min(GLOB.singularity_counter * SINGULO_BEACON_DISTURBANCE, SINGULO_BEACON_MAX_DISTURBANCE)
- endWhen = startWhen + 60
+ start_when *= 1 - min(GLOB.singularity_counter * SINGULO_BEACON_DISTURBANCE, SINGULO_BEACON_MAX_DISTURBANCE)
+ end_when = start_when + 60
/datum/round_event/meteor_wave/New()
..()
@@ -61,9 +63,9 @@
kill()
/datum/round_event/meteor_wave/announce(fake)
- priority_announce(generateMeteorString(startWhen,TRUE,direction), "Meteor Alert", "meteors", has_important_message = TRUE)
+ priority_announce(generateMeteorString(start_when,TRUE,direction), "Meteor Alert", "meteors", has_important_message = TRUE)
-/proc/generateMeteorString(startWhen,syndiealert,direction)
+/proc/generateMeteorString(start_when,syndiealert,direction)
var/directionstring
switch(direction)
if(NORTH)
@@ -74,7 +76,7 @@
directionstring = " towards starboard"
if(WEST)
directionstring = " towards port"
- return "Meteors have been detected on a collision course with the station[directionstring]. Estimated time until impact: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds.[GLOB.singularity_counter && syndiealert ? " Warning: Anomalous gravity pulse detected, Syndicate technology interference likely." : ""]"
+ return "Meteors have been detected on a collision course with the station[directionstring]. Estimated time until impact: [round((start_when * SSevents.wait) / 10, 0.1)] seconds.[GLOB.singularity_counter && syndiealert ? " Warning: Anomalous gravity pulse detected, Syndicate technology interference likely." : ""]"
/datum/round_event/meteor_wave/tick()
if(ISMULTIPLE(activeFor, 3))
@@ -87,7 +89,7 @@
min_players = 20
max_occurrences = 3
earliest_start = 35 MINUTES
-
+ description = "A meteor wave with higher chance of big meteors."
/datum/round_event/meteor_wave/threatening
wave_name = "threatening"
@@ -100,6 +102,7 @@
min_players = 25
max_occurrences = 3
earliest_start = 45 MINUTES
+ description = "A meteor wave that might summon a tunguska class meteor."
/datum/round_event/meteor_wave/catastrophic
wave_name = "catastrophic"
diff --git a/code/modules/events/mice_migration.dm b/code/modules/events/mice_migration.dm
index 373c495972..5bf842ba47 100644
--- a/code/modules/events/mice_migration.dm
+++ b/code/modules/events/mice_migration.dm
@@ -2,6 +2,8 @@
name = "Mice Migration"
typepath = /datum/round_event/mice_migration
weight = 10
+ category = EVENT_CATEGORY_ENTITIES
+ description = "A horde of mice arrives, and perhaps even the Rat King themselves."
/datum/round_event/mice_migration
var/minimum_mice = 5
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index 62f9c88d49..71351d85be 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -4,6 +4,8 @@
max_occurrences = 1
min_players = 20
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a nightmare, aiming to darken the station."
/datum/round_event/ghost_role/nightmare
minimum_required = 1
diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm
index 8d8adf2735..cbefa58c7b 100644
--- a/code/modules/events/operative.dm
+++ b/code/modules/events/operative.dm
@@ -2,7 +2,9 @@
name = "Lone Operative"
typepath = /datum/round_event/ghost_role/operative
weight = 0 //Admin only
- max_occurrences = 0 //Now it is actually admin only
+ max_occurrences = 1
+ category = EVENT_CATEGORY_INVASION
+ description = "A single nuclear operative assaults the station."
/datum/round_event/ghost_role/operative
minimum_required = 1
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 188d733cd8..1b1fdee584 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -6,6 +6,8 @@
min_players = 10
earliest_start = 30 MINUTES
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_INVASION
+ description = "The crew will either pay up, or face a pirate assault."
#define PIRATES_ROGUES "Rogues"
// #define PIRATES_SILVERSCALES "Silverscales"
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 59bb22e9af..b1b68fb585 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -4,6 +4,8 @@
weight = 2
min_players = 15
earliest_start = 30 MINUTES
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Syndicate troops pour out of portals."
/datum/round_event/portal_storm/syndicate_shocktroop
boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2)
@@ -15,6 +17,8 @@
typepath = /datum/round_event/portal_storm/portal_storm_narsie
weight = 0
max_occurrences = 0
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Nar'sie constructs pour out of portals."
/datum/round_event/portal_storm/portal_storm_narsie
boss_types = list(/mob/living/simple_animal/hostile/construct/builder = 6)
@@ -22,9 +26,9 @@
/mob/living/simple_animal/hostile/construct/wraith/hostile = 6)
/datum/round_event/portal_storm
- startWhen = 7
- endWhen = 999
- announceWhen = 1
+ start_when = 7
+ end_when = 999
+ announce_when = 1
var/list/boss_spawn = list()
var/list/boss_types = list() //only configure this if you have hostiles
@@ -53,7 +57,7 @@
while(number_of_hostiles > hostiles_spawn.len)
hostiles_spawn += get_random_station_turf()
- next_boss_spawn = startWhen + CEILING(2 * number_of_hostiles / number_of_bosses, 1)
+ next_boss_spawn = start_when + CEILING(2 * number_of_hostiles / number_of_bosses, 1)
/datum/round_event/portal_storm/announce(fake)
do_announce()
@@ -117,7 +121,7 @@
/datum/round_event/portal_storm/proc/time_to_end()
if(!hostile_types.len && !boss_types.len)
- endWhen = activeFor
+ end_when = activeFor
if(!number_of_hostiles && number_of_bosses)
- endWhen = activeFor
+ end_when = activeFor
diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm
index d35f4931fb..110ab745a0 100644
--- a/code/modules/events/prison_break.dm
+++ b/code/modules/events/prison_break.dm
@@ -3,10 +3,12 @@
typepath = /datum/round_event/grey_tide
max_occurrences = 2
min_players = 5
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Bolts open all doors in one or more departments."
/datum/round_event/grey_tide
- announceWhen = 50
- endWhen = 20
+ announce_when = 50
+ end_when = 20
var/list/area/areasToOpen = list()
var/list/potential_areas = list(/area/command,
/area/engineering,
@@ -17,8 +19,8 @@
var/severity = 1
/datum/round_event/grey_tide/setup()
- announceWhen = rand(50, 60)
- endWhen = rand(20, 30)
+ announce_when = rand(50, 60)
+ end_when = rand(20, 30)
severity = rand(1,3)
for(var/i in 1 to severity)
var/picked_area = pick_n_take(potential_areas)
diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm
index 6f20d954ed..578520f565 100644
--- a/code/modules/events/processor_overload.dm
+++ b/code/modules/events/processor_overload.dm
@@ -3,9 +3,11 @@
typepath = /datum/round_event/processor_overload
weight = 15
min_players = 20
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Emps the telecomm processors, scrambling radio speech. Might blow up a few."
/datum/round_event/processor_overload
- announceWhen = 1
+ announce_when = 1
/datum/round_event/processor_overload/announce(fake)
var/alert = pick( "Exospheric bubble inbound. Processor overload is likely. Please contact you*%xp25)`6cq-BZZT", \
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index 52a207fd14..5ba3d017e1 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -2,14 +2,16 @@
name = "Radiation Storm"
typepath = /datum/round_event/radiation_storm
max_occurrences = 1
+ category = EVENT_CATEGORY_SPACE
+ description = "Radiation storm affects the station, forcing the crew to escape to maintenance."
/datum/round_event/radiation_storm
/datum/round_event/radiation_storm/setup()
- startWhen = 3
- endWhen = startWhen + 1
- announceWhen = 1
+ start_when = 3
+ end_when = start_when + 1
+ announce_when = 1
/datum/round_event/radiation_storm/announce(fake)
priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", "radiation", has_important_message = TRUE)
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index d90792c88f..8cccaba68b 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -2,7 +2,8 @@
name = "Random Human-level Intelligence"
typepath = /datum/round_event/ghost_role/sentience
weight = 10
-
+ category = EVENT_CATEGORY_FRIENDLY
+ description = "An animal or robot becomes sentient!"
/datum/round_event/ghost_role/sentience
minimum_required = 1
@@ -75,6 +76,8 @@
name = "Station-wide Human-level Intelligence"
typepath = /datum/round_event/ghost_role/sentience/all
weight = 0
+ category = EVENT_CATEGORY_FRIENDLY
+ description = "ALL animals and robots become sentient, provided there is enough ghosts."
/datum/round_event/ghost_role/sentience/all
one = "all"
diff --git a/code/modules/events/shuttle_catastrophe b/code/modules/events/shuttle_catastrophe.dm
similarity index 91%
rename from code/modules/events/shuttle_catastrophe
rename to code/modules/events/shuttle_catastrophe.dm
index d948b39d3b..18b9dc61ca 100644
--- a/code/modules/events/shuttle_catastrophe
+++ b/code/modules/events/shuttle_catastrophe.dm
@@ -3,10 +3,14 @@
typepath = /datum/round_event/shuttle_catastrophe
weight = 10
max_occurrences = 1
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "Replaces the emergency shuttle with a random one."
/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players, gamemode)
if(SSshuttle.emergency.name == "Build your own shuttle kit")
return FALSE //don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems
+ if(SSshuttle.emergency.in_flight())
+ return FALSE //ditto, problems
return ..()
diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm
index 38d52797d7..49a0f94000 100644
--- a/code/modules/events/shuttle_loan.dm
+++ b/code/modules/events/shuttle_loan.dm
@@ -13,10 +13,12 @@
typepath = /datum/round_event/shuttle_loan
max_occurrences = 1
earliest_start = 7 MINUTES
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "If cargo accepts the offer, fills the shuttle with loot and/or enemies."
/datum/round_event/shuttle_loan
- announceWhen = 1
- endWhen = 500
+ announce_when = 1
+ end_when = 500
var/dispatched = 0
var/dispatch_type = 0
var/bonus_points = 10000
@@ -72,7 +74,7 @@
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
D.adjust_money(bonus_points)
- endWhen = activeFor + 1
+ end_when = activeFor + 1
SSshuttle.supply.mode = SHUTTLE_CALL
SSshuttle.supply.destination = SSshuttle.getDock("supply_home")
@@ -101,9 +103,9 @@
/datum/round_event/shuttle_loan/tick()
if(dispatched)
if(SSshuttle.supply.mode != SHUTTLE_IDLE)
- endWhen = activeFor
+ end_when = activeFor
else
- endWhen = activeFor + 1
+ end_when = activeFor + 1
/datum/round_event/shuttle_loan/end()
if(SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched)
diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm
index e06666b6bb..d06327ec0f 100644
--- a/code/modules/events/space_dragon.dm
+++ b/code/modules/events/space_dragon.dm
@@ -6,11 +6,13 @@
earliest_start = 30 MINUTES
min_players = 20
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns a space dragon, which will try to take over the station."
/datum/round_event/ghost_role/space_dragon
minimum_required = 1
role_name = "Space Dragon"
- announceWhen = 10
+ announce_when = 10
/datum/round_event/ghost_role/space_dragon/announce(fake)
priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert", has_important_message = TRUE)
diff --git a/code/modules/events/space_ninja.dm b/code/modules/events/space_ninja.dm
index 4a9dc4d2eb..aaf3f1a302 100644
--- a/code/modules/events/space_ninja.dm
+++ b/code/modules/events/space_ninja.dm
@@ -6,6 +6,8 @@
earliest_start = 30 MINUTES
min_players = 15
dynamic_should_hijack = TRUE
+ category = EVENT_CATEGORY_INVASION
+ description = "A space ninja infiltrates the station."
/datum/round_event/ghost_role/space_ninja
minimum_required = 1
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index a96e74f1e4..1bbcb0dd92 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -4,6 +4,8 @@
weight = 15
max_occurrences = 3
min_players = 20
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Kudzu begins to overtake the station. Might spawn man-traps."
/datum/round_event/spacevine
fakeable = FALSE
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index 23ce6ce730..6e8c29d3a9 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -4,15 +4,17 @@
weight = 5
max_occurrences = 1
min_players = 15
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Spawns spider eggs, ready to hatch."
/datum/round_event/spider_infestation
- announceWhen = 400
+ announce_when = 400
var/spawncount = 1
/datum/round_event/spider_infestation/setup()
- announceWhen = rand(announceWhen, announceWhen + 50)
+ announce_when = rand(announce_when, announce_when + 50)
spawncount = rand(5, 8)
/datum/round_event/spider_infestation/announce(fake)
diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm
index 7705ece78a..31b14bb8b9 100644
--- a/code/modules/events/spontaneous_appendicitis.dm
+++ b/code/modules/events/spontaneous_appendicitis.dm
@@ -5,6 +5,8 @@
max_occurrences = 4
earliest_start = 10 MINUTES
min_players = 5 // To make your chance of getting help a bit higher.
+ category = EVENT_CATEGORY_HEALTH
+ description = "A random crewmember gets appendicitis."
/datum/round_event/spontaneous_appendicitis
fakeable = FALSE
diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm
index 2464432d72..f7240f9e8b 100644
--- a/code/modules/events/stray_cargo.dm
+++ b/code/modules/events/stray_cargo.dm
@@ -5,6 +5,8 @@
weight = 5
max_occurrences = 4
earliest_start = 10 MINUTES
+ category = EVENT_CATEGORY_BUREAUCRATIC
+ description = "A pod containing a random supply crate lands on the station."
///Spawns a cargo pod containing a random cargo supply pack on a random area of the station
/datum/round_event/stray_cargo
@@ -20,7 +22,7 @@
* Also randomizes the start timer
*/
/datum/round_event/stray_cargo/setup()
- startWhen = rand(20, 40)
+ start_when = rand(20, 40)
impact_area = find_event_area()
if(!impact_area)
CRASH("No valid areas for cargo pod found.")
@@ -89,6 +91,7 @@
weight = 0
max_occurrences = 0
earliest_start = 30 MINUTES
+ description = "A pod containing syndicate gear lands on the station."
/datum/round_event/stray_cargo/syndicate
possible_pack_types = list(/datum/supply_pack/misc/syndicate)
diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm
index d873e6ca8f..08deaae7d5 100644
--- a/code/modules/events/supermatter_surge.dm
+++ b/code/modules/events/supermatter_surge.dm
@@ -4,6 +4,8 @@
weight = 20
max_occurrences = 5
earliest_start = 10 MINUTES
+ category = EVENT_CATEGORY_ENGINEERING
+ description = "Randomly modifies the supermatter's power, giving the engineers a lot of headaches."
/datum/round_event_control/supermatter_surge/canSpawnEvent()
if(GLOB.main_supermatter_engine?.has_been_powered)
diff --git a/code/modules/events/supernova.dm b/code/modules/events/supernova.dm
index 7862e85709..16c81e4056 100644
--- a/code/modules/events/supernova.dm
+++ b/code/modules/events/supernova.dm
@@ -4,18 +4,20 @@
weight = 5
max_occurrences = 1
min_players = 2
+ category = EVENT_CATEGORY_SPACE
+ description = "Several modified radstorms hit the station."
/datum/round_event/supernova
- announceWhen = 40
- startWhen = 1
- endWhen = 300
+ announce_when = 40
+ start_when = 1
+ end_when = 300
var/power = 1
var/datum/sun/supernova
var/storm_count = 0
var/announced = FALSE
/datum/round_event/supernova/setup()
- announceWhen = rand(4, 60)
+ announce_when = rand(4, 60)
supernova = new
SSsun.suns += supernova
switch(rand(1,5))
@@ -53,10 +55,10 @@
sucker_light.give_home_power()
/datum/round_event/supernova/tick()
- var/midpoint = round((endWhen-startWhen)/2)
+ var/midpoint = round((end_when-start_when)/2)
if(activeFor < midpoint)
supernova.power_mod = min(supernova.power_mod*1.2, power)
- if(activeFor > endWhen-10)
+ if(activeFor > end_when-10)
supernova.power_mod /= 4
if(prob(round(supernova.power_mod)) && prob(5-storm_count) && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
SSweather.run_weather(/datum/weather/rad_storm/supernova)
diff --git a/code/modules/events/travelling_trader.dm b/code/modules/events/travelling_trader.dm
index be697c9b6d..ac9d103f5f 100644
--- a/code/modules/events/travelling_trader.dm
+++ b/code/modules/events/travelling_trader.dm
@@ -4,10 +4,12 @@
weight = 8
max_occurrences = 2
earliest_start = 0 MINUTES
+ category = EVENT_CATEGORY_FRIENDLY
+ description = "A mysterious figure requests something to the crew and rewards them with something for getting it done."
/datum/round_event/travelling_trader
- startWhen = 0
- endWhen = 900 //you effectively have 15 minutes to complete the traders request, before they disappear
+ start_when = 0
+ end_when = 900 //you effectively have 15 minutes to complete the traders request, before they disappear
var/mob/living/carbon/human/dummy/travelling_trader/trader
var/atom/spawn_location //where the trader appears
diff --git a/code/modules/events/untie_shoes.dm b/code/modules/events/untie_shoes.dm
index 5a0da3ebc2..17dd2ca8b2 100644
--- a/code/modules/events/untie_shoes.dm
+++ b/code/modules/events/untie_shoes.dm
@@ -4,6 +4,8 @@
weight = 50
max_occurrences = 10
alert_observers = FALSE
+ category = EVENT_CATEGORY_HEALTH
+ description = "Unties people's shoes, with a chance to knot them as well."
/datum/round_event/untied_shoes
fakeable = FALSE
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 2c03531bcc..fb88f81598 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -3,11 +3,13 @@
typepath = /datum/round_event/vent_clog
weight = 10
max_occurrences = 3
+ category = EVENT_CATEGORY_HEALTH
+ description = "All the scrubbers onstation spit random chemicals in smoke form."
/datum/round_event/vent_clog
- announceWhen = 1
- startWhen = 5
- endWhen = 35
+ announce_when = 1
+ start_when = 5
+ end_when = 35
var/interval = 2
var/list/vents = list()
var/randomProbability = 0
@@ -62,7 +64,7 @@
priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert", has_important_message = TRUE)
/datum/round_event/vent_clog/setup()
- endWhen = rand(120, 180)
+ end_when = rand(120, 180)
for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/temp_vent in GLOB.machines)
var/turf/T = get_turf(temp_vent)
var/area/A = T.loc
@@ -108,6 +110,7 @@
min_players = 15
max_occurrences = 1
earliest_start = 35 MINUTES
+ description = "Extra dangerous chemicals come out of the scrubbers."
/datum/round_event/vent_clog/threatening
randomProbability = 10
@@ -120,6 +123,7 @@
min_players = 25
max_occurrences = 1
earliest_start = 45 MINUTES
+ description = "EXTREMELY dangerous chemicals come out of the scrubbers."
/datum/round_event/vent_clog/catastrophic
randomProbability = 30
@@ -129,6 +133,7 @@
name = "Clogged Vents: Beer"
typepath = /datum/round_event/vent_clog/beer
max_occurrences = 0
+ description = "Spits out beer through the scrubber system."
/datum/round_event/vent_clog/beer
reagentsAmount = 100
@@ -137,6 +142,7 @@
name = "Anti-Plasma Flood"
typepath = /datum/round_event/vent_clog/plasma_decon
max_occurrences = 0
+ description = "Freezing smoke comes out of the scrubbers."
/datum/round_event/vent_clog/beer/announce()
priority_announce("The scrubbers network is experiencing an unexpected surge of pressurized beer. Some ejection of contents may occur.", "Atmospherics alert")
diff --git a/code/modules/events/wisdomcow.dm b/code/modules/events/wisdomcow.dm
index 553dd8f309..3c03911d1d 100644
--- a/code/modules/events/wisdomcow.dm
+++ b/code/modules/events/wisdomcow.dm
@@ -3,6 +3,8 @@
typepath = /datum/round_event/wisdomcow
max_occurrences = 1
weight = 10
+ category = EVENT_CATEGORY_FRIENDLY
+ description = "A cow appears to tell you wise words."
/datum/round_event/wisdomcow/announce(fake)
priority_announce("A wise cow has been spotted in the area. Be sure to ask for her advice.", "Nanotrasen Cow Ranching Agency")
diff --git a/code/modules/events/wizard/aid.dm b/code/modules/events/wizard/aid.dm
index 5f49b48900..2380027284 100644
--- a/code/modules/events/wizard/aid.dm
+++ b/code/modules/events/wizard/aid.dm
@@ -6,6 +6,7 @@
typepath = /datum/round_event/wizard/robelesscasting
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Wizard no longer needs robes to cast spells."
/datum/round_event/wizard/robelesscasting/start()
@@ -28,6 +29,7 @@
typepath = /datum/round_event/wizard/improvedcasting
max_occurrences = 4 //because that'd be max level spells
earliest_start = 0 MINUTES
+ description = "Levels up the wizard's spells."
/datum/round_event/wizard/improvedcasting/start()
for(var/i in GLOB.mob_living_list)
diff --git a/code/modules/events/wizard/blobies.dm b/code/modules/events/wizard/blobies.dm
index 7438b462f6..6933847f80 100644
--- a/code/modules/events/wizard/blobies.dm
+++ b/code/modules/events/wizard/blobies.dm
@@ -3,6 +3,7 @@
weight = 3
typepath = /datum/round_event/wizard/blobies
max_occurrences = 3
+ description = "Spawns a blob spore on every corpse."
/datum/round_event/wizard/blobies/start()
diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm
index 061de0ea7c..639673ac15 100644
--- a/code/modules/events/wizard/curseditems.dm
+++ b/code/modules/events/wizard/curseditems.dm
@@ -5,6 +5,7 @@
max_occurrences = 3
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE
+ description = "Gives everyone a cursed item."
//Note about adding items to this: Because of how NODROP_1 works if an item spawned to the hands can also be equiped to a slot
//it will be able to be put into that slot from the hand, but then get stuck there. To avoid this make a new subtype of any
diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm
index ce6b4802fa..f08a40751d 100644
--- a/code/modules/events/wizard/departmentrevolt.dm
+++ b/code/modules/events/wizard/departmentrevolt.dm
@@ -5,6 +5,7 @@
max_occurrences = 1
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "A department is turned into an independent state."
/datum/round_event/wizard/deprevolt/start()
diff --git a/code/modules/events/wizard/embeddies.dm b/code/modules/events/wizard/embeddies.dm
index fe08b9c743..32c9affb6d 100644
--- a/code/modules/events/wizard/embeddies.dm
+++ b/code/modules/events/wizard/embeddies.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/embedpocalypse
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Everything becomes pointy enough to embed in people when thrown."
/datum/round_event/wizard/embedpocalypse/start()
for(var/obj/item/I in world)
@@ -26,6 +27,7 @@
typepath = /datum/round_event/wizard/embedpocalypse/sticky
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Everything becomes sticky enough to be glued to people when thrown."
/datum/round_event_control/wizard/embedpocalypse/sticky/canSpawnEvent(players_amt, gamemode)
if(GLOB.embedpocalypse)
diff --git a/code/modules/events/wizard/fakeexplosion.dm b/code/modules/events/wizard/fakeexplosion.dm
index 3ba20f4768..7a89fc14bd 100644
--- a/code/modules/events/wizard/fakeexplosion.dm
+++ b/code/modules/events/wizard/fakeexplosion.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/fake_explosion
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "The nuclear explosion cutscene begins to play to scare the crew."
/datum/round_event/wizard/fake_explosion/start()
sound_to_playing_players('sound/machines/alarm.ogg')
diff --git a/code/modules/events/wizard/ghost.dm b/code/modules/events/wizard/ghost.dm
index c288953efb..5a11616b8f 100644
--- a/code/modules/events/wizard/ghost.dm
+++ b/code/modules/events/wizard/ghost.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/ghost
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Ghosts become visible."
/datum/round_event/wizard/ghost/start()
var/msg = "You suddenly feel extremely obvious... "
@@ -18,6 +19,7 @@
typepath = /datum/round_event/wizard/possession
max_occurrences = 5
earliest_start = 0 MINUTES
+ description = "Ghosts become visible and gain the power of possession."
/datum/round_event/wizard/possession/start()
for(var/mob/dead/observer/G in GLOB.player_list)
diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm
index 82e72df3b9..1864ad6d20 100644
--- a/code/modules/events/wizard/greentext.dm
+++ b/code/modules/events/wizard/greentext.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/greentext
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "The Green Text appears on the station, tempting people to try and pick it up."
/datum/round_event/wizard/greentext/start()
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index 29704168e9..2b7e9bcf17 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/imposter
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Spawns a doppelganger of the wizard."
/datum/round_event/wizard/imposter/start()
for(var/datum/mind/M in SSticker.mode.wizards)
diff --git a/code/modules/events/wizard/invincible.dm b/code/modules/events/wizard/invincible.dm
index b69d1541ee..0dea1fbaf7 100644
--- a/code/modules/events/wizard/invincible.dm
+++ b/code/modules/events/wizard/invincible.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/invincible
max_occurrences = 5
earliest_start = 0 MINUTES
+ description = "Everyone is invincible for a short time ticks."
/datum/round_event/wizard/invincible/start()
diff --git a/code/modules/events/wizard/lava.dm b/code/modules/events/wizard/lava.dm
index 9a882b45df..75962c81ad 100644
--- a/code/modules/events/wizard/lava.dm
+++ b/code/modules/events/wizard/lava.dm
@@ -4,9 +4,10 @@
typepath = /datum/round_event/wizard/lava
max_occurrences = 3
earliest_start = 0 MINUTES
+ description = "Turns the floor into hot lava."
/datum/round_event/wizard/lava
- endWhen = 0
+ end_when = 0
var/started = FALSE
/datum/round_event/wizard/lava/start()
diff --git a/code/modules/events/wizard/madness.dm b/code/modules/events/wizard/madness.dm
index ac86236623..de2c30a032 100644
--- a/code/modules/events/wizard/madness.dm
+++ b/code/modules/events/wizard/madness.dm
@@ -3,6 +3,7 @@
weight = 1
typepath = /datum/round_event/wizard/madness
earliest_start = 0 MINUTES
+ description = "Reveals a horrifying truth to everyone, giving them a trauma."
var/forced_secret
diff --git a/code/modules/events/wizard/magicarp.dm b/code/modules/events/wizard/magicarp.dm
index 052143722e..b900a2da9f 100644
--- a/code/modules/events/wizard/magicarp.dm
+++ b/code/modules/events/wizard/magicarp.dm
@@ -4,13 +4,14 @@
typepath = /datum/round_event/wizard/magicarp
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Summons a school of carps with magic projectiles."
/datum/round_event/wizard/magicarp
- announceWhen = 3
- startWhen = 50
+ announce_when = 3
+ start_when = 50
/datum/round_event/wizard/magicarp/setup()
- startWhen = rand(40, 60)
+ start_when = rand(40, 60)
/datum/round_event/wizard/magicarp/announce(fake)
priority_announce("Unknown magical entities have been detected near [station_name()], please stand-by.", "Lifesign Alert")
diff --git a/code/modules/events/wizard/petsplosion.dm b/code/modules/events/wizard/petsplosion.dm
index fb4d433905..ff3ebdd128 100644
--- a/code/modules/events/wizard/petsplosion.dm
+++ b/code/modules/events/wizard/petsplosion.dm
@@ -5,6 +5,7 @@
max_occurrences = 1 //Exponential growth is nothing to sneeze at!
earliest_start = 0 MINUTES
var/mobs_to_dupe = 0
+ description = "Rapidly multiplies the animals on the station."
/datum/round_event_control/wizard/petsplosion/preRunEvent()
for(var/mob/living/simple_animal/F in GLOB.alive_mob_list)
@@ -16,7 +17,7 @@
..()
/datum/round_event/wizard/petsplosion
- endWhen = 61 //1 minute (+1 tick for endWhen not to interfere with tick)
+ end_when = 61 //1 minute (+1 tick for end_when not to interfere with tick)
var/countdown = 0
var/mobs_duped = 0
diff --git a/code/modules/events/wizard/race.dm b/code/modules/events/wizard/race.dm
index 5c3b8432c1..8c45a9504b 100644
--- a/code/modules/events/wizard/race.dm
+++ b/code/modules/events/wizard/race.dm
@@ -5,6 +5,7 @@
max_occurrences = 5
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE
+ description = "Gives everyone a random race."
/datum/round_event/wizard/race
var/list/stored_name
@@ -16,7 +17,7 @@
stored_name = list()
stored_species = list()
stored_dna = list()
- endWhen = rand(600,1200) //10 to 20 minutes
+ end_when = rand(600,1200) //10 to 20 minutes
..()
/datum/round_event/wizard/race/start()
diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm
index bf3104450e..911a9bf665 100644
--- a/code/modules/events/wizard/rpgloot.dm
+++ b/code/modules/events/wizard/rpgloot.dm
@@ -4,6 +4,7 @@
typepath = /datum/round_event/wizard/rpgloot
max_occurrences = 1
earliest_start = 0 MINUTES
+ description = "Every item in the world will have fantastical names."
/datum/round_event/wizard/rpgloot/start()
var/upgrade_scroll_chance = 0
diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm
index 18b8c8e21c..5659818b48 100644
--- a/code/modules/events/wizard/shuffle.dm
+++ b/code/modules/events/wizard/shuffle.dm
@@ -8,6 +8,7 @@
max_occurrences = 5
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "Shuffles everyone around on the station."
/datum/round_event/wizard/shuffleloc/start()
var/list/moblocs = list()
@@ -45,6 +46,7 @@
max_occurrences = 5
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "Shuffles the names of everyone around the station."
/datum/round_event/wizard/shufflenames/start()
var/list/mobnames = list()
@@ -80,6 +82,7 @@
max_occurrences = 3
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "Shuffles the minds of everyone around the station, except for the wizard."
/datum/round_event/wizard/shuffleminds/start()
var/list/mobs = list()
diff --git a/code/modules/events/wizard/summons.dm b/code/modules/events/wizard/summons.dm
index ac1160e0f5..886dfdfd10 100644
--- a/code/modules/events/wizard/summons.dm
+++ b/code/modules/events/wizard/summons.dm
@@ -5,6 +5,7 @@
max_occurrences = 1
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "Summons a gun for everyone. Might turn people into survivalists."
/datum/round_event_control/wizard/summonguns/New()
if(CONFIG_GET(flag/no_summon_guns))
@@ -21,6 +22,7 @@
max_occurrences = 1
earliest_start = 0 MINUTES
can_be_midround_wizard = FALSE // not removing it completely yet
+ description = "Summons a magic item for everyone. Might turn people into survivalists."
/datum/round_event_control/wizard/summonmagic/New()
if(CONFIG_GET(flag/no_summon_magic))
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index 3207e3b635..413658e34d 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -4,11 +4,12 @@
max_occurrences = 3
weight = 2
min_players = 2
-
+ category = EVENT_CATEGORY_SPACE
+ description = "Space time anomalies appear on the station, randomly teleporting people who walk into them."
/datum/round_event/wormholes
- announceWhen = 10
- endWhen = 60
+ announce_when = 10
+ end_when = 60
var/list/pick_turfs = list()
var/list/wormholes = list()
@@ -16,8 +17,8 @@
var/number_of_wormholes = 400
/datum/round_event/wormholes/setup()
- announceWhen = rand(0, 20)
- endWhen = rand(40, 80)
+ announce_when = rand(0, 20)
+ end_when = rand(40, 80)
/datum/round_event/wormholes/start()
for(var/turf/open/floor/T in world)
diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm
index 10b825440a..9c538f4bde 100644
--- a/code/modules/holiday/easter.dm
+++ b/code/modules/holiday/easter.dm
@@ -5,6 +5,8 @@
weight = -1
max_occurrences = 1
earliest_start = 0 MINUTES
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Hides surprise filled easter eggs in maintenance."
/datum/round_event/easter/announce(fake)
priority_announce(pick("Hip-hop into Easter!","Find some Bunny's stash!","Today is National 'Hunt a Wabbit' Day.","Be kind, give Chocolate Eggs!"))
@@ -16,6 +18,8 @@
typepath = /datum/round_event/rabbitrelease
weight = 5
max_occurrences = 10
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Summons a wave of cute rabbits."
/datum/round_event/rabbitrelease/announce(fake)
priority_announce("Unidentified furry objects detected coming aboard [station_name()]. Beware of Adorable-ness.", "Fluffy Alert", "aliens")
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
index 5dd8ad9155..8ef60d8b54 100644
--- a/code/modules/holiday/halloween/jacqueen.dm
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -15,6 +15,8 @@
weight = -1 //forces it to be called, regardless of weight
max_occurrences = 1
earliest_start = 0 MINUTES
+ category = EVENT_CATEGORY_HOLIDAY
+ description = "Spawns Jacq, a friendly mob that gives players a couple fun stuff to do."
/datum/round_event/jacqueen/start()
..()
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index 155efcd7dc..52ed3eca5e 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -366,7 +366,7 @@
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", "Peacekeeper", "Prisoner")
/proc/get_all_job_icons() //For all existing HUD icons
- return get_all_jobs() + list("Prisoner")
+ return get_all_jobs()
/proc/get_all_centcom_jobs()
return list("VIP Guest","Custodian","Thunderdome Overseer","CentCom Official","Medical Officer","Death Commando","Research Officer","Special Ops Officer","Admiral","CentCom Commander","Emergency Response Team Commander","Security Response Officer","Engineer Response Officer", "Medical Response Officer","CentCom Bartender","Nuclear Waste Expert") //No idea how to modularly edit a global proc
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 7a5ffbbbae..ce8c54a685 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -309,12 +309,13 @@
C.assignment = J.title
//End of skyrat change
C.update_label()
- for(var/A in SSeconomy.bank_accounts)
- var/datum/bank_account/B = A
- if(B.account_id == H.account_id)
- C.registered_account = B
- B.bank_cards += C
- break
+ if(J.title != "Stowaway")
+ for(var/A in SSeconomy.bank_accounts)
+ var/datum/bank_account/B = A
+ if(B.account_id == H.account_id)
+ C.registered_account = B
+ B.bank_cards += C
+ break
H.sec_hud_set_ID()
var/obj/item/pda/PDA = H.get_item_by_slot(pda_slot)
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 7839d1ecb9..c998580cae 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -38,7 +38,7 @@
display_order = JOB_DISPLAY_ORDER_HEAD_OF_SECURITY
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/blindness, /datum/quirk/monophobia, /datum/quirk/insanity)
threat = 3
-
+
family_heirlooms = list(
/obj/item/book/manual/wiki/security_space_law
)
@@ -51,7 +51,7 @@
belt = /obj/item/pda/heads/hos
ears = /obj/item/radio/headset/heads/hos/alt
uniform = /obj/item/clothing/under/rank/security/head_of_security
- shoes = /obj/item/clothing/shoes/jackboots
+ shoes = /obj/item/clothing/shoes/jackboots/sec
suit = /obj/item/clothing/suit/armor/hos/trenchcoat
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/HoS/beret
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 8dedc6c6dc..f4a234d9b2 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -28,7 +28,7 @@
display_order = JOB_DISPLAY_ORDER_SECURITY_OFFICER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/blindness, /datum/quirk/monophobia)
threat = 2
-
+
family_heirlooms = list(
/obj/item/book/manual/wiki/security_space_law,
/obj/item/clothing/head/beret/sec
@@ -131,7 +131,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/helmet/sec
suit = /obj/item/clothing/suit/armor/vest/alt
- shoes = /obj/item/clothing/shoes/jackboots
+ shoes = /obj/item/clothing/shoes/jackboots/sec
l_pocket = /obj/item/restraints/handcuffs
r_pocket = /obj/item/assembly/flash/handheld
backpack_contents = list(/obj/item/melee/baton/loaded=1)
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index 714c599024..ec4e81caea 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -29,7 +29,7 @@
display_order = JOB_DISPLAY_ORDER_WARDEN
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/blindness, /datum/quirk/monophobia)
threat = 2
-
+
family_heirlooms = list(
/obj/item/book/manual/wiki/security_space_law
)
@@ -46,7 +46,7 @@
belt = /obj/item/pda/warden
ears = /obj/item/radio/headset/headset_sec/alt
uniform = /obj/item/clothing/under/rank/security/warden
- shoes = /obj/item/clothing/shoes/jackboots
+ shoes = /obj/item/clothing/shoes/jackboots/sec
suit = /obj/item/clothing/suit/armor/vest/warden/alt
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/warden
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index c351cd25c6..e7674c7a2b 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -47,7 +47,8 @@ GLOBAL_LIST_INIT(civilian_positions, list(
"Clown",
"Mime",
"Prisoner",
- "Assistant"))
+ "Assistant",
+ "Stowaway"))
GLOBAL_LIST_INIT(security_positions, list(
"Head of Security",
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index 378313311e..ea52239754 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -30,7 +30,6 @@
age = rand(AGE_MIN,AGE_MAX)
/datum/preferences/proc/update_preview_icon(current_tab)
- var/equip_job = (current_tab != APPEARANCE_TAB)
// Determine what job is marked as 'High' priority, and dress them up as such.
var/datum/job/previewJob = get_highest_job()
@@ -49,13 +48,14 @@
mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER))
copy_to(mannequin, initial_spawn = TRUE)
- if(current_tab == LOADOUT_TAB)
- //give it its loadout if not on the appearance tab
- SSjob.equip_loadout(parent.mob, mannequin, FALSE, bypass_prereqs = TRUE, can_drop = FALSE)
- else
- if(previewJob && equip_job)
- mannequin.job = previewJob.title
- previewJob.equip(mannequin, TRUE, preference_source = parent)
+ switch(preview_pref)
+ if(PREVIEW_PREF_JOB)
+ if(previewJob)
+ mannequin.job = previewJob.title
+ previewJob.equip(mannequin, TRUE, preference_source = parent)
+ if(PREVIEW_PREF_LOADOUT)
+ SSjob.equip_loadout(parent.mob, mannequin, bypass_prereqs = TRUE, can_drop = FALSE)
+ SSjob.post_equip_loadout(parent.mob, mannequin, bypass_prereqs = TRUE, can_drop = FALSE)
mannequin.regenerate_icons()
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index c31b566cf3..66a0827059 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -94,6 +94,10 @@
C.dna.copy_dna(brainmob.stored_dna)
if(HAS_TRAIT(L, TRAIT_NOCLONE))
LAZYSET(brainmob.status_traits, TRAIT_NOCLONE, L.status_traits[TRAIT_NOCLONE])
+ // Sandstorm edit: DNC Order quirk
+ if(HAS_TRAIT(L, TRAIT_DNC_ORDER))
+ LAZYSET(brainmob.status_traits, TRAIT_DNC_ORDER, L.status_traits[TRAIT_DNC_ORDER])
+ // End Sandstorm edit
var/obj/item/organ/zombie_infection/ZI = L.getorganslot(ORGAN_SLOT_ZOMBIE)
if(ZI)
brainmob.set_species(ZI.old_species) //For if the brain is cloned
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 84b52007b8..3ec63cea1a 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -8,6 +8,7 @@
GLOB.carbon_list += src
blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio)
add_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
+ register_context()
/mob/living/carbon/Destroy()
//This must be done first, so the mob ghosts correctly before DNA etc is nulled
diff --git a/code/modules/mob/living/carbon/carbon_context.dm b/code/modules/mob/living/carbon/carbon_context.dm
new file mode 100644
index 0000000000..e7fc4d11bf
--- /dev/null
+++ b/code/modules/mob/living/carbon/carbon_context.dm
@@ -0,0 +1,48 @@
+/mob/living/carbon/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if (!isnull(held_item))
+ return .
+
+ if (!ishuman(user))
+ return .
+
+ var/combat_mode = SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)
+
+ if(user == src)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HELP, "Check injuries")
+ else if(!lying)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HELP, "Comfort")
+ else if (health >= 0 && !HAS_TRAIT(src, TRAIT_FAKEDEATH))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HELP, "Shake")
+ else
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HELP, "CPR")
+
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_DISARM, "Disarm")
+ if(combat_mode && (src != user))
+ LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_DISARM, "Shove")
+
+ if(src != user)
+ if (pulledby == user)
+ switch (user.grab_state)
+ if (GRAB_PASSIVE)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_GRAB, "Grip")
+ if (GRAB_AGGRESSIVE)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_GRAB, "Choke")
+ if (GRAB_NECK)
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_GRAB, "Strangle")
+ else
+ return .
+ else
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_GRAB, "Pull")
+
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Attack")
+
+ // Monkeys cannot be grabbed harder using ctrl-click, don't ask.
+ if((pulledby != user) && (src != user))
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Pull")
+ // Happens on any intent i believe
+ if((user == src) && combat_mode && lying)
+ LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Force to get up")
+
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 2ef3aa36a6..113853d315 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -153,10 +153,6 @@
if(MOOD_LEVEL_HAPPY4 to INFINITY)
. += "[t_He] look[p_s()] ecstatic."
- if(HAS_TRAIT(src, TRAIT_IN_HEAT) && (HAS_TRAIT(user, TRAIT_HEAT_DETECT) || src == user))
- . += ""
- . += "[t_He] [t_is] currently in [gender == MALE ? "rut" : "heat"]. "
-
if(LAZYLEN(.) > 1)
.[1] += " "
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index edb5de8188..373c7e2d79 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -460,10 +460,6 @@
if (length(msg))
. += "[msg.Join("")] "
- if(HAS_TRAIT(src, TRAIT_IN_HEAT) && (HAS_TRAIT(user, TRAIT_HEAT_DETECT) || src == user))
- . += ""
- . += "[t_He] [t_is] currently in [gender == MALE ? "rut" : "heat"]. "
-
var/trait_exam = common_trait_examine()
if (!isnull(trait_exam))
. += trait_exam
diff --git a/code/modules/mob/living/carbon/human/human_context.dm b/code/modules/mob/living/carbon/human/human_context.dm
new file mode 100644
index 0000000000..5229fba3ea
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/human_context.dm
@@ -0,0 +1,36 @@
+/mob/living/carbon/human/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+
+ if (!ishuman(user))
+ return .
+
+ var/aim_for_mouth = user.zone_selected == "mouth"
+ var/target_on_help = a_intent == INTENT_HELP
+ var/target_aiming_for_mouth = zone_selected == "mouth"
+ var/target_restrained = restrained()
+ var/same_dir = (dir & user.dir)
+ var/aim_for_groin = user.zone_selected == "groin"
+ var/target_aiming_for_groin = zone_selected == "groin"
+
+ if(aim_for_mouth && (target_on_help || target_restrained || target_aiming_for_mouth))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_DISARM, "Slap face")
+
+ else if(aim_for_groin && (src == user || lying || same_dir) && (target_on_help || target_restrained || target_aiming_for_groin))
+ LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_DISARM, "Slap ass")
+
+ // Humans can actually be upgrade grabbed using ctrl-click
+ if(src != user)
+ if (pulledby == user)
+ switch (user.grab_state)
+ if (GRAB_PASSIVE)
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Grip")
+ if (GRAB_AGGRESSIVE)
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Choke")
+ if (GRAB_NECK)
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Strangle")
+ else
+ return .
+ else
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Pull")
+
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index fa216db0b0..af3a0693dd 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -190,6 +190,10 @@
if(ITEM_SLOT_SUITSTORE)
s_store = I
update_inv_s_store()
+ if(ITEM_SLOT_ACCESSORY)
+ var/obj/item/clothing/under/attach_target = w_uniform
+ attach_target.attach_accessory(I, src, TRUE)
+ // updates handled by attach_accessory
else
to_chat(src, "You are trying to equip this item to an unsupported inventory slot. Report this to a coder! ")
not_handled = TRUE
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 6052229e35..0c30c42a65 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1523,6 +1523,39 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE))
return TRUE
return FALSE
+ if(ITEM_SLOT_ACCESSORY)
+ if(istype(I, /obj/item/clothing/accessory/ring))
+ if(istype(H.gloves))
+ var/obj/item/clothing/gloves/attaching_target = H.gloves
+ if(length(attaching_target.attached_accessories) > attaching_target.max_accessories)
+ if(return_warning)
+ return_warning[1] = "\The [attaching_target] is at maximum capacity!"
+ return FALSE
+ if(attaching_target.dummy_thick)
+ if(return_warning)
+ return_warning[1] = "\The [attaching_target] are too bulky and cannot have accessories attached to it!"
+ return FALSE
+ else
+ return TRUE
+ else if(return_warning)
+ return_warning[1] = "\The [H.w_uniform] cannot have any attachments."
+ return FALSE
+ else
+ if(istype(H.w_uniform, /obj/item/clothing/under))
+ var/obj/item/clothing/under/attaching_target = H.w_uniform
+ if(length(attaching_target.attached_accessories) > attaching_target.max_accessories)
+ if(return_warning)
+ return_warning[1] = "\The [attaching_target] is at maximum capacity!"
+ return FALSE
+ if(attaching_target.dummy_thick)
+ if(return_warning)
+ return_warning[1] = "\The [attaching_target] is too bulky and cannot have accessories attached to it!"
+ return FALSE
+ else
+ return TRUE
+ else if(return_warning)
+ return_warning[1] = "\The [H.w_uniform] cannot have any attachments."
+ return FALSE
return FALSE //Unsupported slot
/datum/species/proc/equip_delay_self_check(obj/item/I, mob/living/carbon/human/H, bypass_equip_delay_self)
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 589c9a1dd0..5093a9f24a 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -64,10 +64,6 @@
brutemod = 0.9
species_language_holder = /datum/language_holder/lizard/ash
-
-#define HEAT_CYCLE_LENGTH 32
-#define HEAT_CYCLE_OFFSET 11
-
/datum/species/lizard/ashwalker/on_species_gain(mob/living/carbon/human/C, datum/species/old_species)
if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines
C.dna.features["tail_lizard"] = "Smooth"
@@ -80,12 +76,6 @@
C.dna.features["mam_snouts"] = "Sharp"
C.dna.features["mcolor2"] = C.dna.features["mcolor"] //for no funne rainbows
C.dna.features["mcolor3"] = C.dna.features["mcolor"]
- ADD_TRAIT(C, TRAIT_HEAT_DETECT, SPECIES_TRAIT)
- var/temp = text2num(GLOB.round_id)
- var/tempish = ((temp + (HEAT_CYCLE_OFFSET + 2)) % HEAT_CYCLE_LENGTH)
- if(tempish <= 2 && tempish >= 0)
- to_chat(C, "It's this time again.. Your loins lay restless as they await a potential mate. ")
- ADD_TRAIT(C, TRAIT_IN_HEAT, SPECIES_TRAIT)
if(C.gender == MALE)
C.dna.features["has_cock"] = TRUE
@@ -103,6 +93,3 @@
C.give_genitals(1)
C.update_body()
return ..()
-
-#undef HEAT_CYCLE_LENGTH
-#undef HEAT_CYCLE_OFFSET
diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm
index f49c5933bb..b50aeb5f48 100644
--- a/code/modules/mob/living/silicon/damage_procs.dm
+++ b/code/modules/mob/living/silicon/damage_procs.dm
@@ -9,9 +9,6 @@
adjustBruteLoss(damage_amount, forced = forced)
if(BURN)
adjustFireLoss(damage_amount, forced = forced)
- if(OXY)
- if(damage < 0 || forced) //we shouldn't be taking oxygen damage through this proc, but we'll let it heal.
- adjustOxyLoss(damage_amount, forced = forced)
return 1
@@ -30,7 +27,7 @@
/mob/living/silicon/setCloneLoss(amount, updating_health = TRUE, forced = FALSE)
return FALSE
-/mob/living/silicon/adjustStaminaLoss(amount, updating_health = 1, forced = FALSE)//immune to stamina damage.
+/mob/living/silicon/adjustStaminaLoss(amount, updating_health = 1, forced = FALSE) //immune to stamina damage.
return FALSE
/mob/living/silicon/setStaminaLoss(amount, updating_health = 1)
@@ -41,3 +38,15 @@
/mob/living/silicon/setOrganLoss(slot, amount)
return FALSE
+
+/mob/living/silicon/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE) //immune to oxygen damage
+ if(istype(src, /mob/living/silicon/ai)) //ais are snowflakes and use oxyloss for being in AI cards and having no battery
+ return ..()
+
+ return FALSE
+
+/mob/living/silicon/setOxyLoss(amount, updating_health = TRUE, forced = FALSE)
+ if(istype(src, /mob/living/silicon/ai)) //ditto
+ return ..()
+
+ return FALSE
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 9e81725731..e54d5e05b7 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -85,15 +85,6 @@
/mob/living/silicon/pai/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
return take_holo_damage(amount)
-/mob/living/silicon/pai/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE, toxins_type = TOX_DEFAULT)
- return FALSE
-
-/mob/living/silicon/pai/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE)
- return FALSE
-
-/mob/living/silicon/pai/adjustCloneLoss(amount, updating_health = TRUE, forced = FALSE)
- return FALSE
-
/mob/living/silicon/pai/adjustStaminaLoss(amount, updating_health, forced = FALSE)
if(forced)
take_holo_damage(amount)
@@ -108,27 +99,3 @@
/mob/living/silicon/pai/getFireLoss()
return emittermaxhealth - emitterhealth
-
-/mob/living/silicon/pai/getToxLoss(toxins_type = TOX_OMNI)
- return FALSE
-
-/mob/living/silicon/pai/getOxyLoss()
- return FALSE
-
-/mob/living/silicon/pai/getCloneLoss()
- return FALSE
-
-/mob/living/silicon/pai/getStaminaLoss()
- return FALSE
-
-/mob/living/silicon/pai/setCloneLoss()
- return FALSE
-
-/mob/living/silicon/pai/setStaminaLoss()
- return FALSE
-
-/mob/living/silicon/pai/setToxLoss(toxins_type = TOX_OMNI)
- return FALSE
-
-/mob/living/silicon/pai/setOxyLoss()
- return FALSE
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index e902d57ddd..8a0b8def51 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -1,7 +1,6 @@
/mob/living/silicon/robot/BiologicalLife(delta_time, times_fired)
if(!(. = ..()))
return
- adjustOxyLoss(-10) //we're a robot!
handle_robot_hud_updates()
handle_robot_cell()
diff --git a/code/modules/mob/living/silicon/robot/update_icons.dm b/code/modules/mob/living/silicon/robot/update_icons.dm
index 088c93cff1..59c3c1794c 100644
--- a/code/modules/mob/living/silicon/robot/update_icons.dm
+++ b/code/modules/mob/living/silicon/robot/update_icons.dm
@@ -17,12 +17,16 @@
icon_state = "[module.cyborg_base_icon]-wreck"
if(module.cyborg_pixel_offset)
- pixel_x = module.cyborg_pixel_offset
+ var/matrix/M = transform
+ M.c = module.cyborg_pixel_offset
+ transform = M
//End of citadel changes
if(module.cyborg_base_icon == "robot")
icon = 'icons/mob/robots.dmi'
- pixel_x = initial(pixel_x)
+ var/matrix/M = transform
+ M.c = 0 // Cyborg's initial x offset is very likely to be 0
+ transform = M
if(stat != DEAD && !(IsUnconscious() || IsStun() || IsParalyzed() || low_power_mode)) //Not dead, not stunned.
if(!eye_lights)
eye_lights = new()
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index a87c0a81ab..e1a179ea52 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -45,14 +45,6 @@
adjustBruteLoss(damage)
if(BURN)
adjustFireLoss(damage)
- if(TOX)
- adjustToxLoss(damage)
- if(OXY)
- adjustOxyLoss(damage)
- if(CLONE)
- adjustCloneLoss(damage)
- if(STAMINA)
- adjustStaminaLoss(damage)
/mob/living/silicon/attack_paw(mob/living/user)
return attack_hand(user)
diff --git a/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm b/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm
index 6f5f0e3dba..9c4becbdcf 100644
--- a/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm
+++ b/code/modules/mob/living/simple_animal/gremlin/gremlin_event.dm
@@ -5,8 +5,8 @@
max_occurrences = 2
earliest_start = 20 MINUTES
min_players = 5
-
-
+ category = EVENT_CATEGORY_ENTITIES
+ description = "Annoying little creatures go around the station causing havoc and hacking everything."
/datum/round_event/gremlin
var/static/list/acceptable_spawns = list("xeno_spawn", "generic event spawn", "blobstart", "Assistant")
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index 2c6bef81c2..113a0b33fa 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -43,6 +43,7 @@
to_chat(usr, "You slide \the [pen] into \the [src]'s pen slot. ")
inserted_item = pen
playsound(src, 'sound/machines/button.ogg', 50, 1)
+ SStgui.update_uis(src)
/obj/item/modular_computer/tablet/proc/remove_pen()
if(hasSiliconAccessInArea(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
@@ -52,6 +53,7 @@
usr.put_in_hands(inserted_item)
to_chat(usr, "You remove [inserted_item] from \the [src]'s pen slot. ")
inserted_item = null
+ SStgui.update_uis(src)
else
to_chat(usr, "\The [src] does not have a pen in it! ")
@@ -78,9 +80,21 @@
QDEL_NULL(inserted_item)
return ..()
+/obj/item/modular_computer/tablet/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ if(action == "TABLET_eject_pen")
+ if(istype(src, /obj/item/modular_computer/tablet))
+ var/obj/item/modular_computer/tablet/self = src
+ if(self.can_have_pen)
+ self.remove_pen()
+ return TRUE
+
/obj/item/modular_computer/tablet/ui_data(mob/user)
. = ..()
- .["PC_showpeneject"] = inserted_item ? 1 : 0
+ .["TABLET_show_pen_eject"] = inserted_item ? 1 : 0
+
/obj/item/modular_computer/tablet/update_icon_state()
if(has_variants)
if(!finish_color)
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 4f181c0e34..ff5240306b 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -75,9 +75,8 @@
restoring = FALSE
return
ai_slot.locked = TRUE
- A.adjustOxyLoss(-5, FALSE)
- A.adjustFireLoss(-5, FALSE)
- A.adjustToxLoss(-5, FALSE)
+ A.adjustOxyLoss(-5, FALSE, FALSE)
+ A.adjustFireLoss(-5, FALSE, FALSE)
A.adjustBruteLoss(-5, FALSE)
// Please don't forget to update health, otherwise the below if statements will probably always fail.
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index 18e717191e..cfeae5730b 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -167,7 +167,8 @@
if(!computer || !authenticated)
return
if(minor)
- if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != "Assistant")
+ var/assign = GetJobName(target_id_card)
+ if(!(assign in head_subordinates) && assign != "Assistant")
return
target_id_card.access -= get_all_centcom_access() + get_all_accesses()
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index 29f9a37433..b849df2fc8 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -19,6 +19,7 @@
"AI",
"Assistant",
"Prisoner",
+ "Stowaway",
"Cyborg",
"Captain",
"Head of Personnel",
diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm
index 6f85cdb3a6..dace901cea 100644
--- a/code/modules/photography/_pictures.dm
+++ b/code/modules/photography/_pictures.dm
@@ -76,7 +76,7 @@
/proc/load_photo_from_disk(id, location)
var/datum/picture/P = load_picture_from_disk(id)
if(istype(P))
- var/obj/item/photo/p = new(location, P)
+ var/obj/item/photo/old/p = new(location, P)
return p
/proc/load_picture_from_disk(id)
diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm
index 6f35e7a99d..48a9203553 100644
--- a/code/modules/photography/photos/album.dm
+++ b/code/modules/photography/photos/album.dm
@@ -48,7 +48,7 @@
for(var/i in ids)
if(i in current_ids)
continue
- var/obj/item/photo/P = load_photo_from_disk(i)
+ var/obj/item/photo/old/P = load_photo_from_disk(i)
if(istype(P))
if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE))
qdel(P)
diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm
index 4000bf843c..45aa4aeb79 100644
--- a/code/modules/photography/photos/frame.dm
+++ b/code/modules/photography/photos/frame.dm
@@ -98,7 +98,7 @@
load_from_id(data[persistence_id])
/obj/structure/sign/picture_frame/proc/load_from_id(id)
- var/obj/item/photo/P = load_photo_from_disk(id)
+ var/obj/item/photo/old/P = load_photo_from_disk(id)
if(istype(P))
if(istype(framed))
framed.forceMove(drop_location())
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 27fb30a459..67ab3817ae 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -242,8 +242,9 @@
to_chat(user, "You need to secure the assembly before you can add glass. ")
return
var/obj/item/stack/sheet/S = W
- if(S.use(2))
- glass_type = W.type
+ S = S.split_stack(amount=2)
+ if(S)
+ glass_type = S
playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
user.visible_message("[user] places the glass on the solar assembly. ", "You place the glass on the solar assembly. ")
if(tracker)
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 9b10a843e8..5fd158c028 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -181,7 +181,6 @@
/obj/item/gun/energy/kinetic_accelerator/emp_act(severity)
return
-/* moved to modular_sand
/obj/item/gun/energy/kinetic_accelerator/proc/reload()
cell.give(cell.maxcharge)
process_chamber()
@@ -191,7 +190,7 @@
to_chat(loc, "[src] silently charges up. ")
update_icon()
overheat = FALSE
-*/
+
/obj/item/gun/energy/kinetic_accelerator/update_overlays()
. = ..()
if(!can_shoot())
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 8bbaa55a66..f68c925d74 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -57,7 +57,7 @@
/datum/reagent/blood/on_mob_life(mob/living/carbon/C) //Because lethals are preferred over stamina. damnifino.
var/blood_id = C.get_blood_id()
- if((blood_id in GLOB.blood_reagent_types) && !HAS_TRAIT(C, TRAIT_NOMARROW) && !HAS_TRAIT(C, BLOODFLEDGE))
+ if((blood_id in GLOB.blood_reagent_types) && !HAS_TRAIT(C, TRAIT_NOMARROW) && !HAS_TRAIT(C, TRAIT_BLOODFLEDGE))
if(!data || !(data["blood_type"] in get_safe_blood(C.dna.blood_type))) //we only care about bloodtype here because this is where the poisoning should be
C.adjustToxLoss(rand(2,8)*REAGENTS_EFFECT_MULTIPLIER, TRUE, TRUE) //forced to ensure people don't use it to gain beneficial toxin as slime person
..()
diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm
index 1b1c710e8c..0ac303a3a0 100644
--- a/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm
+++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm
@@ -92,6 +92,11 @@
category = list("initial","Tools","Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SERVICE
+/datum/design/airlock_painter/decal/tile
+ name = "Tile Sprayer"
+ id = "tile_sprayer"
+ build_path = /obj/item/airlock_painter/decal/tile
+
/datum/design/cultivator
name = "Cultivator"
id = "cultivator"
diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo.dm
index ea8e59a6b3..f2514b6f9c 100644
--- a/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo.dm
+++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo.dm
@@ -17,7 +17,15 @@
build_path = /obj/item/circuitboard/computer/cargo/request
category = list("Computer Boards")
departmental_flags = DEPARTMENTAL_FLAG_CARGO
-
+
+/datum/design/board/bounty
+ name = "Computer Design (Bounty Console)"
+ desc = "Allows for the construction of circuit boards used to build a Bounty Console."
+ id = "bounty"
+ build_path = /obj/item/circuitboard/computer/bounty
+ category = list("Computer Boards")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
/datum/design/board/mining
name = "Computer Design (Outpost Status Display)"
desc = "Allows for the construction of circuit boards used to build an outpost status display console."
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index 98b04a8382..7862f24715 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -232,9 +232,12 @@
var/amount = materials.mat_container.materials[mat_id]
var/ref = REF(M)
l += "* [amount] of [M.name]: "
- if(amount >= MINERAL_MATERIAL_AMOUNT) l += "Eject [RDSCREEN_NOBREAK]"
+ if(amount >= MINERAL_MATERIAL_AMOUNT) l += "1x [RDSCREEN_NOBREAK]"
if(amount >= MINERAL_MATERIAL_AMOUNT*5) l += "5x [RDSCREEN_NOBREAK]"
- if(amount >= MINERAL_MATERIAL_AMOUNT) l += "All [RDSCREEN_NOBREAK]"
+ if(amount >= MINERAL_MATERIAL_AMOUNT*10) l += "10x [RDSCREEN_NOBREAK]"
+ if(amount >= MINERAL_MATERIAL_AMOUNT*20) l += "20x [RDSCREEN_NOBREAK]"
+ if(amount >= MINERAL_MATERIAL_AMOUNT*50) l += "50x [RDSCREEN_NOBREAK]"
+ if(amount >= MINERAL_MATERIAL_AMOUNT) l += "Max Stack [RDSCREEN_NOBREAK]"
l += ""
l += "[RDSCREEN_NOBREAK]"
return l
diff --git a/code/modules/research/techweb/nodes/tools_nodes.dm b/code/modules/research/techweb/nodes/tools_nodes.dm
index 86a48ab8de..004795113b 100644
--- a/code/modules/research/techweb/nodes/tools_nodes.dm
+++ b/code/modules/research/techweb/nodes/tools_nodes.dm
@@ -5,7 +5,7 @@
display_name = "Basic Tools"
description = "Basic mechanical, electronic, surgical and botanical tools."
prereq_ids = list("base")
- design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "decal_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop", "broom", "normtrash", "spraycan")
+ design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "decal_painter", "tile_sprayer", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop", "broom", "normtrash", "spraycan")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
/datum/techweb_node/basic_mining
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 1f027c450c..10fab0ed06 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -685,6 +685,7 @@
to_chat(SM, "You also become depressingly aware that you are not a real creature, but instead a holoform. Your existence is limited to the parameters of the holodeck. ")
to_chat(user, "[SM] accepts [src] and suddenly becomes attentive and aware. It worked! ")
SM.copy_languages(user)
+ SM.add_overlay(mutable_appearance('icons/mob/hud.dmi', "brother", ANTAG_LAYER))
after_success(user, SM)
qdel(src)
else
@@ -754,6 +755,7 @@
to_chat(SM, "In a quick flash, you feel your consciousness flow into [SM]! ")
to_chat(SM, "You are now [SM]. Your allegiances, alliances, and role is still the same as it was prior to consciousness transfer! ")
SM.name = "[user.real_name]"
+ SM.add_overlay(mutable_appearance('icons/mob/hud.dmi', "brother", ANTAG_LAYER))
qdel(src)
/obj/item/slimepotion/slime/steroid
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index 99aaa8d8ec..f350dd507e 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -3,6 +3,8 @@
return
/mob/living/carbon/get_bodypart(zone)
+ RETURN_TYPE(/obj/item/bodypart)
+
if(!zone)
zone = BODY_ZONE_CHEST
for(var/X in bodyparts)
diff --git a/code/modules/tgui/states/fun.dm b/code/modules/tgui/states/fun.dm
new file mode 100644
index 0000000000..ba72f40fd0
--- /dev/null
+++ b/code/modules/tgui/states/fun.dm
@@ -0,0 +1,17 @@
+/*!
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/**
+ * tgui state: fun_state
+ *
+ * Checks that the user has the fun privilige.
+ */
+
+GLOBAL_DATUM_INIT(fun_state, /datum/ui_state/fun_state, new)
+
+/datum/ui_state/fun_state/can_use_topic(src_object, mob/user)
+ if(check_rights_for(user.client, R_FUN))
+ return UI_INTERACTIVE
+ return UI_CLOSE
diff --git a/config/config.txt b/config/config.txt
index 8db6b1f725..f9a588e63a 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -42,6 +42,7 @@ $include entries/vote.txt
$include plushies/defines.txt
# Special Sandstorm configs
$include sandstorm/config.txt
+$include sandstorm/balance.txt
# Special SPLURT configs
$include splurt/fetish_content.txt
@@ -52,3 +53,4 @@ $include splurt/maps.txt
$include splurt/server.txt
$include splurt/connections.txt
$include splurt/donator.txt
+$include splurt/autocryo.txt
diff --git a/config/sandstorm/balance.txt b/config/sandstorm/balance.txt
new file mode 100644
index 0000000000..bc10b7a471
--- /dev/null
+++ b/config/sandstorm/balance.txt
@@ -0,0 +1,92 @@
+# Special Sandstorm balance config!
+
+###
+## CRYPTOMINER
+###
+
+## Should cryptominers work in non-atmos turf (ex. space)?
+## Uncomment this to disable atmos processing (cheat mode)
+#CRYPTO_IGNORE_ATMOS
+
+## What point multiplier should cryptominers use?
+## Determined by heat level, or uses MAX in cheat mode
+CRYPTO_MULTIPLIER_MIN 0.2
+CRYPTO_MULTIPLIER_MID 1
+CRYPTO_MULTIPLIER_MAX 3
+
+## What heat thresholds should cryptominers use?
+## This is measured in Kelvins
+CRYPTO_HEAT_THRESHOLD_MIN 225
+CRYPTO_HEAT_THRESHOLD_MID 273
+CRYPTO_HEAT_THRESHOLD_MAX 500
+
+## How much heat should cryptominers produce?
+## This amount is added to the environment on process
+CRYPTO_HEAT_POWER 100
+
+## How long between cryptominers producing resources?
+## Currently unimplemented!
+CRYPTO_MINING_TIME 3000
+
+## What material amount should the cryptominers produce?
+## This is modified by the MULTIPLIER value
+## Currently unimplemented!
+CRYPTO_PAYOUT_AMOUNT 50
+
+## How much power should the cryptominer use?
+## Currently unimplemented!
+CRYPTO_POWER_USE_IDLE 20
+CRYPTO_POWER_USE_ACTIVE 200
+CRYPTO_POWER_USE_PROCESS 20
+
+###
+## AUTODOC
+###
+
+## How long should the autodoc take to perform surgery?
+## This is modified by stock part ratings
+AUTODOC_TIME_SURGERY_BASE 350
+
+###
+## BLUESPACE MINER
+###
+
+## How much should the bluespace miner produce, compared to normal?
+## This is modified by stock part ratings
+BLUESPACEMINER_MULT_OUTPUT 1
+
+## What is the minimum stock part tier to produce bluespace crystals?
+BLUESPACEMINER_CRYSTAL_TIER 5
+
+###
+## TETRIS
+###
+
+## The machine produces science points based on score
+## Uncomment this to prevent it from doing so
+#TETRIS_NO_SCIENCE
+
+## How many prizes can the machine dispense
+## This is also limited by TETRIS_SCORE_MAX
+TETRIS_PRIZES_MAX 5
+
+## How many points required per prize vended
+## Amount is equal to SCORE divided by TETRIS_REWARD_DIVISOR
+TETRIS_REWARD_DIVISOR 1000
+
+## Minimum score required to message admins
+## This is intended for detecting suspicious scores
+TETRIS_SCORE_HIGH 10000
+
+## Maximum score that can be displayed
+## Score values past this number are ignored
+TETRIS_SCORE_MAX 100000
+
+## Maximum research points that can be generated
+## Score values past this number are ignored
+TETRIS_SCORE_MAX_SCI 10000
+
+## Minimum time between giving rewards
+## This applies to both prizes and research points
+## Time is measured in ticks
+TETRIS_TIME_COOLDOWN 600
diff --git a/config/splurt/autocryo.txt b/config/splurt/autocryo.txt
new file mode 100644
index 0000000000..bae707e021
--- /dev/null
+++ b/config/splurt/autocryo.txt
@@ -0,0 +1,5 @@
+## Uncomment to enable automatic SSD cryo
+# AUTOCRYO_ENABLED
+
+## Time in seconds before sending SSD users to cryo
+AUTOCRYO_TIME_TRIGGER 24000
diff --git a/config/splurt/general.txt b/config/splurt/general.txt
index dcc4de101d..6eed643535 100644
--- a/config/splurt/general.txt
+++ b/config/splurt/general.txt
@@ -9,3 +9,8 @@ MAX_INFINIDORMS 5
# Weighted station traits
# If uncommented, the server will pick random station traits according to their weight configuration
WEIGHTED_STATION_TRAITS
+
+# Base amount of max save slots
+# Amount of character slots that players will have without modifiers being applied
+# default is 24
+#BASE_SAVE_SLOTS 24
diff --git a/html/changelogs/archive/2023-01.yml b/html/changelogs/archive/2023-01.yml
index d8475b3fd7..db12f86726 100644
--- a/html/changelogs/archive/2023-01.yml
+++ b/html/changelogs/archive/2023-01.yml
@@ -113,3 +113,99 @@
Winter Schock:
- bugfix: Doll with monophobia dont get stress in someone vore belly
- bugfix: Blind doll with monophobia dont get stress while someone in 1 tile range
+2023-01-21:
+ LeDrascol:
+ - balance: Synthetic Lizardperson, Synthetic Anthropomorph, and IPC no longer experience
+ thirst
+ SandPoot:
+ - rscadd: Added a reskin to the premium kinetic accelerator.
+ - bugfix: Fixed some possible graphical glitches with the mining hardsuit reskin.
+ - tweak: Tweaked skirt peeking into allowing peeking under people that are on top
+ of tables.
+ - refactor: Refactored skirt peeking into element.
+ zeroisthebiggay:
+ - spellcheck: the meme emotes
+2023-01-22:
+ LeDrascol:
+ - bugfix: Fixed missing newline characters for mood events
+ WoolyAypa:
+ - rscadd: Added a new nullrod re-skin
+ - rscadd: Added papal clothing to the armament beacon choices
+2023-01-23:
+ LunarFleet:
+ - tweak: Allowed borgs to craft
+ - tweak: Changes service borgs' items
+2023-01-24:
+ LeDrascol:
+ - tweak: Bloodfledge flavor text updated
+ - tweak: Bloodfledge ability buttons are bloodsucker themed
+ - tweak: Bloodfledge biting considers many more factors
+ - tweak: Bloodfledge biting limits safe blood transfer while passive grabbing
+ - tweak: Bloodfledge biting splatters blood if interrupted
+ - tweak: Bloodfledge biting from same-species non-standard blood species transfers
+ instead of nourishing
+ - tweak: Bloodfledge biting species penalties do not apply on same-species interactions
+ - tweak: Bloodfledge ID cards will use their holder's financial account
+ - tweak: Bloodfledges will become upset when draining robots, slimes, corpses, or
+ undead
+ - tweak: Bloodfledges are penalized for fully draining any body
+ - tweak: Bloodfledges gain a mood effect from drinking from Cursed Blood users
+ - tweak: Bloodfledges can over-drink and become fat
+ - balance: Bloodsucker Fledgling is now a positive quirk, costing 2 points
+ - balance: Bloodfledges have less arbitrary traits
+ - balance: Bloodfledges can't use the power cord implant
+ - balance: Bloodfledge revive cannot be used while starving, the body is too damaged,
+ or the user suicides
+ - balance: Bloodfledge coffin healing rate reduced slightly
+ - balance: Bloodfledge coffin healing costs hunger based on healed amount
+ - balance: Bloodfledge coffin healing does not work on robots
+ - balance: Bloodfledge bite cooldown increased slightly
+ - balance: Bloodfledge interactions benefit from Voracious
+ - balance: Bloodfledge chapel penalties removed
+ - balance: Holy water is more dangerous for bloodfledges
+ - bugfix: Fixed various spelling mistakes related to Bloodsucker Fledglings
+ - bugfix: Fixed Bloodfledge ID card getting lost with full backpacks
+ - imageadd: Added a new icon and overlay for vampire IDs
+ - imagedel: Removed old non-modular vampire ID icon
+ - refactor: Refactored all Bloodsucker Fledgling code
+ - refactor: Trait BLOODFLEDGE is now TRAIT_BLOODFLEDGE
+2023-01-25:
+ LeDrascol:
+ - rscadd: 'Added quirk: DNC Order'
+ - code_imp: Removed redundant definition of DNC quirk
+ - tweak: Moved Research Rack from Data Theory to Biological Technology
+2023-01-26:
+ Kush1Push1:
+ - rscadd: Ported body markings for head; pilot and pilot jaw.
+ ShamanSliph:
+ - rscadd: 'Added new Quirk: Clothes Eater'
+ - bugfix: Fixed insects getting infinite nutrient from chewing on clothes.
+2023-01-27:
+ AshTheDerg:
+ - tweak: Modified Booze Shaker options
+ - bugfix: fixed broken Borg Beer Shaker
+2023-01-28:
+ '@Dexxiol':
+ - imageadd: Done some ui sprites for the underwear slots.
+ LeDrascol:
+ - rscadd: Added the Saliith plushie pinpointer
+ - tweak: The Saliith plushie will throw knives instead of deleting them
+ - tweak: The Saliith plushie will appear in a random location on the map
+ - tweak: Only one Saliith plushie can exist in the game at once
+ - balance: The Saliith plushie is now indestructible
+ - balance: The Saliith plushie cannot be destroyed by any means
+ - balance: The Saliith plushie now uses a lightning smite instead of gibbing
+ - balance: The Saliith plushie will no longer smite brainwashed victims
+ - balance: The Saliith plushie will now retaliate against plushmium use
+ - imageadd: Added a green pinpointer sprite
+2023-01-30:
+ LeDrascol:
+ - server: Added configuration settings for Cryptominer, Autodoc, and Bluespace Miner.
+ Moribun:
+ - rscadd: nightstalker tail
+ - rscdel: striked out issue from zombie making blood recovery not possible
+ - tweak: vault suit to work
+2023-01-31:
+ LeDrascol:
+ - balance: Reduced the price of individual condoms from 40 to 10 credits
+ - balance: Reduced the price of bulk condoms from 200 to 80 credits
diff --git a/html/changelogs/archive/2023-02.yml b/html/changelogs/archive/2023-02.yml
new file mode 100644
index 0000000000..7b87736aba
--- /dev/null
+++ b/html/changelogs/archive/2023-02.yml
@@ -0,0 +1,101 @@
+2023-02-02:
+ LeDrascol:
+ - tweak: Hypnotic Gaze checks for ten new conditions
+ - tweak: Hypnotic Gaze supports personal pronouns
+ - tweak: Hypnotic Gaze accounts for non-con preferences
+ - balance: Hypnotic Gaze interaction time reduced from 12s to 5s
+ - balance: Hypnotic Gaze can be blocked by eye protection and mind shielding
+ - rscadd: Added quirk Estrous Detection
+ - rscadd: Added quirk In Estrous
+ - tweak: Ashwalkers now spawn with the Estrous Detection quirk
+ - tweak: Ashwalkers will now gain the In Estrous quirk during some rounds
+2023-02-03:
+ LeDrascol:
+ - code_imp: Removed obsolete entries for In Heat and Estrus Detection
+ - rscadd: Added the Imaginary Friend Action Figure
+ - rscadd: Added the Dissociative Mirror
+ - tweak: Restored techweb node and circuit printing for Cryptominers
+2023-02-04:
+ AshTheDerg:
+ - rscadd: Added 10 new varieties of Milkshake
+ LeDrascol:
+ - rscadd: Added variant of Subtle verb with typing indicator
+ - tweak: Default binding for Subtle is now Control-5
+2023-02-07:
+ LeDrascol:
+ - tweak: Added gain, lose, and medical text to werewolf quirk
+ - tweak: Updated werewolf ability tooltip
+ - tweak: Changed body part sprites used for werewolf
+ - tweak: Werewolves cannot transform while asleep, restrained, or stunned
+ - tweak: Werewolf transformation now has a five second cooldown
+ - tweak: Werewolf species name prefix is now based on gender
+ - tweak: Werewolves can be slime and jelly entities
+ - tweak: Werewolves will gain an appropriate taur body if the owner had one
+ - bugfix: Werewolves cannot transform while dead
+ - bugfix: Werewolf transformation organ bug fixed for some species
+ - bugfix: Werewolf old species attributes are no longer lost on action removal
+ - bugfix: Werewolves custom species name properly applies
+ - bugfix: Werewolves will regain exotic eye types
+ - code_imp: Added a werewolf quirk trait
+ - refactor: Updated werewolf quirk variable names
+ - tweak: Added Bluespace Light Replacer to the Science protolathe
+ The-Real-Goku:
+ - rscadd: Added persistent photo album.
+ - rscadd: Added custom name for player album. ([Player name]'s album)
+ - rscadd: Added specific names for the photo albums located in the Heads of Departments'
+ lockers. (Ex. Captain, HoP...)
+ - bugfix: Fixed in-hand sprite for Photo Album. (They looked like briefcases before).
+ zeroisthebiggay:
+ - bugfix: ai research
+2023-02-09:
+ BongaTheProto:
+ - rscadd: New tail, Snake Tail (Large)
+ - tweak: Now the max amount of character slots can be configured, and donators get
+ more slots
+ - bugfix: Photographer quirk will no longer fuck over your neck loadout items
+ Comicao1:
+ - rscadd: Added a few icons and code. Not modularized because it's not possible.
+ - rscadd: Adds the inflate verb.
+2023-02-10:
+ Comicao1:
+ - rscadd: Beach ball may be inserted a vibrator in it.
+ - rscadd: Syndicate beach ball also might be bought in the traitor's uplink.
+2023-02-11:
+ SandPoot:
+ - refactor: Interaction menu is no longer stored in your mind, and should clear
+ references and all properly when deleting.
+ WoolyAypa:
+ - rscadd: Added a new polychromic Princess Leia outfits to the ClothesMate (and
+ loadout)
+ - rscadd: Added a polychromic version of the "Performers one piece" to the ClothesMate
+ (and loadout)
+ - rscadd: Added high-heel sandals to the KinkMate (and loadout)
+2023-02-12:
+ BongaTheProto:
+ - bugfix: snake tail (large) is now available for humans and lizards too
+ WoolyAypa:
+ - bugfix: Space princess outfit now can be used along with digitigrade legs
+ - rscadd: Added some of the old dullahan stuff to the new one
+ - bugfix: Fixed the overlay glitches with the new dullahan quirk
+ - bugfix: Space princess outfit and the polychromic performers one piece can now
+ be colored in the loadout
+2023-02-13:
+ Neyusha:
+ - rscadd: Added stowaway role
+2023-02-14:
+ LeDrascol:
+ - tweak: SSD players are automatically sent to cryo after 40 minutes
+ - code_imp: Reverted non-modular SPLURT edits to TETRIS
+ - balance: T.E.T.R.I.S. arcade machine now generates research points if used by
+ scientists
+ - balance: T.E.T.R.I.S. machine maximum score, prize count, and points payout are
+ now limited
+ - balance: T.E.T.R.I.S. machine can only dispense rewards once per configured time
+ threshold
+ - server: Added configuration settings for the T.E.T.R.I.S. arcade machine
+2023-02-16:
+ Zattarra:
+ - rscadd: Added the shortcrop, longcrop, and formalcrop.
+ - rscadd: Added croptops to loadout.
+ - rscadd: Added croptops to clothesmate.
+ - imageadd: Gave the croptops sprites.
diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi
new file mode 100644
index 0000000000..144287ced0
Binary files /dev/null and b/icons/effects/random_spawners.dmi differ
diff --git a/icons/emoji_32.dmi b/icons/emoji_32.dmi
index d976daa366..1a0684c2fd 100644
Binary files a/icons/emoji_32.dmi and b/icons/emoji_32.dmi differ
diff --git a/icons/mob/OnFire.dmi b/icons/mob/OnFire.dmi
index 7768bf0269..e82442f4d0 100644
Binary files a/icons/mob/OnFire.dmi and b/icons/mob/OnFire.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/veymed.dmi b/icons/mob/augmentation/cosmetic_prosthetic/veymed.dmi
new file mode 100644
index 0000000000..aaf11cfd9f
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/veymed.dmi differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index 2338fa37a0..f7062b9f4e 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi
index 3360521b51..88b63f46b8 100644
Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ
diff --git a/icons/mob/clothing/feet_digi.dmi b/icons/mob/clothing/feet_digi.dmi
index 82d4856d38..41b75ec240 100644
Binary files a/icons/mob/clothing/feet_digi.dmi and b/icons/mob/clothing/feet_digi.dmi differ
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index 1d4a76e519..3060ae8f2a 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi
index fb0246e6e3..26a8b1808f 100644
Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ
diff --git a/icons/mob/icemoon/64x64megafauna.dmi b/icons/mob/icemoon/64x64megafauna.dmi
index 5a622dc7bc..e466b62556 100644
Binary files a/icons/mob/icemoon/64x64megafauna.dmi and b/icons/mob/icemoon/64x64megafauna.dmi differ
diff --git a/icons/mob/inhands/equipment/belt_lefthand.dmi b/icons/mob/inhands/equipment/belt_lefthand.dmi
index 81b12c60f9..944022713b 100644
Binary files a/icons/mob/inhands/equipment/belt_lefthand.dmi and b/icons/mob/inhands/equipment/belt_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/belt_righthand.dmi b/icons/mob/inhands/equipment/belt_righthand.dmi
index 42ed3a30c6..78c5fc47b5 100644
Binary files a/icons/mob/inhands/equipment/belt_righthand.dmi and b/icons/mob/inhands/equipment/belt_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index d0ebbb4501..b694d0ba35 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 0d87e7871c..3836f4c713 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/axes_lefthand.dmi b/icons/mob/inhands/weapons/axes_lefthand.dmi
index 6b4041e477..021c005fc3 100644
Binary files a/icons/mob/inhands/weapons/axes_lefthand.dmi and b/icons/mob/inhands/weapons/axes_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/axes_righthand.dmi b/icons/mob/inhands/weapons/axes_righthand.dmi
index 30553d6320..c19440b531 100644
Binary files a/icons/mob/inhands/weapons/axes_righthand.dmi and b/icons/mob/inhands/weapons/axes_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/melee_lefthand.dmi b/icons/mob/inhands/weapons/melee_lefthand.dmi
index bb6de0432e..0477b1b0f9 100644
Binary files a/icons/mob/inhands/weapons/melee_lefthand.dmi and b/icons/mob/inhands/weapons/melee_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/melee_righthand.dmi b/icons/mob/inhands/weapons/melee_righthand.dmi
index 346f6d5977..cf2cf50ad9 100644
Binary files a/icons/mob/inhands/weapons/melee_righthand.dmi and b/icons/mob/inhands/weapons/melee_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi
index 62e48f626b..860bc5a3de 100644
Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi
index 658e7304df..326821dc8b 100644
Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ
diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi
index 00b57726f1..d9fd50c9ed 100644
Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ
diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi
index 0799920a6c..5d9bad32ac 100644
Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ
diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi
index 48aaa3a418..70548c5719 100644
Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index 70c48c52da..855936dfb4 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index 6e8ea50d39..0831f7be2f 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi
index 50de425aad..2406e61190 100644
Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ
diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi
index 9af0a6ba3a..eadf4a1591 100644
Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index 567fc8ace8..73cc4b6853 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi
index 7fc2eaae7f..27301fa272 100644
Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 3e57f4eca4..b958ca201c 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi
index 95a04717e9..73ba2fcdb0 100644
Binary files a/icons/obj/objects.dmi and b/icons/obj/objects.dmi differ
diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi
index 7926ead12d..077a3b43db 100644
Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ
diff --git a/icons/turf/decals.dmi b/icons/turf/decals.dmi
index 4ed8c8db9a..6b42ad12c7 100644
Binary files a/icons/turf/decals.dmi and b/icons/turf/decals.dmi differ
diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm
index 4c09236872..197f088356 100644
--- a/modular_citadel/code/modules/client/loadout/__donator.dm
+++ b/modular_citadel/code/modules/client/loadout/__donator.dm
@@ -54,9 +54,10 @@
/datum/gear/donator/kiaramedal
name = "Insignia of Steele"
- slot = ITEM_SLOT_BACKPACK
+ slot = ITEM_SLOT_ACCESSORY
path = /obj/item/clothing/accessory/medal/steele
ckeywhitelist = list("inferno707")
+ handle_post_equip = TRUE
/datum/gear/donator/hheart
name = "The Hollow Heart"
diff --git a/modular_citadel/code/modules/client/loadout/_loadout.dm b/modular_citadel/code/modules/client/loadout/_loadout.dm
index 5f895c4805..f33beecb32 100644
--- a/modular_citadel/code/modules/client/loadout/_loadout.dm
+++ b/modular_citadel/code/modules/client/loadout/_loadout.dm
@@ -55,6 +55,7 @@ GLOBAL_LIST_EMPTY(loadout_whitelist_ids)
var/geargroupID //defines the ID that the gear inherits from the config
var/loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION
var/list/loadout_initial_colors = list()
+ var/handle_post_equip = FALSE
//NEW DONATOR SYTSEM STUFF
var/donoritem //autoset on new if null
diff --git a/modular_citadel/code/modules/client/loadout/_service.dm b/modular_citadel/code/modules/client/loadout/_service.dm
index 848ad6233c..4f96b36e2e 100644
--- a/modular_citadel/code/modules/client/loadout/_service.dm
+++ b/modular_citadel/code/modules/client/loadout/_service.dm
@@ -2,20 +2,20 @@
name = "Staff Assistant's jumpsuit"
path = /obj/item/clothing/under/misc/staffassistant
subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS
- restricted_roles = list("Assistant")
+ restricted_roles = list("Assistant", "Stowaway")
/datum/gear/suit/neetsuit
name = "D.A.B. suit"
path = /obj/item/clothing/suit/assu_suit
subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS
- restricted_roles = list("Assistant")
+ restricted_roles = list("Assistant", "Stowaway")
cost = 2
/datum/gear/head/neethelm
name = "D.A.B. helmet"
path = /obj/item/clothing/head/assu_helmet
subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS
- restricted_roles = list("Assistant")
+ restricted_roles = list("Assistant", "Stowaway")
cost = 2
/datum/gear/backpack/plushvar
diff --git a/modular_citadel/code/modules/client/loadout/accessory.dm b/modular_citadel/code/modules/client/loadout/accessory.dm
new file mode 100644
index 0000000000..55cce181a5
--- /dev/null
+++ b/modular_citadel/code/modules/client/loadout/accessory.dm
@@ -0,0 +1,20 @@
+/datum/gear/accessory
+ category = LOADOUT_CATEGORY_ACCESSORY
+ slot = ITEM_SLOT_ACCESSORY
+ handle_post_equip = TRUE
+
+/datum/gear/accessory/necklace
+ name = "A renameable necklace"
+ path = /obj/item/clothing/accessory/necklace
+
+/datum/gear/accessory/polymaidapron
+ name = "Polychromic maid apron"
+ path = /obj/item/clothing/accessory/maidapron/polychromic
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#333333", "#FFFFFF")
+
+/datum/gear/accessory/pridepin
+ name = "Pride pin"
+ path = /obj/item/clothing/accessory/pride
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION
+ cost = 0
diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm
index a0be000b31..24545e7b4c 100644
--- a/modular_citadel/code/modules/client/loadout/backpack.dm
+++ b/modular_citadel/code/modules/client/loadout/backpack.dm
@@ -2,6 +2,7 @@
category = LOADOUT_CATEGORY_BACKPACK
subcategory = LOADOUT_SUBCATEGORY_BACKPACK_GENERAL
slot = ITEM_SLOT_BACKPACK
+ handle_post_equip = TRUE
/datum/gear/backpack/plushbox
name = "Plushie Choice Box"
@@ -172,25 +173,6 @@
path = /obj/item/storage/fancy/ringbox/diamond
cost = 5
-/datum/gear/backpack/necklace //this is here because loadout doesn't support proper accessories
- name = "A renameable necklace"
- path = /obj/item/clothing/accessory/necklace
- subcategory = LOADOUT_SUBCATEGORY_BACKPACK_ACCESSORIES
-
-/datum/gear/backpack/polymaidapron //this is ALSO here because loadout doesn't support proper accessories
- name = "Polychromic maid apron"
- path = /obj/item/clothing/accessory/maidapron/polychromic
- loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
- loadout_initial_colors = list("#333333", "#FFFFFF")
- subcategory = LOADOUT_SUBCATEGORY_BACKPACK_ACCESSORIES
-
-/datum/gear/backpack/pridepin //what the two comments above said
- name = "Pride pin"
- path = /obj/item/clothing/accessory/pride
- loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION
- subcategory = LOADOUT_SUBCATEGORY_BACKPACK_ACCESSORIES
- cost = 0
-
// Moved here from quirks
/datum/gear/backpack/dyespray
name = "Hair dye spray"
diff --git a/modular_citadel/code/modules/client/loadout/head.dm b/modular_citadel/code/modules/client/loadout/head.dm
index d0563d2986..fdaee6f309 100644
--- a/modular_citadel/code/modules/client/loadout/head.dm
+++ b/modular_citadel/code/modules/client/loadout/head.dm
@@ -157,7 +157,7 @@
description = "A visored cap. Intended to be used with ORV uniform."
path = /obj/item/clothing/head/kepi/orvi
subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS
- restricted_roles = list("Assistant")
+ restricted_roles = list("Assistant", "Stowaway")
/*Commenting out Until next Christmas or made automatic
/datum/gear/santahatr
@@ -195,6 +195,12 @@
restricted_desc = "Security"
restricted_roles = list("Warden","Detective","Security Officer","Head of Security")
+/datum/gear/head/cowboyhat/polychromic
+ name = "Cowboy Hat, Polychromic"
+ path = /obj/item/clothing/head/cowboyhat/polychromic
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#5F5F5F", "#DDDDDD")
+
/datum/gear/head/wkepi
name = "white kepi"
path = /obj/item/clothing/head/kepi
diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm
index 5c22485c1f..280f6a2485 100644
--- a/modular_citadel/code/modules/client/loadout/uniform.dm
+++ b/modular_citadel/code/modules/client/loadout/uniform.dm
@@ -623,7 +623,7 @@
name = "ORV uniform, assistant"
path = /obj/item/clothing/under/trek/orv
subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS
- restricted_roles = list("Assistant")
+ restricted_roles = list("Assistant", "Stowaway")
/datum/gear/uniform/orvsrv
name = "ORV uniform, other"
diff --git a/modular_citadel/code/modules/mentor/mentor_memo.dm b/modular_citadel/code/modules/mentor/mentor_memo.dm
index 59ea92febd..c64101c30e 100644
--- a/modular_citadel/code/modules/mentor/mentor_memo.dm
+++ b/modular_citadel/code/modules/mentor/mentor_memo.dm
@@ -94,7 +94,7 @@
var/datum/db_query/update_query = SSdbcore.NewQuery({"
UPDATE [format_table_name("mentor_memo")]
SET memotext = :new_memo, last_editor = :ckey, edits = :edit_text WHERE ckey = :target_ckey
- "}, list("new_memo" = new_memo, "ckey" = ckey, "edit_text" = (edit_text ? "" : edit_text), "target_ckey" = target_ckey))
+ "}, list("new_memo" = new_memo, "ckey" = ckey, "edit_text" = (edit_text || ""), "target_ckey" = target_ckey))
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
qdel(update_query)
diff --git a/modular_sand/code/_globalvars/bitfields.dm b/modular_sand/code/_globalvars/bitfields.dm
index 934786c792..7a2bb8d87d 100644
--- a/modular_sand/code/_globalvars/bitfields.dm
+++ b/modular_sand/code/_globalvars/bitfields.dm
@@ -4,7 +4,28 @@
(this sort of bitfield definition will ALWAYS
use this menu so you can't var edit normally)
*/
+
+DEFINE_BITFIELD(flags_inv, list(
+ "HIDEACCESSORY" = HIDEACCESSORY,
+ "HIDEEARS" = HIDEEARS,
+ "HIDEEYES" = HIDEEYES,
+ "HIDEFACE" = HIDEFACE,
+ "HIDEFACIALHAIR" = HIDEFACIALHAIR,
+ "HIDEGLOVES" = HIDEGLOVES,
+ "HIDEHAIR" = HIDEHAIR,
+ "HIDEJUMPSUIT" = HIDEJUMPSUIT,
+ "HIDEMASK" = HIDEMASK,
+ "HIDENECK" = HIDENECK,
+ "HIDESHOES" = HIDESHOES,
+ "HIDESNOUT" = HIDESNOUT,
+ "HIDESUITSTORAGE" = HIDESUITSTORAGE,
+ "HIDETAUR" = HIDETAUR,
+ "HIDEUNDERWEAR" = HIDEUNDERWEAR,
+ "HIDEWRISTS" = HIDEWRISTS,
+))
+
DEFINE_BITFIELD(slot_flags, list(
+ "ITEM_SLOT_ACCESSORY" = ITEM_SLOT_ACCESSORY,
"ITEM_SLOT_BACK" = ITEM_SLOT_BACK,
"ITEM_SLOT_BACKPACK" = ITEM_SLOT_BACKPACK,
"ITEM_SLOT_BELT" = ITEM_SLOT_BELT,
@@ -31,3 +52,22 @@ DEFINE_BITFIELD(slot_flags, list(
"ITEM_SLOT_UNDERWEAR" = ITEM_SLOT_UNDERWEAR,
"ITEM_SLOT_WRISTS" = ITEM_SLOT_WRISTS,
))
+
+DEFINE_BITFIELD(vis_flags_inv, list(
+ "HIDEACCESSORY" = HIDEACCESSORY,
+ "HIDEEARS" = HIDEEARS,
+ "HIDEEYES" = HIDEEYES,
+ "HIDEFACE" = HIDEFACE,
+ "HIDEFACIALHAIR" = HIDEFACIALHAIR,
+ "HIDEGLOVES" = HIDEGLOVES,
+ "HIDEHAIR" = HIDEHAIR,
+ "HIDEJUMPSUIT" = HIDEJUMPSUIT,
+ "HIDEMASK" = HIDEMASK,
+ "HIDENECK" = HIDENECK,
+ "HIDESHOES" = HIDESHOES,
+ "HIDESNOUT" = HIDESNOUT,
+ "HIDESUITSTORAGE" = HIDESUITSTORAGE,
+ "HIDETAUR" = HIDETAUR,
+ "HIDEUNDERWEAR" = HIDEUNDERWEAR,
+ "HIDEWRISTS" = HIDEWRISTS,
+))
diff --git a/modular_sand/code/_globalvars/lists/lewd_content.dm b/modular_sand/code/_globalvars/lists/lewd_content.dm
new file mode 100644
index 0000000000..686e610126
--- /dev/null
+++ b/modular_sand/code/_globalvars/lists/lewd_content.dm
@@ -0,0 +1,23 @@
+/*
+ * List of clothes with possible peeking under,
+ * It is prefilled with clothes that cannot be caught
+ * by the automatic system (has no skirt in path)
+*/
+GLOBAL_LIST_INIT(skirt_peekable, list(
+ /obj/item/clothing/under/rank/civilian/janitor/maid = TRUE,
+ /obj/item/clothing/under/costume/loincloth = TRUE,
+ /obj/item/clothing/under/costume/loincloth/cloth = TRUE,
+ /obj/item/clothing/under/costume/loincloth/cloth/sensor = TRUE,
+ ))
+
+/*
+ * List of clothes you can't say a pair of.
+ * Ex:
+ * A pair of jockstrap?
+*/
+GLOBAL_LIST_INIT(pairless_panties, list(
+ /obj/item/clothing/underwear/briefs/jockstrap = TRUE,
+ /obj/item/clothing/underwear/briefs/panties/thong = TRUE,
+ /obj/item/clothing/underwear/briefs/panties/thong/babydoll = TRUE,
+ /obj/item/clothing/underwear/briefs/mankini = TRUE
+))
diff --git a/modular_sand/code/_globalvars/lists/objects.dm b/modular_sand/code/_globalvars/lists/objects.dm
index dd737fbe73..bbdfa5207d 100644
--- a/modular_sand/code/_globalvars/lists/objects.dm
+++ b/modular_sand/code/_globalvars/lists/objects.dm
@@ -1,2 +1,3 @@
GLOBAL_LIST_EMPTY(ic_jammers)
GLOBAL_LIST_EMPTY(ic_speakers)
+GLOBAL_DATUM_INIT(saliith_plushie, /obj/item/toy/plush/lizardplushie/saliith, new)
diff --git a/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm b/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm
new file mode 100644
index 0000000000..8d738c425a
--- /dev/null
+++ b/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm
@@ -0,0 +1,94 @@
+/// CRYPTOMINERS ///
+// Should cryptominers work in non-atmos turf
+/datum/config_entry/flag/crypto_ignore_atmos
+
+// Cryptominer point multipliers
+/datum/config_entry/number/crypto_multiplier_min
+ config_entry_value = 0.20
+ integer = FALSE
+
+/datum/config_entry/number/crypto_multiplier_mid
+ config_entry_value = 1
+ integer = FALSE
+
+/datum/config_entry/number/crypto_multiplier_max
+ config_entry_value = 3
+ integer = FALSE
+
+// Cryptominer heat thresholds
+/datum/config_entry/number/crypto_heat_threshold_min
+ config_entry_value = 225
+
+/datum/config_entry/number/crypto_heat_threshold_mid
+ config_entry_value = 273
+
+/datum/config_entry/number/crypto_heat_threshold_max
+ config_entry_value = 500
+
+// Cryptominer heat produced
+/datum/config_entry/number/crypto_heat_power
+ config_entry_value = 100
+
+/*
+ * The contained configuration values are currently unimplemented
+ *
+// Cryptominer processing time
+/datum/config_entry/number/crypto_mining_time
+ config_entry_value = 3000
+
+// Cryptominer base payout
+/datum/config_entry/number/crypto_payout_amount
+ config_entry_value = 50
+
+// Cryptominer power use
+/datum/config_entry/number/crypto_power_use_idle
+ config_entry_value = 20
+
+/datum/config_entry/number/crypto_power_use_active
+ config_entry_value = 200
+
+/datum/config_entry/number/crypto_power_use_process
+ config_entry_value = 20
+*/
+
+/// AUTODOC ///
+// Autodoc processing time
+/datum/config_entry/number/autodoc_time_surgery_base
+ config_entry_value = 350
+
+/// BLUESPACE MINER ///
+// BSM production output multiplier
+/datum/config_entry/number/bluespaceminer_mult_output
+ config_entry_value = 1
+
+// BSM minimum tier for bluespace crystals
+/datum/config_entry/number/bluespaceminer_crystal_tier
+ config_entry_value = 5
+
+/// TETRIS ARCADE MACHINE ///
+// If the machine should skip producing science points
+/datum/config_entry/flag/tetris_no_science
+
+// Points required per prize vended
+/datum/config_entry/number/tetris_reward_divisor
+ config_entry_value = 1000
+
+// Points required per prize vended
+/datum/config_entry/number/tetris_prizes_max
+ config_entry_value = 5
+
+// Minimum score required to message admins
+/datum/config_entry/number/tetris_score_high
+ config_entry_value = 10000
+
+// Maximum research points that can be generated
+/datum/config_entry/number/tetris_score_max
+ config_entry_value = 100000
+
+// Maximum research points that can be generated
+/datum/config_entry/number/tetris_score_max_sci
+ config_entry_value = 10000
+
+// Minimum time between giving rewards
+/datum/config_entry/number/tetris_time_cooldown
+ config_entry_value = 600
diff --git a/modular_sand/code/controllers/subsystem/interactions.dm b/modular_sand/code/controllers/subsystem/interactions.dm
index f4a1a08c5e..dee26167c3 100644
--- a/modular_sand/code/controllers/subsystem/interactions.dm
+++ b/modular_sand/code/controllers/subsystem/interactions.dm
@@ -4,6 +4,9 @@ SUBSYSTEM_DEF(interactions)
init_order = INIT_ORDER_INTERACTIONS
var/list/interactions
VAR_PROTECTED/list/blacklisted_mobs = list(
+ /mob/dead,
+ /mob/dview,
+ /mob/camera, // Although it would be funny to fuck the sentient disease or AI hologram
/mob/living/simple_animal/pet,
/mob/living/simple_animal/cockroach,
/mob/living/simple_animal/babyKiwi,
diff --git a/modular_sand/code/datums/interactions/interaction_interface.dm b/modular_sand/code/datums/components/interaction_menu_granter.dm
similarity index 81%
rename from modular_sand/code/datums/interactions/interaction_interface.dm
rename to modular_sand/code/datums/components/interaction_menu_granter.dm
index 2d7f794132..04c743fecd 100644
--- a/modular_sand/code/datums/interactions/interaction_interface.dm
+++ b/modular_sand/code/datums/components/interaction_menu_granter.dm
@@ -1,59 +1,69 @@
/// Attempts to open the tgui menu
-/mob/living/verb/interact_with()
+/mob/verb/interact_with()
set name = "Interact With"
set desc = "Perform an interaction with someone."
set category = "IC"
set src in view(usr.client)
- if(!usr.mind) //Mindless boys, honestly just don't, it's better this way
- return
- if(!usr.mind.interaction_holder)
- usr.mind.interaction_holder = new(usr.mind)
- usr.mind.interaction_holder.target = src
- usr.mind.interaction_holder.ui_interact(usr)
-
-/*
-/// Allows "cyborg" players to change gender at will
-/mob/living/silicon/robot/verb/toggle_gender()
- set name = "Set Gender"
- set desc = "Allows you to set your gender."
-
- if(stat != CONSCIOUS)
- to_chat(usr, span_warning("You cannot toggle your gender while unconcious!"))
+ var/datum/component/interaction_menu_granter/menu = usr.GetComponent(/datum/component/interaction_menu_granter)
+ if(!menu)
+ to_chat(usr, span_warning("You must have done something really bad to not have an interaction component."))
return
- var/choice = tgui_alert(usr, "Select Gender.", "Gender", list("Both", "Male", "Female"))
- switch(choice)
- if("Both")
- has_penis = TRUE
- has_vagina = TRUE
- if("Male")
- has_penis = TRUE
- has_vagina = FALSE
- if("Female")
- has_penis = FALSE
- has_vagina = TRUE
-*/
+ if(!src)
+ to_chat(usr, span_warning("Your interaction target is gone!"))
+ return
+ menu.open_menu(usr, src)
#define INTERACTION_NORMAL 0
#define INTERACTION_LEWD 1
#define INTERACTION_EXTREME 2
-/datum/mind
- var/datum/interaction_menu/interaction_holder
-
-/datum/mind/New(key)
- . = ..()
- interaction_holder = new(src)
-
/// The menu itself, only var is target which is the mob you are interacting with
-/datum/interaction_menu
+/datum/component/interaction_menu_granter
var/mob/living/target
-/datum/interaction_menu/ui_state(mob/user)
- return GLOB.conscious_state
+/datum/component/interaction_menu_granter/Initialize(...)
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/mob/parent_mob = parent
+ if(!parent_mob.client)
+ return COMPONENT_INCOMPATIBLE
+ . = ..()
-/datum/interaction_menu/ui_interact(mob/user, datum/tgui/ui)
+/datum/component/interaction_menu_granter/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_MOB_CTRLSHIFTCLICKON, .proc/open_menu)
+
+/datum/component/interaction_menu_granter/Destroy(force, ...)
+ target = null
+ . = ..()
+
+/datum/component/interaction_menu_granter/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_MOB_CTRLSHIFTCLICKON)
+ . = ..()
+
+/// The one interacting is clicker, the interacted is clicked.
+/datum/component/interaction_menu_granter/proc/open_menu(mob/clicker, mob/clicked)
+ // Don't cancel admin quick spawn
+ if(isobserver(clicked) && check_rights_for(clicker, R_SPAWN))
+ return FALSE
+ // COMSIG_MOB_CTRLSHIFTCLICKON accepts `atom`s, prevent it
+ if(!istype(clicked))
+ return FALSE
+ target = clicked
+ ui_interact(clicker)
+ return COMSIG_MOB_CANCEL_CLICKON
+
+/datum/component/interaction_menu_granter/ui_state(mob/user)
+ // Funny admin, don't you dare be the extra funny now.
+ if(user.client.holder && !user.client.holder.deadmined)
+ return GLOB.always_state
+ if(user == parent)
+ return GLOB.conscious_state
+ return GLOB.never_state
+
+/datum/component/interaction_menu_granter/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MobInteraction", "Interactions")
@@ -68,10 +78,10 @@
else
return 0
-/datum/interaction_menu/ui_data(mob/user)
+/datum/component/interaction_menu_granter/ui_data(mob/user)
. = ..()
//Getting player
- var/mob/living/self = user
+ var/mob/living/self = parent
//Getting info
.["isTargetSelf"] = target == self
.["interactingWith"] = target != self ? "Interacting with \the [target]..." : "Interacting with yourself..."
@@ -136,22 +146,22 @@
genital_entry["arousal_state"] = genital.aroused_state
genital_entry["always_accessible"] = genital.always_accessible
genitals += list(genital_entry)
- if(iscarbon(self) && !self.getorganslot(ORGAN_SLOT_ANUS))
- var/simulated_ass = list()
- simulated_ass["name"] = "Anus"
- simulated_ass["key"] = "anus"
- var/visibility = "Invalid"
- switch(self.anus_exposed)
- if(1)
- visibility = "Always visible"
- if(0)
- visibility = "Hidden by underwear"
- else
- visibility = "Always hidden"
- simulated_ass["visibility"] = visibility
- simulated_ass["possible_choices"] = GLOB.genitals_visibility_toggles - GEN_VISIBLE_NO_CLOTHES
- simulated_ass["always_accessible"] = self.anus_always_accessible
- genitals += list(simulated_ass)
+ if(!get_genitals.getorganslot(ORGAN_SLOT_ANUS)) //SPLURT Edit
+ var/simulated_ass = list()
+ simulated_ass["name"] = "Anus"
+ simulated_ass["key"] = "anus"
+ var/visibility = "Invalid"
+ switch(get_genitals.anus_exposed)
+ if(1)
+ visibility = "Always visible"
+ if(0)
+ visibility = "Hidden by underwear"
+ else
+ visibility = "Always hidden"
+ simulated_ass["visibility"] = visibility
+ simulated_ass["possible_choices"] = GLOB.genitals_visibility_toggles - GEN_VISIBLE_NO_CLOTHES
+ simulated_ass["always_accessible"] = get_genitals.anus_always_accessible
+ genitals += list(simulated_ass)
.["genitals"] = genitals
//Get their genitals
@@ -227,18 +237,19 @@
else
return "No"
-/datum/interaction_menu/ui_act(action, params)
+/datum/component/interaction_menu_granter/ui_act(action, params)
if(..())
return
+ var/mob/living/parent_mob = parent
switch(action)
if("interact")
var/datum/interaction/o = SSinteractions.interactions[params["interaction"]]
if(o)
- o.do_action(usr, target)
+ o.do_action(parent_mob, target)
return TRUE
return FALSE
if("genital")
- var/mob/living/carbon/self = usr
+ var/mob/living/carbon/self = parent_mob
if("visibility" in params)
if(params["genital"] == "anus")
self.anus_toggle_visibility(params["visibility"])
@@ -301,7 +312,7 @@
return TRUE
return FALSE
if("char_pref")
- var/datum/preferences/prefs = usr.client.prefs
+ var/datum/preferences/prefs = parent_mob.client.prefs
var/value = num_to_pref(params["value"])
switch(params["char_pref"])
if("erp_pref")
@@ -341,7 +352,7 @@
prefs.save_character()
return TRUE
if("pref")
- var/datum/preferences/prefs = usr.client.prefs
+ var/datum/preferences/prefs = parent_mob.client.prefs
switch(params["pref"])
if("verb_consent")
TOGGLE_BITFIELD(prefs.toggles, VERB_CONSENT)
diff --git a/modular_sand/code/datums/elements/holder_micro.dm b/modular_sand/code/datums/elements/holder_micro.dm
index f264ded3b5..0161e9ac90 100644
--- a/modular_sand/code/datums/elements/holder_micro.dm
+++ b/modular_sand/code/datums/elements/holder_micro.dm
@@ -168,9 +168,12 @@
set category = "IC"
set src in view(usr.client)
- if(!usr.mind) //Mindless boys, honestly just don't, it's better this way
+ var/datum/component/interaction_menu_granter/menu = usr.GetComponent(/datum/component/interaction_menu_granter)
+ if(!menu)
+ to_chat(usr, span_warning("You must have done something really bad to not have an interaction component."))
return
- if(!usr.mind.interaction_holder)
- usr.mind.interaction_holder = new(usr.mind)
- usr.mind.interaction_holder.target = src.held_mob
- usr.mind.interaction_holder.ui_interact(usr)
+
+ if(!src)
+ to_chat(usr, span_warning("Your interaction target is gone!"))
+ return
+ menu.open_menu(usr, src)
diff --git a/modular_sand/code/datums/elements/skirt_peeking.dm b/modular_sand/code/datums/elements/skirt_peeking.dm
new file mode 100644
index 0000000000..96207fbffa
--- /dev/null
+++ b/modular_sand/code/datums/elements/skirt_peeking.dm
@@ -0,0 +1,125 @@
+/datum/element/skirt_peeking
+ element_flags = ELEMENT_DETACH
+
+/datum/element/skirt_peeking/Attach(datum/peeked)
+ . = ..()
+ if(!ishuman(peeked))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(peeked, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+ RegisterSignal(peeked, COMSIG_PARENT_EXAMINE_MORE, .proc/on_closer_look)
+
+/datum/element/skirt_peeking/proc/can_skirt_peek(mob/living/carbon/human/peeked, mob/peeker)
+ var/mob/living/living_peeker = peeker
+ var/obj/item/clothing/under/worn_uniform = peeked.get_item_by_slot(ITEM_SLOT_ICLOTHING)
+
+ // Unfortunately, you can't see it
+ var/obj/item/clothing/suit/outer_clothing = peeked.get_item_by_slot(ITEM_SLOT_OCLOTHING)
+ if(outer_clothing && CHECK_MULTIPLE_BITFIELDS(outer_clothing.body_parts_covered, CHEST | GROIN | LEGS | FEET))
+ return FALSE
+ //
+
+ // Valid clothing section
+ if(worn_uniform && is_type_in_typecache(worn_uniform.type, GLOB.skirt_peekable))
+ // We are being peeked by a spooky ghost who sees all?
+ if(isobserver(peeker))
+ return TRUE
+ // Are you a living creature (and not us)?
+ if(istype(living_peeker) && (living_peeker != peeked))
+ // And are you under us while we're standing up?
+ if(!(CHECK_BITFIELD(living_peeker.mobility_flags, MOBILITY_STAND)) && (CHECK_BITFIELD(peeked.mobility_flags, MOBILITY_STAND)) && (peeked.loc == living_peeker.loc))
+ return TRUE
+ // Do you happen to be small enough to easily look under us?
+ if(COMPARE_SIZES(peeked, peeker) >= 2)
+ return TRUE
+ // Or are you nearby and we are up high
+ // to-do SOMEONE PLEASE PORT /datum/element/climbable
+ var/obj/structure/high_ground_peeked = locate(/obj/structure) in get_turf(peeked)
+ var/obj/structure/high_ground_peeker = locate(/obj/structure) in get_turf(peeker)
+ if(high_ground_peeked && high_ground_peeked.climbable && CHECK_BITFIELD(peeked.mobility_flags, MOBILITY_STAND) && peeked.Adjacent(peeker))
+ // Funnily enough, if we're at the same height, they can't just peek under us!
+ if(!(high_ground_peeker && high_ground_peeker.climbable))
+ return TRUE
+ return FALSE
+
+/datum/element/skirt_peeking/proc/on_examine(mob/living/carbon/human/peeked, mob/peeker, list/examine_list)
+ if(can_skirt_peek(peeked, peeker))
+ examine_list += span_purple("[peeked.p_theyre(TRUE)] wearing a skirt! I can probably give it a little peek looking closer .")
+
+/datum/element/skirt_peeking/proc/on_closer_look(mob/living/carbon/human/peeked, mob/peeker, list/examine_content)
+ if(can_skirt_peek(peeked, peeker))
+ var/obj/item/clothing/under/worn_uniform = peeked.get_item_by_slot(ITEM_SLOT_ICLOTHING)
+ var/string = "Peeking under [peeked]'s [worn_uniform.name], you can see "
+ var/obj/item/clothing/underwear/worn_underwear = peeked.get_item_by_slot(ITEM_SLOT_UNDERWEAR)
+ if(worn_underwear)
+ string += "a "
+ if(!is_type_in_typecache(worn_underwear.type, GLOB.pairless_panties)) //a pair of thong
+ string += "pair of "
+ if(worn_underwear.color)
+ string += "[worn_underwear.name] ."
+ else
+ string += "[worn_underwear.name]."
+
+ var/obj/item/organ/genital/penis/penis = peeked.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/genital/vagina/vagina = peeked.getorganslot(ORGAN_SLOT_VAGINA)
+ if(penis?.aroused_state)
+ string += span_love(" There's a visible bulge on [peeked.p_their()] front.")
+ else if(vagina?.aroused_state)
+ string += span_love(" [peeked.p_theyre(TRUE)] wet with arousal.")
+
+ else
+ string += "[peeked.p_theyre()] not wearing anything!\n[peeked.p_their(TRUE)]"
+ var/list/genitals = list()
+ for(var/obj/item/organ/genital/genital in peeked.internal_organs)
+ if(CHECK_BITFIELD(genital.genital_flags, (GENITAL_INTERNAL|GENITAL_HIDDEN)))
+ continue
+
+ var/appended
+ switch(genital.type)
+ if(/obj/item/organ/genital/vagina)
+ if(genital.aroused_state)
+ appended += " wet"
+ if(lowertext(genital.shape) != "human")
+ appended += " [lowertext(genital.shape)]"
+ if(lowertext(genital.shape) != "cloaca") //their wet cloaca vagina
+ appended += " [lowertext(genital.name)]" // goodbye pussy
+
+ if(/obj/item/organ/genital/testicles)
+ var/obj/item/organ/genital/testicles/nuts = genital
+ appended += " [lowertext(nuts.size_name)] [lowertext(nuts.name)]"
+ if(/obj/item/organ/genital/penis)
+ if(genital.aroused_state)
+ appended += " fully erect"
+ if(lowertext(genital.shape) != "human")
+ appended += " [lowertext(genital.shape)]"
+ appended += " [lowertext(genital.name)]" // Name it something funny, i dare you.
+ if(/obj/item/organ/genital/butt)
+ var/obj/item/organ/genital/butt/booty = genital
+ appended += " [booty.size_name] [lowertext(booty.name)]" // Maybe " average butt pair" isn't the best for now
+ else
+ continue
+ genitals += appended
+
+ string += english_list(genitals, " featureless groin", " and", ",")
+ string += " on full display."
+
+ examine_content += span_purple(string)
+ // Let's see if we caught them, addtimer so it appears after the peek.
+ addtimer(CALLBACK(src, .proc/try_notice, peeked, peeker), 1)
+
+/// Alright, they've peeked us and everything, did we notice it though?
+/datum/element/skirt_peeking/proc/try_notice(mob/living/carbon/human/peeked, mob/living/peeker)
+ if(!peeked || !peeker)
+ return
+ if(!istype(peeked) || !istype(peeker))
+ return
+ var/obj/item/clothing/under/worn_uniform = peeked.get_item_by_slot(ITEM_SLOT_ICLOTHING)
+ if(!istype(worn_uniform))
+ return
+ var/obj/item/clothing/glasses/eye_blocker = peeker.get_item_by_slot(ITEM_SLOT_EYES)
+ if(!(!peeked.client && (peeked.stat == CONSCIOUS) && !HAS_TRAIT(peeked, TRAIT_BLIND) && !is_blind(peeked) && \
+ !peeker.is_eyes_covered(FALSE) && !(eye_blocker && eye_blocker.tint > 0) && \
+ !(peeker.invisibility > peeked.invisibility) && !(peeker.alpha <= 30)))
+ return
+ to_chat(peeked, span_warning("You notice [peeker] looking under your [worn_uniform.name]!"))
+ to_chat(peeker, span_warning("[peeked] notices you peeking under [peeked.p_their()] [worn_uniform.name]!"))
diff --git a/modular_sand/code/datums/interactions/_interaction.dm b/modular_sand/code/datums/interactions/_interaction.dm
index b7077ae80d..5ba1efe8e8 100644
--- a/modular_sand/code/datums/interactions/_interaction.dm
+++ b/modular_sand/code/datums/interactions/_interaction.dm
@@ -10,13 +10,15 @@
- Makes all the code good because yes as well - SandPoot
**/
-/mob/living/proc/list_interaction_attributes()
- var/dat = list()
+/mob/proc/list_interaction_attributes()
+ return list()
+
+/mob/living/list_interaction_attributes()
+ . = ..()
if(has_hands())
- dat += "...have hands."
+ . += "...have hands."
if(has_mouth())
- dat += "...have a mouth, which is [mouth_is_free() ? "uncovered" : "covered"]."
- return dat
+ . += "...have a mouth, which is [mouth_is_free() ? "uncovered" : "covered"]."
/// The base of all interactions
/datum/interaction
diff --git a/modular_sand/code/datums/interactions/lewd_interactions.dm b/modular_sand/code/datums/interactions/lewd_interactions.dm
index 2171ec0b7f..ccd0708d93 100644
--- a/modular_sand/code/datums/interactions/lewd_interactions.dm
+++ b/modular_sand/code/datums/interactions/lewd_interactions.dm
@@ -516,18 +516,18 @@
return ..()
/mob/living/list_interaction_attributes(mob/living/LM)
- var/dat = ..()
+ . = ..()
if(!COOLDOWN_FINISHED(LM, refractory_period))
- dat += "...are sexually exhausted for the time being."
+ . += "...are sexually exhausted for the time being."
switch(a_intent)
if(INTENT_HELP)
- dat += "...are acting gentle."
+ . += "...are acting gentle."
if(INTENT_DISARM)
- dat += "...are acting playful."
+ . += "...are acting playful."
if(INTENT_GRAB)
- dat += "...are acting rough."
+ . += "...are acting rough."
if(INTENT_HARM)
- dat += "...are fighting anyone who comes near."
+ . += "...are fighting anyone who comes near."
//Here comes the fucking weird shit.
if(client)
var/client/cli = client
@@ -536,45 +536,44 @@
if(!ucli || (ucli.prefs.extremepref != "No"))
if(!get_item_by_slot(ITEM_SLOT_EARS_LEFT) && !get_item_by_slot(ITEM_SLOT_EARS_RIGHT))
if(has_ears())
- dat += "...have unprotected ears."
+ . += "...have unprotected ears."
else
- dat += "...have a hole where their ears should be."
+ . += "...have a hole where their ears should be."
else
- dat += "...have covered ears."
+ . += "...have covered ears."
if(!get_item_by_slot(ITEM_SLOT_EYES))
if(has_eyes())
- dat += "...have exposed eyes."
+ . += "...have exposed eyes."
else
- dat += "...have exposed eyesockets."
+ . += "...have exposed eyesockets."
else
- dat += "...have covered eyes."
+ . += "...have covered eyes."
//
// check those loops only once, thanks
var/is_topless = is_topless()
var/is_bottomless = is_bottomless()
if(is_topless && is_bottomless)
- dat += "...are naked."
+ . += "...are naked."
else
if((is_topless && !is_bottomless) || (!is_topless && is_bottomless))
- dat += "...are partially clothed."
+ . += "...are partially clothed."
else
- dat += "...are clothed."
+ . += "...are clothed."
if(has_breasts(REQUIRE_EXPOSED))
- dat += "...have breasts."
+ . += "...have breasts."
if(has_penis(REQUIRE_EXPOSED))
- dat += "...have a penis."
+ . += "...have a penis."
if(has_strapon(REQUIRE_EXPOSED))
- dat += "...have a strapon."
+ . += "...have a strapon."
if(has_balls(REQUIRE_EXPOSED))
- dat += "...have a ballsack."
+ . += "...have a ballsack."
if(has_vagina(REQUIRE_EXPOSED))
- dat += "...have a vagina."
+ . += "...have a vagina."
if(has_anus(REQUIRE_EXPOSED))
- dat += "...have an anus."
+ . += "...have an anus."
if(has_feet(REQUIRE_EXPOSED))
switch(has_feet(REQUIRE_EXPOSED))
if(2)
- dat += "...have a pair of feet."
+ . += "...have a pair of feet."
if(1)
- dat += "...have a single foot."
- return dat
+ . += "...have a single foot."
diff --git a/modular_sand/code/datums/traits/negative.dm b/modular_sand/code/datums/traits/negative.dm
index 3a50591875..2d238f1589 100644
--- a/modular_sand/code/datums/traits/negative.dm
+++ b/modular_sand/code/datums/traits/negative.dm
@@ -15,3 +15,10 @@
/datum/quirk/sheltered/remove() //i mean, the lose text explains it, so i'm making it actually work
var/mob/living/carbon/human/H = quirk_holder
H.grant_language(/datum/language/common)
+
+/datum/quirk/dnc_order
+ name = "DNC Order"
+ desc = "You have a Do Not Clone order on your record, stating that you may not be cloned. You can still be revived by other means."
+ value = -2
+ mob_trait = TRAIT_DNC_ORDER
+ medical_record_text = "Patient has a DNC (Do Not Clone) order and will be rejected by cloning mechanisms as a result."
diff --git a/modular_sand/code/datums/traits/neutral.dm b/modular_sand/code/datums/traits/neutral.dm
index 89486dc153..363f6f7e25 100644
--- a/modular_sand/code/datums/traits/neutral.dm
+++ b/modular_sand/code/datums/traits/neutral.dm
@@ -22,3 +22,41 @@
lose_text = span_love("You feel the warm blow of life flooding your womb, full of newfound, vibrant fertility!")
medical_record_text = "Patient doesn't seem able to ovulate properly..."
*/
+
+/datum/quirk/estrous_detection
+ name = "Estrous Detection"
+ desc = "You have a mammalian sense of detecting if someone\'s body longs for breeding."
+ value = 0
+ mob_trait = TRAIT_ESTROUS_DETECT
+ gain_text = span_love("Your senses adjust, allowing a mammalian sense of others' fertility.")
+ lose_text = span_notice("Your sense of others' fertility fades.")
+
+/datum/quirk/estrous_active
+ name = "In Estrous"
+ desc = "Your system burns with the desire to be bred. Satisfying your lust will make you happy, while ignoring it may cause you to become sad and needy."
+ value = 0
+ mob_trait = TRAIT_ESTROUS_ACTIVE
+ gain_text = span_love("You body burns with the desire to be bred.")
+ lose_text = span_notice("You feel more in control of your body and thoughts.")
+
+/datum/quirk/estrous_active/add()
+ // Add examine hook
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/quirk_examine_estrous_active)
+
+/datum/quirk/estrous_active/remove()
+ // Remove examine hook
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/estrous_active/proc/quirk_examine_estrous_active(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Check if human examiner exists
+ if(!istype(examiner))
+ return
+
+ // Check if examiner lacks the trait, or is self examining
+ if(!HAS_TRAIT(examiner, TRAIT_ESTROUS_DETECT) || (examiner == quirk_holder))
+ return
+
+ // Add quirk message
+ examine_list += span_love("[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] currently influenced by the estrous cycle, and long for breeding.")
diff --git a/modular_sand/code/game/machinery/autodoc.dm b/modular_sand/code/game/machinery/autodoc.dm
index fcd00555c4..d4d266200b 100644
--- a/modular_sand/code/game/machinery/autodoc.dm
+++ b/modular_sand/code/game/machinery/autodoc.dm
@@ -1,3 +1,6 @@
+// Configuration defines
+#define AUTODOC_TIME_BASE CONFIG_GET(number/autodoc_time_surgery_base)
+
/obj/machinery/autodoc
name = "autodoc"
desc = "An advanced machine used for inserting organs and implants into the occupant."
@@ -17,8 +20,11 @@
. = ..()
update_icon()
+ // Set initial time based on config
+ surgerytime = max(AUTODOC_TIME_BASE,10)
+
/obj/machinery/autodoc/RefreshParts()
- var/max_time = 350
+ var/max_time = AUTODOC_TIME_BASE
for(var/obj/item/stock_parts/L in component_parts)
max_time -= (L.rating*10)
surgerytime = max(max_time,10)
@@ -165,3 +171,5 @@
return
obj_flags |= EMAGGED
to_chat(user, span_warning("You reprogram [src]'s surgery procedures."))
+
+#undef AUTODOC_TIME_BASE
diff --git a/modular_sand/code/game/machinery/computer/arcade/tetris.dm b/modular_sand/code/game/machinery/computer/arcade/tetris.dm
index 1769ebe2aa..0ddd5be709 100644
--- a/modular_sand/code/game/machinery/computer/arcade/tetris.dm
+++ b/modular_sand/code/game/machinery/computer/arcade/tetris.dm
@@ -1,13 +1,20 @@
-#define REWARD_DIVISOR 1000
-// At the end of the game, the thing yields tetris_score/REWARD_DIVISOR prize tickets. Adjust accordingly.
+// Configuration defines
+#define TETRIS_REWARD_DIVISOR CONFIG_GET(number/tetris_reward_divisor)
+#define TETRIS_PRIZES_MAX CONFIG_GET(number/tetris_prizes_max)
+#define TETRIS_SCORE_HIGH CONFIG_GET(number/tetris_score_high)
+#define TETRIS_SCORE_MAX CONFIG_GET(number/tetris_score_max)
+#define TETRIS_SCORE_MAX_SCI CONFIG_GET(number/tetris_score_max_sci)
+#define TETRIS_TIME_COOLDOWN CONFIG_GET(number/tetris_time_cooldown)
+#define TETRIS_NO_SCIENCE CONFIG_GET(flag/tetris_no_science)
+
+// Cooldown defines
+#define TETRIS_COOLDOWN_MAIN cooldown_timer
/obj/machinery/computer/arcade/tetris
name = "T.E.T.R.I.S."
desc = "The pinnacle of human technology."
- icon = 'icons/obj/computer.dmi'
- icon_state = "arcade"
circuit = /obj/item/circuitboard/computer/arcade/tetris
- light_color = LIGHT_COLOR_GREEN
+ COOLDOWN_DECLARE(TETRIS_COOLDOWN_MAIN)
/obj/machinery/computer/arcade/tetris/Topic(href, href_list)
if(..())
@@ -15,13 +22,63 @@
else
usr.set_machine(src)
if(href_list["tetrisScore"])
- var/temp_score = text2num(href_list["tetrisScore"])
- say("YOUR SCORE: [temp_score]!")
- var/reward = round(temp_score/REWARD_DIVISOR)
- message_admins("[ADMIN_LOOKUPFLW(usr)] used [src] with score [temp_score] and total_rewards [reward]!!!")
- if(reward > 5)
- reward = 4
- prizevend(usr, reward)
+ // Sanitize score as an integer
+ // Restricts maximum score to (default) 100,000
+ var/temp_score = sanitize_num_clamp(text2num(href_list["tetrisScore"]), max=TETRIS_SCORE_MAX)
+
+ // Check for high score
+ if(temp_score > TETRIS_SCORE_HIGH)
+ // Alert admins
+ message_admins("[ADMIN_LOOKUPFLW(usr)] [ADMIN_KICK(usr)] has achieved a score of [temp_score] on [src] in [get_area(src.loc)]! Score exceeds configured suspicion threshold.")
+
+ // Round and clamp prize count from 0 to (default) 5
+ var/reward_count = clamp(round(temp_score/TETRIS_REWARD_DIVISOR), 0, TETRIS_PRIZES_MAX)
+
+ // Define score text
+ var/score_text = (reward_count ? temp_score : "PATHETIC! TRY HARDER")
+
+ // Display normal message
+ say("YOUR SCORE: [score_text]!")
+
+ // Check if any prize would be vended
+ if(!reward_count)
+ // Return without further effects
+ return
+
+ // Check cooldown
+ if(!COOLDOWN_FINISHED(src, TETRIS_COOLDOWN_MAIN))
+ // Play a fake prize vend effect based on prizevend()
+ playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
+ visible_message(span_notice("[src] sputters for a moment before going quiet."))
+
+ // Return with no further effects
+ return
+
+ // Set cooldown time
+ COOLDOWN_START(src, TETRIS_COOLDOWN_MAIN, TETRIS_TIME_COOLDOWN)
+
+ // Vend prizes
+ prizevend(usr, reward_count)
+
+ // Check if science points are possible and allowed
+ if((!SSresearch.science_tech) || TETRIS_NO_SCIENCE)
+ return
+
+ // Define user ID card
+ var/obj/item/card/id/user_id = usr.get_idcard()
+
+ // Check if ID exists
+ // Check if ID has science access
+ if(istype(user_id) && (ACCESS_RESEARCH in user_id.access))
+ // Limit maximum research points to (default) 10,000
+ var/score_research_points = clamp(temp_score, 0, TETRIS_SCORE_MAX_SCI)
+
+ // Add science points based on score
+ SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = score_research_points))
+
+ // Announce points earned
+ say("Research personnel detected. Applying gathered data to algorithms...")
+
return
/obj/machinery/computer/arcade/tetris/attack_ai(user as mob)
@@ -99,3 +156,13 @@
user << browse(dat, "window=tetris;size=435x550")
user.set_machine(src)
onclose(user, "tetris")
+
+// Remove defines
+#undef TETRIS_REWARD_DIVISOR
+#undef TETRIS_PRIZES_MAX
+#undef TETRIS_SCORE_HIGH
+#undef TETRIS_SCORE_MAX
+#undef TETRIS_SCORE_MAX_SCI
+#undef TETRIS_TIME_COOLDOWN
+#undef TETRIS_NO_SCIENCE
+#undef TETRIS_COOLDOWN_MAIN
diff --git a/modular_sand/code/game/machinery/computer/cloning.dm b/modular_sand/code/game/machinery/computer/cloning.dm
new file mode 100644
index 0000000000..e7844ef944
--- /dev/null
+++ b/modular_sand/code/game/machinery/computer/cloning.dm
@@ -0,0 +1,15 @@
+// Proc for scanning a mob in a cloning machine
+/obj/machinery/computer/cloning/can_scan(datum/dna/dna, mob/living/mob_occupant, experimental = FALSE, datum/bank_account/account)
+ // Check for DNC Order quirk
+ if(HAS_TRAIT(mob_occupant, TRAIT_DNC_ORDER))
+ // Set scan failure reason
+ scantemp = "Subject has an active DNC order on file. Further operations terminated."
+
+ // Play error sound
+ playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
+
+ // Return without scanning
+ return
+
+ // Return normally
+ . = ..()
diff --git a/modular_sand/code/game/machinery/cryptominers.dm b/modular_sand/code/game/machinery/cryptominers.dm
index 33f87a8e9f..6738511b4f 100644
--- a/modular_sand/code/game/machinery/cryptominers.dm
+++ b/modular_sand/code/game/machinery/cryptominers.dm
@@ -1,3 +1,22 @@
+// Configuration defines
+/*
+ * Some entries are currently unimplemented
+ *
+#define CRYPTO_POWER_USE CONFIG_GET(number/crypto_power_use_process)
+#define CRYPTO_POWER_IDLE CONFIG_GET(number/crypto_power_use_idle)
+#define CRYPTO_POWER_ACTIVE CONFIG_GET(number/crypto_power_use_active)
+#define CRYPTO_MININGTIME CONFIG_GET(number/crypto_mining_time)
+#define CRYPTO_MININGPOINTS CONFIG_GET(number/crypto_payout_amount)
+*/
+#define CRYPTO_TEMP_MIN CONFIG_GET(number/crypto_heat_threshold_min)
+#define CRYPTO_TEMP_MID CONFIG_GET(number/crypto_heat_threshold_mid)
+#define CRYPTO_TEMP_MAX CONFIG_GET(number/crypto_heat_threshold_max)
+#define CRYPTO_MULT_MIN CONFIG_GET(number/crypto_multiplier_min)
+#define CRYPTO_MULT_MID CONFIG_GET(number/crypto_multiplier_mid)
+#define CRYPTO_MULT_MAX CONFIG_GET(number/crypto_multiplier_max)
+#define CRYPTO_HEATING_POWER CONFIG_GET(number/crypto_heat_power)
+#define CRYPTO_IGNORE_ATMOS CONFIG_GET(flag/crypto_ignore_atmos)
+
/obj/machinery/cryptominer
name = "cryptocurrency miner"
desc = "This handy-dandy machine will produce credits for your enjoyment."
@@ -12,11 +31,6 @@
var/mining = FALSE
var/miningtime = 3000
var/miningpoints = 50
- var/mintemp = TCRYO // 225K equals approximately -55F or -48C
- var/midtemp = T0C // 273K equals 32F or 0C
- var/maxtemp = 500 // 500K equals approximately 440F or 226C
- var/heatingPower = 100 // Heat added each processing
- var/require_conductivity = TRUE // Prevent use in space
var/datum/bank_account/pay_me = null
/obj/machinery/cryptominer/Initialize(mapload)
@@ -59,7 +73,7 @@
return
to_chat(user, span_notice("You link \the [CARD] to \the [src]."))
pay_me = CARD.registered_account
- say("Now using [pay_me.account_holder ? "[pay_me.account_holder]'s" : span_boldwarning("ERROR")] account.")
+ say("Now using [pay_me.account_holder ? "[pay_me.account_holder]s" : span_boldwarning("ERROR")] account.")
return
/obj/machinery/cryptominer/AltClick(mob/user)
@@ -68,13 +82,13 @@
balloon_alert(user, "resetting")
if(do_after(user, 5 SECONDS, target = src))
pay_me = SSeconomy.get_dep_account(ACCOUNT_CAR)
- say("Now using [pay_me.account_holder]'s account.")
+ say("Now using [pay_me.account_holder]s account.")
/obj/machinery/cryptominer/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- . += span_notice("A little screen on the machine reads: Currently the linked bank account is [pay_me.account_holder ? "[pay_me.account_holder]'s" : span_boldwarning("ERROR")].")
- . += "Modify the destination of the credits using your id on it while it is inactive and has it's panel open."
+ . += span_notice("A little screen on the machine reads: Currently the linked bank account is [pay_me.account_holder ? "[pay_me.account_holder]s" : span_boldwarning("ERROR")].")
+ . += "Modify the destination of the credits using your id on it while it is inactive and has its panel open."
. += "Alt-Click to reset to the Cargo budget."
/obj/machinery/cryptominer/process()
@@ -85,18 +99,19 @@
// Check for tiles with no conductivity (space)
if(T.thermal_conductivity == 0)
+ // Cheat mode: Skip all atmos code and give points
+ // Placed first, as servers are more likely to use it
+ if(CRYPTO_IGNORE_ATMOS)
+ produce_points(CRYPTO_MULT_MAX)
+ return
+
// Normal mode: Warn the user and stop processing
- if(require_conductivity)
+ else
say("Invalid atmospheric conditions detected! Shutting off!")
playsound(loc, 'sound/machines/beep.ogg', 50, TRUE, -1)
set_mining(FALSE)
return
- // Cheat mode: Skip all atmos code and give points
- else
- produce_points(3)
- return
-
// Get air
var/datum/gas_mixture/env = T.return_air()
if(!env)
@@ -105,24 +120,29 @@
// Get temp
var/env_temp = env.return_temperature()
+ // Define temperature settings
+ var/temp_min = CRYPTO_TEMP_MIN // 225K equals approximately -55F or -48C
+ var/temp_mid = CRYPTO_TEMP_MID // 273K equals 32F or 0C
+ var/temp_max = CRYPTO_TEMP_MAX // 500K equals approximately 440F or 226C
+
// Check for temperature effects
// Minimum (most likely)
- if(env_temp <= mintemp)
- produce_points(3)
+ if(env_temp <= temp_min)
+ produce_points(CRYPTO_MULT_MAX)
// Mid
- else if((env_temp <= midtemp) && (env_temp >= mintemp))
- produce_points(1)
+ else if((env_temp <= temp_mid) && (env_temp >= temp_min))
+ produce_points(CRYPTO_MULT_MID)
// Maximum
- else if((env_temp <= maxtemp) && (env_temp >= midtemp))
- produce_points(0.20)
+ else if((env_temp <= temp_max) && (env_temp >= temp_mid))
+ produce_points(CRYPTO_MULT_MIN)
// Overheat
- else if(env_temp >= maxtemp)
+ else if(env_temp >= temp_max)
say("Critical overheating detected! Shutting off!")
playsound(loc, 'sound/machines/beep.ogg', 50, TRUE, -1)
set_mining(FALSE)
- // Increase heat by heatingPower
- env.set_temperature(env_temp + heatingPower)
+ // Increase heat by heating_power
+ env.set_temperature(env_temp + CRYPTO_HEATING_POWER)
// Update air
air_update_turf()
@@ -148,15 +168,25 @@
set_mining(TRUE)
/obj/machinery/cryptominer/proc/set_mining(new_value)
+ // Check if status changed
if(new_value == mining)
return //No changes
- mining = new_value
- if(mining)
- START_PROCESSING(SSmachines, src)
- else
- STOP_PROCESSING(SSmachines, src)
- update_icon()
+ // Set status new value
+ mining = new_value
+
+ // Check if mining should run
+ if(mining)
+ // Start processing
+ START_PROCESSING(SSmachines, src)
+
+ // Mining should not run
+ else
+ // Stop processing
+ STOP_PROCESSING(SSmachines, src)
+
+ // Update machine icon
+ update_icon()
/obj/machinery/cryptominer/syndie
name = "syndicate cryptocurrency miner"
@@ -198,3 +228,21 @@
icon_state = "loop_nano"
else
icon_state = "on_nano"
+
+/*
+ * Some entries are currently unimplemented
+ *
+#undef CRYPTO_POWER_USE
+#undef CRYPTO_POWER_IDLE
+#undef CRYPTO_POWER_ACTIVE
+#undef CRYPTO_MININGTIME
+#undef CRYPTO_MININGPOINTS
+*/
+#undef CRYPTO_TEMP_MIN
+#undef CRYPTO_TEMP_MID
+#undef CRYPTO_TEMP_MAX
+#undef CRYPTO_MULT_MIN
+#undef CRYPTO_MULT_MID
+#undef CRYPTO_MULT_MAX
+#undef CRYPTO_HEATING_POWER
+#undef CRYPTO_IGNORE_ATMOS
diff --git a/modular_sand/code/game/objects/effects/contraband.dm b/modular_sand/code/game/objects/effects/contraband.dm
index 0ec0d17ab9..6272882944 100644
--- a/modular_sand/code/game/objects/effects/contraband.dm
+++ b/modular_sand/code/game/objects/effects/contraband.dm
@@ -1,5 +1,5 @@
/obj/structure/sign/poster/contraband/yes_erp
name = "Yes ERP"
- desc = "This poster negates that Eroticism, Rape and Pornography should be banned from Nanotrasen stations."
+ desc = "This poster scrutinizes the banning of Eroticism, Rape and Pornography from Nanotrasen stations."
icon = 'modular_sand/icons/obj/contraband.dmi'
- icon_state = "poster1" //It is one because different file
+ icon_state = "poster_yeserp"
diff --git a/modular_sand/code/game/objects/items/fleshlight.dm b/modular_sand/code/game/objects/items/fleshlight.dm
index 344d3eecd2..1db9cbc504 100644
--- a/modular_sand/code/game/objects/items/fleshlight.dm
+++ b/modular_sand/code/game/objects/items/fleshlight.dm
@@ -625,6 +625,7 @@
var/targetting = CUM_TARGET_VAGINA
equip_delay_self = 2 SECONDS
equip_delay_other = 5 SECONDS
+ is_edible = 0
/obj/item/clothing/underwear/briefs/panties/portalpanties/attack_self(mob/user)
. = ..()
diff --git a/modular_sand/code/game/objects/items/plushes.dm b/modular_sand/code/game/objects/items/plushes.dm
new file mode 100644
index 0000000000..fb50c80ce9
--- /dev/null
+++ b/modular_sand/code/game/objects/items/plushes.dm
@@ -0,0 +1,241 @@
+// Honestly, Saliith was just sad when he made this. Leave this file in the game to let people hug him.
+
+/obj/item/toy/plush/lizardplushie/saliith
+ name = "Saliith plushie"
+ desc = "He looks like he needs a friend."
+ icon = 'modular_sand/icons/obj/plushes.dmi'
+ icon_state = "saliith"
+ gender = MALE
+ can_random_spawn = FALSE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF // Protected by a higher power
+ unstuffable = TRUE // Prevent grenades
+
+/obj/item/toy/plush/lizardplushie/saliith/Initialize(mapload, set_snowflake_id)
+ // Check if plush already exists
+ if(GLOB.saliith_plushie && (GLOB.saliith_plushie != src))
+ return INITIALIZE_HINT_QDEL
+
+ // Appear on orbit menu
+ GLOB.poi_list += src
+
+ // Return normally
+ . = ..()
+
+/obj/item/toy/plush/lizardplushie/saliith/Destroy()
+ // Let's not keep the reference hanging around
+ GLOB.poi_list -= src
+ . = ..()
+
+/obj/item/toy/plush/lizardplushie/saliith/ComponentInitialize()
+ . = ..()
+
+ // Add respawn component
+ AddComponent(/datum/component/stationloving)
+
+ // Define pronouns
+ var/p_they = p_they()
+ var/p_s = p_s()
+
+ // Add custom description
+ normal_desc = "[p_they] look[p_s] like [p_they] need[p_s] a friend."
+
+/obj/item/toy/plush/lizardplushie/saliith/examine(mob/user)
+ . = ..()
+
+ // Define pronouns
+ var/p_them = p_them()
+ //var/p_they = p_they()
+ //var/p_are = p_are()
+
+ // Check for stuffing
+ if(!stuffed)
+ // Update examine text and return
+ . += span_deadsay("[p_they(TRUE)] [p_are()] dead.")
+ return
+
+ // Check if user is Saliith himself
+ if(user.ckey == "sandpoot")
+ // Update examine text and return
+ . += span_deadsay("You feel a sense of familiarity from [p_them].")
+ return
+
+ // Check for antag datums
+ if((length(user?.mind?.antag_datums) >= 1))
+ // Update examine text
+ . += span_warning("[src] gives you a menacing glare! Patting [p_them] would be a dangerous mistake.")
+
+/obj/item/toy/plush/lizardplushie/saliith/attack_self(mob/living/carbon/human/user)
+ // Check if user exists
+ if(!user)
+ // Return normally
+ return ..()
+
+ // Check if user has a mind
+ if(!user.mind)
+ // Return normally
+ return ..()
+
+ // Define pronouns
+ var/p_they = p_they()
+ //var/p_their = p_their()
+ var/p_s = p_s()
+
+ // Check if user is Saliith himself
+ if(user.ckey == "sandpoot")
+ // Alert him and return
+ to_chat(user, span_notice("[p_they] give[p_s] you a hesitant gaze, but accept[p_s] the gesture anyhow."))
+ return ..()
+
+ // Check if user is an antagonist role
+ if((length(user?.mind?.antag_datums) >= 1))
+ // Check if user is a xenobio changeling
+ if(user?.mind?.has_antag_datum(/datum/antagonist/changeling/xenobio))
+ // Alert the user
+ to_chat(user, span_notice("[src] senses what you really are, but decides to spare you."))
+
+ // Check if user is a brainwashed victim
+ else if(user?.mind?.has_antag_datum(/datum/antagonist/brainwashed))
+ // Alert the user
+ to_chat(user, span_notice("[src] senses that you're not in control of your actions, and offers [p_their()] sympathy."))
+
+ // User is not a whitelisted antagonist
+ else
+ // Drop the item
+ user.dropItemToGround(src)
+
+ // Warn user
+ user.visible_message(span_warning("[src] smites [user] with an otherworldly wrath!"), span_boldwarning("You've made a grave mistake."))
+
+ // Get lightning location
+ var/turf/turf_target = get_step(get_step(user, NORTH), NORTH)
+
+ // Perform lightning effect
+ turf_target.Beam(user, icon_state="lightning[rand(1,12)]", time = 5)
+ user.electrocution_animation(40)
+
+ // Play sound effect
+ playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, 1)
+
+ // Add fire damage
+ user.adjustFireLoss(120)
+
+ // Return
+ return
+
+ // User has no antagonist status
+
+ // Alert the user
+ to_chat(user, span_notice("[p_they] give[p_s] you a hesitant gaze, but accepts the gesture anyhow."))
+
+ // Return
+ return ..()
+
+/obj/item/toy/plush/lizardplushie/saliith/attackby(obj/item/item_used, mob/living/user, params)
+ // Check for sharp object
+ if(item_used.get_sharpness())
+ // Warn in local chat
+ visible_message(span_warning("[src] knocks \the [item_used] out of [user]'s hands!"), span_warning("[src] knocks \the [item_used] out of your hands!"))
+
+ // Drop the item
+ user.dropItemToGround(item_used)
+
+ // Throw the item away
+ item_used.throw_at(pick(oview(7,get_turf(src))),10,1)
+
+ // Return
+ return
+
+ // Check if user is Saliith himself
+ if(user.ckey == "sandpoot")
+ // Return with no effects
+ return ..()
+
+ // Check for grenade
+ if(istype(item_used, /obj/item/grenade))
+ // Warn in local chat
+ visible_message(span_warning("[src] forces \the [item_used] into [user]'s mouth!"), span_warning("[src] forces \the [item_used] into your mouth!"))
+
+ // Define the grenade item
+ var/obj/item/grenade/item_grenade = item_used
+
+ // Move grenade to the user
+ item_grenade.forceMove(user)
+
+ // Set the detonation time
+ item_grenade.preprime(volume = 10)
+
+ // Return
+ return
+
+ // Return normally
+ return ..()
+
+/obj/item/toy/plush/lizardplushie/saliith/ex_act(severity, target, origin)
+ return
+
+/obj/item/toy/plush/plushling/plushie_absorb(obj/item/toy/plush/victim)
+ // Check if target is the Saliith plushie
+ if(istype(victim, /obj/item/toy/plush/lizardplushie/saliith))
+ // Warn in local chat
+ visible_message(span_warning("[victim] violently parries the impostor! [src] is utterly annihilated!"))
+
+ // Create a gib effect
+ new /obj/effect/gibspawner(get_turf(src))
+
+ // Delete the plushling
+ qdel(src)
+
+ // Return
+ return
+
+ // Return normally
+ return ..()
+
+/obj/item/toy/plush/love(obj/item/toy/plush/Kisser, mob/living/user)
+ // Define saliith plush
+ var/plush_saliith = /obj/item/toy/plush/lizardplushie/saliith
+
+ // Check if interaction involves the Saliith plush
+ if(istype(src, plush_saliith) || istype(Kisser, plush_saliith))
+ // Check if user is Saliith himself
+ if(user.ckey == "sandpoot")
+ // Return normally
+ return ..()
+
+ // User is not Saliith
+ // Warn in local chat
+ user.visible_message(span_warning("[user] tried to force [Kisser] to kiss [src] against their will, and has been yeeted!"), span_warning("You try to force [Kisser] to kiss [src], but get yeeted instead!"))
+
+ // Display voice of god message
+ say("YEET", spans = list("colossus","yell"))
+
+ // Play sound
+ playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 5)
+
+ // Drop the plushies if held
+ if(src in user.held_items)
+ user.dropItemToGround(src)
+ if(Kisser in user.held_items)
+ user.dropItemToGround(Kisser)
+
+ // Launch user away
+ var/turf/yeet_target = get_edge_target_turf(user, pick(GLOB.alldirs))
+ user.throw_at(yeet_target, 10, 14)
+ log_combat(src, user, "plush yeeted")
+
+ // Return
+ return
+
+ // Interaction does not involve Saliith
+ // Return normally
+ return ..()
+
+// Pinpointer for plushie toy
+/obj/item/pinpointer/plushie_saliith
+ name = "Saliith plushie pinpointer"
+ desc = "A handheld tracking device that locates Saliith's plushie."
+ icon = 'modular_sand/icons/obj/device.dmi'
+ icon_state = "pinpointer_saliith"
+
+/obj/item/pinpointer/plushie_saliith/scan_for_target()
+ set_target(GLOB.saliith_plushie, src)
diff --git a/modular_sand/code/modules/client/loadout/accessories.dm b/modular_sand/code/modules/client/loadout/accessories.dm
index a317069bf4..61562dd38b 100644
--- a/modular_sand/code/modules/client/loadout/accessories.dm
+++ b/modular_sand/code/modules/client/loadout/accessories.dm
@@ -1,6 +1,5 @@
/datum/gear/accessories/ring
- category = LOADOUT_CATEGORY_GLOVES
- slot = ITEM_SLOT_HANDS
+ slot = ITEM_SLOT_ACCESSORY
/datum/gear/accessories/ring/goldring
name = "A gold ring"
diff --git a/modular_sand/code/modules/client/loadout/backpack.dm b/modular_sand/code/modules/client/loadout/backpack.dm
new file mode 100644
index 0000000000..c95cbab2c3
--- /dev/null
+++ b/modular_sand/code/modules/client/loadout/backpack.dm
@@ -0,0 +1,3 @@
+/datum/gear/backpack/pinpointer/plushie_saliith
+ name = "Saliith Plushie Pinpointer"
+ path = /obj/item/pinpointer/plushie_saliith
diff --git a/modular_sand/code/modules/clothing/gloves/accessories.dm b/modular_sand/code/modules/clothing/gloves/accessories.dm
index 1c3986de1f..e2afe193ff 100644
--- a/modular_sand/code/modules/clothing/gloves/accessories.dm
+++ b/modular_sand/code/modules/clothing/gloves/accessories.dm
@@ -3,7 +3,8 @@
desc = "A tiny gold ring, sized to wrap around a finger."
gender = NEUTER
w_class = WEIGHT_CLASS_TINY
- slot_flags = ITEM_SLOT_GLOVES
+ slot_flags = ITEM_SLOT_ACCESSORY | ITEM_SLOT_GLOVES
+ slot_equipment_priority = ITEM_SLOT_ACCESSORY | ITEM_SLOT_GLOVES | ITEM_SLOT_BACKPACK
icon = 'icons/obj/ring.dmi'
mob_overlay_icon = 'icons/mob/clothing/hands.dmi'
icon_state = "ringgold"
diff --git a/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm b/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
index 63777f7482..a73639e808 100644
--- a/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
@@ -136,7 +136,7 @@
"Conscript" = list(
"name" = "Conscript suit",
"desc" = "..and so he left, with new orders and new questions. ",
- "icon" = 'modular_sand/icons/mob/clothing/suit.dmi',
+ "icon" = 'modular_sand/icons/obj/clothing/suits.dmi',
"icon_state" = "commando-armor",
"mob_overlay_icon" = 'modular_sand/icons/mob/clothing/suit.dmi',
"anthro_mob_worn_overlay" = 'modular_sand/icons/mob/clothing/suit_digi.dmi'
@@ -151,10 +151,15 @@
. = ..()
switch(current_skin)
if("Conscript")
- if(slot == ITEM_SLOT_OCLOTHING)
- icon = 'modular_sand/icons/mob/clothing/suit.dmi'
- else
- icon = 'modular_sand/icons/obj/clothing/suits.dmi'
+ var/datum/component/armor_plate/armor_comp = GetComponent(/datum/component/armor_plate)
+ var/armor_level = 0
+ var/armor_max = 0
+ if(armor_comp)
+ armor_level = armor_comp.amount
+ armor_max = armor_comp.maxamount
+
+ upgrade_icon(amount = armor_level, maxamount = armor_max)
+
else
return
@@ -171,37 +176,39 @@
if(istype(helmet))
mining_helmet = helmet
+ /// h suffix for helmet
+ var/datum/component/armor_plate/armor_comp_h = mining_helmet.GetComponent(/datum/component/armor_plate)
+ var/armor_level_h = 0
+ var/armor_max_h = 0
+ if(armor_comp_h)
+ armor_level_h = armor_comp_h.amount
+ armor_max_h = armor_comp_h.maxamount
+
switch(current_skin)
if("Default")
- if(armor_level != 0)
- upgrade_icon(amount = armor_level, maxamount = armor_max)
+ upgrade_icon(amount = armor_level, maxamount = armor_max)
if(mining_helmet)
mining_helmet.name = initial(mining_helmet.name)
mining_helmet.desc = initial(mining_helmet.desc)
mining_helmet.icon = initial(mining_helmet.icon)
mining_helmet.icon_state = initial(mining_helmet.icon_state)
- if(armor_level != 0)
- mining_helmet.upgrade_icon(amount = armor_level, maxamount = armor_max)
+ mining_helmet.upgrade_icon(amount = armor_level_h, maxamount = armor_max_h)
mining_helmet.mob_overlay_icon = initial(mining_helmet.mob_overlay_icon)
mining_helmet.anthro_mob_worn_overlay = initial(mining_helmet.anthro_mob_worn_overlay)
/// Sprited by Dexxiol#3462 :)
if("Conscript")
- if(armor_level != 0)
- upgrade_icon(amount = armor_level, maxamount = armor_max)
+ upgrade_icon(amount = armor_level, maxamount = armor_max)
if(mining_helmet)
mining_helmet.name = "Conscript helmet"
mining_helmet.desc = "It shines briefly, full of life."
- mining_helmet.icon = 'modular_sand/icons/mob/clothing/head.dmi'
+ mining_helmet.icon = 'modular_sand/icons/obj/clothing/hats.dmi'
mining_helmet.icon_state = "commando-helmet"
- if(armor_level != 0)
- mining_helmet.upgrade_icon(amount = armor_level, maxamount = armor_max)
+ mining_helmet.upgrade_icon(amount = armor_level_h, maxamount = armor_max_h)
mining_helmet.mob_overlay_icon = 'modular_sand/icons/mob/clothing/head.dmi'
mining_helmet.anthro_mob_worn_overlay = 'modular_sand/icons/mob/clothing/head_muzzled.dmi'
- if(current_equipped_slot != ITEM_SLOT_OCLOTHING)
- icon = 'modular_sand/icons/obj/clothing/suits.dmi'
/obj/item/clothing/head/helmet/space/hardsuit/mining/update_icon_state()
switch(suit.current_skin)
@@ -224,7 +231,7 @@
icon_state = "commando[hardsuit_type]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
- if(wearer.head == src)
+ if(istype(wearer) && (wearer.head == src))
wearer.update_inv_head()
else
. = ..()
@@ -238,10 +245,10 @@
hardsuit_type = "2-armor"
if(amount == maxamount)
hardsuit_type = "3-armor"
- icon_state = "commando[hardsuit_type]"
+ icon_state = "commando[hardsuit_type][current_equipped_slot != ITEM_SLOT_OCLOTHING ? "-inhand" : ""]"
if(ishuman(loc))
var/mob/living/carbon/human/wearer = loc
- if(wearer.wear_suit == src)
+ if(istype(wearer) && (wearer.wear_suit == src))
wearer.update_inv_wear_suit()
else
. = ..()
diff --git a/modular_sand/code/modules/clothing/under/_under.dm b/modular_sand/code/modules/clothing/under/_under.dm
index fe6dcc8fba..7542a0c85a 100644
--- a/modular_sand/code/modules/clothing/under/_under.dm
+++ b/modular_sand/code/modules/clothing/under/_under.dm
@@ -1,7 +1,3 @@
-GLOBAL_LIST_INIT(skirt_peekable, list(
- /obj/item/clothing/under/rank/civilian/janitor/maid = TRUE /* This one hangs about because no skirt in path */
- ))
-
/obj/item/clothing/under/Initialize(mapload)
. = ..()
if(!is_type_in_typecache(type, GLOB.skirt_peekable) && findlasttext("[type]", "skirt"))
diff --git a/modular_sand/code/modules/clothing/under/color.dm b/modular_sand/code/modules/clothing/under/color.dm
deleted file mode 100644
index 786fc955b3..0000000000
--- a/modular_sand/code/modules/clothing/under/color.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/obj/item/clothing/under/color/jumpskirt
- is_skirt = TRUE
diff --git a/modular_sand/code/modules/clothing/under/misc.dm b/modular_sand/code/modules/clothing/under/misc.dm
deleted file mode 100644
index 87cb52eefc..0000000000
--- a/modular_sand/code/modules/clothing/under/misc.dm
+++ /dev/null
@@ -1,8 +0,0 @@
-/obj/item/clothing/under/rank/prisoner/skirt
- is_skirt = TRUE
-
-/obj/item/clothing/under/misc/durathread/skirt
- is_skirt = TRUE
-
-/obj/item/clothing/under/misc/cog/jumpskirt
- is_skirt = TRUE
diff --git a/modular_sand/code/modules/clothing/under/skirt_dress.dm b/modular_sand/code/modules/clothing/under/skirt_dress.dm
deleted file mode 100644
index 79bf474a80..0000000000
--- a/modular_sand/code/modules/clothing/under/skirt_dress.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/obj/item/clothing/under/dress/skirt //inb4 this breaks something
- is_skirt = TRUE
diff --git a/modular_sand/code/modules/clothing/under/suits.dm b/modular_sand/code/modules/clothing/under/suits.dm
deleted file mode 100644
index 04a37b0471..0000000000
--- a/modular_sand/code/modules/clothing/under/suits.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-/obj/item/clothing/under/suit/white_on_white/skirt
- is_skirt = TRUE
-
-/obj/item/clothing/under/suit/black/skirt
- is_skirt = TRUE
diff --git a/modular_sand/code/modules/clothing/under/syndicate.dm b/modular_sand/code/modules/clothing/under/syndicate.dm
deleted file mode 100644
index 04f160c705..0000000000
--- a/modular_sand/code/modules/clothing/under/syndicate.dm
+++ /dev/null
@@ -1,8 +0,0 @@
-/obj/item/clothing/under/syndicate/skirt
- is_skirt = TRUE
-
-/obj/item/clothing/under/syndicate/tacticool/skirt
- is_skirt = TRUE
-
-/obj/item/clothing/under/syndicate/cosmetic/skirt
- is_skirt = TRUE
diff --git a/modular_sand/code/modules/jobs/job_types/prisoner.dm b/modular_sand/code/modules/jobs/job_types/prisoner.dm
new file mode 100644
index 0000000000..3921e11891
--- /dev/null
+++ b/modular_sand/code/modules/jobs/job_types/prisoner.dm
@@ -0,0 +1,3 @@
+// I'm letting you get 5 spawn positions because latejoin is broken, do not disappoint
+/datum/job/prisoner
+ spawn_positions = 5
diff --git a/modular_sand/code/modules/mining/machine_bluespaceminer.dm b/modular_sand/code/modules/mining/machine_bluespaceminer.dm
index 9241f8745e..80fd0dadd9 100644
--- a/modular_sand/code/modules/mining/machine_bluespaceminer.dm
+++ b/modular_sand/code/modules/mining/machine_bluespaceminer.dm
@@ -1,3 +1,7 @@
+// Configuration defines
+#define BLUESPACE_MINER_BONUS_MULT CONFIG_GET(number/bluespaceminer_mult_output)
+#define BLUESPACE_MINER_CRYSTAL_TIER CONFIG_GET(number/bluespaceminer_crystal_tier)
+
/obj/machinery/mineral/bluespace_miner
name = "bluespace mining machine"
desc = "A machine that uses the magic of Bluespace to slowly generate materials and add them to a linked ore silo."
@@ -8,7 +12,16 @@
circuit = /obj/item/circuitboard/machine/bluespace_miner
layer = BELOW_OBJ_LAYER
init_process = TRUE
- var/list/ore_rates = list(/datum/material/iron = 0.3, /datum/material/glass = 0.3, /datum/material/plasma = 0.1, /datum/material/silver = 0.1, /datum/material/gold = 0.05, /datum/material/titanium = 0.05, /datum/material/uranium = 0.05, /datum/material/diamond = 0.02)
+ var/list/ore_rates = list(
+ /datum/material/iron = 0.3,
+ /datum/material/glass = 0.3,
+ /datum/material/plasma = 0.1,
+ /datum/material/silver = 0.1,
+ /datum/material/gold = 0.05,
+ /datum/material/titanium = 0.05,
+ /datum/material/uranium = 0.05,
+ /datum/material/diamond = 0.02
+ )
var/datum/component/remote_materials/materials
var/multiplier = 0 //Multiplier by tier, has been made fair and everything
@@ -16,11 +29,14 @@
. = ..()
materials = AddComponent(/datum/component/remote_materials, "bsm", mapload)
+ // Set initial multiplier based on config
+ multiplier *= BLUESPACE_MINER_BONUS_MULT
+
/obj/machinery/mineral/bluespace_miner/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += span_notice("A small screen on the machine reads, \"Efficiency at [multiplier * 100]%\"")
- if(multiplier >= 5)
+ if(multiplier >= BLUESPACE_MINER_CRYSTAL_TIER)
. += span_notice("Bluespace generation is active.")
if(!anchored)
. += span_warning("The machine won't work while not firmly secured to the ground.")
@@ -38,11 +54,14 @@
multiplier += L.rating
stock_amt++
multiplier /= stock_amt
- if(multiplier >= 5)
+ if(multiplier >= BLUESPACE_MINER_CRYSTAL_TIER)
ore_rates[/datum/material/bluespace] = 0.01
else
ore_rates -= /datum/material/bluespace
+ // Apply config multiplier here to not interfere with bluespace material check
+ multiplier *= BLUESPACE_MINER_BONUS_MULT
+
/obj/machinery/mineral/bluespace_miner/Destroy()
materials = null
return ..()
@@ -108,3 +127,6 @@
if(default_unfasten_wrench(user, I))
return TRUE
return FALSE
+
+#undef BLUESPACE_MINER_BONUS_MULT
+#undef BLUESPACE_MINER_CRYSTAL_TIER
diff --git a/modular_sand/code/modules/mob/living/carbon/human/examine.dm b/modular_sand/code/modules/mob/living/carbon/human/examine.dm
deleted file mode 100644
index 5e0b39bce8..0000000000
--- a/modular_sand/code/modules/mob/living/carbon/human/examine.dm
+++ /dev/null
@@ -1,77 +0,0 @@
-GLOBAL_LIST_INIT(pairless_panties, list(
- /obj/item/clothing/underwear/briefs/jockstrap = TRUE,
- /obj/item/clothing/underwear/briefs/panties/thong = TRUE,
- /obj/item/clothing/underwear/briefs/panties/thong/babydoll = TRUE,
- /obj/item/clothing/underwear/briefs/mankini = TRUE
-))
-
-/mob/living/carbon/human/examine(mob/user)
- . = ..()
- var/mob/living/living = user
- var/obj/item/clothing/under/worn_uniform = get_item_by_slot(ITEM_SLOT_ICLOTHING)
- if(worn_uniform && is_type_in_typecache(worn_uniform.type, GLOB.skirt_peekable) && (isobserver(user) || (isliving(user) && (user != src) && !(living.mobility_flags & MOBILITY_STAND) && (mobility_flags & MOBILITY_STAND) && (loc == living.loc) && (istype(worn_uniform)))))
- . += span_purple("[p_theyre(TRUE)] wearing a [worn_uniform.name]! You can probably give it a little peek by looking closer .")
-
-/mob/living/carbon/human/Initialize(mapload)
- . = ..()
- RegisterSignal(src, COMSIG_PARENT_EXAMINE_MORE, .proc/peek_skirt)
-
-/mob/living/carbon/human/proc/peek_skirt(mob/examined, mob/examiner, list/examine_content)
- var/mob/living/living = examiner
- var/obj/item/clothing/under/worn_uniform = get_item_by_slot(ITEM_SLOT_ICLOTHING)
- if(worn_uniform && is_type_in_typecache(worn_uniform.type, GLOB.skirt_peekable) && (isobserver(examiner) || (isliving(examiner) && (examiner != src) && !(living.mobility_flags & MOBILITY_STAND) && (mobility_flags & MOBILITY_STAND) && (loc == living.loc) && (istype(worn_uniform)))))
- var/string = "Peeking under [src]'s [worn_uniform.name], you can see "
- var/obj/item/clothing/underwear/worn_underwear = get_item_by_slot(ITEM_SLOT_UNDERWEAR)
- if(worn_underwear)
- string += "a "
- if(!is_type_in_typecache(worn_underwear.type, GLOB.pairless_panties)) //a pair of thong
- string += "pair of "
- if(worn_underwear.color)
- string += "[worn_underwear.name] ."
- else
- string += "[worn_underwear.name]."
-
- var/obj/item/organ/genital/penis/penis = getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/genital/vagina/vagina = getorganslot(ORGAN_SLOT_VAGINA)
- if(penis?.aroused_state)
- string += span_love(" There's a visible bulge on [p_their()] front.")
- else if(vagina?.aroused_state)
- string += span_love(" [p_theyre(TRUE)] wet with arousal.")
-
- else
- string += "[p_theyre()] not wearing anything!\n[p_their(TRUE)]"
- var/list/genitals = list()
- for(var/obj/item/organ/genital/genital in internal_organs)
- if(genital.genital_flags & (GENITAL_INTERNAL|GENITAL_HIDDEN))
- continue
-
- var/appended
- switch(genital.type)
- if(/obj/item/organ/genital/vagina)
- if(genital.aroused_state)
- appended += " wet"
- if(lowertext(genital.shape) != "human")
- appended += " [lowertext(genital.shape)]"
- if(lowertext(genital.shape) != "cloaca") //their wet cloaca vagina
- appended += " [lowertext(genital.name)]" // goodbye pussy
-
- if(/obj/item/organ/genital/testicles)
- var/obj/item/organ/genital/testicles/nuts = genital
- appended += " [lowertext(nuts.size_name)] [lowertext(nuts.name)]"
- if(/obj/item/organ/genital/penis)
- if(genital.aroused_state)
- appended += " fully erect"
- if(lowertext(genital.shape) != "human")
- appended += " [lowertext(genital.shape)]"
- appended += " [lowertext(genital.name)]" // Name it something funny, i dare you.
- if(/obj/item/organ/genital/butt)
- var/obj/item/organ/genital/butt/booty = genital
- appended += " [booty.size_name] [lowertext(booty.name)]" // Maybe " average butt pair" isn't the best for now
- else
- continue
- genitals += appended
-
- string += english_list(genitals, " featureless groin", " and", ",")
- string += " on full display."
-
- examine_content += span_purple(string)
diff --git a/modular_sand/code/modules/mob/living/carbon/human/human.dm b/modular_sand/code/modules/mob/living/carbon/human/human.dm
index 4c4b8e7fc3..5abc5a899b 100644
--- a/modular_sand/code/modules/mob/living/carbon/human/human.dm
+++ b/modular_sand/code/modules/mob/living/carbon/human/human.dm
@@ -1,3 +1,4 @@
/mob/living/carbon/human/ComponentInitialize()
. = ..()
AddElement(/datum/element/mob_holder/micro, "micro")
+ AddElement(/datum/element/skirt_peeking)
diff --git a/modular_sand/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm b/modular_sand/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm
new file mode 100644
index 0000000000..66ba0e63e2
--- /dev/null
+++ b/modular_sand/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm
@@ -0,0 +1,8 @@
+/datum/species/mammal/synthetic/New()
+ . = ..()
+
+ // Define inherent traits to add
+ var/modular_inherent_traits = list(TRAIT_NOTHIRST)
+
+ // Add new traits to list
+ LAZYADD(inherent_traits, modular_inherent_traits)
diff --git a/modular_sand/code/modules/mob/living/carbon/human/species_types/ipc.dm b/modular_sand/code/modules/mob/living/carbon/human/species_types/ipc.dm
new file mode 100644
index 0000000000..aa1ad76fbf
--- /dev/null
+++ b/modular_sand/code/modules/mob/living/carbon/human/species_types/ipc.dm
@@ -0,0 +1,8 @@
+/datum/species/ipc/New()
+ . = ..()
+
+ // Define inherent traits to add
+ var/modular_inherent_traits = list(TRAIT_NOTHIRST)
+
+ // Add new traits to list
+ LAZYADD(inherent_traits, modular_inherent_traits)
diff --git a/modular_sand/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/modular_sand/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 505aaeeeca..a238241b79 100644
--- a/modular_sand/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/modular_sand/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -1,3 +1,31 @@
+#define ESTROUS_CYCLE_LENGTH 32
+#define ESTROUS_CYCLE_OFFSET 11
+
/datum/species/lizard/New()
mutant_bodyparts += list("ears" = "None")
. = ..()
+
+/datum/species/lizard/ashwalker/on_species_gain(mob/living/carbon/human/C, datum/species/old_species)
+ . = ..()
+
+ // Add estrous detect quirk
+ C.add_quirk(/datum/quirk/estrous_detection, SPECIES_TRAIT)
+
+ // Define round ID
+ // Requires a server database to use this
+ var/round_id = text2num(GLOB.round_id) || null
+
+ // Define round mating season value
+ var/round_season = ((round_id + (ESTROUS_CYCLE_OFFSET + 2)) % ESTROUS_CYCLE_LENGTH)
+
+ // Check for mating season
+ // Default to active without variable
+ if((!round_id) || round_season <= 2 && round_season >= 0)
+ // Alert user in chat
+ to_chat(C, span_userlove("It\'s that time again. Your loins lay restless as they await a potential mate."))
+
+ // Add estrous quirk
+ C.add_quirk(/datum/quirk/estrous_active, SPECIES_TRAIT)
+
+#undef ESTROUS_CYCLE_LENGTH
+#undef ESTROUS_CYCLE_OFFSET
diff --git a/modular_sand/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/modular_sand/code/modules/mob/living/carbon/human/species_types/synthliz.dm
new file mode 100644
index 0000000000..6ae0536688
--- /dev/null
+++ b/modular_sand/code/modules/mob/living/carbon/human/species_types/synthliz.dm
@@ -0,0 +1,8 @@
+/datum/species/synthliz/New()
+ . = ..()
+
+ // Define inherent traits to add
+ var/modular_inherent_traits = list(TRAIT_NOTHIRST)
+
+ // Add new traits to list
+ LAZYADD(inherent_traits, modular_inherent_traits)
diff --git a/modular_sand/code/modules/mob/living/silicon/robot/robot.dm b/modular_sand/code/modules/mob/living/silicon/robot/robot.dm
index 1ae172a5c4..184b318d1e 100644
--- a/modular_sand/code/modules/mob/living/silicon/robot/robot.dm
+++ b/modular_sand/code/modules/mob/living/silicon/robot/robot.dm
@@ -18,3 +18,24 @@
/mob/living/silicon/robot/modules/roleplay/binarycheck()
return 0 //Roleplay borgs aren't truly borgs
+
+/// Allows "cyborg" players to change gender at will
+/mob/living/silicon/robot/verb/toggle_gender()
+ set name = "Set Gender"
+ set desc = "Allows you to set your gender."
+
+ if(stat != CONSCIOUS)
+ to_chat(usr, span_warning("You cannot toggle your gender while unconcious!"))
+ return
+
+ var/choice = tgui_alert(usr, "Select Gender.", "Gender", list("Both", "Male", "Female"))
+ switch(choice)
+ if("Both")
+ has_penis = TRUE
+ has_vagina = TRUE
+ if("Male")
+ has_penis = TRUE
+ has_vagina = FALSE
+ if("Female")
+ has_penis = FALSE
+ has_vagina = TRUE
diff --git a/modular_sand/code/modules/mob/mob.dm b/modular_sand/code/modules/mob/mob.dm
index 05b0920a20..7957645e54 100644
--- a/modular_sand/code/modules/mob/mob.dm
+++ b/modular_sand/code/modules/mob/mob.dm
@@ -1,3 +1,22 @@
+// Only Clients should have a panel for them, okay?
+/mob/Login()
+ . = ..()
+ AddComponent(/datum/component/interaction_menu_granter)
+
+/mob/Logout()
+ qdel(GetComponent(/datum/component/interaction_menu_granter))
+ . = ..()
+
+/mob/Initialize(mapload)
+ . = ..()
+ register_context()
+
+/mob/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ . = ..()
+ if(user.GetComponent(/datum/component/interaction_menu_granter))
+ LAZYSET(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB], INTENT_ANY, "Interact with")
+ return CONTEXTUAL_SCREENTIP_SET
+
///Adjust the thirst of a mob
/mob/proc/adjust_thirst(change, max = THIRST_LEVEL_THRESHOLD)
thirst = clamp(thirst + change, 0, max)
diff --git a/modular_sand/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/modular_sand/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index ac51d7e5ec..12189b10ba 100644
--- a/modular_sand/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/modular_sand/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -1,21 +1,3 @@
-//Kinetic accelerator charging meme bugfix
-/obj/item/gun/energy/kinetic_accelerator/
- var/chargetimer = null
-
-/obj/item/gun/energy/kinetic_accelerator/proc/reload()
- if(ismob(loc) || isturf(loc)) //Kinetic accelerators won't charge inside objects. Period.
- cell.give(cell.maxcharge)
- if(!suppressed)
- playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1)
- else
- to_chat(loc, span_warning("[src] silently charges up."))
- update_icon()
- overheat = FALSE
- else //this is a terrible solution, but it ensures that it wont be stuck on dischaged if it fails to reload in an obj
- if(chargetimer)
- deltimer(chargetimer)
- chargetimer = addtimer(CALLBACK(src, .proc/reload), overheat_time * 2, TIMER_STOPPABLE)
-
//BDM pka
/obj/item/gun/energy/kinetic_accelerator/premiumka/bdminer
name = "bloody accelerator"
@@ -568,3 +550,37 @@
/obj/item/borg/upgrade/modkit/aoe/heavy/modify_projectile(obj/item/projectile/kinetic/K)
K.name = "heavy kinetic explosion"
+
+/obj/item/gun/energy/kinetic_accelerator/premiumka
+ unique_reskin = list(
+ "Default" = list(
+ "name" = "premium accelerator",
+ "desc" = "A premium kinetic accelerator fitted with an extended barrel and increased pressure tank.",
+ "icon" = 'icons/obj/guns/energy.dmi',
+ "icon_state" = "premiumgun",
+ "item_state" = "premiumgun",
+ "lefthand_file" = 'icons/mob/inhands/weapons/guns_lefthand.dmi',
+ "righthand_file" = 'icons/mob/inhands/weapons/guns_righthand.dmi'
+ ),
+ "Conscript's tapper" = list(
+ "name" = "Conscript's tapper",
+ "desc" = "Good 'ol kinetic handgun that has been revised to mining and killing tool, works better in pair.", // lies
+ "icon" = 'modular_sand/icons/obj/guns/energy.dmi',
+ "icon_state" = "commando-gun",
+ "item_state" = "commando-gun",
+ "lefthand_file" = 'modular_sand/icons/mob/inhands/weapons/guns_lefthand.dmi',
+ "righthand_file" = 'modular_sand/icons/mob/inhands/weapons/guns_righthand.dmi'
+ )
+ )
+
+/obj/item/gun/energy/kinetic_accelerator/premiumka/reskin_obj(mob/user)
+ . = ..()
+ if(ismob(loc) && current_equipped_slot == ITEM_SLOT_HANDS)
+ var/mob/update_hands = loc
+ update_hands.update_inv_hands()
+
+/obj/item/gun/energy/kinetic_accelerator/premiumka/update_overlays()
+ . = ..()
+ if(current_skin == "Conscript's tapper")
+ if(can_shoot())
+ . += "[icon_state]_cocked"
diff --git a/modular_sand/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_sand/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
new file mode 100644
index 0000000000..903dbbd43c
--- /dev/null
+++ b/modular_sand/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -0,0 +1,28 @@
+// Plushmium object reaction
+/datum/reagent/fermi/plushmium/reaction_obj(obj/O, reac_volume)
+ // Check for Saliith plush
+ if(istype(O, /obj/item/toy/plush/lizardplushie/saliith))
+ // Check if a carbon user exists
+ if((!usr) || (!iscarbon(usr)))
+ // Return without any effects
+ return
+
+ // Warn in local chat
+ O.loc.visible_message(span_warning("[src] is sprayed with a strange chemical, and reacts with overwhelming hostility! [usr] is sprayed with a concoction of horrible chemicals!"))
+
+ // Define user mob
+ var/mob/living/carbon/human/spray_user = usr
+
+ // Add chemicals
+ spray_user.reagents.add_reagent(/datum/reagent/toxin/mutagen, 20)
+ spray_user.reagents.add_reagent(/datum/reagent/toxin/mindbreaker, 20)
+ spray_user.reagents.add_reagent(/datum/reagent/toxin/mutetoxin, 20)
+ //spray_user.reagents.add_reagent(/datum/reagent/toxin/histamine, 30)
+ spray_user.reagents.add_reagent(/datum/reagent/toxin/bonehurtingjuice, 30)
+ spray_user.reagents.add_reagent(/datum/reagent/toxin/brainhurtingjuice, 30)
+
+ // Return without further effects
+ return
+
+ // Return normally
+ . = ..()
diff --git a/modular_sand/code/modules/research/designs/machine_designs.dm b/modular_sand/code/modules/research/designs/machine_designs.dm
index 64cd845088..e5a4887cd1 100644
--- a/modular_sand/code/modules/research/designs/machine_designs.dm
+++ b/modular_sand/code/modules/research/designs/machine_designs.dm
@@ -33,21 +33,21 @@
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
//Cryptocurrency Miners
-// /datum/design/board/cryptominer
-// name = "Machine Design (Cryptocurrency Miner)"
-// desc = "The circuit board for a Cryptocurrency Miner."
-// id = "cryptominer"
-// build_path = /obj/item/circuitboard/machine/cryptominer
-// category = list("Misc. Machinery")
-// departmental_flags = DEPARTMENTAL_FLAG_CARGO
+/datum/design/board/cryptominer
+ name = "Machine Design (Cryptocurrency Miner)"
+ desc = "The circuit board for a Cryptocurrency Miner."
+ id = "cryptominer"
+ build_path = /obj/item/circuitboard/machine/cryptominer
+ category = list("Misc. Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
-// /datum/design/board/cryptominer/syndie
-// name = "Machine Design (Syndicate Cryptocurrency Miner)"
-// desc = "The circuit board for a Syndicate Cryptocurrency Miner."
-// id = "cryptominersyndie"
-// build_path = /obj/item/circuitboard/machine/cryptominer/syndie
-// category = list("Misc. Machinery")
-// departmental_flags = DEPARTMENTAL_FLAG_CARGO
+/datum/design/board/cryptominer/syndie
+ name = "Machine Design (Syndicate Cryptocurrency Miner)"
+ desc = "The circuit board for a Syndicate Cryptocurrency Miner."
+ id = "cryptominersyndie"
+ build_path = /obj/item/circuitboard/machine/cryptominer/syndie
+ category = list("Misc. Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
//BS miner
/datum/design/board/bluespace_miner
diff --git a/modular_sand/code/modules/research/techweb/nodes/bluespace_nodes.dm b/modular_sand/code/modules/research/techweb/nodes/bluespace_nodes.dm
index 59abd0c034..e341a5c1ab 100644
--- a/modular_sand/code/modules/research/techweb/nodes/bluespace_nodes.dm
+++ b/modular_sand/code/modules/research/techweb/nodes/bluespace_nodes.dm
@@ -1,18 +1,18 @@
-// /datum/techweb_node/cryptominer
-// id = "cryptominer"
-// display_name = "Cryptocurrency Mining"
-// description = "Harness the power of cryptocurrency to make credits for Cargo-- slowly."
-// prereq_ids = list("bluespace_mining")
-// design_ids = list("cryptominer")
-// research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
+/datum/techweb_node/cryptominer
+ id = "cryptominer"
+ display_name = "Cryptocurrency Mining"
+ description = "Harness the power of cryptocurrency to make credits for Cargo-- slowly."
+ prereq_ids = list("bluespace_mining")
+ design_ids = list("cryptominer")
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
-// /datum/techweb_node/cryptominersyndie
-// id = "cryptominersyndie"
-// display_name = "Illegal Cryptocurrency Mining"
-// description = "Harness the power of bluespace to make credits for Cargo-- slowly."
-// prereq_ids = list("cryptominer","syndicate_basic")
-// design_ids = list("cryptominersyndie")
-// research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
+/datum/techweb_node/cryptominersyndie
+ id = "cryptominersyndie"
+ display_name = "Illegal Cryptocurrency Mining"
+ description = "Harness the power of bluespace to make credits for Cargo-- slowly."
+ prereq_ids = list("cryptominer","syndicate_basic")
+ design_ids = list("cryptominersyndie")
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
/datum/techweb_node/computermath
id = "computermath"
diff --git a/modular_sand/code/modules/resize/resizing.dm b/modular_sand/code/modules/resize/resizing.dm
index 9d0302561d..913fcbb294 100644
--- a/modular_sand/code/modules/resize/resizing.dm
+++ b/modular_sand/code/modules/resize/resizing.dm
@@ -19,12 +19,12 @@
return TRUE
//Doing messages
- if(abs(get_size(user)/get_size(target) >= 2)) //if the initiator is twice the size of the micro
+ if(COMPARE_SIZES(user, target) >= 2) //if the initiator is twice the size of the micro
now_pushing = 0
user.forceMove(target.loc)
//Smaller person being stepped on
- if(get_size(user) > get_size(target) && iscarbon(src))
+ if(iscarbon(src))
if(istype(user) && user.dna.features["taur"] == "Naga" || user.dna.features["taur"] == "Tentacle")
target.visible_message(span_notice("[src] carefully slithers around [target]."), span_notice("[src]'s huge tail slithers besides you."))
else
@@ -32,7 +32,7 @@
return TRUE
//Smaller person stepping under a larger person
- if(abs(get_size(target)/get_size(user)) >= 2)
+ if(COMPARE_SIZES(target, user) >= 2)
user.forceMove(target.loc)
now_pushing = 0
micro_step_under(target)
@@ -59,7 +59,7 @@
user.forceMove(target.loc)
return TRUE
- if(abs(get_size(user)/get_size(target)) >= 2)
+ if(COMPARE_SIZES(user, target) >= 2)
log_combat(user, target, "stepped on", addition="[user.a_intent] trample")
if(user.a_intent == "disarm" && CHECK_MOBILITY(user, MOBILITY_MOVE) && !user.buckled)
now_pushing = 0
@@ -67,7 +67,7 @@
user.sizediffStamLoss(target)
user.add_movespeed_modifier(/datum/movespeed_modifier/stomp, TRUE) //Full stop
addtimer(CALLBACK(user, /mob/.proc/remove_movespeed_modifier, MOVESPEED_ID_STOMP, TRUE), 3) //0.3 seconds
- if(get_size(user) > get_size(target) && iscarbon(user))
+ if(iscarbon(user))
if(istype(user) && user.dna.features["taur"] == "Naga" || user.dna.features["taur"] == "Tentacle")
target.visible_message(span_danger("[src] carefully rolls their tail over [target]!"), span_danger("[src]'s huge tail rolls over you!"))
else
@@ -83,7 +83,7 @@
user.add_movespeed_modifier(/datum/movespeed_modifier/stomp, TRUE)
addtimer(CALLBACK(user, /mob/.proc/remove_movespeed_modifier, MOVESPEED_ID_STOMP, TRUE), 10) //1 second
//user.Stun(20)
- if(get_size(user) > get_size(target) && iscarbon(user))
+ if(iscarbon(user))
if(istype(user) && (user.dna.features["taur"] == "Naga" || user.dna.features["taur"] == "Tentacle"))
target.visible_message(span_danger("[src] mows down [target] under their tail!"), span_userdanger("[src] plows their tail over you mercilessly!"))
else
@@ -97,7 +97,7 @@
user.sizediffStun(target)
user.add_movespeed_modifier(/datum/movespeed_modifier/stomp, TRUE)
addtimer(CALLBACK(user, /mob/.proc/remove_movespeed_modifier, MOVESPEED_ID_STOMP, TRUE), 7)//About 3/4th a second
- if(get_size(user) > get_size(target) && iscarbon(user))
+ if(iscarbon(user))
var/feetCover = (user.wear_suit && (user.wear_suit.body_parts_covered & FEET)) || (user.w_uniform && (user.w_uniform.body_parts_covered & FEET) || (user.shoes && (user.shoes.body_parts_covered & FEET)))
if(feetCover)
if(user?.dna?.features["taur"] == "Naga" || user?.dna?.features["taur"] == "Tentacle")
@@ -116,7 +116,7 @@
SEND_SIGNAL(target, COMSIG_MICRO_PICKUP_FEET, user)
return TRUE
- if(abs(get_size(target)/get_size(user)) >= 2)
+ if(COMPARE_SIZES(target, user) >= 2)
user.forceMove(target.loc)
now_pushing = 0
micro_step_under(target)
@@ -141,17 +141,17 @@
//Proc for scaling stamina damage on size difference
/mob/living/carbon/proc/sizediffStamLoss(mob/living/carbon/target)
- var/S = (get_size(src)/get_size(target)*25) //macro divided by micro, times 25
+ var/S = COMPARE_SIZES(src, target) * 25 //macro divided by micro, times 25
target.Knockdown(S) //final result in stamina knockdown
//Proc for scaling stuns on size difference (for grab intent)
/mob/living/carbon/proc/sizediffStun(mob/living/carbon/target)
- var/T = (get_size(src)/get_size(target)*2) //Macro divided by micro, times 2
+ var/T = COMPARE_SIZES(src, target) * 2 //Macro divided by micro, times 2
target.Stun(T)
//Proc for scaling brute damage on size difference
/mob/living/carbon/proc/sizediffBruteloss(mob/living/carbon/target)
- var/B = (get_size(src)/get_size(target)*3) //macro divided by micro, times 3
+ var/B = COMPARE_SIZES(src, target) * 3 //macro divided by micro, times 3
target.adjustBruteLoss(B) //final result in brute loss
//Proc for instantly grabbing valid size difference. Code optimizations soon(TM)
diff --git a/modular_sand/icons/UI_Icons/inventory/socks.png b/modular_sand/icons/UI_Icons/inventory/socks.png
index 9a5be04056..51ce9251de 100644
Binary files a/modular_sand/icons/UI_Icons/inventory/socks.png and b/modular_sand/icons/UI_Icons/inventory/socks.png differ
diff --git a/modular_sand/icons/UI_Icons/inventory/undershirt.png b/modular_sand/icons/UI_Icons/inventory/undershirt.png
index 3f581ec749..4ff410e90b 100644
Binary files a/modular_sand/icons/UI_Icons/inventory/undershirt.png and b/modular_sand/icons/UI_Icons/inventory/undershirt.png differ
diff --git a/modular_sand/icons/UI_Icons/inventory/underwear.png b/modular_sand/icons/UI_Icons/inventory/underwear.png
index b03245044c..1862bee7ca 100644
Binary files a/modular_sand/icons/UI_Icons/inventory/underwear.png and b/modular_sand/icons/UI_Icons/inventory/underwear.png differ
diff --git a/modular_sand/icons/mob/clothing/head.dmi b/modular_sand/icons/mob/clothing/head.dmi
index e4e8b762c5..b0b9a34673 100644
Binary files a/modular_sand/icons/mob/clothing/head.dmi and b/modular_sand/icons/mob/clothing/head.dmi differ
diff --git a/modular_sand/icons/mob/clothing/head_muzzled.dmi b/modular_sand/icons/mob/clothing/head_muzzled.dmi
index 168b605f70..19eb0eaf85 100644
Binary files a/modular_sand/icons/mob/clothing/head_muzzled.dmi and b/modular_sand/icons/mob/clothing/head_muzzled.dmi differ
diff --git a/modular_sand/icons/mob/clothing/suit.dmi b/modular_sand/icons/mob/clothing/suit.dmi
index cf40ce721c..0cfaf655f8 100644
Binary files a/modular_sand/icons/mob/clothing/suit.dmi and b/modular_sand/icons/mob/clothing/suit.dmi differ
diff --git a/modular_sand/icons/mob/inhands/weapons/guns_lefthand.dmi b/modular_sand/icons/mob/inhands/weapons/guns_lefthand.dmi
index 8871ec5afd..1b9839a127 100644
Binary files a/modular_sand/icons/mob/inhands/weapons/guns_lefthand.dmi and b/modular_sand/icons/mob/inhands/weapons/guns_lefthand.dmi differ
diff --git a/modular_sand/icons/mob/inhands/weapons/guns_righthand.dmi b/modular_sand/icons/mob/inhands/weapons/guns_righthand.dmi
index a07597f370..ac450ce359 100644
Binary files a/modular_sand/icons/mob/inhands/weapons/guns_righthand.dmi and b/modular_sand/icons/mob/inhands/weapons/guns_righthand.dmi differ
diff --git a/modular_sand/icons/mob/screen_clockwork.dmi b/modular_sand/icons/mob/screen_clockwork.dmi
index aa72185aeb..eefd18fdc5 100644
Binary files a/modular_sand/icons/mob/screen_clockwork.dmi and b/modular_sand/icons/mob/screen_clockwork.dmi differ
diff --git a/modular_sand/icons/mob/screen_liteweb.dmi b/modular_sand/icons/mob/screen_liteweb.dmi
index 15ad6f42c6..80223eaa84 100644
Binary files a/modular_sand/icons/mob/screen_liteweb.dmi and b/modular_sand/icons/mob/screen_liteweb.dmi differ
diff --git a/modular_sand/icons/mob/screen_midnight.dmi b/modular_sand/icons/mob/screen_midnight.dmi
index 1b991bb960..9366925181 100644
Binary files a/modular_sand/icons/mob/screen_midnight.dmi and b/modular_sand/icons/mob/screen_midnight.dmi differ
diff --git a/modular_sand/icons/mob/screen_operative.dmi b/modular_sand/icons/mob/screen_operative.dmi
index 70824ea898..a435e1bece 100644
Binary files a/modular_sand/icons/mob/screen_operative.dmi and b/modular_sand/icons/mob/screen_operative.dmi differ
diff --git a/modular_sand/icons/mob/screen_plasmafire.dmi b/modular_sand/icons/mob/screen_plasmafire.dmi
index 3a07a34232..61d501dbe2 100644
Binary files a/modular_sand/icons/mob/screen_plasmafire.dmi and b/modular_sand/icons/mob/screen_plasmafire.dmi differ
diff --git a/modular_sand/icons/mob/screen_retro.dmi b/modular_sand/icons/mob/screen_retro.dmi
index ba20a9e8bd..a25def4d11 100644
Binary files a/modular_sand/icons/mob/screen_retro.dmi and b/modular_sand/icons/mob/screen_retro.dmi differ
diff --git a/modular_sand/icons/mob/screen_slimecore.dmi b/modular_sand/icons/mob/screen_slimecore.dmi
index a4854f16c3..1b51613f97 100644
Binary files a/modular_sand/icons/mob/screen_slimecore.dmi and b/modular_sand/icons/mob/screen_slimecore.dmi differ
diff --git a/modular_sand/icons/obj/clothing/hats.dmi b/modular_sand/icons/obj/clothing/hats.dmi
index e393feeb57..b269f8d166 100644
Binary files a/modular_sand/icons/obj/clothing/hats.dmi and b/modular_sand/icons/obj/clothing/hats.dmi differ
diff --git a/modular_sand/icons/obj/clothing/suits.dmi b/modular_sand/icons/obj/clothing/suits.dmi
index f2b9bedc0e..05ba6f3f04 100644
Binary files a/modular_sand/icons/obj/clothing/suits.dmi and b/modular_sand/icons/obj/clothing/suits.dmi differ
diff --git a/modular_sand/icons/obj/contraband.dmi b/modular_sand/icons/obj/contraband.dmi
index 37d26caed6..81f26f8c34 100644
Binary files a/modular_sand/icons/obj/contraband.dmi and b/modular_sand/icons/obj/contraband.dmi differ
diff --git a/modular_sand/icons/obj/device.dmi b/modular_sand/icons/obj/device.dmi
index 555e379fb6..28b1b5503e 100644
Binary files a/modular_sand/icons/obj/device.dmi and b/modular_sand/icons/obj/device.dmi differ
diff --git a/modular_sand/icons/obj/guns/energy.dmi b/modular_sand/icons/obj/guns/energy.dmi
index 18cb252bd8..5b5e9867cf 100644
Binary files a/modular_sand/icons/obj/guns/energy.dmi and b/modular_sand/icons/obj/guns/energy.dmi differ
diff --git a/modular_sand/icons/obj/plushes.dmi b/modular_sand/icons/obj/plushes.dmi
index 55aeacf397..71bd3885c4 100644
Binary files a/modular_sand/icons/obj/plushes.dmi and b/modular_sand/icons/obj/plushes.dmi differ
diff --git a/modular_splurt/code/_globalvars/lists/mobs.dm b/modular_splurt/code/_globalvars/lists/mobs.dm
index 92a3c83791..22062af329 100644
--- a/modular_splurt/code/_globalvars/lists/mobs.dm
+++ b/modular_splurt/code/_globalvars/lists/mobs.dm
@@ -1 +1,2 @@
GLOBAL_LIST_EMPTY(discord_passthrough)
+GLOBAL_LIST_EMPTY(ssd_mob_list) // Player mobs that are SSD
diff --git a/modular_splurt/code/controllers/configuration/entries/splurt_autocryo.dm b/modular_splurt/code/controllers/configuration/entries/splurt_autocryo.dm
new file mode 100644
index 0000000000..c94e2e3d98
--- /dev/null
+++ b/modular_splurt/code/controllers/configuration/entries/splurt_autocryo.dm
@@ -0,0 +1,8 @@
+// Time before sending the user to cryo
+/datum/config_entry/number/autocryo_time_trigger
+ config_entry_value = 24000
+ min_val = 600
+ integer = TRUE
+
+// Should this system be used?
+/datum/config_entry/flag/autocryo_enabled
diff --git a/modular_splurt/code/controllers/configuration/entries/splurt_general.dm b/modular_splurt/code/controllers/configuration/entries/splurt_general.dm
index 8c79ccfe15..cf0b983434 100644
--- a/modular_splurt/code/controllers/configuration/entries/splurt_general.dm
+++ b/modular_splurt/code/controllers/configuration/entries/splurt_general.dm
@@ -4,3 +4,6 @@
config_entry_value = 5
/datum/config_entry/flag/weighted_station_traits
+
+/datum/config_entry/number/base_save_slots
+ config_entry_value = DEFAULT_SAVE_SLOTS
diff --git a/modular_splurt/code/controllers/subsystem/afk.dm b/modular_splurt/code/controllers/subsystem/afk.dm
new file mode 100644
index 0000000000..70e213c269
--- /dev/null
+++ b/modular_splurt/code/controllers/subsystem/afk.dm
@@ -0,0 +1,43 @@
+// Define config entries for cryo
+#define SUBSYSTEM_CRYO_CAN_RUN CONFIG_GET(flag/autocryo_enabled)
+#define SUBSYSTEM_CRYO_TIME CONFIG_GET(number/autocryo_time_trigger)
+
+SUBSYSTEM_DEF(auto_cryo)
+ name = "Automated Cryogenics"
+ flags = SS_BACKGROUND
+
+/datum/controller/subsystem/auto_cryo/Initialize()
+ // Check config before running
+ if(!SUBSYSTEM_CRYO_CAN_RUN)
+ can_fire = FALSE
+
+ return ..()
+
+/datum/controller/subsystem/auto_cryo/fire()
+ // Check for any targets
+ if(!GLOB.ssd_mob_list.len)
+ // No SSD mobs exist
+ return
+
+ // Check possible targets
+ for(var/mob/living/cryo_mob in GLOB.ssd_mob_list)
+ // Get SSD time
+ // This is set when disconnecting
+ var/afk_time = world.time - cryo_mob.lastclienttime
+
+ // Check if client meets the time requirement
+ if(afk_time < SUBSYSTEM_CRYO_TIME)
+ continue
+
+ // Send to cryo
+ cryoMob(cryo_mob, effects = TRUE)
+
+ // Remove from SSD list
+ GLOB.ssd_mob_list -= cryo_mob
+
+ // Log cryo interaction
+ log_game("[cryo_mob] was sent to cryo after being SSD for [afk_time] ticks.")
+
+// Remove defines
+#undef SUBSYSTEM_CRYO_CAN_RUN
+#undef SUBSYSTEM_CRYO_TIME
diff --git a/modular_splurt/code/datums/components/dullahan.dm b/modular_splurt/code/datums/components/dullahan.dm
index a8558a5835..6fae1b981d 100644
--- a/modular_splurt/code/datums/components/dullahan.dm
+++ b/modular_splurt/code/datums/components/dullahan.dm
@@ -1,3 +1,15 @@
+#define HEAD_ACCESSORIES_PATHS list(\
+ /obj/item/clothing/head = 'icons/mob/clothing/head.dmi',\
+ /obj/item/clothing/glasses = 'icons/mob/clothing/eyes.dmi',\
+ /obj/item/reagent_containers/glass/bucket = 'icons/mob/clothing/head.dmi',\
+ /obj/item/reagent_containers/rag/towel = 'icons/mob/clothing/head.dmi',\
+ /obj/item/paper = 'icons/mob/clothing/head.dmi',\
+ /obj/item/nullrod/fedora = 'icons/mob/clothing/head.dmi',\
+ )
+
+/obj/item/organ/eyes/dullahan
+ tint = 0
+
/datum/component/neckfire
var/mutable_appearance/neck_fire
@@ -30,7 +42,7 @@
neck_fire.icon_state = "neckfire"
neck_fire.color = fire_color
- neck_fire.plane = 19 // glowy i hope
+ //neck_fire.plane = 19 // glowy i hope
var/datum/action/neckfire/A = new /datum/action/neckfire(src)
A.Grant(M)
@@ -72,3 +84,84 @@
var/mob/living/carbon/human/H = quirk_holder
if(H.dna.features["neckfire"] && !istype(H, /mob/living/carbon/human/dummy))
H.AddComponent(/datum/component/neckfire, H.dna.features["neckfire_color"])
+
+/datum/component/dullahan
+ var/obj/item/clothing/head_accessory
+ var/mutable_appearance/head_accessory_MA
+
+/datum/component/dullahan/Initialize()
+ . = ..()
+ RegisterSignal(dullahan_head, COMSIG_MOUSEDROPPED_ONTO, .proc/on_mouse_dropped)
+ RegisterSignal(dullahan_head, COMSIG_MOUSEDROP_ONTO, .proc/on_mouse_drop)
+
+/datum/component/dullahan/proc/add_head_accessory(obj/item/clothing/I, item_path)
+ head_accessory_MA = mutable_appearance(I.mob_overlay_icon || HEAD_ACCESSORIES_PATHS[item_path])
+ head_accessory_MA.icon_state = I.icon_state
+ I.forceMove(dullahan_head)
+
+ dullahan_head.add_overlay(head_accessory_MA)
+ head_accessory = I
+
+/datum/component/dullahan/proc/remove_head_accessory(obj/item/clothing/I)
+ dullahan_head.cut_overlay(head_accessory_MA)
+ head_accessory = null
+
+/datum/component/dullahan/proc/on_mouse_dropped(datum/source, obj/item/I, mob/living/user)
+ var/mob/living/carbon/owner = dullahan_head.owner
+ var/item_path
+
+ for(var/type in HEAD_ACCESSORIES_PATHS)
+ if(istype(I, type))
+ item_path = type
+ break
+
+ if(item_path && !head_accessory)
+ if(istype(I, /obj/item/clothing))
+ var/obj/item/organ/eyes/dullahan/eyes = owner.getorganslot(ORGAN_SLOT_EYES)
+ var/obj/item/clothing/clothing = I
+
+ eyes.flash_protect = clothing.flash_protect
+ eyes.tint = clothing.tint
+ owner.update_tint()
+
+ add_head_accessory(I, item_path)
+ else
+ to_chat(user, span_notice("You can't put \the [I.name] on the head of \the [owner.name]"))
+ return
+
+/datum/component/dullahan/proc/on_mouse_drop(datum/source, atom/A, mob/living/user)
+ var/mob/living/carbon/owner = dullahan_head.owner
+
+ if(head_accessory)
+ if(istype(A, /turf/open))
+ head_accessory.forceMove(A)
+ else if(istype(A, /atom/movable/screen/inventory/hand))
+ var/atom/movable/screen/inventory/hand/H = A
+
+ user.put_in_hand(head_accessory, H.held_index)
+ else
+ return
+
+ var/obj/item/organ/eyes/dullahan/eyes = owner.getorganslot(ORGAN_SLOT_EYES)
+ eyes.flash_protect = 0
+ eyes.tint = 0
+ owner.update_tint()
+
+ remove_head_accessory(head_accessory)
+
+/datum/action/item_action/organ_action/dullahan/proc/toggle_monochromacy()
+ var/obj/item/organ/eyes/eyes = owner.getorganslot(ORGAN_SLOT_EYES)
+
+ if(eyes.monochromacy_on)
+ owner.remove_client_colour(/datum/client_colour/monochrome)
+ else
+ owner.add_client_colour(/datum/client_colour/monochrome)
+
+ eyes.monochromacy_on = !eyes.monochromacy_on
+
+/obj/item/organ/eyes
+ var/monochromacy_on = FALSE
+
+/datum/action/item_action/organ_action/dullahan/Grant(mob/M)
+ . = ..()
+ toggle_monochromacy()
diff --git a/modular_splurt/code/datums/elements/mob_holder.dm b/modular_splurt/code/datums/elements/mob_holder.dm
new file mode 100644
index 0000000000..ff64372b70
--- /dev/null
+++ b/modular_splurt/code/datums/elements/mob_holder.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/head/mob_holder
+ is_edible = -1
diff --git a/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm b/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
index ca6fddf6e8..896cc1c05f 100644
--- a/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
+++ b/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
@@ -469,6 +469,20 @@
if(gut)
gut.modify_size(-1)
+/datum/interaction/lewd/inflate_belly
+ description = "Inflate belly"
+ require_user_belly = REQUIRE_EXPOSED
+ interaction_sound = null
+ max_distance = 0
+ user_is_target = TRUE
+ write_log_user = "inflated their belly"
+ write_log_target = null
+
+/datum/interaction/lewd/inflate_belly/display_interaction(mob/living/carbon/user)
+ var/obj/item/organ/genital/belly/gut = user.getorganslot(ORGAN_SLOT_BELLY)
+ if(gut)
+ gut.modify_size(1)
+
/datum/interaction/lewd/nuzzle_belly
description = "Nuzzle their belly."
require_target_belly = REQUIRE_EXPOSED
diff --git a/modular_splurt/code/datums/mood_events/drug_events.dm b/modular_splurt/code/datums/mood_events/drug_events.dm
index 3cbe25cf67..906326aab2 100644
--- a/modular_splurt/code/datums/mood_events/drug_events.dm
+++ b/modular_splurt/code/datums/mood_events/drug_events.dm
@@ -3,6 +3,6 @@
description = span_nicegreen("I feel like I\'m finally coping!")
/datum/mood_event/moth_in_chief
- description = span_nicegreen("The mantle rests well upon your shoulders?")
+ description = span_nicegreen("The mantle rests well upon your shoulders?\n")
mood_change = 10
timeout = 5 MINUTES
diff --git a/modular_splurt/code/datums/mood_events/generic_negative_events.dm b/modular_splurt/code/datums/mood_events/generic_negative_events.dm
index 204bc9c5cd..7d63afe9f0 100644
--- a/modular_splurt/code/datums/mood_events/generic_negative_events.dm
+++ b/modular_splurt/code/datums/mood_events/generic_negative_events.dm
@@ -1,5 +1,5 @@
/datum/mood_event/masked_mook_incomplete
- description = span_warning("I feel incomplete without a gas mask...")
+ description = span_warning("I feel incomplete without a gas mask...\n")
mood_change = -4
/datum/mood_event/creampie/cheesed
@@ -12,7 +12,7 @@
if(!ishuman(actual_owner))
return .
if(iscatperson(actual_owner))
- description = span_warning("CHEESE!!! WAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!! ")
+ description = span_warning("CHEESE!!! WAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!! \n")
mood_change = -5
timeout = 5 MINUTES
@@ -23,3 +23,14 @@
/datum/mood_event/dorsualiphobic_mood_negative
description = span_warning("I can't let anyone find out if I'm wearing a backpack or not!\n")
mood_change = -4
+
+// Matches drinking synth blood (drankblood_synth)
+/datum/mood_event/drankblood_slime
+ description = span_boldwarning("I drank liquid slime. What is wrong with me?\n")
+ mood_change = -7
+ timeout = 15 MINUTES
+
+/datum/mood_event/drank_cursed_bad
+ description = span_warning("I can feel a pale curse from the blood I drank.\n")
+ mood_change = -1
+ timeout = 2 MINUTES
diff --git a/modular_splurt/code/datums/mood_events/generic_positive_events.dm b/modular_splurt/code/datums/mood_events/generic_positive_events.dm
index 0696042bd2..161be9cd46 100644
--- a/modular_splurt/code/datums/mood_events/generic_positive_events.dm
+++ b/modular_splurt/code/datums/mood_events/generic_positive_events.dm
@@ -1,22 +1,35 @@
/datum/mood_event/lewd_headpat
- description = span_nicegreen("I love headpats so much!")
+ description = span_nicegreen("I love headpats so much!\n")
mood_change = 3
timeout = 2 MINUTES
/datum/mood_event/qareen_bliss
- description = span_umbra("So.. horny...")
+ description = span_umbra("So.. horny...\n")
mood_change = 5
/datum/mood_event/qareen_bliss/add_effects()
- description = span_umbra("Must.. breed. , [pick("Nngggghh", "Can't.. think.", "It feels so good.", "Need.. fuck.")]...")
+ description = span_umbra("Must.. breed. , [pick("Nngggghh", "Can't.. think.", "It feels so good.", "Need.. fuck.")]...\n")
/datum/mood_event/masked_mook
- description = span_nicegreen("I feel more complete with gas mask on.")
+ description = span_nicegreen("I feel more complete with gas mask on.\n")
mood_change = 1
+/datum/mood_event/cloth_eaten
+ description = " That sure was a tasty outfit!\n"
+ mood_change = 3
+ timeout = 2400
+
+/datum/mood_event/cloth_eaten/add_effects(obj/item/clothing/eaten)
+ description = "That sure was a [pick("tasty","good","linty","amazing")] [eaten.name]!\n"
+
/datum/mood_event/nudist_positive
- description = span_nicegreen("I'm delighted to not be constricted by clothing.")
+ description = span_nicegreen("I'm delighted to not be constricted by clothing.\n")
mood_change = 1
/datum/mood_event/dorsualiphobic_mood_positive
- description = span_nicegreen("Nobody will know if I'm wearing a backpack or not.")
+ description = span_nicegreen("Nobody will know if I'm wearing a backpack or not.\n")
mood_change = 1
+
+/datum/mood_event/drank_cursed_good
+ description = span_nicegreen("I\'ve tasted sympathy from a fellow curse bearer.\n")
+ mood_change = 1
+ timeout = 2 MINUTES
diff --git a/modular_splurt/code/datums/mood_events/needs_events.dm b/modular_splurt/code/datums/mood_events/needs_events.dm
index 6b92548f72..046a796a19 100644
--- a/modular_splurt/code/datums/mood_events/needs_events.dm
+++ b/modular_splurt/code/datums/mood_events/needs_events.dm
@@ -3,14 +3,14 @@
var/mob/living/carbon/human/actual_owner = owner_mob()
if(!HAS_TRAIT(actual_owner, TRAIT_VORACIOUS))
return
- description = span_nicegreen("MORE FOOD!!! MORE FOOD!!! MORE FOOD!!!")
+ description = span_nicegreen("MORE FOOD!!! MORE FOOD!!! MORE FOOD!!!\n")
mood_change = 8
/datum/mood_event/cum_craving
- description = "I... NEED... CUM...\n"
+ description = span_warning("I... NEED... CUM...\n")
mood_change = -20
/datum/mood_event/cum_stuffed
- description = span_nicegreen("It feels so good inside me!")
+ description = span_nicegreen("It feels so good inside me!\n")
mood_change = 8
timeout = 5 MINUTES
diff --git a/modular_splurt/code/datums/mood_events/preg_events.dm b/modular_splurt/code/datums/mood_events/preg_events.dm
index b5ccd1444f..628d10b089 100644
--- a/modular_splurt/code/datums/mood_events/preg_events.dm
+++ b/modular_splurt/code/datums/mood_events/preg_events.dm
@@ -1,8 +1,8 @@
/datum/mood_event/pregnant_negative
- description = span_boldwarning("THE BABY IS COMING OUT...")
+ description = span_boldwarning("THE BABY IS COMING OUT...\n")
mood_change = -7
/datum/mood_event/pregnant_positive
- description = span_nicegreen("The baby came out...phew")
+ description = span_nicegreen("The baby came out...phew\n")
mood_change = 3
timeout = 2 MINUTES
diff --git a/modular_splurt/code/datums/traits/good.dm b/modular_splurt/code/datums/traits/good.dm
index b305c79997..70a84f4d82 100644
--- a/modular_splurt/code/datums/traits/good.dm
+++ b/modular_splurt/code/datums/traits/good.dm
@@ -1,3 +1,18 @@
+//Main code edits
+/datum/quirk/photographer
+ desc = "You carry your camera and personal photo album everywhere you go, and you're quicker at taking pictures."
+
+/datum/quirk/photographer/on_spawn()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/storage/photo_album/photo_album = new(get_turf(H))
+ H.put_in_hands(photo_album)
+ H.equip_to_slot(photo_album, ITEM_SLOT_BACKPACK)
+ photo_album.persistence_id = "personal_[H.mind.key]" // this is a persistent album, the ID is tied to the account's key to avoid tampering
+ photo_album.persistence_load()
+ photo_album.name = "[H.real_name]'s photo album"
+
+//Own stuff
/datum/quirk/tough
name = "Tough"
desc = "Your body is abnormally enduring and can take 10% more damage."
@@ -143,3 +158,10 @@
desc = "You are able to move about freely in pressurized low-gravity environments be it through the use of wings, magic, or some other physiological nonsense."
value = 1
mob_trait = TRAIT_FLUTTER
+
+/datum/quirk/cloth_eater
+ name = "Clothes Eater"
+ desc = "You can eat most apparel to gain a boost in mood, and to gain some nutrients. (Insects already have this.)"
+ value = 1
+ var/mood_category ="cloth_eaten"
+ mob_trait = TRAIT_CLOTH_EATER
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
index 04a1c1825d..a000de17ab 100644
--- a/modular_splurt/code/datums/traits/negative.dm
+++ b/modular_splurt/code/datums/traits/negative.dm
@@ -66,13 +66,6 @@
speech_args[SPEECH_MESSAGE] = message
//Own stuff
-/datum/quirk/no_clone
- name = "DNC"
- desc = "You have filed a Do Not Clone order, stating that you do not wish to be cloned. You can still be revived by other means."
- value = -2
- mob_trait = TRAIT_NO_CLONE
- medical_record_text = "Patient has a DNC (Do Not Clone) order and will be rejected by cloning mechanisms as a result."
-
/datum/quirk/no_guns
name = "Fat-Fingered"
desc = "Due to the shape of your hands, width of your fingers or just not having fingers at all, you're unable to fire guns without accommodation."
diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm
index ef6552c70c..5fdca5e6f5 100644
--- a/modular_splurt/code/datums/traits/neutral.dm
+++ b/modular_splurt/code/datums/traits/neutral.dm
@@ -61,14 +61,6 @@
. = ..()
quirk_holder.RemoveElement(/datum/element/wuv/headpat)
-/datum/quirk/in_heat
- name = "In Heat"
- desc = "Your system burns with the desire to be bred. Satisfying your lust will make you happy, but ignoring it may cause you to become sad and needy."
- value = 0
- mob_trait = TRAIT_IN_HEAT
- gain_text = span_notice("You body burns with the desire to be bred.")
- lose_text = span_notice("You feel more in control of your body and thoughts.")
-
/datum/quirk/Hypnotic_gaze
name = "Hypnotic Gaze"
desc = "Be it through mysterious patterns, flickering colors, or some genetic oddity, prolonged eye contact with you will place the viewer into a highly-suggestible hypnotic trance."
@@ -84,14 +76,6 @@
spell.Grant(Hypno_eyes)
spell.owner = Hypno_eyes
-/datum/quirk/heat
- name = "Estrus Detection"
- desc = "You have a animalistic sense of detecting if someone is in heat."
- value = 0
- mob_trait = TRAIT_HEAT_DETECT
- gain_text = span_notice("You feel your senses adjust, allowing a animalistic sense of others' fertility.")
- lose_text = span_notice("You feel your sense of others' fertility fade.")
-
/datum/quirk/overweight
name = "Overweight"
desc = "You're particularly fond of food, and join the shift being overweight."
@@ -365,105 +349,204 @@
var/mob/living/carbon/human/H = quirk_holder
H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
-/datum/quirk/vampire//splurt change start
- name = "Bloodsucker Fledgeling"
- desc = "You are a fledgeling of an ancient Bloodsucker bloodline; your skin is incurably pale and your mouth glimmers with vampiric fangs. Only blood will sate your hungers, and holy energies will cause your flesh to char."
- value = 0
- medical_record_text = "this person was partially infected by a bloodsucker"
- mob_trait = BLOODFLEDGE
- gain_text = span_notice("You feel an otherworldly thirst.")
- lose_text = span_notice("you feel an otherworldy burden remove itself")
+/datum/quirk/bloodfledge
+ name = "Bloodsucker Fledgling"
+ desc = "You are a fledgling belonging to ancient Bloodsucker bloodline. While the blessing has yet to fully convert you, some things have changed. Only blood will sate your hungers, and holy energies will cause your flesh to char. This is NOT an antagonist role! "
+ value = 2
+ medical_record_text = "Patient exhibits onset symptoms of a sanguine curse."
+ mob_trait = TRAIT_BLOODFLEDGE
+ gain_text = span_notice("You feel a sanguine thirst.")
+ lose_text = span_notice("You feel the sanguine thirst fade away.")
processing_quirk = TRUE
-/datum/quirk/vampire/add()
+/datum/quirk/bloodfledge/add()
. = ..()
- var/mob/living/carbon/human/H = quirk_holder
- ADD_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_COLDBLOODED,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_NOBREATH,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_QUICKER_CARRY,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_AUTO_CATCH_ITEM,ROUNDSTART_TRAIT)//these two make the vampire fast and enables some sexy "bet you didnt think i could do this" romance
- if(!H.dna.skin_tone_override)
- H.skin_tone = "albino"
- var/datum/action/vbite/B = new
- var/datum/action/vrevive/R = new
- B.Grant(H)
- R.Grant(H)
- H.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
-/datum/quirk/vampire/on_process()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk traits
+ ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+ // Set skin tone, if possible
+ if(!quirk_mob.dna.skin_tone_override)
+ quirk_mob.skin_tone = "albino"
+
+ // Add quirk ability action datums
+ var/datum/action/bloodfledge/bite/act_bite = new
+ var/datum/action/bloodfledge/revive/act_revive = new
+ act_bite.Grant(quirk_mob)
+ act_revive.Grant(quirk_mob)
+
+ // Add quirk language
+ quirk_mob.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
+
+/datum/quirk/bloodfledge/on_process()
. = ..()
- var/mob/living/carbon/human/H = quirk_holder
- var/area/A = get_area(H)
- if(istype(A, /area/service/chapel) && H.mind?.assigned_role != "Chaplain")
- H.adjustStaminaLoss(2)
- H.adjust_nutrition(-0.3)//changed these to be less deadly and more of an inconvinience
- H.adjust_disgust(1)
- if(istype(H.loc, /obj/structure/closet/crate/coffin))//heals the vampire if in a coffin, except burn which fire can be considered holy
- H.heal_overall_damage(4,4)
- H.adjust_disgust(-7)
- H.adjustOxyLoss(-4)
- H.adjustCloneLoss(-4)
- H.adjustBruteLoss(-0.3)
- H.adjustFireLoss(-0.3)
- if(!is_species(H, /datum/species/jelly)) //checks species
- H.adjustToxLoss(-5)//heals toxin if not slime
+
+ // Check if the current area is a coffin
+ if(istype(quirk_holder.loc, /obj/structure/closet/crate/coffin))
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Quirk mob must be injured
+ if(quirk_mob.health >= quirk_mob.maxHealth)
+ return
+
+ // Prevent healing for robots
+ // This caused numerous technical issues
+ if(quirk_mob.mob_biotypes & MOB_ROBOTIC)
+ // Display a warning chat message (10% chance)
+ if(prob(20))
+ to_chat(quirk_mob, span_warning("Your mechanical body rejects the curse's healing properties!"))
+
+ // Return without healing, due robotic nature
+ return
+
+ // Nutrition (blood) level must be above STARVING
+ if(quirk_mob.nutrition <= NUTRITION_LEVEL_STARVING)
+ // Display a warning chat message (10% chance)
+ if(prob(20))
+ to_chat(quirk_mob, span_warning("You need more blood before you can regenerate!"))
+
+ // Return without healing, due to lack of blood
+ return
+
+ // Define initial health
+ var/health_start = quirk_mob.health
+
+ // Heal brute and burn
+ // Accounts for robotic limbs
+ quirk_mob.heal_overall_damage(2,2)
+ /*
+ // Heal brute
+ quirk_mob.adjustBruteLoss(-2)
+ // Heal burn
+ quirk_mob.adjustFireLoss(-2)
+ */
+ // Heal oxygen
+ quirk_mob.adjustOxyLoss(-2)
+ // Heal clone
+ quirk_mob.adjustCloneLoss(-2)
+
+ // Check for slime race
+ // NOT a slime
+ if(!isslimeperson(quirk_mob))
+ // Heal toxin
+ quirk_mob.adjustToxLoss(-2)
+ // IS a slime
else
- H.adjustToxLoss(5)//heals toxin if slime
- return
- if(H.nutrition == 0)
- if(H.staminaloss < 99)//makes them tired but dosent stun them
- H.adjustStaminaLoss(3, FALSE, TRUE)
- else
- H.adjustStaminaLoss(-1,FALSE, FALSE)//this also helps with if someone is stuck in the chapel for way too long, and i tested with a stun sword that stunning for sec is still possible
- if(prob(2)) //2 percent chance (if it was true randome D:<)
- to_chat(H, span_warning("I need blood NOW!!!"))
+ // Grant toxin (heals slimes)
+ quirk_mob.adjustToxLoss(2)
-/datum/quirk/vampire/remove()
+ // Update health
+ quirk_mob.updatehealth()
+
+ // Determine healed amount
+ var/health_restored = quirk_mob.health - health_start
+
+ // Remove nutrition (blood) as compensation for healing
+ // Amount is equal to 50% of healing done
+ quirk_mob.adjust_nutrition(health_restored*-1)
+
+/datum/quirk/bloodfledge/remove()
. = ..()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/action/vbite/B = locate() in H.actions
- var/datum/action/vrevive/R = locate() in H.actions
- REMOVE_TRAIT(H, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H, TRAIT_COLDBLOODED, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H, TRAIT_NOBREATH, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H,TRAIT_QUICKER_CARRY,ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H,TRAIT_AUTO_CATCH_ITEM,ROUNDSTART_TRAIT)
- B.Remove(H)
- R.Remove(H)
- H.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
-/datum/quirk/vampire/on_spawn()
- var/mob/living/carbon/human/H = quirk_holder
- var/obj/item/card/id/vampire/vcard = new /obj/item/card/id/vampire
- H.equip_to_slot(vcard, ITEM_SLOT_BACKPACK)
- vcard.registered_name = H.real_name
- vcard.update_label(addtext(vcard.registered_name, " the vampire"))
- //var/obj/item/card/id/I = H.get_idcard(FALSE) maybe later, hop can just give the extra card proper access if needed, as for banking, that can be set on spawn by the player using the card in hand
- //vcard.access = I.access
- H.regenerate_icons()
+ // Remove quirk traits
+ REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
+
+ // Remove quirk ability action datums
+ var/datum/action/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
+ var/datum/action/bloodfledge/revive/act_revive = locate() in quirk_mob.actions
+ act_bite.Remove(quirk_mob)
+ act_revive.Remove(quirk_mob)
+
+ // Remove quirk language
+ quirk_mob.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
+
+/datum/quirk/bloodfledge/on_spawn()
. = ..()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Create vampire ID card
+ var/obj/item/card/id/vampire/id_vampire = new /obj/item/card/id/vampire(get_turf(quirk_holder))
+
+ // Update card information
+ id_vampire.registered_name = quirk_mob.real_name
+ id_vampire.update_label(addtext(id_vampire.registered_name, "'s Bloodfledge"))
+
+ // Determine banking ID information
+ for(var/bank_account in SSeconomy.bank_accounts)
+ // Define current iteration's account
+ var/datum/bank_account/account = bank_account
+
+ // Check for match
+ if(account.account_id == quirk_mob.account_id)
+ // Add to cards list
+ account.bank_cards += src
+
+ // Assign account
+ id_vampire.registered_account = account
+
+ // Stop searching
+ break
+
+ // Try to add ID to backpack
+ var/id_in_bag = quirk_mob.equip_to_slot_if_possible(id_vampire, ITEM_SLOT_BACKPACK) || FALSE
+
+ // Text for where the item was sent
+ var/id_location = (id_in_bag ? "in your backpack" : "at your feet" )
+
+ // Alert user in chat
+ // This should not post_add, because the ID is added by on_spawn
+ to_chat(quirk_holder, span_boldnotice("There is a bloodfledge's ID card [id_location], linked to your station account. It functions as a spare ID, but lacks job access."))
/datum/quirk/werewolf //adds the werewolf quirk
name = "Werewolf"
- desc = "A beastly affliction allows you to shapeshift into a more wolfish appearance at will. This will increase your size (In general and below!) and cause you to behave as though you were an anthropomorphic canine. (This is still being tested. Please send any bugs to nukechicken on discord)"
+ desc = "A beastly affliction allows you to shape-shift into a large anthropomorphic canine at will."
value = 0
+ mob_trait = TRAIT_WEREWOLF
+ gain_text = span_notice("You feel the full moon beckon.")
+ lose_text = span_notice("The moon's call hushes into silence.")
+ medical_record_text = "Patient has been reported howling at the night sky."
+ var/list/old_features
/datum/quirk/werewolf/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/action/werewolf/W = new
- W.Grant(H)
+ // Define old features
+ old_features = list("species" = SPECIES_HUMAN, "legs" = "Plantigrade", "size" = 1, "bark")
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Record features
+ old_features = quirk_mob.dna.features.Copy()
+ old_features["species"] = quirk_mob.dna.species.type
+ old_features["custom_species"] = quirk_mob.custom_species
+ old_features["size"] = get_size(quirk_mob)
+ old_features["bark"] = quirk_mob.vocal_bark_id
+ old_features["taur"] = quirk_mob.dna.features["taur"]
+ old_features["eye_type"] = quirk_mob.dna.species.eye_type
+
+/datum/quirk/werewolf/post_add()
+ // Define quirk action
+ var/datum/action/cooldown/werewolf/transform/quirk_action = new
+
+ // Grant quirk action
+ quirk_action.Grant(quirk_holder)
/datum/quirk/werewolf/remove()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/action/werewolf/W = locate() in H.actions
- W.Remove(H)
- . = ..()
+ // Define quirk action
+ var/datum/action/cooldown/werewolf/transform/quirk_action = locate() in quirk_holder.actions
+ // Revoke quirk action
+ quirk_action.Remove(quirk_holder)
/datum/quirk/gargoyle //Mmmm yes stone time
name = "Gargoyle"
diff --git a/modular_splurt/code/datums/traits/trait_actions.dm b/modular_splurt/code/datums/traits/trait_actions.dm
index 792f7b8e8f..da19988868 100644
--- a/modular_splurt/code/datums/traits/trait_actions.dm
+++ b/modular_splurt/code/datums/traits/trait_actions.dm
@@ -1,4 +1,5 @@
-#define BLOOD_DRAIN_NUM 50
+#define BLOODFLEDGE_DRAIN_NUM 50
+#define BLOODFLEDGE_COOLDOWN_BITE 60
//
// Quirk: Hypnotic Gaze
@@ -10,71 +11,205 @@
button_icon_state = "Hypno_eye"
icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi'
background_icon_state = "bg_alien"
- var/mob/living/carbon/T //hypnosis target
- var/mob/living/carbon/human/H //Person with the quirk
/datum/action/innate/Hypnotize/Activate()
- var/mob/living/carbon/human/H = owner
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
- if(!H.pulling || !isliving(H.pulling) || H.grab_state < GRAB_AGGRESSIVE)
- to_chat(H, span_warning("You need to aggressively grab someone to hypnotize them!"))
+ // Define target
+ var/grab_target = action_owner.pulling
+
+ // Check for target
+ if(!grab_target)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You you need to grab someone first!"))
return
- var/mob/living/carbon/T = H.pulling
-
- if(T.IsSleeping())
- to_chat(H, "You can't hypnotize [T] whilst they're asleep!")
+ // Check for cyborg
+ if(iscyborg(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't hypnotize a cyborg!"))
return
- to_chat(H, span_notice("You stare deeply into [T]'s eyes..."))
- to_chat(T, span_warning("[H] stares intensely into your eyes..."))
- if(!do_mob(H, T, 12 SECONDS))
+ // Check for alien
+ // Taken from eyedropper check
+ /*
+ if(isalien(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[grab_target] doesn\'t seem to have any eyes!"))
+ return
+ */
+
+ // Check for carbon human target
+ if(!ishuman(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("That's not a valid creature!"))
return
- if(H.pulling !=T || H.grab_state < GRAB_AGGRESSIVE)
+ // Check if target is alive
+ if(!isliving(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't hypnotize the dead!"))
return
- if(!(H in view(1, H.loc)))
+ // Check for aggressive grab
+ if(action_owner.grab_state < GRAB_AGGRESSIVE)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need a stronger grip before trying this!"))
return
- if(!(T.client?.prefs.cit_toggles & HYPNO))
+ // Define target
+ var/mob/living/carbon/human/action_target = grab_target
+
+ // Check if target has a mind
+ if(!action_target.mind)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[grab_target] doesn\'t have a compatible mind!"))
return
- var/response = alert(T, "Do you wish to fall into a hypnotic sleep?(This will allow [H] to issue hypnotic suggestions)", "Hypnosis", "Yes", "No")
-
- if(response == "Yes")
- T.visible_message(span_warning("[T] falls into a deep slumber!"), "Your eyelids gently shut as you fall into a deep slumber. All you can hear is [H]'s voice as you commit to following all of their suggestions. ")
-
- T.SetSleeping(1200)
- T.drowsyness = max(T.drowsyness, 40)
- T = H.pulling
- var/response2 = alert(H, "Would you like to release your subject or give them a suggestion?", "Hypnosis", "Suggestion", "Release")
-
- if(response2 == "Suggestion")
- if(get_dist(H, T) > 1)
- to_chat(H, "You must stand in whisper range of [T].")
- return
-
- var/text = input("What would you like to suggest?", "Hypnotic suggestion", null, null)
- text = sanitize(text)
- if(!text)
- return
-
- to_chat(H, "You whisper your suggestion in a smooth calming voice to [T]")
- to_chat(T, span_hypnophrase("...[text]..."))
-
- T.visible_message(span_warning("[T] wakes up from their deep slumber!"), "Your eyelids gently open as you see [H]'s face staring back at you. ")
- T.SetSleeping(0)
- T = null
- return
-
- if(response2 == "Release")
- T.SetSleeping(0)
- return
- else
- T.visible_message(span_warning("[T]'s attention breaks, despite the attempt to hypnotize them! They clearly don't want this!"), "Your concentration breaks as you realise you have no interest in following [H]'s words! ")
+ /* Unused: Replaced by get_eye_protection
+ // Check if target's eyes are obscured
+ // ... by headwear
+ if((action_target.head && action_target.head.flags_cover & HEADCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.head]."))
return
+ // ... by a mask
+ else if((action_target.wear_mask && action_target.wear_mask.flags_cover & MASKCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.wear_mask]."))
+ return
+
+ // ... by glasses
+ else if((action_target.glasses && action_target.glasses.flags_cover & GLASSESCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.glasses]."))
+ return
+ */
+
+ // Check if target has eye protection
+ if(action_target.get_eye_protection())
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You have difficulty focusing on [action_target]'s eyes due to some form of protection, and are left unable to hypnotize them."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely at you, but stops after a moment."))
+ return
+
+ // Check if target is blind
+ if(HAS_TRAIT(action_target, TRAIT_BLIND))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but see nothing but emptiness."))
+ return
+
+ // Check for anti-magic
+ // This does not include TRAIT_HOLY
+ if(action_target.anti_magic_check())
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes. They stare back at you as if nothing had happened."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment. You sense nothing out of the ordinary from them."))
+ return
+
+ // Check client pref for hypno
+ if(action_target.client?.prefs.cit_toggles & NEVER_HYPNO)
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You sense that [action_target] would rather not be hypnotized, and decide to respect their wishes."))
+ to_chat(action_target, span_notice("[action_owner] stares into your eyes with a strange conviction, but turns away after a moment."))
+ return
+
+ // Check for mindshield implant
+ if(HAS_TRAIT(action_target, TRAIT_MINDSHIELD))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but hear a faint buzzing from [action_target.p_their()] head. It seems something is interfering."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment, before a buzzing sound emits from your head."))
+ return
+
+ // Check for sleep immunity
+ // This is required for SetSleeping to trigger
+ if(HAS_TRAIT(action_target, TRAIT_SLEEPIMMUNE))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, and see nothing but unrelenting energy. You won't be able to subdue [action_target.p_them()] in this state!"))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes, but sees something unusual about you..."))
+ return
+
+ // Check for sleep
+ if(action_target.IsSleeping())
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't hypnotize [action_target] whilst [action_target.p_theyre()] asleep!"))
+ return
+
+ // Check for combat mode
+ if(SEND_SIGNAL(action_target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("[action_target] is acting too defensively! You'll need [action_target.p_them()] to lower [action_target.p_their()] guard first!"))
+ to_chat(action_target, span_notice("[action_owner] tries to stare into your eyes, but can't get a read on you."))
+ return
+
+ // Display chat messages
+ to_chat(action_owner, span_notice("You stare deeply into [action_target]'s eyes..."))
+ to_chat(action_target, span_warning("[action_owner] stares intensely into your eyes..."))
+
+ // Try to perform action timer
+ if(!do_mob(action_owner, action_target, 5 SECONDS))
+ // Action timer was interrupted
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You lose concentration on [action_target], and fail to hypnotize [action_target.p_them()]!"))
+ to_chat(action_target, span_notice("[action_owner]'s gaze is broken prematurely, freeing you from any potential effects."))
+ return
+
+ // Define blank response
+ var/input_consent
+
+ // Check for non-consensual setting
+ if(action_target.client?.prefs.nonconpref != "Yes")
+ // Non-consensual is NOT enabled
+ // Prompt target for consent response
+ input_consent = alert(action_target, "Will you fall into a hypnotic stupor? This will allow [action_owner] to issue hypnotic suggestions.", "Hypnosis", "Yes", "No")
+
+ // When consent is denied
+ if(input_consent == "No")
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("[action_target]'s attention breaks, despite the attempt to hypnotize [action_target.p_them()]! [action_target.p_they()] clearly don't want this!"))
+ to_chat(action_target, span_notice("Your concentration breaks as you realize you have no interest in following [action_owner]'s words!"))
+ return
+
+ // Display local message
+ action_target.visible_message(span_warning("[action_target] falls into a deep slumber!"), span_danger("Your eyelids gently shut as you fall into a deep slumber. All you can hear is [action_owner]'s voice as you commit to following all of their suggestions."))
+
+ // Set sleeping
+ action_target.SetSleeping(1200)
+
+ // Set drowsiness
+ action_target.drowsyness = max(action_target.drowsyness, 40)
+
+ // Prompt action owner for response
+ var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead.", "Hypnotic suggestion", null, null)
+
+ // Check if input text exists
+ if(!input_suggestion)
+ // Alert user of no input
+ to_chat(action_owner, "You decide not to give [action_target] a suggestion.")
+
+ // Remove sleep, then return
+ action_target.SetSleeping(0)
+ return
+
+ // Sanitize input text
+ input_suggestion = sanitize(input_suggestion)
+
+ // Display message to users
+ to_chat(action_owner, "You whisper your suggestion in a smooth calming voice to [action_target]")
+ to_chat(action_target, span_hypnophrase("...[input_suggestion]..."))
+
+ // Play a sound effect
+ playsound(action_target, 'sound/magic/domain.ogg', 20, 1)
+
+ // Display local message
+ action_target.visible_message(span_warning("[action_target] wakes up from their deep slumber!"), span_danger("Your eyelids gently open as you see [action_owner]'s face staring back at you."))
+
+ // Remove sleep, then return
+ action_target.SetSleeping(0)
+ return
+
//
// Quirk: Hydra Heads
//
@@ -109,165 +244,650 @@
// Quirk: Bloodsucker Fledgling / Vampire
//
-/datum/action/vbite
- name = "Bite"
- button_icon_state = "power_feed"
+// Basic action preset
+/datum/action/bloodfledge
+ name = "Broken Bloodfledge Ability"
+ desc = "You shouldn't be seeing this!"
+ button_icon_state = "power_torpor"
+ background_icon_state = "vamp_power_off"
+ buttontooltipstyle = "cult"
icon_icon = 'icons/mob/actions/bloodsucker.dmi'
- desc = "Sink your vampiric fangs into the person you are grabbing."
+ button_icon = 'icons/mob/actions/bloodsucker.dmi'
+
+// Action: Bite
+/datum/action/bloodfledge/bite
+ name = "Fledgling Bite"
+ desc = "Sink your vampiric fangs into the person you are grabbing, and attempt to drink their blood."
+ button_icon_state = "power_feed"
var/drain_cooldown = 0
-/datum/action/vbite/Trigger()
+/datum/action/bloodfledge/bite/Trigger()
. = ..()
- if(iscarbon(owner))
- var/mob/living/carbon/H = owner
- if(H.nutrition >= 500)
- to_chat(H, span_notice("You are too full to drain any more."))
- return
- if(drain_cooldown >= world.time)
- to_chat(H, span_notice("You just drained blood, wait a few seconds."))
- return
- if(!H.pulling || !iscarbon(H.pulling))
- if(H.getStaminaLoss() >= 80 && H.nutrition > 20)//prevents being stunlocked in the chapel
- to_chat(H,(span_notice("you use some of your power to energize")))
- H.adjustStaminaLoss(-20)
- H.adjust_nutrition(-20)
- H.resting = TRUE
- if(H.pulling && (iscarbon(H.pulling) || (istype(H.pulling,/obj/structure/arachnid/cocoon) && locate(/mob/living/carbon) in H.pulling.contents)))
- var/mob/living/carbon/victim
- if(iscarbon(H.pulling))
- victim = H.pulling
- else if(istype(H.pulling,/obj/structure/arachnid/cocoon))
- victim = locate(/mob/living/carbon) in H.pulling.contents
- drain_cooldown = world.time + 25
- if(victim.anti_magic_check(FALSE, TRUE, FALSE, 0))
- to_chat(victim, span_warning("[H] tries to bite you, but stops before touching you!"))
- to_chat(H, span_warning("[victim] is blessed! You stop just in time to avoid catching fire."))
- return
- //Here we check now for both the garlic cloves on the neck and for blood in the victims bloodstream.
- if(!blood_sucking_checks(victim, TRUE, TRUE))
- return
- H.visible_message(span_danger("[H] bites down on [victim]'s neck!"))
- victim.add_splatter_floor(get_turf(victim), TRUE)
- to_chat(victim, span_userdanger("[H] is draining your blood!"))
- if(!do_after(H, 30, target = victim))
- return
- var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - H.blood_volume //How much capacity we have left to absorb blood
- var/drained_blood = min(victim.blood_volume, BLOOD_DRAIN_NUM, blood_volume_difference)
- H.reagents.add_reagent(/datum/reagent/blood/, drained_blood)
- to_chat(victim, span_danger("[H] has taken some of your blood!"))
- to_chat(H, span_notice("You drain some blood!"))
- playsound(H, 'sound/items/drink.ogg', 30, 1, -2)
- victim.blood_volume = clamp(victim.blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
- log_combat(H,victim,"vampire bit")//logs the biting action for admins
- if(!victim.blood_volume)
- to_chat(H, span_warning("You finish off [victim]'s blood supply!"))
+ // Check for carbon owner
+ if(!iscarbon(owner))
+ return
-/datum/action/vrevive
- name = "Resurrect"
- button_icon_state = "power_strength"
- icon_icon = 'icons/mob/actions/bloodsucker.dmi'
- desc = "Use all your energy to come back to life!"
+ // Define action owner
+ var/mob/living/carbon/action_owner = owner
-/datum/action/vrevive/Trigger()
- . = ..()
- var/mob/living/carbon/C = owner
- var/mob/living/carbon/human/H = owner
- if(H.stat == DEAD && istype(C.loc, /obj/structure/closet/crate/coffin))
- H.revive(TRUE, FALSE)
- H.set_nutrition(0)
- H.Daze(20)
- H.drunkenness = 70
+ // Check for cooldown
+ if(drain_cooldown >= world.time)
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("That ability isn't ready yet."))
+ return
+
+ // Check for any grabbed target
+ if(!action_owner.pulling)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need a victim first!"))
+ return
+
+ // Limit maximum nutrition
+ if(action_owner.nutrition >= NUTRITION_LEVEL_FAT)
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You are too full to drain any more."))
+ return
+
+ // Limit maximum potential nutrition
+ if(action_owner.nutrition + BLOODFLEDGE_DRAIN_NUM >= NUTRITION_LEVEL_FAT)
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You would become too full by draining any more blood."))
+ return
+
+ // Check for muzzle
+ if(action_owner.is_muzzled())
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You can't bite things while muzzled!"))
+ return
+
+ // Define pulled target
+ var/pull_target = action_owner.pulling
+
+ // Define bite target
+ var/mob/living/carbon/bite_target
+
+ // Check if the target is carbon
+ if(iscarbon(pull_target))
+ // Set the bite target
+ bite_target = pull_target
+
+ // Or cocooned carbon
+ else if(istype(pull_target,/obj/structure/arachnid/cocoon))
+ // Define if cocoon has a valid target
+ // This cannot use pull_target
+ var/possible_cocoon_target = locate(/mob/living/carbon) in action_owner.pulling.contents
+
+ // Check defined cocoon target
+ if(possible_cocoon_target)
+ // Set the bite target
+ bite_target = possible_cocoon_target
+
+ // Or a blood tomato
+ else if(istype(pull_target,/obj/item/reagent_containers/food/snacks/grown/tomato/blood))
+ // Warn the user, then return
+ to_chat(action_owner, span_danger("You plunge your fangs into [pull_target]! It's not very nutritious."))
+ return
+
+ // This doesn't actually interact with the item
+
+ // Or none of the above
else
- to_chat(H,span_warning("You need to be dead and in a coffin to revive!"))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't drain blood from [pull_target]!"))
+ return
+
+ // Check for anti-magic
+ if(bite_target.anti_magic_check(FALSE, TRUE, FALSE, 0))
+ // Warn the user and target, then return
+ to_chat(bite_target, span_warning("[action_owner] tries to bite you, but stops before touching you!"))
+ to_chat(action_owner, span_warning("[bite_target] is blessed! You stop just in time to avoid catching fire."))
+ return
+
+ // Check for garlic necklace or garlic in the bloodstream
+ if(!blood_sucking_checks(bite_target, TRUE, TRUE))
+ // Warn the user and target, then return
+ to_chat(bite_target, span_warning("[action_owner] tries to bite you, but is warded off by your Allium Sativum!"))
+ to_chat(action_owner, span_warning("You sense that [bite_target] is protected by Allium Sativum, and refrain from biting them."))
+ return
+
+ // Define bite target's blood volume
+ var/target_blood_volume = bite_target.blood_volume
+
+ // Check for sufficient blood volume
+ if(!target_blood_volume)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("There's not enough blood in [bite_target]!"))
+ return
+
+ // Check if total blood would become too low
+ if((target_blood_volume - BLOODFLEDGE_DRAIN_NUM) <= BLOOD_VOLUME_OKAY)
+ // Check for aggressive grab
+ if(action_owner.grab_state < GRAB_AGGRESSIVE)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You sense that [bite_target] is running low on blood. You'll need a tighter grip on [bite_target.p_them()] to continue."))
+ return
+
+ // Check for pacifist
+ if(HAS_TRAIT(action_owner, TRAIT_PACIFISM))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't drain any more blood from [bite_target] without hurting [bite_target.p_them()]!"))
+ return
+
+ // Set cooldown and action times
+ var/time_cooldown = BLOODFLEDGE_COOLDOWN_BITE
+ var/time_interact = 30
+
+ // Check for voracious
+ if(HAS_TRAIT(action_owner, TRAIT_VORACIOUS))
+ // Make times twice as fast
+ time_cooldown *= 0.5
+ time_interact*= 0.5
+
+ // Set cooldown
+ drain_cooldown = world.time + time_cooldown
+
+ // Display local chat message
+ action_owner.visible_message(span_danger("[action_owner] begins to bite down on [bite_target]'s neck!"))
+
+ // Warn bite target
+ to_chat(bite_target, span_userdanger("[action_owner] has bitten your neck, and is trying to drain your blood!"))
+
+ // Play a bite sound effect
+ playsound(action_owner, 'sound/weapons/bite.ogg', 30, 1, -2)
+
+ // Try to perform action timer
+ if(!do_after(action_owner, time_interact, target = bite_target))
+ // When failing
+ // Display a local chat message
+ action_owner.visible_message(span_danger("[action_owner]'s fangs are prematurely torn from [bite_target]'s neck, spilling [bite_target.p_their()] blood!"))
+
+ // Bite target "drops" the blood
+ // This creates large blood splatter
+ bite_target.bleed(BLOODFLEDGE_DRAIN_NUM, FALSE)
+
+ // Play splatter sound
+ playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
+
+ // Check for masochism
+ if(!HAS_TRAIT(bite_target, TRAIT_MASO))
+ // Force bite_target to play the scream emote
+ bite_target.emote("scream")
+
+ // Log the biting action failure
+ log_combat(action_owner,bite_target,"bloodfledge bitten (interrupted)")
+
+ // Return
+ return
+
+ // Check if bite target species has blood
+ if(NOBLOOD in bite_target.dna.species.species_traits)
+ // Warn the user and target, then return
+ to_chat(bite_target, span_warning("[action_owner] tried to drain you, but didn't find any blood!"))
+ to_chat(action_owner, span_warning("[bite_target] doesn't have any blood to drink!"))
+ return
+
+ // Create blood splatter
+ bite_target.add_splatter_floor(get_turf(bite_target), TRUE)
+
+ // Checks for exotic species blood below
+
+ // Variable for species with non-blood blood volumes
+ var/blood_valid = TRUE
+
+ // Variable for gaining blood volume
+ var/blood_transfer = FALSE
+
+ // Name of blood volume to be taken
+ // Action owner assumes blood until after drinking
+ var/blood_name = "blood"
+
+ // Check bite target for synth blood
+ if(bite_target.mob_biotypes & MOB_ROBOTIC)
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Set blood type name
+ blood_name = "coolant"
+
+ // Check if the action owner is also a synth
+ if (action_owner.mob_biotypes & MOB_ROBOTIC)
+ // Allow gaining blood from this
+ blood_transfer = TRUE
+
+ // Action owner is not a synth
+ else
+ // Warn the user
+ to_chat(action_owner, span_warning("That didn't taste like blood at all..."))
+
+ // Add disgust
+ action_owner.adjust_disgust(2)
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_synth", /datum/mood_event/drankblood_synth)
+
+ // Check if bite target is a slime
+ if (isslimeperson(bite_target))
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Set blood type name
+ blood_name = "slime"
+
+ // Check if the action owner is also a slime
+ if(isslimeperson(action_owner))
+ // Allow gaining blood from this
+ blood_transfer = TRUE
+
+ // Action owner is not a slime
+ else
+ // Warn the user
+ to_chat(action_owner, span_warning("You feel a sloshing presence inside you, but it dies out after a few moments."))
+
+ // Add disgust
+ action_owner.adjust_disgust(2)
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_slime", /datum/mood_event/drankblood_slime)
+
+ // End of species blood checks
+
+ // Define user's remaining capacity to absorb blood
+ var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - action_owner.blood_volume
+ var/drained_blood = min(target_blood_volume, BLOODFLEDGE_DRAIN_NUM, blood_volume_difference)
+
+ // Remove blood from bite target
+ bite_target.blood_volume = clamp(target_blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
+
+ // Perform a blood transfer
+ // This is done to transfer compatible diseases
+ // Grants nothing, unless blood transfer variable is set
+ bite_target.transfer_blood_to(action_owner, (blood_transfer ? drained_blood : 0), TRUE)
+
+ // Check if action owner received valid (nourishing) blood
+ if(blood_valid)
+ // Add blood reagent to the user
+ action_owner.reagents.add_reagent(/datum/reagent/blood/, drained_blood)
+
+ // Alert the bite target and local user of success
+ // Yes, this is AFTER the message for non-valid blood
+ to_chat(bite_target, span_danger("[action_owner] has taken some of your [blood_name]!"))
+ to_chat(action_owner, span_notice("You've drained some of [bite_target]'s [blood_name]!"))
+
+ // Alert the action holder if blood volume limit was exceeded
+ if(blood_transfer && (action_owner.blood_volume >= BLOOD_VOLUME_MAXIMUM))
+ to_chat(action_owner, span_warning("You body fails to absorb any more [blood_name]. The remainder has been lost."))
+
+ // Play a heartbeat sound effect
+ // This was changed to match bloodsucker
+ playsound(action_owner, 'sound/effects/singlebeat.ogg', 30, 1, -2)
+
+ // Log the biting action success
+ log_combat(action_owner,bite_target,"bloodfledge bitten (successfully), transferring [blood_name]")
+
+ // Mood events
+ // Check if bite target is dead or undead
+ if((bite_target.stat >= DEAD) || (bite_target.mob_biotypes & MOB_UNDEAD))
+ // Warn the user
+ to_chat(action_owner, span_warning("The rotten [blood_name] tasted foul."))
+
+ // Add disgust
+ action_owner.adjust_disgust(2)
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_dead", /datum/mood_event/drankblood_dead)
+
+ // Check if bite target's blood has been depleted
+ if(!bite_target.blood_volume)
+ // Warn the user
+ to_chat(action_owner, span_warning("You've depleted [bite_target]'s [blood_name] supply!"))
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_killed", /datum/mood_event/drankkilled)
+
+ // Check if bite target has cursed blood
+ if(HAS_TRAIT(bite_target, TRAIT_CURSED_BLOOD))
+ // Check action owner for cursed blood
+ var/owner_cursed = HAS_TRAIT(action_owner, TRAIT_CURSED_BLOOD)
+
+ // Set chat message based on action owner's trait status
+ var/warn_message = (owner_cursed ? "You taste the unholy touch of a familiar curse in [bite_target]\'s blood." : "You experience a sensation of intense dread just after drinking from [bite_target]. Something about their blood feels... wrong.")
+
+ // Alert user in chat
+ to_chat(action_owner, span_notice(warn_message))
+
+ // Set mood type based on curse status
+ var/mood_type = (owner_cursed ? /datum/mood_event/drank_cursed_good : /datum/mood_event/drank_cursed_bad)
+
+ // Cause mood event
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_cursed_blood", mood_type)
+
+// Action: Revive
+/datum/action/bloodfledge/revive
+ name = "Fledgling Revive"
+ desc = "Expend all of your remaining energy to escape death."
+ button_icon_state = "power_strength"
+
+/datum/action/bloodfledge/revive/Trigger()
+ . = ..()
+
+ // Define mob
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Early check for being dead
+ // Users are most likely to click this while alive
+ if(action_owner.stat != DEAD)
+ // Warn user in chat
+ to_chat(action_owner, "You can't use this ability while alive!")
+
+ // Return
+ return
+
+ // Define failure message
+ var/revive_failed
+
+ // Condition: Mob isn't in a closed coffin
+ if(!istype(action_owner.loc, /obj/structure/closet/crate/coffin))
+ revive_failed += "\n- You need to be in a closed coffin!"
+
+ // Condition: Insufficient nutrition (blood)
+ if(action_owner.nutrition <= NUTRITION_LEVEL_STARVING)
+ revive_failed += "\n- You don't have enough blood left!"
+
+ // Condition: Can be revived
+ // This is used by revive(), and must be checked here to prevent false feedback
+ if(!action_owner.can_be_revived())
+ revive_failed += "\n- Your body is too weak to sustain life!"
+
+ // Condition: Damage limit, brute
+ if(action_owner.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE)
+ revive_failed += "\n- Your body is too battered!"
+
+ // Condition: Damage limit, burn
+ if(action_owner.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE)
+ revive_failed += "\n- Your body is too badly burned!"
+
+ // Condition: Suicide
+ if(action_owner.suiciding)
+ revive_failed += "\n- You chose this path."
+
+ // Condition: No revivals
+ if(HAS_TRAIT(action_owner, TRAIT_NOCLONE))
+ revive_failed += "\n- You only had one chance."
+
+ // Condition: Demonic contract
+ if(action_owner.hellbound)
+ revive_failed += "\n- The soul pact must be honored."
+
+ // Check for failure
+ if(revive_failed)
+ // Set combined message
+ revive_failed = span_warning("You can't revive right now because: [revive_failed]")
+
+ // Alert user in chat of failure
+ to_chat(action_owner, revive_failed)
+
+ // Return
+ return
+
+ // Define time dead
+ // Used for revive policy
+ var/time_dead = world.time - action_owner.timeofdeath
+
+ // Revive the action owner
+ action_owner.revive()
+
+ // Alert the user in chat of success
+ action_owner.visible_message(span_notice("An ominous energy radiates from the [action_owner.loc]..."), span_warning("You've expended all remaining blood to bring your body back to life!"))
+
+ // Play a haunted sound effect
+ playsound(action_owner, 'sound/hallucinations/growl1.ogg', 30, 1, -2)
+
+ // Remove all nutrition (blood)
+ action_owner.set_nutrition(0)
+
+ // Apply daze effect
+ action_owner.Daze(20)
+
+ // Define time limit for revival
+ // Determines memory loss, using defib time and policies
+ var/revive_time_limit = CONFIG_GET(number/defib_cmd_time_limit) * 10
+
+ // Define revive time threshold
+ // Late causes memory loss, according to policy
+ var/time_late = revive_time_limit && (time_dead > revive_time_limit)
+
+ // Define policy to use
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/time_policy = time_late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
+
+ // Check if policy exists
+ if(time_policy)
+ // Alert user in chat of policy
+ to_chat(action_owner, time_policy)
+
+ // Log the revival and effective policy
+ action_owner.log_message("revived using a vampire quirk ability after being dead for [time_dead] deciseconds. Considered [time_late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
//
// Quirk: Werewolf
//
-/datum/action/werewolf
- name = "Transform"
- desc = "Transform into your wolf form."
+/datum/action/cooldown/werewolf
+ name = "Werewolf Ability"
+ desc = "Do something related to werewolves."
icon_icon = 'modular_splurt/icons/mob/actions/misc_actions.dmi'
button_icon_state = "Transform"
- var/transformed = FALSE
- var/list/old_features = list("species" = SPECIES_HUMAN, "legs" = "Plantigrade", "size" = 1, "bark")
+ check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUN | AB_CHECK_CONSCIOUS | AB_CHECK_ALIVE
+ cooldown_time = 5 SECONDS
+ transparent_when_unavailable = TRUE
-/datum/action/werewolf/Trigger()
+/datum/action/cooldown/werewolf/transform
+ name = "Toggle Werewolf Form"
+ desc = "Transform in or out of your wolf form."
+ var/transformed = FALSE
+ var/species_changed = FALSE
+ var/werewolf_gender = "Lycan"
+ var/list/old_features
+
+/datum/action/cooldown/werewolf/transform/Grant()
. = ..()
- var/mob/living/carbon/human/H = owner
- var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/genital/breasts/B = H.getorganslot(ORGAN_SLOT_BREASTS)
- var/obj/item/organ/genital/vagina/V = H.getorganslot(ORGAN_SLOT_VAGINA)
- H.shake_animation(2)
- if(!transformed) // transform them
- H.visible_message(span_danger("[H] shivers, their flesh bursting with a sudden growth of thick fur and their features contorting to that of a beast's, fully transforming them into a werewolf!"))
- H.set_species(/datum/species/mammal, 1)
- H.dna.species.mutant_bodyparts["mam_tail"] = "Wolf"
- H.dna.species.mutant_bodyparts["legs"] = "Digitigrade"
- H.Digitigrade_Leg_Swap(FALSE)
- H.dna.species.mutant_bodyparts["mam_snouts"] = "Mammal, Thick"
- H.dna.features["mam_ears"] = "Wolf"
- H.dna.features["mam_tail"] = "Wolf"
- H.dna.features["mam_snouts"] = "Mammal, Thick"
- H.dna.features["legs"] = "Digitigrade"
- H.update_size(get_size(H) + 0.5)
- H.set_bark("bark")
- H.custom_species = "Werewolf"
- if(!(H.dna.species.species_traits.Find(DIGITIGRADE)))
- H.dna.species.species_traits += DIGITIGRADE
- H.update_body()
- H.update_body_parts()
- if(B)
- B.color = "#[H.dna.features["mcolor"]]"
- B.update()
- if(P)
- P.shape = "Knotted"
- P.color = "#ff7c80"
- P.update()
- P.modify_size(6)
- if(V)
- V.shape = "Furred"
- V.color = "#[H.dna.features["mcolor"]]"
- V.update()
- else // untransform them
- H.visible_message(span_danger("[H] shrinks, their wolfish features quickly receding."))
- H.set_species(old_features["species"], TRUE)
- H.set_bark(old_features["bark"])
- H.dna.features["mam_ears"] = old_features["mam_ears"]
- H.dna.features["mam_snouts"] = old_features["mam_snouts"]
- H.dna.features["mam_tail"] = old_features["mam_tail"]
- H.dna.features["legs"] = old_features["legs"] //i hate legs i hate legs i hate legs i hate legs i hate legs i hate legs i hate legs
+
+ // Define carbon owner
+ var/mob/living/carbon/action_owner_carbon = owner
+
+ // Define parent quirk
+ var/datum/quirk/werewolf/quirk_data = locate() in action_owner_carbon.roundstart_quirks
+
+ // Check if data was copied
+ if(!quirk_data)
+ // Log error and return
+ log_game("Failed to get species data for werewolf action!")
+ return
+
+ // Define stored features
+ old_features = quirk_data.old_features.Copy()
+
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Set species gendered name
+ switch(action_owner.gender)
+ if(MALE)
+ werewolf_gender = "Wer"
+ if(FEMALE)
+ werewolf_gender = "Wīf"
+ if(PLURAL)
+ werewolf_gender = "Hie"
+ if(NEUTER)
+ werewolf_gender = "Þing"
+
+/datum/action/cooldown/werewolf/transform/Trigger()
+ . = ..()
+
+ // Check if unavailable
+ // Checks the parent function's return value
+ if(!.)
+ // Messages will not display here
+ return FALSE
+
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Check for restraints
+ if(!CHECK_MOBILITY(action_owner, MOBILITY_USE))
+ // Warn user, then return
+ action_owner.visible_message(span_warning("You cannot transform while restrained!"))
+ return
+
+ // Define citadel organs
+ var/obj/item/organ/genital/penis/organ_penis = action_owner.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/genital/breasts/organ_breasts = action_owner.getorganslot(ORGAN_SLOT_BREASTS)
+ var/obj/item/organ/genital/vagina/organ_vagina = action_owner.getorganslot(ORGAN_SLOT_VAGINA)
+
+ // Play shake animation
+ action_owner.shake_animation(2)
+
+ // Transform into wolf form
+ if(!transformed)
+ // Define current species type
+ var/datum/species/owner_species = action_owner.dna.species.type
+
+ // Check if species has changed
+ if(old_features["species"] != owner_species)
+ // Set old species
+ old_features["species"] = owner_species
+
+ // Define species prefix
+ var/custom_species_prefix
+
+ // Check if species is mammal (anthro)
+ if(ismammal(action_owner))
+ // Do nothing!
+
+ // Check if species is already a mammal sub-type
+ else if(owner_species in subtypesof(/datum/species/mammal))
+ // Do nothing!
+
+ // Check if species is a jelly
+ else if(isjellyperson(action_owner))
+ // Set species prefix
+ custom_species_prefix = "Jelly "
+
+ // Check if species is a jelly subtype
+ else if(owner_species in subtypesof(/datum/species/jelly))
+ // Set species prefix
+ custom_species_prefix = "Slime "
+
+ // Species is not a mammal
+ else
+ // Change species
+ action_owner.set_species(/datum/species/mammal, 1)
+
+ // Set species changed
+ species_changed = TRUE
+
+ // Set species features
+ action_owner.dna.custom_species = "[custom_species_prefix][werewolf_gender]wulf"
+ action_owner.dna.species.mutant_bodyparts["mam_tail"] = "Otusian"
+ action_owner.dna.species.mutant_bodyparts["legs"] = "Digitigrade"
+ action_owner.Digitigrade_Leg_Swap(FALSE)
+ action_owner.dna.species.mutant_bodyparts["mam_snouts"] = "Sergal"
+ action_owner.dna.features["mam_ears"] = "Jackal"
+ action_owner.dna.features["mam_tail"] = "Otusian"
+ action_owner.dna.features["mam_snouts"] = "Sergal"
+ action_owner.dna.features["legs"] = "Digitigrade"
+ action_owner.dna.features["insect_fluff"] = "Hyena"
+ action_owner.update_size(get_size(action_owner) + 0.5)
+ action_owner.set_bark("bark")
+ if(old_features["taur"] != "None")
+ action_owner.dna.features["taur"] = "Canine"
+ if(!(action_owner.dna.species.species_traits.Find(DIGITIGRADE)))
+ action_owner.dna.species.species_traits += DIGITIGRADE
+ action_owner.update_body()
+ action_owner.update_body_parts()
+
+ // Update possible citadel organs
+ if(organ_breasts)
+ organ_breasts.color = "#[action_owner.dna.features["mcolor"]]"
+ organ_breasts.update()
+ if(organ_penis)
+ organ_penis.shape = "Knotted"
+ organ_penis.color = "#ff7c80"
+ organ_penis.update()
+ organ_penis.modify_size(6)
+ if(organ_vagina)
+ organ_vagina.shape = "Furred"
+ organ_vagina.color = "#[action_owner.dna.features["mcolor"]]"
+ organ_vagina.update()
+
+ // Un-transform from wolf form
+ else
+ // Check if species was already mammal (anthro)
+ if(!species_changed)
+ // Do nothing!
+
+ // Species was not a mammal
+ else
+ // Revert species
+ action_owner.set_species(old_features["species"], TRUE)
+
+ // Clear species changed flag
+ species_changed = FALSE
+
+ // Revert species trait
+ action_owner.set_bark(old_features["bark"])
+ action_owner.dna.custom_species = old_features["custom_species"]
+ action_owner.dna.features["mam_ears"] = old_features["mam_ears"]
+ action_owner.dna.features["mam_snouts"] = old_features["mam_snouts"]
+ action_owner.dna.features["mam_tail"] = old_features["mam_tail"]
+ action_owner.dna.features["legs"] = old_features["legs"]
+ action_owner.dna.features["insect_fluff"] = old_features["insect_fluff"]
+ action_owner.dna.species.eye_type = old_features["eye_type"]
+ if(old_features["taur"] != "None")
+ action_owner.dna.features["taur"] = old_features["taur"]
if(old_features["legs"] == "Plantigrade")
- H.dna.species.species_traits -= DIGITIGRADE
- H.Digitigrade_Leg_Swap(TRUE)
- H.dna.species.mutant_bodyparts["legs"] = old_features["legs"]
- H.update_body()
- H.update_body_parts()
- H.update_size(get_size(H) - 0.5)
- if(B)
- B.color = "#[old_features["breasts_color"]]"
- B.update()
- if(H.has_penis())
- P.shape = old_features["cock_shape"]
- P.color = "#[old_features["cock_color"]]"
- P.update()
- P.modify_size(-6)
- if(H.has_vagina())
- V.shape = old_features["vag_shape"]
- V.color = "#[old_features["vag_color"]]"
- V.update()
- V.update_size()
+ action_owner.dna.species.species_traits -= DIGITIGRADE
+ action_owner.Digitigrade_Leg_Swap(TRUE)
+ action_owner.dna.species.mutant_bodyparts["legs"] = old_features["legs"]
+ action_owner.update_body()
+ action_owner.update_body_parts()
+ action_owner.update_size(get_size(action_owner) - 0.5)
+
+ // Revert citadel organs
+ if(organ_breasts)
+ organ_breasts.color = "#[old_features["breasts_color"]]"
+ organ_breasts.update()
+ if(action_owner.has_penis())
+ organ_penis.shape = old_features["cock_shape"]
+ organ_penis.color = "#[old_features["cock_color"]]"
+ organ_penis.update()
+ organ_penis.modify_size(-6)
+ if(action_owner.has_vagina())
+ organ_vagina.shape = old_features["vag_shape"]
+ organ_vagina.color = "#[old_features["vag_color"]]"
+ organ_vagina.update()
+ organ_vagina.update_size()
+
+ // Set transformation message
+ var/owner_p_their = action_owner.p_their()
+ var/toggle_message = (!transformed ? "[action_owner] shivers, [owner_p_their] flesh bursting with a sudden growth of thick fur as [owner_p_their] features contort to that of a beast, fully transforming [action_owner.p_them()] into a werewolf!" : "[action_owner] shrinks, [owner_p_their] wolfish features quickly receding.")
+
+ // Alert in local chat
+ action_owner.visible_message(span_danger(toggle_message))
+
+ // Toggle transformation state
transformed = !transformed
-/datum/action/werewolf/Grant()// on grant sets some variables
- . = ..()
- var/mob/living/carbon/human/H = owner
- old_features = H.dna.features.Copy()
- old_features["species"] = H.dna.species.type
- old_features["size"] = get_size(H)
- old_features["bark"] = H.vocal_bark_id
+ // Start cooldown
+ StartCooldown()
+ // Return success
+ return TRUE
+
+//
+// Quirk: Gargoyle
+//
/datum/action/gargoyle/transform
name = "Transform"
@@ -307,10 +927,6 @@
to_chat(H, span_warning("You have transformed too recently; you cannot yet transform again!"))
return 0
-//
-// Quirk: Gargoyle
-//
-
/datum/action/gargoyle/check
name = "Check"
desc = "Check your current energy levels."
diff --git a/modular_splurt/code/game/objects/effects/landmarks.dm b/modular_splurt/code/game/objects/effects/landmarks.dm
index 1d26983266..6825efe6ca 100644
--- a/modular_splurt/code/game/objects/effects/landmarks.dm
+++ b/modular_splurt/code/game/objects/effects/landmarks.dm
@@ -23,6 +23,12 @@
icon = 'modular_splurt/icons/mob/landmarks.dmi'
icon_state = "Psychologist"
+/obj/effect/landmark/start/stowaway
+ name = "Stowaway"
+ icon = 'modular_splurt/icons/mob/landmarks.dmi'
+ icon_state = "Stowaway"
+ jobspawn_override = TRUE //spawn in maint
+ delete_after_roundstart = FALSE
//Landmark that creates destinations for the navigate verb to path to
/obj/effect/landmark/navigate_destination
diff --git a/modular_splurt/code/game/objects/items/cards_ids.dm b/modular_splurt/code/game/objects/items/cards_ids.dm
index 0d24bd0085..69debcec83 100644
--- a/modular_splurt/code/game/objects/items/cards_ids.dm
+++ b/modular_splurt/code/game/objects/items/cards_ids.dm
@@ -8,9 +8,12 @@
desc = "A cheap ID used by slave traders. This guy seems to run the show."
/obj/item/card/id/vampire
- name = "Vampire ID"
- desc = "An ID made to easily recognize vampires without needing to do tests"
+ name = "Bloodfledge ID"
+ desc = "An ID made to easily recognize bloodsucker fledglings without requiring medical scans."
+ icon = 'modular_splurt/icons/obj/card.dmi'
icon_state = "vampire"
+ assignment = "Bloodsucker Fledgling"
+ uses_overlays = FALSE
/obj/item/card/id/away/hotel/splurt
name = "Staff ID"
diff --git a/modular_splurt/code/game/objects/items/holy_weapons.dm b/modular_splurt/code/game/objects/items/holy_weapons.dm
index 0af2838523..b14e87f890 100644
--- a/modular_splurt/code/game/objects/items/holy_weapons.dm
+++ b/modular_splurt/code/game/objects/items/holy_weapons.dm
@@ -9,3 +9,38 @@
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("smacked", "struck", "cracked", "beaten")
+
+/obj/item/nullrod/papal_staff
+ name = "papal staff"
+ desc = "A staff used by traditional bishops and popes."
+ icon = 'modular_splurt/icons/obj/items_and_weapons.dmi'
+ icon_state = "papal_staff"
+ item_state = "papal_staff"
+ lefthand_file = 'modular_splurt/icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'modular_splurt/icons/mob/inhands/weapons/melee_righthand.dmi'
+ w_class = WEIGHT_CLASS_BULKY
+ attack_verb = list("smacked", "struck", "cracked", "beaten", "purified")
+
+/obj/item/clothing/head/mitre
+ name = "papal mitre"
+ desc = "A traditional headdress, worn by bishops and popes in traditional Christianity"
+ icon = 'modular_splurt/icons/obj/clothing/hats.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/32x48_head.dmi'
+ icon_state = "mitre"
+ flags_inv = HIDEHAIR | HIDEFACIALHAIR
+
+/obj/item/clothing/suit/chaplain/papal
+ name = "papal robe"
+ desc = "A short cape over a cassock, worn by bishops and popes in traditional Christianity"
+ icon = 'modular_splurt/icons/obj/clothing/suits.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mobs/suits.dmi'
+ icon_state = "papalrobe"
+ body_parts_covered = CHEST|GROIN|LEGS|ARMS
+ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+
+/obj/item/storage/box/holy/papal
+ name = "Papal Kit"
+
+/obj/item/storage/box/holy/papal/PopulateContents()
+ new /obj/item/clothing/head/mitre(src)
+ new /obj/item/clothing/suit/chaplain/papal(src)
diff --git a/modular_splurt/code/game/objects/items/lewd_items/genital_equipment/condom.dm b/modular_splurt/code/game/objects/items/lewd_items/genital_equipment/condom.dm
index 072ba15541..ec212f5015 100644
--- a/modular_splurt/code/game/objects/items/lewd_items/genital_equipment/condom.dm
+++ b/modular_splurt/code/game/objects/items/lewd_items/genital_equipment/condom.dm
@@ -9,6 +9,7 @@
icon_state = "b_condom_wrapped"
var/unwrapped = 0
w_class = WEIGHT_CLASS_TINY
+ custom_price = PRICE_CHEAP_AS_FREE // 10 credits
/obj/item/genital_equipment/condom/Initialize()
create_reagents(300, DRAWABLE|NO_REACT)
diff --git a/modular_splurt/code/game/objects/items/miscellaneous.dm b/modular_splurt/code/game/objects/items/miscellaneous.dm
index 72cd6781e5..e01b30ceea 100644
--- a/modular_splurt/code/game/objects/items/miscellaneous.dm
+++ b/modular_splurt/code/game/objects/items/miscellaneous.dm
@@ -190,12 +190,12 @@
name = "security holo badge"
desc = "A more futuristic hard-light badge"
icon_state = "security_badge_holo"
-
+
/obj/item/clothing/accessory/badge/deputy
name = "security deputy badge"
desc = "A shiny silver badge for deputies on the Security force"
icon_state = "security_badge_deputy"
-
+
/datum/design/sec_badge
name = "Security Badge"
desc = "A shiny badge to show the bearer is part of the Security force."
@@ -215,3 +215,41 @@
build_path = /obj/item/clothing/accessory/badge/deputy
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/obj/item/handmirror/split_personality
+ name = "dissociative mirror"
+ desc = "An enchanted hand mirror. You may not recognize who stares back."
+ var/item_used
+
+/obj/item/handmirror/split_personality/attack_self(mob/user)
+ // Check if already used
+ if(item_used)
+ // Warn user, then return
+ to_chat(user, span_warning("[src] is no longer functional."))
+ return
+
+ // Check if human user exists
+ if(!ishuman(user))
+ // Warn user, then return
+ to_chat(user, span_warning("You see nothing in [src]."))
+ return
+
+ // Define human user
+ var/mob/living/carbon/human/mirror_user = user
+
+ // Add brain trauma
+ mirror_user.gain_trauma(/datum/brain_trauma/severe/split_personality, TRAUMA_RESILIENCE_SURGERY)
+
+ // Set item used variable
+ // This prevents future use
+ item_used = TRUE
+
+ // Alert in local chat
+ mirror_user.visible_message(span_warning("The [src] shatters in [mirror_user]'s hands!"), span_warning("The mirror shatters in your hands!"))
+
+ // Play mirror break sound
+ playsound(src, 'sound/effects/Glassbr3.ogg', 50, 1)
+
+ // Set flavor text
+ name = "broken hand mirror"
+ desc = "You won\'t get much use out of it."
diff --git a/modular_splurt/code/game/objects/items/robot/robot_items.dm b/modular_splurt/code/game/objects/items/robot/robot_items.dm
index 9189c814e7..125f733903 100644
--- a/modular_splurt/code/game/objects/items/robot/robot_items.dm
+++ b/modular_splurt/code/game/objects/items/robot/robot_items.dm
@@ -79,3 +79,15 @@
source = /datum/robot_energy_storage/wrapping_paper
/// End Cargo Borg Items ///
+
+
+/obj/item/gripper/service
+ name = "service gripper"
+ desc = "A simple grasping tool for interacting with food and condiments."
+ can_hold = list(
+ /obj/item/reagent_containers/glass,
+ /obj/item/reagent_containers/food,
+ /obj/item/kitchen,
+ /obj/item/storage/bag/tray
+ )
+
diff --git a/modular_splurt/code/game/objects/items/storage/boxes.dm b/modular_splurt/code/game/objects/items/storage/boxes.dm
index 34d085605d..a205279caa 100644
--- a/modular_splurt/code/game/objects/items/storage/boxes.dm
+++ b/modular_splurt/code/game/objects/items/storage/boxes.dm
@@ -58,7 +58,7 @@
desc = "A large collection of condoms, suitable for the safest of sluts!"
icon = 'modular_sand/icons/obj/fleshlight.dmi'
icon_state = "box"
- custom_price = PRICE_ALMOST_EXPENSIVE // Half the price of buying individually
+ custom_price = PRICE_BELOW_NORMAL // 20% discount from buying individually
/obj/item/storage/box/bulk_condoms/ComponentInitialize()
. = ..()
diff --git a/modular_splurt/code/game/objects/items/toys.dm b/modular_splurt/code/game/objects/items/toys.dm
index 1079de98bf..b131fd5c74 100644
--- a/modular_splurt/code/game/objects/items/toys.dm
+++ b/modular_splurt/code/game/objects/items/toys.dm
@@ -3,3 +3,106 @@
icon = 'modular_splurt/icons/obj/toy.dmi'
icon_state = "savannahivanovtoy"
desc = "Mini-Mecha action figure! Collect them all! 13/12."
+
+/obj/item/toy/figure/assistant/imaginary_friend
+ name = "imaginary friend action figure"
+ desc = "A toy that resembles a special friend."
+ toysay = "I'll always be your best friend!"
+ var/item_used
+
+/obj/item/toy/figure/assistant/imaginary_friend/attack_self(mob/user as mob)
+ // Check if already used
+ if(item_used)
+ // Warn user, then return
+ to_chat(user, span_warning("[src] does nothing. It must be broken."))
+ return
+
+ // Check if human user exists
+ if(!ishuman(user))
+ // Warn user, then return
+ to_chat(user, span_warning("You refrain from handling [src]."))
+ return
+
+ // Define human user
+ var/mob/living/carbon/human/mirror_user = user
+
+ // Add brain trauma
+ mirror_user.gain_trauma(/datum/brain_trauma/special/imaginary_friend, TRAUMA_RESILIENCE_SURGERY)
+
+ // Set item used variable
+ // This prevents future use
+ item_used = TRUE
+
+ // Alert in local chat
+ mirror_user.visible_message(span_warning("[mirror_user] plays with [src]."), span_warning("You start to remember [src], as if they were a real person!"))
+
+ // Set flavor text
+ name = "generic action figure"
+ desc = "It\'s just a normal toy."
+
+/obj/item/toy/beach_ball
+ var/obj/item/vibrator
+ var/enabled = FALSE
+
+/obj/item/toy/beach_ball/syndicate
+ icon_state = "ballsyndicate"
+ icon = 'modular_splurt/icons/misc/beach.dmi'
+ desc = "Hmm. This ball is a bit heavier and tougher than the others."
+
+/obj/item/toy/beach_ball/attackby(obj/item/I, mob/living/user)
+ if(istype(I, /obj/item/electropack/vibrator))
+ if(vibrator)
+ to_chat(user, span_warning("There is already a vibrator inside this!"))
+ else
+ if(!user.transferItemToLoc(I,src))
+ return
+ to_chat(user, span_notice("You put [I] inside [src]."))
+ vibrator = I
+
+/obj/item/toy/beach_ball/attack_self(mob/user)
+ var/list/options_list = list()
+ if(vibrator)
+ options_list += list("Eject" = image(icon = 'icons/radials/taperecorder.dmi', icon_state = "eject", dir = EAST))
+ options_list += list("Play" = image(icon = 'icons/radials/taperecorder.dmi', icon_state = "play", dir = WEST))
+ if(options_list)
+ var/selection = show_radial_menu(user, src, options_list, radius = 38, require_near = TRUE, tooltips = TRUE)
+ if(!selection)
+ return
+ switch(selection)
+ if("Play")
+ playsound(user, 'sound/effects/clock_tick.ogg', 50, 1, -1)
+ enabled = !enabled
+ if(enabled)
+ START_PROCESSING(SSobj, src)
+ else
+ STOP_PROCESSING(SSobj, src)
+ to_chat(user, "You toggle the [vibrator]. ")
+ if("Eject")
+ playsound(user, 'sound/weapons/empty.ogg', 100, 1)
+ to_chat(user, "You remove [vibrator] from [src]. ")
+ user.put_in_hands(vibrator)
+ enabled = FALSE
+ vibrator = null
+ update_icon()
+
+/obj/item/toy/beach_ball/process()
+ if(vibrator && enabled)
+ throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),3,1)
+ playsound(src, 'modular_splurt/sound/lewd/vibrate.ogg', 40, 1, -1)
+
+/obj/item/toy/beach_ball/syndicate/process()
+ . = ..()
+ if(vibrator && enabled)
+ throwforce = 60
+
+/obj/item/toy/beach_ball/syndicate/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+ if(ishuman(thrower))
+ throwforce = 0
+ . = ..()
+
+/obj/item/toy/beach_ball/syndicate/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ . = ..()
+ if(istype(hit_atom, /turf/closed/wall) && throwforce > 0)
+ var/turf/closed/wall/W = hit_atom
+ W.dismantle_wall()
+
diff --git a/modular_splurt/code/modules/antagonists/slaver/slaver.dm b/modular_splurt/code/modules/antagonists/slaver/slaver.dm
index 610e12b74d..7638cc11f3 100644
--- a/modular_splurt/code/modules/antagonists/slaver/slaver.dm
+++ b/modular_splurt/code/modules/antagonists/slaver/slaver.dm
@@ -44,6 +44,7 @@ GLOBAL_LIST_INIT(slavers_ransom_values, list(
"Scientist" = SLAVER_STANDARD_RANSOM,
"Roboticist" = SLAVER_STANDARD_RANSOM,
"Prisoner" = SLAVER_STANDARD_RANSOM,
+ "Stowaway" = SLAVER_STANDARD_RANSOM,
"Curator" = SLAVER_STANDARD_RANSOM,
"Lawyer" = SLAVER_STANDARD_RANSOM,
"Chaplain" = SLAVER_STANDARD_RANSOM,
diff --git a/modular_splurt/code/modules/client/loadout/accessory.dm b/modular_splurt/code/modules/client/loadout/accessory.dm
new file mode 100644
index 0000000000..e2d2e6d8be
--- /dev/null
+++ b/modular_splurt/code/modules/client/loadout/accessory.dm
@@ -0,0 +1,11 @@
+/datum/gear/accessory/shortcrop
+ name = "Short sleeved croptop"
+ path = /obj/item/clothing/accessory/shortcrop
+
+/datum/gear/accessory/longcrop
+ name = "Long sleeved croptop"
+ path = /obj/item/clothing/accessory/longcrop
+
+/datum/gear/accessory/formalcrop
+ name = "Formal croptop"
+ path = /obj/item/clothing/accessory/formalcrop
diff --git a/modular_splurt/code/modules/client/loadout/shoes.dm b/modular_splurt/code/modules/client/loadout/shoes.dm
index d10f823444..b75b1cd621 100644
--- a/modular_splurt/code/modules/client/loadout/shoes.dm
+++ b/modular_splurt/code/modules/client/loadout/shoes.dm
@@ -2,7 +2,7 @@
/datum/gear/shoes/footwraps
name = "Cloth Footwraps"
path= /obj/item/clothing/shoes/footwraps
-
+
/datum/gear/shoes/invisiboots
name = "Invisifiber Footwraps"
path= /obj/item/clothing/shoes/invisiboots
@@ -36,3 +36,7 @@
/datum/gear/shoes/puttee
restricted_roles = list("Security Officer", "Warden", "Head of Security", "Brig Physician", "Blueshield")
+
+/datum/gear/shoes/highheel_sandals
+ name = "High-heel Sandals"
+ path = /obj/item/clothing/shoes/highheel_sandals
diff --git a/modular_splurt/code/modules/client/loadout/uniform.dm b/modular_splurt/code/modules/client/loadout/uniform.dm
index 29d91596d7..1333a8c764 100644
--- a/modular_splurt/code/modules/client/loadout/uniform.dm
+++ b/modular_splurt/code/modules/client/loadout/uniform.dm
@@ -146,3 +146,15 @@
path = /obj/item/clothing/under/goner/fake/poly
loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
loadout_initial_colors = list("#E6E6E6")
+
+/datum/gear/uniform/leia_outfit
+ name = "Princess Leia Outfit"
+ path = /obj/item/clothing/under/misc/leia_outfit
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#C61818", "#D4AF37")
+
+/datum/gear/uniform/performer/polychromic
+ name = "Polychromic performers one piece"
+ path = /obj/item/clothing/under/performer/polychromic
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#ffffff")
diff --git a/modular_splurt/code/modules/client/preferences.dm b/modular_splurt/code/modules/client/preferences.dm
index 683afd00cf..13b9bc882b 100644
--- a/modular_splurt/code/modules/client/preferences.dm
+++ b/modular_splurt/code/modules/client/preferences.dm
@@ -1,12 +1,18 @@
/datum/preferences
+ max_save_slots = DEFAULT_SAVE_SLOTS
var/unholypref = "No" //Goin 2 hell fo dis one
-
var/list/gfluid_blacklist = list() //Stuff you don't want people to cum into you
/datum/preferences/New(client/C)
if(!GLOB.genital_fluids_list)
build_genital_fluids_list() //I DON'T KNOW where else to put it, ok??
+ //Extra saves for donators
+ max_save_slots = CONFIG_GET(number/base_save_slots)
+ if(istype(C))
+ var/extra_slots = (IS_CKEY_DONATOR_GROUP(C.key, DONATOR_GROUP_TIER_1) + IS_CKEY_DONATOR_GROUP(C.key, DONATOR_GROUP_TIER_2) + IS_CKEY_DONATOR_GROUP(C.key, DONATOR_GROUP_TIER_3)) * 10
+ max_save_slots += extra_slots
+
. = ..()
/proc/build_genital_fluids_list()
diff --git a/modular_splurt/code/modules/clothing/clothing.dm b/modular_splurt/code/modules/clothing/clothing.dm
new file mode 100644
index 0000000000..d006aa26f1
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/clothing.dm
@@ -0,0 +1,43 @@
+/obj/item/clothing
+ var/last_bites = 3 //Once clothes is shredded, this determines how many more bites till its deleted.
+ var/is_edible = 0 //Controls what can or can't be eaten by Clothes Eaters/Insects
+
+//Cloth eaters get some nutrients. A Jumpsuit will roughly give back 50 Nutrition. IF eaten fully.
+/obj/item/reagent_containers/food/snacks/clothing
+ list_reagents = list(/datum/reagent/consumable/nutriment = 3)
+
+//A call on attemp_forcefeed() without async to properly know if it worked or not. In theory this shouldn't cause any issues as only a small part of the population should ever run this.VS normal eating.
+/obj/item/reagent_containers/food/snacks/clothing/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ return attempt_forcefeed(M, user)
+
+
+//As a bonus for having the Cloth Eater trait. You gain extra mood from eatin clothes, but damage them at the same time.
+/obj/item/clothing/attack(mob/M, mob/user, def_zone)
+ if(user.a_intent != INTENT_HARM)
+ if(HAS_TRAIT(M,TRAIT_CLOTH_EATER) || isinsect(M))
+ if(is_edible == 0) //This checks if an item can be shredded.
+ to_chat(M, "This item is too tough to eat.")
+ return FALSE //Return False to prevent the player smacking themselves with the item. Didn't want to risk a player accidently hurting themselves trying to eat anything.
+ var/obj/item/reagent_containers/food/snacks/clothing/clothing_as_food = new
+ clothing_as_food.name = name
+ var/mob/living/H = M
+ if(clothing_as_food.attack(M, user, def_zone)) //Staggered as calling it in the original IF will cause anyone who lacks either to eat.
+ if(damaged_clothes == CLOTHING_SHREDDED) //Check if we need to start breaking clothes
+ last_bites -= 1
+ to_chat(M, "There isn't much of the [name] left to eat.")
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cloth_consumed", /datum/mood_event/cloth_eaten, src)
+ take_damage(20, sound_effect=FALSE)
+ qdel(clothing_as_food)
+ if(last_bites <= 0)
+ user.visible_message("[user] finishes eating the [name].")
+ qdel(src)
+ return TRUE
+ return ..()
+
+// Set the clothing's integrity back to 100%, remove all damage to bodyparts, and generally fix it up
+/obj/item/clothing/repair(mob/user, params)
+ .=..()
+ last_bites = 3
+
diff --git a/modular_splurt/code/modules/clothing/glasses/_glasses.dm b/modular_splurt/code/modules/clothing/glasses/_glasses.dm
index f4c6811218..04d23745dc 100644
--- a/modular_splurt/code/modules/clothing/glasses/_glasses.dm
+++ b/modular_splurt/code/modules/clothing/glasses/_glasses.dm
@@ -8,3 +8,9 @@
flash_protect = 1
tint = 1
glass_colour_type = /datum/client_colour/glass_colour/gray
+
+/obj/item/clothing/glasses/eyepatch
+ is_edible = 1
+
+/obj/item/clothing/glasses/sunglasses/blindfold
+ is_edible = 1
diff --git a/modular_splurt/code/modules/clothing/gloves.dm b/modular_splurt/code/modules/clothing/gloves.dm
index 5f2731516c..74af3a9f02 100644
--- a/modular_splurt/code/modules/clothing/gloves.dm
+++ b/modular_splurt/code/modules/clothing/gloves.dm
@@ -14,6 +14,7 @@
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
armor = list("melee" = 5, "bullet" = 0, "laser" = 5,"energy" = 5, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 100)
strip_mod = 1.5
+ is_edible = 0
/obj/item/clothing/gloves/cbrn/engineer
name = "engineer CBRN gloves"
diff --git a/modular_splurt/code/modules/clothing/gloves/_gloves.dm b/modular_splurt/code/modules/clothing/gloves/_gloves.dm
new file mode 100644
index 0000000000..22f54d4075
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/gloves/_gloves.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/gloves
+ is_edible = 1
diff --git a/modular_splurt/code/modules/clothing/head/_head.dm b/modular_splurt/code/modules/clothing/head/_head.dm
new file mode 100644
index 0000000000..281c24b862
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/head/_head.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/head
+ is_edible = 1 //Helment and Hardhats + some Special misc are unedible
diff --git a/modular_splurt/code/modules/clothing/head/hardhat.dm b/modular_splurt/code/modules/clothing/head/hardhat.dm
new file mode 100644
index 0000000000..24c83f606d
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/head/hardhat.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/head/hardhat
+ is_edible = 0
diff --git a/modular_splurt/code/modules/clothing/head/helmet.dm b/modular_splurt/code/modules/clothing/head/helmet.dm
index defe0f0fcc..aaad3a6243 100644
--- a/modular_splurt/code/modules/clothing/head/helmet.dm
+++ b/modular_splurt/code/modules/clothing/head/helmet.dm
@@ -1,4 +1,7 @@
// GWTB-inspired stuff wooo
+/obj/item/clothing/head/helmet
+ is_edible = 0
+
/obj/item/clothing/head/helmet/goner
name = "trencher helmet"
desc = "A No Man's Land-type helmet with purple paint applied."
diff --git a/modular_splurt/code/modules/clothing/head/misc.dm b/modular_splurt/code/modules/clothing/head/misc.dm
index dddc2f6b05..eb14ad475d 100644
--- a/modular_splurt/code/modules/clothing/head/misc.dm
+++ b/modular_splurt/code/modules/clothing/head/misc.dm
@@ -51,6 +51,7 @@
flags_inv = HIDEHAIR|HIDEEARS
resistance_flags = ACID_PROOF
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
+ is_edible = 0
/obj/item/clothing/head/helmet/cbrn/mopp
name = "MOPP hood"
@@ -59,6 +60,7 @@
item_state = "mopphood"
can_flashlight = 1
armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 100)
+ is_edible = 0
/obj/item/clothing/head/helmet/cbrn/mopp/advance
name = "advance MOPP hood"
@@ -66,6 +68,7 @@
can_flashlight = 1
armor = list("melee" = 50, "bullet" = 40, "laser" = 40,"energy" = 20, "bomb" = 35, "bio" = 110, "rad" = 110, "fire" = 50, "acid" = 110)
clothing_flags = NONE
+ is_edible = 0
// research nods
diff --git a/modular_splurt/code/modules/clothing/head/misc_special.dm b/modular_splurt/code/modules/clothing/head/misc_special.dm
new file mode 100644
index 0000000000..9a86ddcb53
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/head/misc_special.dm
@@ -0,0 +1,5 @@
+/obj/item/clothing/head/welding
+ is_edible = 0
+
+/obj/item/clothing/head/hardhat/cakehat
+ is_edible = 1
diff --git a/modular_splurt/code/modules/clothing/kinkyclothes.dm b/modular_splurt/code/modules/clothing/kinkyclothes.dm
index a2bcaee4f6..4707c98cce 100644
--- a/modular_splurt/code/modules/clothing/kinkyclothes.dm
+++ b/modular_splurt/code/modules/clothing/kinkyclothes.dm
@@ -114,7 +114,10 @@
icon_state = "vaultsuit"
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/uniform.dmi'
item_state = "b_suit"
+ item_state = "b_suit"
can_adjust = FALSE
+ anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/uniform_digi.dmi'
+ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_ALL_TAURIC
var/firstpickup = TRUE
var/pickupsound = TRUE
diff --git a/modular_splurt/code/modules/clothing/masks/_mask.dm b/modular_splurt/code/modules/clothing/masks/_mask.dm
new file mode 100644
index 0000000000..d5f6e0f180
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/masks/_mask.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/mask
+ is_edible = 1
diff --git a/modular_splurt/code/modules/clothing/masks/boxing.dm b/modular_splurt/code/modules/clothing/masks/boxing.dm
new file mode 100644
index 0000000000..89015338f3
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/masks/boxing.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/mask/infiltrator
+ is_edible = 0
diff --git a/modular_splurt/code/modules/clothing/masks/gasmask.dm b/modular_splurt/code/modules/clothing/masks/gasmask.dm
index 9405e3f315..7d7066ab86 100644
--- a/modular_splurt/code/modules/clothing/masks/gasmask.dm
+++ b/modular_splurt/code/modules/clothing/masks/gasmask.dm
@@ -1,3 +1,6 @@
+/obj/item/clothing/mask/gas
+ is_edible = 0
+
/obj/item/clothing/mask/gas/radmask
name = "radiation mask"
desc = "An mask that somewhat protects the user from radiation. Not as effective like a radiation hood, but is better than nothing."
diff --git a/modular_splurt/code/modules/clothing/masks/miscellaneous.dm b/modular_splurt/code/modules/clothing/masks/miscellaneous.dm
index 4d8c1bad27..865d5633ea 100644
--- a/modular_splurt/code/modules/clothing/masks/miscellaneous.dm
+++ b/modular_splurt/code/modules/clothing/masks/miscellaneous.dm
@@ -35,6 +35,7 @@
visor_flags_inv = 0
flavor_adjust = FALSE
armor = list("melee" = 5, "bullet" = 0, "laser" = 5,"energy" = 5, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 100)
+ is_edible = 0
/obj/item/clothing/mask/gas/cbrn/mopp
name = "MOPP gas mask"
diff --git a/modular_splurt/code/modules/clothing/neck/_neck.dm b/modular_splurt/code/modules/clothing/neck/_neck.dm
index f17afa4e9f..40ae719af7 100644
--- a/modular_splurt/code/modules/clothing/neck/_neck.dm
+++ b/modular_splurt/code/modules/clothing/neck/_neck.dm
@@ -1,9 +1,16 @@
+/obj/item/clothing/neck
+ is_edible = 1
+
+/obj/item/clothing/neck/stethoscope
+ is_edible = 2
+
/obj/item/clothing/neck/petcollar/locked/security
name = "security collar"
desc = "For when you need to show everyone who your pet belongs to."
icon = 'modular_splurt/icons/obj/clothing/neck.dmi'
icon_state = "seccollar"
poly_states = 0
+ is_edible = 0
/obj/item/clothing/neck/petcollar/spike
name = "Spiked Pet Collar"
@@ -12,6 +19,7 @@
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/neck.dmi'
icon_state = "collar_spik"
poly_states = 0
+ is_edible = 0
/obj/item/clothing/neck/petcollar/locked/spike
name = "Spiked Pet Collar"
@@ -36,6 +44,7 @@
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/neck.dmi'
icon_state = "collar_holo"
poly_states = 0
+ is_edible = 0
/obj/item/clothing/neck/petcollar/casino
name = "Casino Collar"
@@ -52,6 +61,7 @@
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/neck.dmi'
icon_state = "casinoslave_available"
poly_states = 0
+ is_edible = 0
/obj/item/clothing/neck/petcollar/locked/casino/attackby(obj/item/K, mob/user, params)
. = ..()
diff --git a/modular_splurt/code/modules/clothing/shoes/_shoes.dm b/modular_splurt/code/modules/clothing/shoes/_shoes.dm
new file mode 100644
index 0000000000..bc1fb9812f
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/shoes/_shoes.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/shoes
+ is_edible = 1
diff --git a/modular_splurt/code/modules/clothing/shoes/magboots.dm b/modular_splurt/code/modules/clothing/shoes/magboots.dm
new file mode 100644
index 0000000000..b3c8d6dff8
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/shoes/magboots.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/shoes/magboots
+ is_edible = 0
diff --git a/modular_splurt/code/modules/clothing/shoes/miscellaneous.dm b/modular_splurt/code/modules/clothing/shoes/miscellaneous.dm
index 5d50562d5a..d87fd74ce7 100644
--- a/modular_splurt/code/modules/clothing/shoes/miscellaneous.dm
+++ b/modular_splurt/code/modules/clothing/shoes/miscellaneous.dm
@@ -51,6 +51,7 @@
resistance_flags = ACID_PROOF
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
armor = list("melee" = 5, "bullet" = 0, "laser" = 5,"energy" = 5, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 100)
+ is_edible = 0
/obj/item/clothing/shoes/jackboots/cbrn/mopp
name = "MOPP boots"
@@ -83,3 +84,15 @@
build_path = /obj/item/clothing/shoes/jackboots/cbrn/mopp
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/obj/item/clothing/shoes/highheel_sandals
+ name = "high-heel sandals"
+ desc = "A pair of high-heel sandals"
+ icon = 'modular_splurt/icons/obj/clothing/shoes.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/shoes.dmi'
+ anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/shoes_digi.dmi'
+ icon_state = "highheel_sandals"
+
+/obj/item/clothing/shoes/highheel_sandals/Initialize()
+ . = ..()
+ AddComponent(/datum/component/squeak, list('modular_splurt/sound/effects/footstep/highheel1.ogg' = 1,'modular_splurt/sound/effects/footstep/highheel2.ogg' = 1), 20)
diff --git a/modular_splurt/code/modules/clothing/suits/cloaks.dm b/modular_splurt/code/modules/clothing/suits/cloaks.dm
index 8de8a40071..bbbf127829 100644
--- a/modular_splurt/code/modules/clothing/suits/cloaks.dm
+++ b/modular_splurt/code/modules/clothing/suits/cloaks.dm
@@ -31,6 +31,7 @@
mob_overlay_icon = 'modular_splurt/icons/mob/clothing/neck.dmi'
armor = list(MELEE = 35, BULLET = 40, LASER = 25, ENERGY = 10, BOMB = 25, BIO = 20, RAD = 20, FIRE = 60, ACID = 60)
body_parts_covered = CHEST|GROIN|ARMS
+ is_edible = 0
/obj/item/clothing/neck/cloak/binary
name = "Binary cloak"
diff --git a/modular_splurt/code/modules/clothing/suits/utility.dm b/modular_splurt/code/modules/clothing/suits/utility.dm
new file mode 100644
index 0000000000..8f7077a3c4
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/suits/utility.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/head/radiation
+ is_edible = 0
diff --git a/modular_splurt/code/modules/clothing/under/_under.dm b/modular_splurt/code/modules/clothing/under/_under.dm
index 847ba40c3c..b9b28a778e 100644
--- a/modular_splurt/code/modules/clothing/under/_under.dm
+++ b/modular_splurt/code/modules/clothing/under/_under.dm
@@ -1,3 +1,6 @@
+/obj/item/clothing/under
+ is_edible = 1 //Most jumpsuits are made of cloth so this is a safe bet.
+
/obj/item/clothing/under/Initialize(mapload)
. = ..()
if(!is_type_in_typecache(type, GLOB.skirt_peekable))
diff --git a/modular_splurt/code/modules/clothing/under/accessories.dm b/modular_splurt/code/modules/clothing/under/accessories.dm
new file mode 100644
index 0000000000..eafad46702
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/under/accessories.dm
@@ -0,0 +1,26 @@
+/obj/item/clothing/accessory/shortcrop
+ name = "short sleeved crop top"
+ desc = "A short sleeved crop top, for when it gets too hot."
+ icon = 'modular_splurt/icons/obj/clothing/accessories.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/accessories.dmi'
+ icon_state = "shortcrop"
+ item_state = "shortcrop"
+ minimize_when_attached = FALSE
+
+/obj/item/clothing/accessory/longcrop
+ name = "long sleeved crop top"
+ desc = "A long sleeved crop top, now with long sleeves!"
+ icon = 'modular_splurt/icons/obj/clothing/accessories.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/accessories.dmi'
+ icon_state = "longcrop"
+ item_state = "longcrop"
+ minimize_when_attached = FALSE
+
+/obj/item/clothing/accessory/formalcrop
+ name = "formal crop top"
+ desc = "A 'formal' crop top. Not sure how this'll fool anyone."
+ icon = 'modular_splurt/icons/obj/clothing/accessories.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/accessories.dmi'
+ icon_state = "formalcrop"
+ item_state = "formalcrop"
+ minimize_when_attached = FALSE
diff --git a/modular_splurt/code/modules/clothing/under/miscellaneous.dm b/modular_splurt/code/modules/clothing/under/miscellaneous.dm
index 3f6b806260..990dedb2a8 100644
--- a/modular_splurt/code/modules/clothing/under/miscellaneous.dm
+++ b/modular_splurt/code/modules/clothing/under/miscellaneous.dm
@@ -311,3 +311,25 @@
/obj/item/clothing/under/misc/gear_harness
body_parts_covered = NONE
+/obj/item/clothing/under/misc/leia_outfit
+ name = "space princess outfit"
+ desc = "Chain for your Master's erotic asphyxiation not included."
+ icon = 'modular_splurt/icons/obj/clothing/uniforms.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/uniform.dmi'
+ icon_state = "leia"
+ can_adjust = FALSE
+ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
+
+/obj/item/clothing/under/misc/leia_outfit/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#C61818", "#D4AF37"), 2)
+
+/obj/item/clothing/under/performer/polychromic
+ name = "polychromic performers one piece"
+ icon = 'modular_splurt/icons/obj/clothing/uniforms.dmi'
+ mob_overlay_icon = 'modular_splurt/icons/mob/clothing/uniform.dmi'
+ icon_state = "poly_performer"
+
+/obj/item/clothing/under/performer/polychromic/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#ffffff"), 1)
diff --git a/modular_splurt/code/modules/clothing/underwear/_underwear.dm b/modular_splurt/code/modules/clothing/underwear/_underwear.dm
new file mode 100644
index 0000000000..045d1f8ce7
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/underwear/_underwear.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/underwear/
+ is_edible = 1
diff --git a/modular_splurt/code/modules/events/bruh_moment.dm b/modular_splurt/code/modules/events/bruh_moment.dm
index 564c409c70..d05dff6e61 100644
--- a/modular_splurt/code/modules/events/bruh_moment.dm
+++ b/modular_splurt/code/modules/events/bruh_moment.dm
@@ -4,9 +4,10 @@
weight = 10
min_players = 1
max_occurrences = 0
+ category = EVENT_CATEGORY_FRIENDLY
/datum/round_event/bruh_moment
- startWhen = 8
+ start_when = 8
fakeable = FALSE
/datum/round_event/bruh_moment/start()
diff --git a/modular_splurt/code/modules/events/crystalline_reentry.dm b/modular_splurt/code/modules/events/crystalline_reentry.dm
index 02f8ff68ae..2894e4a484 100644
--- a/modular_splurt/code/modules/events/crystalline_reentry.dm
+++ b/modular_splurt/code/modules/events/crystalline_reentry.dm
@@ -4,6 +4,7 @@
min_players = 15
max_occurrences = 0 //Deactivated for now
var/atom/special_target
+ category = EVENT_CATEGORY_SPACE
/datum/round_event_control/crystalline_reentry/admin_setup()
if(!check_rights(R_FUN))
@@ -13,8 +14,8 @@
special_target = get_turf(usr)
/datum/round_event/crystalline_reentry
- announceWhen = 0
- startWhen = 10
+ announce_when = 0
+ start_when = 10
fakeable = FALSE
/datum/round_event/crystalline_reentry/announce(fake)
@@ -34,6 +35,7 @@
min_players = 35
max_occurrences = 0 //This is only an admin spawn. Ergo, wrath of the gods.
var/atom/special_target
+ category = EVENT_CATEGORY_SPACE
/datum/round_event_control/crystalline_wave/admin_setup()
if(!check_rights(R_FUN))
@@ -47,9 +49,9 @@
message_admins("A crystalline asteroid wave has been triggered. Maybe you should add some music for the players? Consider this random selection: [randselect]")
/datum/round_event/crystalline_wave
- announceWhen = 0
- startWhen = 15
- endWhen = 60 //45 seconds of pain
+ announce_when = 0
+ start_when = 15
+ end_when = 60 //45 seconds of pain
fakeable = FALSE
/datum/round_event/crystalline_wave/announce(fake)
diff --git a/modular_splurt/code/modules/food_and_drinks/recipes/drink_recipes.dm b/modular_splurt/code/modules/food_and_drinks/recipes/drink_recipes.dm
index a587e9fc71..c388d8fe3c 100644
--- a/modular_splurt/code/modules/food_and_drinks/recipes/drink_recipes.dm
+++ b/modular_splurt/code/modules/food_and_drinks/recipes/drink_recipes.dm
@@ -189,3 +189,104 @@
/datum/reagent/consumable/ethanol/moonshine = 2,
/datum/reagent/consumable/ethanol/brave_bull = 1
)
+
+//Milkshakes
+/datum/chemical_reaction/milkshake_base
+ name = "Plain Milkshake"
+ id = /datum/reagent/consumable/milkshake_base
+ results = list(/datum/reagent/consumable/milkshake_base = 3)
+ required_reagents = list(
+ /datum/reagent/consumable/milk = 1,
+ /datum/reagent/consumable/ice = 1,
+ /datum/reagent/consumable/cream =1
+ )
+
+/datum/chemical_reaction/milkshake_vanilla
+ name = "Vanilla Milkshake"
+ id = /datum/reagent/consumable/milkshake_vanilla
+ results = list(/datum/reagent/consumable/milkshake_vanilla = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base =1,
+ /datum/reagent/consumable/vanilla =1
+ )
+
+/datum/chemical_reaction/milkshake_choc
+ name = "Chocolate Milkshake"
+ id = /datum/reagent/consumable/milkshake_choc
+ results = list(/datum/reagent/consumable/milkshake_choc = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/coco = 1
+ )
+
+/datum/chemical_reaction/milkshake_strawberry
+ name = "Strawberry Milkshake"
+ id = /datum/reagent/consumable/milkshake_strawberry
+ results = list(/datum/reagent/consumable/milkshake_strawberry = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/strawberryjuice = 1
+ )
+
+/datum/chemical_reaction/milkshake_banana
+ name = "Banana Milkshake"
+ id = /datum/reagent/consumable/milkshake_banana
+ results = list(/datum/reagent/consumable/milkshake_banana = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/banana = 1
+ )
+
+/datum/chemical_reaction/milkshake_berry
+ name = "Berry Milkshake"
+ id = /datum/reagent/consumable/milkshake_berry
+ results = list(/datum/reagent/consumable/milkshake_berry = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/berryjuice = 1
+ )
+
+/datum/chemical_reaction/milkshake_cola
+ name = "Cola Milkshake"
+ id = /datum/reagent/consumable/milkshake_cola
+ results = list(/datum/reagent/consumable/milkshake_cola = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/space_cola = 1
+ )
+
+/datum/chemical_reaction/milkshake_gibb
+ name = "Dr. Gibb Milkshake"
+ id = /datum/reagent/consumable/milkshake_gibb
+ results = list(/datum/reagent/consumable/milkshake_gibb = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/dr_gibb = 1
+ )
+
+/datum/chemical_reaction/milkshake_peach
+ name = "Peach Milkshake"
+ id = /datum/reagent/consumable/milkshake_peach
+ results = list(/datum/reagent/consumable/milkshake_peach = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/peachjuice = 1
+ )
+
+/datum/chemical_reaction/milkshake_pineapple
+ name = "Pineapple Milkshake"
+ id = /datum/reagent/consumable/milkshake_pineapple
+ results = list(/datum/reagent/consumable/milkshake_pineapple = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/pineapplejuice = 1
+ )
+
+/datum/chemical_reaction/milkshake_melon
+ name = "Watermelon Milkshake"
+ id = /datum/reagent/consumable/milkshake_melon
+ results = list(/datum/reagent/consumable/milkshake_melon = 2)
+ required_reagents = list(
+ /datum/reagent/consumable/milkshake_base = 1,
+ /datum/reagent/consumable/watermelonjuice = 1
+ )
diff --git a/modular_splurt/code/modules/jobs/job_types/stowaway.dm b/modular_splurt/code/modules/jobs/job_types/stowaway.dm
new file mode 100644
index 0000000000..76056726d0
--- /dev/null
+++ b/modular_splurt/code/modules/jobs/job_types/stowaway.dm
@@ -0,0 +1,63 @@
+GLOBAL_LIST_EMPTY(roundstart_stowaway)
+
+/datum/job/stowaway
+ title = "Stowaway"
+ flag = STOWAWAY
+ department_head = NONE
+ department_flag = NONE
+ paycheck = 0
+ paycheck_department = null
+ faction = "Station"
+ total_positions = 0
+ spawn_positions = 3
+ threat = 0.5
+ supervisors = "no one"
+ minimal_player_age = 3
+ exp_requirements = 100
+ exp_type = EXP_TYPE_CREW
+ considered_combat_role = FALSE
+ custom_spawn_text = "You are a stowaway. You should NOT be starting fights or attempting to fight security for any reason. At best you should be actively on the run from them as you are on the station illegally. "
+ outfit = /datum/outfit/job/stowaway
+ plasma_outfit = /datum/outfit/plasmaman/stowaway
+ display_order = JOB_DISPLAY_ORDER_STOWAWAY
+
+/datum/job/stowaway/get_latejoin_spawn_point()
+ return get_roundstart_spawn_point()
+
+/datum/outfit/job/stowaway/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
+ ..()
+ var/suited = !preference_source || preference_source.prefs.jumpsuit_style == PREF_SUIT
+ uniform = suited ? /obj/item/clothing/under/color/random : /obj/item/clothing/under/color/jumpskirt/random
+
+/obj/item/card/id/stowaway_stolen
+ name = "unknown ID card"
+ desc = "An strange-looking old ID card."
+ icon_state = "data_1"
+ uses_overlays = FALSE
+ registered_name = null
+ assignment = null
+ access = list(ACCESS_MAINT_TUNNELS)
+ bank_support = ID_NO_BANK_ACCOUNT
+
+/datum/outfit/job/stowaway
+ name = "Stowaway"
+ jobtype = /datum/job/stowaway
+ uniform = /obj/item/clothing/under/color/random
+ shoes = /obj/item/clothing/shoes/sneakers/black
+ id = /obj/item/card/id/stowaway_stolen
+ ears = null
+ belt = /obj/item/storage/belt/utility/atmostech
+ gloves = /obj/item/clothing/gloves/color/yellow
+ r_pocket = /obj/item/radio
+ l_pocket = /obj/item/flashlight
+
+/datum/outfit/plasmaman/stowaway
+ name = "Stowaway Plasmaman"
+ shoes = /obj/item/clothing/shoes/sneakers/black
+ id = /obj/item/card/id/stowaway_stolen
+ ears = null
+ belt = /obj/item/storage/belt/utility/full
+ gloves = /obj/item/clothing/gloves/color/yellow
+ r_pocket = /obj/item/radio
+ l_pocket = /obj/item/flashlight
+
diff --git a/modular_splurt/code/modules/keybindings/keybind/communication.dm b/modular_splurt/code/modules/keybindings/keybind/communication.dm
new file mode 100644
index 0000000000..3aa6aeca1a
--- /dev/null
+++ b/modular_splurt/code/modules/keybindings/keybind/communication.dm
@@ -0,0 +1,13 @@
+/datum/keybinding/client/communication/subtle
+ hotkey_keys = list("Ctrl5")
+
+/datum/keybinding/client/communication/subtle_indicator
+ hotkey_keys = list("5")
+ name = "Subtle_Indicator"
+ full_name = "Subtle Emote (with indicator)"
+ clientside = "subtle-indicator"
+
+/datum/keybinding/client/communication/subtle_indicator/down(client/user)
+ var/mob/living/mob_keybound = user.mob
+ mob_keybound.subtle_indicator()
+ return TRUE
diff --git a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/body_markings.dm b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/body_markings.dm
index ac3b448ca1..1d38f77db0 100644
--- a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/body_markings.dm
+++ b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/body_markings.dm
@@ -113,6 +113,18 @@
icon_state = "sloog"
covered_limbs = list("Chest" = MATRIX_RED_GREEN)
+/datum/sprite_accessory/mam_body_markings/pilot
+ name = "Pilot"
+ icon = 'modular_splurt/icons/mob/mam_markings.dmi'
+ icon_state = "pilot"
+ covered_limbs = list("Head" = MATRIX_ALL)
+
+/datum/sprite_accessory/mam_body_markings/pilot_jaw
+ name = "Pilot Jaw"
+ icon = 'modular_splurt/icons/mob/mam_markings.dmi'
+ icon_state = "pilotjaw"
+ covered_limbs = list("Head" = MATRIX_RED_BLUE)
+
/******************************************
************* Insect Markings *************
*******************************************/
diff --git a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/snouts.dm b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/snouts.dm
index 6b87b38f09..549793e24f 100644
--- a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/snouts.dm
+++ b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/snouts.dm
@@ -221,6 +221,12 @@
icon = 'modular_splurt/icons/mob/mam_snouts.dmi'
color_src = MUTCOLORS
+/datum/sprite_accessory/snouts/mam_snouts/corvidbeak
+ name = "Corvid Beak"
+ icon_state = "corvidbeak"
+ icon = 'modular_splurt/icons/mob/mam_snouts.dmi'
+ matrixed_sections = MATRIX_GREEN
+
/datum/sprite_accessory/snouts/mam_snouts/deoxys
name = "Deoxys"
icon_state = "deoxys"
diff --git a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm
index f08fba7584..7f9eff453e 100644
--- a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm
+++ b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm
@@ -235,6 +235,38 @@
icon_state = "fluffy"
color_src = MUTCOLORS
+/datum/sprite_accessory/tails/mam_tails/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails/mam_tails/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/mam_tails_animated/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
+
//Lizard tails
/datum/sprite_accessory/tails/lizard/tailmaw
name = "Tailmaw"
@@ -278,6 +310,38 @@
icon = 'modular_splurt/icons/mob/mam_tails.dmi'
matrixed_sections = MATRIX_ALL
+/datum/sprite_accessory/tails/lizard/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/lizard/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails/lizard/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/lizard/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
+
//Human tails
/datum/sprite_accessory/tails/human/deer
name = "Deer"
@@ -358,3 +422,35 @@
dimension_x = 64
center = TRUE
color_src = MUTCOLORS
+
+/datum/sprite_accessory/tails/human/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/human/nightstalker
+ name = "Nightstalker"
+ icon_state = "nightstalker"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/mam_tails.dmi'
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails/human/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
+
+/datum/sprite_accessory/tails_animated/human/snakelarge
+ name = "Snake Tail (Large)"
+ icon_state = "snakelarge"
+ color_src = MATRIXED
+ icon = 'modular_splurt/icons/mob/64_mam_tails.dmi'
+ dimension_x = 64
+ center = TRUE
+ matrixed_sections = MATRIX_RED_GREEN
diff --git a/modular_splurt/code/modules/mob/living/carbon/human/emote.dm b/modular_splurt/code/modules/mob/living/carbon/human/emote.dm
new file mode 100644
index 0000000000..8877e15eab
--- /dev/null
+++ b/modular_splurt/code/modules/mob/living/carbon/human/emote.dm
@@ -0,0 +1,12 @@
+/datum/emote/sound/human/huh
+ key = "huh"
+ key_third_person = "huh's"
+ message = "seems confused."
+ sound = 'modular_splurt/sound/voice/huh.ogg'
+
+/datum/emote/sound/human/whine
+ key = "whine"
+ key_third_person = "whines"
+ message = "whines."
+ sound = 'modular_splurt/sound/voice/whine.ogg'
+
diff --git a/modular_splurt/code/modules/mob/living/carbon/human/species_types/zombies2.dm b/modular_splurt/code/modules/mob/living/carbon/human/species_types/zombies2.dm
index 53e887e98c..e812380fbc 100644
--- a/modular_splurt/code/modules/mob/living/carbon/human/species_types/zombies2.dm
+++ b/modular_splurt/code/modules/mob/living/carbon/human/species_types/zombies2.dm
@@ -1,3 +1,4 @@
+/* //striked out for now because I dont know what the fuck was planned here but this is breaking blood regain.
/mob/living/carbon/human/handle_blood()
if(iszombie(src)) //We're basically pudding pops.
return
@@ -8,7 +9,7 @@
var/obj/item/organ/heart/decayed_heart/decaying = getorgan(/obj/item/organ/heart/decayed_heart)
if(decaying)
. += "Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM]."
-
+ */
/datum/species/mammal/undead
// takes 30% more damage but doesn't crit
id = SPECIES_UMAMMAL
diff --git a/modular_splurt/code/modules/mob/living/login.dm b/modular_splurt/code/modules/mob/living/login.dm
new file mode 100644
index 0000000000..2424653534
--- /dev/null
+++ b/modular_splurt/code/modules/mob/living/login.dm
@@ -0,0 +1,10 @@
+/mob/living/Login()
+ . = ..()
+
+ // Check if mob is in SSD list
+ if(src in GLOB.ssd_mob_list)
+ // Remove from SSD list
+ GLOB.ssd_mob_list -= src
+
+ // Log mob SSD removal
+ log_game("[src] was removed from the SSD list.")
diff --git a/modular_splurt/code/modules/mob/living/logout.dm b/modular_splurt/code/modules/mob/living/logout.dm
new file mode 100644
index 0000000000..e640cfbf18
--- /dev/null
+++ b/modular_splurt/code/modules/mob/living/logout.dm
@@ -0,0 +1,25 @@
+/mob/living/Logout()
+ . = ..()
+
+ // Check for BORIS AI borgs
+ if(src in GLOB.available_ai_shells)
+ return
+
+ // Check for the AI
+ if(isAI(src))
+ return
+
+ // Check for VR mob
+ if(src.GetComponent(/datum/component/virtual_reality))
+ return
+
+ // Check if mob is in a VR pod
+ // This is not an ideal solution
+ if(istype(src.loc, /obj/machinery/vr_sleeper))
+ return
+
+ // Add to SSD list
+ GLOB.ssd_mob_list |= src
+
+ // Log mob SSD status
+ log_game("[src] was added to the SSD list.")
diff --git a/modular_splurt/code/modules/mob/living/silicon/robot/robot.dm b/modular_splurt/code/modules/mob/living/silicon/robot/robot.dm
index 7532f3a8f7..65ece9be12 100644
--- a/modular_splurt/code/modules/mob/living/silicon/robot/robot.dm
+++ b/modular_splurt/code/modules/mob/living/silicon/robot/robot.dm
@@ -1,7 +1,7 @@
//Main code edits
/// Allows "cyborg" players to change gender at will - Modularised here
// FUNCTION MOVED TO living.dm AS PROC
-/mob/living/silicon/robot/verb/toggle_gender()
+/mob/living/silicon/robot/toggle_gender()
set name = "Set Gender"
set desc = "Allows you to set your gender."
set category = "Robot Commands"
@@ -41,3 +41,15 @@
laws = new /datum/ai_laws/slaver_override
laws.associate(src)
update_icons()
+
+/mob/living/silicon/robot/Initialize(mapload)
+ .=..()
+ AddComponent(/datum/component/personal_crafting)
+
+
+/mob/living/silicon/robot/pick_module()
+ .=..()
+ var/datum/hud/R = hud_used
+ var/atom/movable/screen/craft/C = locate() in R.static_inventory
+ C.icon = 'icons/mob/screen_midnight.dmi'
+ C.screen_loc = "CENTER+5:5,SOUTH+1:5"
diff --git a/modular_splurt/code/modules/mob/living/silicon/robot/robot_modules.dm b/modular_splurt/code/modules/mob/living/silicon/robot/robot_modules.dm
index 16ddbdc8f5..710f4d4745 100644
--- a/modular_splurt/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/modular_splurt/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -265,7 +265,13 @@
var/list/extra = list(
/obj/item/dogborg/jaws/small,
/obj/item/analyzer/nose,
- /obj/item/soap/tongue/scrubpup
+ /obj/item/soap/tongue/scrubpup,
+ /obj/item/gripper/service,
+ /obj/item/kitchen/rollingpin,
+ /obj/item/kitchen/unrollingpin,
+ /obj/item/kitchen/knife/butcher,
+ /obj/item/kitchen/efink,
+ /obj/item/kitchen/knife
)
LAZYADD(basic_modules, extra)
. = ..()
diff --git a/modular_splurt/code/modules/mob/say_vr.dm b/modular_splurt/code/modules/mob/say_vr.dm
index 56be623e0f..7cb5e02a6f 100644
--- a/modular_splurt/code/modules/mob/say_vr.dm
+++ b/modular_splurt/code/modules/mob/say_vr.dm
@@ -48,3 +48,30 @@
return
message = trim(html_encode(message), MAX_MESSAGE_LEN)
emote("narrate", message=message)
+
+/datum/emote/living/subtle/subtle_indicator
+ key = "subtle-indicator"
+ key_third_person = "subtle-indicator"
+
+/mob/living/verb/subtle_indicator()
+ // Set data
+ set name = "Subtle (Indicator)"
+ set category = "IC"
+
+ // Check if say is disabled
+ if(GLOB.say_disabled)
+ // Warn user and return
+ to_chat(usr, span_danger("Speech is currently admin-disabled."))
+ return
+
+ // Display typing indicator
+ display_typing_indicator()
+
+ // Prompt user for text input
+ var/input_message = input(usr, "What would you like to subtly emote, with a typing indicator?", "Input subtle emote") as message|null
+
+ // Remove typing indicator
+ clear_typing_indicator()
+
+ // Run subtle emote with input
+ usr.emote("subtle", message = input_message)
diff --git a/modular_splurt/code/modules/mob/splurt_emotes.dm b/modular_splurt/code/modules/mob/splurt_emotes.dm
index e6af828c5f..b9d3412e50 100644
--- a/modular_splurt/code/modules/mob/splurt_emotes.dm
+++ b/modular_splurt/code/modules/mob/splurt_emotes.dm
@@ -212,8 +212,8 @@
/datum/emote/living/bruh
key = "bruh"
- key_third_person = "thinks this is a bruh moment"
- message = "thinks this is a bruh moment"
+ key_third_person = "bruhs"
+ message = "thinks this is a bruh moment."
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
restraint_check = FALSE
@@ -378,7 +378,7 @@
/datum/emote/living/swaos
key = "swaos"
key_third_person = "swaos"
- message = "mutters swaos"
+ message = "mutters swaos."
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
@@ -411,7 +411,7 @@
/datum/emote/living/eyebrow3
key = "eyebrow3"
key_third_person = "eyebrows3"
- message = "raises an eyebrow quizzaciously. "
+ message = "raises an eyebrow quizzaciously ."
/datum/emote/living/eyebrow3/run_emote(mob/user, params, type_override, intentional)
if(!(. = ..()))
@@ -467,7 +467,7 @@
/datum/emote/living/laugh4
key = "laugh4"
key_third_person = "laughs4"
- message = "burst out a laugh."
+ message = "burst into laughter!"
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
@@ -497,7 +497,7 @@
/datum/emote/living/laugh6
key = "laugh6"
key_third_person = "laughs6"
- message = "sounds like a tea kettle."
+ message = "laughs like a kettle!"
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
@@ -553,7 +553,7 @@
/datum/emote/living/spoonful
key = "spoonful"
key_third_person = "spoonfuls"
- message = "draws a comically large spoon."
+ message = "asks for a spoonful."
emote_type = EMOTE_AUDIBLE
muzzle_ignore = TRUE
@@ -583,7 +583,7 @@
/datum/emote/living/whatthehell
key = "wth"
key_third_person = "wths"
- message = "condemns the abysses of hell."
+ message = "condemns the abysses of hell!"
emote_type = EMOTE_AUDIBLE
muzzle_ignore = TRUE
@@ -637,7 +637,7 @@
/datum/emote/living/illuminati
key = "illuminati"
key_third_person = "illuminatis"
- message = "emits some X-files vibe"
+ message = "exudes a mysterious aura!"
/datum/emote/living/illuminati/run_emote(mob/user, params, type_override, intentional)
if(!(. = ..()))
@@ -650,7 +650,7 @@
/datum/emote/living/bonerif
key = "bonerif"
key_third_person = "bonerifs"
- message = "riffs"
+ message = "riffs!"
/datum/emote/living/bonerif/run_emote(mob/user, params, type_override, intentional)
if(!(. = ..()))
@@ -679,7 +679,7 @@
/datum/emote/living/choir
key = "choir"
key_third_person = "choirs"
- message = "let out a choir."
+ message = "let out a choir!"
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
@@ -694,7 +694,7 @@
/datum/emote/living/sicko
key = "sicko"
key_third_person = "sickos"
- message = "briefly goes sicko mode."
+ message = "briefly goes sicko mode!"
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
@@ -709,7 +709,7 @@
/datum/emote/living/chill
key = "chill"
key_third_person = "chills"
- message = "felt a chill running down their spine..."
+ message = "feels a chill running down their spine..."
/datum/emote/living/chill/run_emote(mob/user, params, type_override, intentional)
if(!(. = ..()))
@@ -758,6 +758,19 @@
user.nextsoundemote = world.time + 7
playsound(user, 'modular_splurt/sound/voice/weh_s.ogg', 50, 1, -1)
+/datum/emote/living/waa
+ key = "waa"
+ key_third_person = "waas"
+ message = "let out a waa!"
+
+/datum/emote/living/waa/run_emote(mob/user, params, type_override, intentional)
+ if(!(. = ..()))
+ return
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 10
+ playsound(user, 'modular_splurt/sound/voice/waa.ogg', 50, 1, -1)
+
/datum/emote/living/mlem
key = "mlem"
key_third_person = "mlems"
@@ -766,7 +779,7 @@
/datum/emote/living/snore/snore2
key = "snore2"
- key_third_person = "snores"
+ key_third_person = "snores2"
message = "lets out an earthshaking snore"
/datum/emote/living/snore/snore2/run_emote(mob/user, params, type_override, intentional)
@@ -836,7 +849,7 @@
/datum/emote/living/ara_ara
key = "ara"
key_third_person = "aras"
- message = "seems sultrily surprised~"
+ message = "coos with sultry surprise~..."
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
var/voicesound = 'modular_splurt/sound/voice/ara-ara.ogg'
@@ -857,7 +870,7 @@
/datum/emote/living/missouri
key = "missouri"
key_third_person = "missouris"
- message = "appears to believe %THEYRE in Missouri"
+ message = "appears to believe %THEYRE in Missouri."
emote_type = EMOTE_AUDIBLE
muzzle_ignore = FALSE
diff --git a/modular_splurt/code/modules/photography/photos/album.dm b/modular_splurt/code/modules/photography/photos/album.dm
new file mode 100644
index 0000000000..f7795a670e
--- /dev/null
+++ b/modular_splurt/code/modules/photography/photos/album.dm
@@ -0,0 +1,27 @@
+/obj/item/storage/photo_album
+ desc = "A big book used to store photos and mementos."
+ item_state = "album"
+ lefthand_file = 'modular_splurt/icons/mob/inhands/misc/books_lefthand.dmi'
+ righthand_file = 'modular_splurt/icons/mob/inhands/misc/books_righthand.dmi'
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/storage/photo_album/HoS
+ name = "photo album (Head of Security)"
+
+/obj/item/storage/photo_album/RD
+ name = "photo album (Research Director)"
+
+/obj/item/storage/photo_album/HoP
+ name = "photo album (Head of Personnel)"
+
+/obj/item/storage/photo_album/Captain
+ name = "photo album (Captain)"
+
+/obj/item/storage/photo_album/CMO
+ name = "photo album (Chief Medical Officer)"
+
+/obj/item/storage/photo_album/QM
+ name = "photo album (Quartermaster)"
+
+/obj/item/storage/photo_album/CE
+ name = "photo album (Chief Engineer)"
diff --git a/modular_splurt/code/modules/photography/photos/photo.dm b/modular_splurt/code/modules/photography/photos/photo.dm
new file mode 100644
index 0000000000..3eb0f70752
--- /dev/null
+++ b/modular_splurt/code/modules/photography/photos/photo.dm
@@ -0,0 +1,3 @@
+/obj/item/photo/old
+ icon = 'modular_splurt/icons/obj/items_and_weapons.dmi'
+ icon_state = "photo_old"
diff --git a/modular_splurt/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/modular_splurt/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 3b8aad9ea7..46ea1e07c5 100644
--- a/modular_splurt/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/modular_splurt/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -50,3 +50,118 @@
glass_name = "glass of töchtaüse syrup"
glass_desc = "Not for drinking on its own."
+/datum/reagent/consumable/milkshake_base
+ name = "Milkshake"
+ description = "A basic milkshake. Could use something else?"
+ color = "#FFFDD0"
+ nutriment_factor = 1
+ taste_description = "thick, creamy, and sweet"
+ glass_icon_state = "vanillashake"
+ glass_name = "glass of plain milkshake"
+ glass_desc = "A glass of plain milkshake, a bit boring, but still good."
+
+/datum/reagent/consumable/milkshake_vanilla
+ name = "Vanilla Milkshake"
+ description = "A vanilla milkshake. Basic, but delicious."
+ color = "#FFFDD0"
+ nutriment_factor = 1
+ taste_description = "thick, creamy, and sweet"
+ glass_icon_state = "vanillashake"
+ glass_name = "glass of vanilla milkshake"
+ glass_desc = "A glass of vanilla milkshake, a bit boring, but still good."
+
+/datum/reagent/consumable/milkshake_choc
+ name = "Chocolate Milkshake"
+ description = "A delicious Chocolate Milkshake"
+ color = "#7B3F00"
+ nutriment_factor = 1
+ taste_description = "sweet, creamy chocolate"
+ glass_icon_state = "choccyshake"
+ glass_name = "glass of chocolate milkshake"
+ glass_desc = "A glass of chocolate milkshake, what a treat!"
+
+/datum/reagent/consumable/milkshake_strawberry
+ name = "Strawberry Milkshake"
+ description = "Frozen Strawberry Milk!"
+ color = "#F4E1EA"
+ nutriment_factor = 1
+ taste_description = "summer memories"
+ glass_icon_state = "strawberryshake"
+ glass_name = "glass of strawberry milkshake"
+ glass_desc = "A glass of sweet, pink Strawberry Shake"
+
+/datum/reagent/consumable/milkshake_banana
+ name = "Banana Milkshake"
+ description = "Deliciously tricky!"
+ color = "#FFE135"
+ nutriment_factor = 1
+ taste_description = "funny pranks and clowning around"
+ glass_icon_state = "bananashake"
+ glass_name = "glass of Banana Milkshake"
+ glass_desc = "A banana milkshake! Honk!"
+
+/datum/reagent/consumable/milkshake_berry
+ name = "Wild Berry Milkshake"
+ description = "A summer favorite!"
+ color = "#b17179"
+ nutriment_factor = 1
+ taste_description = "warm summer days"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "berryshake"
+ glass_name = "glass of Wild Berry Milkshake"
+ glass_desc = "A berry milkshake"
+
+/datum/reagent/consumable/milkshake_cola
+ name = "Cola Milkshake"
+ description = "Sweet milkshake mixed with cola"
+ color = "#3c3024"
+ nutriment_factor = 1
+ taste_description = "cola and milkshake"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "colashake"
+ glass_name = "glass of Cola Milkshake"
+ glass_desc = "A cola milkshake, it's like a ticker float!"
+
+/datum/reagent/consumable/milkshake_gibb
+ name = "Dr. Gibb Milkshake"
+ description = "Sweet milkshake mixed with Dr. Gibb"
+ color = "#5e312b"
+ nutriment_factor = 1
+ taste_description = "cola and milkshake"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "gibbshake"
+ glass_name = "glass of Gibb Milkshake"
+ glass_desc = "A Dr. Gibb milkshake, it's like a ticker float!"
+
+/datum/reagent/consumable/milkshake_peach
+ name = "Peach Milkshake"
+ description = "A tasty Peach Milkshake"
+ color = "#5e312b"
+ nutriment_factor = 1
+ taste_description = "peaches and cream"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "peachshake"
+ glass_name = "glass of Peace Milkshake"
+ glass_desc = "Peaches and Cream, Peaches and Cream!"
+
+/datum/reagent/consumable/milkshake_pineapple
+ name = "Pineapple Milkshake"
+ description = "A tangy Pineapple Milkshake"
+ color = "#feea63"
+ nutriment_factor = 1
+ taste_description = "citrus and cream"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "pineappleshake"
+ glass_name = "glass of Pineapple Milkshake"
+ glass_desc = "A Pineapple milkshake, a bit sweet and a bit sour, but all delicious!"
+
+/datum/reagent/consumable/milkshake_melon
+ name = "Watermelon Milkshake"
+ description = "Delicous Watermelon Milkshake"
+ color = "#E37383"
+ nutriment_factor = 1
+ taste_description = "warm sun and sweet cream"
+ glass_icon = 'modular_splurt/icons/obj/drinks.dmi'
+ glass_icon_state = "melonshake"
+ glass_name = "glass of Watermelon Milkshake"
+ glass_desc = "A Watermelon milkshake, it's like summer all over again!"
diff --git a/modular_splurt/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/modular_splurt/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index f483f1ec83..07917089d2 100644
--- a/modular_splurt/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/modular_splurt/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -1,10 +1,17 @@
-//Main code edits
-/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M)
- if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO))
- if(!HAS_TRAIT(M, TRAIT_IN_HEAT))
- to_chat(M, span_userlove("Your need for sex is overpowering!"))
- M.log_message("Made In Heat by hexacrocin.", LOG_EMOTE)
- ADD_TRAIT(M, TRAIT_IN_HEAT, APHRO_TRAIT)
+/datum/reagent/drug/aphrodisiacplus/overdose_start(mob/living/M)
+ // Check for pre-existing heat trait
+ if(!HAS_TRAIT(M, TRAIT_ESTROUS_ACTIVE))
+ // Check client preferences
+ if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO))
+ // Add quirk
+ M.add_quirk(/datum/quirk/estrous_active, APHRO_TRAIT)
+
+ // Chat message is handled by the quirk
+
+ // Log interaction
+ M.log_message("Given the In Estrous quirk by hexacrocin overdose.", LOG_EMOTE)
+
+ // Return normally
. = ..()
//Own stuff
diff --git a/modular_splurt/code/modules/reagents/chemistry/reagents/other_reagents.dm b/modular_splurt/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 74d6eb5142..e327fc1409 100644
--- a/modular_splurt/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/modular_splurt/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -28,16 +28,18 @@
/datum/reagent/blood/on_mob_life(mob/living/carbon/C)
. = ..()
- if(HAS_TRAIT(C,BLOODFLEDGE))
- C.adjust_nutrition(6) //3/4ed this, felt it was a bit too much
- C.adjust_disgust(-3) //makes the churches effects easily negated
+ if(HAS_TRAIT(C,TRAIT_BLOODFLEDGE))
+ C.adjust_nutrition(6)
+ C.adjust_disgust(-2) // Negates the chapel's disgust effect
+ C.adjustStaminaLoss(1) // Mitigates the chapel's stamina effect
/datum/reagent/water/holywater/on_mob_life(mob/living/carbon/M)
. = ..()
- //makes holy water slightly disgusting and hungering for vampires
- if(HAS_TRAIT(M,BLOODFLEDGE))
- M.adjust_disgust(1)
- M.adjust_nutrition(-0.1)
+ // Makes holy water disgusting and hungering for bloodfledges
+ // Directly antithetic to the effects of blood
+ if(HAS_TRAIT(M,TRAIT_BLOODFLEDGE))
+ M.adjust_disgust(2)
+ M.adjust_nutrition(-6)
// Cursed blood effect moved here
if(HAS_TRAIT(M, TRAIT_CURSED_BLOOD))
diff --git a/modular_splurt/code/modules/reagents/reagent_containers/borghydro.dm b/modular_splurt/code/modules/reagents/reagent_containers/borghydro.dm
new file mode 100644
index 0000000000..3c3e062b0e
--- /dev/null
+++ b/modular_splurt/code/modules/reagents/reagent_containers/borghydro.dm
@@ -0,0 +1,12 @@
+/obj/item/reagent_containers/borghypo/borgshaker/beershaker/Initialize()
+ var/list/extra_reagents = list(
+ /datum/reagent/consumable/ethanol/amaretto,
+ /datum/reagent/consumable/ethanol/applejack,
+ /datum/reagent/consumable/ethanol/curacao,
+ /datum/reagent/consumable/ethanol/hcider,
+ /datum/reagent/consumable/ethanol/navy_rum,
+ /datum/reagent/consumable/ethanol/sake,
+ /datum/reagent/consumable/ethanol
+ )
+ LAZYADD(reagent_ids, extra_reagents)
+ . = ..()
diff --git a/modular_splurt/code/modules/research/designs/misc_designs.dm b/modular_splurt/code/modules/research/designs/misc_designs.dm
new file mode 100644
index 0000000000..e6105fd3f3
--- /dev/null
+++ b/modular_splurt/code/modules/research/designs/misc_designs.dm
@@ -0,0 +1,2 @@
+/datum/design/light_replacer_blue/New()
+ departmental_flags |= DEPARTMENTAL_FLAG_SCIENCE
diff --git a/modular_splurt/code/modules/research/techweb/nodes/misc_nodes.dm b/modular_splurt/code/modules/research/techweb/nodes/biotech_nodes.dm
similarity index 51%
rename from modular_splurt/code/modules/research/techweb/nodes/misc_nodes.dm
rename to modular_splurt/code/modules/research/techweb/nodes/biotech_nodes.dm
index 7db0714874..ff81163de3 100644
--- a/modular_splurt/code/modules/research/techweb/nodes/misc_nodes.dm
+++ b/modular_splurt/code/modules/research/techweb/nodes/biotech_nodes.dm
@@ -1,3 +1,3 @@
-/datum/techweb_node/datatheory/New()
+/datum/techweb_node/biotech/New()
design_ids += "sex_research"
. = ..()
diff --git a/modular_splurt/code/modules/research/techweb/nodes/robotic_nodes.dm b/modular_splurt/code/modules/research/techweb/nodes/robotic_nodes.dm
index f90fac955d..9256485813 100644
--- a/modular_splurt/code/modules/research/techweb/nodes/robotic_nodes.dm
+++ b/modular_splurt/code/modules/research/techweb/nodes/robotic_nodes.dm
@@ -1,7 +1,7 @@
/datum/techweb_node/neural_programming
design_ids = list("impant_radio")
-/datum/techweb_node/ai/Initialize()
+/datum/techweb_node/ai/New()
var/extra_designs = list(
"slut_module",
"shebang_module",
diff --git a/modular_splurt/code/modules/surgery/organs/augments_arms.dm b/modular_splurt/code/modules/surgery/organs/augments_arms.dm
index d49fe172f5..aa7db49438 100644
--- a/modular_splurt/code/modules/surgery/organs/augments_arms.dm
+++ b/modular_splurt/code/modules/surgery/organs/augments_arms.dm
@@ -24,3 +24,17 @@
w_class = WEIGHT_CLASS_BULKY
sharpness = SHARP_POINTY
attack_verb = list("slashed", "cut")
+
+// Synth power cord interaction override
+/obj/item/apc_powercord/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ // Define user
+ var/mob/living/carbon/human/cord_user = user
+
+ // Check for bloodfledge
+ if(HAS_TRAIT(cord_user, TRAIT_BLOODFLEDGE))
+ // Warn user and return
+ to_chat(cord_user, span_warning("You try to siphon energy from [target], but a sanguine force prevents you from absorbing any charge!"))
+ return
+
+ // Return normally
+ . = ..()
diff --git a/modular_splurt/code/modules/uplink/uplink_items/uplink_devices.dm b/modular_splurt/code/modules/uplink/uplink_items/uplink_devices.dm
new file mode 100644
index 0000000000..fe3de42d03
--- /dev/null
+++ b/modular_splurt/code/modules/uplink/uplink_items/uplink_devices.dm
@@ -0,0 +1,6 @@
+/datum/uplink_item/device_tools/syndicate_ball
+ name = "Syndicate Beach Ball"
+ item = /obj/item/toy/beach_ball/syndicate
+ desc = "A beach ball, it reacts when a vibrator is inserted inside of it. Watch out!"
+ cost = 3
+
diff --git a/modular_splurt/code/modules/vending/clothesmate.dm b/modular_splurt/code/modules/vending/clothesmate.dm
index d7d67e4011..d6dee6e947 100644
--- a/modular_splurt/code/modules/vending/clothesmate.dm
+++ b/modular_splurt/code/modules/vending/clothesmate.dm
@@ -16,7 +16,12 @@
/obj/item/clothing/under/officesexy = 3,
/obj/item/clothing/suit/toggle/tunnelfox = 3,
/obj/item/clothing/under/performer = 2,
- /obj/item/clothing/under/bluedress = 3
+ /obj/item/clothing/under/bluedress = 3,
+ /obj/item/clothing/accessory/shortcrop = 3,
+ /obj/item/clothing/accessory/longcrop = 3,
+ /obj/item/clothing/accessory/formalcrop = 3,
+ /obj/item/clothing/under/misc/leia_outfit = 2,
+ /obj/item/clothing/under/performer/polychromic = 2
)
var/list/extra_contraband = list(
/obj/item/clothing/under/rank/civilian/lawyer/galaxy_red = 3,
diff --git a/modular_splurt/code/modules/vending/kinkmate.dm b/modular_splurt/code/modules/vending/kinkmate.dm
index 1d2f3c5a94..53b6abb542 100644
--- a/modular_splurt/code/modules/vending/kinkmate.dm
+++ b/modular_splurt/code/modules/vending/kinkmate.dm
@@ -29,7 +29,8 @@
/obj/item/restraints/bondage_rope = 5,
/obj/item/clothing/under/domina = 5,
/obj/item/clothing/under/performer = 3,
- /obj/item/clothing/shoes/invisiboots = 10 // Added here to go with the Gear Harness
+ /obj/item/clothing/shoes/invisiboots = 10, // Added here to go with the Gear Harness
+ /obj/item/clothing/shoes/highheel_sandals = 3
)
var/list/extra_contraband = list(
//Lewd-Clothes
diff --git a/modular_splurt/icons/misc/beach.dmi b/modular_splurt/icons/misc/beach.dmi
new file mode 100644
index 0000000000..0bc186df19
Binary files /dev/null and b/modular_splurt/icons/misc/beach.dmi differ
diff --git a/modular_splurt/icons/mob/64_mam_tails.dmi b/modular_splurt/icons/mob/64_mam_tails.dmi
index 115d051436..71249b8896 100644
Binary files a/modular_splurt/icons/mob/64_mam_tails.dmi and b/modular_splurt/icons/mob/64_mam_tails.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/32x48_head.dmi b/modular_splurt/icons/mob/clothing/32x48_head.dmi
new file mode 100644
index 0000000000..d34f1f1e0c
Binary files /dev/null and b/modular_splurt/icons/mob/clothing/32x48_head.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/accessories.dmi b/modular_splurt/icons/mob/clothing/accessories.dmi
new file mode 100644
index 0000000000..024e1d5443
Binary files /dev/null and b/modular_splurt/icons/mob/clothing/accessories.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/current_head_accessories.dmi b/modular_splurt/icons/mob/clothing/current_head_accessories.dmi
new file mode 100644
index 0000000000..6d189aeba5
Binary files /dev/null and b/modular_splurt/icons/mob/clothing/current_head_accessories.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/shoes.dmi b/modular_splurt/icons/mob/clothing/shoes.dmi
index 0956dfbdd1..005bdc7b7d 100644
Binary files a/modular_splurt/icons/mob/clothing/shoes.dmi and b/modular_splurt/icons/mob/clothing/shoes.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/shoes_digi.dmi b/modular_splurt/icons/mob/clothing/shoes_digi.dmi
index a37e1178f1..755d9c92e9 100644
Binary files a/modular_splurt/icons/mob/clothing/shoes_digi.dmi and b/modular_splurt/icons/mob/clothing/shoes_digi.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/uniform.dmi b/modular_splurt/icons/mob/clothing/uniform.dmi
index 1555bade4d..dccf18cc43 100644
Binary files a/modular_splurt/icons/mob/clothing/uniform.dmi and b/modular_splurt/icons/mob/clothing/uniform.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/uniform_digi.dmi b/modular_splurt/icons/mob/clothing/uniform_digi.dmi
index ce963b6af7..951d678785 100644
Binary files a/modular_splurt/icons/mob/clothing/uniform_digi.dmi and b/modular_splurt/icons/mob/clothing/uniform_digi.dmi differ
diff --git a/modular_splurt/icons/mob/inhands/misc/books_lefthand.dmi b/modular_splurt/icons/mob/inhands/misc/books_lefthand.dmi
new file mode 100644
index 0000000000..cd59fe6d03
Binary files /dev/null and b/modular_splurt/icons/mob/inhands/misc/books_lefthand.dmi differ
diff --git a/modular_splurt/icons/mob/inhands/misc/books_righthand.dmi b/modular_splurt/icons/mob/inhands/misc/books_righthand.dmi
new file mode 100644
index 0000000000..a2fc3cc73b
Binary files /dev/null and b/modular_splurt/icons/mob/inhands/misc/books_righthand.dmi differ
diff --git a/modular_splurt/icons/mob/inhands/weapons/melee_lefthand.dmi b/modular_splurt/icons/mob/inhands/weapons/melee_lefthand.dmi
index 93508f584a..751e41c260 100644
Binary files a/modular_splurt/icons/mob/inhands/weapons/melee_lefthand.dmi and b/modular_splurt/icons/mob/inhands/weapons/melee_lefthand.dmi differ
diff --git a/modular_splurt/icons/mob/inhands/weapons/melee_righthand.dmi b/modular_splurt/icons/mob/inhands/weapons/melee_righthand.dmi
index 71d886823e..da23681d2a 100644
Binary files a/modular_splurt/icons/mob/inhands/weapons/melee_righthand.dmi and b/modular_splurt/icons/mob/inhands/weapons/melee_righthand.dmi differ
diff --git a/modular_splurt/icons/mob/landmarks.dmi b/modular_splurt/icons/mob/landmarks.dmi
index 7ee3942be1..5c596e0c95 100644
Binary files a/modular_splurt/icons/mob/landmarks.dmi and b/modular_splurt/icons/mob/landmarks.dmi differ
diff --git a/modular_splurt/icons/mob/mam_markings.dmi b/modular_splurt/icons/mob/mam_markings.dmi
index d2b389c66e..8d2ca68181 100644
Binary files a/modular_splurt/icons/mob/mam_markings.dmi and b/modular_splurt/icons/mob/mam_markings.dmi differ
diff --git a/modular_splurt/icons/mob/mam_snouts.dmi b/modular_splurt/icons/mob/mam_snouts.dmi
index 27bab79b2d..fb6071b8c2 100644
Binary files a/modular_splurt/icons/mob/mam_snouts.dmi and b/modular_splurt/icons/mob/mam_snouts.dmi differ
diff --git a/modular_splurt/icons/mob/mam_tails.dmi b/modular_splurt/icons/mob/mam_tails.dmi
index 4d4f4e6755..e2ec928443 100644
Binary files a/modular_splurt/icons/mob/mam_tails.dmi and b/modular_splurt/icons/mob/mam_tails.dmi differ
diff --git a/modular_splurt/icons/mobs/suits.dmi b/modular_splurt/icons/mobs/suits.dmi
index 1369bc912a..57f709fdbb 100644
Binary files a/modular_splurt/icons/mobs/suits.dmi and b/modular_splurt/icons/mobs/suits.dmi differ
diff --git a/modular_splurt/icons/obj/card.dmi b/modular_splurt/icons/obj/card.dmi
new file mode 100644
index 0000000000..6c7eaeb624
Binary files /dev/null and b/modular_splurt/icons/obj/card.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/accessories.dmi b/modular_splurt/icons/obj/clothing/accessories.dmi
new file mode 100644
index 0000000000..c15501ea74
Binary files /dev/null and b/modular_splurt/icons/obj/clothing/accessories.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/hats.dmi b/modular_splurt/icons/obj/clothing/hats.dmi
index 472ff0cb1b..7af75114a5 100644
Binary files a/modular_splurt/icons/obj/clothing/hats.dmi and b/modular_splurt/icons/obj/clothing/hats.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/shoes.dmi b/modular_splurt/icons/obj/clothing/shoes.dmi
index 71b62b42bc..311174d23d 100644
Binary files a/modular_splurt/icons/obj/clothing/shoes.dmi and b/modular_splurt/icons/obj/clothing/shoes.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/suits.dmi b/modular_splurt/icons/obj/clothing/suits.dmi
index 418f999575..de921cc6bd 100644
Binary files a/modular_splurt/icons/obj/clothing/suits.dmi and b/modular_splurt/icons/obj/clothing/suits.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/uniforms.dmi b/modular_splurt/icons/obj/clothing/uniforms.dmi
index 3880f77532..f6648a1fac 100644
Binary files a/modular_splurt/icons/obj/clothing/uniforms.dmi and b/modular_splurt/icons/obj/clothing/uniforms.dmi differ
diff --git a/modular_splurt/icons/obj/drinks.dmi b/modular_splurt/icons/obj/drinks.dmi
index a1541fddb1..4ea6784f4e 100644
Binary files a/modular_splurt/icons/obj/drinks.dmi and b/modular_splurt/icons/obj/drinks.dmi differ
diff --git a/modular_splurt/icons/obj/items_and_weapons.dmi b/modular_splurt/icons/obj/items_and_weapons.dmi
index b0c383513b..a5b3d02d5b 100644
Binary files a/modular_splurt/icons/obj/items_and_weapons.dmi and b/modular_splurt/icons/obj/items_and_weapons.dmi differ
diff --git a/modular_splurt/sound/voice/huh.ogg b/modular_splurt/sound/voice/huh.ogg
new file mode 100644
index 0000000000..accf4cab3c
Binary files /dev/null and b/modular_splurt/sound/voice/huh.ogg differ
diff --git a/modular_splurt/sound/voice/waa.ogg b/modular_splurt/sound/voice/waa.ogg
new file mode 100644
index 0000000000..9e5ba7866f
Binary files /dev/null and b/modular_splurt/sound/voice/waa.ogg differ
diff --git a/modular_splurt/sound/voice/whine.ogg b/modular_splurt/sound/voice/whine.ogg
new file mode 100644
index 0000000000..2d2f825a83
Binary files /dev/null and b/modular_splurt/sound/voice/whine.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index a00bce28c7..984621b38c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -109,6 +109,7 @@
#include "code\__DEFINES\rust_g.dm"
#include "code\__DEFINES\rust_g_overrides.dm"
#include "code\__DEFINES\say.dm"
+#include "code\__DEFINES\screentips.dm"
#include "code\__DEFINES\security_levels.dm"
#include "code\__DEFINES\shuttles.dm"
#include "code\__DEFINES\sight.dm"
@@ -160,7 +161,9 @@
#include "code\__DEFINES\dcs\signals\signals_medical.dm"
#include "code\__DEFINES\dcs\signals\signals_mod.dm"
#include "code\__DEFINES\dcs\signals\signals_movable.dm"
+#include "code\__DEFINES\dcs\signals\signals_painting.dm"
#include "code\__DEFINES\dcs\signals\signals_reagent.dm"
+#include "code\__DEFINES\dcs\signals\signals_screentips.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm"
@@ -215,6 +218,7 @@
#include "code\__HELPERS\reagents.dm"
#include "code\__HELPERS\roundend.dm"
#include "code\__HELPERS\sanitize_values.dm"
+#include "code\__HELPERS\screentips.dm"
#include "code\__HELPERS\shell.dm"
#include "code\__HELPERS\stat_tracking.dm"
#include "code\__HELPERS\text.dm"
@@ -236,6 +240,7 @@
#include "code\__HELPERS\sorts\MergeSort.dm"
#include "code\__HELPERS\sorts\TimSort.dm"
#include "code\__SANDCODE\DEFINES\chat.dm"
+#include "code\__SANDCODE\DEFINES\DNA.dm"
#include "code\__SANDCODE\DEFINES\language.dm"
#include "code\__SANDCODE\DEFINES\lewd.dm"
#include "code\__SANDCODE\DEFINES\loadout.dm"
@@ -251,6 +256,7 @@
#include "code\__SANDCODE\DEFINES\subsystems.dm"
#include "code\__SANDCODE\DEFINES\traits.dm"
#include "code\__SANDCODE\DEFINES\wires.dm"
+#include "code\__SANDCODE\DEFINES\dcs\signals\signals_mob_main.dm"
#include "code\__SANDCODE\DEFINES\dcs\signals\sizecode.dm"
#include "code\__SANDCODE\HELPERS\sizecode.dm"
#include "code\__SPLURTCODE\DEFINES\access.dm"
@@ -266,6 +272,7 @@
#include "code\__SPLURTCODE\DEFINES\lewd.dm"
#include "code\__SPLURTCODE\DEFINES\login.dm"
#include "code\__SPLURTCODE\DEFINES\mobs.dm"
+#include "code\__SPLURTCODE\DEFINES\preferences.dm"
#include "code\__SPLURTCODE\DEFINES\pregnancy.dm"
#include "code\__SPLURTCODE\DEFINES\quirks.dm"
#include "code\__SPLURTCODE\DEFINES\radiation.dm"
@@ -765,6 +772,10 @@
#include "code\datums\elements\ventcrawling.dm"
#include "code\datums\elements\weather_listener.dm"
#include "code\datums\elements\wuv.dm"
+#include "code\datums\elements\screentips\contextual_screentip_bare_hands.dm"
+#include "code\datums\elements\screentips\contextual_screentip_item_typechecks.dm"
+#include "code\datums\elements\screentips\contextual_screentip_sharpness.dm"
+#include "code\datums\elements\screentips\contextual_screentip_tools.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\icon_snapshot.dm"
@@ -820,6 +831,8 @@
#include "code\datums\ruins\lavaland.dm"
#include "code\datums\ruins\space.dm"
#include "code\datums\ruins\station.dm"
+#include "code\datums\screentips\atom_context.dm"
+#include "code\datums\screentips\item_context.dm"
#include "code\datums\skills\_check_skills.dm"
#include "code\datums\skills\_skill.dm"
#include "code\datums\skills\_skill_holder.dm"
@@ -1517,6 +1530,7 @@
#include "code\modules\admin\create_object.dm"
#include "code\modules\admin\create_poll.dm"
#include "code\modules\admin\create_turf.dm"
+#include "code\modules\admin\force_event.dm"
#include "code\modules\admin\fun_balloon.dm"
#include "code\modules\admin\holder2.dm"
#include "code\modules\admin\ipintel.dm"
@@ -1995,6 +2009,7 @@
#include "code\modules\buildmode\submodes\throwing.dm"
#include "code\modules\buildmode\submodes\variable_edit.dm"
#include "code\modules\cargo\bounty.dm"
+#include "code\modules\cargo\bounty_console.dm"
#include "code\modules\cargo\centcom_podlauncher.dm"
#include "code\modules\cargo\console.dm"
#include "code\modules\cargo\coupon.dm"
@@ -2222,6 +2237,7 @@
#include "code\modules\events\processor_overload.dm"
#include "code\modules\events\radiation_storm.dm"
#include "code\modules\events\sentience.dm"
+#include "code\modules\events\shuttle_catastrophe.dm"
#include "code\modules\events\shuttle_loan.dm"
#include "code\modules\events\space_dragon.dm"
#include "code\modules\events\space_ninja.dm"
@@ -2689,6 +2705,7 @@
#include "code\modules\mob\living\brain\status_procs.dm"
#include "code\modules\mob\living\carbon\carbon.dm"
#include "code\modules\mob\living\carbon\carbon_active_parry.dm"
+#include "code\modules\mob\living\carbon\carbon_context.dm"
#include "code\modules\mob\living\carbon\carbon_defense.dm"
#include "code\modules\mob\living\carbon\carbon_defines.dm"
#include "code\modules\mob\living\carbon\carbon_movement.dm"
@@ -2744,6 +2761,7 @@
#include "code\modules\mob\living\carbon\human\examine.dm"
#include "code\modules\mob\living\carbon\human\human.dm"
#include "code\modules\mob\living\carbon\human\human_block.dm"
+#include "code\modules\mob\living\carbon\human\human_context.dm"
#include "code\modules\mob\living\carbon\human\human_defense.dm"
#include "code\modules\mob\living\carbon\human\human_defines.dm"
#include "code\modules\mob\living\carbon\human\human_helpers.dm"
@@ -3737,10 +3755,12 @@
#include "code\modules\tgui\states\debug.dm"
#include "code\modules\tgui\states\deep_inventory.dm"
#include "code\modules\tgui\states\default.dm"
+#include "code\modules\tgui\states\fun.dm"
#include "code\modules\tgui\states\hands.dm"
#include "code\modules\tgui\states\human_adjacent.dm"
#include "code\modules\tgui\states\inventory.dm"
#include "code\modules\tgui\states\language_menu.dm"
+#include "code\modules\tgui\states\never.dm"
#include "code\modules\tgui\states\new_player.dm"
#include "code\modules\tgui\states\not_incapacitated.dm"
#include "code\modules\tgui\states\notcontained.dm"
@@ -3885,6 +3905,7 @@
#include "modular_citadel\code\modules\client\loadout\_medical.dm"
#include "modular_citadel\code\modules\client\loadout\_security.dm"
#include "modular_citadel\code\modules\client\loadout\_service.dm"
+#include "modular_citadel\code\modules\client\loadout\accessory.dm"
#include "modular_citadel\code\modules\client\loadout\backpack.dm"
#include "modular_citadel\code\modules\client\loadout\glasses.dm"
#include "modular_citadel\code\modules\client\loadout\gloves.dm"
@@ -3955,17 +3976,20 @@
#include "modular_citadel\code\modules\vectorcrafts\vectortruck.dm"
#include "modular_citadel\code\modules\vectorcrafts\vectorvariants.dm"
#include "modular_sand\code\_globalvars\bitfields.dm"
+#include "modular_sand\code\_globalvars\lists\lewd_content.dm"
#include "modular_sand\code\_globalvars\lists\misc.dm"
#include "modular_sand\code\_globalvars\lists\objects.dm"
#include "modular_sand\code\_onclick\hud\hud.dm"
#include "modular_sand\code\_onclick\hud\screen_objects.dm"
#include "modular_sand\code\controllers\configuration\entries\sandstorm.dm"
+#include "modular_sand\code\controllers\configuration\entries\sandstorm_balance.dm"
#include "modular_sand\code\controllers\subsystem\interactions.dm"
#include "modular_sand\code\controllers\subsystem\job.dm"
#include "modular_sand\code\controllers\subsystem\language.dm"
#include "modular_sand\code\datums\action.dm"
#include "modular_sand\code\datums\ai_laws.dm"
#include "modular_sand\code\datums\shuttles.dm"
+#include "modular_sand\code\datums\components\interaction_menu_granter.dm"
#include "modular_sand\code\datums\components\mood.dm"
#include "modular_sand\code\datums\components\riding.dm"
#include "modular_sand\code\datums\components\container_item\container_item.dm"
@@ -3974,8 +3998,8 @@
#include "modular_sand\code\datums\components\storage\concrete\dresser.dm"
#include "modular_sand\code\datums\diseases\advance\symptoms\species.dm"
#include "modular_sand\code\datums\elements\holder_micro.dm"
+#include "modular_sand\code\datums\elements\skirt_peeking.dm"
#include "modular_sand\code\datums\interactions\_interaction.dm"
-#include "modular_sand\code\datums\interactions\interaction_interface.dm"
#include "modular_sand\code\datums\interactions\interaction_mob.dm"
#include "modular_sand\code\datums\interactions\lewd_definitions.dm"
#include "modular_sand\code\datums\interactions\lewd_interactions.dm"
@@ -4017,6 +4041,7 @@
#include "modular_sand\code\game\machinery\cryopod.dm"
#include "modular_sand\code\game\machinery\cryptominers.dm"
#include "modular_sand\code\game\machinery\Sleeper.dm"
+#include "modular_sand\code\game\machinery\computer\cloning.dm"
#include "modular_sand\code\game\machinery\computer\arcade\tetris.dm"
#include "modular_sand\code\game\machinery\pipe\construction.dm"
#include "modular_sand\code\game\machinery\telecomms\machine_interactions.dm"
@@ -4036,6 +4061,7 @@
#include "modular_sand\code\game\objects\items\extinguisher.dm"
#include "modular_sand\code\game\objects\items\fleshlight.dm"
#include "modular_sand\code\game\objects\items\miscellaneous.dm"
+#include "modular_sand\code\game\objects\items\plushes.dm"
#include "modular_sand\code\game\objects\items\circuitboards\computer_circuitboards.dm"
#include "modular_sand\code\game\objects\items\circuitboards\machine_circuitboards.dm"
#include "modular_sand\code\game\objects\items\devices\dogborg_sleeper.dm"
@@ -4107,6 +4133,7 @@
#include "modular_sand\code\modules\client\preferences_savefile.dm"
#include "modular_sand\code\modules\client\loadout\_security.dm"
#include "modular_sand\code\modules\client\loadout\accessories.dm"
+#include "modular_sand\code\modules\client\loadout\backpack.dm"
#include "modular_sand\code\modules\client\loadout\boxers.dm"
#include "modular_sand\code\modules\client\loadout\hands.dm"
#include "modular_sand\code\modules\client\loadout\head.dm"
@@ -4128,12 +4155,7 @@
#include "modular_sand\code\modules\clothing\spacesuits\hardsuit.dm"
#include "modular_sand\code\modules\clothing\suits\miscellaneous.dm"
#include "modular_sand\code\modules\clothing\under\_under.dm"
-#include "modular_sand\code\modules\clothing\under\color.dm"
#include "modular_sand\code\modules\clothing\under\costumes.dm"
-#include "modular_sand\code\modules\clothing\under\misc.dm"
-#include "modular_sand\code\modules\clothing\under\skirt_dress.dm"
-#include "modular_sand\code\modules\clothing\under\suits.dm"
-#include "modular_sand\code\modules\clothing\under\syndicate.dm"
#include "modular_sand\code\modules\clothing\under\uniform.dm"
#include "modular_sand\code\modules\clothing\underwear\_underwear.dm"
#include "modular_sand\code\modules\clothing\underwear\boxers.dm"
@@ -4150,6 +4172,7 @@
#include "modular_sand\code\modules\integrated_electronics\subtypes\output.dm"
#include "modular_sand\code\modules\jobs\job_types\_job.dm"
#include "modular_sand\code\modules\jobs\job_types\_job_alt_titles.dm"
+#include "modular_sand\code\modules\jobs\job_types\prisoner.dm"
#include "modular_sand\code\modules\keybindings\keybind\carbon.dm"
#include "modular_sand\code\modules\language\dragon.dm"
#include "modular_sand\code\modules\language\language.dm"
@@ -4177,13 +4200,15 @@
#include "modular_sand\code\modules\mob\living\carbon\carbon.dm"
#include "modular_sand\code\modules\mob\living\carbon\life.dm"
#include "modular_sand\code\modules\mob\living\carbon\show.dm"
-#include "modular_sand\code\modules\mob\living\carbon\human\examine.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\human.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\human_defines.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\human_stripping.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\life.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\species.dm"
+#include "modular_sand\code\modules\mob\living\carbon\human\species_types\anthropomorph.dm"
+#include "modular_sand\code\modules\mob\living\carbon\human\species_types\ipc.dm"
#include "modular_sand\code\modules\mob\living\carbon\human\species_types\lizardpeople.dm"
+#include "modular_sand\code\modules\mob\living\carbon\human\species_types\synthliz.dm"
#include "modular_sand\code\modules\mob\living\silicon\silicon.dm"
#include "modular_sand\code\modules\mob\living\silicon\ai\ai.dm"
#include "modular_sand\code\modules\mob\living\silicon\ai\vox_sounds.dm"
@@ -4231,6 +4256,7 @@
#include "modular_sand\code\modules\reagents\chemistry\reagents\alcohol_reagents.dm"
#include "modular_sand\code\modules\reagents\chemistry\reagents\cit_reagents.dm"
#include "modular_sand\code\modules\reagents\chemistry\reagents\drink_reagents.dm"
+#include "modular_sand\code\modules\reagents\chemistry\reagents\fermi_reagents.dm"
#include "modular_sand\code\modules\reagents\chemistry\reagents\medicine_reagents.dm"
#include "modular_sand\code\modules\reagents\chemistry\reagents\other_reagents.dm"
#include "modular_sand\code\modules\reagents\chemistry\recipes\others.dm"
@@ -4305,6 +4331,7 @@
#include "modular_splurt\code\_onclick\hud\robot.dm"
#include "modular_splurt\code\_onclick\hud\screen_objects.dm"
#include "modular_splurt\code\_rendering\fullscreen\fullscreen.dm"
+#include "modular_splurt\code\controllers\configuration\entries\splurt_autocryo.dm"
#include "modular_splurt\code\controllers\configuration\entries\splurt_comms.dm"
#include "modular_splurt\code\controllers\configuration\entries\splurt_connections.dm"
#include "modular_splurt\code\controllers\configuration\entries\splurt_discord.dm"
@@ -4313,6 +4340,7 @@
#include "modular_splurt\code\controllers\configuration\entries\splurt_general.dm"
#include "modular_splurt\code\controllers\configuration\entries\splurt_maps.dm"
#include "modular_splurt\code\controllers\configuration\entries\splurt_server.dm"
+#include "modular_splurt\code\controllers\subsystem\afk.dm"
#include "modular_splurt\code\controllers\subsystem\discord.dm"
#include "modular_splurt\code\controllers\subsystem\redbot.dm"
#include "modular_splurt\code\controllers\subsystem\ticker.dm"
@@ -4336,6 +4364,7 @@
#include "modular_splurt\code\datums\components\crafting\recipes\recipes_robot.dm"
#include "modular_splurt\code\datums\components\storage\concrete\pockets.dm"
#include "modular_splurt\code\datums\elements\crawl_under.dm"
+#include "modular_splurt\code\datums\elements\mob_holder.dm"
#include "modular_splurt\code\datums\elements\smalltalk.dm"
#include "modular_splurt\code\datums\elements\spooky.dm"
#include "modular_splurt\code\datums\elements\wuv.dm"
@@ -4552,6 +4581,7 @@
#include "modular_splurt\code\modules\client\client_procs.dm"
#include "modular_splurt\code\modules\client\preferences.dm"
#include "modular_splurt\code\modules\client\preferences_savefile.dm"
+#include "modular_splurt\code\modules\client\loadout\accessory.dm"
#include "modular_splurt\code\modules\client\loadout\backpack.dm"
#include "modular_splurt\code\modules\client\loadout\boxers.dm"
#include "modular_splurt\code\modules\client\loadout\glasses.dm"
@@ -4572,14 +4602,19 @@
#include "modular_splurt\code\modules\client\verbs\looc.dm"
#include "modular_splurt\code\modules\client\verbs\ooc.dm"
#include "modular_splurt\code\modules\clothing\back.dm"
+#include "modular_splurt\code\modules\clothing\clothing.dm"
#include "modular_splurt\code\modules\clothing\gloves.dm"
#include "modular_splurt\code\modules\clothing\kinkyclothes.dm"
#include "modular_splurt\code\modules\clothing\sizeaccessories.dm"
#include "modular_splurt\code\modules\clothing\glasses\_glasses.dm"
#include "modular_splurt\code\modules\clothing\glasses\hud.dm"
+#include "modular_splurt\code\modules\clothing\gloves\_gloves.dm"
+#include "modular_splurt\code\modules\clothing\head\_head.dm"
+#include "modular_splurt\code\modules\clothing\head\hardhat.dm"
#include "modular_splurt\code\modules\clothing\head\helmet.dm"
#include "modular_splurt\code\modules\clothing\head\jobs.dm"
#include "modular_splurt\code\modules\clothing\head\misc.dm"
+#include "modular_splurt\code\modules\clothing\head\misc_special.dm"
#include "modular_splurt\code\modules\clothing\lewd_clothing\collar\kink_collars.dm"
#include "modular_splurt\code\modules\clothing\lewd_clothing\eyes\hypnogoggles.dm"
#include "modular_splurt\code\modules\clothing\lewd_clothing\foot\lewd_shoes.dm"
@@ -4587,11 +4622,15 @@
#include "modular_splurt\code\modules\clothing\lewd_clothing\head\deprivation_helmet.dm"
#include "modular_splurt\code\modules\clothing\lewd_clothing\head\hats.dm"
#include "modular_splurt\code\modules\clothing\lewd_clothing\uniform\latex_catsuit.dm"
+#include "modular_splurt\code\modules\clothing\masks\_mask.dm"
+#include "modular_splurt\code\modules\clothing\masks\boxing.dm"
#include "modular_splurt\code\modules\clothing\masks\gasmask.dm"
#include "modular_splurt\code\modules\clothing\masks\hailer.dm"
#include "modular_splurt\code\modules\clothing\masks\miscellaneous.dm"
#include "modular_splurt\code\modules\clothing\neck\_neck.dm"
#include "modular_splurt\code\modules\clothing\outfits\ert.dm"
+#include "modular_splurt\code\modules\clothing\shoes\_shoes.dm"
+#include "modular_splurt\code\modules\clothing\shoes\magboots.dm"
#include "modular_splurt\code\modules\clothing\shoes\miscellaneous.dm"
#include "modular_splurt\code\modules\clothing\spacesuits\hardsuit.dm"
#include "modular_splurt\code\modules\clothing\suits\armor.dm"
@@ -4599,14 +4638,17 @@
#include "modular_splurt\code\modules\clothing\suits\heavy.dm"
#include "modular_splurt\code\modules\clothing\suits\jobs.dm"
#include "modular_splurt\code\modules\clothing\suits\miscellaneous.dm"
+#include "modular_splurt\code\modules\clothing\suits\utility.dm"
#include "modular_splurt\code\modules\clothing\suits\vest.dm"
#include "modular_splurt\code\modules\clothing\under\_under.dm"
+#include "modular_splurt\code\modules\clothing\under\accessories.dm"
#include "modular_splurt\code\modules\clothing\under\miscellaneous.dm"
#include "modular_splurt\code\modules\clothing\under\shorts.dm"
#include "modular_splurt\code\modules\clothing\under\jobs\command.dm"
#include "modular_splurt\code\modules\clothing\under\jobs\engineering.dm"
#include "modular_splurt\code\modules\clothing\under\jobs\security.dm"
#include "modular_splurt\code\modules\clothing\under\jobs\civilian\civilian.dm"
+#include "modular_splurt\code\modules\clothing\underwear\_underwear.dm"
#include "modular_splurt\code\modules\clothing\underwear\boxers.dm"
#include "modular_splurt\code\modules\clothing\underwear\shirts.dm"
#include "modular_splurt\code\modules\clothing\underwear\socks.dm"
@@ -4651,6 +4693,8 @@
#include "modular_splurt\code\modules\jobs\job_types\security_officer.dm"
#include "modular_splurt\code\modules\jobs\job_types\service.dm"
#include "modular_splurt\code\modules\jobs\job_types\station_engineer.dm"
+#include "modular_splurt\code\modules\jobs\job_types\stowaway.dm"
+#include "modular_splurt\code\modules\keybindings\keybind\communication.dm"
#include "modular_splurt\code\modules\keybindings\keybind\human.dm"
#include "modular_splurt\code\modules\keybindings\keybind\movement.dm"
#include "modular_splurt\code\modules\language\xenocommon.dm"
@@ -4685,10 +4729,13 @@
#include "modular_splurt\code\modules\mob\living\living_mobility.dm"
#include "modular_splurt\code\modules\mob\living\living_movement.dm"
#include "modular_splurt\code\modules\mob\living\living_signals.dm"
+#include "modular_splurt\code\modules\mob\living\login.dm"
+#include "modular_splurt\code\modules\mob\living\logout.dm"
#include "modular_splurt\code\modules\mob\living\navigation.dm"
#include "modular_splurt\code\modules\mob\living\say.dm"
#include "modular_splurt\code\modules\mob\living\brain\brain_item.dm"
#include "modular_splurt\code\modules\mob\living\carbon\carbon.dm"
+#include "modular_splurt\code\modules\mob\living\carbon\human\emote.dm"
#include "modular_splurt\code\modules\mob\living\carbon\human\human.dm"
#include "modular_splurt\code\modules\mob\living\carbon\human\human_defines.dm"
#include "modular_splurt\code\modules\mob\living\carbon\human\inventory.dm"
@@ -4721,6 +4768,8 @@
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\megafauna\king_of_goats.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\megafauna\sand.dm"
#include "modular_splurt\code\modules\paperwork\pen.dm"
+#include "modular_splurt\code\modules\photography\photos\album.dm"
+#include "modular_splurt\code\modules\photography\photos\photo.dm"
#include "modular_splurt\code\modules\power\cell.dm"
#include "modular_splurt\code\modules\power\reactor\fluffed.dm"
#include "modular_splurt\code\modules\projectiles\ammunition\ballistic\pistol.dm"
@@ -4753,6 +4802,7 @@
#include "modular_splurt\code\modules\reagents\chemistry\reagents\other_reagents.dm"
#include "modular_splurt\code\modules\reagents\chemistry\recipes\drugs.dm"
#include "modular_splurt\code\modules\reagents\chemistry\recipes\lewd.dm"
+#include "modular_splurt\code\modules\reagents\reagent_containers\borghydro.dm"
#include "modular_splurt\code\modules\reagents\reagent_containers\bottle.dm"
#include "modular_splurt\code\modules\reagents\reagent_containers\hypospray.dm"
#include "modular_splurt\code\modules\reagents\reagent_containers\hypovial.dm"
@@ -4766,15 +4816,16 @@
#include "modular_splurt\code\modules\research\designs\mecha_designs.dm"
#include "modular_splurt\code\modules\research\designs\mechfabricator_designs.dm"
#include "modular_splurt\code\modules\research\designs\medical_designs.dm"
+#include "modular_splurt\code\modules\research\designs\misc_designs.dm"
#include "modular_splurt\code\modules\research\designs\power_designs.dm"
#include "modular_splurt\code\modules\research\designs\stock_parts_designs.dm"
#include "modular_splurt\code\modules\research\designs\tool_designs.dm"
#include "modular_splurt\code\modules\research\designs\autolathe_desings\autolathe_designs_sec_and_hacked.dm"
#include "modular_splurt\code\modules\research\designs\machine_designs\machine_designs_all_misc.dm"
+#include "modular_splurt\code\modules\research\techweb\nodes\biotech_nodes.dm"
#include "modular_splurt\code\modules\research\techweb\nodes\bluespace_nodes.dm"
#include "modular_splurt\code\modules\research\techweb\nodes\mecha_nodes.dm"
#include "modular_splurt\code\modules\research\techweb\nodes\medical_nodes.dm"
-#include "modular_splurt\code\modules\research\techweb\nodes\misc_nodes.dm"
#include "modular_splurt\code\modules\research\techweb\nodes\robotic_nodes.dm"
#include "modular_splurt\code\modules\resize\smallsprite_action.dm"
#include "modular_splurt\code\modules\ruins\objects_and_mobs\ash_walker_den.dm"
diff --git a/tgui/packages/common/collections.ts b/tgui/packages/common/collections.ts
index a5ccd7003e..ae3f402b13 100644
--- a/tgui/packages/common/collections.ts
+++ b/tgui/packages/common/collections.ts
@@ -306,3 +306,24 @@ export const zip = (...arrays: T): Zip => {
export const zipWith = iterateeFn => (...arrays) => {
return map(values => iterateeFn(...values))(zip(...arrays));
};
+
+/**
+ * This method takes a collection of items and a number, returning a collection
+ * of collections, where the maximum amount of items in each is that second arg
+ */
+export const paginate = (collection: T[], maxPerPage: number): T[][] => {
+ const pages: T[][] = [];
+ let page: T[] = [];
+ let itemsToAdd = maxPerPage;
+
+ for (const item of collection) {
+ page.push(item);
+ itemsToAdd--;
+ if (!itemsToAdd) {
+ itemsToAdd = maxPerPage;
+ pages.push(page);
+ page = [];
+ }
+ }
+ return pages;
+};
diff --git a/tgui/packages/tgui/interfaces/CargoBountyConsole.js b/tgui/packages/tgui/interfaces/CargoBountyConsole.js
new file mode 100644
index 0000000000..ad485231eb
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CargoBountyConsole.js
@@ -0,0 +1,119 @@
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, Section, Table } from '../components';
+import { formatMoney } from '../format';
+import { Window } from '../layouts';
+
+export const CargoBountyConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ bountydata = [],
+ } = data;
+ return (
+
+
+ }
+ buttons={(
+ act('Print')} />
+ )}>
+
+
+
+ Bounty Object
+
+
+ Description
+
+
+ Progress
+
+
+ Value
+
+
+ Claim
+
+
+ {bountydata.map(bounty => (
+
+
+ {bounty.name}
+
+
+ {bounty.description}
+
+
+ {bounty.priority === 1
+ ? High Priority
+ : ""}
+ {bounty.completion_string}
+
+
+ {bounty.reward_string}
+
+
+ act('ClaimBounty', {
+ bounty: bounty.bounty_ref,
+ })} />
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const BountyHeader = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ stored_cash,
+ } = data;
+ return (
+
+ formatMoney(value)} />
+ {' credits'}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/DecalPainter.js b/tgui/packages/tgui/interfaces/DecalPainter.js
deleted file mode 100644
index f116d09c18..0000000000
--- a/tgui/packages/tgui/interfaces/DecalPainter.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import { useBackend } from '../backend';
-import { Box, Button, Section } from '../components';
-import { Window } from '../layouts';
-import { classes } from 'common/react';
-
-export const DecalPainter = (props, context) => {
- const { act, data } = useBackend(context);
- const decal_list = data.decal_list || [];
- const color_list = data.color_list || [];
- const dir_list = data.dir_list || [];
- return (
-
-
-
- {decal_list.map(decal => (
- act('select decal', {
- decals: decal.decal,
- })}>
-
-
- ))}
-
-
- {color_list.map(color => {
- return (
- act('select color', {
- colors: color.colors,
- })}>
-
-
- );
- })}
-
-
- {dir_list.map(dir => {
- return (
- act('selected direction', {
- dirs: dir.dirs,
- })}>
-
-
- );
- })}
-
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/DecalPainter.tsx b/tgui/packages/tgui/interfaces/DecalPainter.tsx
new file mode 100644
index 0000000000..1a211cfba4
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/DecalPainter.tsx
@@ -0,0 +1,162 @@
+import { useBackend } from '../backend';
+import { Button, ColorBox, Flex, Section } from '../components';
+import { Window } from '../layouts';
+
+type DecalInfo = {
+ name: string;
+ decal: string;
+};
+
+type ColorInfo = {
+ name: string;
+ color: string;
+};
+
+type DirInfo = {
+ name: string;
+ dir: number;
+};
+
+type DecalPainterData = {
+ icon_prefix: string;
+ decal_list: DecalInfo[];
+ color_list: ColorInfo[];
+ dir_list: DirInfo[];
+ nondirectional_decals: string[];
+ supports_custom_color: number;
+ current_decal: string;
+ current_color: string;
+ current_dir: number;
+ current_custom_color: string;
+};
+
+const filterBoxColor = (color: string) => {
+ if (!color.startsWith('#')) {
+ return color;
+ }
+
+ // cut alpha
+ return color.substring(0, 7);
+};
+
+export const DecalPainter = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const custom_color_selected = !data.color_list.some(
+ (color) => color.color === data.current_color
+ );
+ const supports_custom_color = !!data.supports_custom_color;
+
+ // Handle custom color icon correctly
+ const preview_color = custom_color_selected ? 'custom' : data.current_color;
+
+ return (
+
+
+
+ {data.color_list.map((color) => {
+ return (
+
+ act('select color', {
+ color: color.color,
+ })}>
+
+ {color.name}
+
+ );
+ })}
+ {supports_custom_color && (
+ act('pick custom color')}>
+
+ Custom
+
+ )}
+
+
+
+ {data.decal_list.map((decal) => {
+ const nondirectional = data.nondirectional_decals.includes(
+ decal.decal
+ );
+
+ return nondirectional ? (
+ // Tallll button for nondirectional
+
+ ) : (
+ // 4 buttons for directional
+
+ {data.dir_list.map((dir) => {
+ const selected
+ = decal.decal === data.current_decal
+ && dir.dir === data.current_dir;
+
+ return (
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+
+ );
+};
+
+type IconButtonParams = {
+ decal: string;
+ dir: number;
+ color: string;
+ label: string;
+ selected: boolean;
+};
+
+const IconButton = (props: IconButtonParams, context) => {
+ const { act, data } = useBackend(context);
+
+ const generateIconKey = (decal: string, dir: number, color: string) =>
+ `${data.icon_prefix} ${decal}_${dir}_${color.replace('#', '')}`;
+
+ const icon = generateIconKey(props.decal, props.dir, props.color);
+
+ return (
+
+ act('select decal', {
+ decal: props.decal,
+ dir: props.dir,
+ })}>
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/ForceEvent.tsx b/tgui/packages/tgui/interfaces/ForceEvent.tsx
new file mode 100644
index 0000000000..a486f52482
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ForceEvent.tsx
@@ -0,0 +1,197 @@
+import { paginate } from 'common/collections';
+import { useBackend, useLocalState } from '../backend';
+import { Stack, Button, Icon, Input, Section, Tabs } from '../components';
+import { Window } from '../layouts';
+
+const CATEGORY_PAGE_ITEMS = 4;
+const EVENT_PAGE_ITEMS = 2;
+const EVENT_PAGE_MAXCHARS = 48;
+
+/**
+ * Same as paginate, but respecting event names with a character max length
+ * that will also create a new page if created
+ */
+const paginateEvents = (events: Event[], maxPerPage: number): Event[][] => {
+ const pages: Event[][] = [];
+ let page: Event[] = [];
+ // conditions that make a new page
+ let itemsToAdd = maxPerPage;
+ let maxChars = EVENT_PAGE_MAXCHARS;
+
+ for (const event of events) {
+ maxChars -= event.name.length;
+ if (maxChars <= 0) {
+ // would overflow the next line over
+ itemsToAdd = maxPerPage;
+ maxChars = EVENT_PAGE_MAXCHARS - event.name.length;
+ pages.push(page);
+ page = [];
+ }
+ page.push(event);
+ itemsToAdd--;
+ if (!itemsToAdd) {
+ // max amount of items we allow
+ itemsToAdd = maxPerPage;
+ maxChars = EVENT_PAGE_MAXCHARS;
+ pages.push(page);
+ page = [];
+ }
+ }
+ if (page.length) {
+ pages.push(page);
+ }
+ return pages;
+};
+
+type Event = {
+ name: string;
+ description: string;
+ type: string;
+ category: string;
+};
+
+type Category = {
+ name: string;
+ icon: string;
+};
+
+type ForceEventData = {
+ categories: Category[];
+ events: Event[];
+};
+
+export const ForceEvent = (props, context) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const PanelOptions = (props, context) => {
+ const [searchQuery, setSearchQuery] = useLocalState(
+ context,
+ 'searchQuery',
+ ''
+ );
+
+ const [announce, setAnnounce] = useLocalState(context, 'announce', true);
+
+ return (
+
+
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search..."
+ value={searchQuery}
+ />
+
+
+ setAnnounce(!announce)}>
+ Announce
+
+
+
+ );
+};
+
+export const EventSection = (props, context) => {
+ const { data, act } = useBackend(context);
+ const { categories, events } = data;
+
+ const [category] = useLocalState(context, 'category', categories[0]);
+ const [searchQuery] = useLocalState(context, 'searchQuery', '');
+ const [announce] = useLocalState(context, 'announce', true);
+
+ const preparedEvents = paginateEvents(
+ events.filter((event) => {
+ // remove events not in the category you're looking at
+ if (!searchQuery && event.category !== category.name) {
+ return false;
+ }
+ // remove events not being searched for, if a search is active
+ if (searchQuery && !event.name.toLowerCase().includes(searchQuery)) {
+ return false;
+ }
+ return true;
+ }),
+ EVENT_PAGE_ITEMS
+ );
+
+ const sectionTitle = searchQuery ? 'Searching...' : category.name + ' Events';
+
+ return (
+ }>
+
+ {preparedEvents.map((eventPage, i) => (
+
+
+ {eventPage.map((event) => (
+
+
+ act('forceevent', {
+ type: event.type,
+ announce: announce,
+ })}>
+ {event.name}
+
+
+ ))}
+
+
+ ))}
+
+
+ );
+};
+
+export const EventTabs = (props, context) => {
+ const { data } = useBackend(context);
+ const { categories } = data;
+
+ const [category, setCategory] = useLocalState(
+ context,
+ 'category',
+ categories[0]
+ );
+
+ const layerCats = paginate(categories, CATEGORY_PAGE_ITEMS);
+
+ return (
+
+ {layerCats.map((page, i) => (
+
+ {page.map((cat) => (
+ setCategory(cat)}>
+ {cat.name}
+
+ ))}
+
+ ))}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NtosBountyConsole.js b/tgui/packages/tgui/interfaces/NtosBountyConsole.js
new file mode 100644
index 0000000000..bfd2b0e4c9
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosBountyConsole.js
@@ -0,0 +1,119 @@
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, Section, Table } from '../components';
+import { formatMoney } from '../format';
+import { NtosWindow } from '../layouts';
+
+export const NtosBountyConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ bountydata = [],
+ } = data;
+ return (
+
+
+ }
+ buttons={(
+ act('Print')} />
+ )}>
+
+
+
+ Bounty Object
+
+
+ Description
+
+
+ Progress
+
+
+ Value
+
+
+ Claim
+
+
+ {bountydata.map(bounty => (
+
+
+ {bounty.name}
+
+
+ {bounty.description}
+
+
+ {bounty.priority === 1
+ ? High Priority
+ : ""}
+ {bounty.completion_string}
+
+
+ {bounty.reward_string}
+
+
+ act('ClaimBounty', {
+ bounty: bounty.bounty_ref,
+ })} />
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const BountyHeader = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ stored_cash,
+ } = data;
+ return (
+
+ formatMoney(value)} />
+ {' credits'}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/OperatingComputer.js b/tgui/packages/tgui/interfaces/OperatingComputer.js
index d181dbb3fd..068d85e3f0 100644
--- a/tgui/packages/tgui/interfaces/OperatingComputer.js
+++ b/tgui/packages/tgui/interfaces/OperatingComputer.js
@@ -127,7 +127,7 @@ const PatientStateView = (props, context) => {
)}
- {!!data.alternative_step && (
+ {procedure.alternative_step && (
{procedure.alternative_step}
{procedure.alt_chems_needed && (
diff --git a/tgui/packages/tgui/layouts/NtosWindow.js b/tgui/packages/tgui/layouts/NtosWindow.js
index 5c282c369a..b6a0146431 100644
--- a/tgui/packages/tgui/layouts/NtosWindow.js
+++ b/tgui/packages/tgui/layouts/NtosWindow.js
@@ -28,7 +28,7 @@ export const NtosWindow = (props, context) => {
PC_stationtime,
PC_programheaders = [],
PC_showexitprogram,
- PC_showpeneject,
+ TABLET_show_pen_eject,
} = data;
return (
{
src={resolveAsset(PC_apclinkicon)} />
)}
- {!!PC_showpeneject && (
+ {!!TABLET_show_pen_eject && (
{
icon="pen-alt"
tooltip="Eject pen"
tooltipPosition="bottom-end"
- onClick={() => act('eject_pen')} />
+ onClick={() => act('TABLET_eject_pen')} />
)}
{!!PC_showexitprogram && (