From d6aef39e573ebf74b9dcec9ce4075ec37b999137 Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Thu, 12 Dec 2024 18:36:07 +1100
Subject: [PATCH 01/10] Cleaning up equip_adjust list access/definition.

---
 code/game/objects/item_mob_overlay.dm         |  2 +-
 code/modules/bodytype/bodytype_offsets.dm     | 14 +--
 code/modules/mob/living/human/update_icons.dm | 38 ++++----
 .../mob/living/silicon/robot/drone/drone.dm   | 12 +--
 .../mob/living/simple_animal/friendly/cat.dm  | 16 ++--
 .../living/simple_animal/friendly/corgi.dm    | 16 ++--
 .../mob/living/simple_animal/friendly/crab.dm |  8 +-
 .../mob/living/simple_animal/passive/deer.dm  |  8 +-
 .../mob/living/simple_animal/passive/fox.dm   |  8 +-
 .../species/station/monkey_bodytypes.dm       | 12 +--
 mods/content/fantasy/datum/hnoll/bodytypes.dm | 10 +--
 .../fantasy/datum/kobaloi/bodytypes.dm        | 32 +++----
 mods/mobs/dionaea/mob/_nymph.dm               |  8 +-
 .../species/ascent/datum/species_bodytypes.dm |  4 +-
 .../adherent/datum/species_bodytypes.dm       | 26 +++---
 .../tajaran/datum/species_bodytypes.dm        | 23 ++++-
 mods/species/drakes/species_bodytypes.dm      | 70 +++++++--------
 .../neoavians/datum/species_bodytypes.dm      | 86 ++++++++++++++++---
 .../serpentid/datum/species_bodytypes.dm      | 58 +++++++++++--
 .../utility_frames/species_bodytypes.dm       |  8 +-
 mods/species/vox/datum/species_bodytypes.dm   | 22 ++---
 21 files changed, 293 insertions(+), 188 deletions(-)

diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm
index 5827dbba478..be29ad618ca 100644
--- a/code/game/objects/item_mob_overlay.dm
+++ b/code/game/objects/item_mob_overlay.dm
@@ -70,7 +70,7 @@ var/global/list/icon_state_cache = list()
 		var/mob_icon = global.default_onmob_icons[slot]
 		var/decl/bodytype/root_bodytype = user_mob?.get_bodytype()
 		if(istype(root_bodytype))
-			var/use_slot = (bodypart in root_bodytype.equip_adjust) ? bodypart : slot
+			var/use_slot = (bodypart in root_bodytype.get_equip_adjustments(user_mob)) ? bodypart : slot
 			return root_bodytype.get_offset_overlay_image(user_mob, mob_icon, mob_state, color, use_slot)
 		return overlay_image(mob_icon, mob_state, color, RESET_COLOR)
 
diff --git a/code/modules/bodytype/bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm
index e0090189112..279b43e9918 100644
--- a/code/modules/bodytype/bodytype_offsets.dm
+++ b/code/modules/bodytype/bodytype_offsets.dm
@@ -4,8 +4,8 @@ each one can be in the NORTH, SOUTH, EAST, and WEST direction. Specify
 the x and y amounts to shift the thing for a given direction.
 
 example:
-	equip_adjust = list(
-		slot_back_str = list("[NORTH]" = list(-12, 7), "[EAST]" = list(-2, -12))
+	_equip_adjust = list(
+		(slot_back_str) = list("[NORTH]" = list(-12, 7), "[EAST]" = list(-2, -12))
 	)
 
 This would shift back items (backpacks, axes, etc.) when the mob
@@ -18,15 +18,15 @@ The slots that you can use are found in items_clothing.dm and are the inventory
 */
 
 /decl/bodytype
-	var/list/equip_adjust
-	var/list/equip_overlays = list()
+	VAR_PRIVATE/list/_equip_adjust
+	VAR_PRIVATE/list/equip_overlays = list()
 
-/decl/bodytype/proc/get_equip_adjust(mob/mob)
-	return equip_adjust
+/decl/bodytype/proc/get_equip_adjustments(mob/mob)
+	return _equip_adjust
 
 /decl/bodytype/proc/get_offset_overlay_image(mob/mob, mob_icon, mob_state, color, slot)
 	// If we don't actually need to offset this, don't bother with any of the generation/caching.
-	var/list/use_equip_adjust = get_equip_adjust(mob)
+	var/list/use_equip_adjust = get_equip_adjustments(mob)
 	if(length(use_equip_adjust) && use_equip_adjust[slot] && length(use_equip_adjust[slot]))
 
 		// Check the cache for previously made icons.
diff --git a/code/modules/mob/living/human/update_icons.dm b/code/modules/mob/living/human/update_icons.dm
index 34fcb8ccc31..eafc3556942 100644
--- a/code/modules/mob/living/human/update_icons.dm
+++ b/code/modules/mob/living/human/update_icons.dm
@@ -320,27 +320,23 @@ Please contact me on #coderbus IRC. ~Carn x
 //UNDERWEAR OVERLAY
 
 /mob/living/human/proc/update_underwear(var/update_icons=1)
-	var/list/undies = list()
-	for(var/entry in worn_underwear)
-
-		var/obj/item/underwear/UW = entry
-		if (!UW?.icon) // Avoid runtimes for nude underwear types
-			continue
-
-		var/decl/bodytype/root_bodytype = get_bodytype()
-		if(!root_bodytype)
-			continue // Avoid runtimes for dummy mobs with no bodytype set
-
-		var/image/I
-		if(UW.slot_offset_str && LAZYACCESS(root_bodytype.equip_adjust, UW.slot_offset_str))
-			I = root_bodytype.get_offset_overlay_image(src, UW.icon, UW.icon_state, UW.color, UW.slot_offset_str)
-		else
-			I = image(icon = UW.icon, icon_state = UW.icon_state)
-			I.color = UW.color
-		if(I) // get_offset_overlay_image() may potentially return null
-			I.appearance_flags |= RESET_COLOR
-			undies += I
-	set_current_mob_overlay(HO_UNDERWEAR_LAYER, undies, update_icons)
+	var/list/undies_overlays = list()
+	var/decl/bodytype/root_bodytype = get_bodytype()
+	if(root_bodytype)
+		var/list/adjustments = root_bodytype.get_equip_adjustments(src)
+		for(var/obj/item/underwear/undies as anything in worn_underwear)
+			if (!undies?.icon) // Avoid runtimes for nude underwear types
+				continue
+			var/image/undies_overlay
+			if(undies.slot_offset_str && (undies.slot_offset_str in adjustments))
+				undies_overlay = root_bodytype.get_offset_overlay_image(src, undies.icon, undies.icon_state, undies.color, undies.slot_offset_str)
+			else
+				undies_overlay = image(icon = undies.icon, icon_state = undies.icon_state)
+				undies_overlay.color = undies.color
+			if(undies_overlay) // get_offset_overlay_image() may potentially return null
+				undies_overlay.appearance_flags |= RESET_COLOR
+				undies_overlays += undies_overlay
+	set_current_mob_overlay(HO_UNDERWEAR_LAYER, undies_overlays, update_icons)
 
 /mob/living/human/update_hair(var/update_icons=1)
 	var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD, /obj/item/organ/external/head)
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 0563fce5521..90516b06381 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -117,7 +117,7 @@
 	uid = "bodytype_drone_construction"
 
 /decl/bodytype/drone/construction/Initialize()
-	equip_adjust = list(
+	_equip_adjust = list(
 		slot_head_str = list(
 			"[NORTH]" = list(1, -12),
 			"[SOUTH]" = list(1, -12),
@@ -355,13 +355,13 @@
 	uid = "bodytype_drone"
 
 /decl/bodytype/drone/Initialize()
-	if(!length(equip_adjust))
-		equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_equip_adjust))
+		_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list(0, -13),
 				"[SOUTH]" = list(0, -13),
-				"[EAST]" =  list(0, -13),
-				"[WEST]" =  list(0, -13)
+				"[EAST]"  = list(0, -13),
+				"[WEST]"  = list(0, -13)
 			)
 		)
 	. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index f4b20f9b75b..a9b8e6891b8 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -70,12 +70,12 @@
 	uid = "bodytype_animal_cat"
 
 /decl/bodytype/quadruped/animal/cat/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 1,  -9),
 			"[SOUTH]" = list( 1, -12),
-			"[EAST]" =  list( 7, -10),
-			"[WEST]" =  list(-7, -10)
+			"[EAST]"  = list( 7, -10),
+			"[WEST]"  = list(-7, -10)
 		)
 	)
 	. = ..()
@@ -210,12 +210,12 @@
 	uid = "bodytype_animal_kitten"
 
 /decl/bodytype/quadruped/animal/kitten/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 1, -14),
 			"[SOUTH]" = list( 1, -14),
-			"[EAST]" =  list( 5, -14),
-			"[WEST]" =  list(-5, -14)
+			"[EAST]"  = list( 5, -14),
+			"[WEST]"  = list(-5, -14)
 		)
 	)
 	. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm
index c8ee4417ebf..64ea0d6d25f 100644
--- a/code/modules/mob/living/simple_animal/friendly/corgi.dm
+++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm
@@ -42,12 +42,12 @@
 	uid = "bodytype_animal_corgi"
 
 /decl/bodytype/quadruped/animal/corgi/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 1, -8),
 			"[SOUTH]" = list( 1, -8),
-			"[EAST]" =  list( 7, -8),
-			"[WEST]" =  list(-7, -8)
+			"[EAST]"  = list( 7, -8),
+			"[WEST]"  = list(-7, -8)
 		)
 	)
 	. = ..()
@@ -151,12 +151,12 @@
 	uid = "bodytype_animal_puppy"
 
 /decl/bodytype/quadruped/animal/puppy/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 0, -12),
 			"[SOUTH]" = list( 0, -12),
-			"[EAST]" =  list( 5, -14),
-			"[WEST]" =  list(-5, -14)
+			"[EAST]"  = list( 5, -14),
+			"[WEST]"  = list(-5, -14)
 		)
 	)
 	. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm
index 992ee0b67a9..bcd84ddc3df 100644
--- a/code/modules/mob/living/simple_animal/friendly/crab.dm
+++ b/code/modules/mob/living/simple_animal/friendly/crab.dm
@@ -42,12 +42,12 @@
 	uid = "bodytype_animal_crab"
 
 /decl/bodytype/hexapod/animal/crab/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list(-1, -10),
 			"[SOUTH]" = list(-1, -10),
-			"[EAST]" =  list(-1, -10),
-			"[WEST]" =  list(-1, -10)
+			"[EAST]"  = list(-1, -10),
+			"[WEST]"  = list(-1, -10)
 		)
 	)
 	. = ..()
diff --git a/code/modules/mob/living/simple_animal/passive/deer.dm b/code/modules/mob/living/simple_animal/passive/deer.dm
index 03acf0a006d..da09424bce8 100644
--- a/code/modules/mob/living/simple_animal/passive/deer.dm
+++ b/code/modules/mob/living/simple_animal/passive/deer.dm
@@ -36,12 +36,12 @@
 	uid = "bodytype_animal_deer"
 
 /decl/bodytype/quadruped/animal/deer/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 1, -4),
 			"[SOUTH]" = list( 1, -4),
-			"[EAST]" =  list( 9, -4),
-			"[WEST]" =  list(-9, -4)
+			"[EAST]"  = list( 9, -4),
+			"[WEST]"  = list(-9, -4)
 		)
 	)
 	return ..()
diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm
index cfa2421fcd3..c2db673e6e8 100644
--- a/code/modules/mob/living/simple_animal/passive/fox.dm
+++ b/code/modules/mob/living/simple_animal/passive/fox.dm
@@ -32,12 +32,12 @@
 	uid = "bodytype_animal_fox"
 
 /decl/bodytype/quadruped/animal/fox/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list( 1,  -9),
 			"[SOUTH]" = list( 1,  -8),
-			"[EAST]" =  list( 11,  -9),
-			"[WEST]" =  list(-11,  -9)
+			"[EAST]"  = list( 11,  -9),
+			"[WEST]"  = list(-11,  -9)
 		)
 	)
 	return ..()
diff --git a/code/modules/species/station/monkey_bodytypes.dm b/code/modules/species/station/monkey_bodytypes.dm
index 804d6d0add1..b1b22de545c 100644
--- a/code/modules/species/station/monkey_bodytypes.dm
+++ b/code/modules/species/station/monkey_bodytypes.dm
@@ -19,12 +19,12 @@
 	uid = "bodytype_monkey"
 
 /decl/bodytype/monkey/Initialize()
