diff --git a/_maps/RandomZLevels/museum.dmm b/_maps/RandomZLevels/museum.dmm
index 7429c0b45382e..f40ecc09015c6 100644
--- a/_maps/RandomZLevels/museum.dmm
+++ b/_maps/RandomZLevels/museum.dmm
@@ -3147,6 +3147,7 @@
/obj/structure/transport/linear/tram/slow,
/obj/structure/thermoplastic,
/obj/effect/spawner/random/structure/closet_empty/crate/with_loot,
+/obj/effect/spawner/random/maintenance/no_decals/five,
/turf/open/chasm/true/no_smooth,
/area/awaymission/museum)
"Cc" = (
@@ -4988,7 +4989,7 @@
/obj/machinery/door/window/elevator/right/directional/south{
transport_linked_id = "museum_cargo"
},
-/obj/effect/spawner/random/maintenance/seven,
+/obj/effect/spawner/random/maintenance/no_decals/seven,
/turf/open/chasm/true/no_smooth,
/area/awaymission/museum)
"Um" = (
@@ -5343,7 +5344,7 @@
"WJ" = (
/obj/structure/transport/linear/tram/slow,
/obj/structure/thermoplastic,
-/obj/effect/spawner/random/maintenance/seven,
+/obj/effect/spawner/random/maintenance/no_decals/seven,
/obj/item/storage/pill_bottle/maintenance_pill/full,
/obj/effect/spawner/random/structure/closet_empty/crate/with_loot,
/turf/open/chasm/true/no_smooth,
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index b2fad9e88d2b6..25141a232c003 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -5456,10 +5456,6 @@
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plating,
/area/station/security/tram)
-"cgb" = (
-/obj/machinery/duct,
-/turf/open/floor/iron/kitchen/small,
-/area/station/security/prison/mess)
"cgy" = (
/obj/effect/turf_decal/stripes/red/line{
dir = 4
@@ -11344,7 +11340,9 @@
/area/station/maintenance/starboard/aft)
"ena" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/reagent_dispensers/plumbed,
+/obj/structure/reagent_dispensers/plumbed{
+ dir = 1
+ },
/turf/open/floor/iron,
/area/station/maintenance/department/medical/central)
"enb" = (
@@ -14096,7 +14094,6 @@
"fls" = (
/obj/structure/cable,
/obj/effect/decal/cleanable/dirt,
-/obj/machinery/duct,
/obj/effect/mapping_helpers/broken_floor,
/obj/effect/decal/cleanable/glass/plastitanium,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -16053,6 +16050,12 @@
/obj/machinery/airalarm/directional/west,
/turf/open/floor/iron/white,
/area/station/medical/medbay/central)
+"fRZ" = (
+/obj/structure/cable,
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/turf/open/floor/plating,
+/area/station/maintenance/fore/greater)
"fSe" = (
/turf/closed/wall/rust,
/area/station/cargo/miningfoundry)
@@ -17810,7 +17813,6 @@
/obj/structure/cable,
/obj/effect/decal/cleanable/dirt,
/obj/effect/mapping_helpers/broken_floor,
-/obj/machinery/duct,
/obj/machinery/power/apc/auto_name/directional/west,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -24624,6 +24626,9 @@
"iSW" = (
/obj/structure/rack,
/obj/item/clothing/gloves/boxing/yellow,
+/obj/item/clothing/gloves/boxing/green{
+ pixel_y = 4
+ },
/turf/open/floor/plating,
/area/station/maintenance/fore/greater)
"iTn" = (
@@ -24697,8 +24702,7 @@
/turf/open/floor/plating,
/area/station/engineering/atmos)
"iUy" = (
-/obj/structure/rack,
-/obj/item/clothing/gloves/boxing/green,
+/obj/structure/reagent_dispensers/plumbed,
/turf/open/floor/plating,
/area/station/maintenance/fore/greater)
"iUz" = (
@@ -27370,7 +27374,6 @@
"jPr" = (
/obj/structure/cable,
/obj/effect/decal/cleanable/dirt,
-/obj/machinery/duct,
/obj/effect/mapping_helpers/broken_floor,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -28482,6 +28485,12 @@
/obj/item/storage/backpack/duffelbag,
/turf/open/floor/plating,
/area/station/maintenance/fore/greater)
+"kld" = (
+/obj/structure/cable,
+/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/obj/machinery/duct,
+/turf/open/floor/iron/kitchen/small,
+/area/station/security/prison/mess)
"klf" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/chair{
@@ -30608,7 +30617,6 @@
"kZo" = (
/obj/structure/cable,
/obj/effect/decal/cleanable/dirt,
-/obj/machinery/duct,
/obj/effect/spawner/random/trash,
/obj/effect/decal/cleanable/dirt,
/obj/effect/mapping_helpers/broken_floor,
@@ -32653,6 +32661,12 @@
/obj/structure/chair/stool/directional/south,
/turf/open/floor/carpet/donk,
/area/station/command/heads_quarters/qm)
+"lHi" = (
+/obj/structure/cable,
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/duct,
+/turf/open/floor/iron/kitchen/small,
+/area/station/security/prison/mess)
"lHk" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -49391,7 +49405,9 @@
/area/station/maintenance/port/lesser)
"row" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/reagent_dispensers/plumbed,
+/obj/structure/reagent_dispensers/plumbed{
+ dir = 4
+ },
/turf/open/floor/plating,
/area/station/maintenance/central/greater)
"roz" = (
@@ -62899,6 +62915,7 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{
dir = 8
},
+/obj/machinery/duct,
/turf/open/floor/iron/kitchen/small,
/area/station/security/prison/mess)
"vEe" = (
@@ -89949,7 +89966,7 @@ qeP
lrE
oPV
pId
-ect
+kld
qRM
vSx
eGL
@@ -90205,7 +90222,7 @@ uPr
qeP
lrE
oPV
-cgb
+eHf
vEb
eHf
qsR
@@ -90463,7 +90480,7 @@ lub
tBA
oPV
iNz
-jDe
+lHi
jDe
tSB
eGL
@@ -110124,7 +110141,7 @@ aJN
tIE
nFW
iSW
-rdh
+fRZ
liR
jQL
uwH
diff --git a/_maps/map_files/Deathmatch/The_Brig.dmm b/_maps/map_files/Deathmatch/The_Brig.dmm
index 15048da48e2b9..208b2c994737d 100644
--- a/_maps/map_files/Deathmatch/The_Brig.dmm
+++ b/_maps/map_files/Deathmatch/The_Brig.dmm
@@ -532,10 +532,6 @@
},
/turf/open/indestructible,
/area/deathmatch)
-"jB" = (
-/obj/machinery/light_switch/directional/west,
-/turf/open/indestructible/dark,
-/area/deathmatch)
"jN" = (
/obj/structure/table,
/obj/machinery/light/small/directional/north,
@@ -894,10 +890,6 @@
},
/turf/open/indestructible,
/area/deathmatch)
-"oy" = (
-/obj/machinery/light_switch/directional/west,
-/turf/open/floor/iron/grimy,
-/area/deathmatch)
"oA" = (
/obj/machinery/light/directional/north,
/turf/open/indestructible,
@@ -1162,13 +1154,6 @@
/obj/effect/decal/cleanable/oil/slippery,
/turf/open/indestructible,
/area/deathmatch)
-"rN" = (
-/obj/effect/turf_decal/trimline/red/filled/line{
- dir = 4
- },
-/obj/machinery/light_switch/directional/east,
-/turf/open/indestructible,
-/area/deathmatch)
"sb" = (
/obj/structure/closet{
name = "Evidence Closet 4"
@@ -1526,9 +1511,6 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
-/obj/machinery/light_switch/directional/west{
- pixel_y = -12
- },
/obj/structure/extinguisher_cabinet/directional/west,
/turf/open/indestructible/dark,
/area/deathmatch)
@@ -1683,7 +1665,6 @@
"yT" = (
/obj/effect/turf_decal/tile/red,
/obj/machinery/light/directional/west,
-/obj/machinery/light_switch/directional/west,
/obj/effect/turf_decal/tile/red{
dir = 8
},
@@ -2344,16 +2325,6 @@
},
/turf/open/indestructible/dark,
/area/deathmatch)
-"Ii" = (
-/obj/effect/turf_decal/tile/red{
- dir = 1
- },
-/obj/effect/turf_decal/tile/red{
- dir = 8
- },
-/obj/machinery/light_switch/directional/east,
-/turf/open/indestructible/dark,
-/area/deathmatch)
"Ik" = (
/obj/effect/turf_decal/trimline/red/filled/corner,
/turf/open/indestructible,
@@ -3022,11 +2993,6 @@
/obj/item/crowbar,
/turf/open/indestructible,
/area/deathmatch)
-"RL" = (
-/obj/machinery/light_switch/directional/south,
-/obj/structure/cable,
-/turf/open/floor/carpet,
-/area/deathmatch)
"RR" = (
/obj/structure/cable,
/obj/machinery/door/poddoor/preopen{
@@ -4117,7 +4083,7 @@ tH
xi
Am
EP
-Ii
+tH
Kh
Rv
UU
@@ -4172,7 +4138,7 @@ yH
uD
xk
Bf
-jB
+Ux
JU
YW
yy
@@ -4462,7 +4428,7 @@ Hz
Ik
Zv
ad
-oy
+Pt
Pt
eD
QG
@@ -4563,7 +4529,7 @@ Cx
ri
yO
cW
-rN
+Os
Os
Hp
UQ
@@ -4671,7 +4637,7 @@ cf
cf
YN
aE
-RL
+cf
Yh
Wg
qP
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index d95f80e47138e..ed91e1bd69733 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -4573,6 +4573,10 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/turf/open/floor/iron/white,
/area/station/medical/psychology)
+"bgq" = (
+/obj/machinery/bluespace_vendor/directional/west,
+/turf/open/floor/iron/dark,
+/area/station/hallway/floor2/fore)
"bgr" = (
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/iron/dark/textured_corner,
@@ -7446,6 +7450,12 @@
},
/turf/open/floor/iron/dark,
/area/station/security/checkpoint/third)
+"bQo" = (
+/obj/machinery/bluespace_vendor/directional/north,
+/turf/open/floor/iron/dark/side{
+ dir = 4
+ },
+/area/station/hallway/floor4/aft)
"bQv" = (
/obj/machinery/power/terminal{
dir = 4
@@ -8049,9 +8059,7 @@
/obj/machinery/atmospherics/pipe/heat_exchanging/manifold/layer2{
dir = 4
},
-/obj/machinery/atmospherics/components/unary/vent_scrubber{
- dir = 4
- },
+/obj/machinery/atmospherics/components/unary/vent_scrubber,
/turf/open/floor/engine/vacuum,
/area/station/science/ordnance/freezerchamber)
"bYp" = (
@@ -10363,9 +10371,6 @@
/obj/effect/spawner/structure/window/hollow/plasma/middle{
dir = 4
},
-/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{
- dir = 4
- },
/turf/open/floor/engine/vacuum,
/area/station/science/ordnance/freezerchamber)
"cBU" = (
@@ -16517,6 +16522,7 @@
dir = 8
},
/obj/item/storage/toolbox/emergency,
+/obj/machinery/bluespace_vendor/directional/south,
/turf/open/floor/iron/dark,
/area/station/commons/storage/tools)
"eir" = (
@@ -17584,6 +17590,7 @@
/area/station/security/office)
"exv" = (
/obj/machinery/atmospherics/pipe/smart/manifold/purple/visible/layer2,
+/obj/machinery/atmospherics/pipe/smart/simple/purple,
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
"exw" = (
@@ -22107,8 +22114,9 @@
/area/station/maintenance/floor3/starboard)
"fLX" = (
/obj/structure/railing/corner,
-/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{
- dir = 4
+/obj/machinery/atmospherics/pipe/smart/simple/purple,
+/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible/layer4{
+ dir = 8
},
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
@@ -22358,8 +22366,8 @@
/turf/open/floor/grass,
/area/station/security/courtroom)
"fPd" = (
-/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{
- dir = 4
+/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible/layer4{
+ dir = 8
},
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
@@ -24907,6 +24915,13 @@
/obj/structure/sign/poster/official/random/directional/south,
/turf/open/floor/iron/dark/side,
/area/station/hallway/floor2/fore)
+"gxE" = (
+/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/obj/structure/cable,
+/obj/machinery/bluespace_vendor/directional/north,
+/turf/open/floor/iron,
+/area/station/hallway/floor1/aft)
"gxH" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -32627,6 +32642,9 @@
/obj/structure/railing{
dir = 4
},
+/obj/machinery/atmospherics/components/unary/portables_connector/visible{
+ dir = 1
+ },
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
"ixf" = (
@@ -39349,6 +39367,7 @@
dir = 1
},
/obj/effect/spawner/structure/window/hollow/plasma/middle,
+/obj/machinery/atmospherics/pipe/smart/simple/purple,
/turf/open/floor/engine/vacuum,
/area/station/science/ordnance/freezerchamber)
"kiA" = (
@@ -46081,6 +46100,13 @@
/obj/structure/flora/bush/flowers_yw/style_random,
/turf/open/floor/grass,
/area/station/science/genetics)
+"lRw" = (
+/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/smart/simple/purple,
+/turf/open/floor/iron/dark,
+/area/station/science/ordnance/testlab)
"lRF" = (
/obj/effect/turf_decal/trimline/brown/filled/line{
dir = 1
@@ -47622,6 +47648,10 @@
dir = 4
},
/area/station/hallway/floor3/fore)
+"mlc" = (
+/obj/machinery/atmospherics/components/unary/bluespace_sender,
+/turf/open/floor/iron/dark,
+/area/station/engineering/atmos/project)
"mlg" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -54276,6 +54306,7 @@
/obj/structure/chair/sofa/bench/left{
dir = 4
},
+/obj/machinery/bluespace_vendor/directional/west,
/turf/open/floor/iron/dark,
/area/station/hallway/floor1/fore)
"nTI" = (
@@ -54876,8 +54907,8 @@
/area/station/engineering/lobby)
"obC" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{
- dir = 4
+/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible/layer4{
+ dir = 8
},
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
@@ -64010,6 +64041,10 @@
/obj/structure/cable,
/turf/open/floor/carpet/royalblue,
/area/station/commons/dorms/room4)
+"qwd" = (
+/obj/machinery/bluespace_vendor/directional/north,
+/turf/open/floor/iron/dark,
+/area/station/hallway/floor3/fore)
"qwg" = (
/obj/structure/chair/comfy/brown{
dir = 1
@@ -74556,6 +74591,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/machinery/camera/autoname/directional/south,
+/obj/machinery/bluespace_vendor/directional/south,
/turf/open/floor/iron/dark/side,
/area/station/hallway/floor2/aft)
"tjV" = (
@@ -81832,6 +81868,10 @@
/obj/machinery/duct,
/turf/open/floor/pod/dark,
/area/station/maintenance/floor2/port)
+"vdT" = (
+/obj/machinery/bluespace_vendor/directional/north,
+/turf/open/floor/engine,
+/area/station/engineering/supermatter/room)
"vdU" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/landmark/generic_maintenance_landmark,
@@ -88679,6 +88719,12 @@
dir = 5
},
/area/station/security/prison)
+"wLQ" = (
+/obj/machinery/atmospherics/pipe/layer_manifold/scrubbers/visible{
+ dir = 4
+ },
+/turf/open/floor/iron/dark,
+/area/station/science/ordnance/testlab)
"wLV" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 4
@@ -90129,10 +90175,10 @@
"xgo" = (
/obj/machinery/door/firedoor/border_only,
/obj/structure/railing,
-/obj/machinery/atmospherics/components/unary/portables_connector/visible{
+/obj/effect/turf_decal/box/red,
+/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer4{
dir = 8
},
-/obj/effect/turf_decal/box/red,
/turf/open/floor/iron/dark,
/area/station/science/ordnance/testlab)
"xgC" = (
@@ -136653,7 +136699,7 @@ bJU
pYs
uyu
kbu
-yhr
+gxE
eeQ
mdR
gpf
@@ -141275,7 +141321,7 @@ qEw
qEw
qEw
sAH
-xgW
+vdT
kfo
iZP
kcB
@@ -143054,7 +143100,7 @@ owI
owI
css
lwc
-vEN
+mlc
vEN
tGq
vmr
@@ -178028,7 +178074,7 @@ kEI
wwu
sbI
jtJ
-qfv
+bgq
hat
qfv
lXq
@@ -243052,7 +243098,7 @@ nVq
nVq
cyX
wRJ
-sJO
+qwd
mUs
oJo
oJo
@@ -314996,7 +315042,7 @@ xpL
qRI
mKO
rDL
-fPd
+wLQ
jha
nPE
xVV
@@ -316023,7 +316069,7 @@ rrs
bYl
kiw
exv
-wMU
+lRw
fLX
ixd
bDn
@@ -321441,7 +321487,7 @@ txa
txa
txa
nWW
-kQk
+bQo
nQX
kQk
eOP
diff --git a/code/__DEFINES/construction/structures.dm b/code/__DEFINES/construction/structures.dm
index 453de8ebf0591..e52b82f248e4e 100644
--- a/code/__DEFINES/construction/structures.dm
+++ b/code/__DEFINES/construction/structures.dm
@@ -62,7 +62,7 @@
#define GEAR_SECURE 1
#define GEAR_LOOSE 2
-// Stationary gas tanks
+//Stationary gas tanks
#define TANK_FRAME 0
#define TANK_PLATING_UNSECURED 1
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index d9514d735d0f1..90485367815f6 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -78,28 +78,30 @@
#define ALIEN_BODYPART "alien"
#define LARVA_BODYPART "larva"
-//Bodytype defines for how things can be worn, surgery, and other misc things.
+//Bodytype defines for surgery, and other misc things.
///The limb is organic.
#define BODYTYPE_ORGANIC (1<<0)
///The limb is robotic.
#define BODYTYPE_ROBOTIC (1<<1)
+///A placeholder bodytype for xeno larva, so their limbs cannot be attached to anything.
+#define BODYTYPE_LARVA_PLACEHOLDER (1<<2)
+///The limb is from a xenomorph.
+#define BODYTYPE_ALIEN (1<<3)
+///The limb is from a golem
+#define BODYTYPE_GOLEM (1<<4)
+
+// Bodyshape defines for how things can be worn, i.e., what "shape" the mob sprite is
///The limb fits the human mold. This is not meant to be literal, if the sprite "fits" on a human, it is "humanoid", regardless of origin.
-#define BODYTYPE_HUMANOID (1<<2)
+#define BODYSHAPE_HUMANOID (1<<0)
///The limb fits the monkey mold.
-#define BODYTYPE_MONKEY (1<<3)
+#define BODYSHAPE_MONKEY (1<<1)
///The limb is digitigrade.
-#define BODYTYPE_DIGITIGRADE (1<<4)
+#define BODYSHAPE_DIGITIGRADE (1<<2)
///The limb is snouted.
-#define BODYTYPE_SNOUTED (1<<5)
-///A placeholder bodytype for xeno larva, so their limbs cannot be attached to anything.
-#define BODYTYPE_LARVA_PLACEHOLDER (1<<6)
-///The limb is from a xenomorph.
-#define BODYTYPE_ALIEN (1<<7)
-///The limb is from a golem
-#define BODYTYPE_GOLEM (1<<8)
+#define BODYSHAPE_SNOUTED (1<<3)
-#define BODYTYPE_BIOSCRAMBLE_COMPATIBLE (BODYTYPE_HUMANOID | BODYTYPE_MONKEY | BODYTYPE_ALIEN)
-#define BODYTYPE_CAN_BE_BIOSCRAMBLED(bodytype) (!(bodytype & BODYTYPE_ROBOTIC) && (bodytype & BODYTYPE_BIOSCRAMBLE_COMPATIBLE))
+#define BODYTYPE_BIOSCRAMBLE_INCOMPATIBLE (BODYTYPE_ROBOTIC | BODYTYPE_LARVA_PLACEHOLDER | BODYTYPE_GOLEM)
+#define BODYTYPE_CAN_BE_BIOSCRAMBLED(bodytype) (!(bodytype & BODYTYPE_BIOSCRAMBLE_INCOMPATIBLE))
// Defines for Species IDs. Used to refer to the name of a species, for things like bodypart names or species preferences.
#define SPECIES_ABDUCTOR "abductor"
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 648f86ff9999c..eef432fb2edfe 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -211,8 +211,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BLOOD_DEFICIENCY "blood_deficiency"
#define TRAIT_JOLLY "jolly"
#define TRAIT_NOCRITDAMAGE "no_crit"
-/// Prevents shovies against a dense object from knocking them down.
-#define TRAIT_SHOVE_KNOCKDOWN_BLOCKED "shove_knockdown_blocked"
+/// Prevents shovies and some strong blows such as unarmed punches and (unreliably) tackles the owner down
+#define TRAIT_BRAWLING_KNOCKDOWN_BLOCKED "brawling_knockdown_blocked"
+/// Prevents some severe head injuries being sustained from heavy collisions or blunt force injuries.
+#define TRAIT_HEAD_INJURY_BLOCKED "head_injury_blocked"
/// Prevents staggering.
#define TRAIT_NO_STAGGER "no_stagger"
/// Getting hit by thrown movables won't push you away
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 1d17aea8a76e8..4135428194b8f 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -374,10 +374,6 @@ DEFINE_BITFIELD(reaction_flags, list(
DEFINE_BITFIELD(bodytype, list(
"BODYTYPE_ORGANIC" = BODYTYPE_ORGANIC,
"BODYTYPE_ROBOTIC" = BODYTYPE_ROBOTIC,
- "BODYTYPE_HUMANOID" = BODYTYPE_HUMANOID,
- "BODYTYPE_MONKEY" = BODYTYPE_MONKEY,
- "BODYTYPE_DIGITIGRADE" = BODYTYPE_DIGITIGRADE,
- "BODYTYPE_SNOUTED" = BODYTYPE_SNOUTED,
"BODYTYPE_LARVA_PLACEHOLDER" = BODYTYPE_LARVA_PLACEHOLDER,
"BODYTYPE_ALIEN" = BODYTYPE_ALIEN,
"BODYTYPE_GOLEM" = BODYTYPE_GOLEM,
@@ -386,15 +382,25 @@ DEFINE_BITFIELD(bodytype, list(
DEFINE_BITFIELD(acceptable_bodytype, list(
"BODYTYPE_ORGANIC" = BODYTYPE_ORGANIC,
"BODYTYPE_ROBOTIC" = BODYTYPE_ROBOTIC,
- "BODYTYPE_HUMANOID" = BODYTYPE_HUMANOID,
- "BODYTYPE_MONKEY" = BODYTYPE_MONKEY,
- "BODYTYPE_DIGITIGRADE" = BODYTYPE_DIGITIGRADE,
- "BODYTYPE_SNOUTED" = BODYTYPE_SNOUTED,
"BODYTYPE_LARVA_PLACEHOLDER" = BODYTYPE_LARVA_PLACEHOLDER,
"BODYTYPE_ALIEN" = BODYTYPE_ALIEN,
"BODYTYPE_GOLEM" = BODYTYPE_GOLEM,
))
+DEFINE_BITFIELD(bodyshape, list(
+ "BODYSHAPE_HUMANOID" = BODYSHAPE_HUMANOID,
+ "BODYSHAPE_MONKEY" = BODYSHAPE_MONKEY,
+ "BODYSHAPE_DIGITIGRADE" = BODYSHAPE_DIGITIGRADE,
+ "BODYSHAPE_SNOUTED" = BODYSHAPE_SNOUTED,
+))
+
+DEFINE_BITFIELD(acceptable_bodyshape, list(
+ "BODYSHAPE_HUMANOID" = BODYSHAPE_HUMANOID,
+ "BODYSHAPE_MONKEY" = BODYSHAPE_MONKEY,
+ "BODYSHAPE_DIGITIGRADE" = BODYSHAPE_DIGITIGRADE,
+ "BODYSHAPE_SNOUTED" = BODYSHAPE_SNOUTED,
+))
+
DEFINE_BITFIELD(bodypart_flags, list(
"BODYPART_UNREMOVABLE" = BODYPART_UNREMOVABLE,
"BODYPART_PSEUDOPART" = BODYPART_PSEUDOPART,
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 83157bd6202a3..15715ebd0a050 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -140,6 +140,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BONSAI" = TRAIT_BONSAI,
"TRAIT_BOOZE_SLIDER" = TRAIT_BOOZE_SLIDER,
"TRAIT_BRAINWASHING" = TRAIT_BRAINWASHING,
+ "TRAIT_BRAWLING_KNOCKDOWN_BLOCKED" = TRAIT_BRAWLING_KNOCKDOWN_BLOCKED,
"TRAIT_BYPASS_EARLY_IRRADIATED_CHECK" = TRAIT_BYPASS_EARLY_IRRADIATED_CHECK,
"TRAIT_BYPASS_MEASURES" = TRAIT_BYPASS_MEASURES,
"TRAIT_CANNOT_BE_UNBUCKLED" = TRAIT_CANNOT_BE_UNBUCKLED,
@@ -237,6 +238,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HAS_CRANIAL_FISSURE" = TRAIT_HAS_CRANIAL_FISSURE,
"TRAIT_HAS_MARKINGS" = TRAIT_HAS_MARKINGS,
"TRAIT_HATED_BY_DOGS" = TRAIT_HATED_BY_DOGS,
+ "TRAIT_HEAD_INJURY_BLOCKED" = TRAIT_HEAD_INJURY_BLOCKED,
"TRAIT_HEALS_FROM_CARP_RIFTS" = TRAIT_HEALS_FROM_CARP_RIFTS,
"TRAIT_HEALS_FROM_CULT_PYLONS" = TRAIT_HEALS_FROM_CULT_PYLONS,
"TRAIT_HEAR_THROUGH_DARKNESS" = TRAIT_HEAR_THROUGH_DARKNESS,
@@ -399,7 +401,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SHAVED" = TRAIT_SHAVED,
"TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES,
"TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE,
- "TRAIT_SHOVE_KNOCKDOWN_BLOCKED" = TRAIT_SHOVE_KNOCKDOWN_BLOCKED,
"TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG,
"TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER,
"TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS,
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index c1f3850c331fc..d9a0118a06ded 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -390,7 +390,7 @@
glasses.add_mob_blood(src)
update_worn_glasses()
- if(!attacking_item.get_sharpness() && armor_block < 50)
+ if(!attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_HEAD_INJURY_BLOCKED))
if(prob(damage_done))
adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
if(stat == CONSCIOUS)
@@ -420,7 +420,7 @@
w_uniform.add_mob_blood(src)
update_worn_undersuit()
- if(stat == CONSCIOUS && !attacking_item.get_sharpness() && armor_block < 50)
+ if(stat == CONSCIOUS && !attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
if(prob(damage_done))
visible_message(
span_danger("[src] is knocked down!"),
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index ec79374cb4fdb..f46ab02433ddc 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -117,7 +117,7 @@
return FALSE
return TRUE
-/atom/ui_status(mob/user)
+/atom/ui_status(mob/user, datum/ui_state/state)
. = ..()
//Check if both user and atom are at the same location
if(!can_interact(user))
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
index 0f7fe6ef142f2..144de535d5a84 100644
--- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
@@ -8,7 +8,8 @@
. = ..()
if(!controller.blackboard[targeting_strategy_key])
CRASH("No targeting strategy was supplied in the blackboard for [controller.pawn]")
-
+ if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED))
+ return FALSE
//Hiding location is priority
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
@@ -67,6 +68,8 @@
/datum/ai_behavior/basic_ranged_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
+ if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED))
+ return FALSE
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm
index ecfedb86dafd5..215f0a96302f3 100644
--- a/code/datums/ai/monkey/monkey_controller.dm
+++ b/code/datums/ai/monkey/monkey_controller.dm
@@ -76,7 +76,8 @@ have ways of interacting with a specific mob and control it.
return AI_CONTROLLER_INCOMPATIBLE
var/mob/living/living_pawn = new_pawn
- living_pawn.AddElement(/datum/element/relay_attackers)
+ if(!HAS_TRAIT(living_pawn, TRAIT_RELAYING_ATTACKER))
+ living_pawn.AddElement(/datum/element/relay_attackers)
RegisterSignal(new_pawn, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked))
RegisterSignal(new_pawn, COMSIG_LIVING_START_PULL, PROC_REF(on_startpulling))
RegisterSignal(new_pawn, COMSIG_LIVING_TRY_SYRINGE, PROC_REF(on_try_syringe))
diff --git a/code/datums/components/creamed.dm b/code/datums/components/creamed.dm
index d1c76b4759dba..be536bb792d97 100644
--- a/code/datums/components/creamed.dm
+++ b/code/datums/components/creamed.dm
@@ -39,9 +39,9 @@ GLOBAL_LIST_INIT(creamable, typecacheof(list(
qdel(src)
return
bodypart_overlay = new()
- if(carbon_parent.bodytype & BODYTYPE_SNOUTED) //stupid, but external organ bodytypes are not stored on the limb
+ if(carbon_parent.bodyshape & BODYSHAPE_SNOUTED) //stupid, but external organ bodytypes are not stored on the limb
bodypart_overlay.icon_state = "creampie_lizard"
- else if(my_head.bodytype & BODYTYPE_MONKEY)
+ else if(my_head.bodyshape & BODYSHAPE_MONKEY)
bodypart_overlay.icon_state = "creampie_monkey"
else
bodypart_overlay.icon_state = "creampie_human"
diff --git a/code/datums/components/payment.dm b/code/datums/components/payment.dm
index 005cd1fe03ea7..1220614e9c386 100644
--- a/code/datums/components/payment.dm
+++ b/code/datums/components/payment.dm
@@ -114,8 +114,7 @@
physical_cash_total -= total_cost
if(physical_cash_total > 0)
- var/obj/item/holochip/holochange = new /obj/item/holochip(user.loc) //Change is made in holocredits exclusively.
- holochange.credits = physical_cash_total
+ var/obj/item/holochip/holochange = new /obj/item/holochip(user.loc, physical_cash_total) //Change is made in holocredits exclusively.
holochange.name = "[holochange.credits] credit holochip"
if(ishuman(user))
var/mob/living/carbon/human/paying_customer = user
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 7d724f1c9effa..d19ef45e4b648 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -400,7 +400,7 @@
defense_mod += 2
if(tackle_target.mob_negates_gravity())
defense_mod += 1
- if(HAS_TRAIT(tackle_target, TRAIT_SHOVE_KNOCKDOWN_BLOCKED)) // riot armor and such
+ if(HAS_TRAIT(tackle_target, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)) // riot armor and such
defense_mod += 5
var/obj/item/organ/external/tail/lizard/el_tail = tackle_target.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)
@@ -451,7 +451,7 @@
attack_mod += 15
human_sacker.adjustStaminaLoss(100) //AHAHAHAHAHAHAHAHA
- if(HAS_TRAIT(human_sacker, TRAIT_SHOVE_KNOCKDOWN_BLOCKED)) // tackling with riot specialized armor, like riot armor, is effective but tiring
+ if(HAS_TRAIT(human_sacker, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)) // tackling with riot specialized armor, like riot armor, is effective but tiring
attack_mod += 2
human_sacker.adjustStaminaLoss(20)
@@ -499,14 +499,10 @@
var/danger_zone = (speed - 1) * 13 // for every extra speed we have over 1, take away 13 of the safest chance
danger_zone = max(min(danger_zone, 100), 1)
- if(ishuman(user))
- var/mob/living/carbon/human/S = user
- var/head_slot = S.get_item_by_slot(ITEM_SLOT_HEAD)
- var/suit_slot = S.get_item_by_slot(ITEM_SLOT_OCLOTHING)
- if(head_slot && (istype(head_slot,/obj/item/clothing/head/helmet) || istype(head_slot,/obj/item/clothing/head/utility/hardhat)))
- oopsie_mod -= 6
- if(suit_slot && (istype(suit_slot,/obj/item/clothing/suit/armor/riot)))
- oopsie_mod -= 6
+ if(HAS_TRAIT(user, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
+ oopsie_mod -= 6
+ if(HAS_TRAIT(user, TRAIT_HEAD_INJURY_BLOCKED))
+ oopsie_mod -= 6
if(HAS_TRAIT(user, TRAIT_CLUMSY))
oopsie_mod += 6 //honk!
diff --git a/code/datums/components/tactical.dm b/code/datums/components/tactical.dm
index 4f9f00c0668b3..59df008b2b100 100644
--- a/code/datums/components/tactical.dm
+++ b/code/datums/components/tactical.dm
@@ -1,15 +1,19 @@
///A simple component that replacess the user's appearance with that of the parent item when equipped.
/datum/component/tactical
- ///The allowed slot(s) for the effect.
- var/allowed_slot
+ ///The allowed slots for the effect.
+ var/allowed_slots
///A cached of where the item is currently equipped.
var/current_slot
-/datum/component/tactical/Initialize(allowed_slot)
+/datum/component/tactical/Initialize(allowed_slots)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
- src.allowed_slot = allowed_slot
+ src.allowed_slots = allowed_slots
+
+/datum/component/tactical/Destroy()
+ unmodify()
+ return ..()
/datum/component/tactical/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(modify))
@@ -24,32 +28,39 @@
))
unmodify()
-/datum/component/tactical/Destroy()
- unmodify()
- return ..()
-
/datum/component/tactical/proc/modify(obj/item/source, mob/user, slot)
SIGNAL_HANDLER
+ if(current_slot == slot)
+ return
- if(allowed_slot && !(slot & allowed_slot))
+ if(allowed_slots && !(slot & allowed_slots))
if(current_slot)
unmodify(source, user)
return
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(tactical_update))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(unmodify))
- RegisterSignal(parent, COMSIG_ATOM_UPDATED_ICON, PROC_REF(tactical_update))
+ RegisterSignal(parent, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_icon_update))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
current_slot = slot
+ on_icon_update(source)
+
+/datum/component/tactical/proc/on_icon_update(obj/item/source)
+ SIGNAL_HANDLER
+ var/mob/user = source.loc
+ if(!istype(user))
+ return
+
+ user.remove_alt_appearance("sneaking_mission[REF(src)]")
var/obj/item/master = parent
var/image/image = image(master, loc = user)
image.copy_overlays(master)
image.override = TRUE
image.layer = ABOVE_MOB_LAYER
image.plane = FLOAT_PLANE
- source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission[REF(src)]", image)
+ user.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission[REF(src)]", image)
/datum/component/tactical/proc/unmodify(obj/item/source, mob/user)
SIGNAL_HANDLER
@@ -60,10 +71,16 @@
if(!istype(user))
return
- user.remove_alt_appearance("sneaking_mission[REF(src)]")
+ UnregisterSignal(source, list(
+ COMSIG_MOVABLE_Z_CHANGED,
+ COMSIG_ITEM_DROPPED,
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_ATOM_UPDATED_ICON,
+ ))
current_slot = null
- UnregisterSignal(parent, list(COMSIG_MOVABLE_Z_CHANGED, COMSIG_ITEM_DROPPED, COMSIG_ATOM_UPDATED_ICON, COMSIG_MOVABLE_MOVED))
+ user.remove_alt_appearance("sneaking_mission[REF(src)]")
+///Checks if a mob is holding us, and if so we will modify our appearance to properly match w/ the mob.
/datum/component/tactical/proc/tactical_update(obj/item/source)
SIGNAL_HANDLER
if(!ismob(source.loc))
diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm
index 464f1a58a07ff..45ee6697e5302 100644
--- a/code/datums/components/twohanded.dm
+++ b/code/datums/components/twohanded.dm
@@ -2,21 +2,32 @@
* Two Handed Component
*
* When applied to an item it will make it two handed
- *
+ * Only one of the component can exist on an item.
*/
/datum/component/two_handed
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS // Only one of the component can exist on an item
- var/wielded = FALSE /// Are we holding the two handed item properly
- var/force_multiplier = 0 /// The multiplier applied to force when wielded, does not work with force_wielded, and force_unwielded
- var/force_wielded = 0 /// The force of the item when weilded
- var/force_unwielded = 0 /// The force of the item when unweilded
- var/wieldsound = FALSE /// Play sound when wielded
- var/unwieldsound = FALSE /// Play sound when unwielded
- var/attacksound = FALSE /// Play sound on attack when wielded
- var/require_twohands = FALSE /// Does it have to be held in both hands
- var/icon_wielded = FALSE /// The icon that will be used when wielded
- var/obj/item/offhand/offhand_item = null /// Reference to the offhand created for the item
- var/sharpened_increase = 0 /// The amount of increase recived from sharpening the item
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ /// Are we holding the two handed item properly
+ var/wielded = FALSE
+ /// The multiplier applied to force when wielded, does not work with force_wielded, and force_unwielded
+ var/force_multiplier = 0
+ /// The force of the item when weilded
+ var/force_wielded = 0
+ /// The force of the item when unweilded
+ var/force_unwielded = 0
+ /// Boolean whether to play sound when wielded
+ var/wieldsound = FALSE
+ /// Boolean whether to play sound when unwielded
+ var/unwieldsound = FALSE
+ /// Boolean whether to play sound on attack, if wielded
+ var/attacksound = FALSE
+ /// Boolean on whether it has to be held in both hands
+ var/require_twohands = FALSE
+ /// The icon that will be used when wielded
+ var/icon_wielded = FALSE
+ /// Reference to the offhand created for the item
+ var/obj/item/offhand/offhand_item = null
+ /// The amount of increase recived from sharpening the item
+ var/sharpened_increase = 0
/// A callback on the parent to be called when the item is wielded
var/datum/callback/wield_callback
/// A callback on the parent to be called when the item is unwielded
@@ -36,9 +47,18 @@
* * force_unwielded (optional) The force setting when the item is unwielded, do not use with force_multiplier
* * icon_wielded (optional) The icon to be used when wielded
*/
-/datum/component/two_handed/Initialize(require_twohands=FALSE, wieldsound=FALSE, unwieldsound=FALSE, attacksound=FALSE, \
- force_multiplier=0, force_wielded=0, force_unwielded=0, icon_wielded=FALSE, \
- datum/callback/wield_callback, datum/callback/unwield_callback)
+/datum/component/two_handed/Initialize(
+ require_twohands = FALSE,
+ wieldsound = FALSE,
+ unwieldsound = FALSE,
+ attacksound = FALSE,
+ force_multiplier = 0,
+ force_wielded = 0,
+ force_unwielded = 0,
+ icon_wielded = FALSE,
+ datum/callback/wield_callback,
+ datum/callback/unwield_callback,
+)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -63,9 +83,20 @@
return ..()
// Inherit the new values passed to the component
-/datum/component/two_handed/InheritComponent(datum/component/two_handed/new_comp, original, require_twohands, wieldsound, unwieldsound, attacksound, \
- force_multiplier, force_wielded, force_unwielded, icon_wielded, \
- datum/callback/wield_callback, datum/callback/unwield_callback)
+/datum/component/two_handed/InheritComponent(
+ datum/component/two_handed/new_comp,
+ original,
+ require_twohands,
+ wieldsound,
+ unwieldsound,
+ attacksound,
+ force_multiplier,
+ force_wielded,
+ force_unwielded,
+ icon_wielded,
+ datum/callback/wield_callback,
+ datum/callback/unwield_callback,
+)
if(!original)
return
if(require_twohands)
@@ -101,32 +132,19 @@
RegisterSignal(parent, COMSIG_ITEM_APPLY_FANTASY_BONUSES, PROC_REF(apply_fantasy_bonuses))
RegisterSignal(parent, COMSIG_ITEM_REMOVE_FANTASY_BONUSES, PROC_REF(remove_fantasy_bonuses))
-/datum/component/two_handed/proc/apply_fantasy_bonuses(obj/item/source, bonus)
- SIGNAL_HANDLER
- force_wielded = source.modify_fantasy_variable("force_wielded", force_wielded, bonus)
- force_unwielded = source.modify_fantasy_variable("force_unwielded", force_unwielded, bonus)
- if(wielded && ismob(source.loc))
- unwield(source.loc)
- if(force_multiplier)
- force_multiplier = source.modify_fantasy_variable("force_multiplier", force_multiplier, bonus/10, minimum = 1)
-
-/datum/component/two_handed/proc/remove_fantasy_bonuses(obj/item/source, bonus)
- SIGNAL_HANDLER
- force_wielded = source.reset_fantasy_variable("force_wielded", force_wielded)
- force_unwielded = source.reset_fantasy_variable("force_unwielded", force_unwielded)
- if(wielded && ismob(source.loc))
- unwield(source.loc)
- force_multiplier = source.reset_fantasy_variable("force_multiplier", force_multiplier)
-
// Remove all siginals registered to the parent item
/datum/component/two_handed/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED,
- COMSIG_ITEM_DROPPED,
- COMSIG_ITEM_ATTACK_SELF,
- COMSIG_ITEM_ATTACK,
- COMSIG_ATOM_UPDATE_ICON,
- COMSIG_MOVABLE_MOVED,
- COMSIG_ITEM_SHARPEN_ACT))
+ UnregisterSignal(parent, list(
+ COMSIG_ITEM_EQUIPPED,
+ COMSIG_ITEM_DROPPED,
+ COMSIG_ITEM_ATTACK_SELF,
+ COMSIG_ITEM_ATTACK,
+ COMSIG_ATOM_UPDATE_ICON,
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_ITEM_SHARPEN_ACT,
+ COMSIG_ITEM_APPLY_FANTASY_BONUSES,
+ COMSIG_ITEM_REMOVE_FANTASY_BONUSES,
+ ))
/// Triggered on equip of the item containing the component
/datum/component/two_handed/proc/on_equip(datum/source, mob/user, slot)
@@ -365,9 +383,25 @@
sharpened_increase = min(amount, (max_amount - wielded_val))
return COMPONENT_BLOCK_SHARPEN_APPLIED
+/datum/component/two_handed/proc/apply_fantasy_bonuses(obj/item/source, bonus)
+ SIGNAL_HANDLER
+ force_wielded = source.modify_fantasy_variable("force_wielded", force_wielded, bonus)
+ force_unwielded = source.modify_fantasy_variable("force_unwielded", force_unwielded, bonus)
+ if(wielded && ismob(source.loc))
+ unwield(source.loc)
+ if(force_multiplier)
+ force_multiplier = source.modify_fantasy_variable("force_multiplier", force_multiplier, bonus/10, minimum = 1)
+
+/datum/component/two_handed/proc/remove_fantasy_bonuses(obj/item/source, bonus)
+ SIGNAL_HANDLER
+ force_wielded = source.reset_fantasy_variable("force_wielded", force_wielded)
+ force_unwielded = source.reset_fantasy_variable("force_unwielded", force_unwielded)
+ if(wielded && ismob(source.loc))
+ unwield(source.loc)
+ force_multiplier = source.reset_fantasy_variable("force_multiplier", force_multiplier)
+
/**
* The offhand dummy item for two handed items
- *
*/
/obj/item/offhand
name = "offhand"
diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm
index ec09caed73d59..7984939cddc87 100644
--- a/code/datums/elements/decals/blood.dm
+++ b/code/datums/elements/decals/blood.dm
@@ -9,10 +9,14 @@
/datum/element/decal/blood/Detach(atom/source)
UnregisterSignal(source, COMSIG_ATOM_GET_EXAMINE_NAME)
+ if(isitem(source))
+ var/obj/item/source_item = source
+ REMOVE_KEEP_TOGETHER(source_item, type)
return ..()
/datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, source)
var/obj/item/I = source
+ ADD_KEEP_TOGETHER(I, type)
var/icon = I.icon
var/icon_state = I.icon_state
if(!icon || !icon_state)
diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm
index 823585a9d7369..99438be18cc02 100644
--- a/code/datums/wires/_wires.dm
+++ b/code/datums/wires/_wires.dm
@@ -277,7 +277,7 @@
/datum/wires/ui_host()
return holder
-/datum/wires/ui_status(mob/user)
+/datum/wires/ui_status(mob/user, datum/ui_state/state)
if(interactable(user))
return ..()
return UI_CLOSE
diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm
index b9e8bd9d1e9e4..91a10673dd6dd 100644
--- a/code/game/machinery/computer/arcade/arcade.dm
+++ b/code/game/machinery/computer/arcade/arcade.dm
@@ -71,8 +71,9 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/storage/box/party_poppers = 2))
/obj/machinery/computer/arcade
- name = "random arcade"
- desc = "random arcade machine"
+ name = "\proper the arcade cabinet which shouldn't exist"
+ desc = "This arcade cabinet has no games installed, and in fact, should not exist. \
+ Report the location of this machine to your local diety."
icon_state = "arcade"
icon_keyboard = null
icon_screen = "invaders"
@@ -136,19 +137,21 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
new empprize(loc)
explosion(src, devastation_range = -1, light_impact_range = 1+num_of_prizes, flame_range = 1+num_of_prizes)
-/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params)
- if(istype(O, /obj/item/stack/arcadeticket))
- var/obj/item/stack/arcadeticket/T = O
- var/amount = T.get_amount()
- if(amount <2)
- to_chat(user, span_warning("You need 2 tickets to claim a prize!"))
- return
- prizevend(user)
- T.pay_tickets()
- T.update_appearance()
- O = T
- to_chat(user, span_notice("You turn in 2 tickets to the [src] and claim a prize!"))
- return
+/obj/machinery/computer/arcade/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
+ . = ..()
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ return .
+ if(!istype(tool, /obj/item/stack/arcadeticket))
+ return .
+
+ var/obj/item/stack/arcadeticket/tickets = tool
+ if(!tickets.use(2))
+ balloon_alert(user, "need 2 tickets!")
+ return ITEM_INTERACT_BLOCKING
+
+ prizevend(user)
+ balloon_alert(user, "prize claimed")
+ return ITEM_INTERACT_SUCCESS
// ** BATTLE ** //
/obj/machinery/computer/arcade/battle
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index dd933ba5e041f..ea2fbb3eb8eda 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -9,6 +9,92 @@
/obj/structure/frame/computer/Initialize(mapload)
. = ..()
AddComponent(/datum/component/simple_rotation)
+ register_context()
+ update_appearance(UPDATE_ICON_STATE)
+
+/obj/structure/frame/computer/deconstruct(disassembled = TRUE)
+ if(!(obj_flags & NO_DECONSTRUCTION))
+ var/atom/drop_loc = drop_location()
+ if(state == FRAME_COMPUTER_STATE_GLASSED)
+ if(disassembled)
+ new /obj/item/stack/sheet/glass(drop_loc, 2)
+ else
+ new /obj/item/shard(drop_loc)
+ new /obj/item/shard(drop_loc)
+ if(state >= FRAME_COMPUTER_STATE_WIRED)
+ new /obj/item/stack/cable_coil(drop_loc, 5)
+
+ return ..()
+
+/obj/structure/frame/computer/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = NONE
+ if(isnull(held_item))
+ return
+
+ switch(state)
+ if(FRAME_COMPUTER_STATE_EMPTY)
+ if(held_item.tool_behaviour == TOOL_WRENCH)
+ context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(anchored && istype(held_item, /obj/item/circuitboard/computer))
+ context[SCREENTIP_CONTEXT_LMB] = "Install board"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_WELDER)
+ context[SCREENTIP_CONTEXT_LMB] = "Unweld frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "Disassemble frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_COMPUTER_STATE_BOARD_INSTALLED)
+ if(held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Pry out board"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "Secure board"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_COMPUTER_STATE_BOARD_SECURED)
+ if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "Unsecure board"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, /obj/item/stack/cable_coil))
+ context[SCREENTIP_CONTEXT_LMB] = "Install cable"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_COMPUTER_STATE_WIRED)
+ if(held_item.tool_behaviour == TOOL_WIRECUTTER)
+ context[SCREENTIP_CONTEXT_LMB] = "Cut out cable"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, /obj/item/stack/sheet/glass))
+ context[SCREENTIP_CONTEXT_LMB] = "Install panel"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_COMPUTER_STATE_GLASSED)
+ if(held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Pry out glass"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "Complete frame"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/structure/frame/computer/examine(user)
+ . = ..()
+
+ switch(state)
+ if(FRAME_STATE_EMPTY)
+ . += span_notice("It can be [EXAMINE_HINT("wrenched")] [anchored ? "loose" : "in place"].")
+ if(anchored)
+ . += span_warning("It's missing a circuit board.")
+ . += span_notice("It can be [EXAMINE_HINT("welded")] or [EXAMINE_HINT("screwed")] apart.")
+ if(FRAME_COMPUTER_STATE_BOARD_INSTALLED)
+ . += span_warning("An [circuit.name] is installed and should be [EXAMINE_HINT("screwed")] in place.")
+ . += span_notice("The circuit board can be [EXAMINE_HINT("pried")] out.")
+ if(FRAME_COMPUTER_STATE_BOARD_SECURED)
+ . += span_warning("Its requires [EXAMINE_HINT("5 cable")] pieces to wire it.")
+ . += span_notice("The circuit board can be [EXAMINE_HINT("screwed")] loose.")
+ if(FRAME_COMPUTER_STATE_WIRED)
+ . += span_notice("The wires can be cut out with [EXAMINE_HINT("wire cutters")].")
+ . += span_warning("Its requires [EXAMINE_HINT("2 glass")] sheets to complete the screen.")
+ if(FRAME_COMPUTER_STATE_GLASSED)
+ . += span_notice("The screen can be [EXAMINE_HINT("pried")] out.")
+ . += span_notice("The moniter can be [EXAMINE_HINT("screwed")] to complete it")
/obj/structure/frame/computer/circuit_added(obj/item/circuitboard/added)
state = FRAME_COMPUTER_STATE_BOARD_INSTALLED
@@ -22,7 +108,45 @@
if(state != FRAME_COMPUTER_STATE_EMPTY)
balloon_alert(user, "circuit already installed!")
return FALSE
- return ..()
+ if(!anchored && istype(board))
+ balloon_alert(user, "frame must be anchored!")
+ return FALSE
+ . = ..()
+ if(. && !by_hand) // Installing via RPED auto-secures it
+ state = FRAME_COMPUTER_STATE_BOARD_SECURED
+ update_appearance(UPDATE_ICON_STATE)
+ return .
+
+/obj/structure/frame/computer/install_parts_from_part_replacer(mob/living/user, obj/item/storage/part_replacer/replacer, no_sound = FALSE)
+ switch(state)
+ if(FRAME_COMPUTER_STATE_BOARD_SECURED)
+ var/obj/item/stack/cable_coil/cable = locate() in replacer
+ if(isnull(cable))
+ return FALSE
+
+ if(add_cabling(user, cable, time = 0))
+ if(!no_sound)
+ replacer.play_rped_sound()
+ if(replacer.works_from_distance)
+ user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+ no_sound = TRUE
+ return install_parts_from_part_replacer(user, replacer, no_sound = no_sound) // Recursive call to handle the next part
+
+ return FALSE
+
+ if(FRAME_COMPUTER_STATE_WIRED)
+ var/obj/item/stack/sheet/glass/glass_sheets = locate() in replacer
+ if(isnull(glass_sheets))
+ return FALSE
+
+ if(add_glass(user, glass_sheets, time = 0))
+ if(!no_sound)
+ replacer.play_rped_sound()
+ if(replacer.works_from_distance)
+ user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+ return TRUE
+
+ return FALSE
/obj/structure/frame/computer/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
. = ..()
@@ -48,11 +172,9 @@
if(istype(tool, /obj/item/storage/part_replacer))
return install_parts_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
- return .
-
/obj/structure/frame/computer/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
- if(. != NONE)
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
switch(state)
@@ -83,6 +205,9 @@
return ITEM_INTERACT_BLOCKING
/obj/structure/frame/computer/crowbar_act(mob/living/user, obj/item/tool)
+ if(user.combat_mode)
+ return NONE
+
switch(state)
if(FRAME_COMPUTER_STATE_BOARD_INSTALLED)
tool.play_tool_sound(src)
@@ -92,14 +217,12 @@
return ITEM_INTERACT_SUCCESS
if(FRAME_COMPUTER_STATE_BOARD_SECURED)
- if(!user.combat_mode)
- balloon_alert(user, "unsecure the circuit!")
- return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "unsecure the circuit!")
+ return ITEM_INTERACT_BLOCKING
if(FRAME_COMPUTER_STATE_WIRED)
- if(!user.combat_mode)
- balloon_alert(user, "remove the wiring!")
- return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "remove the wiring!")
+ return ITEM_INTERACT_BLOCKING
if(FRAME_COMPUTER_STATE_GLASSED)
tool.play_tool_sound(src)
@@ -111,38 +234,34 @@
dropped_glass.add_fingerprint(user)
return ITEM_INTERACT_SUCCESS
-/obj/structure/frame/computer/install_parts_from_part_replacer(mob/living/user, obj/item/storage/part_replacer/replacer, no_sound = FALSE)
- switch(state)
- if(FRAME_COMPUTER_STATE_BOARD_SECURED)
- var/obj/item/stack/cable_coil/cable = locate() in replacer
- if(isnull(cable))
- return FALSE
-
- if(add_cabling(user, cable, time = 0))
- if(!no_sound)
- replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- no_sound = TRUE
- return install_parts_from_part_replacer(user, replacer, no_sound = no_sound) // Recursive call to handle the next part
-
- return FALSE
+/obj/structure/frame/computer/wirecutter_act(mob/living/user, obj/item/tool)
+ if(user.combat_mode)
+ return NONE
- if(FRAME_COMPUTER_STATE_WIRED)
- var/obj/item/stack/sheet/glass/glass_sheets = locate() in replacer
- if(isnull(glass_sheets))
- return FALSE
+ if(state != FRAME_COMPUTER_STATE_WIRED)
+ return ITEM_INTERACT_BLOCKING
- if(add_glass(user, glass_sheets, time = 0))
- if(!no_sound)
- replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- return TRUE
+ tool.play_tool_sound(src)
+ balloon_alert(user, "cables removed")
+ state = FRAME_COMPUTER_STATE_BOARD_SECURED
+ update_appearance(UPDATE_ICON_STATE)
- return FALSE
+ var/obj/item/stack/cable_coil/dropped_cables = new (drop_location(), 5)
+ if (!QDELETED(dropped_cables))
+ dropped_cables.add_fingerprint(user)
+ return ITEM_INTERACT_SUCCESS
+/**
+ * Adds cable to the computer to wire it
+ * Arguments
+ *
+ * * mob/living/user - the player who is adding the cable
+ * * obj/item/stack/cable_coil/cable - the cable we are trying to add
+ * * time - time taken to complete the operation
+ */
/obj/structure/frame/computer/proc/add_cabling(mob/living/user, obj/item/stack/cable_coil/cable, time = 2 SECONDS)
+ PRIVATE_PROC(TRUE)
+
if(state != FRAME_COMPUTER_STATE_BOARD_SECURED)
return FALSE
if(!cable.tool_start_check(user, amount = 5))
@@ -156,7 +275,17 @@
update_appearance(UPDATE_ICON_STATE)
return TRUE
+/**
+ * Adds glass sheets to the computer to complete the screen
+ * Arguments
+ *
+ * * mob/living/user - the player who is adding the glass
+ * * obj/item/stack/sheet/glass/glass - the glass we are trying to add
+ * * time - time taken to complete the operation
+ */
/obj/structure/frame/computer/proc/add_glass(mob/living/user, obj/item/stack/sheet/glass/glass, time = 2 SECONDS)
+ PRIVATE_PROC(TRUE)
+
if(state != FRAME_COMPUTER_STATE_WIRED)
return FALSE
if(!glass.tool_start_check(user, amount = 2))
@@ -171,22 +300,6 @@
update_appearance(UPDATE_ICON_STATE)
return TRUE
-/obj/structure/frame/computer/wirecutter_act(mob/living/user, obj/item/tool)
- if(state != FRAME_COMPUTER_STATE_WIRED)
- return NONE
-
- tool.play_tool_sound(src)
- balloon_alert(user, "cables removed")
- state = FRAME_COMPUTER_STATE_BOARD_SECURED
- update_appearance(UPDATE_ICON_STATE)
- var/obj/item/stack/cable_coil/dropped_cables = new (drop_location(), 5)
- if (!QDELETED(dropped_cables))
- dropped_cables.add_fingerprint(user)
- return ITEM_INTERACT_SUCCESS
-
-/obj/structure/frame/computer/AltClick(mob/user)
- return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation
-
/obj/structure/frame/computer/finalize_construction(mob/living/user, obj/item/tool)
tool.play_tool_sound(src)
var/obj/machinery/new_machine = new circuit.build_path(loc)
@@ -217,20 +330,6 @@
qdel(src)
return TRUE
-/obj/structure/frame/computer/deconstruct(disassembled = TRUE)
- if(!(obj_flags & NO_DECONSTRUCTION))
- var/atom/drop_loc = drop_location()
- if(state == FRAME_COMPUTER_STATE_GLASSED)
- if(disassembled)
- new /obj/item/stack/sheet/glass(drop_loc, 2)
- else
- new /obj/item/shard(drop_loc)
- new /obj/item/shard(drop_loc)
- if(state >= FRAME_COMPUTER_STATE_WIRED)
- new /obj/item/stack/cable_coil(drop_loc, 5)
-
- return ..()
-
/// Helpers for rcd
/obj/structure/frame/computer/rcd
icon = 'icons/hud/radial.dmi'
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index a1a1fe0ada2a9..37514c142fe8a 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -76,7 +76,7 @@
ui = new(user, src, "CameraConsole", name)
ui.open()
-/obj/machinery/computer/security/ui_status(mob/user)
+/obj/machinery/computer/security/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(. == UI_DISABLED)
return UI_CLOSE
diff --git a/code/game/machinery/computer/records/medical.dm b/code/game/machinery/computer/records/medical.dm
index 362c8468aa6f4..c4242ace958d5 100644
--- a/code/game/machinery/computer/records/medical.dm
+++ b/code/game/machinery/computer/records/medical.dm
@@ -65,6 +65,7 @@
quirk_notes = target.quirk_notes,
rank = target.rank,
species = target.species,
+ trim = target.trim,
))
data["records"] = records
diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm
index 3a929f7a298a0..27b8e75e545ef 100644
--- a/code/game/machinery/computer/records/security.dm
+++ b/code/game/machinery/computer/records/security.dm
@@ -128,6 +128,7 @@
note = target.security_note,
rank = target.rank,
species = target.species,
+ trim = target.trim,
wanted_status = target.wanted_status,
))
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 6a1d2d60f8c8d..8a5efc193ca9f 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -24,7 +24,7 @@
new /obj/item/stack/sheet/iron(drop_loc, 5)
circuit?.forceMove(drop_loc)
- qdel(src)
+ return ..()
/// Called when circuit has been set to a new board
/obj/structure/frame/proc/circuit_added(obj/item/circuitboard/added)
@@ -97,13 +97,13 @@
return FALSE
/obj/structure/frame/wrench_act(mob/living/user, obj/item/tool)
+ . = NONE
switch(default_unfasten_wrench(user, tool, 4 SECONDS))
if(SUCCESSFUL_UNFASTEN)
return ITEM_INTERACT_SUCCESS
if(FAILED_UNFASTEN)
return ITEM_INTERACT_BLOCKING
-
- return NONE
+ return .
/obj/structure/frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
. = ..()
@@ -196,421 +196,3 @@
*/
/obj/structure/frame/proc/install_parts_from_part_replacer(mob/living/user, obj/item/storage/part_replacer/replacer, no_sound = FALSE)
return FALSE
-
-/obj/structure/frame/machine
- name = "machine frame"
- desc = "The standard frame for most station appliances. Its appearance and function is controlled by the inserted board."
- board_type = /obj/item/circuitboard/machine
- /// List of all compnents inside the frame contributing to its construction
- var/list/components
- /// List of all components required to construct the frame
- var/list/req_components
- /// User-friendly list of names of required components
- var/list/req_component_names
-
-/obj/structure/frame/machine/examine(user)
- . = ..()
- if(state != FRAME_STATE_BOARD_INSTALLED)
- return .
-
- if(!length(req_components))
- . += span_info("It requires no components.")
- return .
-
- if(!req_component_names)
- stack_trace("[src]'s req_components list has items but its req_component_names list is null!")
- return .
-
- var/list/nice_list = list()
- for(var/component in req_components)
- if(!ispath(component))
- stack_trace("An item in [src]'s req_components list is not a path!")
- continue
- if(!req_components[component])
- continue
-
- nice_list += list("[req_components[component]] [req_component_names[component]]\s")
- . += span_info("It requires [english_list(nice_list, "no more components")].")
- return .
-
-/**
- * Collates the displayed names of the machine's components
- *
- * Arguments:
- * * specific_parts - If true, the component should not use base name, but a specific tier
- */
-/obj/structure/frame/machine/proc/update_namelist(specific_parts)
- if(!req_components)
- return
-
- req_component_names = list()
- for(var/component_path in req_components)
- if(!ispath(component_path))
- continue
-
- if(ispath(component_path, /obj/item/stack))
- var/obj/item/stack/stack_path = component_path
- if(initial(stack_path.singular_name))
- req_component_names[component_path] = initial(stack_path.singular_name)
- else
- req_component_names[component_path] = initial(stack_path.name)
- else if(ispath(component_path, /datum/stock_part))
- var/datum/stock_part/stock_part = component_path
- var/obj/item/physical_object_type = initial(stock_part.physical_object_type)
-
- req_component_names[component_path] = initial(physical_object_type.name)
- else if(ispath(component_path, /obj/item/stock_parts))
- var/obj/item/stock_parts/stock_part = component_path
-
- if(!specific_parts && initial(stock_part.base_name))
- req_component_names[component_path] = initial(stock_part.base_name)
- else
- req_component_names[component_path] = initial(stock_part.name)
- else if(ispath(component_path, /obj/item))
- var/obj/item/part = component_path
-
- req_component_names[component_path] = initial(part.name)
- else
- stack_trace("Invalid component part [component_path] in [type], couldn't get its name")
- req_component_names[component_path] = "[component_path] (this is a bug)"
-
-/obj/structure/frame/machine/proc/get_req_components_amt()
- var/amt = 0
- for(var/path in req_components)
- amt += req_components[path]
- return amt
-
-/obj/structure/frame/machine/try_dissassemble(mob/living/user, obj/item/tool, disassemble_time)
- if(anchored)
- balloon_alert(user, "must be unsecured first!")
- return FALSE
- return ..()
-
-/obj/structure/frame/machine/install_board(mob/living/user, obj/item/circuitboard/machine/board, by_hand = TRUE)
- if(state == FRAME_STATE_EMPTY)
- balloon_alert(user, "needs wiring!")
- return FALSE
- if(state == FRAME_STATE_BOARD_INSTALLED)
- balloon_alert(user, "circuit already installed!")
- return FALSE
- if(!anchored && istype(board) && board.needs_anchored)
- balloon_alert(user, "frame must be anchored!")
- return FALSE
-
- return ..()
-
-/obj/structure/frame/machine/circuit_added(obj/item/circuitboard/machine/added)
- state = FRAME_STATE_BOARD_INSTALLED
- update_appearance(UPDATE_ICON_STATE)
- //add circuit board as the first component to the list of components
- //required for part_replacer to locate it while exchanging parts
- //so it does not early return in /obj/machinery/proc/exchange_parts
- components = list(circuit)
- req_components = added.req_components.Copy()
- update_namelist(added.specific_parts)
-
-/obj/structure/frame/machine/circuit_removed(obj/item/circuitboard/machine/removed)
- state = FRAME_STATE_WIRED
- update_appearance(UPDATE_ICON_STATE)
-
-/obj/structure/frame/machine/install_parts_from_part_replacer(mob/living/user, obj/item/storage/part_replacer/replacer, no_sound = FALSE)
- if(!length(replacer.contents) || !get_req_components_amt())
- return FALSE
-
- var/play_sound = FALSE
- var/list/part_list = replacer.get_sorted_parts() //parts sorted in order of tier
- for(var/path in req_components)
- var/target_path
- if(ispath(path, /datum/stock_part))
- var/datum/stock_part/datum_part = path
- target_path = initial(datum_part.physical_object_base_type)
- else
- target_path = path
-
- var/obj/item/part
- while(req_components[path] > 0 && (part = look_for(part_list, target_path, ispath(path, /obj/item/stack/ore/bluespace_crystal) ? /obj/item/stack/sheet/bluespace_crystal : null)))
- part_list -= part
- if(istype(part, /obj/item/stack))
- var/obj/item/stack/S = part
- var/used_amt = min(round(S.get_amount()), req_components[path])
- var/stack_name = S.singular_name
- if(!used_amt || !S.use(used_amt))
- continue
- req_components[path] -= used_amt
- // No balloon alert here so they can look back and see what they added
- to_chat(user, span_notice("You add [used_amt] [stack_name] to [src]."))
- play_sound = TRUE
- else if(replacer.atom_storage.attempt_remove(part, src))
- var/stock_part_datum = GLOB.stock_part_datums_per_object[part.type]
- if (!isnull(stock_part_datum))
- components += stock_part_datum
- qdel(part)
- else
- components += part
- part.forceMove(src)
- req_components[path]--
- // No balloon alert here so they can look back and see what they added
- to_chat(user, span_notice("You add [part] to [src]."))
- play_sound = TRUE
-
- if(play_sound && !no_sound)
- replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- return TRUE
-
-/**
- * Attempts to add the passed part to the frame
- *
- * Requires no sanity check that the passed part is a stock part
- *
- * Arguments
- * * user - the player
- * * tool - the part to add
- */
-/obj/structure/frame/machine/proc/add_part(mob/living/user, obj/item/tool)
- for(var/stock_part_base in req_components)
- if (req_components[stock_part_base] == 0)
- continue
-
- var/stock_part_path
-
- if(ispath(stock_part_base, /obj/item))
- stock_part_path = stock_part_base
- else if(ispath(stock_part_base, /datum/stock_part))
- var/datum/stock_part/stock_part_datum_type = stock_part_base
- stock_part_path = initial(stock_part_datum_type.physical_object_type)
- else
- stack_trace("Bad stock part in req_components: [stock_part_base]")
- continue
-
- //if we require an bluespace crystall and we have an full sheet of them we can allow that
- if(ispath(stock_part_path, /obj/item/stack/ore/bluespace_crystal) && istype(tool, /obj/item/stack/sheet/bluespace_crystal))
- //allow it
- pass()
- else if(!istype(tool, stock_part_path))
- continue
-
- if(isstack(tool))
- var/obj/item/stack/S = tool
- var/used_amt = min(round(S.get_amount()), req_components[stock_part_path])
- if(used_amt && S.use(used_amt))
- req_components[stock_part_path] -= used_amt
- // No balloon alert here so they can look back and see what they added
- to_chat(user, span_notice("You add [tool] to [src]."))
- return
-
- // We might end up qdel'ing the part if it's a stock part datum.
- // In practice, this doesn't have side effects to the name,
- // but academically we should not be using an object after it's deleted.
- var/part_name = "[tool]"
-
- if (ispath(stock_part_base, /datum/stock_part))
- // We can't just reuse stock_part_path here or its singleton,
- // or else putting in a tier 2 part will deconstruct to a tier 1 part.
- var/stock_part_datum = GLOB.stock_part_datums_per_object[tool.type]
- if (isnull(stock_part_datum))
- stack_trace("tool.type] does not have an associated stock part datum!")
- continue
-
- components += stock_part_datum
-
- // We regenerate the stock parts on deconstruct.
- // This technically means we lose unique qualities of the stock part, but
- // it's worth it for how dramatically this simplifies the code.
- // The only place I can see it affecting anything is like...RPG qualities. :P
- qdel(tool)
- else if(user.transferItemToLoc(tool, src))
- components += tool
- else
- break
-
- // No balloon alert here so they can look back and see what they added
- to_chat(user, span_notice("You add [part_name] to [src]."))
- req_components[stock_part_base]--
- return TRUE
-
- balloon_alert(user, "can't add that!")
- return FALSE
-
-/**
- * Attempt to finalize the construction of the frame into a machine
- * as according to our circuit and parts
- *
- * If successful, results in qdel'ing the frame and newing of a machine
- *
- * Arguments
- * * user - the player
- * * tool - the tool used to finalize the construction
- */
-/obj/structure/frame/machine/finalize_construction(mob/living/user, obj/item/tool)
- for(var/component in req_components)
- if(req_components[component] > 0)
- user.balloon_alert(user, "missing components!")
- return FALSE
-
- tool.play_tool_sound(src)
- var/obj/machinery/new_machine = new circuit.build_path(loc)
- if(istype(new_machine))
- new_machine.clear_components()
- // Set anchor state
- new_machine.set_anchored(anchored)
- // Prevent us from dropping stuff thanks to /Exited
- var/obj/item/circuitboard/machine/leaving_circuit = circuit
- circuit = null
- // Assign the circuit & parts & move them all at once into the machine
- // no need to seperatly move circuit board as its already part of the components list
- new_machine.circuit = leaving_circuit
- new_machine.component_parts = components
- for (var/obj/new_part in components)
- new_part.forceMove(new_machine)
- //Inform machine that its finished & cleanup
- new_machine.RefreshParts()
- new_machine.on_construction(user)
- components = null
- qdel(src)
- return TRUE
-
-/obj/structure/frame/machine/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
- . = ..()
- if(. & ITEM_INTERACT_ANY_BLOCKER)
- return .
-
- switch(state)
- if(FRAME_STATE_EMPTY)
- if(istype(tool, /obj/item/stack/cable_coil))
- if(!tool.tool_start_check(user, amount = 5))
- return ITEM_INTERACT_BLOCKING
-
- balloon_alert(user, "adding cables...")
- if(!tool.use_tool(src, user, 2 SECONDS, volume = 50, amount = 5) || state != FRAME_STATE_EMPTY)
- return ITEM_INTERACT_BLOCKING
-
- state = FRAME_STATE_WIRED
- update_appearance(UPDATE_ICON_STATE)
- return ITEM_INTERACT_SUCCESS
-
- if(FRAME_STATE_WIRED)
- if(isnull(circuit) && istype(tool, /obj/item/storage/part_replacer))
- return install_circuit_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
-
- if(FRAME_STATE_BOARD_INSTALLED)
- if(istype(tool, /obj/item/storage/part_replacer))
- return install_parts_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
-
- if(!user.combat_mode)
- return add_part(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
-
- return .
-
-/obj/structure/frame/machine/screwdriver_act(mob/living/user, obj/item/tool)
- . = ..()
- if(. & ITEM_INTERACT_ANY_BLOCKER)
- return .
- if(state != FRAME_STATE_BOARD_INSTALLED)
- return .
-
- if(finalize_construction(user, tool))
- return ITEM_INTERACT_SUCCESS
-
- return ITEM_INTERACT_BLOCKING
-
-/obj/structure/frame/machine/can_be_unfasten_wrench(mob/user, silent)
- . = ..()
- if(. != SUCCESSFUL_UNFASTEN)
- return .
-
- if(circuit?.needs_anchored)
- balloon_alert(user, "circuit must be anchored!")
- return FAILED_UNFASTEN
-
- return .
-
-/obj/structure/frame/machine/wirecutter_act(mob/living/user, obj/item/tool)
- if(state != FRAME_STATE_WIRED)
- return NONE
-
- balloon_alert(user, "removing cables...")
- if(!tool.use_tool(src, user, 2 SECONDS, volume = 50) || state != FRAME_STATE_WIRED)
- return ITEM_INTERACT_BLOCKING
-
- state = FRAME_STATE_EMPTY
- update_appearance(UPDATE_ICON_STATE)
- new /obj/item/stack/cable_coil(drop_location(), 5)
- return ITEM_INTERACT_SUCCESS
-
-/obj/structure/frame/machine/crowbar_act(mob/living/user, obj/item/tool)
- if(state != FRAME_STATE_BOARD_INSTALLED)
- return NONE
-
- tool.play_tool_sound(src)
- var/list/leftover_components = components.Copy() - circuit
- dump_contents()
- balloon_alert(user, "circuit board[length(leftover_components) ? " and components" : ""] removed")
- // Circuit exited handles updating state
- return ITEM_INTERACT_SUCCESS
-
-/obj/structure/frame/machine/Exited(atom/movable/gone, direction)
- if(gone == circuit)
- components -= circuit
- return ..()
-
-/obj/structure/frame/machine/Destroy()
- QDEL_LIST(components)
- return ..()
-
-/**
- * Returns the instance of path1 in list, else path2 in list
- *
- * Arguments
- * * parts - the list of parts to search
- * * path1 - the first path to search for
- * * path2 - the second path to search for, if path1 is not found
- */
-/obj/structure/frame/machine/proc/look_for(list/parts, path1, path2)
- return (locate(path1) in parts) || (path2 ? (locate(path2) in parts) : null)
-
-/obj/structure/frame/machine/deconstruct(disassembled = TRUE)
- if(!(obj_flags & NO_DECONSTRUCTION))
- if(state >= FRAME_STATE_WIRED)
- new /obj/item/stack/cable_coil(drop_location(), 5)
- dump_contents()
- return ..()
-
-/obj/structure/frame/machine/dump_contents()
- var/atom/drop_loc = drop_location()
-
- // We need a snowflake check for stack items since they don't exist anymore
- for(var/component in circuit?.req_components)
- if(!ispath(component, /obj/item/stack))
- continue
- var/obj/item/stack/stack_path = component
- var/stack_amount = circuit.req_components[component] - req_components[component]
- if(stack_amount > 0)
- new stack_path(drop_loc, stack_amount)
-
- // Rest of the stuff can just be spat out (this includes the circuitboard0)
- for(var/component in components)
- if(ismovable(component))
- var/atom/movable/atom_component = component
- atom_component.forceMove(drop_loc)
-
- else if(istype(component, /datum/stock_part))
- var/datum/stock_part/stock_part_datum = component
- var/physical_object_type = initial(stock_part_datum.physical_object_type)
- new physical_object_type(drop_loc)
-
- else
- stack_trace("Invalid component [component] was found in constructable frame")
-
- components = null
- req_components = null
- req_component_names = null
-
-/obj/structure/frame/machine/secured
- state = FRAME_STATE_WIRED
- anchored = TRUE
-
-/obj/structure/frame/machine/secured/Initialize(mapload)
- . = ..()
- update_appearance(UPDATE_ICON_STATE)
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 098c887f2a2d1..9d19d0d9be98b 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -44,7 +44,7 @@
icon_state = "[base_icon_state][music_player.active_song_sound ? "-active" : null]"
return ..()
-/obj/machinery/jukebox/ui_status(mob/user)
+/obj/machinery/jukebox/ui_status(mob/user, datum/ui_state/state)
if(isobserver(user))
return ..()
if(!anchored)
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 7ed48a4c9c5ce..05c0fe242461f 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -46,6 +46,7 @@ Possible to do for anyone motivated enough:
max_integrity = 300
armor_type = /datum/armor/machinery_holopad
circuit = /obj/item/circuitboard/machine/holopad
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
// Blue, dim light
light_power = 0.8
light_color = LIGHT_COLOR_BLUE
@@ -271,7 +272,7 @@ Possible to do for anyone motivated enough:
return ..()
-/obj/machinery/holopad/ui_status(mob/user)
+/obj/machinery/holopad/ui_status(mob/user, datum/ui_state/state)
if(!is_operational)
return UI_CLOSE
if(outgoing_call && !calling)
diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm
new file mode 100644
index 0000000000000..a3c074937f0f7
--- /dev/null
+++ b/code/game/machinery/machine_frame.dm
@@ -0,0 +1,474 @@
+/obj/structure/frame/machine
+ name = "machine frame"
+ desc = "The standard frame for most station appliances. Its appearance and function is controlled by the inserted board."
+ board_type = /obj/item/circuitboard/machine
+ /// List of all compnents inside the frame contributing to its construction
+ var/list/components
+ /// List of all components required to construct the frame
+ var/list/req_components
+ /// User-friendly list of names of required components
+ var/list/req_component_names
+
+/obj/structure/frame/machine/Initialize(mapload)
+ . = ..()
+ register_context()
+ update_appearance(UPDATE_ICON_STATE)
+
+/obj/structure/frame/machine/Destroy()
+ QDEL_LIST(components)
+ return ..()
+
+/obj/structure/frame/machine/deconstruct(disassembled = TRUE)
+ if(!(obj_flags & NO_DECONSTRUCTION))
+ if(state >= FRAME_STATE_WIRED)
+ new /obj/item/stack/cable_coil(drop_location(), 5)
+ dump_contents()
+ return ..()
+
+/obj/structure/frame/machine/try_dissassemble(mob/living/user, obj/item/tool, disassemble_time)
+ if(anchored)
+ balloon_alert(user, "must be unsecured first!")
+ return FALSE
+ return ..()
+
+/obj/structure/frame/machine/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = NONE
+ if(isnull(held_item))
+ return
+
+ switch(state)
+ if(FRAME_STATE_EMPTY)
+ if(istype(held_item, /obj/item/stack/cable_coil))
+ context[SCREENTIP_CONTEXT_LMB] = "Wire Frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_WRENCH)
+ context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_WELDER)
+ context[SCREENTIP_CONTEXT_LMB] = "Unweld frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "Disassemble frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_STATE_WIRED)
+ if(held_item.tool_behaviour == TOOL_WIRECUTTER)
+ context[SCREENTIP_CONTEXT_LMB] = "Cut wires"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(istype(held_item, board_type))
+ context[SCREENTIP_CONTEXT_LMB] = "Insert board"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(FRAME_STATE_BOARD_INSTALLED)
+ if(held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Pry out components"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(held_item.tool_behaviour == TOOL_WRENCH)
+ if(!circuit.needs_anchored)
+ context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+ else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ var/needs_components = FALSE
+ for(var/component in req_components)
+ if(!req_components[component])
+ continue
+ needs_components = TRUE
+ break
+ if(!needs_components)
+ context[SCREENTIP_CONTEXT_LMB] = "Complete frame"
+ return CONTEXTUAL_SCREENTIP_SET
+ else if(!istype(held_item, /obj/item/storage/part_replacer))
+ for(var/component in req_components)
+ if(!req_components[component])
+ continue
+ var/stock_part_path
+ if(ispath(component, /obj/item))
+ stock_part_path = component
+ else if(ispath(component, /datum/stock_part))
+ var/datum/stock_part/stock_part_datum_type = component
+ stock_part_path = initial(stock_part_datum_type.physical_object_type)
+ if(istype(held_item, stock_part_path))
+ context[SCREENTIP_CONTEXT_LMB] = "Insert part"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/structure/frame/machine/examine(user)
+ . = ..()
+ if(!circuit?.needs_anchored)
+ . += span_notice("It can be [EXAMINE_HINT("anchored")] [anchored ? "loose" : "in place"]")
+ if(state == FRAME_STATE_EMPTY)
+ . += span_warning("It needs [EXAMINE_HINT("5 cable")] pieces to wire it.")
+ return
+ if(state == FRAME_STATE_WIRED)
+ . += span_info("Its wires can be cut with a [EXAMINE_HINT("wirecutter")].")
+ if(state != FRAME_STATE_BOARD_INSTALLED)
+ . += span_warning("Its missing a circuit board..")
+ return
+ if(!length(req_components))
+ . += span_info("It requires no components.")
+ return
+
+ var/list/nice_list = list()
+ for(var/component in req_components)
+ if(!req_components[component])
+ continue
+ nice_list += list("[req_components[component]] [req_component_names[component]]\s")
+ . += span_info("It requires [english_list(nice_list, "no more components")].")
+
+ . += span_info("All the components can be [EXAMINE_HINT("pried")] out.")
+ if(!length(nice_list))
+ . += span_info("The frame can be [EXAMINE_HINT("screwed")] to complete it.")
+
+/obj/structure/frame/machine/dump_contents()
+ var/atom/drop_loc = drop_location()
+
+ // We need a snowflake check for stack items since they don't exist anymore
+ for(var/component in circuit?.req_components)
+ if(!ispath(component, /obj/item/stack))
+ continue
+ var/obj/item/stack/stack_path = component
+ var/stack_amount = circuit.req_components[component] - req_components[component]
+ if(stack_amount > 0)
+ new stack_path(drop_loc, stack_amount)
+
+ // Rest of the stuff can just be spat out (this includes the circuitboard0)
+ for(var/component in components)
+ if(ismovable(component))
+ var/atom/movable/atom_component = component
+ atom_component.forceMove(drop_loc)
+
+ else if(istype(component, /datum/stock_part))
+ var/datum/stock_part/stock_part_datum = component
+ var/physical_object_type = initial(stock_part_datum.physical_object_type)
+ new physical_object_type(drop_loc)
+
+ else
+ stack_trace("Invalid component [component] was found in constructable frame")
+
+ components = null
+ req_components = null
+ req_component_names = null
+
+/obj/structure/frame/machine/install_board(mob/living/user, obj/item/circuitboard/machine/board, by_hand = TRUE)
+ if(state == FRAME_STATE_EMPTY)
+ balloon_alert(user, "needs wiring!")
+ return FALSE
+ if(state == FRAME_STATE_BOARD_INSTALLED)
+ balloon_alert(user, "circuit already installed!")
+ return FALSE
+ if(!anchored && istype(board) && board.needs_anchored)
+ balloon_alert(user, "frame must be anchored!")
+ return FALSE
+
+ return ..()
+
+/obj/structure/frame/machine/circuit_added(obj/item/circuitboard/machine/added)
+ state = FRAME_STATE_BOARD_INSTALLED
+ update_appearance(UPDATE_ICON_STATE)
+
+ //add circuit board as the first component to the list of components
+ //required for part_replacer to locate it while exchanging parts
+ //so it does not early return in /obj/machinery/proc/exchange_parts
+ components = list(circuit)
+ req_components = added.req_components.Copy()
+ if(!req_components)
+ return
+
+ //creates a list of names from all the required parts
+ req_component_names = list()
+ for(var/component_path in req_components)
+ if(!ispath(component_path))
+ continue
+
+ if(ispath(component_path, /obj/item/stack))
+ var/obj/item/stack/stack_path = component_path
+ if(initial(stack_path.singular_name))
+ req_component_names[component_path] = initial(stack_path.singular_name)
+ else
+ req_component_names[component_path] = initial(stack_path.name)
+ else if(ispath(component_path, /datum/stock_part))
+ var/datum/stock_part/stock_part = component_path
+ var/obj/item/physical_object_type = initial(stock_part.physical_object_type)
+
+ req_component_names[component_path] = initial(physical_object_type.name)
+ else if(ispath(component_path, /obj/item/stock_parts))
+ var/obj/item/stock_parts/stock_part = component_path
+
+ if(!added.specific_parts && initial(stock_part.base_name))
+ req_component_names[component_path] = initial(stock_part.base_name)
+ else
+ req_component_names[component_path] = initial(stock_part.name)
+ else if(ispath(component_path, /obj/item))
+ var/obj/item/part = component_path
+
+ req_component_names[component_path] = initial(part.name)
+ else
+ stack_trace("Invalid component part [component_path] in [type], couldn't get its name")
+ req_component_names[component_path] = "[component_path] (this is a bug)"
+
+/obj/structure/frame/machine/circuit_removed(obj/item/circuitboard/machine/removed)
+ components -= removed
+ state = FRAME_STATE_WIRED
+ update_appearance(UPDATE_ICON_STATE)
+
+/**
+ * Returns the instance of path1 in list, else path2 in list
+ *
+ * Arguments
+ * * parts - the list of parts to search
+ * * path1 - the first path to search for
+ * * path2 - the second path to search for, if path1 is not found
+ */
+/obj/structure/frame/machine/proc/look_for(list/parts, path1, path2)
+ PRIVATE_PROC(TRUE)
+
+ return (locate(path1) in parts) || (path2 ? (locate(path2) in parts) : null)
+
+/obj/structure/frame/machine/install_parts_from_part_replacer(mob/living/user, obj/item/storage/part_replacer/replacer, no_sound = FALSE)
+ if(!length(replacer.contents))
+ return FALSE
+ var/amt = 0
+ for(var/path in req_components)
+ amt += req_components[path]
+ if(!amt)
+ return FALSE
+
+ var/play_sound = FALSE
+ var/list/part_list = replacer.get_sorted_parts() //parts sorted in order of tier
+ for(var/path in req_components)
+ var/target_path
+ if(ispath(path, /datum/stock_part))
+ var/datum/stock_part/datum_part = path
+ target_path = initial(datum_part.physical_object_base_type)
+ else
+ target_path = path
+
+ var/obj/item/part
+ while(req_components[path] > 0 && (part = look_for(part_list, target_path, ispath(path, /obj/item/stack/ore/bluespace_crystal) ? /obj/item/stack/sheet/bluespace_crystal : null)))
+ part_list -= part
+ if(istype(part, /obj/item/stack))
+ var/obj/item/stack/S = part
+ var/used_amt = min(round(S.get_amount()), req_components[path])
+ var/stack_name = S.singular_name
+ if(!used_amt || !S.use(used_amt))
+ continue
+ req_components[path] -= used_amt
+ // No balloon alert here so they can look back and see what they added
+ to_chat(user, span_notice("You add [used_amt] [stack_name] to [src]."))
+ play_sound = TRUE
+ else if(replacer.atom_storage.attempt_remove(part, src))
+ var/stock_part_datum = GLOB.stock_part_datums_per_object[part.type]
+ if (!isnull(stock_part_datum))
+ components += stock_part_datum
+ qdel(part)
+ else
+ components += part
+ part.forceMove(src)
+ req_components[path]--
+ // No balloon alert here so they can look back and see what they added
+ to_chat(user, span_notice("You add [part] to [src]."))
+ play_sound = TRUE
+
+ if(play_sound && !no_sound)
+ replacer.play_rped_sound()
+ if(replacer.works_from_distance)
+ user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+ return TRUE
+
+/obj/structure/frame/machine/can_be_unfasten_wrench(mob/user, silent)
+ . = ..()
+ if(. != SUCCESSFUL_UNFASTEN)
+ return .
+
+ if(circuit?.needs_anchored)
+ balloon_alert(user, "frame must be anchored!")
+ return FAILED_UNFASTEN
+
+ return .
+
+/obj/structure/frame/machine/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ return .
+ if(state != FRAME_STATE_BOARD_INSTALLED)
+ return .
+
+ if(finalize_construction(user, tool))
+ return ITEM_INTERACT_SUCCESS
+
+ return ITEM_INTERACT_BLOCKING
+
+/obj/structure/frame/machine/wirecutter_act(mob/living/user, obj/item/tool)
+ if(user.combat_mode)
+ return NONE
+ if(state != FRAME_STATE_WIRED)
+ return ITEM_INTERACT_BLOCKING
+
+ balloon_alert(user, "removing cables...")
+ if(!tool.use_tool(src, user, 2 SECONDS, volume = 50) || state != FRAME_STATE_WIRED)
+ return ITEM_INTERACT_BLOCKING
+
+ state = FRAME_STATE_EMPTY
+ update_appearance(UPDATE_ICON_STATE)
+ new /obj/item/stack/cable_coil(drop_location(), 5)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/structure/frame/machine/crowbar_act(mob/living/user, obj/item/tool)
+ if(user.combat_mode)
+ return NONE
+ if(state != FRAME_STATE_BOARD_INSTALLED)
+ return ITEM_INTERACT_BLOCKING
+
+ tool.play_tool_sound(src)
+ var/list/leftover_components = components.Copy() - circuit
+ dump_contents()
+ balloon_alert(user, "circuit board[length(leftover_components) ? " and components" : ""] removed")
+ // Circuit exited handles updating state
+ return ITEM_INTERACT_SUCCESS
+
+/**
+ * Attempts to add the passed part to the frame
+ *
+ * Requires no sanity check that the passed part is a stock part
+ *
+ * Arguments
+ * * user - the player
+ * * tool - the part to add
+ */
+/obj/structure/frame/machine/proc/add_part(mob/living/user, obj/item/tool)
+ PRIVATE_PROC(TRUE)
+
+ for(var/stock_part_base in req_components)
+ if (req_components[stock_part_base] == 0)
+ continue
+
+ var/stock_part_path
+
+ if(ispath(stock_part_base, /obj/item))
+ stock_part_path = stock_part_base
+ else if(ispath(stock_part_base, /datum/stock_part))
+ var/datum/stock_part/stock_part_datum_type = stock_part_base
+ stock_part_path = initial(stock_part_datum_type.physical_object_type)
+ else
+ stack_trace("Bad stock part in req_components: [stock_part_base]")
+ continue
+
+ //if we require an bluespace crystall and we have an full sheet of them we can allow that
+ if(ispath(stock_part_path, /obj/item/stack/ore/bluespace_crystal) && istype(tool, /obj/item/stack/sheet/bluespace_crystal))
+ pass() //allow it
+ else if(!istype(tool, stock_part_path))
+ continue
+
+ if(isstack(tool))
+ var/obj/item/stack/S = tool
+ var/used_amt = min(round(S.get_amount()), req_components[stock_part_path])
+ if(used_amt && S.use(used_amt))
+ req_components[stock_part_path] -= used_amt
+ // No balloon alert here so they can look back and see what they added
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ return
+
+ // We might end up qdel'ing the part if it's a stock part datum.
+ // In practice, this doesn't have side effects to the name,
+ // but academically we should not be using an object after it's deleted.
+ var/part_name = "[tool]"
+
+ if (ispath(stock_part_base, /datum/stock_part))
+ // We can't just reuse stock_part_path here or its singleton,
+ // or else putting in a tier 2 part will deconstruct to a tier 1 part.
+ var/stock_part_datum = GLOB.stock_part_datums_per_object[tool.type]
+ if (isnull(stock_part_datum))
+ stack_trace("tool.type] does not have an associated stock part datum!")
+ continue
+
+ components += stock_part_datum
+
+ // We regenerate the stock parts on deconstruct.
+ // This technically means we lose unique qualities of the stock part, but
+ // it's worth it for how dramatically this simplifies the code.
+ // The only place I can see it affecting anything is like...RPG qualities. :P
+ qdel(tool)
+ else if(user.transferItemToLoc(tool, src))
+ components += tool
+ else
+ break
+
+ // No balloon alert here so they can look back and see what they added
+ to_chat(user, span_notice("You add [part_name] to [src]."))
+ req_components[stock_part_base]--
+ return TRUE
+
+ balloon_alert(user, "can't add that!")
+ return FALSE
+
+/obj/structure/frame/machine/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
+ . = ..()
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ return .
+
+ switch(state)
+ if(FRAME_STATE_EMPTY)
+ if(istype(tool, /obj/item/stack/cable_coil))
+ if(!tool.tool_start_check(user, amount = 5))
+ return ITEM_INTERACT_BLOCKING
+
+ balloon_alert(user, "adding cables...")
+ if(!tool.use_tool(src, user, 2 SECONDS, volume = 50, amount = 5) || state != FRAME_STATE_EMPTY)
+ return ITEM_INTERACT_BLOCKING
+
+ state = FRAME_STATE_WIRED
+ update_appearance(UPDATE_ICON_STATE)
+ return ITEM_INTERACT_SUCCESS
+
+ if(FRAME_STATE_WIRED)
+ if(isnull(circuit) && istype(tool, /obj/item/storage/part_replacer))
+ return install_circuit_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+
+ if(FRAME_STATE_BOARD_INSTALLED)
+ if(istype(tool, /obj/item/storage/part_replacer))
+ return install_parts_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+
+ if(!user.combat_mode)
+ return add_part(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+
+ return .
+
+/**
+ * Attempt to finalize the construction of the frame into a machine
+ * as according to our circuit and parts
+ *
+ * If successful, results in qdel'ing the frame and newing of a machine
+ *
+ * Arguments
+ * * user - the player
+ * * tool - the tool used to finalize the construction
+ */
+/obj/structure/frame/machine/finalize_construction(mob/living/user, obj/item/tool)
+ for(var/component in req_components)
+ if(req_components[component] > 0)
+ user.balloon_alert(user, "missing components!")
+ return FALSE
+
+ tool.play_tool_sound(src)
+ var/obj/machinery/new_machine = new circuit.build_path(loc)
+ if(istype(new_machine))
+ new_machine.clear_components()
+ // Set anchor state
+ new_machine.set_anchored(anchored)
+ // Prevent us from dropping stuff thanks to /Exited
+ var/obj/item/circuitboard/machine/leaving_circuit = circuit
+ circuit = null
+ // Assign the circuit & parts & move them all at once into the machine
+ // no need to seperatly move circuit board as its already part of the components list
+ new_machine.circuit = leaving_circuit
+ new_machine.component_parts = components
+ for (var/obj/new_part in components)
+ new_part.forceMove(new_machine)
+ //Inform machine that its finished & cleanup
+ new_machine.RefreshParts()
+ new_machine.on_construction(user)
+ components = null
+ qdel(src)
+ return TRUE
+
+/obj/structure/frame/machine/secured
+ state = FRAME_STATE_WIRED
+ anchored = TRUE
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index d0c0744f19ecb..809171af1efa7 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -1168,7 +1168,7 @@ DEFINE_BITFIELD(turret_flags, list(
if(properties["team_color"])
team_color = properties["team_color"]
-/obj/machinery/porta_turret/lasertag/ui_status(mob/user)
+/obj/machinery/porta_turret/lasertag/ui_status(mob/user, datum/ui_state/state)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(team_color == "blue" && istype(H.wear_suit, /obj/item/clothing/suit/redtag))
diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm
index 2b8abcb17282f..075e927d65328 100644
--- a/code/game/objects/effects/misc.dm
+++ b/code/game/objects/effects/misc.dm
@@ -43,6 +43,10 @@
stack_trace("Warning: something tried to forceMove() a qdeleted [src]([type]) to non-null destination [destination]([destination.type])!")
return ..()
+/// Override to define loot blacklist behavior
+/obj/effect/spawner/proc/can_spawn(atom/loot)
+ return TRUE
+
/obj/effect/list_container
name = "list container"
diff --git a/code/game/objects/effects/spawners/random/maintenance.dm b/code/game/objects/effects/spawners/random/maintenance.dm
index 242613e403d6a..234b69e85716d 100644
--- a/code/game/objects/effects/spawners/random/maintenance.dm
+++ b/code/game/objects/effects/spawners/random/maintenance.dm
@@ -4,6 +4,13 @@
icon_state = "loot"
// see code/_globalvars/lists/maintenance_loot.dm for loot table
+/// A subtype of maintenance loot spawner that does not spawn any decals, for when you want to place them on chasm turfs and such
+/// decals such as ashes will cause NeverShouldHaveComeHere() to fail on such turfs, which creates annoying rng based CI failures
+/obj/effect/spawner/random/maintenance/no_decals
+
+/obj/effect/spawner/random/maintenance/no_decals/can_spawn(atom/loot)
+ return !istype(loot, /obj/effect/decal)
+
/obj/effect/spawner/random/maintenance/examine(mob/user)
. = ..()
. += span_info("This spawner has an effective loot count of [get_effective_lootcount()].")
@@ -64,3 +71,31 @@
/obj/effect/spawner/random/maintenance/eight
name = "8 x maintenance loot spawner"
spawn_loot_count = 8
+
+/obj/effect/spawner/random/maintenance/no_decals/two
+ name = "2 x maintenance loot spawner"
+ spawn_loot_count = 2
+
+/obj/effect/spawner/random/maintenance/no_decals/three
+ name = "3 x maintenance loot spawner"
+ spawn_loot_count = 3
+
+/obj/effect/spawner/random/maintenance/no_decals/four
+ name = "4 x maintenance loot spawner"
+ spawn_loot_count = 4
+
+/obj/effect/spawner/random/maintenance/no_decals/five
+ name = "5 x maintenance loot spawner"
+ spawn_loot_count = 5
+
+/obj/effect/spawner/random/maintenance/no_decals/six
+ name = "6 x maintenance loot spawner"
+ spawn_loot_count = 6
+
+/obj/effect/spawner/random/maintenance/no_decals/seven
+ name = "7 x maintenance loot spawner"
+ spawn_loot_count = 7
+
+/obj/effect/spawner/random/maintenance/no_decals/eight
+ name = "8 x maintenance loot spawner"
+ spawn_loot_count = 8
diff --git a/code/game/objects/effects/spawners/random/random.dm b/code/game/objects/effects/spawners/random/random.dm
index e8653a30cfb0c..e518b9ad4717c 100644
--- a/code/game/objects/effects/spawners/random/random.dm
+++ b/code/game/objects/effects/spawners/random/random.dm
@@ -57,6 +57,9 @@
var/pixel_divider = FLOOR(16 / spawn_loot_split_pixel_offsets, 1) // 16 pixels offsets is max that should be allowed in any direction
while((spawn_loot_count-loot_spawned) && loot.len)
var/lootspawn = pick_weight_recursive(loot)
+ if(!can_spawn(lootspawn))
+ loot.Remove(lootspawn)
+ continue
if(!spawn_loot_double)
loot.Remove(lootspawn)
if(lootspawn && (spawn_scatter_radius == 0 || spawn_locations.len))
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 04c0c57955335..d395905cd2631 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -1455,7 +1455,7 @@
/obj/item/card/id/advanced/chameleon/ui_state(mob/user)
return GLOB.always_state
-/obj/item/card/id/advanced/chameleon/ui_status(mob/user)
+/obj/item/card/id/advanced/chameleon/ui_status(mob/user, datum/ui_state/state)
var/target = theft_target?.resolve()
if(!target)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 395faaa5449c9..e7c4e8eadde9b 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -31,6 +31,7 @@
attack_verb_continuous = list("attacks", "colours")
attack_verb_simple = list("attack", "colour")
grind_results = list()
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
/// Icon state to use when capped
var/icon_capped
diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm
index eecef73fd4304..8e9fb78172f60 100644
--- a/code/game/objects/items/credit_holochip.dm
+++ b/code/game/objects/items/credit_holochip.dm
@@ -9,10 +9,13 @@
w_class = WEIGHT_CLASS_TINY
var/credits = 0
-/obj/item/holochip/Initialize(mapload, amount)
+/obj/item/holochip/Initialize(mapload, amount = 1)
. = ..()
if(amount)
credits = amount
+ if(credits <= 0 && !mapload)
+ stack_trace("Holochip created with 0 or less credits in [get_area_name(src)]!")
+ return INITIALIZE_HINT_QDEL
update_appearance()
/obj/item/holochip/examine(mob/user)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 67369d91a17e9..248955e0fa493 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -10,6 +10,7 @@
worn_icon_state = "radio"
desc = "A basic handheld radio that communicates with local telecommunication networks."
dog_fashion = /datum/dog_fashion/back
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_ALLOW_USER_LOCATION | INTERACT_ATOM_IGNORE_MOBILITY
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
diff --git a/code/game/objects/items/devices/scanners/t_scanner.dm b/code/game/objects/items/devices/scanners/t_scanner.dm
index c9f1ae0fd9d92..555e6cc88619f 100644
--- a/code/game/objects/items/devices/scanners/t_scanner.dm
+++ b/code/game/objects/items/devices/scanners/t_scanner.dm
@@ -4,7 +4,6 @@
custom_price = PAYCHECK_LOWER * 0.7
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "t-ray0"
- var/on = FALSE
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
inhand_icon_state = "electronic"
@@ -12,6 +11,10 @@
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 1.5)
+ /// Is this T-Ray scanner currently on?
+ var/on = FALSE
+ /// Will this T-Ray scanner shut off on de-equip? (Cyborgs only)
+ var/shut_off_on_unequip = TRUE
/obj/item/t_scanner/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -29,6 +32,8 @@
toggle_on()
/obj/item/t_scanner/cyborg_unequip(mob/user)
+ if(!shut_off_on_unequip)
+ return
if(!on)
return
toggle_on()
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index ce1245098c860..3515e7f52c3ce 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -431,7 +431,7 @@ effective or pretty fucking useless.
toolbox = null
qdel(src)
-/obj/machinery/porta_turret/syndicate/toolbox/ui_status(mob/user)
+/obj/machinery/porta_turret/syndicate/toolbox/ui_status(mob/user, datum/ui_state/state)
if(faction_check(user.faction, faction))
return ..()
diff --git a/code/game/objects/items/food/meatslab.dm b/code/game/objects/items/food/meatslab.dm
index c6b7d8110e7ee..c1b5a6a186f5f 100644
--- a/code/game/objects/items/food/meatslab.dm
+++ b/code/game/objects/items/food/meatslab.dm
@@ -340,7 +340,6 @@
icon_state = "meatwheat_clump"
bite_consumption = 4
tastes = list("meat" = 1, "wheat" = 1)
- foodtypes = GRAIN
/obj/item/food/meat/slab/gorilla
name = "gorilla meat"
diff --git a/code/game/objects/items/food/misc.dm b/code/game/objects/items/food/misc.dm
index 840d431bfa55e..0e598c6820296 100644
--- a/code/game/objects/items/food/misc.dm
+++ b/code/game/objects/items/food/misc.dm
@@ -246,6 +246,7 @@
w_class = WEIGHT_CLASS_BULKY
tastes = list("cherry" = 1, "crepe" = 1)
foodtypes = GRAIN | FRUIT | SUGAR
+ food_flags = FOOD_FINGER_FOOD
crafting_complexity = FOOD_COMPLEXITY_5
/obj/item/food/branrequests
diff --git a/code/game/objects/items/food/pie.dm b/code/game/objects/items/food/pie.dm
index 46674fb735e94..e57759915208d 100644
--- a/code/game/objects/items/food/pie.dm
+++ b/code/game/objects/items/food/pie.dm
@@ -484,3 +484,30 @@
tastes = list("pie" = 1, "the far off year of 2010" = 1)
foodtypes = GRAIN
crafting_complexity = FOOD_COMPLEXITY_2
+
+/obj/item/food/pie/bacid_pie
+ name = "battery acid pie"
+ desc = "Ooh it's a pie made of... battery acid? You suppose an ethereal could find some enjoyement in eating this."
+ icon_state = "bacid_pie"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 18,
+ /datum/reagent/consumable/liquidelectricity/enriched = 18
+ )
+ tastes = list("battery acid" = 2, "electricity" = 2, "a cyber world" = 2)
+ foodtypes = TOXIC
+ slice_type = /obj/item/food/pieslice/bacid_pie
+ yield = 4
+ crafting_complexity = FOOD_COMPLEXITY_3
+
+
+/obj/item/food/pieslice/bacid_pie
+ name = "battery acid pie slice"
+ desc = "The battery acid filling has a concerningly appealing bright green color"
+ icon_state = "bacid_pie_slice"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4.5,
+ /datum/reagent/consumable/liquidelectricity/enriched = 4.5
+ )
+ tastes = list("battery acid" = 1, "electricity" = 1, "a cyber world" = 1)
+ foodtypes = TOXIC
+ crafting_complexity = FOOD_COMPLEXITY_3
diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm
index 1e8a148e9b1e8..9a55b1cd57464 100644
--- a/code/game/objects/items/kitchen.dm
+++ b/code/game/objects/items/kitchen.dm
@@ -235,6 +235,13 @@
if(!target_mob.reagents || reagents.total_volume <= 0)
return ..()
+ if(target_mob.is_mouth_covered(ITEM_SLOT_HEAD) || target_mob.is_mouth_covered(ITEM_SLOT_MASK))
+ if(target_mob == user)
+ target_mob.balloon_alert(user, "can't eat with mouth covered!")
+ else
+ target_mob.balloon_alert(user, "[target_mob.p_their()] mouth is covered!")
+ return TRUE
+
if(target_mob == user)
user.visible_message(
span_notice("[user] scoops a spoonful into [user.p_their()] mouth."),
diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm
index 7e14ca91b3ff1..9570614b400b3 100644
--- a/code/game/objects/items/rcd/RCD.dm
+++ b/code/game/objects/items/rcd/RCD.dm
@@ -521,7 +521,7 @@
///How much charge is used up for each matter unit.
var/mass_to_energy = 16
-/obj/item/construction/rcd/exosuit/ui_status(mob/user)
+/obj/item/construction/rcd/exosuit/ui_status(mob/user, datum/ui_state/state)
if(ismecha(owner))
return owner.ui_status(user)
return UI_CLOSE
diff --git a/code/game/objects/items/rcd/RCL.dm b/code/game/objects/items/rcd/RCL.dm
index 9134a7ac1e537..1d966d37670f4 100644
--- a/code/game/objects/items/rcd/RCL.dm
+++ b/code/game/objects/items/rcd/RCL.dm
@@ -16,7 +16,7 @@
w_class = WEIGHT_CLASS_NORMAL
var/max_amount = 90
var/active = FALSE
- actions_types = list(/datum/action/item_action/rcl_col,/datum/action/item_action/rcl_gui,)
+ actions_types = list(/datum/action/item_action/rcl_col,/datum/action/item_action/rcl_gui)
var/list/colors = list("red", "yellow", "green", "blue", "pink", "orange", "cyan", "white")
var/current_color_index = 1
var/ghetto = FALSE
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index e92b4332846c6..367c619333e81 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -381,7 +381,7 @@
else
return ..()
-/obj/item/robot_suit/ui_status(mob/user)
+/obj/item/robot_suit/ui_status(mob/user, datum/ui_state/state)
if(isobserver(user))
return ..()
var/obj/item/held_item = user.get_active_held_item()
diff --git a/code/game/objects/items/stacks/tickets.dm b/code/game/objects/items/stacks/tickets.dm
index 20e6843b97707..d1bd7681b8a8b 100644
--- a/code/game/objects/items/stacks/tickets.dm
+++ b/code/game/objects/items/stacks/tickets.dm
@@ -8,14 +8,9 @@
max_amount = 30
merge_type = /obj/item/stack/arcadeticket
-/obj/item/stack/arcadeticket/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
- . = ..()
- update_appearance()
-
/obj/item/stack/arcadeticket/update_icon_state()
. = ..()
- var/amount = get_amount()
- switch(amount)
+ switch(get_amount())
if(12 to INFINITY)
icon_state = "arcade-ticket_4"
if(6 to 12)
@@ -25,10 +20,5 @@
else
icon_state = "arcade-ticket"
-/obj/item/stack/arcadeticket/proc/pay_tickets()
- amount -= 2
- if (amount == 0)
- qdel(src)
-
/obj/item/stack/arcadeticket/thirty
amount = 30
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index ce848e92cbdd3..fb925cdb7dae8 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -110,7 +110,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
toggle_cabinet(user)
/obj/structure/extinguisher_cabinet/attack_hand_secondary(mob/living/user)
- if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS))
+ if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING))
return ..()
toggle_cabinet(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index 873090118ab68..4d74f5425608f 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -54,7 +54,7 @@
/obj/effect/fun_balloon/sentience/ui_state(mob/user)
return GLOB.admin_state
-/obj/effect/fun_balloon/sentience/ui_status(mob/user)
+/obj/effect/fun_balloon/sentience/ui_status(mob/user, datum/ui_state/state)
if(popped)
return UI_CLOSE
if(isAdminObserver(user)) // ignore proximity if we're an admin
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index b76a25ca7203a..5bd5ce8c2ecfc 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -71,7 +71,7 @@
if(do_after(user,100, target = src))
TeleporterSend()
-/obj/machinery/abductor/console/ui_status(mob/user)
+/obj/machinery/abductor/console/ui_status(mob/user, datum/ui_state/state)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm
index 163b7515872d2..8d8f9e14b8954 100644
--- a/code/modules/antagonists/abductor/machinery/dispenser.dm
+++ b/code/modules/antagonists/abductor/machinery/dispenser.dm
@@ -22,7 +22,7 @@
gland_colors[i] = random_color()
amounts[i] = rand(1,5)
-/obj/machinery/abductor/gland_dispenser/ui_status(mob/user)
+/obj/machinery/abductor/gland_dispenser/ui_status(mob/user, datum/ui_state/state)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index 333a03b45f8fc..711923daa442f 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -59,7 +59,7 @@
span_notice("You successfully break out of [src]!"))
open_machine()
-/obj/machinery/abductor/experiment/ui_status(mob/user)
+/obj/machinery/abductor/experiment/ui_status(mob/user, datum/ui_state/state)
if(user == occupant)
return UI_CLOSE
return ..()
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 4c3fdb45dc98a..223664f0fa449 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -209,7 +209,7 @@
/datum/objective/convert_brother
name = "convert brother"
- explanation_text = "Convert someone else using your flash."
+ explanation_text = "Convert a brainwashable person using your flash. Any flash will work if you lose or break your starting flash."
admin_grantable = FALSE
martyr_compatible = TRUE
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 0353744b298e0..478d500072136 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -181,7 +181,7 @@
. = ..()
refreshBeam()
-/obj/item/assembly/infra/ui_status(mob/user)
+/obj/item/assembly/infra/ui_status(mob/user, datum/ui_state/state)
if(is_secured(user))
return ..()
return UI_CLOSE
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index 5b6bf30668766..031a3f78eadeb 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -134,7 +134,7 @@
. += "prox_scanning"
attached_overlays += "prox_scanning"
-/obj/item/assembly/prox_sensor/ui_status(mob/user)
+/obj/item/assembly/prox_sensor/ui_status(mob/user, datum/ui_state/state)
if(is_secured(user))
return ..()
return UI_CLOSE
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 3823706358faf..5af89f10ec1b4 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -68,7 +68,7 @@
. = ..()
holder?.update_appearance()
-/obj/item/assembly/signaler/ui_status(mob/user)
+/obj/item/assembly/signaler/ui_status(mob/user, datum/ui_state/state)
if(is_secured(user))
return ..()
return UI_CLOSE
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index 1619f080abbe4..8366bb0f2b382 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -83,7 +83,7 @@
. += "timer_timing"
attached_overlays += "timer_timing"
-/obj/item/assembly/timer/ui_status(mob/user)
+/obj/item/assembly/timer/ui_status(mob/user, datum/ui_state/state)
if(is_secured(user))
return ..()
return UI_CLOSE
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index dee1f93f4bf69..c8e93257b8064 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -185,7 +185,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
if(AIR_ALARM_BUILD_COMPLETE)
. += span_notice("Right-click to [locked ? "unlock" : "lock"] the interface.")
-/obj/machinery/airalarm/ui_status(mob/user)
+/obj/machinery/airalarm/ui_status(mob/user, datum/ui_state/state)
if(user.has_unlimited_silicon_privilege && aidisabled)
to_chat(user, "AI control has been disabled.")
else if(!shorted)
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index ce70c93a558bb..be455ea6d4709 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -21,6 +21,7 @@
max_integrity = 200
obj_flags = CAN_BE_HIT
armor_type = /datum/armor/machinery_atmospherics
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
///Check if the object can be unwrenched
var/can_unwrench = FALSE
diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm
index 130c144d42439..e72d72b3d5955 100644
--- a/code/modules/atmospherics/machinery/components/components_base.dm
+++ b/code/modules/atmospherics/machinery/components/components_base.dm
@@ -213,7 +213,7 @@
// UI Stuff
-/obj/machinery/atmospherics/components/ui_status(mob/user)
+/obj/machinery/atmospherics/components/ui_status(mob/user, datum/ui_state/state)
if(allowed(user))
return ..()
to_chat(user, span_danger("Access denied."))
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm b/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
index 269e0943004a2..7d2efbd932103 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
@@ -4,6 +4,7 @@
base_icon_state = "bluespace_sender"
name = "Bluespace Gas Sender"
desc = "Sends gases to the bluespace network to be shared with the connected vendors, who knows what's beyond!"
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
density = TRUE
max_integrity = 300
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index 5c894274619ad..46adfee054e6e 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -5,6 +5,8 @@
icon_state = "thermo_base"
plane = GAME_PLANE
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
+
name = "Temperature control unit"
desc = "Heats or cools gas in connected pipes."
@@ -257,7 +259,7 @@
return ITEM_INTERACT_SUCCESS
return
-/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user)
+/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user, datum/ui_state/state)
if(interactive)
return ..()
return UI_CLOSE
diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm
index bfb883f3f3406..7edfe9e2a9cf8 100644
--- a/code/modules/cargo/packs/imports.dm
+++ b/code/modules/cargo/packs/imports.dm
@@ -319,3 +319,20 @@
)
crate_name = "materials market crate"
crate_type = /obj/structure/closet/crate/cargo
+
+/datum/supply_pack/imports/floortilecamo
+ name = "Floor-tile Camouflage Uniform"
+ desc = "Thank you for shopping from Camo-J's, our uniquely designed \
+ floor-tile 'NT SCUM' styled camouflage fatigues is the ultimate \
+ espionage uniform used by the very best. Providing the best \
+ flexibility, with our latest Camo-tech threads. Perfect for \
+ risky espionage hallway operations. Enjoy our product!"
+ hidden = TRUE
+ cost = CARGO_CRATE_VALUE * 6
+ contains = list(/obj/item/clothing/under/syndicate/floortilecamo = 4,
+ /obj/item/clothing/mask/floortilebalaclava = 4,
+ /obj/item/clothing/gloves/combat/floortile = 4,
+ /obj/item/clothing/shoes/jackboots/floortile = 4
+ )
+ crate_name = "floortile camouflauge crate"
+ crate_type = /obj/structure/closet/crate/secure/weapon
diff --git a/code/modules/clothing/gloves/combat.dm b/code/modules/clothing/gloves/combat.dm
index 770308227163f..efc5bd40b0587 100644
--- a/code/modules/clothing/gloves/combat.dm
+++ b/code/modules/clothing/gloves/combat.dm
@@ -24,3 +24,9 @@
icon_state = "wizard"
greyscale_colors = null
inhand_icon_state = null
+
+/obj/item/clothing/gloves/combat/floortile
+ name = "floortile camouflage gloves"
+ desc = "Is it just me or is there a pair of gloves on the floor?"
+ icon_state = "ftc_gloves"
+ inhand_icon_state = "greyscale_gloves"
diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm
index 8e52a8c525822..2129c598aae25 100644
--- a/code/modules/clothing/head/hardhat.dm
+++ b/code/modules/clothing/head/hardhat.dm
@@ -23,7 +23,7 @@
var/hat_type = "yellow"
///Whether the headlamp is on or off.
var/on = FALSE
-
+ clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
/datum/armor/utility_hardhat
melee = 15
@@ -216,6 +216,7 @@
light_color = "#fff2bf"
worn_y_offset = 1
dog_fashion = /datum/dog_fashion/head/pumpkin/unlit
+ clothing_traits = list()
/obj/item/clothing/head/utility/hardhat/pumpkinhead/set_light_on(new_value)
. = ..()
@@ -269,6 +270,6 @@
flags_inv = 0
armor_type = /datum/armor/none
light_range = 1 //luminosity when on
-
+ clothing_traits = list()
dog_fashion = /datum/dog_fashion/head/reindeer
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 2960260255fbc..7741251f3f5b2 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -184,6 +184,7 @@
visor_flags_inv = HIDEFACE|HIDESNOUT
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
visor_flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
+ clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
/datum/armor/toggleable_riot
melee = 50
@@ -247,6 +248,7 @@
strip_delay = 80
resistance_flags = FIRE_PROOF | ACID_PROOF
dog_fashion = null
+ clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
/datum/armor/helmet_swat
melee = 40
@@ -394,6 +396,7 @@
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
strip_delay = 80
dog_fashion = null
+ clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
/datum/armor/helmet_knight
melee = 50
diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm
index 9c947b53a9399..78ba0764b81ec 100644
--- a/code/modules/clothing/masks/boxing.dm
+++ b/code/modules/clothing/masks/boxing.dm
@@ -12,6 +12,21 @@
/obj/item/clothing/mask/balaclava/attack_self(mob/user)
adjustmask(user)
+/obj/item/clothing/mask/floortilebalaclava
+ name = "floortile balaclava"
+ desc = "The newest floortile camouflage balaclava used for hallway warfare. \
+ The best breathability, flexibility and comfort. Designed by Camo-J's."
+ icon_state = "floortile_balaclava"
+ inhand_icon_state = "balaclava"
+ flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ visor_flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
+ alternate_worn_layer = LOW_FACEMASK_LAYER
+ w_class = WEIGHT_CLASS_SMALL
+ actions_types = list(/datum/action/item_action/adjust)
+
+/obj/item/clothing/mask/floortilebalaclava/attack_self(mob/user)
+ adjustmask(user)
+
/obj/item/clothing/mask/luchador
name = "Luchador Mask"
desc = "Worn by robust fighters, flying high to defeat their foes!"
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index cdfa38ba5d2d2..67b50fbe67213 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -469,7 +469,7 @@
var/true_price = round(price*profit_scaling)
to_chat(user, span_notice("[selling ? "Sold" : "Getting the price of"] [I], value: [true_price] credits[I.contents.len ? " (exportable contents included)" : ""].[profit_scaling < 1 && selling ? "[round(price-true_price)] credit\s taken as processing fee\s." : ""]"))
if(selling)
- new /obj/item/holochip(get_turf(user),true_price)
+ new /obj/item/holochip(get_turf(user), true_price)
else
to_chat(user, span_warning("There is no export value for [I] or any items within it."))
diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm
index b359f94db3da5..dbfa4f8b3c40d 100644
--- a/code/modules/clothing/shoes/boots.dm
+++ b/code/modules/clothing/shoes/boots.dm
@@ -64,6 +64,12 @@
/obj/item/clothing/shoes/jackboots/sec
icon_state = "jackboots_sec"
+/obj/item/clothing/shoes/jackboots/floortile
+ name = "floortile camouflage jackboots"
+ desc = "Is it just me or is there a pair of jackboots on the floor?"
+ icon_state = "ftc_boots"
+ inhand_icon_state = null
+
/obj/item/clothing/shoes/winterboots
name = "winter boots"
desc = "Boots lined with 'synthetic' animal fur."
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index ef10536341e98..47bd32280ed89 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -282,7 +282,7 @@
armor_type = /datum/armor/armor_riot
strip_delay = 80
equip_delay_other = 60
- clothing_traits = list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED)
+ clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
/datum/armor/armor_riot
melee = 50
@@ -386,7 +386,7 @@
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
slowdown = 0.7
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- clothing_traits = list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED)
+ clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
//All of the armor below is mostly unused
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 45152f7569ae5..20a117e112e48 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -151,7 +151,7 @@
if((supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION) && ishuman(user))
var/mob/living/carbon/human/wearer = user
- if(wearer.bodytype & BODYTYPE_DIGITIGRADE)
+ if(wearer.bodyshape & BODYSHAPE_DIGITIGRADE)
adjusted = DIGITIGRADE_STYLE
update_appearance()
diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm
index 20a5cda059202..3a27ee6c1ebb2 100644
--- a/code/modules/clothing/under/syndicate.dm
+++ b/code/modules/clothing/under/syndicate.dm
@@ -111,6 +111,15 @@
can_adjust = FALSE
supports_variations_flags = NONE
+/obj/item/clothing/under/syndicate/floortilecamo
+ name = "floortile camouflage fatigues"
+ desc = "The newest floortile camouflage fatigues used for hallway warfare. \
+ The best breathability, flexibility and comfort. Designed by Camo-J's."
+ icon_state = "camofloortile"
+ inhand_icon_state = "gy_suit"
+ can_adjust = FALSE
+ supports_variations_flags = NONE
+
/obj/item/clothing/under/syndicate/soviet
name = "Ratnik 5 tracksuit"
desc = "Badly translated labels tell you to clean this in Vodka. Great for squatting in."
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index 480e1975fb91a..f994987427e86 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -17,6 +17,8 @@
if(!isnull(species_override))
user.set_species(species_override)
+ else if (istype(user.dna.species.outfit_important_for_life)) //plasmamen get lit on fire and die
+ user.set_species(/datum/species/human)
for(var/datum/action/act as anything in granted_spells)
var/datum/action/new_ability = new act(user)
new_ability.Grant(user)
@@ -31,7 +33,7 @@
name = "Deathmatch: Assistant loadout"
display_name = "Assistant"
desc = "A simple assistant loadout: greyshirt and a toolbox"
-
+
l_hand = /obj/item/storage/toolbox/mechanical
uniform = /obj/item/clothing/under/color/grey
back = /obj/item/storage/backpack
@@ -48,7 +50,7 @@
name = "Deathmatch: Operative"
display_name = "Operative"
desc = "A syndicate operative."
-
+
uniform = /obj/item/clothing/under/syndicate
shoes = /obj/item/clothing/shoes/combat
gloves = /obj/item/clothing/gloves/combat
@@ -59,7 +61,7 @@
name = "Deathmatch: Ranged Operative"
display_name = "Ranged Operative"
desc = "A syndicate operative with a gun and a knife."
-
+
l_hand = /obj/item/gun/ballistic/automatic/pistol
l_pocket = /obj/item/knife/combat
backpack_contents = list(/obj/item/ammo_box/magazine/m9mm = 5)
@@ -68,7 +70,7 @@
name = "Deathmatch: Melee Operative"
display_name = "Melee Operative"
desc = "A syndicate operative with multiple knives."
-
+
gloves = /obj/item/clothing/gloves/tackler/combat/insulated
suit = /obj/item/clothing/suit/armor/vest
head = /obj/item/clothing/head/helmet
@@ -80,7 +82,7 @@
name = "Deathmatch: Security Officer"
display_name = "Security Officer"
desc = "A security officer."
-
+
uniform = /datum/outfit/job/security::uniform
suit = /datum/outfit/job/security::suit
suit_store = /datum/outfit/job/security::suit_store
@@ -100,7 +102,7 @@
name = "DM: Instagib"
display_name = "Instagib"
desc = "Assistant with an instakill rifle."
-
+
l_hand = /obj/item/gun/energy/laser/instakill
/datum/outfit/deathmatch_loadout/operative/sniper
@@ -143,7 +145,7 @@
name = "Deathmatch: Battler Base"
display_name = "Battler"
desc = "What is a battler whith out weapone?."
-
+
shoes = /obj/item/clothing/shoes/combat
uniform = /obj/item/clothing/under/syndicate
gloves = /obj/item/clothing/gloves/combat
@@ -154,14 +156,14 @@
name = "Deathmatch: Soldier"
display_name = "Soldier"
desc = "Ready for combat."
-
+
l_hand = /obj/item/gun/ballistic/rifle/boltaction
l_pocket = /obj/item/knife/combat
uniform = /obj/item/clothing/under/syndicate/rus_army
suit = /obj/item/clothing/suit/armor/vest
head = /obj/item/clothing/head/helmet/rus_helmet
gloves = /obj/item/clothing/gloves/tackler/combat/insulated
-
+
backpack_contents = list(
/obj/item/grenade/smokebomb = 2,
/obj/item/ammo_box/strilka310 = 2,
@@ -199,7 +201,7 @@
name = "Deathmatch: North Star"
display_name = "North Star"
desc = "flip flip flip"
-
+
uniform = /obj/item/clothing/under/suit/carpskin
head = /obj/item/clothing/head/fedora/carpskin
gloves = /obj/item/clothing/gloves/rapid
@@ -212,7 +214,7 @@
name = "Deathmatch: Janitor"
display_name = "Janitor"
desc = "Regular work"
-
+
uniform = /obj/item/clothing/under/rank/civilian/janitor
suit = /obj/item/clothing/suit/caution
head = /obj/item/reagent_containers/cup/bucket
@@ -229,7 +231,7 @@
name = "Deathmatch: Surgeon"
display_name = "Surgeon"
desc = "Treatment has come"
-
+
uniform = /obj/item/clothing/under/rank/medical/scrubs/blue
suit = /obj/item/clothing/suit/apron/surgical
head = /obj/item/clothing/head/utility/surgerycap
@@ -238,7 +240,7 @@
l_pocket = /obj/item/reagent_containers/hypospray/combat
r_pocket = /obj/item/reagent_containers/hypospray/medipen/penthrite
l_hand = /obj/item/chainsaw
-
+
backpack_contents = list(
/obj/item/storage/medkit/tactical,
/obj/item/reagent_containers/hypospray/medipen/stimulants,
@@ -248,7 +250,7 @@
name = "Deathmatch: Raider"
display_name = "Raider"
desc = "Not from Shadow Legends"
-
+
l_hand = /obj/item/nullrod/claymore/chainsaw_sword
r_pocket = /obj/item/switchblade
uniform = /obj/item/clothing/under/costume/jabroni
@@ -260,7 +262,7 @@
name = "DM: Clown"
display_name = "Clown (Man Of Honk)"
desc = "Who called this honking clown"
-
+
uniform = /datum/outfit/job/clown::uniform
belt = /datum/outfit/job/clown::belt
shoes = /datum/outfit/job/clown::shoes
@@ -285,7 +287,7 @@
name = "Deathmatch: Coder"
display_name = "Coder"
desc = "What"
-
+
l_hand = /obj/item/toy/katana
uniform = /obj/item/clothing/under/costume/schoolgirl
suit = /obj/item/clothing/suit/costume/joker
@@ -299,7 +301,7 @@
name = "Deathmatch: Engineer"
display_name = "Engineer"
desc = "Meet the engineer"
-
+
l_hand = /obj/item/storage/toolbox/emergency/turret
uniform = /obj/item/clothing/under/rank/engineering/engineer
shoes = /obj/item/clothing/shoes/magboots
@@ -311,7 +313,7 @@
name = "Deathmatch: Scientist"
display_name = "Scientist"
desc = "What a nerd"
-
+
uniform = /obj/item/clothing/under/rank/rnd/scientist
suit = /obj/item/clothing/suit/armor/reactive/stealth
mask = /obj/item/clothing/mask/gas
@@ -353,7 +355,7 @@
name = "Deathmatch: Ripper"
display_name = "Ripper"
desc = "Die die die!!!"
-
+
l_hand = /obj/item/gun/ballistic/shotgun/hook
r_hand = /obj/item/gun/ballistic/shotgun/hook
uniform = /obj/item/clothing/under/costume/skeleton
@@ -366,7 +368,7 @@
name = "Deathmatch: Cowboy"
display_name = "Cowboy"
desc = "Yeehaw partner"
-
+
r_hand = /obj/item/clothing/mask/cigarette/cigar
l_hand = /obj/item/melee/curator_whip
l_pocket = /obj/item/lighter
diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm
index a8169190fe8d6..e498f662b5e39 100644
--- a/code/modules/deathmatch/deathmatch_lobby.dm
+++ b/code/modules/deathmatch/deathmatch_lobby.dm
@@ -65,7 +65,7 @@
/datum/deathmatch_lobby/proc/find_spawns_and_start_delay(datum/lazy_template/source, list/atoms)
SIGNAL_HANDLER
for(var/thing in atoms)
- if(istype(thing, /obj/effect/landmark/deathmatch_player_spawn))
+ if(istype(thing, /obj/effect/landmark/deathmatch_player_spawn))
player_spawns += thing
UnregisterSignal(source, COMSIG_LAZY_TEMPLATE_LOADED)
@@ -147,10 +147,10 @@
if(players.len)
var/list/winner_info = players[pick(players)]
if(!isnull(winner_info["mob"]))
- winner = winner_info["mob"] //only one should remain anyway but incase of a draw
-
+ winner = winner_info["mob"] //only one should remain anyway but incase of a draw
+
announce(span_reallybig("THE GAME HAS ENDED.
THE WINNER IS: [winner ? winner.real_name : "no one"]."))
-
+
for(var/ckey in players)
var/mob/loser = players[ckey]["mob"]
UnregisterSignal(loser, list(COMSIG_MOB_GHOSTIZED, COMSIG_QDELETING))
@@ -174,7 +174,7 @@
if(player_info["mob"] && player_info["mob"] == player)
ckey = potential_ckey
break
-
+
if(!islist(players[ckey])) // if we STILL didnt find a good ckey
return
@@ -183,7 +183,7 @@
var/mob/dead/observer/ghost = !player.client ? player.get_ghost() : player.ghostize() //this doesnt work on those who used the ghost verb
if(!isnull(ghost))
add_observer(ghost, (host == ckey))
-
+
announce(span_reallybig("[player.real_name] HAS DIED.
[players.len] REMAIN."))
if(!gibbed && !QDELING(player)) // for some reason dusting or deleting in chasm storage messes up tgui bad
@@ -239,7 +239,7 @@
players[key]["host"] = TRUE
break
GLOB.deathmatch_game.passoff_lobby(ckey, host)
-
+
remove_ckey_from_play(ckey)
/datum/deathmatch_lobby/proc/join(mob/player)
@@ -295,6 +295,11 @@
/datum/deathmatch_lobby/ui_state(mob/user)
return GLOB.observer_state
+/// fills the lobby with fake players for the sake of UI debug, can only be called via VV
+/datum/deathmatch_lobby/proc/fakefill(count)
+ for(var/i = 1 to count)
+ players["[rand(1,999)]"] = list("mob" = usr, "host" = FALSE, "ready" = FALSE, "loadout" = pick(loadouts))
+
/datum/deathmatch_lobby/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, null)
if(!ui)
@@ -314,7 +319,7 @@
.["admin"] = check_rights_for(user.client, R_ADMIN)
.["global_chat"] = global_chat
.["playing"] = playing
- .["loadouts"] = list()
+ .["loadouts"] = list("Randomize")
for (var/datum/outfit/deathmatch_loadout/loadout as anything in loadouts)
.["loadouts"] += initial(loadout.display_name)
.["map"] = list()
@@ -371,6 +376,9 @@
return FALSE
if (params["player"] != usr.ckey && host != usr.ckey)
return FALSE
+ if (params["loadout"] == "Randomize")
+ players[params["player"]]["loadout"] = pick(loadouts)
+ return TRUE
for (var/datum/outfit/deathmatch_loadout/possible_loadout as anything in loadouts)
if (params["loadout"] != initial(possible_loadout.display_name))
continue
diff --git a/code/modules/deathmatch/deathmatch_maps.dm b/code/modules/deathmatch/deathmatch_maps.dm
index 60594d903bb02..d437bffbb3cd7 100644
--- a/code/modules/deathmatch/deathmatch_maps.dm
+++ b/code/modules/deathmatch/deathmatch_maps.dm
@@ -107,6 +107,7 @@
desc = "Choose your battler!"
max_players = 10
allowed_loadouts = list(
+ /datum/outfit/deathmatch_loadout/battler/soldier, // First because its a good and easy loadout and is picked by default
/datum/outfit/deathmatch_loadout/battler/bloodminer,
/datum/outfit/deathmatch_loadout/battler/clown,
/datum/outfit/deathmatch_loadout/battler/cowboy,
@@ -117,7 +118,6 @@
/datum/outfit/deathmatch_loadout/battler/raider,
/datum/outfit/deathmatch_loadout/battler/ripper,
/datum/outfit/deathmatch_loadout/battler/scientist,
- /datum/outfit/deathmatch_loadout/battler/soldier,
/datum/outfit/deathmatch_loadout/battler/surgeon,
/datum/outfit/deathmatch_loadout/battler/tgcoder,
/datum/outfit/deathmatch_loadout/naked,
diff --git a/code/modules/economy/holopay.dm b/code/modules/economy/holopay.dm
index c9ce23ccb98de..4755df02fbdbf 100644
--- a/code/modules/economy/holopay.dm
+++ b/code/modules/economy/holopay.dm
@@ -137,7 +137,7 @@
ui = new(user, src, "HoloPay")
ui.open()
-/obj/structure/holopay/ui_status(mob/user)
+/obj/structure/holopay/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(!in_range(user, src) && !isobserver(user))
return UI_CLOSE
diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm
index 0aa5532de13df..e1d027306eac6 100644
--- a/code/modules/food_and_drinks/machinery/processor.dm
+++ b/code/modules/food_and_drinks/machinery/processor.dm
@@ -69,8 +69,9 @@
var/cached_multiplier = (recipe.food_multiplier * rating_amount)
for(var/i in 1 to cached_multiplier)
var/atom/processed_food = new recipe.output(drop_location())
- processed_food.reagents.clear_reagents()
- what.reagents.copy_to(processed_food, what.reagents.total_volume, multiplier = 1 / cached_multiplier)
+ if(processed_food.reagents && what.reagents)
+ processed_food.reagents.clear_reagents()
+ what.reagents.copy_to(processed_food, what.reagents.total_volume, multiplier = 1 / cached_multiplier)
if(cached_mats)
processed_food.set_custom_materials(cached_mats, 1 / cached_multiplier)
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm
index 93851558cffe3..a9f1ad23d8e26 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm
@@ -237,3 +237,11 @@
)
result = /obj/item/food/pie/asdfpie
category = CAT_PIE
+
+/datum/crafting_recipe/food/bacid_pie
+ reqs = list(
+ /obj/item/food/pie/plain = 1,
+ /obj/item/stock_parts/cell = 2,
+ )
+ result = /obj/item/food/pie/bacid_pie
+ category = CAT_PIE
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index 00a52ab82a868..5b8ce2a930939 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -458,7 +458,7 @@
update_appearance(UPDATE_ICON)
-/obj/machinery/biogenerator/ui_status(mob/user)
+/obj/machinery/biogenerator/ui_status(mob/user, datum/ui_state/state)
if(machine_stat & BROKEN || panel_open)
return UI_CLOSE
diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm
index 948a9d404c69e..2bcc2860458bb 100644
--- a/code/modules/hydroponics/grown/cereals.dm
+++ b/code/modules/hydroponics/grown/cereals.dm
@@ -89,7 +89,7 @@
gender = PLURAL
bite_consumption_mod = 0.5
seed = /obj/item/seeds/wheat/meat
- foodtypes = MEAT | GRAIN
+ foodtypes = MEAT
grind_results = list(/datum/reagent/consumable/flour = 0, /datum/reagent/blood = 0)
tastes = list("meatwheat" = 1)
can_distill = FALSE
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index e96bae827fa9b..2be7bba5154d4 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -88,6 +88,17 @@
if(!visualsOnly)
other_chefs.cooks++
+/datum/outfit/job/cook/post_equip(mob/living/carbon/human/user, visualsOnly = FALSE)
+ . = ..()
+ // Update PDA to match possible new trim.
+ var/obj/item/card/id/worn_id = user.wear_id
+ var/obj/item/modular_computer/pda/pda = user.get_item_by_slot(pda_slot)
+ if(!istype(worn_id) || !istype(pda))
+ return
+ var/assignment = worn_id.get_trim_assignment()
+ if(!isnull(assignment))
+ pda.imprint_id(user.real_name, assignment)
+
/datum/outfit/job/cook/get_types_to_preload()
. = ..()
. += /obj/item/clothing/suit/apron/chef
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 25d76bf0156d8..35a3a218f7b1a 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -117,6 +117,12 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
SSid_access.apply_trim_to_card(worn_id, dep_trim)
spawning.sec_hud_set_ID()
+ // Update PDA to match new trim.
+ var/obj/item/modular_computer/pda/pda = spawning.get_item_by_slot(ITEM_SLOT_BELT)
+ var/assignment = worn_id.get_trim_assignment()
+ if(istype(pda) && !isnull(assignment))
+ pda.imprint_id(spawning.real_name, assignment)
+
var/spawn_point = pick(LAZYACCESS(GLOB.department_security_spawns, department))
if(!CONFIG_GET(flag/sec_start_brig) && (destination || spawn_point))
diff --git a/code/modules/library/admin_only.dm b/code/modules/library/admin_only.dm
index 3e10617d9fe98..847f28070e6ac 100644
--- a/code/modules/library/admin_only.dm
+++ b/code/modules/library/admin_only.dm
@@ -80,7 +80,7 @@
page_count = round(max(bookcount - 1, 0) / BOOKS_PER_PAGE) //This is just floor()
search_page = clamp(search_page, 0, page_count)
-/obj/machinery/computer/libraryconsole/admin_only_do_not_map_in_you_fucker/ui_status(mob/user)
+/obj/machinery/computer/libraryconsole/admin_only_do_not_map_in_you_fucker/ui_status(mob/user, datum/ui_state/state)
if(!check_rights_for(user.client, R_BAN))
return UI_CLOSE
if(!SSdbcore.Connect())
@@ -339,7 +339,7 @@
ui.set_autoupdate(FALSE) // Nothing is changing here brother
ui.open()
-/datum/admin_book_viewer/ui_status(mob/user)
+/datum/admin_book_viewer/ui_status(mob/user, datum/ui_state/state)
if(!check_rights_for(user.client, R_BAN))
return UI_CLOSE
return UI_INTERACTIVE
diff --git a/code/modules/library/skill_learning/skill_station.dm b/code/modules/library/skill_learning/skill_station.dm
index 697b34d742e2d..b376501f758fd 100644
--- a/code/modules/library/skill_learning/skill_station.dm
+++ b/code/modules/library/skill_learning/skill_station.dm
@@ -10,7 +10,7 @@
occupant_typecache = list(/mob/living/carbon) //todo make occupant_typecache per type
state_open = TRUE
// Only opens UI when inside; also, you can use the machine while lying down (for paraplegics and the like)
- interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_IGNORE_MOBILITY
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
circuit = /obj/item/circuitboard/machine/skill_station
/// Currently implanting/removing
var/working = FALSE
diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm
index 484720e7159c7..92464c3c7d83a 100644
--- a/code/modules/mining/equipment/mineral_scanner.dm
+++ b/code/modules/mining/equipment/mineral_scanner.dm
@@ -49,6 +49,9 @@
/// The range of the scanner in tiles.
var/range = 7
+/obj/item/t_scanner/adv_mining_scanner/cyborg
+ shut_off_on_unequip = FALSE
+
/obj/item/t_scanner/adv_mining_scanner/cyborg/Initialize(mapload)
. = ..()
toggle_on()
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index 64cff6780dfb9..e9dc43837fff6 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -16,18 +16,19 @@
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
maxHealth = 220
health = 220
+ initial_language_holder = /datum/language_holder/monkey
response_help_continuous = "prods"
response_help_simple = "prod"
response_disarm_continuous = "challenges"
response_disarm_simple = "challenge"
response_harm_continuous = "thumps"
response_harm_simple = "thump"
- speed = 0.5
+ speed = -0.1
melee_attack_cooldown = CLICK_CD_MELEE
- melee_damage_lower = 15
- melee_damage_upper = 18
+ melee_damage_lower = 25
+ melee_damage_upper = 30
damage_coeff = list(BRUTE = 1, BURN = 1.5, TOX = 1.5, STAMINA = 0, OXY = 1.5)
- obj_damage = 20
+ obj_damage = 40
attack_verb_continuous = "pummels"
attack_verb_simple = "pummel"
attack_sound = 'sound/weapons/punch1.ogg'
@@ -123,7 +124,7 @@
/// Gorillas are slower when carrying something
/datum/movespeed_modifier/gorilla_standing
blacklisted_movetypes = (FLYING|FLOATING)
- multiplicative_slowdown = 0.5
+ multiplicative_slowdown = 1.2
/// A smaller gorilla summoned via magic
/mob/living/basic/gorilla/lesser
diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm
index 1a08d2446d257..4387f7db3eca0 100644
--- a/code/modules/mob/living/carbon/alien/adult/queen.dm
+++ b/code/modules/mob/living/carbon/alien/adult/queen.dm
@@ -19,7 +19,7 @@
// as a wise man once wrote: "pull over that ass too fat"
REMOVE_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
// that'd be a too cheeky shield bashing strat
- ADD_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED, INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, INNATE_TRAIT)
AddComponent(/datum/component/seethrough_mob)
/mob/living/carbon/alien/adult/royal/on_lying_down(new_lying_angle)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7c036fe22390d..84fe6dd5edf41 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -85,12 +85,18 @@
span_userdanger("You violently crash into [victim][extra_speed ? " extra hard" : ""], but [victim] managed to block the worst of it!"))
log_combat(src, victim, "crashed into and was blocked by")
return
+ else if(HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
+ victim.take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
+ victim.apply_damage(10 + 10 * extra_speed, STAMINA)
+ victim.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH * 2, 10 SECONDS)
+ visible_message(span_danger("[src] crashes into [victim][extra_speed ? " really hard" : ""], but [victim] was able to stay on their feet!"),\
+ span_userdanger("You violently crash into [victim][extra_speed ? " extra hard" : ""], but [victim] managed to stay on their feet!"))
else
victim.Paralyze(2 SECONDS)
victim.take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
visible_message(span_danger("[src] crashes into [victim][extra_speed ? " really hard" : ""], knocking them both over!"),\
span_userdanger("You violently crash into [victim][extra_speed ? " extra hard" : ""]!"))
- log_combat(src, victim, "crashed into")
+ log_combat(src, victim, "crashed into")
if(oof_noise)
playsound(src,'sound/weapons/punch1.ogg',50,TRUE)
@@ -1023,6 +1029,7 @@
set_usable_hands(usable_hands + 1)
synchronize_bodytypes()
+ synchronize_bodyshapes()
///Proc to hook behavior on bodypart removals. Do not directly call. You're looking for [/obj/item/bodypart/proc/drop_limb()].
/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart, special)
@@ -1049,6 +1056,7 @@
set_usable_hands(usable_hands - 1)
synchronize_bodytypes()
+ synchronize_bodyshapes()
///Updates the bodypart speed modifier based on our bodyparts.
/mob/living/carbon/proc/update_bodypart_speed_modifier()
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index a8b7af95ae611..d3e57c4ec1df1 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -118,6 +118,9 @@
var/last_top_offset
/// A bitfield of "bodytypes", updated by /obj/item/bodypart/proc/synchronize_bodytypes()
- var/bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC
+ var/bodytype = BODYTYPE_ORGANIC
+
+ /// A bitfield of "bodyshapes", updated by /obj/item/bodypart/proc/synchronize_bodyshapes()
+ var/bodyshape = BODYSHAPE_HUMANOID
COOLDOWN_DECLARE(bleeding_message_cd)
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 6b97a36bab399..e37048ca32fee 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -34,33 +34,25 @@
. += span_deadsay("It appears that [t_his] brain is missing...")
var/list/msg = list("")
- var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
- var/list/disabled = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
- if(BP.bodypart_disabled)
- disabled += BP
- missing -= BP.body_zone
- for(var/obj/item/I in BP.embedded_objects)
- if(I.isEmbedHarmless())
- msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] stuck to [t_his] [BP.name]!\n"
+ for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/embedded_item as anything in bodypart.embedded_objects)
+ if(embedded_item.isEmbedHarmless())
+ msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] stuck to [t_his] [bodypart.name]!\n"
else
- msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] embedded in [t_his] [BP.name]!\n"
- for(var/i in BP.wounds)
- var/datum/wound/W = i
- msg += "[W.get_examine_description(user)]\n"
+ msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] embedded in [t_his] [bodypart.name]!\n"
+ for(var/datum/wound/bodypart_wound as anything in bodypart.wounds)
+ msg += "[bodypart_wound.get_examine_description(user)]\n"
- for(var/X in disabled)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/disabled_limb as anything in get_disabled_limbs())
var/damage_text
- damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg
- msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n"
+ damage_text = (disabled_limb.brute_dam >= disabled_limb.burn_dam) ? disabled_limb.heavy_brute_msg : disabled_limb.heavy_burn_msg
+ msg += "[t_His] [disabled_limb.name] is [damage_text]!\n"
- for(var/t in missing)
- if(t == BODY_ZONE_HEAD)
- msg += "[span_deadsay("[t_His] [parse_zone(t)] is missing!")]\n"
+ for(var/obj/item/bodypart/missing_limb as anything in get_missing_limbs())
+ if(missing_limb == BODY_ZONE_HEAD)
+ msg += "[span_deadsay("[t_His] [parse_zone(missing_limb)] is missing!")]\n"
continue
- msg += "[span_warning("[t_His] [parse_zone(t)] is missing!")]\n"
+ msg += "[span_warning("[t_His] [parse_zone(missing_limb)] is missing!")]\n"
var/temp = getBruteLoss()
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 3ac855061ed29..ff07ad2ef0e73 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -693,7 +693,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
working_shirt.pixel_y += height_offset
standing += working_shirt
- if(species_human.socks && species_human.num_legs >= 2 && !(species_human.bodytype & BODYTYPE_DIGITIGRADE))
+ if(species_human.socks && species_human.num_legs >= 2 && !(species_human.bodyshape & BODYSHAPE_DIGITIGRADE))
var/datum/sprite_accessory/socks/socks = GLOB.socks_list[species_human.socks]
if(socks)
standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
@@ -911,7 +911,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(ITEM_SLOT_FEET)
if(H.num_legs < 2)
return FALSE
- if((H.bodytype & BODYTYPE_DIGITIGRADE) && !(I.item_flags & IGNORE_DIGITIGRADE))
+ if((H.bodyshape & BODYSHAPE_DIGITIGRADE) && !(I.item_flags & IGNORE_DIGITIGRADE))
if(!(I.supports_variations_flags & (CLOTHING_DIGITIGRADE_VARIATION|CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON)))
if(!disable_warning)
to_chat(H, span_warning("The footwear around here isn't compatible with your feet!"))
@@ -942,7 +942,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_ICLOTHING)
var/obj/item/bodypart/chest = H.get_bodypart(BODY_ZONE_CHEST)
- if(chest && (chest.bodytype & BODYTYPE_MONKEY))
+ if(chest && (chest.bodyshape & BODYSHAPE_MONKEY))
if(!(I.supports_variations_flags & CLOTHING_MONKEY_VARIATION))
if(!disable_warning)
to_chat(H, span_warning("[I] doesn't fit your [chest.name]!"))
@@ -1201,13 +1201,15 @@ GLOBAL_LIST_EMPTY(features_by_species)
log_combat(user, target, "punched")
//If we rolled a punch high enough to hit our stun threshold, or our target is staggered and they have at least 40 damage+stamina loss, we knock them down
- if((target.stat != DEAD) && prob(limb_accuracy) || (target.stat != DEAD) && staggered && (target.getStaminaLoss() + user.getBruteLoss()) >= 40)
- target.visible_message(span_danger("[user] knocks [target] down!"), \
- span_userdanger("You're knocked down by [user]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_danger("You knock [target] down!"))
- var/knockdown_duration = 4 SECONDS + (target.getStaminaLoss() + (target.getBruteLoss()*0.5))*0.8 //50 total damage = 4 second base stun + 4 second stun modifier = 8 second knockdown duration
- target.apply_effect(knockdown_duration, EFFECT_KNOCKDOWN, armor_block)
- log_combat(user, target, "got a stun punch with their previous punch")
+ //This does not work against opponents who are knockdown immune, such as from wearing riot armor.
+ if(!HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
+ if((target.stat != DEAD) && prob(limb_accuracy) || (target.stat != DEAD) && staggered && (target.getStaminaLoss() + user.getBruteLoss()) >= 40)
+ target.visible_message(span_danger("[user] knocks [target] down!"), \
+ span_userdanger("You're knocked down by [user]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_danger("You knock [target] down!"))
+ var/knockdown_duration = 4 SECONDS + (target.getStaminaLoss() + (target.getBruteLoss()*0.5))*0.8 //50 total damage = 4 second base stun + 4 second stun modifier = 8 second knockdown duration
+ target.apply_effect(knockdown_duration, EFFECT_KNOCKDOWN, armor_block)
+ log_combat(user, target, "got a stun punch with their previous punch")
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(user.body_position != STANDING_UP)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 1860fd0dd0917..89ad4700aad71 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -459,30 +459,34 @@
return FALSE
if (target.stat == DEAD || HAS_TRAIT(target, TRAIT_FAKEDEATH))
- to_chat(src, span_warning("[target.name] is dead!"))
+ balloon_alert(src, "[target.p_they()] [target.p_are()] dead!")
return FALSE
if (is_mouth_covered())
- to_chat(src, span_warning("Remove your mask first!"))
+ balloon_alert(src, "remove your mask first!")
return FALSE
if (target.is_mouth_covered())
- to_chat(src, span_warning("Remove [p_their()] mask first!"))
+ balloon_alert(src, "remove [target.p_their()] mask first!")
return FALSE
- if (!get_organ_slot(ORGAN_SLOT_LUNGS))
- to_chat(src, span_warning("You have no lungs to breathe with, so you cannot perform CPR!"))
+ if(HAS_TRAIT_FROM(src, TRAIT_NOBREATH, DISEASE_TRAIT))
+ to_chat(src, span_warning("you can't breathe!"))
return FALSE
- if (HAS_TRAIT(src, TRAIT_NOBREATH))
- to_chat(src, span_warning("You do not breathe, so you cannot perform CPR!"))
+ var/obj/item/organ/internal/lungs/human_lungs = get_organ_slot(ORGAN_SLOT_LUNGS)
+ if(isnull(human_lungs))
+ balloon_alert(src, "you don't have lungs!")
+ return FALSE
+ if(human_lungs.organ_flags & ORGAN_FAILING)
+ balloon_alert(src, "your lungs are too damaged!")
return FALSE
visible_message(span_notice("[src] is trying to perform CPR on [target.name]!"), \
span_notice("You try to perform CPR on [target.name]... Hold still!"))
if (!do_after(src, delay = panicking ? CPR_PANIC_SPEED : (3 SECONDS), target = target))
- to_chat(src, span_warning("You fail to perform CPR on [target]!"))
+ balloon_alert(src, "you fail to perform CPR!")
return FALSE
if (target.health > target.crit_threshold)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index b976b2952d406..6e1f9037018b6 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -146,7 +146,7 @@
return
if(!(shove_flags & SHOVE_KNOCKDOWN_BLOCKED))
target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
- if(!HAS_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED))
+ if(!HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"),
span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 8d4b2d3110343..290fe4ce03b94 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -93,26 +93,26 @@ There are several things that need to be remembered:
var/mutable_appearance/uniform_overlay
//This is how non-humanoid clothing works. You check if the mob has the right bodyflag, and the clothing has the corresponding clothing flag.
- //handled_by_bodytype is used to track whether or not we successfully used an alternate sprite. It's set to TRUE to ease up on copy-paste.
+ //handled_by_bodyshape is used to track whether or not we successfully used an alternate sprite. It's set to TRUE to ease up on copy-paste.
//icon_file MUST be set to null by default, or it causes issues.
- //handled_by_bodytype MUST be set to FALSE under the if(!icon_exists()) statement, or everything breaks.
- //"override_file = handled_by_bodytype ? icon_file : null" MUST be added to the arguments of build_worn_icon()
+ //handled_by_bodyshape MUST be set to FALSE under the if(!icon_exists()) statement, or everything breaks.
+ //"override_file = handled_by_bodyshape ? icon_file : null" MUST be added to the arguments of build_worn_icon()
//Friendly reminder that icon_exists(file, state, scream = TRUE) is your friend when debugging this code.
- var/handled_by_bodytype = TRUE
+ var/handled_by_bodyshape = TRUE
var/icon_file
var/woman
//BEGIN SPECIES HANDLING
- if((bodytype & BODYTYPE_MONKEY) && (uniform.supports_variations_flags & CLOTHING_MONKEY_VARIATION))
+ if((bodyshape & BODYSHAPE_MONKEY) && (uniform.supports_variations_flags & CLOTHING_MONKEY_VARIATION))
icon_file = MONKEY_UNIFORM_FILE
- else if((bodytype & BODYTYPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION))
+ else if((bodyshape & BODYSHAPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION))
icon_file = DIGITIGRADE_UNIFORM_FILE
//Female sprites have lower priority than digitigrade sprites
- else if(dna.species.sexes && (bodytype & BODYTYPE_HUMANOID) && physique == FEMALE && !(uniform.female_sprite_flags & NO_FEMALE_UNIFORM)) //Agggggggghhhhh
+ else if(dna.species.sexes && (bodyshape & BODYSHAPE_HUMANOID) && physique == FEMALE && !(uniform.female_sprite_flags & NO_FEMALE_UNIFORM)) //Agggggggghhhhh
woman = TRUE
if(!icon_exists(icon_file, RESOLVE_ICON_STATE(uniform)))
icon_file = DEFAULT_UNIFORM_FILE
- handled_by_bodytype = FALSE
+ handled_by_bodyshape = FALSE
//END SPECIES HANDLING
uniform_overlay = uniform.build_worn_icon(
@@ -121,7 +121,7 @@ There are several things that need to be remembered:
isinhands = FALSE,
female_uniform = woman ? uniform.female_sprite_flags : null,
override_state = target_overlay,
- override_file = handled_by_bodytype ? icon_file : null,
+ override_file = handled_by_bodyshape ? icon_file : null,
)
var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST)
diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm
index d11e4f5208bc7..7a2e7bb74747d 100644
--- a/code/modules/mob/living/carbon/human/monkey.dm
+++ b/code/modules/mob/living/carbon/human/monkey.dm
@@ -2,7 +2,6 @@
icon_state = "monkey" //for mapping
race = /datum/species/monkey
ai_controller = /datum/ai_controller/monkey
- faction = list(FACTION_NEUTRAL, FACTION_MONKEY)
/mob/living/carbon/human/species/monkey/Initialize(mapload, cubespawned = FALSE, mob/spawner)
if (cubespawned)
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index b67f3efb3ef17..4de716fe953d0 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -22,6 +22,7 @@
)
no_equip_flags = ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_SUITSTORE
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | ERT_SPAWN | SLIME_EXTRACT
+ inherent_factions = list(FACTION_MONKEY)
sexes = FALSE
species_language_holder = /datum/language_holder/monkey
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index 7f58c2e911d9c..286a3b30ed843 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -61,7 +61,7 @@
update_damage_overlays()
damage_dealt = actual_hit.get_damage() - delta // Unfortunately bodypart receive_damage doesn't return damage dealt so we do it manually
else
- damage_dealt = adjustBruteLoss(damage_amount, forced = forced)
+ damage_dealt = -1 * adjustBruteLoss(damage_amount, forced = forced)
if(BURN)
if(isbodypart(def_zone))
var/obj/item/bodypart/actual_hit = def_zone
@@ -77,17 +77,17 @@
damage_source = attacking_item,
))
update_damage_overlays()
- damage_dealt = delta - actual_hit.get_damage() // See above
+ damage_dealt = actual_hit.get_damage() - delta // See above
else
- damage_dealt = adjustFireLoss(damage_amount, forced = forced)
+ damage_dealt = -1 * adjustFireLoss(damage_amount, forced = forced)
if(TOX)
- damage_dealt = adjustToxLoss(damage_amount, forced = forced)
+ damage_dealt = -1 * adjustToxLoss(damage_amount, forced = forced)
if(OXY)
- damage_dealt = adjustOxyLoss(damage_amount, forced = forced)
+ damage_dealt = -1 * adjustOxyLoss(damage_amount, forced = forced)
if(STAMINA)
- damage_dealt = adjustStaminaLoss(damage_amount, forced = forced)
+ damage_dealt = -1 * adjustStaminaLoss(damage_amount, forced = forced)
if(BRAIN)
- damage_dealt = adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_amount)
+ damage_dealt = -1 * adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_amount)
SEND_SIGNAL(src, COMSIG_MOB_AFTER_APPLY_DAMAGE, damage_dealt, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, attacking_item)
return damage_dealt
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 6c6be7eeb21ee..f0f3516ae7e0e 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -744,7 +744,7 @@
. |= SHOVE_CAN_MOVE
if(!buckled)
. |= SHOVE_CAN_HIT_SOMETHING
- if(HAS_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED))
+ if(HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
. |= SHOVE_KNOCKDOWN_BLOCKED
///Send the chat feedback message for shoving
diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm
index c4cc256244c3f..d963fc77be62f 100644
--- a/code/modules/mob/living/silicon/ai/robot_control.dm
+++ b/code/modules/mob/living/silicon/ai/robot_control.dm
@@ -14,7 +14,7 @@
return FALSE
return TRUE
-/datum/robot_control/ui_status(mob/user)
+/datum/robot_control/ui_status(mob/user, datum/ui_state/state)
if(is_interactable(user))
return ..()
return UI_CLOSE
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index e17ba69550d34..3cd9d45f5c107 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -75,7 +75,7 @@
TRAIT_MADNESS_IMMUNE,
TRAIT_MARTIAL_ARTS_IMMUNE,
TRAIT_NOFIRE_SPREAD,
- TRAIT_SHOVE_KNOCKDOWN_BLOCKED,
+ TRAIT_BRAWLING_KNOCKDOWN_BLOCKED,
)
add_traits(traits_to_apply, ROUNDSTART_TRAIT)
diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm
index 320276cd00d44..240c0897b33a1 100644
--- a/code/modules/mod/mod_paint.dm
+++ b/code/modules/mod/mod_paint.dm
@@ -68,7 +68,7 @@
QDEL_NULL(proxy_view)
current_color = COLOR_MATRIX_IDENTITY
-/obj/item/mod/paint/ui_status(mob/user)
+/obj/item/mod/paint/ui_status(mob/user, datum/ui_state/state)
if(check_menu(editing_mod, user))
return ..()
return UI_CLOSE
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index 4d8f8b4c6b703..81ef6ac0be17a 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -698,7 +698,7 @@
a few years out of date, leading to an overall lower capacity for modules."
default_skin = "security"
armor_type = /datum/armor/mod_theme_security
- complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 2
slowdown_inactive = 1
slowdown_active = 0.5
allowed_suit_storage = list(
@@ -1575,7 +1575,9 @@
anti-corrosion coated suit for high-ranking CentCom Officers, deploying pristine protective armor and \
advanced actuators, feeling practically weightless when turned on. Scraping the paint of this suit is \
counted as a war-crime and reason for immediate execution in over fifty Nanotrasen space stations. \
- The resemblance to a Gorlex Marauder helmet is purely coincidental."
+ The resemblance to a Gorlex Marauder helmet is purely coincidental. This is the newest V2 revision, which has \
+ reflective reinforced-plasmaglass shielding weaved with advanced kevlar fibers. Sources say that some of the armor \
+ is ripped straight from an Apocryphal MODsuit."
default_skin = "corporate"
armor_type = /datum/armor/mod_theme_corporate
resistance_flags = FIRE_PROOF|ACID_PROOF
@@ -1618,11 +1620,11 @@
)
/datum/armor/mod_theme_corporate
- melee = 50
- bullet = 40
- laser = 50
+ melee = 65
+ bullet = 65
+ laser = 55
energy = 50
- bomb = 50
+ bomb = 60
bio = 100
fire = 100
acid = 100
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index 0933c7e8f185d..0659dd6208ba6 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -55,6 +55,7 @@
/obj/item/mod/module/flashlight,
/obj/item/mod/module/tether,
/obj/item/mod/module/magboot,
+ /obj/item/mod/module/headprotector,
)
default_pins = list(
/obj/item/mod/module/magboot,
@@ -72,6 +73,7 @@
/obj/item/mod/module/magboot,
/obj/item/mod/module/t_ray,
/obj/item/mod/module/quick_carry,
+ /obj/item/mod/module/headprotector,
)
default_pins = list(
/obj/item/mod/module/magboot,
@@ -87,6 +89,7 @@
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/jetpack,
+ /obj/item/mod/module/headprotector,
)
default_pins = list(
/obj/item/mod/module/magboot/advanced,
@@ -153,6 +156,7 @@
/obj/item/mod/module/flashlight,
/obj/item/mod/module/circuit,
/obj/item/mod/module/t_ray,
+ /obj/item/mod/module/headprotector,
)
/obj/item/mod/control/pre_equipped/security
@@ -165,6 +169,7 @@
/obj/item/mod/module/criminalcapture,
/obj/item/mod/module/dispenser/mirage,
/obj/item/mod/module/quick_cuff,
+ /obj/item/mod/module/headprotector,
)
/obj/item/mod/control/pre_equipped/safeguard
@@ -179,6 +184,7 @@
/obj/item/mod/module/projectile_dampener,
/obj/item/mod/module/pepper_shoulders,
/obj/item/mod/module/quick_cuff,
+ /obj/item/mod/module/headprotector,
)
default_pins = list(
/obj/item/mod/module/jetpack,
@@ -194,6 +200,7 @@
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/pathfinder,
/obj/item/mod/module/quick_cuff,
+ /obj/item/mod/module/headprotector,
)
default_pins = list(
/obj/item/mod/module/jetpack/advanced,
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index cf72f62d6c4a5..a35e39365b948 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -11,7 +11,7 @@
module_type = MODULE_TOGGLE
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding)
+ incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding, /obj/item/mod/module/headprotector)
cooldown_time = 0.5 SECONDS
overlay_state_inactive = "module_armorbooster_off"
overlay_state_active = "module_armorbooster_on"
@@ -26,6 +26,8 @@
var/list/armor_mod = /datum/armor/mod_module_armor_boost
/// List of parts of the suit that are spaceproofed, for giving them back the pressure protection.
var/list/spaceproofed = list()
+ /// List of traits added when the mod is activated
+ var/list/traits_to_add = list(TRAIT_HEAD_INJURY_BLOCKED)
/datum/armor/mod_module_armor_boost
melee = 25
@@ -50,6 +52,7 @@
actual_speed_added = max(0, min(mod.slowdown_active, speed_added))
mod.slowdown -= actual_speed_added
mod.wearer.update_equipment_speed_mods()
+ mod.wearer.add_traits(traits_to_add, MOD_TRAIT)
var/list/parts = mod.mod_parts + mod
for(var/obj/item/part as anything in parts)
part.set_armor(part.get_armor().add_other_armor(armor_mod))
@@ -69,6 +72,7 @@
balloon_alert(mod.wearer, "armor retracts, EVA ready")
mod.slowdown += actual_speed_added
mod.wearer.update_equipment_speed_mods()
+ mod.wearer.remove_traits(traits_to_add, MOD_TRAIT)
var/list/parts = mod.mod_parts + mod
for(var/obj/item/part as anything in parts)
part.set_armor(part.get_armor().subtract_other_armor(armor_mod))
@@ -483,12 +487,15 @@
/obj/item/mod/module/infiltrator
name = "MOD infiltration core programs module"
desc = "The primary stealth systems operating within the suit. Utilizing electromagnetic signals, \
- the wearer simply cannot be observed closely, or heard clearly by those around them."
+ the wearer simply cannot be observed closely, or heard clearly by those around them.\
+ It also contains some dampening systems to help protect a user from blows to the head."
icon_state = "infiltrator"
complexity = 0
removable = FALSE
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0
- incompatible_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/armor_booster, /obj/item/mod/module/welding)
+ incompatible_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/armor_booster, /obj/item/mod/module/welding, /obj/item/mod/module/headprotector)
+ /// List of traits added when the suit is activated
+ var/list/traits_to_add = list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN, TRAIT_HEAD_INJURY_BLOCKED)
/obj/item/mod/module/infiltrator/on_install()
mod.item_flags |= EXAMINE_SKIP
@@ -497,11 +504,11 @@
mod.item_flags &= ~EXAMINE_SKIP
/obj/item/mod/module/infiltrator/on_suit_activation()
- mod.wearer.add_traits(list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN), MOD_TRAIT)
+ mod.wearer.add_traits(traits_to_add, MOD_TRAIT)
mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
/obj/item/mod/module/infiltrator/on_suit_deactivation(deleting = FALSE)
- mod.wearer.remove_traits(list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN), MOD_TRAIT)
+ mod.wearer.remove_traits(traits_to_add, MOD_TRAIT)
if(deleting)
return
mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
index 9095bbd2c2467..fa746048e8201 100644
--- a/code/modules/mod/modules/modules_engineering.dm
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -204,6 +204,24 @@
rcd_scan(src, fade_time = 10 SECONDS)
drain_power(use_power_cost)
+///Safety-First Head Protection - Protects your brain matter from sudden impacts.
+/obj/item/mod/module/headprotector
+ name = "MOD Safety-First Head Protection module"
+ desc = "A series of dampening plates are installed along the back and upper areas of \
+ the helmet. These plates absorb abrupt kinetic shocks delivered to the skull. \
+ The bulk of this module prevents it from being installed in any suit that is capable \
+ of combat armor adjustments. However, the rudimentry nature of the module makes it \
+ relatively easy to install into most other suits."
+ icon_state = "welding"
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/infiltrator)
+
+/obj/item/mod/module/constructor/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT)
+
+/obj/item/mod/module/constructor/on_suit_deactivation(deleting = FALSE)
+ REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT)
+
///Mister - Sprays water over an area.
/obj/item/mod/module/mister
name = "MOD water mister module"
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
index 0b8c0299f04b9..f5033cea4f67e 100644
--- a/code/modules/mod/modules/modules_security.dm
+++ b/code/modules/mod/modules/modules_security.dm
@@ -587,10 +587,10 @@
incompatible_modules = list(/obj/item/mod/module/shove_blocker)
/obj/item/mod/module/shove_blocker/on_suit_activation()
- mod.wearer.add_traits(list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
+ mod.wearer.add_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
/obj/item/mod/module/shove_blocker/on_suit_deactivation(deleting = FALSE)
- mod.wearer.remove_traits(list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
+ mod.wearer.remove_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
/obj/item/mod/module/shove_blocker/locked
name = "superglued MOD bulwark module"
diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm
index 38e64d6742c64..674520f1fb211 100644
--- a/code/modules/modular_computers/computers/item/pda.dm
+++ b/code/modules/modular_computers/computers/item/pda.dm
@@ -13,7 +13,7 @@
steel_sheet_cost = 2
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 3, /datum/material/glass=SMALL_MATERIAL_AMOUNT, /datum/material/plastic=SMALL_MATERIAL_AMOUNT)
- interaction_flags_atom = INTERACT_ATOM_ALLOW_USER_LOCATION | INTERACT_ATOM_IGNORE_MOBILITY
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_ALLOW_USER_LOCATION | INTERACT_ATOM_IGNORE_MOBILITY
icon_state_menu = "menu"
max_capacity = 64
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 2b3b8f6ea8c95..8eb3190afa589 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -88,7 +88,7 @@
cam_screen.display_to(user)
user.client.register_map_obj(cam_background)
-/datum/computer_file/program/secureye/ui_status(mob/user)
+/datum/computer_file/program/secureye/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(. == UI_DISABLED)
return UI_CLOSE
diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm
index da3bfe4e0ce14..c1a9c5a88ba04 100644
--- a/code/modules/pai/card.dm
+++ b/code/modules/pai/card.dm
@@ -92,7 +92,7 @@
ui = new(user, src, "PaiCard")
ui.open()
-/obj/item/pai_card/ui_status(mob/user)
+/obj/item/pai_card/ui_status(mob/user, datum/ui_state/state)
if(user in get_nested_locs(src))
return UI_INTERACTIVE
return ..()
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 1c1daaad51140..5aac6d93bc3a3 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -333,7 +333,7 @@
return
. += span_warning("You cannot read it!")
-/obj/item/paper/ui_status(mob/user,/datum/ui_state/state)
+/obj/item/paper/ui_status(mob/user, datum/ui_state/state)
// Are we on fire? Hard to read if so
if(resistance_flags & ON_FIRE)
return UI_CLOSE
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index a6cb83aafc0e1..e5a30474f8721 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -598,8 +598,7 @@ GLOBAL_LIST_INIT(paper_blanks, init_paper_blanks())
toner_cartridge.charges = 0
/obj/machinery/photocopier/MouseDrop_T(mob/target, mob/user)
- check_ass() //Just to make sure that you can re-drag somebody onto it after they moved off.
- if(!istype(target) || target.anchored || target.buckled || !Adjacent(target) || !user.can_perform_action(src) || target == ass || copier_blocked())
+ if(!istype(target) || target.anchored || target.buckled || !Adjacent(target) || !user.can_perform_action(src, action_bitflags = ALLOW_RESTING) || target == ass || copier_blocked())
return
add_fingerprint(user)
if(target == user)
diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm
index c36d9c108e28c..ae120a6d3d70a 100644
--- a/code/modules/power/apc/apc_main.dm
+++ b/code/modules/power/apc/apc_main.dm
@@ -393,7 +393,7 @@
update_appearance()
remote_control_user = null
-/obj/machinery/power/apc/ui_status(mob/user)
+/obj/machinery/power/apc/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(!QDELETED(remote_control_user) && user == remote_control_user)
. = UI_INTERACTIVE
diff --git a/code/modules/reagents/chemistry/holder/ui_data.dm b/code/modules/reagents/chemistry/holder/ui_data.dm
index bc8b3d6c713f6..39ceaaa06f002 100644
--- a/code/modules/reagents/chemistry/holder/ui_data.dm
+++ b/code/modules/reagents/chemistry/holder/ui_data.dm
@@ -9,7 +9,7 @@
ui.open()
-/datum/reagents/ui_status(mob/user)
+/datum/reagents/ui_status(mob/user, datum/ui_state/state)
return UI_INTERACTIVE //please advise
/datum/reagents/ui_state(mob/user)
diff --git a/code/modules/reagents/chemistry/items.dm b/code/modules/reagents/chemistry/items.dm
index 37d089aab4231..25f805acdaf1b 100644
--- a/code/modules/reagents/chemistry/items.dm
+++ b/code/modules/reagents/chemistry/items.dm
@@ -315,7 +315,7 @@
. = ..()
INVOKE_ASYNC(src, PROC_REF(remove_thermometer), user)
-/obj/item/thermometer/ui_status(mob/user)
+/obj/item/thermometer/ui_status(mob/user, datum/ui_state/state)
if(!(in_range(src, user)))
return UI_CLOSE
return UI_INTERACTIVE
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 25751c5903319..188fbfe622ec9 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2409,7 +2409,8 @@
/datum/reagent/magillitis/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
if((ishuman(affected_mob)) && current_cycle > 10)
- affected_mob.gorillize()
+ var/mob/living/basic/gorilla/new_gorilla = affected_mob.gorillize()
+ new_gorilla.AddComponent(/datum/component/regenerator, regeneration_delay = 12 SECONDS, brute_per_second = 1.5, outline_colour = COLOR_PALE_GREEN)
/datum/reagent/growthserum
name = "Growth Serum"
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index 83754dd571f3e..9deb658250856 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -82,7 +82,7 @@
var/obj/item/bodypart/affecting = user.zone_selected //Find what the player is aiming at
var/armor_block = 0 //Get the target's armor values for normal attack damage.
- var/armor_duration = 0 //The more force the bottle has, the longer the duration.
+ var/knockdown_effectiveness = 0 //The more force the bottle has, the longer the duration.
//Calculating duration and calculating damage.
if(ishuman(target))
@@ -95,23 +95,23 @@
if(istype(H.head, /obj/item/clothing/head) && affecting == BODY_ZONE_HEAD)
headarmor = H.head.get_armor_rating(MELEE)
//Calculate the knockdown duration for the target.
- armor_duration = (bottle_knockdown_duration - headarmor) + force
+ knockdown_effectiveness = (bottle_knockdown_duration - headarmor) + force
else
//Only humans can have armor, right?
armor_block = living_target.run_armor_check(affecting, MELEE)
if(affecting == BODY_ZONE_HEAD)
- armor_duration = bottle_knockdown_duration + force
+ knockdown_effectiveness = bottle_knockdown_duration + force
//Apply the damage!
armor_block = min(90,armor_block)
living_target.apply_damage(force, BRUTE, affecting, armor_block)
// You are going to knock someone down for longer if they are not wearing a helmet.
var/head_attack_message = ""
- if(affecting == BODY_ZONE_HEAD && iscarbon(target))
+ if(affecting == BODY_ZONE_HEAD && iscarbon(target) && !HAS_TRAIT(target, TRAIT_HEAD_INJURY_BLOCKED))
head_attack_message = " on the head"
- if(armor_duration)
- living_target.apply_effect(min(armor_duration, 200) , EFFECT_KNOCKDOWN)
+ if(knockdown_effectiveness && prob(knockdown_effectiveness))
+ living_target.apply_effect(min(knockdown_effectiveness, 200) , EFFECT_KNOCKDOWN)
//Display an attack message.
if(target != user)
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 9f1486c3d6bdf..78ffff197bce8 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -326,6 +326,7 @@
name = "disposal unit"
desc = "A pneumatic waste disposal unit."
icon_state = "disposal"
+ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
// attack by item places it in to disposal
/obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params)
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 08c31feb06e87..0081fe052eca7 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -2049,6 +2049,17 @@
category = list(
RND_CATEGORY_MODSUIT_MODULES + RND_SUBCATEGORY_MODSUIT_MODULES_ENGINEERING
)
+/datum/design/module/mod_head_protection
+ name = "Safety-First Head Protection Module"
+ id = "mod_safety"
+ materials = list(
+ /datum/material/iron =SMALL_MATERIAL_AMOUNT*5,
+ /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/mod/module/headprotector
+ category = list(
+ RND_CATEGORY_MODSUIT_MODULES + RND_SUBCATEGORY_MODSUIT_MODULES_ENGINEERING
+ )
/datum/design/module/mod_t_ray
name = "T-Ray Scanner Module"
id = "mod_t_ray"
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 4a02e368a8e06..acf17851eb34a 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -209,6 +209,7 @@
"mod_plating_standard",
"mod_storage",
"mod_welding",
+ "mod_safety",
"mod_mouthhole",
"mod_flashlight",
"mod_longfall",
diff --git a/code/modules/research/xenobiology/crossbreeding/_misc.dm b/code/modules/research/xenobiology/crossbreeding/_misc.dm
index 8eb166bf8207f..7b85a878ec39b 100644
--- a/code/modules/research/xenobiology/crossbreeding/_misc.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_misc.dm
@@ -175,6 +175,12 @@ Slimecrossing Items
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "capturedevice"
+ ///traits we give and remove from the mob on exit and entry
+ var/static/list/traits_on_transfer = list(
+ TRAIT_IMMOBILIZED,
+ TRAIT_HANDS_BLOCKED,
+ TRAIT_AI_PAUSED,
+ )
/obj/item/capturedevice/attack(mob/living/pokemon, mob/user)
if(length(contents))
@@ -211,11 +217,11 @@ Slimecrossing Items
/obj/item/capturedevice/proc/store(mob/living/pokemon)
pokemon.forceMove(src)
- pokemon.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), ABSTRACT_ITEM_TRAIT)
+ pokemon.add_traits(traits_on_transfer, ABSTRACT_ITEM_TRAIT)
pokemon.cancel_camera()
/obj/item/capturedevice/proc/release()
for(var/mob/living/pokemon in contents)
pokemon.forceMove(get_turf(loc))
- pokemon.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), ABSTRACT_ITEM_TRAIT)
+ pokemon.remove_traits(traits_on_transfer, ABSTRACT_ITEM_TRAIT)
pokemon.cancel_camera()
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index 210a1d7dc9d9b..e4b2eaa6c2829 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -52,7 +52,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
data["bsa_unlock"] = GLOB.bsa_unlock
return data
-/obj/machinery/keycard_auth/ui_status(mob/user)
+/obj/machinery/keycard_auth/ui_status(mob/user, datum/ui_state/state)
if(isdrone(user))
return UI_CLOSE
if(!isanimal_or_basicmob(user))
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index e5b8d5e87ff8a..d92402859911a 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -378,15 +378,13 @@
var/change = FALSE
if(payees[AM] > 0)
change = TRUE
- var/obj/item/holochip/HC = new /obj/item/holochip(AM.loc) //Change is made in holocredits exclusively.
- HC.credits = payees[AM]
- HC.name = "[HC.credits] credit holochip"
+ var/obj/item/holochip/holocred = new /obj/item/holochip(AM.loc, payees[AM]) //Change is made in holocredits exclusively.
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
- if(!H.put_in_hands(HC))
- AM.pulling = HC
+ if(!H.put_in_hands(holocred))
+ AM.pulling = holocred
else
- AM.pulling = HC
+ AM.pulling = holocred
payees[AM] -= payees[AM]
say("Welcome to first class, [driver_holdout ? "[driver_holdout]" : "[AM]" ]![change ? " Here is your change." : ""]")
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index c81b691980ad9..58e91646c0fee 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -34,8 +34,10 @@
* Set to BIO_STANDARD_UNJOINTED because most species have both flesh bone and blood in their limbs.
*/
var/biological_state = BIO_STANDARD_UNJOINTED
- ///A bitfield of bodytypes for clothing, surgery, and misc information
- var/bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC
+ ///A bitfield of bodytypes for surgery, and misc information
+ var/bodytype = BODYTYPE_ORGANIC
+ ///A bitfield of bodyshapes for clothing and other sprite information
+ var/bodyshape = BODYSHAPE_HUMANOID
///Defines when a bodypart should not be changed. Example: BP_BLOCK_CHANGE_SPECIES prevents the limb from being overwritten on species gain
var/change_exempt_flags = NONE
///Random flags that describe this bodypart
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index fcbde3c76712b..03f4cc98e1401 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -270,7 +270,7 @@
return FALSE
var/obj/item/bodypart/chest/mob_chest = new_limb_owner.get_bodypart(BODY_ZONE_CHEST)
- if(mob_chest && !(mob_chest.acceptable_bodytype & bodytype) && !special)
+ if(mob_chest && !(mob_chest.acceptable_bodytype & bodytype) && !(mob_chest.acceptable_bodyshape & bodyshape) && !special)
return FALSE
return TRUE
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 0373401d9047c..f1ea0c06645b4 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -212,7 +212,7 @@
husk_type = "monkey"
icon_state = "default_monkey_head"
limb_id = SPECIES_MONKEY
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_MONKEY
should_draw_greyscale = FALSE
dmg_overlay_type = SPECIES_MONKEY
is_dimorphic = FALSE
@@ -229,7 +229,8 @@
px_y = 0
bodypart_flags = BODYPART_UNREMOVABLE
max_damage = LIMB_MAX_HP_ALIEN_CORE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
/obj/item/bodypart/head/larva
icon = 'icons/mob/human/species/alien/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index 52671b5fbc16c..126bd3db33a4f 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -179,6 +179,16 @@
bodytype = all_limb_flags
+/// Makes sure that the owner's bodyshape flags match the flags of all of it's parts and organs
+/mob/living/carbon/proc/synchronize_bodyshapes()
+ var/all_limb_flags = NONE
+ for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/organ/external/ext_organ in limb)
+ all_limb_flags |= ext_organ.external_bodyshapes
+ all_limb_flags |= limb.bodyshape
+
+ bodyshape = all_limb_flags
+
/proc/skintone2hex(skin_tone)
. = 0
switch(skin_tone)
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index be1a7828c2e99..776207a2ccee9 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -13,8 +13,10 @@
grind_results = null
wound_resistance = 10
bodypart_trait_source = CHEST_TRAIT
+ ///The bodyshape(s) allowed to attach to this chest.
+ var/acceptable_bodyshape = BODYSHAPE_HUMANOID
///The bodytype(s) allowed to attach to this chest.
- var/acceptable_bodytype = BODYTYPE_HUMANOID
+ var/acceptable_bodytype = ALL
var/obj/item/cavity_item
@@ -78,8 +80,8 @@
should_draw_greyscale = FALSE
is_dimorphic = FALSE
wound_resistance = -10
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
- acceptable_bodytype = BODYTYPE_MONKEY
+ bodyshape = BODYSHAPE_MONKEY
+ acceptable_bodyshape = BODYSHAPE_MONKEY
dmg_overlay_type = SPECIES_MONKEY
/obj/item/bodypart/chest/alien
@@ -87,12 +89,13 @@
icon_static = 'icons/mob/human/species/alien/bodyparts.dmi'
icon_state = "alien_chest"
limb_id = BODYPART_ID_ALIEN
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
is_dimorphic = FALSE
should_draw_greyscale = FALSE
bodypart_flags = BODYPART_UNREMOVABLE
max_damage = LIMB_MAX_HP_ALIEN_CORE
- acceptable_bodytype = BODYTYPE_HUMANOID
+ acceptable_bodyshape = BODYSHAPE_HUMANOID
wing_types = NONE
/obj/item/bodypart/chest/larva
@@ -245,7 +248,7 @@
icon_state = "default_monkey_l_arm"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_MONKEY
wound_resistance = -10
px_x = -5
px_y = -3
@@ -260,7 +263,8 @@
icon_static = 'icons/mob/human/species/alien/bodyparts.dmi'
icon_state = "alien_l_arm"
limb_id = BODYPART_ID_ALIEN
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
px_x = 0
px_y = 0
bodypart_flags = BODYPART_UNREMOVABLE
@@ -341,7 +345,7 @@
husk_type = "monkey"
icon_state = "default_monkey_r_arm"
limb_id = SPECIES_MONKEY
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_MONKEY
should_draw_greyscale = FALSE
wound_resistance = -10
px_x = 5
@@ -357,7 +361,8 @@
icon_static = 'icons/mob/human/species/alien/bodyparts.dmi'
icon_state = "alien_r_arm"
limb_id = BODYPART_ID_ALIEN
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
px_x = 0
px_y = 0
bodypart_flags = BODYPART_UNREMOVABLE
@@ -457,7 +462,7 @@
icon_state = "default_monkey_l_leg"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_MONKEY
wound_resistance = -10
px_y = 4
dmg_overlay_type = SPECIES_MONKEY
@@ -471,7 +476,8 @@
icon_static = 'icons/mob/human/species/alien/bodyparts.dmi'
icon_state = "alien_l_leg"
limb_id = BODYPART_ID_ALIEN
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
px_x = 0
px_y = 0
bodypart_flags = BODYPART_UNREMOVABLE
@@ -547,7 +553,7 @@
icon_state = "default_monkey_r_leg"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_MONKEY | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_MONKEY
wound_resistance = -10
px_y = 4
dmg_overlay_type = SPECIES_MONKEY
@@ -561,7 +567,8 @@
icon_static = 'icons/mob/human/species/alien/bodyparts.dmi'
icon_state = "alien_r_leg"
limb_id = BODYPART_ID_ALIEN
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodytype = BODYTYPE_ALIEN | BODYTYPE_ORGANIC
+ bodyshape = BODYSHAPE_HUMANOID
px_x = 0
px_y = 0
bodypart_flags = BODYPART_UNREMOVABLE
diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm
index 3f5e6972ed6b5..ca4b61228da42 100644
--- a/code/modules/surgery/bodyparts/robot_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm
@@ -23,7 +23,8 @@
icon_state = "borg_l_arm"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
@@ -56,7 +57,8 @@
icon_state = "borg_r_arm"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
@@ -90,7 +92,8 @@
icon_state = "borg_l_leg"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
@@ -137,7 +140,8 @@
icon_state = "borg_r_leg"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
@@ -183,7 +187,8 @@
icon_state = "borg_chest"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
@@ -357,7 +362,8 @@
icon_state = "borg_head"
is_dimorphic = FALSE
should_draw_greyscale = FALSE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ROBOTIC
+ bodytype = BODYTYPE_ROBOTIC
+ bodyshape = BODYSHAPE_HUMANOID
change_exempt_flags = BP_BLOCK_CHANGE_SPECIES
dmg_overlay_type = "robotic"
diff --git a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
index 24178d9bd1610..8dccd4b4a2d5b 100644
--- a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
@@ -45,7 +45,7 @@
/obj/item/bodypart/leg/left/digitigrade
icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi'
limb_id = BODYPART_ID_DIGITIGRADE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC | BODYTYPE_DIGITIGRADE
+ bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE
/obj/item/bodypart/leg/left/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE)
. = ..()
@@ -71,7 +71,7 @@
/obj/item/bodypart/leg/right/digitigrade
icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi'
limb_id = BODYPART_ID_DIGITIGRADE
- bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC | BODYTYPE_DIGITIGRADE
+ bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE
/obj/item/bodypart/leg/right/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE)
. = ..()
diff --git a/code/modules/surgery/organs/external/_external_organ.dm b/code/modules/surgery/organs/external/_external_organ.dm
index 540c090d2e430..b58f08fe97d7e 100644
--- a/code/modules/surgery/organs/external/_external_organ.dm
+++ b/code/modules/surgery/organs/external/_external_organ.dm
@@ -25,6 +25,8 @@
var/use_mob_sprite_as_obj_sprite = FALSE
///Does this organ have any bodytypes to pass to it's bodypart_owner?
var/external_bodytypes = NONE
+ ///Does this organ have any bodyshapes to pass to it's bodypart_owner?
+ var/external_bodyshapes = NONE
///Which flags does a 'modification tool' need to have to restyle us, if it all possible (located in code/_DEFINES/mobs)
var/restyle_flags = NONE
@@ -80,12 +82,15 @@
if(external_bodytypes)
receiver.synchronize_bodytypes()
+ if(external_bodyshapes)
+ receiver.synchronize_bodyshapes()
receiver.update_body_parts()
/obj/item/organ/external/mob_remove(mob/living/carbon/organ_owner, special, moving)
if(!special)
organ_owner.synchronize_bodytypes()
+ organ_owner.synchronize_bodyshapes()
organ_owner.update_body_parts()
return ..()
@@ -216,7 +221,7 @@
slot = ORGAN_SLOT_EXTERNAL_SNOUT
preference = "feature_lizard_snout"
- external_bodytypes = BODYTYPE_SNOUTED
+ external_bodyshapes = BODYSHAPE_SNOUTED
dna_block = DNA_SNOUT_BLOCK
restyle_flags = EXTERNAL_RESTYLE_FLESH
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index f066b308e7b0c..c174d12260806 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -446,7 +446,7 @@
/obj/item/organ/internal/eyes/robotic/glow/ui_state(mob/user)
return GLOB.default_state
-/obj/item/organ/internal/eyes/robotic/glow/ui_status(mob/user)
+/obj/item/organ/internal/eyes/robotic/glow/ui_status(mob/user, datum/ui_state/state)
if(!QDELETED(owner))
if(owner == user)
return min(
diff --git a/code/modules/surgery/prosthetic_replacement.dm b/code/modules/surgery/prosthetic_replacement.dm
index 4b285d161bb06..818c7a788d953 100644
--- a/code/modules/surgery/prosthetic_replacement.dm
+++ b/code/modules/surgery/prosthetic_replacement.dm
@@ -55,7 +55,7 @@
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
var/obj/item/bodypart/chest/target_chest = human_target.get_bodypart(BODY_ZONE_CHEST)
- if(!(bodypart_to_attach.bodytype & target_chest.acceptable_bodytype))
+ if((!(bodypart_to_attach.bodyshape & target_chest.acceptable_bodyshape)) && (!(bodypart_to_attach.bodytype & target_chest.acceptable_bodytype)))
to_chat(user, span_warning("[bodypart_to_attach] doesn't match the patient's morphology."))
return SURGERY_STEP_FAIL
if(bodypart_to_attach.check_for_frankenstein(target))
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index 16fa83445c508..92fa3b5b5c190 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -71,10 +71,9 @@
return UI_UPDATE
return UI_INTERACTIVE
-/mob/living/shared_ui_interaction(src_object)
+/mob/living/shared_ui_interaction(atom/src_object)
. = ..()
- var/obj/item/object = src_object
- if(!(mobility_flags & MOBILITY_UI) && !(object.interaction_flags_atom & INTERACT_ATOM_IGNORE_MOBILITY) && . == UI_INTERACTIVE)
+ if(!(mobility_flags & MOBILITY_UI) && !(src_object.interaction_flags_atom & INTERACT_ATOM_IGNORE_MOBILITY) && . == UI_INTERACTIVE)
return UI_UPDATE
/mob/living/silicon/ai/shared_ui_interaction(src_object)
diff --git a/code/modules/transport/elevator/elev_panel.dm b/code/modules/transport/elevator/elev_panel.dm
index 3e9e0e073c19f..24b6e0fa3175c 100644
--- a/code/modules/transport/elevator/elev_panel.dm
+++ b/code/modules/transport/elevator/elev_panel.dm
@@ -254,7 +254,7 @@
ui = new(user, src, "ElevatorPanel", name)
ui.open()
-/obj/machinery/elevator_control_panel/ui_status(mob/user)
+/obj/machinery/elevator_control_panel/ui_status(mob/user, datum/ui_state/state)
// We moved up a z-level, probably via the elevator itself, so don't preserve the UI.
if(user.z != z)
return UI_CLOSE
diff --git a/code/modules/transport/tram/tram_controls.dm b/code/modules/transport/tram/tram_controls.dm
index db8fe767155d2..435b47f9d1bfd 100644
--- a/code/modules/transport/tram/tram_controls.dm
+++ b/code/modules/transport/tram/tram_controls.dm
@@ -83,7 +83,7 @@
/obj/machinery/computer/tram_controls/ui_state(mob/user)
return GLOB.not_incapacitated_state
-/obj/machinery/computer/tram_controls/ui_status(mob/user,/datum/tgui/ui)
+/obj/machinery/computer/tram_controls/ui_status(mob/user, datum/tgui/ui)
var/datum/transport_controller/linear/tram/tram = transport_ref?.resolve()
if(tram?.controller_active)
diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm
index 683cba3b2fd5f..842f5f1f7ebe6 100644
--- a/code/modules/unit_tests/outfit_sanity.dm
+++ b/code/modules/unit_tests/outfit_sanity.dm
@@ -5,7 +5,9 @@
if (!outfit_item) { \
TEST_FAIL("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \
} \
- outfit_item.on_outfit_equip(H, FALSE, ##slot_name); \
+ else { \
+ outfit_item.on_outfit_equip(H, FALSE, ##slot_name); \
+ } \
}
/// See #66313 and #60901. outfit_sanity used to runtime whenever you had two mergable sheets in either hand. Previously, this only had a 3% chance of occuring. Now 100%.
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index 8793bc0fbe8e3..3af8674e4fcf5 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -156,16 +156,17 @@
/datum/uplink_item/role_restricted/magillitis_serum
name = "Magillitis Serum Autoinjector"
desc = "A single-use autoinjector which contains an experimental serum that causes rapid muscular growth in Hominidae. \
- Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
+ Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas. \
+ Now also contains regenerative chemicals to keep users healthy as they exercise their newfound muscles."
item = /obj/item/reagent_containers/hypospray/medipen/magillitis
cost = 15
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR)
-/datum/uplink_item/role_restricted/gorillacubes
- name = "Box of Gorilla Cubes"
- desc = "A box with three Waffle Co. brand gorilla cubes. Eat big to get big. \
+/datum/uplink_item/role_restricted/gorillacube
+ name = "Gorilla Cube"
+ desc = "A Waffle Co. brand gorilla cube. Eat big to get big. \
Caution: Product may rehydrate when exposed to water."
- item = /obj/item/storage/box/gorillacubes
+ item = /obj/item/food/monkeycube/gorilla
cost = 6
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR)
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 05b3470358f0d..e03847037c8db 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -457,9 +457,9 @@
. += span_notice("[icon2html(ME, user)] \A [ME].")
if(mecha_flags & PANEL_OPEN)
if(servo)
- . += span_notice("Micro-servos reduce movement power usage by [100 - round(100 / servo.rating)]%")
+ . += span_notice("Servo reduces movement power usage by [100 - round(100 / servo.rating)]%")
else
- . += span_warning("It's missing a micro-servo.")
+ . += span_warning("It's missing a servo.")
if(capacitor)
. += span_notice("Capacitor increases armor against energy attacks by [capacitor.rating * 5].")
else
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index 335e8bc5a3e12..c0047eb91d7f0 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -552,6 +552,7 @@
range = MECHA_MELEE
toolspeed = 0.8
mech_flags = EXOSUIT_MODULE_PADDY
+ projectiles_per_shot = 0
///Chassis but typed for the cargo_hold var
var/obj/vehicle/sealed/mecha/ripley/secmech
///Audio for using the hydraulic clamp
@@ -570,10 +571,13 @@
secmech = null
return ..()
-/obj/item/mecha_parts/mecha_equipment/weapon/paddy_claw/action(mob/living/source, atom/target, list/modifiers)
+/obj/item/mecha_parts/mecha_equipment/weapon/paddy_claw/action(mob/source, atom/target, list/modifiers)
if(!secmech.cargo_hold) //We did try
CRASH("Mech [chassis] has a claw device, but no internal storage. This should be impossible.")
- if(ismob(target))
+ if(!action_checks(target))
+ return
+ if(isliving(target))
+ . = ..()
var/mob/living/mobtarget = target
if(mobtarget.move_resist == MOVE_FORCE_OVERPOWERING) //No megafauna or bolted AIs, please.
to_chat(source, "[span_warning("[src] is unable to lift [mobtarget].")]")
@@ -596,11 +600,12 @@
carbontarget.update_handcuffed()
return
- if(!istype(target, /obj/machinery/door))
+ if(istype(target, /obj/machinery/door))
+ . = ..()
+ var/obj/machinery/door/target_door = target
+ playsound(chassis, clampsound, 50, FALSE, -6)
+ target_door.try_to_crowbar(src, source)
return
- var/obj/machinery/door/target_door = target
- playsound(chassis, clampsound, 50, FALSE, -6)
- target_door.try_to_crowbar(src, source)
/obj/item/mecha_parts/mecha_equipment/weapon/paddy_claw/get_snowflake_data()
return list(
diff --git a/code/modules/vehicles/mecha/mecha_construction_paths.dm b/code/modules/vehicles/mecha/mecha_construction_paths.dm
index 3287d66c7bf2c..9a05e3df696d6 100644
--- a/code/modules/vehicles/mecha/mecha_construction_paths.dm
+++ b/code/modules/vehicles/mecha/mecha_construction_paths.dm
@@ -197,24 +197,24 @@
"key" = /obj/item/stock_parts/servo,
"action" = ITEM_MOVE_INSIDE,
"back_key" = TOOL_SCREWDRIVER,
- "desc" = "Scanning module is secured, the micro-servo can be added.",
- "forward_message" = "added micro-servo",
+ "desc" = "Scanning module is secured, the servo can be added.",
+ "forward_message" = "added servo",
"backward_message" = "unsecured capacitor"
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
- "desc" = "Micro-servo is installed, and can be screwed into place.",
- "forward_message" = "secured micro-servo",
- "backward_message" = "removed micro-servo"
+ "desc" = "Servo is installed, and can be screwed into place.",
+ "forward_message" = "secured servo",
+ "backward_message" = "removed servo"
),
list(
"key" = /obj/item/stock_parts/cell,
"action" = ITEM_MOVE_INSIDE,
"back_key" = TOOL_SCREWDRIVER,
- "desc" = "Micro-servo is secured, and the power cell can be added.",
+ "desc" = "Servo is secured, and the power cell can be added.",
"forward_message" = "added power cell",
- "backward_message" = "unsecured micro-servo"
+ "backward_message" = "unsecured servo"
),
list(
"key" = TOOL_SCREWDRIVER,
@@ -534,7 +534,7 @@
list(
"key" = /obj/item/stock_parts/servo,
"action" = ITEM_MOVE_INSIDE,
- "desc" = "Humor micro-servo can be added!",
+ "desc" = "Humor servo can be added!",
"forward_message" = "added smile"
),
list(
@@ -680,24 +680,24 @@
"key" = /obj/item/stock_parts/servo,
"action" = ITEM_MOVE_INSIDE,
"back_key" = TOOL_SCREWDRIVER,
- "desc" = "Capacitor is secured, the micro-servo can be added.",
- "forward_message" = "added micro-servo",
+ "desc" = "Capacitor is secured, the servo can be added.",
+ "forward_message" = "added servo",
"backward_message" = "unsecured capacitor"
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
- "desc" = "Micro-servo is installed, and can be screwed into place.",
- "forward_message" = "secured micro-servo",
- "backward_message" = "removed micro-servo"
+ "desc" = "Servo is installed, and can be screwed into place.",
+ "forward_message" = "secured servo",
+ "backward_message" = "removed servo"
),
list(
"key" = /obj/item/stack/ore/bluespace_crystal,
"amount" = 1,
"back_key" = TOOL_SCREWDRIVER,
- "desc" = "Micro-servo is secured, and the bluespace crystal can be added.",
+ "desc" = "Servo is secured, and the bluespace crystal can be added.",
"forward_message" = "added bluespace crystal",
- "backward_message" = "unsecured micro-servo"
+ "backward_message" = "unsecured servo"
),
list(
"key" = /obj/item/stack/cable_coil,
diff --git a/code/modules/vehicles/mecha/mecha_movement.dm b/code/modules/vehicles/mecha/mecha_movement.dm
index a77381fe97a87..aa86d57a0da8f 100644
--- a/code/modules/vehicles/mecha/mecha_movement.dm
+++ b/code/modules/vehicles/mecha/mecha_movement.dm
@@ -97,7 +97,7 @@
if(isnull(capacitor))
missing_parts += "capacitor"
if(isnull(servo))
- missing_parts += "micro-servo"
+ missing_parts += "servo"
if(length(missing_parts))
if(TIMER_COOLDOWN_FINISHED(src, COOLDOWN_MECHA_MESSAGE))
to_chat(occupants, "[icon2html(src, occupants)][span_warning("Missing [english_list(missing_parts)].")]")
diff --git a/code/modules/vehicles/mecha/mecha_ui.dm b/code/modules/vehicles/mecha/mecha_ui.dm
index 1bf5b8674a47d..712d5f0d712bf 100644
--- a/code/modules/vehicles/mecha/mecha_ui.dm
+++ b/code/modules/vehicles/mecha/mecha_ui.dm
@@ -9,7 +9,7 @@
ui.open()
ui_view.display_to(user)
-/obj/vehicle/sealed/mecha/ui_status(mob/user)
+/obj/vehicle/sealed/mecha/ui_status(mob/user, datum/ui_state/state)
if(contains(user))
return UI_INTERACTIVE
return min(
diff --git a/code/modules/wiremod/components/admin/proccall.dm b/code/modules/wiremod/components/admin/proccall.dm
index 56baaee1447a5..b86e05e6c8a8b 100644
--- a/code/modules/wiremod/components/admin/proccall.dm
+++ b/code/modules/wiremod/components/admin/proccall.dm
@@ -88,7 +88,7 @@
. = list()
.["possible_types"] = GLOB.wiremod_fundamental_types
-/obj/item/circuit_component/proccall/ui_status(mob/user)
+/obj/item/circuit_component/proccall/ui_status(mob/user, datum/ui_state/state)
if(!check_rights_for(user.client, R_VAREDIT))
return UI_CLOSE
return UI_INTERACTIVE
diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm
index 0499a56dcd474..7dde89e511612 100644
--- a/code/modules/wiremod/core/integrated_circuit.dm
+++ b/code/modules/wiremod/core/integrated_circuit.dm
@@ -413,7 +413,7 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
return FALSE
return ..()
-/obj/item/integrated_circuit/ui_status(mob/user)
+/obj/item/integrated_circuit/ui_status(mob/user, datum/ui_state/state)
. = ..()
if (isobserver(user))
diff --git a/html/changelogs/AutoChangeLog-pr-81540.yml b/html/changelogs/AutoChangeLog-pr-81540.yml
new file mode 100644
index 0000000000000..5157e6ceeb2aa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81540.yml
@@ -0,0 +1,7 @@
+author: "IndieanaJones"
+delete-after: True
+changes:
+ - balance: "Gorillas are faster, stronger, but not bigger too. Note while holding an item, they are the same speed as they were prior."
+ - balance: "Gorillas now have the understanding of languages as monkeys do."
+ - balance: "The Gorilla Cube Box for traitors has been replaced with a singular gorilla cube. Due to the aforementioned changes, this singular gorilla should be as scary if not scarier than 3 gorillas were prior."
+ - balance: " Magillitis Serum Autoinjector now grants the resulting gorilla a slow passive regeneration effect which kicks in after not taking damage for 12 seconds."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81541.yml b/html/changelogs/AutoChangeLog-pr-81541.yml
deleted file mode 100644
index b152c6498dbdb..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81541.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "nevimer"
-delete-after: True
-changes:
- - bugfix: "ForceEvent tgui panel search is more reliable."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81577.yml b/html/changelogs/AutoChangeLog-pr-81577.yml
deleted file mode 100644
index 16702f1297aa8..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81577.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "13spacemen"
-delete-after: True
-changes:
- - refactor: "Bloody item overlays no longer use icon procs to generate the overlay"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81588.yml b/html/changelogs/AutoChangeLog-pr-81588.yml
deleted file mode 100644
index fc9163ce3efcd..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81588.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "13spacemen, Kapu1178"
-delete-after: True
-changes:
- - refactor: "Footprint sprites are now based on your shoes and legs, shoes have priority"
- - image: "Monkey legs now produce pawprints instead of footprints"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81589.yml b/html/changelogs/AutoChangeLog-pr-81589.yml
deleted file mode 100644
index 4cdcda2320ac0..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81589.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JohnFulpWillard"
-delete-after: True
-changes:
- - spellcheck: "Removed a double space in health analyzer's message telling you someone is deaf."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81595.yml b/html/changelogs/AutoChangeLog-pr-81595.yml
deleted file mode 100644
index 9a9db0f69a221..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81595.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Melbert"
-delete-after: True
-changes:
- - rscdel: "Deletes Mythril Coins from random coin spawners"
- - rscdel: "Replaces Mythril sheets in icebox vent fishing with Runite sheets"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81596.yml b/html/changelogs/AutoChangeLog-pr-81596.yml
new file mode 100644
index 0000000000000..fdd8366297830
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81596.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "The Paddy's Claw should be properly unusable in situations which it should be properly unusable."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81597.yml b/html/changelogs/AutoChangeLog-pr-81597.yml
deleted file mode 100644
index 87369c39be1f8..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81597.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "00-Steven"
-delete-after: True
-changes:
- - bugfix: "Chefs (not to be confused with cooks) actually display as being a part of service on the crew monitor."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81599.yml b/html/changelogs/AutoChangeLog-pr-81599.yml
new file mode 100644
index 0000000000000..84bf337648e9f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81599.yml
@@ -0,0 +1,4 @@
+author: "TJatPBnJ"
+delete-after: True
+changes:
+ - balance: "Power crepes are now finger food"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81605.yml b/html/changelogs/AutoChangeLog-pr-81605.yml
new file mode 100644
index 0000000000000..7f6f91ac658e2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81605.yml
@@ -0,0 +1,4 @@
+author: "ArcaneMusic"
+delete-after: True
+changes:
+ - bugfix: "Fixed instances where holochip/holocredits would spawn with a total of zero credits contained within."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81606.yml b/html/changelogs/AutoChangeLog-pr-81606.yml
deleted file mode 100644
index 02bee239288bd..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-81606.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JohnFulpWillard"
-delete-after: True
-changes:
- - refactor: "Holographic mobs now gives better feedback to players and should more consistently not give any drops."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81627.yml b/html/changelogs/AutoChangeLog-pr-81627.yml
new file mode 100644
index 0000000000000..8961474bf7f5d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81627.yml
@@ -0,0 +1,4 @@
+author: "vinylspiders"
+delete-after: True
+changes:
+ - bugfix: "fixes cardboard cutouts not updating when held and using the crayon on them to change their appearance, and fixes the alt_appearance being added to the cutout instead of the mob holding it"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81629.yml b/html/changelogs/AutoChangeLog-pr-81629.yml
new file mode 100644
index 0000000000000..d7c9aa58f9c5c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81629.yml
@@ -0,0 +1,5 @@
+author: "mc-oofert"
+delete-after: True
+changes:
+ - rscadd: "Added an option to deathmatch loadout dropdown that allows you to pick a random loadout"
+ - bugfix: "In deathmatch, plasmamen are made humans and the UI supports more players"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81637.yml b/html/changelogs/AutoChangeLog-pr-81637.yml
new file mode 100644
index 0000000000000..1e735fde35982
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81637.yml
@@ -0,0 +1,4 @@
+author: "00-Steven"
+delete-after: True
+changes:
+ - bugfix: "Medical/security records now show an icon based on the registered trim, rather than showing a question mark for records with customized titles."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81638.yml b/html/changelogs/AutoChangeLog-pr-81638.yml
new file mode 100644
index 0000000000000..c6d7b9e46333d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81638.yml
@@ -0,0 +1,4 @@
+author: "00-Steven"
+delete-after: True
+changes:
+ - bugfix: "Newscasters no longer say \"No wanted issue posted. Have a secure day.\" when there is, in fact, an active wanted issue currently posted."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81641.yml b/html/changelogs/AutoChangeLog-pr-81641.yml
new file mode 100644
index 0000000000000..37efab9484f10
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81641.yml
@@ -0,0 +1,6 @@
+author: "JohnFulpWillard"
+delete-after: True
+changes:
+ - qol: "You can use atmos machines, holopads, crayons, spray cans, and disposal bins while floored."
+ - bugfix: "You can close extinguisher cabinets while floored."
+ - bugfix: "You can climb onto a photocopier from the floor."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81661.yml b/html/changelogs/AutoChangeLog-pr-81661.yml
new file mode 100644
index 0000000000000..13a28048d7be8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81661.yml
@@ -0,0 +1,4 @@
+author: "Singul0"
+delete-after: True
+changes:
+ - bugfix: "The say TGUI would recognize :g (changeling hivemind) prefix. and give a visual indicator in it that you are talking in the right channel"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81662.yml b/html/changelogs/AutoChangeLog-pr-81662.yml
new file mode 100644
index 0000000000000..ab75566b57e30
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81662.yml
@@ -0,0 +1,4 @@
+author: "13spacemen"
+delete-after: True
+changes:
+ - bugfix: "Blood overlays on items no longer leak onto other objects"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81671.yml b/html/changelogs/AutoChangeLog-pr-81671.yml
new file mode 100644
index 0000000000000..853a56fb32e7a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81671.yml
@@ -0,0 +1,4 @@
+author: "Ben10Omintrix"
+delete-after: True
+changes:
+ - bugfix: "fixes pokemon ai still being active when inside the pokeball"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81684.yml b/html/changelogs/AutoChangeLog-pr-81684.yml
new file mode 100644
index 0000000000000..d75725dd9ce89
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81684.yml
@@ -0,0 +1,4 @@
+author: "mc-oofert"
+delete-after: True
+changes:
+ - bugfix: "You may no longer make deathmatch arenas dark with a lightswitch."
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml
index de87de3d7f2b0..3209584f8c161 100644
--- a/html/changelogs/archive/2024-02.yml
+++ b/html/changelogs/archive/2024-02.yml
@@ -643,3 +643,92 @@
- rscadd: Machine frames can now be deconstructed with screwdrivers, and computer
frames can now be deconstructed with welding torches. Welders are faster.
- bugfix: Organs have the blood DNA of their owns on them again
+2024-02-23:
+ 00-Steven:
+ - bugfix: Chefs (not to be confused with cooks) actually display as being a part
+ of service on the crew monitor.
+ 13spacemen:
+ - refactor: Bloody item overlays no longer use icon procs to generate the overlay
+ 13spacemen, Kapu1178:
+ - refactor: Footprint sprites are now based on your shoes and legs, shoes have priority
+ - image: Monkey legs now produce pawprints instead of footprints
+ JohnFulpWillard:
+ - refactor: Holographic mobs now gives better feedback to players and should more
+ consistently not give any drops.
+ - spellcheck: Removed a double space in health analyzer's message telling you someone
+ is deaf.
+ Melbert:
+ - bugfix: You can now take our your anger upon arcade cabinets upon losing (they
+ can be hit again)
+ - rscdel: Deletes Mythril Coins from random coin spawners
+ - rscdel: Replaces Mythril sheets in icebox vent fishing with Runite sheets
+ SgtHunk:
+ - bugfix: The Summon Simians spell will now properly check for monkey faction, allowing
+ people who are monkeys to not be mauled.
+ nevimer:
+ - bugfix: ForceEvent tgui panel search is more reliable.
+2024-02-24:
+ 00-Steven:
+ - bugfix: Chef and department security have the assignment on their ID imprinted
+ onto their PDA by default, instead of defaulting to Cook/Security Officer and
+ requiring you to do so manually.
+ A.C.M.O.:
+ - qol: CPR can be administered by species which do not need to breathe, so long
+ as they have functional lungs.
+ ArcaneMusic:
+ - bugfix: Mining cyborgs now keep the state of their mining scanner when stored,
+ without shutting off.
+ Echriser:
+ - bugfix: you can no longer feed people with covered mouths using spoons or ladles
+ JohnFulpWillard:
+ - bugfix: Alien larvas' examine text no longer tells you they are missing arms and
+ legs.
+ Melbert:
+ - bugfix: Cyborgs spark and spit out oil when smacked again. Among other things.
+ vinylspiders:
+ - bugfix: fixes spurious CI failure in museum
+ - code_imp: adds a blacklist feature to spawners
+2024-02-25:
+ bigfatbananacyclops:
+ - rscadd: floortile camouflage suit, boots, gloves and crate containing the former
+ items
+2024-02-26:
+ 00-Steven:
+ - bugfix: Disabled the popsicle stick maximizer. (Producing popsicle sticks actually
+ deletes the input logs.)
+ 13spacemen:
+ - refactor: Bodytypes to do with character sprite shape now have their own bodyshape
+ var, all sprite handling is done with bodyshape and not bodytype anymore
+ Cheshify:
+ - bugfix: meatwheat is now firmly made out of meat, instead of sometimes being wheat
+ and sometimes being meat and sometimes being both.
+ - bugfix: The North Star finally has a bluespace gas sender system.
+ Derpguy3:
+ - bugfix: Three stationary water tanks on Birdshot which supplied a sink in the
+ bar, a sink in the medical storage and a sink in the perma-brig's kitchen are
+ now properly rotated and connected with the plumbing.
+ - bugfix: The unisex showers on Birdshot have been given a water tank for its plumbing.
+ EEASAS:
+ - balance: buffs corporate modsuit(centcom only)
+ Higgin:
+ - qol: Blood Brother convert objective now explains who you can convert and what
+ to do if you mess up with your flash.
+ MrDas:
+ - spellcheck: The tier of servo in mech construction is no longer explicitly stated.
+ necromanceranne:
+ - balance: Nanotrasen, in direct response to the increasing danger posed by wannabe
+ martial artists and rioters in the fringes of the Spinward Sector, have upgraded
+ the impact dampeners found in their riot armor. Staff have also started to rediscover
+ the value of medieval armor; it isn't particularly easy to topple a knight in
+ a suit of plate with just your fists.
+ - balance: Melee-focused armor is now more able to protect you from various RNG-based
+ knockdowns, such as critical hits from punches (as well as the ones applied
+ through the staggered status), shoves, critical hits with a blunt weapon to
+ the chest, and body throws.
+ - balance: Melee-focused helmets also protect you from head injuries, such as bottle
+ smashes, accidentally hitting something dense during a tackle, and critical
+ hits from a blunt weapon to the head.
+ - balance: Bottlesmash knockdowns are less reliable in general.
+ - rscadd: A new module, the Safety-First Head Protection module, protects you from
+ head trauma! Available in most modsuits expected to take hits to the head often.
+ And from roundstart exofabricators.
diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi
index 3a6faaf1ef8e6..5bd3005ab8e58 100644
Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index a99bff784cee0..d0e5093d459c5 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi
index 24e8622344e4e..db1259996c867 100644
Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ
diff --git a/icons/mob/clothing/under/syndicate.dmi b/icons/mob/clothing/under/syndicate.dmi
index 57e0cf14a3a76..0e9d8dfc91b02 100644
Binary files a/icons/mob/clothing/under/syndicate.dmi and b/icons/mob/clothing/under/syndicate.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index 2a5fea8c20bb6..d8a2fc0bd13cd 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 1dd2ac7d71c7e..568c059a54d78 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi
index 104812240fec6..0acc23d128217 100644
Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ
diff --git a/icons/obj/clothing/under/syndicate.dmi b/icons/obj/clothing/under/syndicate.dmi
index ffa19bd0d6257..de712a41f6b3f 100644
Binary files a/icons/obj/clothing/under/syndicate.dmi and b/icons/obj/clothing/under/syndicate.dmi differ
diff --git a/icons/obj/food/piecake.dmi b/icons/obj/food/piecake.dmi
index 8474ba29fe9f8..e6c0a71022d50 100644
Binary files a/icons/obj/food/piecake.dmi and b/icons/obj/food/piecake.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 3f560e3208ab5..5df1d8c26f7cb 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1954,6 +1954,7 @@
#include "code\game\machinery\launch_pad.dm"
#include "code\game\machinery\lightswitch.dm"
#include "code\game\machinery\limbgrower.dm"
+#include "code\game\machinery\machine_frame.dm"
#include "code\game\machinery\mass_driver.dm"
#include "code\game\machinery\mechlaunchpad.dm"
#include "code\game\machinery\medical_kiosk.dm"
diff --git a/tgui/packages/tgui-say/constants.ts b/tgui/packages/tgui-say/constants.ts
index 3033154e50999..0c3d7943c2676 100644
--- a/tgui/packages/tgui-say/constants.ts
+++ b/tgui/packages/tgui-say/constants.ts
@@ -21,6 +21,7 @@ export const RADIO_PREFIXES = {
':b ': 'io',
':c ': 'Cmd',
':e ': 'Engi',
+ ':g ': 'Cling',
':m ': 'Med',
':n ': 'Sci',
':o ': 'AI',
diff --git a/tgui/packages/tgui-say/styles/colors.scss b/tgui/packages/tgui-say/styles/colors.scss
index 475d8b25b26ef..ded5ffeeb9729 100644
--- a/tgui/packages/tgui-say/styles/colors.scss
+++ b/tgui/packages/tgui-say/styles/colors.scss
@@ -10,6 +10,7 @@ $_channel_map: (
'Admin': #ffbbff,
'AI': #d65d95,
'CCom': #2681a5,
+ 'Cling': #4c701f,
'Cmd': #fcdf03,
'Engi': #f37746,
'Hive': #855d85,
diff --git a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
index 6c1a2221a4d70..2e9b80181873f 100644
--- a/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
+++ b/tgui/packages/tgui/interfaces/DeathmatchLobby.tsx
@@ -44,11 +44,11 @@ type Data = {
export const DeathmatchLobby = (props) => {
const { act, data } = useBackend();
return (
-
+
-
-
+
+
@@ -148,6 +148,7 @@ export const DeathmatchLobby = (props) => {
{data.map.desc}
+
Maximum Play Time: {`${data.map.time / 600}min`}
diff --git a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx
index fc190385e7bb7..23aafd99a6105 100644
--- a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx
+++ b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx
@@ -90,7 +90,7 @@ const CrewTab = (props: { record: MedicalRecord }) => {
const { act, data } = useBackend();
const { assigned_view } = data;
const { record } = props;
- const { crew_ref, name, rank } = record;
+ const { crew_ref, name, trim } = record;
/** Sets the record to preview */
const selectRecord = (record: MedicalRecord) => {
@@ -109,7 +109,7 @@ const CrewTab = (props: { record: MedicalRecord }) => {
selected={selectedRecord?.crew_ref === crew_ref}
>
- {name}
+ {name}
);
diff --git a/tgui/packages/tgui/interfaces/MedicalRecords/types.ts b/tgui/packages/tgui/interfaces/MedicalRecords/types.ts
index 369cad0747e62..878f81ab65e2c 100644
--- a/tgui/packages/tgui/interfaces/MedicalRecords/types.ts
+++ b/tgui/packages/tgui/interfaces/MedicalRecords/types.ts
@@ -26,6 +26,7 @@ export type MedicalRecord = {
quirk_notes: string;
rank: string;
species: string;
+ trim: string;
};
export type MedicalNote = {
diff --git a/tgui/packages/tgui/interfaces/Newscaster.jsx b/tgui/packages/tgui/interfaces/Newscaster.jsx
index 431e7d606e624..b3a30eb2bdd85 100644
--- a/tgui/packages/tgui/interfaces/Newscaster.jsx
+++ b/tgui/packages/tgui/interfaces/Newscaster.jsx
@@ -326,9 +326,11 @@ const NewscasterWantedScreen = (props) => {
>
) : (
- {wanted.active
- ? 'Please contact your local security officer if spotted.'
- : 'No wanted issue posted. Have a secure day.'}
+ {wanted.map((activeWanted) =>
+ activeWanted.active
+ ? 'Please contact your local security officer if spotted.'
+ : 'No wanted issue posted. Have a secure day.',
+ )}
)}
diff --git a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
index d6e3d431ba4a6..492321b3d3a07 100644
--- a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
+++ b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
@@ -91,7 +91,7 @@ const CrewTab = (props: { record: SecurityRecord }) => {
const { act, data } = useBackend();
const { assigned_view } = data;
const { record } = props;
- const { crew_ref, name, rank, wanted_status } = record;
+ const { crew_ref, name, trim, wanted_status } = record;
/** Chooses a record */
const selectRecord = (record: SecurityRecord) => {
@@ -112,7 +112,7 @@ const CrewTab = (props: { record: SecurityRecord }) => {
selected={isSelected}
>
- {name}
+ {name}
);
diff --git a/tgui/packages/tgui/interfaces/SecurityRecords/types.ts b/tgui/packages/tgui/interfaces/SecurityRecords/types.ts
index d090ecc5b58e1..2c54ab880bd0a 100644
--- a/tgui/packages/tgui/interfaces/SecurityRecords/types.ts
+++ b/tgui/packages/tgui/interfaces/SecurityRecords/types.ts
@@ -23,6 +23,7 @@ export type SecurityRecord = {
note: string;
rank: string;
species: string;
+ trim: string;
wanted_status: string;
voice: string;
};