diff --git a/_maps/RandomRuins/RockRuins/rockplanet_shippingdock.dmm b/_maps/RandomRuins/RockRuins/rockplanet_shippingdock.dmm
index 6bb4f2e48c99..f5040429ad76 100644
--- a/_maps/RandomRuins/RockRuins/rockplanet_shippingdock.dmm
+++ b/_maps/RandomRuins/RockRuins/rockplanet_shippingdock.dmm
@@ -2417,7 +2417,7 @@
/obj/item/toy/plush/moth/firewatch,
/obj/item/toy/plush/lizardplushie,
/obj/item/toy/plush/knight,
-/obj/item/toy/prize/mauler,
+/obj/item/toy/prize/touro,
/obj/item/toy/talking/AI,
/turf/open/floor/plasteel/mono/white,
/area/ruin/rockplanet/shippingdockwarehouse)
diff --git a/_maps/configs/pgf_crying_sun.json b/_maps/configs/pgf_crying_sun.json
index d4eb67b1fb50..23114c8aa274 100644
--- a/_maps/configs/pgf_crying_sun.json
+++ b/_maps/configs/pgf_crying_sun.json
@@ -21,7 +21,7 @@
"officer": true,
"slots": 1
},
- "Bridge Crew": {
+ "Helmsman": {
"outfit": "/datum/outfit/job/gezena/assistant/bridge",
"slots": 1
},
@@ -37,7 +37,7 @@
"outfit": "/datum/outfit/job/gezena/assistant",
"slots": 2
},
- "Marine Sergeant": {
+ "Marine Lieutenant": {
"outfit": "/datum/outfit/job/gezena/hos",
"slots": 1
},
diff --git a/_maps/outpost/indie_space.dmm b/_maps/outpost/indie_space.dmm
index fee7b6d66bd4..86a2a8d102b1 100644
--- a/_maps/outpost/indie_space.dmm
+++ b/_maps/outpost/indie_space.dmm
@@ -108,10 +108,25 @@
/turf/closed/indestructible/reinforced,
/area/outpost/crew/library)
"aM" = (
-/obj/structure/rack,
-/obj/effect/spawner/lootdrop/prison_contraband,
-/turf/open/floor/plasteel/patterned/cargo_one,
-/area/outpost/vacant_rooms)
+/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/structure/table,
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{
+ dir = 8
+ },
+/obj/effect/spawner/lootdrop/plushie{
+ pixel_x = 16;
+ pixel_y = 4
+ },
+/obj/machinery/computer/cryopod/directional/north,
+/obj/item/folder{
+ pixel_x = -5;
+ pixel_y = 2
+ },
+/obj/structure/cable/yellow{
+ icon_state = "2-4"
+ },
+/turf/open/floor/plasteel,
+/area/outpost/crew/cryo)
"aP" = (
/obj/structure/railing{
dir = 8
@@ -916,6 +931,7 @@
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 1
},
+/obj/machinery/newscaster/directional/north,
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
"gq" = (
@@ -1463,8 +1479,16 @@
/area/outpost/maintenance/fore)
"kh" = (
/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{
+ dir = 1
+ },
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey,
+/obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{
+ dir = 4
+ },
+/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel,
-/area/outpost/vacant_rooms)
+/area/outpost/crew/cryo)
"km" = (
/obj/effect/decal/cleanable/dirt/dust,
/obj/effect/decal/cleanable/confetti,
@@ -1695,18 +1719,8 @@
/turf/open/floor/plating,
/area/outpost/maintenance/fore)
"lG" = (
-/obj/machinery/door/airlock/public,
-/obj/effect/turf_decal/industrial/warning,
-/obj/effect/turf_decal/industrial/warning{
- dir = 1
- },
-/obj/machinery/door/firedoor/border_only,
-/obj/machinery/door/firedoor/border_only{
- dir = 1
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
-/turf/open/floor/plasteel/tech,
-/area/outpost/vacant_rooms)
+/turf/closed/indestructible/reinforced,
+/area/outpost/crew/cryo)
"lH" = (
/obj/structure/cable/yellow{
icon_state = "1-2"
@@ -1722,6 +1736,15 @@
/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/wood,
/area/outpost/crew/library)
+"lJ" = (
+/obj/effect/turf_decal/corner/opaque/grey/full,
+/obj/effect/turf_decal/corner/opaque/neutral{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4,
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2,
+/turf/open/floor/plasteel,
+/area/outpost/hallway/port)
"lM" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/steeldecal/steel_decals9,
@@ -2013,11 +2036,17 @@
/area/outpost/cargo)
"nz" = (
/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
-/obj/structure/chair{
- dir = 1
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{
+ dir = 8
},
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel,
-/area/outpost/vacant_rooms)
+/area/outpost/crew/cryo)
"nK" = (
/obj/machinery/door/firedoor,
/obj/effect/turf_decal/industrial/warning{
@@ -2473,6 +2502,11 @@
dir = 4
},
/obj/effect/decal/cleanable/dirt/dust,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
+/obj/structure/cable/yellow{
+ icon_state = "1-8"
+ },
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
"qT" = (
@@ -2552,7 +2586,6 @@
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 1
},
-/obj/structure/extinguisher_cabinet/directional/north,
/obj/effect/turf_decal/floordetail/tiled,
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
@@ -3399,10 +3432,18 @@
/turf/open/floor/plating/asteroid,
/area/outpost/external)
"vd" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/structure/chair{
+ dir = 8
+ },
+/obj/structure/extinguisher_cabinet/directional/east{
+ pixel_y = -7
+ },
+/obj/machinery/firealarm/directional/east{
+ pixel_y = 6
+ },
/turf/open/floor/plasteel,
-/area/outpost/vacant_rooms)
+/area/outpost/crew/cryo)
"vp" = (
/obj/effect/turf_decal/corner/opaque/grey/full,
/turf/open/floor/plasteel,
@@ -3587,6 +3628,25 @@
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel/patterned,
/area/outpost/cargo)
+"ww" = (
+/obj/machinery/door/airlock/glass{
+ name = "Cryogenics"
+ },
+/obj/effect/turf_decal/industrial/warning,
+/obj/effect/turf_decal/industrial/warning{
+ dir = 1
+ },
+/obj/machinery/door/firedoor/border_only,
+/obj/machinery/door/firedoor/border_only{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/turf/open/floor/plasteel/tech,
+/area/outpost/crew/cryo)
"wA" = (
/obj/effect/decal/cleanable/food/tomato_smudge,
/turf/open/floor/wood/mahogany,
@@ -3669,6 +3729,16 @@
/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/plasteel,
/area/outpost/storage)
+"wY" = (
+/obj/machinery/cryopod,
+/obj/effect/turf_decal/corner_techfloor_grid{
+ dir = 5
+ },
+/obj/structure/sign/poster/random{
+ pixel_y = 30
+ },
+/turf/open/floor/plasteel/tech/techmaint,
+/area/outpost/crew/cryo)
"xc" = (
/obj/effect/turf_decal/corner/opaque/red{
dir = 4
@@ -3738,10 +3808,12 @@
/turf/open/floor/plasteel/tech/techmaint,
/area/outpost/maintenance/central)
"xI" = (
-/obj/machinery/camera/autoname,
-/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
-/turf/open/floor/plasteel,
-/area/outpost/vacant_rooms)
+/obj/machinery/cryopod,
+/obj/effect/turf_decal/corner_techfloor_grid{
+ dir = 5
+ },
+/turf/open/floor/plasteel/tech/techmaint,
+/area/outpost/crew/cryo)
"xJ" = (
/obj/effect/turf_decal/corner/opaque/grey/full,
/obj/structure/disposalpipe/segment{
@@ -4779,7 +4851,6 @@
dir = 4
},
/obj/effect/decal/cleanable/dirt/dust,
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
/obj/effect/turf_decal/corner/opaque/black{
dir = 4
},
@@ -5361,15 +5432,20 @@
/turf/open/floor/plating,
/area/outpost/maintenance/fore)
"HB" = (
-/obj/structure/rack,
-/obj/effect/spawner/lootdrop/glowstick,
-/obj/effect/spawner/lootdrop/glowstick,
-/obj/effect/spawner/lootdrop/glowstick,
-/obj/effect/spawner/lootdrop/glowstick,
-/obj/effect/spawner/lootdrop/glowstick,
-/obj/machinery/firealarm/directional/east,
-/turf/open/floor/plasteel/patterned/cargo_one,
-/area/outpost/vacant_rooms)
+/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/structure/table,
+/obj/item/paper_bin{
+ pixel_y = 6;
+ pixel_x = 6
+ },
+/obj/item/pen,
+/obj/machinery/power/apc/auto_name/directional/north,
+/obj/structure/cable/yellow{
+ icon_state = "0-8"
+ },
+/obj/effect/turf_decal/steeldecal/steel_decals_central6,
+/turf/open/floor/plasteel,
+/area/outpost/crew/cryo)
"HD" = (
/turf/closed/indestructible/rock,
/area/outpost/external)
@@ -5628,11 +5704,13 @@
/turf/open/floor/plating,
/area/outpost/maintenance/fore)
"IS" = (
-/obj/machinery/light/dim/directional/east,
-/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4,
/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/machinery/light/dim/directional/east,
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{
+ dir = 8
+ },
/turf/open/floor/plasteel,
-/area/outpost/vacant_rooms)
+/area/outpost/crew/cryo)
"IT" = (
/obj/machinery/camera/autoname,
/obj/effect/turf_decal/corner/opaque/red{
@@ -6036,10 +6114,12 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
dir = 4
},
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4,
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 8
},
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{
+ dir = 4
+ },
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
"Lh" = (
@@ -6140,10 +6220,10 @@
/area/outpost/maintenance/fore)
"LS" = (
/obj/effect/turf_decal/corner/opaque/grey/full,
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 1
},
+/obj/structure/extinguisher_cabinet/directional/north,
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
"LU" = (
@@ -6332,6 +6412,15 @@
},
/turf/open/floor/plasteel,
/area/outpost/hallway/central)
+"MQ" = (
+/obj/machinery/cryopod{
+ dir = 1
+ },
+/obj/effect/turf_decal/corner_techfloor_grid{
+ dir = 10
+ },
+/turf/open/floor/plasteel/tech/techmaint,
+/area/outpost/crew/cryo)
"Nc" = (
/turf/closed/indestructible/rock,
/area/outpost/hallway/central)
@@ -6615,6 +6704,18 @@
},
/turf/open/floor/plasteel,
/area/outpost/hallway/central)
+"Pt" = (
+/obj/machinery/cryopod{
+ dir = 1
+ },
+/obj/effect/turf_decal/corner_techfloor_grid{
+ dir = 10
+ },
+/obj/machinery/camera/autoname{
+ dir = 4
+ },
+/turf/open/floor/plasteel/tech/techmaint,
+/area/outpost/crew/cryo)
"Pw" = (
/obj/structure/rack,
/obj/effect/spawner/lootdrop/maintenance/four,
@@ -6626,6 +6727,10 @@
/obj/structure/extinguisher_cabinet/directional/east,
/turf/open/floor/plating,
/area/outpost/maintenance/fore)
+"PA" = (
+/obj/effect/spawner/structure/window/reinforced/indestructable,
+/turf/open/floor/plating,
+/area/outpost/crew/cryo)
"PD" = (
/obj/structure/chair/sofa/brown/right/directional/west,
/turf/open/floor/carpet/royalblack,
@@ -6679,6 +6784,26 @@
},
/turf/open/floor/wood,
/area/outpost/crew/bar)
+"PQ" = (
+/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey/corner{
+ dir = 1
+ },
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey/corner{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{
+ dir = 6
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
+ dir = 10
+ },
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/open/floor/plasteel,
+/area/outpost/crew/cryo)
"PS" = (
/obj/structure/falsewall/reinforced,
/obj/structure/cable/yellow{
@@ -6714,7 +6839,6 @@
/area/outpost/security)
"Qa" = (
/obj/effect/turf_decal/corner/opaque/grey/full,
-/obj/machinery/newscaster/directional/north,
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 1
},
@@ -7301,10 +7425,14 @@
/turf/open/floor/plasteel,
/area/outpost/security)
"Tg" = (
-/obj/structure/rack,
-/obj/effect/spawner/lootdrop/donut,
-/turf/open/floor/plasteel/patterned/cargo_one,
-/area/outpost/vacant_rooms)
+/obj/effect/turf_decal/corner/opaque/neutral/diagonal,
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey{
+ dir = 1
+ },
+/obj/effect/turf_decal/spline/fancy/opaque/lightgrey,
+/obj/machinery/light/dim/directional/west,
+/turf/open/floor/plasteel,
+/area/outpost/crew/cryo)
"Th" = (
/turf/closed/indestructible/reinforced,
/area/outpost/cargo/office)
@@ -7829,6 +7957,11 @@
/obj/effect/turf_decal/corner/opaque/neutral{
dir = 1
},
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4,
+/obj/structure/cable/yellow{
+ icon_state = "1-2"
+ },
/turf/open/floor/plasteel,
/area/outpost/hallway/port)
"Wz" = (
@@ -16959,11 +17092,11 @@ LL
Uq
Aw
LL
-wL
-wL
-wL
-wL
-wL
+lG
+lG
+lG
+lG
+lG
BM
Ky
yj
@@ -17082,11 +17215,11 @@ LL
LL
LL
LL
-wL
-aM
+lG
+wY
Tg
-aM
-wL
+Pt
+PA
rm
Kl
hW
@@ -17205,11 +17338,11 @@ HD
HD
HD
HD
-wL
+lG
xI
kh
-ev
-wL
+MQ
+PA
Qa
LY
wu
@@ -17328,14 +17461,14 @@ HD
HD
HD
HD
-wL
+lG
aM
-kh
+PQ
nz
-wL
-Fi
+ww
+Wv
qR
-wu
+lJ
iV
Pw
AW
@@ -17451,7 +17584,7 @@ HD
HD
HD
HD
-wL
+lG
HB
IS
vd
@@ -17825,7 +17958,7 @@ yN
XA
di
wL
-Wv
+Fi
vp
kb
XD
diff --git a/_maps/shuttles/independent/independent_box.dmm b/_maps/shuttles/independent/independent_box.dmm
index 577290c6581c..31891a371d8c 100644
--- a/_maps/shuttles/independent/independent_box.dmm
+++ b/_maps/shuttles/independent/independent_box.dmm
@@ -249,9 +249,6 @@
dir = 9
},
/obj/structure/catwalk/over,
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2{
- dir = 4
- },
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{
dir = 9
},
@@ -261,9 +258,8 @@
/obj/effect/turf_decal/trimline/opaque/yellow/filled/warning{
dir = 4
},
-/obj/structure/cable{
- icon_state = "4-8"
- },
+/obj/machinery/meter/atmos/layer2,
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2,
/turf/open/floor/plating,
/area/ship/engineering)
"bk" = (
@@ -324,6 +320,11 @@
dir = 1
},
/obj/effect/decal/cleanable/wrapping,
+/obj/machinery/light_switch{
+ pixel_x = 19;
+ pixel_y = 13;
+ dir = 8
+ },
/turf/open/floor/plating,
/area/ship/engineering)
"bo" = (
@@ -620,12 +621,11 @@
/turf/open/floor/carpet/nanoweave/blue,
/area/ship/bridge)
"cr" = (
-/obj/effect/turf_decal/industrial/hatch/yellow,
-/obj/machinery/atmospherics/components/unary/portables_connector/layer2,
-/obj/structure/cable{
- icon_state = "6-8"
+/obj/machinery/iv_drip/saline,
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2{
+ dir = 1
},
-/obj/machinery/portable_atmospherics/canister/air,
+/obj/effect/turf_decal/industrial/outline/yellow,
/turf/open/floor/plating,
/area/ship/engineering)
"cu" = (
@@ -1497,6 +1497,10 @@
dir = 8
},
/obj/structure/catwalk/over,
+/obj/machinery/power/apc/auto_name/directional/north,
+/obj/structure/cable{
+ icon_state = "0-4"
+ },
/turf/open/floor/plating,
/area/ship/engineering)
"ql" = (
@@ -1520,27 +1524,24 @@
/obj/effect/turf_decal/trimline/opaque/yellow/filled/warning{
dir = 1
},
-/obj/structure/cable{
- icon_state = "5-9"
- },
/obj/structure/sign/warning/electricshock{
pixel_x = 32
},
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
+ dir = 10
+ },
/turf/open/floor/plating,
/area/ship/engineering)
"qX" = (
/turf/open/floor/plasteel/white,
/area/ship/medical)
"ri" = (
-/obj/structure/railing{
- dir = 8
- },
-/obj/machinery/iv_drip/saline,
-/obj/effect/turf_decal/industrial/hatch/yellow,
-/obj/structure/railing{
- dir = 4
+/obj/machinery/atmospherics/components/unary/portables_connector/layer2{
+ dir = 1
},
+/obj/machinery/portable_atmospherics/canister/air,
/obj/machinery/light/small/directional/south,
+/obj/effect/turf_decal/industrial/outline/yellow,
/turf/open/floor/plating,
/area/ship/engineering)
"ro" = (
@@ -1619,11 +1620,11 @@
/obj/effect/turf_decal/trimline/opaque/yellow/filled/warning{
dir = 1
},
-/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
- dir = 9
+/obj/structure/cable{
+ icon_state = "2-8"
},
-/obj/structure/cable/yellow{
- icon_state = "2-5"
+/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
+ dir = 5
},
/turf/open/floor/plating,
/area/ship/engineering)
@@ -1836,13 +1837,10 @@
/turf/open/floor/plating,
/area/ship/bridge)
"wb" = (
-/obj/machinery/power/port_gen/pacman{
- anchored = 1
- },
-/obj/effect/turf_decal/industrial/outline/yellow,
+/obj/machinery/power/port_gen/pacman,
/obj/item/wrench,
-/obj/structure/cable/yellow,
-/obj/machinery/airalarm/directional/south,
+/obj/structure/cable,
+/obj/effect/turf_decal/industrial/hatch/yellow,
/turf/open/floor/plating,
/area/ship/engineering)
"wd" = (
@@ -1871,10 +1869,13 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{
dir = 8
},
+/obj/structure/catwalk/over,
/obj/structure/cable{
icon_state = "2-4"
},
-/obj/structure/catwalk/over,
+/obj/structure/cable{
+ icon_state = "2-8"
+ },
/turf/open/floor/plating,
/area/ship/engineering)
"wj" = (
@@ -2200,21 +2201,11 @@
/turf/open/floor/plasteel/dark,
/area/ship/crew)
"CR" = (
-/obj/structure/railing{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/layer_manifold/visible{
- dir = 8
- },
-/obj/structure/reagent_dispensers/watertank,
-/obj/effect/turf_decal/industrial/outline/yellow,
-/obj/item/storage/bag/trash{
- pixel_x = 6
+/obj/machinery/power/smes/engineering{
+ charge = 1e+006
},
-/obj/item/reagent_containers/glass/bucket,
-/obj/item/mop,
-/obj/machinery/power/apc/auto_name/directional/south,
/obj/structure/cable,
+/obj/effect/turf_decal/industrial/hatch/yellow,
/turf/open/floor/plating,
/area/ship/engineering)
"Dm" = (
@@ -2302,18 +2293,16 @@
/turf/open/floor/plasteel/white,
/area/ship/cargo)
"FL" = (
-/obj/machinery/firealarm/directional/south,
-/obj/structure/catwalk/over,
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer2,
-/obj/machinery/meter/atmos/layer2,
/obj/effect/turf_decal/trimline/opaque/yellow/filled/corner{
dir = 4
},
-/obj/machinery/light_switch{
- dir = 1;
- pixel_x = 10;
- pixel_y = -19
+/obj/machinery/power/terminal{
+ dir = 8
+ },
+/obj/structure/cable{
+ icon_state = "0-4"
},
+/obj/structure/catwalk/over,
/turf/open/floor/plating,
/area/ship/engineering)
"Gb" = (
@@ -2326,10 +2315,10 @@
/obj/structure/cable{
icon_state = "1-2"
},
-/obj/item/wallframe/firealarm,
/obj/effect/turf_decal/trimline/opaque/yellow/filled/warning{
dir = 4
},
+/obj/machinery/firealarm/directional/west,
/turf/open/floor/plating,
/area/ship/engineering)
"Gi" = (
@@ -2372,24 +2361,10 @@
/turf/open/floor/plating,
/area/ship/engineering)
"HM" = (
-/obj/machinery/power/terminal{
- dir = 4
- },
-/obj/structure/cable/yellow{
- icon_state = "0-10"
- },
-/obj/effect/spawner/lootdrop/maintenance/three,
-/obj/structure/rack,
-/obj/item/areaeditor/shuttle,
-/obj/item/flashlight{
- pixel_x = 3;
- pixel_y = 3
- },
-/obj/item/storage/toolbox/mechanical{
- pixel_y = 4
+/obj/structure/catwalk/over,
+/obj/machinery/atmospherics/components/binary/pump/layer2{
+ dir = 8
},
-/obj/item/bot_assembly/hygienebot,
-/obj/machinery/firealarm/directional/north,
/turf/open/floor/plating,
/area/ship/engineering)
"Ic" = (
@@ -2412,12 +2387,11 @@
/turf/open/floor/plasteel/mono/dark,
/area/ship/medical)
"In" = (
-/obj/machinery/power/smes/engineering{
- charge = 1e+006
- },
-/obj/structure/cable{
- icon_state = "0-10"
+/obj/machinery/atmospherics/components/unary/tank/air{
+ dir = 8;
+ piping_layer = 2
},
+/obj/effect/turf_decal/industrial/hatch/yellow,
/turf/open/floor/plating,
/area/ship/engineering)
"Ja" = (
@@ -2704,7 +2678,6 @@
/obj/structure/railing{
dir = 4
},
-/obj/machinery/firealarm/directional/north,
/turf/open/floor/plating,
/area/ship/engineering)
"SJ" = (
@@ -2832,10 +2805,20 @@
/turf/open/floor/plating,
/area/ship/bridge)
"WW" = (
-/obj/machinery/atmospherics/components/unary/tank/air{
- dir = 4
+/obj/structure/cable/yellow{
+ icon_state = "0-10"
},
-/obj/effect/turf_decal/industrial/hatch/yellow,
+/obj/effect/spawner/lootdrop/maintenance/three,
+/obj/structure/rack,
+/obj/item/areaeditor/shuttle,
+/obj/item/flashlight{
+ pixel_x = 3;
+ pixel_y = 3
+ },
+/obj/item/storage/toolbox/mechanical{
+ pixel_y = 4
+ },
+/obj/item/bot_assembly/hygienebot,
/obj/machinery/light/small/directional/west,
/turf/open/floor/plating,
/area/ship/engineering)
@@ -2963,9 +2946,15 @@
/turf/open/floor/plasteel/tech,
/area/ship/medical)
"ZN" = (
+/obj/structure/reagent_dispensers/watertank,
+/obj/effect/turf_decal/industrial/outline/yellow,
+/obj/item/storage/bag/trash{
+ pixel_x = 6
+ },
+/obj/item/reagent_containers/glass/bucket,
+/obj/item/mop,
/obj/machinery/atmospherics/pipe/simple/orange/hidden,
/obj/machinery/light/small/directional/east,
-/obj/structure/salvageable/machine,
/turf/open/floor/plating,
/area/ship/engineering)
diff --git a/_maps/shuttles/inteq/inteq_talos.dmm b/_maps/shuttles/inteq/inteq_talos.dmm
index dc0e84542cae..ddad109a06fb 100644
--- a/_maps/shuttles/inteq/inteq_talos.dmm
+++ b/_maps/shuttles/inteq/inteq_talos.dmm
@@ -874,7 +874,6 @@
icon_state = "0-2"
},
/obj/effect/decal/cleanable/dirt,
-/obj/structure/extinguisher_cabinet/directional/north,
/obj/structure/extinguisher_cabinet/directional/east,
/turf/open/floor/plasteel/grimy,
/area/ship/crew)
diff --git a/_maps/shuttles/pgf/pgf_crying_sun.dmm b/_maps/shuttles/pgf/pgf_crying_sun.dmm
index e016ef725525..c0584df1e945 100644
--- a/_maps/shuttles/pgf/pgf_crying_sun.dmm
+++ b/_maps/shuttles/pgf/pgf_crying_sun.dmm
@@ -1,4 +1,18 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"ae" = (
+/obj/structure/cable/blue{
+ icon_state = "0-4"
+ },
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/door/poddoor{
+ dir = 4;
+ id = "lib_engine_blast"
+ },
+/obj/machinery/power/shuttle/engine/fire{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/ship/engineering/engines/port)
"ai" = (
/obj/structure/railing,
/obj/structure/cable/yellow{
@@ -129,6 +143,13 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
/turf/open/floor/plasteel/white,
/area/ship/hallway/port)
+"bi" = (
+/obj/machinery/computer/crew{
+ dir = 8
+ },
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/turf/open/floor/plasteel/telecomms_floor,
+/area/ship/bridge)
"bl" = (
/obj/effect/turf_decal/industrial/traffic,
/obj/machinery/power/apc/auto_name/directional/north,
@@ -215,10 +236,6 @@
/obj/item/storage/box/flashes{
pixel_y = -1
},
-/obj/item/screwdriver{
- pixel_x = -8;
- pixel_y = -5
- },
/obj/item/screwdriver{
pixel_x = -5;
pixel_y = -5
@@ -235,10 +252,6 @@
pixel_x = 4;
pixel_y = -5
},
-/obj/item/screwdriver{
- pixel_x = 7;
- pixel_y = -5
- },
/obj/structure/closet/secure_closet/wall/directional/west{
icon_state = "sec_wall";
name = "equipment locker"
@@ -406,9 +419,6 @@
dir = 6
},
/obj/structure/catwalk/over/plated_catwalk/dark,
-/obj/structure/cable{
- icon_state = "1-8"
- },
/obj/structure/cable{
icon_state = "1-2"
},
@@ -416,13 +426,28 @@
/area/ship/engineering/engines/starboard)
"du" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 6
+ dir = 6;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-8"
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
+"dv" = (
+/obj/structure/cable/blue{
+ icon_state = "0-4"
+ },
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/door/poddoor{
+ dir = 4;
+ id = "lib_engine_blast"
+ },
+/obj/machinery/power/shuttle/engine/fire{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/ship/engineering/engines/starboard)
"dz" = (
/obj/effect/turf_decal/corner_steel_grid{
dir = 10
@@ -464,10 +489,10 @@
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/hallway/port)
"dK" = (
-/obj/machinery/computer/cargo{
- dir = 8
+/obj/structure/chair/comfy/shuttle{
+ dir = 4
},
-/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/effect/turf_decal/floordetail/tiled,
/obj/structure/cable{
icon_state = "1-2"
},
@@ -523,9 +548,6 @@
dir = 8;
name = "engine fuel pump"
},
-/obj/structure/cable{
- icon_state = "2-4"
- },
/obj/machinery/camera/autoname{
dir = 2;
network = list("GEC")
@@ -1010,12 +1032,27 @@
/obj/machinery/light/directional/north,
/turf/open/floor/plating,
/area/ship/engineering)
+"hX" = (
+/obj/structure/window/reinforced/fulltile/shuttle,
+/obj/structure/grille,
+/obj/machinery/door/firedoor/border_only{
+ dir = 8
+ },
+/obj/machinery/door/firedoor/border_only{
+ dir = 1
+ },
+/obj/machinery/door/poddoor/shutters{
+ id = "lib_bridge_shut"
+ },
+/turf/open/floor/plating,
+/area/ship/bridge)
"if" = (
/obj/structure/cable{
icon_state = "0-8"
},
/obj/machinery/porta_turret/ship/weak{
- dir = 4
+ dir = 4;
+ mode = 1
},
/obj/structure/catwalk/over/plated_catwalk/dark,
/turf/open/floor/plating/airless,
@@ -1166,12 +1203,12 @@
/area/ship/hangar/starboard)
"jm" = (
/obj/machinery/holopad/emergency,
-/obj/effect/turf_decal/spline/fancy/opaque/lime{
- dir = 1
- },
/obj/structure/cable{
icon_state = "4-8"
},
+/obj/effect/turf_decal/spline/fancy/opaque/lime{
+ dir = 1
+ },
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"jt" = (
@@ -1253,7 +1290,7 @@
},
/obj/machinery/button/door{
id = "lib_armory_1";
- name = "Sergeant Access";
+ name = "Lieutenant Access";
dir = 4;
pixel_x = -20;
pixel_y = -5;
@@ -1421,7 +1458,7 @@
/obj/structure/table/reinforced,
/obj/effect/turf_decal/corner/opaque/lime/mono,
/obj/machinery/camera/autoname{
- dir = 1
+ dir = 10
},
/obj/machinery/light/directional/south,
/turf/open/floor/plasteel/mono/dark,
@@ -1457,14 +1494,13 @@
pixel_x = 4;
pixel_y = 4
},
+/obj/machinery/light/directional/north,
/obj/item/reagent_containers/food/drinks/bottle/whiskey{
pixel_x = -10;
pixel_y = 3;
- name = "'Anastheasia'";
+ name = "'Anesthesia'";
desc = "A bottle of Git's with the word 'Anastheasia' written over it in marker."
},
-/obj/machinery/light/directional/north,
-/obj/item/clothing/neck/stethoscope,
/turf/open/floor/mineral/titanium,
/area/ship/medical)
"mL" = (
@@ -1473,7 +1509,7 @@
},
/obj/structure/closet/crate{
icon_state = "wooden";
- name = "myning geer";
+ name = "mining gear";
desc = "A rectangular steel crate with 'myning geer' spelled out in crayon on top."
},
/obj/item/storage/bag/ore{
@@ -1608,9 +1644,12 @@
/turf/open/floor/plasteel/white,
/area/ship/hallway/port)
"no" = (
-/obj/structure/bed,
-/obj/item/bedsheet/cosmos,
/obj/structure/curtain/cloth/grey,
+/obj/structure/railing{
+ dir = 1
+ },
+/obj/structure/bed,
+/obj/item/bedsheet/brown,
/turf/open/floor/plasteel/dark,
/area/ship/crew/dorm)
"nv" = (
@@ -1665,6 +1704,26 @@
},
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/crew/cryo)
+"om" = (
+/obj/effect/turf_decal/corner/opaque/lime/mono,
+/obj/structure/table/reinforced,
+/obj/item/clipboard{
+ pixel_x = -9;
+ pixel_y = -1
+ },
+/obj/item/pen{
+ pixel_x = -10;
+ pixel_y = 1
+ },
+/obj/item/gps{
+ pixel_x = 8;
+ pixel_y = -2
+ },
+/obj/item/binoculars{
+ pixel_y = 9
+ },
+/turf/open/floor/plasteel/mono/dark,
+/area/ship/bridge)
"ou" = (
/obj/structure/railing{
dir = 8
@@ -1784,22 +1843,22 @@
/obj/structure/railing{
dir = 4
},
-/obj/structure/table,
/obj/item/toy/plush/knight{
- pixel_y = 10;
+ pixel_y = 11;
pixel_x = -10
},
/obj/item/toy/plush/hornet{
- pixel_y = 8;
- pixel_x = -3
+ pixel_y = 10;
+ pixel_x = -2
},
/obj/item/toy/plush/mora{
- pixel_y = 4;
- pixel_x = 5
+ pixel_y = 6;
+ pixel_x = 6
},
/obj/effect/turf_decal/corner_steel_grid{
dir = 10
},
+/obj/structure/dresser,
/turf/open/floor/plasteel/dark,
/area/ship/crew/dorm)
"oX" = (
@@ -1848,6 +1907,22 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2,
/turf/open/floor/plasteel/mono/dark,
/area/ship/hallway/central)
+"pK" = (
+/obj/structure/cable/blue{
+ icon_state = "0-8"
+ },
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/door/firedoor/border_only{
+ dir = 4
+ },
+/obj/structure/window/plasma/reinforced{
+ dir = 8
+ },
+/obj/machinery/atmospherics/components/unary/shuttle/fire_heater{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/ship/engineering/engines/starboard)
"pW" = (
/obj/machinery/computer/helm/viewscreen/directional/west,
/obj/effect/turf_decal/siding/wood{
@@ -1883,15 +1958,8 @@
/turf/open/floor/plasteel/sepia,
/area/ship/crew/dorm/dormthree)
"qj" = (
-/obj/structure/table/reinforced,
-/obj/item/desk_flag/gezena{
- pixel_x = 9;
- pixel_y = 1
- },
-/obj/machinery/recharger{
- pixel_y = 1;
- pixel_x = -6
- },
+/obj/structure/chair/comfy/shuttle,
+/obj/effect/turf_decal/floordetail/tiled,
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/bridge)
"qm" = (
@@ -1904,7 +1972,6 @@
/turf/open/floor/plasteel/dark,
/area/ship/crew/canteen)
"qp" = (
-/obj/machinery/atmospherics/pipe/simple/orange/visible,
/obj/structure/cable{
icon_state = "1-10"
},
@@ -1915,6 +1982,9 @@
dir = 1
},
/obj/item/paper/guides/jobs/engi/combustion_thruster,
+/obj/machinery/atmospherics/pipe/manifold/orange/visible{
+ dir = 4
+ },
/turf/open/floor/plasteel/tech,
/area/ship/engineering/engines/starboard)
"qy" = (
@@ -2104,11 +2174,6 @@
/turf/closed/wall/mineral/plastitanium,
/area/ship/engineering/engines/starboard)
"sF" = (
-/obj/machinery/door/airlock/security/glass{
- req_one_access_txt = "1";
- req_access = list(1);
- name = "Checkpoint"
- },
/obj/structure/cable{
icon_state = "1-2"
},
@@ -2117,6 +2182,10 @@
dir = 1
},
/obj/machinery/door/firedoor/border_only,
+/obj/machinery/door/airlock/security/glass{
+ req_one_access_txt = "1";
+ name = "Checkpoint"
+ },
/turf/open/floor/plasteel/dark,
/area/ship/hallway/port)
"sJ" = (
@@ -2241,7 +2310,6 @@
pixel_y = 3
},
/obj/effect/turf_decal/corner/opaque/lime/mono,
-/obj/machinery/light/directional/south,
/obj/machinery/light_switch{
dir = 4;
pixel_x = -19;
@@ -2260,26 +2328,31 @@
/turf/closed/wall/mineral/titanium/exterior,
/area/ship/crew/cryo)
"tQ" = (
-/obj/structure/chair/comfy/shuttle,
/obj/effect/turf_decal/corner_steel_grid{
dir = 9
},
+/obj/structure/chair/comfy/shuttle,
+/obj/effect/turf_decal/floordetail/tiled,
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/bridge)
"tU" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 5
+ dir = 5;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-2"
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
-"uh" = (
-/obj/structure/chair/comfy/shuttle{
- dir = 4
+"tY" = (
+/obj/machinery/modular_computer/console/preset/command{
+ dir = 1
},
-/obj/effect/turf_decal/floordetail/tiled,
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/turf/open/floor/plasteel/telecomms_floor,
+/area/ship/bridge)
+"uh" = (
/obj/structure/cable{
icon_state = "4-8"
},
@@ -2585,12 +2658,12 @@
/turf/open/floor/plasteel/dark,
/area/ship/crew/canteen)
"wz" = (
-/obj/effect/turf_decal/corner_steel_grid{
- dir = 9
- },
/obj/structure/cable{
icon_state = "4-8"
},
+/obj/effect/turf_decal/corner_steel_grid{
+ dir = 9
+ },
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/bridge)
"wK" = (
@@ -2812,6 +2885,19 @@
/obj/machinery/airalarm/directional/east,
/turf/open/floor/plasteel/tech,
/area/ship/engineering/engines/port)
+"yy" = (
+/obj/structure/table/reinforced,
+/obj/item/desk_flag/gezena{
+ pixel_x = 9;
+ pixel_y = 1
+ },
+/obj/machinery/recharger{
+ pixel_y = 1;
+ pixel_x = -6
+ },
+/obj/effect/turf_decal/floordetail/tiled,
+/turf/open/floor/plasteel/telecomms_floor,
+/area/ship/bridge)
"yF" = (
/obj/effect/turf_decal/industrial/traffic{
dir = 1
@@ -2932,9 +3018,6 @@
icon_state = "1-4"
},
/obj/structure/catwalk/over/plated_catwalk,
-/obj/structure/table/reinforced{
- color = "#c1b6a5"
- },
/obj/machinery/cell_charger,
/obj/item/stock_parts/cell/high,
/obj/structure/cable,
@@ -2945,6 +3028,7 @@
dir = 5
},
/obj/machinery/airalarm/directional/west,
+/obj/structure/table/reinforced,
/turf/open/floor/plating,
/area/ship/engineering)
"zL" = (
@@ -2970,7 +3054,7 @@
},
/obj/machinery/door/airlock/security{
req_access = list(3);
- name = "Sergeant's Quarters"
+ name = "Lieutenant's Quarters"
},
/turf/open/floor/plasteel/tech,
/area/ship/security)
@@ -3092,6 +3176,22 @@
/obj/machinery/vending/toyliberationstation,
/turf/open/floor/plasteel/tech,
/area/ship/security)
+"AM" = (
+/obj/structure/cable/blue{
+ icon_state = "0-8"
+ },
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/door/firedoor/border_only{
+ dir = 4
+ },
+/obj/structure/window/plasma/reinforced{
+ dir = 8
+ },
+/obj/machinery/atmospherics/components/unary/shuttle/fire_heater{
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/ship/engineering/engines/port)
"AN" = (
/obj/structure/window/reinforced{
dir = 1
@@ -3353,7 +3453,8 @@
/area/ship/crew/canteen)
"CF" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 10
+ dir = 10;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-4"
@@ -3583,10 +3684,7 @@
/turf/closed/wall/mineral/plastitanium/nodiagonal,
/area/ship/external/dark)
"El" = (
-/obj/structure/railing{
- dir = 8
- },
-/obj/machinery/modular_computer/console/preset/command{
+/obj/machinery/computer/security{
dir = 1
},
/obj/structure/catwalk/over/plated_catwalk/dark,
@@ -3635,7 +3733,8 @@
/area/ship/hallway/central)
"Eu" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 5
+ dir = 5;
+ mode = 1
},
/obj/structure/cable,
/turf/open/floor/engine/hull,
@@ -3898,7 +3997,8 @@
/area/ship/hallway/central)
"FJ" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 5
+ dir = 5;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-4"
@@ -3918,7 +4018,8 @@
/area/ship/engineering/engines/starboard)
"FO" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 8
+ dir = 8;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-4"
@@ -3946,10 +4047,10 @@
/turf/open/floor/plasteel/mono/dark,
/area/ship/hallway/central)
"Gb" = (
-/obj/machinery/computer/crew{
- dir = 8
+/obj/structure/chair/comfy/shuttle{
+ dir = 4
},
-/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/effect/turf_decal/floordetail/tiled,
/obj/structure/cable{
icon_state = "1-8"
},
@@ -4104,8 +4205,8 @@
"GW" = (
/obj/structure/rack,
/obj/item/stock_parts/cell/gun/pgf{
- pixel_y = 12;
- pixel_x = -3
+ pixel_x = -6;
+ pixel_y = -2
},
/obj/item/stock_parts/cell/gun/pgf{
pixel_y = 10;
@@ -4128,6 +4229,13 @@
pixel_x = 3
},
/obj/effect/turf_decal/corner/opaque/neutral/full,
+/obj/item/stock_parts/cell/gun/pgf{
+ pixel_x = -12
+ },
+/obj/item/stock_parts/cell/gun/pgf{
+ pixel_x = -8;
+ pixel_y = -2
+ },
/turf/open/floor/vault,
/area/ship/security/armory)
"Ha" = (
@@ -4146,7 +4254,8 @@
/area/ship/security/armory)
"Hb" = (
/obj/machinery/porta_turret/ship/weak{
- dir = 4
+ dir = 4;
+ mode = 1
},
/obj/structure/cable{
icon_state = "0-2"
@@ -4300,10 +4409,6 @@
/area/ship/crew/canteen)
"Ig" = (
/obj/machinery/computer/helm/viewscreen/directional/north,
-/obj/structure/chair/comfy/shuttle{
- dir = 4
- },
-/obj/effect/turf_decal/floordetail/tiled,
/turf/open/floor/plasteel/telecomms_floor,
/area/ship/bridge)
"Ir" = (
@@ -4466,6 +4571,20 @@
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
+"KH" = (
+/obj/structure/window/reinforced/fulltile/shuttle,
+/obj/structure/grille,
+/obj/machinery/door/poddoor/shutters{
+ id = "lib_bridge_shut"
+ },
+/obj/machinery/door/firedoor/border_only{
+ dir = 8
+ },
+/obj/machinery/door/firedoor/border_only{
+ dir = 1
+ },
+/turf/open/floor/plating,
+/area/ship/bridge)
"KM" = (
/turf/closed/wall/mineral/titanium/nodiagonal,
/area/ship/hallway/port)
@@ -4516,9 +4635,6 @@
dir = 8;
name = "engine fuel pump"
},
-/obj/structure/cable{
- icon_state = "1-4"
- },
/obj/machinery/camera/autoname{
dir = 1;
network = list("GEC")
@@ -4677,7 +4793,7 @@
"Mw" = (
/obj/structure/bed,
/obj/item/bedsheet/hos{
- name = "Sergeant's Bedsheet";
+ name = "Lieutenant's Bedsheet";
desc = "It is decorated with a shield emblem."
},
/obj/effect/turf_decal/borderfloorblack,
@@ -4713,9 +4829,6 @@
pixel_x = -9;
pixel_y = 3
},
-/obj/item/clothing/head/gezena/medic/flap{
- pixel_x = 12
- },
/obj/item/clothing/head/gezena/medic{
pixel_y = 4;
pixel_x = 7
@@ -4743,6 +4856,11 @@
pixel_x = -13;
pixel_y = -19
},
+/obj/item/clothing/neck/stethoscope,
+/obj/item/clothing/head/gezena/flap/medic{
+ pixel_y = 1;
+ pixel_x = 12
+ },
/turf/open/floor/mineral/titanium,
/area/ship/medical)
"MF" = (
@@ -4801,6 +4919,9 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{
dir = 8
},
+/obj/structure/railing/corner{
+ dir = 4
+ },
/turf/open/floor/plasteel/tech,
/area/ship/bridge)
"MZ" = (
@@ -4877,7 +4998,7 @@
dir = 8
},
/obj/structure/closet/crate{
- name = "Mishun Acomplshed"
+ name = "Mission Accomplished"
},
/obj/item/poster/mission_accomplished_7,
/obj/item/poster/mission_accomplished_6,
@@ -4917,8 +5038,13 @@
/area/ship/external/dark)
"NF" = (
/obj/structure/closet/crate/radiation,
-/obj/effect/turf_decal/borderfloorblack{
- dir = 4
+/obj/item/clothing/suit/radiation,
+/obj/item/clothing/suit/radiation,
+/obj/item/clothing/head/radiation{
+ pixel_x = -7
+ },
+/obj/item/clothing/head/radiation{
+ pixel_x = -7
},
/turf/open/floor/plasteel/dark,
/area/ship/construction)
@@ -5119,6 +5245,9 @@
/turf/open/floor/vault,
/area/ship/security/armory)
"Pb" = (
+/obj/structure/railing{
+ dir = 4
+ },
/turf/open/floor/plasteel/stairs{
icon = 'icons/obj/stairs.dmi';
dir = 2
@@ -5145,7 +5274,6 @@
/turf/open/floor/plating,
/area/ship/engineering)
"Ph" = (
-/obj/machinery/atmospherics/pipe/simple/orange/visible,
/obj/structure/cable{
icon_state = "2-9"
},
@@ -5155,6 +5283,9 @@
/obj/machinery/atmospherics/components/trinary/mixer/layer2{
dir = 1
},
+/obj/machinery/atmospherics/pipe/manifold/orange/visible{
+ dir = 4
+ },
/turf/open/floor/plasteel/tech,
/area/ship/engineering/engines/port)
"Pi" = (
@@ -5360,6 +5491,13 @@
/obj/effect/turf_decal/corner/opaque/neutral/full,
/turf/open/floor/vault,
/area/ship/security/armory)
+"Qi" = (
+/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/computer/cargo{
+ dir = 8
+ },
+/turf/open/floor/plasteel/telecomms_floor,
+/area/ship/bridge)
"Qq" = (
/obj/structure/rack,
/obj/item/kitchen/knife/combat/survival{
@@ -5867,6 +6005,7 @@
/obj/item/gun/energy/kalix/pistol{
pixel_y = -16
},
+/obj/item/screwdriver,
/turf/open/floor/plasteel/sepia,
/area/ship/crew/dorm/dormthree)
"UH" = (
@@ -5915,13 +6054,6 @@
pixel_x = -10;
pixel_y = -7
},
-/obj/item/clothing/under/gezena/marine{
- pixel_x = -10;
- pixel_y = -7
- },
-/obj/item/clothing/gloves/gezena/marine{
- pixel_y = 11
- },
/obj/item/clothing/gloves/gezena/marine{
pixel_y = 11
},
@@ -5931,22 +6063,6 @@
/obj/item/clothing/gloves/gezena/marine{
pixel_y = 11
},
-/obj/item/clothing/head/gezena/marine/flap{
- pixel_x = -10
- },
-/obj/item/clothing/head/gezena/marine/flap{
- pixel_x = -10
- },
-/obj/item/clothing/head/gezena/marine/flap{
- pixel_x = -10
- },
-/obj/item/clothing/head/gezena/marine/flap{
- pixel_x = -10
- },
-/obj/item/clothing/head/gezena/marine{
- pixel_y = 10;
- pixel_x = -10
- },
/obj/item/clothing/head/gezena/marine{
pixel_y = 10;
pixel_x = -10
@@ -5975,13 +6091,6 @@
/obj/item/storage/backpack/duffelbag/sec{
pixel_y = -13
},
-/obj/item/storage/backpack/duffelbag/sec{
- pixel_y = -13
- },
-/obj/item/storage/backpack/satchel/sec{
- pixel_x = -8;
- pixel_y = -17
- },
/obj/item/storage/backpack/satchel/sec{
pixel_x = -8;
pixel_y = -17
@@ -6006,11 +6115,19 @@
pixel_x = 3;
pixel_y = 0
},
-/obj/item/clothing/shoes/combat/gezena{
- pixel_x = 3;
- pixel_y = 0
- },
/obj/effect/turf_decal/corner/opaque/neutral/full,
+/obj/item/clothing/head/gezena/flap/marine{
+ pixel_y = 1;
+ pixel_x = -10
+ },
+/obj/item/clothing/head/gezena/flap/marine{
+ pixel_y = 1;
+ pixel_x = -10
+ },
+/obj/item/clothing/head/gezena/flap/marine{
+ pixel_y = 1;
+ pixel_x = -10
+ },
/turf/open/floor/vault,
/area/ship/security/armory)
"Vp" = (
@@ -6059,11 +6176,11 @@
/turf/open/floor/plasteel/mono,
/area/ship/hangar/starboard)
"VM" = (
-/obj/machinery/power/terminal{
- dir = 8
- },
-/obj/structure/cable,
/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/atmospherics/components/binary/pump{
+ dir = 8;
+ name = "engine fuel pump"
+ },
/turf/open/floor/plating,
/area/ship/engineering/engines/starboard)
"VY" = (
@@ -6075,7 +6192,7 @@
anchored = 1;
can_be_unanchored = 1;
icon_state = "warden";
- name = "sergeant's locker";
+ name = "Lieutenant's locker";
req_access_txt = "3";
req_access = list(3)
},
@@ -6106,10 +6223,6 @@
pixel_y = 1;
pixel_x = -11
},
-/obj/item/clothing/head/gezena/marine/lead/flap{
- pixel_y = 9;
- pixel_x = -11
- },
/obj/item/clothing/suit/armor/gezena/marinecoat{
pixel_y = 8
},
@@ -6117,14 +6230,6 @@
pixel_y = 11;
pixel_x = 5
},
-/obj/item/radio/headset/pgf/captain{
- pixel_x = -4;
- pixel_y = 9
- },
-/obj/item/kitchen/knife/combat/survival{
- pixel_y = 0;
- pixel_x = 1
- },
/obj/item/storage/belt/military/gezena{
pixel_y = 1
},
@@ -6132,6 +6237,10 @@
pixel_x = -8;
pixel_y = -17
},
+/obj/item/radio/headset/pgf/captain{
+ pixel_x = -2;
+ pixel_y = 9
+ },
/obj/item/storage/backpack/duffelbag/sec{
pixel_y = -13
},
@@ -6140,7 +6249,14 @@
pixel_x = -3
},
/obj/item/clothing/head/helmet/gezena{
- pixel_y = -11
+ pixel_x = -11
+ },
+/obj/item/clothing/suit/armor/gezena/marine{
+ pixel_y = 8
+ },
+/obj/item/clothing/head/gezena/flap/marine/lead{
+ pixel_y = 10;
+ pixel_x = -10
},
/turf/open/floor/plasteel/tech,
/area/ship/crew/dorm/dormtwo)
@@ -6171,6 +6287,16 @@
},
/turf/open/floor/plasteel/sepia,
/area/ship/crew/dorm)
+"Wg" = (
+/obj/effect/turf_decal/spline/fancy/opaque/lime{
+ dir = 1
+ },
+/obj/effect/turf_decal/spline/fancy/opaque/lime,
+/obj/structure/railing{
+ dir = 1
+ },
+/turf/open/floor/plasteel/tech,
+/area/ship/bridge)
"Wk" = (
/obj/machinery/power/smes/shuttle/precharged{
dir = 4
@@ -6229,9 +6355,6 @@
dir = 5
},
/obj/structure/catwalk/over/plated_catwalk/dark,
-/obj/structure/cable{
- icon_state = "2-8"
- },
/obj/structure/cable{
icon_state = "1-2"
},
@@ -6384,13 +6507,11 @@
/turf/open/floor/plasteel/tech,
/area/ship/construction)
"Yd" = (
-/obj/machinery/power/terminal{
- dir = 8
- },
-/obj/structure/cable{
- icon_state = "0-2"
- },
/obj/structure/catwalk/over/plated_catwalk/dark,
+/obj/machinery/atmospherics/components/binary/pump{
+ dir = 8;
+ name = "engine fuel pump"
+ },
/turf/open/floor/plating,
/area/ship/engineering/engines/port)
"Yo" = (
@@ -6707,14 +6828,14 @@ xz
xz
lc
hJ
-hJ
+ae
is
lc
xz
xz
TV
FN
-fN
+dv
fN
TV
xz
@@ -6735,14 +6856,14 @@ xz
lc
lc
CB
-CB
+AM
UM
lc
FO
LD
TV
bB
-Wk
+pK
Wk
TV
TV
@@ -7914,8 +8035,8 @@ bb
wz
tQ
El
-Ms
-MF
+Wg
+om
MF
eO
Ly
@@ -7941,10 +8062,10 @@ MF
Ig
uh
qj
-Ms
-CZ
-xz
-Mk
+tY
+hX
+MF
+MF
Bc
KS
Sp
@@ -7968,10 +8089,10 @@ Hb
vr
dK
Gb
-Ms
+yy
+KH
CZ
xz
-xz
Mk
iK
sQ
@@ -7993,13 +8114,13 @@ xz
xz
xz
cR
-Rx
-bD
-bD
+Sd
+Qi
+bi
+Ms
CZ
xz
xz
-xz
Mk
Er
Mk
@@ -8021,10 +8142,10 @@ xz
xz
xz
xz
-xz
-xz
-xz
-xz
+Rx
+bD
+bD
+CZ
xz
xz
xz
diff --git a/_maps/shuttles/solgov/solgov_chronicle.dmm b/_maps/shuttles/solgov/solgov_chronicle.dmm
index 012261bda246..b234552b67c6 100644
--- a/_maps/shuttles/solgov/solgov_chronicle.dmm
+++ b/_maps/shuttles/solgov/solgov_chronicle.dmm
@@ -267,7 +267,7 @@
/obj/item/door_remote/captain,
/obj/item/storage/belt/sabre/solgov,
/obj/item/clothing/under/solgov/dress,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/captain,
+/obj/item/clothing/suit/armor/vest/solgov/captain,
/obj/item/stamp/solgov,
/obj/item/clothing/suit/armor/solgov_trenchcoat,
/obj/item/spacecash/bundle/loadsamoney,
@@ -955,7 +955,7 @@
/obj/item/clothing/gloves/combat,
/obj/item/radio/headset/solgov/alt,
/obj/item/storage/backpack,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
/obj/effect/turf_decal/techfloor{
dir = 1
},
@@ -4105,7 +4105,7 @@
/area/ship/crew/office)
"OU" = (
/obj/item/clothing/neck/cloak/overseer,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/overseer,
+/obj/item/clothing/suit/armor/vest/solgov/overseer,
/obj/structure/closet/secure_closet/head_of_personnel{
anchored = 1;
name = "\proper overseer's locker";
diff --git a/_maps/shuttles/solgov/solgov_inkwell.dmm b/_maps/shuttles/solgov/solgov_inkwell.dmm
index f2cf61f1f33b..14bce5a912e1 100644
--- a/_maps/shuttles/solgov/solgov_inkwell.dmm
+++ b/_maps/shuttles/solgov/solgov_inkwell.dmm
@@ -177,7 +177,7 @@
/obj/item/clothing/gloves/combat,
/obj/item/radio/headset/solgov/alt,
/obj/item/storage/backpack,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
/obj/effect/turf_decal/techfloor,
/obj/effect/turf_decal/industrial/outline/red,
/obj/structure/window/reinforced{
@@ -308,7 +308,7 @@
/obj/item/clothing/gloves/combat,
/obj/item/radio/headset/solgov/alt,
/obj/item/storage/backpack,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
/obj/effect/turf_decal/techfloor,
/obj/effect/turf_decal/industrial/outline/red,
/obj/structure/window/reinforced{
@@ -1526,9 +1526,9 @@
/obj/structure/closet/cabinet{
name = "armor cabinet"
},
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
/obj/item/clothing/head/solgov/sonnensoldner,
/obj/item/clothing/head/solgov/sonnensoldner,
/obj/item/clothing/head/solgov/sonnensoldner,
@@ -3647,7 +3647,7 @@
/obj/item/clothing/gloves/combat,
/obj/item/radio/headset/solgov/alt,
/obj/item/storage/backpack,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov,
+/obj/item/clothing/suit/armor/vest/solgov,
/obj/effect/turf_decal/techfloor,
/obj/effect/turf_decal/industrial/outline/red,
/turf/open/floor/plasteel/white,
@@ -5839,7 +5839,7 @@
/area/ship/cargo)
"LB" = (
/obj/item/clothing/head/solgov/captain,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/captain,
+/obj/item/clothing/suit/armor/vest/solgov/captain,
/obj/item/clothing/under/solgov/formal/captain,
/obj/item/clothing/shoes/laceup,
/obj/item/clothing/gloves/combat,
diff --git a/_maps/shuttles/solgov/solgov_paracelsus.dmm b/_maps/shuttles/solgov/solgov_paracelsus.dmm
index 4700c74ee7b0..87d7a39790f0 100644
--- a/_maps/shuttles/solgov/solgov_paracelsus.dmm
+++ b/_maps/shuttles/solgov/solgov_paracelsus.dmm
@@ -2463,7 +2463,7 @@
/area/ship/bridge)
"ye" = (
/obj/item/clothing/neck/cloak/overseer,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/overseer,
+/obj/item/clothing/suit/armor/vest/solgov/overseer,
/obj/structure/closet/secure_closet/head_of_personnel{
anchored = 1;
name = "\proper overseer's locker";
@@ -3560,7 +3560,7 @@
/area/ship/crew/crewtwo)
"IX" = (
/obj/item/clothing/head/solgov/captain,
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/captain,
+/obj/item/clothing/suit/armor/vest/solgov/captain,
/obj/item/clothing/under/solgov/formal/captain,
/obj/item/clothing/shoes/laceup,
/obj/item/clothing/gloves/combat,
diff --git a/_maps/shuttles/subshuttles/pgf_nail.dmm b/_maps/shuttles/subshuttles/pgf_nail.dmm
index 9a5224dc4573..02255b90a5c0 100644
--- a/_maps/shuttles/subshuttles/pgf_nail.dmm
+++ b/_maps/shuttles/subshuttles/pgf_nail.dmm
@@ -391,7 +391,8 @@
icon_state = "0-4"
},
/obj/machinery/porta_turret/ship/weak{
- dir = 8
+ dir = 8;
+ mode = 1
},
/turf/open/floor/engine/hull/reinforced/interior,
/area/ship/external/dark)
diff --git a/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm b/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm
index 8a00270e81b7..7daf18d1f9c3 100644
--- a/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm
+++ b/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm
@@ -4260,7 +4260,7 @@
/turf/open/floor/plating,
/area/ship/engineering)
"PA" = (
-/obj/structure/mecha_wreckage/mauler,
+/obj/structure/mecha_wreckage/touro,
/obj/effect/turf_decal/techfloor{
dir = 8
},
diff --git a/_maps/shuttles/syndicate/syndicate_panacea.dmm b/_maps/shuttles/syndicate/syndicate_panacea.dmm
index 4fdcc0741037..24334160f64b 100644
--- a/_maps/shuttles/syndicate/syndicate_panacea.dmm
+++ b/_maps/shuttles/syndicate/syndicate_panacea.dmm
@@ -2877,7 +2877,7 @@
pixel_x = 8;
pixel_y = -6
},
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/xo{
+/obj/item/clothing/suit/armor/vest/suns/xo{
pixel_y = 7;
pixel_x = -5
},
@@ -3100,7 +3100,7 @@
pixel_x = 6;
pixel_y = -9
},
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/captain{
+/obj/item/clothing/suit/armor/vest/suns/captain{
pixel_y = -1
},
/obj/item/clothing/under/syndicate/suns/captain{
@@ -4895,7 +4895,7 @@
pixel_x = -1;
pixel_y = 4
},
-/obj/item/clothing/suit/armor/vest/bulletproof/suns{
+/obj/item/clothing/suit/armor/vest/suns{
pixel_y = 4;
pixel_x = 8
},
diff --git a/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm b/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm
index 1adef58948fe..7d738e1057b1 100644
--- a/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm
+++ b/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm
@@ -7323,8 +7323,8 @@
/obj/item/clothing/glasses/hud/security/suns,
/obj/item/clothing/gloves/tackler/dolphin/suns,
/obj/item/clothing/suit/toggle/suns/pkcoat,
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/hos,
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/ehos,
+/obj/item/clothing/suit/armor/vest/suns/hos,
+/obj/item/clothing/suit/armor/vest/suns/ehos,
/obj/item/clothing/head/welding/suns/hos,
/obj/item/clothing/under/syndicate/suns/pkuniform,
/obj/item/radio/headset/syndicate/alt{
diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm
index c55604dcebdd..a37fda280ec0 100644
--- a/code/__DEFINES/achievements.dm
+++ b/code/__DEFINES/achievements.dm
@@ -24,6 +24,7 @@
#define MEDAL_SNAIL "KKKiiilll mmmeee"
#define MEDAL_LOOKOUTSIR "Look Out, Sir!"
#define MEDAL_GOTTEM "GOTTEM"
+#define MEDAL_SPRINGLOCK "The Man Inside the Modsuit"
//Skill medal hub IDs
#define MEDAL_LEGENDARY_MINER "Legendary Miner"
diff --git a/code/__DEFINES/actions.dm b/code/__DEFINES/actions.dm
new file mode 100644
index 000000000000..ca2068106994
--- /dev/null
+++ b/code/__DEFINES/actions.dm
@@ -0,0 +1,20 @@
+///Action button checks if hands are unusable
+#define AB_CHECK_HANDS_BLOCKED (1<<0)
+///Action button checks if user is immobile
+#define AB_CHECK_IMMOBILE (1<<1)
+///Action button checks if user is resting
+#define AB_CHECK_LYING (1<<2)
+///Action button checks if user is conscious
+#define AB_CHECK_CONSCIOUS (1<<3)
+
+///Action button triggered with right click
+#define TRIGGER_SECONDARY_ACTION (1<<0)
+
+// Defines for formatting cooldown actions for the stat panel.
+/// The stat panel the action is displayed in.
+#define PANEL_DISPLAY_PANEL "panel"
+/// The status shown in the stat panel.
+/// Can be stuff like "ready", "on cooldown", "active", "charges", "charge cost", etc.
+#define PANEL_DISPLAY_STATUS "status"
+/// The name shown in the stat panel.
+#define PANEL_DISPLAY_NAME "name"
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index dfb53f12ff79..b043f77daed4 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -22,16 +22,6 @@
#define APPRENTICE_ROBELESS "robeless"
#define APPRENTICE_HEALING "healing"
-
-//Blob
-/// blob gets a free reroll every X time
-#define BLOB_REROLL_TIME 2400
-#define BLOB_SPREAD_COST 4
-/// blob refunds this much if it attacks and doesn't spread
-#define BLOB_ATTACK_REFUND 2
-#define BLOB_REFLECTOR_COST 15
-
-
//ERT Types
#define ERT_BLUE "Blue"
#define ERT_RED "Red"
diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index ec5286ca34a4..22e69cd06d9f 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -33,6 +33,10 @@
/// Amount of air to take a from a tile
#define BREATH_PERCENTAGE (BREATH_VOLUME/CELL_VOLUME)
+/// This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive.
+#define BODYTEMP_AUTORECOVERY_DIVISOR 28
+/// The natural temperature for a body
+#define BODYTEMP_NORMAL 310.15
//EXCITED GROUPS
/// number of FULL air controller ticks before an excited group breaks down (averages gas contents across turfs)
diff --git a/code/__DEFINES/blackmarket.dm b/code/__DEFINES/blackmarket.dm
index 042066df522d..c87f0443c4e1 100644
--- a/code/__DEFINES/blackmarket.dm
+++ b/code/__DEFINES/blackmarket.dm
@@ -5,4 +5,6 @@
#define SHIPPING_METHOD_LTSRBT "LTSRBT"
// Throws the item from somewhere at the uplink.
#define SHIPPING_METHOD_LAUNCH "Launch"
+// Drops the item somewhere on the map
+#define SHIPPING_METHOD_DEAD_DROP "Dead drop"
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 69886107d61c..6fa77815ff2b 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -168,3 +168,14 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(/obj/item/gun)))
//We will round to this value in damage calculations.
#define DAMAGE_PRECISION 0.1
+
+/// Alternate attack defines. Return these at the end of procs like afterattack_secondary.
+/// Calls the normal attack proc. For example, if returned in afterattack_secondary, will call afterattack.
+/// Will continue the chain depending on the return value of the non-alternate proc, like with normal attacks.
+#define SECONDARY_ATTACK_CALL_NORMAL 1
+
+/// Cancels the attack chain entirely.
+#define SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN 2
+
+/// Proceed with the attack chain, but don't call the normal methods.
+#define SECONDARY_ATTACK_CONTINUE_CHAIN 3
diff --git a/code/__DEFINES/dcs/signals/signals.dm b/code/__DEFINES/dcs/signals/signals.dm
index 5552a60890d8..38f7d8692853 100644
--- a/code/__DEFINES/dcs/signals/signals.dm
+++ b/code/__DEFINES/dcs/signals/signals.dm
@@ -59,6 +59,14 @@
#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success"
///from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMSIG_PARENT_ATTACKBY "atom_attackby"
+/// From base of [/obj/item/proc/pre_attack_secondary()]: (atom/target, mob/user, params)
+#define COMSIG_ITEM_PRE_ATTACK_SECONDARY "item_pre_attack_secondary"
+ #define COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN (1<<0)
+ #define COMPONENT_SECONDARY_CONTINUE_ATTACK_CHAIN (1<<1)
+ #define COMPONENT_SECONDARY_CALL_NORMAL_ATTACK_CHAIN (1<<2)
+#define COMSIG_PARENT_ATTACKBY_SECONDARY "atom_attackby_secondary"
+/// From base of [/atom/proc/attack_hand_secondary]: (mob/user, list/modifiers) - Called when the atom receives a secondary unarmed attack.
+#define COMSIG_ATOM_ATTACK_HAND_SECONDARY "atom_attack_hand_secondary"
///Return this in response if you don't want afterattack to be called
#define COMPONENT_NO_AFTERATTACK (1<<0)
///from base of atom/attack_hulk(): (/mob/living/carbon/human)
@@ -121,8 +129,6 @@
#define COMSIG_ATOM_CHECKPARTS "atom_checkparts"
///from base of atom/CheckParts(): (atom/movable/new_craft) - The atom has just been used in a crafting recipe and has been moved inside new_craft.
#define COMSIG_ATOM_USED_IN_CRAFT "atom_used_in_craft"
-///from base of atom/blob_act(): (/obj/structure/blob)
-#define COMSIG_ATOM_BLOB_ACT "atom_blob_act"
///from base of atom/acid_act(): (acidpwr, acid_volume)
#define COMSIG_ATOM_ACID_ACT "atom_acid_act"
///from base of atom/emag_act(): (/mob/user)
@@ -158,6 +164,7 @@
///from internal loop in atom/movable/proc/CanReach(): (list/next)
#define COMSIG_ATOM_CANREACH "atom_can_reach"
#define COMPONENT_BLOCK_REACH 1
+ #define COMPONENT_ALLOW_REACH (1<<0)
///for when an atom has been created through processing (atom/original_atom, list/chosen_processing_option)
#define COMSIG_ATOM_CREATEDBY_PROCESSING "atom_createdby_processing"
@@ -337,6 +344,37 @@
#define COMSIG_MOVABLE_LIGHT_OVERLAY_TOGGLE_ON "movable_light_overlay_toggle_on"
///called when the movable's glide size is updated: (new_glide_size)
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size"
+/// from base of atom/movable/Process_Spacemove(): (movement_dir, continuous_move)
+#define COMSIG_MOVABLE_SPACEMOVE "spacemove"
+ #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0)
+ ///from datum/component/drift/apply_initial_visuals(): ()
+#define COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT "movable_drift_visual_attempt"
+ #define DRIFT_VISUAL_FAILED (1<<0)
+ ///from datum/component/drift/allow_final_movement(): ()
+#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input"
+ #define DRIFT_ALLOW_INPUT (1<<0)
+
+///signal sent out by an atom when it checks if it can be pulled, for additional checks
+#define COMSIG_ATOM_CAN_BE_PULLED "movable_can_be_pulled"
+ #define COMSIG_ATOM_CANT_PULL (1 << 0)
+///signal sent out by an atom when it is no longer being pulled by something else
+#define COMSIG_ATOM_NO_LONGER_PULLED "movable_no_longer_pulled"
+///signal sent out by an atom when it is no longer pulling something : (atom/pulling)
+#define COMSIG_ATOM_NO_LONGER_PULLING "movable_no_longer_pulling"
+///called on /living, when pull is attempted, but before it completes, from base of [/mob/living/start_pulling]: (atom/movable/thing, force)
+#define COMSIG_LIVING_TRY_PULL "living_try_pull"
+ #define COMSIG_LIVING_CANCEL_PULL (1 << 0)
+/// Called from /mob/living/update_pull_movespeed
+#define COMSIG_LIVING_UPDATING_PULL_MOVESPEED "living_updating_pull_movespeed"
+/// Called from /mob/living/PushAM -- Called when this mob is about to push a movable, but before it moves
+/// (aotm/movable/being_pushed)
+#define COMSIG_LIVING_PUSHING_MOVABLE "living_pushing_movable"
+///from base of [/atom/proc/interact]: (mob/user)
+#define COMSIG_ATOM_UI_INTERACT "atom_ui_interact"
+///called on /living when attempting to pick up an item, from base of /mob/living/put_in_hand_check(): (obj/item/I)
+#define COMSIG_LIVING_TRY_PUT_IN_HAND "living_try_put_in_hand"
+ /// Can't pick up
+ #define COMPONENT_LIVING_CANT_PUT_IN_HAND (1<<0)
// /mob signals
@@ -358,6 +396,9 @@
#define COMSIG_MOB_ALTCLICKON "mob_altclickon"
#define COMSIG_MOB_CANCEL_CLICKON (1<<0)
+///From base of mob/living/MobBump() (mob/living)
+#define COMSIG_LIVING_MOB_BUMP "living_mob_bump"
+
///from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj
#define COMSIG_MOB_ALLOWED "mob_allowed"
///from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources)
@@ -549,13 +590,6 @@
// /obj/mecha signals
#define COMSIG_MECHA_ACTION_ACTIVATE "mecha_action_activate" //sent from mecha action buttons to the mecha they're linked to
-// /mob/living/carbon/human signals
-#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity)
-#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity)
-#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
-#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
-#define COMSIG_JOB_RECEIVED "job_received" //Whenever EquipRanked is called, called after job is set
-
// /datum/species signals
#define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species)
#define COMSIG_SPECIES_LOSS "species_loss" //from datum/species/on_species_loss(): (datum/species/lost_species)
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
new file mode 100644
index 000000000000..2428eddf1346
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -0,0 +1,82 @@
+///Called from /mob/living/carbon/help_shake_act, before any hugs have ocurred. (mob/living/helper)
+#define COMSIG_CARBON_PRE_HELP_ACT "carbon_pre_help"
+ /// Stops the rest of help act (hugging, etc) from occuring
+ #define COMPONENT_BLOCK_HELP_ACT (1<<0)
+
+///Called from /mob/living/carbon/help_shake_act on the person being helped, after any hugs have ocurred. (mob/living/helper)
+#define COMSIG_CARBON_HELP_ACT "carbon_help"
+///Called from /mob/living/carbon/help_shake_act on the helper, after any hugs have ocurred. (mob/living/helped)
+#define COMSIG_CARBON_HELPED "carbon_helped_someone"
+
+///Before a carbon mob is shoved, sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target)
+#define COMSIG_CARBON_DISARM_PRESHOVE "carbon_disarm_preshove"
+ #define COMSIG_CARBON_ACT_SOLID (1<<0) //Tells disarm code to act as if the mob was shoved into something solid, even we we're not
+///When a carbon mob is disarmed, this is sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
+#define COMSIG_CARBON_DISARM_COLLIDE "carbon_disarm_collision"
+ #define COMSIG_CARBON_SHOVE_HANDLED (1<<0)
+
+// /mob/living/carbon physiology signals
+#define COMSIG_CARBON_GAIN_WOUND "carbon_gain_wound" //from /datum/wound/proc/apply_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L)
+#define COMSIG_CARBON_LOSE_WOUND "carbon_lose_wound" //from /datum/wound/proc/remove_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L)
+///from base of /obj/item/bodypart/proc/attach_limb(): (new_limb, special) allows you to fail limb attachment
+#define COMSIG_CARBON_ATTACH_LIMB "carbon_attach_limb"
+#define COMSIG_CARBON_REMOVE_LIMB "carbon_remove_limb" //from base of /obj/item/bodypart/proc/drop_limb(lost_limb, dismembered)
+#define COMSIG_BODYPART_GAUZED "bodypart_gauzed" // from /obj/item/bodypart/proc/apply_gauze(/obj/item/stack/gauze)
+#define COMSIG_BODYPART_GAUZE_DESTROYED "bodypart_degauzed" // from [/obj/item/bodypart/proc/seep_gauze] when it runs out of absorption
+
+///Called when someone attempts to cuff a carbon
+#define COMSIG_CARBON_CUFF_ATTEMPTED "carbon_attempt_cuff"
+///Called when a carbon mutates (source = dna, mutation = mutation added)
+#define COMSIG_CARBON_GAIN_MUTATION "carbon_gain_mutation"
+///Called when a carbon loses a mutation (source = dna, mutation = mutation lose)
+#define COMSIG_CARBON_LOSE_MUTATION "carbon_lose_mutation"
+///Called when a carbon becomes addicted (source = what addiction datum, addicted_mind = mind of the addicted carbon)
+#define COMSIG_CARBON_GAIN_ADDICTION "carbon_gain_addiction"
+///Called when a carbon is no longer addicted (source = what addiction datum was lost, addicted_mind = mind of the freed carbon)
+#define COMSIG_CARBON_LOSE_ADDICTION "carbon_lose_addiction"
+///Called when a carbon gets a brain trauma (source = carbon, trauma = what trauma was added) - this is before on_gain()
+#define COMSIG_CARBON_GAIN_TRAUMA "carbon_gain_trauma"
+///Called when a carbon loses a brain trauma (source = carbon, trauma = what trauma was removed)
+#define COMSIG_CARBON_LOSE_TRAUMA "carbon_lose_trauma"
+///Called when a carbon updates their health (source = carbon)
+#define COMSIG_CARBON_HEALTH_UPDATE "carbon_health_update"
+///Called when a carbon updates their sanity (source = carbon)
+#define COMSIG_CARBON_SANITY_UPDATE "carbon_sanity_update"
+///Called when a carbon breathes, before the breath has actually occured
+#define COMSIG_CARBON_PRE_BREATHE "carbon_pre_breathe"
+///Called when a carbon updates their mood
+#define COMSIG_CARBON_MOOD_UPDATE "carbon_mood_update"
+
+// /mob/living/carbon/human signals
+
+///Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
+#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit"
+///Whenever EquipRanked is called, called after job is set
+#define COMSIG_JOB_RECEIVED "job_received"
+///from /mob/living/carbon/human/proc/set_coretemperature(): (oldvalue, newvalue)
+#define COMSIG_HUMAN_CORETEMP_CHANGE "human_coretemp_change"
+///from /datum/species/handle_fire. Called when the human is set on fire and burning clothes and stuff
+#define COMSIG_HUMAN_BURNING "human_burning"
+///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers)
+#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack"
+///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers)
+#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack"
+//from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
+#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby"
+//from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration)
+#define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields"
+ #define SHIELD_BLOCK (1<<0)
+
+// Mob transformation signals
+///Called when a human turns into a monkey, from /mob/living/carbon/proc/finish_monkeyize()
+#define COMSIG_HUMAN_MONKEYIZE "human_monkeyize"
+///Called when a monkey turns into a human, from /mob/living/carbon/proc/finish_humanize(species)
+#define COMSIG_MONKEY_HUMANIZE "monkey_humanize"
+
+///From mob/living/carbon/human/suicide()
+#define COMSIG_HUMAN_SUICIDE_ACT "human_suicide_act"
+
+/// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot)
+#define COMSIG_MOB_EQUIPPED_ITEM "mob_equipped_item"
+/// A mob has just unequipped an item.
+#define COMSIG_MOB_UNEQUIPPED_ITEM "mob_unequipped_item"
diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm
new file mode 100644
index 000000000000..e5c27a902a65
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_mod.dm
@@ -0,0 +1,25 @@
+//MODsuit signals
+/// Called when a module is selected to be the active one from on_select(obj/item/mod/module/module)
+#define COMSIG_MOD_MODULE_SELECTED "mod_module_selected"
+/// Called when a MOD activation is called from toggle_activate(mob/user)
+#define COMSIG_MOD_ACTIVATE "mod_activate"
+ /// Cancels the suit's activation
+ #define MOD_CANCEL_ACTIVATE (1 << 0)
+/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar)
+#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal"
+ /// Cancels the removal of modules
+ #define MOD_CANCEL_REMOVAL (1 << 0)
+/// Called when a module attempts to activate, however it does. At the end of checks so you can add some yourself, or work on trigger behavior (mob/user)
+#define COMSIG_MODULE_TRIGGERED "mod_module_triggered"
+ // Cancels activation, with no message. include feedback on your cancel.
+ #define MOD_ABORT_USE (1<<0)
+/// Called when a module activates, after all checks have passed and cooldown started.
+#define COMSIG_MODULE_ACTIVATED "mod_module_activated"
+/// Called when a module deactivates, after all checks have passed.
+#define COMSIG_MODULE_DEACTIVATED "mod_module_deactivated"
+/// Called when a module is used, after all checks have passed and cooldown started.
+#define COMSIG_MODULE_USED "mod_module_used"
+/// Called when the MODsuit wearer is set.
+#define COMSIG_MOD_WEARER_SET "mod_wearer_set"
+/// Called when the MODsuit wearer is unset.
+#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset"
diff --git a/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm b/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm
index 536efc724f92..1a562b84a4a1 100644
--- a/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm
+++ b/code/__DEFINES/dcs/signals/signals_obj/signals_item/signals_item.dm
@@ -13,7 +13,8 @@
///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone)
#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone"
///from base of obj/item/hit_reaction(): (list/args)
-#define COMSIG_ITEM_HIT_REACT "item_hit_react"
+#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args)
+ #define COMPONENT_HIT_REACTION_BLOCK (1<<0)
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
diff --git a/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm b/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm
index 137925811720..136b73ffb3d2 100644
--- a/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_obj/signals_object.dm
@@ -10,3 +10,5 @@
#define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench"
///from base of /turf/proc/levelupdate(). (intact) true to hide and false to unhide
#define COMSIG_OBJ_HIDE "obj_hide"
+/// from base of [/atom/proc/obj_destruction]: (damage_flag)
+#define COMSIG_OBJ_DESTRUCTION "obj_destruction"
diff --git a/code/__DEFINES/dcs/signals/signals_storage.dm b/code/__DEFINES/dcs/signals/signals_storage.dm
new file mode 100644
index 000000000000..456ac3c0781a
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_storage.dm
@@ -0,0 +1,4 @@
+/// Sent when /datum/storage/dump_content_at(): (obj/item/storage_source, mob/user)
+#define COMSIG_STORAGE_DUMP_CONTENT "storage_dump_contents"
+ /// Return to stop the standard dump behavior.
+ #define STORAGE_DUMP_HANDLED (1<<0)
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index c27a78ffd2de..77e608ac922e 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -46,6 +46,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define SHOW_BEHIND_LARGE_ICONS_1 (1<<12)
/// Should we use the initial icon for display? Mostly used by overlay only objects
#define HTML_USE_INITAL_ICON_1 (1<<20)
+// Whether or not this atom is storing contents for a disassociated storage object
+#define HAS_DISASSOCIATED_STORAGE_1 (1<<24)
// Update flags for [/atom/proc/update_appearance]
/// Update the atom's name
@@ -83,8 +85,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
////////////////Area flags\\\\\\\\\\\\\\
/// If it's a valid territory for cult summoning or the CRAB-17 phone to spawn
#define VALID_TERRITORY (1<<0)
-/// If blobs can spawn there and if it counts towards their score.
-#define BLOBS_ALLOWED (1<<1)
/// If mining tunnel generation is allowed in this area
#define CAVES_ALLOWED (1<<2)
/// If flora are allowed to spawn in this area randomly through tunnel generation
@@ -108,7 +108,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define PASSTABLE (1<<0)
#define PASSGLASS (1<<1)
#define PASSGRILLE (1<<2)
-#define PASSBLOB (1<<3)
#define PASSMOB (1<<4)
#define PASSCLOSEDTURF (1<<5)
/// Let thrown things past us. **ONLY MEANINGFUL ON pass_flags_self!**
diff --git a/code/__DEFINES/generators.dm b/code/__DEFINES/generators.dm
new file mode 100644
index 000000000000..e9d373c9a65d
--- /dev/null
+++ b/code/__DEFINES/generators.dm
@@ -0,0 +1,15 @@
+//generator types
+#define GEN_NUM "num"
+#define GEN_VECTOR "vector"
+#define GEN_BOX "box"
+#define GEN_COLOR "color"
+#define GEN_CIRCLE "circle"
+#define GEN_SPHERE "sphere"
+#define GEN_SQUARE "square"
+#define GEN_CUBE "cube"
+
+///particle editor var modifiers
+#define P_DATA_GENERATOR "generator"
+#define P_DATA_ICON_ADD "icon_add"
+#define P_DATA_ICON_REMOVE "icon_remove"
+#define P_DATA_ICON_WEIGHT "icon_edit"
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index 64aa6aa52627..6cad7078b6ec 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -46,6 +46,7 @@
#define HIDEFACIALHAIR (1<<9)
#define HIDENECK (1<<10)
#define HIDEHORNS (1<<11) // Used for hiding Sarathi horns.
+#define HIDESNOUT (1<<11)
//bitflags for clothing coverage - also used for limbs
#define HEAD (1<<0)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 241136c297b9..d1db31e6a4fc 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -8,6 +8,8 @@
#define isweakref(D) (istype(D, /datum/weakref))
+#define isgenerator(A) (istype(A, /generator))
+
//Turfs
//#define isturf(A) (istype(A, /turf)) This is actually a byond built-in. Added here for completeness sake.
@@ -155,8 +157,6 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isnewplayer(A) (istype(A, /mob/dead/new_player))
-#define isovermind(A) (istype(A, /mob/camera/blob))
-
#define iscameramob(A) (istype(A, /mob/camera))
#define isaicamera(A) (istype(A, /mob/camera/aiEye))
@@ -166,6 +166,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isitem(A) (istype(A, /obj/item))
+#define isstack(A) (istype(A, /obj/item/stack))
+
#define isgrenade(A) (istype(A, /obj/item/grenade))
#define islandmine(A) (istype(A, /obj/item/mine))
@@ -227,8 +229,6 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
#define iseffect(O) (istype(O, /obj/effect))
-#define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob))
-
#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
#define isProbablyWallMounted(O) (O.pixel_x > 20 || O.pixel_x < -20 || O.pixel_y > 20 || O.pixel_y < -20)
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index d021558901f4..bd9b0f0063bb 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -1,5 +1,6 @@
//Defines for atom layers and planes
//KEEP THESE IN A NICE ACSCENDING ORDER, PLEASE
+#define LOWEST_EVER_PLANE -100
#define CLICKCATCHER_PLANE -99
@@ -21,6 +22,20 @@
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
#define BLACKNESS_PLANE_RENDER_TARGET "BLACKNESS_PLANE"
+#define ABOVE_GAME_PLANE 1
+
+//-------------------- Rendering ---------------------
+#define RENDER_PLANE_GAME 100
+#define RENDER_PLANE_NON_GAME 101
+#define RENDER_PLANE_MASTER 102
+
+// Lummox I swear to god I will find you
+// NOTE! You can only ever have planes greater then -10000, if you add too many with large offsets you will brick multiz
+// Same can be said for large multiz maps. Tread carefully mappers
+#define HIGHEST_EVER_PLANE RENDER_PLANE_MASTER
+/// The range unique planes can be in
+#define PLANE_RANGE (HIGHEST_EVER_PLANE - LOWEST_EVER_PLANE)
+
#define SPACE_LAYER 1.8
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
#define MID_TURF_LAYER 2.02
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index 719f06f2a812..a442ddb464b8 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -290,3 +290,11 @@
/// Like SPT_PROB_RATE but easier to use, simply put `if(SPT_PROB(10, 5))`
#define SPT_PROB(prob_per_second_percent, seconds_per_tick) (prob(100*SPT_PROB_RATE((prob_per_second_percent)/100, (seconds_per_tick))))
+
+/// Converts a probability/second chance to probability/delta_time chance
+/// For example, if you want an event to happen with a 10% per second chance, but your proc only runs every 5 seconds, do `if(prob(100*DT_PROB_RATE(0.1, 5)))`
+#define DT_PROB_RATE(prob_per_second, delta_time) (1 - (1 - (prob_per_second)) ** (delta_time))
+
+/// Like DT_PROB_RATE but easier to use, simply put `if(DT_PROB(10, 5))`
+#define DT_PROB(prob_per_second_percent, delta_time) (prob(100*DT_PROB_RATE((prob_per_second_percent)/100, (delta_time))))
+// )
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index ddd395d3879a..3c815ef1fc37 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -277,9 +277,6 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define NUKE_SYNDICATE_BASE 3
#define STATION_DESTROYED_NUKE 4
#define STATION_EVACUATED 5
-#define BLOB_WIN 8
-#define BLOB_NUKE 9
-#define BLOB_DESTROYED 10
#define CULT_ESCAPE 11
#define CULT_FAILURE 12
#define CULT_SUMMON 13
diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm
new file mode 100644
index 000000000000..29a450eceb91
--- /dev/null
+++ b/code/__DEFINES/mod.dm
@@ -0,0 +1,40 @@
+/// Default value for the max_complexity var on MODsuits
+#define DEFAULT_MAX_COMPLEXITY 15
+
+/// Default cell drain per process on MODsuits
+#define DEFAULT_CHARGE_DRAIN 0.09
+
+/// Default time for a part to seal
+#define MOD_ACTIVATION_STEP_TIME (2 SECONDS)
+
+/// Passive module, just acts when put in naturally.
+#define MODULE_PASSIVE 0
+/// Usable module, does something when you press a button.
+#define MODULE_USABLE 1
+/// Toggle module, you turn it on/off and it does stuff.
+#define MODULE_TOGGLE 2
+/// Actively usable module, you may only have one selected at a time.
+#define MODULE_ACTIVE 3
+
+//Defines used by the theme for clothing flags and similar
+#define CONTROL_LAYER "control_layer"
+#define HELMET_FLAGS "helmet_flags"
+#define CHESTPLATE_FLAGS "chestplate_flags"
+#define GAUNTLETS_FLAGS "gauntlets_flags"
+#define BOOTS_FLAGS "boots_flags"
+
+#define UNSEALED_LAYER "unsealed_layer"
+#define UNSEALED_CLOTHING "unsealed_clothing"
+#define SEALED_CLOTHING "sealed_clothing"
+#define UNSEALED_INVISIBILITY "unsealed_invisibility"
+#define SEALED_INVISIBILITY "sealed_invisibility"
+#define UNSEALED_COVER "unsealed_cover"
+#define SEALED_COVER "sealed_cover"
+#define CAN_OVERSLOT "can_overslot"
+
+//Defines used to override MOD clothing's icon and worn icon files in the skin.
+#define MOD_ICON_OVERRIDE "mod_icon_override"
+#define MOD_WORN_ICON_OVERRIDE "mod_worn_icon_override"
+
+/// Global list of all /datum/mod_theme
+GLOBAL_LIST_INIT(mod_themes, setup_mod_themes())
diff --git a/code/__DEFINES/particles.dm b/code/__DEFINES/particles.dm
new file mode 100644
index 000000000000..5657566a63bb
--- /dev/null
+++ b/code/__DEFINES/particles.dm
@@ -0,0 +1,5 @@
+// /obj/effect/abstract/particle_holder/var/particle_flags
+// Flags that effect how a particle holder displays something
+
+/// If we're inside something inside a mob, display off that mob too
+#define PARTICLE_ATTACH_MOB (1<<0)
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 9e253563fe89..874c27ac276f 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -18,7 +18,6 @@
#define ROLE_ALIEN "Xenomorph"
#define ROLE_PAI "pAI"
#define ROLE_CULTIST "Cultist"
-#define ROLE_BLOB "Blob"
#define ROLE_NINJA "Space Ninja"
#define ROLE_MONKEY "Monkey"
#define ROLE_ABDUCTOR "Abductor"
@@ -54,7 +53,6 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_ALIEN,
ROLE_PAI,
ROLE_CULTIST = /datum/game_mode/cult,
- ROLE_BLOB,
ROLE_NINJA,
ROLE_OBSESSED,
ROLE_SPACE_DRAGON,
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 1bd23038c600..285e7ce5ff80 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -174,4 +174,36 @@
#define SOUND_AREA_ICEMOON SOUND_ENVIRONMENT_CAVE
#define SOUND_AREA_WOODFLOOR SOUND_ENVIRONMENT_CITY
+/// List of all of our sound keys.
+#define SFX_BODYFALL "bodyfall"
+#define SFX_BULLET_MISS "bullet_miss"
+#define SFX_CAN_OPEN "can_open"
+#define SFX_CLOWN_STEP "clown_step"
+#define SFX_DESECRATION "desecration"
+#define SFX_EXPLOSION "explosion"
+#define SFX_EXPLOSION_CREAKING "explosion_creaking"
+#define SFX_HISS "hiss"
+#define SFX_HONKBOT_E "honkbot_e"
+#define SFX_HULL_CREAKING "hull_creaking"
+#define SFX_HYPERTORUS_CALM "hypertorus_calm"
+#define SFX_HYPERTORUS_MELTING "hypertorus_melting"
+#define SFX_IM_HERE "im_here"
+#define SFX_LAW "law"
+#define SFX_PAGE_TURN "page_turn"
+#define SFX_PUNCH "punch"
+#define SFX_REVOLVER_SPIN "revolver_spin"
+#define SFX_RICOCHET "ricochet"
+#define SFX_RUSTLE "rustle"
+#define SFX_SHATTER "shatter"
+#define SFX_SM_CALM "sm_calm"
+#define SFX_SM_DELAM "sm_delam"
+#define SFX_SPARKS "sparks"
+#define SFX_SUIT_STEP "suit_step"
+#define SFX_SWING_HIT "swing_hit"
+#define SFX_TERMINAL_TYPE "terminal_type"
+#define SFX_WARPSPEED "warpspeed"
+#define SFX_CRUNCHY_BUSH_WHACK "crunchy_bush_whack"
+#define SFX_TREE_CHOP "tree_chop"
+#define SFX_ROCK_TAP "rock_tap"
+
#define SOUND_EMPTY_MAG 'sound/weapons/empty.ogg'
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index d1fbf26616d5..68913e3925fd 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -99,6 +99,14 @@
#define STATUS_EFFECT_METAB_FROZEN /datum/status_effect/metab_frozen // Affected cannot process chems
+//Incapacitated status effect flags
+/// If the incapacitated status effect will ignore a mob in restraints (handcuffs)
+#define IGNORE_RESTRAINTS (1<<0)
+/// If the incapacitated status effect will ignore a mob in stasis (stasis beds)
+#define IGNORE_STASIS (1<<1)
+/// If the incapacitated status effect will ignore a mob being agressively grabbed
+#define IGNORE_GRAB (1<<2)
+
/////////////
// NEUTRAL //
/////////////
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 6a85a5c82fde..7b687330d71a 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -180,7 +180,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_VIRUSIMMUNE "virus_immunity"
#define TRAIT_PIERCEIMMUNE "pierce_immunity"
#define TRAIT_NODISMEMBER "dismember_immunity"
+#define TRAIT_LAVA_IMMUNE "lava_immunity"
+#define TRAIT_SNOWSTORM_IMMUNE "snow_immunity"
+#define TRAIT_ASHSTORM_IMMUNE "ash_immunity"
+#define TRAIT_SANDSTORM_IMMUNE "sand_immunity"
#define TRAIT_NOFIRE "nonflammable"
+/// Prevents plasmamen from self-igniting if only their helmet is missing
+#define TRAIT_NOSELFIGNITION_HEAD_ONLY "no_selfignition_head_only"
#define TRAIT_NOGUNS "no_guns"
#define TRAIT_NOHUNGER "no_hunger"
#define TRAIT_NOMETABOLISM "no_metabolism"
@@ -216,6 +222,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NOMOBSWAP "no-mob-swap"
#define TRAIT_XRAY_VISION "xray_vision"
#define TRAIT_THERMAL_VISION "thermal_vision"
+/// Like antimagic, but doesn't block the user from casting
+#define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock"
/// We have some form of forced gravity acting on us
#define TRAIT_FORCED_GRAVITY "forced_gravity"
#define TRAIT_ABDUCTOR_TRAINING "abductor-training"
@@ -249,6 +257,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_GAMERGOD "gamer-god" //double arcade prizes
#define TRAIT_GIANT "giant"
#define TRAIT_DWARF "dwarf"
+#define TRAIT_FASTMED "fast_med_use"
#define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" //makes your footsteps completely silent
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good....
/// The holder of this trait has antennae or whatever that hurt a ton when noogied
@@ -280,6 +289,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_AREA_SENSITIVE "area-sensitive"
///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags]
+///every object that is currently the active storage of some client mob has this trait
+#define TRAIT_ACTIVE_STORAGE "active_storage"
+
#define TRAIT_KEEP_TOGETHER "keep-together"
// item traits
@@ -363,6 +375,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define LYING_DOWN_TRAIT "lying-down"
/// Trait associated to lacking electrical power.
#define POWER_LACK_TRAIT "power-lack"
+/// Trait applied by MODsuits.
+#define MOD_TRAIT "mod"
// unique trait sources, still defines
#define CLONING_POD_TRAIT "cloning-pod"
@@ -436,6 +450,31 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define BEAUTY_ELEMENT_TRAIT "beauty_element"
#define MOOD_COMPONENT_TRAIT "mood_component"
+// mobility flag traits
+// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
+// BUT FOR NOW, THESE ARE HOOKED TO DO update_mobility() VIA COMSIG IN living_mobility.dm
+// SO IF YOU ADD MORE, BESURE TO UPDATE IT THERE.
+
+/// Disallow movement
+#define TRAIT_MOBILITY_NOMOVE "mobility_nomove"
+/// Disallow pickup
+#define TRAIT_MOBILITY_NOPICKUP "mobility_nopickup"
+/// Disallow item use
+#define TRAIT_MOBILITY_NOUSE "mobility_nouse"
+///Disallow resting/unresting
+#define TRAIT_MOBILITY_NOREST "mobility_norest"
+
+#define TRAIT_FORCED_STANDING "forcedstanding"
+
+///Movement type traits for movables. See elements/movetype_handler.dm
+#define TRAIT_MOVE_GROUND "move_ground"
+#define TRAIT_MOVE_FLYING "move_flying"
+#define TRAIT_MOVE_VENTCRAWLING "move_ventcrawling"
+#define TRAIT_MOVE_FLOATING "move_floating"
+#define TRAIT_MOVE_PHASING "move_phasing"
+/// Disables the floating animation. See above.
+#define TRAIT_NO_FLOATING_ANIM "no-floating-animation"
+
/// Trait granted by [mob/living/silicon/ai]
/// Applied when the ai anchors itself
#define AI_ANCHOR_TRAIT "ai_anchor"
diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm
index 5d9522b18ee4..602473b6086d 100644
--- a/code/__DEFINES/vv.dm
+++ b/code/__DEFINES/vv.dm
@@ -92,6 +92,7 @@
#define VV_HK_AUTO_RENAME "auto_rename"
#define VV_HK_RADIATE "radiate"
#define VV_HK_EDIT_FILTERS "edit_filters"
+#define VV_HK_EDIT_PARTICLES "edit_particles"
// /obj
#define VV_HK_OSAY "osay"
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index f603e85292ad..435b83e29797 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -109,13 +109,25 @@
return "[output][and_text][input[index]]"
-//Checks for specific types in a list
-/proc/is_type_in_list(atom/A, list/L)
- if(!LAZYLEN(L) || !A)
+/**
+ * Checks for specific types in a list.
+ *
+ * If using zebra mode the list should be an assoc list with truthy/falsey values.
+ * The check short circuits so earlier entries in the input list will take priority.
+ * Ergo, subtypes should come before parent types.
+ * Notice that this is the opposite priority of [/proc/typecacheof].
+ *
+ * Arguments:
+ * - [type_to_check][/datum]: An instance to check.
+ * - [list_to_check][/list]: A list of typepaths to check the type_to_check against.
+ * - zebra: Whether to use the value of the matching type in the list instead of just returning true when a match is found.
+ */
+/proc/is_type_in_list(datum/type_to_check, list/list_to_check, zebra = FALSE)
+ if(!LAZYLEN(list_to_check) || !type_to_check)
return FALSE
- for(var/type in L)
- if(istype(A, type))
- return TRUE
+ for(var/type in list_to_check)
+ if(istype(type_to_check, type))
+ return !zebra || list_to_check[type] // Subtypes must come first in zebra lists.
return FALSE
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
diff --git a/code/__HELPERS/_planes.dm b/code/__HELPERS/_planes.dm
new file mode 100644
index 000000000000..d8306c356d4c
--- /dev/null
+++ b/code/__HELPERS/_planes.dm
@@ -0,0 +1,80 @@
+// This file contains helper macros for plane operations
+// See the planes section of Visuals.md for more detail, but essentially
+// When we render multiz, we do it by placing all atoms on lower levels on well, lower planes
+// This is done with stacks of plane masters (things we use to apply effects to planes)
+// These macros exist to facilitate working with this system, and other associated small bits
+
+/// Takes an atom to change the plane of, a new plane value, and something that can be used as a reference to a z level as input
+/// Modifies the new value to match the plane we actually want. Note, if you pass in an already offset plane the offsets will add up
+/// Use PLANE_TO_TRUE() to avoid this
+#define SET_PLANE(thing, new_value, z_reference) (thing.plane = MUTATE_PLANE(new_value, z_reference))
+
+/// Takes a plane and a z reference, and offsets the plane by the mutation
+/// The SSmapping.max_plane_offset bit here is technically redundant, but saves a bit of work in the base case
+/// And the base case is important to me. Non multiz shouldn't get hit too bad by this code
+#define MUTATE_PLANE(new_value, z_reference) ((SSmapping.max_plane_offset) ? GET_NEW_PLANE(new_value, GET_TURF_PLANE_OFFSET(z_reference)) : (new_value))
+
+/// Takes a z reference that we are unsure of, sanity checks it
+/// Returns either its offset, or 0 if it's not a valid ref
+#define GET_TURF_PLANE_OFFSET(z_reference) ((SSmapping.max_plane_offset && isatom(z_reference)) ? GET_Z_PLANE_OFFSET(z_reference.z) : 0)
+/// Essentially just an unsafe version of GET_TURF_PLANE_OFFSET()
+/// Takes a z value we returns its offset with a list lookup
+/// Will runtime during parts of init. Be careful :)
+#define GET_Z_PLANE_OFFSET(z) (SSmapping.z_level_to_plane_offset[z])
+
+/// Takes a plane to offset, and the multiplier to use, and well, does the offsetting
+/// Respects a blacklist we use to remove redundant plane masters, such as hud objects
+#define GET_NEW_PLANE(new_value, multiplier) (SSmapping.plane_offset_blacklist?["[new_value]"] ? new_value : (new_value) - (PLANE_RANGE * (multiplier)))
+
+// Now for the more niche things
+
+/// Takes an object, new plane, and multipler, and offsets the plane
+/// This is for cases where you have a multipler precalculated, and just want to use it
+/// Often an optimization, sometimes a necessity
+#define SET_PLANE_W_SCALAR(thing, new_value, multiplier) (thing.plane = GET_NEW_PLANE(new_value, multiplier))
+
+
+/// Implicit plane set. We take the turf from the object we're changing the plane of, and use ITS z as a spokesperson for our plane value
+#define SET_PLANE_IMPLICIT(thing, new_value) SET_PLANE_EXPLICIT(thing, new_value, thing)
+
+// This is an unrolled and optimized version of SET_PLANE, for use anywhere where you are unsure of a source's "turfness"
+// The plane is cached to allow for fancy stuff to be eval'd once, rather then often
+#define SET_PLANE_EXPLICIT(thing, new_value, source) \
+ do {\
+ if(SSmapping.max_plane_offset) {\
+ var/_cached_plane = new_value;\
+ var/turf/_our_turf = get_turf(source);\
+ if(_our_turf){\
+ thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\
+ }\
+ }\
+ else {\
+ thing.plane = new_value;\
+ }\
+ }\
+ while (FALSE)
+
+// Now for macros that exist to get info from SSmapping
+// Mostly about details of planes, or z levels
+
+/// Takes a z level, gets the lowest plane offset in its "stack"
+#define GET_LOWEST_STACK_OFFSET(z) ((SSmapping.max_plane_offset) ? SSmapping.z_level_to_lowest_plane_offset[z] : 0)
+/// Takes a plane, returns the canonical, unoffset plane it represents
+#define PLANE_TO_TRUE(plane) ((SSmapping.plane_offset_to_true) ? SSmapping.plane_offset_to_true["[plane]"] : plane)
+/// Takes a plane, returns the offset it uses
+#define PLANE_TO_OFFSET(plane) ((SSmapping.plane_to_offset) ? SSmapping.plane_to_offset["[plane]"] : plane)
+/// Takes a plane, returns TRUE if it is of critical priority, FALSE otherwise
+#define PLANE_IS_CRITICAL(plane) ((SSmapping.plane_to_offset) ? !!SSmapping.critical_planes["[plane]"] : FALSE)
+/// Takes a true plane, returns the offset planes that would canonically represent it
+#define TRUE_PLANE_TO_OFFSETS(plane) ((SSmapping.true_to_offset_planes) ? SSmapping.true_to_offset_planes["[plane]"] : list(plane))
+/// Takes a render target and an offset, returns a canonical render target string for it
+#define OFFSET_RENDER_TARGET(render_target, offset) (_OFFSET_RENDER_TARGET(render_target, SSmapping.render_offset_blacklist?["[render_target]"] ? 0 : offset))
+/// Helper macro for the above
+/// Honestly just exists to make the pattern of render target strings more readable
+#define _OFFSET_RENDER_TARGET(render_target, offset) ("[(render_target)] #[(offset)]")
+
+// Known issues:
+// Potentially too much client load? Hard to tell due to not having a potato pc to hand.
+// This is solvable with lowspec preferences, which would not be hard to implement
+// Player popups will now render their effects, like overlay lights. this is fixable, but I've not gotten to it
+// I think overlay lights can render on the wrong z layer. s fucked
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
new file mode 100644
index 000000000000..dbb42122ff45
--- /dev/null
+++ b/code/__HELPERS/atoms.dm
@@ -0,0 +1,9 @@
+///Returns the src and all recursive contents as a list.
+/atom/proc/get_all_contents(ignore_flag_1)
+ . = list(src)
+ var/i = 0
+ while(i < length(.))
+ var/atom/checked_atom = .[++i]
+ if(checked_atom.flags_1 & ignore_flag_1)
+ continue
+ . += checked_atom.contents
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index e7af7f31884d..34cacd872d42 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -596,3 +596,31 @@ block( \
continue
C.energy_fail(rand(duration_min,duration_max))
+
+///Returns a list of turfs around a center based on RANGE_TURFS()
+/proc/circle_range_turfs(center = usr, radius = 3)
+
+ var/turf/center_turf = get_turf(center)
+ var/list/turfs = new/list()
+ var/rsq = radius * (radius + 0.5)
+
+ for(var/turf/checked_turf as anything in RANGE_TURFS(radius, center_turf))
+ var/dx = checked_turf.x - center_turf.x
+ var/dy = checked_turf.y - center_turf.y
+ if(dx * dx + dy * dy <= rsq)
+ turfs += checked_turf
+ return turfs
+
+///Returns a list of turfs around a center based on view()
+/proc/circle_view_turfs(center=usr,radius=3) //Is there even a diffrence between this proc and circle_range_turfs()?
+
+ var/turf/center_turf = get_turf(center)
+ var/list/turfs = new/list()
+ var/rsq = radius * (radius + 0.5)
+
+ for(var/turf/checked_turf in view(radius, center_turf))
+ var/dx = checked_turf.x - center_turf.x
+ var/dy = checked_turf.y - center_turf.y
+ if(dx * dx + dy * dy <= rsq)
+ turfs += checked_turf
+ return turfs
diff --git a/code/__HELPERS/generators.dm b/code/__HELPERS/generators.dm
new file mode 100644
index 000000000000..d50df7deba19
--- /dev/null
+++ b/code/__HELPERS/generators.dm
@@ -0,0 +1,11 @@
+/**
+ * returns the arguments given to a generator and manually extracts them from the internal byond object
+ * returns:
+ * * flat list of strings for args given to the generator.
+ * * Note: this means things like "list(1,2,3)" will need to be processed
+ */
+/proc/return_generator_args(generator/target)
+ var/string_repr = "[target]" //the name of the generator is the string representation of it's _binobj, which also contains it's args
+ string_repr = copytext(string_repr, 11, length(string_repr)) // strips extraneous data
+ string_repr = replacetext(string_repr, "\"", "") // removes the " around the type
+ return splittext(string_repr, ", ")
diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm
new file mode 100644
index 000000000000..983ecc800274
--- /dev/null
+++ b/code/__HELPERS/maths.dm
@@ -0,0 +1,13 @@
+///Calculate the angle between two movables and the west|east coordinate
+/proc/get_angle(atom/movable/start, atom/movable/end)//For beams.
+ if(!start || !end)
+ return 0
+ var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y)
+ var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x)
+ if(!dy)
+ return (dx >= 0) ? 90 : 270
+ . = arctan(dx/dy)
+ if(dy < 0)
+ . += 180
+ else if(dx < 0)
+ . += 360
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 6a3028443dcc..ae5a1c1ce929 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -257,7 +257,19 @@ GLOBAL_LIST_EMPTY(species_list)
return ..()
/**
- * Timed action involving one mob user. A target can also be specified, but it is optional.
+ * Used to get the amount of change between two body temperatures
+ *
+ * When passed the difference between two temperatures returns the amount of change to temperature to apply.
+ * The change rate should be kept at a low value tween 0.16 and 0.02 for optimal results.
+ * vars:
+ * * temp_diff (required) The differance between two temperatures
+ * * change_rate (optional)(Default: 0.06) The rate of range multiplyer
+ */
+/proc/get_temp_change_amount(temp_diff, change_rate = 0.06)
+ if(temp_diff < 0)
+ return -(BODYTEMP_AUTORECOVERY_DIVISOR / 2) * log(1 - (temp_diff * change_rate))
+
+/* Timed action involving one mob user. A target can also be specified, but it is optional.
*
* Checks that `user` does not move, change hands, get stunned, etc. for the
* given `delay`. Returns `TRUE` on success or `FALSE` on failure.
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 5301f0ec9d3e..97740a79537e 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -77,10 +77,6 @@
var/pos = length(file_data["[escape_status]"]) + 1
file_data["[escape_status]"]["[pos]"] = mob_data
- var/datum/station_state/end_state = new /datum/station_state()
- end_state.count()
- var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
- file_data["additional data"]["station integrity"] = station_integrity
WRITE_FILE(json_file, json_encode(file_data))
SSblackbox.record_feedback("nested tally", "round_end_stats", num_survivors, list("survivors", "total"))
@@ -88,7 +84,6 @@
SSblackbox.record_feedback("nested tally", "round_end_stats", GLOB.joined_player_list.len - num_survivors, list("players", "dead"))
. = list()
.[POPCOUNT_SURVIVORS] = num_survivors
- .["station_integrity"] = station_integrity
/datum/controller/subsystem/ticker/proc/gather_antag_data()
var/team_gid = 1
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 185c6c595b80..36764c6bae9f 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -295,7 +295,7 @@ Turf and target are separate in case you want to teleport some distance from a t
return "[pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]"
//Returns a list of all items of interest with their name
-/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE, only_realname = FALSE)
+/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE)
var/list/mobs = sortmobs()
var/list/namecounts = list()
var/list/pois = list()
@@ -305,11 +305,7 @@ Turf and target are separate in case you want to teleport some distance from a t
continue
if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins
continue
- var/name = ""
- if(only_realname)
- name = avoid_assoc_duplicate_keys(M.real_name, namecounts)
- else
- name = avoid_assoc_duplicate_keys(M.name, namecounts) + M.get_realname_string()
+ var/name = avoid_assoc_duplicate_keys(M.name, namecounts) + M.get_realname_string()
if(M.stat == DEAD && specify_dead_role)
if(isobserver(M))
@@ -325,7 +321,6 @@ Turf and target are separate in case you want to teleport some distance from a t
pois[avoid_assoc_duplicate_keys(A.name, namecounts)] = A
return pois
-
//Orders mobs by type then by name
/proc/sortmobs()
var/list/moblist = list()
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index bbfb0d3a74c5..d9957c7db0f1 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -50,7 +50,6 @@ DEFINE_BITFIELD(appearance_flags, list(
DEFINE_BITFIELD(area_flags, list(
"VALID_TERRITORY" = VALID_TERRITORY,
- "BLOBS_ALLOWED" = BLOBS_ALLOWED,
"CAVES_ALLOWED" = CAVES_ALLOWED,
"FLORA_ALLOWED" = FLORA_ALLOWED,
"MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED,
@@ -213,7 +212,6 @@ DEFINE_BITFIELD(obj_flags, list(
DEFINE_BITFIELD(pass_flags, list(
"LETPASSTHROW" = LETPASSTHROW,
- "PASSBLOB" = PASSBLOB,
"PASSCLOSEDTURF" = PASSCLOSEDTURF,
"PASSGLASS" = PASSGLASS,
"PASSGRILLE" = PASSGRILLE,
diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm
index 56037ab30389..30280560c33c 100644
--- a/code/_globalvars/game_modes.dm
+++ b/code/_globalvars/game_modes.dm
@@ -5,7 +5,6 @@ GLOBAL_VAR(survivor_report) //Contains shared survivor report for roundend repor
GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name
-GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report
//TODO clear this one up too
GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index ff4237d1e892..332ac86582a0 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -30,7 +30,6 @@ GLOBAL_LIST_EMPTY(tdome2)
GLOBAL_LIST_EMPTY(tdomeobserve)
GLOBAL_LIST_EMPTY(tdomeadmin)
GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped
-GLOBAL_LIST_EMPTY(blobstart) //blobs, santa, respawning devils
GLOBAL_LIST_EMPTY(secequipment) //sec equipment lockers that scale with the number of sec players
GLOBAL_LIST_EMPTY(deathsquadspawn)
GLOBAL_LIST_EMPTY(emergencyresponseteamspawn)
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index fb2bac175034..6cd9aa8a0849 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -56,10 +56,16 @@
if(id && usr.client) //try to (un)remember position
usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = locked ? moved : null
return TRUE
+ var/trigger_flags
+ if(LAZYACCESS(modifiers, ALT_CLICK))
+ if(locked)
+ to_chat(usr, "Action button \"[name]\" is locked, unlock it first.")
+ return TRUE
+ trigger_flags |= TRIGGER_SECONDARY_ACTION
if(usr.next_click > world.time)
return
usr.next_click = world.time + 1
- linked_action.Trigger()
+ linked_action.Trigger(trigger_flags)
return TRUE
//Hide/Show Action Buttons ... Button
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 2b43444c31ae..4329dd333e3d 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -420,14 +420,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
icon_state = "alien_noqueen"
alerttooltipstyle = "alien"
-//BLOBS
-
-/atom/movable/screen/alert/nofactory
- name = "No Factory"
- desc = "You have no factory, and are slowly dying!"
- icon_state = "blobbernaut_nofactory"
- alerttooltipstyle = "blob"
-
// BLOODCULT
/atom/movable/screen/alert/bloodsense
@@ -587,6 +579,29 @@ Recharging stations are available in robotics, the dormitory bathrooms, and the
desc = "Your blood's electric charge is becoming dangerously high, find an outlet for your energy. Use Grab Intent on an APC to channel your energy into it."
icon_state = "ethereal_overcharge"
+//MODsuit unique
+/atom/movable/screen/alert/nocore
+ name = "Missing Core"
+ desc = "Unit has no core. No modules available until a core is reinstalled. Robotics may provide assistance."
+ icon_state = "no_cell"
+
+/atom/movable/screen/alert/emptycell/plasma
+ name = "Out of Power"
+ desc = "Unit's plasma core has no charge remaining. No modules available until plasma core is recharged. \
+ Unit can be refilled through plasma fuel."
+
+/atom/movable/screen/alert/emptycell/plasma/update_desc()
+ . = ..()
+ desc = initial(desc)
+
+/atom/movable/screen/alert/lowcell/plasma
+ name = "Low Charge"
+ desc = "Unit's plasma core is running low. Unit can be refilled through plasma fuel."
+
+/atom/movable/screen/alert/lowcell/plasma/update_desc()
+ . = ..()
+ desc = initial(desc)
+
//Need to cover all use cases - emag, illegal upgrade module, malf AI hack, traitor cyborg
/atom/movable/screen/alert/hacked
name = "Hacked"
diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm
deleted file mode 100644
index cd7597e33f0d..000000000000
--- a/code/_onclick/hud/blob_overmind.dm
+++ /dev/null
@@ -1,187 +0,0 @@
-
-/atom/movable/screen/blob
- icon = 'icons/mob/blob.dmi'
-
-/atom/movable/screen/blob/MouseEntered(location,control,params)
- . = ..()
- openToolTip(usr,src,params,title = name,content = desc, theme = "blob")
-
-/atom/movable/screen/blob/MouseExited()
- closeToolTip(usr)
-
-/atom/movable/screen/blob/BlobHelp
- icon_state = "ui_help"
- name = "Blob Help"
- desc = "Help on playing blob!"
-
-/atom/movable/screen/blob/BlobHelp/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.blob_help()
-
-/atom/movable/screen/blob/JumpToNode
- icon_state = "ui_tonode"
- name = "Jump to Node"
- desc = "Moves your camera to a selected blob node."
-
-/atom/movable/screen/blob/JumpToNode/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.jump_to_node()
-
-/atom/movable/screen/blob/JumpToCore
- icon_state = "ui_tocore"
- name = "Jump to Core"
- desc = "Moves your camera to your blob core."
-
-/atom/movable/screen/blob/JumpToCore/MouseEntered(location,control,params)
- if(hud && hud.mymob && isovermind(hud.mymob))
- var/mob/camera/blob/B = hud.mymob
- if(!B.placed)
- name = "Place Blob Core"
- desc = "Attempt to place your blob core at this location."
- else
- name = initial(name)
- desc = initial(desc)
- return ..()
-
-/atom/movable/screen/blob/JumpToCore/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- if(!B.placed)
- B.place_blob_core(0)
- B.transport_core()
-
-/atom/movable/screen/blob/Blobbernaut
- icon_state = "ui_blobbernaut"
- name = "Produce Blobbernaut (40)"
- desc = "Produces a strong, smart blobbernaut from a factory blob for 40 resources.
The factory blob used will become fragile and unable to produce spores."
-
-/atom/movable/screen/blob/Blobbernaut/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.create_blobbernaut()
-
-/atom/movable/screen/blob/ResourceBlob
- icon_state = "ui_resource"
- name = "Produce Resource Blob (40)"
- desc = "Produces a resource blob for 40 resources.
Resource blobs will give you resources every few seconds."
-
-/atom/movable/screen/blob/ResourceBlob/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.create_resource()
-
-/atom/movable/screen/blob/NodeBlob
- icon_state = "ui_node"
- name = "Produce Node Blob (50)"
- desc = "Produces a node blob for 50 resources.
Node blobs will expand and activate nearby resource and factory blobs."
-
-/atom/movable/screen/blob/NodeBlob/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.create_node()
-
-/atom/movable/screen/blob/FactoryBlob
- icon_state = "ui_factory"
- name = "Produce Factory Blob (60)"
- desc = "Produces a factory blob for 60 resources.
Factory blobs will produce spores every few seconds."
-
-/atom/movable/screen/blob/FactoryBlob/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.create_factory()
-
-/atom/movable/screen/blob/ReadaptStrain
- icon_state = "ui_chemswap"
- name = "Readapt Strain (40)"
- desc = "Allows you to choose a new strain from 4 random choices for 40 resources."
-
-/atom/movable/screen/blob/ReadaptStrain/MouseEntered(location,control,params)
- if(hud && hud.mymob && isovermind(hud.mymob))
- var/mob/camera/blob/B = hud.mymob
- if(B.free_strain_rerolls)
- name = "Readapt Strain (FREE)"
- desc = "Randomly rerolls your strain for free."
- else
- name = initial(name)
- desc = initial(desc)
- ..()
-
-/atom/movable/screen/blob/ReadaptStrain/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.strain_reroll()
-
-/atom/movable/screen/blob/RelocateCore
- icon_state = "ui_swap"
- name = "Relocate Core (80)"
- desc = "Swaps a node and your core for 80 resources."
-
-/atom/movable/screen/blob/RelocateCore/Click()
- if(isovermind(usr))
- var/mob/camera/blob/B = usr
- B.relocate_core()
-
-/datum/hud/blob_overmind/New(mob/owner)
- ..()
- var/atom/movable/screen/using
-
- blobpwrdisplay = new /atom/movable/screen()
- blobpwrdisplay.name = "blob power"
- blobpwrdisplay.icon_state = "block"
- blobpwrdisplay.screen_loc = ui_health
- blobpwrdisplay.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- blobpwrdisplay.layer = ABOVE_HUD_LAYER
- blobpwrdisplay.plane = ABOVE_HUD_PLANE
- blobpwrdisplay.hud = src
- infodisplay += blobpwrdisplay
-
- healths = new /atom/movable/screen/healths/blob()
- healths.hud = src
- infodisplay += healths
-
- using = new /atom/movable/screen/blob/BlobHelp()
- using.screen_loc = "WEST:6,NORTH:-3"
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/JumpToNode()
- using.screen_loc = ui_inventory
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/JumpToCore()
- using.screen_loc = ui_zonesel
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/Blobbernaut()
- using.screen_loc = ui_belt
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/ResourceBlob()
- using.screen_loc = ui_back
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/NodeBlob()
- using.screen_loc = ui_hand_position(2)
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/FactoryBlob()
- using.screen_loc = ui_hand_position(1)
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/ReadaptStrain()
- using.screen_loc = ui_storage1
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/blob/RelocateCore()
- using.screen_loc = ui_storage2
- using.hud = src
- static_inventory += using
diff --git a/code/_onclick/hud/blobbernauthud.dm b/code/_onclick/hud/blobbernauthud.dm
deleted file mode 100644
index 430e8e81e7f8..000000000000
--- a/code/_onclick/hud/blobbernauthud.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-
-/datum/hud/blobbernaut/New(mob/owner)
- ..()
-
- blobpwrdisplay = new /atom/movable/screen/healths/blob/naut/core()
- blobpwrdisplay.hud = src
- infodisplay += blobpwrdisplay
-
- healths = new /atom/movable/screen/healths/blob/naut()
- healths.hud = src
- infodisplay += healths
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 27b220d7fdb7..1e59295205eb 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -29,8 +29,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/ling/chems/lingchemdisplay
var/atom/movable/screen/ling/sting/lingstingdisplay
- var/atom/movable/screen/blobpwrdisplay
-
var/atom/movable/screen/alien_plasma_display
var/atom/movable/screen/alien_queen_finder
@@ -113,7 +111,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
lingchemdisplay = null
devilsouldisplay = null
lingstingdisplay = null
- blobpwrdisplay = null
alien_plasma_display = null
alien_queen_finder = null
combo_display = null
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index ef1f614809fe..bf171d339912 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -597,22 +597,6 @@
icon = 'icons/hud/screen_cyborg.dmi'
screen_loc = ui_borg_health
-/atom/movable/screen/healths/blob
- name = "blob health"
- icon_state = "block"
- screen_loc = ui_internal
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
-/atom/movable/screen/healths/blob/naut
- name = "health"
- icon = 'icons/mob/blob.dmi'
- icon_state = "nauthealth"
-
-/atom/movable/screen/healths/blob/naut/core
- name = "overmind health"
- icon_state = "corehealth"
- screen_loc = ui_health
-
/atom/movable/screen/healths/guardian
name = "summoner health"
icon = 'icons/mob/guardian.dmi'
diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm
deleted file mode 100644
index d6b8994f82f0..000000000000
--- a/code/_onclick/overmind.dm
+++ /dev/null
@@ -1,36 +0,0 @@
-// Blob Overmind Controls
-
-
-/mob/camera/blob/ClickOn(atom/A, params) //Expand blob
- var/list/modifiers = params2list(params)
- if(LAZYACCESS(modifiers, MIDDLE_CLICK))
- MiddleClickOn(A, params)
- return
- if(LAZYACCESS(modifiers, SHIFT_CLICK))
- ShiftClickOn(A)
- return
- if(LAZYACCESS(modifiers, ALT_CLICK))
- AltClickOn(A)
- return
- if(LAZYACCESS(modifiers, CTRL_CLICK))
- CtrlClickOn(A)
- return
- var/turf/T = get_turf(A)
- if(T)
- expand_blob(T)
-
-/mob/camera/blob/MiddleClickOn(atom/A) //Rally spores
- . = ..()
- var/turf/T = get_turf(A)
- if(T)
- rally_spores(T)
-
-/mob/camera/blob/CtrlClickOn(atom/A) //Create a shield
- var/turf/T = get_turf(A)
- if(T)
- create_shield(T)
-
-/mob/camera/blob/AltClickOn(atom/A) //Remove a blob
- var/turf/T = get_turf(A)
- if(T)
- remove_blob(T)
diff --git a/code/controllers/subsystem/blackmarket.dm b/code/controllers/subsystem/blackmarket.dm
index cbd07fcd8fda..99a6932570b8 100644
--- a/code/controllers/subsystem/blackmarket.dm
+++ b/code/controllers/subsystem/blackmarket.dm
@@ -6,7 +6,8 @@ SUBSYSTEM_DEF(blackmarket)
/// Descriptions for each shipping methods.
var/shipping_method_descriptions = list(
SHIPPING_METHOD_LAUNCH="Launches the item at your coordinates from across deep space. Cheap, but you might not recieve your item at all. We recommend being stationary in space, away from any large structures, for best results.",
- SHIPPING_METHOD_LTSRBT="Long-To-Short-Range-Bluespace-Transceiver, a machine that prepares items at a remote storage location and then teleports them to the location of the LTRSBT."
+ SHIPPING_METHOD_DEAD_DROP="Our couriers will fire your item via orbital drop pod at the nearest safe abandoned structure for discreet pick up. Reliable, but you'll have to find your package yourself. We accept no responsibility for lost packages if you try to do this in empty space or the outpost.",
+ SHIPPING_METHOD_LTSRBT="Long-To-Short-Range-Bluespace-Transceiver, a machine that prepares items at a remote storage location and then teleports them to the location of the LTRSBT. Secure, quick and reliable, though it ain't cheap to do."
)
/// List of all existing markets.
@@ -32,6 +33,9 @@ SUBSYSTEM_DEF(blackmarket)
markets[M].add_item(item, FALSE)
qdel(I)
+ for(var/market in markets)
+ var/datum/blackmarket_market/market_to_cycle = markets[market]
+ market_to_cycle.cycle_stock()
. = ..()
/datum/controller/subsystem/blackmarket/fire(resumed)
@@ -62,16 +66,72 @@ SUBSYSTEM_DEF(blackmarket)
var/startSide = pick(GLOB.cardinals)
var/turf/T = get_turf(purchase.uplink)
var/datum/virtual_level/vlevel = T.get_virtual_level()
- var/pickedloc = vlevel.get_side_turf(startSide)
+ var/turf/pickedloc
+
+ switch(startSide)
+ if(NORTH)
+ pickedloc = locate(T.x, (vlevel.high_y - vlevel.reserved_margin),T.z)
+ if(EAST)
+ pickedloc = locate((vlevel.high_x - vlevel.reserved_margin), T.y ,T.z)
+ if(SOUTH)
+ pickedloc = locate(T.x, (vlevel.low_y + vlevel.reserved_margin),T.z)
+ if(WEST)
+ pickedloc = locate((vlevel.low_x + vlevel.reserved_margin), T.y ,T.z)
+ else
+ pickedloc = vlevel.get_side_turf(startSide)
var/atom/movable/item = purchase.entry.spawn_item(pickedloc)
- item.safe_throw_at(purchase.uplink, 3, 3, spin = FALSE)
-
+ item.Move(get_step(pickedloc,get_dir(pickedloc,T)))
to_chat(recursive_loc_check(purchase.uplink.loc, /mob), "[purchase.uplink] flashes a message noting the order is being launched at your coordinates from [dir2text(startSide)].")
queued_purchases -= purchase
qdel(purchase)
+ // Drop the order somewhere with the bounds of overmap encounter's ruin
+ if(SHIPPING_METHOD_DEAD_DROP)
+ var/datum/overmap/dynamic/overmap_loc = SSovermap.get_overmap_object_by_location(purchase.uplink, TRUE)
+ var/datum/virtual_level/zlevel = purchase.uplink.get_virtual_level()
+ var/turf/landing_turf
+ var/datum/map_template/ruin
+ if(!isnull(overmap_loc))
+ for(var/possible_ruin in overmap_loc.ruin_turfs)
+ var/turf/lowerbound = overmap_loc.ruin_turfs[possible_ruin]
+ ruin = overmap_loc.spawned_ruins[possible_ruin]
+ var/list/possible_ruin_turfs = zlevel.get_block_portion(lowerbound.x,lowerbound.y,(lowerbound.x + ruin.width),(lowerbound.y + ruin.height))
+ for(var/cycle in 1 to length(possible_ruin_turfs))
+ var/potential_turf = pick_n_take(possible_ruin_turfs)
+ if(!isopenturf(potential_turf))
+ continue
+ var/turf/open/potential_open_turf = potential_turf
+ if(ischasm(potential_open_turf))
+ continue
+ if(islava(potential_open_turf))
+ var/turf/open/lava/potential_lava_floor = potential_open_turf
+ if(!potential_lava_floor.is_safe())
+ continue
+ if(istype(potential_open_turf, /turf/open/water/acid))
+ var/turf/open/water/acid/potential_acid_floor = potential_open_turf
+ if(!potential_acid_floor.is_safe_to_cross())
+ continue
+ if(potential_open_turf.is_blocked_turf())
+ continue
+
+ //yippee, there's a viable turf for the package to land on
+ landing_turf = potential_open_turf
+ to_chat(recursive_loc_check(purchase.uplink.loc, /mob),"[purchase.uplink] flashes a message noting the order is being launched at a structure in your local area.")
+ break
+
+ if(!landing_turf)
+ landing_turf = zlevel.get_random_position_in_margin()
+ to_chat(recursive_loc_check(purchase.uplink.loc, /mob), "[purchase.uplink] flashes a message that the pod was unable to reach it's designated landing spot, and has landed somewhere in the local area instead.")
+
+ var/obj/structure/closet/supplypod/pod = new()
+ pod.setStyle(STYLE_BOX)
+ purchase.entry.spawn_item(pod)
+ pod.explosionSize = list(0,0,0,1)
+ new /obj/effect/pod_landingzone(landing_turf, pod)
+ queued_purchases -= purchase
+ qdel(purchase)
if(MC_TICK_CHECK)
break
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 03720e4d641f..a8f905e5fe25 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -42,6 +42,28 @@ SUBSYSTEM_DEF(mapping)
/// Translation of virtual level ID to a virtual level reference
var/list/virtual_z_translation = list()
+ /// List of z level (as number) -> plane offset of that z level
+ /// Used to maintain the plane cube
+ var/list/z_level_to_plane_offset = list()
+ /// List of z level (as number) -> The lowest plane offset in that z stack
+ var/list/z_level_to_lowest_plane_offset = list()
+ // This pair allows for easy conversion between an offset plane, and its true representation
+ // Both are in the form "input plane" -> output plane(s)
+ /// Assoc list of string plane values to their true, non offset representation
+ var/list/plane_offset_to_true
+ /// Assoc list of true string plane values to a list of all potential offset planess
+ var/list/true_to_offset_planes
+ /// Assoc list of string plane to the plane's offset value
+ var/list/plane_to_offset
+ /// List of planes that do not allow for offsetting
+ var/list/plane_offset_blacklist
+ /// List of render targets that do not allow for offsetting
+ var/list/render_offset_blacklist
+ /// List of plane masters that are of critical priority
+ var/list/critical_planes
+ /// The largest plane offset we've generated so far
+ var/max_plane_offset = 0
+
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(initialized)
return
diff --git a/code/controllers/subsystem/overmap.dm b/code/controllers/subsystem/overmap.dm
index 1304eeeb34ea..113bfefa7a52 100644
--- a/code/controllers/subsystem/overmap.dm
+++ b/code/controllers/subsystem/overmap.dm
@@ -291,6 +291,7 @@ SUBSYSTEM_DEF(overmap)
mapgen.generate_turfs(vlevel.get_unreserved_block())
var/list/ruin_turfs = list()
+ var/list/ruin_templates = list()
if(used_ruin)
var/turf/ruin_turf = locate(
rand(
@@ -302,6 +303,7 @@ SUBSYSTEM_DEF(overmap)
)
used_ruin.load(ruin_turf)
ruin_turfs[used_ruin.name] = ruin_turf
+ ruin_templates[used_ruin.name] = used_ruin
// fill in the turfs, AFTER generating the ruin. this prevents them from generating within the ruin
// and ALSO prevents the ruin from being spaced when it spawns in
@@ -376,7 +378,7 @@ SUBSYSTEM_DEF(overmap)
quaternary_dock.dwidth = 0
docking_ports += quaternary_dock
- return list(mapzone, docking_ports, ruin_turfs)
+ return list(mapzone, docking_ports, ruin_turfs, ruin_templates)
/**
* Returns a random, usually empty turf in the overmap
@@ -418,10 +420,10 @@ SUBSYSTEM_DEF(overmap)
* Gets the parent overmap object (e.g. the planet the atom is on) for a given atom.
* * source - The object you want to get the corresponding parent overmap object for.
*/
-/datum/controller/subsystem/overmap/proc/get_overmap_object_by_location(atom/source)
+/datum/controller/subsystem/overmap/proc/get_overmap_object_by_location(atom/source, exclude_ship = FALSE)
var/turf/T = get_turf(source)
var/area/ship/A = get_area(source)
- while(istype(A) && A.mobile_port)
+ while(istype(A) && A.mobile_port && !exclude_ship)
if(A.mobile_port.current_ship)
return A.mobile_port.current_ship
A = A.mobile_port.underlying_turf_area[T]
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 6ae8b0e62409..a635a3d6389e 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -300,8 +300,6 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
mode.post_setup()
- GLOB.start_state = new /datum/station_state()
- GLOB.start_state.count()
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
@@ -444,12 +442,6 @@ SUBSYSTEM_DEF(ticker)
news_message = "[station_name()] has been evacuated after transmitting the following distress beacon:\n\n[emergency_reason]"
else
news_message = "The crew of [station_name()] has been evacuated amid unconfirmed reports of enemy activity."
- if(BLOB_WIN)
- news_message = "[station_name()] was overcome by an unknown biological outbreak, killing all crew on board. Don't let it happen to you! Remember, a clean work station is a safe work station."
- if(BLOB_NUKE)
- news_message = "[station_name()] is currently undergoing decontanimation after a controlled burst of radiation was used to remove a biological ooze. All employees were safely evacuated prior, and are enjoying a relaxing vacation."
- if(BLOB_DESTROYED)
- news_message = "[station_name()] is currently undergoing decontamination procedures after the destruction of a biological hazard. As a reminder, any crew members experiencing cramps or bloating should report immediately to security for incineration."
if(CULT_ESCAPE)
news_message = "Security Alert: A group of religious fanatics have escaped from [station_name()]."
if(CULT_FAILURE)
diff --git a/code/datums/action.dm b/code/datums/action.dm
index cdca8729984f..e8d0ea303888 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -1,8 +1,3 @@
-#define AB_CHECK_HANDS_BLOCKED (1<<0)
-#define AB_CHECK_IMMOBILE (1<<1)
-#define AB_CHECK_LYING (1<<2)
-#define AB_CHECK_CONSCIOUS (1<<3)
-
/datum/action
var/name = "Generic Action"
var/desc = null
@@ -91,7 +86,7 @@
button.locked = FALSE
button.id = null
-/datum/action/proc/Trigger()
+/datum/action/proc/Trigger(trigger_flags)
if(!IsAvailable())
return FALSE
if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER)
diff --git a/code/datums/cinematic.dm b/code/datums/cinematic.dm
index c36fb3961664..7711092b9778 100644
--- a/code/datums/cinematic.dm
+++ b/code/datums/cinematic.dm
@@ -150,7 +150,6 @@
flick("station_intact_fade_red",screen)
screen.icon_state = "summary_nukefail"
-//Also used for blob
/datum/cinematic/nuke_selfdestruct
id = CINEMATIC_SELFDESTRUCT
diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm
new file mode 100644
index 000000000000..3451a75538ad
--- /dev/null
+++ b/code/datums/components/jetpack.dm
@@ -0,0 +1,149 @@
+// Welcome to the jetpack component
+// Apply this to something when you want it to be "like a jetpack"
+// So propulsion through space on move, that sort of thing
+/datum/component/jetpack
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ var/datum/callback/check_on_move
+ var/datum/callback/get_mover
+ /// If we should stabilize ourselves when not drifting
+ var/stabilize = FALSE
+ /// The signal we listen for as an activation
+ var/activation_signal
+ /// The signal we listen for as a de-activation
+ var/deactivation_signal
+ /// The return flag our parent expects for a failed activation
+ var/return_flag
+ var/datum/effect_system/trail_follow/trail
+ /// The typepath to instansiate our trail as, when we need it
+ var/effect_type
+
+/**
+ * Arguments:
+ * * stabilize - If we should drift when we finish moving, or sit stable in space]
+ * * activation_signal - Signal we activate on
+ * * deactivation_signal - Signal we deactivate on
+ * * return_flag - Flag to return if activation fails
+ * * get_mover - Callback we use to get the "moving" thing, for trail purposes, alongside signal registration
+ * * check_on_move - Callback we call each time we attempt a move, we expect it to retun true if the move is ok, false otherwise. It expects an arg, TRUE if fuel should be consumed, FALSE othewise
+ * * effect_type - Type of trail_follow to spawn
+ */
+/datum/component/jetpack/Initialize(stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/get_mover, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
+ . = ..()
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+ if(!activation_signal) // Can't activate? go away
+ return COMPONENT_INCOMPATIBLE
+
+ RegisterSignal(parent, activation_signal, PROC_REF(activate))
+ if(deactivation_signal)
+ RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate))
+
+ src.check_on_move = check_on_move
+ src.get_mover = get_mover
+ src.stabilize = stabilize
+ src.return_flag = return_flag
+ src.activation_signal = activation_signal
+ src.deactivation_signal = deactivation_signal
+ src.effect_type = effect_type
+
+/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/get_mover, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
+ UnregisterSignal(parent, src.activation_signal)
+ if(src.deactivation_signal)
+ UnregisterSignal(parent, src.deactivation_signal)
+ RegisterSignal(parent, activation_signal, PROC_REF(activate))
+ if(deactivation_signal)
+ RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate))
+
+ src.check_on_move = check_on_move
+ src.get_mover = get_mover
+ src.stabilize = stabilize
+ src.activation_signal = activation_signal
+ src.deactivation_signal = deactivation_signal
+ src.effect_type = effect_type
+
+ if(trail && effect_type != trail.type)
+ QDEL_NULL(trail)
+ setup_trail()
+
+/datum/component/jetpack/Destroy()
+ QDEL_NULL(trail)
+ QDEL_NULL(check_on_move)
+ return ..()
+
+/datum/component/jetpack/proc/setup_trail()
+ var/mob/moving = get_mover.Invoke()
+ if(!moving || trail)
+ return
+ trail = new effect_type
+ trail.auto_process = FALSE
+ trail.set_up(moving)
+
+/datum/component/jetpack/proc/activate(datum/source)
+ SIGNAL_HANDLER
+ var/mob/moving = get_mover.Invoke()
+ if(!thrust(moving))
+ return return_flag
+ trail.start()
+ RegisterSignal(moving, COMSIG_MOVABLE_MOVED, PROC_REF(move_react))
+ RegisterSignal(moving, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react))
+ RegisterSignal(moving, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(spacemove_react))
+ RegisterSignal(moving, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT, PROC_REF(block_starting_visuals))
+ RegisterSignal(moving, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(ignore_ending_block))
+
+/datum/component/jetpack/proc/deactivate(datum/source)
+ SIGNAL_HANDLER
+ QDEL_NULL(trail)
+ var/mob/moving = get_mover.Invoke()
+ if(moving)
+ UnregisterSignal(moving, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(moving, COMSIG_MOVABLE_PRE_MOVE)
+ UnregisterSignal(moving, COMSIG_MOVABLE_SPACEMOVE)
+ UnregisterSignal(moving, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT)
+ UnregisterSignal(moving, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT)
+
+/datum/component/jetpack/proc/move_react(mob/user)
+ SIGNAL_HANDLER
+ if(!user || !user.client)//Don't allow jet self using
+ return
+ if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet
+ return
+ if(!(user.movement_type & FLOATING) || user.buckled)//You don't want use jet in gravity or while buckled.
+ return
+ if(user.pulledby)//You don't must use jet if someone pull you
+ return
+ if(user.throwing)//You don't must use jet if you thrown
+ return
+ if(length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes.
+ thrust()
+
+/datum/component/jetpack/proc/pre_move_react(mob/user)
+ SIGNAL_HANDLER
+ trail.oldposition = get_turf(user)
+
+/datum/component/jetpack/proc/spacemove_react(mob/user, movement_dir, continuous_move)
+ SIGNAL_HANDLER
+ if(!continuous_move && movement_dir)
+ return COMSIG_MOVABLE_STOP_SPACEMOVE
+ // Check if we have the fuel to stop this. Do NOT cosume any fuel, just check
+ // This is done because things other then us can use our fuel
+ if(stabilize && check_on_move.Invoke(FALSE))
+ return COMSIG_MOVABLE_STOP_SPACEMOVE
+
+/// Returns true if the thrust went well, false otherwise
+/datum/component/jetpack/proc/thrust()
+ if(!check_on_move.Invoke(TRUE))
+ return FALSE
+ if(!trail)
+ setup_trail()
+ trail.generate_effect()
+ return TRUE
+
+/// Basically, tell the drift component not to do its starting visuals, because they look dumb for us
+/datum/component/jetpack/proc/block_starting_visuals(datum/source)
+ SIGNAL_HANDLER
+ return DRIFT_VISUAL_FAILED
+
+/// If we're on, don't let the drift component block movements at the end since we can speed
+/datum/component/jetpack/proc/ignore_ending_block(datum/source)
+ SIGNAL_HANDLER
+ return DRIFT_ALLOW_INPUT
diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm
new file mode 100644
index 000000000000..81cb0c2b4d40
--- /dev/null
+++ b/code/datums/components/shielded.dm
@@ -0,0 +1,186 @@
+/**
+ * The shielded component causes the parent item to nullify a certain number of attacks against the wearer, see: shielded vests.
+ */
+
+/datum/component/shielded
+ /// The person currently wearing us
+ var/mob/living/wearer
+ /// How many charges we can have max, and how many we start with
+ var/max_charges
+ /// How many charges we currently have
+ var/current_charges
+ /// How long we have to avoid being hit to replenish charges. If set to 0, we never recharge lost charges
+ var/recharge_start_delay = 20 SECONDS
+ /// Once we go unhit long enough to recharge, we replenish charges this often. The floor is effectively 1 second, AKA how often SSdcs processes
+ var/charge_increment_delay = 1 SECONDS
+ /// How many charges we recover on each charge increment
+ var/charge_recovery = 1
+ /// What .dmi we're pulling the shield icon from
+ var/shield_icon_file = 'icons/effects/effects.dmi'
+ /// What icon is used when someone has a functional shield up
+ var/shield_icon = "shield-old"
+ /// Do we still shield if we're being held in-hand? If FALSE, it needs to be equipped to a slot to work
+ var/shield_inhand = FALSE
+ /// Should the shield lose charges equal to the damage dealt by a hit?
+ var/lose_multiple_charges = FALSE
+ /// The cooldown tracking when we were last hit
+ COOLDOWN_DECLARE(recently_hit_cd)
+ /// The cooldown tracking when we last replenished a charge
+ COOLDOWN_DECLARE(charge_add_cd)
+ /// A callback for the sparks/message that play when a charge is used, see [/datum/component/shielded/proc/default_run_hit_callback]
+ var/datum/callback/on_hit_effects
+
+/datum/component/shielded/Initialize(max_charges = 3, recharge_start_delay = 20 SECONDS, charge_increment_delay = 1 SECONDS, charge_recovery = 1, lose_multiple_charges = FALSE, starting_charges = null, shield_icon_file = 'icons/effects/effects.dmi', shield_icon = "shield-old", shield_inhand = FALSE, run_hit_callback)
+ if(!isitem(parent) || max_charges <= 0)
+ return COMPONENT_INCOMPATIBLE
+
+ src.max_charges = max_charges
+ src.recharge_start_delay = recharge_start_delay
+ src.charge_increment_delay = charge_increment_delay
+ src.charge_recovery = charge_recovery
+ src.lose_multiple_charges = lose_multiple_charges
+ src.shield_icon_file = shield_icon_file
+ src.shield_icon = shield_icon
+ src.shield_inhand = shield_inhand
+ src.on_hit_effects = run_hit_callback || CALLBACK(src, PROC_REF(default_run_hit_callback))
+ if(isnull(starting_charges))
+ current_charges = max_charges
+ else
+ current_charges = starting_charges
+ if(recharge_start_delay)
+ START_PROCESSING(SSdcs, src)
+
+/datum/component/shielded/Destroy(force, silent)
+ if(wearer)
+ shield_icon = "broken"
+ UnregisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS)
+ wearer.update_appearance(UPDATE_ICON)
+ wearer = null
+ QDEL_NULL(on_hit_effects)
+ return ..()
+
+/datum/component/shielded/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped))
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(lost_wearer))
+ RegisterSignal(parent, COMSIG_ITEM_HIT_REACT, PROC_REF(on_hit_react))
+ RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(check_recharge_rune))
+ var/atom/shield = parent
+ if(ismob(shield.loc))
+ var/mob/holder = shield.loc
+ if(holder.is_holding(parent) && !shield_inhand)
+ return
+ set_wearer(holder)
+
+/datum/component/shielded/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_HIT_REACT, COMSIG_PARENT_ATTACKBY))
+ var/atom/shield = parent
+ if(shield.loc == wearer)
+ lost_wearer(src, wearer)
+
+// Handle recharging, if we want to
+/datum/component/shielded/process(delta_time)
+ if(current_charges >= max_charges)
+ STOP_PROCESSING(SSdcs, src)
+ return
+
+ if(!COOLDOWN_FINISHED(src, recently_hit_cd))
+ return
+ if(!COOLDOWN_FINISHED(src, charge_add_cd))
+ return
+
+ var/obj/item/item_parent = parent
+ COOLDOWN_START(src, charge_add_cd, charge_increment_delay)
+ adjust_charge(charge_recovery) // set the number of charges to current + recovery per increment, clamped from zero to max_charges
+ playsound(item_parent, 'sound/magic/charge.ogg', 50, TRUE)
+ if(current_charges == max_charges)
+ playsound(item_parent, 'sound/machines/ding.ogg', 50, TRUE)
+
+/datum/component/shielded/proc/adjust_charge(change)
+ current_charges = clamp(current_charges + change, 0, max_charges)
+ if(wearer)
+ wearer.update_appearance(UPDATE_ICON)
+
+/// Check if we've been equipped to a valid slot to shield
+/datum/component/shielded/proc/on_equipped(datum/source, mob/user, slot)
+ SIGNAL_HANDLER
+
+ if(slot == ITEM_SLOT_HANDS && !shield_inhand)
+ lost_wearer(source, user)
+ return
+ set_wearer(source, user)
+
+/// Either we've been dropped or our wearer has been QDEL'd. Either way, they're no longer our problem
+/datum/component/shielded/proc/lost_wearer(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(wearer)
+ UnregisterSignal(wearer, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_PARENT_QDELETING))
+ wearer.update_appearance(UPDATE_ICON)
+ wearer = null
+
+/datum/component/shielded/proc/set_wearer(mob/user)
+ wearer = user
+ RegisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
+ RegisterSignal(wearer, COMSIG_PARENT_QDELETING, PROC_REF(lost_wearer))
+ if(current_charges)
+ wearer.update_appearance(UPDATE_ICON)
+
+/// Used to draw the shield overlay on the wearer
+/datum/component/shielded/proc/on_update_overlays(atom/parent_atom, list/overlays)
+ SIGNAL_HANDLER
+
+ overlays += mutable_appearance(shield_icon_file, (current_charges > 0 ? shield_icon : "broken"), ABOVE_MOB_LAYER)
+
+/**
+ * This proc fires when we're hit, and is responsible for checking if we're charged, then deducting one + returning that we're blocking if so.
+ * It then runs the callback in [/datum/component/shielded/var/on_hit_effects] which handles the messages/sparks (so the visuals)
+ */
+/datum/component/shielded/proc/on_hit_react(datum/source, mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
+ SIGNAL_HANDLER
+
+ COOLDOWN_START(src, recently_hit_cd, recharge_start_delay)
+
+ if(current_charges <= 0)
+ return
+ . = COMPONENT_HIT_REACTION_BLOCK
+
+ var/charge_loss = 1 // how many charges do we lose
+
+ if(lose_multiple_charges) // if the shield has health like damage we'll lose charges equal to the damage of the hit
+ charge_loss = damage
+
+ adjust_charge(-charge_loss)
+
+ INVOKE_ASYNC(src, PROC_REF(actually_run_hit_callback), owner, attack_text, current_charges)
+
+ if(!recharge_start_delay) // if recharge_start_delay is 0, we don't recharge
+ if(!current_charges) // obviously if someone ever adds a manual way to replenish charges, change this
+ qdel(src)
+ return
+
+ START_PROCESSING(SSdcs, src) // if we DO recharge, start processing so we can do that
+
+/// The wrapper to invoke the on_hit callback, so we don't have to worry about blocking in the signal handler
+/datum/component/shielded/proc/actually_run_hit_callback(mob/living/owner, attack_text, current_charges)
+ on_hit_effects.Invoke(owner, attack_text, current_charges)
+
+/// Default on_hit proc, since cult robes are stupid and have different descriptions/sparks
+/datum/component/shielded/proc/default_run_hit_callback(mob/living/owner, attack_text, current_charges)
+ do_sparks(2, TRUE, owner)
+ owner.visible_message(span_danger("Щит [owner] отражает [attack_text]!"))
+ if(current_charges <= 0)
+ owner.visible_message(span_warning("Щит [owner] перегружается!"))
+
+/datum/component/shielded/proc/check_recharge_rune(datum/source, obj/item/wizard_armour_charge/recharge_rune, mob/living/user)
+ /*SIGNAL_HANDLER
+
+ if(!istype(recharge_rune))
+ return
+ . = COMPONENT_NO_AFTERATTACK
+ if(!istype(parent, /obj/item/clothing/suit/space/hardsuit/shielded/wizard))
+ to_chat(user, span_warning("Руна может быть использована только на броне боевого мага!"))
+ return
+
+ current_charges += recharge_rune.restored_charges
+ to_chat(user, span_notice("Заряжаю [parent]. Теперь она сможет поглотить [current_charges] ударов."))
+ qdel(recharge_rune)*/
diff --git a/code/datums/components/sizzle.dm b/code/datums/components/sizzle.dm
index 88fadb377ceb..ecb45e448d84 100644
--- a/code/datums/components/sizzle.dm
+++ b/code/datums/components/sizzle.dm
@@ -3,9 +3,11 @@
var/sizzlealpha = 0
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
-/datum/component/sizzle/Initialize()
+/datum/component/sizzle/Initialize(_alpha)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
+ if(_alpha)
+ sizzlealpha = _alpha
setup_sizzle()
/datum/component/sizzle/InheritComponent(datum/component/C, i_am_original)
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 368b70b64c0c..1307d5d0ec8e 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -28,7 +28,7 @@
/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override, extrarange, falloff_exponent, fallof_distance)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), PROC_REF(play_squeak))
+ RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), PROC_REF(play_squeak))
if(ismovable(parent))
RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), PROC_REF(play_squeak))
AddComponent(/datum/component/connect_loc_behalf, parent, item_connections)
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 89831dafab72..765e14e5db64 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -424,7 +424,7 @@
/datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M)
var/atom/A = parent
var/atom/dump_destination = dest_object.get_dumping_location()
- if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination))
+ if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination))
if(locked)
to_chat(M, "[parent] seems to be [locked_flavor]!")
return FALSE
@@ -433,6 +433,12 @@
return TRUE
return FALSE
+/datum/component/storage/proc/get_dumping_location(atom/dest_object)
+ var/datum/component/storage/storage = dest_object.GetComponent(/datum/component/storage)
+ if(storage)
+ return storage.real_location()
+ return dest_object.get_dumping_location()
+
//This proc is called when you want to place an item into the storage item.
/datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params)
SIGNAL_HANDLER
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 68c74921b8b7..0d3d520c2b54 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -270,11 +270,10 @@
if(ishuman(target))
var/mob/living/carbon/human/T = target
- var/suit_slot = T.get_item_by_slot(ITEM_SLOT_OCLOTHING)
if(isnull(T.wear_suit) && isnull(T.w_uniform)) // who honestly puts all of their effort into tackling a naked guy?
defense_mod += 2
- if(suit_slot && (istype(suit_slot,/obj/item/clothing/suit/space/hardsuit)))
+ if(T.mob_negates_gravity())
defense_mod += 1
if(T.is_shove_knockdown_blocked()) // riot armor and such
defense_mod += 5
diff --git a/code/datums/elements/empprotection.dm b/code/datums/elements/empprotection.dm
new file mode 100644
index 000000000000..8d5d798c3cb8
--- /dev/null
+++ b/code/datums/elements/empprotection.dm
@@ -0,0 +1,20 @@
+/datum/element/empprotection
+ element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE
+ id_arg_index = 2
+ var/flags = NONE
+
+/datum/element/empprotection/Attach(datum/target, _flags)
+ . = ..()
+ if(. == ELEMENT_INCOMPATIBLE || !isatom(target))
+ return ELEMENT_INCOMPATIBLE
+ flags = _flags
+ RegisterSignal(target, COMSIG_ATOM_EMP_ACT, PROC_REF(getEmpFlags))
+
+/datum/element/empprotection/Detach(atom/target)
+ UnregisterSignal(target, COMSIG_ATOM_EMP_ACT)
+ return ..()
+
+/datum/element/empprotection/proc/getEmpFlags(datum/source, severity)
+ SIGNAL_HANDLER
+
+ return flags
diff --git a/code/datums/guestbook.dm b/code/datums/guestbook.dm
index 99104f09d715..ecb7087ed08e 100644
--- a/code/datums/guestbook.dm
+++ b/code/datums/guestbook.dm
@@ -67,7 +67,7 @@
given_name = reject_bad_name(given_name)
if(!given_name)
if(!silent)
- to_chat(user, span_warning("That's a pretty terrible name. You can do better."))
+ to_chat(user, span_warning("That's a pretty terrible name."))
return FALSE
if(!visibility_checks(user, guest, silent))
return FALSE
diff --git a/code/datums/looping_sounds/weather.dm b/code/datums/looping_sounds/weather.dm
index 4398e7d5b1b2..0e26b4592eb0 100644
--- a/code/datums/looping_sounds/weather.dm
+++ b/code/datums/looping_sounds/weather.dm
@@ -74,3 +74,20 @@
/datum/looping_sound/weather/rain/indoors
volume = 30
+
+/datum/looping_sound/weather/rain/storm
+ mid_sounds = list(
+ 'sound/ambience/storm_outdoors.ogg' = 1
+ )
+ mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
+ start_sound = 'sound/ambience/acidrain_start.ogg'
+ start_length = null
+ end_sound = null
+ volume = 50
+
+/datum/looping_sound/weather/rain/storm/indoors
+ volume = 30
+ mid_sounds = list(
+ 'sound/ambience/storm_indoors.ogg' = 1
+ )
+ mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
diff --git a/code/datums/map_zones.dm b/code/datums/map_zones.dm
index c50b93cb2dd7..c4e304fb5394 100644
--- a/code/datums/map_zones.dm
+++ b/code/datums/map_zones.dm
@@ -533,6 +533,9 @@
/datum/virtual_level/proc/get_block()
return block(locate(low_x,low_y,z_value), locate(high_x,high_y,z_value))
+/datum/virtual_level/proc/get_block_portion(lower_x, lower_y, higher_x, higher_y)
+ return block(locate(lower_x,lower_y,z_value), locate(higher_x,higher_y,z_value))
+
/datum/virtual_level/proc/get_unreserved_block()
return block(locate(low_x + reserved_margin, low_y + reserved_margin, z_value), locate(high_x - reserved_margin,high_y - reserved_margin,z_value))
@@ -542,6 +545,9 @@
/datum/virtual_level/proc/get_random_position()
return locate(rand(low_x, high_x), rand(low_y, high_y), z_value)
+/datum/virtual_level/proc/get_random_position_in_margin()
+ return locate(rand(low_x + reserved_margin, high_x - reserved_margin), rand(low_y + reserved_margin, high_y - reserved_margin), z_value)
+
/datum/virtual_level/proc/get_below_turf(turf/Turf)
if(!down_linkage)
return
diff --git a/code/datums/weather/weather_types/rain.dm b/code/datums/weather/weather_types/rain.dm
index fbbb0269ed2a..591a569b19ec 100644
--- a/code/datums/weather/weather_types/rain.dm
+++ b/code/datums/weather/weather_types/rain.dm
@@ -47,3 +47,17 @@
desc = "Storm with rain and lightning."
weather_message = "The clouds blacken and the sky starts to flash as thunder strikes down!"
thunder_chance = 10
+
+/datum/weather/rain/heavy/storm_intense
+ name = "storm"
+ desc = "Storm with rain and lightning."
+ weather_overlay = "storm_very"
+ thunder_chance = 20
+ weather_color = "#a3daf7"
+ weather_duration_lower = 420690
+ weather_duration_upper = 420690
+
+ sound_active_outside = /datum/looping_sound/weather/rain/storm/indoors
+ sound_active_inside = /datum/looping_sound/weather/rain/storm
+ sound_weak_outside = /datum/looping_sound/weather/rain/storm/indoors
+ sound_weak_inside = /datum/looping_sound/weather/rain/storm
diff --git a/code/datums/wires/mod.dm b/code/datums/wires/mod.dm
new file mode 100644
index 000000000000..b5805557eafa
--- /dev/null
+++ b/code/datums/wires/mod.dm
@@ -0,0 +1,57 @@
+/datum/wires/mod
+ holder_type = /obj/item/mod/control
+ proper_name = "MOD control unit"
+
+/datum/wires/mod/New(atom/holder)
+ wires = list(WIRE_HACK, WIRE_DISABLE, WIRE_SHOCK, WIRE_INTERFACE)
+ add_duds(2)
+ ..()
+
+/datum/wires/mod/interactable(mob/user)
+ if(!..())
+ return FALSE
+ var/obj/item/mod/control/mod = holder
+ return mod.open
+
+/datum/wires/mod/get_status()
+ var/obj/item/mod/control/mod = holder
+ var/list/status = list()
+ status += "The orange light is [mod.seconds_electrified ? "on" : "off"]."
+ status += "The red light is [mod.malfunctioning ? "off" : "blinking"]."
+ status += "The green light is [mod.locked ? "on" : "off"]."
+ status += "The yellow light is [mod.interface_break ? "off" : "on"]."
+ return status
+
+/datum/wires/mod/on_pulse(wire)
+ var/obj/item/mod/control/mod = holder
+ switch(wire)
+ if(WIRE_HACK)
+ mod.locked = !mod.locked
+ if(WIRE_DISABLE)
+ mod.malfunctioning = TRUE
+ if(WIRE_SHOCK)
+ mod.seconds_electrified = MACHINE_DEFAULT_ELECTRIFY_TIME
+ if(WIRE_INTERFACE)
+ mod.interface_break = !mod.interface_break
+
+/datum/wires/mod/on_cut(wire, mend)
+ var/obj/item/mod/control/mod = holder
+ switch(wire)
+ if(WIRE_HACK)
+ if(!mend)
+ mod.req_access = list()
+ if(WIRE_DISABLE)
+ mod.malfunctioning = !mend
+ if(WIRE_SHOCK)
+ if(mend)
+ mod.seconds_electrified = MACHINE_NOT_ELECTRIFIED
+ else
+ mod.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT
+ if(WIRE_INTERFACE)
+ mod.interface_break = !mend
+
+/datum/wires/mod/ui_act(action, params)
+ var/obj/item/mod/control/mod = holder
+ if(!issilicon(usr) && mod.seconds_electrified && mod.shock(usr))
+ return FALSE
+ return ..()
diff --git a/code/game/MapData/shuttles/pgf_crying_sun.dm b/code/game/MapData/shuttles/pgf_crying_sun.dm
index 6df40aa0efdd..2851518e669a 100644
--- a/code/game/MapData/shuttles/pgf_crying_sun.dm
+++ b/code/game/MapData/shuttles/pgf_crying_sun.dm
@@ -2,7 +2,7 @@
name = "The UCWLWM"
desc = "It's looks old and worn out."
icon_state = "book3"
- author = "Welds-the-Steel"
+ author = "Senior Engineer Wihlz-Saai"
title = "The Universal Colossal Warship Linear Weapon Mount"
dat = {"
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index 366f9127cd55..3fb4fcd5efeb 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -70,7 +70,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
ambientsounds = RUINS
always_unpowered = FALSE
requires_power = TRUE
- area_flags = UNIQUE_AREA | BLOBS_ALLOWED
+ area_flags = UNIQUE_AREA
/area/asteroid/nearstation/bomb_site
name = "Bomb Testing Asteroid"
@@ -83,7 +83,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
ambientsounds = MAINTENANCE
lighting_colour_tube = "#ffe5cb"
lighting_colour_bulb = "#ffdbb4"
- area_flags = BLOBS_ALLOWED | UNIQUE_AREA
+ area_flags = UNIQUE_AREA
sound_environment = SOUND_AREA_TUNNEL_ENCLOSED
//Departments
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index e8bdb66c1898..0e2106d04026 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -13,7 +13,7 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
invisibility = INVISIBILITY_LIGHTING
- var/area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA
+ var/area_flags = VALID_TERRITORY | UNIQUE_AREA
var/fire = null
///Whether there is an atmos alarm in this area
@@ -633,7 +633,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
power_environ = FALSE
always_unpowered = FALSE
area_flags &= ~VALID_TERRITORY
- area_flags &= ~BLOBS_ALLOWED
addSorted()
/**
* Set the area size of the area
diff --git a/code/game/area/areas/outpost.dm b/code/game/area/areas/outpost.dm
index f50b7655a119..d7dd90688230 100644
--- a/code/game/area/areas/outpost.dm
+++ b/code/game/area/areas/outpost.dm
@@ -3,7 +3,7 @@
/area/outpost
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
has_gravity = STANDARD_GRAVITY
- area_flags = VALID_TERRITORY | BLOBS_ALLOWED | NOTELEPORT // not unique, in case multiple outposts get loaded. all derivatives should also be NOTELEPORT
+ area_flags = VALID_TERRITORY | NOTELEPORT // not unique, in case multiple outposts get loaded. all derivatives should also be NOTELEPORT
flags_1 = CAN_BE_DIRTY_1
sound_environment = SOUND_AREA_STANDARD_STATION
lighting_colour_tube = "#ffce99"
diff --git a/code/game/area/areas/ruins/_ruins.dm b/code/game/area/areas/ruins/_ruins.dm
index 1ba5d0e18ec6..766ec66392b5 100644
--- a/code/game/area/areas/ruins/_ruins.dm
+++ b/code/game/area/areas/ruins/_ruins.dm
@@ -4,7 +4,7 @@
name = "unexplored location"
icon_state = "away"
has_gravity = STANDARD_GRAVITY
- area_flags = HIDDEN_AREA | BLOBS_ALLOWED
+ area_flags = HIDDEN_AREA
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
ambientsounds = RUINS
flags_1 = CAN_BE_DIRTY_1
diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm
index 6060367ca51b..625129ae236f 100644
--- a/code/game/area/areas/shuttles.dm
+++ b/code/game/area/areas/shuttles.dm
@@ -112,7 +112,6 @@
/area/shuttle/custom
name = "Custom player shuttle"
- area_flags = BLOBS_ALLOWED
flags_1 = CAN_BE_DIRTY_1
/area/shuttle/custom/powered
@@ -125,19 +124,15 @@
/area/shuttle/pod_1
name = "Escape Pod One"
- area_flags = BLOBS_ALLOWED
/area/shuttle/pod_2
name = "Escape Pod Two"
- area_flags = BLOBS_ALLOWED
/area/shuttle/pod_3
name = "Escape Pod Three"
- area_flags = BLOBS_ALLOWED
/area/shuttle/pod_4
name = "Escape Pod Four"
- area_flags = BLOBS_ALLOWED
/area/shuttle/mining
name = "Mining Shuttle"
@@ -155,7 +150,6 @@
/area/shuttle/escape
name = "Emergency Shuttle"
- area_flags = BLOBS_ALLOWED
flags_1 = CAN_BE_DIRTY_1
/area/shuttle/escape/backup
diff --git a/code/game/area/ship_areas.dm b/code/game/area/ship_areas.dm
index 9732893523b7..81fb4d20d124 100644
--- a/code/game/area/ship_areas.dm
+++ b/code/game/area/ship_areas.dm
@@ -69,7 +69,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/ship
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
always_unpowered = FALSE
- area_flags = VALID_TERRITORY | BLOBS_ALLOWED // Loading the same shuttle map at a different time will produce distinct area instances.
+ area_flags = VALID_TERRITORY // Loading the same shuttle map at a different time will produce distinct area instances.
icon_state = "shuttle"
flags_1 = CAN_BE_DIRTY_1
lighting_colour_tube = "#fff0dd"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 80db6272f2fd..f52b9bdace9e 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -44,6 +44,14 @@
*/
var/list/atom_colours
+ /// Lazylist of all images (hopefully attached to us) to update when we change z levels
+ /// You will need to manage adding/removing from this yourself, but I'll do the updating for you
+ var/list/image/update_on_z
+
+ /// Lazylist of all overlays attached to us to update when we change z levels
+ /// You will need to manage adding/removing from this yourself, but I'll do the updating for you
+ /// Oh and note, if order of addition is important this WILL break that. so mind yourself
+ var/list/image/update_overlays_on_z
/// a very temporary list of overlays to remove
var/list/remove_overlays
@@ -779,15 +787,6 @@
contents_explosion(severity, target)
SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
-/**
- * React to a hit by a blob objecd
- *
- * default behaviour is to send the [COMSIG_ATOM_BLOB_ACT] signal
- */
-/atom/proc/blob_act(obj/structure/blob/B)
- SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B)
- return
-
/atom/proc/fire_act(exposed_temperature, exposed_volume)
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
return
@@ -977,7 +976,7 @@
return TRUE
///Get the best place to dump the items contained in the source storage item?
-/atom/proc/get_dumping_location(obj/item/storage/source,mob/user)
+/atom/proc/get_dumping_location()
return null
/**
@@ -1733,7 +1732,20 @@
/// Returns the atom name that should be used on screentip
/atom/proc/get_screentip_name(client/hovering_client)
- return name
+ if(ishuman(src))
+ var/mob/living/carbon/human/guy = src
+ var/mob/client_mob = hovering_client.mob
+ var/datum/guestbook/guestbook = client_mob.mind?.guestbook
+ if(guestbook)
+ var/known_name = guestbook.get_known_name(client_mob, guy)
+ if(known_name)
+ return known_name
+ else
+ return guy.get_visible_name()
+ else
+ return guy.real_name
+ else
+ return name
///Called whenever a player is spawned on the same turf as this atom.
/atom/proc/join_player_here(mob/M)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index beb7cef2718f..79326ab9e2ad 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -772,6 +772,44 @@
. = movement_type
movement_type = newval
+/**
+ * Called when a movable changes z-levels.
+ *
+ * Arguments:
+ * * old_turf - The previous turf they were on before.
+ * * new_turf - The turf they have now entered.
+ * * same_z_layer - If their old and new z levels are on the same level of plane offsets or not
+ * * notify_contents - Whether or not to notify the movable's contents that their z-level has changed. NOTE, IF YOU SET THIS, YOU NEED TO MANUALLY SET PLANE OF THE CONTENTS LATER
+ */
+/atom/movable/proc/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_turf, new_turf, same_z_layer)
+
+ // If our turfs are on different z "layers", recalc our planes
+ if(!same_z_layer && !QDELETED(src))
+ SET_PLANE(src, PLANE_TO_TRUE(src.plane), new_turf)
+ // a TON of overlays use planes, and thus require offsets
+ // so we do this. sucks to suck
+ update_appearance()
+
+ if(update_on_z)
+ // I so much wish this could be somewhere else. alas, no.
+ for(var/image/update in update_on_z)
+ SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf)
+ if(update_overlays_on_z)
+ // This EVEN more so
+ cut_overlay(update_overlays_on_z)
+ // This even more so
+ for(var/mutable_appearance/update in update_overlays_on_z)
+ SET_PLANE(update, PLANE_TO_TRUE(update.plane), new_turf)
+ add_overlay(update_overlays_on_z)
+
+ if(!notify_contents)
+ return
+
+ for (var/atom/movable/content as anything in src) // Notify contents of Z-transition.
+ content.on_changed_z_level(old_turf, new_turf, same_z_layer)
+
/**
* Called whenever an object moves and by mobs when they attempt to move themselves through space
* And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move]
@@ -784,6 +822,9 @@
* * movement_dir - 0 when stopping or any dir when trying to move
*/
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
+ if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir) & COMSIG_MOVABLE_STOP_SPACEMOVE)
+ return TRUE
+
if(has_gravity(src))
return 1
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 613182cea211..a0166efb556c 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -335,29 +335,6 @@
else
return ..()
-//////////////////////////////////////////////
-// //
-// BLOB (GHOST) //
-// //
-//////////////////////////////////////////////
-
-/datum/dynamic_ruleset/midround/from_ghosts/blob
- name = "Blob"
- antag_datum = /datum/antagonist/blob
- antag_flag = ROLE_BLOB
- enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
- required_enemies = list(2,2,1,1,1,1,1,0,0,0)
- required_candidates = 1
- weight = 4
- cost = 10
- requirements = list(101,101,101,80,60,50,30,20,10,10)
- high_population_requirement = 50
- repeatable = TRUE
-
-/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant)
- var/body = applicant.become_overmind()
- return body
-
//////////////////////////////////////////////
// //
// XENOMORPH (GHOST) //
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index d7f80fc45680..24f66242d58e 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/toy/prize/honk = 1,
/obj/item/toy/prize/marauder = 1,
/obj/item/toy/prize/seraph = 1,
- /obj/item/toy/prize/mauler = 1,
+ /obj/item/toy/prize/touro = 1,
/obj/item/toy/prize/odysseus = 1,
/obj/item/toy/prize/phazon = 1,
/obj/item/toy/prize/reticence = 1,
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 938e6b837fcf..9313ca338c1e 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -484,10 +484,6 @@ DEFINE_BITFIELD(turret_flags, list(
if(assess_perp(Mech.occupant) >= 4)
targets += Mech
- if((turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) && GLOB.blobs.len && (mode == TURRET_LETHAL))
- for(var/obj/structure/blob/B in view(scan_range, base))
- targets += B
-
if(targets.len)
tryToShootAt(targets)
else if(!always_up)
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index b0e030206a0f..1e1718623816 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -22,6 +22,7 @@
/obj/item/modular_computer,
/obj/item/gun/ballistic/automatic/powered,
/obj/item/gun/ballistic/automatic/assault/e40,
+ /obj/item/stock_parts/cell/gun
))
/obj/machinery/recharger/RefreshParts()
@@ -31,19 +32,22 @@
/obj/machinery/recharger/examine(mob/user)
. = ..()
if(!in_range(user, src) && !issilicon(user) && !isobserver(user))
- . += "You're too far away to examine [src]'s contents and display!"
+ . += span_warning("You're too far away to examine [src]'s contents and display!")
return
if(charging)
- . += {"\The [src] contains:
- - \A [charging]."}
+ . += span_notice("\The [src] contains:")
+ . += span_notice("- \A [charging].")
if(!(machine_stat & (NOPOWER|BROKEN)))
- . += "The status display reads:"
- . += "- Recharging [recharge_coeff*10]% cell charge per cycle."
+ . += span_notice("The status display reads:")
+ . += span_notice("- Recharging [recharge_coeff*10]% cell charge per cycle.")
if(charging)
var/obj/item/stock_parts/cell/C = charging.get_cell()
- . += "- \The [charging]'s cell is at [C.percent()]%."
+ if(istype(charging, /obj/item/stock_parts/cell))
+ . += span_notice("- \The [charging]'s charge is at [C.percent()]%.")
+ else
+ . += span_notice("- \The [charging]'s cell is at [C.percent()]%.")
/obj/machinery/recharger/proc/setCharging(new_charging)
@@ -62,11 +66,11 @@
/obj/machinery/recharger/attackby(obj/item/G, mob/user, params)
if(G.tool_behaviour == TOOL_WRENCH)
if(charging)
- to_chat(user, "Remove the charging item first!")
+ to_chat(user, span_notice("Remove the charging item first!"))
return
set_anchored(!anchored)
power_change()
- to_chat(user, "You [anchored ? "attached" : "detached"] [src].")
+ to_chat(user, span_notice("You [anchored ? "attached" : "detached"] [src]."))
G.play_tool_sound(src)
return
@@ -80,13 +84,13 @@
//Checks to make sure he's not in space doing it, and that the area got proper power.
var/area/a = get_area(src)
if(!isarea(a) || a.power_equip == 0)
- to_chat(user, "[src] blinks red as you try to insert [G].")
+ to_chat(user, span_notice("[src] blinks red as you try to insert [G]."))
return TRUE
if (istype(G, /obj/item/gun/energy))
var/obj/item/gun/energy/E = G
if(!E.can_charge)
- to_chat(user, "Your gun has no external power connector.")
+ to_chat(user, span_notice("Your gun has no external power connector."))
return TRUE
if(!user.transferItemToLoc(G, src))
@@ -94,7 +98,7 @@
setCharging(G)
else
- to_chat(user, "[src] isn't connected to anything!")
+ to_chat(user, span_notice("[src] isn't connected to anything!"))
return TRUE
if(anchored && !charging)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 763411adddbd..f6be0b0a036e 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -16,6 +16,7 @@
var/obj/item/clothing/suit/space/suit = null
var/obj/item/clothing/head/helmet/space/helmet = null
var/obj/item/clothing/mask/mask = null
+ var/obj/item/mod/control/mod = null
var/obj/item/storage = null
// if you add more storage slots, update cook() to clear their radiation too.
@@ -27,6 +28,8 @@
var/mask_type = null
/// What type of additional item the unit starts with when spawned.
var/storage_type = null
+ /// What type of MOD the unit starts with when spawned.
+ var/mod_type = null
state_open = FALSE
/// If the SSU's doors are locked closed. Can be toggled manually via the UI, but is also locked automatically when the UV decontamination sequence is running.
@@ -194,6 +197,8 @@
helmet = new helmet_type(src)
if(mask_type)
mask = new mask_type(src)
+ if(mod_type)
+ mod = new mod_type(src)
if(storage_type)
storage = new storage_type(src)
update_appearance()
@@ -215,6 +220,7 @@
QDEL_NULL(suit)
QDEL_NULL(helmet)
QDEL_NULL(mask)
+ QDEL_NULL(mod)
QDEL_NULL(storage)
return ..()
@@ -225,7 +231,7 @@
. += "[base_icon_state]_panel"
if(state_open)
. += "[base_icon_state]_open"
- if(suit)
+ if(suit || mod)
. += "[base_icon_state]_suit"
if(helmet)
. += "[base_icon_state]_helm"
@@ -265,6 +271,7 @@
helmet = null
suit = null
mask = null
+ mod = null
storage = null
occupant = null
@@ -291,6 +298,7 @@
"suit" = create_silhouette_of(/obj/item/clothing/suit/space/eva),
"helmet" = create_silhouette_of(/obj/item/clothing/head/helmet/space/eva),
"mask" = create_silhouette_of(/obj/item/clothing/mask/breath),
+ "mod" = create_silhouette_of(/obj/item/mod/control),
"storage" = create_silhouette_of(/obj/item/tank/internals/oxygen),
)
@@ -402,7 +410,7 @@
if(!is_operational)
to_chat(user, span_warning("The unit is not operational!"))
return
- if(occupant || helmet || suit || storage)
+ if(occupant || helmet || suit || mod || storage)
to_chat(user, span_warning("It's too cluttered inside to fit in!"))
return
@@ -412,7 +420,7 @@
target.visible_message(span_warning("[user] starts shoving [target] into [src]!"), span_userdanger("[user] starts shoving you into [src]!"))
if(do_after(user, 30, target))
- if(occupant || helmet || suit || storage)
+ if(occupant || helmet || suit || mod || storage)
return
if(target == user)
user.visible_message(span_warning("[user] slips into [src] and closes the door behind [user.p_them()]!"), span_notice("You slip into [src]'s cramped space and shut its door."))
@@ -457,6 +465,8 @@
qdel(suit) // Delete everything but the occupant.
mask = null
qdel(mask)
+ mod = null
+ qdel(mod)
storage = null
qdel(storage)
// The wires get damaged too.
@@ -484,6 +494,9 @@
if(mask)
things_to_clear += mask
things_to_clear += mask.GetAllContents()
+ if(mod)
+ things_to_clear += mod
+ things_to_clear += mod.GetAllContents()
if(storage)
things_to_clear += storage
things_to_clear += storage.GetAllContents()
@@ -570,6 +583,13 @@
if(!user.transferItemToLoc(I, src))
return
mask = I
+ else if(istype(I, /obj/item/mod/control))
+ if(mod)
+ to_chat(user, span_warning("The unit already contains a MOD!"))
+ return
+ if(!user.transferItemToLoc(I, src))
+ return
+ mod = I
else
if(storage)
to_chat(user, span_warning("The auxiliary storage compartment is full!"))
@@ -636,6 +656,9 @@
else if(istype(AM, /obj/item/clothing/mask) && !mask)
AM.forceMove(src)
mask = AM
+ else if(istype(AM, /obj/item/mod/control) && !storage)
+ AM.forceMove(src)
+ mod = AM
else if(istype(AM, /obj/item) && !storage)
AM.forceMove(src)
storage = AM
diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm
index cf89811d5f09..4ccf400e6ab1 100644
--- a/code/game/mecha/combat/durand.dm
+++ b/code/game/mecha/combat/durand.dm
@@ -12,6 +12,7 @@
force = 40
wreckage = /obj/structure/mecha_wreckage/durand
var/obj/durand_shield/shield
+ var/shield_passive_drain = 300
/obj/mecha/combat/durand/clip
@@ -46,7 +47,7 @@
/obj/mecha/combat/durand/process()
. = ..()
- if(defense_mode && !use_power(100))
+ if(defense_mode && !use_power(max(0, shield_passive_drain - (scanmod.rating * 10))))
defense_action.Activate(forced_state = TRUE)
/obj/mecha/combat/durand/domove(direction)
@@ -110,14 +111,6 @@ Expects a turf. Returns true if the attack should be blocked, false if not.*/
else
. = ..()
-/obj/mecha/combat/durand/blob_act(obj/structure/blob/B)
- if(defense_check(B.loc))
- log_message("Attack by blob. Attacker - [B].", LOG_MECHA, color="red")
- log_message("Attack absorbed by defense field.", LOG_MECHA, color="orange")
- shield.blob_act(B)
- else
- . = ..()
-
/obj/mecha/combat/durand/attackby(obj/item/W as obj, mob/user as mob, params)
if(defense_check(user.loc))
log_message("Attack absorbed by defense field. Attacker - [user], with [W]", LOG_MECHA, color="orange")
@@ -226,7 +219,7 @@ the shield is disabled by means other than the action button (like running out o
return
. = ..()
flick("shield_impact", src)
- if(!chassis.use_power((max_integrity - obj_integrity) * 100))
+ if(!chassis.use_power(max(1, (max_integrity - obj_integrity + 15) * (10 - chassis.capacitor.rating))))
chassis.cell?.charge = 0
chassis.defense_action.Activate(forced_state = TRUE)
obj_integrity = 10000
diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm
index 5774716fca71..5fe5d9350c14 100644
--- a/code/game/mecha/combat/gygax.dm
+++ b/code/game/mecha/combat/gygax.dm
@@ -14,7 +14,7 @@
wreckage = /obj/structure/mecha_wreckage/gygax
internal_damage_threshold = 35
max_equip = 3
- step_energy_drain = 3
+ base_step_energy_drain = 8
/obj/mecha/combat/gygax/mechturn(direction)
. = ..()
diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm
index d1d4bfc1ea48..825d5e24a1b8 100644
--- a/code/game/mecha/combat/marauder.dm
+++ b/code/game/mecha/combat/marauder.dm
@@ -71,22 +71,22 @@
ME.attach(src)
max_ammo()
-/obj/mecha/combat/marauder/mauler
- desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model."
- name = "\improper Mauler"
- icon_state = "mauler"
+/obj/mecha/combat/marauder/touro
+ desc = "A powerful ICW-era combat exosuit, developed off of Nanotrasen's Marauder model by Cybersun Biodynamics."
+ name = "\improper Touro"
+ icon_state = "touro"
operation_req_access = list(ACCESS_SYNDICATE)
internals_req_access = list(ACCESS_SYNDICATE)
- wreckage = /obj/structure/mecha_wreckage/mauler
+ wreckage = /obj/structure/mecha_wreckage/touro
max_equip = 6
destruction_sleep_duration = 20
-/obj/mecha/combat/marauder/mauler/Initialize()
+/obj/mecha/combat/marauder/touro/Initialize()
. = ..()
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/thrusters/ion(src)
ME.attach(src)
-/obj/mecha/combat/marauder/mauler/loaded/Initialize()
+/obj/mecha/combat/marauder/touro/loaded/Initialize()
. = ..()
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg(src)
ME.attach(src)
diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm
index 95b60eba68d8..f1c48e8cc307 100644
--- a/code/game/mecha/combat/phazon.dm
+++ b/code/game/mecha/combat/phazon.dm
@@ -4,7 +4,7 @@
icon_state = "phazon"
step_in = 2
dir_in = 2 //Facing South.
- step_energy_drain = 3
+ base_step_energy_drain = 8
max_integrity = 200
deflect_chance = 30
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 50, "fire" = 100, "acid" = 100)
diff --git a/code/game/mecha/combat/reticence.dm b/code/game/mecha/combat/reticence.dm
index 0d11859352d8..b63487ca17a5 100644
--- a/code/game/mecha/combat/reticence.dm
+++ b/code/game/mecha/combat/reticence.dm
@@ -14,7 +14,7 @@
add_req_access = 0
internal_damage_threshold = 25
max_equip = 2
- step_energy_drain = 3
+ base_step_energy_drain = 8
color = "#87878715"
stepsound = null
turnsound = null
diff --git a/code/game/mecha/equipment/weapons/mecha_ammo.dm b/code/game/mecha/equipment/weapons/mecha_ammo.dm
index 73597ff21e49..0febe3327cb6 100644
--- a/code/game/mecha/equipment/weapons/mecha_ammo.dm
+++ b/code/game/mecha/equipment/weapons/mecha_ammo.dm
@@ -50,7 +50,7 @@
/obj/item/mecha_ammo/lmg
name = "machine gun ammo"
- desc = "A box of linked ammunition, designed for the Ultra AC 2 exosuit weapon."
+ desc = "A box of linked ammunition, designed for the UMG-2 exosuit weapon."
icon_state = "lmg"
rounds = 300
ammo_type = "lmg"
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index f09c529b45a1..39b39ffeeff0 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -300,7 +300,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine
- name = "\improper FNX-99 \"Hades\" Carbine"
+ name = "\improper FNX-99 \"Phoenix\" Exosuit Carbine"
desc = "A weapon for combat exosuits. Shoots incendiary bullets."
icon_state = "mecha_carbine"
equip_cooldown = 10
@@ -323,7 +323,7 @@
harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot
- name = "\improper LBX AC 10 \"Scattershot\""
+ name = "\improper LBX-10 \"Scattershot\" Heavy Shotgun"
desc = "A weapon for combat exosuits. Shoots a spread of pellets."
icon_state = "mecha_scatter"
equip_cooldown = 20
@@ -337,7 +337,7 @@
ammo_type = "scattershot"
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg
- name = "\improper Ultra AC 2"
+ name = "\improper UMG-2 Mounted Machine Gun"
desc = "A weapon for combat exosuits. Shoots a rapid, three shot burst."
icon_state = "mecha_uac2"
equip_cooldown = 10
@@ -353,7 +353,7 @@
ammo_type = "lmg"
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/mounted
- name = "\improper mounted HMG"
+ name = "\improper Mounted Heavy Machine Gun"
desc = "A heavy calibre machine gun commonly used by motorized forces, famed for it's ability to give people on the recieving end more holes than normal. It is modified to be attached to vehicles"
projectile = /obj/projectile/bullet/lmg
fire_sound = 'sound/weapons/gun/hmg/hmg.ogg'
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 8e233a6410de..b903564c88d6 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -18,8 +18,8 @@
var/mob/living/carbon/occupant = null
var/step_in = 10 //make a step in step_in/10 sec.
var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South.
- var/normal_step_energy_drain = 10 //How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain.
- var/step_energy_drain = 10
+ var/base_step_energy_drain = 15 //The base amount of energy the mech should consume each time it moves. This variable is a backup for when leg actuators affect the energy drain.
+ var/step_energy_drain // How much energy the mech actually consumes when moving after modifiers (Eg, stock parts, leg actuators)
var/melee_energy_drain = 15
var/overload_step_energy_drain_min = 100
max_integrity = 300 //max_integrity is base health
@@ -147,6 +147,7 @@
diag_hud_set_mechcell()
diag_hud_set_mechstat()
become_hearing_sensitive(ROUNDSTART_TRAIT)
+ update_part_values()
/obj/mecha/update_icon_state()
if(silicon_pilot && silicon_icon_state)
@@ -225,11 +226,9 @@
/obj/mecha/proc/update_part_values() ///Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted.
if(scanmod)
- normal_step_energy_drain = 20 - (5 * scanmod.rating) //10 is normal, so on lowest part its worse, on second its ok and on higher its real good up to 0 on best
- step_energy_drain = normal_step_energy_drain
+ step_energy_drain = max(1, base_step_energy_drain - (5 * scanmod.rating)) //10 is normal, so on lowest part its worse, on second its ok and on higher its real good up to 0 on best
else
- normal_step_energy_drain = 500
- step_energy_drain = normal_step_energy_drain
+ step_energy_drain = 500
if(capacitor)
armor = armor.modifyRating(energy = (capacitor.rating * 5)) //Each level of capacitor protects the mech against emp by 5%
else //because we can still be hit without a cap, even if we can't move
diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm
index baeac5bd8f31..6d860558a652 100644
--- a/code/game/mecha/mecha_actions.dm
+++ b/code/game/mecha/mecha_actions.dm
@@ -179,7 +179,7 @@
else
chassis.leg_overload_mode = 0
chassis.step_in = initial(chassis.step_in)
- chassis.step_energy_drain = chassis.normal_step_energy_drain
+ chassis.update_part_values()
chassis.occupant_message("You disable leg actuators overload.")
UpdateButtonIcon()
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index b2dab79ce5e1..b6c72134456d 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -99,10 +99,6 @@
log_message("Attack by hulk. Attacker - [user].", LOG_MECHA, color="red")
log_combat(user, src, "punched", "hulk powers")
-/obj/mecha/blob_act(obj/structure/blob/B)
- log_message("Attack by blob. Attacker - [B].", LOG_MECHA, color="red")
- take_damage(30, BRUTE, "melee", 0, get_dir(src, B))
-
/obj/mecha/attack_tk()
return
diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm
index 27c680120fae..b8808944b5bd 100644
--- a/code/game/mecha/mecha_wreckage.dm
+++ b/code/game/mecha/mecha_wreckage.dm
@@ -129,9 +129,9 @@
name = "\improper Marauder wreckage"
icon_state = "marauder-broken"
-/obj/structure/mecha_wreckage/mauler
- name = "\improper Mauler wreckage"
- icon_state = "mauler-broken"
+/obj/structure/mecha_wreckage/touro
+ name = "\improper Touro wreckage"
+ icon_state = "touro-broken"
desc = "The syndicate won't be very happy about this..."
/obj/structure/mecha_wreckage/seraph
diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm
index 27e139c84ab6..4874ab142647 100644
--- a/code/game/mecha/medical/odysseus.dm
+++ b/code/game/mecha/medical/odysseus.dm
@@ -8,7 +8,7 @@
wreckage = /obj/structure/mecha_wreckage/odysseus
internal_damage_threshold = 35
deflect_chance = 15
- step_energy_drain = 6
+ base_step_energy_drain = 11
/obj/mecha/medical/odysseus/moved_inside(mob/living/carbon/human/H)
. = ..()
diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm
index 57ffd6992808..9f360011df86 100644
--- a/code/game/mecha/working/ripley.dm
+++ b/code/game/mecha/working/ripley.dm
@@ -107,7 +107,7 @@
light_range = 7
light_power = 1
wreckage = /obj/structure/mecha_wreckage/ripley/deathripley
- step_energy_drain = 0
+ base_step_energy_drain = 0
enclosed = TRUE
enter_delay = 40
silicon_icon_state = null
@@ -171,7 +171,7 @@
name = "\improper CLIP APLU Mk-IV \"Rogue\""
icon_state = "clipripley"
base_icon_state = "clipripley"
- step_energy_drain = 15 //overdriven servos are less efficient
+ base_step_energy_drain = 20 //overdriven servos are less efficient
wreckage = /obj/structure/mecha_wreckage/ripley/clip
enclosed = TRUE
enter_delay = 20 //slower than a mk. I, faster than the armored Ripleys
diff --git a/code/game/objects/effects/anomalies/anomalies_bluespace.dm b/code/game/objects/effects/anomalies/anomalies_bluespace.dm
index 58267030960e..f7012e532944 100644
--- a/code/game/objects/effects/anomalies/anomalies_bluespace.dm
+++ b/code/game/objects/effects/anomalies/anomalies_bluespace.dm
@@ -63,7 +63,7 @@
if(istype(A, /obj/item/beacon))
continue // don't teleport beacons because that's just insanely stupid
if(iscameramob(A))
- continue // Don't mess with AI eye, blob eye, xenobio or advanced cameras
+ continue // Don't mess with AI eye, xenobio or advanced cameras
if(A.anchored)
continue
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 3dd327dda08c..5de5510bec7b 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -68,6 +68,8 @@
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
dryname = "dried tracks"
drydesc = "Some old bloody tracks left by wheels. Machines are evil, perhaps."
+ ///Absorb the /squirt subtype when it exists on the turf
+ var/absorb_squirts = TRUE
/obj/effect/decal/cleanable/blood/tracks
icon_state = "tracks"
@@ -278,3 +280,132 @@
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
return 1
return 0
+
+/obj/effect/decal/cleanable/blood/hitsplatter
+ name = "blood splatter"
+ pass_flags = PASSTABLE | PASSGRILLE
+ icon_state = "hitsplatter1"
+ random_icon_states = list("hitsplatter1", "hitsplatter2", "hitsplatter3")
+ /// The turf we just came from, so we can back up when we hit a wall
+ var/turf/prev_loc
+ /// The cached info about the blood
+ var/list/blood_dna_info
+ /// Skip making the final blood splatter when we're done, like if we're not in a turf
+ var/skip = FALSE
+ /// How many tiles/items/people we can paint red
+ var/splatter_strength = 3
+ /// Insurance so that we don't keep moving once we hit a stoppoint
+ var/hit_endpoint = FALSE
+// ///Absorb the /squirt subtype when it exists on the turf
+// var/absorb_squirts = TRUE
+
+/obj/effect/decal/cleanable/blood/hitsplatter/Initialize(mapload, splatter_strength)
+ . = ..()
+ prev_loc = loc //Just so we are sure prev_loc exists
+ if(splatter_strength)
+ src.splatter_strength = splatter_strength
+
+/obj/effect/decal/cleanable/blood/hitsplatter/Destroy()
+ if(isturf(loc) && !skip)
+ playsound(src, 'sound/effects/splatter.ogg', 60, TRUE, -1)
+ if(blood_dna_info)
+ loc.add_blood_DNA(blood_dna_info)
+ return ..()
+
+/// Set the splatter up to fly through the air until it rounds out of steam or hits something. Contains sleep() pending imminent moveloop rework, don't call without async'ing it
+/obj/effect/decal/cleanable/blood/hitsplatter/proc/fly_towards(turf/target_turf, range)
+ splatter_strength = range
+ for(var/i in 1 to range)
+ step_towards(src,target_turf)
+ sleep(2) // Will be resolved pending Potato's moveloop rework
+ for(var/atom/iter_atom in get_turf(src))
+ if(hit_endpoint)
+ return
+ if(splatter_strength <= 0)
+ break
+
+ if(isitem(iter_atom))
+ iter_atom.add_blood_DNA(blood_dna_info)
+ splatter_strength--
+ else if(ishuman(iter_atom))
+ var/mob/living/carbon/human/splashed_human = iter_atom
+ if(splashed_human.wear_suit)
+ splashed_human.wear_suit.add_blood_DNA(blood_dna_info)
+ splashed_human.update_inv_wear_suit() //updates mob overlays to show the new blood (no refresh)
+ if(splashed_human.w_uniform)
+ splashed_human.w_uniform.add_blood_DNA(blood_dna_info)
+ splashed_human.update_inv_w_uniform() //updates mob overlays to show the new blood (no refresh)
+ splatter_strength--
+
+ if(splatter_strength <= 0) // we used all the puff so we delete it.
+ qdel(src)
+ return
+
+ var/obj/effect/decal/cleanable/blood/newsplatter
+ if(splatter_strength <= 3.5)
+ newsplatter = new /obj/effect/decal/cleanable/blood/squirt(get_turf(src), get_dir(prev_loc, loc), blood_dna_info)
+ else
+ newsplatter = new /obj/effect/decal/cleanable/blood/splatter(get_turf(src))
+ newsplatter.add_blood_DNA(blood_dna_info)
+ prev_loc = loc
+
+ qdel(src)
+ return
+
+/obj/effect/decal/cleanable/blood/hitsplatter/Bump(atom/bumped_atom)
+ if(!iswallturf(bumped_atom) && !istype(bumped_atom, /obj/structure/window))
+ qdel(src)
+ return
+
+ if(istype(bumped_atom, /obj/structure/window))
+ var/obj/structure/window/bumped_window = bumped_atom
+ if(!bumped_window.fulltile)
+ qdel(src)
+ return
+
+ hit_endpoint = TRUE
+ if(isturf(prev_loc))
+ abstract_move(bumped_atom)
+ skip = TRUE
+ //Adjust pixel offset to make splatters appear on the wall
+ if(istype(bumped_atom, /obj/structure/window))
+ land_on_window(bumped_atom)
+ else
+ var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc)
+ final_splatter.pixel_x = (dir == EAST ? 32 : (dir == WEST ? -32 : 0))
+ final_splatter.pixel_y = (dir == NORTH ? 32 : (dir == SOUTH ? -32 : 0))
+ else // This will only happen if prev_loc is not even a turf, which is highly unlikely.
+ abstract_move(bumped_atom)
+ qdel(src)
+
+/// A special case for hitsplatters hitting windows, since those can actually be moved around, store it in the window and slap it in the vis_contents
+/obj/effect/decal/cleanable/blood/hitsplatter/proc/land_on_window(obj/structure/window/the_window)
+ if(!the_window.fulltile)
+ return
+ var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new
+ final_splatter.forceMove(the_window)
+ the_window.vis_contents += final_splatter
+ the_window.bloodied = TRUE
+ qdel(src)
+
+/obj/effect/decal/cleanable/blood/squirt
+ name = "blood trail"
+ icon_state = "squirt"
+ random_icon_states = null
+
+/obj/effect/decal/cleanable/blood/squirt/Initialize(mapload, direction, list/blood_dna)
+ . = ..()
+ dir = direction
+ var/obj/effect/decal/cleanable/blood/splatter/existing_blood = locate() in get_turf(src)
+ if(existing_blood?.absorb_squirts)
+ if(blood_dna)
+ existing_blood.add_blood_DNA(blood_dna)
+ existing_blood.bloodiness = min((existing_blood.bloodiness + bloodiness), BLOOD_AMOUNT_PER_DECAL)
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/decal/cleanable/blood/splatter/over_window // special layer/plane set to appear on windows
+ layer = ABOVE_WINDOW_LAYER
+ plane = GAME_PLANE
+ turf_loc_check = FALSE
+ alpha = 180
+ absorb_squirts = FALSE
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 2dfea3173feb..0d2282eeb873 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -14,10 +14,6 @@
)
AddElement(/datum/element/connect_loc, loc_connections)
-/obj/effect/decal/blob_act(obj/structure/blob/B)
- if(B && B.loc == loc)
- qdel(src)
-
/obj/effect/decal/proc/NeverShouldHaveComeHere(turf/T)
return isclosedturf(T) || isgroundlessturf(T)
diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm
index 5f84386668f2..f03fe61a1bea 100644
--- a/code/game/objects/effects/decals/misc.dm
+++ b/code/game/objects/effects/decals/misc.dm
@@ -5,9 +5,6 @@
pass_flags = PASSTABLE | PASSGRILLE
layer = FLY_LAYER
-/obj/effect/decal/chempuff/blob_act(obj/structure/blob/B)
- return
-
/obj/effect/decal/fakelattice
name = "lattice"
desc = "A lightweight support lattice."
diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm
index 3f2b6ecaf94e..efa1de11103f 100644
--- a/code/game/objects/effects/effect_system/effects_other.dm
+++ b/code/game/objects/effects/effect_system/effects_other.dm
@@ -81,6 +81,9 @@
/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I)
I.setDir(holder.dir)
+/datum/effect_system/trail_follow/ion/grav_allowed
+ nograv_required = FALSE
+
//Reagent-based explosion effect
/datum/effect_system/reagents_explosion
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index fea67e7341b4..089b9b987965 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -20,9 +20,6 @@
/obj/effect/mech_melee_attack(obj/mecha/M)
return 0
-/obj/effect/blob_act(obj/structure/blob/B)
- return
-
/obj/effect/attack_hulk(mob/living/carbon/human/user)
return FALSE
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 078c435bd213..ccc3e4f0286a 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -309,16 +309,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
GLOB.xeno_spawn += loc
return INITIALIZE_HINT_QDEL
-//also blobs that have their spawn forcemoved (running out of time when picking their spawn spot), santa and respawning devils
-/obj/effect/landmark/blobstart
- name = "blobstart"
- icon_state = "blob_start"
-
-/obj/effect/landmark/blobstart/Initialize(mapload)
- ..()
- GLOB.blobstart += loc
- return INITIALIZE_HINT_QDEL
-
//spawns sec equipment lockers depending on the number of sec officers
/obj/effect/landmark/secequipment
name = "secequipment"
diff --git a/code/game/objects/effects/particle_emitter.dm b/code/game/objects/effects/particle_emitter.dm
index 3ee4ab8ed461..cc4210f742a1 100644
--- a/code/game/objects/effects/particle_emitter.dm
+++ b/code/game/objects/effects/particle_emitter.dm
@@ -1,7 +1,70 @@
-/obj/effect/particle_emitter
- name = ""
+///objects can only have one particle on them at a time, so we use these abstract effects to hold and display the effects. You know, so multiple particle effects can exist at once.
+///also because some objects do not display particles due to how their visuals are built
+/obj/effect/abstract/particle_holder
+ name = "particle holder"
+ desc = "How are you reading this? Please make a bug report :)"
+ appearance_flags = KEEP_APART|KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE //movable appearance_flags plus KEEP_APART and KEEP_TOGETHER
+ vis_flags = VIS_INHERIT_PLANE
+ layer = ABOVE_ALL_MOB_LAYER
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
- mouse_opacity = 0
+ /// Holds info about how this particle emitter works
+ /// See \code\__DEFINES\particles.dm
+ var/particle_flags = NONE
-/obj/effect/particle_emitter/Initialize(mapload, time)
+ var/atom/parent
+
+/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE)
. = ..()
+ if(!loc)
+ stack_trace("particle holder was created with no loc!")
+ return INITIALIZE_HINT_QDEL
+ // We nullspace ourselves because some objects use their contents (e.g. storage) and some items may drop everything in their contents on deconstruct.
+ parent = loc
+ loc = null
+
+ // Mouse opacity can get set to opaque by some objects when placed into the object's contents (storage containers).
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ src.particle_flags = particle_flags
+ particles = new particle_path()
+ // /atom doesn't have vis_contents, /turf and /atom/movable do
+ var/atom/movable/lie_about_areas = parent
+ lie_about_areas.vis_contents += src
+ RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(parent_deleted))
+
+ if(particle_flags & PARTICLE_ATTACH_MOB)
+ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+ on_move(parent, null, NORTH)
+
+/obj/effect/abstract/particle_holder/Destroy(force)
+ QDEL_NULL(particles)
+ parent = null
+ return ..()
+
+/// Non movables don't delete contents on destroy, so we gotta do this
+/obj/effect/abstract/particle_holder/proc/parent_deleted(datum/source)
+ SIGNAL_HANDLER
+ qdel(src)
+
+/// signal called when a parent that's been hooked into this moves
+/// does a variety of checks to ensure overrides work out properly
+/obj/effect/abstract/particle_holder/proc/on_move(atom/movable/attached, atom/oldloc, direction)
+ SIGNAL_HANDLER
+
+ if(!(particle_flags & PARTICLE_ATTACH_MOB))
+ return
+
+ //remove old
+ if(ismob(oldloc))
+ var/mob/particle_mob = oldloc
+ particle_mob.vis_contents -= src
+
+ // If we're sitting in a mob, we want to emit from it too, for vibes and shit
+ if(ismob(attached.loc))
+ var/mob/particle_mob = attached.loc
+ particle_mob.vis_contents += src
+
+/// Sets the particles position to the passed coordinate list (X, Y, Z)
+/// See [https://www.byond.com/docs/ref/#/{notes}/particles] for position documentation
+/obj/effect/abstract/particle_holder/proc/set_particle_position(list/pos)
+ particles.position = pos
diff --git a/code/game/objects/effects/particles/acid.dm b/code/game/objects/effects/particles/acid.dm
new file mode 100644
index 000000000000..5ce0984991d8
--- /dev/null
+++ b/code/game/objects/effects/particles/acid.dm
@@ -0,0 +1,15 @@
+// Acid related particles.
+/particles/acid
+ icon = 'icons/effects/particles/goop.dmi'
+ icon_state = list("goop_1" = 6, "goop_2" = 2, "goop_3" = 1)
+ width = 100
+ height = 100
+ count = 100
+ spawning = 0.5
+ color = "#00ea2b80" //to get 96 alpha
+ lifespan = 1.5 SECONDS
+ fade = 1 SECONDS
+ grow = -0.025
+ gravity = list(0, 0.15)
+ position = generator(GEN_SPHERE, 0, 16, NORMAL_RAND)
+ spin = generator(GEN_NUM, -15, 15, NORMAL_RAND)
diff --git a/code/game/objects/effects/particles/fire.dm b/code/game/objects/effects/particles/fire.dm
new file mode 100644
index 000000000000..fb20dc778e5e
--- /dev/null
+++ b/code/game/objects/effects/particles/fire.dm
@@ -0,0 +1,55 @@
+// Fire related particles.
+/particles/bonfire
+ icon = 'icons/effects/particles/bonfire.dmi'
+ icon_state = "bonfire"
+ width = 100
+ height = 100
+ count = 1000
+ spawning = 4
+ lifespan = 0.7 SECONDS
+ fade = 1 SECONDS
+ grow = -0.01
+ velocity = list(0, 0)
+ position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND)
+ drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2))
+ gravity = list(0, 0.95)
+ scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND)
+ rotation = 30
+ spin = generator(GEN_NUM, -20, 20)
+
+/particles/embers
+ icon = 'icons/effects/particles/generic.dmi'
+ icon_state = list("dot" = 4,"cross" = 1,"curl" = 1)
+ width = 64
+ height = 96
+ count = 500
+ spawning = 5
+ lifespan = 3 SECONDS
+ fade = 1 SECONDS
+ color = 0
+ color_change = 0.05
+ gradient = list("#FBAF4D", "#FCE6B6", "#FD481C")
+ position = generator(GEN_BOX, list(-12,-16,0), list(12,16,0), NORMAL_RAND)
+ drift = generator(GEN_VECTOR, list(-0.1,0), list(0.1,0.025))
+ spin = generator(GEN_NUM, list(-15,15), NORMAL_RAND)
+ scale = generator(GEN_VECTOR, list(0.5,0.5), list(2,2), NORMAL_RAND)
+
+/particles/embers/lava
+ width = 700
+ height = 700
+ gradient = list(LIGHT_COLOR_FLARE, LIGHT_COLOR_FLARE , COLOR_ALMOST_BLACK)
+ spawning = 1
+
+/particles/lava
+ width = 700
+ height = 700
+ count = 500
+ spawning = 1
+ lifespan = 4 SECONDS
+ fade = 2 SECONDS
+ position = generator(GEN_CIRCLE, 16, 24, NORMAL_RAND)
+ drift = generator(GEN_VECTOR, list(-0.2, -0.2), list(0.6, 0.6))
+ velocity = generator(GEN_CIRCLE, -6, 6, NORMAL_RAND)
+ friction = 0.15
+ gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK)
+ color_change = 0.125
diff --git a/code/game/objects/effects/particles/misc.dm b/code/game/objects/effects/particles/misc.dm
new file mode 100644
index 000000000000..18db20d63798
--- /dev/null
+++ b/code/game/objects/effects/particles/misc.dm
@@ -0,0 +1,32 @@
+// General or un-matched particles, make a new file if a few can be sorted together.
+/particles/pollen
+ icon = 'icons/effects/particles/pollen.dmi'
+ icon_state = "pollen"
+ width = 100
+ height = 100
+ count = 1000
+ spawning = 4
+ lifespan = 0.7 SECONDS
+ fade = 1 SECONDS
+ grow = -0.01
+ velocity = list(0, 0)
+ position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND)
+ drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2))
+ gravity = list(0, 0.95)
+ scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND)
+ rotation = 30
+ spin = generator(GEN_NUM, -20, 20)
+
+/particles/echo
+ icon = 'icons/effects/particles/echo.dmi'
+ icon_state = list("echo1" = 1, "echo2" = 1, "echo3" = 2)
+ width = 480
+ height = 480
+ count = 1000
+ spawning = 0.5
+ lifespan = 2 SECONDS
+ fade = 1 SECONDS
+ gravity = list(0, -0.1)
+ position = generator(GEN_BOX, list(-240, -240), list(240, 240), NORMAL_RAND)
+ drift = generator(GEN_VECTOR, list(-0.1, 0), list(0.1, 0))
+ rotation = generator(GEN_NUM, 0, 360, NORMAL_RAND)
diff --git a/code/game/objects/effects/particles/slime.dm b/code/game/objects/effects/particles/slime.dm
new file mode 100644
index 000000000000..5cef9c976257
--- /dev/null
+++ b/code/game/objects/effects/particles/slime.dm
@@ -0,0 +1,22 @@
+/// Slime particles.
+/particles/slime
+ icon = 'icons/effects/particles/goop.dmi'
+ icon_state = list("goop_1" = 6, "goop_2" = 2, "goop_3" = 1)
+ width = 100
+ height = 100
+ count = 100
+ spawning = 0.5
+ color = "#707070a0"
+ lifespan = 1.5 SECONDS
+ fade = 1 SECONDS
+ grow = -0.025
+ gravity = list(0, -0.05)
+ position = generator(GEN_BOX, list(-8,-16,0), list(8,16,0), NORMAL_RAND)
+ spin = generator(GEN_NUM, -15, 15, NORMAL_RAND)
+ scale = list(0.75, 0.75)
+
+/// Rainbow slime particles.
+/particles/slime/rainbow
+ gradient = list(0, "#f00a", 3, "#0ffa", 6, "#f00a", "loop", "space"=COLORSPACE_HSL)
+ color_change = 0.2
+ color = generator(GEN_NUM, 0, 6, UNIFORM_RAND)
diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm
new file mode 100644
index 000000000000..72807e778f56
--- /dev/null
+++ b/code/game/objects/effects/particles/smoke.dm
@@ -0,0 +1,66 @@
+// All the smoke variant particles.
+/particles/smoke
+ icon = 'icons/effects/particles/smoke.dmi'
+ icon_state = list("smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 2)
+ width = 100
+ height = 100
+ count = 1000
+ spawning = 4
+ lifespan = 1.5 SECONDS
+ fade = 1 SECONDS
+ velocity = list(0, 0.4, 0)
+ position = list(6, 0, 0)
+ drift = generator(GEN_SPHERE, 0, 2, NORMAL_RAND)
+ friction = 0.2
+ gravity = list(0, 0.95)
+ grow = 0.05
+
+/particles/smoke/turf_fire
+ position = generator(GEN_SPHERE, 16, 24, NORMAL_RAND)
+
+/particles/smoke/burning
+ position = list(0, 0, 0)
+
+/particles/smoke/burning/small
+ spawning = 1
+ scale = list(0.8, 0.8)
+ velocity = list(0, 0.4, 0)
+
+/particles/smoke/steam
+ icon_state = list("steam_1" = 1, "steam_2" = 1, "steam_3" = 2)
+ fade = 1.5 SECONDS
+
+/particles/smoke/steam/mild
+ spawning = 1
+ velocity = list(0, 0.3, 0)
+ friction = 0.25
+
+/particles/smoke/steam/bad
+ icon_state = list("steam_1" = 1, "smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 1)
+ spawning = 2
+ velocity = list(0, 0.25, 0)
+
+/particles/smoke/steam/vent
+ position = generator(GEN_SPHERE, 16, 16, NORMAL_RAND)
+ lifespan = 2 SECONDS
+ spawning = 3
+
+/particles/smoke/steam/vent/low
+ spawning = 1
+ velocity = list(0, 0.3, 0)
+ friction = 0.25
+
+/particles/smoke/steam/vent/high
+ spawning = 8
+ velocity = list(0, 0.25, 0)
+ count = 1000
+
+/particles/smoke/ash
+ icon_state = list("ash_1" = 2, "ash_2" = 2, "ash_3" = 1, "smoke_1" = 3, "smoke_2" = 2)
+ count = 500
+ spawning = 1
+ lifespan = 1 SECONDS
+ fade = 0.2 SECONDS
+ fadein = 0.7 SECONDS
+ position = generator(GEN_VECTOR, list(-3, 5, 0), list(3, 6.5, 0), NORMAL_RAND)
+ velocity = generator(GEN_VECTOR, list(-0.1, 0.4, 0), list(0.1, 0.5, 0), NORMAL_RAND)
diff --git a/code/game/objects/effects/particles/water.dm b/code/game/objects/effects/particles/water.dm
new file mode 100644
index 000000000000..456617cb5d8a
--- /dev/null
+++ b/code/game/objects/effects/particles/water.dm
@@ -0,0 +1,17 @@
+// Water related particles.
+/particles/droplets
+ icon = 'icons/effects/particles/generic.dmi'
+ icon_state = list("dot"=2,"drop"=1)
+ width = 32
+ height = 36
+ count = 5
+ spawning = 0.2
+ lifespan = 1 SECONDS
+ fade = 0.5 SECONDS
+ color = "#549EFF"
+ position = generator(GEN_BOX, list(-9,-9,0), list(9,18,0), NORMAL_RAND)
+ scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND)
+ gravity = list(0, -0.9)
+
+/particles/droplets/blood
+ spawning = 0.2
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 4913f9b835ce..2b82f656ac48 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -346,13 +346,6 @@
icon_state = "explosionfast"
duration = 4
-/obj/effect/temp_visual/blob
- name = "blob"
- icon_state = "blob_attack"
- alpha = 140
- randomdir = 0
- duration = 6
-
/obj/effect/temp_visual/desynchronizer
name = "desynchronizer field"
icon_state = "chronofield"
@@ -469,6 +462,11 @@
duration = 12
shrink = FALSE
+/obj/effect/temp_visual/light_ash
+ icon_state = "light_ash"
+ icon = 'icons/effects/weather_effects.dmi'
+ duration = 3.2 SECONDS
+
/obj/effect/temp_visual/warp_cube
duration = 5
var/outgoing = TRUE
diff --git a/code/game/objects/effects/turf_fire.dm b/code/game/objects/effects/turf_fire.dm
index a0c9e0f95a9b..735d8226edfa 100644
--- a/code/game/objects/effects/turf_fire.dm
+++ b/code/game/objects/effects/turf_fire.dm
@@ -65,7 +65,7 @@
/obj/effect/abstract/turf_fire/Initialize(mapload, power, fire_color)
. = ..()
- particles = new /particles/lava
+ particles = new /particles/smoke/turf_fire
var/turf/open/open_turf = loc
if(open_turf.turf_fire)
return INITIALIZE_HINT_QDEL
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 88c5df2262da..45d83621b431 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -31,7 +31,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
///If set, vox wearing this use this instead of their clothing file
var/vox_override_icon
- /// Needs to follow this syntax: either a list() with the x and y coordinates of the pixel you want to get the colour from, or a hexcolour. Colour one replaces red, two replaces blue, and three replaces green in the icon state.
+ /// Needs to follow this syntax: either a list() with the x and y coordinates of the pixel you want to get the colour from, or a hexcolour. Colour one replaces red, two replaces green, and three replaces blue in the icon state.
var/list/greyscale_colors[3]
/// Needs to be a RGB-greyscale format icon state in all species' clothing icon files.
var/greyscale_icon_state
@@ -240,16 +240,57 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
qdel(X)
return ..()
+/// Called when an action associated with our item is deleted
+/obj/item/proc/on_action_deleted(datum/source)
+ SIGNAL_HANDLER
+
+ if(!(source in actions))
+ CRASH("An action ([source.type]) was deleted that was associated with an item ([src]), but was not found in the item's actions list.")
+
+ LAZYREMOVE(actions, source)
+
+
+/// Adds an item action to our list of item actions.
+/// Item actions are actions linked to our item, that are granted to mobs who equip us.
+/// This also ensures that the actions are properly tracked in the actions list and removed if they're deleted.
+/// Can be be passed a typepath of an action or an instance of an action.
+/obj/item/proc/add_item_action(action_or_action_type)
+
+ var/datum/action/action
+ if(ispath(action_or_action_type, /datum/action))
+ action = new action_or_action_type(src)
+ else if(istype(action_or_action_type, /datum/action))
+ action = action_or_action_type
+ else
+ CRASH("item add_item_action got a type or instance of something that wasn't an action.")
+
+ LAZYADD(actions, action)
+ RegisterSignal(action, COMSIG_PARENT_QDELETING, PROC_REF(on_action_deleted))
+ grant_action_to_bearer(action)
+ return action
+
+/// Grant the action to anyone who has this item equipped to an appropriate slot
+/obj/item/proc/grant_action_to_bearer(datum/action/action)
+ if(!ismob(loc))
+ return
+ var/mob/holder = loc
+ give_item_action(action, holder, holder.get_slot_by_item(src))
+
+/// Removes an instance of an action from our list of item actions.
+/obj/item/proc/remove_item_action(datum/action/action)
+ if(!action)
+ return
+
+ UnregisterSignal(action, COMSIG_PARENT_QDELETING)
+ LAZYREMOVE(actions, action)
+ qdel(action)
+
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
if(((src in target) && !target_self) || (!isturf(target.loc) && !isturf(target) && not_inside))
return 0
else
return 1
-/obj/item/blob_act(obj/structure/blob/B)
- if(B && B.loc == loc)
- qdel(src)
-
/obj/item/ComponentInitialize()
. = ..()
// this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:)
@@ -535,9 +576,21 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
playsound(src, pickup_sound, PICKUP_SOUND_VOLUME, ignore_walls = FALSE)
user.update_equipment_speed_mods()
-///sometimes we only want to grant the item's action if it's equipped in a specific slot.
-/obj/item/proc/item_action_slot_check(slot, mob/user)
- if(slot == ITEM_SLOT_BACKPACK || slot == ITEM_SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
+/// Gives one of our item actions to a mob, when equipped to a certain slot
+/obj/item/proc/give_item_action(datum/action/action, mob/to_who, slot)
+ // Some items only give their actions buttons when in a specific slot.
+ if(!item_action_slot_check(slot, to_who))
+ // There is a chance we still have our item action currently,
+ // and are moving it from a "valid slot" to an "invalid slot".
+ // So call Remove() here regardless, even if excessive.
+ action.Remove(to_who)
+ return
+
+ action.Grant(to_who)
+
+/// Sometimes we only want to grant the item's action if it's equipped in a specific slot.
+/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/action)
+ if(slot & (ITEM_SLOT_BACKPACK|ITEM_SLOT_LEGCUFFED)) //these aren't true slots, so avoid granting actions there
return FALSE
return TRUE
@@ -1128,6 +1181,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/get_writing_implement_details()
return null
+/// Whether or not this item can be put into a storage item through attackby
+/obj/item/proc/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
+ return TRUE
/// How many different types of mats will be counted in a bite?
#define MAX_MATS_PER_BITE 2
diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm
index baa541662e20..fce9f308ea0f 100644
--- a/code/game/objects/items/chrono_eraser.dm
+++ b/code/game/objects/items/chrono_eraser.dm
@@ -285,9 +285,5 @@
/obj/structure/chrono_field/ex_act()
return
-/obj/structure/chrono_field/blob_act(obj/structure/blob/B)
- return
-
-
#undef CHRONO_BEAM_RANGE
#undef CHRONO_FRAME_COUNT
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 6528ef4851c7..a9f28d88caed 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -62,7 +62,7 @@
to_chat(user, "[M] doesn't have a head!")
return
- if(light_power < 1)
+ if(light_power < 0.3)
to_chat(user, "\The [src] isn't bright enough to see anything! ")
return
diff --git a/code/game/objects/items/etherealdiscoball.dm b/code/game/objects/items/etherealdiscoball.dm
index 94f1ae2a6062..75084c239e53 100644
--- a/code/game/objects/items/etherealdiscoball.dm
+++ b/code/game/objects/items/etherealdiscoball.dm
@@ -1,18 +1,18 @@
/obj/item/etherealballdeployer
- name = "Portable Ethereal Disco Ball"
- desc = "Press the button for a deployment of slightly-unethical PARTY!"
+ name = "Portable Animatronic Disco Ball"
+ desc = "Press the button for a deployment of a copyright free PARTY!"
icon = 'icons/obj/device.dmi'
icon_state = "ethdisco"
/obj/item/etherealballdeployer/attack_self(mob/living/carbon/user)
.=..()
- to_chat(user, "You deploy the Ethereal Disco Ball.")
+ to_chat(user, span_notice("You deploy the Ethereal Disco Ball."))
new /obj/structure/etherealball(user.loc)
qdel(src)
/obj/structure/etherealball
- name = "Ethereal Disco Ball"
- desc = "The ethics of this discoball are questionable."
+ name = "Animatronic Disco Ball"
+ desc = "A discoball with an animatronic head inside, seemingly in the likeness of a famous elzousza muscisian. A disclaimer on the side says any resemblence to living persons is entirely coincidental."
icon = 'icons/obj/device.dmi'
icon_state = "ethdisco_head_0"
anchored = TRUE
@@ -31,15 +31,15 @@
. = ..()
if(TurnedOn)
TurnOff()
- to_chat(user, "You turn the disco ball off!")
+ to_chat(user, span_notice("You turn the disco ball off!"))
else
TurnOn()
- to_chat(user, "You turn the disco ball on!")
+ to_chat(user, span_notice("You turn the disco ball on!"))
/obj/structure/etherealball/AltClick(mob/living/carbon/human/user)
. = ..()
set_anchored(!anchored)
- to_chat(user, "You [anchored ? null : "un"]lock the disco ball.")
+ to_chat(user, span_notice("You [anchored ? null : "un"]lock the disco ball."))
/obj/structure/etherealball/proc/TurnOn()
TurnedOn = TRUE //Same
diff --git a/code/game/objects/items/grenades/discogrenade.dm b/code/game/objects/items/grenades/discogrenade.dm
index 84ce765d59d3..c582a35e3ff3 100644
--- a/code/game/objects/items/grenades/discogrenade.dm
+++ b/code/game/objects/items/grenades/discogrenade.dm
@@ -1,5 +1,3 @@
-//Ethereal Disco Grenade for Ethereal traitors
-//Does not affect ethereals.
//Some basic code pieces taken from flashbang, spawner grenade and ethereal disco ball for functionality (basically a combination of the 3).
//////////////////////
@@ -7,8 +5,8 @@
//////////////////////
/obj/item/grenade/discogrenade
- name = "Ethereal Disco Grenade"
- desc = "An unethical micro-party that will make all non-Ethereal beings dance to its beat!"
+ name = "Portable Disco Grenade"
+ desc = "An exotic prototype grenade. Through powerful audiovisual hypnotic cues, victims are afflicted with an unstoppable urge to boogie down. "
icon_state = "disco"
item_state = "flashbang"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
@@ -91,7 +89,7 @@
return
if(target.stat != CONSCIOUS) //Only conscious people can dance
return
- if(!target || iselzuose(target)) //Non humans and non etherals can't dance
+ if(!target) //Non humans and non etherals can't dance
return
var/distance = max(0,get_dist(get_turf(src), target_turf))
@@ -102,27 +100,27 @@
target.say(pick(message_social_anxiety))
if(rand(3) && target.get_ear_protection() == 0)
target.drop_all_held_items()
- target.show_message("You cover your ears, the music is just too loud for you.", 2)
+ target.show_message(span_warning("You cover your ears, the music is just too loud for you."), 2)
return
if(HAS_TRAIT(target, TRAIT_MINDSHIELD))
- target.show_message("You resist your inner urges to break out your best moves.", 2)
+ target.show_message(span_warning("You resist your inner urges to break out your best moves."), 2)
target.set_drugginess(5)
return
if(istype(target.get_item_by_slot(ITEM_SLOT_HEAD), /obj/item/clothing/head/foilhat))
- to_chat(target, "THOSE GLOW-IN-THE-DARK NANOTRASEN LIGHTBULBS WON'T CORRUPT ME WITH THEIR AGENDA!")
+ to_chat(target, span_userdanger("BIG DISCO WON'T CORRUPT ME WITH THEIR POST ICW PSY-OP MUSIC!"))
target.emote("scream")
return
target.set_drugginess(10)
- target.show_message("You feel a strong rythme and your muscles spasm uncontrollably, you begin dancing and cannot move!", 2)
+ target.show_message(span_warning("You feel a strong rythme and your muscles spasm uncontrollably, you begin dancing and cannot move!"), 2)
target.Immobilize(30)
//Special actions
switch(rand(0, 6))
if(0)
target.Knockdown(4)
- target.show_message("You [pick("mess", "screw")] up your moves and trip!", 2)
+ target.show_message(span_warning("You [pick("mess", "screw")] up your moves and trip!"), 2)
if(1 to 3)
target.emote("spin")
if(3 to 4)
diff --git a/code/game/objects/items/grenades/smokebomb.dm b/code/game/objects/items/grenades/smokebomb.dm
index e300c3ef07a6..dfea3fb87829 100644
--- a/code/game/objects/items/grenades/smokebomb.dm
+++ b/code/game/objects/items/grenades/smokebomb.dm
@@ -22,7 +22,4 @@
smoke.set_up(4, src)
smoke.start()
qdel(smoke) //And deleted again. Sad really.
- for(var/obj/structure/blob/B in view(8,src))
- var/damage = round(30/(get_dist(B,src)+1))
- B.take_damage(damage, BURN, "melee", 0)
resolve()
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 0affcd107af0..5b91f6b7bd87 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -96,7 +96,7 @@
var/shield_icon = "shield-red"
/obj/item/nullrod/staff/worn_overlays(isinhands)
- . = list()
+ . = ..()
if(isinhands)
. += mutable_appearance('icons/effects/effects.dmi', shield_icon, MOB_LAYER + 0.01)
diff --git a/code/game/objects/items/powerfist.dm b/code/game/objects/items/powerfist.dm
index c41142d4ad24..13d1d10230f0 100644
--- a/code/game/objects/items/powerfist.dm
+++ b/code/game/objects/items/powerfist.dm
@@ -75,6 +75,7 @@
return
var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting)
var/turf/T = get_turf(src)
+ var/mols_used = gasused.total_moles()
if(!T)
return
T.assume_air(gasused)
@@ -86,7 +87,7 @@
target.visible_message("[user]'s powerfist lets out a dull thunk as [user.p_they()] punch[user.p_es()] [target.name]!", \
"[user]'s punches you!")
return
- if(gasused.total_moles() < gasperfist * fisto_setting)
+ if(mols_used < gasperfist * fisto_setting)
to_chat(user, "\The [src]'s piston-ram lets out a weak hiss, it needs more gas!")
playsound(loc, 'sound/weapons/punch4.ogg', 50, TRUE)
target.apply_damage((force / 2), BRUTE)
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 92123969a4cd..125b10a10043 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -603,10 +603,10 @@
new /obj/item/mecha_ammo/scattershot(src)
new /obj/item/storage/belt/utility/syndicate(src)
-/obj/item/storage/backpack/duffelbag/syndie/ammo/mauler
+/obj/item/storage/backpack/duffelbag/syndie/ammo/touro
desc = "A large duffel bag, packed to the brim with various exosuit ammo."
-/obj/item/storage/backpack/duffelbag/syndie/ammo/mauler/PopulateContents()
+/obj/item/storage/backpack/duffelbag/syndie/ammo/touro/PopulateContents()
new /obj/item/mecha_ammo/lmg(src)
new /obj/item/mecha_ammo/lmg(src)
new /obj/item/mecha_ammo/lmg(src)
diff --git a/code/game/objects/items/storage/ration.dm b/code/game/objects/items/storage/ration.dm
index 482ba202a73e..b016cc339260 100644
--- a/code/game/objects/items/storage/ration.dm
+++ b/code/game/objects/items/storage/ration.dm
@@ -9,10 +9,20 @@
resistance_flags = FLAMMABLE
drop_sound = 'sound/items/handling/cardboardbox_drop.ogg'
pickup_sound = 'sound/items/handling/cardboardbox_pickup.ogg'
+ var/emblem_icon_state = "null"
+ var/ration_overlay = "null"
/obj/item/storage/ration/Initialize(mapload)
. = ..()
update_icon()
+ update_overlays()
+
+/obj/item/storage/ration/update_overlays()
+ . = ..()
+ var/mutable_appearance/ration_overlay
+ if(emblem_icon_state)
+ ration_overlay = mutable_appearance(icon, emblem_icon_state)
+ add_overlay(ration_overlay)
/obj/item/storage/ration/ComponentInitialize()
. = ..()
@@ -38,7 +48,7 @@
/obj/item/storage/ration/vegan_chili
name = "vegan chili with beans ration"
desc = "A complete meal package containing a hearty vegan chili with beans, complemented by vegetable crackers, savory cornbread, flavorful pizza crackers, and more. A perfect choice for plant-based nourishment."
-
+ emblem_icon_state = "emblem_vegan_chili"
/obj/item/storage/ration/vegan_chili/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/vegan_chili = 1,
@@ -54,7 +64,7 @@
/obj/item/storage/ration/shredded_beef
name = "shredded beef in barbecue sauce ration"
desc = "Enjoy the rich and savory flavors of shredded beef in smoky barbecue sauce with this satisfying ration. Accompanied by a fruit puree, jerky wrap, cinnamon bun, and additional condiments, this ration is perfect for meat lovers."
-
+ emblem_icon_state = "emblem_shredded_beef"
/obj/item/storage/ration/shredded_beef/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/shredded_beef = 1,
@@ -70,7 +80,7 @@
/obj/item/storage/ration/pork_spaghetti
name = "spaghetti with pork and sauce ration"
desc = "Indulge in a comforting meal of spaghetti with tender pork and savory sauce with this ration. Complemented by a toaster pastry, seasoned bread sticks, dried raisins, and other accompaniments, this ration offers a flavorful experience."
-
+ emblem_icon_state = "emblem_pork_spaghetti"
/obj/item/storage/ration/pork_spaghetti/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/pork_spaghetti = 1,
@@ -86,7 +96,7 @@
/obj/item/storage/ration/fried_fish
name = "fried fish chunks ration"
desc = "Experience the crispy delight of fried fish chunks with this ration. Accompanied by an energy bar, tortillas, toasted corn kernels, and more, this ration provides a satisfying combination of flavors and textures."
-
+ emblem_icon_state = "emblem_fried_fish"
/obj/item/storage/ration/fried_fish/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/fried_fish = 1,
@@ -103,7 +113,7 @@
/obj/item/storage/ration/beef_strips
name = "beef strips in tomato sauce ration"
desc = "Savor the deliciousness of tender beef strips in a flavorful tomato sauce with this ration. Enjoy a chocolate pudding, white wheat snack bread, blackberry preserves, and peppermint candy rings as delightful accompaniments."
-
+ emblem_icon_state = "emblem_beef_strips"
/obj/item/storage/ration/beef_strips/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/beef_strips = 1,
@@ -120,7 +130,7 @@
/obj/item/storage/ration/chili_macaroni
name = "chili and macaroni ration"
desc = "Indulge in the comforting combination of chili and macaroni in this flavorful ration. Satisfy your taste buds with a mix of sweet and savory treats."
-
+ emblem_icon_state = "emblem_chili_macaroni"
/obj/item/storage/ration/chili_macaroni/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/chili_macaroni = 1,
@@ -137,7 +147,7 @@
/obj/item/storage/ration/chicken_wings_hot_sauce
name = "chicken wings in hot sauce ration"
desc = "Experience the bold and spicy flavors of chicken wings drenched in hot sauce. This ration also includes a mix of delightful snacks for a well-rounded meal."
-
+ emblem_icon_state = "emblem_chicken_wings_hot_sauce"
/obj/item/storage/ration/chicken_wings_hot_sauce/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/chicken_wings_hot_sauce = 1,
@@ -153,7 +163,7 @@
/obj/item/storage/ration/fish_stew
name = "fish stew ration"
desc = "Dive into the depths of flavor with this fish stew ration. Enjoy a hearty blend of seafood and vegetables, complemented by a selection of tasty accompaniments."
-
+ emblem_icon_state = "emblem_fish_stew"
/obj/item/storage/ration/fish_stew/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/fish_stew = 1,
@@ -170,7 +180,7 @@
/obj/item/storage/ration/lemon_pepper_chicken
name = "lemon pepper chicken ration"
desc = "A tasty Lemon Pepper Chicken ration that combines the flavors of fruit and meat. Perfect for a satisfying meal."
-
+ emblem_icon_state = "emblem_lemon_pepper_chicken"
/obj/item/storage/ration/lemon_pepper_chicken/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/lemon_pepper_chicken = 1,
@@ -186,7 +196,7 @@
/obj/item/storage/ration/sausage_peppers_onions
name = "sausage, peppers and onions ration"
desc = "Indulge in the delightful combination of juicy sausage, peppers, and onions in this hearty ration."
-
+ emblem_icon_state = "emblem_sausage_peppers_onions"
/obj/item/storage/ration/sausage_peppers_onions/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/sausage_peppers_onions = 1,
@@ -202,7 +212,7 @@
/obj/item/storage/ration/pork_dumplings_chili_sauce
name = "pork dumplings in chili sauce ration"
desc = "Savor the rich flavors of pork dumplings in a spicy chili sauce, accompanied by a variety of complementary snacks."
-
+ emblem_icon_state = "emblem_pork_dumplings_chili_sauce"
/obj/item/storage/ration/pork_dumplings_chili_sauce/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/dumplings_chili_sauce = 1,
@@ -218,7 +228,7 @@
/obj/item/storage/ration/battered_fish_sticks
name = "battered fish sticks ration"
desc = "Enjoy the crispy goodness of battered fish sticks, along with a selection of sides and a delectable dessert."
-
+ emblem_icon_state = "emblem_battered_fish_sticks"
/obj/item/storage/ration/battered_fish_sticks/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/battered_fish_sticks = 1,
@@ -234,7 +244,7 @@
/obj/item/storage/ration/assorted_salted_offal
name = "assorted salted offal ration"
desc = "An adventurous choice, this ration offers an assortment of salted offal, providing a unique culinary experience."
-
+ emblem_icon_state = "emblem_assorted_salted_offal"
/obj/item/storage/ration/assorted_salted_offal/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/assorted_salted_offal = 1,
@@ -250,7 +260,7 @@
/obj/item/storage/ration/maple_pork_sausage_patty
name = "maple pork sausage patty ration"
desc = "Start your day with a satisfying breakfast featuring a maple-infused pork sausage patty and a variety of treats."
-
+ emblem_icon_state = "emblem_maple_pork_sausage_patty"
/obj/item/storage/ration/maple_pork_sausage_patty/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/maple_pork_sausage_patty = 1,
@@ -267,7 +277,7 @@
/obj/item/storage/ration/pepper_jack_beef_patty
name = "jalapeno pepper jack beef patty ration"
desc = "Experience a flavorful fusion of jalapeno, pepper jack cheese, and beef in this grilled beef patty ration."
-
+ emblem_icon_state = "emblem_pepper_jack_beef_patty"
/obj/item/storage/ration/pepper_jack_beef_patty/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/pepper_jack_beef_patty = 1,
@@ -284,7 +294,7 @@
/obj/item/storage/ration/beef_goulash
name = "beef goulash ration"
desc = "Delight in the rich flavors of beef goulash, accompanied by a selection of sides and a sweet treat."
-
+ emblem_icon_state = "emblem_beef_goulash"
/obj/item/storage/ration/beef_goulash/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/beef_goulash = 1,
@@ -301,7 +311,7 @@
/obj/item/storage/ration/pepperoni_pizza_slice
name = "pepperoni pizza slice ration"
desc = "Indulge in the classic taste of pepperoni pizza with this ration, complete with sides and a refreshing beverage."
-
+ emblem_icon_state = "emblem_pepperoni_pizza_slice"
/obj/item/storage/ration/pepperoni_pizza_slice/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/pepperoni_pizza_slice = 1,
@@ -317,7 +327,7 @@
/obj/item/storage/ration/blackened_calamari
name = "blackened calamari in red sauce ration"
desc = "Enjoy the savory delight of blackened calamari served in a rich red sauce."
-
+ emblem_icon_state = "emblem_blackened_calamari"
/obj/item/storage/ration/blackened_calamari/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/blackened_calamari = 1,
@@ -334,7 +344,7 @@
/obj/item/storage/ration/elbow_macaroni
name = "elbow macaroni in tomato sauce ration"
desc = "Savor the comforting taste of elbow macaroni in a delicious tomato sauce."
-
+ emblem_icon_state = "emblem_elbow_macaroni"
/obj/item/storage/ration/elbow_macaroni/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/elbow_macaroni = 1,
@@ -351,7 +361,7 @@
/obj/item/storage/ration/cheese_pizza_slice
name = "cheese pizza slice ration"
desc = "Experience the timeless flavor of a classic cheese pizza slice."
-
+ emblem_icon_state = "emblem_cheese_pizza_slice"
/obj/item/storage/ration/cheese_pizza_slice/PopulateContents()
var/static/items_inside = list(
/obj/item/reagent_containers/food/snacks/ration/entree/cheese_pizza_slice = 1,
@@ -368,7 +378,7 @@
/obj/item/storage/ration/crayons
name = "military grade crayon ration"
desc = "Proven to increase kill count by atleast 1."
-
+ emblem_icon_state = "emblem_crayons"
/obj/item/storage/ration/crayons/PopulateContents()
var/static/items_inside = list(
/obj/item/toy/crayon/red = 1,
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index aff73626906a..c45911f98f4a 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -110,17 +110,6 @@
. += "It feels [descriptive]."
-/obj/item/tank/blob_act(obj/structure/blob/B)
- if(B && B.loc == loc)
- var/turf/location = get_turf(src)
- if(!location)
- qdel(src)
-
- if(air_contents)
- location.assume_air(air_contents)
-
- qdel(src)
-
/obj/item/tank/deconstruct(disassembled = TRUE)
if(!disassembled)
var/turf/T = get_turf(src)
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index 4095d159ea82..50f709dcd65f 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -377,7 +377,7 @@
//Todo : cache these.
/obj/item/reagent_containers/chemtank/worn_overlays(isinhands = FALSE) //apply chemcolor and level
- . = list()
+ . = ..()
//inhands + reagent_filling
if(!isinhands && reagents.total_volume)
var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "backpackmob-10")
diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm
index 3e4ab0d15de1..7c35ddd67d62 100644
--- a/code/game/objects/items/tools/screwdriver.dm
+++ b/code/game/objects/items/tools/screwdriver.dm
@@ -53,7 +53,7 @@
. += base_overlay
/obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file)
- . = list()
+ . = ..()
if(isinhands && random_color)
var/mutable_appearance/M = mutable_appearance(icon_file, "screwdriver_head")
M.appearance_flags = RESET_COLOR
diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm
index 2a821c7a8317..738cfa83c0f5 100644
--- a/code/game/objects/items/toy_mechs.dm
+++ b/code/game/objects/items/toy_mechs.dm
@@ -535,10 +535,10 @@
special_attack_type = SPECIAL_ATTACK_DAMAGE
special_attack_cry = "ROCKET BARRAGE"
-/obj/item/toy/prize/mauler
- name = "toy Mauler"
+/obj/item/toy/prize/touro
+ name = "toy Touro"
desc = "9/13"
- icon_state = "maulertoy"
+ icon_state = "tourotoy"
max_combat_health = 7 //500 integrity
special_attack_type = SPECIAL_ATTACK_DAMAGE
special_attack_cry = "BULLET STORM"
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index a76334a0b7ea..89eb9b0019e4 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -366,7 +366,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/cane
name = "cane"
- desc = "A cane used by a true gentleman. Or a clown."
+ desc = "A cane used by a true gentleman."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "cane"
item_state = "stick"
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 78cfa10a2e0b..e0e115b81d01 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -90,13 +90,6 @@
take_damage(hulk_damage(), BRUTE, "melee", 0, get_dir(src, user))
return TRUE
-/obj/blob_act(obj/structure/blob/B)
- if(isturf(loc))
- var/turf/T = loc
- if(T.intact && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE))
- return
- take_damage(400, BRUTE, "melee", 0, get_dir(src, B))
-
/obj/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal, and attack_slime
user.do_attack_animation(src)
user.changeNext_move(CLICK_CD_MELEE)
@@ -210,6 +203,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, "fire", 0)
if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF))
resistance_flags |= ON_FIRE
+ burning_particles = new(src, /particles/smoke/burning)
SSfire_burning.processing[src] = src
update_appearance()
return 1
@@ -226,6 +220,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
if(resistance_flags & ON_FIRE)
resistance_flags &= ~ON_FIRE
update_appearance()
+ QDEL_NULL(burning_particles)
SSfire_burning.processing -= src
///Called when the obj is hit by a tesla bolt.
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index d4ad3f0e679e..0ffeaa673b53 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -43,6 +43,8 @@
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace.
+ var/obj/effect/abstract/particle_holder/burning_particles
+
FASTDMM_PROP(\
pinned_vars = list("name", "dir")\
)
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index f59e29dd3b9a..1994ff330dd9 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -61,8 +61,6 @@
return FALSE
var/turf/T = get_turf(src)
var/area/A = get_area(src)
- if(!(A.area_flags & BLOBS_ALLOWED))
- return FALSE
if(!A.power_equip)
return FALSE
if(!T.virtual_level_trait(ZTRAIT_STATION))
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 3729f41af36e..3cd9cd13796e 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -293,3 +293,21 @@
new /obj/item/clothing/mask/breath(src)
for(var/i in 1 to 3)
new /obj/item/tank/internals/oxygen(src)
+
+/obj/structure/closet/crate/cyborg
+ name = "Cyborg Construction Crate"
+ desc = "A crate containing the parts to build a cyborg frame."
+ icon_state = "scicrate"
+
+/obj/structure/closet/crate/cyborg/PopulateContents()
+ . = ..()
+ new /obj/item/bodypart/l_arm/robot(src)
+ new /obj/item/bodypart/r_arm/robot(src)
+ new /obj/item/bodypart/leg/left/robot(src)
+ new /obj/item/bodypart/leg/right/robot(src)
+ new /obj/item/bodypart/chest/robot(src)
+ new /obj/item/bodypart/head/robot(src)
+ new /obj/item/robot_suit(src)
+ new /obj/item/stock_parts/cell/high(src)
+ for(var/i in 1 to 2)
+ new /obj/item/assembly/flash/handheld(src)
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
index f6de885caf77..24c0f71d322c 100644
--- a/code/game/objects/structures/fireaxe.dm
+++ b/code/game/objects/structures/fireaxe.dm
@@ -98,12 +98,6 @@
new /obj/item/stack/sheet/metal(loc, 2)
qdel(src)
-/obj/structure/fireaxecabinet/blob_act(obj/structure/blob/B)
- if(fireaxe)
- fireaxe.forceMove(loc)
- fireaxe = null
- qdel(src)
-
/obj/structure/fireaxecabinet/attack_hand(mob/user)
. = ..()
if(.)
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 0fca2bcca6ee..1698f90ec7cd 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -1056,3 +1056,39 @@
T.air.adjust_moles(GAS_CO2, -amt)
T.atmos_spawn_air("o2=[amt];TEMP=293.15")
lastcycle = world.time
+
+/obj/structure/fluff/steam_vent
+ name = "steam vent"
+ desc = "A outlet for steam, usually for water coming in contact with steam pipes."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "steamvent"
+ deconstructible = FALSE
+ layer = GAS_PUMP_LAYER
+
+ var/particle_to_spawn = /particles/smoke/steam/vent
+ var/obj/effect/particle_holder/part_hold
+
+/obj/structure/fluff/steam_vent/Initialize()
+ . = ..()
+ part_hold = new(get_turf(src))
+ part_hold.layer = EDGED_TURF_LAYER
+ part_hold.particles = new particle_to_spawn()
+ underlays.Cut()
+
+/obj/structure/fluff/steam_vent/Destroy()
+ . = ..()
+ QDEL_NULL(part_hold)
+
+/obj/structure/fluff/steam_vent/low
+ particle_to_spawn = /particles/smoke/steam/vent/low
+
+/obj/structure/fluff/steam_vent/high
+ particle_to_spawn = /particles/smoke/steam/vent/high
+
+/obj/effect/particle_holder
+ name = ""
+ anchored = TRUE
+ mouse_opacity = 0
+
+/obj/effect/particle_emitter/Initialize(mapload, time)
+ . = ..()
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 5815bcc475e3..30999b58a620 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -30,9 +30,6 @@
if(LAT != src)
QDEL_IN(LAT, 0)
-/obj/structure/lattice/blob_act(obj/structure/blob/B)
- return
-
/obj/structure/lattice/attackby(obj/item/C, mob/user, params)
if(resistance_flags & INDESTRUCTIBLE)
return
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 0df440340eed..5f3e2914bc47 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -79,9 +79,6 @@ FLOOR SAFES
to_chat(user, "You can't put [I] into the safe while it is closed!")
return
-/obj/structure/safe/blob_act(obj/structure/blob/B)
- return
-
/obj/structure/safe/ex_act(severity, target)
if(((severity == 2 && target == src) || severity == 1) && explosion_count < BROKEN_THRESHOLD)
explosion_count++
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 49c3823cf1ce..5420cc06b490 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -32,6 +32,9 @@
hitsound_type = PROJECTILE_HITSOUND_GLASS
+ /// If some inconsiderate jerk has had their blood spilled on this window, thus making it cleanable
+ var/bloodied = FALSE
+
/obj/structure/window/examine(mob/user)
. = ..()
if(flags_1 & NODECONSTRUCT_1)
diff --git a/code/game/say.dm b/code/game/say.dm
index 2d53eea65e75..cac8bafe5365 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -72,7 +72,7 @@ GLOBAL_LIST_INIT(freqcolor, list())
namepart = "[known_name]"
else
var/mob/living/carbon/human/human_narrator = reliable_narrator
- namepart = "[human_narrator.get_generic_name(prefixed = TRUE, lowercase = FALSE)]"
+ namepart = "[human_narrator.get_generic_name(prefixed = TRUE, lowercase = TRUE)]"
//End name span.
var/endspanpart = ""
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index 7be9fb0e21d6..f93faeb8297d 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -117,13 +117,6 @@
if(!density)
..()
-
-/turf/closed/wall/blob_act(obj/structure/blob/B)
- if(prob(50))
- dismantle_wall()
- else
- add_dent(WALL_DENT_HIT)
-
/turf/closed/wall/mech_melee_attack(obj/mecha/M)
M.do_attack_animation(src)
switch(M.damtype)
diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm
index d74edb27a658..f7eac409b836 100644
--- a/code/game/turfs/open/floor.dm
+++ b/code/game/turfs/open/floor.dm
@@ -78,9 +78,6 @@
for(var/obj/structure/A in contents)
return 1
-/turf/open/floor/blob_act(obj/structure/blob/B)
- return
-
/turf/open/floor/update_icon()
. = ..()
update_visuals()
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index eb2132940bd9..90fd6610721c 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -220,19 +220,13 @@
/turf/open/lava/smooth/airless
initial_gas_mix = AIRLESS_ATMOS
-/particles/lava
- width = 700
- height = 700
- count = 1000
- spawning = 1
- lifespan = 3 SECONDS
- fade = 2 SECONDS
- position = generator("circle", 16, 24, NORMAL_RAND)
- drift = generator("vector", list(-0.2, -0.2), list(0.2, 0.2))
- velocity = generator("circle", -6, 6, NORMAL_RAND)
- friction = 0.15
- gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK)
- color_change = 0.125
+/obj/effect/particle_holder
+ name = ""
+ anchored = TRUE
+ mouse_opacity = 0
+
+/obj/effect/particle_emitter/Initialize(mapload, time)
+ . = ..()
/obj/effect/particle_emitter/lava
- particles = new/particles/lava
+ particles = new/particles/embers/lava
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 82e75d63b30e..d05fd3ee3b98 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -153,7 +153,6 @@
body += "Make Robot"
body += "Make Alien"
body += "Make Slime"
- body += "Make Blob"
//Simple Animals
if(isanimal(M))
diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm
index bde0a7676fac..28646666b46a 100644
--- a/code/modules/admin/sql_ban_system.dm
+++ b/code/modules/admin/sql_ban_system.dm
@@ -284,7 +284,7 @@
output += ""
var/list/long_job_lists = list("Service" = GLOB.service_positions,
"Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE),
- "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB,
+ "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN,
ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST,
ROLE_DEVIL, ROLE_INTERNAL_AFFAIRS, ROLE_MALF,
ROLE_MONKEY, ROLE_NINJA, ROLE_OPERATIVE,
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 2e4b1c60acdd..d17583060942 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -117,13 +117,6 @@
else
message_admins("[key_name_admin(usr)] tried to create a death squad. Unfortunately, there were not enough candidates available.")
log_admin("[key_name(usr)] failed to create a death squad.")
- if("blob")
- var/strength = input("Set Blob Resource Gain Rate","Set Resource Rate",1) as num|null
- if(!strength)
- return
- message_admins("[key_name(usr)] spawned a blob with base resource gain [strength].")
- log_admin("[key_name(usr)] spawned a blob with base resource gain [strength].")
- new/datum/round_event/ghost_role/blob(TRUE, strength)
if("centcom")
message_admins("[key_name(usr)] is creating a response team...")
if(src.makeEmergencyresponseteam())
@@ -1077,18 +1070,6 @@
usr.client.cmd_admin_slimeize(H)
- else if(href_list["makeblob"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makeblob"])
- if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE)
- return
-
- usr.client.cmd_admin_blobize(H)
-
-
else if(href_list["makerobot"])
if(!check_rights(R_SPAWN))
return
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 9a226bbcb617..2c269ce1ee9a 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -52,21 +52,6 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
else
alert("Invalid mob")
-/client/proc/cmd_admin_blobize(mob/M in GLOB.mob_list)
- set category = "Event.Fun"
- set name = "Make Blob"
-
- if(!SSticker.HasRoundStarted())
- alert("Wait until the game starts")
- return
- if(ishuman(M))
- log_admin("[key_name(src)] has blobized [M.key].")
- var/mob/living/carbon/human/H = M
- H.become_overmind()
- else
- alert("Invalid mob")
-
-
/client/proc/cmd_admin_animalize(mob/M in GLOB.mob_list)
set category = "Event.Fun"
set name = "Make Simple Animal"
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 028c9729131a..abdecf91de60 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -17,7 +17,6 @@
Make Traitors
Make Changelings
Make Cult
- Make Blob
Make Wizard (Requires Ghosts)
Make Nuke Team (Requires Ghosts)
Make Response Team (Requires Ghosts)
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
deleted file mode 100644
index 80acd5cefe1f..000000000000
--- a/code/modules/antagonists/blob/blob.dm
+++ /dev/null
@@ -1,67 +0,0 @@
-/datum/antagonist/blob
- name = "Blob"
- roundend_category = "blobs"
- antagpanel_category = "Blob"
- show_to_ghosts = TRUE
- job_rank = ROLE_BLOB
-
- var/datum/action/innate/blobpop/pop_action
- var/starting_points_human_blob = 60
- var/point_rate_human_blob = 2
-
-/datum/antagonist/blob/roundend_report()
- var/basic_report = ..()
- //Display max blobpoints for blebs that lost
- if(isovermind(owner.current)) //embarrasing if not
- var/mob/camera/blob/overmind = owner.current
- if(!overmind.victory_in_progress) //if it won this doesn't really matter
- var/point_report = "
[owner.name] took over [overmind.max_count] tiles at the height of its growth."
- return basic_report+point_report
- return basic_report
-
-/datum/antagonist/blob/greet()
- if(!isovermind(owner.current))
- to_chat(owner,"You feel bloated.")
-
-/datum/antagonist/blob/on_gain()
- create_objectives()
- . = ..()
-
-/datum/antagonist/blob/proc/create_objectives()
- var/datum/objective/blob_takeover/main = new
- main.owner = owner
- objectives += main
-
-/datum/antagonist/blob/apply_innate_effects(mob/living/mob_override)
- if(!isovermind(owner.current))
- if(!pop_action)
- pop_action = new
- pop_action.Grant(owner.current)
-
-/datum/objective/blob_takeover
- explanation_text = "Reach critical mass!"
-
-//Non-overminds get this on blob antag assignment
-/datum/action/innate/blobpop
- name = "Pop"
- desc = "Unleash the blob"
- icon_icon = 'icons/mob/blob.dmi'
- button_icon_state = "blob"
-
-/datum/action/innate/blobpop/Activate()
- var/mob/old_body = owner
- var/datum/antagonist/blob/blobtag = owner.mind.has_antag_datum(/datum/antagonist/blob)
- if(!blobtag)
- Remove()
- return
- var/mob/camera/blob/B = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob)
- owner.mind.transfer_to(B)
- old_body.gib()
- B.place_blob_core(blobtag.point_rate_human_blob, pop_override = TRUE)
-
-/datum/antagonist/blob/antag_listing_status()
- . = ..()
- if(owner && owner.current)
- var/mob/camera/blob/B = owner.current
- if(istype(B))
- . += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm
deleted file mode 100644
index 639017e100f3..000000000000
--- a/code/modules/antagonists/blob/blob_mobs.dm
+++ /dev/null
@@ -1,333 +0,0 @@
-
-////////////////
-// BASE TYPE //
-////////////////
-
-//Do not spawn
-/mob/living/simple_animal/hostile/blob
- icon = 'icons/mob/blob.dmi'
- pass_flags = PASSBLOB
- faction = list(ROLE_BLOB)
- bubble_icon = "blob"
- speak_emote = null //so we use verb_yell/verb_say/etc
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- maxbodytemp = 360
- unique_name = 1
- a_intent = INTENT_HARM
- see_in_dark = 8
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- initial_language_holder = /datum/language_holder/empty
- var/mob/camera/blob/overmind = null
- var/obj/structure/blob/factory/factory = null
- var/independent = FALSE
-
-/mob/living/simple_animal/hostile/blob/update_icons()
- if(overmind)
- add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
- else
- remove_atom_colour(FIXED_COLOUR_PRIORITY)
-
-/mob/living/simple_animal/hostile/blob/Initialize()
- . = ..()
- if(!independent) //no pulling people deep into the blob
- remove_verb(src, /mob/living/verb/pulled)
- else
- pass_flags &= ~PASSBLOB
-
-/mob/living/simple_animal/hostile/blob/Destroy()
- if(overmind)
- overmind.blob_mobs -= src
- return ..()
-
-/mob/living/simple_animal/hostile/blob/get_status_tab_items()
- . = ..()
- if(overmind)
- . += "Blobs to Win: [overmind.blobs_legit.len]/[overmind.blobwincount]"
-
-/mob/living/simple_animal/hostile/blob/blob_act(obj/structure/blob/B)
- if(stat != DEAD && health < maxHealth)
- for(var/i in 1 to 2)
- var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
- if(overmind)
- H.color = overmind.blobstrain.complementary_color
- else
- H.color = "#000000"
- adjustHealth(-maxHealth*0.0125)
-
-/mob/living/simple_animal/hostile/blob/fire_act(exposed_temperature, exposed_volume)
- ..()
- if(exposed_temperature)
- adjustFireLoss(clamp(0.01 * exposed_temperature, 1, 5))
- else
- adjustFireLoss(5)
-
-/mob/living/simple_animal/hostile/blob/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- if(istype(mover, /obj/structure/blob))
- return TRUE
-
-/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
- for(var/obj/structure/blob/B in range(1, src))
- return 1
- return ..()
-
-/mob/living/simple_animal/hostile/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
- if(!overmind)
- ..()
- return
- var/spanned_message = say_quote(message)
- var/rendered = "\[Blob Telepathy\] [real_name] [spanned_message]"
- for(var/M in GLOB.mob_list)
- if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
- to_chat(M, rendered)
- if(isobserver(M))
- var/link = FOLLOW_LINK(M, src)
- to_chat(M, "[link] [rendered]")
-
-////////////////
-// BLOB SPORE //
-////////////////
-
-/mob/living/simple_animal/hostile/blob/blobspore
- name = "blob spore"
- desc = "A floating, fragile spore."
- icon_state = "blobpod"
- icon_living = "blobpod"
- health_doll_icon = "blobpod"
- health = 30
- maxHealth = 30
- verb_say = "psychically pulses"
- verb_ask = "psychically probes"
- verb_exclaim = "psychically yells"
- verb_yell = "psychically screams"
- melee_damage_lower = 2
- melee_damage_upper = 4
- obj_damage = 20
- environment_smash = ENVIRONMENT_SMASH_STRUCTURES
- attack_verb_continuous = "hits"
- attack_verb_simple = "hit"
- attack_sound = 'sound/weapons/genhit1.ogg'
- movement_type = FLYING
- del_on_death = TRUE
- deathmessage = "explodes into a cloud of gas!"
- gold_core_spawnable = HOSTILE_SPAWN
- var/death_cloud_size = 1 //size of cloud produced from a dying spore
- var/mob/living/carbon/human/oldguy
- var/is_zombie = FALSE
-
-/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, obj/structure/blob/factory/linked_node)
- if(!istype(linked_node))
- return INITIALIZE_HINT_QDEL
- factory = linked_node
- factory.spores += src
- . = ..()
- if(linked_node.overmind && istype(linked_node.overmind.blobstrain, /datum/blobstrain/reagent/distributed_neurons) && !istype(src, /mob/living/simple_animal/hostile/blob/blobspore/weak))
- notify_ghosts("A controllable spore has been created in \the [get_area(src)].", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Sentient Spore Created")
-
-/mob/living/simple_animal/hostile/blob/blobspore/Life()
- if(!is_zombie && isturf(src.loc))
- for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
- if(H.stat == DEAD)
- Zombify(H)
- break
- if(factory && z != factory.z)
- death()
- ..()
-
-/mob/living/simple_animal/hostile/blob/blobspore/attack_ghost(mob/user)
- . = ..()
- if(.)
- return
- humanize_pod(user)
-
-/mob/living/simple_animal/hostile/blob/blobspore/proc/humanize_pod(mob/user)
- if((!overmind || istype(src, /mob/living/simple_animal/hostile/blob/blobspore/weak) || !istype(overmind.blobstrain, /datum/blobstrain/reagent/distributed_neurons)) && !is_zombie)
- return
- if(key || stat)
- return
- var/pod_ask = alert("Become a blob spore?", "Are you bulbous enough?", "Yes", "No")
- if(pod_ask == "No" || !src || QDELETED(src))
- return
- if(key)
- to_chat(user, "Someone else already took this spore!")
- return
- key = user.key
- log_game("[key_name(src)] took control of [name].")
-
-/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H)
- is_zombie = 1
- if(H.wear_suit)
- var/obj/item/clothing/suit/armor/A = H.wear_suit
- maxHealth += A.armor.melee //That zombie's got armor, I want armor!
- maxHealth += 40
- health = maxHealth
- name = "blob zombie"
- desc = "A shambling corpse animated by the blob."
- mob_biotypes |= MOB_HUMANOID
- melee_damage_lower += 8
- melee_damage_upper += 11
- movement_type = GROUND
- death_cloud_size = 0
- icon = H.icon
- icon_state = "zombie"
- H.hairstyle = null
- H.update_hair()
- H.forceMove(src)
- oldguy = H
- update_icons()
- visible_message("The corpse of [H.name] suddenly rises!")
- if(!key)
- notify_ghosts("\A [src] has been created in \the [get_area(src)].", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Blob Zombie Created")
-
-/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
- // On death, create a small smoke of harmful gas (s-Acid)
- var/datum/effect_system/smoke_spread/chem/S = new
- var/turf/location = get_turf(src)
-
- // Create the reagents to put into the air
- create_reagents(10)
-
-
-
- if(overmind && overmind.blobstrain)
- overmind.blobstrain.on_sporedeath(src)
- else
- reagents.add_reagent(/datum/reagent/toxin/spore, 10)
-
- // Attach the smoke spreader and setup/start it.
- S.attach(location)
- S.set_up(reagents, death_cloud_size, location, silent = TRUE)
- S.start()
- if(factory)
- factory.spore_delay = world.time + factory.spore_cooldown //put the factory on cooldown
-
- ..()
-
-/mob/living/simple_animal/hostile/blob/blobspore/Destroy()
- if(factory)
- factory.spores -= src
- factory = null
- if(oldguy)
- oldguy.forceMove(get_turf(src))
- oldguy = null
- return ..()
-
-/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
- if(overmind)
- add_atom_colour(overmind.blobstrain.complementary_color, FIXED_COLOUR_PRIORITY)
- else
- remove_atom_colour(FIXED_COLOUR_PRIORITY)
- if(is_zombie)
- copy_overlays(oldguy, TRUE)
- var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
- if(overmind)
- blob_head_overlay.color = overmind.blobstrain.complementary_color
- color = initial(color)//looks better.
- add_overlay(blob_head_overlay)
-
-/mob/living/simple_animal/hostile/blob/blobspore/weak
- name = "fragile blob spore"
- health = 15
- maxHealth = 15
- melee_damage_lower = 1
- melee_damage_upper = 2
- death_cloud_size = 0
-
-/////////////////
-// BLOBBERNAUT //
-/////////////////
-
-/mob/living/simple_animal/hostile/blob/blobbernaut
- name = "blobbernaut"
- desc = "A hulking, mobile chunk of blobmass."
- icon_state = "blobbernaut"
- icon_living = "blobbernaut"
- icon_dead = "blobbernaut_dead"
- health = 200
- maxHealth = 200
- damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
- melee_damage_lower = 20
- melee_damage_upper = 20
- obj_damage = 60
- attack_verb_continuous = "slams"
- attack_verb_simple = "slam"
- attack_sound = 'sound/effects/blobattack.ogg'
- verb_say = "gurgles"
- verb_ask = "demands"
- verb_exclaim = "roars"
- verb_yell = "bellows"
- force_threshold = 10
- pressure_resistance = 50
- mob_size = MOB_SIZE_LARGE
- hud_type = /datum/hud/blobbernaut
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
- if(..())
- var/list/blobs_in_area = range(2, src)
- if(independent)
- return // strong independent blobbernaut that don't need no blob
- var/damagesources = 0
- if(!(locate(/obj/structure/blob) in blobs_in_area))
- damagesources++
- if(!factory)
- damagesources++
- else
- if(locate(/obj/structure/blob/core) in blobs_in_area)
- adjustHealth(-maxHealth*0.1)
- var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
- if(overmind)
- H.color = overmind.blobstrain.complementary_color
- else
- H.color = "#000000"
- if(locate(/obj/structure/blob/node) in blobs_in_area)
- adjustHealth(-maxHealth*0.05)
- var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src))
- if(overmind)
- H.color = overmind.blobstrain.complementary_color
- else
- H.color = "#000000"
- if(damagesources)
- for(var/i in 1 to damagesources)
- adjustHealth(maxHealth*0.025) //take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both
- var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01)
- I.appearance_flags = RESET_COLOR
- if(overmind)
- I.color = overmind.blobstrain.complementary_color
- flick_overlay_view(I, src, 8)
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
- . = ..()
- if(updating_health)
- update_health_hud()
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/update_health_hud()
- if(hud_used)
- hud_used.healths.maptext = "[round((health / maxHealth) * 100, 0.5)]%
"
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
- . = ..()
- if(. && isliving(target) && overmind)
- overmind.blobstrain.blobbernaut_attack(target, src)
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/update_icons()
- ..()
- if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
- melee_damage_lower = 4
- melee_damage_upper = 4
- attack_verb_continuous = overmind.blobstrain.blobbernaut_message
- else
- melee_damage_lower = initial(melee_damage_lower)
- melee_damage_upper = initial(melee_damage_upper)
- attack_verb_continuous = initial(attack_verb_continuous)
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
- ..(gibbed)
- if(factory)
- factory.naut = null //remove this naut from its factory
- factory.max_integrity = initial(factory.max_integrity)
- flick("blobbernaut_death", src)
-
-/mob/living/simple_animal/hostile/blob/blobbernaut/independent
- independent = TRUE
- gold_core_spawnable = HOSTILE_SPAWN
diff --git a/code/modules/antagonists/blob/blob_report.dm b/code/modules/antagonists/blob/blob_report.dm
deleted file mode 100644
index 10e77fdb3ee4..000000000000
--- a/code/modules/antagonists/blob/blob_report.dm
+++ /dev/null
@@ -1,72 +0,0 @@
-/datum/station_state
- var/floor = 0
- var/wall = 0
- var/r_wall = 0
- var/window = 0
- var/door = 0
- var/grille = 0
- var/mach = 0
-
-/datum/station_state/proc/count()
- floor = 0
- wall = 0
- r_wall = 0
- window = 0
- door = 0
- grille = 0
- mach = 0
- for(var/Z in SSmapping.virtual_levels_by_trait(ZTRAIT_STATION))
- for(var/turf/T in block(locate(1,1,Z), locate(world.maxx,world.maxy,Z)))
- // don't count shuttles since they may have just left
- if(istype(T.loc, /area/shuttle))
- continue
-
- if(isfloorturf(T))
- var/turf/open/floor/TF = T
- if(!(TF.burnt))
- floor += 12
- else
- floor += 1
-
- if(iswallturf(T))
- var/turf/closed/wall/TW = T
- if(TW.intact)
- wall += 2
- else
- wall += 1
-
- if(istype(T, /turf/closed/wall/r_wall))
- var/turf/closed/wall/r_wall/TRW = T
- if(TRW.intact)
- r_wall += 2
- else
- r_wall += 1
-
-
- for(var/obj/O in T.contents)
- if(istype(O, /obj/structure/window))
- window += 1
- else if(istype(O, /obj/structure/grille))
- var/obj/structure/grille/GR = O
- if(!GR.broken)
- grille += 1
- else if(istype(O, /obj/machinery/door))
- door += 1
- else if(ismachinery(O))
- mach += 1
- CHECK_TICK
- CHECK_TICK
- CHECK_TICK
-
-/datum/station_state/proc/score(datum/station_state/result)
- if(!result)
- return 0
- var/output = 0
- output += (result.floor / max(floor,1))
- output += (result.r_wall/ max(r_wall,1))
- output += (result.wall / max(wall,1))
- output += (result.window / max(window,1))
- output += (result.door / max(door,1))
- output += (result.grille / max(grille,1))
- output += (result.mach / max(mach,1))
- return (output/7)
diff --git a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm
deleted file mode 100644
index 29060afd8f6a..000000000000
--- a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm
+++ /dev/null
@@ -1,81 +0,0 @@
-GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/blobstrain/reagent, /datum/blobstrain/multiplex))
-
-/datum/blobstrain
- var/name
- var/description
- var/color = "#000000"
- var/complementary_color = "#000000" //a color that's complementary to the normal blob color
- var/shortdesc = null //just damage and on_mob effects, doesn't include special, blob-tile only effects
- var/effectdesc = null //any long, blob-tile specific effects
- var/analyzerdescdamage = "Unknown. Report this bug to a coder, or just adminhelp."
- var/analyzerdesceffect = "N/A"
- var/blobbernaut_message = "slams" //blobbernaut attack verb
- var/message = "The blob strikes you" //message sent to any mob hit by the blob
- var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt
- var/core_regen = 2
- var/resource_delay = 0
- var/point_rate = 2
- var/mob/camera/blob/overmind
-
-/datum/blobstrain/New(mob/camera/blob/new_overmind)
- if (!istype(new_overmind))
- stack_trace("blobstrain created without overmind")
- overmind = new_overmind
-
-/datum/blobstrain/Destroy()
- overmind = null
- return ..()
-
-/datum/blobstrain/proc/on_gain()
- overmind.color = complementary_color
- for(var/BL in GLOB.blobs)
- var/obj/structure/blob/B = BL
- B.update_appearance()
- for(var/BLO in overmind.blob_mobs)
- var/mob/living/simple_animal/hostile/blob/BM = BLO
- BM.update_icons() //If it's getting a new strain, tell it what it does!
- to_chat(BM, "Your overmind's blob strain is now: [name]!")
- to_chat(BM, "The [name] strain [shortdesc ? "[shortdesc]" : "[description]"]")
-
-/datum/blobstrain/proc/on_lose()
-
-/datum/blobstrain/proc/on_sporedeath(mob/living/spore)
-
-/datum/blobstrain/proc/send_message(mob/living/M)
- var/totalmessage = message
- if(message_living && !issilicon(M))
- totalmessage += message_living
- totalmessage += "!"
- to_chat(M, "[totalmessage]")
-
-/datum/blobstrain/proc/core_process()
- if(resource_delay <= world.time)
- resource_delay = world.time + 10 // 1 second
- overmind.add_points(point_rate)
- overmind.blob_core.obj_integrity = min(overmind.blob_core.max_integrity, overmind.blob_core.obj_integrity+core_regen)
-
-/datum/blobstrain/proc/attack_living(mob/living/L, list/nearby_blobs) // When the blob attacks people
- send_message(L)
-
-/datum/blobstrain/proc/blobbernaut_attack(mob/living/L, blobbernaut) // When this blob's blobbernaut attacks people
-
-/datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
- return coefficient*damage
-
-/datum/blobstrain/proc/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
- return
-
-/datum/blobstrain/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
- return
-
-/datum/blobstrain/proc/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
- return 1 //return 0 to ignore damage
-
-/datum/blobstrain/proc/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
- return
-
-/datum/blobstrain/proc/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
- return
-
-/datum/blobstrain/proc/examine(mob/user)
- return list("Progress to Critical Mass: [overmind.blobs_legit.len]/[overmind.blobwincount].")
diff --git a/code/modules/antagonists/blob/blobstrains/_reagent.dm b/code/modules/antagonists/blob/blobstrains/_reagent.dm
deleted file mode 100644
index 3d210188d9b6..000000000000
--- a/code/modules/antagonists/blob/blobstrains/_reagent.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones
- var/datum/reagent/reagent
-
-/datum/blobstrain/reagent/New(mob/camera/blob/new_overmind)
- . = ..()
- reagent = new reagent()
-
-
-/datum/blobstrain/reagent/attack_living(mob/living/L)
- var/mob_protection = L.get_permeability_protection()
- reagent.expose_mob(L, VAPOR, 25, 1, mob_protection, overmind)
- send_message(L)
-
-/datum/blobstrain/reagent/blobbernaut_attack(mob/living/L)
- var/mob_protection = L.get_permeability_protection()
- reagent.expose_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
-
-/datum/blobstrain/reagent/on_sporedeath(mob/living/spore)
- spore.reagents.add_reagent(reagent.type, 10)
-
-// These can only be applied by blobs. They are what (reagent) blobs are made out of.
-/datum/reagent/blob
- name = "Unknown"
- description = "shouldn't exist and you should adminhelp immediately."
- color = "#FFFFFF"
- taste_description = "bad code and slime"
- can_synth = FALSE
-
-
-/datum/reagent/blob/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
- return 0 //the dead, and blob mobs, don't cause reactions
- return round(reac_volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume.
diff --git a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm
deleted file mode 100644
index d34ed8041ac7..000000000000
--- a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-
-//sets you on fire, does burn damage, explodes into flame when burnt, weak to water
-/datum/blobstrain/reagent/blazing_oil
- name = "Blazing Oil"
- description = "will do medium burn damage and set targets on fire."
- effectdesc = "will also release bursts of flame when burnt, but takes damage from water."
- analyzerdescdamage = "Does medium burn damage and sets targets on fire."
- analyzerdesceffect = "Releases fire when burnt, but takes damage from water and other extinguishing liquids."
- color = "#B68D00"
- complementary_color = "#BE5532"
- blobbernaut_message = "splashes"
- message = "The blob splashes you with burning oil"
- message_living = ", and you feel your skin char and melt"
- reagent = /datum/reagent/blob/blazing_oil
-
-/datum/blobstrain/reagent/blazing_oil/extinguish_reaction(obj/structure/blob/B)
- B.take_damage(1.5, BURN, "energy")
-
-/datum/blobstrain/reagent/blazing_oil/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BURN && damage_flag != "energy")
- for(var/turf/open/T in range(1, B))
- var/obj/structure/blob/C = locate() in T
- if(!(C && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) && prob(80))
- new /obj/effect/hotspot(T)
- if(damage_flag == "fire")
- return 0
- return ..()
-
-/datum/reagent/blob/blazing_oil
- name = "Blazing Oil"
- taste_description = "burning oil"
- color = "#B68D00"
-
-/datum/reagent/blob/blazing_oil/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.adjust_fire_stacks(round(reac_volume/10))
- M.IgniteMob()
- if(M)
- M.apply_damage(0.8*reac_volume, BURN)
- if(iscarbon(M))
- M.emote("scream")
diff --git a/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm b/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm
deleted file mode 100644
index 6f86417e8d42..000000000000
--- a/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-//does brute, burn, and toxin damage, and cools targets down
-/datum/blobstrain/reagent/cryogenic_poison
- name = "Cryogenic Poison"
- description = "will inject targets with a freezing poison that does high damage over time."
- analyzerdescdamage = "Injects targets with a freezing poison that will gradually solidify the target's internal organs."
- color = "#8BA6E9"
- complementary_color = "#7D6EB4"
- blobbernaut_message = "injects"
- message = "The blob stabs you"
- message_living = ", and you feel like your insides are solidifying"
- reagent = /datum/reagent/blob/cryogenic_poison
-
-/datum/reagent/blob/cryogenic_poison
- name = "Cryogenic Poison"
- description = "will inject targets with a freezing poison that does high damage over time."
- color = "#8BA6E9"
- taste_description = "brain freeze"
-
-/datum/reagent/blob/cryogenic_poison/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- if(M.reagents)
- M.reagents.add_reagent(/datum/reagent/consumable/frostoil, 0.3*reac_volume)
- M.reagents.add_reagent(/datum/reagent/consumable/ice, 0.3*reac_volume)
- M.reagents.add_reagent(/datum/reagent/blob/cryogenic_poison, 0.3*reac_volume)
- M.apply_damage(0.2*reac_volume, BRUTE)
-
-/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
- M.adjustBruteLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
- M.adjustFireLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
- M.adjustToxLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
- . = 1
- ..()
diff --git a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm
deleted file mode 100644
index c39f2ce3d37a..000000000000
--- a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm
+++ /dev/null
@@ -1,63 +0,0 @@
-#define DEBRIS_DENSITY (length(core.contents) / length(overmind.blobs_legit)) // items per blob
-
-// Accumulates junk liberally
-/datum/blobstrain/debris_devourer
- name = "Debris Devourer"
- description = "will launch accumulated debris into targets."
- analyzerdescdamage = "Does medium brute damage and may grab onto melee weapons."
- analyzerdesceffect = "Devours loose items left on the floor, and releases them when attacking or attacked."
- color = "#8B1000"
- complementary_color = "#00558B"
- blobbernaut_message = "blasts"
- message = "The blob blasts you"
-
-
-/datum/blobstrain/debris_devourer/attack_living(mob/living/L, list/nearby_blobs)
- send_message(L)
- for (var/obj/structure/blob/blob in nearby_blobs)
- debris_attack(L, blob)
-
-/datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore)
- for(var/i in 1 to 3)
- var/obj/item/I = locate() in overmind.blob_core
- if (I && !QDELETED(I))
- I.forceMove(get_turf(spore))
- I.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 3, 5)
-
-/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
- for (var/obj/item/I in T)
- I.forceMove(overmind.blob_core)
-
-/datum/blobstrain/debris_devourer/proc/debris_attack(mob/living/L, source)
- var/obj/structure/blob/core/core = overmind.blob_core
- if (prob(20 * DEBRIS_DENSITY)) // Pretend the items are spread through the blob and its mobs and not in the core.
- var/obj/item/I = locate() in core
- if (I && !QDELETED(I))
- I.forceMove(get_turf(source))
- I.throw_at(L, 2, 5)
-
-/datum/blobstrain/debris_devourer/blobbernaut_attack(mob/living/L, mob/living/blobbernaut) // When this blob's blobbernaut attacks people
- debris_attack(L,blobbernaut)
-
-/datum/blobstrain/debris_devourer/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
- var/obj/structure/blob/core/core = overmind.blob_core
- return round(max((coefficient*damage)-min(coefficient*DEBRIS_DENSITY, 10), 0)) // reduce damage taken by items per blob, up to 10
-
-/datum/blobstrain/debris_devourer/examine(mob/user)
- . = ..()
- var/obj/structure/blob/core/core = overmind.blob_core
- if (isobserver(user))
- . += "Absorbed debris is currently reducing incoming damage by [round(max(min(DEBRIS_DENSITY, 10),0))]"
- else
- switch (round(max(min(DEBRIS_DENSITY, 10),0)))
- if (0)
- . += "There is not currently enough absorbed debris to reduce damage."
- if (1 to 3)
- . += "Absorbed debris is currently reducing incoming damage by a very low amount." // these roughly correspond with force description strings
- if (4 to 7)
- . += "Absorbed debris is currently reducing incoming damage by a low amount."
- if (8 to 10)
- . += "Absorbed debris is currently reducing incoming damage by a medium amount."
-
-
-#undef DEBRIS_DENSITY
diff --git a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm
deleted file mode 100644
index 80aa0e3d8db2..000000000000
--- a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-//kills unconscious targets and turns them into blob zombies, produces fragile spores when killed. Spore produced by factories are sentient.
-/datum/blobstrain/reagent/distributed_neurons
- name = "Distributed Neurons"
- description = "will do very low toxin damage and turns unconscious targets into blob zombies."
- effectdesc = "will also produce fragile spores when killed. Spores produced by factories are sentient."
- shortdesc = "will do very low toxin damage and will turn unconscious targets into blob zombies for additional resources(for your overmind). Spores produced by factories are sentient."
- analyzerdescdamage = "Does very low toxin damage and kills unconscious humans, turning them into blob zombies."
- analyzerdesceffect = "Produces spores when killed. Spores produced by factories are sentient."
- color = "#E88D5D"
- complementary_color = "#823ABB"
- message_living = ", and you feel tired"
- reagent = /datum/reagent/blob/distributed_neurons
-
-/datum/blobstrain/reagent/distributed_neurons/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage <= 20 && B.obj_integrity - damage <= 0 && prob(15)) //if the cause isn't fire or a bomb, the damage is less than 21, we're going to die from that damage, 15% chance of a shitty spore.
- B.visible_message("A spore floats free of the blob!")
- var/mob/living/simple_animal/hostile/blob/blobspore/weak/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(B.loc)
- BS.overmind = B.overmind
- BS.update_icons()
- B.overmind.blob_mobs.Add(BS)
- return ..()
-
-/datum/reagent/blob/distributed_neurons
- name = "Distributed Neurons"
- color = "#E88D5D"
-
-/datum/reagent/blob/distributed_neurons/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.6*reac_volume, TOX)
- if(O && ishuman(M) && (M.stat == UNCONSCIOUS || M.stat == HARD_CRIT))
- M.death() //sleeping in a fight? bad plan.
- var/points = rand(5, 10)
- var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore/weak(get_turf(M))
- BS.overmind = O
- BS.update_icons()
- O.blob_mobs.Add(BS)
- BS.Zombify(M)
- O.add_points(points)
- to_chat(O, "Gained [points] resources from the zombification of [M].")
diff --git a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm
deleted file mode 100644
index f307a077f888..000000000000
--- a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm
+++ /dev/null
@@ -1,37 +0,0 @@
-//does burn damage and EMPs, slightly fragile
-/datum/blobstrain/reagent/electromagnetic_web
- name = "Electromagnetic Web"
- color = "#83ECEC"
- complementary_color = "#EC8383"
- description = "will do high burn damage and EMP targets."
- effectdesc = "will also take massively increased damage and release an EMP when killed."
- analyzerdescdamage = "Does low burn damage and EMPs targets."
- analyzerdesceffect = "Is fragile to all types of damage, but takes massive damage from brute. In addition, releases a small EMP when killed."
- reagent = /datum/reagent/blob/electromagnetic_web
-
-/datum/blobstrain/reagent/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BRUTE) //take full brute
- switch(B.brute_resist)
- if(0.5)
- return damage * 2
- if(0.25)
- return damage * 4
- if(0.1)
- return damage * 10
- return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob
-
-/datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
- empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it
-
-/datum/reagent/blob/electromagnetic_web
- name = "Electromagnetic Web"
- taste_description = "pop rocks"
- color = "#83ECEC"
-
-/datum/reagent/blob/electromagnetic_web/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- if(prob(reac_volume*2))
- M.emp_act(EMP_LIGHT)
- if(M)
- M.apply_damage(reac_volume, BURN)
diff --git a/code/modules/antagonists/blob/blobstrains/energized_jelly.dm b/code/modules/antagonists/blob/blobstrains/energized_jelly.dm
deleted file mode 100644
index 56a4aca744eb..000000000000
--- a/code/modules/antagonists/blob/blobstrains/energized_jelly.dm
+++ /dev/null
@@ -1,34 +0,0 @@
-//does tons of oxygen damage and a little stamina, immune to tesla bolts, weak to EMP
-/datum/blobstrain/reagent/energized_jelly
- name = "Energized Jelly"
- description = "will cause low stamina and high oxygen damage, and cause targets to be unable to breathe."
- effectdesc = "will also conduct electricity, but takes damage from EMPs."
- analyzerdescdamage = "Does low stamina damage, high oxygen damage, and prevents targets from breathing."
- analyzerdesceffect = "Is immune to electricity and will easily conduct it, but is weak to EMPs."
- color = "#EFD65A"
- complementary_color = "#00E5B1"
- reagent = /datum/reagent/blob/energized_jelly
-
-/datum/blobstrain/reagent/energized_jelly/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && B.obj_integrity - damage <= 0 && prob(10))
- do_sparks(rand(2, 4), FALSE, B)
- return ..()
-
-/datum/blobstrain/reagent/energized_jelly/tesla_reaction(obj/structure/blob/B, power)
- return 0
-
-/datum/blobstrain/reagent/energized_jelly/emp_reaction(obj/structure/blob/B, severity)
- var/damage = rand(30, 50) - severity * rand(10, 15)
- B.take_damage(damage, BURN, "energy")
-
-/datum/reagent/blob/energized_jelly
- name = "Blob Energized Jelly"
- taste_description = "gelatin"
- color = "#EFD65A"
-
-/datum/reagent/blob/energized_jelly/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.losebreath += round(0.2*reac_volume)
- M.adjustStaminaLoss(reac_volume)
- if(M)
- M.apply_damage(0.6*reac_volume, OXY)
diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm
deleted file mode 100644
index ef07676a2301..000000000000
--- a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-//does aoe brute damage when hitting targets, is immune to explosions
-/datum/blobstrain/reagent/explosive_lattice
- name = "Explosive Lattice"
- description = "will do brute damage in an area around targets."
- effectdesc = "will also resist explosions, but takes increased damage from fire and other energy sources."
- analyzerdescdamage = "Does medium brute damage and causes damage to everyone near its targets."
- analyzerdesceffect = "Is highly resistant to explosions, but takes increased damage from fire and other energy sources."
- color = "#8B2500"
- complementary_color = "#00668B"
- blobbernaut_message = "blasts"
- message = "The blob blasts you"
- reagent = /datum/reagent/blob/explosive_lattice
-
-/datum/blobstrain/reagent/explosive_lattice/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_flag == "bomb")
- return 0
- else if(damage_flag != "melee" && damage_flag != "bullet" && damage_flag != "laser")
- return damage * 1.5
- return ..()
-
-/datum/reagent/blob/explosive_lattice
- name = "Explosive Lattice"
- taste_description = "the bomb"
- color = "#8B2500"
-
-/datum/reagent/blob/explosive_lattice/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- var/initial_volume = reac_volume
- reac_volume = ..()
- if(reac_volume >= 10) //if it's not a spore cloud, bad time incoming
- var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M))
- E.alpha = 150
- for(var/mob/living/L in orange(get_turf(M), 1))
- if(ROLE_BLOB in L.faction) //no friendly fire
- continue
- var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
- L.apply_damage(0.4*aoe_volume, BRUTE)
- if(M)
- M.apply_damage(0.6*reac_volume, BRUTE)
- else
- M.apply_damage(0.6*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blobstrains/multiplex.dm b/code/modules/antagonists/blob/blobstrains/multiplex.dm
deleted file mode 100644
index aaebf1d0526b..000000000000
--- a/code/modules/antagonists/blob/blobstrains/multiplex.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-/datum/blobstrain/multiplex
- var/list/blobstrains
- var/typeshare
-
-/datum/blobstrain/multiplex/New(mob/camera/blob/new_overmind, list/blobstrains)
- . = ..()
- for (var/bt in blobstrains)
- if (ispath(bt, /datum/blobstrain))
- src.blobstrains += new bt(overmind)
- else if (istype(bt, /datum/blobstrain))
- var/datum/blobstrain/bts = bt
- bts.overmind = overmind
- src.blobstrains += bt
- typeshare = (0.8 * length(src.blobstrains)) - (length(src.blobstrains)-1) // 1 is 80%, 2 are 60% etc
-
-/datum/blobstrain/multiplex/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.damage_reaction(B, damage, damage_type, damage_flag, coefficient*typeshare)
-
-/datum/blobstrain/multiplex/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.death_reaction(B, damage_flag, coefficient*typeshare)
-
-/datum/blobstrain/multiplex/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.expand_reaction(B, newB, T, O, coefficient*typeshare)
-
-/datum/blobstrain/multiplex/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.tesla_reaction(B, power, coefficient*typeshare)
- if (prob(. / length(blobstrains) * 100))
- return 1
-
-/datum/blobstrain/multiplex/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.extinguish_reaction(B, coefficient*typeshare)
-
-/datum/blobstrain/multiplex/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this
- for (var/datum/blobstrain/bt in blobstrains)
- . += bt.emp_reaction(B, severity, coefficient*typeshare)
diff --git a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm
deleted file mode 100644
index d3997aa8ce82..000000000000
--- a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-//does massive brute and burn damage, but can only expand manually
-/datum/blobstrain/reagent/networked_fibers
- name = "Networked Fibers"
- description = "will do high brute and burn damage and will generate resources quicker, but can only expand manually."
- shortdesc = "will do high brute and burn damage."
- effectdesc = "will move your core when manually expanding near it."
- analyzerdescdamage = "Does high brute and burn damage."
- analyzerdesceffect = "Is highly mobile and generates resources rapidly."
- color = "#4F4441"
- complementary_color = "#414C4F"
- reagent = /datum/reagent/blob/networked_fibers
-
-/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(!O && newB.overmind)
- if(!istype(B, /obj/structure/blob/node))
- newB.overmind.add_points(1)
- qdel(newB)
- else
- var/area/A = get_area(T)
- if(!isspaceturf(T) && !istype(A, /area/shuttle))
- for(var/obj/structure/blob/core/C in range(1, newB))
- if(C.overmind == O)
- newB.forceMove(get_turf(C))
- C.forceMove(T)
- C.setDir(get_dir(newB, C))
- O.add_points(1)
-
-//does massive brute and burn damage, but can only expand manually
-/datum/reagent/blob/networked_fibers
- name = "Networked Fibers"
- taste_description = "efficiency"
- color = "#4F4441"
-
-/datum/reagent/blob/networked_fibers/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.6*reac_volume, BRUTE)
- if(!QDELETED(M))
- M.apply_damage(0.6*reac_volume, BURN)
diff --git a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm
deleted file mode 100644
index d1d3768acb74..000000000000
--- a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm
+++ /dev/null
@@ -1,51 +0,0 @@
-//does low brute damage, oxygen damage, and stamina damage and wets tiles when damaged
-/datum/blobstrain/reagent/pressurized_slime
- name = "Pressurized Slime"
- description = "will do low brute, oxygen, and stamina damage, and wet tiles under targets."
- effectdesc = "will also wet tiles near blobs that are attacked or killed."
- analyzerdescdamage = "Does low brute damage, low oxygen damage, drains stamina, and wets tiles under targets, extinguishing them."
- analyzerdesceffect = "When attacked or killed, lubricates nearby tiles, extinguishing anything on them."
- color = "#AAAABB"
- complementary_color = "#BBBBAA"
- blobbernaut_message = "emits slime at"
- message = "The blob splashes into you"
- message_living = ", and you gasp for breath"
- reagent = /datum/reagent/blob/pressurized_slime
-
-/datum/blobstrain/reagent/pressurized_slime/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") || damage_type != BURN)
- extinguisharea(B, damage)
- return ..()
-
-/datum/blobstrain/reagent/pressurized_slime/death_reaction(obj/structure/blob/B, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
- B.visible_message("The blob ruptures, spraying the area with liquid!")
- extinguisharea(B, 50)
-
-/datum/blobstrain/reagent/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance)
- for(var/turf/open/T in range(1, B))
- if(prob(probchance))
- T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
- for(var/obj/O in T)
- O.extinguish()
- for(var/mob/living/L in T)
- L.adjust_fire_stacks(-2.5)
- L.ExtinguishMob()
-
-/datum/reagent/blob/pressurized_slime
- name = "Pressurized Slime"
- taste_description = "a sponge"
- color = "#AAAABB"
-
-/datum/reagent/blob/pressurized_slime/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- var/turf/open/T = get_turf(M)
- if(istype(T) && prob(reac_volume))
- T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
- M.adjust_fire_stacks(-(reac_volume / 10))
- M.ExtinguishMob()
- M.apply_damage(0.4*reac_volume, BRUTE)
- if(M)
- M.apply_damage(0.4*reac_volume, OXY)
- if(M)
- M.adjustStaminaLoss(reac_volume)
diff --git a/code/modules/antagonists/blob/blobstrains/reactive_spines.dm b/code/modules/antagonists/blob/blobstrains/reactive_spines.dm
deleted file mode 100644
index 9849c1177a71..000000000000
--- a/code/modules/antagonists/blob/blobstrains/reactive_spines.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-//does brute damage through armor and bio resistance
-/datum/blobstrain/reagent/reactive_spines
- name = "Reactive Spines"
- description = "will do medium brute damage through armor and bio resistance."
- effectdesc = "will also react when attacked with brute damage, attacking all near the attacked blob."
- analyzerdescdamage = "Does medium brute damage, ignoring armor and bio resistance."
- analyzerdesceffect = "When attacked with brute damage, will lash out, attacking everything near it."
- color = "#9ACD32"
- complementary_color = "#FFA500"
- blobbernaut_message = "stabs"
- message = "The blob stabs you"
- reagent = /datum/reagent/blob/reactive_spines
-
-/datum/blobstrain/reagent/reactive_spines/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage && damage_type == BRUTE && B.obj_integrity - damage > 0) //is there any damage, is it brute, and will we be alive
- if(damage_flag == "melee")
- B.visible_message("The blob retaliates, lashing out!")
- for(var/atom/A in range(1, B))
- A.blob_act(B)
- return ..()
-
-/datum/reagent/blob/reactive_spines
- name = "Reactive Spines"
- taste_description = "rock"
- color = "#9ACD32"
-
-/datum/reagent/blob/reactive_spines/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- if(M.stat == DEAD || istype(M, /mob/living/simple_animal/hostile/blob))
- return 0 //the dead, and blob mobs, don't cause reactions
- M.adjustBruteLoss(0.8*reac_volume)
diff --git a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm
deleted file mode 100644
index 500418b7be6d..000000000000
--- a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-//does toxin damage, hallucination, targets think they're not hurt at all
-/datum/blobstrain/reagent/regenerative_materia
- name = "Regenerative Materia"
- description = "will do toxin damage and cause targets to believe they are fully healed."
- analyzerdescdamage = "Does toxin damage and injects a toxin that causes the target to believe they are fully healed."
- color = "#A88FB7"
- complementary_color = "#AF7B8D"
- message_living = ", and you feel alive"
- reagent = /datum/reagent/blob/regenerative_materia
-
-/datum/reagent/blob/regenerative_materia
- name = "Regenerative Materia"
- taste_description = "heaven"
- color = "#A88FB7"
-
-/datum/reagent/blob/regenerative_materia/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.adjust_drugginess(reac_volume)
- if(M.reagents)
- M.reagents.add_reagent(/datum/reagent/blob/regenerative_materia, 0.2*reac_volume)
- M.reagents.add_reagent(/datum/reagent/toxin/spore, 0.2*reac_volume)
- M.apply_damage(0.7*reac_volume, TOX)
-
-/datum/reagent/blob/regenerative_materia/on_mob_life(mob/living/carbon/C)
- C.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER)
- C.hal_screwyhud = SCREWYHUD_HEALTHY //fully healed, honest
- ..()
-
-/datum/reagent/blob/regenerative_materia/on_mob_end_metabolize(mob/living/M)
- if(iscarbon(M))
- var/mob/living/carbon/N = M
- N.hal_screwyhud = 0
- ..()
diff --git a/code/modules/antagonists/blob/blobstrains/replicating_foam.dm b/code/modules/antagonists/blob/blobstrains/replicating_foam.dm
deleted file mode 100644
index 4800aed698d2..000000000000
--- a/code/modules/antagonists/blob/blobstrains/replicating_foam.dm
+++ /dev/null
@@ -1,35 +0,0 @@
-/datum/blobstrain/reagent/replicating_foam
- name = "Replicating Foam"
- description = "will do medium brute damage and occasionally expand again when expanding."
- shortdesc = "will do medium brute damage."
- effectdesc = "will also expand when attacked with burn damage, but takes more brute damage."
- color = "#7B5A57"
- complementary_color = "#57787B"
- analyzerdescdamage = "Does medium brute damage."
- analyzerdesceffect = "Expands when attacked with burn damage, will occasionally expand again when expanding, and is fragile to brute damage."
- reagent = /datum/reagent/blob/replicating_foam
-
-
-/datum/blobstrain/reagent/replicating_foam/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_type == BRUTE)
- damage = damage * 2
- else if(damage_type == BURN && damage > 0 && B.obj_integrity - damage > 0 && prob(60))
- var/obj/structure/blob/newB = B.expand(null, null, 0)
- if(newB)
- newB.obj_integrity = B.obj_integrity - damage
- newB.update_appearance()
- return ..()
-
-
-/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(prob(30))
- newB.expand(null, null, 0) //do it again!
-
-/datum/reagent/blob/replicating_foam
- name = "Replicating Foam"
- taste_description = "duplication"
- color = "#7B5A57"
-
-/datum/reagent/blob/replicating_foam/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm b/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm
deleted file mode 100644
index c615427463f6..000000000000
--- a/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm
+++ /dev/null
@@ -1,36 +0,0 @@
-//does brute damage, shifts away when damaged
-/datum/blobstrain/reagent/shifting_fragments
- name = "Shifting Fragments"
- description = "will do medium brute damage."
- effectdesc = "will also cause blob parts to shift away when attacked."
- analyzerdescdamage = "Does medium brute damage."
- analyzerdesceffect = "When attacked, may shift away from the attacker."
- color = "#C8963C"
- complementary_color = "#3C6EC8"
- reagent = /datum/reagent/blob/shifting_fragments
-
-/datum/blobstrain/reagent/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O)
- if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
- newB.forceMove(get_turf(B))
- B.forceMove(T)
-
-/datum/blobstrain/reagent/shifting_fragments/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if((damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") && damage > 0 && B.obj_integrity - damage > 0 && prob(60-damage))
- var/list/blobstopick = list()
- for(var/obj/structure/blob/OB in orange(1, B))
- if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind.blobstrain.type == B.overmind.blobstrain.type)
- blobstopick += OB //as long as the blob picked is valid; ie, a normal or shield blob that has the same chemical as we do, we can swap with it
- if(blobstopick.len)
- var/obj/structure/blob/targeted = pick(blobstopick) //randomize the blob chosen, because otherwise it'd tend to the lower left
- var/turf/T = get_turf(targeted)
- targeted.forceMove(get_turf(B))
- B.forceMove(T) //swap the blobs
- return ..()
-
-/datum/reagent/blob/shifting_fragments
- name = "Shifting Fragments"
- color = "#C8963C"
-
-/datum/reagent/blob/shifting_fragments/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm
deleted file mode 100644
index 02ee0cb1941a..000000000000
--- a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-//does brute damage, bonus damage for each nearby blob, and spreads damage out
-/datum/blobstrain/reagent/synchronous_mesh
- name = "Synchronous Mesh"
- description = "will do massively increased brute damage for each blob near the target."
- effectdesc = "will also spread damage between each blob near the attacked blob."
- analyzerdescdamage = "Does brute damage, increasing for each blob near the target."
- analyzerdesceffect = "When attacked, spreads damage between all blobs near the attacked blob."
- color = "#65ADA2"
- complementary_color = "#AD6570"
- blobbernaut_message = "synchronously strikes"
- message = "The blobs strike you"
- reagent = /datum/reagent/blob/synchronous_mesh
-
-/datum/blobstrain/reagent/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag)
- if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser") //the cause isn't fire or bombs, so split the damage
- var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs
- for(var/obj/structure/blob/C in orange(1, B))
- if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it
- damagesplit += 1
- for(var/obj/structure/blob/C in orange(1, B))
- if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes
- C.take_damage(damage/damagesplit, CLONE, 0, 0)
- return damage / damagesplit
- else
- return damage * 1.25
-
-/datum/reagent/blob/synchronous_mesh
- name = "Synchronous Mesh"
- taste_description = "toxic mold"
- color = "#65ADA2"
-
-/datum/reagent/blob/synchronous_mesh/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
- reac_volume = ..()
- M.apply_damage(0.2*reac_volume, BRUTE)
- if(M && reac_volume)
- for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
- if(M)
- B.blob_attack_animation(M) //show them they're getting a bad time
- M.apply_damage(0.3*reac_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
deleted file mode 100644
index 3255b4aea69c..000000000000
--- a/code/modules/antagonists/blob/overmind.dm
+++ /dev/null
@@ -1,288 +0,0 @@
-//Few global vars to track the blob
-GLOBAL_LIST_EMPTY(blobs) //complete list of all blobs made.
-GLOBAL_LIST_EMPTY(blob_cores)
-GLOBAL_LIST_EMPTY(overminds)
-GLOBAL_LIST_EMPTY(blob_nodes)
-
-
-/mob/camera/blob
- name = "Blob Overmind"
- real_name = "Blob Overmind"
- desc = "The overmind. It controls the blob."
- icon = 'icons/mob/cameramob.dmi'
- icon_state = "marker"
- mouse_opacity = MOUSE_OPACITY_ICON
- move_on_shuttle = 1
- see_in_dark = 8
- invisibility = INVISIBILITY_OBSERVER
- layer = FLY_LAYER
-
- pass_flags = PASSBLOB
- faction = list(ROLE_BLOB)
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- hud_type = /datum/hud/blob_overmind
- var/obj/structure/blob/core/blob_core = null // The blob overmind's core
- var/blob_points = 0
- var/max_blob_points = 100
- var/last_attack = 0
- var/datum/blobstrain/blobstrain
- var/list/blob_mobs = list()
- var/list/resource_blobs = list()
- var/free_strain_rerolls = 1 //one free strain reroll
- var/last_reroll_time = 0 //time since we last rerolled, used to give free rerolls
- var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
- var/placed = 0
- var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
- var/autoplace_max_time = 3600 //six minutes, as long as should be needed
- var/list/blobs_legit = list()
- var/max_count = 0 //The biggest it got before death
- var/blobwincount = 400
- var/victory_in_progress = FALSE
- var/rerolling = FALSE
- var/announcement_size = 75
- var/announcement_time
- var/has_announced = FALSE
-
-/mob/camera/blob/Initialize(mapload, starting_points = 60)
- validate_location()
- blob_points = starting_points
- manualplace_min_time += world.time
- autoplace_max_time += world.time
- GLOB.overminds += src
- var/new_name = "[initial(name)] ([rand(1, 999)])"
- name = new_name
- real_name = new_name
- last_attack = world.time
- var/datum/blobstrain/BS = pick(GLOB.valid_blobstrains)
- set_strain(BS)
- color = blobstrain.complementary_color
- if(blob_core)
- blob_core.update_appearance()
- announcement_time = world.time + 6000
- . = ..()
- START_PROCESSING(SSobj, src)
-
-/mob/camera/blob/proc/validate_location()
- var/turf/T = get_turf(src)
- if(!is_valid_turf(T) && LAZYLEN(GLOB.blobstart))
- var/list/blobstarts = shuffle(GLOB.blobstart)
- for(var/_T in blobstarts)
- if(is_valid_turf(_T))
- T = _T
- break
- if(!T)
- CRASH("No blobspawnpoints and blob spawned in nullspace.")
- forceMove(T)
-
-/mob/camera/blob/proc/set_strain(datum/blobstrain/new_strain)
- if (ispath(new_strain))
- var/hadstrain = FALSE
- if (istype(blobstrain))
- blobstrain.on_lose()
- qdel(blobstrain)
- hadstrain = TRUE
- blobstrain = new new_strain(src)
- blobstrain.on_gain()
- if (hadstrain)
- to_chat(src, "Your strain is now: [blobstrain.name]!")
- to_chat(src, "The [blobstrain.name] strain [blobstrain.description]")
- if(blobstrain.effectdesc)
- to_chat(src, "The [blobstrain.name] strain [blobstrain.effectdesc]")
-
-
-/mob/camera/blob/proc/is_valid_turf(turf/T)
- var/area/A = get_area(T)
- if(!T || (A && !(A.area_flags & BLOBS_ALLOWED)) || isspaceturf(T))
- return FALSE
- return TRUE
-
-/mob/camera/blob/process()
- if(!blob_core)
- if(!placed)
- if(manualplace_min_time && world.time >= manualplace_min_time)
- to_chat(src, "You may now place your blob core.")
- to_chat(src, "You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].")
- manualplace_min_time = 0
- if(autoplace_max_time && world.time >= autoplace_max_time)
- place_blob_core(1)
- else
- qdel(src)
- else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
- victory_in_progress = TRUE
- priority_announce("Biohazard has reached critical mass. Sector loss is imminent.", "Biohazard Alert")
- set_security_level("delta")
- SSredbot.send_discord_message("admin","A blob has reached critical mass.","round ending event")
- max_blob_points = INFINITY
- blob_points = INFINITY
- addtimer(CALLBACK(src, PROC_REF(victory)), 450)
- else if(!free_strain_rerolls && (last_reroll_time + BLOB_REROLL_TIMEYou have gained another free strain re-roll.")
- free_strain_rerolls = 1
-
- if(!victory_in_progress && max_count < blobs_legit.len)
- max_count = blobs_legit.len
-
- if((world.time >= announcement_time || blobs_legit.len >= announcement_size) && !has_announced)
- priority_announce("Confirmed outbreak of level 5 biohazard in [station_name()]. All able individuals must assist in containing the outbreak.", "Biohazard Alert", 'sound/ai/outbreak5.ogg')
- has_announced = TRUE
-
- if((world.time >= announcement_time || blobs_legit.len >= announcement_size) && !has_announced)
- priority_announce("Confirmed outbreak of level 5 biohazard in [station_name()]. All able individuals must assist in containing the outbreak.", "Biohazard Alert", 'sound/ai/outbreak5.ogg')
- has_announced = TRUE
-
-/mob/camera/blob/proc/victory()
- sound_to_playing_players('sound/machines/alarm.ogg')
- sleep(100)
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
- var/turf/T = get_turf(L)
- if(L.virtual_z() != virtual_z())
- continue
-
- if(L in GLOB.overminds || (L.pass_flags & PASSBLOB))
- continue
-
- var/area/Ablob = get_area(T)
-
- if(!(Ablob.area_flags & BLOBS_ALLOWED))
- continue
-
- if(!(ROLE_BLOB in L.faction))
- playsound(L, 'sound/effects/splat.ogg', 50, TRUE)
- L.death()
- new/mob/living/simple_animal/hostile/blob/blobspore(T)
- else
- L.fully_heal(admin_revive = FALSE)
-
- for(var/area/A in GLOB.sortedAreas)
- if(!(A.area_flags & BLOBS_ALLOWED))
- continue
- A.color = blobstrain.color
- A.name = "blob"
- A.icon = 'icons/mob/blob.dmi'
- A.icon_state = "blob_shield"
- A.layer = BELOW_MOB_LAYER
- A.invisibility = 0
- A.blend_mode = 0
- var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
- if(B)
- var/datum/objective/blob_takeover/main_objective = locate() in B.objectives
- if(main_objective)
- main_objective.completed = TRUE
- to_chat(world, "[real_name] consumed the sector in an unstoppable tide!")
- SSticker.news_report = BLOB_WIN
- SSticker.force_ending = 1
-
-/mob/camera/blob/Destroy()
- QDEL_NULL(blobstrain)
- for(var/BL in GLOB.blobs)
- var/obj/structure/blob/B = BL
- if(B && B.overmind == src)
- B.overmind = null
- B.update_appearance() //reset anything that was ours
- for(var/BLO in blob_mobs)
- var/mob/living/simple_animal/hostile/blob/BM = BLO
- if(BM)
- BM.overmind = null
- BM.update_icons()
- GLOB.overminds -= src
-
- STOP_PROCESSING(SSobj, src)
-
- return ..()
-
-/mob/camera/blob/Login()
- . = ..()
- if(!. || !client)
- return FALSE
- to_chat(src, "You are the overmind!")
- blob_help()
- update_health_hud()
- add_points(0)
-
-/mob/camera/blob/examine(mob/user)
- . = ..()
- if(blobstrain)
- . += "Its strain is [blobstrain.name]."
-
-/mob/camera/blob/update_health_hud()
- if(blob_core)
- hud_used.healths.maptext = "[round(blob_core.obj_integrity)]
"
- for(var/mob/living/simple_animal/hostile/blob/blobbernaut/B in blob_mobs)
- if(B.hud_used && B.hud_used.blobpwrdisplay)
- B.hud_used.blobpwrdisplay.maptext = "[round(blob_core.obj_integrity)]
"
-
-/mob/camera/blob/proc/add_points(points)
- blob_points = clamp(blob_points + points, 0, max_blob_points)
- hud_used.blobpwrdisplay.maptext = "[round(blob_points)]
"
-
-/mob/camera/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
- if (!message)
- return
-
- if (src.client)
- if(client.prefs.muted & MUTE_IC)
- to_chat(src, "You cannot send IC messages (muted).")
- return
- if (!(ignore_spam || forced) && src.client.handle_spam_prevention(message,MUTE_IC))
- return
-
- if (stat)
- return
-
- blob_talk(message)
-
-/mob/camera/blob/proc/blob_talk(message)
-
- message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
-
- if (!message)
- return
-
- src.log_talk(message, LOG_SAY)
-
- var/message_a = say_quote(message)
- var/rendered = "\[Blob Telepathy\] [name]([blobstrain.name]) [message_a]"
-
- for(var/mob/M in GLOB.mob_list)
- if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
- to_chat(M, rendered)
- if(isobserver(M))
- var/link = FOLLOW_LINK(M, src)
- to_chat(M, "[link] [rendered]")
-
-/mob/camera/blob/blob_act(obj/structure/blob/B)
- return
-
-/mob/camera/blob/get_status_tab_items()
- . = ..()
- if(blob_core)
- . += "Core Health: [blob_core.obj_integrity]"
- . += "Power Stored: [blob_points]/[max_blob_points]"
- . += "Blobs to Win: [blobs_legit.len]/[blobwincount]"
- if(free_strain_rerolls)
- . += "You have [free_strain_rerolls] Free Strain Reroll\s Remaining"
- if(!placed)
- if(manualplace_min_time)
- . += "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]"
- . += "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]"
-
-/mob/camera/blob/Move(NewLoc, Dir = 0)
- if(placed)
- var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
- if(B)
- forceMove(NewLoc)
- else
- return 0
- else
- var/area/A = get_area(NewLoc)
- if(isspaceturf(NewLoc) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
- return 0
- forceMove(NewLoc)
- return 1
-
-/mob/camera/blob/mind_initialize()
- . = ..()
- var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
- if(!B)
- mind.add_antag_datum(/datum/antagonist/blob)
diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm
deleted file mode 100644
index 2fba8e460e22..000000000000
--- a/code/modules/antagonists/blob/powers.dm
+++ /dev/null
@@ -1,394 +0,0 @@
-/mob/camera/blob/proc/can_buy(cost = 15)
- if(blob_points < cost)
- to_chat(src, "You cannot afford this, you need at least [cost] resources!")
- return 0
- add_points(-cost)
- return 1
-
-// Power verbs
-
-/mob/camera/blob/proc/place_blob_core(placement_override, pop_override = FALSE)
- if(placed && placement_override != -1)
- return 1
- if(!placement_override)
- if(!pop_override)
- for(var/mob/living/M in range(7, src))
- if(ROLE_BLOB in M.faction)
- continue
- if(M.client)
- to_chat(src, "There is someone too close to place your blob core!")
- return 0
- for(var/mob/living/M in view(13, src))
- if(ROLE_BLOB in M.faction)
- continue
- if(M.client)
- to_chat(src, "Someone could see your blob core from here!")
- return 0
- var/turf/T = get_turf(src)
- if(T.density)
- to_chat(src, "This spot is too dense to place a blob core on!")
- return 0
- var/area/A = get_area(T)
- if(isspaceturf(T) || A && !(A.area_flags & BLOBS_ALLOWED))
- to_chat(src, "You cannot place your core here!")
- return 0
- for(var/obj/O in T)
- if(istype(O, /obj/structure/blob))
- if(istype(O, /obj/structure/blob/normal))
- qdel(O)
- else
- to_chat(src, "There is already a blob here!")
- return 0
- else if(O.density)
- to_chat(src, "This spot is too dense to place a blob core on!")
- return 0
- if(!pop_override && world.time <= manualplace_min_time && world.time <= autoplace_max_time)
- to_chat(src, "It is too early to place your blob core!")
- return 0
- else if(placement_override == 1)
- var/turf/T = pick(GLOB.blobstart)
- forceMove(T) //got overrided? you're somewhere random, motherfucker
- if(placed && blob_core)
- blob_core.forceMove(loc)
- else
- var/obj/structure/blob/core/core = new(get_turf(src), src, 1)
- core.overmind = src
- blobs_legit += src
- blob_core = core
- core.update_appearance()
- update_health_hud()
- placed = 1
- return 1
-
-/mob/camera/blob/verb/transport_core()
- set category = "Blob"
- set name = "Jump to Core"
- set desc = "Move your camera to your core."
- if(blob_core)
- forceMove(blob_core.drop_location())
-
-/mob/camera/blob/verb/jump_to_node()
- set category = "Blob"
- set name = "Jump to Node"
- set desc = "Move your camera to a selected node."
- if(GLOB.blob_nodes.len)
- var/list/nodes = list()
- for(var/i in 1 to GLOB.blob_nodes.len)
- var/obj/structure/blob/node/B = GLOB.blob_nodes[i]
- nodes["Blob Node #[i] ([get_area_name(B)])"] = B
- var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
- var/obj/structure/blob/node/chosen_node = nodes[node_name]
- if(chosen_node)
- forceMove(chosen_node.loc)
-
-/mob/camera/blob/proc/createSpecial(price, blobstrain, nearEquals, needsNode, turf/T)
- if(!T)
- T = get_turf(src)
- var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
- if(!B)
- to_chat(src, "There is no blob here!")
- return
- if(!istype(B, /obj/structure/blob/normal))
- to_chat(src, "Unable to use this blob, find a normal one.")
- return
- if(needsNode)
- var/area/A = get_area(src)
- if(!(A.area_flags & BLOBS_ALLOWED)) //factory and resource blobs must be legit
- to_chat(src, "This type of blob must be placed on the station!")
- return
- if(nodes_required && !(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T)))
- to_chat(src, "You need to place this blob closer to a node or core!")
- return //handholdotron 2000
- if(nearEquals)
- for(var/obj/structure/blob/L in orange(nearEquals, T))
- if(L.type == blobstrain)
- to_chat(src, "There is a similar blob nearby, move more than [nearEquals] tiles away from it!")
- return
- if(!can_buy(price))
- return
- var/obj/structure/blob/N = B.change_to(blobstrain, src)
- return N
-
-/mob/camera/blob/verb/toggle_node_req()
- set category = "Blob"
- set name = "Toggle Node Requirement"
- set desc = "Toggle requiring nodes to place resource and factory blobs."
- nodes_required = !nodes_required
- if(nodes_required)
- to_chat(src, "You now require a nearby node or core to place factory and resource blobs.")
- else
- to_chat(src, "You no longer require a nearby node or core to place factory and resource blobs.")
-
-/mob/camera/blob/verb/create_shield_power()
- set category = "Blob"
- set name = "Create/Upgrade Shield Blob (15)"
- set desc = "Create a shield blob, which will block fire and is hard to kill. Using this on an existing shield blob turns it into a reflective blob, capable of reflecting most projectiles but making it twice as weak to brute attacks."
- create_shield()
-
-/mob/camera/blob/proc/create_shield(turf/T)
- var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T
- if(S)
- if(!can_buy(BLOB_REFLECTOR_COST))
- return
- if(S.obj_integrity < S.max_integrity * 0.5)
- add_points(BLOB_REFLECTOR_COST)
- to_chat(src, "This shield blob is too damaged to be modified properly!")
- return
- to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced integrity.")
- S.change_to(/obj/structure/blob/shield/reflective, src)
- else
- createSpecial(15, /obj/structure/blob/shield, 0, 0, T)
-
-/mob/camera/blob/verb/create_resource()
- set category = "Blob"
- set name = "Create Resource Blob (40)"
- set desc = "Create a resource tower which will generate resources for you."
- createSpecial(40, /obj/structure/blob/resource, 4, 1)
-
-/mob/camera/blob/verb/create_node()
- set category = "Blob"
- set name = "Create Node Blob (50)"
- set desc = "Create a node, which will power nearby factory and resource blobs."
- createSpecial(50, /obj/structure/blob/node, 5, 0)
-
-/mob/camera/blob/verb/create_factory()
- set category = "Blob"
- set name = "Create Factory Blob (60)"
- set desc = "Create a spore tower that will spawn spores to harass your enemies."
- createSpecial(60, /obj/structure/blob/factory, 7, 1)
-
-/mob/camera/blob/verb/create_blobbernaut()
- set category = "Blob"
- set name = "Create Blobbernaut (40)"
- set desc = "Create a powerful blobbernaut which is mildly smart and will attack enemies."
- var/turf/T = get_turf(src)
- var/obj/structure/blob/factory/B = locate(/obj/structure/blob/factory) in T
- if(!B)
- to_chat(src, "You must be on a factory blob!")
- return
- if(B.naut) //if it already made a blobbernaut, it can't do it again
- to_chat(src, "This factory blob is already sustaining a blobbernaut.")
- return
- if(B.obj_integrity < B.max_integrity * 0.5)
- to_chat(src, "This factory blob is too damaged to sustain a blobbernaut.")
- return
- if(!can_buy(40))
- return
-
- B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory.
- to_chat(src, "You attempt to produce a blobbernaut.")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
- if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now.
- B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
- B.obj_integrity = min(B.obj_integrity, B.max_integrity)
- B.update_appearance()
- B.visible_message("The blobbernaut [pick("rips", "tears", "shreds")] its way out of the factory blob!")
- playsound(B.loc, 'sound/effects/splat.ogg', 50, TRUE)
- var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new /mob/living/simple_animal/hostile/blob/blobbernaut(get_turf(B))
- flick("blobbernaut_produce", blobber)
- B.naut = blobber
- blobber.factory = B
- blobber.overmind = src
- blobber.update_icons()
- blobber.adjustHealth(blobber.maxHealth * 0.5)
- blob_mobs += blobber
- var/mob/dead/observer/C = pick(candidates)
- blobber.key = C.key
- SEND_SOUND(blobber, sound('sound/effects/blobattack.ogg'))
- SEND_SOUND(blobber, sound('sound/effects/attackblob.ogg'))
- to_chat(blobber, "You are a blobbernaut!")
- to_chat(blobber, "You are powerful, hard to kill, and slowly regenerate near nodes and cores, but will slowly die if not near the blob or if the factory that made you is killed.")
- to_chat(blobber, "You can communicate with other blobbernauts and overminds via :b")
- to_chat(blobber, "Your overmind's blob reagent is: [blobstrain.name]!")
- to_chat(blobber, "The [blobstrain.name] reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")
- else
- to_chat(src, "You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.")
- add_points(40)
- B.naut = null
-
-/mob/camera/blob/verb/relocate_core()
- set category = "Blob"
- set name = "Relocate Core (80)"
- set desc = "Swaps the locations of your core and the selected node."
- var/turf/T = get_turf(src)
- var/obj/structure/blob/node/B = locate(/obj/structure/blob/node) in T
- if(!B)
- to_chat(src, "You must be on a blob node!")
- return
- if(!blob_core)
- to_chat(src, "You have no core and are about to die! May you rest in peace.")
- return
- var/area/A = get_area(T)
- if(isspaceturf(T) || A && !(A.area_flags & BLOBS_ALLOWED))
- to_chat(src, "You cannot relocate your core here!")
- return
- if(!can_buy(80))
- return
- var/turf/old_turf = get_turf(blob_core)
- var/olddir = blob_core.dir
- blob_core.forceMove(T)
- blob_core.setDir(B.dir)
- B.forceMove(old_turf)
- B.setDir(olddir)
-
-/mob/camera/blob/verb/revert()
- set category = "Blob"
- set name = "Remove Blob"
- set desc = "Removes a blob, giving you back some resources."
- var/turf/T = get_turf(src)
- remove_blob(T)
-
-/mob/camera/blob/proc/remove_blob(turf/T)
- var/obj/structure/blob/B = locate() in T
- if(!B)
- to_chat(src, "There is no blob there!")
- return
- if(B.point_return < 0)
- to_chat(src, "Unable to remove this blob.")
- return
- if(max_blob_points < B.point_return + blob_points)
- to_chat(src, "You have too many resources to remove this blob!")
- return
- if(B.point_return)
- add_points(B.point_return)
- to_chat(src, "Gained [B.point_return] resources from removing \the [B].")
- qdel(B)
-
-// commented out to fix errors with non-constant name/desc. Do we even need this code?
-// /mob/camera/blob/verb/expand_blob_power()
-// set category = "Blob"
-// set name = "Expand/Attack Blob ([BLOB_SPREAD_COST])"
-// set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects and refunding [BLOB_ATTACK_REFUND] points."
-// var/turf/T = get_turf(src)
-// expand_blob(T)
-
-/mob/camera/blob/proc/expand_blob(turf/T)
- if(world.time < last_attack)
- return
- var/list/possibleblobs = list()
- for(var/obj/structure/blob/AB in range(T, 1))
- possibleblobs += AB
- if(!possibleblobs.len)
- to_chat(src, "There is no blob adjacent to the target tile!")
- return
- if(can_buy(BLOB_SPREAD_COST))
- var/attacksuccess = FALSE
- for(var/mob/living/L in T)
- if(ROLE_BLOB in L.faction) //no friendly/dead fire
- continue
- if(L.stat != DEAD)
- attacksuccess = TRUE
- blobstrain.attack_living(L, possibleblobs)
- var/obj/structure/blob/B = locate() in T
- if(B)
- if(attacksuccess) //if we successfully attacked a turf with a blob on it, only give an attack refund
- B.blob_attack_animation(T, src)
- add_points(BLOB_ATTACK_REFUND)
- else
- to_chat(src, "There is a blob there!")
- add_points(BLOB_SPREAD_COST) //otherwise, refund all of the cost
- else
- var/list/cardinalblobs = list()
- var/list/diagonalblobs = list()
- for(var/I in possibleblobs)
- var/obj/structure/blob/IB = I
- if(get_dir(IB, T) in GLOB.cardinals)
- cardinalblobs += IB
- else
- diagonalblobs += IB
- var/obj/structure/blob/OB
- if(cardinalblobs.len)
- OB = pick(cardinalblobs)
- if(!OB.expand(T, src))
- add_points(BLOB_ATTACK_REFUND) //assume it's attacked SOMETHING, possibly a structure
- else
- OB = pick(diagonalblobs)
- if(attacksuccess)
- OB.blob_attack_animation(T, src)
- playsound(OB, 'sound/effects/splat.ogg', 50, TRUE)
- add_points(BLOB_ATTACK_REFUND)
- else
- add_points(BLOB_SPREAD_COST) //if we're attacking diagonally and didn't hit anything, refund
- if(attacksuccess)
- last_attack = world.time + CLICK_CD_MELEE
- else
- last_attack = world.time + CLICK_CD_RAPID
-
-/mob/camera/blob/verb/rally_spores_power()
- set category = "Blob"
- set name = "Rally Spores"
- set desc = "Rally your spores to move to a target location."
- var/turf/T = get_turf(src)
- rally_spores(T)
-
-/mob/camera/blob/proc/rally_spores(turf/T)
- to_chat(src, "You rally your spores.")
- var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
- if(!surrounding_turfs.len)
- return
- for(var/mob/living/simple_animal/hostile/blob/blobspore/BS in blob_mobs)
- if(isturf(BS.loc) && get_dist(BS, T) <= 35 && !BS.key)
- BS.LoseTarget()
- BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
-
-/mob/camera/blob/verb/blob_broadcast()
- set category = "Blob"
- set name = "Blob Broadcast"
- set desc = "Speak with your blob spores and blobbernauts as your mouthpieces."
- var/speak_text = stripped_input(src, "What would you like to say with your minions?", "Blob Broadcast", null)
- if(!speak_text)
- return
- else
- to_chat(src, "You broadcast with your minions, [speak_text]")
- for(var/BLO in blob_mobs)
- var/mob/living/simple_animal/hostile/blob/BM = BLO
- if(BM.stat == CONSCIOUS)
- BM.say(speak_text)
-
-/mob/camera/blob/verb/strain_reroll()
- set category = "Blob"
- set name = "Reactive Strain Adaptation (40)"
- set desc = "Replaces your strain with a random, different one."
- if(!rerolling && (free_strain_rerolls || can_buy(40)))
- rerolling = TRUE
- reroll_strain()
- rerolling = FALSE
- if(free_strain_rerolls)
- free_strain_rerolls--
- last_reroll_time = world.time
-
-/mob/camera/blob/proc/reroll_strain()
- var/list/choices = list()
- while (length(choices) < 4)
- var/datum/blobstrain/bs = pick((GLOB.valid_blobstrains))
- choices[initial(bs.name)] = bs
-
- var/choice = input(usr, "Please choose a new strain","Strain") as anything in sortList(choices, /proc/cmp_typepaths_asc)
- if (choice && choices[choice] && !QDELETED(src))
- var/datum/blobstrain/bs = choices[choice]
- set_strain(bs)
-
-
-/mob/camera/blob/verb/blob_help()
- set category = "Blob"
- set name = "*Blob Help*"
- set desc = "Help on how to blob."
- to_chat(src, "As the overmind, you can control the blob!")
- to_chat(src, "Your blob reagent is: [blobstrain.name]!")
- to_chat(src, "The [blobstrain.name] reagent [blobstrain.description]")
- if(blobstrain.effectdesc)
- to_chat(src, "The [blobstrain.name] reagent [blobstrain.effectdesc]")
- to_chat(src, "You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.")
- to_chat(src, "Normal Blobs will expand your reach and can be upgraded into special blobs that perform certain functions.")
- to_chat(src, "You can upgrade normal blobs into the following types of blob:")
- to_chat(src, "Shield Blobs are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires. Upgrading them again will result in a reflective blob, capable of reflecting most projectiles at the cost of the strong blob's extra health.")
- to_chat(src, "Resource Blobs are blobs which produce more resources for you, build as many of these as possible to consume the station. This type of blob must be placed near node blobs or your core to work.")
- to_chat(src, "Factory Blobs are blobs that spawn blob spores which will attack nearby enemies. This type of blob must be placed near node blobs or your core to work.")
- to_chat(src, "Blobbernauts can be produced from factories for a cost, and are hard to kill, powerful, and moderately smart. The factory used to create one will become fragile and briefly unable to produce spores.")
- to_chat(src, "Node Blobs are blobs which grow, like the core. Like the core it can activate resource and factory blobs.")
- to_chat(src, "In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.")
- to_chat(src, "Shortcuts: Click = Expand Blob | Middle Mouse Click = Rally Spores | Ctrl Click = Create Shield Blob | Alt Click = Remove Blob")
- to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
- if(!placed && autoplace_max_time <= world.time)
- to_chat(src, "You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].")
- to_chat(src, "You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.")
diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm
deleted file mode 100644
index 31401dab8bb6..000000000000
--- a/code/modules/antagonists/blob/structures/_blob.dm
+++ /dev/null
@@ -1,361 +0,0 @@
-//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back
-/obj/structure/blob
- name = "blob"
- icon = 'icons/mob/blob.dmi'
- light_range = 2
- desc = "A thick wall of writhing tendrils."
- density = FALSE //this being false causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse.
- opacity = FALSE
- anchored = TRUE
- layer = BELOW_MOB_LAYER
- pass_flags_self = PASSBLOB
- CanAtmosPass = ATMOS_PASS_PROC
- var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
- max_integrity = 30
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70)
- var/health_regen = 2 //how much health this blob regens when pulsed
- var/pulse_timestamp = 0 //we got pulsed when?
- var/heal_timestamp = 0 //we got healed when?
- var/brute_resist = 0.5 //multiplies brute damage by this
- var/fire_resist = 1 //multiplies burn damage by this
- var/atmosblock = FALSE //if the blob blocks atmos and heat spread
- var/mob/camera/blob/overmind
-
-/obj/structure/blob/Initialize(mapload, owner_overmind)
- . = ..()
- if(owner_overmind)
- overmind = owner_overmind
- var/area/Ablob = get_area(src)
- if(Ablob.area_flags & BLOBS_ALLOWED) //Is this area allowed for winning as blob?
- overmind.blobs_legit += src
- GLOB.blobs += src //Keep track of the blob in the normal list either way
- setDir(pick(GLOB.cardinals))
- update_appearance()
- if(atmosblock)
- air_update_turf(TRUE)
- ConsumeTile()
-
-/obj/structure/blob/proc/creation_action() //When it's created by the overmind, do this.
- return
-
-/obj/structure/blob/Destroy()
- if(atmosblock)
- atmosblock = FALSE
- air_update_turf(TRUE)
- if(overmind)
- overmind.blobs_legit -= src //if it was in the legit blobs list, it isn't now
- GLOB.blobs -= src //it's no longer in the all blobs list either
- playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) //Expand() is no longer broken, no check necessary.
- return ..()
-
-/obj/structure/blob/blob_act()
- return
-
-/obj/structure/blob/Adjacent(atom/neighbour)
- . = ..()
- if(.)
- var/result = 0
- var/direction = get_dir(src, neighbour)
- var/list/dirs = list("[NORTHWEST]" = list(NORTH, WEST), "[NORTHEAST]" = list(NORTH, EAST), "[SOUTHEAST]" = list(SOUTH, EAST), "[SOUTHWEST]" = list(SOUTH, WEST))
- for(var/A in dirs)
- if(direction == text2num(A))
- for(var/B in dirs[A])
- var/C = locate(/obj/structure/blob) in get_step(src, B)
- if(C)
- result++
- . -= result - 1
-
-/obj/structure/blob/BlockThermalConductivity()
- return atmosblock
-
-/obj/structure/blob/CanAtmosPass(turf/T)
- return !atmosblock
-
-/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
- . = ..()
- if(overmind)
- add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
- else
- remove_atom_colour(FIXED_COLOUR_PRIORITY)
-
-/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
- if(QDELETED(pulsing_overmind))
- pulsing_overmind = overmind
- Be_Pulsed()
- var/expanded = FALSE
- if(prob(70) && expand())
- expanded = TRUE
- var/list/blobs_to_affect = list()
- for(var/obj/structure/blob/B in urange(claim_range, src, 1))
- blobs_to_affect += B
- shuffle_inplace(blobs_to_affect)
- for(var/L in blobs_to_affect)
- var/obj/structure/blob/B = L
- if(!B.overmind && !istype(B, /obj/structure/blob/core) && prob(30))
- B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
- B.update_appearance()
- var/distance = get_dist(get_turf(src), get_turf(B))
- var/expand_probablity = max(20 - distance * 8, 1)
- if(B.Adjacent(src))
- expand_probablity = 20
- if(distance <= expand_range)
- var/can_expand = TRUE
- if(blobs_to_affect.len >= 120 && B.heal_timestamp > world.time)
- can_expand = FALSE
- if(can_expand && B.pulse_timestamp <= world.time && prob(expand_probablity))
- var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion
- if(newB)
- if(expanded)
- qdel(newB)
- expanded = TRUE
- if(distance <= pulse_range)
- B.Be_Pulsed()
-
-/obj/structure/blob/proc/Be_Pulsed()
- if(pulse_timestamp <= world.time)
- ConsumeTile()
- if(heal_timestamp <= world.time)
- obj_integrity = min(max_integrity, obj_integrity+health_regen)
- heal_timestamp = world.time + 20
- update_appearance()
- pulse_timestamp = world.time + 10
- return 1 //we did it, we were pulsed!
- return 0 //oh no we failed
-
-/obj/structure/blob/proc/ConsumeTile()
- for(var/atom/A in loc)
- A.blob_act(src)
- if(iswallturf(loc))
- loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
-
-/obj/structure/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
- var/obj/effect/temp_visual/blob/O = new /obj/effect/temp_visual/blob(src.loc)
- O.setDir(dir)
- if(controller)
- var/mob/camera/blob/BO = controller
- O.color = BO.blobstrain.color
- O.alpha = 200
- else if(overmind)
- O.color = overmind.blobstrain.color
- if(A)
- O.do_attack_animation(A) //visually attack the whatever
- return O //just in case you want to do something to the animation.
-
-/obj/structure/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1)
- if(!T)
- var/list/dirs = list(1,2,4,8)
- for(var/i = 1 to 4)
- var/dirn = pick(dirs)
- dirs.Remove(dirn)
- T = get_step(src, dirn)
- if(!(locate(/obj/structure/blob) in T))
- break
- else
- T = null
- if(!T)
- return 0
- var/make_blob = TRUE //can we make a blob?
-
- if(isspaceturf(T) && !(locate(/obj/structure/lattice) in T) && prob(80))
- make_blob = FALSE
- playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) //Let's give some feedback that we DID try to spawn in space, since players are used to it
-
- ConsumeTile() //hit the tile we're in, making sure there are no border objects blocking us
- if(!T.CanPass(src, get_dir(T, src))) //is the target turf impassable
- make_blob = FALSE
- T.blob_act(src) //hit the turf if it is
- for(var/atom/A in T)
- if(!A.CanPass(src, get_dir(T, src))) //is anything in the turf impassable
- make_blob = FALSE
- A.blob_act(src) //also hit everything in the turf
-
- if(make_blob) //well, can we?
- var/obj/structure/blob/B = new /obj/structure/blob/normal(src.loc, (controller || overmind))
- B.density = TRUE
- if(T.Enter(B,src)) //NOW we can attempt to move into the tile
- B.density = initial(B.density)
- B.forceMove(T)
- B.update_appearance()
- if(B.overmind && expand_reaction)
- B.overmind.blobstrain.expand_reaction(src, B, T, controller)
- return B
- else
- blob_attack_animation(T, controller)
- T.blob_act(src) //if we can't move in hit the turf again
- qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile
- return null
- else
- blob_attack_animation(T, controller) //if we can't, animate that we attacked
- return null
-
-/obj/structure/blob/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if(severity > 0)
- if(overmind)
- overmind.blobstrain.emp_reaction(src, severity)
- if(prob(100 - severity * 30))
- new /obj/effect/temp_visual/emp(get_turf(src))
-
-/obj/structure/blob/zap_act(power)
- . = ..()
- if(overmind)
- if(overmind.blobstrain.tesla_reaction(src, power))
- take_damage(power/400, BURN, "energy")
- else
- take_damage(power/400, BURN, "energy")
-
-/obj/structure/blob/extinguish()
- ..()
- if(overmind)
- overmind.blobstrain.extinguish_reaction(src)
-
-/obj/structure/blob/hulk_damage()
- return 15
-
-/obj/structure/blob/attackby(obj/item/I, mob/user, params)
- if(I.tool_behaviour == TOOL_ANALYZER)
- user.changeNext_move(CLICK_CD_MELEE)
- to_chat(user, "The analyzer beeps once, then reports:
")
- SEND_SOUND(user, sound('sound/machines/ping.ogg'))
- if(overmind)
- to_chat(user, "Progress to Critical Mass: [overmind.blobs_legit.len]/[overmind.blobwincount].")
- to_chat(user, chemeffectreport(user).Join("\n"))
- else
- to_chat(user, "Blob core neutralized. Critical mass no longer attainable.")
- to_chat(user, typereport(user).Join("\n"))
- else
- return ..()
-
-/obj/structure/blob/proc/chemeffectreport(mob/user)
- RETURN_TYPE(/list)
- . = list()
- if(overmind)
- . += list("Material: [overmind.blobstrain.name].",
- "Material Effects: [overmind.blobstrain.analyzerdescdamage]",
- "Material Properties: [overmind.blobstrain.analyzerdesceffect]")
- else
- . += "No Material Detected!"
-
-/obj/structure/blob/proc/typereport(mob/user)
- RETURN_TYPE(/list)
- return list("Blob Type: [uppertext(initial(name))]",
- "Health: [obj_integrity]/[max_integrity]",
- "Effects: [scannerreport()]")
-
-
-/obj/structure/blob/attack_animal(mob/living/simple_animal/M)
- if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut
- return
- ..()
-
-/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
- switch(damage_type)
- if(BRUTE)
- if(damage_amount)
- playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE)
- else
- playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
- if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
-
-/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
- switch(damage_type)
- if(BRUTE)
- damage_amount *= brute_resist
- if(BURN)
- damage_amount *= fire_resist
- if(CLONE)
- else
- return 0
- var/armor_protection = 0
- if(damage_flag)
- armor_protection = armor.getRating(damage_flag)
- damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
- if(overmind && damage_flag)
- damage_amount = overmind.blobstrain.damage_reaction(src, damage_amount, damage_type, damage_flag)
- return damage_amount
-
-/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
- . = ..()
- if(. && obj_integrity > 0)
- update_appearance()
-
-/obj/structure/blob/obj_destruction(damage_flag)
- if(overmind)
- overmind.blobstrain.death_reaction(src, damage_flag)
- ..()
-
-/obj/structure/blob/proc/change_to(type, controller)
- if(!ispath(type))
- CRASH("change_to(): invalid type for blob")
- var/obj/structure/blob/B = new type(src.loc, controller)
- B.creation_action()
- B.update_appearance()
- B.setDir(dir)
- qdel(src)
- return B
-
-/obj/structure/blob/examine(mob/user)
- . = ..()
- var/datum/atom_hud/hud_to_check = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
- if(user.research_scanner || hud_to_check.hudusers[user])
- . += "Your HUD displays an extensive report...
"
- if(overmind)
- . += overmind.blobstrain.examine(user)
- else
- . += "Core neutralized. Critical mass no longer attainable."
- . += chemeffectreport(user)
- . += typereport(user)
- else
- if((user == overmind || isobserver(user)) && overmind)
- . += overmind.blobstrain.examine(user)
- . += "It seems to be made of [get_chem_name()]."
-
-/obj/structure/blob/proc/scannerreport()
- return "A generic blob. Looks like someone forgot to override this proc, adminhelp this."
-
-/obj/structure/blob/proc/get_chem_name()
- if(overmind)
- return overmind.blobstrain.name
- return "some kind of organic tissue"
-
-/obj/structure/blob/normal
- name = "normal blob"
- icon_state = "blob"
- light_range = 0
- obj_integrity = 21 //doesn't start at full health
- max_integrity = 25
- health_regen = 1
- brute_resist = 0.25
-
-/obj/structure/blob/normal/scannerreport()
- if(obj_integrity <= 15)
- return "Currently weak to brute damage."
- return "N/A"
-
-/obj/structure/blob/normal/update_name()
- . = ..()
- name = "[(obj_integrity <= 15) ? "fragile " : (overmind ? null : "dead ")][initial(name)]"
-
-/obj/structure/blob/normal/update_desc()
- . = ..()
- if(obj_integrity <= 15)
- desc = "A thin lattice of slightly twitching tendrils."
- else if(overmind)
- desc = "A thick wall of writhing tendrils."
- else
- desc = "A thick wall of lifeless tendrils."
-
-/obj/structure/blob/normal/update_icon_state()
- icon_state = "blob[(obj_integrity <= 15) ? "_damaged" : null]"
-
- /// - [] TODO: Move this elsewhere
- if(obj_integrity <= 15)
- brute_resist = 0.5
- else if (overmind)
- brute_resist = 0.25
- else
- brute_resist = 0.25
- return ..()
diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm
deleted file mode 100644
index 6a1ccb1dd465..000000000000
--- a/code/modules/antagonists/blob/structures/core.dm
+++ /dev/null
@@ -1,70 +0,0 @@
-/obj/structure/blob/core
- name = "blob core"
- icon = 'icons/mob/blob.dmi'
- icon_state = "blank_blob"
- desc = "A huge, pulsating yellow mass."
- max_integrity = 400
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90)
- explosion_block = 6
- point_return = -1
- health_regen = 0 //we regen in Life() instead of when pulsed
- resistance_flags = LAVA_PROOF
-
-/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, placed = 0)
- GLOB.blob_cores += src
- START_PROCESSING(SSobj, src)
- GLOB.poi_list |= src
- update_appearance() //so it atleast appears
- if(!placed && !overmind)
- return INITIALIZE_HINT_QDEL
- if(overmind)
- update_appearance()
- . = ..()
-
-/obj/structure/blob/core/Destroy()
- GLOB.blob_cores -= src
- GLOB.poi_list -= src
- if(overmind)
- overmind.blob_core = null
- overmind = null
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/structure/blob/core/scannerreport()
- return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
-
-/obj/structure/blob/core/update_overlays()
- . = ..()
- var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
- if(overmind)
- blob_overlay.color = overmind.blobstrain.color
- . += blob_overlay
- . += mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay")
-
-/obj/structure/blob/core/update_appearance()
- color = null
- return ..()
-
-/obj/structure/blob/core/ex_act(severity, target)
- var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
- take_damage(damage, BRUTE, "bomb", 0)
-
-/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1)
- . = ..()
- if(obj_integrity > 0)
- if(overmind) //we should have an overmind, but...
- overmind.update_health_hud()
-
-/obj/structure/blob/core/process()
- if(QDELETED(src))
- return
- if(!overmind)
- qdel(src)
- if(overmind)
- overmind.blobstrain.core_process()
- overmind.update_health_hud()
- Pulse_Area(overmind, 12, 4, 3)
- for(var/obj/structure/blob/normal/B in range(1, src))
- if(prob(5))
- B.change_to(/obj/structure/blob/shield/core, overmind)
- ..()
diff --git a/code/modules/antagonists/blob/structures/factory.dm b/code/modules/antagonists/blob/structures/factory.dm
deleted file mode 100644
index 138a359e72e8..000000000000
--- a/code/modules/antagonists/blob/structures/factory.dm
+++ /dev/null
@@ -1,47 +0,0 @@
-/obj/structure/blob/factory
- name = "factory blob"
- icon = 'icons/mob/blob.dmi'
- icon_state = "blob_factory"
- desc = "A thick spire of tendrils."
- max_integrity = 200
- health_regen = 1
- point_return = 25
- resistance_flags = LAVA_PROOF
- var/list/spores = list()
- var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null
- var/max_spores = 3
- var/spore_delay = 0
- var/spore_cooldown = 80 //8 seconds between spores and after spore death
-
-
-/obj/structure/blob/factory/scannerreport()
- if(naut)
- return "It is currently sustaining a blobbernaut, making it fragile and unable to produce blob spores."
- return "Will produce a blob spore every few seconds."
-
-/obj/structure/blob/factory/Destroy()
- for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores)
- if(spore.factory == src)
- spore.factory = null
- if(naut)
- naut.factory = null
- to_chat(naut, "Your factory was destroyed! You feel yourself dying!")
- naut.throw_alert("nofactory", /atom/movable/screen/alert/nofactory)
- spores = null
- return ..()
-
-/obj/structure/blob/factory/Be_Pulsed()
- . = ..()
- if(naut)
- return
- if(spores.len >= max_spores)
- return
- if(spore_delay > world.time)
- return
- flick("blob_factory_glow", src)
- spore_delay = world.time + spore_cooldown
- var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
- if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore
- BS.overmind = overmind
- BS.update_icons()
- overmind.blob_mobs.Add(BS)
diff --git a/code/modules/antagonists/blob/structures/node.dm b/code/modules/antagonists/blob/structures/node.dm
deleted file mode 100644
index de7c674b807c..000000000000
--- a/code/modules/antagonists/blob/structures/node.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-/obj/structure/blob/node
- name = "blob node"
- icon = 'icons/mob/blob.dmi'
- icon_state = "blank_blob"
- desc = "A large, pulsating yellow mass."
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90)
- health_regen = 3
- point_return = 25
- resistance_flags = LAVA_PROOF
-
-
-/obj/structure/blob/node/Initialize()
- GLOB.blob_nodes += src
- START_PROCESSING(SSobj, src)
- . = ..()
-
-/obj/structure/blob/node/scannerreport()
- return "Gradually expands and sustains nearby blob spores and blobbernauts."
-
-/obj/structure/blob/node/update_icon()
- color = null
- return ..()
-
-/obj/structure/blob/special/node/update_overlays()
- . = ..()
- var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
- if(overmind)
- blob_overlay.color = overmind.blobstrain.color
- . += blob_overlay
- . += mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay")
-
-/obj/structure/blob/node/Destroy()
- GLOB.blob_nodes -= src
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/structure/blob/node/process()
- if(overmind)
- Pulse_Area(overmind, 10, 3, 2)
diff --git a/code/modules/antagonists/blob/structures/resource.dm b/code/modules/antagonists/blob/structures/resource.dm
deleted file mode 100644
index 81eda7f4edde..000000000000
--- a/code/modules/antagonists/blob/structures/resource.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-/obj/structure/blob/resource
- name = "resource blob"
- icon = 'icons/mob/blob.dmi'
- icon_state = "blob_resource"
- desc = "A thin spire of slightly swaying tendrils."
- max_integrity = 60
- point_return = 15
- resistance_flags = LAVA_PROOF
- var/resource_delay = 0
-
-/obj/structure/blob/resource/scannerreport()
- return "Gradually supplies the blob with resources, increasing the rate of expansion."
-
-/obj/structure/blob/resource/creation_action()
- if(overmind)
- overmind.resource_blobs += src
-
-/obj/structure/blob/resource/Destroy()
- if(overmind)
- overmind.resource_blobs -= src
- return ..()
-
-/obj/structure/blob/resource/Be_Pulsed()
- . = ..()
- if(resource_delay > world.time)
- return
- flick("blob_resource_glow", src)
- if(overmind)
- overmind.add_points(1)
- resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
- else
- resource_delay = world.time + 40
diff --git a/code/modules/antagonists/blob/structures/shield.dm b/code/modules/antagonists/blob/structures/shield.dm
deleted file mode 100644
index f0a01f70e1a8..000000000000
--- a/code/modules/antagonists/blob/structures/shield.dm
+++ /dev/null
@@ -1,49 +0,0 @@
-/obj/structure/blob/shield
- name = "strong blob"
- icon = 'icons/mob/blob.dmi'
- icon_state = "blob_shield"
- desc = "A solid wall of slightly twitching tendrils."
- var/damaged_desc = "A wall of twitching tendrils."
- max_integrity = 150
- brute_resist = 0.25
- explosion_block = 3
- point_return = 4
- atmosblock = TRUE
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
-
-/obj/structure/blob/shield/scannerreport()
- if(atmosblock)
- return "Will prevent the spread of atmospheric changes."
- return "N/A"
-
-/obj/structure/blob/shield/core
- point_return = 0
-
-/obj/structure/blob/shield/update_name(updates)
- . = ..()
- name = "[(obj_integrity < (max_integrity * 0.5)) ? "weakened " : null][initial(name)]"
-
-/obj/structure/blob/shield/update_desc(updates)
- . = ..()
- desc = (obj_integrity < (max_integrity * 0.5)) ? "[damaged_desc]" : initial(desc)
-
-/obj/structure/blob/shield/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir)
- . = ..()
- if(. && obj_integrity > 0)
- atmosblock = obj_integrity < (max_integrity * 0.5)
- air_update_turf(TRUE, atmosblock)
-
-/obj/structure/blob/shield/update_icon_state()
- icon_state = "[initial(icon_state)][(obj_integrity < (max_integrity * 0.5)) ? "_damaged" : null]"
- return ..()
-
-/obj/structure/blob/shield/reflective
- name = "reflective blob"
- desc = "A solid wall of slightly twitching tendrils with a reflective glow."
- damaged_desc = "A wall of twitching tendrils with a reflective glow."
- icon_state = "blob_glow"
- flags_ricochet = RICOCHET_SHINY
- point_return = 8
- max_integrity = 100
- brute_resist = 0.5
- explosion_block = 2
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 53d17bf325aa..69c9248d6fb9 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -423,7 +423,7 @@
return 0
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands)
- . = list()
+ . = ..()
if(!isinhands && current_charges)
. += mutable_appearance('icons/effects/cult_effects.dmi', "shield-cult", MOB_LAYER + 0.01)
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
index ace534f94dfa..ff744ef05840 100644
--- a/code/modules/antagonists/devil/devil.dm
+++ b/code/modules/antagonists/devil/devil.dm
@@ -423,42 +423,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
check_regression()
/datum/antagonist/devil/proc/create_new_body()
- if(GLOB.blobstart.len > 0)
- var/turf/targetturf = get_turf(pick(GLOB.blobstart))
- var/mob/currentMob = owner.current
- if(!currentMob)
- currentMob = owner.get_ghost()
- if(!currentMob)
- message_admins("[key_name_admin(owner)]'s devil resurrection failed due to client logoff. Aborting.")
- return -1
- if(currentMob.mind != owner)
- message_admins("[key_name_admin(owner)]'s devil resurrection failed due to becoming a new mob. Aborting.")
- return -1
- currentMob.change_mob_type(/mob/living/carbon/human, targetturf, null, 1)
- var/mob/living/carbon/human/H = owner.current
- H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/civilian/lawyer/black(H), ITEM_SLOT_ICLOTHING)
- H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), ITEM_SLOT_FEET)
- H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), ITEM_SLOT_HANDS)
- H.equip_to_slot_or_del(new /obj/item/pen(H), ITEM_SLOT_LPOCKET)
- if(SOULVALUE >= BLOOD_THRESHOLD)
- H.set_species(/datum/species/lizard, 1)
- H.underwear = "Nude"
- H.undershirt = "Nude"
- H.socks = "Nude"
- H.dna.features["mcolor"] = "511"
- H.regenerate_icons()
- if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
- var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(targetturf)
- A.faction |= "hell"
- H.forceMove(A)
- A.oldform = H
- owner.transfer_to(A, TRUE)
- A.set_devil_name()
- if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
- A.convert_to_archdevil()
- else
- CRASH("Unable to find a blobstart landmark for hellish resurrection")
-
+ CRASH("Unable to find a blobstart landmark for hellish resurrection")
/datum/antagonist/devil/proc/update_hud()
if(iscarbon(owner.current))
diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm
index 8f73b93f8fd4..df962f83e814 100644
--- a/code/modules/antagonists/gang/gang.dm
+++ b/code/modules/antagonists/gang/gang.dm
@@ -301,12 +301,7 @@
antag_hud_name = "Tojo"
/datum/antagonist/gang/yakuza/check_gang_objective()
- var/datum/station_state/current_state = new /datum/station_state()
- current_state.count()
- var/station_integrity = min(PERCENT(GLOB.start_state.score(current_state)), 100)
- if(station_integrity < 85)
- return FALSE
- return TRUE
+ CRASH("Sorry this was cruft")
/datum/antagonist/gang/jackbros
name = "Jack Bros"
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 648803185661..920fbbdd557b 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -436,11 +436,6 @@
else
. = timer_set
-/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B)
- if(exploding)
- return
- qdel(src)
-
/obj/machinery/nuclearbomb/zap_act(power, zap_flags)
..()
if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 084176f4b8c4..81f8facd465a 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -176,9 +176,6 @@
/mob/living/simple_animal/revenant/ex_act(severity, target)
return 1 //Immune to the effects of explosions.
-/mob/living/simple_animal/revenant/blob_act(obj/structure/blob/B)
- return //blah blah blobs aren't in tune with the spirit world, or something.
-
/mob/living/simple_animal/revenant/singularity_act()
return //don't walk into the singularity expecting to find corpses, okay?
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
deleted file mode 100644
index 26c6b4823dce..000000000000
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Code for the Wild West map by Brotemis
- * Contains:
- * Wish Granter
- * Meat Grinder
- */
-
-///////////////Meatgrinder//////////////
-
-
-/obj/effect/meatgrinder
- name = "Meat Grinder"
- desc = "What is that thing?"
- density = TRUE
- anchored = TRUE
- icon = 'icons/mob/blob.dmi'
- icon_state = "blobpod"
- var/triggered = 0
-
-/obj/effect/meatgrinder/Initialize()
- . = ..()
- var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
- )
- AddElement(/datum/element/connect_loc, loc_connections)
-
-/obj/effect/meatgrinder/proc/on_entered(datum/source, atom/movable/AM)
- SIGNAL_HANDLER
- Bumped(AM)
-
-/obj/effect/meatgrinder/Bumped(atom/movable/AM)
-
- if(triggered)
- return
- if(!ishuman(AM))
- return
-
- var/mob/living/carbon/human/M = AM
-
- if(M.stat != DEAD && M.ckey)
- visible_message("[M] triggered [src]!")
- triggered = 1
-
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- explosion(M, 1, 0, 0, 0)
- qdel(src)
diff --git a/code/modules/cargo/blackmarket/blackmarket_item.dm b/code/modules/cargo/blackmarket/blackmarket_item.dm
index cbadddcf1221..00ed7e3bee38 100644
--- a/code/modules/cargo/blackmarket/blackmarket_item.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_item.dm
@@ -24,23 +24,56 @@
var/stock_min = 1
/// Maximum amount that there should be of this item in the market if generated randomly.
var/stock_max = 0
+ /// Whether the item is visible and purchasable on the market
+ var/available = TRUE
/// Probability for this item to be available. Used by SSblackmarket on init.
var/availability_prob = 0
+ /// If this item should be more or less likely to spawn than usual. Positive is more likely, negative is less
+ var/weight = 0
+ /// If this item is affected by avalibility weight. For items that shouldnt appear on their own (paired items), should always appear, or items paticularly rare or powerful that we dont want showing up too often
+ var/spawn_weighting
// Should there be an unlimited stock of an item
var/unlimited = FALSE
/// Should another item spawn alongside this one in the catalogue?
- var/datum/blackmarket_item/pair_item
+ var/list/pair_item = null
+
/datum/blackmarket_item/New()
if(isnull(price))
- price = rand(price_min, price_max)
+ randomize_price()
if(isnull(stock))
- stock = rand(stock_min, stock_max)
+ randomize_stock()
+ if(isnull(spawn_weighting))
+ if(availability_prob == 0 || availability_prob == 100)
+ spawn_weighting = FALSE
+ else
+ spawn_weighting = TRUE
/// Used for spawning the wanted item, override if you need to do something special with the item.
/datum/blackmarket_item/proc/spawn_item(loc)
return new item(loc)
+/datum/blackmarket_item/proc/randomize_price()
+ price = rand(price_min, price_max)
+
+/datum/blackmarket_item/proc/randomize_stock()
+ stock = rand(stock_min, stock_max)
+
+/datum/blackmarket_item/proc/cycle(price = TRUE, availibility = TRUE, stock = FALSE, force_appear = FALSE)
+ if(price)
+ randomize_price()
+ if(stock)
+ randomize_stock()
+ if(availibility)
+ if(spawn_weighting ? prob(max(0, (availability_prob + (weight * 10)))) : prob(availability_prob))
+ available = TRUE
+ weight--
+ else
+ available = FALSE
+ weight++
+ if(force_appear)
+ available = TRUE
+
/// Buys the item and makes SSblackmarket handle it.
/datum/blackmarket_item/proc/buy(obj/item/blackmarket_uplink/uplink, mob/buyer, shipping_method)
// Sanity
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/ammo.dm b/code/modules/cargo/blackmarket/blackmarket_items/ammo.dm
new file mode 100644
index 000000000000..dcc5c56b93c2
--- /dev/null
+++ b/code/modules/cargo/blackmarket/blackmarket_items/ammo.dm
@@ -0,0 +1,221 @@
+/datum/blackmarket_item/ammo
+ category = "Ammunition"
+
+/datum/blackmarket_item/ammo/shotgun_dart
+ name = "Shotgun Dart"
+ desc = "These handy darts can be filled up with any chemical and be shot with a shotgun! \
+ Prank your friends by shooting them with laughter! \
+ Not recommended for comercial use."
+ item = /obj/item/ammo_casing/shotgun/dart
+
+ price_min = 10
+ price_max = 50
+ stock_min = 10
+ stock_max = 60
+ availability_prob = 40
+
+/datum/blackmarket_item/ammo/himehabu_mag
+ name = "Himehabu Magazines"
+ desc = "Compact 10 round .22 LR magazines for use in the Himehabu pistol."
+ item = /obj/item/ammo_box/magazine/m22lr
+
+ price_min = 100
+ price_max = 200
+ stock_min = 6
+ stock_max = 10
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/himehabu_box
+ name = ".22 LR Ammo Box"
+ desc = "A 75 round ammo box of .22 LR. Trust me, you'll need every shot."
+ item = /obj/item/ammo_box/c22lr_box
+
+ price_min = 100
+ price_max = 300
+ stock_min = 6
+ stock_max = 10
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/a357_box
+ name = ".357 Ammo Box"
+ desc = "A 50 round ammo box of .357."
+ item = /obj/item/ammo_box/a357_box
+
+ price_min = 150
+ price_max = 500
+ stock_min = 3
+ stock_max = 6
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/e40_mag
+ name = "Eoehoma .299 Caseless Magazine"
+ desc = "A 30 round magazine for the E-40 Hybrid Rifle."
+ item = /obj/item/ammo_box/magazine/e40
+
+ price_min = 750
+ price_max = 1250
+ stock = 6
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/saber_mag
+ name = "Saber 9mm SMG Magazines"
+ desc = "Magazines for use in the Saber 9mm SMG. No, they don't work as swords."
+ item = /obj/item/ammo_box/magazine/smgm9mm
+
+ price_min = 500
+ price_max = 1000
+ stock_min = 4
+ stock_max = 6
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/model_h_mag
+ name = "Model H Magazine"
+ desc = "A 10 round magazine for Model H slug pistol."
+ item = /obj/item/ammo_box/magazine/modelh
+
+ price_min = 500
+ price_max = 1000
+ stock_max = 4
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/sgg_stripper
+ name = "8x58mm Stripper Clip"
+ desc = "A five round 8x58mm stripper clip for use with the SGG-669C. Also doubles as a paperweight, because of course it does. Fucking Solarians."
+ item = /obj/item/ammo_box/a858
+
+ price_min = 500
+ price_max = 1000
+ stock_min = 6
+ stock_max = 8
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/pistole_c_mag
+ name = "5.56 Caseless Magazine"
+ desc = "A 12 round magazine for the Pistole Cheese."
+ item = /obj/item/ammo_box/magazine/pistol556mm
+
+ price_min = 250
+ price_max = 750
+ stock = 2
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/proto_gauss_mag
+ name = "Prototype Gauss Rifle Magazine"
+ desc = "A 25 round ferromagnetic pellet magazine for the prototype gauss rifle. Choking hazard, keep pellets away from children under the age of 5."
+ item = /obj/item/ammo_box/magazine/gauss
+
+ price_min = 500
+ price_max = 800
+ stock_min = 3
+ stock_max = 5
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/tec_mag
+ name = "TEC-9 AP Magazine"
+ desc = "A 20 round magazine of AP ammo for the TEC-9 machine pistol."
+ item = /obj/item/ammo_box/magazine/tec9
+
+ price_min = 500
+ price_max = 1000
+ stock_min = 3
+ stock_max = 5
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/polymer_clip
+ name = "7.62 Stripper Clip"
+ desc = "A 5 round stripper clip of 7.62x40mm CLIP."
+ item = /obj/item/ammo_box/a762_stripper
+
+ price_min = 500
+ price_max = 750
+ stock_min = 4
+ stock_max = 6
+ availability_prob = 0
+
+/datum/blackmarket_item/ammo/carbine_mag
+ name = "SKM-24v Magazine"
+ desc = "A 30 round magazine of 4.6x30mm for the SKM-24v. A hermit classic."
+ item = /obj/item/ammo_box/magazine/skm_545_39
+
+ price_min = 500
+ price_max = 1000
+ stock_min = 3
+ stock_max = 5
+ availability_prob = 40
+
+/datum/blackmarket_item/ammo/skm_extended
+ name = "Extended SKM Magazine"
+ desc = "An extended 40 round 7.62x40mm CLIP magazine for the SKM family of assault rifles. Extra curves mean extra ammo."
+ item = /obj/item/ammo_box/magazine/skm_762_40/extended
+
+ price_min = 1000
+ price_max = 3000
+ stock_max = 4
+ availability_prob = 40
+
+/datum/blackmarket_item/ammo/skm_drum
+ name = "SKM Drum Magazine"
+ desc = "Do you have too much ammo on your hands? Do you have someone you really hate? \
+ Do you want them to be absolutely suppressed for the next 15 seconds? \
+ This 75 round 7.62x40mm CLIP drum magazine is perfect for you! (SKM not included.)"
+ item = /obj/item/ammo_box/magazine/skm_762_40/drum
+
+ price_min = 1500
+ price_max = 3500
+ stock = 2
+ availability_prob = 20
+
+/datum/blackmarket_item/ammo/damaged_cell
+ name = "Discount Weapon Power Cells"
+ desc = "These cells got a little banged up during a raid by GOLD authorities, but they still should be safe to use. Probably."
+ item = /obj/item/stock_parts/cell/gun
+
+ price_min = 100
+ price_max = 400
+ stock_min = 5
+ stock_max = 10
+ availability_prob = 80
+
+/datum/blackmarket_item/ammo/damaged_cell/spawn_item(loc)
+ var/obj/item/stock_parts/cell/damaged_cell = ..()
+ damaged_cell.name = "dented weapon power cell"
+ damaged_cell.desc = "A rechargeable electrochemical power cell. This one doesn't appear to be in the greatest condition."
+ if(prob(35))
+ damaged_cell.rigged = TRUE
+ damaged_cell.show_rigged = FALSE
+
+ return new damaged_cell(loc)
+
+/datum/blackmarket_item/ammo/advanced_weapon_cell
+ name = "Upgraded Weapon Power Cells"
+ desc = "These upgraded weapon powercells come with twice the capacity of the standard cells, and quality checked to make sure they won't explode!"
+ item = /obj/item/stock_parts/cell/gun/upgraded
+
+ price_min = 1000
+ price_max = 1750
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 25
+
+/datum/blackmarket_item/ammo/huge_weapon_cell
+ name = "Extra Large Weapon Power Cells"
+ desc = "We're way past double A now. These extra-large power cells (in both charge and size!) are purpose built for the most heavy duty energy weapons."
+ item = /obj/item/stock_parts/cell/gun/large
+
+ price_min = 2500
+ price_max = 4000
+ stock = 2
+ availability_prob = 20
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/ammo/mecha_hades_ammo
+ name = "FNX-99 Incediary Ammo"
+ desc = "A box of 24 incendiary shells for the FNX-99 mounted carbine."
+ item = /obj/item/mecha_ammo/incendiary
+
+ price_min = 250
+ price_max = 350
+ stock_min = 3
+ stock_max = 5
+ availability_prob = 0
+
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm b/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm
index a4c4195beca0..d049589fe40a 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm
@@ -11,6 +11,27 @@
stock_max = 5
availability_prob = 80
+/datum/blackmarket_item/clothing/straitjacket
+ name = "Straitjacket"
+ desc = "These straitjackets might be a tight fit, but you can certain the poor sod wont be getting away anytime soon."
+ item = /obj/item/clothing/suit/straight_jacket
+
+ price_min = 500
+ price_max = 1000
+ stock_max = 3
+ availability_prob = 40
+
+/datum/blackmarket_item/clothing/surplus_uniform
+ name = "Surplus Combat Uniforms"
+ desc = "A mass produced and non-descript surplus combat uniform. For when you need to look like another faceless thug in the crowd."
+ item = /obj/item/clothing/under/rank/security/officer/military
+
+ price_min = 50
+ price_max = 200
+ stock_min = 5
+ stock_max = 10
+ availability_prob = 80
+
/datum/blackmarket_item/clothing/crown
name = "Crown"
desc = "A beautiful golden crown, rich with history and pedigree. Better worn than left to collect dust in a museum, right?"
@@ -18,7 +39,7 @@
price_min = 1000
price_max = 2000
- stock_max = 1
+ stock = 1
availability_prob = 20
/datum/blackmarket_item/clothing/galaxy_blue
@@ -98,11 +119,31 @@
new /obj/item/clothing/head/helmet/bulletproof/x11/frontier(B)
return B
+/datum/blackmarket_item/clothing/frontiersmen_armor_fireproof
+ name = "Fireproof Armor Set"
+ desc = "Get it while it's hot! This fireproofed armor and uniform set is made with a pre-Night Of Fire miracle material that renders it almost impervious to flames. The Frontiersmen swear by the stuff. It's kept each of it's previous owners safe until they passed away from illness."
+ item = /obj/item/storage/box
+
+ price_min = 1000
+ price_max = 1750
+ stock_max = 3
+ availability_prob = 50
+
+/datum/blackmarket_item/clothing/frontiersmen_armor_fireproof/spawn_item(loc)
+ var/obj/item/storage/box/B = ..()
+ B.name = "Fireproof Armor Set Box"
+ B.desc = "A singed box with some folded clothes and a helmet inside."
+ new /obj/item/clothing/suit/armor/frontier/fireproof(B)
+ new /obj/item/clothing/head/helmet/bulletproof/x11/frontier/fireproof(B)
+ new /obj/item/clothing/under/frontiersmen/fireproof(B)
+ new /obj/item/clothing/mask/gas/frontiersmen(B)
+ return B
+
/datum/blackmarket_item/clothing/gezena_armor
name = "Raksha-Plating vest"
desc = "Genuine armor vests used by the PGF Marine Corp. If a military guy in a cape comes by, play dumb."
item = /obj/item/clothing/suit/armor/gezena/marine
- pair_item = /datum/blackmarket_item/clothing/gezena_helmet
+ pair_item = list(/datum/blackmarket_item/clothing/gezena_helmet)
price_min = 750
price_max = 1250
@@ -137,6 +178,54 @@
new /obj/item/clothing/head/helmet/space(B)
return B
+/datum/blackmarket_item/clothing/syndie_spacesuit_set
+ name = "\improper Syndicate Branded Spacesuit Box"
+ desc = "An armored syndicate softsuit, popular among the ACLF operatives who were too broke to get an actual hardsuit."
+ item = /obj/item/storage/box/syndie_kit
+
+ price_min = 750
+ price_max = 2500
+ stock_max = 3
+ availability_prob = 50
+
+/datum/blackmarket_item/clothing/syndie_spacesuit_set/spawn_item(loc)
+ var/obj/item/storage/box/syndie_kit/B = ..()
+ B.name = "Spacesuit Box"
+ B.desc = "It has a Syndicate logo on it."
+ var/suit_color = pick(list("red","green","dark green","blue","orange","black","black-green","black-blue","black-orange","black-red"))
+ switch(suit_color)
+ if("red")
+ new /obj/item/clothing/head/helmet/space/syndicate(B)
+ new /obj/item/clothing/suit/space/syndicate(B)
+ if("green")
+ new /obj/item/clothing/head/helmet/space/syndicate/green(B)
+ new /obj/item/clothing/suit/space/syndicate/green(B)
+ if("dark-green")
+ new /obj/item/clothing/head/helmet/space/syndicate/green/dark(B)
+ new /obj/item/clothing/suit/space/syndicate/green/dark(B)
+ if("orange")
+ new /obj/item/clothing/head/helmet/space/syndicate/orange(B)
+ new /obj/item/clothing/suit/space/syndicate/orange(B)
+ if("blue")
+ new /obj/item/clothing/head/helmet/space/syndicate/blue(B)
+ new /obj/item/clothing/suit/space/syndicate/blue(B)
+ if("black")
+ new /obj/item/clothing/head/helmet/space/syndicate/black(B)
+ new /obj/item/clothing/suit/space/syndicate/black(B)
+ if("black-green")
+ new /obj/item/clothing/head/helmet/space/syndicate/black/green(B)
+ new /obj/item/clothing/suit/space/syndicate/black/green(B)
+ if("black-blue")
+ new /obj/item/clothing/head/helmet/space/syndicate/black/blue(B)
+ new /obj/item/clothing/suit/space/syndicate/black/blue(B)
+ if("black-orange")
+ new /obj/item/clothing/head/helmet/space/syndicate/black/orange(B)
+ new /obj/item/clothing/suit/space/syndicate/black/orange(B)
+ if("black-red")
+ new /obj/item/clothing/head/helmet/space/syndicate/black/red(B)
+ new /obj/item/clothing/suit/space/syndicate/black/red(B)
+ return B
+
/datum/blackmarket_item/clothing/chameleon_hat
name = "Chameleon Hat"
desc = "Pick any hat you want with this Handy device. Not Quality Tested."
@@ -147,6 +236,17 @@
stock_max = 2
availability_prob = 70
+/datum/blackmarket_item/clothing/cham_kit
+ name = "Chameleon Kit"
+ desc = "Not sure what to wear? This adaptive set of clothing can change to suit whatever you desire! Quality tested."
+ item = /obj/item/storage/box/syndie_kit/chameleon
+
+ price_min = 1000
+ price_max = 2500
+ stock_max = 2
+ availability_prob = 10
+ spawn_weighting = FALSE
+
/datum/blackmarket_item/clothing/combatmedic_suit
name = "Combat Medic Hardsuit"
desc = "A discarded combat medic hardsuit, found in the ruins of a carpet bombed xeno hive. Definitely used, but as sturdy as an anchor."
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm b/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm
index e3a20e0e8905..2d9f3af83c06 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm
@@ -17,10 +17,10 @@
desc = "A random cocktail of luxury drugs that are sure to put a smile on your face!"
item = /obj/item/storage/pill_bottle
- stock_min = 2
- stock_max = 3
- price_min = 200
- price_max = 500
+ stock_min = 4
+ stock_max = 6
+ price_min = 50
+ price_max = 300
availability_prob = 50
/datum/blackmarket_item/consumable/suspicious_pills/spawn_item(loc)
@@ -42,6 +42,28 @@
price_max = 60
availability_prob = 50
+/datum/blackmarket_item/consumable/cannabis
+ name = "Cannabis Leaves"
+ desc = "Homegrown cannabis, fresh off the garden just for your pleasure!"
+ item = /obj/item/reagent_containers/food/snacks/grown/cannabis
+
+ stock_min = 4
+ stock_max = 6
+ price_min = 50
+ price_max = 300
+ availability_prob = 50
+
+/datum/blackmarket_item/consumable/syndie_cigs
+ name = "Syndicate Cigarettes"
+ desc = "Who said smoking was bad for you? These omnizine laced cigarettes will have you feeling like a million bucks!"
+ item = /obj/item/storage/fancy/cigarettes/cigpack_syndicate
+
+ stock_min = 4
+ stock_max = 6
+ price_min = 50
+ price_max = 300
+ availability_prob = 50
+
/datum/blackmarket_item/consumable/trickwine
name = "Trickwine"
desc = "The SRM keeps the recipes for their trickwines a closely guarded secret. The Hunters carrying those bottles? Less so."
@@ -62,16 +84,16 @@
/obj/item/reagent_containers/food/drinks/breakawayflask/vintage/prismwine))
return new trickwine(loc)
+/datum/blackmarket_item/consumable/stimpack
+ name = "Stimpack"
+ desc = "A quick inject medipen loaded with a cocktail of powerful stimulants. Side effects may include nasuea, heartburn, constipation, weight loss, increased blood pressure, kidney stones, liver damage, mood swings, mania, anemia, weight gain, total organ failure, runny nose and minor retinal irritation."
+ item = /obj/item/reagent_containers/hypospray/medipen/stimpack/traitor
-/datum/blackmarket_item/consumable/pumpup
- name = "Maintenance Pump-Up"
- desc = "Resist any Baton stun with this handy instant tetanus free injector!."
- item = /obj/item/reagent_containers/hypospray/medipen/pumpup
-
- stock_max = 3
- price_min = 50
- price_max = 150
- availability_prob = 90
+ stock_min = 4
+ stock_max = 6
+ price_min = 250
+ price_max = 500
+ availability_prob = 50
/datum/blackmarket_item/consumable/morphine
name = "Morphine Bottle"
@@ -80,6 +102,7 @@
price_min = 50
price_max = 150
+ stock_min = 2
stock_max = 4
availability_prob = 50
@@ -88,9 +111,10 @@
desc = "Cyanide, a tried and true classic for all your poisoning needs."
item = /obj/item/reagent_containers/glass/bottle/cyanide
- price_min = 300
- price_max = 600
- stock_max = 3
+ price_min = 200
+ price_max = 400
+ stock_min = 2
+ stock_max = 4
availability_prob = 30
/datum/blackmarket_item/consumable/sodium_thiopental
@@ -98,19 +122,21 @@
desc = "Sodium Thiopental, a potent and fast acting sedative for any occasion."
item = /obj/item/reagent_containers/glass/bottle/sodium_thiopental
- price_min = 300
+ price_min = 250
price_max = 600
- stock_max = 3
+ stock_min = 2
+ stock_max = 4
availability_prob = 30
/datum/blackmarket_item/consumable/amanitin
- name = "Amanitin bottle"
+ name = "Amanitin Bottle"
desc = "A slow acting, but nearly undetectable poison. For the dignified assassin."
item = /obj/item/reagent_containers/glass/bottle/amanitin
price_min = 300
price_max = 600
- stock_max = 3
+ stock_max = 2
+ stock_max = 4
availability_prob = 30
/datum/blackmarket_item/consumable/gumballs
@@ -124,15 +150,21 @@
stock_max = 20
availability_prob = 80
-/datum/blackmarket_item/consumable/xeno_meat
- name = "Xenomorph steak"
+/datum/blackmarket_item/consumable/xeno_corpse
+ name = "Xenomorph Corpse"
desc = "The Frontier's most dangerous game, delivered right to your plate! May constitute a violation of your local BARD laws and regulations."
- item = /obj/item/reagent_containers/food/snacks/meat/slab/xeno
+ item = /mob/living/simple_animal/hostile/alien
- price_min = 300
- price_max = 500
- stock_max = 5
- availability_prob = 20
+ price_min = 6000
+ price_max = 10000
+ stock = 1
+ availability_prob = 10
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/consumable/xeno_corpse/spawn_item(loc)
+ var/mob/living/simple_animal/hostile/alien = ..()
+ alien.stat = DEAD
+ return new alien(loc)
/datum/blackmarket_item/consumable/berries
name = "Berries"
@@ -157,7 +189,7 @@
item = /obj/effect/spawner/lootdrop/ration
price_min = 150
- price_max = 400
+ price_max = 300
availability_prob = 80
unlimited = TRUE
@@ -177,10 +209,10 @@
desc = "A bundle of sutures for stitching up your latest bullet wound."
item = /obj/item/stack/medical/suture
- price_min = 200
- price_max = 450
- stock_min = 2
- stock_max = 5
+ price_min = 25
+ price_max = 150
+ stock_min = 4
+ stock_max = 6
availability_prob = 40
/datum/blackmarket_item/consumable/regen_mesh
@@ -188,10 +220,10 @@
desc = "A smoothing pack of regenerative mesh for your burns."
item = /obj/item/stack/medical/mesh
- price_min = 200
- price_max = 450
- stock_min = 2
- stock_max = 5
+ price_min = 25
+ price_max = 150
+ stock_min = 4
+ stock_max = 6
availability_prob = 40
/datum/blackmarket_item/consumable/bruise_pack
@@ -199,19 +231,53 @@
desc = "A bundle of old bruise packs, for you guessed it, bruises. Any rumors of these containing hazardous chemicals are just that. Rumors."
item = /obj/item/stack/medical/bruise_pack
- price_min = 300
- price_max = 500
- stock_min = 2
- stock_max = 5
+ price_min = 50
+ price_max = 175
+ stock_min = 4
+ stock_max = 6
availability_prob = 30
/datum/blackmarket_item/consumable/ointment
- name = "Burn ointment"
+ name = "Burn Ointment"
desc = "A tube of burn ointment. It's past the expiry date, but those are only suggestions."
item = /obj/item/stack/medical/ointment
- price_min = 300
- price_max = 500
- stock_min = 2
- stock_max = 5
+ price_min = 50
+ price_max = 175
+ stock_min = 4
+ stock_max = 6
availability_prob = 30
+
+/datum/blackmarket_item/consumable/goliath
+ name = "A Live Goliath"
+ desc = "We reappropiated an outpost freighter a week back, and the entire thing was packed with goliaths for whatever reason. Point is, we're sick and tired of eating them, so we're selling what's left so we can buy some actual take out."
+ item = /mob/living/simple_animal/hostile/asteroid/goliath/beast
+
+ price_min = 750
+ price_max = 2000
+ stock_max = 4
+ availability_prob = 15
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/consumable/color_salve
+ name = "Color Salve"
+ desc = "A cosmetic salve used for changing the hue of Elzouse. Now with 20% less harmful chemical dyes!"
+ item = /obj/item/colorsalve
+
+ price_min = 100
+ price_max = 200
+ stock_min = 4
+ stock_max = 10
+ availability_prob = 80
+
+/datum/blackmarket_item/consumable/secret_sauce
+ name = "Family Sauce Recipe"
+ desc = "This used to belong to a good friend of mine before the authorities did em in. Best goddamn sauce I've ever tasted, but I could never get it right myself. Maybe you can do it justice."
+ item = /obj/item/paper/secretrecipe
+
+ price_min = 1000
+ price_max = 2000
+ stock = 1
+ availability_prob = 5
+ spawn_weighting = FALSE
+
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/explosives.dm b/code/modules/cargo/blackmarket/blackmarket_items/explosives.dm
index 7fe78cdcd055..ab7f62830324 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/explosives.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/explosives.dm
@@ -11,6 +11,16 @@
stock_max = 5
availability_prob = 50
+/datum/blackmarket_item/explosive/smoke_grenade
+ name = "Smoke Grenade"
+ desc = "Too much heat on your back? This handy smoke grenade is perfect for a hasty getaway."
+ item = /obj/item/grenade/smokebomb
+
+ price_min = 100
+ price_max = 400
+ stock_max = 5
+ availability_prob = 50
+
/datum/blackmarket_item/explosive/h_e
name = "HE Grenade"
desc = "These high explosive grenades are sure to get some bang for your buck."
@@ -57,7 +67,7 @@
/datum/blackmarket_item/explosive/slipocalypse
name = "Slipocalyse Cluster Bomb"
- desc = "Wash away the opposition with sudstastic grenade!"
+ desc = "Wash away the opposition with this sudstastic grenade!"
item = /obj/item/grenade/clusterbuster/soap
price_min = 500
@@ -65,9 +75,21 @@
stock = 1
availability_prob = 10
+/datum/blackmarket_item/explosive/disco_grenade
+ name = "Portable Disco Grenade"
+ desc = "Become the life of the party with this groovy grenade!"
+ item = /obj/item/grenade/discogrenade
+
+ price_min = 500
+ price_max = 750
+ stock_min = 2
+ stock_max = 3
+ availability_prob = 10
+ spawn_weighting = FALSE
+
/datum/blackmarket_item/explosive/rusted_mine
name = "Landmine"
- desc = "Recovered from a decades old ICW battlefield by our best EOD tech, Nicky Nine Fingers."
+ desc = "Recovered from a decade old ICW battlefield by our best EOD tech, Nicky Nine Fingers."
item = /obj/item/mine/pressure/explosive/rusty
price_min = 250
@@ -84,5 +106,32 @@
price_max = 6500
stock_min = 2
stock_max = 5
- availability_prob = 20
+ availability_prob = 10
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/explosive/live_bomb
+ name = "Active ICW Era Ordinance"
+ desc = "Look, I won't mince words. This thing is counting down and I don't want to be the next causualty of ICW after it's already ended. I'll sell it to you real cheap."
+ item = /obj/machinery/syndicatebomb
+
+ price_min = 500
+ price_max = 1000
+ stock = 1
+ availability_prob = 5
+ spawn_weighting = FALSE
+/datum/blackmarket_item/explosive/live_bomb/spawn_item(loc)
+ var/obj/machinery/syndicatebomb/bomb = ..()
+ bomb.activate()
+ return new bomb(loc)
+
+/datum/blackmarket_item/explosive/firecrackers
+ name = "Firecracker"
+ desc = "Nuclear Bomb brand extra strength firecrackers, painted in the signature blood red of the Gorlex Marauders. Enjoyed a successful, albeit short run in PGF space due to a certain event in 492 FS made selling them somewhat in poor taste."
+ item = /obj/item/grenade/firecracker
+
+ price_min = 50
+ price_max = 250
+ stock_min = 5
+ stock_max = 10
+ availability_prob = 50
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/misc.dm b/code/modules/cargo/blackmarket/blackmarket_items/misc.dm
index a5e2c67175af..8f51514de804 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/misc.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/misc.dm
@@ -56,31 +56,11 @@
desc = "Why, it could be anything. Are you feeling lucky?"
item = /obj/structure/closet/crate/secure/loot
- price_min = 250
- price_max = 400
+ price_min = 100
+ price_max = 300
availability_prob = 100
unlimited = TRUE
-/datum/blackmarket_item/misc/spygass
- name = "Spy Glass Kit"
- desc = "A set of trick glasses and a linked camera. Suit and dashing shades not included."
- item = /obj/item/storage/box/rxglasses/spyglasskit
-
- price_min = 250
- price_max = 1000
- stock_max = 3
- availability_prob = 30
-
-/datum/blackmarket_item/misc/ripley_mk_4
- name = "Ripley Mk IV Upgrade Kit"
- desc = "Pimp out your Ripley to the CLIP Mark IV Rogue Model today! Killjoy bureaucrats not included, thank god."
- item = /obj/item/mecha_parts/mecha_equipment/conversion_kit/ripley/clip
-
- price_min = 1500
- price_max = 2500
- stock_max = 3
- availability_prob = 30
-
/datum/blackmarket_item/misc/secret_docs
name = "Classified Documents"
desc = "Good people died to get these. Luckily, we aren't good people."
@@ -107,3 +87,57 @@
price_max = 10000
stock = 1
availability_prob = 40
+
+/datum/blackmarket_item/misc/knockoff_plush
+ name = "Knockoff T4LI Plush"
+ desc = "You'll hardly be able to tell that it's an offbrand rip off!"
+ item = /obj/item/toy/plush/tali
+
+ price_min = 50
+ price_max = 150
+ stock_min = 2
+ stock_max = 5
+ availability_prob = 60
+
+/datum/blackmarket_item/misc/knockoff_plush/spawn_item(loc)
+ var/obj/item/toy/plush/tali/plush = ..()
+ plush.name = "T3MMI"
+ plush.desc = "A rather shoddy and unlicensed plushie 'paying homage' to a character from the RILENA series."
+ return new plush(loc)
+
+/datum/blackmarket_item/misc/pens
+ name = "Pen"
+ desc = "We found an old Cybersun blacksite, and came across an unmarked crate full of pens. Want one?"
+ item = /obj/item/pen
+
+ price_min = 50
+ price_max = 150
+ unlimited = TRUE
+ availability_prob = 60
+
+/datum/blackmarket_item/misc/pens/spawn_item(loc)
+ var/pen = pick(list(/obj/item/pen,
+ /obj/item/pen/blue,
+ /obj/item/pen/red,
+ /obj/item/pen/fourcolor,
+ /obj/item/pen/fountain,
+ /obj/item/pen/fountain/captain,
+ /obj/item/pen/solgov,
+ /obj/item/pen/fountain/solgov,
+ /obj/item/pen/edagger,
+ /obj/item/pen/survival,
+ /obj/item/pen/sleepy))
+ return new pen(loc)
+
+/datum/blackmarket_item/misc/hexacrete
+ name = "Jug of Hexacrete"
+ desc = "Need to make a blacksite in a jiffy? Skip the fuss with this 150u jug of hexacrete!"
+ item = /obj/item/reagent_containers/glass/chem_jug/hexacrete
+
+ price_min = 750
+ price_max = 1500
+ stock_min = 3
+ stock_max = 10
+ availability_prob = 30
+
+
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/tech.dm b/code/modules/cargo/blackmarket/blackmarket_items/tech.dm
new file mode 100644
index 000000000000..63db8cbb75d1
--- /dev/null
+++ b/code/modules/cargo/blackmarket/blackmarket_items/tech.dm
@@ -0,0 +1,214 @@
+/datum/blackmarket_item/tech
+ category = "Technology"
+
+/datum/blackmarket_item/tech/ripley_mk_4
+ name = "Ripley Mk IV Upgrade Kit"
+ desc = "Pimp out your Ripley to the CLIP Mark IV Rogue Model today! Killjoy bureaucrats not included, thank god."
+ item = /obj/item/mecha_parts/mecha_equipment/conversion_kit/ripley/clip
+
+ price_min = 1500
+ price_max = 2500
+ stock_max = 3
+ availability_prob = 30
+
+/datum/blackmarket_item/tech/chem_master
+ name = "Chem Master Board"
+ desc = "A Chem Master board, capable of seperating and packaging reagents. Perfect for any aspiring at home chemist."
+ item = /obj/item/circuitboard/machine/chem_master
+
+ price_min = 1000
+ price_max = 3000
+ stock = 1
+ availability_prob = 30
+
+/datum/blackmarket_item/tech/ltrsbt
+ name = "Black Market Long-To-Short-Range-Bluespace-Transciever"
+ desc = "Need a faster and better way of transporting your illegal goods from and to the sector? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) is here to help. This handy teleporter will teleport your purchases directly to you once built."
+ item = /obj/item/circuitboard/machine/ltsrbt
+
+ price_min = 500
+ price_max = 1000
+ stock_max = 3
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/mrs_pacman
+ name = "MRSPACMAN-type Generator Board"
+ desc = "A ridiciously overclocked PACMAN generator that somehow burns diamonds as fuel."
+ item = /obj/item/circuitboard/machine/pacman/mrs
+
+ price_min = 2000
+ price_max = 3000
+ stock = 1
+ availability_prob = 30
+
+/datum/blackmarket_item/tech/ai_core
+ name = "AI Core Board"
+ desc = "The future is now! Become one with your ship with this AI core board! (Some assembly required.)"
+ item = /obj/item/circuitboard/aicore
+ pair_item = list(/datum/blackmarket_item/tech/boris, /datum/blackmarket_item/tech/mmi, /datum/blackmarket_item/tech/borg)
+
+ price_min = 5000
+ price_max = 8000
+ stock = 1
+ availability_prob = 5
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/tech/boris
+ name = "B.O.R.I.S Module"
+ desc = "A Bluespace Optimi-blah blah blah, I'm bored already. This module will convert a cyborg frame into an AI compatible shell."
+ item = /obj/item/borg/upgrade/ai
+
+ price_min = 500
+ price_max = 1000
+ stock = 1
+ availability_prob = 0
+
+/datum/blackmarket_item/tech/mmi
+ name = "Man Machine Interface"
+ desc = "Transcend the weakness of your flesh with this man machine interface, compatible with AIs, Cyborgs and Mechs!"
+ item = /obj/item/mmi
+ pair_item = list(/datum/blackmarket_item/tech/borg)
+
+ price_min = 250
+ price_max = 750
+ stock_max = 3
+ availability_prob = 40
+
+/datum/blackmarket_item/tech/borg
+ name = "Cyborg Construction Kit"
+ desc = "This durable and verastile cyborg frame is capable of fufilling a number of roles and survive situations that would kill the average person. Brain sold seperately."
+ item = /obj/structure/closet/crate/cyborg
+
+ price_min = 1000
+ price_max = 2000
+ stock_max = 2
+ availability_prob = 0
+
+/datum/blackmarket_item/tech/t4_capacitor
+ name = "Quadratic Capacitor"
+ desc = "A top grade quadractic capacitor. These highly effiecent capacitors are capable of storing massive amounts of electricity. Keep away from open plugs."
+ item = /obj/item/stock_parts/capacitor/quadratic
+
+ price_min = 400
+ price_max = 700
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/t4_scanner
+ name = "Triphasic Scanning Module"
+ desc = "A top grade triphasic scanning module. These finely tuned scanning modules are usually reserved for vital systems like early warning defence radars against raiders and pirates. We decided to put it to better use."
+ item = /obj/item/stock_parts/scanning_module/triphasic
+
+ price_min = 400
+ price_max = 700
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/t4_manip
+ name = "Femto Manipulator"
+ desc = "A top grade femto manipulator. These insanely precise manipuators are capable of manipulating particles up to a quadtillionth of a meter. Still not precise enough to find a single braincell in an NT exec's head though."
+ item = /obj/item/stock_parts/manipulator/femto
+
+ price_min = 400
+ price_max = 700
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/t4_laser
+ name = "Quad-Ultra Microlaser"
+ desc = "A top grade quad-ultra microlaser. A bit too micro of a laser to actually kill anyone with, but more than enough to get the most out of your materials."
+ item = /obj/item/stock_parts/micro_laser/quadultra
+
+ price_min = 400
+ price_max = 700
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/t4_bin
+ name = "Bluespace Matter Bin"
+ desc = "A top grade bluespace matter bin. Uses the power of bluespace to contain tons of matter without all the hassle of actually needing to carry literal tons with only a miniscule chance of ripping a hole in reality."
+ item = /obj/item/stock_parts/matter_bin/bluespace
+
+ price_min = 400
+ price_max = 700
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 20
+
+/datum/blackmarket_item/tech/crew_monitor
+ name = "Crew Monitor Board"
+ desc = "A crew monitor computer board, for watching your hapless crew die in real time."
+ item = /obj/machinery/computer/crew
+
+ price_min = 750
+ price_max = 1250
+ stock_max = 3
+ availability_prob = 40
+
+/datum/blackmarket_item/tech/sec_cam
+ name = "Camera Console Board"
+ desc = "A camera console computer board, for when you want to invade your crew's privacy."
+ item = /obj/item/circuitboard/computer/security
+
+ price_min = 750
+ price_max = 1250
+ stock_max = 3
+ availability_prob = 40
+
+/datum/blackmarket_item/tech/emag_limited
+ name = "Limited Cryptographic Sequencer"
+ desc = "These cryptographic sequencers are perfect for bypassing any mechanical safties or just breaking shit in general. They're pretty old though, and will probably burn out after a single use. Do not keep in the same wallet as your credit card."
+ item = /obj/item/card/emag/limited
+
+ price_min = 750
+ price_max = 1500
+ stock_min = 2
+ stock_max = 7
+ availability_prob = 30
+
+/datum/blackmarket_item/tech/joywire
+ name = "Pleasure Vivifier Neural Implant"
+ desc = "Midi-Sim's ever popular pleasure vivifier implant promises a constant rush of dopamine to get you high on life."
+ item = /obj/item/organ/cyberimp/brain/joywire
+
+ price_min = 500
+ price_max = 1000
+ stock_min = 3
+ stock_max = 5
+ availability_prob = 50
+
+/datum/blackmarket_item/tech/joywire/spawn_item(loc)
+ if(prob(10))
+ var/obj/item/organ/cyberimp/brain/mindscrew/implant = ..()
+ implant.name = "\improper Midi-Sed pleasure vivifier"
+ implant.desc = "A widely popular (and addictive) implant produced by Miditeke-Sedari Tokoce that \
+ stimulates the brain's pleasure centers. \
+ Dramatically increases mood, but interferes with taste reception even if uninstalled. \
+ Its wires seem a little loose."
+ return new implant(loc)
+ return ..()
+
+/datum/blackmarket_item/tech/mindscrew
+ name = "MNDFCK Neural Implant"
+ desc = "Got a tough customer who refuses to crack? This aftermarket modification of the Midi-Sed pleasure vivifier will amplify their pain receptors and get them talking fast."
+ item = /obj/item/organ/cyberimp/brain/mindscrew
+
+ price_min = 500
+ price_max = 1500
+ stock_max = 3
+ availability_prob = 30
+
+/datum/blackmarket_item/tech/arm_gun
+ name = "Arm Mounted Laser Cannon Implant"
+ desc = "A retractable laser cannon that fits inside your arm for concealment. You won't be passing any metal detector scans though."
+ item = /obj/item/organ/cyberimp/arm/gun/laser
+
+ price_min = 2000
+ price_max = 4000
+ stock = 1
+ availability_prob = 15
+ spawn_weighting = FALSE
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/tools.dm b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm
index d24cbf68171d..412ba303b977 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/tools.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm
@@ -18,12 +18,23 @@
price_min = 500
price_max = 2000
+ stock_min = 1
+ stock_max = 3
+ availability_prob = 40
+
+/datum/blackmarket_item/tool/surgery_duffel
+ name = "Cybersun Advanced Surgical Kit"
+ desc = "You might say it's morally wrong to steal. I say it's justified when it's Cybersun."
+ item = /obj/item/storage/backpack/duffelbag/syndie/surgery
+
+ price_min = 2500
+ price_max = 5000
stock = 1
- availability_prob = 30
+ availability_prob = 25
/datum/blackmarket_item/tool/binoculars
name = "Binoculars"
- desc = "Increase your sight by 150% with this handy Tool!"
+ desc = "Twice as effective as a monocular for seeing across long distances."
item = /obj/item/binoculars
price_min = 50
@@ -32,6 +43,28 @@
stock_max = 4
availability_prob = 70
+/datum/blackmarket_item/tool/whetstone
+ name = "Whetstone"
+ desc = "Your blades not making the cut? This whetstone will give you the edge you need!"
+ item = /obj/item/sharpener
+
+ price_min = 100
+ price_max = 300
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 50
+
+/datum/blackmarket_item/tool/cham_stamp
+ name = "Chameleon Stamp"
+ desc = "Can't find a forger? Look no further than these handy chameleon stamps, capable of replicating all manner of offical or government seals."
+ item = /obj/item/stamp/chameleon
+
+ price_min = 50
+ price_max = 200
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 80
+
/datum/blackmarket_item/tool/riot_shield
name = "Riot Shield"
desc = "Protect yourself from an unexpected Riot at your local Police department!"
@@ -47,8 +80,8 @@
desc = "30u of Thermite to assist in creating a quick access point or get away!"
item = /obj/item/reagent_containers/glass/bottle/thermite
- price_min = 100
- price_max = 600
+ price_min = 75
+ price_max = 300
stock_max = 10
availability_prob = 50
@@ -58,7 +91,7 @@
item = /obj/item/reagent_containers/glass/chem_jug/thermite
price_min = 400
- price_max = 1500
+ price_max = 1200
stock_max = 3
availability_prob = 20
@@ -80,7 +113,8 @@
price_min = 1000
price_max = 3000
stock = 1
- availability_prob = 20
+ availability_prob = 10
+ spawn_weighting = FALSE
/datum/blackmarket_item/tool/jumpboots
name = "Jump Boots"
@@ -101,16 +135,7 @@
price_max = 2000
stock_max = 3
availability_prob = 30
-
-/datum/blackmarket_item/tool/chem_master
- name = "Chem Master Board"
- desc = "A Chem Master board, capable of seperating and packaging reagents. Perfect for any aspiring at home chemist."
- item = /obj/item/circuitboard/machine/chem_master
-
- price_min = 1000
- price_max = 3000
- stock = 1
- availability_prob = 30
+ spawn_weighting = FALSE
/datum/blackmarket_item/tool/rcd
name = "Rapid Construction Device"
@@ -128,7 +153,7 @@
item = /obj/item/attachment/silencer
price_min = 100
- price_max = 700
+ price_max = 300
stock_min = 3
stock_max = 6
availability_prob = 60
diff --git a/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm b/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm
index 3d6b32a67569..5c6f83ad5734 100644
--- a/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm
@@ -12,19 +12,6 @@
stock_max = 7
availability_prob = 40
-/datum/blackmarket_item/weapon/shotgun_dart
- name = "Shotgun Dart"
- desc = "These handy darts can be filled up with any chemical and be shot with a shotgun! \
- Prank your friends by shooting them with laughter! \
- Not recommended for comercial use."
- item = /obj/item/ammo_casing/shotgun/dart
-
- price_min = 10
- price_max = 50
- stock_min = 10
- stock_max = 60
- availability_prob = 40
-
/datum/blackmarket_item/weapon/bone_spear
name = "Bone Spear"
desc = "Authentic tribal spear, made from real bones! A steal at any price, especially if you're a caveman."
@@ -45,6 +32,15 @@
stock_max = 3
availability_prob = 50
+/datum/blackmarket_item/weapon/powerfist
+ name = "Powerfist"
+ desc = "Need a bit more... omph in your right hook? This gas operated powerfist will put you in the heavyweight."
+ item = /obj/item/melee/powerfist
+ price_min = 1500
+ price_max = 4000
+ stock_max = 2
+ availability_prob = 50
+
/datum/blackmarket_item/weapon/sabre
name = "SUNS Dueling Sabre"
desc = "A mastercrafted sabre formerly wielded by a SUNS academic. It's very sharp, we had to spend hours stitching our fingers back on after getting it."
@@ -55,53 +51,67 @@
stock = 1
availability_prob = 25
+/datum/blackmarket_item/weapon/mag_cleaver
+ name = "Magnetic Cleaver"
+ desc = "A prototype modification to the standard crusher, featuring an energy blade rather than the standard alloy cutting edge allowing for much more devasting detonations. The guy who sold this to us disappeared the next week, but that's probably a coincidence."
+ item = /obj/item/kinetic_crusher/syndie_crusher
+
+ price_min = 1750
+ price_max = 3000
+ stock = 2
+ availability_prob = 15
+ spawn_weighting = FALSE
+
/datum/blackmarket_item/weapon/derringer
name = "Derringer"
desc = "A concealable handgun small enough to hide nearly anywhere. Uses .38 revolver rounds."
item = /obj/item/gun/ballistic/derringer
+
price_min = 100
- price_max = 500
+ price_max = 300
stock_max = 6
availability_prob = 50
-/datum/blackmarket_item/weapon/golden
- name = "Golden Derringer"
- desc = "A rare custom-made concealable weapon designed to fire illegal .357 rounds."
- item = /obj/item/gun/ballistic/derringer/gold
- price_min = 1000
- price_max = 3000
+/datum/blackmarket_item/weapon/syndi_derringer
+ name = ".357 Derringer"
+ desc = "A concealable hangun with a tasteful red and black paintjob, which makes it slightly more noticable. Chambered in .357, so you actually have a chance at killing something."
+ item = /obj/item/gun/ballistic/derringer/traitor
+ pair_item = list(/datum/blackmarket_item/ammo/a357_box)
+
+ price_min = 300
+ price_max = 800
+ stock = 2
+ availability_prob = 30
+
+/datum/blackmarket_item/weapon/disposable_gun_disk
+ name = "Disposable Gun Design Disk"
+ desc = "An autolathe compatible fabrication disk for printing disposable guns chambered in .22 LR. Improper disposal or recycling of these guns is an enviromental felony misdemeanor in Solarian space. Luckily, we aren't in Solarian space, so litter all you want."
+ item = /obj/item/disk/design_disk/disposable_gun
+
+ price_min = 1500
+ price_max = 2500
stock = 1
availability_prob = 10
+ spawn_weighting = FALSE
/datum/blackmarket_item/weapon/himehabu
name = "Himehabu Pistol"
desc = "Great things come in small packages. The Himehabu is perfect for all your espionage needs. Chambered in .22lr."
item = /obj/item/gun/ballistic/automatic/pistol/himehabu
- pair_item = /datum/blackmarket_item/weapon/himehabu_mag
+ pair_item = list(/datum/blackmarket_item/ammo/himehabu_mag, /datum/blackmarket_item/ammo/himehabu_box)
price_min = 100
price_max = 600
stock_max = 6
availability_prob = 50
-/datum/blackmarket_item/weapon/himehabu_mag
- name = "Himehabu Magazines"
- desc = "Compact 10 round .22lr magazines for use in the Himehabu pistol."
- item = /obj/item/ammo_box/magazine/m22lr
-
- price_min = 100
- price_max = 200
- stock_min = 3
- stock_max = 6
- availability_prob = 0
-
/datum/blackmarket_item/weapon/e10
name = "E-10 Laser Pistol"
desc = "Sharplite letting you down? Try these classic Eoehoma Firearms E-10 Laser Pistols."
item = /obj/item/gun/energy/laser/e10
price_min = 500
- price_max = 1250
+ price_max = 1000
stock_max = 5
availability_prob = 20
@@ -119,55 +129,47 @@
name = "E-40 Hybrid Assault Rifle"
desc = "A dual mode hybrid assault rifle made by the now defunct Eoehoma Firearms. Capable of firing both bullets AND lasers, for the discerning dealer in death. Chambered in Eoehoma .299 Caseless."
item = /obj/item/gun/ballistic/automatic/assault/e40
- pair_item = /datum/blackmarket_item/weapon/e40_mag
+ pair_item = list(/datum/blackmarket_item/ammo/e40_mag)
price_min = 7000
price_max = 15000
stock_max = 2
- availability_prob = 20
-
-/datum/blackmarket_item/weapon/e40_mag
- name = "Eoehoma .299 Caseless Magazine"
- desc = "A 30 round magazine for the E-40 Hybrid Rifle."
- item = /obj/item/ammo_box/magazine/e40
-
- price_min = 750
- price_max = 1250
- stock_min = 2
- stock_max = 6
- availability_prob = 0
+ availability_prob = 10
+ spawn_weighting = FALSE
/datum/blackmarket_item/weapon/e50
name = "E-50 Energy Emitter"
desc = "An Eoehoma Firearms E-50 Emitter cannon. For when you want a send a message. A really big message."
item = /obj/item/gun/energy/laser/e50
+ pair_item = (/datum/blackmarket_item/ammo/huge_weapon_cell)
price_min = 4000
price_max = 7000
stock_max = 2
availability_prob = 20
+ spawn_weighting = FALSE
+
+/datum/blackmarket_item/weapon/e60
+ name = "E-60 Disabler"
+ desc = "Looking for a live capture? This Eoehoma Firearms E-60 disabler will get your man."
+ item = /obj/item/gun/energy/disabler/e60
+
+ price_min = 500
+ price_max = 750
+ stock_max = 3
+ availability_prob = 40
/datum/blackmarket_item/weapon/saber_smg
name = "Saber 9mm SMG"
desc = "A prototype 9mm submachine gun. Most of these never got past the RND phase and into distribution. But we happen know a guy."
item = /obj/item/gun/ballistic/automatic/smg/skm_carbine/inteq/proto
- pair_item = /datum/blackmarket_item/weapon/saber_mag
+ pair_item = list(/datum/blackmarket_item/ammo/saber_mag)
- price_min = 2500
- price_max = 4200
+ price_min = 2250
+ price_max = 3750
stock_max = 2
availability_prob = 25
-/datum/blackmarket_item/weapon/saber_mag
- name = "Saber 9mm SMG Magazines"
- desc = "Magazines for use in the Saber 9mm SMG. No, they don't work as swords."
- item = /obj/item/ammo_box/magazine/smgm9mm
-
- price_min = 500
- price_max = 1000
- stock = 2
- availability_prob = 0
-
/datum/blackmarket_item/weapon/bg_16
name = "BG-16 Beam Gun"
desc = "Not satisfied by Etherbor's civilian offerings? Try this military grade one we found!"
@@ -184,7 +186,7 @@
item = /obj/item/gun/ballistic/rifle/illestren/sawn
price_min = 600
- price_max = 1250
+ price_max = 1000
stock_min = 2
stock_max = 5
availability_prob = 60
@@ -194,8 +196,8 @@
desc = "Are your arms tired from pumping Hunter's Pride shotguns? This semi-automatic combat shotgun will make killing a breeze."
item = /obj/item/gun/ballistic/shotgun/automatic/combat
- price_min = 2000
- price_max = 4000
+ price_min = 1750
+ price_max = 3500
stock_max = 3
availability_prob = 40
@@ -207,7 +209,8 @@
price_min = 1000
price_max = 2000
stock_max = 3
- availability_prob = 30
+ availability_prob = 20
+ spawn_weighting = FALSE
/datum/blackmarket_item/weapon/mecha_syringe_gun
name = "Mounted Syringe Gun"
@@ -217,35 +220,25 @@
price_min = 5000
price_max = 7000
stock = 1
- availability_prob = 15
+ availability_prob = 10
+ spawn_weighting = FALSE
/datum/blackmarket_item/weapon/mecha_hades
name = "Mounted FNX-99 Carbine"
- desc = "This so called \"Hades\" carbine is sure to burn brightly above the competition! Not to be confused with the \"Hades\" energy rifle. Exosuit not included."
+ desc = "This so called \"Phoenix\" carbine is sure to burn brightly above the competition! Exosuit not included."
item = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine
- pair_item = /datum/blackmarket_item/weapon/mecha_hades_ammo
+ pair_item = list(/datum/blackmarket_item/ammo/mecha_hades_ammo)
price_min = 2000
price_max = 3000
stock_max = 2
availability_prob = 25
-/datum/blackmarket_item/weapon/mecha_hades_ammo
- name = "FNX-99 Incediary Ammo"
- desc = "A box of 24 incendiary shells for the FNX-99 mounted carbine."
- item = /obj/item/mecha_ammo/incendiary
-
- price_min = 250
- price_max = 350
- stock_min = 3
- stock_max = 5
- availability_prob = 0
-
/datum/blackmarket_item/weapon/model_h
name = "Model H"
desc = "A Model H slug pistol. The H stands for Hurt. Chambered in ferromagnetic slugs."
item = /obj/item/gun/ballistic/automatic/powered/gauss/modelh
- pair_item = /datum/blackmarket_item/weapon/model_h_mag
+ pair_item = list(/datum/blackmarket_item/ammo/model_h_mag)
price_min = 2000
price_max = 3500
@@ -257,122 +250,81 @@
/obj/item/gun/ballistic/automatic/powered/gauss/modelh))
return new model_h(loc)
-/datum/blackmarket_item/weapon/model_h_mag
- name = "Model H Magazine"
- desc = "A 10 round magazine for Model H slug pistol."
- item = /obj/item/ammo_box/magazine/modelh
-
- price_min = 500
- price_max = 1000
- stock_max = 4
- availability_prob = 0
-
/datum/blackmarket_item/weapon/sgg
name = "SSG-669C Rotary Sniper Rifle"
- desc = "I could tell you it's full name, but we'd be here all day. It's a sniper rifle. It shoots people from far away. Chambered in 8x58mm."
+ desc = "I could tell you it's full name, but we'd be here all day. It's a sniper rifle. It shoots people from far away. Chambered in 8x58mm caseless."
item = /obj/item/gun/ballistic/rifle/solgov
- pair_item = /datum/blackmarket_item/weapon/sgg_stripper
+ pair_item = list(/datum/blackmarket_item/ammo/sgg_stripper)
price_min = 3000
price_max = 6000
stock = 1
availability_prob = 20
-/datum/blackmarket_item/weapon/sgg_stripper
- name = "8x58mm Stripper Clip"
- desc = "A five round 8x58mm stripper clip for use with the SGG-669C."
- item = /obj/item/ammo_box/a858
-
- price_min = 500
- price_max = 1000
- stock_min = 4
- stock_max = 6
- availability_prob = 0
-
/datum/blackmarket_item/weapon/pistole_c
name = "Pistole C"
desc = "Pistole Compact? Pistole Caseless? Pistole Cheese? Fuck if I know. All I know is these little numbers pack a nasty sting. Chambered in 5.56 caseless."
item = /obj/item/gun/ballistic/automatic/pistol/solgov/old
- pair_item = /datum/blackmarket_item/weapon/pistole_c_mag
+ pair_item = list(/datum/blackmarket_item/ammo/pistole_c_mag)
price_min = 900
price_max = 1250
stock_max = 3
availability_prob = 30
-/datum/blackmarket_item/weapon/pistole_c_mag
- name = "5.56 Caseless Magazine"
- desc = "A 12 round magazine for the Pistole Cheese."
- item = /obj/item/ammo_box/magazine/pistol556mm
-
- price_min = 250
- price_max = 750
- stock_max = 2
- availability_prob = 0
-
/datum/blackmarket_item/weapon/proto_gauss
name = "Prototype Gauss Rifle"
desc = "A prototype gauss rifle made by Nanotrasen. Perfect for making swiss cheese out of people. Chambered in ferromagnetic pellets."
item = /obj/item/gun/ballistic/automatic/powered/gauss
- pair_item = /datum/blackmarket_item/weapon/proto_gauss_mag
+ pair_item = list(/datum/blackmarket_item/ammo/proto_gauss_mag)
- price_min = 3500
- price_max = 6000
+ price_min = 2500
+ price_max = 4000
stock = 2
availability_prob = 25
-/datum/blackmarket_item/weapon/proto_gauss_mag
- name = "Prototype Gauss Rifle Magazine"
- desc = "A 25 round ferromagnetic pellet magazine for the prototype gauss rifle."
- item = /obj/item/ammo_box/magazine/gauss
-
- price_min = 600
- price_max = 1100
- stock_min = 2
- stock_max = 4
- availability_prob = 0
-
/datum/blackmarket_item/weapon/tec
name = "TEC-9 Machine Pistol"
desc = "Hallelujah! It's raining lead! This 9mm machine pistol is capable of spitting out bullets at rapid pace."
item = /obj/item/gun/ballistic/automatic/pistol/tec9
- pair_item = /datum/blackmarket_item/weapon/tec_mag
+ pair_item = list(/datum/blackmarket_item/ammo/tec_mag)
price_min = 1500
price_max = 2750
- stock_max = 2
+ stock = 2
availability_prob = 35
-/datum/blackmarket_item/weapon/tec_mag
- name = "TEC-9 AP Magazine"
- desc = "A 20 round magazine of AP ammo for the TEC-9 machine pistol."
- item = /obj/item/ammo_box/magazine/tec9
+/datum/blackmarket_item/weapon/syringe_gun
+ name = "Dart Pistol"
+ desc = "A compact dart pistol, for clandestine poisoining from a distance."
+ item = /obj/item/gun/syringe/syndicate
- price_min = 600
- price_max = 1000
- stock_max = 2
- availability_prob = 0
+ price_min = 750
+ price_max = 1500
+ stock = 2
+ availability_prob = 30
-/datum/blackmarket_item/weapon/scout
- name = "HP Scout"
- desc = "A scoped rifle chambered in .300 Magnum. As the name would imply, perfect for scouts. Try not to tunnel vision with the scope like the last guy."
- item = /obj/item/gun/ballistic/rifle/scout
- pair_item = /datum/blackmarket_item/weapon/scout_stripper
+/datum/blackmarket_item/weapon/polymer
+ name = "Polymer Survivor Rifle"
+ desc = "A slapdash rifle held together by spite, dreams and a good helping of duct tape. Chambered in 7.62x40mm CLIP."
+ item = /obj/item/gun/ballistic/rifle/polymer
+ pair_item = list(/datum/blackmarket_item/ammo/polymer_clip)
- price_min = 4000
- price_max = 6500
- stock = 1
- availability_prob = 20
+ price_min = 600
+ price_max = 1250
+ stock_min = 2
+ stock_max = 4
+ availability_prob = 50
-/datum/blackmarket_item/weapon/scout_stripper
- name = ".300 Magnum Stripper Clip"
- desc = "A 5 round .300 Magnum stripper clips for use with the HP Scout."
- item = /obj/item/ammo_box/a300
+/datum/blackmarket_item/weapon/skm_carbine
+ name = "SKM-24v Carbine"
+ desc = "Technically this is just a sawn down SKM-24 assault rifle, but what's CLIP going to do? Sue us? Chambered in 4.6x30mm."
+ item = /obj/item/gun/ballistic/automatic/smg/skm_carbine
+ pair_item = list(/datum/blackmarket_item/ammo/carbine_mag)
- price_min = 500
- price_max = 1000
- stock_min = 4
- stock_max = 6
- availability_prob = 0
+ price_min = 3000
+ price_max = 4500
+ stock_max = 2
+ availability_prob = 20
diff --git a/code/modules/cargo/blackmarket/blackmarket_market.dm b/code/modules/cargo/blackmarket/blackmarket_market.dm
index 3e055048a85b..5b0c2365b3df 100644
--- a/code/modules/cargo/blackmarket/blackmarket_market.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_market.dm
@@ -12,11 +12,37 @@
/// Item categories available from this market, only items which are in these categories can be gotten from this market.
var/list/categories = list()
+/datum/blackmarket_market/New()
+ . = ..()
+ addtimer(CALLBACK(src, PROC_REF(cycle_stock)), 60 MINUTES, TIMER_STOPPABLE|TIMER_LOOP|TIMER_DELETE_ME)
+
+/datum/blackmarket_market/proc/cycle_stock()
+ var/list/pair_items_to_handle = list()
+
+ for(var/category in available_items)
+ for(var/item in available_items[category])
+ if(istype(item, /datum/blackmarket_item))
+ var/datum/blackmarket_item/b_item = item
+ b_item.cycle()
+ if(b_item.available == TRUE)
+ for(var/paired_item in b_item.pair_item)
+ var/datum/blackmarket_item/item_to_set = get_item_in_market(paired_item)
+ if(!(item_to_set in pair_items_to_handle) && !isnull(item_to_set))
+ pair_items_to_handle += item_to_set
+
+ for(var/item in pair_items_to_handle)
+ var/datum/blackmarket_item/b_item = item
+ b_item.cycle(TRUE,FALSE,FALSE,TRUE)
+
+// returns the blackmarket_item datum currently in the availible items list. Null if not in the list
+/datum/blackmarket_market/proc/get_item_in_market(datum/blackmarket_item/item)
+ for(var/item_to_find in available_items[item.category])
+ if(istype(item_to_find,item))
+ return item_to_find
+ return null
+
/// Adds item to the available items and add it's category if it is not in categories yet.
/datum/blackmarket_market/proc/add_item(datum/blackmarket_item/item, paired)
- if(!prob(initial(item.availability_prob)) && !paired)
- return FALSE
-
if(ispath(item))
item = new item()
@@ -26,8 +52,10 @@
available_items[item.category] += item
- if(item.pair_item)
- add_item(item.pair_item, TRUE)
+ if(prob(initial(item.availability_prob)) || paired)
+ item.available = TRUE
+ else
+ item.available = FALSE
return TRUE
@@ -52,5 +80,6 @@
/datum/blackmarket_market/blackmarket
name = "Black Market"
- shipping = list(SHIPPING_METHOD_LTSRBT =50,
- SHIPPING_METHOD_LAUNCH =10)
+ shipping = list(SHIPPING_METHOD_LTSRBT =100,
+ SHIPPING_METHOD_LAUNCH =10,
+ SHIPPING_METHOD_DEAD_DROP = 20)
diff --git a/code/modules/cargo/blackmarket/blackmarket_telepad.dm b/code/modules/cargo/blackmarket/blackmarket_telepad.dm
index 14211cad6878..bd8ec96e80b0 100644
--- a/code/modules/cargo/blackmarket/blackmarket_telepad.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_telepad.dm
@@ -3,11 +3,8 @@
icon_state = "bluespacearray"
build_path = /obj/machinery/ltsrbt
req_components = list(
- /obj/item/stack/ore/bluespace_crystal = 2,
- /obj/item/stock_parts/subspace/ansible = 1,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stock_parts/scanning_module = 2)
- def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial)
/obj/machinery/ltsrbt
name = "Long-To-Short-Range-Bluespace-Transciever"
@@ -121,3 +118,17 @@
if(queue.len)
recieving = pick_n_take(queue)
+
+/datum/crafting_recipe/blackmarket_telepad
+ name = "Black Market LTRSBT Board"
+ result = /obj/item/circuitboard/machine/ltsrbt
+ time = 30
+ tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER, TOOL_MULTITOOL)
+ reqs = list(
+ /obj/item/stack/ore/bluespace_crystal = 2,
+ /obj/item/stack/tape/industrial = 5,
+ /obj/item/card/bank = 1,
+ /obj/item/computer_hardware/network_card = 1,
+ /obj/item/circuitboard = 1
+ )
+ category = CAT_MISC
diff --git a/code/modules/cargo/blackmarket/blackmarket_uplink.dm b/code/modules/cargo/blackmarket/blackmarket_uplink.dm
index 26363bf71b82..2d25c9c444f4 100644
--- a/code/modules/cargo/blackmarket/blackmarket_uplink.dm
+++ b/code/modules/cargo/blackmarket/blackmarket_uplink.dm
@@ -82,13 +82,14 @@
if(viewing_category && market)
if(market.available_items[viewing_category])
for(var/datum/blackmarket_item/I in market.available_items[viewing_category])
- data["items"] += list(list(
- "id" = I.type,
- "name" = I.name,
- "cost" = I.price,
- "amount" = I.unlimited ? "INF" : I.stock,
- "desc" = I.desc || I.name
- ))
+ if(I.available)
+ data["items"] += list(list(
+ "id" = I.type,
+ "name" = I.name,
+ "cost" = I.price,
+ "amount" = I.unlimited ? "INF" : I.stock,
+ "desc" = I.desc || I.name
+ ))
return data
/obj/item/blackmarket_uplink/ui_static_data(mob/user)
diff --git a/code/modules/cargo/packs/food.dm b/code/modules/cargo/packs/food.dm
index 9c366406af5c..6bf438f22867 100644
--- a/code/modules/cargo/packs/food.dm
+++ b/code/modules/cargo/packs/food.dm
@@ -8,7 +8,7 @@
/datum/supply_pack/food/donkpockets
name = "Donk Pocket Variety Crate"
desc = "Featuring a line up of Donk Co.'s most popular pastry!"
- cost = 2000
+ cost = 500
contains = list(/obj/item/storage/box/donkpockets/donkpocketspicy,
/obj/item/storage/box/donkpockets/donkpocketteriyaki,
/obj/item/storage/box/donkpockets/donkpocketpizza,
@@ -25,7 +25,7 @@
/datum/supply_pack/food/pizza
name = "Pizza Crate"
desc = "Best prices on this side of the galaxy. All deliveries are guaranteed to be 99.5% anomaly-free!"
- cost = 3000// Best prices this side of the galaxy.
+ cost = 750// Best prices this side of the galaxy.
contains = list(/obj/item/pizzabox/margherita,
/obj/item/pizzabox/mushroom,
/obj/item/pizzabox/meat,
@@ -42,6 +42,19 @@
fourfiveeight.boxtag = P.boxtag
qdel(P)
+/datum/supply_pack/food/ration
+ name = "Ration Crate"
+ desc = "6 standerd issue rations."
+ cost = 500
+ contains = list(/obj/effect/spawner/lootdrop/ration,
+ /obj/effect/spawner/lootdrop/ration,
+ /obj/effect/spawner/lootdrop/ration,
+ /obj/effect/spawner/lootdrop/ration,
+ /obj/effect/spawner/lootdrop/ration,
+ /obj/effect/spawner/lootdrop/ration)
+ crate_name = "ration crate"
+ crate_type = /obj/structure/closet/crate
+
/*
Ingredients
*/
@@ -49,7 +62,7 @@
/datum/supply_pack/food/ingredients_basic
name = "Basic Ingredients Crate"
desc = "Get things cooking with this crate full of useful ingredients! Contains a dozen eggs, two slabs of meat, some flour, some rice, a bottle of milk, a bottle of soymilk, and a bag of sugar."
- cost = 1000
+ cost = 300
contains = list(/obj/item/reagent_containers/food/condiment/flour,
/obj/item/reagent_containers/food/condiment/flour,
/obj/item/reagent_containers/food/condiment/rice,
@@ -63,16 +76,17 @@
crate_name = "food crate"
crate_type = /obj/structure/closet/crate/freezer
-/datum/supply_pack/food/ingredients_specialized
- name = "Advanced Cooking Crate"
- desc = "For the discerning chef. Contains a bottle of enzyme, a salt shaker, a pepper mill, a bottle of ketchup, a bottle of hot sauce, and a bottle of cream."
- cost = 2000
+/datum/supply_pack/food/ingredients_condiments
+ name = "Condiments Crate"
+ desc = "A variety of garnishes for topping off your dish with a little extra pizzaz. Contains a bottle of enzyme, a salt shaker, a pepper mill, a bottle of ketchup, a bottle of hot sauce, a bottle of BBQ sauce, and a bottle of cream."
+ cost = 250
contains = list(/obj/item/reagent_containers/food/condiment/enzyme,
/obj/item/reagent_containers/food/condiment/saltshaker,
/obj/item/reagent_containers/food/condiment/peppermill,
/obj/item/reagent_containers/food/condiment/ketchup,
/obj/item/reagent_containers/food/condiment/hotsauce,
- /obj/item/reagent_containers/food/drinks/bottle/cream
+ /obj/item/reagent_containers/food/drinks/bottle/cream,
+ /obj/item/reagent_containers/food/condiment/bbqsauce
)
crate_name = "condiments crate"
crate_type = /obj/structure/closet/crate/freezer
@@ -80,7 +94,7 @@
/datum/supply_pack/food/ingredients_randomized
name = "Exotic Meat Crate"
desc = "The best cuts in the whole galaxy. Probably."
- cost = 1000
+ cost = 500
contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
/obj/item/reagent_containers/food/snacks/meat/slab/killertomato,
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
@@ -103,7 +117,7 @@
/datum/supply_pack/food/ingredients_randomized/meat
name = "Standard Meat Crate"
desc = "Less interesting cuts of meat, but filling nonetheless."
- cost = 1500
+ cost = 300
contains = list(/obj/item/reagent_containers/food/snacks/meat/slab,
/obj/item/reagent_containers/food/snacks/meat/slab/chicken,
/obj/item/reagent_containers/food/snacks/meat/slab/synthmeat,
@@ -116,7 +130,7 @@
/datum/supply_pack/food/ingredients_randomized/vegetables
name = "Vegetables Crate"
desc = "Grown in vats."
- cost = 1300
+ cost = 250
contains = list(/obj/item/reagent_containers/food/snacks/grown/chili,
/obj/item/reagent_containers/food/snacks/grown/corn,
/obj/item/reagent_containers/food/snacks/grown/tomato,
@@ -132,7 +146,7 @@
/datum/supply_pack/food/ingredients_randomized/fruits
name = "Fruit Crate"
desc = "Rich of vitamins, may contain oranges."
- cost = 1500
+ cost = 250
contains = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
@@ -147,7 +161,7 @@
/datum/supply_pack/food/ingredients_randomized/grains
name = "Grains Crate"
desc = "A crate full of various grains. How interesting."
- cost = 1000
+ cost = 250
contains = list(/obj/item/reagent_containers/food/snacks/grown/wheat,
/obj/item/reagent_containers/food/snacks/grown/wheat,
/obj/item/reagent_containers/food/snacks/grown/wheat, //Weighted to be more common
@@ -162,7 +176,7 @@
/datum/supply_pack/food/ingredients_randomized/bread
name = "Bread Crate"
desc = "A crate full of various breads. Bready to either be eaten or made into delicious meals."
- cost = 1000
+ cost = 300
contains = list(/obj/item/food/bread/plain,
/obj/item/food/breadslice/plain,
/obj/item/food/breadslice/plain,
@@ -181,17 +195,16 @@
/datum/supply_pack/food/grill
name = "Grilling Starter Kit"
desc = "Sometimes the stresses of the world are too much to bear. Some times, for God's sake, you just want to grill. This crate is for those times."
- cost = 5000
+ cost = 1000
contains = list(/obj/item/stack/sheet/mineral/coal/five,
- /obj/machinery/grill/unwrenched,
- /obj/item/reagent_containers/food/drinks/soda_cans/xeno_energy)
+ /obj/machinery/grill/unwrenched)
crate_name = "grilling starter kit crate"
crate_type = /obj/structure/closet/crate/large
/datum/supply_pack/food/grillfuel
name = "Grilling Fuel Kit"
desc = "Contains propane and propane accessories. (Note: doesn't contain any actual propane.)"
- cost = 2000
+ cost = 250
contains = list(/obj/item/stack/sheet/mineral/coal/ten)
crate_name = "grilling fuel kit crate"
@@ -202,7 +215,7 @@
/datum/supply_pack/food/hydrotank
name = "Hydroponics Backpack Crate"
desc = "Bring on the flood with this high-capacity backpack crate. Contains 500 units of life-giving H2O."
- cost = 1000
+ cost = 750
contains = list(/obj/item/watertank)
crate_name = "hydroponics backpack crate"
crate_type = /obj/structure/closet/crate/hydroponics
@@ -210,7 +223,7 @@
/datum/supply_pack/food/gardening
name = "Gardening Crate"
desc = "Supplies for growing a great garden! Contains two bottles of ammonia, two Plant-B-Gone spray bottles, a hatchet, cultivator, plant analyzer, as well as a pair of leather gloves and a botanist's apron."
- cost = 1500
+ cost = 500
contains = list(/obj/item/reagent_containers/spray/plantbgone,
/obj/item/reagent_containers/spray/plantbgone,
/obj/item/reagent_containers/glass/bottle/ammonia,
@@ -227,7 +240,7 @@
/datum/supply_pack/food/weedcontrol
name = "Weed Control Crate"
desc = "Contains a scythe, gasmask, and two anti-weed defoliant grenades, for when your garden grows out of control."
- cost = 1500
+ cost = 750
contains = list(/obj/item/scythe,
/obj/item/clothing/mask/gas,
/obj/item/grenade/chem_grenade/antiweed,
@@ -238,7 +251,7 @@
/datum/supply_pack/food/seeds
name = "Seeds Crate"
desc = "Big things have small beginnings. Contains fourteen different seeds."
- cost = 2000
+ cost = 750
contains = list(/obj/item/seeds/chili,
/obj/item/seeds/cotton,
/obj/item/seeds/berry,
@@ -259,7 +272,7 @@
/datum/supply_pack/food/exoticseeds
name = "Exotic Seeds Crate"
desc = "Any entrepreneuring botanist's dream. Contains eleven different seeds, including two mystery seeds!"
- cost = 3000
+ cost = 1000
contains = list(/obj/item/seeds/nettle,
/obj/item/seeds/plump,
/obj/item/seeds/liberty,
@@ -281,7 +294,7 @@
/datum/supply_pack/food/beekeeping_suits
name = "Beekeeper Suit Crate"
desc = "Bee business booming? Better be benevolent and boost botany by bestowing bi-Beekeeper-suits! Contains two beekeeper suits and matching headwear."
- cost = 2000
+ cost = 1000
contains = list(/obj/item/clothing/head/beekeeper_head,
/obj/item/clothing/suit/beekeeper_suit,
/obj/item/clothing/head/beekeeper_head,
@@ -292,7 +305,7 @@
/datum/supply_pack/food/beekeeping_fullkit
name = "Beekeeping Starter Crate"
desc = "BEES BEES BEES. Contains three honey frames, a beekeeper suit and helmet, flyswatter, bee house, and, of course, a pure-bred Nanotrasen-Standardized Queen Bee!"
- cost = 3000
+ cost = 2000
contains = list(/obj/structure/beebox/unwrenched,
/obj/item/honey_frame,
/obj/item/honey_frame,
@@ -304,16 +317,5 @@
crate_name = "beekeeping starter crate"
crate_type = /obj/structure/closet/crate/hydroponics
-/datum/supply_pack/food/ration
- name = "Ration Crate"
- desc = "6 standerd issue rations."
- cost = 2000
- contains = list(/obj/effect/spawner/lootdrop/ration,
- /obj/effect/spawner/lootdrop/ration,
- /obj/effect/spawner/lootdrop/ration,
- /obj/effect/spawner/lootdrop/ration,
- /obj/effect/spawner/lootdrop/ration,
- /obj/effect/spawner/lootdrop/ration)
- crate_name = "ration crate"
- crate_type = /obj/structure/closet/crate
+
diff --git a/code/modules/cargo/packs/machinery.dm b/code/modules/cargo/packs/machinery.dm
index f25e4818329e..6e5a12f30f5b 100644
--- a/code/modules/cargo/packs/machinery.dm
+++ b/code/modules/cargo/packs/machinery.dm
@@ -168,18 +168,6 @@
crate_name = "holofield generator crate"
crate_type = /obj/structure/closet/crate/engineering
-/datum/supply_pack/machinery/blackmarket_telepad
- name = "Black Market LTSRBT"
- desc = "Need a faster and better way of transporting your illegal goods from and to the sector? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) is here to help. Contains a LTSRBT circuit, two bluespace crystals, and one ansible."
- cost = 1000
- contains = list(
- /obj/item/circuitboard/machine/ltsrbt,
- /obj/item/stack/ore/bluespace_crystal/artificial,
- /obj/item/stack/ore/bluespace_crystal/artificial,
- /obj/item/stock_parts/subspace/ansible
- )
- crate_type = /obj/structure/closet/crate/science
-
/datum/supply_pack/machinery/shuttle_in_a_box
name = "Shuttle in a Box"
desc = "The bare minimum amount of machine and computer boards required to create a working spacecraft."
diff --git a/code/modules/cargo/packs/mechs.dm b/code/modules/cargo/packs/mechs.dm
index fd978f99749e..7790e696ee15 100644
--- a/code/modules/cargo/packs/mechs.dm
+++ b/code/modules/cargo/packs/mechs.dm
@@ -262,7 +262,7 @@ weapons
)
/datum/supply_pack/mech/weapon/scattershot
- name = "LBX AC 10 kit"
+ name = "LBX-10 kit"
desc = "Contains a \"Scattershot\" gun to mount on combat exosuits."
cost = 1750
contains = list(
@@ -270,7 +270,7 @@ weapons
)
/datum/supply_pack/mech/weapon/lmg
- name = "Ultra AC 2 kit"
+ name = "UMG-2 kit"
desc = "Contains a mounted gun which fires in three round bursts."
cost = 2250
contains = list(
@@ -295,16 +295,16 @@ ammo
crate_name = "exosuit ammo crate"
/datum/supply_pack/mech/ammo/scattershot_ammo
- name = "LBX AC 10 ammo box"
- desc = "Contains a fourty-round box of upscaled buckshot, to be loaded directly in a mounted LBX AC 10."
+ name = "LBX-10 ammo box"
+ desc = "Contains a fourty-round box of upscaled buckshot, to be loaded directly in a mounted LBX-10."
cost = 500
contains = list(
/obj/item/mecha_ammo/scattershot
)
/datum/supply_pack/mech/ammo/lmg_ammo
- name = "Ultra AC 2 ammo box"
- desc = "Contains a three hundred-round box of heavy ammunition for the Ultra AC 2."
+ name = "UMG-2 ammo box"
+ desc = "Contains a three hundred-round box of heavy ammunition for the UMG-2."
cost = 750
contains = list(
/obj/item/mecha_ammo/lmg
diff --git a/code/modules/client/loadout/loadout_accessories.dm b/code/modules/client/loadout/loadout_accessories.dm
index c1e4d7a088a8..a8acc1544654 100644
--- a/code/modules/client/loadout/loadout_accessories.dm
+++ b/code/modules/client/loadout/loadout_accessories.dm
@@ -50,7 +50,39 @@
display_name = "tie, recolorable"
path = /obj/item/clothing/neck/tie
+//Gloves
+
+/datum/gear/accessory/gloves
+ subtype_path = /datum/gear/accessory/gloves
+ slot = ITEM_SLOT_GLOVES
+
+/datum/gear/accessory/gloves/black
+ display_name = "gloves, black"
+ description = "Standard hand coverings for everyday use."
+ path = /obj/item/clothing/gloves/color/black
+
+/datum/gear/accessory/gloves/white
+ display_name = "gloves, white"
+ description = "Standard hand coverings for everyday use."
+ path = /obj/item/clothing/gloves/color/white
+
+/datum/gear/accessory/gloves/brown
+ display_name = "gloves, brown"
+ description = "Standard hand coverings for everyday use."
+ path = /obj/item/clothing/gloves/color/brown
+
+/datum/gear/accessory/gloves/fingerless
+ display_name = "gloves, fingerless"
+ description = "Radical hand coverings for everyday use."
+ path = /obj/item/clothing/gloves/fingerless
+
+/datum/gear/accessory/gloves/evening
+ display_name = "gloves, evening"
+ description = "Excessively fancy elbow-length gloves."
+ path = /obj/item/clothing/gloves/color/evening
+
//Bone
+
/datum/gear/accessory/fangnecklace
display_name = "wolf fang necklace"
path = /obj/item/clothing/neck/fangnecklace
@@ -60,6 +92,36 @@
path = /obj/item/clothing/accessory/bonearmlet
slot = null
+//Masks
+
+/datum/gear/accessory/mask
+ subtype_path = /datum/gear/accessory/mask
+ slot = ITEM_SLOT_MASK
+
+/datum/gear/accessory/mask/bandana/red
+ display_name = "bandana, red"
+ path = /obj/item/clothing/mask/bandana/red
+
+/datum/gear/accessory/mask/bandana/skull
+ display_name = "bandana, skull"
+ path = /obj/item/clothing/mask/bandana/skull
+
+/datum/gear/accessory/mask/bandana/black
+ display_name = "bandana, black"
+ path = /obj/item/clothing/mask/bandana/black
+
+/datum/gear/accessory/mask/bandana/blue
+ display_name = "bandana, blue"
+ path = /obj/item/clothing/mask/bandana/blue
+
+/datum/gear/accessory/mask/surgical
+ display_name = "surgical mask"
+ path = /obj/item/clothing/mask/surgical
+
+/datum/gear/accessory/mask/balaclava
+ display_name = "balaclava"
+ path = /obj/item/clothing/mask/balaclava
+
//Misc
/datum/gear/accessory/waistcoat
@@ -72,23 +134,11 @@
path = /obj/item/clothing/neck/stethoscope
allowed_roles = list("Medical Doctor", "Chief Medical Officer")
-/datum/gear/accessory/gloves/black
- display_name = "black gloves"
- description = "Standard hand coverings for everyday use."
- path = /obj/item/clothing/gloves/color/black
-
-/datum/gear/accessory/gloves/white
- display_name = "white gloves"
- description = "Standard hand coverings for everyday use."
- path = /obj/item/clothing/gloves/color/white
+/datum/gear/accessory/headphones
+ display_name = "headphones"
+ slot = ITEM_SLOT_EARS
+ path = /obj/item/instrument/piano_synth/headphones
-/datum/gear/accessory/gloves/fingerless
- display_name = "fingerless gloves"
- description = "Radical hand coverings for everyday use."
- path = /obj/item/clothing/gloves/fingerless
-
-/datum/gear/accessory/gloves/evening
- display_name = "evening gloves"
- description = "Excessively fancy elbow-length gloves."
- path = /obj/item/clothing/gloves/color/evening
- slot = ITEM_SLOT_GLOVES
+/datum/gear/accessory/pocketprotector
+ display_name = "pocket protector"
+ path = /obj/item/clothing/accessory/pocketprotector
diff --git a/code/modules/client/loadout/loadout_eyewear.dm b/code/modules/client/loadout/loadout_eyewear.dm
index 99e868ad0854..3ea37d68fed8 100644
--- a/code/modules/client/loadout/loadout_eyewear.dm
+++ b/code/modules/client/loadout/loadout_eyewear.dm
@@ -46,7 +46,10 @@
description = "A blindfold you can still see through."
path = /obj/item/clothing/glasses/trickblindfold
-
+/datum/gear/eyewear/doubleeyepatch
+ display_name = "double eyepatch"
+ description = "Two eyepatches at once! Effectively a blindfold, though."
+ path = /obj/item/clothing/glasses/blindfold/eyepatch
/datum/gear/eyewear/glasses/cold
display_name = "cold goggles"
diff --git a/code/modules/client/loadout/loadout_footwear.dm b/code/modules/client/loadout/loadout_footwear.dm
index dd4632f9bc34..3e65cd9ed5d3 100644
--- a/code/modules/client/loadout/loadout_footwear.dm
+++ b/code/modules/client/loadout/loadout_footwear.dm
@@ -64,3 +64,17 @@
/datum/gear/footwear/color/white
display_name = "white shoes"
path = /obj/item/clothing/shoes/sneakers/white
+
+//Cowboy boots
+
+/datum/gear/footwear/cowboy
+ display_name = "cowboy boots, brown"
+ path = /obj/item/clothing/shoes/cowboy
+
+/datum/gear/footwear/cowboy/black
+ display_name = "cowboy boots, black"
+ path = /obj/item/clothing/shoes/cowboy/black
+
+/datum/gear/footwear/cowboy/white
+ display_name = "cowboy boots, white"
+ path = /obj/item/clothing/shoes/cowboy/white
diff --git a/code/modules/client/loadout/loadout_general.dm b/code/modules/client/loadout/loadout_general.dm
index 8bb3ff3cb69d..712500f9fd62 100644
--- a/code/modules/client/loadout/loadout_general.dm
+++ b/code/modules/client/loadout/loadout_general.dm
@@ -14,10 +14,6 @@
display_name = "lipstick, red"
path = /obj/item/lipstick
-/datum/gear/balaclava
- display_name = "balaclava"
- path = /obj/item/clothing/mask/balaclava
-
/datum/gear/vape
display_name = "vape"
path = /obj/item/clothing/mask/vape
@@ -26,10 +22,6 @@
display_name = "e-cigar"
path = /obj/item/clothing/mask/vape/cigar
-/datum/gear/bandana
- display_name = "bandana, red"
- path = /obj/item/clothing/mask/bandana/red
-
/datum/gear/flask
display_name = "flask"
path = /obj/item/reagent_containers/food/drinks/flask
@@ -58,12 +50,17 @@
display_name = "toy, deck of cards"
path = /obj/item/toy/cards/deck
+/datum/gear/kotahi
+ display_name = "toy, deck of KOTAHI cards"
+ path = /obj/item/toy/cards/deck/kotahi
+
/datum/gear/eightball
display_name = "toy, magic eight ball"
path = /obj/item/toy/eightball
/datum/gear/pai
display_name = "personal AI device"
+ description = "A synthetic friend that fits in your pocket."
path = /obj/item/paicard
/datum/gear/tablet
@@ -86,6 +83,10 @@
display_name = "pen, four-color"
path = /obj/item/pen/fourcolor
+/datum/gear/fountainpen
+ display_name = "pen, fountain"
+ path = /obj/item/pen/fountain
+
/datum/gear/paperbin
display_name = "paper bin"
path = /obj/item/paper_bin
@@ -127,7 +128,6 @@
display_name = "toy, rilena tali plushie"
path = /obj/item/toy/plush/tali
-// Shiptest edit
/datum/gear/amongus
display_name = "toy, suspicious pill plushie"
path = /obj/item/toy/plush/among
@@ -150,8 +150,6 @@
display_name = "table bell, brass"
path = /obj/item/table_bell/brass
-// End Shiptest
-
/datum/gear/flashlight
display_name = "tool, flashlight"
path = /obj/item/flashlight
@@ -160,11 +158,11 @@
display_name = "tool, emergency crowbar"
path = /obj/item/crowbar/red
-/datum/gear/surgical_mask
- display_name = "surgical mask"
- path = /obj/item/clothing/mask/surgical
-
/datum/gear/rilena_poster
display_name = "poster, rilena"
path = /obj/item/poster/random_rilena
description = "A random poster of the RILENA series."
+
+/datum/gear/camera
+ display_name = "polaroid camera"
+ path = /obj/item/camera
diff --git a/code/modules/client/loadout/loadout_hat.dm b/code/modules/client/loadout/loadout_hat.dm
index 23e34d7d19c3..32384a0d59fa 100644
--- a/code/modules/client/loadout/loadout_hat.dm
+++ b/code/modules/client/loadout/loadout_hat.dm
@@ -27,6 +27,10 @@
display_name = "beret, red"
path = /obj/item/clothing/head/beret
+/datum/gear/hat/beret/black
+ display_name = "beret, black"
+ path = /obj/item/clothing/head/beret/black
+
/datum/gear/hat/beret/departmental
display_name = "beret, departmental"
path = /obj/item/clothing/head/beret/grey
@@ -74,24 +78,83 @@
path = /obj/item/clothing/head/beret/eng/hazard
allowed_roles = list("Station Engineer", "Atmospheric Technician", "Chief Engineer")
+//Soft caps
+
+/datum/gear/hat/softcap/red
+ display_name = "cap, red"
+ path = /obj/item/clothing/head/soft/red
+
+/datum/gear/hat/softcap/blue
+ display_name = "cap, blue"
+ path = /obj/item/clothing/head/soft/blue
+
+/datum/gear/hat/softcap/grey
+ display_name = "cap, grey"
+ path = /obj/item/clothing/head/soft/grey
+
+/datum/gear/hat/softcap/white
+ display_name = "cap, white"
+ path = /obj/item/clothing/head/soft/mime
+
+/datum/gear/hat/softcap/black
+ display_name = "cap, black"
+ path = /obj/item/clothing/head/soft/black
+
+//Beanies
+
+/datum/gear/hat/beanie
+ display_name = "beanie, white"
+ path = /obj/item/clothing/head/beanie
+
+/datum/gear/hat/beanie/black
+ display_name = "beanie, black"
+ path = /obj/item/clothing/head/beanie/black
+
+/datum/gear/hat/beanie/red
+ display_name = "beanie, red"
+ path = /obj/item/clothing/head/beanie/red
+
+/datum/gear/hat/beanie/green
+ display_name = "beanie, green"
+ path = /obj/item/clothing/head/beanie/green
+
+/datum/gear/hat/beanie/purple
+ display_name = "beanie, purple"
+ path = /obj/item/clothing/head/beanie/purple
+
+/datum/gear/hat/beanie/blue
+ display_name = "beanie, blue"
+ path = /obj/item/clothing/head/beanie/darkblue
+
+/datum/gear/hat/beanie/orange
+ display_name = "beanie, orange"
+ path = /obj/item/clothing/head/beanie/orange
//Misc
+/datum/gear/hat/bowler
+ display_name = "bowler hat"
+ path = /obj/item/clothing/head/bowler
+
/datum/gear/hat/that
display_name = "top hat"
path = /obj/item/clothing/head/that
/datum/gear/hat/fedora
- display_name = "fedora"
+ display_name = "fedora, black"
path = /obj/item/clothing/head/fedora
+/datum/gear/hat/fedora/white
+ display_name = "fedora, white"
+ path = /obj/item/clothing/head/fedora/white
+
+/datum/gear/hat/fedora/beige
+ display_name = "fedora, beige"
+ path = /obj/item/clothing/head/fedora/beige
+
/datum/gear/hat/flatcap
display_name = "flatcap"
path = /obj/item/clothing/head/flatcap
-/datum/gear/hat/beanie
- display_name = "beanie"
- path = /obj/item/clothing/head/beanie
-
/datum/gear/hat/wig
display_name = "wig"
path = /obj/item/clothing/head/wig
diff --git a/code/modules/client/loadout/loadout_suit.dm b/code/modules/client/loadout/loadout_suit.dm
index f8757bfa5b38..217998802d48 100644
--- a/code/modules/client/loadout/loadout_suit.dm
+++ b/code/modules/client/loadout/loadout_suit.dm
@@ -55,8 +55,6 @@
display_name = "suit jacket, charcoal"
path = /obj/item/clothing/suit/toggle/lawyer/charcoal
-/datum/gear/suit/jacket/navy //why is this blank? i dont know
-
/datum/gear/suit/jacket/hoodie_black
display_name = "hoodie, black"
path = /obj/item/clothing/suit/hooded/hoodie/black
@@ -93,6 +91,19 @@
display_name = "hazard jacket"
path = /obj/item/clothing/suit/toggle/hazard
+//Suspenders
+/datum/gear/suit/suspenders/red
+ display_name = "suspenders, red"
+ path = /obj/item/clothing/suit/toggle/suspenders
+
+/datum/gear/suit/suspenders/blue
+ display_name = "suspenders, blue"
+ path = /obj/item/clothing/suit/toggle/suspenders/blue
+
+/datum/gear/suit/suspenders/gray
+ display_name = "suspenders, gray"
+ path = /obj/item/clothing/suit/toggle/suspenders/gray
+
//Misc
/datum/gear/suit/grponcho
display_name = "poncho, green"
@@ -119,3 +130,11 @@
display_name = "floral shirt"
description = "From grills to guns, this shirt's seen it all."
path = /obj/item/clothing/suit/hawaiian
+
+/datum/gear/suit/hazardvest
+ display_name = "hazard vest"
+ path = /obj/item/clothing/suit/hazardvest
+
+/datum/gear/suit/longcoat
+ display_name = "longcoat"
+ path = /obj/item/clothing/suit/longcoat
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 006074e74bcd..ec5b7d188db0 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -5,7 +5,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//doohickeys for savefiles
var/path
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
- var/max_save_slots = 20
+ var/max_save_slots = 30
//non-preference stuff
var/muted = 0
@@ -227,7 +227,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
load_path(C.ckey)
unlock_content = C.IsByondMember()
if(unlock_content)
- max_save_slots = 30
+ max_save_slots = 50
var/loaded_preferences_successfully = load_preferences()
if(loaded_preferences_successfully)
if(load_character())
@@ -1435,7 +1435,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
balance -= initial(quirk_type.value)
switch(change_type)
if("species")
- if((quirk_name in SSquirks.species_blacklist) && (pref_species.id in SSquirks.species_blacklist[quirk_name]))
+ if((quirk_name in SSquirks.species_blacklist) && (target_species.id in SSquirks.species_blacklist[quirk_name]))
all_quirks_new -= quirk_name
balance += initial(quirk_type.value)
if("mood")
diff --git a/code/modules/clothing/factions/clip.dm b/code/modules/clothing/factions/clip.dm
index c8a0dbdda278..7ac3668aaf77 100644
--- a/code/modules/clothing/factions/clip.dm
+++ b/code/modules/clothing/factions/clip.dm
@@ -140,10 +140,10 @@
icon_state = "clip_trenchcoat"
item_state = "trenchcoat_solgov"
- body_parts_covered = CHEST|LEGS|ARMS
+ body_parts_covered = CHEST|GROIN
armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
- cold_protection = CHEST|LEGS|ARMS
- heat_protection = CHEST|LEGS|ARMS
+ cold_protection = CHEST|GROIN|LEGS|ARMS
+ heat_protection = CHEST|GROIN|LEGS|ARMS
supports_variations = DIGITIGRADE_VARIATION_SAME_ICON_FILE
@@ -156,7 +156,7 @@
icon_state = "clip_captaincoat"
item_state = "clip_captaincoat"
- body_parts_covered = CHEST|LEGS|ARMS
+ body_parts_covered = CHEST
armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
cold_protection = CHEST|LEGS|ARMS
heat_protection = CHEST|LEGS|ARMS
diff --git a/code/modules/clothing/factions/frontiersmen.dm b/code/modules/clothing/factions/frontiersmen.dm
index d232f775e607..77af8138fb40 100644
--- a/code/modules/clothing/factions/frontiersmen.dm
+++ b/code/modules/clothing/factions/frontiersmen.dm
@@ -73,13 +73,13 @@
name = "reinforced fur coat"
desc = "A stiff olive-green coat, meant for frigid conditions. Commonly worn by Frontiersmen command."
icon_state = "frontier_coat"
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
cold_protection = CHEST|GROIN|ARMS
heat_protection = CHEST|GROIN|ARMS
icon_state = "frontier_coat"
item_state = "frontier_coat"
blood_overlay_type = "coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
icon = 'icons/obj/clothing/faction/frontiersmen/suits.dmi'
mob_overlay_icon = 'icons/mob/clothing/faction/frontiersmen/suits.dmi'
@@ -87,7 +87,7 @@
name = "frontiersmen fireproof coat"
desc = "A stiff olive-green coat, used particularly by Frontiersmen flame troopers. It seems to be lined with asbestos, to provide maximum heat and fire deterrence... At the cost of comfort. And mesothelioma."
icon_state = "frontier_fireproof_suit"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
w_class = WEIGHT_CLASS_BULKY
gas_transfer_coefficient = 0.9
permeability_coefficient = 0.5
@@ -144,7 +144,7 @@
name = "\improper Frontiersmen officer beret"
desc = "A scratchy olive green beret emblazoned with the Frontiersmen insignia, worn by Frontiersmen who want to look good while intimidating freighter captains."
icon_state = "frontier_officer_beret"
- armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/head/frontier
name = "frontier surgical cap"
diff --git a/code/modules/clothing/factions/gezena.dm b/code/modules/clothing/factions/gezena.dm
index 96c6eee3c734..eabc0fe752c4 100644
--- a/code/modules/clothing/factions/gezena.dm
+++ b/code/modules/clothing/factions/gezena.dm
@@ -36,10 +36,12 @@
item_state = "bluecloth"
blood_overlay_type = "coat"
togglename = "zipper"
- body_parts_covered = CHEST
+ body_parts_covered = CHEST|GROIN|ARMS
+ cold_protection = CHEST|GROIN|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
pocket_storage_component_path = /datum/component/storage/concrete/pockets/exo
supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON
- armor = list("melee" = 20, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
//Armored suit
@@ -130,6 +132,9 @@
desc = "The standard cap of the PGF military, in Navy colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects."
icon_state = "navalflap"
item_state = "bluecloth"
+ cold_protection = HEAD
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/head/gezena/marine
name = "\improper PGFMC Cap"
@@ -137,7 +142,7 @@
icon_state = "marinehat"
item_state = "marinecloth"
-/obj/item/clothing/head/gezena/marine/flap
+/obj/item/clothing/head/gezena/flap/marine
name = "\improper PGFMC Betzu-il cap"
desc = "The standard cap of the PGF military, in Marine Corps colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects."
icon_state = "marineflap"
@@ -149,7 +154,7 @@
icon_state = "squadhat"
item_state = "marinecloth"
-/obj/item/clothing/head/gezena/marine/lead/flap
+/obj/item/clothing/head/gezena/flap/marine/lead
name = "\improper PGFMC Commander's' Betzu-il cap"
desc = "The standard cap of the PGF military, in Marine Corps colors. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects. The silver markings denote it as a commander's cap."
icon_state = "squadflap"
@@ -161,7 +166,7 @@
icon_state = "medichat"
item_state = "whitecloth"
-/obj/item/clothing/head/gezena/medic/flap
+/obj/item/clothing/head/gezena/flap/medic
name = "\improper PGF medic Betzu-il cap"
desc = "The standard cap of the PGF military. “betzu-il”, translating to “sun-blocker”, refers to the flap at the back for protection against natural hazards such as sunburns, sandstorms, and biting insects. The coloring indicates the wearer as a medical officer."
icon_state = "medicflap"
@@ -260,8 +265,8 @@
item_state = "blackcloth"
/obj/item/clothing/neck/cloak/gezena/lead
- name = "sergeant's Azuilhauz"
- desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as a squad commander."
+ name = "marine officer's Azuilhauz"
+ desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as a squad leader."
icon_state = "squadcape"
item_state = "blackcloth"
@@ -278,7 +283,7 @@
item_state = "whitecloth"
/obj/item/clothing/neck/cloak/gezena/command
- name = "officer's Azuilhauz"
+ name = "navy officer's Azuilhauz"
desc = "The “Aziulhauz”, or “rank-cape”, is the method with which PGF military members display their rank to others. Wearing one while on duty is required by uniform code. This variant displays the wearer's rank as an officer."
icon_state = "commandcape"
item_state = "whitecloth"
diff --git a/code/modules/clothing/factions/hardliners.dm b/code/modules/clothing/factions/hardliners.dm
index b1a7c5e96e2a..a02275d13e05 100644
--- a/code/modules/clothing/factions/hardliners.dm
+++ b/code/modules/clothing/factions/hardliners.dm
@@ -69,22 +69,22 @@
/obj/item/clothing/suit/armor/hardliners/sergeant
name = "hardliners sergeant jacket"
desc = "An armored jacket typically worn by sergeant of the Hardliners. They're reminiscent of the garb worn by old Gorlex navymen, prior to its destruction."
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
icon_state = "hl_sergeant"
item_state = "hl_sergeant"
blood_overlay_type = "coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/suit/toggle/armor/vest/hardliners
name = "hardliners captain coat"
desc = "An imposing armored coat worn by captains of Hardliner fleets, hand-designed by Cybersun tailors to provide maximum protection to its wearer."
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
icon_state = "hl_captain"
item_state = "hl_captain"
icon = 'icons/obj/clothing/faction/hardliners/suits.dmi'
mob_overlay_icon = 'icons/mob/clothing/faction/hardliners/suits.dmi'
blood_overlay_type = "coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
togglename = "buttons"
///////////////
diff --git a/code/modules/clothing/factions/nanotrasen.dm b/code/modules/clothing/factions/nanotrasen.dm
index 664c534d236e..7aa871ad7f3e 100644
--- a/code/modules/clothing/factions/nanotrasen.dm
+++ b/code/modules/clothing/factions/nanotrasen.dm
@@ -230,7 +230,7 @@
icon_state = "armor"
item_state = "blackcloth"
body_parts_covered = CHEST|GROIN
- armor = list("melee" = 30, "bullet" = 40, "laser" = 30, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
resistance_flags = FIRE_PROOF
/obj/item/clothing/suit/armor/nanotrasen/slim
@@ -241,8 +241,8 @@
name = "security director's overcoat"
desc = "A tailored black overcoat, made from cutting-edge ballistic fabrics and composites. Vigilitas's 'VI' logo is embossed on every button. Intimidating and profoundly stylish."
icon_state = "command_coat"
- body_parts_covered = CHEST|GROIN|ARMS
- armor = list("melee" = 30, "bullet" = 0, "laser" = 30, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
+ body_parts_covered = CHEST|GROIN
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90)
/obj/item/clothing/suit/armor/nanotrasen/captain
name = "captain's jacket"
@@ -250,7 +250,7 @@
icon_state = "armor_captain"
item_state = "bluecloth"
body_parts_covered = CHEST|GROIN
- armor = list("melee" = 50, "bullet" = 60, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
+ armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
resistance_flags = FIRE_PROOF
/obj/item/clothing/suit/armor/nanotrasen/captain/parade
@@ -258,7 +258,7 @@
desc = "An exquisitely-decorated fine blue jacket, suitable for especially formal situations, or for a commanding officer who wants to flaunt their status even more than usual. Richly decorated with gold thread and embroidered Nanotrasen logos."
icon_state = "captain_formal"
item_state = "bluecloth"
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
armor = list("melee" = 30, "bullet" = 0, "laser" = 30, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
// Hats //
diff --git a/code/modules/clothing/factions/ngr.dm b/code/modules/clothing/factions/ngr.dm
index 4a70d10327c6..7098dd6f1de9 100644
--- a/code/modules/clothing/factions/ngr.dm
+++ b/code/modules/clothing/factions/ngr.dm
@@ -82,20 +82,20 @@
/obj/item/clothing/suit/armor/ngr/lieutenant
name = "\improper 2nd Battlegroup overcoat"
desc = "An armored overcoat worn by the lieutenants of the New Gorlex Republic's 2nd Battlegroup."
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
icon_state = "ngr_lieutenant"
item_state = "ngr_lieutenant"
blood_overlay_type = "coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/suit/armor/ngr/captain
name = "\improper 2nd Battlegroup coat"
desc = "An armored coat worn by captains the New Gorlex Republic's 2nd Battlegroup."
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
icon_state = "ngr_captain"
item_state = "ngr_captain"
blood_overlay_type = "coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
///////////////
//Spacesuits//
@@ -123,6 +123,7 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/ngr
lightweight = 1
jetpack = null
+ greyscale_colors = list("#33353a", "#d9ad82", "#8c1a34")
/obj/item/clothing/head/helmet/space/plasmaman/ngr
name = "NGR phorid envirosuit helmet"
@@ -142,7 +143,6 @@
icon_state = "ngr_garrison"
icon = 'icons/obj/clothing/faction/ngr/head.dmi'
mob_overlay_icon = 'icons/mob/clothing/faction/ngr/head.dmi'
- armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/head/ngr/flap
name = "beige flap cap"
diff --git a/code/modules/clothing/factions/srm.dm b/code/modules/clothing/factions/srm.dm
index 07639696bb01..34e6b4218fe4 100644
--- a/code/modules/clothing/factions/srm.dm
+++ b/code/modules/clothing/factions/srm.dm
@@ -21,7 +21,7 @@
desc = "A coat made from hard leather. Meant to withstand long hunts in harsh wilderness."
icon_state = "armor_rouma"
item_state = "rouma_coat"
- body_parts_covered = CHEST|GROIN|ARMS
+ body_parts_covered = CHEST|GROIN
cold_protection = CHEST|GROIN|ARMS
heat_protection = CHEST|GROIN|ARMS
icon = 'icons/obj/clothing/faction/srm/suits.dmi'
@@ -41,7 +41,7 @@
icon = 'icons/obj/clothing/faction/srm/suits.dmi'
mob_overlay_icon = 'icons/mob/clothing/faction/srm/suits.dmi'
icon_state = "rouma_med_coat"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50)
supports_variations = KEPORI_VARIATION
/obj/item/clothing/suit/hazardvest/roumain
@@ -51,7 +51,7 @@
mob_overlay_icon = 'icons/mob/clothing/faction/srm/suits.dmi'
icon_state = "armor_rouma_machinist"
item_state = "rouma_coat"
- armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 40, "bomb" = 35, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60)
+ armor = list("melee" = 35, "bullet" = 20, "laser" = 20, "energy" = 40, "bomb" = 35, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60)
/obj/item/clothing/suit/armor/roumain/flamebearer
name = "saint-roumain flamebearer robes"
@@ -64,7 +64,7 @@
desc = "A well-maintained hard leather coat typically worn to denote the rank of Colligne, a trainee Hunter Montagne. It is treated with bullet-resistant materials, and lined with the dark fur of Illestrian dire wolves."
icon_state = "armor_rouma_colligne"
item_state = "rouma_coat"
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
+ body_parts_covered = CHEST|GROIN
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
supports_variations = KEPORI_VARIATION
@@ -74,8 +74,8 @@
desc = "A stylish red coat to indicate that you are, in fact, a Hunter Montagne. Made of extra hard exotic leather, treated with bullet-resistant materials, and lined with the fur of some unidentifiable creature."
icon_state = "armor_rouma_montagne"
item_state = "rouma_montagne_coat"
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
- armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90)
+ body_parts_covered = CHEST|GROIN
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
supports_variations = KEPORI_VARIATION
diff --git a/code/modules/clothing/factions/suns.dm b/code/modules/clothing/factions/suns.dm
index df6d831e479b..b005b85caa2e 100644
--- a/code/modules/clothing/factions/suns.dm
+++ b/code/modules/clothing/factions/suns.dm
@@ -162,7 +162,7 @@
/////////////////
-/obj/item/clothing/suit/armor/vest/bulletproof/suns
+/obj/item/clothing/suit/armor/vest/suns
name = "peacekeeper plating"
desc = "A standard issue set of plate assigned to peacekeepers, both durable and stylish."
icon_state = "suns_pkarmor"
@@ -171,21 +171,22 @@
mob_overlay_icon = 'icons/mob/clothing/faction/suns/suits.dmi'
lefthand_file = 'icons/mob/inhands/faction/suns/suns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/faction/suns/suns_righthand.dmi'
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/hos
+/obj/item/clothing/suit/armor/vest/suns/hos
name = "gilded peacekeeper plating"
desc = "A set of plate assigned to peacekeepers, both durable and stylish. This one has a gold lining to indicate rank."
icon_state = "suns_lpkarmor"
item_state = "suns_pkarmor"
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/ehos //remind me to make this something to buy
+/obj/item/clothing/suit/armor/vest/suns/ehos //remind me to make this something to buy
name = "peacekeeper greatcoat"
desc = "A funky armored coat worn by eccentric peacekeepers. Closing the coat is socially improper."
icon_state = "suns_greatcoat"
item_state = "suns_greatcoat"
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
/obj/item/clothing/suit/toggle/suns/pkcoat
name = "peacekeeper coat"
@@ -195,23 +196,23 @@
armor = list("melee" = 15, "bullet" = 30, "laser" = 10, "energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 25)
lefthand_file = 'icons/mob/inhands/faction/suns/suns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/faction/suns/suns_righthand.dmi'
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
+ body_parts_covered = CHEST|GROIN
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/captain
+/obj/item/clothing/suit/armor/vest/suns/captain
name = "decorated academic coat"
desc = "An armored coat intended for SUNS captains on the frontier. Go forth, and spread the message of the academy."
icon_state = "suns_captaincoat"
item_state = "suns_overblack"
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
-/obj/item/clothing/suit/armor/vest/bulletproof/suns/xo
+/obj/item/clothing/suit/armor/vest/suns/xo
name = "academic staff coat"
desc = "A white coat used by SUNS academic staff. It designates the second in command on the ship."
icon_state = "suns_xojacket"
item_state = "suns_overwhite"
-
///////////////
//Spacesuits//
//////////////
@@ -343,7 +344,7 @@
icon_state = "sunsvisor"
item_state = "suns_pkhelmet"
tint = 0
- armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) // identical stats to bulletproof helmet, as chest matches bulletproof vest
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) // identical stats to vest
clothing_flags = BLOCK_GAS_SMOKE_EFFECT | ALLOWINTERNALS //Why? Because I'm not giving PK's sec masks nor hud sunglasses.
icon = 'icons/obj/clothing/faction/suns/head.dmi'
mob_overlay_icon = 'icons/mob/clothing/faction/suns/head.dmi'
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 59530e24a542..c1b54fc5adc5 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -378,7 +378,7 @@
colored_before = TRUE
/obj/item/clothing/glasses/blindfold/white/worn_overlays(isinhands = FALSE, file2use)
- . = list()
+ . = ..()
if(!isinhands && ishuman(loc) && !colored_before)
var/mob/living/carbon/human/H = loc
var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/eyes.dmi', "blindfoldwhite")
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 61c06125d8f8..a6e9f22ea3e7 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -25,7 +25,7 @@
return TRUE
/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm
index 4039402588fd..aa1114e6b182 100644
--- a/code/modules/clothing/head/_head.dm
+++ b/code/modules/clothing/head/_head.dm
@@ -60,7 +60,7 @@
/obj/item/clothing/head/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet")
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index cdfe4672d46f..435ad484a4ae 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -6,7 +6,7 @@
icon_state = "helmet"
item_state = "helmet"
var/flashlight_state = "helmet_flight_overlay"
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35,"energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
cold_protection = HEAD
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
heat_protection = HEAD
@@ -327,7 +327,7 @@
desc = "An extremely robust, space-worthy helmet in a nefarious red and black stripe pattern."
icon_state = "swatsyndie"
item_state = "swatsyndie"
- armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 40, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100)
+ armor = list("melee" = 40, "bullet" = 35, "laser" = 35,"energy" = 40, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100)
cold_protection = HEAD
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
heat_protection = HEAD
diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm
index 24e2f95f03bd..5b8e228b49ee 100644
--- a/code/modules/clothing/head/misc_special.dm
+++ b/code/modules/clothing/head/misc_special.dm
@@ -241,7 +241,7 @@
return ..()
/obj/item/clothing/head/wig/worn_overlays(isinhands = FALSE, file2use)
- . = list()
+ . = ..()
if(!isinhands)
var/datum/sprite_accessory/S = GLOB.hairstyles_list[hairstyle]
if(!S)
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 03ca246b60af..a4c1d5d509fa 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -32,7 +32,7 @@
/obj/item/clothing/mask/proc/handle_speech()
/obj/item/clothing/mask/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(body_parts_covered & HEAD)
if(damaged_clothes)
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 36217ac9b932..2165baaa2b0e 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -11,7 +11,7 @@
greyscale_icon_state = "scarf"
/obj/item/clothing/neck/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(body_parts_covered & HEAD)
if(damaged_clothes)
diff --git a/code/modules/clothing/outfits/ert/solgov_ert.dm b/code/modules/clothing/outfits/ert/solgov_ert.dm
index da3a1146648d..d6830b751498 100644
--- a/code/modules/clothing/outfits/ert/solgov_ert.dm
+++ b/code/modules/clothing/outfits/ert/solgov_ert.dm
@@ -6,7 +6,7 @@
id = /obj/item/card/id/solgov
uniform = /obj/item/clothing/under/solgov
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/solgov
+ suit = /obj/item/clothing/suit/armor/vest/solgov
mask = null
ears = /obj/item/radio/headset/solgov/alt
gloves = /obj/item/clothing/gloves/combat
diff --git a/code/modules/clothing/outfits/factions/solgov.dm b/code/modules/clothing/outfits/factions/solgov.dm
index 972b863bbbda..8e8710c86dbf 100644
--- a/code/modules/clothing/outfits/factions/solgov.dm
+++ b/code/modules/clothing/outfits/factions/solgov.dm
@@ -47,7 +47,7 @@
gloves = /obj/item/clothing/gloves/combat
ears = /obj/item/radio/headset/solgov/alt/captain
uniform = /obj/item/clothing/under/solgov/formal/captain
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/solgov/captain
+ suit = /obj/item/clothing/suit/armor/vest/solgov/captain
shoes = /obj/item/clothing/shoes/laceup
head = /obj/item/clothing/head/solgov/captain
backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1)
@@ -69,7 +69,7 @@
id = /obj/item/card/id/solgov
uniform = /obj/item/clothing/under/solgov
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/solgov
+ suit = /obj/item/clothing/suit/armor/vest/solgov
ears = /obj/item/radio/headset/solgov/alt
gloves = /obj/item/clothing/gloves/combat
head = /obj/item/clothing/head/solgov/sonnensoldner
@@ -115,7 +115,7 @@
uniform = /obj/item/clothing/under/solgov/formal
head = /obj/item/clothing/head/solgov
neck = /obj/item/clothing/neck/cloak/overseer
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/solgov/overseer
+ suit = /obj/item/clothing/suit/armor/vest/solgov/overseer
shoes = /obj/item/clothing/shoes/laceup
backpack_contents = list(/obj/item/storage/box/ids=1,\
diff --git a/code/modules/clothing/outfits/factions/syndicate.dm b/code/modules/clothing/outfits/factions/syndicate.dm
index 479965e756b4..639839d1b65a 100644
--- a/code/modules/clothing/outfits/factions/syndicate.dm
+++ b/code/modules/clothing/outfits/factions/syndicate.dm
@@ -353,7 +353,7 @@
shoes = /obj/item/clothing/shoes/combat/suns
head = /obj/item/clothing/head/suns/captain
gloves = /obj/item/clothing/gloves/suns/captain
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/suns/captain
+ suit = /obj/item/clothing/suit/armor/vest/suns/captain
belt = /obj/item/storage/belt/sabre/suns/captain
mask = /obj/item/clothing/mask/breath/suns
neck = /obj/item/clothing/neck/cloak/suns/cap
@@ -540,7 +540,7 @@
id_assignment = "Academic Staff"
uniform = /obj/item/clothing/under/syndicate/suns/xo
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/suns/xo
+ suit = /obj/item/clothing/suit/armor/vest/suns/xo
belt = /obj/item/storage/belt/sabre/suns
shoes = /obj/item/clothing/shoes/combat/suns
head = /obj/item/clothing/head/suns
@@ -633,7 +633,7 @@
id_assignment = "Senior Peacekeeper"
uniform = /obj/item/clothing/under/syndicate/suns/pkuniform
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/suns/hos
+ suit = /obj/item/clothing/suit/armor/vest/suns/hos
belt = /obj/item/melee/sabre/suns/telescopic
gloves = /obj/item/clothing/gloves/tackler/dolphin/suns
shoes = /obj/item/clothing/shoes/combat/suns
@@ -648,7 +648,7 @@
/datum/outfit/job/syndicate/hos/suns/alt
name = "Syndicate - Senior Peacekeeper Alt (SUNS)"
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/suns/ehos
+ suit = /obj/item/clothing/suit/armor/vest/suns/ehos
head = /obj/item/clothing/head/HoS/syndicate/suns
/datum/outfit/job/syndicate/hos/suns/twink
@@ -992,7 +992,7 @@
id_assignment = "Peacekeeper"
uniform = /obj/item/clothing/under/syndicate/suns/pkuniform
- suit = /obj/item/clothing/suit/armor/vest/bulletproof/suns
+ suit = /obj/item/clothing/suit/armor/vest/suns
alt_suit = /obj/item/clothing/suit/toggle/suns/pkcoat
belt = /obj/item/melee/sabre/suns/telescopic
gloves = /obj/item/clothing/gloves/tackler/dolphin/suns
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index 336ac43c7d4d..cd4474588090 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -29,7 +29,7 @@
var/atom/movable/screen/alert/our_alert
/obj/item/clothing/shoes/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe")
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index 2069b1e1e7f3..3c54e44cf035 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -734,23 +734,6 @@
item_state = "capspacesuit"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/swat/captain
- //Clown
-/obj/item/clothing/head/helmet/space/hardsuit/clown
- name = "cosmohonk hardsuit helmet"
- desc = "A special helmet designed for work in a hazardous, low-humor environment. Has radiation shielding."
- icon_state = "hardsuit0-clown"
- item_state = "hardsuit0-clown"
- armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 60, "acid" = 30)
- hardsuit_type = "clown"
-
-/obj/item/clothing/suit/space/hardsuit/clown
- name = "cosmohonk hardsuit"
- desc = "A special suit that protects against hazardous, low humor environments. Has radiation shielding. Only a true clown can wear it."
- icon_state = "hardsuit-clown"
- item_state = "clown_hardsuit"
- armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 60, "acid" = 30)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/clown
-
//Old Prototype
/obj/item/clothing/head/helmet/space/hardsuit/ancient
name = "prototype RIG hardsuit helmet"
@@ -863,7 +846,7 @@
C.update_inv_wear_suit()
/obj/item/clothing/suit/space/hardsuit/shielded/worn_overlays(isinhands)
- . = list()
+ . = ..()
if(!isinhands)
. += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 0e7edb63f068..30d3c3c3c9ba 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -19,7 +19,7 @@
mob_overlay_icon = 'icons/mob/clothing/suit.dmi'
/obj/item/clothing/suit/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 4b5da2de4f8e..a16680353b6f 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -2,7 +2,7 @@
icon = 'icons/obj/clothing/suits/armor.dmi'
mob_overlay_icon = 'icons/mob/clothing/suits/armor.dmi'
allowed = null
- body_parts_covered = CHEST
+ body_parts_covered = CHEST|GROIN
cold_protection = CHEST|GROIN
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
heat_protection = CHEST|GROIN
@@ -11,7 +11,7 @@
equip_delay_other = 40
max_integrity = 250
resistance_flags = NONE
- armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
greyscale_colors = list(list(18, 19), list(13, 18), list(20, 15))
greyscale_icon_state = "armor"
@@ -81,7 +81,6 @@
desc = "A long, intimidating black coat. This one is reinforced and ideal for protecting its wearer from rain, sun, dust, and bullets."
icon_state = "armor_duster"
item_state = "duster_sec"
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
@@ -90,8 +89,7 @@
desc = "A greatcoat enhanced with a special alloy for some extra protection and style for those with a commanding presence."
icon_state = "armor_hos"
item_state = "greatcoat"
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
- armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90)
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90)
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
strip_delay = 80
@@ -108,7 +106,6 @@
desc = "A black armored jacket with silver shoulder designations and '/Warden/' stitched into one of the chest pockets."
icon_state = "armor_warden"
item_state = "armor"
- body_parts_covered = CHEST|GROIN|ARMS
cold_protection = CHEST|GROIN|ARMS|HANDS
heat_protection = CHEST|GROIN|ARMS|HANDS
strip_delay = 70
@@ -135,7 +132,6 @@
desc = "Lightly armored leather overcoat meant as casual wear for high-ranking officers. Bears the crest of Nanotrasen Security."
icon_state = "armor_leathercoat-sec"
item_state = "hostrench"
- body_parts_covered = CHEST|GROIN|ARMS|LEGS
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
dog_fashion = null
@@ -145,7 +141,6 @@
desc = "A fireproof armored chestpiece reinforced with ceramic plates and plasteel pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to NT's finest, although it does chafe your nipples."
icon_state = "carapace_nt"
item_state = "armor"
- body_parts_covered = CHEST|GROIN
armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
dog_fashion = null
resistance_flags = FIRE_PROOF
@@ -209,7 +204,6 @@
icon_state = "laserproof"
item_state = "armor_reflec"
blood_overlay_type = "armor"
- body_parts_covered = CHEST|GROIN|ARMS
cold_protection = CHEST|GROIN|ARMS
heat_protection = CHEST|GROIN|ARMS
armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 60, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
@@ -360,28 +354,37 @@
/obj/item/melee/baton,
)
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov
+/obj/item/clothing/suit/armor/vest/solgov
name = "\improper Sonnensoldner gambison"
desc = "A standard armor vest fielded for SolGov's Sonnensoldners."
icon_state = "solgov_gambison"
item_state = "solgov_gambison"
supports_variations = DIGITIGRADE_VARIATION
+ body_parts_covered = CHEST|GROIN
+ cold_protection = CHEST|GROIN|ARMS
+ heat_protection = CHEST|GROIN|ARMS
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/overseer
+/obj/item/clothing/suit/armor/vest/solgov/overseer
name = "\improper SolGov Overseer robe"
desc = "An elaborately designed robe utilized by SolGov overseers."
icon_state = "solgov_overseer_robe"
item_state = "solgov_overseer_robe"
supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ cold_protection = CHEST|GROIN|LEGS|ARMS
+ heat_protection = CHEST|GROIN|LEGS|ARMS
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/captain
+/obj/item/clothing/suit/armor/vest/solgov/captain
name = "\improper SolGov Captain coat"
desc = "An armored coat typically used by SolGov captains."
icon_state = "solgov_coat"
item_state = "solgov_coat"
supports_variations = DIGITIGRADE_VARIATION_NO_NEW_ICON
+ armor = list("melee" = 35, "bullet" = 35, "laser" = 35, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ cold_protection = CHEST|GROIN|LEGS|ARMS
+ heat_protection = CHEST|GROIN|LEGS|ARMS
-/obj/item/clothing/suit/armor/vest/bulletproof/solgov/Initialize()
+/obj/item/clothing/suit/armor/vest/solgov/Initialize()
. = ..()
allowed |= list(/obj/item/gun/ballistic/automatic/assault/swiss_cheese, /obj/item/tank)
@@ -421,7 +424,6 @@
desc = "A solgov official's trenchcoat. Has a lot of pockets."
icon_state = "armor_solgov_trenchcoat"
item_state = "trenchcoat_solgov"
- body_parts_covered = CHEST|LEGS|ARMS
armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
cold_protection = CHEST|LEGS|ARMS
heat_protection = CHEST|LEGS|ARMS
@@ -430,7 +432,6 @@
//JACKETS
/obj/item/clothing/suit/armor/vest/security
item_state = "armor"
- body_parts_covered = CHEST|ARMS
cold_protection = CHEST|GROIN|ARMS|HANDS
heat_protection = CHEST|GROIN|ARMS|HANDS
strip_delay = 70
@@ -441,32 +442,27 @@
name = "security officer's jacket"
desc = "This jacket is for those special occasions when a security officer isn't required to wear their armor."
icon_state = "armor_officerjacket"
- body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/armor/vest/security/warden
name = "warden's jacket"
desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig."
icon_state = "armor_warden"
- body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/armor/vest/security/hos
name = "head of security's jacket"
desc = "This piece of clothing was specifically designed for asserting superior authority."
icon_state = "armor_hosjacket"
- body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/armor/vest/security/brig_phys
name = "brig physician's jacket"
desc = "A black jacket with dark blue and silver accents, for the brig physician to prove they're a real member of security in style."
icon_state = "armor_brigphysjacket"
- body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal
name = "\improper CentCom formal coat"
desc = "A stylish coat given to CentCom Commanders. Perfect for sending ERTs to suicide missions with style!"
icon_state = "centcom_formal"
item_state = "centcom"
- body_parts_covered = CHEST|GROIN|ARMS
armor = list("melee" = 35, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 35, "bio" = 10, "rad" = 10, "fire" = 10, "acid" = 60)
togglename = "buttons"
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 3ca620095c61..312f14dec507 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -196,7 +196,7 @@
//Mime
/obj/item/clothing/suit/toggle/suspenders
name = "suspenders"
- desc = "They suspend the illusion of the mime's play."
+ desc = "The symbol of hard labor and dirty jobs."
icon = 'icons/obj/clothing/belts.dmi'
icon_state = "suspenders"
blood_overlay_type = "armor" //it's the less thing that I can put here
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index a28d6d323a83..bc8cb512906b 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -27,7 +27,7 @@
supports_variations = VOX_VARIATION
/obj/item/clothing/under/worn_overlays(isinhands = FALSE)
- . = list()
+ . = ..()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
deleted file mode 100644
index 18131e6ed9ce..000000000000
--- a/code/modules/events/blob.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-/datum/round_event_control/blob
- name = "Blob"
- typepath = /datum/round_event/ghost_role/blob
- weight = 10
- max_occurrences = 1
-
- min_players = 20
-
- gamemode_blacklist = list("blob") //Just in case a blob survives that long
-
-/datum/round_event/ghost_role/blob
- announceChance = 0
- role_name = "blob overmind"
- fakeable = TRUE
-
-/datum/round_event/ghost_role/blob/announce(fake)
- priority_announce("Confirmed outbreak of level 5 biohazard in the vicinity of [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/ai/outbreak5.ogg')
-
-/datum/round_event/ghost_role/blob/spawn_role()
- if(!GLOB.blobstart.len)
- return MAP_ERROR
- var/list/candidates = get_candidates(ROLE_BLOB, null, ROLE_BLOB)
- if(!candidates.len)
- return NOT_ENOUGH_PLAYERS
- var/mob/dead/observer/new_blob = pick(candidates)
- var/mob/camera/blob/BC = new_blob.become_overmind()
- spawned_mobs += BC
- message_admins("[ADMIN_LOOKUPFLW(BC)] has been made into a blob overmind by an event.")
- log_game("[key_name(BC)] was spawned as a blob overmind by an event.")
- return SUCCESSFUL_SPAWN
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index f38d21b868c4..43e60c3137c3 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -74,13 +74,3 @@
/datum/round_event/santa/announce(fake)
priority_announce("Santa is coming to town!", "Unknown Transmission")
-
-/datum/round_event/santa/start()
- var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150)
- if(LAZYLEN(candidates))
- var/mob/dead/observer/C = pick(candidates)
- santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
- santa.key = C.key
-
- var/datum/antagonist/santa/A = new
- santa.mind.add_antag_datum(A)
diff --git a/code/modules/events/wizard/blobies.dm b/code/modules/events/wizard/blobies.dm
deleted file mode 100644
index 7438b462f60c..000000000000
--- a/code/modules/events/wizard/blobies.dm
+++ /dev/null
@@ -1,10 +0,0 @@
-/datum/round_event_control/wizard/blobies //avast!
- name = "Zombie Outbreak"
- weight = 3
- typepath = /datum/round_event/wizard/blobies
- max_occurrences = 3
-
-/datum/round_event/wizard/blobies/start()
-
- for(var/mob/living/carbon/human/H in GLOB.dead_mob_list)
- new /mob/living/simple_animal/hostile/blob/blobspore(H.loc)
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index 6ffea82fb945..ec10f7dfb0f2 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -762,7 +762,7 @@
desc = "If you ever wondered where air came from..."
list_reagents = list(/datum/reagent/oxygen = 6, /datum/reagent/nitrogen = 24)
icon = 'icons/obj/food/ration.dmi'
- icon_state = "ration_package"
+ icon_state = "ration_drink"
drop_sound = 'sound/items/handling/cardboardbox_drop.ogg'
pickup_sound = 'sound/items/handling/cardboardbox_pickup.ogg'
in_container = TRUE
@@ -773,7 +773,7 @@
/obj/item/reagent_containers/food/drinks/ration/proc/open_ration(mob/user)
to_chat(user, "You tear open \the [src].")
- playsound(user.loc, 'sound/effects/rip3.ogg', 50)
+ playsound(user.loc, 'sound/items/glass_cap.ogg', 50)
reagents.flags |= OPENCONTAINER
spillable = TRUE
diff --git a/code/modules/food_and_drinks/food/ration.dm b/code/modules/food_and_drinks/food/ration.dm
index 261fe707ed7f..6766a6aedbda 100644
--- a/code/modules/food_and_drinks/food/ration.dm
+++ b/code/modules/food_and_drinks/food/ration.dm
@@ -83,7 +83,7 @@
list_reagents = list(/datum/reagent/consumable/nutriment = 4)
/obj/item/reagent_containers/food/snacks/ration/snack
- icon_state = "ration_side"
+ icon_state = "ration_snack"
list_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/sugar = 3)
/obj/item/reagent_containers/food/snacks/ration/bar
@@ -92,7 +92,7 @@
/obj/item/reagent_containers/food/snacks/ration/condiment
name = "condiment pack"
- desc = "Just your average condiment pacl."
+ desc = "Just your average condiment pack."
icon_state = "ration_condi"
volume = 10
amount_per_transfer_from_this = 10
@@ -126,7 +126,7 @@
/obj/item/reagent_containers/food/snacks/ration/pack
name = "powder pack"
desc = "Mix into a bottle of water and shake."
- icon_state = "ration_condi"
+ icon_state = "ration_pack"
volume = 10
amount_per_transfer_from_this = 10
possible_transfer_amounts = list()
@@ -742,84 +742,105 @@
/obj/item/reagent_containers/food/snacks/ration/condiment/cheese_spread
name = "cheese spread pack"
+ filling_color = "#ffcc00"
list_reagents = list(/datum/reagent/consumable/cheese_spread = 8)
/obj/item/reagent_containers/food/snacks/ration/condiment/hot_cheese_spread
name = "jalapeno cheddar cheese spread pack"
+ filling_color = "#ffaa00"
list_reagents = list(/datum/reagent/consumable/cheese_spread = 5 , /datum/reagent/consumable/capsaicin = 3)
/obj/item/reagent_containers/food/snacks/ration/condiment/garlic_cheese_spread
name = "garlic parmesan cheese spread pack"
+ filling_color = "#ffff00"
list_reagents = list(/datum/reagent/consumable/cheese_spread = 8)
/obj/item/reagent_containers/food/snacks/ration/condiment/bacon_cheddar_cheese_spread
name = "bacon cheddar cheese spread pack"
+ filling_color = "#ff9900"
list_reagents = list(/datum/reagent/consumable/cheese_spread = 8)
/obj/item/reagent_containers/food/snacks/ration/condiment/peanut_butter
name = "peanut butter pack"
+ filling_color = "#664400"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/peanut_butter = 5)
/obj/item/reagent_containers/food/snacks/ration/condiment/chunky_peanut_butter
name = "chunky peanut butter pack"
+ filling_color = "#663300"
list_reagents = list(/datum/reagent/consumable/peanut_butter = 10)
/obj/item/reagent_containers/food/snacks/ration/condiment/maple_syrup
name = "maple syrup pack"
+ filling_color = "#661100"
list_reagents = list(/datum/reagent/consumable/sugar = 10)
/obj/item/reagent_containers/food/snacks/ration/pack/chocolate_protein_beverage
name = "chocolate hazelnut protein drink powder pack"
+ filling_color = "#664400"
list_reagents = list(/datum/reagent/consumable/coco = 5, /datum/reagent/consumable/eggyolk = 5)
/obj/item/reagent_containers/food/snacks/ration/pack/fruit_beverage
name = "fruit punch beverage powder, carb-electrolyte pack"
+ filling_color = "#ff4400"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/applejuice = 2, /datum/reagent/consumable/orangejuice = 2)
/obj/item/reagent_containers/food/snacks/ration/pack/fruit_smoothie_beverage
name = "tropical blend fruit and vegetable smoothie powder pack"
+ filling_color = "#ffaa00"
list_reagents = list(/datum/reagent/consumable/pineapplejuice = 3, /datum/reagent/consumable/orangejuice = 3, /datum/reagent/consumable/eggyolk = 3)
/obj/item/reagent_containers/food/snacks/ration/pack/grape_beverage
name = "grape beverage powder, carb-fortified pack"
+ filling_color = "#9900ff"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/grapejuice = 5)
/obj/item/reagent_containers/food/snacks/ration/pack/grape_beverage_sugar_free
name = "sugar-free grape beverage base powder"
+ filling_color = "#9900ff"
list_reagents = list(/datum/reagent/consumable/grapejuice = 10)
/obj/item/reagent_containers/food/snacks/ration/pack/lemonade_beverage
name = "lemonade drink powder pack"
+ filling_color = "#ffff80"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/lemonjuice = 5)
/obj/item/reagent_containers/food/snacks/ration/pack/lemonade_beverage_suger_free
name = "lemonade sugar-free beverage base pack"
+ filling_color = "#ffff00"
list_reagents = list(/datum/reagent/consumable/lemonjuice = 10)
/obj/item/reagent_containers/food/snacks/ration/pack/orange_beverage
name = "orange beverage powder, carb-fortified pack"
+ filling_color = "#ffbb00"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/orangejuice = 5)
/obj/item/reagent_containers/food/snacks/ration/pack/orange_beverage_sugar_free
name = "orange beverage base, sugar-free pack"
+ filling_color = "#ff9900"
list_reagents = list(/datum/reagent/consumable/orangejuice = 10)
/obj/item/reagent_containers/food/snacks/ration/pack/cherry_beverage
name = "cherry high-energy beverage powder pack"
+ filling_color = "#ff5555"
list_reagents = list(/datum/reagent/consumable/sugar = 5, /datum/reagent/consumable/cherryjelly = 5)
/obj/item/reagent_containers/food/snacks/ration/pack/pineapple_beverage
name = "pinapple fruit beverage base pack"
+ filling_color = "#fff111"
list_reagents = list(/datum/reagent/consumable/pineapplejuice = 10)
/obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_orange
name = "freeze-dried coffee flavored with orange pack"
+ filling_color = "#cc7400"
list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/orangejuice = 3)
/obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_chocolate
name = "freeze-dried coffee flavored with chocolate pack"
+ filling_color = "#803300"
list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/coco = 3)
/obj/item/reagent_containers/food/snacks/ration/pack/freeze_dried_coffee_hazelnut
name = "freeze-dried coffee flavored with hazelnut pack"
+ filling_color = "#553300"
list_reagents = list(/datum/reagent/consumable/coffee = 5, /datum/reagent/consumable/coco = 3)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill.dm b/code/modules/food_and_drinks/kitchen_machinery/grill.dm
index c349c7511752..c150fe94498d 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/grill.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/grill.dm
@@ -9,7 +9,7 @@
layer = BELOW_OBJ_LAYER
use_power = NO_POWER_USE
var/grill_fuel = 0
- var/obj/item/reagent_containers/food/grilled_item
+ var/obj/item/reagent_containers/food/snacks/grilled_item
var/grill_time = 0
var/datum/looping_sound/grill/grill_loop
@@ -27,68 +27,74 @@
else
icon_state = "grill_open"
return ..()
+
+/obj/machinery/grill/examine(mob/user)
+ . = ..()
+ if(grill_fuel)
+ . += span_notice("\The [src] has [grill_fuel] units of fuel left.")
+ else
+ . += span_warning("\The [src] is out of fuel! Add some wood or coal!")
+
/obj/machinery/grill/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/sheet/mineral/coal) || istype(I, /obj/item/stack/sheet/mineral/wood))
var/obj/item/stack/S = I
var/stackamount = S.get_amount()
- to_chat(user, "You put [stackamount] [I]s in [src].")
+ to_chat(user, span_notice("You put [stackamount] [I]s in [src]."))
if(istype(I, /obj/item/stack/sheet/mineral/coal))
- grill_fuel += (500 * stackamount)
- else
grill_fuel += (50 * stackamount)
+ else
+ grill_fuel += (5 * stackamount)
S.use(stackamount)
update_appearance()
return
if(I.resistance_flags & INDESTRUCTIBLE)
- to_chat(user, "You don't feel it would be wise to grill [I]...")
+ to_chat(user, span_warning("You don't feel it would be wise to grill [I]..."))
return ..()
- if(istype(I, /obj/item/reagent_containers))
- if(istype(I, /obj/item/reagent_containers/food) && !istype(I, /obj/item/reagent_containers/food/drinks))
- var/obj/item/reagent_containers/food/food_item = I
- if(HAS_TRAIT(food_item, TRAIT_NODROP) || (food_item.item_flags & (ABSTRACT | DROPDEL)))
- return ..()
- else if(food_item.foodtype & GRILLED)
- to_chat(user, "[food_item] has already been grilled!")
- return
- else if(!grill_fuel)
- to_chat(user, "There is not enough fuel!")
- return
- else if(!grilled_item && user.transferItemToLoc(food_item, src))
- grilled_item = food_item
- grilled_item.foodtype |= GRILLED
- to_chat(user, "You put the [grilled_item] on [src].")
- update_appearance()
- grill_loop.start()
- return
- else
- if(I.reagents.has_reagent(/datum/reagent/consumable/xeno_energy))
- grill_fuel += (20 * (I.reagents.get_reagent_amount(/datum/reagent/consumable/xeno_energy)))
- to_chat(user, "You pour the Monkey Energy in [src].")
- I.reagents.remove_reagent(/datum/reagent/consumable/xeno_energy, I.reagents.get_reagent_amount(/datum/reagent/consumable/xeno_energy))
- update_appearance()
- return
+
+ if(istype(I, /obj/item/reagent_containers/food/snacks))
+ var/obj/item/reagent_containers/food/snacks/food_item = I
+ if(HAS_TRAIT(food_item, TRAIT_NODROP) || (food_item.item_flags & (ABSTRACT | DROPDEL)))
+ return ..()
+ else if(food_item.foodtype & GRILLED)
+ to_chat(user, span_notice("[food_item] has already been grilled!"))
+ return
+ else if(grill_fuel <= 0)
+ to_chat(user, span_warning("There is not enough fuel!"))
+ return
+ else if(grilled_item)
+ to_chat(user,span_warning("\The [src] is already grilling something, take it out first!"))
+ return
+ else if(user.transferItemToLoc(food_item, src))
+ START_PROCESSING(SSmachines, src)
+ grilled_item = food_item
+ to_chat(user, span_notice("You put the [grilled_item] on [src]."))
+ update_appearance()
+ grill_loop.start()
+ return
..()
/obj/machinery/grill/process()
..()
+ if(!grilled_item)
+ return PROCESS_KILL
update_appearance()
- if(!grill_fuel)
+ if(grill_fuel <= 0)
+ grill_fuel = 0
+ visible_message(span_warning("\The [src] is out of fuel!"))
+ if(grilled_item)
+ grilled_item.forceMove(loc)
+ finish_grill()
return
- else
- grill_fuel -= 1
- if(prob(1))
- var/datum/effect_system/smoke_spread/bad/smoke = new
- smoke.set_up(1, loc)
- smoke.start()
- if(grilled_item)
- grill_time += 1
- grill_fuel -= 10
- grilled_item.AddComponent(/datum/component/sizzle)
+ grill_time += 1
+ grill_fuel -= 1
+ if(prob(1))
+ var/datum/effect_system/smoke_spread/bad/smoke = new
+ smoke.set_up(1, loc)
+ smoke.start()
/obj/machinery/grill/Exited(atom/movable/AM)
if(AM == grilled_item)
finish_grill()
- grilled_item = null
. = ..()
/obj/machinery/grill/Destroy()
@@ -118,13 +124,15 @@
/obj/machinery/grill/attack_hand(mob/user)
if(grilled_item)
- to_chat(user, "You take out [grilled_item] from [src].")
+ to_chat(user, span_notice("You take out [grilled_item] from [src]."))
grilled_item.forceMove(drop_location())
update_appearance()
return
return ..()
/obj/machinery/grill/proc/finish_grill()
+ if(grill_time >= 10 && grilled_item.cooked_type)
+ grilled_item = grilled_item.microwave_act()
switch(grill_time) //no 0-9 to prevent spam
if(10 to 15)
grilled_item.name = "lightly-grilled [grilled_item.name]"
@@ -141,8 +149,12 @@
grilled_item.name = "Powerfully Grilled [grilled_item.name]"
grilled_item.desc = "A [grilled_item.name]. Reminds you of your wife, wait, no, it's prettier!"
grilled_item.foodtype |= FRIED
+ grilled_item.AddComponent(/datum/component/sizzle, (grill_time * 7.5))
+ grilled_item.foodtype |= GRILLED
grill_time = 0
grill_loop.stop()
+ grilled_item = null
+ update_appearance()
/obj/machinery/grill/unwrenched
anchored = FALSE
diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
index 2762892110f8..0df04a08658b 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
@@ -362,7 +362,7 @@
name = "flameless ration heater"
desc = "A magnisium based ration heater. It can be used to heat up entrees and other food items. reaches the same temperature as a microwave with half the volume."
icon = 'icons/obj/food/ration.dmi'
- icon_state = "ration_package"
+ icon_state = "ration_heater"
grind_results = list(/datum/reagent/iron = 10, /datum/reagent/water = 10, /datum/reagent/consumable/sodiumchloride = 5)
heat = 3800
var/obj/item/tocook = null
diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm
index 772893e3ff16..b5c4c2c42b85 100644
--- a/code/modules/food_and_drinks/pizzabox.dm
+++ b/code/modules/food_and_drinks/pizzabox.dm
@@ -96,7 +96,7 @@
. += tag_overlay
/obj/item/pizzabox/worn_overlays(isinhands, icon_file)
- . = list()
+ . = ..()
var/current_offset = 2
if(isinhands)
for(var/V in boxes) //add EXTRA BOX per box
diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm
index 2e40c8ed04c6..5baed7a45309 100644
--- a/code/modules/holiday/easter.dm
+++ b/code/modules/holiday/easter.dm
@@ -143,7 +143,7 @@
/obj/item/toy/prize/durand,
/obj/item/toy/prize/marauder,
/obj/item/toy/prize/seraph,
- /obj/item/toy/prize/mauler,
+ /obj/item/toy/prize/touro,
/obj/item/toy/prize/odysseus,
/obj/item/toy/prize/phazon,
/obj/item/toy/prize/reticence,
diff --git a/code/modules/lighting/emissive_blocker.dm b/code/modules/lighting/emissive_blocker.dm
index 308ba8f2448c..84c71203dd2a 100644
--- a/code/modules/lighting/emissive_blocker.dm
+++ b/code/modules/lighting/emissive_blocker.dm
@@ -32,9 +32,6 @@
/atom/movable/emissive_blocker/singularity_pull()
return
-/atom/movable/emissive_blocker/blob_act()
- return
-
//Prevents people from moving these after creation, because they shouldn't be.
/atom/movable/emissive_blocker/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE)
if(harderforce)
diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm
index 7b6bc79aec45..d2d95678ef04 100644
--- a/code/modules/lighting/lighting_object.dm
+++ b/code/modules/lighting/lighting_object.dm
@@ -130,9 +130,6 @@
/atom/movable/lighting_object/singularity_pull()
return
-/atom/movable/lighting_object/blob_act()
- return
-
/atom/movable/lighting_object/wash(clean_types)
return
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 7ba30d327444..b1a31e21a5ca 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -5,7 +5,7 @@
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
- area_flags = BLOBS_ALLOWED | UNIQUE_AREA
+ area_flags = UNIQUE_AREA
flags_1 = CAN_BE_DIRTY_1
//Survival Capsule
diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm
index 2af4c1b5ce4f..e44dd4b14dce 100644
--- a/code/modules/mining/equipment/wormhole_jaunter.dm
+++ b/code/modules/mining/equipment/wormhole_jaunter.dm
@@ -41,10 +41,7 @@
var/turf/targetturf = find_safe_turf()
if(!targetturf)
- if(GLOB.blobstart.len > 0)
- targetturf = get_turf(pick(GLOB.blobstart))
- else
- CRASH("Unable to find a blobstart landmark")
+ CRASH("Unable to find a blobstart landmark")
var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), 100, null, FALSE, targetturf)
log_game("[user] Has jaunted to [loc_name(targetturf)].")
message_admins("[user] Has jaunted to [ADMIN_VERBOSEJMP(targetturf)].")
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index e48f4d5af5aa..a2b48d9319b9 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -1103,7 +1103,7 @@
C.update_inv_wear_suit()
/obj/item/clothing/suit/armor/ascetic/worn_overlays(isinhands)
- . = list()
+ . = ..()
if(!isinhands)
. += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER - 0.01)
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index 7d5bd8b42860..170cb25b49cf 100644
--- a/code/modules/mob/camera/camera.dm
+++ b/code/modules/mob/camera/camera.dm
@@ -1,4 +1,4 @@
-// Camera mob, used by AI camera and blob.
+// Camera mob, used by AI camera.
/mob/camera
name = "camera mob"
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index cc5a69dd095f..71b66ac54fb4 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -52,7 +52,9 @@
var/random_species_type = GLOB.species_list[pick(GLOB.roundstart_races)]
pref_species = new random_species_type
if(randomise[RANDOM_NAME])
- real_name = pref_species.random_name(gender,1)
+ real_name = pref_species.random_name(gender, TRUE)
+ if(randomise[RANDOM_AGE])
+ age = rand(pref_species.species_age_min, pref_species.species_age_max)
/datum/preferences/proc/update_preview_icon(show_gear = TRUE, show_loadout = FALSE)
// Set up the dummy for its photoshoot
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 4a768fc1c501..c15c4a1af835 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -495,6 +495,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(isobserver(usr)) //Make sure they're an observer!
+
var/list/dest = list() //List of possible destinations (mobs)
var/target = null //Chosen target.
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index da3bc7c2dfb5..86d54577538c 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -57,7 +57,7 @@
var/list/misc = list()
var/list/npcs = list()
- var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE, only_realname = TRUE)
+ var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE)
for (var/name in pois)
var/list/serialized = list()
serialized["name"] = name
@@ -67,8 +67,6 @@
serialized["ref"] = REF(poi)
var/mob/M = poi
-
- serialized["fake_name"] = M.name
if (istype(M))
if (isobserver(M))
ghosts += list(serialized)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 1910347e4fdd..8e2962e70d2f 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -51,19 +51,31 @@
if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
if(prob(10))
to_chat(src, "You feel terribly bloated.")
+
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
- if(prob(5))
+
+ if(prob(1))
to_chat(src, "You feel [word].")
- adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
+ if(oxyloss < 20)
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
+
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
- adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
- if(prob(5))
- blur_eyes(6)
+ if(eye_blurry < 50)
+ adjust_blurriness(5)
+ if(oxyloss < 40)
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
+ else
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
+
+ if(prob(15))
+ Unconscious(rand(2 SECONDS,6 SECONDS))
to_chat(src, "You feel very [word].")
+
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
- adjustOxyLoss(5)
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
+ adjustToxLoss(2)
if(prob(15))
- Unconscious(rand(20,60))
+ Unconscious(rand(2 SECONDS,6 SECONDS))
to_chat(src, "You feel extremely [word].")
if(-INFINITY to BLOOD_VOLUME_SURVIVE)
if(!HAS_TRAIT(src, TRAIT_NODEATH))
@@ -81,25 +93,76 @@
BP.adjust_bleeding(0.1, BLOOD_LOSS_DAMAGE_MAXIMUM)
limb_bleed += BP.bleeding
+ var/message_cooldown = 5 SECONDS
+ var/bleeeding_wording
+// var/bleed_change_wording
+ switch(limb_bleed)
+ if(0 to 0.5)
+ bleeeding_wording = "You hear droplets of blood drip down."
+ message_cooldown *= 2.5
+ if(0.5 to 1)
+ bleeeding_wording = "You feel your blood flow quietly to the floor."
+ message_cooldown *= 2
+ if(1 to 2)
+ bleeeding_wording = "The flow of blood leaving your body onto the ground is worrying..."
+ message_cooldown *= 1.7
+ if(2 to 4)
+ bleeeding_wording = "You're losing blood very fast, which is freaking you out!"
+ message_cooldown *= 1.5
+ if(4 to INFINITY)
+ bleeeding_wording = "Your heartbeat beats unstably fast as you lose a massive amount of blood!!"
+
if(limb_bleed && !bleedsuppress && !HAS_TRAIT(src, TRAIT_FAKEDEATH))
bleed(limb_bleed)
+ if(!blood_particle)
+ blood_particle = new(src, /particles/droplets/blood, PARTICLE_ATTACH_MOB)
+ blood_particle.particles.color = dna.blood_type.color //mouthful
+ blood_particle.particles.spawning = (limb_bleed/2)
+ blood_particle.particles.count = (round(clamp((limb_bleed * 2), 1, INFINITY)))
+
+ if(COOLDOWN_FINISHED(src, bloodloss_message) && bleeeding_wording)
+ to_chat(src, span_warning("[bleeeding_wording]"))
+ COOLDOWN_START(src, bloodloss_message, message_cooldown)
+ else
+ if(blood_particle)
+ QDEL_NULL(blood_particle)
+
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/proc/bleed(amt)
if(blood_volume)
blood_volume = max(blood_volume - amt, 0)
if (prob(sqrt(amt)*BLOOD_DRIP_RATE_MOD))
if(isturf(src.loc) && !isgroundlessturf(src.loc)) //Blood loss still happens in locker, floor stays clean
- if(amt >= 10)
- add_splatter_floor(src.loc)
+ if(amt >= 2)
+ add_splatter_floor(src.loc, amt = amt)
else
- add_splatter_floor(src.loc, 1)
+ add_splatter_floor(src.loc, TRUE, amt)
/mob/living/carbon/human/bleed(amt)
amt *= physiology.bleed_mod
if(!(NOBLOOD in dna.species.species_traits))
..()
+/**
+ * This proc is a helper for spraying blood for things like slashing/piercing wounds and dismemberment.
+ *
+ * The strength of the splatter in the second argument determines how much it can dirty and how far it can go
+ *
+ * Arguments:
+ * * splatter_direction: Which direction the blood is flying
+ * * splatter_strength: How many tiles it can go, and how many items it can pass over and dirty
+ */
+/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3)
+ if(!isturf(loc))
+ return
+ var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc)
+
+// our_splatter.transfer_mob_blood_dna(return_blood_DNA(src))
+ our_splatter.blood_dna_info = get_blood_dna_list()
+ our_splatter.transfer_mob_blood_dna(src)
+ var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
+ INVOKE_ASYNC(our_splatter, TYPE_PROC_REF(/obj/effect/decal/cleanable/blood/hitsplatter, fly_towards), targ, splatter_strength)
/mob/living/proc/restore_blood()
@@ -229,13 +292,14 @@
return blood_type.color
//to add a splatter of blood or other mob liquid.
-/mob/living/proc/add_splatter_floor(turf/T, small_drip)
+/mob/living/proc/add_splatter_floor(turf/T, small_drip, amt)
if(get_blood_id() != /datum/reagent/blood)
return
if(!T)
T = get_turf(src)
var/list/temp_blood_DNA
+
if(small_drip)
// Only a certain number of drips (or one large splatter) can be on a given turf.
var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T
@@ -248,7 +312,7 @@
else
temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter
qdel(drop)//the drip is replaced by a bigger splatter
- else
+ else if (amt < 2)
drop = new(T, get_static_viruses())
drop.transfer_mob_blood_dna(src)
return
@@ -261,7 +325,11 @@
B = candidate
break
if(!B)
- B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
+ if(amt > 4)
+ B = new /obj/effect/decal/cleanable/blood(T, get_static_viruses())
+ else
+ B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
+
if(QDELETED(B)) //Give it up
return
B.bloodiness = min((B.bloodiness + BLOOD_AMOUNT_PER_DECAL), BLOOD_POOL_MAX)
@@ -269,11 +337,11 @@
if(temp_blood_DNA)
B.add_blood_DNA(temp_blood_DNA)
-/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip)
+/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, amt)
if(!(NOBLOOD in dna.species.species_traits))
..()
-/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip)
+/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip, amt)
if(!T)
T = get_turf(src)
var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents
@@ -281,7 +349,7 @@
B = new(T)
B.add_blood_DNA(list("UNKNOWN DNA" = "X*"))
-/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip)
+/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip, amt)
if(!T)
T = get_turf(src)
var/obj/effect/decal/cleanable/oil/B = locate() in T.contents
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index 80daa8de3e3c..fa1267bdc232 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -41,9 +41,6 @@
/mob/living/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up.
return
-/mob/living/brain/blob_act(obj/structure/blob/B)
- return
-
/mob/living/brain/get_eye_protection()//no eyes
return 2
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 43cefa251e34..5330ee1841f6 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -352,13 +352,6 @@
if(is_type_in_typecache(active_item, GLOB.shove_disarming_types))
visible_message("[name] regains their grip on \the [active_item]!", "You regain your grip on \the [active_item]", null, COMBAT_MESSAGE_RANGE)
-/mob/living/carbon/blob_act(obj/structure/blob/B)
- if (stat == DEAD)
- return
- else
- show_message("The blob attacks!")
- adjustBruteLoss(10)
-
/mob/living/carbon/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_CONTENTS)
@@ -653,3 +646,20 @@
ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
else if(getOxyLoss() <= 50)
REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
+
+/mob/living/carbon/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
+ var/mob/living/carbon/human/current_user = src //is this a good idea? who can say?
+ var/armor = run_armor_check(def_zone, P.flag, P.armour_penetration, silent = TRUE)
+ var/on_hit_state = P.on_hit(src, armor, piercing_hit)
+ if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK && !QDELETED(src)) //QDELETED literally just for the instagib rifle. Yeah.
+ apply_damage(P.damage, P.damage_type, def_zone, armor, sharpness = TRUE)
+ if(P.damage-armor >= 15 && P.damage_type == BRUTE && (!armor || prob(40) || P.damage-armor >= 25))
+ spray_blood(get_dir(P.starting,src), (P.damage-armor)/5)
+ var/obj/item/bodypart/targeted_bodypart = null
+ bleed((P.damage-armor)/2)
+
+ recoil_camera(src, clamp((P.damage-armor)/4,0.5,10), clamp((P.damage-armor)/4,0.5,10), P.damage/8, P.Angle)
+ apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize)
+ if(P.dismemberment)
+ check_projectile_dismemberment(P, def_zone)
+ return on_hit_state ? BULLET_ACT_HIT : BULLET_ACT_BLOCK
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 8c1a36c2061b..1804a1497187 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -31,6 +31,9 @@
if(prob(50))
step(W, pick(GLOB.alldirs))
var/atom/Tsec = drop_location()
+ var/amount_of_streams_to_spawn = rand(2,4)
+ for(var/i in 1 to amount_of_streams_to_spawn)
+ spray_blood(pick(GLOB.alldirs), rand(1,6))
for(var/mob/M in src)
M.forceMove(Tsec)
visible_message("[M] bursts out of [src]!")
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 7bbe9fb1de72..cf3f5ec725ec 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -25,7 +25,7 @@
//if we have no guestbook, we just KNOW okay?
var/known_name = user.mind?.guestbook ? user.mind.guestbook.get_known_name(user, src, face_name) : face_name
if(known_name)
- . += "You know them as [known_name]."
+ . += "You know [t_him] as [known_name]."
else
. += "You don't recognize [t_him]. You can Ctrl-Shift click [t_him] to memorize their face."
else
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 64dfdfde91ce..7a7bc349b26c 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -473,16 +473,6 @@
if(!max_limb_loss)
break
-
-/mob/living/carbon/human/blob_act(obj/structure/blob/B)
- if(stat == DEAD)
- return
- show_message("The blob attacks you!")
- var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
- apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee"))
-
-
///Calculates the siemens coeff based on clothing and species, can also restart hearts.
/mob/living/carbon/human/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
//If it doesnt have physiology its prob still initializing.
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 567523c11d79..df006ead1f39 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -77,3 +77,7 @@
/// How many "units of blood" we have on our hands
var/blood_in_hands = 0
+ ///blood particle effect
+ var/obj/effect/abstract/particle_holder/blood_particle
+
+ COOLDOWN_DECLARE(bloodloss_message)
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 9e2cfe4f1556..cd588a89b9ce 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -192,23 +192,65 @@
/mob/living/carbon/human/proc/get_age()
var/obscured = check_obscured_slots()
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
- if((obscured & ITEM_SLOT_ICLOTHING) && skipface || isipc(src))
+ if((obscured & ITEM_SLOT_ICLOTHING) && skipface || isipc(src) || isskeleton(src)) // sorry ladies no middle aged robots
return FALSE
- switch(age)
- if(70 to INFINITY)
- return "Geriatric"
- if(60 to 70)
- return "Elderly"
- if(50 to 60)
- return "Old"
- if(40 to 50)
- return "Middle-Aged"
- if(24 to 40)
- return FALSE //not necessary because this is basically the most common age range
- if(18 to 24)
- return "Young"
- else
- return "Puzzling"
+ if(islizard(src))
+ switch(age)
+ if(175 to INFINITY)
+ return "Ancient"
+ if(130 to 175)
+ return "Elderly"
+ if(100 to 130)
+ return "Old"
+ if(65 to 100)
+ return "Middle-Aged"
+ if(40 to 65)
+ return FALSE
+ if(18 to 40)
+ return "Young"
+ else if(isvox(src))
+ switch(age)
+ if(280 to INFINITY)
+ return "Ancient"
+ if(200 to 280)
+ return "Elderly"
+ if(160 to 200)
+ return "Old"
+ if(120 to 160)
+ return "Middle-Aged"
+ if(60 to 120)
+ return FALSE
+ if(18 to 60)
+ return "Young"
+ else if(iselzuose(src))
+ switch(age)
+ if(300 to INFINITY)
+ return "Ancient"
+ if(260 to 300)
+ return "Elderly"
+ if(160 to 260)
+ return "Old"
+ if(100 to 160)
+ return "Middle-Aged"
+ if(40 to 100)
+ return FALSE // most common age range
+ if(18 to 40)
+ return "Young"
+ else
+ switch(age)
+ if(70 to INFINITY)
+ return "Ancient"
+ if(60 to 70)
+ return "Elderly"
+ if(50 to 60)
+ return "Old"
+ if(40 to 50)
+ return "Middle-Aged"
+ if(24 to 40)
+ return FALSE // most common age range
+ if(18 to 24)
+ return "Young"
+ return "Puzzling"
/mob/living/carbon/human/proc/get_generic_name(prefixed = FALSE, lowercase = FALSE)
var/final_string = ""
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 77e2045e357c..aab8b681bf50 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -26,25 +26,8 @@
return 0
return ..()
-/mob/living/carbon/human/experience_pressure_difference(pressure_difference)
- if(pressure_difference > 100)
- playsound_local(null, 'sound/effects/space_wind_big.ogg', clamp(pressure_difference / 50, 10, 100), 1)
- else
- playsound_local(null, 'sound/effects/space_wind.ogg', clamp(pressure_difference, 10, 100), 1)
- if(shoes && istype(shoes, /obj/item/clothing))
- var/obj/item/clothing/S = shoes
- if((S.clothing_flags & NOSLIP))
- return 0
- return ..()
-
-/mob/living/carbon/human/mob_has_gravity()
- . = ..()
- if(!.)
- if(mob_negates_gravity())
- . = 1
-
/mob/living/carbon/human/mob_negates_gravity()
- return ((shoes && shoes.negates_gravity()) || (dna.species.negates_gravity(src)))
+ return dna.species.negates_gravity(src) || ..()
/mob/living/carbon/human/Move(NewLoc, direct)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 756af00f1839..25e045064a44 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -334,7 +334,8 @@
if(equip_to_slot_if_possible(thing, slot_type))
update_inv_hands()
return
- if(!SEND_SIGNAL(equipped_item, COMSIG_CONTAINS_STORAGE)) // not a storage item
+ var/datum/component/storage/storage = equipped_item.GetComponent(/datum/component/storage)
+ if(!storage)
if(!thing)
equipped_item.attack_hand(src)
else
@@ -344,10 +345,11 @@
if(!SEND_SIGNAL(equipped_item, COMSIG_TRY_STORAGE_INSERT, thing, src))
to_chat(src, "You can't fit [thing] into your [equipped_item.name]!")
return
- if(!equipped_item.contents.len) // nothing to take out
+ var/atom/real_location = storage.real_location()
+ if(!real_location.contents.len) // nothing to take out
to_chat(src, "There's nothing in your [equipped_item.name] to take out!")
return
- var/obj/item/stored = equipped_item.contents[equipped_item.contents.len]
+ var/obj/item/stored = real_location.contents[real_location.contents.len]
if(!stored || stored.on_found(src))
return
stored.attack_hand(src) // take out thing from item in storage slot
diff --git a/code/modules/mob/living/carbon/human/species_types/vox.dm b/code/modules/mob/living/carbon/human/species_types/vox.dm
index b9cc8306762e..b61b334e6ec2 100644
--- a/code/modules/mob/living/carbon/human/species_types/vox.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vox.dm
@@ -3,7 +3,6 @@
name = "\improper Vox"
id = SPECIES_VOX
default_color = "6060FF"
- species_age_min = 17
species_age_max = 280
species_traits = list(EYECOLOR, NO_UNDERWEAR)
mutant_bodyparts = list("vox_head_quills", "vox_neck_quills")
@@ -12,7 +11,7 @@
disliked_food = GRAIN
liked_food = MEAT
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
- loreblurb = "Vox test"
+ loreblurb = "Vox are a big bird-like species with quills, much larger and much more long-lasting than other species. Sadly, not much else is known."
attack_verb = "slash"
attack_sound = 'sound/weapons/slash.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 462abc5cd38c..540dddb9a489 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -804,7 +804,7 @@ There are several things that need to be remembered:
handled_by_bodytype = TRUE
if(!icon_exists(icon_file, RESOLVE_ICON_STATE(I)))
- icon_file = DEFAULT_BACK_PATH
+ icon_file = I.mob_overlay_icon ? I.mob_overlay_icon : DEFAULT_BACK_PATH
handled_by_bodytype = TRUE
var/use_autogen = handled_by_bodytype ? dna.species : null
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index d5b97a942da2..9525ebd6ec9b 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -14,6 +14,30 @@
return legcuffed
return null
+/mob/living/carbon/get_slot_by_item(obj/item/looking_for)
+ if(looking_for == back)
+ return ITEM_SLOT_BACK
+
+ if(back && (looking_for in back))
+ return ITEM_SLOT_BACKPACK
+
+ if(looking_for == wear_mask)
+ return ITEM_SLOT_MASK
+
+ if(looking_for == wear_neck)
+ return ITEM_SLOT_NECK
+
+ if(looking_for == head)
+ return ITEM_SLOT_HEAD
+
+ if(looking_for == handcuffed)
+ return ITEM_SLOT_HANDCUFFED
+
+ if(looking_for == legcuffed)
+ return ITEM_SLOT_LEGCUFFED
+
+ return ..()
+
/mob/living/carbon/proc/equip_in_one_of_slots(obj/item/I, list/slots, qdel_on_fail = 1)
for(var/slot in slots)
if(equip_to_slot_if_possible(I, slots[slot], qdel_on_fail = 0, disable_warning = TRUE))
diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm
index c80c9a821fd0..34bd7dd8632c 100644
--- a/code/modules/mob/living/carbon/update_icons.dm
+++ b/code/modules/mob/living/carbon/update_icons.dm
@@ -211,6 +211,9 @@
//eg: ammo counters, primed grenade flashing, etc.
//"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file
/obj/item/proc/worn_overlays(isinhands = FALSE, icon_file)
+ SHOULD_CALL_PARENT(TRUE)
+ RETURN_TYPE(/list)
+
. = list()
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 6615edb051f0..0687d90da75b 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -1,4 +1,4 @@
-/mob/living/gib(no_brain, no_organs, no_bodyparts)
+/mob/living/gib(no_brain, no_organs, no_bodyparts, safe_gib = FALSE)
var/prev_lying = lying_angle
if(stat != DEAD)
death(TRUE)
@@ -12,7 +12,8 @@
spread_bodyparts(no_brain, no_organs)
spawn_gibs(no_bodyparts)
- qdel(src)
+ if(!safe_gib)
+ qdel(src)
/mob/living/proc/gib_animation()
return
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index a25025294497..c054b7d50bf4 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -122,7 +122,7 @@
return
/mob/living/proc/handle_gravity()
- var/gravity = mob_has_gravity()
+ var/gravity = has_gravity()
update_gravity(gravity)
if(gravity > STANDARD_GRAVITY)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 269c74a837bd..1258df8b84c5 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -97,6 +97,7 @@
if(m_intent == MOVE_INTENT_WALK)
return TRUE
+ SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M)
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
@@ -831,7 +832,7 @@
return pick("trails_1", "trails_2")
/mob/living/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)
- if(buckled)
+ if(buckled || mob_negates_gravity())
return
if(client && client.move_delay >= world.time + world.tick_lag*2)
pressure_resistance_prob_delta -= 30
diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm
index d6cb89bab492..5134b8b1c79a 100644
--- a/code/modules/mob/living/silicon/ai/ai_defense.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defense.dm
@@ -20,13 +20,6 @@
/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user)
return //immune to slimes
-/mob/living/silicon/ai/blob_act(obj/structure/blob/B)
- if (stat != DEAD)
- adjustBruteLoss(60)
- updatehealth()
- return 1
- return 0
-
/mob/living/silicon/ai/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm
index 80c643e0ceef..9813ac88d43e 100644
--- a/code/modules/mob/living/silicon/damage_procs.dm
+++ b/code/modules/mob/living/silicon/damage_procs.dm
@@ -1,5 +1,5 @@
-/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, break_modifier = 1, sharpness = FALSE)
+/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE)
var/hit_percent = (100-blocked)/100
if((!damage || (!forced && hit_percent <= 0)))
return 0
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 4a3e284addd6..ecdf3355e5aa 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -1,7 +1,3 @@
-
-/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
- return FALSE
-
/mob/living/silicon/pai/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 376b4ddcca4b..e2eec65479da 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -391,14 +391,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
laws.associate(src)
update_icons()
-
-/mob/living/silicon/robot/blob_act(obj/structure/blob/B)
- if(stat != DEAD)
- adjustBruteLoss(30)
- else
- gib()
- return TRUE
-
/mob/living/silicon/robot/ex_act(severity, target)
switch(severity)
if(1)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index aa6a0c368537..f6644bfbe6a7 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -142,10 +142,6 @@
bloss = bloss / 1.5
adjustBruteLoss(bloss)
-/mob/living/simple_animal/blob_act(obj/structure/blob/B)
- adjustBruteLoss(20)
- return
-
/mob/living/simple_animal/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect)
if(!no_effect && !visual_effect_icon && melee_damage_upper)
if(melee_damage_upper < 10)
diff --git a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
index 7caaf4f1e436..e72211f66012 100644
--- a/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mecha_pilot.dm
@@ -25,7 +25,7 @@ Featuring:
search_objects = 0
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
- var/spawn_mecha_type = /obj/mecha/combat/marauder/mauler/loaded
+ var/spawn_mecha_type = /obj/mecha/combat/marauder/touro/loaded
var/obj/mecha/mecha //Ref to pilot's mecha instance
var/required_mecha_charge = 7500 //If the pilot doesn't have a mecha, what charge does a potential Grand Theft Mecha need? (Defaults to half a battery)
var/mecha_charge_evacuate = 50 //Amount of charge at which the pilot tries to abandon the mecha
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 06c7a9af52d8..b1788a7aa50d 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -313,6 +313,14 @@
/mob/proc/get_item_by_slot(slot_id)
return null
+/// Gets what slot the item on the mob is held in.
+/// Returns null if the item isn't in any slots on our mob.
+/// Does not check if the passed item is null, which may result in unexpected outcoms.
+/mob/proc/get_slot_by_item(obj/item/looking_for)
+ if(looking_for in held_items)
+ return ITEM_SLOT_HANDS
+
+ return null
///Is the mob incapacitated
/mob/proc/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 42d217cf96e4..bd1227d94d7d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -28,6 +28,45 @@
zone = BODY_ZONE_CHEST
return zone
+/// Returns a generic path of the object based on the slot
+/proc/get_path_by_slot(slot_id)
+ switch(slot_id)
+ if(ITEM_SLOT_BACK)
+ return /obj/item/storage/backpack
+ if(ITEM_SLOT_MASK)
+ return /obj/item/clothing/mask
+ if(ITEM_SLOT_NECK)
+ return /obj/item/clothing/neck
+ if(ITEM_SLOT_HANDCUFFED)
+ return /obj/item/restraints/handcuffs
+ if(ITEM_SLOT_LEGCUFFED)
+ return /obj/item/restraints/legcuffs
+ if(ITEM_SLOT_BELT)
+ return /obj/item/storage/belt
+ if(ITEM_SLOT_ID)
+ return /obj/item/card/id
+ if(ITEM_SLOT_EARS)
+ return /obj/item/clothing/ears
+ if(ITEM_SLOT_EYES)
+ return /obj/item/clothing/glasses
+ if(ITEM_SLOT_GLOVES)
+ return /obj/item/clothing/gloves
+ if(ITEM_SLOT_HEAD)
+ return /obj/item/clothing/head
+ if(ITEM_SLOT_FEET)
+ return /obj/item/clothing/shoes
+ if(ITEM_SLOT_OCLOTHING)
+ return /obj/item/clothing/suit
+ if(ITEM_SLOT_ICLOTHING)
+ return /obj/item/clothing/under
+ if(ITEM_SLOT_LPOCKET)
+ return /obj/item
+ if(ITEM_SLOT_RPOCKET)
+ return /obj/item
+ if(ITEM_SLOT_SUITSTORE)
+ return /obj/item
+ return null
+
/**
* Return the zone or randomly, another valid zone
*
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 59b64f63d139..acc026c16dda 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -507,13 +507,6 @@
. = new_slime
qdel(src)
-/mob/proc/become_overmind(starting_points = 60)
- var/mob/camera/blob/B = new /mob/camera/blob(get_turf(src), starting_points)
- B.key = key
- . = B
- qdel(src)
-
-
/mob/living/carbon/human/proc/corgize()
if (notransform)
return
diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm
new file mode 100644
index 000000000000..1df1f7b8894b
--- /dev/null
+++ b/code/modules/mod/mod_actions.dm
@@ -0,0 +1,193 @@
+/datum/action/item_action/mod
+ background_icon_state = "bg_tech_blue"
+ icon_icon = 'icons/mob/actions/actions_mod.dmi'
+ check_flags = AB_CHECK_CONSCIOUS
+ /// Whether this action is intended for the AI. Stuff breaks a lot if this is done differently.
+ var/ai_action = FALSE
+
+/datum/action/item_action/mod/New(Target)
+ ..()
+ if(!istype(Target, /obj/item/mod/control))
+ qdel(src)
+ return
+ if(ai_action)
+ background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND
+
+/datum/action/item_action/mod/Grant(mob/user)
+ var/obj/item/mod/control/mod = target
+ if(ai_action && user != mod.ai)
+ return
+ else if(!ai_action && user == mod.ai)
+ return
+ return ..()
+
+/datum/action/item_action/mod/Remove(mob/user)
+ var/obj/item/mod/control/mod = target
+ if(ai_action && user != mod.ai)
+ return
+ else if(!ai_action && user == mod.ai)
+ return
+ return ..()
+
+/datum/action/item_action/mod/Trigger(trigger_flags)
+ if(!IsAvailable())
+ return FALSE
+ var/obj/item/mod/control/mod = target
+ if(mod.malfunctioning && prob(75))
+ mod.balloon_alert(usr, "button malfunctions!")
+ return FALSE
+ return TRUE
+
+/datum/action/item_action/mod/deploy
+ name = "Deploy MODsuit"
+ desc = "LMB: Deploy/Undeploy part. Alt Click: Deploy/Undeploy full suit."
+ button_icon_state = "deploy"
+
+/datum/action/item_action/mod/deploy/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ if(trigger_flags & TRIGGER_SECONDARY_ACTION)
+ mod.quick_deploy(usr)
+ else
+ mod.choose_deploy(usr)
+
+/datum/action/item_action/mod/deploy/ai
+ ai_action = TRUE
+
+/datum/action/item_action/mod/activate
+ name = "Activate MODsuit"
+ desc = "LMB: Activate/Deactivate suit with prompt. Alt Click: Activate/Deactivate suit skipping prompt."
+ button_icon_state = "activate"
+ /// First time clicking this will set it to TRUE, second time will activate it.
+ var/ready = FALSE
+
+/datum/action/item_action/mod/activate/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ if(!(trigger_flags & TRIGGER_SECONDARY_ACTION) && !ready)
+ ready = TRUE
+ button_icon_state = "activate-ready"
+ if(!ai_action)
+ background_icon_state = "bg_tech"
+ UpdateButtonIcon()
+ addtimer(CALLBACK(src, PROC_REF(reset_ready)), 3 SECONDS)
+ return
+ var/obj/item/mod/control/mod = target
+ reset_ready()
+ mod.toggle_activate(usr)
+
+/// Resets the state requiring to be doubleclicked again.
+/datum/action/item_action/mod/activate/proc/reset_ready()
+ ready = FALSE
+ button_icon_state = initial(button_icon_state)
+ if(!ai_action)
+ background_icon_state = initial(background_icon_state)
+ UpdateButtonIcon()
+
+/datum/action/item_action/mod/activate/ai
+ ai_action = TRUE
+
+/datum/action/item_action/mod/module
+ name = "Toggle Module"
+ desc = "Toggle a MODsuit module."
+ button_icon_state = "module"
+
+/datum/action/item_action/mod/module/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ mod.quick_module(usr)
+
+/datum/action/item_action/mod/module/ai
+ ai_action = TRUE
+
+/datum/action/item_action/mod/panel
+ name = "MODsuit Panel"
+ desc = "Open the MODsuit's panel."
+ button_icon_state = "panel"
+
+/datum/action/item_action/mod/panel/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ mod.ui_interact(usr)
+
+/datum/action/item_action/mod/panel/ai
+ ai_action = TRUE
+
+/datum/action/item_action/mod/pinned_module
+ desc = "Activate the module."
+ /// Overrides the icon applications.
+ var/override = FALSE
+ /// Module we are linked to.
+ var/obj/item/mod/module/module
+ /// A ref to the mob we are pinned to.
+ var/pinner_ref
+
+/datum/action/item_action/mod/pinned_module/New(Target, obj/item/mod/module/linked_module, mob/user)
+ if(isAI(user))
+ ai_action = TRUE
+ ..()
+ module = linked_module
+ name = "Activate [capitalize(linked_module.name)]"
+ desc = "Quickly activate [linked_module]."
+ icon_icon = linked_module.icon
+ button_icon_state = linked_module.icon_state
+ RegisterSignal(linked_module, COMSIG_MODULE_ACTIVATED, PROC_REF(on_module_activate))
+ RegisterSignal(linked_module, COMSIG_MODULE_DEACTIVATED, PROC_REF(on_module_deactivate))
+ RegisterSignal(linked_module, COMSIG_MODULE_USED, PROC_REF(on_module_use))
+
+/datum/action/item_action/mod/pinned_module/Destroy()
+ module.pinned_to -= pinner_ref
+ module = null
+ return ..()
+
+/datum/action/item_action/mod/pinned_module/Grant(mob/user)
+ var/user_ref = REF(user)
+ if(!pinner_ref)
+ pinner_ref = user_ref
+ module.pinned_to[pinner_ref] = src
+ else if(pinner_ref != user_ref)
+ return
+ return ..()
+
+/datum/action/item_action/mod/pinned_module/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ module.on_select()
+
+/datum/action/item_action/mod/pinned_module/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force)
+ . = ..(current_button, force = TRUE)
+ if(override)
+ return
+ var/obj/item/mod/control/mod = target
+ if(module == mod.selected_module)
+ current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected", layer = FLOAT_LAYER-0.1))
+ else if(module.active)
+ current_button.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_active", layer = FLOAT_LAYER-0.1))
+ if(!COOLDOWN_FINISHED(module, cooldown_timer))
+ var/image/cooldown_image = image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown")
+ current_button.add_overlay(cooldown_image)
+ addtimer(CALLBACK(current_button, TYPE_PROC_REF(/image, cut_overlay), cooldown_image), COOLDOWN_TIMELEFT(module, cooldown_timer))
+
+
+/datum/action/item_action/mod/pinned_module/proc/on_module_activate(datum/source)
+ SIGNAL_HANDLER
+
+ UpdateButtonIcon()
+
+/datum/action/item_action/mod/pinned_module/proc/on_module_deactivate(datum/source)
+ SIGNAL_HANDLER
+
+ UpdateButtonIcon()
+
+/datum/action/item_action/mod/pinned_module/proc/on_module_use(datum/source)
+ SIGNAL_HANDLER
+
+ UpdateButtonIcon()
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
new file mode 100644
index 000000000000..cb61728f2cbb
--- /dev/null
+++ b/code/modules/mod/mod_activation.dm
@@ -0,0 +1,244 @@
+#define MOD_ACTIVATION_STEP_FLAGS IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_INCAPACITATED
+
+/// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted.
+/obj/item/mod/control/proc/choose_deploy(mob/user)
+ if(!length(mod_parts))
+ return
+ var/list/display_names = list()
+ var/list/items = list()
+ for(var/obj/item/part as anything in mod_parts)
+ display_names[part.name] = REF(part)
+ var/image/part_image = image(icon = part.icon, icon_state = part.icon_state)
+ if(part.loc != src)
+ part_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
+ items += list(part.name = part_image)
+ var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE, tooltips = TRUE)
+ if(!pick)
+ return
+ var/part_reference = display_names[pick]
+ var/obj/item/part = locate(part_reference) in mod_parts
+ if(!istype(part) || user.incapacitated())
+ return
+ if((active && part != helmet) || activating)
+ balloon_alert(user, "deactivate the suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ var/parts_to_check = mod_parts - part
+ if(part.loc == src)
+ deploy(user, part)
+ for(var/obj/item/checking_part as anything in parts_to_check)
+ if(checking_part.loc != src)
+ continue
+ choose_deploy(user)
+ break
+ else
+ retract(user, part)
+ for(var/obj/item/checking_part as anything in parts_to_check)
+ if(checking_part.loc == src)
+ continue
+ choose_deploy(user)
+ break
+
+/// Quickly deploys all parts (or retracts if all are on the wearer)
+/obj/item/mod/control/proc/quick_deploy(mob/user)
+ if(active || activating)
+ balloon_alert(user, "deactivate the suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ var/deploy = FALSE
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc != src)
+ continue
+ deploy = TRUE
+ for(var/obj/item/part as anything in mod_parts)
+ if(deploy && part.loc == src)
+ deploy(null, part)
+ else if(!deploy && part.loc != src)
+ retract(null, part)
+ wearer.visible_message(span_notice("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."),
+ span_notice("[src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."),
+ span_hear("You hear a mechanical hiss."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ return TRUE
+
+/// Deploys a part of the suit onto the user.
+/obj/item/mod/control/proc/deploy(mob/user, obj/item/part)
+ if(part.loc != src)
+ if(!user)
+ return FALSE
+ balloon_alert(user, "[part.name] already deployed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ if(part in overslotting_parts)
+ var/obj/item/overslot = wearer.get_item_by_slot(part.slot_flags)
+ if(overslot)
+ overslotting_parts[part] = overslot
+ wearer.transferItemToLoc(overslot, part, force = TRUE)
+ RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit))
+ if(wearer.equip_to_slot_if_possible(part, part.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
+ ADD_TRAIT(part, TRAIT_NODROP, MOD_TRAIT)
+ if(!user)
+ return TRUE
+ wearer.visible_message(span_notice("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss."),
+ span_notice("[part] deploy[part.p_s()] with a mechanical hiss."),
+ span_hear("You hear a mechanical hiss."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ return TRUE
+ else
+ if(!user)
+ return FALSE
+ balloon_alert(user, "bodypart clothed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+
+/// Retract a part of the suit from the user.
+/obj/item/mod/control/proc/retract(mob/user, obj/item/part)
+ if(part.loc == src)
+ if(!user)
+ return FALSE
+ balloon_alert(user, "[part.name] already retracted!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ REMOVE_TRAIT(part, TRAIT_NODROP, MOD_TRAIT)
+ wearer.transferItemToLoc(part, src, force = TRUE)
+ if(overslotting_parts[part])
+ UnregisterSignal(part, COMSIG_ATOM_EXITED)
+ var/obj/item/overslot = overslotting_parts[part]
+ if(!wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
+ wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE)
+ overslotting_parts[part] = null
+ if(!user)
+ return
+ wearer.visible_message(span_notice("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss."),
+ span_notice("[part] retract[part.p_s()] back into [src] with a mechanical hiss."),
+ span_hear("You hear a mechanical hiss."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+
+/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on
+/obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE)
+ if(!wearer)
+ if(!force_deactivate)
+ balloon_alert(user, "put suit on back!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE))
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ for(var/obj/item/part as anything in mod_parts)
+ if(!force_deactivate && part.loc == src)
+ balloon_alert(user, "deploy all parts first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(locked && !active && !allowed(user) && !force_deactivate)
+ balloon_alert(user, "access insufficient!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!get_charge() && !force_deactivate)
+ balloon_alert(user, "suit not powered!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(open && !force_deactivate)
+ balloon_alert(user, "close the suit panel!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(activating)
+ if(!force_deactivate)
+ balloon_alert(user, "suit already [active ? "shutting down" : "starting up"]!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.active || module.allowed_inactive)
+ continue
+ module.on_deactivation(display_message = FALSE)
+ activating = TRUE
+ to_chat(wearer, span_notice("MODsuit [active ? "shutting down" : "starting up"]."))
+ if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE))
+ to_chat(wearer, span_notice("[boots] [active ? "relax their grip on your legs" : "seal around your feet"]."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(boots, seal = !active)
+ if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE))
+ to_chat(wearer, span_notice("[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"]."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(gauntlets, seal = !active)
+ if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE))
+ to_chat(wearer, span_notice("[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"]."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(chestplate, seal = !active)
+ if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE))
+ to_chat(wearer, span_notice("[helmet] hisses [active ? "open" : "closed"]."))
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(helmet, seal = !active)
+ if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE))
+ to_chat(wearer, span_notice("Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer]."))
+ if(ai)
+ to_chat(ai, span_notice("SYSTEMS [active ? "DEACTIVATED. GOODBYE" : "ACTIVATED. WELCOME"]: \"[ai]\""))
+ finish_activation(on = !active)
+ if(active)
+ playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
+ if(!malfunctioning)
+ wearer.playsound_local(get_turf(src), 'sound/mecha/nominal.ogg', 50)
+ else
+ playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
+ activating = FALSE
+ return TRUE
+
+///Seals or unseals the given part
+/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal)
+ if(seal)
+ part.clothing_flags |= part.visor_flags
+ part.flags_inv |= part.visor_flags_inv
+ part.flags_cover |= part.visor_flags_cover
+ part.heat_protection = initial(part.heat_protection)
+ part.cold_protection = initial(part.cold_protection)
+ part.alternate_worn_layer = null
+ else
+ part.flags_cover &= ~part.visor_flags_cover
+ part.flags_inv &= ~part.visor_flags_inv
+ part.clothing_flags &= ~part.visor_flags
+ part.heat_protection = NONE
+ part.cold_protection = NONE
+ part.alternate_worn_layer = mod_parts[part]
+ if(part == boots)
+ boots.icon_state = "[skin]-boots[seal ? "-sealed" : ""]"
+ wearer.update_inv_shoes()
+ if(part == gauntlets)
+ gauntlets.icon_state = "[skin]-gauntlets[seal ? "-sealed" : ""]"
+ wearer.update_inv_gloves()
+ if(part == chestplate)
+ chestplate.icon_state = "[skin]-chestplate[seal ? "-sealed" : ""]"
+ wearer.update_inv_wear_suit()
+ wearer.update_inv_w_uniform()
+ if(part == helmet)
+ helmet.icon_state = "[skin]-helmet[seal ? "-sealed" : ""]"
+ wearer.update_inv_head()
+ wearer.update_inv_wear_mask()
+ wearer.update_inv_glasses()
+ wearer.update_hair()
+
+/// Finishes the suit's activation, starts processing
+/obj/item/mod/control/proc/finish_activation(on)
+ active = on
+ if(active)
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_suit_activation()
+ START_PROCESSING(SSobj, src)
+ else
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_suit_deactivation()
+ STOP_PROCESSING(SSobj, src)
+ update_speed()
+ update_icon_state()
+ wearer.update_inv_back(slot_flags)
+
+/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
+/obj/item/mod/control/proc/quick_activation()
+ var/seal = TRUE
+ for(var/obj/item/part as anything in mod_parts)
+ if(!deploy(null, part))
+ seal = FALSE
+ if(!seal)
+ return
+ for(var/obj/item/part as anything in mod_parts)
+ seal_part(part, seal = TRUE)
+ finish_activation(on = TRUE)
+
+/obj/item/mod/control/proc/has_wearer()
+ return wearer
diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm
new file mode 100644
index 000000000000..a05717970348
--- /dev/null
+++ b/code/modules/mod/mod_ai.dm
@@ -0,0 +1,125 @@
+/**
+ * Simple proc to insert the pAI into the MODsuit.
+ *
+ * user - The person trying to put the pAI into the MODsuit.
+ * card - The pAI card we're slotting in the MODsuit.
+ */
+
+/obj/item/mod/control/proc/insert_pai(mob/user, obj/item/paicard/card)
+ if(ai)
+ balloon_alert(user, "ai already installed!")
+ return
+ if(!card.pai || !card.pai.mind)
+ balloon_alert(user, "pai unresponsive!")
+ return
+ balloon_alert(user, "transferring to suit...")
+ if(!do_after(user, 5 SECONDS, target = src))
+ balloon_alert(user, "interrupted!")
+ return FALSE
+ if(!user.transferItemToLoc(card, src))
+ return
+
+ card.pai.canholo = FALSE
+ ai = card.pai
+ balloon_alert(user, "pAI transferred to suit")
+ balloon_alert(ai, "transferred to a suit")
+ ai.remote_control = src
+ for(var/datum/action/action as anything in actions)
+ action.Grant(ai)
+ return TRUE
+
+/**
+ * Simple proc to extract the pAI from the MODsuit. It's the proc to call if you want to take it out,
+ * remove_pai() is there so atom_destruction() doesn't have any risk of sleeping.
+ *
+ * user - The person trying to take out the pAI from the MODsuit.
+ * forced - Whether or not we skip the checks and just eject the pAI. Defaults to FALSE.
+ * feedback - Whether to give feedback via balloon alerts or not. Defaults to TRUE.
+ */
+/obj/item/mod/control/proc/extract_pai(mob/user, forced = FALSE, feedback = TRUE)
+ if(!ai)
+ if(user && feedback)
+ balloon_alert(user, "no pAI to remove!")
+ return
+ if(!ispAI(ai))
+ if(user && feedback)
+ balloon_alert(user, "onboard AI cannot fit in this card!")
+ return
+ if(!forced)
+ if(!open)
+ if(user && feedback)
+ balloon_alert(user, "open the suit panel!")
+ return FALSE
+ if(!do_after(user, 5 SECONDS, target = src))
+ if(user && feedback)
+ balloon_alert(user, "interrupted!")
+ return FALSE
+
+ remove_pai(feedback)
+
+ if(feedback && user)
+ balloon_alert(user, "pAI removed from the suit")
+
+/**
+ * Simple proc that handles the safe removal of the pAI from a MOD control unit.
+ *
+ * Arguments:
+ * * feedback - Whether or not we want to give balloon alert feedback to the ai. Defaults to FALSE.
+ */
+/obj/item/mod/control/proc/remove_pai(feedback = FALSE)
+ if(!ispAI(ai))
+ return
+ var/mob/living/silicon/pai/pai = ai
+ var/turf/drop_off = get_turf(src)
+ if(drop_off) // In case there's no drop_off, the pAI will simply get deleted.
+ pai.card.forceMove(drop_off)
+
+ for(var/datum/action/action as anything in actions)
+ if(action.owner == pai)
+ action.Remove(pai)
+
+ if(feedback)
+ balloon_alert(pai, "removed from a suit")
+ pai.remote_control = null
+ pai.canholo = TRUE
+ pai = null
+
+#define MOVE_DELAY 2
+#define WEARER_DELAY 1
+#define LONE_DELAY 5
+#define CELL_PER_STEP (DEFAULT_CHARGE_DRAIN * 2.5)
+#define AI_FALL_TIME (1 SECONDS)
+
+/*obj/item/mod/control/relaymove(mob/user, direction)
+ var/cell = get_cell()
+ if((!active && wearer) || !cell || cell.charge < CELL_PER_STEP || user != ai || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE))
+ return FALSE
+ var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? SQRT_2 : 1) * (wearer ? WEARER_DELAY : LONE_DELAY)
+ if(wearer && !wearer.Process_Spacemove(direction))
+ return FALSE
+ else if(!wearer && (!has_gravity() || !isturf(loc)))
+ return FALSE
+ COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown)
+ cell.charge = max(0, cell.charge - CELL_PER_STEP)
+ playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE)
+ if(ismovable(wearer?.loc))
+ return wearer.loc.relaymove(wearer, direction)
+ else if(wearer)
+
+ var/atom/movable/mover = wearer || src
+ return step(mover, direction)
+
+#undef MOVE_DELAY
+#undef WEARER_DELAY
+#undef LONE_DELAY
+#undef CELL_PER_STEP
+#undef AI_FALL_TIME
+
+ return
+ REMOVE_TRAIT(wearer, TRAIT_MOBILITY_NOREST, MOD_TRAIT)
+
+/obj/item/mod/control/ui_state(mob/user)
+ if(user == ai)
+ return GLOB.contained_state
+ return ..()
+*/
diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm
new file mode 100644
index 000000000000..7a9e710c9382
--- /dev/null
+++ b/code/modules/mod/mod_clothes.dm
@@ -0,0 +1,56 @@
+/obj/item/clothing/head/mod
+ name = "MOD helmet"
+ desc = "A helmet for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-helmet"
+ base_icon_state = "helmet"
+ mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = HEAD
+ heat_protection = HEAD
+ cold_protection = HEAD
+ obj_flags = IMMUTABLE_SLOW
+ visor_flags = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|ALLOWINTERNALS
+
+/obj/item/clothing/suit/mod
+ name = "MOD chestplate"
+ desc = "A chestplate for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-chestplate"
+ base_icon_state = "chestplate"
+ mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ blood_overlay_type = "armor"
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = CHEST|GROIN
+ heat_protection = CHEST|GROIN
+ cold_protection = CHEST|GROIN
+ obj_flags = IMMUTABLE_SLOW
+
+/obj/item/clothing/gloves/mod
+ name = "MOD gauntlets"
+ desc = "A pair of gauntlets for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-gauntlets"
+ base_icon_state = "gauntlets"
+ mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = HANDS|ARMS
+ heat_protection = HANDS|ARMS
+ cold_protection = HANDS|ARMS
+ obj_flags = IMMUTABLE_SLOW
+
+/obj/item/clothing/shoes/mod
+ name = "MOD boots"
+ desc = "A pair of boots for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-boots"
+ base_icon_state = "boots"
+ mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = FEET|LEGS
+ heat_protection = FEET|LEGS
+ cold_protection = FEET|LEGS
+ obj_flags = IMMUTABLE_SLOW
+ supports_variations = DIGITIGRADE_VARIATION
+ can_be_tied = FALSE
+ visor_flags_inv = HIDESHOES
diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm
new file mode 100644
index 000000000000..0f37a4fd1f11
--- /dev/null
+++ b/code/modules/mod/mod_construction.dm
@@ -0,0 +1,275 @@
+/obj/item/mod/construction
+ desc = "A part used in MOD construction."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ item_state = "rack_parts"
+
+/obj/item/mod/construction/helmet
+ name = "MOD helmet"
+ icon_state = "helmet"
+
+/obj/item/mod/construction/helmet/examine(mob/user)
+ . = ..()
+ . += span_notice("You could insert these into a MOD shell...")
+
+/obj/item/mod/construction/chestplate
+ name = "MOD chestplate"
+ icon_state = "chestplate"
+
+/obj/item/mod/construction/chestplate/examine(mob/user)
+ . = ..()
+ . += span_notice("You could insert these into a MOD shell...")
+
+/obj/item/mod/construction/gauntlets
+ name = "MOD gauntlets"
+ icon_state = "gauntlets"
+
+/obj/item/mod/construction/gauntlets/examine(mob/user)
+ . = ..()
+ . += span_notice("You could insert these into a MOD shell...")
+
+/obj/item/mod/construction/boots
+ name = "MOD boots"
+ icon_state = "boots"
+
+/obj/item/mod/construction/boots/examine(mob/user)
+ . = ..()
+ . += span_notice("You could insert these into a MOD shell...")
+
+/obj/item/mod/construction/broken_core
+ name = "broken MOD core"
+ icon_state = "mod-core"
+ desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though."
+
+/obj/item/mod/construction/broken_core/examine(mob/user)
+ . = ..()
+ . += span_notice("You could repair it with a screwdriver...")
+
+/obj/item/mod/construction/broken_core/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!tool.use_tool(src, user, 5 SECONDS, volume = 30))
+ return
+ new /obj/item/mod/core/standard(drop_location())
+ qdel(src)
+
+/obj/item/mod/construction/plating
+ name = "MOD external plating"
+ desc = "External plating used to finish a MOD control unit."
+ icon_state = "standard-plating"
+ var/datum/mod_theme/theme = /datum/mod_theme
+
+/obj/item/mod/construction/plating/Initialize(mapload)
+ . = ..()
+ var/datum/mod_theme/used_theme = GLOB.mod_themes[theme]
+ name = "MOD [used_theme.name] external plating"
+ desc = "[desc] [used_theme.desc]"
+ icon_state = "[used_theme.default_skin]-plating"
+
+/obj/item/mod/construction/plating/engineering
+ theme = /datum/mod_theme/engineering
+
+/obj/item/mod/construction/plating/atmospheric
+ theme = /datum/mod_theme/atmospheric
+
+/obj/item/mod/construction/plating/medical
+ theme = /datum/mod_theme/medical
+
+/obj/item/mod/construction/plating/security
+ theme = /datum/mod_theme/security
+
+#define START_STEP "start"
+#define CORE_STEP "core"
+#define SCREWED_CORE_STEP "screwed_core"
+#define HELMET_STEP "helmet"
+#define CHESTPLATE_STEP "chestplate"
+#define GAUNTLETS_STEP "gauntlets"
+#define BOOTS_STEP "boots"
+#define WRENCHED_ASSEMBLY_STEP "wrenched_assembly"
+#define SCREWED_ASSEMBLY_STEP "screwed_assembly"
+
+/obj/item/mod/construction/shell
+ name = "MOD shell"
+ icon_state = "mod-construction_start"
+ desc = "A MOD shell."
+ var/obj/item/core
+ var/obj/item/helmet
+ var/obj/item/chestplate
+ var/obj/item/gauntlets
+ var/obj/item/boots
+ var/step = START_STEP
+
+/obj/item/mod/construction/shell/examine(mob/user)
+ . = ..()
+ var/display_text
+ switch(step)
+ if(START_STEP)
+ display_text = "It looks like it's missing a MOD core..."
+ if(CORE_STEP)
+ display_text = "The core seems loose..."
+ if(SCREWED_CORE_STEP)
+ display_text = "It looks like it's missing a helmet..."
+ if(HELMET_STEP)
+ display_text = "It looks like it's missing a chestplate..."
+ if(CHESTPLATE_STEP)
+ display_text = "It looks like it's missing gauntlets..."
+ if(GAUNTLETS_STEP)
+ display_text = "It looks like it's missing boots..."
+ if(BOOTS_STEP)
+ display_text = "The assembly seems unsecured..."
+ if(WRENCHED_ASSEMBLY_STEP)
+ display_text = "The assembly seems loose..."
+ if(SCREWED_ASSEMBLY_STEP)
+ display_text = "All it's missing is external plating..."
+ . += span_notice(display_text)
+
+/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, params)
+ . = ..()
+ switch(step)
+ if(START_STEP)
+ if(!istype(part, /obj/item/mod/core))
+ return
+ if(!user.transferItemToLoc(part, src))
+ balloon_alert(user, "core stuck to your hand!")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "core inserted")
+ core = part
+ step = CORE_STEP
+ if(CORE_STEP)
+ if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "core screwed")
+ step = SCREWED_CORE_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ core.forceMove(drop_location())
+ balloon_alert(user, "core taken out")
+ step = START_STEP
+ if(SCREWED_CORE_STEP)
+ if(istype(part, /obj/item/mod/construction/helmet)) //Construct
+ if(!user.transferItemToLoc(part, src))
+ balloon_alert(user, "helmet stuck to your hand!")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "helmet added")
+ helmet = part
+ step = HELMET_STEP
+ else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "core unscrewed")
+ step = CORE_STEP
+ if(HELMET_STEP)
+ if(istype(part, /obj/item/mod/construction/chestplate)) //Construct
+ if(!user.transferItemToLoc(part, src))
+ balloon_alert(user, "chestplate stuck to your hand!")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "chestplate added")
+ chestplate = part
+ step = CHESTPLATE_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ helmet.forceMove(drop_location())
+ balloon_alert(user, "helmet removed")
+ helmet = null
+ step = SCREWED_CORE_STEP
+ if(CHESTPLATE_STEP)
+ if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct
+ if(!user.transferItemToLoc(part, src))
+ balloon_alert(user, "gauntlets stuck to your hand!")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "gauntlets added")
+ gauntlets = part
+ step = GAUNTLETS_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ chestplate.forceMove(drop_location())
+ balloon_alert(user, "chestplate removed")
+ chestplate = null
+ step = HELMET_STEP
+ if(GAUNTLETS_STEP)
+ if(istype(part, /obj/item/mod/construction/boots)) //Construct
+ if(!user.transferItemToLoc(part, src))
+ balloon_alert(user, "boots added")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "you fit [part] onto [src].")
+ boots = part
+ step = BOOTS_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ gauntlets.forceMove(drop_location())
+ balloon_alert(user, "gauntlets removed")
+ gauntlets = null
+ step = CHESTPLATE_STEP
+ if(BOOTS_STEP)
+ if(part.tool_behaviour == TOOL_WRENCH) //Construct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "assembly secured")
+ step = WRENCHED_ASSEMBLY_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ boots.forceMove(drop_location())
+ balloon_alert(user, "boots removed")
+ boots = null
+ step = GAUNTLETS_STEP
+ if(WRENCHED_ASSEMBLY_STEP)
+ if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "assembly screwed")
+ step = SCREWED_ASSEMBLY_STEP
+ else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "assembly unsecured")
+ step = BOOTS_STEP
+ if(SCREWED_ASSEMBLY_STEP)
+ if(istype(part, /obj/item/mod/construction/plating)) //Construct
+ var/obj/item/mod/construction/plating/external_plating = part
+ if(!user.transferItemToLoc(part, src))
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ balloon_alert(user, "suit finished")
+ var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core)
+ core = null
+ qdel(src)
+ user.put_in_hands(mod)
+ else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume=30))
+ balloon_alert(user, "assembly unscrewed")
+ step = SCREWED_ASSEMBLY_STEP
+ update_icon_state()
+
+/obj/item/mod/construction/shell/update_icon_state()
+ . = ..()
+ icon_state = "mod-construction_[step]"
+
+/obj/item/mod/construction/shell/Destroy()
+ QDEL_NULL(core)
+ QDEL_NULL(helmet)
+ QDEL_NULL(chestplate)
+ QDEL_NULL(gauntlets)
+ QDEL_NULL(boots)
+ return ..()
+
+/obj/item/mod/construction/shell/handle_atom_del(atom/deleted_atom)
+ if(deleted_atom == core)
+ core = null
+ if(deleted_atom == helmet)
+ helmet = null
+ if(deleted_atom == chestplate)
+ chestplate = null
+ if(deleted_atom == gauntlets)
+ gauntlets = null
+ if(deleted_atom == boots)
+ boots = null
+ return ..()
+
+#undef START_STEP
+#undef CORE_STEP
+#undef SCREWED_CORE_STEP
+#undef HELMET_STEP
+#undef CHESTPLATE_STEP
+#undef GAUNTLETS_STEP
+#undef BOOTS_STEP
+#undef WRENCHED_ASSEMBLY_STEP
+#undef SCREWED_ASSEMBLY_STEP
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
new file mode 100644
index 000000000000..0199662862f8
--- /dev/null
+++ b/code/modules/mod/mod_control.dm
@@ -0,0 +1,713 @@
+/// MODsuits, trade-off between armor and utility
+/obj/item/mod
+ name = "Base MOD"
+ desc = "You should not see this, yell at a coder!"
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+
+/obj/item/mod/control
+ name = "MOD control unit"
+ desc = "The control unit of a Modular Outerwear Device, a powered, back-mounted suit that protects against various environments."
+ icon_state = "control"
+ base_icon_state = "control"
+ item_state = "mod_control"
+ mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ strip_delay = 10 SECONDS
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "fire" = 0, "acid" = 0)
+ actions_types = list(
+ /datum/action/item_action/mod/deploy,
+ /datum/action/item_action/mod/activate,
+ /datum/action/item_action/mod/panel,
+ /datum/action/item_action/mod/module,
+ /datum/action/item_action/mod/deploy/ai,
+ /datum/action/item_action/mod/activate/ai,
+ /datum/action/item_action/mod/panel/ai,
+ /datum/action/item_action/mod/module/ai,
+ )
+ resistance_flags = NONE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ siemens_coefficient = 0.5
+ //alternate_worn_layer = HAND_LAYER+0.1 //we want it to go above generally everything, but not hands
+ /// The MOD's theme, decides on some stuff like armor and statistics.
+ var/datum/mod_theme/theme = /datum/mod_theme
+ /// Looks of the MOD.
+ var/skin = "standard"
+ /// Theme of the MOD TGUI
+ var/ui_theme = "ntos"
+ /// If the suit is deployed and turned on.
+ var/active = FALSE
+ /// If the suit wire/module hatch is open.
+ var/open = FALSE
+ /// If the suit is ID locked.
+ var/locked = FALSE
+ /// If the suit is malfunctioning.
+ var/malfunctioning = FALSE
+ /// If the suit is currently activating/deactivating.
+ var/activating = FALSE
+ /// How long the MOD is electrified for.
+ var/seconds_electrified = MACHINE_NOT_ELECTRIFIED
+ /// If the suit interface is broken.
+ var/interface_break = FALSE
+ /// How much module complexity can this MOD carry.
+ var/complexity_max = DEFAULT_MAX_COMPLEXITY
+ /// How much module complexity this MOD is carrying.
+ var/complexity = 0
+ /// Power usage of the MOD.
+ var/charge_drain = DEFAULT_CHARGE_DRAIN
+ /// Slowdown of the MOD when not active.
+ var/slowdown_inactive = 1.25
+ /// Slowdown of the MOD when active.
+ var/slowdown_active = 0.75
+ /// How long this MOD takes each part to seal.
+ var/activation_step_time = MOD_ACTIVATION_STEP_TIME
+ /// Extended description of the theme.
+ var/extended_desc
+ /// MOD helmet.
+ var/obj/item/clothing/head/mod/helmet
+ /// MOD chestplate.
+ var/obj/item/clothing/suit/mod/chestplate
+ /// MOD gauntlets.
+ var/obj/item/clothing/gloves/mod/gauntlets
+ /// MOD boots.
+ var/obj/item/clothing/shoes/mod/boots
+ /// MOD core.
+ var/obj/item/mod/core/core
+ /// Associated list of parts (helmet, chestplate, gauntlets, boots) to their unsealed worn layer.
+ var/list/mod_parts = list()
+ /// Associated list of parts that can overslot to their overslot (overslot means the part can cover another layer of clothing).
+ var/list/overslotting_parts = list()
+ /// Modules the MOD should spawn with.
+ var/list/initial_modules = list()
+ /// Modules the MOD currently possesses.
+ var/list/modules = list()
+ /// Currently used module.
+ var/obj/item/mod/module/selected_module
+ /// AI mob inhabiting the MOD.
+ var/mob/living/silicon/ai/ai
+ /// Delay between moves as AI.
+ var/movedelay = 0
+ /// Cooldown for AI moves.
+ COOLDOWN_DECLARE(cooldown_mod_move)
+ /// Person wearing the MODsuit.
+ var/mob/living/carbon/human/wearer
+
+/obj/item/mod/control/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core)
+ . = ..()
+ if(new_theme)
+ theme = new_theme
+ theme = GLOB.mod_themes[theme]
+ slot_flags = theme.slot_flags
+ extended_desc = theme.extended_desc
+ slowdown_inactive = theme.slowdown_inactive
+ slowdown_active = theme.slowdown_active
+ complexity_max = theme.complexity_max
+ ui_theme = theme.ui_theme
+ charge_drain = theme.charge_drain
+ initial_modules += theme.inbuilt_modules
+ wires = new /datum/wires/mod(src)
+ if(length(req_access))
+ locked = TRUE
+ new_core?.install(src)
+ helmet = new /obj/item/clothing/head/mod(src)
+ mod_parts += helmet
+ chestplate = new /obj/item/clothing/suit/mod(src)
+ chestplate.allowed = typecacheof(theme.allowed_suit_storage)
+ mod_parts += chestplate
+ gauntlets = new /obj/item/clothing/gloves/mod(src)
+ mod_parts += gauntlets
+ boots = new /obj/item/clothing/shoes/mod(src)
+ mod_parts += boots
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.name = "[theme.name] [part.name]"
+ part.desc = "[part.desc] [theme.desc]"
+ part.armor = getArmor(arglist(theme.armor))
+ part.resistance_flags = theme.resistance_flags
+ part.flags_1 |= theme.atom_flags //flags like initialization or admin spawning are here, so we cant set, have to add
+ part.heat_protection = NONE
+ part.cold_protection = NONE
+ part.max_heat_protection_temperature = theme.max_heat_protection_temperature
+ part.min_cold_protection_temperature = theme.min_cold_protection_temperature
+ part.siemens_coefficient = theme.siemens_coefficient
+ for(var/obj/item/part as anything in mod_parts)
+ RegisterSignal(part, COMSIG_OBJ_DESTRUCTION, PROC_REF(on_part_destruction))
+ RegisterSignal(part, COMSIG_PARENT_QDELETING, PROC_REF(on_part_deletion))
+ set_mod_skin(new_skin || theme.default_skin)
+ update_speed()
+ for(var/obj/item/mod/module/module as anything in initial_modules)
+ module = new module(src)
+ install(module)
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ movedelay = CONFIG_GET(number/movedelay/run_delay)
+
+/obj/item/mod/control/Destroy()
+ if(active)
+ STOP_PROCESSING(SSobj, src)
+ for(var/obj/item/mod/module/module as anything in modules)
+ uninstall(module, deleting = TRUE)
+ for(var/obj/item/part as anything in mod_parts)
+ overslotting_parts -= part
+ var/atom/deleting_atom
+ if(!QDELETED(helmet))
+ deleting_atom = helmet
+ helmet = null
+ mod_parts -= deleting_atom
+ qdel(deleting_atom)
+ if(!QDELETED(chestplate))
+ deleting_atom = chestplate
+ chestplate = null
+ mod_parts -= deleting_atom
+ qdel(deleting_atom)
+ if(!QDELETED(gauntlets))
+ deleting_atom = gauntlets
+ gauntlets = null
+ mod_parts -= deleting_atom
+ qdel(deleting_atom)
+ if(!QDELETED(boots))
+ deleting_atom = boots
+ boots = null
+ mod_parts -= deleting_atom
+ qdel(deleting_atom)
+ if(core)
+ QDEL_NULL(core)
+ QDEL_NULL(wires)
+ return ..()
+
+/obj/item/mod/control/obj_destruction(damage_flag)
+ for(var/obj/item/mod/module/module as anything in modules)
+ uninstall(module)
+ for(var/obj/item/part as anything in mod_parts)
+ if(!overslotting_parts[part])
+ continue
+ var/obj/item/overslot = overslotting_parts[part]
+ overslot.forceMove(drop_location())
+ overslotting_parts[part] = null
+ /*if(ai)
+ ai.controlled_equipment = null
+ ai.remote_control = null
+ for(var/datum/action/action as anything in actions)
+ if(action.owner == ai)
+ action.Remove(ai)
+ new /obj/item/mod/ai_minicard(drop_location(), ai)*/
+ return ..()
+
+/obj/item/mod/control/examine(mob/user)
+ . = ..()
+ if(active)
+ . += span_notice("Charge: [core ? "[get_charge_percent()]%" : "No core"].")
+ . += span_notice("Selected module: [selected_module || "None"].")
+ if(!open && !active)
+ . += span_notice("You could put it on your back to turn it on.")
+ . += span_notice("You could open the cover with a screwdriver.")
+ else if(open)
+ . += span_notice("You could close the cover with a screwdriver.")
+ . += span_notice("You could use modules on it to install them.")
+ . += span_notice("You could remove modules with a crowbar.")
+ . += span_notice("You could update the access lock with an ID.")
+ . += span_notice("You could access the wire panel with a wire tool.")
+ if(core)
+ . += span_notice("You could remove [core] with a wrench.")
+ else
+ . += span_notice("You could use a MOD core on it to install one.")
+ if(ai)
+ . += span_notice("You could remove [ai] with an intellicard.")
+ else
+ . += span_notice("You could install an AI with an intellicard.")
+
+/obj/item/mod/control/examine_more(mob/user)
+ . = ..()
+ . += "[extended_desc]"
+
+/obj/item/mod/control/process(delta_time)
+ if(seconds_electrified > MACHINE_NOT_ELECTRIFIED)
+ seconds_electrified--
+ if(!get_charge() && active && !activating)
+ power_off()
+ return PROCESS_KILL
+ var/malfunctioning_charge_drain = 0
+ if(malfunctioning)
+ malfunctioning_charge_drain = rand(1,20)
+ subtract_charge((charge_drain + malfunctioning_charge_drain)*delta_time)
+ update_charge_alert()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(malfunctioning && module.active && DT_PROB(5, delta_time))
+ module.on_deactivation(display_message = TRUE)
+ module.on_process(delta_time)
+
+/obj/item/mod/control/equipped(mob/user, slot)
+ ..()
+ if(slot == slot_flags)
+ set_wearer(user)
+ else if(wearer)
+ unset_wearer()
+
+/obj/item/mod/control/dropped(mob/user)
+ . = ..()
+ if(!wearer)
+ return
+ clean_up()
+
+/obj/item/mod/control/item_action_slot_check(slot)
+ if(slot & slot_flags)
+ return TRUE
+
+/obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
+ . = ..()
+ if(!wearer || old_loc != wearer || loc == wearer)
+ return
+ clean_up()
+
+/obj/item/mod/control/allow_attack_hand_drop(mob/user)
+ if(user != wearer)
+ return ..()
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc != src)
+ balloon_alert(user, "retract parts first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+
+/obj/item/mod/control/MouseDrop(atom/over_object)
+ if(usr != wearer || !istype(over_object, /atom/movable/screen/inventory/hand))
+ return ..()
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc != src)
+ balloon_alert(wearer, "retract parts first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(!wearer.incapacitated())
+ var/atom/movable/screen/inventory/hand/ui_hand = over_object
+ if(wearer.putItemFromInventoryInHandIfPossible(src, ui_hand.held_index))
+ add_fingerprint(usr)
+ return ..()
+
+/obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench)
+ if(..())
+ return TRUE
+ if(seconds_electrified && get_charge() && shock(user))
+ return TRUE
+ if(open)
+ if(!core)
+ balloon_alert(user, "no core!")
+ return TRUE
+ balloon_alert(user, "removing core...")
+ wrench.play_tool_sound(src, 100)
+ if(!wrench.use_tool(src, user, 3 SECONDS) || !open)
+ balloon_alert(user, "interrupted!")
+ return TRUE
+ wrench.play_tool_sound(src, 100)
+ balloon_alert(user, "core removed")
+ core.forceMove(drop_location())
+ update_charge_alert()
+ return TRUE
+ return ..()
+
+/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver)
+ if(..())
+ return TRUE
+ if(active || activating)// || ai_controller)
+ balloon_alert(user, "deactivate suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ balloon_alert(user, "[open ? "closing" : "opening"] cover...")
+ screwdriver.play_tool_sound(src, 100)
+ if(screwdriver.use_tool(src, user, 1 SECONDS))
+ if(active || activating)
+ balloon_alert(user, "deactivate suit first!")
+ screwdriver.play_tool_sound(src, 100)
+ balloon_alert(user, "cover [open ? "closed" : "opened"]")
+ open = !open
+ else
+ balloon_alert(user, "interrupted!")
+ return TRUE
+
+/obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar)
+ . = ..()
+ if(!open)
+ balloon_alert(user, "open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!allowed(user))
+ balloon_alert(user, "insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL)
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(length(modules))
+ var/list/removable_modules = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.removable)
+ continue
+ removable_modules += module
+ var/obj/item/mod/module/module_to_remove = tgui_input_list(user, "Which module to remove?", "Module Removal", removable_modules)
+ if(!module_to_remove?.mod)
+ return FALSE
+ uninstall(module_to_remove)
+ module_to_remove.forceMove(drop_location())
+ crowbar.play_tool_sound(src, 100)
+ return TRUE
+ balloon_alert(user, "no modules!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+
+/obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params)
+ if(istype(attacking_item, /obj/item/mod/module))
+ if(!open)
+ balloon_alert(user, "open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ install(attacking_item, user)
+ return TRUE
+ else if(istype(attacking_item, /obj/item/mod/core))
+ if(!open)
+ balloon_alert(user, "open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(core)
+ balloon_alert(user, "core already installed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ var/obj/item/mod/core/attacking_core = attacking_item
+ attacking_core.install(src)
+ balloon_alert(user, "core installed")
+ playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ update_charge_alert()
+ return TRUE
+ else if(is_wire_tool(attacking_item) && open)
+ wires.interact(user)
+ return TRUE
+ else if(open && attacking_item.GetID())
+ update_access(user, attacking_item.GetID())
+ return TRUE
+ else if(open && istype(attacking_item, /obj/item/stock_parts/cell) && istype(core, /obj/item/mod/core/standard))
+ var/obj/item/mod/core/standard/attacked_core = core
+ attacked_core.on_attackby(src, attacking_item, wearer)
+ return TRUE
+ return ..()
+
+/obj/item/mod/control/get_cell()
+ if(!open)
+ return
+ var/obj/item/stock_parts/cell/cell = get_charge_source()
+ if(!istype(cell))
+ return
+ return cell
+
+/obj/item/mod/control/GetAccess()
+ /*if(ai_controller)
+ return req_access.Copy()
+ else */
+ return ..()
+
+/obj/item/mod/control/emag_act(mob/user)
+ locked = !locked
+ balloon_alert(user, "suit access [locked ? "locked" : "unlocked"]")
+
+/obj/item/mod/control/emp_act(severity)
+ . = ..()
+ if(!active || !wearer)
+ return
+ to_chat(wearer, span_notice("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!"))
+ if(. & EMP_PROTECT_CONTENTS)
+ return
+ selected_module?.on_deactivation(display_message = TRUE)
+ wearer.apply_damage(10 / severity, BURN, spread_damage=TRUE)
+ to_chat(wearer, span_danger("You feel [src] heat up from the EMP, burning you slightly."))
+ if(wearer.stat < UNCONSCIOUS && prob(10))
+ wearer.emote("scream")
+
+/*obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
+ if(visuals_only)
+ set_wearer(outfit_wearer) //we need to set wearer manually since it doesnt call equipped
+ quick_activation()*/
+
+/obj/item/mod/control/doStrip(mob/stripper, mob/owner)
+ if(active && !toggle_activate(stripper, force_deactivate = TRUE))
+ return
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc == src)
+ continue
+ retract(null, part)
+ return ..()
+
+/obj/item/mod/control/worn_overlays(isinhands = FALSE, icon_file)
+ . = ..()
+ for(var/obj/item/mod/module/module as anything in modules)
+ var/list/module_icons = module.generate_worn_overlay(src.layer)
+ if(!length(module_icons))
+ continue
+ . += module_icons
+
+/obj/item/mod/control/update_icon_state()
+ item_state = "[skin]-control[active ? "-sealed" : ""]"
+ return ..()
+
+/obj/item/mod/control/proc/set_wearer(mob/user)
+ wearer = user
+ SEND_SIGNAL(src, COMSIG_MOD_WEARER_SET, wearer)
+ RegisterSignal(wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ RegisterSignal(wearer, COMSIG_SPECIES_GAIN, PROC_REF(on_species_gain))
+ update_charge_alert()
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_equip()
+
+/obj/item/mod/control/proc/unset_wearer()
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_unequip()
+ UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN))
+ wearer.clear_alert("mod_charge")
+ SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer)
+ wearer = null
+
+/obj/item/mod/control/proc/clean_up()
+ if(active || activating)
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.active)
+ continue
+ module.on_deactivation(display_message = FALSE)
+ for(var/obj/item/part as anything in mod_parts)
+ seal_part(part, seal = FALSE)
+ for(var/obj/item/part as anything in mod_parts)
+ retract(null, part)
+ if(active)
+ finish_activation(on = FALSE)
+ var/mob/old_wearer = wearer
+ unset_wearer()
+ old_wearer.temporarilyRemoveItemFromInventory(src)
+
+/obj/item/mod/control/proc/on_species_gain(datum/source, datum/species/new_species, datum/species/old_species)
+ SIGNAL_HANDLER
+
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part in all_parts)
+ if(!(part.slot_flags in new_species.no_equip) || is_type_in_list(new_species, part.species_exception))
+ continue
+ forceMove(drop_location())
+ return
+
+/obj/item/mod/control/proc/quick_module(mob/user)
+ if(!length(modules))
+ return
+ var/list/display_names = list()
+ var/list/items = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(module.module_type == MODULE_PASSIVE)
+ continue
+ display_names[module.name] = REF(module)
+ var/image/module_image = image(icon = module.icon, icon_state = module.icon_state)
+ if(module == selected_module)
+ module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected")
+ else if(module.active)
+ module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
+ if(!COOLDOWN_FINISHED(module, cooldown_timer))
+ module_image.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown"))
+ items += list(module.name = module_image)
+ if(!length(items))
+ return
+ var/radial_anchor = src
+ if(istype(user.loc, /obj/effect/dummy/phased_mob))
+ radial_anchor = get_turf(user.loc) //they're phased out via some module, anchor the radial on the turf so it may still display
+ var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = TRUE, tooltips = TRUE)
+ if(!pick)
+ return
+ var/module_reference = display_names[pick]
+ var/obj/item/mod/module/picked_module = locate(module_reference) in modules
+ if(!istype(picked_module))
+ return
+ picked_module.on_select()
+
+/obj/item/mod/control/proc/shock(mob/living/user)
+ if(!istype(user) || get_charge() < 1)
+ return FALSE
+ do_sparks(5, TRUE, src)
+ var/check_range = TRUE
+ return electrocute_mob(user, get_charge_source(), src, 0.7, check_range)
+
+/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user)
+ for(var/obj/item/mod/module/old_module as anything in modules)
+ if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
+ if(user)
+ balloon_alert(user, "[new_module] incompatible with [old_module]!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(is_type_in_list(new_module, theme.module_blacklist))
+ if(user)
+ balloon_alert(user, "[src] doesn't accept [new_module]!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ var/complexity_with_module = complexity
+ complexity_with_module += new_module.complexity
+ if(complexity_with_module > complexity_max)
+ if(user)
+ balloon_alert(user, "[new_module] would make [src] too complex!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ new_module.forceMove(src)
+ modules += new_module
+ complexity += new_module.complexity
+ new_module.mod = src
+ new_module.on_install()
+ if(wearer)
+ new_module.on_equip()
+
+ if(user)
+ balloon_alert(user, "[new_module] added")
+ playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+
+/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE)
+ modules -= old_module
+ complexity -= old_module.complexity
+ if(active)
+ old_module.on_suit_deactivation(deleting = deleting)
+ if(old_module.active)
+ old_module.on_deactivation(display_message = !deleting, deleting = deleting)
+ old_module.on_uninstall(deleting = deleting)
+ QDEL_LIST_ASSOC_VAL(old_module.pinned_to)
+ old_module.mod = null
+
+/// Intended for callbacks, don't use normally, just get wearer by itself.
+/obj/item/mod/control/proc/get_wearer()
+ return wearer
+
+/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card)
+ if(!allowed(user))
+ balloon_alert(user, "insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ req_access = card.access.Copy()
+ balloon_alert(user, "access updated")
+
+/obj/item/mod/control/proc/get_charge_source()
+ return core?.charge_source()
+
+/obj/item/mod/control/proc/get_charge()
+ return core?.charge_amount() || 0
+
+/obj/item/mod/control/proc/get_max_charge()
+ return core?.max_charge_amount() || 1 //avoid dividing by 0
+
+/obj/item/mod/control/proc/get_charge_percent()
+ return ROUND_UP((get_charge() / get_max_charge()) * 100)
+
+/obj/item/mod/control/proc/add_charge(amount)
+ return core?.add_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/subtract_charge(amount)
+ return core?.subtract_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/check_charge(amount)
+ return core?.check_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/update_charge_alert()
+ if(!wearer)
+ return
+ if(!core)
+ wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocore)
+ return
+ core.update_charge_alert()
+
+/obj/item/mod/control/proc/update_speed()
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(all_parts)
+ wearer?.update_equipment_speed_mods()
+
+/obj/item/mod/control/proc/power_off()
+ balloon_alert(wearer, "Нет энергии!")
+ toggle_activate(wearer, force_deactivate = TRUE)
+
+/obj/item/mod/control/proc/set_mod_color(new_color)
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
+ wearer?.regenerate_icons()
+
+/obj/item/mod/control/proc/set_mod_skin(new_skin)
+ if(active)
+ CRASH("[src] tried to set skin while active!")
+ skin = new_skin
+ var/list/used_skin = theme.skins[new_skin]
+ if(used_skin[CONTROL_LAYER])
+ alternate_worn_layer = used_skin[CONTROL_LAYER]
+ var/list/skin_updating = mod_parts + src
+ for(var/obj/item/part as anything in skin_updating)
+ part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ //part.mob_overlay_icon = used_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ part.icon_state = "[skin]-[part.base_icon_state]"
+ for(var/obj/item/clothing/part as anything in mod_parts)
+ var/used_category
+ if(part == helmet)
+ used_category = HELMET_FLAGS
+ if(part == chestplate)
+ used_category = CHESTPLATE_FLAGS
+ if(part == gauntlets)
+ used_category = GAUNTLETS_FLAGS
+ if(part == boots)
+ used_category = BOOTS_FLAGS
+ var/list/category = used_skin[used_category]
+ part.clothing_flags = category[UNSEALED_CLOTHING] || NONE
+ part.visor_flags = category[SEALED_CLOTHING] || NONE
+ part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE
+ part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE
+ part.flags_cover = category[UNSEALED_COVER] || NONE
+ part.visor_flags_cover = category[SEALED_COVER] || NONE
+ part.alternate_worn_layer = category[UNSEALED_LAYER]
+ mod_parts[part] = part.alternate_worn_layer
+ if(!category[CAN_OVERSLOT])
+ if(overslotting_parts[part])
+ var/obj/item/overslot = overslotting_parts[part]
+ overslot.forceMove(drop_location())
+ overslotting_parts -= part
+ continue
+ overslotting_parts |= part
+ wearer?.regenerate_icons()
+
+/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction)
+ SIGNAL_HANDLER
+
+ if(part.loc == src)
+ return
+ if(part == core)
+ core.uninstall()
+ update_charge_alert()
+ return
+ if(part.loc == wearer)
+ return
+ if(part in modules)
+ uninstall(part)
+ return
+ if(part in mod_parts)
+ if(!wearer)
+ part.forceMove(src)
+ return
+ retract(wearer, part)
+ if(active)
+ INVOKE_ASYNC(src, PROC_REF(toggle_activate), wearer, TRUE)
+
+/obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag)
+ SIGNAL_HANDLER
+
+ if(overslotting_parts[part])
+ var/obj/item/overslot = overslotting_parts[part]
+ overslot.forceMove(drop_location())
+ overslotting_parts[part] = null
+ if(QDELETED(src))
+ return
+ obj_destruction(damage_flag)
+
+/obj/item/mod/control/proc/on_part_deletion(obj/item/part)
+ SIGNAL_HANDLER
+
+ if(QDELETED(src))
+ return
+ qdel(src)
+
+/obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction)
+ SIGNAL_HANDLER
+
+ if(overslot != overslotting_parts[source])
+ return
+ overslotting_parts[source] = null
diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm
new file mode 100644
index 000000000000..4c9d16ef7b7d
--- /dev/null
+++ b/code/modules/mod/mod_core.dm
@@ -0,0 +1,357 @@
+/obj/item/mod/core
+ name = "MOD core"
+ desc = "A non-functional MOD core. Inform the admins if you see this."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "mod-core"
+ item_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
+ /// MOD unit we are powering.
+ var/obj/item/mod/control/mod
+
+/obj/item/mod/core/Destroy()
+ if(mod)
+ uninstall()
+ return ..()
+
+/obj/item/mod/core/proc/install(obj/item/mod/control/mod_unit)
+ mod = mod_unit
+ mod.core = src
+ forceMove(mod)
+
+/obj/item/mod/core/proc/uninstall()
+ mod.core = null
+ mod = null
+
+/obj/item/mod/core/proc/charge_source()
+ return
+
+/obj/item/mod/core/proc/charge_amount()
+ return 0
+
+/obj/item/mod/core/proc/max_charge_amount()
+ return 1
+
+/obj/item/mod/core/proc/add_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/subtract_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/check_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/update_charge_alert()
+ mod.wearer.clear_alert("mod_charge")
+
+/obj/item/mod/core/infinite
+ name = "MOD infinite core"
+ icon_state = "mod-core-infinite"
+ desc = "A fusion core using the rare Fixium to sustain enough energy for the lifetime of the MOD's user. \
+ This might be because of the slowly killing poison inside, but those are just rumors."
+
+/obj/item/mod/core/infinite/charge_source()
+ return src
+
+/obj/item/mod/core/infinite/charge_amount()
+ return INFINITY
+
+/obj/item/mod/core/infinite/max_charge_amount()
+ return INFINITY
+
+/obj/item/mod/core/infinite/add_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/infinite/subtract_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/infinite/check_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/standard
+ name = "MOD standard core"
+ icon_state = "mod-core-standard"
+ desc = "Growing in the most lush, fertile areas of the planet Sprout, there is a crystal known as the Heartbloom. \
+ These rare, organic piezoelectric crystals are of incredible cultural significance to the artist castes of the \
+ Ethereals, owing to their appearance; which is exactly similar to that of an Ethereal's heart.\n\
+ Which one you have in your suit is unclear, but either way, \
+ it's been repurposed to be an internal power source for a Modular Outerwear Device."
+ /// Installed cell.
+ var/obj/item/stock_parts/cell/cell
+
+/obj/item/mod/core/standard/Destroy()
+ if(cell)
+ QDEL_NULL(cell)
+ return ..()
+
+/obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit)
+ . = ..()
+ if(cell)
+ install_cell(cell)
+ RegisterSignal(mod, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(mod, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
+ RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(mod, COMSIG_MOD_WEARER_SET, PROC_REF(on_wearer_set))
+ if(mod.wearer)
+ on_wearer_set(mod, mod.wearer)
+
+/obj/item/mod/core/standard/uninstall()
+ if(!QDELETED(cell))
+ cell.forceMove(drop_location())
+ UnregisterSignal(mod, list(COMSIG_PARENT_EXAMINE, COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_ATTACKBY, COMSIG_MOD_WEARER_SET))
+ if(mod.wearer)
+ on_wearer_unset(mod, mod.wearer)
+ return ..()
+
+/obj/item/mod/core/standard/charge_source()
+ return cell
+
+/obj/item/mod/core/standard/charge_amount()
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ return charge_source?.charge || 0
+
+/obj/item/mod/core/standard/max_charge_amount(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ return charge_source?.maxcharge || 1
+
+/obj/item/mod/core/standard/add_charge(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ return charge_source.give(amount)
+
+/obj/item/mod/core/standard/subtract_charge(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ return charge_source.use(amount, TRUE)
+
+/obj/item/mod/core/standard/check_charge(amount)
+ return charge_amount() >= amount
+
+/obj/item/mod/core/standard/update_charge_alert()
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocell)
+ return
+ var/remaining_cell = charge_amount() / max_charge_amount()
+ switch(remaining_cell)
+ if(0.75 to INFINITY)
+ mod.wearer.clear_alert("mod_charge")
+ if(0.5 to 0.75)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 1)
+ if(0.25 to 0.5)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 2)
+ if(0.01 to 0.25)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 3)
+ else
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/emptycell)
+
+/obj/item/mod/core/standard/proc/install_cell(new_cell)
+ cell = new_cell
+ cell.forceMove(src)
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+
+/obj/item/mod/core/standard/proc/uninstall_cell()
+ if(!cell)
+ return
+ cell = null
+ UnregisterSignal(src, COMSIG_ATOM_EXITED)
+
+/obj/item/mod/core/standard/proc/on_exit(datum/source, obj/item/stock_parts/cell, direction)
+ SIGNAL_HANDLER
+
+ if(!istype(cell) || cell.loc == src)
+ return
+ uninstall_cell()
+
+/obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text)
+ SIGNAL_HANDLER
+
+ if(!mod.open)
+ return
+ examine_text += cell ? "You could remove the cell with an empty hand." : "You could use a cell on it to install one."
+
+/obj/item/mod/core/standard/proc/on_attack_hand(datum/source, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(mod.seconds_electrified && charge_amount() && mod.shock(user))
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+ if(mod.open && mod.loc == user)
+ INVOKE_ASYNC(src, PROC_REF(mod_uninstall_cell), user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+ return NONE
+
+/obj/item/mod/core/standard/proc/mod_uninstall_cell(mob/living/user)
+ if(!cell)
+ mod.balloon_alert(user, "no cell!")
+ return
+ mod.balloon_alert(user, "removing cell...")
+ if(!do_after(user, 1.5 SECONDS, target = mod))
+ mod.balloon_alert(user, "interrupted!")
+ return
+ mod.balloon_alert(user, "cell removed")
+ playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ var/obj/item/cell_to_move = cell
+ cell_to_move.forceMove(drop_location())
+ user.put_in_hands(cell_to_move)
+ mod.update_charge_alert()
+
+/obj/item/mod/core/standard/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
+ SIGNAL_HANDLER
+
+ if(istype(attacking_item, /obj/item/stock_parts/cell))
+ if(!mod.open)
+ mod.balloon_alert(user, "open the cover first!")
+ playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return NONE
+ if(cell)
+ mod.balloon_alert(user, "cell already installed!")
+ playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return COMPONENT_NO_AFTERATTACK
+ install_cell(attacking_item)
+ mod.balloon_alert(user, "cell installed")
+ playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ mod.update_charge_alert()
+ return COMPONENT_NO_AFTERATTACK
+ return NONE
+
+/obj/item/mod/core/standard/proc/on_wearer_set(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ RegisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
+ RegisterSignal(mod, COMSIG_MOD_WEARER_UNSET, PROC_REF(on_wearer_unset))
+
+/obj/item/mod/core/standard/proc/on_wearer_unset(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT)
+ UnregisterSignal(mod, COMSIG_MOD_WEARER_UNSET)
+
+/obj/item/mod/core/standard/proc/on_borg_charge(datum/source, amount)
+ SIGNAL_HANDLER
+
+ add_charge(amount)
+ mod.update_charge_alert()
+
+/obj/item/mod/core/ethereal
+ name = "MOD ethereal core"
+ icon_state = "mod-core-ethereal"
+ desc = "A reverse engineered core of a Modular Outerwear Device. Using natural liquid electricity from Ethereals, \
+ preventing the need to use external sources to convert electric charge."
+ /// A modifier to all charge we use, ethereals don't need to spend as much energy as normal suits.
+ var/charge_modifier = 0.1
+
+/obj/item/mod/core/ethereal/charge_source()
+ var/obj/item/organ/stomach/ethereal/ethereal_stomach = mod.wearer.getorganslot(ORGAN_SLOT_STOMACH)
+ if(!istype(ethereal_stomach))
+ return
+ return ethereal_stomach
+
+/obj/item/mod/core/ethereal/charge_amount()
+ var/obj/item/organ/stomach/ethereal/charge_source = charge_source()
+ return charge_source?.crystal_charge || ELZUOSE_CHARGE_NONE
+
+/obj/item/mod/core/ethereal/max_charge_amount()
+ return ELZUOSE_CHARGE_FULL
+
+/obj/item/mod/core/ethereal/add_charge(amount)
+ var/obj/item/organ/stomach/ethereal/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ charge_source.adjust_charge(amount*charge_modifier)
+ return TRUE
+
+/obj/item/mod/core/ethereal/subtract_charge(amount)
+ var/obj/item/organ/stomach/ethereal/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ charge_source.adjust_charge(-amount*charge_modifier)
+ return TRUE
+
+/obj/item/mod/core/ethereal/check_charge(amount)
+ return charge_amount() >= amount*charge_modifier
+
+/obj/item/mod/core/ethereal/update_charge_alert()
+ var/obj/item/organ/stomach/ethereal/charge_source = charge_source()
+ if(charge_source)
+ mod.wearer.clear_alert("mod_charge")
+ return
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocell)
+
+/obj/item/mod/core/plasma
+ name = "MOD plasma core"
+ icon_state = "mod-core-plasma"
+ desc = "Nanotrasen's attempt at capitalizing on their plasma research. These plasma cores are refueled \
+ through plasma ore, allowing for easy continued use by their mining squads."
+ /// How much charge we can store.
+ var/maxcharge = 10000
+ /// How much charge we are currently storing.
+ var/charge = 10000
+ /// Associated list of charge sources and how much they charge, only stacks allowed.
+ var/list/charger_list = list(/obj/item/stack/ore/plasma = 1500, /obj/item/stack/sheet/mineral/plasma = 2000)
+
+/obj/item/mod/core/plasma/install(obj/item/mod/control/mod_unit)
+ . = ..()
+ RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby))
+
+/obj/item/mod/core/plasma/uninstall()
+ UnregisterSignal(mod, COMSIG_PARENT_ATTACKBY)
+ return ..()
+
+/obj/item/mod/core/plasma/attackby(obj/item/attacking_item, mob/user, params)
+ if(charge_plasma(attacking_item, user))
+ return TRUE
+ return ..()
+
+/obj/item/mod/core/plasma/charge_source()
+ return src
+
+/obj/item/mod/core/plasma/charge_amount()
+ return charge
+
+/obj/item/mod/core/plasma/max_charge_amount()
+ return maxcharge
+
+/obj/item/mod/core/plasma/add_charge(amount)
+ charge = min(maxcharge, charge + amount)
+ return TRUE
+
+/obj/item/mod/core/plasma/subtract_charge(amount)
+ charge = max(0, charge - amount)
+ return TRUE
+
+/obj/item/mod/core/plasma/check_charge(amount)
+ return charge_amount() >= amount
+
+/obj/item/mod/core/plasma/update_charge_alert()
+ var/remaining_plasma = charge_amount() / max_charge_amount()
+ switch(remaining_plasma)
+ if(0.75 to INFINITY)
+ mod.wearer.clear_alert("mod_charge")
+ if(0.5 to 0.75)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 1)
+ if(0.25 to 0.5)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 2)
+ if(0.01 to 0.25)
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell/plasma, 3)
+ else
+ mod.wearer.throw_alert("mod_charge", /atom/movable/screen/alert/emptycell/plasma)
+
+/obj/item/mod/core/plasma/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
+ SIGNAL_HANDLER
+
+ if(charge_plasma(attacking_item, user))
+ return COMPONENT_NO_AFTERATTACK
+ return NONE
+
+/obj/item/mod/core/plasma/proc/charge_plasma(obj/item/stack/plasma, mob/user)
+ var/charge_given = is_type_in_list(plasma, charger_list, zebra = TRUE)
+ if(!charge_given)
+ return FALSE
+ var/uses_needed = min(plasma.amount, ROUND_UP((max_charge_amount() - charge_amount()) / charge_given))
+ if(!plasma.use(uses_needed))
+ return FALSE
+ add_charge(uses_needed * charge_given)
+ balloon_alert(user, "core refueled")
+ return TRUE
diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm
new file mode 100644
index 000000000000..aead577224bc
--- /dev/null
+++ b/code/modules/mod/mod_paint.dm
@@ -0,0 +1,192 @@
+#define MODPAINT_MAX_COLOR_VALUE 1.25
+#define MODPAINT_MIN_COLOR_VALUE 0
+#define MODPAINT_MAX_SECTION_COLORS 2
+#define MODPAINT_MIN_SECTION_COLORS 0.25
+#define MODPAINT_MAX_OVERALL_COLORS 4
+#define MODPAINT_MIN_OVERALL_COLORS 1.5
+
+/obj/item/mod/paint
+ name = "MOD paint kit"
+ desc = "This kit will repaint your MODsuit to something unique."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "paintkit"
+ var/obj/item/mod/control/editing_mod
+ var/atom/movable/screen/map_view/proxy_view
+ var/list/current_color
+
+/obj/item/mod/paint/Initialize(mapload)
+ . = ..()
+ current_color = color_matrix_identity()
+
+/obj/item/mod/paint/examine(mob/user)
+ . = ..()
+ . += span_notice("Left-click a MODsuit to change skin.")
+ //. += span_notice("Right-click a MODsuit to recolor.")
+
+/obj/item/mod/paint/pre_attack(atom/attacked_atom, mob/living/user, params)
+ if(!istype(attacked_atom, /obj/item/mod/control))
+ return ..()
+ var/obj/item/mod/control/mod = attacked_atom
+ if(mod.active || mod.activating)
+ balloon_alert(user, "suit is active!")
+ return TRUE
+ paint_skin(mod, user)
+
+/*obj/item/mod/paint/pre_attack_secondary(atom/attacked_atom, mob/living/user, params)
+ if(!istype(attacked_atom, /obj/item/mod/control))
+ return .()
+ var/obj/item/mod/control/mod = attacked_atom
+ if(mod.active || mod.activating)
+ balloon_alert(user, "suit is active!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(editing_mod)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ editing_mod = mod
+ proxy_view = new()
+ proxy_view.generate_view("color_matrix_proxy_[REF(user.client)]")
+
+ proxy_view.appearance = editing_mod.appearance
+ proxy_view.color = null
+ proxy_view.display_to(user)
+ ui_interact(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN*/
+
+/obj/item/mod/paint/ui_interact(mob/user, datum/tgui/ui)
+ if(!editing_mod)
+ return
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "MODpaint", name)
+ ui.open()
+
+/obj/item/mod/paint/ui_host()
+ return editing_mod
+
+/obj/item/mod/paint/ui_close(mob/user)
+ . = ..()
+ editing_mod = null
+ QDEL_NULL(proxy_view)
+ current_color = color_matrix_identity()
+
+/obj/item/mod/paint/ui_status(mob/user)
+ if(check_menu(editing_mod, user))
+ return ..()
+ return UI_CLOSE
+
+/obj/item/mod/paint/ui_static_data(mob/user)
+ var/list/data = list()
+ data["mapRef"] = proxy_view.assigned_map
+ return data
+
+/obj/item/mod/paint/ui_data(mob/user)
+ var/list/data = list()
+ data["currentColor"] = current_color
+ return data
+
+/obj/item/mod/paint/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("transition_color")
+ current_color = params["color"]
+ animate(proxy_view, time = 0.5 SECONDS, color = current_color)
+ if("confirm")
+ if(length(current_color) != 20) //20 is the length of a matrix identity list
+ return
+ for(var/color_value in current_color)
+ if(isnum(color_value))
+ continue
+ return
+ var/total_color_value = 0
+ var/list/total_colors = current_color.Copy()
+ total_colors.Cut(13, length(total_colors)) // 13 to 20 are just a and c, dont want to count them
+ var/red_value = current_color[1] + current_color[5] + current_color[9] //rr + gr + br
+ var/green_value = current_color[2] + current_color[6] + current_color[10] //rg + gg + bg
+ var/blue_value = current_color[3] + current_color[7] + current_color[11] //rb + gb + bb
+ if(red_value > MODPAINT_MAX_SECTION_COLORS)
+ balloon_alert(usr, "total red too high! ([red_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
+ return
+ else if(red_value < MODPAINT_MIN_SECTION_COLORS)
+ balloon_alert(usr, "total red too low! ([red_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
+ return
+ if(green_value > MODPAINT_MAX_SECTION_COLORS)
+ balloon_alert(usr, "total green too high! ([green_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
+ return
+ else if(green_value < MODPAINT_MIN_SECTION_COLORS)
+ balloon_alert(usr, "total green too low! ([green_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
+ return
+ if(blue_value > MODPAINT_MAX_SECTION_COLORS)
+ balloon_alert(usr, "total blue too high! ([blue_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
+ return
+ else if(blue_value < MODPAINT_MIN_SECTION_COLORS)
+ balloon_alert(usr, "total blue too low! ([blue_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
+ return
+ for(var/color_value in total_colors)
+ total_color_value += color_value
+ if(color_value > MODPAINT_MAX_COLOR_VALUE)
+ balloon_alert(usr, "one of colors too high! ([color_value*100]%/[MODPAINT_MAX_COLOR_VALUE*100]%")
+ return
+ else if(color_value < MODPAINT_MIN_COLOR_VALUE)
+ balloon_alert(usr, "one of colors too low! ([color_value*100]%/[MODPAINT_MIN_COLOR_VALUE*100]%")
+ return
+ if(total_color_value > MODPAINT_MAX_OVERALL_COLORS)
+ balloon_alert(usr, "total colors too high! ([total_color_value*100]%/[MODPAINT_MAX_OVERALL_COLORS*100]%)")
+ return
+ else if(total_color_value < MODPAINT_MIN_OVERALL_COLORS)
+ balloon_alert(usr, "total colors too low! ([total_color_value*100]%/[MODPAINT_MIN_OVERALL_COLORS*100]%)")
+ return
+ editing_mod.set_mod_color(current_color)
+ SStgui.close_uis(src)
+
+/obj/item/mod/paint/proc/paint_skin(obj/item/mod/control/mod, mob/user)
+ if(length(mod.theme.skins) <= 1)
+ balloon_alert(user, "no alternate skins!")
+ return
+ var/list/skins = list()
+ for(var/mod_skin in mod.theme.skins)
+ skins[mod_skin] = image(icon = mod.icon, icon_state = "[mod_skin]-control")
+ var/pick = show_radial_menu(user, mod, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), mod, user), require_near = TRUE)
+ if(!pick)
+ balloon_alert(user, "no skin picked!")
+ return
+ mod.set_mod_skin(pick)
+
+/obj/item/mod/paint/proc/check_menu(obj/item/mod/control/mod, mob/user)
+ if(user.incapacitated() || !user.is_holding(src) || !mod || mod.active || mod.activating)
+ return FALSE
+ return TRUE
+
+#undef MODPAINT_MAX_COLOR_VALUE
+#undef MODPAINT_MIN_COLOR_VALUE
+#undef MODPAINT_MAX_SECTION_COLORS
+#undef MODPAINT_MIN_SECTION_COLORS
+#undef MODPAINT_MAX_OVERALL_COLORS
+#undef MODPAINT_MIN_OVERALL_COLORS
+
+/obj/item/mod/skin_applier
+ name = "MOD skin applier"
+ desc = "This one-use skin applier will add a skin to MODsuits of a specific type."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "skinapplier"
+ var/skin = "civilian"
+ var/compatible_theme = /datum/mod_theme
+
+/obj/item/mod/skin_applier/Initialize(mapload)
+ . = ..()
+ name = "MOD [skin] skin applier"
+
+/obj/item/mod/skin_applier/pre_attack(atom/attacked_atom, mob/living/user, params)
+ if(!istype(attacked_atom, /obj/item/mod/control))
+ return ..()
+ var/obj/item/mod/control/mod = attacked_atom
+ if(mod.active || mod.activating)
+ balloon_alert(user, "suit is active!")
+ return TRUE
+ if(!istype(mod.theme, compatible_theme))
+ balloon_alert(user, "incompatible theme!")
+ return TRUE
+ mod.set_mod_skin(skin)
+ balloon_alert(user, "skin applied")
+ qdel(src)
+ return TRUE
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
new file mode 100644
index 000000000000..2e6325df919f
--- /dev/null
+++ b/code/modules/mod/mod_theme.dm
@@ -0,0 +1,1154 @@
+/// Global proc that sets up all MOD themes as singletons in a list and returns it.
+/proc/setup_mod_themes()
+ . = list()
+ for(var/path in typesof(/datum/mod_theme))
+ var/datum/mod_theme/new_theme = new path()
+ .[path] = new_theme
+
+/// MODsuit theme, instanced once and then used by MODsuits to grab various statistics.
+/datum/mod_theme
+ /// Theme name for the MOD.
+ var/name = "standard"
+ /// Description added to the MOD.
+ var/desc = "A MOD suit. Placeholder Desc"
+ /// Extended description on examine_more
+ var/extended_desc = "Placeholder Desc"
+ /// Default skin of the MOD.
+ var/default_skin = "standard"
+ /// The slot this mod theme fits on
+ var/slot_flags = ITEM_SLOT_BACK
+ /// Armor shared across the MOD parts.
+ var/armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 100, "fire" = 25, "acid" = 25)
+ /// Resistance flags shared across the MOD parts.
+ var/resistance_flags = NONE
+ /// Atom flags shared across the MOD parts.
+ var/atom_flags = NONE
+ /// Max heat protection shared across the MOD parts.
+ var/max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
+ /// Max cold protection shared across the MOD parts.
+ var/min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ /// Siemens shared across the MOD parts.
+ var/siemens_coefficient = 0.5
+ /// How much modules can the MOD carry without malfunctioning.
+ var/complexity_max = DEFAULT_MAX_COMPLEXITY
+ /// How much battery power the MOD uses by just being on
+ var/charge_drain = DEFAULT_CHARGE_DRAIN
+ /// Slowdown of the MOD when not active.
+ var/slowdown_inactive = 1.25
+ /// Slowdown of the MOD when active.
+ var/slowdown_active = 0.75
+ /// Theme used by the MOD TGUI.
+ var/ui_theme = "ntos"
+ /// List of inbuilt modules. These are different from the pre-equipped suits, you should mainly use these for unremovable modules with 0 complexity.
+ var/list/inbuilt_modules = list()
+ /// Modules blacklisted from the MOD.
+ var/list/module_blacklist = list()
+ /// Allowed items in the chestplate's suit storage.
+ var/list/allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ )
+ /// List of skins with their appropriate clothing flags.
+ var/list/skins = list(
+ "standard" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "civilian" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/engineering
+ name = "engineering"
+ default_skin = "engineering"
+ armor = list("melee" = 10, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 25)
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1.5
+ slowdown_active = 1
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/construction/rcd,
+ /obj/item/storage/bag/construction,
+ )
+ skins = list(
+ "engineering" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/atmospheric
+ name = "atmospheric"
+ default_skin = "atmospheric"
+ armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 15, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 75)
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ slowdown_inactive = 1.5
+ slowdown_active = 1
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/analyzer,
+ /obj/item/t_scanner,
+ /obj/item/pipe_dispenser,
+ )
+ skins = list(
+ "atmospheric" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/advanced
+ name = "advanced"
+ default_skin = "advanced"
+ armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 15, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 90)
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.5
+ inbuilt_modules = list(/obj/item/mod/module/magboot/advanced)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/analyzer,
+ /obj/item/t_scanner,
+ /obj/item/pipe_dispenser,
+ /obj/item/construction/rcd,
+ /obj/item/storage/bag/construction,
+ /obj/item/melee/classic_baton/telescopic,
+ )
+ skins = list(
+ "advanced" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/mining
+ name = "mining"
+ default_skin = "mining"
+ armor = list("melee" = 15, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 75)
+ resistance_flags = FIRE_PROOF|LAVA_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 5
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/resonator,
+ /obj/item/mining_scanner,
+ /obj/item/t_scanner/adv_mining_scanner,
+ /obj/item/pickaxe,
+ /obj/item/kinetic_crusher,
+ /obj/item/stack/ore/plasma,
+ /obj/item/storage/bag/ore,
+ )
+ inbuilt_modules = list()
+ skins = list(
+ "mining" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "asteroid" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/loader
+ name = "loader"
+ default_skin = "loader"
+ armor = list("melee" = 15, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 10, "fire" = 25, "acid" = 25)
+ max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
+ siemens_coefficient = 0.25
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 5
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/paper
+ )
+ inbuilt_modules = list(/obj/item/mod/module/clamp/loader, /obj/item/mod/module/magnet)
+ skins = list(
+ "loader" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
+ SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ SEALED_CLOTHING = THICKMATERIAL,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ SEALED_CLOTHING = THICKMATERIAL,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/medical
+ name = "medical"
+ default_skin = "medical"
+ armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 100, "fire" = 60, "acid" = 75)
+ charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
+ slowdown_inactive = 1
+ slowdown_active = 0.5
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/healthanalyzer,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/reagent_containers/pill,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/stack/medical,
+ /obj/item/sensor_device,
+ /obj/item/storage/pill_bottle,
+ /obj/item/storage/bag/chemistry,
+ /obj/item/storage/bag/bio,
+ )
+ skins = list(
+ "medical" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "corpsman" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/rescue
+ name = "rescue"
+ default_skin = "rescue"
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 5, "energy" = 5, "bomb" = 10, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/healthanalyzer,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/reagent_containers/pill,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/stack/medical,
+ /obj/item/sensor_device,
+ /obj/item/storage/pill_bottle,
+ /obj/item/storage/bag/chemistry,
+ /obj/item/storage/bag/bio,
+ /obj/item/melee/classic_baton/telescopic,
+ )
+ skins = list(
+ "rescue" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/research
+ name = "research"
+ default_skin = "research"
+ armor = list("melee" = 20, "bullet" = 15, "laser" = 5, "energy" = 5, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ slowdown_inactive = 1.75
+ slowdown_active = 1.25
+ inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/analyzer,
+ /obj/item/dnainjector,
+ /obj/item/storage/bag/bio,
+ /obj/item/melee/classic_baton/telescopic,
+ )
+ skins = list(
+ "research" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/security
+ name = "security"
+ default_skin = "security"
+ armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 25, "bio" = 100, "fire" = 75, "acid" = 75)
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ slowdown_inactive = 1
+ slowdown_active = 0.5
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ )
+ skins = list(
+ "security" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/safeguard
+ name = "safeguard"
+ default_skin = "safeguard"
+ armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 40, "bio" = 100, "fire" = 100, "acid" = 95)
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ )
+ skins = list(
+ "safeguard" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/magnate
+ name = "magnate"
+ default_skin = "magnate"
+ armor = list("melee" = 20, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ )
+ skins = list(
+ "magnate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/syndicate
+ name = "syndicate"
+ default_skin = "syndicate"
+ armor = list("melee" = 15, "bullet" = 20, "laser" = 15, "energy" = 15, "bomb" = 35, "bio" = 100, "fire" = 50, "acid" = 90)
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.5
+ ui_theme = "syndicate"
+ inbuilt_modules = list(/obj/item/mod/module/armor_booster)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/transforming/energy/sword,
+ /obj/item/shield/energy,
+ )
+ skins = list(
+ "syndicate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/elite
+ name = "elite"
+ default_skin = "elite"
+ armor = list("melee" = 35, "bullet" = 30, "laser" = 35, "energy" = 35, "bomb" = 55, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.5
+ ui_theme = "syndicate"
+ inbuilt_modules = list(/obj/item/mod/module/armor_booster)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/transforming/energy/sword,
+ /obj/item/shield/energy,
+ )
+ skins = list(
+ "elite" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/ninja
+ name = "ninja"
+ default_skin = "ninja"
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 20, "energy" = 30, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = LAVA_PROOF|FIRE_PROOF|ACID_PROOF
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ ui_theme = "hackerman"
+ inbuilt_modules = list(/obj/item/mod/module/welding/camera_vision, /obj/item/mod/module/hacker, /obj/item/mod/module/weapon_recall, /obj/item/mod/module/adrenaline_boost, /obj/item/mod/module/energy_net)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/gun,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/melee/baton,
+ /obj/item/restraints/handcuffs,
+ )
+ skins = list(
+ "ninja" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
+ SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/prototype
+ name = "prototype"
+ default_skin = "prototype"
+ armor = list("melee" = 20, "bullet" = 5, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 75)
+ resistance_flags = FIRE_PROOF
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ slowdown_inactive = 2
+ slowdown_active = 1.5
+ ui_theme = "hackerman"
+ //inbuilt_modules = list(/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype)
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/analyzer,
+ /obj/item/t_scanner,
+ /obj/item/pipe_dispenser,
+ /obj/item/construction/rcd,
+ )
+ skins = list(
+ "prototype" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/responsory
+ name = "responsory"
+ default_skin = "responsory"
+ armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 90)
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ )
+ skins = list(
+ "responsory" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "inquisitory" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/apocryphal
+ name = "apocryphal"
+ default_skin = "apocryphal"
+ armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 60, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 10
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/transforming/energy/sword,
+ /obj/item/shield/energy,
+ )
+ skins = list(
+ "apocryphal" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
+ SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/corporate
+ name = "corporate"
+ default_skin = "corporate"
+ armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/melee/baton,
+ )
+ skins = list(
+ "corporate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/chrono
+ name = "chrono"
+ default_skin = "chrono"
+ armor = list("melee" = 60, "bullet" = 60, "laser" = 60, "energy" = 60, "bomb" = 30, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 10
+ slowdown_inactive = 0
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/restraints/handcuffs,
+ )
+ skins = list(
+ "chrono" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = NECK_LAYER,
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/debug
+ name = "debug"
+ default_skin = "debug"
+ armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = FIRE_PROOF|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = 50
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/gun,
+ )
+ skins = list(
+ "debug" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/administrative
+ name = "administrative"
+ default_skin = "debug"
+ armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "fire" = 100, "acid" = 100)
+ resistance_flags = INDESTRUCTIBLE|LAVA_PROOF|FIRE_PROOF|UNACIDABLE|ACID_PROOF
+ atom_flags = PREVENT_CONTENTS_EXPLOSION_1
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ complexity_max = 1000
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0
+ siemens_coefficient = 0
+ slowdown_inactive = 0
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/flashlight,
+ /obj/item/tank/internals,
+ /obj/item/gun,
+ )
+ skins = list(
+ "debug" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCKS_SHOVE_KNOCKDOWN,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
new file mode 100644
index 000000000000..6cd33197e423
--- /dev/null
+++ b/code/modules/mod/mod_types.dm
@@ -0,0 +1,331 @@
+/obj/item/mod/control/pre_equipped
+ /// The skin we apply to the suit, defaults to the default_skin of the theme.
+ var/applied_skin
+ /// The MOD core we apply to the suit.
+ var/applied_core = /obj/item/mod/core/standard
+ /// The cell we apply to the core. Only applies to standard core suits.
+ var/applied_cell = /obj/item/stock_parts/cell/high
+
+/obj/item/mod/control/pre_equipped/Initialize(mapload, new_theme, new_skin, new_core)
+ new_skin = applied_skin
+ new_core = new applied_core(src)
+ if(istype(new_core, /obj/item/mod/core/standard))
+ var/obj/item/mod/core/standard/cell_core = new_core
+ cell_core.cell = new applied_cell()
+ return ..()
+
+/obj/item/mod/control/pre_equipped/standard
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ )
+
+/obj/item/mod/control/pre_equipped/engineering
+ theme = /datum/mod_theme/engineering
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/magboot,
+ )
+
+/obj/item/mod/control/pre_equipped/atmospheric
+ theme = /datum/mod_theme/atmospheric
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/t_ray,
+ )
+
+/obj/item/mod/control/pre_equipped/advanced
+ theme = /datum/mod_theme/advanced
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/jetpack,
+ )
+
+/obj/item/mod/control/pre_equipped/loader
+ theme = /datum/mod_theme/loader
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/paper_dispenser,
+ /obj/item/mod/module/stamp,
+ )
+
+/obj/item/mod/control/pre_equipped/mining
+ theme = /datum/mod_theme/mining
+ applied_core = /obj/item/mod/core/plasma
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/gps,
+ /obj/item/mod/module/orebag,
+ /obj/item/mod/module/clamp,
+ /obj/item/mod/module/drill,
+ )
+
+/obj/item/mod/control/pre_equipped/medical
+ theme = /datum/mod_theme/medical
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/health_analyzer,
+ /obj/item/mod/module/quick_carry,
+ )
+
+/obj/item/mod/control/pre_equipped/rescue
+ theme = /datum/mod_theme/rescue
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/health_analyzer,
+ /obj/item/mod/module/injector,
+ )
+
+/obj/item/mod/control/pre_equipped/research
+ theme = /datum/mod_theme/research
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ //obj/item/mod/module/circuit,
+ /obj/item/mod/module/t_ray,
+ )
+
+/obj/item/mod/control/pre_equipped/security
+ theme = /datum/mod_theme/security
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/flashlight,
+ )
+
+/obj/item/mod/control/pre_equipped/safeguard
+ theme = /datum/mod_theme/safeguard
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/megaphone,
+ )
+
+/obj/item/mod/control/pre_equipped/magnate
+ theme = /datum/mod_theme/magnate
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/hat_stabilizer,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/traitor
+ theme = /datum/mod_theme/syndicate
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/dna_lock,
+ )
+
+/obj/item/mod/control/pre_equipped/traitor_elite
+ theme = /datum/mod_theme/elite
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ initial_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/dna_lock,
+ )
+
+/obj/item/mod/control/pre_equipped/nuclear
+ theme = /datum/mod_theme/syndicate
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ req_access = list(ACCESS_SYNDICATE)
+ initial_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ )
+
+/obj/item/mod/control/pre_equipped/elite
+ theme = /datum/mod_theme/elite
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ req_access = list(ACCESS_SYNDICATE)
+ initial_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ )
+
+/obj/item/mod/control/pre_equipped/elite/flamethrower
+ initial_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/thermal_regulator,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/flamethrower,
+ )
+
+/obj/item/mod/control/pre_equipped/ninja
+ theme = /datum/mod_theme/ninja
+ applied_cell = /obj/item/stock_parts/cell/super
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/noslip,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/stealth/ninja,
+ /obj/item/mod/module/dispenser/ninja,
+ /obj/item/mod/module/dna_lock/reinforced,
+ /obj/item/mod/module/emp_shield/pulse,
+ )
+
+/obj/item/mod/control/pre_equipped/prototype
+ theme = /datum/mod_theme/prototype
+ req_access = list(ACCESS_AWAY_GENERAL)
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/tether,
+ )
+
+/obj/item/mod/control/pre_equipped/responsory
+ theme = /datum/mod_theme/responsory
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ req_access = list(ACCESS_CENT_GENERAL)
+ initial_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/flashlight,
+ )
+ /// The insignia type, insignias show what sort of member of the ERT you're dealing with.
+ var/insignia_type = /obj/item/mod/module/insignia
+ /// Additional module we add, as a treat.
+ var/additional_module = /obj/item/mod/module
+
+/obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin, new_core)
+ initial_modules.Insert(1, insignia_type)
+ initial_modules.Add(additional_module)
+ return ..()
+
+/obj/item/mod/control/pre_equipped/responsory/commander
+ insignia_type = /obj/item/mod/module/insignia/commander
+ additional_module = /obj/item/mod/module/power_kick
+
+/obj/item/mod/control/pre_equipped/responsory/security
+ insignia_type = /obj/item/mod/module/insignia/security
+ additional_module = /obj/item/mod/module/megaphone
+
+/obj/item/mod/control/pre_equipped/responsory/engineer
+ insignia_type = /obj/item/mod/module/insignia/engineer
+ additional_module = /obj/item/mod/module/magboot
+
+/obj/item/mod/control/pre_equipped/responsory/medic
+ insignia_type = /obj/item/mod/module/insignia/medic
+ additional_module = /obj/item/mod/module/quick_carry
+
+/obj/item/mod/control/pre_equipped/responsory/janitor
+ insignia_type = /obj/item/mod/module/insignia/janitor
+ additional_module = /obj/item/mod/module/clamp
+
+/obj/item/mod/control/pre_equipped/responsory/chaplain
+ insignia_type = /obj/item/mod/module/insignia/chaplain
+ additional_module = /obj/item/mod/module/injector
+
+/obj/item/mod/control/pre_equipped/apocryphal
+ theme = /datum/mod_theme/apocryphal
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ req_access = list(ACCESS_CENT_SPECOPS)
+ initial_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/emp_shield/advanced,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/jetpack,
+ )
+
+/obj/item/mod/control/pre_equipped/corporate
+ theme = /datum/mod_theme/corporate
+ applied_core = /obj/item/mod/core/infinite
+ req_access = list(ACCESS_CENT_SPECOPS)
+ initial_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/hat_stabilizer,
+ /obj/item/mod/module/magnetic_harness,
+ /obj/item/mod/module/emp_shield/advanced,
+ )
+
+/*obj/item/mod/control/pre_equipped/chrono
+ theme = /datum/mod_theme/chrono
+ applied_core = /obj/item/mod/core/infinite
+ initial_modules = list(
+ /obj/item/mod/module/eradication_lock,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/timeline_jumper,
+ /obj/item/mod/module/timestopper,
+ /obj/item/mod/module/rewinder,
+ /obj/item/mod/module/tem,
+ /obj/item/mod/module/anomaly_locked/kinesis/plus,
+ )*/
+
+/obj/item/mod/control/pre_equipped/debug
+ theme = /datum/mod_theme/debug
+ applied_core = /obj/item/mod/core/infinite
+ initial_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/tether,
+ /obj/item/mod/module/injector,
+ )
+
+/obj/item/mod/control/pre_equipped/administrative
+ theme = /datum/mod_theme/administrative
+ applied_core = /obj/item/mod/core/infinite
+ initial_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/emp_shield/advanced,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/stealth/ninja,
+ /obj/item/mod/module/quick_carry/advanced,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/jetpack/advanced,
+ //obj/item/mod/module/anomaly_locked/kinesis/plus,
+ )
+
+//these exist for the prefs menu
+/obj/item/mod/control/pre_equipped/empty
+
+/obj/item/mod/control/pre_equipped/empty/syndicate
+ theme = /datum/mod_theme/syndicate
+
+/obj/item/mod/control/pre_equipped/empty/elite
+ theme = /datum/mod_theme/elite
+
+/obj/item/mod/control/pre_equipped/empty/ninja
+ theme = /datum/mod_theme/ninja
+
+INITIALIZE_IMMEDIATE(/obj/item/mod/control/pre_equipped/empty)
diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm
new file mode 100644
index 000000000000..3bfa930dea7e
--- /dev/null
+++ b/code/modules/mod/mod_ui.dm
@@ -0,0 +1,86 @@
+/obj/item/mod/control/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "MODsuit", name)
+ ui.open()
+
+/obj/item/mod/control/ui_data(mob/user)
+ var/data = list()
+ data["interface_break"] = interface_break
+ data["malfunctioning"] = malfunctioning
+ data["open"] = open
+ data["active"] = active
+ data["locked"] = locked
+ data["complexity"] = complexity
+ data["selected_module"] = selected_module?.name
+ data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant"
+ data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job"
+ //data[JOB_AI] = ai?.name
+ data["core"] = core?.name
+ data["charge"] = get_charge_percent()
+ data["modules"] = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ var/list/module_data = list(
+ "module_name" = module.name,
+ "description" = module.desc,
+ "module_type" = module.module_type,
+ "module_active" = module.active,
+ "pinned" = module.pinned_to[user],
+ "idle_power" = module.idle_power_cost,
+ "active_power" = module.active_power_cost,
+ "use_power" = module.use_power_cost,
+ "module_complexity" = module.complexity,
+ "cooldown_time" = module.cooldown_time,
+ "cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS),
+ "id" = module.tgui_id,
+ "ref" = REF(module),
+ "configuration_data" = module.get_configuration()
+ )
+ module_data += module.add_ui_data()
+ data["modules"] += list(module_data)
+ return data
+
+/obj/item/mod/control/ui_static_data(mob/user)
+ var/data = list()
+ data["ui_theme"] = ui_theme
+ data["control"] = name
+ data["complexity_max"] = complexity_max
+ data["helmet"] = helmet?.name
+ data["chestplate"] = chestplate?.name
+ data["gauntlets"] = gauntlets?.name
+ data["boots"] = boots?.name
+ return data
+
+/obj/item/mod/control/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ if(locked && !allowed(usr))
+ balloon_alert(usr, "insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(malfunctioning && prob(75))
+ balloon_alert(usr, "button malfunctions!")
+ return
+ switch(action)
+ if("lock")
+ locked = !locked
+ balloon_alert(usr, "[locked ? "locked" : "unlocked"]!")
+ if("activate")
+ toggle_activate(usr)
+ if("select")
+ var/obj/item/mod/module/module = locate(params["ref"]) in modules
+ if(!module)
+ return
+ module.on_select()
+ if("configure")
+ var/obj/item/mod/module/module = locate(params["ref"]) in modules
+ if(!module)
+ return
+ module.configure_edit(params["key"], params["value"])
+ if("pin")
+ var/obj/item/mod/module/module = locate(params["ref"]) in modules
+ if(!module)
+ return
+ module.pin(usr)
+ return TRUE
diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm
new file mode 100644
index 000000000000..7264066e5d1a
--- /dev/null
+++ b/code/modules/mod/modules/_module.dm
@@ -0,0 +1,399 @@
+///MOD Module - A special device installed in a MODsuit allowing the suit to do new stuff.
+/obj/item/mod/module
+ name = "MOD module"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ icon_state = "module"
+ /// If it can be removed
+ var/removable = TRUE
+ /// If it's passive, togglable, usable or active
+ var/module_type = MODULE_PASSIVE
+ /// Is the module active
+ var/active = FALSE
+ /// How much space it takes up in the MOD
+ var/complexity = 0
+ /// Power use when idle
+ var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// Power use when active
+ var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// Power use when used, we call it manually
+ var/use_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// ID used by their TGUI
+ var/tgui_id
+ /// Linked MODsuit
+ var/obj/item/mod/control/mod
+ /// If we're an active module, what item are we?
+ var/obj/item/device
+ /// Overlay given to the user when the module is inactive
+ var/overlay_state_inactive
+ /// Overlay given to the user when the module is active
+ var/overlay_state_active
+ /// Overlay given to the user when the module is used, lasts until cooldown finishes
+ var/overlay_state_use
+ /// Icon file for the overlay.
+ var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi'
+ /// Does the overlay use the control unit's colors?
+ var/use_mod_colors = FALSE
+ /// What modules are we incompatible with?
+ var/list/incompatible_modules = list()
+ /// Cooldown after use
+ var/cooldown_time = 0
+ /// The mouse button needed to use this module
+ var/used_signal
+ /// List of REF()s mobs we are pinned to, linked with their action buttons
+ var/list/pinned_to = list()
+ /// If we're allowed to use this module while phased out.
+ var/allowed_in_phaseout = FALSE
+ /// If we're allowed to use this module while the suit is disabled.
+ var/allowed_inactive = FALSE
+ /// Timer for the cooldown
+ COOLDOWN_DECLARE(cooldown_timer)
+
+/obj/item/mod/module/Initialize(mapload)
+ . = ..()
+ if(module_type != MODULE_ACTIVE)
+ return
+ if(ispath(device))
+ device = new device(src)
+ ADD_TRAIT(device, TRAIT_NODROP, MOD_TRAIT)
+ RegisterSignal(device, COMSIG_PARENT_QDELETING, PROC_REF(on_device_deletion))
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+
+/obj/item/mod/module/Destroy()
+ mod?.uninstall(src)
+ if(device)
+ UnregisterSignal(device, COMSIG_PARENT_QDELETING)
+ QDEL_NULL(device)
+ return ..()
+
+/obj/item/mod/module/examine(mob/user)
+ . = ..()
+ if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD))
+ . += span_notice("Complexity level: [complexity]")
+
+
+/// Called when the module is selected from the TGUI, radial or the action button
+/obj/item/mod/module/proc/on_select()
+ if(((!mod.active || mod.activating) && !allowed_inactive) || module_type == MODULE_PASSIVE)
+ if(mod.wearer)
+ balloon_alert(mod.wearer, "not active!")
+ return
+ if(module_type != MODULE_USABLE)
+ if(active)
+ on_deactivation()
+ else
+ on_activation()
+ else
+ on_use()
+ SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src)
+
+/// Called when the module is activated
+/obj/item/mod/module/proc/on_activation()
+ if(!COOLDOWN_FINISHED(src, cooldown_timer))
+ balloon_alert(mod.wearer, "on cooldown!")
+ return FALSE
+ if(!mod.active || mod.activating || !mod.get_charge())
+ balloon_alert(mod.wearer, "unpowered!")
+ return FALSE
+ if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
+ //specifically a to_chat because the user is phased out.
+ to_chat(mod.wearer, span_warning("You cannot activate this right now."))
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
+ return FALSE
+ if(module_type == MODULE_ACTIVE)
+ if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE))
+ return FALSE
+ mod.selected_module = src
+ if(device)
+ if(mod.wearer.put_in_hands(device))
+ balloon_alert(mod.wearer, "[device] extended")
+ RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ RegisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey))
+ else
+ balloon_alert(mod.wearer, "can't extend [device]!")
+ mod.wearer.transferItemToLoc(device, src, force = TRUE)
+ return FALSE
+ else
+ var/used_button = MIDDLE_CLICK
+ update_signal(used_button)
+ balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use")
+ active = TRUE
+ COOLDOWN_START(src, cooldown_timer, cooldown_time)
+ mod.wearer.update_inv_back(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED)
+ return TRUE
+
+/// Called when the module is deactivated
+/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE)
+ active = FALSE
+ if(module_type == MODULE_ACTIVE)
+ mod.selected_module = null
+ if(display_message)
+ balloon_alert(mod.wearer, device ? "[device] retracted" : "[src] deactivated")
+ if(device)
+ mod.wearer.transferItemToLoc(device, src, force = TRUE)
+ UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED)
+ UnregisterSignal(mod.wearer, COMSIG_KB_MOB_DROPITEM_DOWN)
+ else
+ UnregisterSignal(mod.wearer, used_signal)
+ used_signal = null
+ mod.wearer.update_inv_back(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED)
+ return TRUE
+
+/// Called when the module is used
+/obj/item/mod/module/proc/on_use()
+ if(!COOLDOWN_FINISHED(src, cooldown_timer))
+ balloon_alert(mod.wearer, "on cooldown!")
+ return FALSE
+ if(!check_power(use_power_cost))
+ balloon_alert(mod.wearer, "not enough charge!")
+ return FALSE
+ if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
+ //specifically a to_chat because the user is phased out.
+ to_chat(mod.wearer, span_warning("You cannot activate this right now."))
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
+ return FALSE
+ COOLDOWN_START(src, cooldown_timer, cooldown_time)
+ addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_inv_back), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts
+ mod.wearer.update_inv_back(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_USED)
+ return TRUE
+
+/// Called when an activated module without a device is used
+/obj/item/mod/module/proc/on_select_use(atom/target)
+ if(mod.wearer.incapacitated(IGNORE_GRAB))
+ return FALSE
+ mod.wearer.face_atom(target)
+ if(!on_use())
+ return FALSE
+ return TRUE
+
+/// Called when an activated module without a device is active and the user alt/middle-clicks
+/obj/item/mod/module/proc/on_special_click(mob/source, atom/target)
+ SIGNAL_HANDLER
+ on_select_use(target)
+ return COMSIG_MOB_CANCEL_CLICKON
+
+/// Called on the MODsuit's process
+/obj/item/mod/module/proc/on_process(delta_time)
+ if(active)
+ if(!drain_power(active_power_cost * delta_time))
+ on_deactivation()
+ return FALSE
+ on_active_process(delta_time)
+ else
+ drain_power(idle_power_cost * delta_time)
+ return TRUE
+
+/// Called on the MODsuit's process if it is an active module
+/obj/item/mod/module/proc/on_active_process(delta_time)
+ return
+
+/// Called from MODsuit's install() proc, so when the module is installed.
+/obj/item/mod/module/proc/on_install()
+ return
+
+/// Called from MODsuit's uninstall() proc, so when the module is uninstalled.
+/obj/item/mod/module/proc/on_uninstall(deleting = FALSE)
+ return
+
+/// Called when the MODsuit is activated
+/obj/item/mod/module/proc/on_suit_activation()
+ return
+
+/// Called when the MODsuit is deactivated
+/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE)
+ return
+
+/// Called when the MODsuit is equipped
+/obj/item/mod/module/proc/on_equip()
+ return
+
+/// Called when the MODsuit is unequipped
+/obj/item/mod/module/proc/on_unequip()
+ return
+
+/// Drains power from the suit charge
+/obj/item/mod/module/proc/drain_power(amount)
+ if(!check_power(amount))
+ return FALSE
+ mod.subtract_charge(amount)
+ mod.update_charge_alert()
+ return TRUE
+
+/// Checks if there is enough power in the suit
+/obj/item/mod/module/proc/check_power(amount)
+ return mod.check_charge(amount)
+
+/// Adds additional things to the MODsuit ui_data()
+/obj/item/mod/module/proc/add_ui_data()
+ return list()
+
+/// Creates a list of configuring options for this module
+/obj/item/mod/module/proc/get_configuration()
+ return list()
+
+/// Generates an element of the get_configuration list with a display name, type and value
+/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values)
+ return list("display_name" = display_name, "type" = type, "value" = value, "values" = values)
+
+/// Receives configure edits from the TGUI and edits the vars
+/obj/item/mod/module/proc/configure_edit(key, value)
+ return
+
+/// Called when the device moves to a different place on active modules
+/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction)
+ SIGNAL_HANDLER
+
+ if(!active)
+ return
+ if(part.loc == src)
+ return
+ if(part.loc == mod.wearer)
+ return
+ if(part == device)
+ on_deactivation(display_message = FALSE)
+
+/// Called when the device gets deleted on active modules
+/obj/item/mod/module/proc/on_device_deletion(datum/source)
+ SIGNAL_HANDLER
+
+ if(source == device)
+ device = null
+ qdel(src)
+
+/// Generates an icon to be used for the suit's worn overlays
+/obj/item/mod/module/proc/generate_worn_overlay(mod_layer)
+ . = list()
+ if(!mod.active)
+ return
+ var/used_overlay
+ if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer))
+ used_overlay = overlay_state_use
+ else if(overlay_state_active && active)
+ used_overlay = overlay_state_active
+ else if(overlay_state_inactive)
+ used_overlay = overlay_state_inactive
+ else
+ return
+ var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay, layer = mod_layer + 0.1)
+ if(!use_mod_colors)
+ module_icon.appearance_flags |= RESET_COLOR
+ . += module_icon
+
+/// Updates the signal used by active modules to be activated
+/obj/item/mod/module/proc/update_signal(value)
+ switch(value)
+ if(MIDDLE_CLICK)
+ mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON
+ if(ALT_CLICK)
+ mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON
+ RegisterSignal(mod.wearer, mod.selected_module.used_signal, TYPE_PROC_REF(/obj/item/mod/module, on_special_click))
+
+/// Pins the module to the user's action buttons
+/obj/item/mod/module/proc/pin(mob/user)
+ var/datum/action/item_action/mod/pinned_module/existing_action = pinned_to[REF(user)]
+ if(existing_action)
+ mod.remove_item_action(existing_action)
+ return
+
+ var/datum/action/item_action/mod/pinned_module/new_action = new(mod, src, user)
+ mod.add_item_action(new_action)
+
+/// On drop key, concels a device item.
+/obj/item/mod/module/proc/dropkey(mob/living/user)
+ SIGNAL_HANDLER
+
+ if(user.get_active_held_item() != device)
+ return
+ on_deactivation()
+ return COMSIG_KB_ACTIVATED
+
+///Anomaly Locked - Causes the module to not function without an anomaly.
+/obj/item/mod/module/anomaly_locked
+ name = "MOD anomaly locked module"
+ desc = "A form of a module, locked behind an anomalous core to function."
+ incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
+ /// The core item the module runs off.
+ var/obj/item/assembly/signaler/anomaly/core
+ /// Accepted types of anomaly cores.
+ var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
+ /// If this one starts with a core in.
+ var/prebuilt = FALSE
+
+/obj/item/mod/module/anomaly_locked/Initialize(mapload)
+ . = ..()
+ if(!prebuilt || !length(accepted_anomalies))
+ return
+ var/core_path = pick(accepted_anomalies)
+ core = new core_path(src)
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/Destroy()
+ QDEL_NULL(core)
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/examine(mob/user)
+ . = ..()
+ if(!length(accepted_anomalies))
+ return
+ if(core)
+ . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...")
+ else
+ var/list/core_list = list()
+ for(var/path in accepted_anomalies)
+ var/atom/core_path = path
+ core_list += initial(core_path.name)
+ . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
+
+/obj/item/mod/module/anomaly_locked/on_select()
+ if(!core)
+ balloon_alert(mod.wearer, "no core!")
+ return
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/on_process(delta_time)
+ . = ..()
+ if(!core)
+ return FALSE
+
+/obj/item/mod/module/anomaly_locked/on_active_process(delta_time)
+ if(!core)
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params)
+ if(item.type in accepted_anomalies)
+ if(core)
+ balloon_alert(user, "core already in!")
+ return
+ if(!user.transferItemToLoc(item, src))
+ return
+ core = item
+ balloon_alert(user, "core installed")
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ update_icon_state()
+ else
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!core)
+ balloon_alert(user, "no core!")
+ return
+ balloon_alert(user, "removing core...")
+ if(!do_after(user, 3 SECONDS, target = src))
+ balloon_alert(user, "interrupted!")
+ return
+ balloon_alert(user, "core removed")
+ core.forceMove(drop_location())
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(core)
+ core = null
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/update_icon_state()
+ icon_state = initial(icon_state) + (core ? "-core" : "")
+ return ..()
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
new file mode 100644
index 000000000000..33edd75e173f
--- /dev/null
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -0,0 +1,398 @@
+//Antag modules for MODsuits
+
+///Armor Booster - Grants your suit more armor and speed in exchange for EVA protection. Also acts as a welding screen.
+/obj/item/mod/module/armor_booster
+ name = "MOD armor booster module"
+ desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \
+ giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \
+ However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \
+ so this extra armor provides zero ability for extravehicular activity while deployed."
+ icon_state = "armor_booster"
+ module_type = MODULE_TOGGLE
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ removable = TRUE
+ incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_armorbooster_off"
+ overlay_state_active = "module_armorbooster_on"
+ use_mod_colors = TRUE
+ /// Whether or not this module removes pressure protection.
+ var/remove_pressure_protection = TRUE
+ /// Speed added to the control unit.
+ var/speed_added = 0.5
+ /// Speed that we actually added.
+ var/actual_speed_added = 0
+ /// Armor values added to the suit parts.
+ var/list/armor_values = list("melee" = 25, "bullet" = 30, "laser" = 15, "energy" = 15)
+ /// List of parts of the suit that are spaceproofed, for giving them back the pressure protection.
+ var/list/spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/on_suit_activation()
+ mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
+
+/obj/item/mod/module/armor_booster/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
+
+/obj/item/mod/module/armor_booster/on_activation()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ actual_speed_added = max(0, min(mod.slowdown_active, speed_added))
+ mod.slowdown -= actual_speed_added
+ mod.wearer.update_equipment_speed_mods()
+ var/list/parts = mod.mod_parts + mod
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.modifyRating(arglist(armor_values))
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE)
+ clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE
+ spaceproofed[clothing_part] = TRUE
+
+/obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(!deleting)
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ mod.slowdown += actual_speed_added
+ mod.wearer.update_equipment_speed_mods()
+ var/list/parts = mod.mod_parts + mod
+ var/list/removed_armor = armor_values.Copy()
+ for(var/armor_type in removed_armor)
+ removed_armor[armor_type] = -removed_armor[armor_type]
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.modifyRating(arglist(removed_armor))
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(spaceproofed[clothing_part])
+ clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE
+ spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
+ return ..()
+
+///Energy Shield - Gives you a rechargeable energy shield that nullifies attacks.
+/obj/item/mod/module/energy_shield
+ name = "MOD energy shield module"
+ desc = "A personal, protective forcefield typically seen in military applications. \
+ This advanced deflector shield is essentially a scaled down version of those seen on starships, \
+ and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \
+ though with its' low amount of separate charges, the user remains mortal."
+ icon_state = "energy_shield"
+ complexity = 3
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/energy_shield)
+ /// Max charges of the shield.
+ var/max_charges = 3
+ /// The time it takes for the first charge to recover.
+ var/recharge_start_delay = 20 SECONDS
+ /// How much time it takes for charges to recover after they started recharging.
+ var/charge_increment_delay = 1 SECONDS
+ /// How much charge is recovered per recovery.
+ var/charge_recovery = 1
+ /// Whether or not this shield can lose multiple charges.
+ var/lose_multiple_charges = FALSE
+ /// The item path to recharge this shielkd.
+ var/recharge_path = null
+ /// The icon file of the shield.
+ var/shield_icon_file = 'icons/effects/effects.dmi'
+ /// The icon_state of the shield.
+ var/shield_icon = "shield-red"
+ /// Charges the shield should start with.
+ var/charges
+
+/obj/item/mod/module/energy_shield/Initialize(mapload)
+ . = ..()
+ charges = max_charges
+
+/obj/item/mod/module/energy_shield/on_suit_activation()
+ mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \
+ charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon)
+ RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(shield_reaction))
+
+/obj/item/mod/module/energy_shield/on_suit_deactivation(deleting = FALSE)
+ var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded)
+ charges = shield.current_charges
+ qdel(shield)
+ UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS)
+
+/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
+ if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK)
+ drain_power(use_power_cost)
+ return SHIELD_BLOCK
+ return NONE
+
+///Insignia - Gives you a skin specific stripe.
+/obj/item/mod/module/insignia
+ name = "MOD insignia module"
+ desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
+ the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
+ they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
+ color patterns to and from the suit."
+ icon_state = "insignia"
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/insignia)
+ overlay_state_inactive = "module_insignia"
+
+/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ . = ..()
+ for(var/mutable_appearance/appearance as anything in .)
+ appearance.color = color
+
+/obj/item/mod/module/insignia/commander
+ color = "#4980a5"
+
+/obj/item/mod/module/insignia/security
+ color = "#b30d1e"
+
+/obj/item/mod/module/insignia/engineer
+ color = "#e9c80e"
+
+/obj/item/mod/module/insignia/medic
+ color = "#ebebf5"
+
+/obj/item/mod/module/insignia/janitor
+ color = "#7925c7"
+
+/obj/item/mod/module/insignia/chaplain
+ color = "#f0a00c"
+
+///Anti Slip - Prevents you from slipping on water.
+/obj/item/mod/module/noslip
+ name = "MOD anti slip module"
+ desc = "These are a modified variant of standard magnetic boots, utilizing piezoelectric crystals on the soles. \
+ The two plates on the bottom of the boots automatically extend and magnetize as the user steps; \
+ a pull that's too weak to offer them the ability to affix to a hull, but just strong enough to \
+ protect against the fact that you didn't read the wet floor sign. Honk Co. has come out numerous times \
+ in protest of these modules being legal."
+ icon_state = "noslip"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
+ incompatible_modules = list(/obj/item/mod/module/noslip)
+
+/obj/item/mod/module/noslip/on_suit_activation()
+ mod.boots.clothing_flags |= NOSLIP
+
+/obj/item/mod/module/noslip/on_suit_deactivation(deleting = FALSE)
+ mod.boots.clothing_flags &= ~NOSLIP
+
+//Bite of 87 Springlock - Equips faster, disguised as DNA lock.
+/obj/item/mod/module/springlock/bite_of_87
+
+/obj/item/mod/module/springlock/bite_of_87/Initialize(mapload)
+ . = ..()
+ var/obj/item/mod/module/dna_lock/the_dna_lock_behind_the_slaughter = /obj/item/mod/module/dna_lock
+ name = initial(the_dna_lock_behind_the_slaughter.name)
+ desc = initial(the_dna_lock_behind_the_slaughter.desc)
+ icon_state = initial(the_dna_lock_behind_the_slaughter.icon_state)
+ complexity = initial(the_dna_lock_behind_the_slaughter.complexity)
+ use_power_cost = initial(the_dna_lock_behind_the_slaughter.use_power_cost)
+
+/obj/item/mod/module/springlock/bite_of_87/on_install()
+ mod.activation_step_time *= 0.1
+
+/obj/item/mod/module/springlock/bite_of_87/on_uninstall(deleting = FALSE)
+ mod.activation_step_time *= 10
+
+/obj/item/mod/module/springlock/bite_of_87/on_suit_activation()
+ ..()
+ if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS] || prob(1))
+ mod.set_mod_color("#b17f00")
+ mod.wearer.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) // turns purple guy purple
+ mod.wearer.add_atom_colour("#704b96", FIXED_COLOUR_PRIORITY)
+
+///Flamethrower - Launches fire across the area.
+/obj/item/mod/module/flamethrower
+ name = "MOD flamethrower module"
+ desc = "A custom-manufactured flamethrower, used to burn through your path. Burn well."
+ icon_state = "flamethrower"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/flamethrower)
+ cooldown_time = 2.5 SECONDS
+ overlay_state_inactive = "module_flamethrower"
+ overlay_state_active = "module_flamethrower_on"
+
+/obj/item/mod/module/flamethrower/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/projectile/flame = new /obj/projectile/bullet/incendiary(mod.wearer.loc)
+ flame.preparePixelProjectile(target, mod.wearer)
+ flame.firer = mod.wearer
+ playsound(src, 'sound/items/modsuit/flamethrower.ogg', 75, TRUE)
+ INVOKE_ASYNC(flame, TYPE_PROC_REF(/obj/projectile, fire))
+ drain_power(use_power_cost)
+
+///Power kick - Lets the user launch themselves at someone to kick them.
+/obj/item/mod/module/power_kick
+ name = "MOD power kick module"
+ desc = "This module uses high-power myomer to generate an incredible amount of energy, transferred into the power of a kick."
+ icon_state = "power_kick"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ use_power_cost = DEFAULT_CHARGE_DRAIN*5
+ incompatible_modules = list(/obj/item/mod/module/power_kick)
+ cooldown_time = 5 SECONDS
+ /// Damage on kick.
+ var/damage = 20
+ /// The wound bonus of the kick.
+ var/wounding_power = 35
+ /// How long we knockdown for on the kick.
+ var/knockdown_time = 2 SECONDS
+
+/obj/item/mod/module/power_kick/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ mod.wearer.visible_message(span_warning("[mod.wearer] starts charging a kick!"), \
+ blind_message = span_hear("You hear a charging sound."))
+ playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
+ balloon_alert(mod.wearer, "you start charging...")
+ animate(mod.wearer, 0.3 SECONDS, pixel_z = 16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT)
+ addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, SpinAnimation), 3, 2), 0.3 SECONDS)
+ if(!do_after(mod.wearer, 1 SECONDS, target = mod))
+ animate(mod.wearer, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT)
+ return
+ animate(mod.wearer)
+ drain_power(use_power_cost)
+ playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
+ var/angle = get_angle(mod.wearer, target) + 180
+ mod.wearer.transform = mod.wearer.transform.Turn(angle)
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact))
+ mod.wearer.throw_at(target, range = 7, speed = 2, thrower = mod.wearer, spin = FALSE, gentle = TRUE, callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle))
+
+/obj/item/mod/module/power_kick/proc/on_throw_end(mob/user, angle)
+ if(!user)
+ return
+ user.transform = user.transform.Turn(angle)
+ animate(user, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT)
+
+/obj/item/mod/module/power_kick/proc/on_throw_impact(mob/living/source, obj/target, datum/thrownthing/thrownthing)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
+ if(!mod?.wearer)
+ return
+ if(isliving(target))
+ var/mob/living/living_target = target
+ living_target.apply_damage(damage, BRUTE, mod.wearer.zone_selected)
+ living_target.Knockdown(knockdown_time)
+ else if(target.obj_integrity)
+ target.take_damage(damage, BRUTE)
+ else
+ return
+ mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH)
+
+///Chameleon - lets the suit disguise as any item that would fit on that slot.
+/obj/item/mod/module/chameleon
+ name = "MOD chameleon module"
+ desc = "A module using chameleon technology to disguise the suit as another object."
+ icon_state = "chameleon"
+ module_type = MODULE_USABLE
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/chameleon)
+ cooldown_time = 0.5 SECONDS
+ allowed_inactive = TRUE
+ /// A list of all the items the suit can disguise as.
+ var/list/possible_disguises = list()
+ /// The path of the item we're disguised as.
+ var/obj/item/current_disguise
+
+/obj/item/mod/module/chameleon/on_install()
+ var/list/all_disguises = sortList(subtypesof(get_path_by_slot(mod.slot_flags)), GLOBAL_PROC_REF(cmp_typepaths_asc))
+ for(var/clothing_path in all_disguises)
+ var/obj/item/clothing = clothing_path
+ if(!initial(clothing.icon_state))
+ continue
+ var/chameleon_item_name = "[initial(clothing.name)] ([initial(clothing.icon_state)])"
+ possible_disguises[chameleon_item_name] = clothing_path
+
+/obj/item/mod/module/chameleon/on_uninstall(deleting = FALSE)
+ if(current_disguise)
+ return_look()
+ possible_disguises = null
+
+/obj/item/mod/module/chameleon/on_use()
+ if(mod.active || mod.activating)
+ balloon_alert(mod.wearer, "suit active!")
+ return
+ . = ..()
+ if(!.)
+ return
+ if(current_disguise)
+ return_look()
+ return
+ var/picked_name = tgui_input_list(mod.wearer, "Select look to change into", "Chameleon Settings", possible_disguises)
+ if(!possible_disguises[picked_name] || mod.active || mod.activating)
+ return
+ current_disguise = possible_disguises[picked_name]
+ update_look()
+
+/obj/item/mod/module/chameleon/proc/update_look()
+ mod.name = initial(current_disguise.name)
+ mod.desc = initial(current_disguise.desc)
+ mod.icon_state = initial(current_disguise.icon_state)
+ mod.icon = initial(current_disguise.icon)
+ mod.mob_overlay_icon = initial(current_disguise.mob_overlay_icon)
+ mod.alternate_worn_layer = initial(current_disguise.alternate_worn_layer)
+ mod.lefthand_file = initial(current_disguise.lefthand_file)
+ mod.righthand_file = initial(current_disguise.righthand_file)
+ //mod.mob_overlay_state = initial(current_disguise.mob_overlay_state)
+ mod.item_state = initial(current_disguise.item_state)
+ mod.wearer.update_inv_back(mod.slot_flags)
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(return_look))
+
+/obj/item/mod/module/chameleon/proc/return_look()
+ mod.name = "[mod.theme.name] [initial(mod.name)]"
+ mod.desc = "[initial(mod.desc)] [mod.theme.desc]"
+ mod.icon_state = "[mod.skin]-[initial(mod.icon_state)]"
+ var/list/mod_skin = mod.theme.skins[mod.skin]
+ mod.icon = mod_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ mod.mob_overlay_icon = mod_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ mod.alternate_worn_layer = mod_skin[CONTROL_LAYER]
+ mod.lefthand_file = initial(mod.lefthand_file)
+ mod.righthand_file = initial(mod.righthand_file)
+ //___callbacknewmod.worn_icon_state = null
+ mod.item_state = null
+ mod.wearer.update_inv_back(mod.slot_flags)
+ current_disguise = null
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+
+///Plate Compression - Compresses the suit to normal size
+/obj/item/mod/module/plate_compression
+ name = "MOD plate compression module"
+ desc = "A module that keeps the suit in a very tightly fit state, lowering the overall size. \
+ Due to the pressure on all the parts, typical storage modules do not fit."
+ icon_state = "plate_compression"
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/plate_compression, /obj/item/mod/module/storage)
+ /// The size we set the suit to.
+ var/new_size = WEIGHT_CLASS_NORMAL
+ /// The suit's size before the module is installed.
+ var/old_size
+
+/obj/item/mod/module/plate_compression/on_install()
+ old_size = mod.w_class
+ mod.w_class = new_size
+
+/obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE)
+ mod.w_class = old_size
+ old_size = null
+ if(!mod.loc)
+ return
+ var/datum/component/storage/concrete/holding_storage = mod.GetComponent(/datum/component/storage/concrete)
+ if(!holding_storage || holding_storage.max_combined_w_class >= mod.w_class)
+ return
+ mod.forceMove(drop_location())
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
new file mode 100644
index 000000000000..4905b3ae691f
--- /dev/null
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -0,0 +1,169 @@
+//Engineering modules for MODsuits
+
+///Welding Protection - Makes the helmet protect from flashes and welding.
+/obj/item/mod/module/welding
+ name = "MOD welding protection module"
+ desc = "A module installed into the visor of the suit, this projects a \
+ polarized, holographic overlay in front of the user's eyes. It's rated high enough for \
+ immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights."
+ icon_state = "welding"
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/welding, /obj/item/mod/module/armor_booster)
+ overlay_state_inactive = "module_welding"
+
+/obj/item/mod/module/welding/on_suit_activation()
+ mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
+
+/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
+
+///T-Ray Scan - Scans the terrain for undertile objects.
+/obj/item/mod/module/t_ray
+ name = "MOD t-ray scan module"
+ desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \
+ to essentially echolocate things beneath the floor, mostly cables and pipes. \
+ A staple of atmospherics work, and counter-smuggling work."
+ icon_state = "tray"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/t_ray)
+ cooldown_time = 0.5 SECONDS
+ /// T-ray scan range.
+ var/range = 4
+
+/obj/item/mod/module/t_ray/on_active_process(delta_time)
+ t_ray_scan(mod.wearer, 0.8 SECONDS, range)
+
+///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips.
+/obj/item/mod/module/magboot
+ name = "MOD magnetic stability module"
+ desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \
+ excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \
+ However, these basic models do not feature computerized systems to automatically toggle them on and off, \
+ so numerous users report a certain stickiness to their steps."
+ icon_state = "magnet"
+ module_type = MODULE_TOGGLE
+ complexity = 2
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/magboot)
+ cooldown_time = 0.5 SECONDS
+ /// Slowdown added onto the suit.
+ var/slowdown_active = 0.5
+
+/obj/item/mod/module/magboot/on_activation()
+ . = ..()
+ if(!.)
+ return
+ ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT)
+ mod.slowdown += slowdown_active
+ mod.wearer.update_gravity(mod.wearer.has_gravity())
+ mod.wearer.update_equipment_speed_mods()
+
+/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT)
+ mod.slowdown -= slowdown_active
+ mod.wearer.update_gravity(mod.wearer.has_gravity())
+ mod.wearer.update_equipment_speed_mods()
+
+/obj/item/mod/module/magboot/advanced
+ name = "MOD advanced magnetic stability module"
+ removable = FALSE
+ complexity = 0
+ slowdown_active = 0
+
+///Emergency Tether - Shoots a grappling hook projectile in 0g that throws the user towards it.
+/obj/item/mod/module/tether
+ name = "MOD emergency tether module"
+ desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
+ While some older models of cargo-oriented grapples have capacities of a few tons, \
+ these are only capable of working in zero-gravity environments, a blessing to some Engineers."
+ icon_state = "tether"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/tether)
+ cooldown_time = 1.5 SECONDS
+
+/obj/item/mod/module/tether/on_use()
+ if(mod.wearer.has_gravity(get_turf(src)))
+ balloon_alert(mod.wearer, "too much gravity!!")
+ playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE)
+ return FALSE
+ return ..()
+
+/obj/item/mod/module/tether/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc)
+ tether.preparePixelProjectile(target, mod.wearer)
+ tether.firer = mod.wearer
+ playsound(src, 'sound/weapons/batonextend.ogg', 25, TRUE)
+ INVOKE_ASYNC(tether, TYPE_PROC_REF(/obj/projectile, fire))
+ drain_power(use_power_cost)
+
+/obj/projectile/tether
+ name = "tether"
+ icon_state = "tether_projectile"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ damage = 0
+ nodamage = TRUE
+ range = 10
+ hitsound = 'sound/weapons/batonextend.ogg'
+ suppressed = SUPPRESSED_VERY
+ //hit_threshhold = LATTICE_LAYER
+ /// Reference to the beam following the projectile.
+ var/line
+
+/obj/projectile/tether/fire(setAngle)
+ if(firer)
+ line = firer.Beam(src, "line", 'icons/obj/clothing/modsuit/mod_modules.dmi')
+ ..()
+
+/obj/projectile/tether/on_hit(atom/target)
+ . = ..()
+ if(firer)
+ firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE)
+
+/obj/projectile/tether/Destroy()
+ QDEL_NULL(line)
+ return ..()
+
+///Mister - Sprays water over an area.
+/obj/item/mod/module/mister
+ name = "MOD water mister module"
+ desc = "A module containing a mister, able to spray it over areas."
+ icon_state = "mister"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ device = /obj/item/reagent_containers/spray/mister
+ incompatible_modules = list(/obj/item/mod/module/mister)
+ cooldown_time = 0.5 SECONDS
+ /// Volume of our reagent holder.
+ var/volume = 500
+
+/obj/item/mod/module/mister/Initialize(mapload)
+ create_reagents(volume, OPENCONTAINER)
+ return ..()
+
+///Resin Mister - Sprays resin over an area.
+/obj/item/mod/module/mister/atmos
+ name = "MOD resin mister module"
+ desc = "An atmospheric resin mister, able to fix up areas quickly."
+ device = /obj/item/extinguisher/mini/nozzle/mod
+ volume = 250
+
+/obj/item/mod/module/mister/atmos/Initialize(mapload)
+ . = ..()
+ reagents.add_reagent(/datum/reagent/water, volume)
+
+/obj/item/extinguisher/mini/nozzle/mod
+ name = "MOD atmospheric mister"
+ desc = "An atmospheric resin mister with three modes, mounted as a module."
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
new file mode 100644
index 000000000000..8c5f9e27cf55
--- /dev/null
+++ b/code/modules/mod/modules/modules_general.dm
@@ -0,0 +1,445 @@
+//General modules for MODsuits
+
+///Ion Jetpack - Lets the user fly freely through space using battery charge.
+/obj/item/mod/module/jetpack
+ name = "MOD ion jetpack module"
+ desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
+ Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
+ charge from the suit's charge. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits."
+ icon_state = "jetpack"
+ module_type = MODULE_TOGGLE
+ complexity = 3
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/jetpack)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_jetpack"
+ overlay_state_active = "module_jetpack_on"
+ /// Do we stop the wearer from gliding in space.
+ var/stabilizers = FALSE
+ /// Do we give the wearer a speed buff.
+ var/full_speed = FALSE
+ var/datum/callback/get_mover
+ var/datum/callback/check_on_move
+
+/obj/item/mod/module/jetpack/Initialize(mapload)
+ . = ..()
+ get_mover = CALLBACK(src, PROC_REF(get_user))
+ check_on_move = CALLBACK(src, PROC_REF(allow_thrust))
+ refresh_jetpack()
+
+/obj/item/mod/module/jetpack/Destroy()
+ get_mover = null
+ check_on_move = null
+ return ..()
+
+/obj/item/mod/module/jetpack/proc/refresh_jetpack()
+ AddComponent(/datum/component/jetpack, stabilizers, COMSIG_MODULE_TRIGGERED, COMSIG_MODULE_DEACTIVATED, MOD_ABORT_USE, get_mover, check_on_move, /datum/effect_system/trail_follow/ion/grav_allowed)
+
+/obj/item/mod/module/jetpack/proc/set_stabilizers(new_stabilizers)
+ if(stabilizers == new_stabilizers)
+ return
+ stabilizers = new_stabilizers
+ refresh_jetpack()
+
+/obj/item/mod/module/jetpack/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(full_speed)
+ mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+
+/obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(full_speed)
+ mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+
+/obj/item/mod/module/jetpack/get_configuration()
+ . = ..()
+ .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
+
+/obj/item/mod/module/jetpack/configure_edit(key, value)
+ switch(key)
+ if("stabilizers")
+ set_stabilizers(text2num(value))
+
+/obj/item/mod/module/jetpack/proc/allow_thrust(use_fuel = TRUE)
+ if(!use_fuel)
+ return check_power(use_power_cost)
+ if(!drain_power(use_power_cost))
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/jetpack/proc/get_user()
+ return mod.wearer
+
+/obj/item/mod/module/jetpack/advanced
+ name = "MOD advanced ion jetpack module"
+ desc = "An improvement on the previous model of electric thrusters. This one achieves higher speeds through \
+ mounting of more jets and a red paint applied on it."
+ icon_state = "jetpack_advanced"
+ overlay_state_inactive = "module_jetpackadv"
+ overlay_state_active = "module_jetpackadv_on"
+ full_speed = TRUE
+
+///Eating Apparatus - Lets the user eat/drink with the suit on.
+/obj/item/mod/module/mouthhole
+ name = "MOD eating apparatus module"
+ desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \
+ to allow eating and drinking while retaining protection and atmosphere. However, it won't free you from masks, \
+ and it will do nothing to improve the taste of a goliath steak."
+ icon_state = "apparatus"
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/mouthhole)
+ overlay_state_inactive = "module_apparatus"
+ /// Former flags of the helmet.
+ var/former_flags = NONE
+ /// Former visor flags of the helmet.
+ var/former_visor_flags = NONE
+
+/obj/item/mod/module/mouthhole/on_install()
+ former_flags = mod.helmet.flags_cover
+ former_visor_flags = mod.helmet.visor_flags_cover
+ mod.helmet.flags_cover &= ~HEADCOVERSMOUTH|PEPPERPROOF
+ mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH|PEPPERPROOF
+
+/obj/item/mod/module/mouthhole/on_uninstall(deleting = FALSE)
+ if(deleting)
+ return
+ mod.helmet.flags_cover |= former_flags
+ mod.helmet.visor_flags_cover |= former_visor_flags
+
+///EMP Shield - Protects the suit from EMPs.
+/obj/item/mod/module/emp_shield
+ name = "MOD EMP shield module"
+ desc = "A field inhibitor installed into the suit, protecting it against feedback such as \
+ electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer. \
+ However, it will take from the suit's power to do so. Luckily, your PDA already has one of these."
+ icon_state = "empshield"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/emp_shield)
+
+/obj/item/mod/module/emp_shield/on_install()
+ mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
+
+/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE)
+ mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
+
+/obj/item/mod/module/emp_shield/advanced
+ name = "MOD advanced EMP shield module"
+ desc = "An enhnanced field inhibitor installed into the suit, protecting it against feedback such as \
+ electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer \
+ including augmentations. However, it will take from the suit's power to do so. Luckily, your PDA already has one of these."
+ complexity = 2
+
+/obj/item/mod/module/emp_shield/advanced/on_suit_activation()
+ mod.wearer.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
+
+/obj/item/mod/module/emp_shield/advanced/on_suit_deactivation(deleting)
+ mod.wearer.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
+
+///Flashlight - Gives the suit a customizable flashlight.
+/obj/item/mod/module/flashlight
+ name = "MOD flashlight module"
+ desc = "A simple pair of flashlights installed on the left and right sides of the helmet."
+ icon_state = "flashlight"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/flashlight)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_light"
+ light_system = MOVABLE_LIGHT_DIRECTIONAL
+ light_color = COLOR_WHITE
+ light_range = 4
+ light_power = 1
+ light_on = FALSE
+ /// Charge drain per range amount.
+ var/base_power = DEFAULT_CHARGE_DRAIN * 0.1
+
+/obj/item/mod/module/flashlight/on_activation()
+ . = ..()
+ if(!.)
+ return
+ set_light_flags(light_flags | LIGHT_ATTACHED)
+ set_light_on(active)
+ active_power_cost = base_power * light_range
+
+/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ set_light_flags(light_flags & ~LIGHT_ATTACHED)
+ set_light_on(active)
+
+/obj/item/mod/module/flashlight/on_process(delta_time)
+ active_power_cost = base_power * light_range
+ return ..()
+
+/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing)
+ . = ..()
+ if(!active)
+ return
+ var/mutable_appearance/light_icon = mutable_appearance(overlay_icon_file, "module_light_on", layer = standing + 0.2)
+ light_icon.appearance_flags = RESET_COLOR
+ light_icon.color = light_color
+ . += light_icon
+
+///Dispenser - Dispenses an item after a time passes.
+/obj/item/mod/module/dispenser
+ name = "MOD burger dispenser module"
+ desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \
+ This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \
+ palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \
+ Notably, all attempts to get it to dispense Earl Grey tea have failed."
+ icon_state = "dispenser"
+ module_type = MODULE_USABLE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/dispenser)
+ cooldown_time = 5 SECONDS
+ /// Path we dispense.
+ var/dispense_type = /obj/item/reagent_containers/food/snacks/burger
+ /// Time it takes for us to dispense.
+ var/dispense_time = 0 SECONDS
+
+/obj/item/mod/module/dispenser/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod))
+ balloon_alert(mod.wearer, "interrupted!")
+ return FALSE
+ var/obj/item/dispensed = new dispense_type(mod.wearer.loc)
+ mod.wearer.put_in_hands(dispensed)
+ balloon_alert(mod.wearer, "[dispensed] dispensed")
+ playsound(src, 'sound/machines/click.ogg', 100, TRUE)
+ drain_power(use_power_cost)
+ return dispensed
+
+///Thermal Regulator - Regulates the wearer's core temperature.
+/obj/item/mod/module/thermal_regulator
+ name = "MOD thermal regulator module"
+ desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \
+ flexible cooling lines. This circulates coolant at various user-controlled temperatures, \
+ ensuring they're comfortable; even if they're some that like it hot."
+ icon_state = "regulator"
+ module_type = MODULE_TOGGLE
+ complexity = 2
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/thermal_regulator)
+ cooldown_time = 0.5 SECONDS
+ /// The temperature we are regulating to.
+ var/temperature_setting = BODYTEMP_NORMAL
+ /// Minimum temperature we can set.
+ var/min_temp = 293.15
+ /// Maximum temperature we can set.
+ var/max_temp = 318.15
+
+/obj/item/mod/module/thermal_regulator/get_configuration()
+ . = ..()
+ .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C)
+
+/obj/item/mod/module/thermal_regulator/configure_edit(key, value)
+ switch(key)
+ if("temperature_setting")
+ temperature_setting = clamp(value + T0C, min_temp, max_temp)
+
+/obj/item/mod/module/thermal_regulator/on_active_process(delta_time)
+ mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time))
+
+///DNA Lock - Prevents people without the set DNA from activating the suit.
+/obj/item/mod/module/dna_lock
+ name = "MOD DNA lock module"
+ desc = "A module which engages with the various locks and seals tied to the suit's systems, \
+ enabling it to only be worn by someone corresponding with the user's exact DNA profile; \
+ however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed."
+ icon_state = "dnalock"
+ module_type = MODULE_USABLE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/dna_lock/*, obj/item/mod/module/eradication_lock*/)
+ cooldown_time = 0.5 SECONDS
+ /// The DNA we lock with.
+ var/dna = null
+
+/obj/item/mod/module/dna_lock/on_install()
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation))
+ RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal))
+ RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
+ RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag))
+
+/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE)
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+ UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL)
+ UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT)
+ UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
+
+/obj/item/mod/module/dna_lock/on_use()
+ . = ..()
+ if(!.)
+ return
+ dna = mod.wearer.dna.unique_enzymes
+ balloon_alert(mod.wearer, "dna updated")
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/dna_lock/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ on_emp(src, severity)
+
+/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card)
+ . = ..()
+ on_emag(src, user, emag_card)
+
+/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
+ if(!iscarbon(user))
+ return FALSE
+ var/mob/living/carbon/carbon_user = user
+ if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna))
+ return TRUE
+ balloon_alert(user, "dna locked!")
+ return FALSE
+
+/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity)
+ SIGNAL_HANDLER
+
+ dna = null
+
+/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card)
+ SIGNAL_HANDLER
+
+ dna = null
+
+/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!dna_check(user))
+ return MOD_CANCEL_ACTIVATE
+
+/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!dna_check(user))
+ return MOD_CANCEL_REMOVAL
+
+///Plasma Stabilizer - Prevents plasmamen from igniting in the suit
+/obj/item/mod/module/plasma_stabilizer
+ name = "MOD plasma stabilizer module"
+ desc = "This system essentially forms an atmosphere of its' own inside the suit, \
+ safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \
+ to have their internal plasma circulate around them somewhat like a sauna. \
+ This prevents them from self-igniting, and leads to greater comfort overall. \
+ The purple glass of the visor seems to be constructed for nostalgic purposes."
+ icon_state = "plasma_stabilizer"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
+ overlay_state_inactive = "module_plasma"
+
+/obj/item/mod/module/plasma_stabilizer/on_equip()
+ ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MOD_TRAIT)
+
+/obj/item/mod/module/plasma_stabilizer/on_unequip()
+ REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MOD_TRAIT)
+
+
+//Finally, https://pipe.miroware.io/5b52ba1d94357d5d623f74aa/mspfa/Nuke%20Ops/Panels/0648.gif can be real:
+///Hat Stabilizer - Allows displaying a hat over the MOD-helmet, à la plasmamen helmets.
+/obj/item/mod/module/hat_stabilizer
+ name = "MOD hat stabilizer module"
+ desc = "A simple set of deployable stands, directly atop one's head; \
+ these will deploy under a select few hats to keep them from falling off, allowing them to be worn atop the sealed helmet. \
+ You still need to take the hat off your head while the helmet deploys, though. \
+ This is a must-have for Nanotrasen Captains, enabling them to show off their authoritative hat even while in their MODsuit."
+ icon_state = "hat_holder"
+ incompatible_modules = list(/obj/item/mod/module/hat_stabilizer)
+ /*Intentionally left inheriting 0 complexity and removable = TRUE;
+ even though it comes inbuilt into the Magnate/Corporate MODS and spawns in maints, I like the idea of stealing them*/
+ /// Currently "stored" hat. No armor or function will be inherited, ONLY the icon.
+ var/obj/item/clothing/head/attached_hat
+ /// Whitelist of attachable hats, read note in Initialize() below this line
+ var/static/list/attachable_hats_list
+
+/obj/item/mod/module/hat_stabilizer/Initialize(mapload)
+ . = ..()
+ attachable_hats_list = typecacheof(
+ //List of attachable hats. Make sure these and their subtypes are all tested, so they dont appear janky.
+ //This list should also be gimmicky, so captains can have fun. I.E. the Santahat, Pirate hat, Tophat, Chefhat...
+ //Yes, I said it, the captain should have fun.
+ list(
+ /obj/item/clothing/head/caphat,
+ /obj/item/clothing/head/crown,
+ /obj/item/clothing/head/centhat,
+ /obj/item/clothing/head/pirate,
+ /obj/item/clothing/head/santa,
+ /obj/item/clothing/head/hardhat/reindeer,
+ /obj/item/clothing/head/sombrero,
+ /obj/item/clothing/head/kitty,
+ /obj/item/clothing/head/rabbitears,
+ /obj/item/clothing/head/festive,
+ /obj/item/clothing/head/powdered_wig,
+ /obj/item/clothing/head/that,
+ /obj/item/clothing/head/nursehat,
+ /obj/item/clothing/head/chefhat,
+ /obj/item/clothing/head/papersack,
+ ))
+
+/obj/item/mod/module/hat_stabilizer/on_suit_activation()
+ RegisterSignal(mod.helmet, COMSIG_PARENT_EXAMINE, PROC_REF(add_examine))
+ RegisterSignal(mod.helmet, COMSIG_PARENT_ATTACKBY, PROC_REF(place_hat))
+ RegisterSignal(mod.helmet, COMSIG_CLICK_ALT, PROC_REF(remove_hat))
+
+/obj/item/mod/module/hat_stabilizer/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ if(attached_hat) //knock off the helmet if its on their head. Or, technically, auto-rightclick it for them; that way it saves us code, AND gives them the bubble
+ remove_hat(src, mod.wearer)
+ UnregisterSignal(mod.helmet, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(mod.helmet, COMSIG_PARENT_ATTACKBY)
+ UnregisterSignal(mod.helmet, COMSIG_CLICK_ALT)
+
+/obj/item/mod/module/hat_stabilizer/proc/add_examine(datum/source, mob/user, list/base_examine)
+ SIGNAL_HANDLER
+ if(attached_hat)
+ base_examine += span_notice("There's \a [attached_hat] placed on the helmet. Alt-click to remove it.")
+ else
+ base_examine += span_notice("There's nothing placed on the helmet. Yet.")
+
+/obj/item/mod/module/hat_stabilizer/proc/place_hat(datum/source, obj/item/hitting_item, mob/user)
+ SIGNAL_HANDLER
+ if(!istype(hitting_item, /obj/item/clothing/head))
+ return
+ if(!mod.active)
+ balloon_alert(user, "suit must be active!")
+ return
+ if(!is_type_in_typecache(hitting_item, attachable_hats_list))
+ balloon_alert(user, "this hat won't fit!")
+ return
+ if(attached_hat)
+ balloon_alert(user, "hat already attached!")
+ return
+ if(mod.wearer.transferItemToLoc(hitting_item, src, force = FALSE, silent = TRUE))
+ attached_hat = hitting_item
+ balloon_alert(user, "hat attached, alt-click to remove")
+ mod.wearer.update_inv_back(mod.slot_flags)
+
+/obj/item/mod/module/hat_stabilizer/generate_worn_overlay()
+ . = ..()
+ if(attached_hat)
+ . += attached_hat.build_worn_icon(default_layer = ABOVE_MOB_LAYER, default_icon_file = 'icons/mob/clothing/head.dmi')
+
+/obj/item/mod/module/hat_stabilizer/proc/remove_hat(datum/source, mob/user)
+ SIGNAL_HANDLER
+ . = SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!attached_hat)
+ return
+ attached_hat.forceMove(drop_location())
+ if(user.put_in_active_hand(attached_hat))
+ balloon_alert(user, "hat removed")
+ else
+ balloon_alert_to_viewers("the hat falls to the floor!")
+ attached_hat = null
+ mod.wearer.update_inv_back(mod.slot_flags)
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
new file mode 100644
index 000000000000..e735654ef2c5
--- /dev/null
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -0,0 +1,148 @@
+//Maint modules for MODsuits
+
+///Springlock Mechanism - allows your modsuit to activate faster, but reagents are very dangerous.
+/obj/item/mod/module/springlock
+ name = "MOD springlock module"
+ desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \
+ This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \
+ up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \
+ into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you?"
+ icon_state = "springlock"
+ complexity = 3 // it is inside every part of your suit, so
+ incompatible_modules = list(/obj/item/mod/module/springlock)
+
+/obj/item/mod/module/springlock/on_install()
+ mod.activation_step_time *= 0.5
+
+/obj/item/mod/module/springlock/on_uninstall(deleting = FALSE)
+ mod.activation_step_time *= 2
+
+/obj/item/mod/module/springlock/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_wearer_exposed))
+
+/obj/item/mod/module/springlock/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS)
+
+///Signal fired when wearer is exposed to reagents
+/obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message)
+ SIGNAL_HANDLER
+
+ if(!(methods & (VAPOR|PATCH|TOUCH)))
+ return //remove non-touch reagent exposure
+ to_chat(mod.wearer, span_danger("[src] makes an ominous click sound..."))
+ playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE)
+ addtimer(CALLBACK(src, PROC_REF(snap_shut)), rand(3 SECONDS, 5 SECONDS))
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block))
+
+///Signal fired when wearer attempts to activate/deactivate suits
+/obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user)
+ SIGNAL_HANDLER
+
+ balloon_alert(user, "springlocks aren't responding...?")
+ return MOD_CANCEL_ACTIVATE
+
+///Delayed death proc of the suit after the wearer is exposed to reagents
+/obj/item/mod/module/springlock/proc/snap_shut()
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+ if(!mod.wearer) //while there is a guaranteed user when on_wearer_exposed() fires, that isn't the same case for this proc
+ return
+ mod.wearer.visible_message("[src] inside [mod.wearer]'s [mod.name] snaps shut, mutilating the user inside!", span_userdanger("*SNAP*"))
+ mod.wearer.emote("scream")
+ playsound(mod.wearer, 'sound/effects/snap.ogg', 75, TRUE, frequency = 0.5)
+ playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5)
+ mod.wearer.apply_damage(500, BRUTE, forced = TRUE, spread_damage = TRUE) //boggers, bogchamp, etc
+ if(!HAS_TRAIT(mod.wearer, TRAIT_NODEATH))
+ mod.wearer.death() //just in case, for some reason, they're still alive
+ flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS)
+
+///Balloon Blower - Blows a balloon.
+/obj/item/mod/module/balloon
+ name = "MOD balloon blower module"
+ desc = "A strange module invented years ago by some ingenious mimes. It blows balloons."
+ icon_state = "bloon"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/balloon)
+ cooldown_time = 15 SECONDS
+
+/obj/item/mod/module/balloon/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(!do_after(mod.wearer, 10 SECONDS, target = mod))
+ return FALSE
+ mod.wearer.adjustOxyLoss(20)
+ playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE)
+ var/obj/item/toy/balloon/balloon = new(get_turf(src))
+ mod.wearer.put_in_hands(balloon)
+ drain_power(use_power_cost)
+
+///Paper Dispenser - Dispenses (sometimes burning) paper sheets.
+/obj/item/mod/module/paper_dispenser
+ name = "MOD paper dispenser module"
+ desc = "A simple module designed by the bureaucrats of Torch Bay. \
+ It dispenses 'warm, clean, and crisp sheets of paper' onto a nearby table. Usually."
+ icon_state = "paper_maker"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/paper_dispenser)
+ cooldown_time = 5 SECONDS
+ /// The total number of sheets created by this MOD. The more sheets, them more likely they set on fire.
+ var/num_sheets_dispensed = 0
+
+/obj/item/mod/module/paper_dispenser/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(!do_after(mod.wearer, 1 SECONDS, target = mod))
+ return FALSE
+
+ var/obj/item/paper/crisp_paper = new(get_turf(src))
+ crisp_paper.desc = "It's crisp and warm to the touch. Must be fresh."
+
+ var/obj/structure/table/nearby_table = locate() in range(1, mod.wearer)
+ playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
+ balloon_alert(mod.wearer, "dispensed paper[nearby_table ? " onto table":""]")
+
+ mod.wearer.put_in_hands(crisp_paper)
+ if(nearby_table)
+ mod.wearer.transferItemToLoc(crisp_paper, nearby_table.drop_location(), silent = FALSE)
+
+ // Up to a 30% chance to set the sheet on fire, +2% per sheet made
+ if(prob(min(num_sheets_dispensed * 2, 30)))
+ if(crisp_paper in mod.wearer.held_items)
+ mod.wearer.dropItemToGround(crisp_paper, force = TRUE)
+ crisp_paper.balloon_alert(mod.wearer, "pc load letter!")
+ crisp_paper.visible_message(span_warning("[crisp_paper] bursts into flames, it's too crisp!"))
+ crisp_paper.fire_act(1000, 100)
+
+ drain_power(use_power_cost)
+ num_sheets_dispensed++
+
+
+///Stamper - Extends a stamp that can switch between accept/deny modes.
+/obj/item/mod/module/stamp
+ name = "MOD stamper module"
+ desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \
+ able to switch between accept and deny modes."
+ icon_state = "stamp"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ device = /obj/item/stamp/mod
+ incompatible_modules = list(/obj/item/mod/module/stamp)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/stamp/mod
+ name = "MOD electronic stamp"
+ desc = "A high-power stamp, able to switch between accept and deny mode when used."
+
+/obj/item/stamp/mod/attack_self(mob/user, modifiers)
+ . = ..()
+ if(icon_state == "stamp-ok")
+ icon_state = "stamp-deny"
+ else
+ icon_state = "stamp-ok"
+ balloon_alert(user, "switched mode")
diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm
new file mode 100644
index 000000000000..798f065ffe7f
--- /dev/null
+++ b/code/modules/mod/modules/modules_medical.dm
@@ -0,0 +1,110 @@
+//Medical modules for MODsuits
+
+#define HEALTH_SCAN "Health"
+#define WOUND_SCAN "Wound"
+#define CHEM_SCAN "Chemical"
+
+///Health Analyzer - Gives the user a ranged health analyzer and their health status in the panel.
+/obj/item/mod/module/health_analyzer
+ name = "MOD health analyzer module"
+ desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \
+ allowing the user indepth information on the vitals and injuries of others even at a distance, \
+ all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \
+ but it's up to you to do something with it."
+ icon_state = "health"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/health_analyzer)
+ cooldown_time = 0.5 SECONDS
+ tgui_id = "health_analyzer"
+ /// Scanning mode, changes how we scan something.
+ var/mode = HEALTH_SCAN
+ /// List of all scanning modes.
+ var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN)
+
+/obj/item/mod/module/health_analyzer/add_ui_data()
+ . = ..()
+ .["userhealth"] = mod.wearer?.health || 0
+ .["usermaxhealth"] = mod.wearer?.getMaxHealth() || 0
+ .["userbrute"] = mod.wearer?.getBruteLoss() || 0
+ .["userburn"] = mod.wearer?.getFireLoss() || 0
+ .["usertoxin"] = mod.wearer?.getToxLoss() || 0
+ .["useroxy"] = mod.wearer?.getOxyLoss() || 0
+
+/obj/item/mod/module/health_analyzer/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!isliving(target) || !mod.wearer.can_read(src))
+ return
+ switch(mode)
+ if(HEALTH_SCAN)
+ healthscan(mod.wearer, target)
+ if(CHEM_SCAN)
+ chemscan(mod.wearer, target)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/health_analyzer/get_configuration()
+ . = ..()
+ .["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes)
+
+/obj/item/mod/module/health_analyzer/configure_edit(key, value)
+ switch(key)
+ if("mode")
+ mode = value
+
+#undef HEALTH_SCAN
+#undef WOUND_SCAN
+#undef CHEM_SCAN
+
+///Quick Carry - Lets the user carry bodies quicker.
+/obj/item/mod/module/quick_carry
+ name = "MOD quick carry module"
+ desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \
+ or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat."
+ icon_state = "carry"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/quick_carry)
+
+/obj/item/mod/module/quick_carry/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE)
+ REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/advanced
+ name = "MOD advanced quick carry module"
+ removable = FALSE
+ complexity = 0
+
+/obj/item/mod/module/quick_carry/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
+ ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE)
+ REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
+ REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
+
+///Injector - Gives the suit an extendable large-capacity piercing syringe.
+/obj/item/mod/module/injector
+ name = "MOD injector module"
+ desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \
+ with a tip fine enough to locate the emergency injection ports on any suit of armor, \
+ penetrating it with ease. Even yours."
+ icon_state = "injector"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ device = /obj/item/reagent_containers/syringe/mod
+ incompatible_modules = list(/obj/item/mod/module/injector)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/reagent_containers/syringe/mod
+ name = "MOD injector syringe"
+ desc = "A high-capacity syringe, with a tip fine enough to locate \
+ the emergency injection ports on any suit of armor, penetrating it with ease. Even yours."
+ amount_per_transfer_from_this = 30
+ possible_transfer_amounts = list(5, 10, 15, 20, 30)
+ volume = 30
diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm
new file mode 100644
index 000000000000..69da2287eb72
--- /dev/null
+++ b/code/modules/mod/modules/modules_ninja.dm
@@ -0,0 +1,446 @@
+//Ninja modules for MODsuits
+
+///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked.
+/obj/item/mod/module/stealth
+ name = "MOD prototype cloaking module"
+ desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \
+ to bend light around the user, as well as mimetic materials to make the surface of the suit match the \
+ surroundings based off sensor data. For some reason, this tech is rarely seen."
+ icon_state = "cloak"
+ module_type = MODULE_TOGGLE
+ complexity = 4
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 10
+ incompatible_modules = list(/obj/item/mod/module/stealth)
+ cooldown_time = 5 SECONDS
+ /// Whether or not the cloak turns off on bumping.
+ var/bumpoff = TRUE
+ /// The alpha applied when the cloak is on.
+ var/stealth_alpha = 50
+
+/obj/item/mod/module/stealth/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(bumpoff)
+ RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, PROC_REF(unstealth))
+ RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
+ RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act))
+ RegisterSignal(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND/*, COMSIG_ATOM_HITBY*/, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), PROC_REF(unstealth))
+ animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(bumpoff)
+ UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
+ UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT/*, COMSIG_ATOM_HITBY*/, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED))
+ animate(mod.wearer, alpha = 255, time = 1.5 SECONDS)
+
+/obj/item/mod/module/stealth/proc/unstealth(datum/source)
+ SIGNAL_HANDLER
+
+ to_chat(mod.wearer, span_warning("[src] gets discharged from contact!"))
+ do_sparks(2, TRUE, src)
+ drain_power(use_power_cost)
+ on_deactivation(display_message = TRUE, deleting = FALSE)
+
+/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target)
+ SIGNAL_HANDLER
+
+ if(!isliving(target))
+ return
+ unstealth(source)
+
+/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/projectile/projectile)
+ SIGNAL_HANDLER
+
+ if(projectile.nodamage)
+ return
+ unstealth(source)
+
+//Advanced Cloaking - Doesn't turf off on bump, less power drain, more stealthy.
+/obj/item/mod/module/stealth/ninja
+ name = "MOD advanced cloaking module"
+ desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \
+ The field has been tuned to be even more responsive and fast-acting, with enough stability to \
+ continue operation of the field even if the user bumps into others. \
+ The power draw has been reduced drastically, making this perfect for activities like \
+ standing near sentry turrets for extended periods of time."
+ icon_state = "cloak_ninja"
+ bumpoff = FALSE
+ stealth_alpha = 20
+ active_power_cost = DEFAULT_CHARGE_DRAIN
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ cooldown_time = 3 SECONDS
+
+///Camera Vision - Prevents flashes, blocks tracking.
+/obj/item/mod/module/welding/camera_vision
+ name = "MOD camera vision module"
+ desc = "A module installed into the suit's helmet. This specialized piece of technology is built for subterfuge, \
+ replacing the standard visor with a nanotech display; capable of displaying specialized imagery at \
+ just the right frequency to jam all known forms of camera tracking and facial recognition, \
+ as well as automatically dimming incoming flashes of light to protect the user's eyes. Become the unseen."
+ icon_state = "welding_camera"
+ removable = FALSE
+ complexity = 0
+ overlay_state_inactive = null
+
+/obj/item/mod/module/welding/camera_vision/on_suit_activation()
+ . = ..()
+ RegisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK, PROC_REF(can_track))
+
+/obj/item/mod/module/welding/camera_vision/on_suit_deactivation(deleting = FALSE)
+ . = ..()
+ UnregisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK)
+
+/obj/item/mod/module/welding/camera_vision/proc/can_track(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ return COMPONENT_CANT_TRACK
+
+//Ninja Star Dispenser - Dispenses ninja stars.
+/obj/item/mod/module/dispenser/ninja
+ name = "MOD ninja star dispenser module"
+ desc = "This piece of Spider Clan technology can exploit known energy-matter equivalence principles, \
+ using the nanites already hosted in the wearer's suit to transmute into monomolecular shuriken. \
+ While these lack the intense bleeding edge of conventional throwing stars, \
+ they have been set to electrify fleeing targets; and branded with the Spider Clan symbol."
+ dispense_type = /obj/item/throwing_star/stamina
+ cooldown_time = 0.5 SECONDS
+
+///Hacker - This module hooks onto your right-clicks with empty hands and causes ninja actions.
+/obj/item/mod/module/hacker
+ name = "MOD hacker module"
+ desc = "Built for one purpose, electronic warfare, this module is built into the hands. \
+ Using near-field communication alongside precise electro-stimulation of the wires in machines, \
+ this decker's dream is normally used to pass through doors like a phantom. \
+ It's also capable of non-precise electro-stimulation of an assassin-saboteur's opponents on disarming attacks."
+ icon_state = "hacker"
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/hacker)
+ /// Minimum amount of power we can drain in a single drain action
+ var/mindrain = 200
+ /// Maximum amount of power we can drain in a single drain action
+ var/maxdrain = 400
+ /// Whether or not the communication console hack was used to summon another antagonist.
+ var/communication_console_hack_success = FALSE
+ /// How many times the module has been used to force open doors.
+ var/door_hack_counter = 0
+ ///Used for the research objective (see antagonist file)
+ var/datum/techweb/stored_research
+
+/obj/item/mod/module/hacker/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, PROC_REF(hack))
+
+/obj/item/mod/module/hacker/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_HUMAN_EARLY_UNARMED_ATTACK)
+
+/obj/item/mod/module/hacker/proc/hack(mob/living/carbon/human/source, atom/target, proximity, modifiers)
+ SIGNAL_HANDLER
+
+ if(!LAZYACCESS(modifiers, RIGHT_CLICK) || !proximity)
+ return NONE
+ target.add_fingerprint(mod.wearer)
+ return target.ninjadrain_act(mod.wearer, src)
+
+/obj/item/mod/module/hacker/proc/charge_message(atom/drained_atom, drain_amount)
+ if(drain_amount)
+ to_chat(mod.wearer, span_notice("Получено [drain_amount] единиц энергии с [drained_atom]."))
+ else
+ to_chat(mod.wearer, span_warning("[drained_atom] истощен, необходимо найти другой источник питания!"))
+
+///Weapon Recall - Teleports your katana to you, prevents gun use.
+/obj/item/mod/module/weapon_recall
+ name = "MOD weapon recall module"
+ desc = "The cornerstone of a clanmember's life as a blademaster, and a module symbolizing their eternal bond with their weapon. \
+ This hooks to the micro bluespace drive inside an energy katana's handle, capable of recalling it to the user's \
+ skilled hands wherever they are. However, those that make such a bond with their weapon are cursed to \
+ fusing their existence with acts of combat, with a singular purpose; Cutting Down Their Opponent. \
+ Their hand a hand that is cutting, their body a body that is cutting, their mind, a mind that is cutting. \
+ Ranged weapons are forbidden."
+ icon_state = "recall"
+ removable = FALSE
+ module_type = MODULE_USABLE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/weapon_recall)
+ cooldown_time = 0.5 SECONDS
+ /// The item linked to the module that will get recalled.
+ var/obj/item/linked_weapon
+ /// The accepted typepath we can link to.
+ var/accepted_type = /obj/item/energy_katana
+
+/obj/item/mod/module/weapon_recall/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_NOGUNS, MOD_TRAIT)
+
+/obj/item/mod/module/weapon_recall/on_suit_deactivation(deleting = FALSE)
+ REMOVE_TRAIT(mod.wearer, TRAIT_NOGUNS, MOD_TRAIT)
+
+/obj/item/mod/module/weapon_recall/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(!linked_weapon)
+ var/obj/item/weapon_to_link = mod.wearer.is_holding_item_of_type(accepted_type)
+ if(!weapon_to_link)
+ balloon_alert(mod.wearer, "can't locate weapon!")
+ return
+ set_weapon(weapon_to_link)
+ balloon_alert(mod.wearer, "[linked_weapon.name] linked")
+ return
+ if(linked_weapon in mod.wearer.get_all_contents())
+ balloon_alert(mod.wearer, "already on self!")
+ return
+ var/distance = get_dist(mod.wearer, linked_weapon)
+ var/in_view = (linked_weapon in view(mod.wearer))
+ if(!in_view && !drain_power(use_power_cost * distance))
+ balloon_alert(mod.wearer, "not enough charge!")
+ return
+ linked_weapon.forceMove(linked_weapon.drop_location())
+ if(in_view)
+ do_sparks(5, FALSE, linked_weapon)
+ mod.wearer.visible_message(span_danger("[linked_weapon] flies towards [mod.wearer]!"),span_warning("You hold out your hand and [linked_weapon] flies towards you!"))
+ linked_weapon.throw_at(mod.wearer, distance+1, linked_weapon.throw_speed, mod.wearer)
+ else
+ recall_weapon()
+
+/obj/item/mod/module/weapon_recall/proc/set_weapon(obj/item/weapon)
+ linked_weapon = weapon
+ RegisterSignal(linked_weapon, COMSIG_MOVABLE_IMPACT, PROC_REF(catch_weapon))
+ RegisterSignal(linked_weapon, COMSIG_PARENT_QDELETING, PROC_REF(deleted_weapon))
+
+/obj/item/mod/module/weapon_recall/proc/recall_weapon(caught = FALSE)
+ linked_weapon.forceMove(get_turf(src))
+ var/alert = ""
+ if(mod.wearer.put_in_hands(linked_weapon))
+ alert = "[linked_weapon.name] teleports to your hand"
+ else if(mod.wearer.equip_to_slot_if_possible(linked_weapon, ITEM_SLOT_BELT, disable_warning = TRUE))
+ alert = "[linked_weapon.name] sheathes itself in your belt"
+ else
+ alert = "[linked_weapon.name] teleports under you"
+ if(caught)
+ if(mod.wearer.is_holding(linked_weapon))
+ alert = "you catch [linked_weapon.name]"
+ else
+ alert = "[linked_weapon.name] lands under you"
+ else
+ do_sparks(5, FALSE, linked_weapon)
+ if(alert)
+ balloon_alert(mod.wearer, alert)
+
+/obj/item/mod/module/weapon_recall/proc/catch_weapon(obj/item/source, atom/hit_atom, datum/thrownthing/thrownthing)
+ SIGNAL_HANDLER
+
+ if(!mod)
+ return
+ if(hit_atom != mod.wearer)
+ return
+ INVOKE_ASYNC(src, PROC_REF(recall_weapon), TRUE)
+ return COMPONENT_MOVABLE_IMPACT_NEVERMIND
+
+/obj/item/mod/module/weapon_recall/proc/deleted_weapon(obj/item/source)
+ SIGNAL_HANDLER
+
+ linked_weapon = null
+
+//Reinforced DNA Lock - Gibs if wrong DNA, emp-proof.
+/obj/item/mod/module/dna_lock/reinforced
+ name = "MOD reinforced DNA lock module"
+ desc = "A module which engages with the various locks and seals tied to the suit's systems, \
+ enabling it to only be worn by someone corresponding with the user's exact DNA profile. \
+ Due to utilizing a skintight dampening shield, this one is entirely sealed against electromagnetic interference; \
+ it also dutifully protects the secrets of the Spider Clan from unknowing outsiders."
+ icon_state = "dnalock_ninja"
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+
+/obj/item/mod/module/dna_lock/reinforced/on_mod_activation(datum/source, mob/user)
+ . = ..()
+ if(. != MOD_CANCEL_ACTIVATE || !isliving(user))
+ return
+ var/mob/living/living_user = user
+ to_chat(living_user, span_danger("fATaL EERRoR: 382200-*#00CODE RED\nUNAUTHORIZED USE DETECteD\nCoMMENCING SUB-R0UTIN3 13...\nTERMInATING U-U-USER..."))
+ living_user.gib()
+
+/obj/item/mod/module/dna_lock/reinforced/on_emp(datum/source, severity)
+ return
+
+//EMP Pulse - In addition to normal shielding, can also launch an EMP itself.
+/obj/item/mod/module/emp_shield/pulse
+ name = "MOD EMP pulse module"
+ desc = "This module is normally set to activate on dramatic gestures, inverting and expanding the suit's \
+ EMP dampening shield to cause an electromagnetic pulse of its own. While this won't interfere with the wearer, \
+ it will piss off everyone around them."
+ icon_state = "emp_pulse"
+ module_type = MODULE_USABLE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 10
+ cooldown_time = 8 SECONDS
+
+/obj/item/mod/module/emp_shield/pulse/on_use()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/effects/empulse.ogg', 60, TRUE)
+ empulse(src, heavy_range = 4, light_range = 6)
+ drain_power(use_power_cost)
+
+///Status Readout - Puts a lot of information including health, nutrition, fingerprints, temperature to the suit TGUI.
+/obj/item/mod/module/status_readout
+ name = "MOD status readout module"
+ desc = "A once-common module, this technology went unfortunately out of fashion; \
+ and right into the arachnid grip of the Spider Clan. This hooks into the suit's spine, \
+ capable of capturing and displaying all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \
+ and even useful information such as their overall health and wellness."
+ icon_state = "status"
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
+ incompatible_modules = list(/obj/item/mod/module/status_readout)
+ tgui_id = "status_readout"
+
+/obj/item/mod/module/status_readout/add_ui_data()
+ . = ..()
+ .["statustime"] = station_time_timestamp()
+ .["statusid"] = GLOB.round_id
+ .["statushealth"] = mod.wearer?.health || 0
+ .["statusmaxhealth"] = mod.wearer?.getMaxHealth() || 0
+ .["statusbrute"] = mod.wearer?.getBruteLoss() || 0
+ .["statusburn"] = mod.wearer?.getFireLoss() || 0
+ .["statustoxin"] = mod.wearer?.getToxLoss() || 0
+ .["statusoxy"] = mod.wearer?.getOxyLoss() || 0
+ .["statustemp"] = mod.wearer?.bodytemperature || 0
+ .["statusnutrition"] = mod.wearer?.nutrition || 0
+ //.["statusfingerprints"] = mod.wearer ? md5(mod.wearer.dna.unique_identity) : null
+ .["statusdna"] = mod.wearer?.dna.unique_enzymes
+ .["statusviruses"] = null
+ if(!length(mod.wearer?.diseases))
+ return
+ var/list/viruses = list()
+ for(var/datum/disease/virus as anything in mod.wearer.diseases)
+ var/list/virus_data = list()
+ virus_data["name"] = virus.name
+ virus_data["type"] = virus.spread_text
+ virus_data["stage"] = virus.stage
+ virus_data["maxstage"] = virus.max_stages
+ virus_data["cure"] = virus.cure_text
+ viruses += list(virus_data)
+ .["statusviruses"] = viruses
+
+///Energy Net - Ensnares enemies in a net that prevents movement.
+/obj/item/mod/module/energy_net
+ name = "MOD energy net module"
+ desc = "A custom-built net-thrower. While conventional implementations of this capturing device \
+ tilize monomolecular fibers or cutting razorwire, this uses hardlight technology to deploy a \
+ trapping field capable of immobilizing even the strongest opponents."
+ icon_state = "energy_net"
+ removable = FALSE
+ module_type = MODULE_ACTIVE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 6
+ incompatible_modules = list(/obj/item/mod/module/energy_net)
+ cooldown_time = 1.5 SECONDS
+
+/obj/item/mod/module/energy_net/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!isliving(target))
+ balloon_alert(mod.wearer, "invalid target!")
+ return
+ var/mob/living/living_target = target
+ if(locate(/obj/structure/energy_net) in get_turf(living_target))
+ balloon_alert(mod.wearer, "already trapped!")
+ return
+ for(var/turf/between_turf as anything in get_line(get_turf(mod.wearer), get_turf(living_target)))
+ if(between_turf.density)
+ balloon_alert(mod.wearer, "not through obstacles!")
+ return
+ //if(IS_SPACE_NINJA(mod.wearer))
+ // mod.wearer.say("Get over here!", forced = type)
+ mod.wearer.Beam(living_target, "n_beam", time = 1.5 SECONDS)
+ var/obj/structure/energy_net/net = new /obj/structure/energy_net(living_target.drop_location())
+ net.affecting = living_target
+ mod.wearer.visible_message(span_danger("[mod.wearer] caught [living_target] with an energy net!"), span_notice("You caught [living_target] with an energy net!"))
+ if(living_target.buckled)
+ living_target.buckled.unbuckle_mob(living_target, force = TRUE)
+ net.buckle_mob(living_target, force = TRUE)
+ drain_power(use_power_cost)
+
+///Adrenaline Boost - Stops all stuns the ninja is affected with, increases his speed.
+/obj/item/mod/module/adrenaline_boost
+ name = "MOD adrenaline boost module"
+ desc = "The secrets of the Spider Clan are many. The exact specifications of their suits, \
+ the techniques they use to make every singular cut make their enemies weep with admiration, \
+ but one of their greatest mysteries is the chemical compound their assassin-saboteurs use in times of need. \
+ It's capable of clearing any fatigue whatsoever from the user, any immobilizing effect, and can even \
+ cure total paralysis. All that's known is that the fluid requires radiation to properly 'cook,' \
+ so this module demands radium to be refilled with."
+ icon_state = "adrenaline_boost"
+ removable = FALSE
+ module_type = MODULE_USABLE
+ incompatible_modules = list(/obj/item/mod/module/adrenaline_boost)
+ cooldown_time = 12 SECONDS
+ /// What reagent we need to refill?
+ var/reagent_required = /datum/reagent/uranium/radium
+ /// How much of a reagent we need to refill the boost.
+ var/reagent_required_amount = 20
+
+/obj/item/mod/module/adrenaline_boost/Initialize(mapload)
+ . = ..()
+ create_reagents(reagent_required_amount)
+ reagents.add_reagent(reagent_required, reagent_required_amount)
+
+/obj/item/mod/module/adrenaline_boost/on_use()
+ if(!reagents.has_reagent(reagent_required, reagent_required_amount))
+ balloon_alert(mod.wearer, "no charge!")
+ return
+ . = ..()
+ if(!.)
+ return
+ //if(IS_SPACE_NINJA(mod.wearer))
+ // mod.wearer.say(pick_list_replacements(NINJA_FILE, "lines"), forced = type)
+ to_chat(mod.wearer, span_notice("You have used the adrenaline boost."))
+ mod.wearer.SetUnconscious(0)
+ mod.wearer.SetStun(0)
+ mod.wearer.SetKnockdown(0)
+ mod.wearer.SetImmobilized(0)
+ mod.wearer.SetParalyzed(0)
+ mod.wearer.adjustStaminaLoss(-200)
+ mod.wearer.stuttering = 0
+ mod.wearer.reagents.add_reagent(/datum/reagent/medicine/stimulants, 5)
+ reagents.remove_reagent(reagent_required, reagents.total_volume * 0.75)
+ addtimer(CALLBACK(src, PROC_REF(boost_aftereffects), mod.wearer), 7 SECONDS)
+
+/obj/item/mod/module/adrenaline_boost/on_install()
+ RegisterSignal(mod, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby))
+
+/obj/item/mod/module/adrenaline_boost/on_uninstall(deleting)
+ UnregisterSignal(mod, COMSIG_PARENT_ATTACKBY)
+
+/obj/item/mod/module/adrenaline_boost/attackby(obj/item/attacking_item, mob/user, params)
+ if(charge_boost(attacking_item, user))
+ return TRUE
+ return ..()
+
+/obj/item/mod/module/adrenaline_boost/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
+ SIGNAL_HANDLER
+
+ if(charge_boost(attacking_item, user))
+ return COMPONENT_NO_AFTERATTACK
+ return NONE
+
+/obj/item/mod/module/adrenaline_boost/proc/charge_boost(obj/item/attacking_item, mob/user)
+ if(!attacking_item.is_open_container())
+ return FALSE
+ if(reagents.has_reagent(reagent_required, reagent_required_amount))
+ balloon_alert(mod.wearer, "already charged!")
+ return FALSE
+ if(!attacking_item.reagents.trans_id_to(src, reagent_required, reagent_required_amount))
+ return FALSE
+ balloon_alert(mod.wearer, "charge [reagents.has_reagent(reagent_required, reagent_required_amount) ? "fully" : "partially"] reloaded")
+ return TRUE
+
+/obj/item/mod/module/adrenaline_boost/proc/boost_aftereffects(mob/affected_mob)
+ if(!affected_mob)
+ return
+ reagents.trans_to(affected_mob, reagents.total_volume)
+ to_chat(affected_mob, span_danger("You are beginning to feel the after-effect of the injection."))
diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm
new file mode 100644
index 000000000000..02025ea1b420
--- /dev/null
+++ b/code/modules/mod/modules/modules_science.dm
@@ -0,0 +1,132 @@
+//Science modules for MODsuits
+
+///Reagent Scanner - Lets the user scan reagents.
+/obj/item/mod/module/reagent_scanner
+ name = "MOD reagent scanner module"
+ desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \
+ containers and projecting the information in an easy-to-read format on the wearer's display. \
+ It cannot detect flavors, so that's up to you."
+ icon_state = "scanner"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/reagent_scanner)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/mod/module/reagent_scanner/on_activation()
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.clothing_flags |= SCAN_REAGENTS
+
+/obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.clothing_flags &= ~SCAN_REAGENTS
+
+/obj/item/mod/module/reagent_scanner/advanced
+ name = "MOD advanced reagent scanner module"
+ complexity = 0
+ removable = FALSE
+ var/explosion_detection_dist = 21
+
+/obj/item/mod/module/reagent_scanner/advanced/on_activation()
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.clothing_flags |= SCAN_REAGENTS
+ RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, PROC_REF(sense_explosion))
+
+/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.clothing_flags |= SCAN_REAGENTS
+ UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION)
+
+/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter,
+ devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
+ SIGNAL_HANDLER
+ var/turf/wearer_turf = get_turf(mod.wearer)
+ if(wearer_turf.z != epicenter.z)
+ return
+ if(get_dist(epicenter, wearer_turf) > explosion_detection_dist)
+ return
+ to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]"))
+
+///Anti-Gravity - Makes the user weightless.
+/obj/item/mod/module/anomaly_locked/antigrav
+ name = "MOD anti-gravity module"
+ desc = "A module that uses a gravitational core to make the user completely weightless."
+ icon_state = "antigrav"
+ module_type = MODULE_TOGGLE
+ complexity = 3
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.7
+ incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
+ cooldown_time = 0.5 SECONDS
+ accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav)
+
+/obj/item/mod/module/anomaly_locked/antigrav/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(mod.wearer.has_gravity())
+ new /obj/effect/temp_visual/mook_dust(get_turf(src))
+ mod.wearer.AddElement(/datum/element/forced_gravity, 0)
+ mod.wearer.update_gravity(mod.wearer.has_gravity())
+ playsound(src, 'sound/effects/gravhit.ogg', 50)
+
+/obj/item/mod/module/anomaly_locked/antigrav/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ mod.wearer.RemoveElement(/datum/element/forced_gravity, 0)
+ mod.wearer.update_gravity(mod.wearer.has_gravity())
+ if(deleting)
+ return
+ if(mod.wearer.has_gravity())
+ new /obj/effect/temp_visual/mook_dust(get_turf(src))
+ playsound(src, 'sound/effects/gravhit.ogg', 50)
+
+/obj/item/mod/module/anomaly_locked/antigrav/prebuilt
+ prebuilt = TRUE
+
+///Teleporter - Lets the user teleport to a nearby location.
+/obj/item/mod/module/anomaly_locked/teleporter
+ name = "MOD teleporter module"
+ desc = "A module that uses a bluespace core to let the user transport their particles elsewhere."
+ icon_state = "teleporter"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ cooldown_time = 5 SECONDS
+ accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace)
+ /// Time it takes to teleport
+ var/teleport_time = 3 SECONDS
+
+/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/turf/open/target_turf = get_turf(target)
+ if(!istype(target_turf) || target_turf.is_blocked_turf() || !(target_turf in view(mod.wearer)))
+ balloon_alert(mod.wearer, "invalid target!")
+ return
+ balloon_alert(mod.wearer, "teleporting...")
+ var/matrix/pre_matrix = matrix()
+ pre_matrix.Scale(4, 0.25)
+ var/matrix/post_matrix = matrix()
+ post_matrix.Scale(0.25, 4)
+ animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
+ if(!do_after(mod.wearer, teleport_time, target = mod))
+ balloon_alert(mod.wearer, "interrupted!")
+ animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
+ return
+ animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
+ if(!do_teleport(mod.wearer, target_turf, asoundin = 'sound/effects/phasein.ogg'))
+ return
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/anomaly_locked/teleporter/prebuilt
+ prebuilt = TRUE
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
new file mode 100644
index 000000000000..d3ac53846461
--- /dev/null
+++ b/code/modules/mod/modules/modules_security.dm
@@ -0,0 +1,136 @@
+//Security modules for MODsuits
+
+///Magnetic Harness - Automatically puts guns in your suit storage when you drop them.
+/obj/item/mod/module/magnetic_harness
+ name = "MOD magnetic harness module"
+ desc = "Based off old TerraGov harness kits, this magnetic harness automatically attaches dropped guns back to the wearer."
+ icon_state = "mag_harness"
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/magnetic_harness)
+ /// Time before we activate the magnet.
+ var/magnet_delay = 0.8 SECONDS
+ /// The typecache of all guns we allow.
+ var/static/list/guns_typecache
+ /// The guns already allowed by the modsuit chestplate.
+ var/list/already_allowed_guns = list()
+
+/obj/item/mod/module/magnetic_harness/Initialize(mapload)
+ . = ..()
+ if(!guns_typecache)
+ guns_typecache = typecacheof(list(/obj/item/gun/ballistic, /obj/item/gun/energy, /obj/item/gun/grenadelauncher, /obj/item/gun/chem, /obj/item/gun/syringe))
+
+/obj/item/mod/module/magnetic_harness/on_install()
+ already_allowed_guns = guns_typecache & mod.chestplate.allowed
+ mod.chestplate.allowed |= guns_typecache
+
+/obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE)
+ if(deleting)
+ return
+ mod.chestplate.allowed -= (guns_typecache - already_allowed_guns)
+
+/obj/item/mod/module/magnetic_harness/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(check_dropped_item))
+
+/obj/item/mod/module/magnetic_harness/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM)
+
+/obj/item/mod/module/magnetic_harness/proc/check_dropped_item(datum/source, obj/item/dropped_item, force, new_location)
+ SIGNAL_HANDLER
+
+ if(!is_type_in_typecache(dropped_item, guns_typecache))
+ return
+ if(new_location != get_turf(src))
+ return
+ addtimer(CALLBACK(src, PROC_REF(pick_up_item), dropped_item), magnet_delay)
+
+/obj/item/mod/module/magnetic_harness/proc/pick_up_item(obj/item/item)
+ if(!isturf(item.loc) || !item.Adjacent(mod.wearer))
+ return
+ if(!mod.wearer.equip_to_slot_if_possible(item, ITEM_SLOT_SUITSTORE, qdel_on_fail = FALSE, disable_warning = TRUE))
+ return
+ playsound(src, 'sound/items/modsuit/magnetic_harness.ogg', 50, TRUE)
+ balloon_alert(mod.wearer, "[item] reattached")
+ drain_power(use_power_cost)
+
+///Holster - Instantly holsters any not huge gun.
+/obj/item/mod/module/holster
+ name = "MOD holster module"
+ desc = "Based off typical storage compartments, this system allows the suit to holster a \
+ standard firearm across its surface and allow for extremely quick retrieval. \
+ While some users prefer the chest, others the forearm for quick deployment, \
+ some law enforcement prefer the holster to extend from the thigh."
+ icon_state = "holster"
+ module_type = MODULE_USABLE
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/holster)
+ cooldown_time = 0.5 SECONDS
+ allowed_inactive = TRUE
+ /// Gun we have holstered.
+ var/obj/item/gun/holstered
+
+/obj/item/mod/module/holster/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(!holstered)
+ var/obj/item/gun/holding = mod.wearer.get_active_held_item()
+ if(!holding)
+ balloon_alert(mod.wearer, "nothing to holster!")
+ return
+ if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY)
+ balloon_alert(mod.wearer, "it doesn't fit!")
+ return
+ if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE))
+ holstered = holding
+ balloon_alert(mod.wearer, "weapon holstered")
+ playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
+ else if(mod.wearer.put_in_active_hand(holstered, forced = FALSE, ignore_animation = TRUE))
+ balloon_alert(mod.wearer, "weapon drawn")
+ playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
+ else
+ balloon_alert(mod.wearer, "holster full!")
+
+/obj/item/mod/module/holster/on_uninstall(deleting = FALSE)
+ if(holstered)
+ holstered.forceMove(drop_location())
+
+/obj/item/mod/module/holster/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == holstered)
+ holstered = null
+
+/obj/item/mod/module/holster/Destroy()
+ QDEL_NULL(holstered)
+ return ..()
+
+///Megaphone - Lets you speak loud.
+/obj/item/mod/module/megaphone
+ name = "MOD megaphone module"
+ desc = "A microchip megaphone linked to a MODsuit, for very important purposes, like: loudness."
+ icon_state = "megaphone"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/megaphone)
+ cooldown_time = 0.5 SECONDS
+ /// List of spans we add to the speaker.
+ var/list/voicespan = list(SPAN_COMMAND)
+
+/obj/item/mod/module/megaphone/on_activation()
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(mod.wearer, COMSIG_MOB_SAY, PROC_REF(handle_speech))
+
+/obj/item/mod/module/megaphone/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ UnregisterSignal(mod.wearer, COMSIG_MOB_SAY)
+
+/obj/item/mod/module/megaphone/proc/handle_speech(datum/source, list/speech_args)
+ SIGNAL_HANDLER
+
+ speech_args[SPEECH_SPANS] |= voicespan
+ drain_power(use_power_cost)
diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm
new file mode 100644
index 000000000000..e983bbc3dbc0
--- /dev/null
+++ b/code/modules/mod/modules/modules_service.dm
@@ -0,0 +1,56 @@
+//Service modules for MODsuits
+
+///Bike Horn - Plays a bike horn sound.
+/obj/item/mod/module/bikehorn
+ name = "MOD bike horn module"
+ desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \
+ precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound."
+ icon_state = "bikehorn"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/bikehorn)
+ cooldown_time = 1 SECONDS
+
+/obj/item/mod/module/bikehorn/on_use()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE)
+ drain_power(use_power_cost)
+
+///Microwave Beam - Microwaves items instantly.
+/obj/item/mod/module/microwave_beam
+ name = "MOD microwave beam module"
+ desc = "An oddly domestic device, this module is installed into the user's palm, \
+ hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \
+ allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes."
+ icon_state = "microwave_beam"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ incompatible_modules = list(/obj/item/mod/module/microwave_beam)
+ cooldown_time = 10 SECONDS
+
+/obj/item/mod/module/microwave_beam/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!istype(target, /obj/item))
+ return
+ if(!isturf(target.loc))
+ balloon_alert(mod.wearer, "must be on the floor!")
+ return
+ var/obj/item/microwave_target = target
+ var/datum/effect_system/spark_spread/spark_effect = new()
+ spark_effect.set_up(2, 1, mod.wearer)
+ spark_effect.start()
+ mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5)
+ if(microwave_target.microwave_act())
+ playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE)
+ else
+ balloon_alert(mod.wearer, "can't be microwaved!")
+ var/datum/effect_system/spark_spread/spark_effect_two = new()
+ spark_effect_two.set_up(2, 1, microwave_target)
+ spark_effect_two.start()
+ drain_power(use_power_cost)
diff --git a/code/modules/mod/modules/modules_storage.dm b/code/modules/mod/modules/modules_storage.dm
new file mode 100644
index 000000000000..25caad6806f7
--- /dev/null
+++ b/code/modules/mod/modules/modules_storage.dm
@@ -0,0 +1,60 @@
+/obj/item/mod/module/storage
+ name = "MOD storage module"
+ desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \
+ the surface of the suit, useful for storing various bits, and or bobs."
+ icon_state = "storage"
+ complexity = 3
+ incompatible_modules = list(/obj/item/mod/module/storage)
+ var/datum/component/storage/concrete/storage
+ var/max_w_class = WEIGHT_CLASS_NORMAL
+ var/max_combined_w_class = 15
+ var/max_items = 7
+
+/obj/item/mod/module/storage/Initialize(mapload)
+ . = ..()
+ storage = AddComponent(/datum/component/storage/concrete)
+ storage.max_w_class = max_w_class
+ storage.max_combined_w_class = max_combined_w_class
+ storage.max_items = max_items
+ storage.allow_big_nesting = TRUE
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+
+/obj/item/mod/module/storage/on_install()
+ var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage)
+ modstorage.max_w_class = max_w_class
+ modstorage.max_combined_w_class = max_combined_w_class
+ modstorage.max_items = max_items
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
+
+/obj/item/mod/module/storage/on_uninstall(deleting = FALSE)
+ var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage)
+ storage.slaves -= modstorage
+ qdel(modstorage)
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+
+/obj/item/mod/module/storage/large_capacity
+ name = "MOD expanded storage module"
+ desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \
+ is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \
+ whether smuggling, or simply hauling."
+ icon_state = "storage_large"
+ max_combined_w_class = 21
+ max_items = 14
+
+/obj/item/mod/module/storage/syndicate
+ name = "MOD syndicate storage module"
+ desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \
+ esoteric technology to compress the physical matter of items put inside of them, \
+ essentially shrinking items for much easier and more portable storage."
+ icon_state = "storage_syndi"
+ max_combined_w_class = 30
+ max_items = 21
+
+/obj/item/mod/module/storage/bluespace
+ name = "MOD bluespace storage module"
+ desc = "A storage system developed by Nanotrasen, these compartments employ \
+ miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
+ icon_state = "storage_large"
+ max_w_class = WEIGHT_CLASS_GIGANTIC
+ max_combined_w_class = 60
+ max_items = 21
diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm
new file mode 100644
index 000000000000..04f0aaf73ad1
--- /dev/null
+++ b/code/modules/mod/modules/modules_supply.dm
@@ -0,0 +1,306 @@
+//Supply modules for MODsuits
+
+///Internal GPS - Extends a GPS you can use.
+/obj/item/mod/module/gps
+ name = "MOD internal GPS module"
+ desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \
+ down to the exact coordinates. This information is fed to a central database viewable from the device itself, \
+ though using it to help people is up to you."
+ icon_state = "gps"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/gps)
+ cooldown_time = 0.5 SECONDS
+ allowed_inactive = TRUE
+
+/obj/item/mod/module/gps/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/gps/item, "MOD0")
+
+/obj/item/mod/module/gps/on_use()
+ . = ..()
+ if(!.)
+ return
+ attack_self(mod.wearer)
+
+///Hydraulic Clamp - Lets you pick up and drop crates.
+/obj/item/mod/module/clamp
+ name = "MOD hydraulic clamp module"
+ desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \
+ However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \
+ A lot of people would say that loading cargo is a dull job, but you could not disagree more."
+ icon_state = "clamp"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/clamp)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_clamp"
+ overlay_state_active = "module_clamp_on"
+ /// Time it takes to load a crate.
+ var/load_time = 3 SECONDS
+ /// The max amount of crates you can carry.
+ var/max_crates = 3
+ /// The crates stored in the module.
+ var/list/stored_crates = list()
+
+/obj/item/mod/module/clamp/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.Adjacent(target))
+ return
+ if(istype(target, /obj/structure/closet/crate))// || istype(target, /obj/item/delivery/big))
+ var/atom/movable/picked_crate = target
+ if(!check_crate_pickup(picked_crate))
+ return
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ if(!do_after(mod.wearer, load_time, target = target))
+ balloon_alert(mod.wearer, "interrupted!")
+ return
+ if(!check_crate_pickup(picked_crate))
+ return
+ stored_crates += picked_crate
+ picked_crate.forceMove(src)
+ balloon_alert(mod.wearer, "picked up [picked_crate]")
+ drain_power(use_power_cost)
+ mod.wearer.update_inv_back()
+ else if(length(stored_crates))
+ var/turf/target_turf = get_turf(target)
+ if(target_turf.is_blocked_turf())
+ return
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ if(!do_after(mod.wearer, load_time, target = target))
+ balloon_alert(mod.wearer, "interrupted!")
+ return
+ if(target_turf.is_blocked_turf())
+ return
+ var/atom/movable/dropped_crate = pop(stored_crates)
+ dropped_crate.forceMove(target_turf)
+ balloon_alert(mod.wearer, "dropped [dropped_crate]")
+ drain_power(use_power_cost)
+ mod.wearer.update_inv_back()
+ else
+ balloon_alert(mod.wearer, "invalid target!")
+
+/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ for(var/atom/movable/crate as anything in stored_crates)
+ crate.forceMove(drop_location())
+ stored_crates -= crate
+
+/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target)
+ if(length(stored_crates) >= max_crates)
+ balloon_alert(mod.wearer, "too many crates!")
+ return FALSE
+ for(var/mob/living/mob in target.get_all_contents())
+ if(mob.mob_size < MOB_SIZE_HUMAN)
+ continue
+ balloon_alert(mod.wearer, "crate too heavy!")
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/clamp/loader
+ name = "MOD loader hydraulic clamp module"
+ icon_state = "clamp_loader"
+ complexity = 0
+ removable = FALSE
+ overlay_state_inactive = null
+ overlay_state_active = "module_clamp_loader"
+ load_time = 1 SECONDS
+ max_crates = 5
+ use_mod_colors = TRUE
+
+///Drill - Lets you dig through rock and basalt.
+/obj/item/mod/module/drill
+ name = "MOD drill module"
+ desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
+ your drill is surely the one that both pierces and creates the heavens."
+ icon_state = "drill"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/drill)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_active = "module_drill"
+
+/obj/item/mod/module/drill/on_activation()
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine))
+
+/obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
+
+/obj/item/mod/module/drill/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.Adjacent(target))
+ return
+ if(istype(target, /turf/closed/mineral))
+ var/turf/closed/mineral/mineral_turf = target
+ mineral_turf.gets_drilled(mod.wearer)
+ drain_power(use_power_cost)
+ else if(istype(target, /turf/open/floor/plating/asteroid))
+ var/turf/open/floor/plating/asteroid/sand_turf = target
+ if(!sand_turf.can_dig(mod.wearer))
+ return
+ sand_turf.getDug()
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
+ SIGNAL_HANDLER
+ if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost))
+ return
+ var/turf/closed/mineral/mineral_turf = bumped_into
+ mineral_turf.gets_drilled(mod.wearer)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+///Ore Bag - Lets you pick up ores and drop them from the suit.
+/obj/item/mod/module/orebag
+ name = "MOD ore bag module"
+ desc = "An integrated ore storage system installed into the suit, \
+ this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
+ It's recommended by Nakamura Engineering to actually deposit that ore at local refineries."
+ icon_state = "ore"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/orebag)
+ cooldown_time = 0.5 SECONDS
+ allowed_inactive = TRUE
+ /// The ores stored in the bag.
+ var/list/ores = list()
+
+/obj/item/mod/module/orebag/on_equip()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(ore_pickup))
+
+/obj/item/mod/module/orebag/on_unequip()
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
+
+/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
+ INVOKE_ASYNC(src, PROC_REF(move_ore), ore)
+ playsound(src, SFX_RUSTLE, 50, TRUE)
+
+/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
+ for(var/obj/item/stack/stored_ore as anything in ores)
+ if(!ore.can_merge(stored_ore))
+ continue
+ ore.merge(stored_ore)
+ if(QDELETED(ore))
+ return
+ break
+ ore.forceMove(src)
+ ores += ore
+
+/obj/item/mod/module/orebag/on_use()
+ . = ..()
+ if(!.)
+ return
+ for(var/obj/item/ore as anything in ores)
+ ore.forceMove(drop_location())
+ ores -= ore
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/disposal_connector
+ name = "MOD disposal selector module"
+ desc = "A module that connects to the disposal pipeline, causing the user to go into their config selected disposal. \
+ Only seems to work when the suit is on."
+ icon_state = "disposal"
+ complexity = 2
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/disposal_connector)
+ var/disposal_tag = NONE
+
+/obj/item/mod/module/disposal_connector/Initialize(mapload)
+ . = ..()
+ disposal_tag = pick(GLOB.TAGGERLOCATIONS)
+
+/obj/item/mod/module/disposal_connector/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING, PROC_REF(disposal_handling))
+
+/obj/item/mod/module/disposal_connector/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_DISPOSING)
+
+/obj/item/mod/module/disposal_connector/get_configuration()
+ . = ..()
+ .["disposal_tag"] = add_ui_configuration("Disposal Tag", "list", GLOB.TAGGERLOCATIONS[disposal_tag], GLOB.TAGGERLOCATIONS)
+
+/obj/item/mod/module/disposal_connector/configure_edit(key, value)
+ switch(key)
+ if("disposal_tag")
+ for(var/tag in 1 to length(GLOB.TAGGERLOCATIONS))
+ if(GLOB.TAGGERLOCATIONS[tag] == value)
+ disposal_tag = tag
+ break
+
+/obj/item/mod/module/disposal_connector/proc/disposal_handling(datum/disposal_source, obj/structure/disposalholder/disposal_holder, obj/machinery/disposal/disposal_machine, hasmob)
+ SIGNAL_HANDLER
+
+ disposal_holder.destinationTag = disposal_tag
+
+/obj/item/mod/module/magnet
+ name = "MOD loader hydraulic magnet module"
+ desc = "A powerful hydraulic electromagnet able to launch crates and lockers towards the user, and keep 'em attached."
+ icon_state = "magnet_loader"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ use_power_cost = DEFAULT_CHARGE_DRAIN*3
+ incompatible_modules = list(/obj/item/mod/module/magnet)
+ cooldown_time = 1.5 SECONDS
+ overlay_state_active = "module_magnet"
+ use_mod_colors = TRUE
+
+/obj/item/mod/module/magnet/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(istype(mod.wearer.pulling, /obj/structure/closet))
+ var/obj/structure/closet/locker = mod.wearer.pulling
+ playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
+ locker.forceMove(mod.wearer.loc)
+ locker.throw_at(target, range = 7, speed = 4, thrower = mod.wearer)
+ return
+ if(!istype(target, /obj/structure/closet) || !(target in view(mod.wearer)))
+ balloon_alert(mod.wearer, "invalid target!")
+ return
+ var/obj/structure/closet/locker = target
+ if(locker.anchored || locker.move_resist >= MOVE_FORCE_OVERPOWERING)
+ balloon_alert(mod.wearer, "target anchored!")
+ return
+ new /obj/effect/temp_visual/mook_dust(get_turf(locker))
+ playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
+ locker.throw_at(mod.wearer, range = 7, speed = 3, force = MOVE_FORCE_WEAK, \
+ callback = CALLBACK(src, PROC_REF(check_locker), locker))
+
+/obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(istype(mod.wearer.pulling, /obj/structure/closet))
+ mod.wearer.stop_pulling()
+
+/obj/item/mod/module/magnet/proc/check_locker(obj/structure/closet/locker)
+ if(!mod?.wearer)
+ return
+ if(!locker.Adjacent(mod.wearer) || !isturf(locker.loc) || !isturf(mod.wearer.loc))
+ return
+ mod.wearer.start_pulling(locker)
+ //locker.strong_grab = TRUE
+ RegisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED, PROC_REF(on_stop_pull))
+
+/obj/item/mod/module/magnet/proc/on_stop_pull(obj/structure/closet/locker, atom/movable/last_puller)
+ SIGNAL_HANDLER
+
+ //locker.strong_grab = FALSE
+ UnregisterSignal(locker, COMSIG_ATOM_NO_LONGER_PULLED)
diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm
new file mode 100644
index 000000000000..e1516c2aa0a1
--- /dev/null
+++ b/code/modules/mod/modules/modules_visor.dm
@@ -0,0 +1,85 @@
+//Visor modules for MODsuits
+
+///Base Visor - Adds a specific HUD and traits to you.
+/obj/item/mod/module/visor
+ name = "MOD visor module"
+ desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you."
+ module_type = MODULE_TOGGLE
+ complexity = 2
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/visor)
+ cooldown_time = 0.5 SECONDS
+ /// The HUD type given by the visor.
+ var/hud_type
+ /// The traits given by the visor.
+ var/list/visor_traits = list()
+
+/obj/item/mod/module/visor/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(hud_type)
+ var/datum/atom_hud/hud = GLOB.huds[hud_type]
+ hud.add_hud_to(mod.wearer)
+ for(var/trait in visor_traits)
+ ADD_TRAIT(mod.wearer, trait, MOD_TRAIT)
+ mod.wearer.update_sight()
+
+/obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(hud_type)
+ var/datum/atom_hud/hud = GLOB.huds[hud_type]
+ hud.remove_hud_from(mod.wearer)
+ for(var/trait in visor_traits)
+ REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT)
+ mod.wearer.update_sight()
+
+//Medical Visor - Gives you a medical HUD.
+/obj/item/mod/module/visor/medhud
+ name = "MOD medical visor module"
+ desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \
+ biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \
+ access data such as patient files in a convenient readout. They say these also let you see behind you."
+ icon_state = "medhud_visor"
+ hud_type = DATA_HUD_MEDICAL_ADVANCED
+ visor_traits = list(TRAIT_MEDICAL_HUD)
+
+//Diagnostic Visor - Gives you a diagnostic HUD.
+/obj/item/mod/module/visor/diaghud
+ name = "MOD diagnostic visor module"
+ desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \
+ from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \
+ and integrity of such. They say these also let you see behind you."
+ icon_state = "diaghud_visor"
+ hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED
+ visor_traits = list(TRAIT_DIAGNOSTIC_HUD)
+
+//Security Visor - Gives you a security HUD.
+/obj/item/mod/module/visor/sechud
+ name = "MOD security visor module"
+ desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \
+ plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \
+ and generally know who to shoot. They say these also let you see behind you."
+ icon_state = "sechud_visor"
+ hud_type = DATA_HUD_SECURITY_ADVANCED
+ visor_traits = list(TRAIT_SECURITY_HUD)
+
+//Meson Visor - Gives you meson vision.
+/obj/item/mod/module/visor/meson
+ name = "MOD meson visor module"
+ desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \
+ technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \
+ through walls, regardless of lighting conditions. They say these also let you see behind you."
+ icon_state = "meson_visor"
+ visor_traits = list(SEE_TURFS)
+
+//Thermal Visor - Gives you thermal vision.
+/obj/item/mod/module/visor/thermal
+ name = "MOD thermal visor module"
+ desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \
+ the thermal radiation output of objects near the user. While it can detect the heat output of even something as \
+ small as a rodent, it still produces irritating red overlay. They say these also let you see behind you."
+ icon_state = "thermal_visor"
+ visor_traits = list(SEE_MOBS)
diff --git a/code/modules/movespeed/modifiers/items.dm b/code/modules/movespeed/modifiers/items.dm
index c858582af6a3..4c967a58a7ec 100644
--- a/code/modules/movespeed/modifiers/items.dm
+++ b/code/modules/movespeed/modifiers/items.dm
@@ -18,3 +18,5 @@
/datum/movespeed_modifier/berserk
multiplicative_slowdown = -0.2
+/datum/movespeed_modifier/sphere
+ multiplicative_slowdown = -0.5
diff --git a/code/modules/overmap/objects/dynamic_datum.dm b/code/modules/overmap/objects/dynamic_datum.dm
index dcc62aad7424..1ca28df922aa 100644
--- a/code/modules/overmap/objects/dynamic_datum.dm
+++ b/code/modules/overmap/objects/dynamic_datum.dm
@@ -27,6 +27,8 @@
var/ruin_type
/// list of ruins and their target turf, indexed by name
var/list/ruin_turfs
+ /// list of ruin templates currently spawned on the planet.
+ var/list/spawned_ruins
/// Whether or not the level is currently loading.
var/loading = FALSE
@@ -198,6 +200,7 @@
mapzone = dynamic_encounter_values[1]
reserve_docks = dynamic_encounter_values[2]
ruin_turfs = dynamic_encounter_values[3]
+ spawned_ruins = dynamic_encounter_values[4]
loading = FALSE
return TRUE
diff --git a/code/modules/overmap/objects/event_datum.dm b/code/modules/overmap/objects/event_datum.dm
index bfed840a1acd..d798fd74778a 100644
--- a/code/modules/overmap/objects/event_datum.dm
+++ b/code/modules/overmap/objects/event_datum.dm
@@ -194,55 +194,25 @@
chance_to_affect = 100
///The currently linked wormhole
var/datum/overmap/event/wormhole/other_wormhole
- ///Amount of times a ship can pass through before it collapses
- var/stability
/datum/overmap/event/wormhole/Initialize(position, _other_wormhole, ...)
. = ..()
- stability = rand(1, 5)
if(_other_wormhole)
other_wormhole = _other_wormhole
if(!other_wormhole)
other_wormhole = new(null, src) //Create a new wormhole at a random location
- token.color = adjust_colors()
- token.light_color = adjust_colors()
+ token.color = "#6d80c7"
+ token.light_color = "#6d80c7"
token.update_appearance()
/datum/overmap/event/wormhole/affect_ship(datum/overmap/ship/controlled/S)
if(!other_wormhole)
qdel(src)
- if(--stability <= 0)
- var/list/results = SSovermap.get_unused_overmap_square()
- S.overmap_move(results["x"], results["y"])
- QDEL_NULL(other_wormhole)
- for(var/MN in GLOB.player_list)
- var/mob/M = MN
- if(S.shuttle_port.is_in_shuttle_bounds(M))
- M.playsound_local(S.shuttle_port, 'sound/effects/explosionfar.ogg', 100)
- shake_camera(M, 10, 10)
-
- return qdel(src)
- other_wormhole.stability = stability
+ return
S.overmap_move(other_wormhole.x, other_wormhole.y)
S.overmap_step(S.get_heading())
- token.color = adjust_colors()
- token.light_color = adjust_colors()
-
-/datum/overmap/event/wormhole/proc/adjust_colors()
- switch(stability)
- if(1)
- return "#753214"
- if(2)
- return "#642f19"
- if(3)
- return"#654650"
- if(4)
- return"#5c5d8b"
- if(5)
- return"#6d80c7"
-
//Carp "meteors" - throws carp at the ship
/datum/overmap/event/meteor/carp
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index f21f95692234..75cf85513236 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -1492,9 +1492,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/power/apc/auto_name, 25)
update()
addtimer(CALLBACK(src, PROC_REF(reset), APC_RESET_EMP), 600)
-/obj/machinery/power/apc/blob_act(obj/structure/blob/B)
- set_broken()
-
/obj/machinery/power/apc/disconnect_terminal()
if(terminal)
terminal.master = null
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 1a2f87c70dd9..b1847513a941 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -19,7 +19,8 @@
var/maxcharge = 1000
custom_materials = list(/datum/material/iron=700, /datum/material/glass=50)
grind_results = list(/datum/reagent/lithium = 15, /datum/reagent/iron = 5, /datum/reagent/silicon = 5)
- var/rigged = FALSE // true if rigged to explode
+ var/rigged = FALSE // true if rigged to
+ var/show_rigged = TRUE // whether if the cell shows it's fauly on examine.
var/chargerate = 100 //how much power is given every tick in a recharger
var/self_recharge = 0 //does it self recharge, over time, or not?
var/ratingdesc = TRUE
@@ -97,7 +98,7 @@
/obj/item/stock_parts/cell/examine(mob/user)
. = ..()
- if(rigged)
+ if(rigged && show_rigged)
. += "This power cell seems to be faulty!"
else
. += "The charge meter reads [round(src.percent())]%."
@@ -148,7 +149,6 @@
if(prob(25))
corrupt()
-//WS Begin -- Ethereal Charge Scaling
/obj/item/stock_parts/cell/attack_self(mob/user)
if(iselzuose(user))
var/mob/living/carbon/human/H = user
@@ -175,10 +175,6 @@
else
to_chat(H, "You can't receive charge from [src]!")
return
-//WS End
-
-/obj/item/stock_parts/cell/blob_act(obj/structure/blob/B)
- SSexplosions.highobj += src
/obj/item/stock_parts/cell/proc/get_electrocute_damage()
if(charge >= 1000)
diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm
index 8d711ad804fa..c9367ceecfbe 100644
--- a/code/modules/power/generator.dm
+++ b/code/modules/power/generator.dm
@@ -13,6 +13,7 @@
var/lastgen = 0
var/lastgenlev = -1
+ var/max_efficiency = 0.45
/obj/machinery/power/generator/Initialize(mapload)
. = ..()
@@ -75,12 +76,13 @@
if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0)
- var/efficiency = 0.45 //WS Edit - Nerfed down to Cit's efficiency
+ var/efficiency = LOGISTIC_FUNCTION(max_efficiency,0.0009,delta_temperature,10000)
+ //2nd (0.0009) effects how 'steep' the curve is, and 4th (10000) effects where the 'middle' is.
var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity)
+ lastgen += energy_transfer * efficiency
var/heat = energy_transfer*(1-efficiency)
- lastgen += LOGISTIC_FUNCTION(500000,0.0009,delta_temperature,10000) //WS Edit - Buries the 3x3 freezer heater TEG into the ground
hot_air.set_temperature(hot_air.return_temperature() - energy_transfer/hot_air_heat_capacity)
cold_air.set_temperature(cold_air.return_temperature() + heat/cold_air_heat_capacity)
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 3ed7e262a53d..b9044a4690cb 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -32,10 +32,6 @@
if(severity == 1) // Very sturdy.
set_broken()
-/obj/machinery/gravity_generator/blob_act(obj/structure/blob/B)
- if(prob(20))
- set_broken()
-
/obj/machinery/gravity_generator/zap_act(power, zap_flags)
..()
if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index e92efb779d67..530b3b8703fc 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -190,11 +190,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/light_construct, 32)
return
return ..()
-/obj/structure/light_construct/blob_act(obj/structure/blob/B)
- if(B && B.loc == loc)
- qdel(src)
-
-
/obj/structure/light_construct/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/metal(loc, sheets_refunded)
diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm
index 1645afe9832b..07b1b0af3637 100644
--- a/code/modules/power/rtg.dm
+++ b/code/modules/power/rtg.dm
@@ -84,9 +84,6 @@
log_bomber(Proj.firer, "triggered a", src, "explosion via projectile")
overload()
-/obj/machinery/power/rtg/abductor/blob_act(obj/structure/blob/B)
- overload()
-
/obj/machinery/power/rtg/abductor/ex_act()
if(going_kaboom)
qdel(src)
diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm
index 77c400ae2b07..a6bebc3c1b88 100644
--- a/code/modules/power/singularity/containment_field.dm
+++ b/code/modules/power/singularity/containment_field.dm
@@ -56,9 +56,6 @@
if(BRUTE)
playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE)
-/obj/machinery/field/containment/blob_act(obj/structure/blob/B)
- return FALSE
-
/obj/machinery/field/containment/ex_act(severity, target)
return FALSE
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index d3e7a31d4cb9..9a965e4f85e3 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -165,12 +165,6 @@ field_generator power level display
if(!anchored)
step(src, get_dir(M, src))
-/obj/machinery/field/generator/blob_act(obj/structure/blob/B)
- if(active)
- return 0
- else
- ..()
-
/obj/machinery/field/generator/bullet_act(obj/projectile/Proj)
if(Proj.flag != "bullet")
power = min(power + Proj.damage, field_generator_max_power)
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index b593d3f546bb..ac0e59d7080b 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -41,10 +41,6 @@
else if(istype(A, /obj/singularity))
var/obj/singularity/S = A
S.energy += energy
- else if(istype(A, /obj/structure/blob))
- var/obj/structure/blob/B = A
- B.take_damage(energy*0.6)
- movement_range = 0
/obj/effect/accelerated_particle/proc/on_entered(datum/source, atom/A)
SIGNAL_HANDLER
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index ddcaf026a40e..0130297c87da 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -261,10 +261,6 @@
return ..()
-/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B)
- if(prob(50))
- qdel(src)
-
/obj/machinery/particle_accelerator/control_box/interact(mob/user)
if(construction_state == PA_CONSTRUCTION_PANEL_OPEN)
wires.interact(user)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 875f9cd441f2..9218b829e940 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -96,9 +96,6 @@
/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man!
return 0
-/obj/singularity/blob_act(obj/structure/blob/B)
- return
-
/obj/singularity/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
diff --git a/code/modules/power/supermatter/cascade.dm b/code/modules/power/supermatter/cascade.dm
index 52c36bf13c3d..9d388d8f0e04 100644
--- a/code/modules/power/supermatter/cascade.dm
+++ b/code/modules/power/supermatter/cascade.dm
@@ -69,14 +69,6 @@
qdel(rip_u)
return
-/turf/open/indestructible/supermatter_cascade/blob_act(obj/structure/blob/blob)
- if(!blob || isspaceturf(loc)) //does nothing in space
- return
- playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
- blob.visible_message("\The [blob] strikes at \the [src] and rapidly flashes to ash.",
- "You hear a loud crack as you are washed with a wave of heat.")
- Consume(blob)
-
/turf/open/indestructible/supermatter_cascade/attack_paw(mob/user, list/modifiers)
dust_mob(user, cause = "monkey attack")
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 5593744219d0..b6fc6367e85c 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -748,19 +748,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
qdel(src)
return gain
-/obj/machinery/power/supermatter_crystal/blob_act(obj/structure/blob/B)
- if(B && !isspaceturf(loc)) //does nothing in space
- playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE)
- damage += B.obj_integrity * 0.5 //take damage equal to 50% of remaining blob health before it tried to eat us
- if(B.obj_integrity > 100)
- B.visible_message("\The [B] strikes at \the [src] and flinches away!",\
- "You hear a loud crack as you are washed with a wave of heat.")
- B.take_damage(100, BURN)
- else
- B.visible_message("\The [B] strikes at \the [src] and rapidly flashes to ash.",\
- "You hear a loud crack as you are washed with a wave of heat.")
- Consume(B)
-
/obj/machinery/power/supermatter_crystal/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index fbece73764a4..7d58610fc7f7 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -280,12 +280,6 @@
else if(closest_type >= BLOB)
continue
- else if(istype(A, /obj/structure/blob))
- var/obj/structure/blob/B = A
- if(!(B.obj_flags & BEING_SHOCKED))
- closest_type = BLOB
- closest_atom = A
-
else if(closest_type >= STRUCTURE)
continue
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index ca1a1a89b8c9..023f6212e06f 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -316,6 +316,9 @@
///This prevents gun from firing until the coodown is done, affected by lag
var/current_cooldown = 0
+ var/gunslinger_recoil_bonus = 0
+ var/gunslinger_spread_bonus = 0
+
/obj/item/gun/Initialize()
. = ..()
RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield))
@@ -802,61 +805,42 @@
/obj/item/gun/proc/before_firing(atom/target,mob/user)
return
-// We do it like this in case theres some specific gun behavior for adjusting recoil, like bipods or folded stocks
/obj/item/gun/proc/calculate_recoil(mob/user, recoil_bonus = 0)
- return recoil_bonus
+ if(HAS_TRAIT(user, TRAIT_GUNSLINGER))
+ recoil_bonus += gunslinger_recoil_bonus
+ return clamp(recoil_bonus, 0 , INFINITY)
-// We do it like this in case theres some specific gun behavior for adjusting spread, like bipods or folded stocks
/obj/item/gun/proc/calculate_spread(mob/user, bonus_spread)
- ///our final spread value
- var/sprd = 0
- ///our randomized value after checking if we are wielded or not
+ var/final_spread = 0
var/randomized_gun_spread = 0
- ///bonus
- var/randomized_bonus_spread
- // do we have poor aim
- var/poor_aim = FALSE
+ var/randomized_bonus_spread = 0
- //do we have bonus_spread ? If so, set sprd to it because it means a subtype's proc messed with it
- sprd += bonus_spread
+ final_spread += bonus_spread
- //reset bonus_spread for poor aim...
- bonus_spread = 0
+ if(HAS_TRAIT(user, TRAIT_GUNSLINGER))
+ randomized_bonus_spread += rand(0, gunslinger_spread_bonus)
- // if we have poor aim, we fuck the shooter over
if(HAS_TRAIT(user, TRAIT_POOR_AIM))
- bonus_spread += 25
- poor_aim = TRUE
- // then we randomize the bonus spread
- randomized_bonus_spread = rand(poor_aim ? 10 : 0, bonus_spread) //poor aim is no longer just a nusiance
-
- //then, we mutiply previous bonus spread as it means dual wielding usually, it also means poor aim is also even more severe
- randomized_bonus_spread *= DUALWIELD_PENALTY_EXTRA_MULTIPLIER
+ randomized_bonus_spread += rand(0, 25)
- // we will then calculate gun spread depending on if we are fully wielding (after do_after) the gun or not
+ //We will then calculate gun spread depending on if we are fully wielding (after do_after) the gun or not
randomized_gun_spread = rand(0, wielded_fully ? spread : spread_unwielded)
- //finally, we put it all together including if sprd has a value
- sprd += randomized_gun_spread + randomized_bonus_spread
-
- //clamp it down to avoid guns with negative spread to have worse recoil...
- sprd = clamp(sprd, 0, INFINITY)
+ final_spread += randomized_gun_spread + randomized_bonus_spread
- // im not sure what this does, i beleive its meant to make it so bullet spread goes in the opposite direction? get back to me on this - update,i have commented it out, however it appears be dapening spread. weird.
- //sprd *= (rand() - 0.5)
+ //Clamp it down to avoid guns with negative spread to have worse recoil...
+ final_spread = clamp(final_spread, 0, INFINITY)
- //coin flip if we mutiply output by -1 so spread isn't JUST to the right
+ //So spread isn't JUST to the right
if(prob(50))
- sprd *= -1
+ final_spread *= -1
- // then we round it up and send it!
- sprd = round(sprd)
+ final_spread = round(final_spread)
- return sprd
+ return final_spread
/obj/item/gun/proc/simulate_recoil(mob/living/user, recoil_bonus = 0, firing_angle)
var/total_recoil = calculate_recoil(user, recoil_bonus)
- total_recoil = clamp(total_recoil, 0 , INFINITY)
var/actual_angle = firing_angle + rand(-recoil_deviation, recoil_deviation) + 180
if(actual_angle > 360)
diff --git a/code/modules/projectiles/guns/ballistic/assault.dm b/code/modules/projectiles/guns/ballistic/assault.dm
index e29196b3401a..bdabf0a5bda9 100644
--- a/code/modules/projectiles/guns/ballistic/assault.dm
+++ b/code/modules/projectiles/guns/ballistic/assault.dm
@@ -14,23 +14,8 @@
rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg'
spread_unwielded = 20
-/obj/item/gun/ballistic/automatic/assault/calculate_recoil(mob/user, recoil_bonus = 0)
- var/gunslinger_bonus = 2
- var/total_recoil = recoil_bonus
-
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_recoil += gunslinger_bonus
-
- return ..(user, total_recoil)
-
-/obj/item/gun/ballistic/automatic/assault/calculate_spread(mob/user, bonus_spread)
- var/gunslinger_bonus = 16
- var/total_spread = bonus_spread
-
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_spread += gunslinger_bonus
-
- return ..(user, total_spread)
+ gunslinger_recoil_bonus = 2
+ gunslinger_spread_bonus = 16
/obj/item/gun/ballistic/automatic/assault/skm
name = "\improper SKM-24"
@@ -262,8 +247,8 @@
secondary.pre_fire(target, user, message, flag, params, zone_override, bonus_spread)
-/obj/item/gun/ballistic/automatic/powered/get_cell()
- return cell
+/obj/item/gun/ballistic/automatic/assault/e40/get_cell()
+ return secondary.get_cell()
/obj/item/gun/ballistic/automatic/assault/e40/update_overlays()
. = ..()
diff --git a/code/modules/projectiles/guns/ballistic/hmg.dm b/code/modules/projectiles/guns/ballistic/hmg.dm
index 8d614958de4f..ccf21a2c7c0e 100644
--- a/code/modules/projectiles/guns/ballistic/hmg.dm
+++ b/code/modules/projectiles/guns/ballistic/hmg.dm
@@ -18,6 +18,9 @@
recoil_unwielded = 4
wield_slowdown = 3
+ gunslinger_recoil_bonus = 2
+ gunslinger_spread_bonus = 20
+
///does this have a bipod?
var/has_bipod = FALSE
///is the bipod deployed?
@@ -116,29 +119,6 @@
. = ..()
retract_bipod(user=user)
-/obj/item/gun/ballistic/automatic/hmg/calculate_recoil(mob/user, recoil_bonus = 0)
- var/gunslinger_bonus = 2
- var/total_recoil = recoil_bonus
-
- if(bipod_deployed)
- total_recoil += deploy_recoil_bonus
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_recoil += gunslinger_bonus
-
- return ..(user, total_recoil)
-
-/obj/item/gun/ballistic/automatic/hmg/calculate_spread(mob/user, bonus_spread)
- var/gunslinger_bonus = 20
- var/total_spread = bonus_spread
-
- if(bipod_deployed)
- total_spread += deploy_spread_bonus
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_spread += gunslinger_bonus
-
- return ..(user, total_spread)
-
-
/obj/item/gun/ballistic/automatic/hmg/update_icon_state()
. = ..()
item_state = "[initial(item_state)][bipod_deployed ? "_deployed" : ""]"
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 545cbf8bf117..6a2e30c9cddb 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -40,6 +40,9 @@
safety_wording = "hammer"
+ gunslinger_recoil_bonus = -1
+ gunslinger_spread_bonus = -8
+
var/gate_loaded = FALSE //for stupid wild west shit
var/gate_offset = 5 //for wild west shit 2: instead of ejecting the chambered round, eject the next round if 1
var/gate_load_direction = REVOLVER_AUTO_ROTATE_RIGHT_LOADING //when we load ammo with a box, which direction do we rotate the cylinder? unused with normal revolvers
@@ -418,7 +421,7 @@
fire_delay = src::fire_delay
if(fan)
rack()
- to_chat(user, "You fan the [bolt_wording] of \the [src]!")
+ to_chat(user, span_notice("You fan the [bolt_wording] of \the [src]!"))
balloon_alert_to_viewers("fans revolver!")
fire_delay = 0 SECONDS
@@ -436,25 +439,6 @@
return
to_chat(user, "The hammer is up on [src]! Pull it down to fire!")
-/obj/item/gun/ballistic/revolver/calculate_recoil(mob/user, recoil_bonus = 0)
- var/gunslinger_bonus = -1
- var/total_recoil = recoil_bonus
-
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus
- total_recoil += gunslinger_bonus
- total_recoil = clamp(total_recoil,0,INFINITY)
-
- return ..(user, total_recoil)
-
-/obj/item/gun/ballistic/revolver/calculate_spread(mob/user, bonus_spread)
- var/gunslinger_bonus = -8
- var/total_spread = bonus_spread
-
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus
- total_spread += gunslinger_bonus
-
- return ..(user, total_spread)
-
/obj/item/gun/ballistic/revolver/pickup(mob/user)
. = ..()
tryflip(user)
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index 9172b919adaa..f63328035959 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -35,6 +35,8 @@
recoil = 1
recoil_unwielded = 4
+ gunslinger_recoil_bonus = -1
+
/obj/item/gun/ballistic/shotgun/blow_up(mob/user)
if(chambered && chambered.BB)
process_fire(user, user, FALSE)
@@ -46,15 +48,6 @@
return TRUE
return FALSE
-/obj/item/gun/ballistic/shotgun/calculate_recoil(mob/user, recoil_bonus = 0)
- var/gunslinger_bonus = -1
- var/total_recoil = recoil_bonus
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger bonus
- total_recoil += gunslinger_bonus
- total_recoil = clamp(total_recoil,0,INFINITY)
-
- return ..(user, total_recoil)
-
// BRIMSTONE SHOTGUN //
/obj/item/gun/ballistic/shotgun/brimstone
diff --git a/code/modules/projectiles/guns/ballistic/smg.dm b/code/modules/projectiles/guns/ballistic/smg.dm
index fbc2fc4d11bc..d43e324cfacd 100644
--- a/code/modules/projectiles/guns/ballistic/smg.dm
+++ b/code/modules/projectiles/guns/ballistic/smg.dm
@@ -19,25 +19,8 @@
eject_sound = 'sound/weapons/gun/smg/smg_unload.ogg'
eject_empty_sound = 'sound/weapons/gun/smg/smg_unload.ogg'
-/obj/item/gun/ballistic/automatic/smg/calculate_recoil(mob/user, recoil_bonus = 0)
- var/gunslinger_bonus = 2
- var/total_recoil
- if(.)
- total_recoil += .
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_recoil += gunslinger_bonus
- . = total_recoil
- return ..()
-
-/obj/item/gun/ballistic/automatic/smg/calculate_spread(mob/user, bonus_spread)
- var/gunslinger_bonus = 16
- var/total_spread = bonus_spread
- if(.)
- total_spread += .
- if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) //gunslinger penalty
- total_spread += gunslinger_bonus
- . = total_spread
- return ..()
+ gunslinger_recoil_bonus = 2
+ gunslinger_spread_bonus = 16
/obj/item/gun/ballistic/automatic/smg/c20r
name = "\improper C-20r SMG"
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index c9791666b54f..ac1cd94b092e 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -86,10 +86,6 @@
if(machine_stat & BROKEN)
. += "waitlight"
-/obj/machinery/chem_master/blob_act(obj/structure/blob/B)
- if (prob(50))
- qdel(src)
-
/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I))
return
diff --git a/code/modules/reagents/reagent_containers/jug.dm b/code/modules/reagents/reagent_containers/jug.dm
index 80ebcbb4d5b3..46c5620ecb20 100644
--- a/code/modules/reagents/reagent_containers/jug.dm
+++ b/code/modules/reagents/reagent_containers/jug.dm
@@ -143,3 +143,8 @@
/obj/item/reagent_containers/glass/chem_jug/thermite
name = "chemical jug (thermite)"
list_reagents = list(/datum/reagent/thermite = 150)
+
+/obj/item/reagent_containers/glass/chem_jug/hexacrete
+ name = "chemical jug (hexacrete)"
+ list_reagents = list(/datum/reagent/concrete/hexacrete = 150)
+
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 84a8292f7263..fafd67305c9c 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -68,9 +68,6 @@
explosion(get_turf(src), 0, 1, 5, flame_range = 5)
qdel(src)
-/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B)
- boom()
-
/obj/structure/reagent_dispensers/fueltank/ex_act()
boom()
@@ -161,12 +158,6 @@
icon_state = "beer"
reagent_id = /datum/reagent/consumable/ethanol/beer
-/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B)
- explosion(src.loc,0,3,5,7,10)
- if(!QDELETED(src))
- qdel(src)
-
-
/obj/structure/reagent_dispensers/virusfood
name = "virus food dispenser"
desc = "A dispenser of low-potency virus mutagenic."
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 06b58f27105b..a2b295f2e6fa 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -286,7 +286,7 @@
build_type = AUTOLATHE | PROTOLATHE
materials = list(/datum/material/iron = 750)
build_path = /obj/item/tank/internals/emergency_oxygen/engi/empty
- category = list("hacked","Misc","Equipment")
+ category = list("initial", "Misc","Equipment")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO
/datum/design/plasmaman_tank_belt
@@ -295,7 +295,7 @@
build_type = AUTOLATHE | PROTOLATHE
materials = list(/datum/material/iron = 800)
build_path = /obj/item/tank/internals/plasmaman/belt/empty
- category = list("hacked","Misc","Equipment")
+ category = list("initial", "Misc","Equipment")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO
/datum/design/generic_gas_tank
@@ -516,14 +516,6 @@
category = list("initial","Misc", "Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
-/datum/design/foilhat
- name = "Tinfoil Hat"
- id = "tinfoil_hat"
- build_type = AUTOLATHE
- materials = list(/datum/material/iron = 5500)
- build_path = /obj/item/clothing/head/foilhat
- category = list("hacked", "Misc")
-
/datum/design/scalpel
name = "Scalpel"
id = "scalpel"
@@ -772,31 +764,13 @@
build_path = /obj/item/ammo_box/foambox
category = list("initial", "Misc")
-//hacked autolathe recipes
-//WS - emagged recipies
-/datum/design/flamethrower
- name = "Flamethrower"
- id = "flamethrower"
- build_type = AUTOLATHE
- materials = list(/datum/material/iron = 500)
- build_path = /obj/item/flamethrower/full
- category = list("hacked", "Security")
-
-/datum/design/electropack
- name = "Electropack"
- id = "electropack"
- build_type = AUTOLATHE
- materials = list(/datum/material/iron = 10000, /datum/material/glass = 2500)
- build_path = /obj/item/electropack
- category = list("hacked", "Tools")
-
/datum/design/handcuffs
name = "Handcuffs"
id = "handcuffs"
build_type = AUTOLATHE
materials = list(/datum/material/iron = 500)
build_path = /obj/item/restraints/handcuffs
- category = list("hacked", "Security")
+ category = list("initial", "Security")
/datum/design/receiver
name = "Modular Receiver"
@@ -804,7 +778,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 15000)
build_path = /obj/item/weaponcrafting/receiver
- category = list("hacked", "Security")
+ category = list("initial", "Security")
/datum/design/c38_surplus
name = "Ammo Box (.38 surplus)"
@@ -828,7 +802,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 1000) //Discount for making individually - no box = less metal!
build_path = /obj/item/ammo_casing/caseless/foam_dart/riot
- category = list("hacked", "Security")
+ category = list("initial", "Security")
/datum/design/riot_darts
name = "Foam Riot Dart Box"
@@ -836,15 +810,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 50000) //Comes with 40 darts
build_path = /obj/item/ammo_box/foambox/riot
- category = list("hacked", "Security")
-
-/datum/design/a357
- name = ".357 Casing"
- id = "a357"
- build_type = AUTOLATHE
- materials = list(/datum/material/iron = 4000)
- build_path = /obj/item/ammo_casing/a357
- category = list("emagged", "Security")
+ category = list("initial", "Security")
/datum/design/c10mm_surplus
name = "Ammo Box (10mm surplus)"
@@ -900,7 +866,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 18000)
build_path = /obj/item/kitchen/knife/butcher
- category = list("hacked", "Dinnerware")
+ category = list("initial", "Dinnerware")
/datum/design/spraycan
name = "Spraycan"
@@ -985,14 +951,6 @@
build_path = /obj/item/modular_computer/tablet
category = list("initial","Misc")
-/datum/design/slime_scanner
- name = "Slime Scanner"
- id = "slime_scanner"
- build_type = AUTOLATHE
- materials = list(/datum/material/iron = 300, /datum/material/glass = 200)
- build_path = /obj/item/slime_scanner
- category = list("initial", "Misc")
-
/datum/design/pet_carrier
name = "Pet Carrier"
id = "pet_carrier"
@@ -1084,7 +1042,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 100, /datum/material/glass = 50)
build_path = /obj/item/toy/gun
- category = list("hacked", "Misc")
+ category = list("initial", "Misc")
/datum/design/capbox
name = "Box of Cap Gun Shots"
@@ -1092,7 +1050,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 20, /datum/material/glass = 5)
build_path = /obj/item/toy/ammo/gun
- category = list("hacked", "Misc")
+ category = list("initial", "Misc")
/datum/design/toy_balloon
name = "Plastic Balloon"
@@ -1100,7 +1058,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/plastic = 1200)
build_path = /obj/item/toy/balloon
- category = list("hacked", "Misc")
+ category = list("initial", "Misc")
/datum/design/toy_meteor
name = "Plastic Toy Meteor"
@@ -1108,15 +1066,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/plastic = 1000)
build_path = /obj/item/toy/minimeteor
- category = list("hacked", "Misc")
-
-/datum/design/toy_armblade
- name = "Plastic Armblade"
- id = "toy_armblade"
- build_type = AUTOLATHE
- materials = list(/datum/material/plastic = 2000)
- build_path = /obj/item/toy/foamblade
- category = list("hacked", "Misc")
+ category = list("initial", "Misc")
/datum/design/plastic_tree
name = "Plastic Potted Plant"
@@ -1181,7 +1131,7 @@
build_type = AUTOLATHE
materials = list(/datum/material/iron = 20000)
build_path = /obj/item/ammo_box/magazine/zip_ammo_9mm
- category = list("hacked", "Security")
+ category = list("initial", "Security")
/datum/design/pipedispenser
name = "Pipe Dispenser (Machine Board)"
diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm
index 61fb07e77502..965a164e35d6 100644
--- a/code/modules/research/designs/mecha_designs.dm
+++ b/code/modules/research/designs/mecha_designs.dm
@@ -133,8 +133,8 @@
////////////////////////////////////////
/datum/design/mech_scattershot
- name = "Exosuit Weapon (LBX AC 10 \"Scattershot\")"
- desc = "Allows for the construction of LBX AC 10."
+ name = "Exosuit Weapon (LBX-10 \"Scattershot\")"
+ desc = "Allows for the construction of LBX-10."
id = "mech_scattershot"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot
@@ -143,8 +143,8 @@
category = list("Exosuit Equipment")
/datum/design/mech_scattershot_ammo
- name = "LBX AC 10 Scattershot Ammunition"
- desc = "Ammunition for the LBX AC 10 exosuit weapon."
+ name = "LBX-10 Scattershot Ammunition"
+ desc = "Ammunition for the LBX-10 exosuit weapon."
id = "mech_scattershot_ammo"
build_type = PROTOLATHE | MECHFAB
build_path = /obj/item/mecha_ammo/scattershot
@@ -153,7 +153,7 @@
category = list("Exosuit Ammunition", "Ammo")
/datum/design/mech_carbine
- name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
+ name = "Exosuit Weapon (FNX-99 \"Phoenix\" Carbine)"
desc = "Allows for the construction of FNX-99 \"Hades\" Carbine."
id = "mech_carbine"
build_type = MECHFAB
@@ -164,7 +164,7 @@
/datum/design/mech_carbine_ammo
name = "FNX-99 Carbine Ammunition"
- desc = "Ammunition for the FNX-99 \"Hades\" Carbine."
+ desc = "Ammunition for the FNX-99 \"Phoenix\" Carbine."
id = "mech_carbine_ammo"
build_type = PROTOLATHE | MECHFAB
build_path = /obj/item/mecha_ammo/incendiary
@@ -403,7 +403,7 @@
category = list("Exosuit Equipment")
/datum/design/mech_lmg
- name = "Exosuit Weapon (\"Ultra AC 2\" LMG)"
+ name = "Exosuit Weapon (\"UMG-2\" LMG)"
desc = "A weapon for combat exosuits. Shoots a rapid, three shot burst."
id = "mech_lmg"
build_type = MECHFAB
@@ -413,8 +413,8 @@
category = list("Exosuit Equipment")
/datum/design/mech_lmg_ammo
- name = "Ultra AC 2 Ammunition"
- desc = "Ammunition for the Ultra AC 2 LMG"
+ name = "UMG-2 Ammunition"
+ desc = "Ammunition for the UMG-2 LMG"
id = "mech_lmg_ammo"
build_type = PROTOLATHE | MECHFAB
build_path = /obj/item/mecha_ammo/lmg
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 6d2236a974b9..31a7c508eecd 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -840,7 +840,7 @@
/datum/techweb_node/mech_scattershot
id = "mecha_tools"
- display_name = "Exosuit Weapon (LBX AC 10 \"Scattershot\")"
+ display_name = "Exosuit Weapon (LBX-10 \"Scattershot\")"
description = "An advanced piece of exosuit weaponry"
prereq_ids = list("ballistic_weapons")
design_ids = list("mech_scattershot", "mech_scattershot_ammo")
@@ -849,7 +849,7 @@
/datum/techweb_node/mech_carbine
id = "mech_carbine"
- display_name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
+ display_name = "Exosuit Weapon (FNX-99 \"Phoenix\" Carbine)"
description = "An advanced piece of exosuit weaponry"
prereq_ids = list("ballistic_weapons")
design_ids = list("mech_carbine", "mech_carbine_ammo")
@@ -948,7 +948,7 @@
/datum/techweb_node/mech_lmg
id = "mech_lmg"
- display_name = "Exosuit Weapon (\"Ultra AC 2\" LMG)"
+ display_name = "Exosuit Weapon (\"UMG-2\" LMG)"
description = "An advanced piece of exosuit weaponry"
prereq_ids = list("ballistic_weapons")
design_ids = list("mech_lmg", "mech_lmg_ammo")
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 42888130b542..395da25aaa5d 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -1,6 +1,5 @@
GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
/mob/living,
- /obj/structure/blob,
/obj/effect/rune,
/obj/structure/spider/spiderling,
/obj/item/disk/nuclear,
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm
index f8d8a9a49384..53e155210310 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/bodyparts.dm
@@ -153,11 +153,6 @@
if(burn_dam > DAMAGE_PRECISION)
. += "This limb has [burn_dam > 30 ? "severe" : "minor"] burns."
-
-/obj/item/bodypart/blob_act()
- take_damage(max_damage)
-
-
/obj/item/bodypart/attack(mob/living/carbon/C, mob/user)
if(ishuman(C))
var/mob/living/carbon/human/H = C
@@ -777,7 +772,11 @@
if (bone_status == BONE_FLAG_NORMAL && body_part & LEGS) // Because arms are not legs
owner.set_broken_legs(owner.broken_legs + 1)
bone_status = BONE_FLAG_BROKEN
- addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, visible_message), "You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound."), 1 SECONDS)
+// addtimer(CALLBACK(src, PROC_REF(break_bone_feedback), 1 SECONDS)) testing sommething
+
+///obj/item/bodypart/proc/break_bone_feedback()
+ owner.visible_message("You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound.")
+ playsound(owner, list('sound/health/bone/bone_break1.ogg','sound/health/bone/bone_break2.ogg','sound/health/bone/bone_break3.ogg','sound/health/bone/bone_break4.ogg','sound/health/bone/bone_break5.ogg','sound/health/bone/bone_break6.ogg'), 100, FALSE, -1)
/obj/item/bodypart/proc/fix_bone()
// owner.update_inv_splints() breaks
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 84485cbe903d..ce89f62761db 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -938,15 +938,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/ammo/mech/bag
name = "Exosuit Support Kit Bag"
- desc = "A duffel bag containing ammo for four full reloads of the scattershotm which is equipped on standard Dark Gygax and Mauler exosuits. Also comes with some support equipment for maintaining the mech, including tools and an inducer."
+ desc = "A duffel bag containing ammo for four full reloads of the scattershot which is equipped on standard Dark Gygax and Touro exosuits. Also comes with some support equipment for maintaining the exosuit, including tools and an inducer."
item = /obj/item/storage/backpack/duffelbag/syndie/ammo/mech
cost = 4
include_modes = list(/datum/game_mode/nuclear)
-/datum/uplink_item/ammo/mauler/bag
- name = "Mauler Ammo Bag"
- desc = "A duffel bag containing ammo for three full reloads of the LMG, scattershot carbine, and SRM-8 missile laucher that are equipped on a standard Mauler exosuit."
- item = /obj/item/storage/backpack/duffelbag/syndie/ammo/mauler
+/datum/uplink_item/ammo/touro/bag
+ name = "Touro Ammo Bag"
+ desc = "A duffel bag containing ammo for three full reloads of the LMG, scattershot carbine, and SRM-8 missile laucher that are equipped on a standard Touro exosuit."
+ item = /obj/item/storage/backpack/duffelbag/syndie/ammo/touro
cost = 6
include_modes = list(/datum/game_mode/nuclear)
@@ -1195,11 +1195,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 80
include_modes = list(/datum/game_mode/nuclear/clown_ops)
-/datum/uplink_item/support/mauler
- name = "Mauler Exosuit"
- desc = "A massive and incredibly deadly military-grade exosuit. Features long-range targeting, thrust vectoring \
+/datum/uplink_item/support/touro
+ name = "Touro Exosuit"
+ desc = "A well-armored and incredibly deadly military-grade exosuit. Features long-range targeting, thrust vectoring \
and deployable smoke. Comes equipped with an LMG, scattershot carbine, missile rack, an antiprojectile armor booster and a Tesla energy array."
- item = /obj/mecha/combat/marauder/mauler/loaded
+ item = /obj/mecha/combat/marauder/touro/loaded
cost = 140
// Stealth Items
diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml
index 2dccf47025f9..722a57377629 100644
--- a/html/changelogs/archive/2024-08.yml
+++ b/html/changelogs/archive/2024-08.yml
@@ -125,3 +125,110 @@
- rscdel: Removed a bunch of random items from fitting in wallets (notably screwdrivers,
cigarettes, suture / mesh / gauze)
- rscadd: Ship keys and derringers can now fit in wallets
+2024-08-26:
+ Aquidu:
+ - rscadd: Rations now have icons that help show the contents.
+ - rscadd: Condiments and drink mixes now have colors when full to help show the
+ contents.
+ - rscadd: Unique sprites for the ration heater, drink packs ration drinks, and "side"
+ category rations.
+ - code_imp: Changes the sound effect for opening the drink ration to the bottle
+ cap sound to line up with the icon.
+ DIB-DOG:
+ - rscadd: Added a standard vest to the armory office locker
+ - rscadd: Added a Security camera console to the bridge
+ - balance: Silkenweave jackets and Betzu hats now provide cold protection but no
+ armor
+ - balance: Crying Sun now has 4 combustion/2 ion engines instead of 2/4
+ - bugfix: Fixes turrets on the Crying Sun and its subshuttle, the Nail.
+ FalloutFalcon, MrSamu99, Fikou:
+ - rscadd: a few shipments of MOD control units have found there way to the frontier,
+ premium versions of existing hard suits with the latest tech!
+ - rscadd: Ported modsuits from tg, no mapped stuff yet
+ Gristlebee:
+ - balance: Durand shield consumes less charge on being hit, passive drain increased.
+ - bugfix: Mechs consume the correct amount of power on movement.
+ - refactor: normal_step_energy_drain is now base_step_energy_drain
+ - rscadd: Grills can now cook food.
+ - rscdel: Xeno-energy working as grill fuel.
+ - balance: Outpost food costs
+ - bugfix: Grills
+ Ms-Mee:
+ - bugfix: fixed handle_quirk_conflict behavior
+ Sadhorizon:
+ - bugfix: Gloves now show up in the loadout preview.
+ - rscadd: Brown gloves were added to the loadout.
+ - tweak: Moved everything out of autolathe contraband - you no longer have to hack
+ it, ever.
+ - rscdel: Removed foilhat, flamethrower, electropack, .357 casing and slime scanner
+ from the autolathe.
+ - rscadd: Added a bunch of new items to the loadout.
+ - tweak: Changed the description of red suspenders.
+ - rscadd: Ihejirka space outpost now has player-accessible cryogenics.
+ SomeguyManperson:
+ - balance: wormholes on the overmap no longer decay when used
+ Thera-Pissed:
+ - code_imp: TEG efficiency now depends on temperature delta.
+ Vekter:
+ - bugfix: Removed an extra fire extinguisher from the crew quarters on the IRMV
+ Talos.
+2024-08-27:
+ Baystation12, Kapu1178, rye-rice:
+ - rscadd: Particles!
+ - rscadd: Bleeding has better feedback
+ - rscadd: Bone breaking now has sound effects
+ - rscadd: Getting shot now throws blood squirts! Live through the somme for REAL
+ this time!
+ - balance: gibbing no longer destroys your chest, no more legion transfers!
+ - bugfix: Lava particles should no longer destroy your FPS
+ FalloutFalcon:
+ - balance: Ballistic weapons now have a minimum camera shake.
+ - bugfix: Gunslinger now functions as intended.
+2024-08-28:
+ Anticept:
+ - bugfix: Adjusted the Box Hospital Ship's engineering layout so that it's less
+ jank, and the power system doesn't loop on itself anymore.
+ FalloutFalcon:
+ - rscdel: Removed blob and alot of stuff close to blob
+ Gristlebee:
+ - rscadd: Weapon cells can be inserted directly into weapon chargers to charge them.
+ - bugfix: e40s not charging in weapons rechargers.
+ firebudgy:
+ - bugfix: Medical Examinations via Flashlight is now an option again. Check eye
+ health or see what's in someone's mouth!
+ meemofcourse:
+ - rscadd: Vox lore blurb
+ - bugfix: Inconsistent capitalization when speaking due to guestbook
+ - code_imp: Changed how the game recognizes Vox and Sarathi age
+ thgvr:
+ - imageadd: Kepori hardsuits now have a fallback icon
+2024-08-29:
+ Apogee-dev:
+ - balance: SUNS and SolCon armor now uses standard armor rather than bulletproof
+ - balance: Normalized armor values between various faction helmets, coats, and vests
+ - balance: Armored coats now provide armor protection for the chest and groin only;
+ environmental protection unchanged
+ - bugfix: removed armor from some overlooked berets and soft hats
+ - refactor: Renamed the Mauler exosuit and several exosuit weapons to avoid confusion
+ Gristlebee:
+ - rscadd: Black Market Stock Cycling and item weighting
+ - rscadd: New black market stock
+ - rscadd: Tech and Ammo tab
+ - rscadd: Dead Drops
+ - rscadd: LTRSBT moved to the Black Market Catalogue
+ - rscadd: LTSRBT Crafting Recipe, 2 BS crystals, a bank card, 5 duct tape, a circuit
+ board and a network card
+ - rscadd: Reflavours the disco grenade
+ - rscadd: variable for powercells to show if they're rigged
+ - rscdel: Some old blackmarket stock
+ - balance: LTRSBT shipping cost up to 100 credits
+ - balance: Launch delivery more reliable
+ - bugfix: Powerfist works again
+ - spellcheck: fixed a few typos in the black market
+ - code_imp: Dynamic Overmap encounters store what ruins were spawned
+ - code_imp: get_block_portion and get_position_in_margin methods in mapzones.dm
+ - code_imp: Pair items now handled by a list
+ - refactor: Black Market item stocking
+2024-08-30:
+ meemofcourse:
+ - rscadd: Character slots have been raised to 40. BYOND members get 50.
diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi
index f7e2e158d422..aed7e9b4fbf4 100644
Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ
diff --git a/icons/effects/magic.dmi b/icons/effects/magic.dmi
new file mode 100644
index 000000000000..480332df1349
Binary files /dev/null and b/icons/effects/magic.dmi differ
diff --git a/icons/effects/particles/bonfire.dmi b/icons/effects/particles/bonfire.dmi
new file mode 100644
index 000000000000..e8e2e36346da
Binary files /dev/null and b/icons/effects/particles/bonfire.dmi differ
diff --git a/icons/effects/particles/echo.dmi b/icons/effects/particles/echo.dmi
new file mode 100644
index 000000000000..60a243a8a7be
Binary files /dev/null and b/icons/effects/particles/echo.dmi differ
diff --git a/icons/effects/particles/generic.dmi b/icons/effects/particles/generic.dmi
new file mode 100644
index 000000000000..dfbb1a47a6ef
Binary files /dev/null and b/icons/effects/particles/generic.dmi differ
diff --git a/icons/effects/particles/goop.dmi b/icons/effects/particles/goop.dmi
new file mode 100644
index 000000000000..673c1a7ad5b6
Binary files /dev/null and b/icons/effects/particles/goop.dmi differ
diff --git a/icons/effects/particles/pollen.dmi b/icons/effects/particles/pollen.dmi
new file mode 100644
index 000000000000..559c4d1846f6
Binary files /dev/null and b/icons/effects/particles/pollen.dmi differ
diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi
new file mode 100644
index 000000000000..4a3239499b96
Binary files /dev/null and b/icons/effects/particles/smoke.dmi differ
diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi
index a8a7185af500..f76e34ec71ec 100644
Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ
diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi
new file mode 100644
index 000000000000..9786d403e0de
Binary files /dev/null and b/icons/hud/radial.dmi differ
diff --git a/icons/mecha/mecha.dmi b/icons/mecha/mecha.dmi
index b894d9191225..9b9c2b479c7b 100644
Binary files a/icons/mecha/mecha.dmi and b/icons/mecha/mecha.dmi differ
diff --git a/icons/mob/actions/actions_mod.dmi b/icons/mob/actions/actions_mod.dmi
new file mode 100644
index 000000000000..7f030ad53d42
Binary files /dev/null and b/icons/mob/actions/actions_mod.dmi differ
diff --git a/icons/mob/blob.dmi b/icons/mob/blob.dmi
deleted file mode 100644
index a197581533c0..000000000000
Binary files a/icons/mob/blob.dmi and /dev/null differ
diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 000000000000..27d4df3b9023
Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 000000000000..11259428cf4d
Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/mob/clothing/species/kepori.dmi b/icons/mob/clothing/species/kepori.dmi
index 1586e80e9b19..9e108db76b87 100644
Binary files a/icons/mob/clothing/species/kepori.dmi and b/icons/mob/clothing/species/kepori.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 000000000000..d2d9e0c72e37
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi
new file mode 100644
index 000000000000..a6be94284af8
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_construction.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 000000000000..69affa3fa499
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/food/ration.dmi b/icons/obj/food/ration.dmi
index 5bcf1f2b490b..42cc013cc140 100644
Binary files a/icons/obj/food/ration.dmi and b/icons/obj/food/ration.dmi differ
diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi
index f5f04901af2a..af3c5cd4be2e 100644
Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index 78aebb9416c4..0894fa0a466a 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/shiptest.dme b/shiptest.dme
index d85c22c39681..55ad11a800d5 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -25,6 +25,7 @@
#include "code\__DEFINES\_tick.dm"
#include "code\__DEFINES\access.dm"
#include "code\__DEFINES\achievements.dm"
+#include "code\__DEFINES\actions.dm"
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\anomalies.dm"
#include "code\__DEFINES\antagonists.dm"
@@ -66,6 +67,7 @@
#include "code\__DEFINES\food.dm"
#include "code\__DEFINES\footsteps.dm"
#include "code\__DEFINES\forensics.dm"
+#include "code\__DEFINES\generators.dm"
#include "code\__DEFINES\guns.dm"
#include "code\__DEFINES\hud.dm"
#include "code\__DEFINES\icon_smoothing.dm"
@@ -94,6 +96,7 @@
#include "code\__DEFINES\menu.dm"
#include "code\__DEFINES\misc.dm"
#include "code\__DEFINES\mobs.dm"
+#include "code\__DEFINES\mod.dm"
#include "code\__DEFINES\monkeys.dm"
#include "code\__DEFINES\move_force.dm"
#include "code\__DEFINES\movement.dm"
@@ -103,6 +106,7 @@
#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\overmap.dm"
#include "code\__DEFINES\paper.dm"
+#include "code\__DEFINES\particles.dm"
#include "code\__DEFINES\pinpointers.dm"
#include "code\__DEFINES\pipe_construction.dm"
#include "code\__DEFINES\plumbing.dm"
@@ -156,6 +160,9 @@
#include "code\__DEFINES\wires.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
+#include "code\__DEFINES\dcs\signals\signals_mod.dm"
+#include "code\__DEFINES\dcs\signals\signals_storage.dm"
+#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_carbon.dm"
#include "code\__DEFINES\dcs\signals\signals.dm"
#include "code\__DEFINES\dcs\signals\signals_obj\signals_object.dm"
#include "code\__DEFINES\dcs\signals\signals_obj\signals_item\signals_clothing.dm"
@@ -169,9 +176,11 @@
#include "code\__HELPERS\_auxtools_api.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_logging.dm"
+#include "code\__HELPERS\_planes.dm"
#include "code\__HELPERS\_string_lists.dm"
#include "code\__HELPERS\areas.dm"
#include "code\__HELPERS\AStar.dm"
+#include "code\__HELPERS\atoms.dm"
#include "code\__HELPERS\bindings.dm"
#include "code\__HELPERS\bitflag_lists.dm"
#include "code\__HELPERS\chat.dm"
@@ -183,12 +192,14 @@
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\filters.dm"
#include "code\__HELPERS\game.dm"
+#include "code\__HELPERS\generators.dm"
#include "code\__HELPERS\global_lists.dm"
#include "code\__HELPERS\heap.dm"
#include "code\__HELPERS\icon_smoothing.dm"
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\level_traits.dm"
#include "code\__HELPERS\lighting.dm"
+#include "code\__HELPERS\maths.dm"
#include "code\__HELPERS\matrices.dm"
#include "code\__HELPERS\mobs.dm"
#include "code\__HELPERS\mouse_control.dm"
@@ -258,7 +269,6 @@
#include "code\_onclick\item_attack.dm"
#include "code\_onclick\observer.dm"
#include "code\_onclick\other_mobs.dm"
-#include "code\_onclick\overmind.dm"
#include "code\_onclick\pai.dm"
#include "code\_onclick\telekinesis.dm"
#include "code\_onclick\hud\_defines.dm"
@@ -267,8 +277,6 @@
#include "code\_onclick\hud\alert.dm"
#include "code\_onclick\hud\alien.dm"
#include "code\_onclick\hud\alien_larva.dm"
-#include "code\_onclick\hud\blob_overmind.dm"
-#include "code\_onclick\hud\blobbernauthud.dm"
#include "code\_onclick\hud\constructs.dm"
#include "code\_onclick\hud\credits.dm"
#include "code\_onclick\hud\devil.dm"
@@ -505,6 +513,7 @@
#include "code\datums\components\hot_ice.dm"
#include "code\datums\components\igniter.dm"
#include "code\datums\components\infective.dm"
+#include "code\datums\components\jetpack.dm"
#include "code\datums\components\jousting.dm"
#include "code\datums\components\knockback.dm"
#include "code\datums\components\knockoff.dm"
@@ -532,6 +541,7 @@
#include "code\datums\components\remote_materials.dm"
#include "code\datums\components\riding.dm"
#include "code\datums\components\rotation.dm"
+#include "code\datums\components\shielded.dm"
#include "code\datums\components\shrink.dm"
#include "code\datums\components\sitcomlaughter.dm"
#include "code\datums\components\sizzle.dm"
@@ -653,6 +663,7 @@
#include "code\datums\elements\digitalcamo.dm"
#include "code\datums\elements\earhealing.dm"
#include "code\datums\elements\embed.dm"
+#include "code\datums\elements\empprotection.dm"
#include "code\datums\elements\firestacker.dm"
#include "code\datums\elements\forced_gravity.dm"
#include "code\datums\elements\lazy_fishing_spot.dm"
@@ -803,6 +814,7 @@
#include "code\datums\wires\fax.dm"
#include "code\datums\wires\microwave.dm"
#include "code\datums\wires\mines.dm"
+#include "code\datums\wires\mod.dm"
#include "code\datums\wires\mulebot.dm"
#include "code\datums\wires\particle_accelerator.dm"
#include "code\datums\wires\r_n_d.dm"
@@ -1109,6 +1121,12 @@
#include "code\game\objects\effects\effect_system\effects_smoke.dm"
#include "code\game\objects\effects\effect_system\effects_sparks.dm"
#include "code\game\objects\effects\effect_system\effects_water.dm"
+#include "code\game\objects\effects\particles\acid.dm"
+#include "code\game\objects\effects\particles\fire.dm"
+#include "code\game\objects\effects\particles\misc.dm"
+#include "code\game\objects\effects\particles\slime.dm"
+#include "code\game\objects\effects\particles\smoke.dm"
+#include "code\game\objects\effects\particles\water.dm"
#include "code\game\objects\effects\spawners\bombspawner.dm"
#include "code\game\objects\effects\spawners\bundle.dm"
#include "code\game\objects\effects\spawners\gibspawner.dm"
@@ -1639,34 +1657,6 @@
#include "code\modules\antagonists\abductor\machinery\experiment.dm"
#include "code\modules\antagonists\abductor\machinery\pad.dm"
#include "code\modules\antagonists\ashwalker\ashwalker.dm"
-#include "code\modules\antagonists\blob\blob.dm"
-#include "code\modules\antagonists\blob\blob_mobs.dm"
-#include "code\modules\antagonists\blob\blob_report.dm"
-#include "code\modules\antagonists\blob\overmind.dm"
-#include "code\modules\antagonists\blob\powers.dm"
-#include "code\modules\antagonists\blob\blobstrains\_blobstrain.dm"
-#include "code\modules\antagonists\blob\blobstrains\_reagent.dm"
-#include "code\modules\antagonists\blob\blobstrains\blazing_oil.dm"
-#include "code\modules\antagonists\blob\blobstrains\cryogenic_poison.dm"
-#include "code\modules\antagonists\blob\blobstrains\debris_devourer.dm"
-#include "code\modules\antagonists\blob\blobstrains\distributed_neurons.dm"
-#include "code\modules\antagonists\blob\blobstrains\electromagnetic_web.dm"
-#include "code\modules\antagonists\blob\blobstrains\energized_jelly.dm"
-#include "code\modules\antagonists\blob\blobstrains\explosive_lattice.dm"
-#include "code\modules\antagonists\blob\blobstrains\multiplex.dm"
-#include "code\modules\antagonists\blob\blobstrains\networked_fibers.dm"
-#include "code\modules\antagonists\blob\blobstrains\pressurized_slime.dm"
-#include "code\modules\antagonists\blob\blobstrains\reactive_spines.dm"
-#include "code\modules\antagonists\blob\blobstrains\regenerative_materia.dm"
-#include "code\modules\antagonists\blob\blobstrains\replicating_foam.dm"
-#include "code\modules\antagonists\blob\blobstrains\shifting_fragments.dm"
-#include "code\modules\antagonists\blob\blobstrains\synchronous_mesh.dm"
-#include "code\modules\antagonists\blob\structures\_blob.dm"
-#include "code\modules\antagonists\blob\structures\core.dm"
-#include "code\modules\antagonists\blob\structures\factory.dm"
-#include "code\modules\antagonists\blob\structures\node.dm"
-#include "code\modules\antagonists\blob\structures\resource.dm"
-#include "code\modules\antagonists\blob\structures\shield.dm"
#include "code\modules\antagonists\blood_contract\blood_contract.dm"
#include "code\modules\antagonists\borer\borer.dm"
#include "code\modules\antagonists\borer\borer_chems.dm"
@@ -1875,7 +1865,6 @@
#include "code\modules\awaymissions\mission_code\spacebattle.dm"
#include "code\modules\awaymissions\mission_code\stationCollision.dm"
#include "code\modules\awaymissions\mission_code\undergroundoutpost45.dm"
-#include "code\modules\awaymissions\mission_code\wildwest.dm"
#include "code\modules\balloon_alert\balloon_alert.dm"
#include "code\modules\buildmode\bm_mode.dm"
#include "code\modules\buildmode\buildmode.dm"
@@ -1911,11 +1900,13 @@
#include "code\modules\cargo\blackmarket\blackmarket_market.dm"
#include "code\modules\cargo\blackmarket\blackmarket_telepad.dm"
#include "code\modules\cargo\blackmarket\blackmarket_uplink.dm"
+#include "code\modules\cargo\blackmarket\blackmarket_items\ammo.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\clothing.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\consumables.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\emergency.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\explosives.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\misc.dm"
+#include "code\modules\cargo\blackmarket\blackmarket_items\tech.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\tools.dm"
#include "code\modules\cargo\blackmarket\blackmarket_items\weapons.dm"
#include "code\modules\cargo\bounties\assistant.dm"
@@ -2112,7 +2103,6 @@
#include "code\modules\events\abductor.dm"
#include "code\modules\events\alien_infestation.dm"
#include "code\modules\events\aurora_caelus.dm"
-#include "code\modules\events\blob.dm"
#include "code\modules\events\borers.dm"
#include "code\modules\events\brain_trauma.dm"
#include "code\modules\events\brand_intelligence.dm"
@@ -2152,7 +2142,6 @@
#include "code\modules\events\holiday\vday.dm"
#include "code\modules\events\holiday\xmas.dm"
#include "code\modules\events\wizard\aid.dm"
-#include "code\modules\events\wizard\blobies.dm"
#include "code\modules\events\wizard\curseditems.dm"
#include "code\modules\events\wizard\departmentrevolt.dm"
#include "code\modules\events\wizard\embeddies.dm"
@@ -2817,6 +2806,30 @@
#include "code\modules\mob\living\simple_animal\slime\slime.dm"
#include "code\modules\mob\living\simple_animal\slime\slime_say.dm"
#include "code\modules\mob\living\simple_animal\slime\subtypes.dm"
+#include "code\modules\mod\mod_actions.dm"
+#include "code\modules\mod\mod_activation.dm"
+#include "code\modules\mod\mod_ai.dm"
+#include "code\modules\mod\mod_clothes.dm"
+#include "code\modules\mod\mod_construction.dm"
+#include "code\modules\mod\mod_control.dm"
+#include "code\modules\mod\mod_core.dm"
+#include "code\modules\mod\mod_paint.dm"
+#include "code\modules\mod\mod_theme.dm"
+#include "code\modules\mod\mod_types.dm"
+#include "code\modules\mod\mod_ui.dm"
+#include "code\modules\mod\modules\_module.dm"
+#include "code\modules\mod\modules\modules_antag.dm"
+#include "code\modules\mod\modules\modules_engineering.dm"
+#include "code\modules\mod\modules\modules_general.dm"
+#include "code\modules\mod\modules\modules_maint.dm"
+#include "code\modules\mod\modules\modules_medical.dm"
+#include "code\modules\mod\modules\modules_ninja.dm"
+#include "code\modules\mod\modules\modules_science.dm"
+#include "code\modules\mod\modules\modules_security.dm"
+#include "code\modules\mod\modules\modules_service.dm"
+#include "code\modules\mod\modules\modules_storage.dm"
+#include "code\modules\mod\modules\modules_supply.dm"
+#include "code\modules\mod\modules\modules_visor.dm"
#include "code\modules\mob_spawner\burrow.dm"
#include "code\modules\mob_spawner\hivebot.dm"
#include "code\modules\mob_spawner\spawner.dm"
diff --git a/sound/ambience/storm_indoors.ogg b/sound/ambience/storm_indoors.ogg
new file mode 100644
index 000000000000..62e9014a05ff
Binary files /dev/null and b/sound/ambience/storm_indoors.ogg differ
diff --git a/sound/ambience/storm_outdoors.ogg b/sound/ambience/storm_outdoors.ogg
new file mode 100644
index 000000000000..35ae8e5297d6
Binary files /dev/null and b/sound/ambience/storm_outdoors.ogg differ
diff --git a/sound/effects/splatter.ogg b/sound/effects/splatter.ogg
new file mode 100644
index 000000000000..1c678cfe1268
Binary files /dev/null and b/sound/effects/splatter.ogg differ
diff --git a/sound/health/bone/bone_break1.ogg b/sound/health/bone/bone_break1.ogg
new file mode 100644
index 000000000000..dd2d22ec792b
Binary files /dev/null and b/sound/health/bone/bone_break1.ogg differ
diff --git a/sound/health/bone/bone_break2.ogg b/sound/health/bone/bone_break2.ogg
new file mode 100644
index 000000000000..aa2537f894de
Binary files /dev/null and b/sound/health/bone/bone_break2.ogg differ
diff --git a/sound/health/bone/bone_break3.ogg b/sound/health/bone/bone_break3.ogg
new file mode 100644
index 000000000000..9f66324be3b2
Binary files /dev/null and b/sound/health/bone/bone_break3.ogg differ
diff --git a/sound/health/bone/bone_break4.ogg b/sound/health/bone/bone_break4.ogg
new file mode 100644
index 000000000000..bbdfac1ecff3
Binary files /dev/null and b/sound/health/bone/bone_break4.ogg differ
diff --git a/sound/health/bone/bone_break5.ogg b/sound/health/bone/bone_break5.ogg
new file mode 100644
index 000000000000..dfee0e9baa72
Binary files /dev/null and b/sound/health/bone/bone_break5.ogg differ
diff --git a/sound/health/bone/bone_break6.ogg b/sound/health/bone/bone_break6.ogg
new file mode 100644
index 000000000000..d41cc8d7cf54
Binary files /dev/null and b/sound/health/bone/bone_break6.ogg differ
diff --git a/sound/items/modsuit/atrocinator_step.ogg b/sound/items/modsuit/atrocinator_step.ogg
new file mode 100644
index 000000000000..deda85ac354b
Binary files /dev/null and b/sound/items/modsuit/atrocinator_step.ogg differ
diff --git a/sound/items/modsuit/ballin.ogg b/sound/items/modsuit/ballin.ogg
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/sound/items/modsuit/ballout.ogg b/sound/items/modsuit/ballout.ogg
new file mode 100644
index 000000000000..f911f1a6a61d
Binary files /dev/null and b/sound/items/modsuit/ballout.ogg differ
diff --git a/sound/items/modsuit/flamethrower.ogg b/sound/items/modsuit/flamethrower.ogg
new file mode 100644
index 000000000000..447245d50b6e
Binary files /dev/null and b/sound/items/modsuit/flamethrower.ogg differ
diff --git a/sound/items/modsuit/inflate_bloon.ogg b/sound/items/modsuit/inflate_bloon.ogg
new file mode 100644
index 000000000000..9b030d66ced7
Binary files /dev/null and b/sound/items/modsuit/inflate_bloon.ogg differ
diff --git a/sound/items/modsuit/loader_charge.ogg b/sound/items/modsuit/loader_charge.ogg
new file mode 100644
index 000000000000..61d5531f72ed
Binary files /dev/null and b/sound/items/modsuit/loader_charge.ogg differ
diff --git a/sound/items/modsuit/loader_launch.ogg b/sound/items/modsuit/loader_launch.ogg
new file mode 100644
index 000000000000..513118f3c682
Binary files /dev/null and b/sound/items/modsuit/loader_launch.ogg differ
diff --git a/sound/items/modsuit/magnetic_harness.ogg b/sound/items/modsuit/magnetic_harness.ogg
new file mode 100644
index 000000000000..3d19fccc5698
Binary files /dev/null and b/sound/items/modsuit/magnetic_harness.ogg differ
diff --git a/sound/items/modsuit/rewinder.ogg b/sound/items/modsuit/rewinder.ogg
new file mode 100644
index 000000000000..2587562dc117
Binary files /dev/null and b/sound/items/modsuit/rewinder.ogg differ
diff --git a/sound/items/modsuit/springlock.ogg b/sound/items/modsuit/springlock.ogg
new file mode 100644
index 000000000000..8d0013d26300
Binary files /dev/null and b/sound/items/modsuit/springlock.ogg differ
diff --git a/sound/items/modsuit/tem_shot.ogg b/sound/items/modsuit/tem_shot.ogg
new file mode 100644
index 000000000000..50905b95f112
Binary files /dev/null and b/sound/items/modsuit/tem_shot.ogg differ
diff --git a/sound/items/modsuit/time_anchor_set.ogg b/sound/items/modsuit/time_anchor_set.ogg
new file mode 100644
index 000000000000..457f8e6dbaee
Binary files /dev/null and b/sound/items/modsuit/time_anchor_set.ogg differ
diff --git a/sound/mecha/hydraulic.ogg b/sound/mecha/hydraulic.ogg
new file mode 100644
index 000000000000..3281ed2dc0f0
Binary files /dev/null and b/sound/mecha/hydraulic.ogg differ
diff --git a/tgui/packages/tgui/interfaces/BlackMarketUplink.js b/tgui/packages/tgui/interfaces/BlackMarketUplink.js
index 2c4a05d09ef5..9fff7cff715f 100644
--- a/tgui/packages/tgui/interfaces/BlackMarketUplink.js
+++ b/tgui/packages/tgui/interfaces/BlackMarketUplink.js
@@ -23,7 +23,7 @@ export const BlackMarketUplink = (props, context) => {
viewing_category,
} = data;
return (
-
+
{
+ switch (param) {
+ case 'red':
+ return [
+ 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0.25, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ ];
+ case 'yellow':
+ return [
+ 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0.25, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0,
+ ];
+ case 'green':
+ return [
+ 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ ];
+ case 'teal':
+ return [
+ 0.25, 0.25, 0.25, 0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0,
+ ];
+ case 'blue':
+ return [
+ 0.25, 0, 0.25, 0, 0, 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ ];
+ case 'purple':
+ return [
+ 0.5, 0, 0.5, 0, 0.25, 0.5, 0.25, 0, 0.5, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0,
+ ];
+ }
+};
+
+const displayText = (param) => {
+ switch (param) {
+ case 'r':
+ return 'Red';
+ case 'g':
+ return 'Green';
+ case 'b':
+ return 'Blue';
+ }
+};
+
+export const MODpaint = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { mapRef, currentColor } = data;
+ const [
+ [rr, rg, rb, ra],
+ [gr, gg, gb, ga],
+ [br, bg, bb, ba],
+ [ar, ag, ab, aa],
+ [cr, cg, cb, ca],
+ ] = currentColor;
+ const presets = ['red', 'yellow', 'green', 'teal', 'blue', 'purple'];
+ const prefixes = ['r', 'g', 'b'];
+ return (
+
+
+
+
+ {[0, 1, 2].map((row) => (
+
+ {[0, 1, 2].map((col) => (
+
+
+
+ {`${displayText(prefixes[col])}:`}
+
+
+
+ `${value}%`}
+ onDrag={(e, value) => {
+ let retColor = currentColor;
+ retColor[row * 4 + col] = value / 100;
+ act('transition_color', { color: retColor });
+ }}
+ />
+
+
+ ))}
+
+ ))}
+
+
+
+
+ {presets.map((preset) => (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/MODsuit.js b/tgui/packages/tgui/interfaces/MODsuit.js
new file mode 100644
index 000000000000..d8edbbc4e3f3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/MODsuit.js
@@ -0,0 +1,797 @@
+import { useBackend, useLocalState } from '../backend';
+import {
+ Button,
+ ColorBox,
+ LabeledList,
+ ProgressBar,
+ Section,
+ Collapsible,
+ Box,
+ Icon,
+ Stack,
+ Table,
+ Dimmer,
+ NumberInput,
+ Flex,
+ AnimatedNumber,
+ Dropdown,
+} from '../components';
+import { Window } from '../layouts';
+
+const ConfigureNumberEntry = (props, context) => {
+ const { name, value, module_ref } = props;
+ const { act } = useBackend(context);
+ return (
+
+ act('configure', {
+ 'key': name,
+ 'value': value,
+ 'ref': module_ref,
+ })
+ }
+ />
+ );
+};
+
+const ConfigureBoolEntry = (props, context) => {
+ const { name, value, module_ref } = props;
+ const { act } = useBackend(context);
+ return (
+
+ act('configure', {
+ 'key': name,
+ 'value': !value,
+ 'ref': module_ref,
+ })
+ }
+ />
+ );
+};
+
+const ConfigureColorEntry = (props, context) => {
+ const { name, value, module_ref } = props;
+ const { act } = useBackend(context);
+ return (
+ <>
+