-	equip_adjust = list(
-		BP_L_HAND =          list("[NORTH]" = list( 1, 3), "[EAST]" = list(-3, 2), "[SOUTH]" = list(-1, 3), "[WEST]" = list( 3, 2)),
-		BP_R_HAND =          list("[NORTH]" = list(-1, 3), "[EAST]" = list( 3, 2), "[SOUTH]" = list( 1, 3), "[WEST]" = list(-3, 2)),
-		slot_shoes_str =     list("[NORTH]" = list( 0, 7), "[EAST]" = list(-1, 7), "[SOUTH]" = list( 0, 7), "[WEST]" = list( 1, 7)),
-		slot_head_str =      list("[NORTH]" = list( 0, 0), "[EAST]" = list(-2, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 2, 0)),
-		slot_wear_mask_str = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-1, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 1, 0))
+	_equip_adjust = list(
+		(BP_L_HAND)          = list("[NORTH]" = list( 1, 3), "[EAST]" = list(-3, 2), "[SOUTH]" = list(-1, 3), "[WEST]" = list( 3, 2)),
+		(BP_R_HAND)          = list("[NORTH]" = list(-1, 3), "[EAST]" = list( 3, 2), "[SOUTH]" = list( 1, 3), "[WEST]" = list(-3, 2)),
+		(slot_shoes_str)     = list("[NORTH]" = list( 0, 7), "[EAST]" = list(-1, 7), "[SOUTH]" = list( 0, 7), "[WEST]" = list( 1, 7)),
+		(slot_head_str)      = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-2, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 2, 0)),
+		(slot_wear_mask_str) = list("[NORTH]" = list( 0, 0), "[EAST]" = list(-1, 0), "[SOUTH]" = list( 0, 0), "[WEST]" = list( 1, 0))
 	)
 	. = ..()
 
diff --git a/mods/content/fantasy/datum/hnoll/bodytypes.dm b/mods/content/fantasy/datum/hnoll/bodytypes.dm
index b5da3ed52b5..218068362ce 100644
--- a/mods/content/fantasy/datum/hnoll/bodytypes.dm
+++ b/mods/content/fantasy/datum/hnoll/bodytypes.dm
@@ -55,21 +55,21 @@
 	)
 
 /decl/bodytype/hnoll/Initialize()
-	if(!equip_adjust)
-		equip_adjust = list(
-			slot_glasses_str = list(
+	if(!_equip_adjust)
+		_equip_adjust = list(
+			(slot_glasses_str) = list(
 				"[NORTH]" = list( 0, 2),
 				"[EAST]"  = list( 0, 2),
 				"[SOUTH]" = list( 0, 2),
 				"[WEST]"  = list( 0, 2)
 			),
-			slot_wear_mask_str = list(
+			(slot_wear_mask_str) = list(
 				"[NORTH]" = list( 0, 2),
 				"[EAST]"  = list( 2, 2),
 				"[SOUTH]" = list( 0, 2),
 				"[WEST]"  = list(-2, 2)
 			),
-			slot_head_str = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 0, 2),
 				"[EAST]"  = list( 0, 2),
 				"[SOUTH]" = list( 0, 2),
diff --git a/mods/content/fantasy/datum/kobaloi/bodytypes.dm b/mods/content/fantasy/datum/kobaloi/bodytypes.dm
index 3e87eafcf54..72740539a49 100644
--- a/mods/content/fantasy/datum/kobaloi/bodytypes.dm
+++ b/mods/content/fantasy/datum/kobaloi/bodytypes.dm
@@ -32,87 +32,87 @@
 	uid = "bodytype_kobaloi"
 
 /decl/bodytype/kobaloi/Initialize()
-	if(!equip_adjust)
-		equip_adjust = list(
-			BP_R_HAND = list(
+	if(!_equip_adjust)
+		_equip_adjust = list(
+			(BP_R_HAND) = list(
 				"[NORTH]" = list( 1, -4),
 				"[EAST]"  = list( 0, -4),
 				"[SOUTH]" = list(-1, -4),
 				"[WEST]"  = list(-1, -4)
 			),
-			BP_L_HAND = list(
+			(BP_L_HAND) = list(
 				"[NORTH]" = list(-1, -4),
 				"[EAST]"  = list( 1, -4),
 				"[SOUTH]" = list( 1, -4),
 				"[WEST]"  = list( 0, -4)
 			),
-			slot_w_uniform_str = list(
+			(slot_w_uniform_str) = list(
 				"[NORTH]" = list( 0, -6),
 				"[EAST]"  = list( 1, -6),
 				"[SOUTH]" = list( 0, -6),
 				"[WEST]"  = list(-1, -6)
 			),
-			slot_belt_str = list(
+			(slot_belt_str) = list(
 				"[NORTH]" = list( 0, -6),
 				"[EAST]"  = list( 1, -6),
 				"[SOUTH]" = list( 0, -6),
 				"[WEST]"  = list(-1, -6)
 			),
-			slot_handcuffed_str = list(
+			(slot_handcuffed_str) = list(
 				"[NORTH]" = list(-1, -4),
 				"[EAST]"  = list( 1, -4),
 				"[SOUTH]" = list( 1, -4),
 				"[WEST]"  = list( 0, -4)
 			),
-			slot_wear_id_str = list(
+			(slot_wear_id_str) = list(
 				"[NORTH]" = list( 0, -6),
 				"[EAST]"  = list( 1, -6),
 				"[SOUTH]" = list( 0, -6),
 				"[WEST]"  = list(-1, -6)
 			),
-			slot_gloves_str = list(
+			(slot_gloves_str) = list(
 				"[NORTH]" = list(-1, -4),
 				"[EAST]"  = list( 1, -4),
 				"[SOUTH]" = list( 1, -4),
 				"[WEST]"  = list( 0, -4)
 			),
-			slot_wear_suit_str = list(
+			(slot_wear_suit_str) = list(
 				"[NORTH]" = list( 0, -6),
 				"[EAST]"  = list( 1, -6),
 				"[SOUTH]" = list( 0, -6),
 				"[WEST]"  = list(-1, -6)
 			),
-			slot_back_str = list(
+			(slot_back_str) = list(
 				"[NORTH]" = list( 0, -5),
 				"[EAST]"  = list( 1, -5),
 				"[SOUTH]" = list( 0, -5),
 				"[WEST]"  = list(-1, -5)
 			),
-			slot_glasses_str = list(
+			(slot_glasses_str) = list(
 				"[NORTH]" = list( 0, -6),
 				"[EAST]"  = list( 3, -6),
 				"[SOUTH]" = list( 0, -6),
 				"[WEST]"  = list(-3, -6)
 			),
-			slot_wear_mask_str = list(
+			(slot_wear_mask_str) = list(
 				"[NORTH]" = list( 0, -7),
 				"[EAST]"  = list( 5, -7),
 				"[SOUTH]" = list( 0, -7),
 				"[WEST]"  = list(-5, -7)
 			),
-			slot_head_str = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 0, -5),
 				"[EAST]"  = list( 3, -5),
 				"[SOUTH]" = list( 0, -5),
 				"[WEST]"  = list(-3, -5)
 			),
-			slot_l_ear_str = list(
+			(slot_l_ear_str) = list(
 				"[NORTH]" = list( 0, -5),
 				"[EAST]"  = list( 3, -5),
 				"[SOUTH]" = list( 0, -5),
 				"[WEST]"  = list(-3, -5)
 			),
-			slot_r_ear_str = list(
+			(slot_r_ear_str) = list(
 				"[NORTH]" = list( 0, -5),
 				"[EAST]"  = list( 3, -5),
 				"[SOUTH]" = list( 0, -5),
diff --git a/mods/mobs/dionaea/mob/_nymph.dm b/mods/mobs/dionaea/mob/_nymph.dm
index 6ec480cda3b..a1c8efe4486 100644
--- a/mods/mobs/dionaea/mob/_nymph.dm
+++ b/mods/mobs/dionaea/mob/_nymph.dm
@@ -95,12 +95,12 @@
 	uid = "bodytype_diona"
 
 /decl/bodytype/diona/Initialize()
-	equip_adjust = list(
-		slot_head_str = list(
+	_equip_adjust = list(
+		(slot_head_str) = list(
 			"[NORTH]" = list(0, -8),
 			"[SOUTH]" = list(0, -8),
-			"[EAST]" =  list(0, -8),
-			"[WEST]" =  list(0, -8)
+			"[EAST]"  = list(0, -8),
+			"[WEST]"  = list(0, -8)
 		)
 	)
 	. = ..()
diff --git a/mods/species/ascent/datum/species_bodytypes.dm b/mods/species/ascent/datum/species_bodytypes.dm
index 05ca4ed987f..9136d47e269 100644
--- a/mods/species/ascent/datum/species_bodytypes.dm
+++ b/mods/species/ascent/datum/species_bodytypes.dm
@@ -85,8 +85,8 @@
 	uid = "bodytype_crystalline_gyne"
 
 /decl/bodytype/crystalline/mantid/gyne/Initialize()
-	equip_adjust = list(
-		BP_L_HAND = list(
+	_equip_adjust = list(
+		(BP_L_HAND) = list(
 			"[NORTH]" = list(-4, 12),
 			"[EAST]"  = list(-4, 12),
 			"[SOUTH]" = list(-4, 12),
diff --git a/mods/species/bayliens/adherent/datum/species_bodytypes.dm b/mods/species/bayliens/adherent/datum/species_bodytypes.dm
index 8e6c7ab2ee9..bbf275eb855 100644
--- a/mods/species/bayliens/adherent/datum/species_bodytypes.dm
+++ b/mods/species/bayliens/adherent/datum/species_bodytypes.dm
@@ -53,54 +53,48 @@
 	uid = "bodytype_crystalline_adherent_turquoise"
 
 /decl/bodytype/crystalline/adherent/Initialize()
-	equip_adjust = list(
-		"[BP_L_HAND]" = list(
+	_equip_adjust = list(
+		(BP_L_HAND) = list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
 			"[WEST]"  = list(0, 14)
 		),
-
-		"[BP_R_HAND]" = list(
+		(BP_R_HAND) = list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
 			"[WEST]"  = list(0, 14)
 		),
-
-		"[slot_back_str]" = list(
+		(slot_back_str) = list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
 			"[WEST]"  = list(0, 14)
 		),
-
-		"[slot_belt_str]" = list(
+		(slot_belt_str) = list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
 			"[WEST]"  = list(0, 14)
 		),
-
-		"[slot_head_str]" =   list(
+		(slot_head_str) =   list(
 			"[NORTH]" = list( 0, 14),
 			"[EAST]"  = list( 3, 14),
 			"[SOUTH]" = list( 0, 14),
 			"[WEST]"  = list(-3, 14)
 		),
-
-		"[slot_l_ear_str]" =  list(
+		(slot_l_ear_str) =  list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
-			"[WEST]"  = list(0,  14)
+			"[WEST]"  = list(0, 14)
 		),
-
-		"[slot_r_ear_str]" =  list(
+		(slot_r_ear_str) =  list(
 			"[NORTH]" = list(0, 14),
 			"[EAST]"  = list(0, 14),
 			"[SOUTH]" = list(0, 14),
-			"[WEST]"  = list(0,  14)
+			"[WEST]"  = list(0, 14)
 		)
 	)
 	. = ..()
diff --git a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm
index 9dbd49a90fe..225bf5bc86e 100644
--- a/mods/species/bayliens/tajaran/datum/species_bodytypes.dm
+++ b/mods/species/bayliens/tajaran/datum/species_bodytypes.dm
@@ -52,10 +52,25 @@
 	)
 
 /decl/bodytype/feline/Initialize()
-	equip_adjust = list(
-		slot_glasses_str =   list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2),  "[WEST]" = list(0, 2)),
-		slot_wear_mask_str = list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2),  "[WEST]" = list(0, 2)),
-		slot_head_str =      list("[NORTH]" = list(0, 2), "[EAST]" = list(0, 2), "[SOUTH]" = list( 0, 2),  "[WEST]" = list(0, 2))
+	_equip_adjust = list(
+		(slot_glasses_str) = list(
+			"[NORTH]" = list(0, 2),
+			"[EAST]"  = list(0, 2),
+			"[SOUTH]" = list( 0, 2),
+			"[WEST]"  = list(0, 2)
+		),
+		(slot_wear_mask_str) = list(
+			"[NORTH]" = list(0, 2),
+			"[EAST]"  = list(0, 2),
+			"[SOUTH]" = list( 0, 2),
+			"[WEST]"  = list(0, 2)
+		),
+		(slot_head_str) = list(
+			"[NORTH]" = list(0, 2),
+			"[EAST]"  = list(0, 2),
+			"[SOUTH]" = list( 0, 2),
+			"[WEST]"  = list(0, 2)
+		)
 	)
 	. = ..()
 
diff --git a/mods/species/drakes/species_bodytypes.dm b/mods/species/drakes/species_bodytypes.dm
index 3d16db25762..dc5afea4ea4 100644
--- a/mods/species/drakes/species_bodytypes.dm
+++ b/mods/species/drakes/species_bodytypes.dm
@@ -134,48 +134,48 @@
 		ARMOR_BOMB   = ARMOR_BOMB_PADDED
 	)
 
-	var/list/sitting_equip_adjust
-	var/list/lying_equip_adjust
+	VAR_PRIVATE/list/_sitting_equip_adjust
+	VAR_PRIVATE/list/_lying_equip_adjust
 
 /decl/bodytype/quadruped/grafadreka/Initialize()
-	if(!length(equip_adjust))
-		equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_equip_adjust))
+		_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list(16,  -8),
 				"[SOUTH]" = list(16, -12),
-				"[EAST]" =  list(38,  -8),
-				"[WEST]" =  list(-6,  -8)
+				"[EAST]"  = list(38,  -8),
+				"[WEST]"  = list(-6,  -8)
 			)
 		)
 
-	if(!length(sitting_equip_adjust))
-		sitting_equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_sitting_equip_adjust))
+		_sitting_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list(16, -2),
 				"[SOUTH]" = list(16, -2),
-				"[EAST]" =  list(22, -2),
-				"[WEST]" =  list(12, -2)
+				"[EAST]"  = list(22, -2),
+				"[WEST]"  = list(12, -2)
 			)
 		)
 
-	if(!length(lying_equip_adjust))
-		lying_equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_lying_equip_adjust))
+		_lying_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 24, -24),
 				"[SOUTH]" = list( 24, -24),
-				"[EAST]" =  list( 24, -24),
-				"[WEST]" =  list(-10, -24)
+				"[EAST]"  = list( 24, -24),
+				"[WEST]"  = list(-10, -24)
 			)
 		)
 
 	return ..()
 
-/decl/bodytype/quadruped/grafadreka/get_equip_adjust(mob/mob)
+/decl/bodytype/quadruped/grafadreka/get_equip_adjustments(mob/mob)
 	switch(mob.current_posture?.name)
 		if("lying", "resting")
-			return lying_equip_adjust
+			return _lying_equip_adjust
 		if("sitting")
-			return sitting_equip_adjust
+			return _sitting_equip_adjust
 	return ..()
 
 /decl/bodytype/quadruped/grafadreka/hatchling
@@ -206,31 +206,31 @@
 	uid = "bodytype_drake_hatchling"
 
 /decl/bodytype/quadruped/grafadreka/hatchling/Initialize()
-	if(!length(equip_adjust))
-		equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_equip_adjust))
+		_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 0, -18),
 				"[SOUTH]" = list( 0, -18),
-				"[EAST]" =  list( 8, -18),
-				"[WEST]" =  list(-8, -18)
+				"[EAST]"  = list( 8, -18),
+				"[WEST]"  = list(-8, -18)
 			)
 		)
-	if(!length(sitting_equip_adjust))
-		sitting_equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_sitting_equip_adjust))
+		_sitting_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 0, -14),
 				"[SOUTH]" = list( 0, -14),
