diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm
index 69e618282e..a43e6129b7 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm
@@ -167,7 +167,7 @@
/obj/item/book/manual/wiki/barman_recipes,
/obj/item/book/granter/action/drink_fling,
/obj/item/reagent_containers/food/drinks/shaker,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/turf/open/floor/wood,
/area/ruin/powered/beach)
"aI" = (
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
index a22d1a21e7..60ae409ea2 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
@@ -161,7 +161,7 @@
/turf/open/floor/plasteel/white,
/area/ruin/powered/animal_hospital)
"av" = (
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/item/reagent_containers/spray/cleaner,
/obj/effect/turf_decal/tile/blue{
dir = 1
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
index b330592a8e..8e0d9a523a 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
@@ -4728,7 +4728,7 @@
/area/ruin/unpowered/syndicate_lava_base/bar)
"lz" = (
/obj/structure/table/wood,
-/obj/item/reagent_containers/glass/rag{
+/obj/item/reagent_containers/rag{
pixel_x = -4;
pixel_y = 9
},
diff --git a/_maps/RandomRuins/SpaceRuins/bus.dmm b/_maps/RandomRuins/SpaceRuins/bus.dmm
index fe624c19bf..c731bcac5e 100644
--- a/_maps/RandomRuins/SpaceRuins/bus.dmm
+++ b/_maps/RandomRuins/SpaceRuins/bus.dmm
@@ -260,7 +260,7 @@
/turf/open/floor/plating/asteroid/airless,
/area/ruin/unpowered/no_grav)
"aQ" = (
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/turf/open/floor/plating/asteroid/airless,
/area/ruin/unpowered/no_grav)
"aR" = (
diff --git a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm
index c64a73eada..69b56946f2 100644
--- a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm
+++ b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm
@@ -2010,7 +2010,7 @@
/area/ruin/space/has_grav/hotel/bar)
"gd" = (
/obj/structure/table/reinforced,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/effect/turf_decal/tile/bar,
/obj/effect/turf_decal/tile/bar{
dir = 1
diff --git a/_maps/RandomZLevels/VR/syndicate_trainer.dmm b/_maps/RandomZLevels/VR/syndicate_trainer.dmm
index f68a799b76..a8c316d770 100644
--- a/_maps/RandomZLevels/VR/syndicate_trainer.dmm
+++ b/_maps/RandomZLevels/VR/syndicate_trainer.dmm
@@ -1973,7 +1973,7 @@
/turf/open/indestructible,
/area/awaymission/centcomAway/thunderdome)
"lB" = (
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/turf/open/indestructible,
/area/awaymission/centcomAway/general)
"lD" = (
diff --git a/_maps/RandomZLevels/beach.dmm b/_maps/RandomZLevels/beach.dmm
index dddeadb0be..7aab25e400 100644
--- a/_maps/RandomZLevels/beach.dmm
+++ b/_maps/RandomZLevels/beach.dmm
@@ -150,7 +150,7 @@
/area/awaymission/beach)
"aB" = (
/obj/structure/table,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/turf/open/floor/wood,
/area/awaymission/beach)
"aC" = (
diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm
index 61c606dcde..7edc7b4518 100644
--- a/_maps/RandomZLevels/moonoutpost19.dmm
+++ b/_maps/RandomZLevels/moonoutpost19.dmm
@@ -5383,7 +5383,7 @@
"kn" = (
/obj/structure/table/reinforced,
/obj/machinery/door/firedoor,
-/obj/item/reagent_containers/glass/rag{
+/obj/item/reagent_containers/rag{
pixel_y = 5
},
/obj/machinery/door/poddoor/shutters{
diff --git a/_maps/RandomZLevels/undergroundoutpost45.dmm b/_maps/RandomZLevels/undergroundoutpost45.dmm
index 39f7d99173..f6d46ef97d 100644
--- a/_maps/RandomZLevels/undergroundoutpost45.dmm
+++ b/_maps/RandomZLevels/undergroundoutpost45.dmm
@@ -3601,7 +3601,7 @@
"hn" = (
/obj/structure/table,
/obj/item/stack/packageWrap,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/effect/turf_decal/tile/bar,
/obj/effect/turf_decal/tile/bar{
dir = 1
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 4420059f62..50b5504af5 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -20908,7 +20908,7 @@
pixel_x = 32
},
/obj/item/book/manual/wiki/barman_recipes,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/effect/turf_decal/tile/bar,
/obj/effect/turf_decal/tile/bar{
dir = 1
@@ -57658,7 +57658,7 @@
/area/ai_monitored/storage/eva)
"vjm" = (
/obj/structure/table/wood,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/machinery/light/small{
dir = 1
},
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index bfc4a60f08..b4aabc3676 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -14482,7 +14482,7 @@
},
/obj/item/book/manual/wiki/barman_recipes,
/obj/item/reagent_containers/food/drinks/shaker,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 4de1657e48..7faca77d70 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -38985,7 +38985,7 @@
pixel_y = 5
},
/obj/item/reagent_containers/food/drinks/shaker,
-/obj/item/reagent_containers/glass/rag{
+/obj/item/reagent_containers/rag{
pixel_y = 5
},
/obj/effect/turf_decal/tile/bar,
@@ -59281,7 +59281,7 @@
},
/area/maintenance/port/aft)
"ckR" = (
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/structure/table/wood,
/turf/open/floor/wood{
icon_state = "wood-broken4"
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index cef0aefc50..0c55f05fcb 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -13484,7 +13484,7 @@
/obj/structure/table/wood,
/obj/item/book/manual/wiki/barman_recipes,
/obj/item/reagent_containers/food/drinks/shaker,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/effect/turf_decal/tile/neutral{
dir = 1
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index e03ba4fcd2..0166dd6a84 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -21118,7 +21118,7 @@
"aYf" = (
/obj/structure/table/reinforced,
/obj/item/book/manual/wiki/barman_recipes,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/effect/turf_decal/tile/red{
dir = 1
},
diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index d796330cc8..d6e522e3a0 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -15122,7 +15122,7 @@
/obj/structure/table/wood,
/obj/item/book/manual/wiki/barman_recipes,
/obj/item/reagent_containers/food/drinks/shaker,
-/obj/item/reagent_containers/glass/rag,
+/obj/item/reagent_containers/rag,
/obj/machinery/newscaster{
pixel_y = -32
},
diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index 1c3832001f..192ed7ce0e 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -36,4 +36,5 @@
#define NOSLIP (1<<4) //prevents from slipping on wet floors, in space etc
#define THICKMATERIAL (1<<5) //prevents syringes, parapens and hypos if the external suit or helmet (if targeting head) has this flag. Example: space suits, biosuit, bombsuits, thick suits that cover your body.
#define VOICEBOX_TOGGLABLE (1<<6) // The voicebox in this clothing can be toggled.
-#define VOICEBOX_DISABLED (1<<7) // The voicebox is currently turned off.
\ No newline at end of file
+#define VOICEBOX_DISABLED (1<<7) // The voicebox is currently turned off.
+#define NO_UNIFORM_REQUIRED (1<<8) // Can be worn on certain slots (currently belt and id) that would otherwise require an uniform.
\ No newline at end of file
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index ef460cd54c..d92ab17584 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/paper/crumpled = 1,
/obj/item/pen = 1,
/obj/item/reagent_containers/spray/pestspray = 1,
- /obj/item/reagent_containers/glass/rag = 3,
+ /obj/item/reagent_containers/rag = 3,
/obj/item/stock_parts/cell = 3,
/obj/item/storage/belt/utility = 2,
/obj/item/storage/box = 2,
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 0a9a135986..2a24ce69b9 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -95,6 +95,12 @@
var/obj/item/toy/crayon/CR = WM.color_source
add_atom_colour(CR.paint_color, WASHABLE_COLOUR_PRIORITY)
+/obj/item/reagents_containers/glass/rag/towel/machine_wash(obj/machinery/washing_machine/WM)
+ if(WM.color_source)
+ if(istype(WM.color_source, /obj/item/toy/crayon))
+ var/obj/item/toy/crayon/CR = WM.color_source
+ add_atom_colour(CR.paint_color, WASHABLE_COLOUR_PRIORITY)
+
/mob/living/simple_animal/pet/dog/corgi/machine_wash(obj/machinery/washing_machine/WM)
gib()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index e1ecf6d14d..5594059463 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -248,7 +248,8 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
new/datum/stack_recipe("bio bag", /obj/item/storage/bag/bio, 4), \
null, \
new/datum/stack_recipe("improvised gauze", /obj/item/stack/medical/gauze/improvised, 1, 2, 6), \
- new/datum/stack_recipe("rag", /obj/item/reagent_containers/glass/rag, 1), \
+ new/datum/stack_recipe("rag", /obj/item/reagent_containers/rag, 1), \
+ new/datum/stack_recipe("towel", /obj/item/reagent_containers/rag/towel, 3), \
new/datum/stack_recipe("bedsheet", /obj/item/bedsheet, 3), \
new/datum/stack_recipe("empty sandbag", /obj/item/emptysandbag, 4), \
null, \
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index b8320c80fb..405e697d3b 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(globalBlankCanvases, new(AMT_OF_CANVASES))
return
//Cleaning one pixel with a soap or rag
- if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/glass/rag))
+ if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/rag))
//Pixel info created only when needed
var/icon/masterpiece = icon(icon,icon_state)
var/thePix = masterpiece.GetPixel(pixX,pixY)
diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
index 0809edaa71..d6d2f18e5a 100644
--- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
@@ -21,8 +21,8 @@
new /obj/item/clothing/head/soft/black(src)
new /obj/item/clothing/shoes/sneakers/black(src)
new /obj/item/clothing/shoes/sneakers/black(src)
- new /obj/item/reagent_containers/glass/rag(src)
- new /obj/item/reagent_containers/glass/rag(src)
+ new /obj/item/reagent_containers/rag(src)
+ new /obj/item/reagent_containers/rag(src)
new /obj/item/storage/box/beanbag(src)
new /obj/item/clothing/suit/armor/vest/alt(src)
new /obj/item/circuitboard/machine/dish_drive(src)
@@ -53,7 +53,7 @@
new /obj/item/clothing/suit/toggle/chef(src)
new /obj/item/clothing/under/rank/chef(src)
new /obj/item/clothing/head/chefhat(src)
- new /obj/item/reagent_containers/glass/rag(src)
+ new /obj/item/reagent_containers/rag(src)
/obj/structure/closet/jcloset
name = "custodial closet"
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index d70838a30b..46db567b10 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -537,7 +537,7 @@
if(istype(O, /obj/item/stack/medical/gauze))
var/obj/item/stack/medical/gauze/G = O
- new /obj/item/reagent_containers/glass/rag(src.loc)
+ new /obj/item/reagent_containers/rag(src.loc)
to_chat(user, "You tear off a strip of gauze and make a rag.")
G.use(1)
return
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 46cc1fa93c..72f5d2d068 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -1633,7 +1633,7 @@
/obj/item/caution,
/obj/item/storage/bag/trash,
/obj/item/reagent_containers/spray/cleaner,
- /obj/item/reagent_containers/glass/rag,
+ /obj/item/reagent_containers/rag,
/obj/item/grenade/chem_grenade/cleaner,
/obj/item/grenade/chem_grenade/cleaner,
/obj/item/grenade/chem_grenade/cleaner,
@@ -1666,7 +1666,7 @@
contains = list(/obj/item/caution,
/obj/item/caution,
/obj/item/caution,
- /obj/item/reagent_containers/glass/rag,
+ /obj/item/reagent_containers/rag,
/obj/item/reagent_containers/glass/bottle/ammonia,
/obj/item/reagent_containers/glass/bottle/ammonia,
/obj/item/reagent_containers/spray/drying_agent)
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 817fe59dcd..f0c1eeb833 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -8,6 +8,7 @@
slot_flags = ITEM_SLOT_GLOVES
attack_verb = list("challenged")
var/transfer_prints = FALSE
+ var/transfer_blood = 0
strip_delay = 20
equip_delay_other = 40
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index ca51fec347..f0665682a9 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -68,7 +68,7 @@
/datum/crafting_recipe/molotov
name = "Molotov"
result = /obj/item/reagent_containers/food/drinks/bottle/molotov
- reqs = list(/obj/item/reagent_containers/glass/rag = 1,
+ reqs = list(/obj/item/reagent_containers/rag = 1,
/obj/item/reagent_containers/food/drinks/bottle = 1)
parts = list(/obj/item/reagent_containers/food/drinks/bottle = 1)
time = 40
diff --git a/code/modules/detectivework/footprints_and_rag.dm b/code/modules/detectivework/footprints_and_rag.dm
deleted file mode 100644
index a25bc01b13..0000000000
--- a/code/modules/detectivework/footprints_and_rag.dm
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/mob
- var/bloody_hands = 0
-
-/obj/item/clothing/gloves
- var/transfer_blood = 0
-
-
-/obj/item/reagent_containers/glass/rag
- name = "damp rag"
- desc = "For cleaning up messes, you suppose."
- w_class = WEIGHT_CLASS_TINY
- icon = 'icons/obj/toy.dmi'
- icon_state = "rag"
- item_flags = NOBLUDGEON
- reagent_flags = OPENCONTAINER
- amount_per_transfer_from_this = 5
- possible_transfer_amounts = list()
- volume = 5
- spillable = FALSE
-
-/obj/item/reagent_containers/glass/rag/suicide_act(mob/user)
- user.visible_message("[user] is smothering [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return (OXYLOSS)
-
-/obj/item/reagent_containers/glass/rag/afterattack(atom/A as obj|turf|area, mob/user,proximity)
- . = ..()
- if(!proximity)
- return
- if(iscarbon(A) && A.reagents && reagents.total_volume)
- var/mob/living/carbon/C = A
- var/reagentlist = pretty_string_from_reagent_list(reagents)
- var/log_object = "a damp rag containing [reagentlist]"
- if(user.a_intent == INTENT_HARM && !C.is_mouth_covered())
- reagents.reaction(C, INGEST)
- reagents.trans_to(C, reagents.total_volume)
- C.visible_message("[user] has smothered \the [C] with \the [src]!", "[user] has smothered you with \the [src]!", "You hear some struggling and muffled cries of surprise.")
- log_combat(user, C, "smothered", log_object)
- else
- reagents.reaction(C, TOUCH)
- reagents.clear_reagents()
- C.visible_message("[user] has touched \the [C] with \the [src].")
- log_combat(user, C, "touched", log_object)
-
- else if(istype(A) && src in user)
- user.visible_message("[user] starts to wipe down [A] with [src]!", "You start to wipe down [A] with [src]...")
- if(do_after(user,30, target = A))
- user.visible_message("[user] finishes wiping off [A]!", "You finish wiping off [A].")
- SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
- return
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
index 8403d533c4..edf0fde83e 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
@@ -91,7 +91,7 @@
else
return initial(pixel_x)
-/mob/living/carbon/alien/humanoid/get_permeability_protection()
+/mob/living/carbon/alien/humanoid/get_permeability_protection(list/target_zones)
return 0.8
/mob/living/carbon/alien/humanoid/alien_evolve(mob/living/carbon/alien/humanoid/new_xeno)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index db7257f0f0..aa87a6e46d 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -634,6 +634,18 @@
else
. += INFINITY
+/mob/living/carbon/get_permeability_protection(list/target_zones = list(HANDS,CHEST,GROIN,LEGS,FEET,ARMS,HEAD))
+ var/list/tally = list()
+ for(var/obj/item/I in get_equipped_items())
+ for(var/zone in target_zones)
+ if(I.body_parts_covered & zone)
+ tally["[zone]"] = max(1 - I.permeability_coefficient, target_zones["[zone]"])
+ var/protection = 0
+ for(var/key in tally)
+ protection += tally[key]
+ protection *= INVERSE(target_zones.len)
+ return protection
+
//this handles hud updates
/mob/living/carbon/update_damage_hud()
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index b42346382f..53f998387b 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -27,7 +27,7 @@
for(var/bp in body_parts)
if(!bp)
continue
- if(bp && istype(bp , /obj/item/clothing))
+ if(istype(bp, /obj/item/clothing))
var/obj/item/clothing/C = bp
if(C.body_parts_covered & def_zone.body_part)
protection += C.armor.getRating(d_type)
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 0b40d3d26a..b65d62b63b 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -111,26 +111,6 @@
return ..()
-/mob/living/carbon/human/get_permeability_protection()
- var/list/prot = list("hands"=0, "chest"=0, "groin"=0, "legs"=0, "feet"=0, "arms"=0, "head"=0)
- for(var/obj/item/I in get_equipped_items())
- if(I.body_parts_covered & HANDS)
- prot["hands"] = max(1 - I.permeability_coefficient, prot["hands"])
- if(I.body_parts_covered & CHEST)
- prot["chest"] = max(1 - I.permeability_coefficient, prot["chest"])
- if(I.body_parts_covered & GROIN)
- prot["groin"] = max(1 - I.permeability_coefficient, prot["groin"])
- if(I.body_parts_covered & LEGS)
- prot["legs"] = max(1 - I.permeability_coefficient, prot["legs"])
- if(I.body_parts_covered & FEET)
- prot["feet"] = max(1 - I.permeability_coefficient, prot["feet"])
- if(I.body_parts_covered & ARMS)
- prot["arms"] = max(1 - I.permeability_coefficient, prot["arms"])
- if(I.body_parts_covered & HEAD)
- prot["head"] = max(1 - I.permeability_coefficient, prot["head"])
- var/protection = (prot["head"] + prot["arms"] + prot["feet"] + prot["legs"] + prot["groin"] + prot["chest"] + prot["hands"])/7
- return protection
-
/mob/living/carbon/human/can_use_guns(obj/item/G)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 176d967d52..4e99120940 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -167,9 +167,9 @@
dropItemToGround(r_store, TRUE) //Again, makes sense for pockets to drop.
if(l_store)
dropItemToGround(l_store, TRUE)
- if(wear_id)
+ if(wear_id && !CHECK_BITFIELD(wear_id.obj_flags, NO_UNIFORM_REQUIRED))
dropItemToGround(wear_id)
- if(belt)
+ if(belt && !CHECK_BITFIELD(belt.obj_flags, NO_UNIFORM_REQUIRED))
dropItemToGround(belt)
w_uniform = null
update_suit_sensors()
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 76d8a10474..196ba9b3f9 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1019,13 +1019,12 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(SLOT_BELT)
if(H.belt)
return FALSE
-
- var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
-
- if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
- if(!disable_warning)
- to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
- return FALSE
+ if(!CHECK_BITFIELD(I.obj_flags, NO_UNIFORM_REQUIRED))
+ var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
+ if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
+ if(!disable_warning)
+ to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
+ return FALSE
if(!(I.slot_flags & ITEM_SLOT_BELT))
return
return equip_delay_self_check(I, H, bypass_equip_delay_self)
@@ -1062,12 +1061,12 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(SLOT_WEAR_ID)
if(H.wear_id)
return FALSE
-
- var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
- if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
- if(!disable_warning)
- to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
- return FALSE
+ if(!CHECK_BITFIELD(I.obj_flags, NO_UNIFORM_REQUIRED))
+ var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
+ if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
+ if(!disable_warning)
+ to_chat(H, "You need a jumpsuit before you can attach this [I.name]!")
+ return FALSE
if( !(I.slot_flags & ITEM_SLOT_ID) )
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index f1a6b58cd1..f9c2e2dd3d 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -152,15 +152,6 @@
return threatcount
-/mob/living/carbon/monkey/get_permeability_protection()
- var/protection = 0
- if(head)
- protection = 1 - head.permeability_coefficient
- if(wear_mask)
- protection = max(1 - wear_mask.permeability_coefficient, protection)
- protection = protection/7 //the rest of the body isn't covered.
- return protection
-
/mob/living/carbon/monkey/IsVocal()
if(!getorganslot(ORGAN_SLOT_LUNGS))
return 0
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index ea0f7ae388..0559e4f3d8 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -813,7 +813,7 @@
return 1
//used in datum/reagents/reaction() proc
-/mob/living/proc/get_permeability_protection()
+/mob/living/proc/get_permeability_protection(list/target_zones)
return 0
/mob/living/proc/harvest(mob/living/user) //used for extra objects etc. in butchering
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index a0126f5fdd..0cb886f11b 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -65,6 +65,7 @@
var/active_hand_index = 1
var/list/held_items = list() //len = number of hands, eg: 2 nulls is 2 empty hands, 1 item and 1 null is 1 full hand and 1 empty hand.
//held_items[active_hand_index] is the actively held item, but please use get_active_held_item() instead, because OOP
+ var/bloody_hands = 0
var/datum/component/storage/active_storage = null//Carbon
diff --git a/code/modules/reagents/reagent_containers/rags.dm b/code/modules/reagents/reagent_containers/rags.dm
new file mode 100644
index 0000000000..a65b07abd7
--- /dev/null
+++ b/code/modules/reagents/reagent_containers/rags.dm
@@ -0,0 +1,124 @@
+/obj/item/reagent_containers/rag
+ name = "damp rag"
+ desc = "For cleaning up messes, you suppose."
+ w_class = WEIGHT_CLASS_TINY
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "rag"
+ item_flags = NOBLUDGEON
+ reagent_flags = OPENCONTAINER
+ amount_per_transfer_from_this = 5
+ possible_transfer_amounts = list()
+ volume = 5
+ spillable = FALSE
+ var/wipe_sound
+ var/soak_efficiency = 1
+ var/extinguish_efficiency = 0
+
+/obj/item/reagent_containers/rag/suicide_act(mob/user)
+ user.visible_message("[user] is smothering [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (OXYLOSS)
+
+/obj/item/reagent_containers/rag/afterattack(atom/A as obj|turf|area, mob/user,proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(iscarbon(A) && A.reagents && reagents.total_volume)
+ var/mob/living/carbon/C = A
+ var/reagentlist = pretty_string_from_reagent_list(reagents)
+ var/log_object = "a damp rag containing [reagentlist]"
+ if(user.a_intent == INTENT_HARM && !C.is_mouth_covered())
+ reagents.reaction(C, INGEST)
+ reagents.trans_to(C, 5)
+ C.visible_message("[user] has smothered \the [C] with \the [src]!", "[user] has smothered you with \the [src]!", "You hear some struggling and muffled cries of surprise.")
+ log_combat(user, C, "smothered", log_object)
+ else
+ reagents.reaction(C, TOUCH)
+ reagents.remove_all(5)
+ C.visible_message("[user] has touched \the [C] with \the [src].")
+ log_combat(user, C, "touched", log_object)
+
+ else if(istype(A) && src in user)
+ user.visible_message("[user] starts to wipe down [A] with [src]!", "You start to wipe down [A] with [src]...")
+ if(do_after(user,30, target = A))
+ user.visible_message("[user] finishes wiping off [A]!", "You finish wiping off [A].")
+ SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
+ return
+
+/obj/item/reagent_containers/rag/pre_altattackby(mob/living/M, mob/living/user, params)
+ if(istype(M) && user.a_intent == INTENT_HELP)
+ user.changeNext_move(CLICK_CD_MELEE)
+ if(user.on_fire)
+ user.visible_message("\The [user] uses \the [src] to pat out [M == user ? "[user.p_their()]" : "\the [M]'s"] flames!")
+ if(hitsound)
+ playsound(M, hitsound, 25, 1)
+ M.adjust_fire_stacks(-extinguish_efficiency)
+ else
+ user.visible_message("\The [user] starts drying [M == user ? "[user.p_them()]self" : "\the [M]"] off with \the [src]...")
+ if(do_mob(user, M, 3 SECONDS))
+ user.visible_message("\The [user] dries [M == user ? "[user.p_them()]self" : "\the [M]"] off with \the [src].")
+ if(wipe_sound)
+ playsound(M, wipe_sound, 25, 1)
+ M.adjust_fire_stacks(-soak_efficiency)
+ return TRUE
+ return ..()
+
+/obj/item/reagent_containers/rag/towel
+ name = "towel"
+ desc = "A soft cotton towel."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "towel"
+ item_state = "towel"
+ slot_flags = ITEM_SLOT_HEAD | ITEM_SLOT_BELT | ITEM_SLOT_OCLOTHING
+ obj_flags = NO_UNIFORM_REQUIRED //so it can be worn on the belt slot even with no uniform.
+ force = 1
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb = list("whipped")
+ hitsound = 'sound/items/towelwhip.ogg'
+ volume = 10
+ total_mass = 2
+ wipe_sound = 'sound/items/towelwipe.ogg'
+ soak_efficiency = 2
+ extinguish_efficiency = 2
+ var/flat_icon = "towel_flat"
+ var/folded_icon = "towel"
+
+/obj/item/reagent_containers/rag/towel/attack(mob/living/M, mob/living/user)
+ if(user.a_intent == INTENT_HARM)
+ DISABLE_BITFIELD(item_flags, NOBLUDGEON)
+ . = TRUE
+ ..()
+ if(.)
+ ENABLE_BITFIELD(item_flags, NOBLUDGEON)
+
+/obj/item/reagent_containers/rag/towel/equipped(mob/living/user, slot)
+ . = ..()
+ switch(slot)
+ if(SLOT_BELT)
+ body_parts_covered = GROIN|LEGS
+ if(SLOT_WEAR_SUIT)
+ body_parts_covered = CHEST|GROIN|LEGS
+ if(SLOT_HEAD)
+ body_parts_covered = HEAD
+ flags_inv = HIDEHAIR
+
+/obj/item/reagent_containers/rag/towel/dropped(mob/user)
+ . = ..()
+ body_parts_covered = NONE
+ flags_inv = NONE
+
+/obj/item/reagent_containers/rag/towel/attack_self(mob/user)
+ if(!user.CanReach(src) || !user.dropItemToGround(src))
+ return
+ to_chat(user, "You lay out \the [src] flat on the ground.")
+ icon_state = flat_icon
+ layer = BELOW_OBJ_LAYER
+ qdel(src)
+
+/obj/item/reagent_containers/rag/towel/pickup(mob/living/user)
+ . = ..()
+ icon_state = folded_icon
+ layer = initial(layer)
+
+/obj/item/reagent_containers/rag/towel/random/Initialize()
+ . = ..()
+ add_atom_colour(pick("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"), FIXED_COLOUR_PRIORITY)
diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm
index 59440cc3ee..1eca915d70 100644
--- a/code/modules/surgery/helpers.dm
+++ b/code/modules/surgery/helpers.dm
@@ -110,22 +110,15 @@
return 0.5
-/proc/get_location_accessible(mob/M, location)
+/proc/get_location_accessible(mob/living/M, location)
var/covered_locations = 0 //based on body_parts_covered
var/face_covered = 0 //based on flags_inv
var/eyesmouth_covered = 0 //based on flags_cover
- if(iscarbon(M))
- var/mob/living/carbon/C = M
- for(var/obj/item/clothing/I in list(C.back, C.wear_mask, C.head))
- covered_locations |= I.body_parts_covered
- face_covered |= I.flags_inv
- eyesmouth_covered |= I.flags_cover
- if(ishuman(C))
- var/mob/living/carbon/human/H = C
- for(var/obj/item/I in list(H.wear_suit, H.w_uniform, H.shoes, H.belt, H.gloves, H.glasses, H.ears))
- covered_locations |= I.body_parts_covered
- face_covered |= I.flags_inv
- eyesmouth_covered |= I.flags_cover
+ for(var/A in M.get_equipped_items())
+ var/obj/item/I = A
+ covered_locations |= I.body_parts_covered
+ face_covered |= I.flags_inv
+ eyesmouth_covered |= I.flags_cover
switch(location)
if(BODY_ZONE_HEAD)
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index 9481d4d086..1bc0e242c8 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -199,7 +199,7 @@
/obj/item/clothing/suit/apron/purple_bartender = 2,
/obj/item/clothing/head/soft/black = 4,
/obj/item/clothing/shoes/sneakers/black = 4,
- /obj/item/reagent_containers/glass/rag = 4,
+ /obj/item/reagent_containers/rag = 4,
/obj/item/storage/box/beanbag = 1,
/obj/item/clothing/suit/armor/vest/alt = 1,
/obj/item/circuitboard/machine/dish_drive = 1,
@@ -227,7 +227,7 @@
/obj/item/clothing/suit/toggle/chef = 2,
/obj/item/clothing/under/rank/chef = 2,
/obj/item/clothing/head/chefhat = 2,
- /obj/item/reagent_containers/glass/rag = 3)
+ /obj/item/reagent_containers/rag = 3)
refill_canister = /obj/item/vending_refill/wardrobe/chef_wardrobe
/obj/item/vending_refill/wardrobe/chef_wardrobe
diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi
index 02fdc52b45..f1e2f4e85a 100644
Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 09d6fe5374..3ca7290a0f 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index f1b125eb20..71c453856a 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 7ef316be17..0ca09ad810 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index 42c32f134a..835f3448b4 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 0d68f00c8b..159805d4ce 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/sound/items/towelwhip.ogg b/sound/items/towelwhip.ogg
new file mode 100644
index 0000000000..49dab750a8
Binary files /dev/null and b/sound/items/towelwhip.ogg differ
diff --git a/sound/items/towelwipe.ogg b/sound/items/towelwipe.ogg
new file mode 100644
index 0000000000..2bee55c38d
Binary files /dev/null and b/sound/items/towelwipe.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 3e493d7135..06f5c09b13 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1545,7 +1545,6 @@
#include "code\modules\crafting\recipes.dm"
#include "code\modules\detectivework\detective_work.dm"
#include "code\modules\detectivework\evidence.dm"
-#include "code\modules\detectivework\footprints_and_rag.dm"
#include "code\modules\detectivework\scanner.dm"
#include "code\modules\emoji\emoji_parse.dm"
#include "code\modules\error_handler\error_handler.dm"
@@ -2498,6 +2497,7 @@
#include "code\modules\reagents\reagent_containers\medspray.dm"
#include "code\modules\reagents\reagent_containers\patch.dm"
#include "code\modules\reagents\reagent_containers\pill.dm"
+#include "code\modules\reagents\reagent_containers\rags.dm"
#include "code\modules\reagents\reagent_containers\spray.dm"
#include "code\modules\reagents\reagent_containers\syringes.dm"
#include "code\modules\recycling\conveyor2.dm"