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/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.
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/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm
index 5827dbba478..d6881129c23 100644
--- a/code/game/objects/item_mob_overlay.dm
+++ b/code/game/objects/item_mob_overlay.dm
@@ -68,9 +68,9 @@ 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.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)
@@ -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/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/bodytype/bodytype_offsets.dm b/code/modules/bodytype/bodytype_offsets.dm
index e0090189112..0a149de8192 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,19 @@ 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
+// 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
/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/crafting/handmade_items.dm b/code/modules/crafting/handmade_items.dm
index 0435e4adc63..93ae530b113 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."
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)
. += "
[D]"
return jointext(., "
")
-
+
/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
..()
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 88acdc74773..386e4b4d60f 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/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/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/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/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/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 7c7c5b2e24e..8f9b601a8c5 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,34 @@ var/global/list/limb_icon_cache = list()
addtimer(CALLBACK(last_owner, TYPE_PROC_REF(/mob, update_hair)), 1, TIMER_UNIQUE)
return ..()
+/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()
- 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 +91,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 +127,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()?.uid || "BAD_BODYTYPE"]_[render_alpha]_[icon]")
// Skeletons don't care about most icon appearance stuff.
if(limb_flags & ORGAN_FLAG_SKELETAL)
@@ -191,7 +207,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 +340,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 +378,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 +389,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(.)
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 c7bb11aa1f3..31cede40eae 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.
@@ -676,3 +677,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/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("\The [src]'s form contorts subtly.")
- 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("\The [src]'s form contorts subtly.")
- 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("\The [src] shifts and contorts, taking the form of \a ["\improper [new_species]"]!")
- 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/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/html/changelog.html b/html/changelog.html
index e3ca3498482..357f4dcea1f 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -98,14 +98,6 @@ Penelope Haze updated:
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.)
Added new furniture to the Shaded Hills inn.
-
- 05 November 2024
- Neerti updated:
-
- - Mining drill braces are now crafted from steel sheets directly.
- - Microlasers added to mining drills no longer multiply ore out of the ground, but make the drill mine faster, proportionally increasing the energy usage.
- - Capacitors added to mining drills are less powerful.
-