-				"[EAST]" =  list( 4, -14),
-				"[WEST]" =  list(-4, -14)
+				"[EAST]"  = list( 4, -14),
+				"[WEST]"  = list(-4, -14)
 			)
 		)
-	if(!length(lying_equip_adjust))
-		lying_equip_adjust = list(
-			slot_head_str = list(
+	if(!length(_lying_equip_adjust))
+		_lying_equip_adjust = list(
+			(slot_head_str) = list(
 				"[NORTH]" = list( 0, -24),
 				"[SOUTH]" = list( 0, -24),
-				"[EAST]" =  list( 0, -24),
-				"[WEST]" =  list( 0, -24)
+				"[EAST]"  = list( 0, -24),
+				"[WEST]"  = list( 0, -24)
 			)
 		)
 	return ..()
diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm
index 5843792a090..35eb54d9a7f 100644
--- a/mods/species/neoavians/datum/species_bodytypes.dm
+++ b/mods/species/neoavians/datum/species_bodytypes.dm
@@ -94,19 +94,79 @@
 	uid                  = "bodytype_avian_additive_raptor"
 
 /decl/bodytype/avian/Initialize()
-	equip_adjust = list(
-		slot_l_ear_str     = list("[NORTH]" = list( 1, -5), "[EAST]" = list(-2, -5), "[SOUTH]" = list(-1, -5),  "[WEST]" = list( 0, -5)),
-		slot_r_ear_str     = list("[NORTH]" = list( 1, -5), "[EAST]" = list( 0, -5), "[SOUTH]" = list(-1, -5),  "[WEST]" = list( 2, -5)),
-		BP_L_HAND          = list("[NORTH]" = list( 3, -3), "[EAST]" = list( 1, -3), "[SOUTH]" = list(-3, -3),  "[WEST]" = list(-5, -3)),
-		BP_R_HAND          = list("[NORTH]" = list(-3, -3), "[EAST]" = list( 5, -3), "[SOUTH]" = list( 3, -3),  "[WEST]" = list(-1, -3)),
-		slot_head_str      = list("[NORTH]" = list( 0, -5), "[EAST]" = list( 1, -5), "[SOUTH]" = list( 0, -5),  "[WEST]" = list(-1, -5)),
-		slot_wear_mask_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 2, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list(-2, -6)),
-		slot_glasses_str   = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 1, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list(-1, -6)),
-		slot_back_str      = list("[NORTH]" = list( 0, -6), "[EAST]" = list( 3, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list(-3, -6)),
-		slot_w_uniform_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list( 1, -6)),
-		slot_wear_id_str   = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list( 1, -6)),
-		slot_wear_suit_str = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list( 1, -6)),
-		slot_belt_str      = list("[NORTH]" = list( 0, -6), "[EAST]" = list(-1, -6), "[SOUTH]" = list( 0, -6),  "[WEST]" = list( 1, -6))
+	_equip_adjust = list(
+		(slot_l_ear_str) = list(
+			"[NORTH]" = list( 1, -5),
+			"[EAST]"  = list(-2, -5),
+			"[SOUTH]" = list(-1, -5),
+			"[WEST]"  = list( 0, -5)
+		),
+		(slot_r_ear_str) = list(
+			"[NORTH]" = list( 1, -5),
+			"[EAST]"  = list( 0, -5),
+			"[SOUTH]" = list(-1, -5),
+			"[WEST]"  = list( 2, -5)
+		),
+		(BP_L_HAND) = list(
+			"[NORTH]" = list( 3, -3),
+			"[EAST]"  = list( 1, -3),
+			"[SOUTH]" = list(-3, -3),
+			"[WEST]"  = list(-5, -3)
+		),
+		(BP_R_HAND) = list(
+			"[NORTH]" = list(-3, -3),
+			"[EAST]"  = list( 5, -3),
+			"[SOUTH]" = list( 3, -3),
+			"[WEST]"  = list(-1, -3)
+		),
+		(slot_head_str) = list(
+			"[NORTH]" = list( 0, -5),
+			"[EAST]"  = list( 1, -5),
+			"[SOUTH]" = list( 0, -5),
+			"[WEST]"  = list(-1, -5)
+		),
+		(slot_wear_mask_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list( 2, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list(-2, -6)
+		),
+		(slot_glasses_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list( 1, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list(-1, -6)
+		),
+		(slot_back_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list( 3, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list(-3, -6)
+		),
+		(slot_w_uniform_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list(-1, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list( 1, -6)
+		),
+		(slot_wear_id_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list(-1, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list( 1, -6)
+		),
+		(slot_wear_suit_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list(-1, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list( 1, -6)
+		),
+		(slot_belt_str) = list(
+			"[NORTH]" = list( 0, -6),
+			"[EAST]"  = list(-1, -6),
+			"[SOUTH]" = list( 0, -6),
+			"[WEST]"  = list( 1, -6)
+		)
 	)
 	. = ..()
 
diff --git a/mods/species/serpentid/datum/species_bodytypes.dm b/mods/species/serpentid/datum/species_bodytypes.dm
index de0c77e659f..6c4e562b451 100644
--- a/mods/species/serpentid/datum/species_bodytypes.dm
+++ b/mods/species/serpentid/datum/species_bodytypes.dm
@@ -65,15 +65,55 @@
 	)
 
 /decl/bodytype/serpentid/Initialize()
-	equip_adjust = list(
-		BP_L_HAND_UPPER =  list("[NORTH]" = list( 0, 8),  "[EAST]" = list(0, 8),  "[SOUTH]" = list(-0, 8),  "[WEST]" = list( 0, 8)),
-		BP_R_HAND_UPPER =  list("[NORTH]" = list( 0, 8),  "[EAST]" = list(0, 8),  "[SOUTH]" = list( 0, 8),  "[WEST]" = list( 0, 8)),
-		BP_L_HAND =        list("[NORTH]" = list( 4, 0),  "[EAST]" = list(0, 0),  "[SOUTH]" = list(-4, 0),  "[WEST]" = list( 0, 0)),
-		BP_R_HAND =        list("[NORTH]" = list(-4, 0),  "[EAST]" = list(0, 0),  "[SOUTH]" = list( 4, 0),  "[WEST]" = list( 0, 0)),
-		slot_head_str =    list("[NORTH]" = list( 0, 7),  "[EAST]" = list(0, 8),  "[SOUTH]" = list( 0, 8),  "[WEST]" = list( 0, 8)),
-		slot_back_str =    list("[NORTH]" = list( 0, 7),  "[EAST]" = list(0, 8),  "[SOUTH]" = list( 0, 8),  "[WEST]" = list( 0, 8)),
-		slot_belt_str =    list("[NORTH]" = list( 0, 0),  "[EAST]" = list(8, 0),  "[SOUTH]" = list( 0, 0),  "[WEST]" = list(-8, 0)),
-		slot_glasses_str = list("[NORTH]" = list( 0, 10), "[EAST]" = list(0, 11), "[SOUTH]" = list( 0, 11), "[WEST]" = list( 0, 11))
+	_equip_adjust = list(
+		(BP_L_HAND_UPPER) = list(
+			"[NORTH]" = list( 0, 8),
+			"[EAST]"  = list( 0, 8),
+			"[SOUTH]" = list(-0, 8),
+			"[WEST]"  = list( 0, 8)
+		),
+		(BP_R_HAND_UPPER) = list(
+			"[NORTH]" = list( 0, 8),
+			"[EAST]"  = list( 0, 8),
+			"[SOUTH]" = list( 0, 8),
+			"[WEST]"  = list( 0, 8)
+		),
+		(BP_L_HAND) = list(
+			"[NORTH]" = list( 4, 0),
+			"[EAST]"  = list( 0, 0),
+			"[SOUTH]" = list(-4, 0),
+			"[WEST]"  = list( 0, 0)
+		),
+		(BP_R_HAND) = list(
+			"[NORTH]" = list(-4, 0),
+			"[EAST]"  = list( 0, 0),
+			"[SOUTH]" = list( 4, 0),
+			"[WEST]"  = list( 0, 0)
+		),
+		(slot_head_str) = list(
+			"[NORTH]" = list( 0, 7),
+			"[EAST]"  = list( 0, 8),
+			"[SOUTH]" = list( 0, 8),
+			"[WEST]"  = list( 0, 8)
+		),
+		(slot_back_str) = list(
+			"[NORTH]" = list( 0, 7),
+			"[EAST]"  = list( 0, 8),
+			"[SOUTH]" = list( 0, 8),
+			"[WEST]"  = list( 0, 8)
+		),
+		(slot_belt_str) = list(
+			"[NORTH]" = list( 0, 0),
+			"[EAST]"  = list( 8, 0),
+			"[SOUTH]" = list( 0, 0),
+			"[WEST]"  = list(-8, 0)
+		),
+		(slot_glasses_str) = list(
+			"[NORTH]" = list( 0, 10),
+			"[EAST]"  = list( 0, 11),
+			"[SOUTH]" = list( 0, 11),
+			"[WEST]"  = list( 0, 11)
+		)
 	)
 	. = ..()
 
diff --git a/mods/species/utility_frames/species_bodytypes.dm b/mods/species/utility_frames/species_bodytypes.dm
index 378c5d86f08..59b71e3abd4 100644
--- a/mods/species/utility_frames/species_bodytypes.dm
+++ b/mods/species/utility_frames/species_bodytypes.dm
@@ -32,14 +32,14 @@
 	uid = "bodytype_prosthetic_utility_frame"
 
 /decl/bodytype/prosthetic/utility_frame/Initialize()
-	equip_adjust = list(
-		"[slot_l_ear_str]" =  list(
+	_equip_adjust = list(
+		(slot_l_ear_str) =  list(
 			"[NORTH]" = list( 2, 0),
 			"[EAST]"  = list( 0, 0),
 			"[SOUTH]" = list(-2, 0),
 			"[WEST]"  = list( 0, 0)
 		),
-		"[slot_r_ear_str]" =  list(
+		(slot_r_ear_str) =  list(
 			"[NORTH]" = list(-2, 0),
 			"[EAST]"  = list( 0, 0),
 			"[SOUTH]" = list( 2, 0),
@@ -48,4 +48,4 @@
 	)
 	. = ..()
 
-DEFINE_ROBOLIMB_DESIGNS(/decl/bodytype/prosthetic/utility_frame, utility_frame)
\ No newline at end of file
+DEFINE_ROBOLIMB_DESIGNS(/decl/bodytype/prosthetic/utility_frame, utility_frame)
diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm
index 25f4e64c180..ee3c6ba7f39 100644
--- a/mods/species/vox/datum/species_bodytypes.dm
+++ b/mods/species/vox/datum/species_bodytypes.dm
@@ -60,17 +60,17 @@
 	var/icon/vox_marking_icon = 'mods/species/vox/icons/body/soldier/markings.dmi'
 
 /decl/bodytype/vox/Initialize()
-	if(!length(equip_adjust))
-		equip_adjust = list(
-			BP_L_HAND           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
-			BP_R_HAND           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
-			slot_head_str       = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list(-3, -2)),
-			slot_wear_mask_str  = list("[NORTH]" = list(0,  0), "[EAST]" = list(4,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-4,  0)),
-			slot_wear_suit_str  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			slot_w_uniform_str  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			slot_underpants_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			slot_undershirt_str = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			slot_back_str       = list("[NORTH]" = list(0,  0), "[EAST]" = list(3,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-3,  0))
+	if(!length(_equip_adjust))
+		_equip_adjust = list(
+			(BP_L_HAND)           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
+			(BP_R_HAND)           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
+			(slot_head_str)       = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list(-3, -2)),
+			(slot_wear_mask_str)  = list("[NORTH]" = list(0,  0), "[EAST]" = list(4,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-4,  0)),
+			(slot_wear_suit_str)  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
+			(slot_w_uniform_str)  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
+			(slot_underpants_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
+			(slot_undershirt_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
+			(slot_back_str)       = list("[NORTH]" = list(0,  0), "[EAST]" = list(3,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-3,  0))
 		)
 	return ..()
 

From 996e803786df59487a026098fa6cfd27ade070c8 Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Thu, 12 Dec 2024 18:44:10 +1100
Subject: [PATCH 02/10] Allows overriding organ appearance bodytype for future
 work.

---
 .../organs/external/_external_icons.dm        | 43 ++++++++++++-------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm
index 7c7c5b2e24e..887bdb0f6c6 100644
--- a/code/modules/organs/external/_external_icons.dm
+++ b/code/modules/organs/external/_external_icons.dm
@@ -27,11 +27,14 @@ var/global/list/limb_icon_cache = list()
 	_icon_cache_key = null
 	skin_tone = null
 	skin_colour = null
+	var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype()
+	if(!icon_bodytype)
+		return
 	// This used to do a bodytype set but that was *really really bad.* Things that need that should do it themselves.
-	skin_blend = bodytype.limb_blend
-	if(!isnull(human.skin_tone) && bodytype?.appearance_flags & HAS_A_SKIN_TONE)
+	skin_blend = icon_bodytype.limb_blend
+	if(!isnull(human.skin_tone) && (icon_bodytype.appearance_flags & HAS_A_SKIN_TONE))
 		skin_tone = human.skin_tone
-	if(bodytype.appearance_flags & HAS_SKIN_COLOR)
+	if(icon_bodytype.appearance_flags & HAS_SKIN_COLOR)
 		skin_colour = human.get_skin_colour()
 
 /obj/item/organ/external/head/sync_colour_to_human(var/mob/living/human/human)
@@ -46,21 +49,28 @@ var/global/list/limb_icon_cache = list()
 		addtimer(CALLBACK(last_owner, TYPE_PROC_REF(/mob, update_hair)), 1, TIMER_UNIQUE)
 	return ..()
 
+// For overriding on shapeshifters/changelings in the future.
+/obj/item/organ/external/proc/get_organ_appearance_bodytype()
+	RETURN_TYPE(/decl/bodytype)
+	return bodytype
+
 /obj/item/organ/external/proc/update_limb_icon_file()
-	if(!bodytype) // This should not happen.
+	var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype()
+	if(!icon_bodytype) // This should not happen.
 		icon = initial(icon)
 	else if(limb_flags & ORGAN_FLAG_SKELETAL)
-		icon = bodytype.get_skeletal_icon(owner)
+		icon = icon_bodytype.get_skeletal_icon(owner)
 	else if(!BP_IS_PROSTHETIC(src) && (status & ORGAN_MUTATED))
-		icon = bodytype.get_base_icon(owner, get_deform = TRUE)
+		icon = icon_bodytype.get_base_icon(owner, get_deform = TRUE)
 	else
-		icon = bodytype.get_base_icon(owner)
+		icon = icon_bodytype.get_base_icon(owner)
 
 var/global/list/organ_icon_cache = list()
 /obj/item/organ/external/proc/generate_mob_icon()
 
 	// Generate base icon with colour and tone.
-	var/icon/ret = bodytype.apply_limb_colouration(src, new /icon(icon, icon_state))
+	var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype()
+	var/icon/ret = icon_bodytype.apply_limb_colouration(src, new /icon(icon, icon_state))
 	if(limb_flags & ORGAN_FLAG_SKELETAL)
 		global.organ_icon_cache[_icon_cache_key] = ret
 		return ret
@@ -75,7 +85,7 @@ var/global/list/organ_icon_cache = list()
 		else
 			ret.Blend(rgb(-skin_tone,  -skin_tone,  -skin_tone), ICON_SUBTRACT)
 
-	if((bodytype.appearance_flags & HAS_SKIN_COLOR) && skin_colour)
+	if((icon_bodytype.appearance_flags & HAS_SKIN_COLOR) && skin_colour)
 		ret.Blend(skin_colour, skin_blend)
 
 	// Body markings, hair, lips, etc.
@@ -111,7 +121,7 @@ var/global/list/organ_icon_cache = list()
 
 /obj/item/organ/external/proc/get_icon_cache_key_components()
 
-	. = list("[icon_state]_[species.name]_[bodytype?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]")
+	. = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]")
 
 	// Skeletons don't care about most icon appearance stuff.
 	if(limb_flags & ORGAN_FLAG_SKELETAL)
@@ -191,7 +201,7 @@ var/global/list/organ_icon_cache = list()
 
 	var/list/refresh_accessories
 	if(accessory_metadata)
-		if(!accessory_decl.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE)))
+		if(!accessory_decl.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE)))
 			return FALSE
 		var/list/existing_metadata = LAZYACCESS(accessories, accessory_type)
 		if(same_entries(existing_metadata, accessory_metadata))
@@ -324,9 +334,10 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888"
 		var/image/temp = image(limb_icon_cache[cache_key])
 		if(species)
 			// Calculate the required colour matrix.
-			var/r = 0.30 * bodytype.health_hud_intensity
-			var/g = 0.59 * bodytype.health_hud_intensity
-			var/b = 0.11 * bodytype.health_hud_intensity
+			var/hud_intensity = get_organ_appearance_bodytype()?.health_hud_intensity || 1
+			var/r = 0.30 * hud_intensity
+			var/g = 0.59 * hud_intensity
+			var/b = 0.11 * hud_intensity
 			temp.color = list(r, r, r, g, g, g, b, b, b)
 		temp.pixel_x = owner.default_pixel_x
 		temp.pixel_y = owner.default_pixel_y
@@ -361,7 +372,7 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888"
 	if(ispath(accessory_style))
 		accessory_style = GET_DECL(accessory_style)
 	// Check if this style is permitted for this species, period.
-	if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE)))
+	if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE)))
 		return null
 	// Check if we are concealed (long hair under a hat for example).
 	if(accessory_style.is_hidden(src))
@@ -372,7 +383,7 @@ var/global/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888"
 	for(var/acc_cat in _sprite_accessories)
 		for(var/accessory in _sprite_accessories[acc_cat])
 			var/decl/sprite_accessory/accessory_style = GET_DECL(accessory)
-			if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, bodytype, (owner ? owner.get_traits() : FALSE)))
+			if(!istype(accessory_style) || !accessory_style?.accessory_is_available(owner, species, get_organ_appearance_bodytype(), (owner ? owner.get_traits() : FALSE)))
 				_sprite_accessories[acc_cat] -= accessory
 				. = TRUE
 	if(.)

From f466649d1f41217b345990adba47c1850261f299 Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Thu, 12 Dec 2024 19:12:17 +1100
Subject: [PATCH 03/10] Adding some supporting code for shapeshifters.

---
 code/game/objects/item_mob_overlay.dm         |  4 ++--
 code/modules/bodytype/bodytype_offsets.dm     |  4 ++++
 code/modules/mob/living/living_organs.dm      | 12 -----------
 code/modules/mob/mob.dm                       | 10 ++++++++-
 code/modules/mob/mob_grabs.dm                 |  9 --------
 code/modules/mob/mob_organs.dm                | 21 +++++++++++++++++++
 .../organs/external/_external_icons.dm        | 16 +++++++++-----
 code/modules/organs/internal/eyes.dm          | 20 +++++++++++-------
 code/modules/organs/organ.dm                  | 14 +++++++++++++
 nebula.dme                                    |  1 +
 10 files changed, 75 insertions(+), 36 deletions(-)
 create mode 100644 code/modules/mob/mob_organs.dm

diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm
index be29ad618ca..d6881129c23 100644
--- a/code/game/objects/item_mob_overlay.dm
+++ b/code/game/objects/item_mob_overlay.dm
@@ -68,7 +68,7 @@ var/global/list/icon_state_cache = list()
 	if(!use_single_icon)
 		var/mob_state = "[item_state || icon_state][state_modifier]"
 		var/mob_icon = global.default_onmob_icons[slot]
-		var/decl/bodytype/root_bodytype = user_mob?.get_bodytype()
+		var/decl/bodytype/root_bodytype = user_mob?.get_equipment_bodytype(slot, bodypart)
 		if(istype(root_bodytype))
 			var/use_slot = (bodypart in root_bodytype.get_equip_adjustments(user_mob)) ? bodypart : slot
 			return root_bodytype.get_offset_overlay_image(user_mob, mob_icon, mob_state, color, use_slot)
@@ -153,7 +153,7 @@ var/global/list/icon_state_cache = list()
 				overlay.icon_state = wielded_state
 		apply_additional_mob_overlays(user_mob, bodytype, overlay, slot, bodypart, use_fallback_if_icon_missing)
 
-		var/decl/bodytype/root_bodytype = user_mob?.get_bodytype()
+		var/decl/bodytype/root_bodytype = user_mob?.get_equipment_bodytype(slot, bodypart)
 		if(root_bodytype && root_bodytype.bodytype_category != bodytype)
 			var/list/overlays_to_offset = overlay.overlays
 			overlay = root_bodytype.get_offset_overlay_image(user_mob, overlay.icon, overlay.icon_state, color, (bodypart || slot))
diff --git a/code/modules/bodytype/bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm
index 279b43e9918..0a149de8192 100644
--- a/code/modules/bodytype/bodytype_offsets.dm
+++ b/code/modules/bodytype/bodytype_offsets.dm
@@ -21,6 +21,10 @@ The slots that you can use are found in items_clothing.dm and are the inventory
 	VAR_PRIVATE/list/_equip_adjust
 	VAR_PRIVATE/list/equip_overlays = list()
 
+// Will be used by changelings/shapeshifters in the future
+/decl/bodytype/proc/resolve_to_equipment_bodytype(mob/living/user)
+	return src
+
 /decl/bodytype/proc/get_equip_adjustments(mob/mob)
 	return _equip_adjust
 
diff --git a/code/modules/mob/living/living_organs.dm b/code/modules/mob/living/living_organs.dm
index 9fefc64c894..dce22c9a993 100644
--- a/code/modules/mob/living/living_organs.dm
+++ b/code/modules/mob/living/living_organs.dm
@@ -1,15 +1,3 @@
-/mob/living/get_organs()
-	for(var/organ in get_external_organs())
-		LAZYADD(., organ)
-	for(var/organ in get_internal_organs())
-		LAZYADD(., organ)
-
-/mob/living/proc/get_external_organs()
-	return
-
-/mob/living/proc/get_internal_organs()
-	return
-
 /mob/living/proc/get_organs_by_categories(var/category)
 	return
 
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 449c6a76a2a..03827667a28 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1110,10 +1110,18 @@
 /mob/proc/get_bodytype()
 	RETURN_TYPE(/decl/bodytype)
 
+// Bit of a stub for now, but should return the bodytype specific
+// to the slot and organ being checked in the future instead of
+// always using the mob root bodytype.
+/mob/proc/get_equipment_bodytype(slot, bodypart)
+	RETURN_TYPE(/decl/bodytype)
+	var/decl/bodytype/root_bodytype = get_bodytype()
+	return root_bodytype?.resolve_to_equipment_bodytype(src)
+
 /mob/proc/has_body_flag(flag, default = FALSE)
 	var/decl/bodytype/root_bodytype = get_bodytype()
 	if(istype(root_bodytype))
-		return root_bodytype.body_flags & flag
+		return (root_bodytype.body_flags & flag)
 	return default
 
 /// Update the mouse pointer of the attached client in this mob.
diff --git a/code/modules/mob/mob_grabs.dm b/code/modules/mob/mob_grabs.dm
index e02bed4b3dc..0cea8113ed7 100644
--- a/code/modules/mob/mob_grabs.dm
+++ b/code/modules/mob/mob_grabs.dm
@@ -7,15 +7,6 @@
 	return
 /mob/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0)
 	return
-/mob/proc/has_organ(organ_tag)
-	return !!get_organ(organ_tag, /obj/item/organ)
-/mob/proc/get_organ(var/organ_tag, var/expected_type)
-	RETURN_TYPE(/obj/item/organ)
-	return
-/mob/proc/get_injured_organs()
-	return
-/mob/proc/get_organs()
-	return
 // End grab casting stubs.
 
 /mob/can_be_grabbed(var/mob/grabber, var/target_zone)
diff --git a/code/modules/mob/mob_organs.dm b/code/modules/mob/mob_organs.dm
new file mode 100644
index 00000000000..eb5b763c95f
--- /dev/null
+++ b/code/modules/mob/mob_organs.dm
@@ -0,0 +1,21 @@
+/mob/proc/has_organ(organ_tag)
+	return !!get_organ(organ_tag, /obj/item/organ)
+
+/mob/proc/get_organ(var/organ_tag, var/expected_type)
+	RETURN_TYPE(/obj/item/organ)
+	return
+
+/mob/proc/get_injured_organs()
+	return
+
+/mob/proc/get_external_organs()
+	return
+
+/mob/proc/get_internal_organs()
+	return
+
+/mob/proc/get_organs()
+	for(var/organ in get_external_organs())
+		LAZYADD(., organ)
+	for(var/organ in get_internal_organs())
+		LAZYADD(., organ)
diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm
index 887bdb0f6c6..8f9b601a8c5 100644
--- a/code/modules/organs/external/_external_icons.dm
+++ b/code/modules/organs/external/_external_icons.dm
@@ -49,10 +49,16 @@ var/global/list/limb_icon_cache = list()
 		addtimer(CALLBACK(last_owner, TYPE_PROC_REF(/mob, update_hair)), 1, TIMER_UNIQUE)
 	return ..()
 
-// For overriding on shapeshifters/changelings in the future.
-/obj/item/organ/external/proc/get_organ_appearance_bodytype()
-	RETURN_TYPE(/decl/bodytype)
-	return bodytype
+/obj/item/organ/external/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE)
+	. = ..()
+	if(.)
+		if(update_sprite_accessories)
+			sanitize_sprite_accessories(skip_update = TRUE)
+		_icon_cache_key = null
+		get_icon_for_bodytype()
+		update_icon()
+		if(owner && !skip_owner_update)
+			owner.update_body()
 
 /obj/item/organ/external/proc/update_limb_icon_file()
 	var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype()
@@ -121,7 +127,7 @@ var/global/list/organ_icon_cache = list()
 
 /obj/item/organ/external/proc/get_icon_cache_key_components()
 
-	. = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.name || "BAD_BODYTYPE"]_[render_alpha]_[icon]")
+	. = list("[icon_state]_[species.name]_[get_organ_appearance_bodytype()?.uid || "BAD_BODYTYPE"]_[render_alpha]_[icon]")
 
 	// Skeletons don't care about most icon appearance stuff.
 	if(limb_flags & ORGAN_FLAG_SKELETAL)
diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm
index f193afaa4da..7dbc75053e6 100644
--- a/code/modules/organs/internal/eyes.dm
+++ b/code/modules/organs/internal/eyes.dm
@@ -36,19 +36,25 @@
 	verbs |= /obj/item/organ/internal/eyes/proc/change_eye_color_verb
 	verbs |= /obj/item/organ/internal/eyes/proc/toggle_eye_glow
 
+/obj/item/organ/external/eyes/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE)
+	. = ..()
+	if(. && owner && !skip_owner_update)
+		owner.update_eyes()
+
 /obj/item/organ/internal/eyes/proc/get_onhead_icon()
+	var/decl/bodytype/icon_bodytype = get_organ_appearance_bodytype()
 	var/modifier = owner?.get_overlay_state_modifier()
 	var/eye_state = modifier ? "eyes[modifier]" : "eyes"
 	last_cached_eye_colour = eye_colour
-	last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]-[eye_state]"
-	if(!bodytype.eye_icon)
+	last_eye_cache_key = "[type]-[icon_bodytype.eye_icon]-[last_cached_eye_colour]-[icon_bodytype.eye_offset]-[eye_state]"
+	if(!icon_bodytype.eye_icon)
 		return
 	if(!global.eye_icon_cache[last_eye_cache_key])
-		var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = eye_state)
-		if(bodytype.eye_offset)
-			eyes_icon.Shift(NORTH, bodytype.eye_offset)
-		if(bodytype.apply_eye_colour)
-			eyes_icon.Blend(last_cached_eye_colour, bodytype.eye_blend)
+		var/icon/eyes_icon = icon(icon = icon_bodytype.eye_icon, icon_state = eye_state)
+		if(icon_bodytype.eye_offset)
+			eyes_icon.Shift(NORTH, icon_bodytype.eye_offset)
+		if(icon_bodytype.apply_eye_colour)
+			eyes_icon.Blend(last_cached_eye_colour, icon_bodytype.eye_blend)
 		global.eye_icon_cache[last_eye_cache_key] = eyes_icon
 	return global.eye_icon_cache[last_eye_cache_key]
 
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 8de64cc2272..92bcc2d8ed7 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -22,6 +22,7 @@
 	var/mob/living/human/owner      // Current mob owning the organ.
 	var/decl/species/species               // Original species.
 	var/decl/bodytype/bodytype             // Original bodytype.
+	var/decl/bodytype/appearance_bodytype  // A bodytype used only for icons, marking validation and equipment offsets.
 	var/list/ailments                      // Current active ailments if any.
 	var/meat_name                          // Taken from first owner.
 
@@ -674,3 +675,16 @@ var/global/list/ailment_reference_cache = list()
 		new /obj/effect/decal/cleanable/ash(loc)
 	if(!QDELETED(src))
 		qdel(src)
+
+// For overriding on shapeshifters/changelings in the future.
+/obj/item/organ/proc/set_organ_appearance_bodytype(decl/bodytype/new_bodytype, update_sprite_accessories = TRUE, skip_owner_update = FALSE)
+	if(ispath(new_bodytype, /decl/bodytype))
+		new_bodytype = GET_DECL(new_bodytype)
+	if((new_bodytype && !istype(new_bodytype)) || appearance_bodytype == new_bodytype || bodytype == new_bodytype)
+		return FALSE
+	appearance_bodytype = new_bodytype
+	return TRUE
+
+/obj/item/organ/proc/get_organ_appearance_bodytype()
+	RETURN_TYPE(/decl/bodytype)
+	return appearance_bodytype || bodytype
diff --git a/nebula.dme b/nebula.dme
index c08cae0b5a9..a0c5a4609a2 100644
--- a/nebula.dme
+++ b/nebula.dme
@@ -2827,6 +2827,7 @@
 #include "code\modules\mob\mob_intent.dm"
 #include "code\modules\mob\mob_layering.dm"
 #include "code\modules\mob\mob_movement.dm"
+#include "code\modules\mob\mob_organs.dm"
 #include "code\modules\mob\mob_snapshot.dm"
 #include "code\modules\mob\mob_status.dm"
 #include "code\modules\mob\mob_temperature.dm"

From ee0259599969a82d60d248fc389855b4375dc13f Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Thu, 12 Dec 2024 19:46:50 +1100
Subject: [PATCH 04/10] Removing old shapeshifter species/bodytype pending
 rewrite.

---
 code/modules/species/species_getters.dm       |   3 -
 code/modules/species/species_shapeshifter.dm  | 129 ------------------
 .../species/species_shapeshifter_bodytypes.dm |  48 -------
 mods/species/vox/datum/species_bodytypes.dm   |  63 +++++++--
 nebula.dme                                    |   2 -
 5 files changed, 54 insertions(+), 191 deletions(-)
 delete mode 100644 code/modules/species/species_shapeshifter.dm
 delete mode 100644 code/modules/species/species_shapeshifter_bodytypes.dm

diff --git a/code/modules/species/species_getters.dm b/code/modules/species/species_getters.dm
index f2e3abce845..4bf80fd91e7 100644
--- a/code/modules/species/species_getters.dm
+++ b/code/modules/species/species_getters.dm
@@ -1,6 +1,3 @@
-/decl/species/proc/get_valid_shapeshifter_forms(var/mob/living/human/H)
-	return list()
-
 /decl/species/proc/get_additional_examine_text(var/mob/living/human/H)
 	return
 
diff --git a/code/modules/species/species_shapeshifter.dm b/code/modules/species/species_shapeshifter.dm
deleted file mode 100644
index f49e307387e..00000000000
--- a/code/modules/species/species_shapeshifter.dm
+++ /dev/null
@@ -1,129 +0,0 @@
-// This is something of an intermediary species used for species that
-// need to emulate the appearance of another race. Currently it is only
-// used for slimes but it may be useful for other species later.
-var/global/list/wrapped_species_by_ref = list()
-
-/decl/species/shapeshifter
-	available_bodytypes = list(/decl/bodytype/shapeshifter)
-	inherent_verbs = list(
-		/mob/living/human/proc/shapeshifter_select_shape,
-		/mob/living/human/proc/shapeshifter_select_hair,
-		/mob/living/human/proc/shapeshifter_select_gender,
-		/mob/living/human/proc/shapeshifter_select_colour
-	)
-	hidden_from_codex = TRUE
-	var/list/valid_transform_species = list()
-	var/monochromatic
-	var/default_form
-
-/decl/species/shapeshifter/Initialize()
-	default_form = global.using_map.default_species
-	valid_transform_species |= default_form
-	. = ..()
-
-/decl/species/shapeshifter/get_valid_shapeshifter_forms(var/mob/living/human/H)
-	return valid_transform_species
-
-/decl/species/shapeshifter/get_root_species_name(var/mob/living/human/H)
-	if(!H) return ..()
-	var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-	return S.get_root_species_name(H)
-
-/decl/species/shapeshifter/handle_pre_spawn(var/mob/living/human/H)
-	..()
-	wrapped_species_by_ref["\ref[H]"] = default_form
-
-/decl/species/shapeshifter/handle_post_spawn(var/mob/living/human/H)
-	if(monochromatic)
-		var/skin_colour = H.get_skin_colour()
-		SET_HAIR_COLOR(H, skin_colour, TRUE)
-		SET_FACIAL_HAIR_COLOR(H, skin_colour, TRUE)
-	..()
-
-/decl/species/shapeshifter/get_pain_emote(var/mob/living/human/H, var/pain_power)
-	var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-	return S.get_pain_emote(H, pain_power)
-
-// Verbs follow.
-/mob/living/human/proc/shapeshifter_select_hair()
-
-	set name = "Select Hair"
-	set category = "Abilities"
-
-	if(stat || is_on_special_ability_cooldown())
-		return
-
-	set_special_ability_cooldown(1 SECOND)
-
-	visible_message("<span class='notice'>\The [src]'s form contorts subtly.</span>")
-	var/decl/bodytype/root_bodytype = get_bodytype()
-	var/list/hairstyles = species.get_available_accessory_types(root_bodytype, SAC_HAIR)
-	if(length(hairstyles))
-		var/decl/sprite_accessory/new_hair = input("Select a hairstyle.", "Shapeshifter Hair") as null|anything in hairstyles
-		SET_HAIR_STYLE(src, (new_hair ? new_hair.type : /decl/sprite_accessory/hair/bald), FALSE)
-
-	var/list/beardstyles = species.get_available_accessory_types(root_bodytype, SAC_FACIAL_HAIR)
-	if(length(beardstyles))
-		var/decl/sprite_accessory/new_hair = input("Select a facial hair style.", "Shapeshifter Hair") as null|anything in beardstyles
-		SET_FACIAL_HAIR_STYLE(src, (new_hair ? new_hair.type : /decl/sprite_accessory/facial_hair/shaved), FALSE)
-
-/mob/living/human/proc/shapeshifter_select_gender()
-
-	set name = "Select Gender"
-	set category = "Abilities"
-
-	if(stat || is_on_special_ability_cooldown())
-		return
-
-	set_special_ability_cooldown(5 SECONDS)
-
-	var/new_gender = input("Please select a gender.", "Shapeshifter Gender") as null|anything in list(FEMALE, MALE, NEUTER, PLURAL)
-	if(!new_gender)
-		return
-
-	visible_message("<span class='notice'>\The [src]'s form contorts subtly.</span>")
-	set_gender(new_gender, TRUE)
-
-/mob/living/human/proc/shapeshifter_select_shape()
-
-	set name = "Select Body Shape"
-	set category = "Abilities"
-
-	if(stat ||is_on_special_ability_cooldown())
-		return
-
-	set_special_ability_cooldown(5 SECONDS)
-
-	var/new_species = input("Please select a species to emulate.", "Shapeshifter Body") as null|anything in species.get_valid_shapeshifter_forms(src)
-	if(!new_species || !get_species_by_key(new_species) || wrapped_species_by_ref["\ref[src]"] == new_species)
-		return
-
-	wrapped_species_by_ref["\ref[src]"] = new_species
-	visible_message("<span class='notice'>\The [src] shifts and contorts, taking the form of \a ["\improper [new_species]"]!</span>")
-	try_refresh_visible_overlays()
-
-/mob/living/human/proc/shapeshifter_select_colour()
-
-	set name = "Select Body Colour"
-	set category = "Abilities"
-
-	if(stat || is_on_special_ability_cooldown())
-		return
-
-	set_special_ability_cooldown(5 SECONDS)
-
-	var/new_skin = input("Please select a new body color.", "Shapeshifter Colour") as color
-	if(!new_skin)
-		return
-	shapeshifter_set_colour(new_skin)
-
-/mob/living/human/proc/shapeshifter_set_colour(var/new_skin)
-	set_skin_colour(new_skin, skip_update = TRUE)
-	var/decl/species/shapeshifter/S = species
-	if(S.monochromatic)
-		var/skin_colour = get_skin_colour()
-		SET_HAIR_COLOR(src, skin_colour, TRUE)
-		SET_FACIAL_HAIR_COLOR(src, skin_colour, TRUE)
-	for(var/obj/item/organ/external/E in get_external_organs())
-		E.sync_colour_to_human(src)
-	try_refresh_visible_overlays()
diff --git a/code/modules/species/species_shapeshifter_bodytypes.dm b/code/modules/species/species_shapeshifter_bodytypes.dm
deleted file mode 100644
index a1ee2815579..00000000000
--- a/code/modules/species/species_shapeshifter_bodytypes.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/decl/bodytype/shapeshifter
-	name = "protean form"
-	bodytype_category = BODYTYPE_HUMANOID
-	uid = "bodytype_shapeshifter"
-
-/decl/bodytype/shapeshifter/apply_limb_colouration(var/obj/item/organ/external/E, var/icon/applying)
-	applying.MapColors("#4d4d4d","#969696","#1c1c1c", "#000000")
-	applying = ..()
-	applying += rgb(,,,180) // Makes the icon translucent, SO INTUITIVE TY BYOND
-	return applying
-
-/decl/bodytype/shapeshifter/check_dismember_type_override(var/disintegrate)
-	if(disintegrate == DISMEMBER_METHOD_EDGE)
-		return DISMEMBER_METHOD_BLUNT
-	return ..()
-
-/decl/bodytype/shapeshifter/get_base_icon(var/mob/living/human/H, var/get_deform)
-	if(!H) return ..(null, get_deform)
-	var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-	return S.default_bodytype.get_base_icon(H, get_deform)
-
-/decl/bodytype/shapeshifter/get_blood_overlays(var/mob/living/human/H)
-	if(!H) return ..()
-	var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-	return S.default_bodytype.get_blood_overlays(H)
-
-/decl/bodytype/shapeshifter/get_damage_overlays(var/mob/living/human/H)
-	if(!H) return ..()
-	var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-	return S.default_bodytype.get_damage_overlays(H)
-
-/decl/bodytype/shapeshifter/get_husk_icon(var/mob/living/human/H)
-	if(H)
-		var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-		if(S) return S.default_bodytype.get_husk_icon(H)
-	 return ..()
-
-/decl/bodytype/shapeshifter/get_icon_cache_uid(var/mob/H)
-	. = ..()
-	if(H)
-		var/decl/species/S = get_species_by_key(wrapped_species_by_ref["\ref[H]"])
-		if(S) return S.default_bodytype.get_icon_cache_uid(H)
-
-/decl/bodytype/shapeshifter/apply_bodytype_organ_modifications(obj/item/organ/org)
-	..()
-	var/obj/item/organ/external/E = org
-	if(istype(E) && E.owner)
-		E.sync_colour_to_human(E.owner)
\ No newline at end of file
diff --git a/mods/species/vox/datum/species_bodytypes.dm b/mods/species/vox/datum/species_bodytypes.dm
index ee3c6ba7f39..cff74b6141d 100644
--- a/mods/species/vox/datum/species_bodytypes.dm
+++ b/mods/species/vox/datum/species_bodytypes.dm
@@ -62,15 +62,60 @@
 /decl/bodytype/vox/Initialize()
 	if(!length(_equip_adjust))
 		_equip_adjust = list(
-			(BP_L_HAND)           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
-			(BP_R_HAND)           = list("[NORTH]" = list(0, -2), "[EAST]" = list(0, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list( 0, -2)),
-			(slot_head_str)       = list("[NORTH]" = list(0, -2), "[EAST]" = list(3, -2), "[SOUTH]" = list( 0, -2),  "[WEST]" = list(-3, -2)),
-			(slot_wear_mask_str)  = list("[NORTH]" = list(0,  0), "[EAST]" = list(4,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-4,  0)),
-			(slot_wear_suit_str)  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			(slot_w_uniform_str)  = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			(slot_underpants_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			(slot_undershirt_str) = list("[NORTH]" = list(0, -1), "[EAST]" = list(0, -1), "[SOUTH]" = list( 0, -1),  "[WEST]" = list( 0, -1)),
-			(slot_back_str)       = list("[NORTH]" = list(0,  0), "[EAST]" = list(3,  0), "[SOUTH]" = list( 0,  0),  "[WEST]" = list(-3,  0))
+			(BP_L_HAND) = list(
+				"[NORTH]" = list(0, -2),
+				"[EAST]"  = list(0, -2),
+				"[SOUTH]" = list( 0, -2),
+				"[WEST]"  = list( 0, -2)
+			),
+			(BP_R_HAND) = list(
+				"[NORTH]" = list(0, -2),
+				"[EAST]"  = list(0, -2),
+				"[SOUTH]" = list( 0, -2),
+				"[WEST]"  = list( 0, -2)
+			),
+			(slot_head_str) = list(
+				"[NORTH]" = list(0, -2),
+				"[EAST]"  = list(3, -2),
+				"[SOUTH]" = list( 0, -2),
+				"[WEST]"  = list(-3, -2)
+			),
+			(slot_wear_mask_str) = list(
+				"[NORTH]" = list(0,  0),
+				"[EAST]"  = list(4,  0),
+				"[SOUTH]" = list( 0,  0),
+				"[WEST]"  = list(-4,  0)
+			),
+			(slot_wear_suit_str) = list(
+				"[NORTH]" = list(0, -1),
+				"[EAST]"  = list(0, -1),
+				"[SOUTH]" = list( 0, -1),
+				"[WEST]"  = list( 0, -1)
+			),
+			(slot_w_uniform_str) = list(
+				"[NORTH]" = list(0, -1),
+				"[EAST]"  = list(0, -1),
+				"[SOUTH]" = list( 0, -1),
+				"[WEST]"  = list( 0, -1)
+			),
+			(slot_underpants_str) = list(
+				"[NORTH]" = list(0, -1),
+				"[EAST]"  = list(0, -1),
+				"[SOUTH]" = list( 0, -1),
+				"[WEST]"  = list( 0, -1)
+			),
+			(slot_undershirt_str) = list(
+				"[NORTH]" = list(0, -1),
+				"[EAST]"  = list(0, -1),
+				"[SOUTH]" = list( 0, -1),
+				"[WEST]"  = list( 0, -1)
+			),
+			(slot_back_str) = list(
+				"[NORTH]" = list(0,  0),
+				"[EAST]"  = list(3,  0),
+				"[SOUTH]" = list( 0,  0),
+				"[WEST]"  = list(-3,  0)
+			)
 		)
 	return ..()
 
diff --git a/nebula.dme b/nebula.dme
index a0c5a4609a2..f7c82a45cff 100644
--- a/nebula.dme
+++ b/nebula.dme
@@ -3771,8 +3771,6 @@
 #include "code\modules\species\species_getters.dm"
 #include "code\modules\species\species_helpers.dm"
 #include "code\modules\species\species_hud.dm"
-#include "code\modules\species\species_shapeshifter.dm"
-#include "code\modules\species\species_shapeshifter_bodytypes.dm"
 #include "code\modules\species\outsider\random.dm"
 #include "code\modules\species\station\golem.dm"
 #include "code\modules\species\station\human.dm"

From 767934966009a76523c111d9bf94e40b2eed7ba3 Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Sun, 5 Jan 2025 12:41:25 +1100
Subject: [PATCH 05/10] Vomit and mud can now be tracked around the floor.

---
 code/_helpers/global_lists.dm                 | 11 +++-
 .../effects/decals/Cleanable/humans.dm        | 26 +++------
 .../objects/effects/decals/Cleanable/misc.dm  | 13 ++++-
 code/game/objects/items/__item.dm             | 45 ++++++++--------
 code/game/objects/items/_item_materials.dm    |  4 +-
 .../objects/items/weapons/swords_axes_etc.dm  |  4 +-
 code/game/turfs/flooring/_flooring.dm         |  6 +++
 code/game/turfs/flooring/flooring_mud.dm      |  9 ++++
 code/game/turfs/floors/_floor.dm              |  8 +++
 code/game/turfs/turf.dm                       |  3 ++
 code/modules/detectivework/tools/uvlight.dm   |  7 +--
 code/modules/mob/living/human/human.dm        | 34 ------------
 code/modules/mob/living/life.dm               |  5 +-
 code/modules/mob/living/living.dm             | 54 +++++++++++++++++--
 maps/away/mining/mining-signal.dmm            |  4 +-
 maps/away/smugglers/smugglers.dmm             |  2 +-
 maps/away/unishi/unishi-2.dmm                 |  4 +-
 .../crashed_pod/crashed_pod.dmm               |  2 +-
 .../deserted_lab/deserted_lab.dmm             |  2 +-
 .../exoplanet_ruins/playablecolony/colony.dmm |  2 +-
 .../spider_nest/spider_nest.dmm               |  4 +-
 maps/tradeship/tradeship-1.dmm                |  2 +-
 22 files changed, 150 insertions(+), 101 deletions(-)

diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 84463762e67..7b98b5f58af 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -107,4 +107,13 @@ var/global/list/bodytype_species_pairs = list() // A list of bodytypes -> specie
 	. = global.playable_species
 /proc/get_bodytype_species_pairs()
 	build_species_lists()
-	. = global.bodytype_species_pairs
\ No newline at end of file
+	. = global.bodytype_species_pairs
+
+// Used to avoid constantly generating new lists during movement.
+var/global/list/all_stance_limbs   = list(
+	ORGAN_CATEGORY_STANCE,
+	ORGAN_CATEGORY_STANCE_ROOT
+)
+var/global/list/child_stance_limbs = list(
+	ORGAN_CATEGORY_STANCE
+)
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index 3cc2dd4bf5e..cbd3f166ddb 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -91,26 +91,12 @@
 /obj/effect/decal/cleanable/blood/Crossed(atom/movable/AM)
 	if(!isliving(AM) || amount < 1)
 		return
-
-	var/mob/living/M = AM
-	var/obj/item/organ/external/l_foot = GET_EXTERNAL_ORGAN(M, BP_L_FOOT)
-	var/obj/item/organ/external/r_foot = GET_EXTERNAL_ORGAN(M, BP_R_FOOT)
-	var/hasfeet = l_foot && r_foot
-
-	var/transferred_data = blood_data ? blood_data[pick(blood_data)] : null
-	var/obj/item/clothing/shoes/shoes = M.get_equipped_item(slot_shoes_str)
-	if(istype(shoes) && !M.buckled)//Adding blood to shoes
-		shoes.add_coating(chemical, amount, transferred_data)
-	else if (hasfeet)//Or feet
-		if(l_foot)
-			l_foot.add_coating(chemical, amount, transferred_data)
-		if(r_foot)
-			r_foot.add_coating(chemical, amount, transferred_data)
-	else if (M.buckled && istype(M.buckled, /obj/structure/bed/chair/wheelchair))
-		var/obj/structure/bed/chair/wheelchair/W = M.buckled
-		W.bloodiness = 4
-
-	M.update_equipment_overlay(slot_shoes_str)
+	var/mob/living/walker = AM
+	if(istype(walker.buckled, /obj/structure/bed/chair/wheelchair))
+		var/obj/structure/bed/chair/wheelchair/wheelchair = walker.buckled
+		wheelchair.bloodiness = 4
+	else
+		walker.add_walking_contaminant(chemical, amount, (blood_data ? blood_data[pick(blood_data)] : null))
 	amount--
 
 /obj/effect/decal/cleanable/blood/proc/dry()
diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm
index 2a22a171886..6fe6ea7441a 100644
--- a/code/game/objects/effects/decals/Cleanable/misc.dm
+++ b/code/game/objects/effects/decals/Cleanable/misc.dm
@@ -90,9 +90,20 @@
 	if(prob(75))
 		set_rotation(pick(90, 180, 270))
 
+/obj/effect/decal/cleanable/vomit/mapped/Initialize(ml, _age)
+	. = ..()
+	add_to_reagents(/decl/material/liquid/acid/stomach, rand(3,5))
+	add_to_reagents(/decl/material/liquid/nutriment, rand(5,8))
+
 /obj/effect/decal/cleanable/vomit/on_update_icon()
 	. = ..()
-	color = reagents.get_color()
+	color = reagents?.get_color()
+
+/obj/effect/decal/cleanable/vomit/Crossed(atom/movable/AM)
+	. = ..()
+	if(!QDELETED(src) && reagents?.total_volume >= 1 && isliving(AM))
+		var/mob/living/walker = AM
+		walker.add_walking_contaminant(reagents, rand(2, 3))
 
 /obj/effect/decal/cleanable/tomato_smudge
 	name               = "tomato smudge"
diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm
index b02237a6b2f..20c732782fa 100644
--- a/code/game/objects/items/__item.dm
+++ b/code/game/objects/items/__item.dm
@@ -12,7 +12,8 @@
 	/// Set to false to skip state checking and never draw an icon on the mob (except when held)
 	var/draw_on_mob_when_equipped = TRUE
 
-	var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
+	/// this saves our blood splatter/coating overlay, which will be processed not to go over the edges of the sprite.
+	var/image/coating_overlay
 	var/randpixel = 6
 	var/material_health_multiplier = 0.2
 	var/hitsound
@@ -245,7 +246,7 @@
 /obj/item/PopulateClone(obj/item/clone)
 	clone = ..()
 	clone.contaminated = contaminated
-	clone.blood_overlay = image(blood_overlay)
+	clone.coating_overlay = image(coating_overlay)
 	clone.current_health = current_health
 
 	//#TODO: once item damage in, check health!
@@ -790,7 +791,7 @@
 	if(was_bloodied && !fluorescent)
 		fluorescent = FLUORESCENT_GLOWS
 		blood_color = COLOR_LUMINOL
-		blood_overlay.color = COLOR_LUMINOL
+		coating_overlay.color = COLOR_LUMINOL
 		update_icon()
 
 /obj/item/add_blood(mob/living/M, amount = 2, list/blood_data)
@@ -814,21 +815,21 @@
 		LAZYSET(blood_DNA, unique_enzymes, blood_type)
 	return TRUE
 
-var/global/list/_blood_overlay_cache = list()
-var/global/icon/_item_blood_mask = icon('icons/effects/blood.dmi', "itemblood")
-/obj/item/proc/generate_blood_overlay(force = FALSE)
-	if(blood_overlay && !force)
+var/global/list/_coating_overlay_cache = list()
+var/global/icon/_item_coating_mask = icon('icons/effects/blood.dmi', "itemblood")
+/obj/item/proc/generate_coating_overlay(force = FALSE)
+	if(coating_overlay && !force)
 		return
 	var/cache_key = "[icon]-[icon_state]"
-	if(global._blood_overlay_cache[cache_key])
-		blood_overlay = global._blood_overlay_cache[cache_key]
+	if(global._coating_overlay_cache[cache_key])
+		coating_overlay = global._coating_overlay_cache[cache_key]
 		return
 	var/icon/I = new /icon(icon, icon_state)
 	I.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1)         // Sets the icon RGB channel to pure white.
-	I.Blend(global._item_blood_mask, ICON_MULTIPLY) // Masks the blood overlay against the generated mask.
-	blood_overlay = image(I)
-	blood_overlay.appearance_flags |= NO_CLIENT_COLOR|RESET_COLOR
-	global._blood_overlay_cache[cache_key] = blood_overlay
+	I.Blend(global._item_coating_mask, ICON_MULTIPLY) // Masks the coating overlay against the generated mask.
+	coating_overlay = image(I)
+	coating_overlay.appearance_flags |= NO_CLIENT_COLOR|RESET_COLOR
+	global._coating_overlay_cache[cache_key] = coating_overlay
 
 /obj/item/proc/showoff(mob/user)
 	for(var/mob/M in view(user))
@@ -1018,13 +1019,15 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out.
 
 /obj/item/proc/add_coating(reagent_type, amount, data)
 	if(!coating)
-		coating = new/datum/reagents(10, src)
-	coating.add_reagent(reagent_type, amount, data)
-
-	if(!blood_overlay)
-		generate_blood_overlay()
-	blood_overlay.color = coating.get_color()
-
+		coating = new /datum/reagents(10, src)
+	if(ispath(reagent_type))
+		coating.add_reagent(reagent_type, amount, data)
+	else if(istype(reagent_type, /datum/reagents))
+		var/datum/reagents/source = reagent_type
+		source.trans_to_holder(coating, amount)
+	if(!coating_overlay)
+		generate_coating_overlay()
+	coating_overlay.color = coating.get_color()
 	update_icon()
 
 /obj/item/proc/remove_coating(amount)
@@ -1037,7 +1040,7 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out.
 /obj/item/clean(clean_forensics=TRUE)
 	. = ..()
 	QDEL_NULL(coating)
-	blood_overlay = null
+	coating_overlay = null
 	if(clean_forensics)
 		var/datum/extension/forensic_evidence/forensics = get_extension(src, /datum/extension/forensic_evidence)
 		if(forensics)
diff --git a/code/game/objects/items/_item_materials.dm b/code/game/objects/items/_item_materials.dm
index 261a8c5f5d0..ecf48b7029e 100644
--- a/code/game/objects/items/_item_materials.dm
+++ b/code/game/objects/items/_item_materials.dm
@@ -5,8 +5,8 @@
 	if((material_alteration & MAT_FLAG_ALTERATION_COLOR) && material)
 		alpha = 100 + material.opacity * 255
 	color = get_color() // avoiding set_color() here as that will set it on paint_color
-	if(blood_overlay)
-		add_overlay(blood_overlay)
+	if(coating_overlay)
+		add_overlay(coating_overlay)
 	if(global.contamination_overlay && contaminated)
 		add_overlay(global.contamination_overlay)
 
diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm
index 124bfb02526..25ecca6f856 100644
--- a/code/game/objects/items/weapons/swords_axes_etc.dm
+++ b/code/game/objects/items/weapons/swords_axes_etc.dm
@@ -66,8 +66,8 @@
 	update_held_icon()
 
 /obj/item/telebaton/on_update_icon()
-	if(length(blood_DNA))
-		generate_blood_overlay(TRUE) // Force recheck.
+	if(coating?.total_volume || blood_DNA)
+		generate_coating_overlay(TRUE) // Force recheck.
 	. = ..()
 	if(on)
 		icon = 'icons/obj/items/weapon/telebaton_extended.dmi'
diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm
index a02efe3cb84..8150d0abd34 100644
--- a/code/game/turfs/flooring/_flooring.dm
+++ b/code/game/turfs/flooring/_flooring.dm
@@ -350,3 +350,9 @@ var/global/list/flooring_cache = list()
 
 /decl/flooring/proc/handle_turf_digging(turf/floor/target)
 	return TRUE
+
+/decl/flooring/proc/turf_crossed(atom/movable/crosser)
+	return
+
+/decl/flooring/proc/can_show_footsteps(turf/target)
+	return TRUE
diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm
index fd4866144b9..2b5fa2c5063 100644
--- a/code/game/turfs/flooring/flooring_mud.dm
+++ b/code/game/turfs/flooring/flooring_mud.dm
@@ -19,6 +19,15 @@
 		return
 	return ..()
 
+/decl/flooring/mud/turf_crossed(atom/movable/crosser)
+	if(!isliving(crosser))
+		return
+	var/mob/living/walker = crosser
+	walker.add_walking_contaminant(/decl/material/solid/soil, rand(2,3))
+
+/decl/flooring/mud/can_show_footsteps(turf/target)
+	return FALSE // So we don't end up covered in a million footsteps that we provided.
+
 /decl/flooring/dry_mud
 	name            = "dry mud"
 	desc            = "This was once mud, but forgot to keep hydrated."
diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm
index 8a5562d03ad..e8ede4d119e 100644
--- a/code/game/turfs/floors/_floor.dm
+++ b/code/game/turfs/floors/_floor.dm
@@ -297,3 +297,11 @@
 /turf/floor/get_plant_growth_rate()
 	var/decl/flooring/flooring = get_topmost_flooring()
 	return flooring ? flooring.growth_value : ..()
+
+/turf/floor/Crossed(atom/movable/AM)
+	var/decl/flooring/flooring = get_topmost_flooring()
+	flooring?.turf_crossed(AM)
+	return ..()
+
+/turf/floor/can_show_footsteps()
+	return ..() && get_topmost_flooring()?.can_show_footsteps(src)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index bdd356e5322..cf4d6f67920 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -822,6 +822,9 @@
 			if(IS_HOE(held) && can_dig_farm(held.material?.hardness))
 				LAZYDISTINCTADD(., /decl/interaction_handler/dig/farm)
 
+/turf/proc/can_show_footsteps()
+	return simulated
+
 /decl/interaction_handler/show_turf_contents
 	name = "Show Turf Contents"
 	expected_user_type = /mob
diff --git a/code/modules/detectivework/tools/uvlight.dm b/code/modules/detectivework/tools/uvlight.dm
index 1b5c3a86b71..90152aa387f 100644
--- a/code/modules/detectivework/tools/uvlight.dm
+++ b/code/modules/detectivework/tools/uvlight.dm
@@ -47,6 +47,7 @@
 			add_overlay(emissive_overlay(icon, "[icon_state]-on"))
 			z_flags |= ZMM_MANGLE_PLANES
 
+// TODO: does this even work with SSoverlays?
 /obj/item/uv_light/proc/clear_last_scan()
 	if(scanned.len)
 		for(var/atom/O in scanned)
@@ -62,7 +63,7 @@
 		stored_alpha.Cut()
 	if(reset_objects.len)
 		for(var/obj/item/I in reset_objects)
-			I.overlays -= I.blood_overlay
+			I.overlays -= I.coating_overlay
 			if(I.fluorescent == FLUORESCENT_GLOWING)
 				I.fluorescent = FLUORESCENT_GLOWS
 		reset_objects.Cut()
@@ -86,6 +87,6 @@
 						A.alpha = use_alpha
 					if(istype(A, /obj/item))
 						var/obj/item/O = A
-						if(O.was_bloodied && !(O.blood_overlay in O.overlays))
-							O.overlays |= O.blood_overlay
+						if(O.was_bloodied && !(O.coating_overlay in O.overlays))
+							O.overlays |= O.coating_overlay
 							reset_objects |= O
\ No newline at end of file
diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm
index 884babdc916..38e6f1d6c10 100644
--- a/code/modules/mob/living/human/human.dm
+++ b/code/modules/mob/living/human/human.dm
@@ -1071,40 +1071,6 @@
 	var/datum/appearance_descriptor/age = LAZYACCESS(bodytype.appearance_descriptors, "age")
 	LAZYSET(appearance_descriptors, "age", (age ? age.sanitize_value(val) : 30))
 
-/mob/living/human/HandleBloodTrail(turf/T, old_loc)
-	// Tracking blood
-	var/obj/item/source
-	var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str)
-	if(istype(shoes))
-		shoes.handle_movement(src, MOVING_QUICKLY(src))
-		if(shoes.coating && shoes.coating.total_volume > 1)
-			source = shoes
-	else
-		for(var/foot_tag in list(BP_L_FOOT, BP_R_FOOT))
-			var/obj/item/organ/external/stomper = GET_EXTERNAL_ORGAN(src, foot_tag)
-			if(stomper && stomper.coating && stomper.coating.total_volume > 1)
-				source = stomper
-	if(!source)
-		species.handle_trail(src, T, old_loc)
-		return
-
-	var/list/bloodDNA
-	var/bloodcolor
-	var/list/blood_data = REAGENT_DATA(source.coating, /decl/material/liquid/blood)
-	if(blood_data)
-		bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE])
-	else
-		bloodDNA = list()
-	bloodcolor = source.coating.get_color()
-	source.remove_coating(1)
-	update_equipment_overlay(slot_shoes_str)
-
-	if(species.get_move_trail(src))
-		T.AddTracks(species.get_move_trail(src),bloodDNA, dir, 0, bloodcolor) // Coming
-		if(isturf(old_loc))
-			var/turf/old_turf = old_loc
-			old_turf.AddTracks(species.get_move_trail(src), bloodDNA, 0, dir, bloodcolor) // Going
-
 /mob/living/human/remove_implant(obj/item/implant, surgical_removal = FALSE, obj/item/organ/external/affected)
 	if((. = ..()) && !surgical_removal)
 		shock_stage += 20
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index fd868a66a6c..446bbe39a05 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -585,8 +585,7 @@
 	if(!root_bodytype)
 		return
 
-	var/static/list/all_stance_limbs = list(ORGAN_CATEGORY_STANCE, ORGAN_CATEGORY_STANCE_ROOT)
-	var/expected_limbs_for_bodytype = root_bodytype.get_expected_organ_count_for_categories(all_stance_limbs)
+	var/expected_limbs_for_bodytype = root_bodytype.get_expected_organ_count_for_categories(global.all_stance_limbs)
 	if(expected_limbs_for_bodytype <= 0)
 		return // we don't care about stance for whatever reason.
 
@@ -598,7 +597,7 @@
 
 	var/found_limbs = 0
 	var/had_limb_pain = FALSE
-	for(var/obj/item/organ/external/limb in get_organs_by_categories(all_stance_limbs))
+	for(var/obj/item/organ/external/limb in get_organs_by_categories(global.all_stance_limbs))
 		found_limbs++
 		var/add_stance_damage = 0
 		if(limb.is_malfunctioning())
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fbb42a1db1a..7685c69fda5 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1552,7 +1552,7 @@ default behaviour is:
 /mob/living/OnSimulatedTurfEntered(turf/T, old_loc)
 	T.add_dirt(0.5)
 
-	HandleBloodTrail(T, old_loc)
+	handle_walking_tracks(T, old_loc)
 
 	if(current_posture.prone)
 		return
@@ -1583,8 +1583,46 @@ default behaviour is:
 			step(src, dir)
 			sleep(1)
 
-/mob/living/proc/HandleBloodTrail(turf/T, old_loc)
-	return
+/mob/living/proc/handle_walking_tracks(turf/T, old_loc)
+
+	if(!T.can_show_footsteps())
+		return
+
+	// Tracking blood or other contaminants
+	var/obj/item/source
+	var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str)
+	if(istype(shoes))
+		shoes.handle_movement(src, MOVING_QUICKLY(src))
+		if(shoes.coating && shoes.coating.total_volume > 1)
+			source = shoes
+	else
+		for(var/obj/item/organ/external/stomper in get_organs_by_categories(global.child_stance_limbs))
+			if(stomper.coating?.total_volume > 1)
+				source = stomper
+				break
+
+	var/decl/species/my_species = get_species()
+	if(!source)
+		my_species?.handle_trail(src, T, old_loc)
+		return
+
+	var/list/bloodDNA
+	var/bloodcolor
+	var/list/blood_data = REAGENT_DATA(source.coating, /decl/material/liquid/blood)
+	if(blood_data)
+		bloodDNA = list(blood_data[DATA_BLOOD_DNA] = blood_data[DATA_BLOOD_TYPE])
+	else
+		bloodDNA = list()
+	bloodcolor = source.coating.get_color()
+	source.remove_coating(1)
+	update_equipment_overlay(slot_shoes_str)
+
+	var/use_move_trail = my_species?.get_move_trail(src)
+	if(use_move_trail)
+		T.AddTracks(use_move_trail, bloodDNA, dir, 0, bloodcolor) // Coming
+		if(isturf(old_loc))
+			var/turf/old_turf = old_loc
+			old_turf.AddTracks(use_move_trail, bloodDNA, 0, dir, bloodcolor) // Going
 
 /mob/living/proc/handle_general_grooming(user, obj/item/grooming/tool)
 	if(tool.grooming_flags & (GROOMABLE_BRUSH|GROOMABLE_COMB))
@@ -1944,3 +1982,13 @@ default behaviour is:
 
 /mob/living/proc/get_age()
 	. = LAZYACCESS(appearance_descriptors, "age") || 30
+
+/mob/living/proc/add_walking_contaminant(material_type, amount, data)
+	var/obj/item/clothing/shoes/shoes = get_equipped_item(slot_shoes_str)
+	if(istype(shoes))
+		if(!buckled)
+			shoes.add_coating(material_type, amount, data)
+	else
+		for(var/obj/item/organ/external/limb in get_organs_by_categories(global.child_stance_limbs))
+			limb.add_coating(material_type, amount, data)
+	update_equipment_overlay(slot_shoes_str)
diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm
index 68863f23666..867c49b5614 100644
--- a/maps/away/mining/mining-signal.dmm
+++ b/maps/away/mining/mining-signal.dmm
@@ -1561,7 +1561,7 @@
 /area/outpost/abandoned)
 "fs" = (
 /obj/effect/decal/cleanable/dirt/visible,
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /turf/floor/tiled/airless,
 /area/outpost/abandoned)
 "ft" = (
@@ -2428,7 +2428,7 @@
 /obj/machinery/atmospherics/pipe/simple/hidden/red{
 	dir = 4
 	},
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/effect/decal/cleanable/dirt/visible,
 /obj/effect/decal/cleanable/dirt/visible,
 /obj/effect/decal/cleanable/dirt/visible,
diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm
index 911447bf612..1881db19743 100644
--- a/maps/away/smugglers/smugglers.dmm
+++ b/maps/away/smugglers/smugglers.dmm
@@ -854,7 +854,7 @@
 /turf/floor,
 /area/smugglers/dorms)
 "cp" = (
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/random/medical/lite,
 /turf/floor/plating/airless,
 /area/smugglers/dorms)
diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm
index 19aad746cee..f31f2097fe4 100644
--- a/maps/away/unishi/unishi-2.dmm
+++ b/maps/away/unishi/unishi-2.dmm
@@ -2283,7 +2283,7 @@
 /turf/space,
 /area/unishi/smresearch)
 "go" = (
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/item/defibrillator,
 /turf/floor/tiled/techfloor,
 /area/unishi/smresearch)
@@ -2397,7 +2397,7 @@
 /obj/machinery/atmospherics/pipe/simple/visible/black{
 	dir = 4
 	},
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
 	dir = 4
 	},
diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm
index b944f249800..c7ef24bae56 100644
--- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm
+++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm
@@ -119,7 +119,7 @@
 "ap" = (
 /obj/effect/decal/cleanable/dirt/visible,
 /obj/effect/decal/cleanable/dirt/visible,
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/effect/decal/cleanable/filth,
 /obj/structure/curtain/open/shower/engineering,
 /obj/structure/hygiene/toilet{
diff --git a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm
index f88a3a7043a..1552d243610 100644
--- a/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm
+++ b/maps/random_ruins/exoplanet_ruins/deserted_lab/deserted_lab.dmm
@@ -184,7 +184,7 @@
 /turf/floor/tiled/white,
 /area/template_noop)
 "aM" = (
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/item/chems/glass/paint/random,
 /obj/structure/closet/medical_wall/filled{
 	pixel_y = 32
diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm
index 6d33bc94adb..c210e95884c 100644
--- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm
+++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm
@@ -4708,7 +4708,7 @@
 /obj/effect/floor_decal/corner/beige{
 	dir = 5
 	},
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /turf/floor/tiled/white/monotile,
 /area/map_template/colony/bathroom)
 "jl" = (
diff --git a/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm b/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm
index dd4f13ac109..c3c28c3bdc9 100644
--- a/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm
+++ b/maps/random_ruins/exoplanet_ruins/spider_nest/spider_nest.dmm
@@ -83,7 +83,7 @@
 /obj/effect/decal/cleanable/blood,
 /obj/item/shard,
 /obj/effect/decal/cleanable/blood/drip,
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /turf/floor/tiled/techfloor,
 /area/template_noop)
 "q" = (
@@ -242,7 +242,7 @@
 /obj/effect/spider/stickyweb,
 /obj/random/voidsuit,
 /obj/random/loot,
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/structure/extinguisher_cabinet{
 	pixel_x = -29;
 	dir = 4
diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm
index 8c98a303b76..8a068ace688 100644
--- a/maps/tradeship/tradeship-1.dmm
+++ b/maps/tradeship/tradeship-1.dmm
@@ -3161,7 +3161,7 @@
 	dir = 1;
 	pixel_y = -21
 	},
-/obj/effect/decal/cleanable/vomit,
+/obj/effect/decal/cleanable/vomit/mapped,
 /obj/structure/hygiene/toilet{
 	dir = 1
 	},

From 456fe957bcf9a5e22300c82cb3ebf8074725bbe3 Mon Sep 17 00:00:00 2001
From: Penelope Haze <out.of.p.haze@proton.me>
Date: Wed, 5 Jun 2024 17:13:10 -0400
Subject: [PATCH 06/10] Allow handmade goods to be used to make moulds

---
 code/modules/crafting/handmade_items.dm | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/code/modules/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm
index 1135a26cc8b..a04fc7eb13f 100644
--- a/code/modules/crafting/handmade_items.dm
+++ b/code/modules/crafting/handmade_items.dm
@@ -12,6 +12,9 @@
 	if((. = ..()))
 		update_icon()
 
+/obj/item/chems/glass/handmade/get_mould_difficulty()
+	return SKILL_NONE
+
 /obj/item/chems/glass/handmade/teapot
 	name = "teapot"
 	desc = "A handmade, slightly lumpy teapot."

From a3acdd5350f92e4a476dc5e6b561b948e7d26bf9 Mon Sep 17 00:00:00 2001
From: Penelope Haze <out.of.p.haze@proton.me>
Date: Wed, 5 Jun 2024 17:09:06 -0400
Subject: [PATCH 07/10] Fix fingerprints infinitely duplicating and lagging the
 server

---
 .../detectivework/evidence/_evidence_type.dm        | 13 +++++++------
 code/modules/detectivework/evidence/fingerprints.dm |  1 +
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/code/modules/detectivework/evidence/_evidence_type.dm b/code/modules/detectivework/evidence/_evidence_type.dm
index 631f8e4c148..3606e7c553f 100644
--- a/code/modules/detectivework/evidence/_evidence_type.dm
+++ b/code/modules/detectivework/evidence/_evidence_type.dm
@@ -4,17 +4,18 @@
 	var/max_entries = 10  //will hold that many entries, removing oldest when overflown
 	var/list/data
 	var/remove_on_transfer //if it should be removed when picked up by forensic samplers
-	var/spot_skill = SKILL_EXPERT	// at what Forensics skill level someone can see it on examine. Set to null, can never see it 
+	var/spot_skill = SKILL_EXPERT	// at what Forensics skill level someone can see it on examine. Set to null, can never see it
 
 //subtypes can implement any merging if needed before calling parent
 /datum/forensics/proc/add_data(newdata)
 	if(!newdata)
 		return
-	if(unique && (newdata in data))
-		return
-	LAZYADD(data, newdata)
+	if(unique)
+		LAZYDISTINCTADD(data, newdata)
+	else
+		LAZYADD(data, newdata)
 	if(length(data) > max_entries)
-		data.Cut(1,2)
+		data.len = max_entries
 
 /datum/forensics/proc/add_from_atom(atom/A)
 
@@ -28,7 +29,7 @@
 	for(var/D in data)
 		. += "<li>[D]"
 	return jointext(., "<br>")
-	
+
 /datum/forensics/proc/can_spot(mob/detective, atom/location)
 	. = FALSE
 	if(spot_skill && detective.skill_check(SKILL_FORENSICS,spot_skill))
diff --git a/code/modules/detectivework/evidence/fingerprints.dm b/code/modules/detectivework/evidence/fingerprints.dm
index 290bccd19c4..0fad8bbb784 100644
--- a/code/modules/detectivework/evidence/fingerprints.dm
+++ b/code/modules/detectivework/evidence/fingerprints.dm
@@ -17,6 +17,7 @@
 			continue
 		for(var/datum/fingerprint/F in data)
 			if(F.merge(newprint))
+				newdata -= newprint
 				continue
 	..()
 

From 8be553419f302977b75f38e72c011f759060a71d Mon Sep 17 00:00:00 2001
From: NebulaSS13Bot <NebulaSS13Bot@gmail.com>
Date: Tue, 7 Jan 2025 00:50:48 +0000
Subject: [PATCH 08/10] Automatic changelog generation [ci skip]

---
 html/changelog.html | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/html/changelog.html b/html/changelog.html
index e3ca3498482..357f4dcea1f 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -98,14 +98,6 @@ <h3 class="author">Penelope Haze updated:</h3>
 				<li class="tweak">Crayons are now slowly used up while writing on paper, at a rate of one charge per 25 characters. (Crayons have a default of 30 charges.)</li>
 				<li class="tweak">Added new furniture to the Shaded Hills inn.</li>
 			</ul>
-
-			<h2 class="date">05 November 2024</h2>
-			<h3 class="author">Neerti updated:</h3>
-			<ul class="changes bgimages16">
-				<li class="tweak">Mining drill braces are now crafted from steel sheets directly.</li>
-				<li class="balance">Microlasers added to mining drills no longer multiply ore out of the ground, but make the drill mine faster, proportionally increasing the energy usage.</li>
-				<li class="balance">Capacitors added to mining drills are less powerful.</li>
-			</ul>
 </div>
 </td></tr></table>
 </body>

From e00cd6f6c016cc8eb598ea922356bc1b301585c3 Mon Sep 17 00:00:00 2001
From: NebulaSS13Bot <NebulaSS13Bot@gmail.com>
Date: Tue, 7 Jan 2025 18:14:07 +1100
Subject: [PATCH 09/10] Automatic changelog generation for PR #4695 [ci skip]

---
 html/changelogs/AutoChangeLog-pr-4695.yml | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 html/changelogs/AutoChangeLog-pr-4695.yml

diff --git a/html/changelogs/AutoChangeLog-pr-4695.yml b/html/changelogs/AutoChangeLog-pr-4695.yml
new file mode 100644
index 00000000000..84dde3ff37c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-4695.yml
@@ -0,0 +1,4 @@
+author: MistakeNot4892
+changes:
+  - {tweak: Mud and blood can now leave footprints.}
+delete-after: true

From 2b87100f52d5b964ad09d92105289c4a7d3c7fec Mon Sep 17 00:00:00 2001
From: MistakeNot4892 <AGMogett@gmail.com>
Date: Tue, 7 Jan 2025 20:12:45 +1100
Subject: [PATCH 10/10] Prevented MC restarts from spamming Discord.

---
 code/controllers/master.dm | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 7db377082bd..f5f59fe940c 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -220,12 +220,17 @@ var/global/datum/controller/master/Master = new
 		CRASH("Attempted to set invalid runlevel: [new_runlevel]")
 
 // Starts the mc, and sticks around to restart it if the loop ever ends.
+var/global/_announced_start = FALSE
 /datum/controller/master/proc/StartProcessing(delay)
 	set waitfor = 0
 	if(delay)
 		sleep(delay)
 	report_progress("Master starting processing")
-	SSwebhooks.send(WEBHOOK_ROUNDPREP, list("map" = station_name(), "url" = get_world_url()))
+
+	if(!global._announced_start) // Only announce roundstart once.
+		SSwebhooks.send(WEBHOOK_ROUNDPREP, list("map" = station_name(), "url" = get_world_url()))
+		global._announced_start = TRUE
+
 	var/rtn = Loop()
 	if (rtn > 0 || processing < 0)
 		return //this was suppose to happen.