From da64374423593ccb71a1aace5d9ef7af19825287 Mon Sep 17 00:00:00 2001
From: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Date: Thu, 19 Mar 2026 20:32:41 -0500
Subject: [PATCH] Reverts "Add prosthetic limb" surgery to involve targeting
limbs, rather than targeting chest. (Adds stumps) (#95252)
## About The Pull Request
- The `prosthetic replacement` surgical operation has been reverted to
be closer to how it used to work: The operation is done targeting the
limb that's missing
The change was made out of necessity, as surgical state was tied to
limbs - you had to operate on the chest to re-attach limbs because there
was no limb to operate on.
To circumvent that, I have done the unthinkable of adding stumps when
you are dismembered.
- Missing limbs are now represented as an invisible, un-removable,
un-interactable limb.
Making this change was not as difficult as originally anticipated, and
(at least surface level) seems to have broken very little.
Surprisingly little had to change to make this work.
Direct accesses to `mob.bodyparts` was changed to `mob.get_bodyparts()`
with an optional `include_stumps` argument.
Similarly, `get_bodypart()` had an optional `include_stumps` added.
This means we ultimately barely needed to change anything, and in fact,
some loops/checks were able to be streamlined.
## Why It's Good For The Game
- As mentioned, this change was out of necessity and was easily the
least intuitive part of the broader changes. Reverting it back to how it
used to work should make it far easier for people to pick up on, and
means we can cut out a bunch of bespoke instruction sets that I had to
include.
- The addition of stumps also adds a ton of future potential - code wise
it allows for stuff like better damage tracking (we can transfer damage
between limb <-> stump rather than limb <-> chest), and feature we can
do "fun" stuff like have stumps bleed on dismemberment that you can
bandage.
## Changelog
:cl: Melbert
del: "Add prosthetic limb" surgical operation has been reverted to be a
bit closer to how it used to work - you operate on the missing limb /
limb stump, rather than on the chest.
refactor: Missing limbs are now represented as limb stumps. In practice
this should change nothing (for now), as no features were rewritten to
make use of these besides surgery. Please report any oddities with
missing limbs, however.
/:cl:
---
code/__DEFINES/combat.dm | 7 +-
code/__DEFINES/surgery.dm | 5 ++
code/__HELPERS/cmp.dm | 2 +-
code/_onclick/hud/screen_objects.dm | 18 ++--
code/_onclick/item_attack.dm | 2 +-
code/controllers/subsystem/radiation.dm | 2 +-
.../texture_bodypart_overlay.dm | 3 +-
code/datums/components/amputating_limbs.dm | 2 +-
code/datums/components/bloodysoles.dm | 2 +-
code/datums/components/butchering.dm | 4 +-
code/datums/components/free_operation.dm | 8 +-
code/datums/components/healing_touch.dm | 2 +-
.../diseases/advance/symptoms/flesh_eating.dm | 2 +-
code/datums/elements/blood_limb_overlay.dm | 6 +-
code/datums/elements/organ_set_bonus.dm | 4 +-
code/datums/elements/spooky.dm | 2 +-
code/datums/embedding.dm | 4 +-
code/datums/mutations/autotomy.dm | 2 +-
code/datums/mutations/hulk.dm | 4 +-
code/datums/mutations/touch.dm | 4 +-
.../quirks/negative_quirks/body_purist.dm | 2 +-
.../quirks/neutral_quirks/transhumanist.dm | 2 +-
.../status_effects/debuffs/rust_corruption.dm | 2 +-
code/datums/voice_of_god_command.dm | 2 +-
.../machinery/computer/operating_computer.dm | 35 ++------
.../dna_infuser/organ_sets/fish_organs.dm | 4 +-
.../experimental_cloning_record.dm | 2 +-
code/game/machinery/harvester.dm | 2 +-
code/game/objects/items.dm | 2 +-
.../items/devices/scanners/health_analyzer.dm | 2 +-
.../stacks/golem_food/golem_status_effects.dm | 6 +-
code/game/objects/items/stacks/medical.dm | 4 +-
.../objects/items/weaponry/melee/sabre.dm | 2 +-
code/game/objects/items/wiki_manuals.dm | 2 +-
code/game/turfs/open/lava.dm | 2 +-
code/modules/admin/smites/berforate.dm | 2 +-
code/modules/admin/smites/bloodless.dm | 2 +-
code/modules/admin/smites/boneless.dm | 2 +-
code/modules/admin/smites/nugget.dm | 2 +-
code/modules/antagonists/abductor/abductor.dm | 2 +-
.../antagonists/changeling/changeling.dm | 2 +-
.../changeling/powers/defib_grasp.dm | 2 +-
code/modules/antagonists/cult/cult_armor.dm | 2 +-
.../modules/antagonists/heretic/influences.dm | 2 +-
.../heretic/items/heretic_armor.dm | 6 +-
.../heretic/items/heretic_grenade.dm | 2 +-
.../heretic/knowledge/flesh_lore.dm | 2 +-
.../heretic/knowledge/moon_lore.dm | 2 +-
.../heretic/knowledge/rust_lore.dm | 2 +-
.../knowledge/side_knowledge/tier_two.dm | 4 +-
.../heretic/knowledge/starting_lore.dm | 2 +-
.../heretic/magic/apetravulnera.dm | 4 +-
.../antagonists/heretic/magic/blood_cleave.dm | 2 +-
.../antagonists/heretic/magic/blood_siphon.dm | 4 +-
.../heretic/magic/crimson_cleave.dm | 2 +-
.../heretic/status_effects/buffs.dm | 4 +-
.../heretic/status_effects/heretic_passive.dm | 4 +-
.../heretic/status_effects/mark_effects.dm | 2 +-
code/modules/antagonists/spy/spy_bounty.dm | 2 +-
.../voidwalker/voidwalker_traumas.dm | 4 +-
.../grand_ritual/finales/immortality.dm | 2 +-
code/modules/cargo/supplypod.dm | 4 +-
.../preferences/species_features/lizard.dm | 2 +-
.../experisci/experiment/experiments.dm | 4 +-
.../modules/fishing/fish/types/holographic.dm | 2 +-
code/modules/fishing/fish/types/ruins.dm | 2 +-
.../food_and_drinks/machinery/gibber.dm | 2 +-
code/modules/hallucination/blood_flow.dm | 4 +-
.../hallucination/screwy_health_doll.dm | 2 +-
.../jobs/job_types/head_of_security.dm | 2 +-
code/modules/jobs/job_types/prisoner.dm | 2 +-
.../jobs/job_types/security_officer.dm | 2 +-
code/modules/jobs/job_types/warden.dm | 2 +-
code/modules/library/bibles.dm | 2 +-
code/modules/lost_crew/damages/post_mortem.dm | 6 +-
code/modules/lost_crew/lost_crew_manager.dm | 3 +
code/modules/mob/inventory.dm | 1 +
.../living/basic/cult/constructs/harvester.dm | 2 +-
.../living/basic/farm_animals/goat/_goat.dm | 2 +-
.../mob/living/basic/ruin_defender/flesh.dm | 2 +-
.../living/basic/space_fauna/lightgeist.dm | 2 +-
code/modules/mob/living/blood.dm | 8 +-
code/modules/mob/living/carbon/carbon.dm | 59 +++++++-----
.../mob/living/carbon/carbon_defense.dm | 5 +-
.../mob/living/carbon/carbon_update_icons.dm | 90 ++++++++++---------
.../modules/mob/living/carbon/damage_procs.dm | 19 ++--
code/modules/mob/living/carbon/death.dm | 21 +++--
code/modules/mob/living/carbon/examine.dm | 12 +--
.../mob/living/carbon/human/_species.dm | 8 +-
code/modules/mob/living/carbon/human/human.dm | 6 +-
.../mob/living/carbon/human/human_defense.dm | 17 ++--
.../living/carbon/human/human_update_icons.dm | 2 +-
.../mob/living/carbon/human/inventory.dm | 2 +
.../carbon/human/species_types/ethereal.dm | 2 +-
code/modules/mob/living/carbon/life.dm | 2 +-
code/modules/mob/living/death.dm | 7 ++
code/modules/mob/living/living.dm | 4 +-
code/modules/mob/mob_helpers.dm | 2 +-
.../projectiles/ammunition/ballistic/rifle.dm | 2 +-
code/modules/projectiles/guns/magic/staff.dm | 2 +-
.../impure_medicine_reagents.dm | 10 +--
.../chemistry/reagents/other_reagents.dm | 2 +-
.../reagents/reagent_containers/patch.dm | 2 +-
.../religion/festival/instrument_rites.dm | 2 +-
code/modules/religion/religion_sects.dm | 6 +-
.../religion/sparring/sparring_datum.dm | 2 +-
.../xenobiology/crossbreeding/_misc.dm | 9 +-
.../spells/spell_types/touch/scream_for_me.dm | 2 +-
code/modules/surgery/bodyparts/_bodyparts.dm | 24 ++---
.../surgery/bodyparts/dismemberment.dm | 11 ++-
code/modules/surgery/bodyparts/head.dm | 1 +
code/modules/surgery/bodyparts/helpers.dm | 71 +++++++++++----
code/modules/surgery/bodyparts/parts.dm | 6 ++
.../surgery/bodyparts/robot_bodyparts.dm | 2 +-
.../bodyparts/species_parts/misc_bodyparts.dm | 1 +
code/modules/surgery/bodyparts/stumps.dm | 72 +++++++++++++++
code/modules/surgery/operations/_operation.dm | 5 +-
.../surgery/operations/operation_add_limb.dm | 88 +++++++-----------
.../surgery/operations/operation_generic.dm | 9 ++
.../operations/operation_generic_mechanic.dm | 10 ++-
code/modules/surgery/surgery_tools.dm | 2 +-
code/modules/unit_tests/limbsanity.dm | 5 +-
.../unit_tests/organ_bodypart_shuffle.dm | 21 ++++-
code/modules/unit_tests/spawn_humans.dm | 20 +++++
code/modules/unit_tests/surgeries.dm | 4 +-
code/modules/vehicles/cars/clowncar.dm | 18 ++--
code/modules/vending/vendor/tilting.dm | 2 +-
tgstation.dme | 1 +
128 files changed, 566 insertions(+), 386 deletions(-)
create mode 100644 code/modules/surgery/bodyparts/stumps.dm
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 878ed53f990..c4e4b40b56c 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -293,10 +293,11 @@ DEFINE_BITFIELD(ammo_box_multiload, list(
#define BODY_ZONE_PRECISE_L_FOOT "l_foot"
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
-GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
-GLOBAL_LIST_INIT(limb_zones, list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+// These lists are ordered as bodyparts would be ordered
+GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+GLOBAL_LIST_INIT(limb_zones, list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
-GLOBAL_LIST_INIT(leg_zones, list(BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+GLOBAL_LIST_INIT(leg_zones, list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
GLOBAL_LIST_INIT(all_precise_body_zones, list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT))
//We will round to this value in damage calculations.
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index ab8fee7c9a0..fcf8b853cad 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -54,6 +54,9 @@
/// Helper to figure out if a limb is a peg limb
#define IS_PEG_LIMB(limb) (limb.bodytype & BODYTYPE_PEG)
+/// Is the bodypart a stump
+#define IS_STUMP(limb) (limb.bodypart_flags & BODYPART_STUMP)
+
// Flags for the bodypart_flags var on /obj/item/bodypart
/// Bodypart cannot be dismembered or amputated
#define BODYPART_UNREMOVABLE (1<<0)
@@ -65,6 +68,8 @@
#define BODYPART_UNHUSKABLE (1<<3)
/// Bodypart has never been added to a mob
#define BODYPART_VIRGIN (1<<4)
+/// Not a full bodypart, but in fact is part of a missing limb
+#define BODYPART_STUMP (1<<5)
// Bodypart change blocking flags
///Bodypart does not get replaced during set_species()
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 4ff1e10a22f..5e6f71b33c9 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -161,7 +161,7 @@
/// Orders bodyparts by their body_part value, ascending.
/proc/cmp_bodypart_by_body_part_asc(obj/item/bodypart/limb_one, obj/item/bodypart/limb_two)
- return limb_one.body_part - limb_two.body_part
+ return limb_one::body_part - limb_two::body_part
/// Orders by integrated circuit weight
/proc/cmp_port_order_asc(datum/port/compare1, datum/port/compare2)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 533099c3918..250219e8740 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -799,8 +799,9 @@
update_appearance()
/atom/movable/screen/healthdoll/human/update_body_zones()
- limbs = list()
vis_contents.Cut()
+ QDEL_LIST_ASSOC_VAL(limbs)
+ limbs ||= list()
var/mob/living/carbon/human/owner = hud.mymob
for(var/body_zone in owner.get_all_limbs())
var/atom/movable/screen/healthdoll_limb/limb = new(src, null)
@@ -827,12 +828,13 @@
var/list/current_animated = LAZYLISTDUPLICATE(animated_zones)
- for(var/obj/item/bodypart/body_part as anything in owner.bodyparts)
+ for(var/part_zone, body_part_untyped in owner.get_bodyparts_by_zones())
var/icon_key = 0
- var/part_zone = body_part.body_zone
-
+ var/obj/item/bodypart/body_part = body_part_untyped
var/list/overridable_key = list(icon_key)
- if(body_part.bodypart_disabled)
+ if(isnull(body_part) || IS_STUMP(body_part))
+ icon_key = 6
+ else if(body_part.bodypart_disabled)
icon_key = 7
else if(owner.stat == DEAD)
icon_key = "DEAD"
@@ -843,15 +845,11 @@
// calculate what icon state (1-5, or 0 if undamaged) to use based on damage
icon_key = clamp(ceil(damage * 5), 0, 5)
- if(length(body_part.wounds))
+ if(length(body_part?.wounds))
LAZYSET(animated_zones, part_zone, TRUE)
else
LAZYREMOVE(animated_zones, part_zone)
limbs[part_zone].icon_state = "[part_zone][icon_key]"
- // handle leftovers
- for(var/missing_zone in owner.get_missing_limbs())
- limbs[missing_zone].icon_state = "[missing_zone]6"
- LAZYREMOVE(animated_zones, missing_zone)
// time to re-sync animations, something changed
if(animated_zones ~! current_animated)
for(var/animated_zone in animated_zones)
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 7f057a85bf8..c37d6d9d417 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -408,7 +408,7 @@
return TRUE
/mob/living/carbon/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
- var/obj/item/bodypart/hit_bodypart = get_bodypart(hit_zone) || bodyparts[1]
+ var/obj/item/bodypart/hit_bodypart = get_bodypart(hit_zone) || get_bodypart()
if(!hit_bodypart.can_bleed())
return FALSE
diff --git a/code/controllers/subsystem/radiation.dm b/code/controllers/subsystem/radiation.dm
index a129c2a3464..316331db7c9 100644
--- a/code/controllers/subsystem/radiation.dm
+++ b/code/controllers/subsystem/radiation.dm
@@ -132,7 +132,7 @@ SUBSYSTEM_DEF(radiation)
/// Returns whether or not the human is covered head to toe in rad-protected clothing.
/datum/controller/subsystem/radiation/proc/wearing_rad_protected_clothing(mob/living/carbon/human/human)
- for (var/obj/item/bodypart/limb as anything in human.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in human.get_bodyparts())
var/protected = FALSE
for (var/obj/item/clothing as anything in human.get_clothing_on_part(limb))
diff --git a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
index 70086d45f9c..920c2973ac8 100644
--- a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
+++ b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
@@ -17,7 +17,8 @@
appearance.add_filter("bodypart_texture_[texture_icon_state]", 1, layering_filter(icon = cached_texture_icon, blend_mode = BLEND_INSET_OVERLAY))
/datum/bodypart_overlay/texture/generate_icon_cache()
- return "[type]"
+ . = ..()
+ . += "[type]"
/datum/bodypart_overlay/texture/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner, mob/living/carbon/owner, is_husked = FALSE)
if (!..())
diff --git a/code/datums/components/amputating_limbs.dm b/code/datums/components/amputating_limbs.dm
index bfaf52ca90c..28a69ee5a19 100644
--- a/code/datums/components/amputating_limbs.dm
+++ b/code/datums/components/amputating_limbs.dm
@@ -58,7 +58,7 @@
return
var/list/valid_targets = list()
- for (var/obj/item/bodypart/possible_target as anything in limbed_victim.bodyparts)
+ for (var/obj/item/bodypart/possible_target as anything in limbed_victim.get_bodyparts())
if (possible_target.bodypart_flags & BODYPART_UNREMOVABLE)
continue
if (!(possible_target.body_zone in target_zones))
diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm
index 4dc7a7feeb6..09c945c2f8d 100644
--- a/code/datums/components/bloodysoles.dm
+++ b/code/datums/components/bloodysoles.dm
@@ -326,7 +326,7 @@
return
// Find any leg of our human and add that to the footprint, instead of the default which is to just add the human type
- for(var/obj/item/bodypart/leg/affecting in wielder.bodyparts)
+ for(var/obj/item/bodypart/leg/affecting in wielder.get_bodyparts())
if(!affecting.bodypart_disabled)
LAZYSET(footprint.species_types, affecting.limb_id, TRUE)
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index b1f7eebab1f..bdc41329a74 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -167,7 +167,7 @@
if (target.body_zone == BODY_ZONE_CHEST && target.owner)
// Cannot butcher the chest until we hack off all the other limbs
- for (var/obj/item/bodypart/limb as anything in target.owner.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in target.owner.get_bodyparts())
if (limb != target && limb.butcher_drops && limb.butcher_replacement)
to_chat(user, span_warning("You need to butcher all other limbs first!"))
return
@@ -249,7 +249,7 @@
for(var/obj/item/result as anything in results)
if (reagents_in_produced)
if (target.owner.reagents)
- target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.bodyparts), remove_blacklisted = TRUE)
+ target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.get_bodyparts()), remove_blacklisted = TRUE)
result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, target.owner.nutrition / /datum/reagent/consumable/nutriment/fat::nutriment_factor / reagents_in_produced)
if(LAZYLEN(diseases))
diff --git a/code/datums/components/free_operation.dm b/code/datums/components/free_operation.dm
index a8002ef7f23..1c557cda5ea 100644
--- a/code/datums/components/free_operation.dm
+++ b/code/datums/components/free_operation.dm
@@ -9,17 +9,17 @@
return COMPONENT_REDUNDANT
ADD_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
var/mob/living/carbon/owner = parent
- if(!istype(owner))
+ if (!istype(owner))
return
- for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in owner.get_bodyparts(include_stumps = TRUE))
ADD_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
/datum/component/free_operation/Destroy(force)
REMOVE_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
var/mob/living/carbon/owner = parent
- if(!istype(owner))
+ if (!istype(owner))
return ..()
- for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in owner.get_bodyparts(include_stumps = TRUE))
REMOVE_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
return ..()
diff --git a/code/datums/components/healing_touch.dm b/code/datums/components/healing_touch.dm
index b8580e2e458..f7a9515c9c0 100644
--- a/code/datums/components/healing_touch.dm
+++ b/code/datums/components/healing_touch.dm
@@ -174,7 +174,7 @@
if (!iscarbon(target))
return (target.get_brute_loss() > 0 && heal_brute) || (target.get_fire_loss() > 0 && heal_burn)
var/mob/living/carbon/carbon_target = target
- for (var/obj/item/bodypart/part in carbon_target.bodyparts)
+ for (var/obj/item/bodypart/part in carbon_target.get_bodyparts())
if (!(part.brute_dam && heal_brute) && !(part.burn_dam && heal_burn))
continue
if (!isnull(required_bodytype) && !(part.bodytype & required_bodytype))
diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm
index d12e0812335..d767f33fae3 100644
--- a/code/datums/diseases/advance/symptoms/flesh_eating.dm
+++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm
@@ -60,7 +60,7 @@ Bonus
if(bleed)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- var/obj/item/bodypart/random_part = pick(H.bodyparts)
+ var/obj/item/bodypart/random_part = pick(H.get_bodyparts())
random_part.adjustBleedStacks(5 * power)
return 1
diff --git a/code/datums/elements/blood_limb_overlay.dm b/code/datums/elements/blood_limb_overlay.dm
index 3028d3f4754..bab7b6cb0ec 100644
--- a/code/datums/elements/blood_limb_overlay.dm
+++ b/code/datums/elements/blood_limb_overlay.dm
@@ -12,10 +12,10 @@
. = ..()
UnregisterSignal(source, list(COMSIG_BODYPART_GET_LIMB_ICON, COMSIG_BODYPART_GENERATE_ICON_KEY))
-/datum/element/blood_limb_overlay/proc/on_limb_icon(obj/item/bodypart/source, list/limb_icons, dropped, mob/living/carbon/update_on)
+/datum/element/blood_limb_overlay/proc/on_limb_icon(obj/item/bodypart/source, list/limb_icons, dropped)
SIGNAL_HANDLER
- var/list/blood_dna_info = source.blood_dna_info || update_on?.get_blood_dna_list()
+ var/list/blood_dna_info = source.blood_dna_info
if (!LAZYLEN(blood_dna_info) || source.is_invisible)
return
@@ -38,4 +38,4 @@
var/list/blood_dna_info = source.blood_dna_info || source.owner?.get_blood_dna_list()
if (LAZYLEN(blood_dna_info) && !source.is_invisible)
- icon_keys += "-blood-[get_color_from_blood_list(blood_dna_info)]"
+ icon_keys += "blood:[get_color_from_blood_list(blood_dna_info)]"
diff --git a/code/datums/elements/organ_set_bonus.dm b/code/datums/elements/organ_set_bonus.dm
index 07d7e2474d2..49cd58ddf93 100644
--- a/code/datums/elements/organ_set_bonus.dm
+++ b/code/datums/elements/organ_set_bonus.dm
@@ -105,7 +105,7 @@
RegisterSignal(carbon_owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(texture_limb))
RegisterSignal(carbon_owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(untexture_limb))
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
if (!(limb.bodytype & BODYTYPE_ORGANIC))
continue
limb.add_bodypart_overlay(new limb_overlay(), update = FALSE)
@@ -139,7 +139,7 @@
if(QDELETED(carbon_owner))
return
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
var/overlay = locate(limb_overlay) in limb.bodypart_overlays
if(!overlay)
continue
diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm
index 85daaaeda78..4ae70bb5095 100644
--- a/code/datums/elements/spooky.dm
+++ b/code/datums/elements/spooky.dm
@@ -52,7 +52,7 @@
if(isskeleton(human))
return FALSE //undeads are unaffected by the spook-pocalypse.
var/bone_amount = 0
- for(var/obj/item/bodypart/part as anything in human.bodyparts)
+ for(var/obj/item/bodypart/part as anything in human.get_bodyparts())
if((part.biological_state & BIO_FLESH_BONE) == BIO_FLESH_BONE)
bone_amount++
if(bone_amount)
diff --git a/code/datums/embedding.dm b/code/datums/embedding.dm
index 77fec4f8746..32d838eedf4 100644
--- a/code/datums/embedding.dm
+++ b/code/datums/embedding.dm
@@ -133,7 +133,7 @@
failed_embed(victim, hit_zone, random = TRUE)
return
- var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.get_bodypart()
embed_into(victim, limb)
return MOVABLE_IMPACT_ZONE_OVERRIDE
@@ -153,7 +153,7 @@
failed_embed(victim, hit_zone, random = TRUE)
return
- var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.get_bodypart()
embed_into(victim, limb)
SEND_SIGNAL(source, COMSIG_PROJECTILE_ON_EMBEDDED, payload, hit)
diff --git a/code/datums/mutations/autotomy.dm b/code/datums/mutations/autotomy.dm
index 03575a8f039..da34bfd4660 100644
--- a/code/datums/mutations/autotomy.dm
+++ b/code/datums/mutations/autotomy.dm
@@ -27,7 +27,7 @@
return
var/list/parts = list()
- for(var/obj/item/bodypart/to_remove as anything in cast_on.bodyparts)
+ for(var/obj/item/bodypart/to_remove as anything in cast_on.get_bodyparts())
if(to_remove.body_zone == BODY_ZONE_HEAD || to_remove.body_zone == BODY_ZONE_CHEST)
continue
if(to_remove.bodypart_flags & BODYPART_UNREMOVABLE)
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index 9de066eeacb..6fd35026309 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -33,7 +33,7 @@
. = ..()
if(!.)
return
- for(var/obj/item/bodypart/part as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
if (part.bodytype & BODYTYPE_ORGANIC)
part.add_color_override(bodypart_color, LIMB_COLOR_HULK)
owner.update_body_parts()
@@ -74,7 +74,7 @@
/datum/mutation/hulk/on_losing(mob/living/carbon/human/owner)
if(..())
return
- for(var/obj/item/bodypart/part as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
part.remove_color_override(LIMB_COLOR_HULK)
owner.update_body_parts()
owner.clear_mood_event("hulk")
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index ba24f4f30bc..7a91cc390e8 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -226,7 +226,7 @@
// Get at least organic limb to transfer the damage to
var/list/mendicant_organic_limbs = list()
- for(var/obj/item/bodypart/possible_limb in mendicant.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in mendicant.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
mendicant_organic_limbs += possible_limb
// None? Gtfo
@@ -258,7 +258,7 @@
// Get the hurtguy's limbs and the mendicant's limbs to attempt a 1-1 transfer.
var/list/hurt_limbs = hurtguy.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC) + hurtguy.get_wounded_bodyparts(BODYTYPE_ORGANIC)
var/list/mendicant_organic_limbs = list()
- for(var/obj/item/bodypart/possible_limb in mendicant.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in mendicant.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
mendicant_organic_limbs += possible_limb
diff --git a/code/datums/quirks/negative_quirks/body_purist.dm b/code/datums/quirks/negative_quirks/body_purist.dm
index 76221df7a5c..063887b8746 100644
--- a/code/datums/quirks/negative_quirks/body_purist.dm
+++ b/code/datums/quirks/negative_quirks/body_purist.dm
@@ -31,7 +31,7 @@
var/mob/living/carbon/owner = quirk_holder
if(!istype(owner))
return
- for(var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in owner.get_bodyparts())
if(IS_ROBOTIC_LIMB(limb))
cybernetics_level++
for(var/obj/item/organ/organ as anything in owner.organs)
diff --git a/code/datums/quirks/neutral_quirks/transhumanist.dm b/code/datums/quirks/neutral_quirks/transhumanist.dm
index 82570e6cf21..6dfa2d16f6c 100644
--- a/code/datums/quirks/neutral_quirks/transhumanist.dm
+++ b/code/datums/quirks/neutral_quirks/transhumanist.dm
@@ -76,7 +76,7 @@
var/organic_bodytypes = 0
var/silicon_bodytypes = 0
var/other_bodytypes = FALSE
- for(var/obj/item/bodypart/part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/part as anything in target.get_bodyparts())
if(part.bodytype & BODYTYPE_ROBOTIC)
silicon_bodytypes += 1
else if(part.bodytype & BODYTYPE_ORGANIC)
diff --git a/code/datums/status_effects/debuffs/rust_corruption.dm b/code/datums/status_effects/debuffs/rust_corruption.dm
index 6692ef111a0..66374898ab7 100644
--- a/code/datums/status_effects/debuffs/rust_corruption.dm
+++ b/code/datums/status_effects/debuffs/rust_corruption.dm
@@ -13,6 +13,6 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/carbon_owner = owner
- for(var/obj/item/bodypart/robotic_limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb as anything in carbon_owner.get_bodyparts())
if(IS_ROBOTIC_LIMB(robotic_limb))
robotic_limb.receive_damage(10)
diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm
index a49c7cb6c34..22a3993fa78 100644
--- a/code/datums/voice_of_god_command.dm
+++ b/code/datums/voice_of_god_command.dm
@@ -205,7 +205,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
/datum/voice_of_god_command/bleed/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/carbon/human/target in listeners)
- var/obj/item/bodypart/chosen_part = pick(target.bodyparts)
+ var/obj/item/bodypart/chosen_part = pick(target.get_bodyparts())
chosen_part.adjustBleedStacks(5)
/// This command sets the listeners ablaze.
diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm
index d9f645c30bc..416f4c16fac 100644
--- a/code/game/machinery/computer/operating_computer.dm
+++ b/code/game/machinery/computer/operating_computer.dm
@@ -236,33 +236,14 @@
"mechanic" = operation.operation_flags & OPERATION_MECHANIC,
))
- if(!any_recommended && table?.patient)
- var/obj/item/part = table.patient.get_bodypart(deprecise_zone(target_zone))
- var/just_drapes = FALSE
- if(table.patient.has_limbs)
- if(isnull(part))
- data["surgeries"] += list(list(
- "name" = "Prepare for [/datum/surgery_operation/prosthetic_replacement::name]",
- "desc" = "Prepare the patient's chest for prosthetic limb attachment.",
- "tool_rec" = "operate on chest",
- "show_as_next" = TRUE,
- "show_in_list" = FALSE,
- ))
-
- else if(!HAS_TRAIT(part, TRAIT_READY_TO_OPERATE))
- just_drapes = TRUE
-
- else if(!HAS_TRAIT(table.patient, TRAIT_READY_TO_OPERATE))
- just_drapes = TRUE
-
- if(just_drapes)
- data["surgeries"] += list(list(
- "name" = "Prepare for surgery",
- "desc" = "Begin surgery by applying surgical drapes to the patient.",
- "tool_rec" = /obj/item/surgical_drapes::name,
- "show_as_next" = TRUE,
- "show_in_list" = FALSE,
- ))
+ if(!any_recommended && table?.patient && !HAS_TRAIT(table.patient, TRAIT_READY_TO_OPERATE))
+ data["surgeries"] += list(list(
+ "name" = "Prepare for surgery",
+ "desc" = "Begin surgery by applying surgical drapes to the patient or by buckling the patient to the surgical table.",
+ "tool_rec" = /obj/item/surgical_drapes::name,
+ "show_as_next" = TRUE,
+ "show_in_list" = FALSE,
+ ))
return data
diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
index 16bf78ee396..2284e2afa18 100644
--- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
@@ -93,7 +93,7 @@
if (new_value >= FISH_INFUSION_ALL_ORGANS && tail_color)
if (!color_active)
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
limb.add_color_override(tail_color, LIMB_COLOR_FISH_INFUSION)
color_active = TRUE
return
@@ -101,7 +101,7 @@
if (!color_active)
return
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
limb.remove_color_override(LIMB_COLOR_FISH_INFUSION)
color_active = FALSE
diff --git a/code/game/machinery/experimental_cloner/experimental_cloning_record.dm b/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
index 96c1b9bc164..caea82e3677 100644
--- a/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
+++ b/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
@@ -75,7 +75,7 @@
for (var/trauma_type in brain_traumas)
subject.gain_trauma(trauma_type)
- for (var/obj/item/bodypart/limb as anything in subject.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in subject.get_bodyparts())
limb.update_limb(is_creating = TRUE)
subject.updateappearance(mutcolor_update = TRUE)
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 70849a1198f..0af6c9933a4 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -101,7 +101,7 @@
header = "Gruesome!",
)
- operation_order = reverseList(carbon_occupant.bodyparts) //Chest and head are first in bodyparts, so we invert it to make them suffer more
+ operation_order = reverse_range(carbon_occupant.get_bodyparts()) //Chest and head are first in bodyparts, so we invert it to make them suffer more
warming_up = TRUE
harvesting = TRUE
visible_message(span_notice("\The [src] begins warming up!"))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 08ab1724c5e..ec0ff3340cd 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -2217,7 +2217,7 @@
return FALSE
if (!istype(target_limb))
- target_limb = victim.get_bodypart(target_limb) || victim.bodyparts[1]
+ target_limb = victim.get_bodypart(target_limb) || victim.get_bodypart()
return get_embed()?.embed_into(victim, target_limb)
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index e4487f62192..01dfb14d891 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -219,7 +219,7 @@
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
var/any_damage = brute_loss > 0 || fire_loss > 0 || oxy_loss > 0 || tox_loss > 0 || fire_loss > 0
- var/any_missing = length(carbontarget.bodyparts) < (carbontarget.dna?.species?.max_bodypart_count || 6)
+ var/any_missing = length(carbontarget.get_missing_limbs())
var/any_wounded = length(carbontarget.all_wounds)
var/any_embeds = carbontarget.has_embedded_objects()
if(any_damage || (mode == SCANNER_VERBOSE && (any_missing || any_wounded || any_embeds)))
diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
index ab8e3796f22..fe44c1eef41 100644
--- a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
+++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
@@ -78,7 +78,7 @@
if(isgolem(owner))
var/mob/living/carbon/golem_owner = owner
- for (var/obj/item/bodypart/part in golem_owner.bodyparts)
+ for (var/obj/item/bodypart/part in golem_owner.get_bodyparts())
// these overlays won't look good on anything but golem limbs
if (part.limb_id != SPECIES_GOLEM)
continue
@@ -307,7 +307,7 @@
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/light_speed)
var/mob/living/carbon/carbon_owner = owner
- for (var/obj/item/bodypart/arm/arm in carbon_owner.bodyparts)
+ for (var/obj/item/bodypart/arm/arm in carbon_owner.get_bodyparts())
set_arm_fluff(arm)
return TRUE
@@ -388,7 +388,7 @@
var/mob/living/carbon/human/human_owner = owner
RegisterSignal(human_owner, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_punched))
human_owner.physiology.brute_mod *= brute_modifier
- for (var/obj/item/bodypart/arm/arm in human_owner.bodyparts)
+ for (var/obj/item/bodypart/arm/arm in human_owner.get_bodyparts())
buff_arm(arm)
/// Give mining mobs an extra slap
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index d244efb3ad4..de4020bc85d 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -230,7 +230,7 @@
PRIVATE_PROC(TRUE)
var/list/other_affected_limbs = list()
- for(var/obj/item/bodypart/limb as anything in patient.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in patient.get_bodyparts())
if(!try_heal_checks(patient, user, limb.body_zone, silent = TRUE))
continue
other_affected_limbs += limb.body_zone
@@ -774,7 +774,7 @@
return BRUTELOSS
patient.emote("scream")
- for(var/obj/item/bodypart/bone as anything in patient.bodyparts)
+ for(var/obj/item/bodypart/bone as anything in patient.get_bodyparts())
// fine to just, use these raw, its a meme anyway
var/datum/wound/blunt/bone/severe/oof_ouch = new
oof_ouch.apply_wound(bone, wound_source = "bone gel")
diff --git a/code/game/objects/items/weaponry/melee/sabre.dm b/code/game/objects/items/weaponry/melee/sabre.dm
index 770f5b53a9c..0cc90020800 100644
--- a/code/game/objects/items/weaponry/melee/sabre.dm
+++ b/code/game/objects/items/weaponry/melee/sabre.dm
@@ -81,7 +81,7 @@
var/list/legs = list()
var/obj/item/bodypart/bodypart
- for(bodypart in Cuser.bodyparts)
+ for(bodypart in Cuser.get_bodyparts())
if(bodypart == holding_bodypart)
continue
if(bodypart.body_part & ARMS)
diff --git a/code/game/objects/items/wiki_manuals.dm b/code/game/objects/items/wiki_manuals.dm
index 5c0c0802464..56efbd0669a 100644
--- a/code/game/objects/items/wiki_manuals.dm
+++ b/code/game/objects/items/wiki_manuals.dm
@@ -213,7 +213,7 @@
var/obj/item/bodypart/head = H.get_bodypart(BODY_ZONE_HEAD)
if(head)
ADD_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC)
- for(var/obj/item/bodypart/part as anything in H.bodyparts)
+ for(var/obj/item/bodypart/part as anything in H.get_bodyparts())
part.adjustBleedStacks(5)
H.gib_animation()
sleep(0.3 SECONDS)
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index d530d00a00f..a931748a577 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -437,7 +437,7 @@
var/list/immune_parts = list() // Parts we can't transform because they're not organic or can't be dismembered
var/list/transform_parts = list() // Parts we want to transform
- for(var/obj/item/bodypart/burn_limb as anything in burn_human.bodyparts)
+ for(var/obj/item/bodypart/burn_limb as anything in burn_human.get_bodyparts())
if(!IS_ORGANIC_LIMB(burn_limb) || !burn_limb.can_dismember())
immune_parts += burn_limb
continue
diff --git a/code/modules/admin/smites/berforate.dm b/code/modules/admin/smites/berforate.dm
index 6b5fffa38e6..8bd027eb8bc 100644
--- a/code/modules/admin/smites/berforate.dm
+++ b/code/modules/admin/smites/berforate.dm
@@ -41,7 +41,7 @@
dude.Immobilize(5 SECONDS)
for (var/wound_bonus_rep in 1 to repetitions)
- for (var/_limb in dude.bodyparts)
+ for (var/_limb in dude.get_bodyparts())
var/obj/item/bodypart/limb = _limb
var/shots_this_limb = 0
for (var/_iter_turf in shuffle(open_adj_turfs))
diff --git a/code/modules/admin/smites/bloodless.dm b/code/modules/admin/smites/bloodless.dm
index c970e920f22..df43ed2048a 100644
--- a/code/modules/admin/smites/bloodless.dm
+++ b/code/modules/admin/smites/bloodless.dm
@@ -8,7 +8,7 @@
to_chat(user, span_warning("This must be used on a carbon mob."), confidential = TRUE)
return
var/mob/living/carbon/carbon_target = target
- for(var/_limb in carbon_target.bodyparts)
+ for(var/_limb in carbon_target.get_bodyparts())
var/obj/item/bodypart/limb = _limb // fine to use this raw, its a meme smite
var/type_wound = pick(list(/datum/wound/slash/flesh/severe, /datum/wound/slash/flesh/moderate))
limb.force_wound_upwards(type_wound, smited = TRUE)
diff --git a/code/modules/admin/smites/boneless.dm b/code/modules/admin/smites/boneless.dm
index 3fd1a1121f4..5dd2afd6ef2 100644
--- a/code/modules/admin/smites/boneless.dm
+++ b/code/modules/admin/smites/boneless.dm
@@ -10,7 +10,7 @@
return
var/mob/living/carbon/carbon_target = target
- for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_target.get_bodyparts())
var/severity = pick_weight(alist(
WOUND_SEVERITY_MODERATE = 1,
WOUND_SEVERITY_SEVERE = 2,
diff --git a/code/modules/admin/smites/nugget.dm b/code/modules/admin/smites/nugget.dm
index 18e5254e615..9efde93dfee 100644
--- a/code/modules/admin/smites/nugget.dm
+++ b/code/modules/admin/smites/nugget.dm
@@ -11,7 +11,7 @@
var/mob/living/carbon/carbon_target = target
var/timer = 2 SECONDS
- for (var/_limb in carbon_target.bodyparts)
+ for (var/_limb in carbon_target.get_bodyparts())
var/obj/item/bodypart/limb = _limb
if (limb.body_part == HEAD || limb.body_part == CHEST)
continue
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 380eea90b96..fdbfc81b23c 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -99,7 +99,7 @@
// If we have a team skincolor, apply it here. Applied by admins or 2% chance of natural occurance
if(!isnull(team.team_skincolor))
- for(var/obj/item/bodypart/part as anything in new_abductor.bodyparts)
+ for(var/obj/item/bodypart/part as anything in new_abductor.get_bodyparts())
part.should_draw_greyscale = TRUE
part.add_color_override(team.team_skincolor, LIMB_COLOR_AYYLMAO)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index b841ec26626..53abe0952e4 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -801,7 +801,7 @@
chosen_dna.copy_dna(user.dna, COPY_DNA_SE|COPY_DNA_SPECIES)
- for(var/obj/item/bodypart/limb as anything in user.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in user.get_bodyparts())
limb.update_limb(is_creating = TRUE)
user.updateappearance(mutcolor_update = TRUE)
diff --git a/code/modules/antagonists/changeling/powers/defib_grasp.dm b/code/modules/antagonists/changeling/powers/defib_grasp.dm
index 38c14efdfe7..5d63b5b2e05 100644
--- a/code/modules/antagonists/changeling/powers/defib_grasp.dm
+++ b/code/modules/antagonists/changeling/powers/defib_grasp.dm
@@ -63,7 +63,7 @@
if(iscarbon(defibber))
var/removed_arms = 0
var/mob/living/carbon/carbon_defibber = defibber
- for(var/obj/item/bodypart/arm/limb in carbon_defibber.bodyparts)
+ for(var/obj/item/bodypart/arm/limb in carbon_defibber.get_bodyparts())
if(limb.dismember(silent = FALSE))
removed_arms++
qdel(limb)
diff --git a/code/modules/antagonists/cult/cult_armor.dm b/code/modules/antagonists/cult/cult_armor.dm
index d4a62d5fd54..36ede426e69 100644
--- a/code/modules/antagonists/cult/cult_armor.dm
+++ b/code/modules/antagonists/cult/cult_armor.dm
@@ -151,7 +151,7 @@
return
if(!SPT_PROB(15, seconds_per_tick))
return
- var/obj/item/bodypart/bone_to_wound = pick(wearer.bodyparts)
+ var/obj/item/bodypart/bone_to_wound = pick(wearer.get_bodyparts())
var/wound_type = pick(list(
/datum/wound/blunt/bone/moderate,
/datum/wound/pierce/bleed/moderate,
diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm
index 71f79a51cbd..ad7478fc6fd 100644
--- a/code/modules/antagonists/heretic/influences.dm
+++ b/code/modules/antagonists/heretic/influences.dm
@@ -141,7 +141,7 @@
// A very elaborate way to suicide
visible_message(span_userdanger("Psychic tendrils lash out from [src], psychically grabbing onto [user]'s psychically sensitive mind and tearing [user.p_their()] head off!"))
- var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
+ var/obj/item/bodypart/head/head = human_user.get_bodypart(BODY_ZONE_HEAD)
if(head?.dismember())
head.forceMove(src) // stored for later fishage
else
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 4d0dffa781c..8e496353fae 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -142,7 +142,7 @@
return
var/mob/living/carbon/victim = user
var/iteration = 0
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
if(istype(limb, /obj/item/bodypart/head) || istype(limb, /obj/item/bodypart/chest))
continue
iteration++
@@ -259,7 +259,7 @@
if(!length(valid_turfs))
var/mob/living/carbon/carbon_target = target
if(iscarbon(target))
- var/obj/item/bodypart/limb = pick(carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = pick(carbon_target.get_bodyparts())
limb.force_wound_upwards(/datum/wound/slash/flesh/severe)
return
throw_blade(pick(valid_turfs), target)
@@ -451,7 +451,7 @@
return
var/mob/living/carbon/victim = user
var/iteration = 0
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
iteration++
addtimer(CALLBACK(limb, TYPE_PROC_REF(/obj/item/bodypart, force_wound_upwards), /datum/wound/slash/flesh/critical), 1 SECONDS * iteration)
diff --git a/code/modules/antagonists/heretic/items/heretic_grenade.dm b/code/modules/antagonists/heretic/items/heretic_grenade.dm
index 14ceaf1183e..3a5e0e2a129 100644
--- a/code/modules/antagonists/heretic/items/heretic_grenade.dm
+++ b/code/modules/antagonists/heretic/items/heretic_grenade.dm
@@ -95,7 +95,7 @@
addtimer(CALLBACK(victim, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/reagent/pepperspray), 10 SECONDS)
victim.update_damage_hud()
victim.adjust_disgust(5)
- for(var/obj/item/bodypart/robotic_limb in victim.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb in victim.get_bodyparts())
if(robotic_limb.biological_state & BIO_ROBOTIC)
robotic_limb.receive_damage(5, 5)
if(methods & INGEST)
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index cb2ac3d3bde..f5282015b0a 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -267,7 +267,7 @@
return
var/mob/living/carbon/carbon_target = target
- var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(carbon_target.get_bodyparts())
var/datum/wound/crit_wound = new wound_type()
crit_wound.apply_wound(bodypart, attack_direction = get_dir(source, target))
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index f3a9a9da712..eb6428aee27 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -300,7 +300,7 @@
to_chat(carbon_view, span_boldbig(span_red(\
"YOUR SENSES REEL AS YOUR MIND IS ENVELOPED BY AN OTHERWORLDLY FORCE ATTEMPTING TO REWRITE YOUR VERY BEING. \
YOU CANNOT EVEN BEGIN TO SCREAM BEFORE YOUR IMPLANT ACTIVATES ITS PSIONIC FAIL-SAFE PROTOCOL, TAKING YOUR HEAD WITH IT.")))
- var/obj/item/bodypart/head/head = locate() in carbon_view.bodyparts
+ var/obj/item/bodypart/head/head = carbon_view.get_bodypart(BODY_ZONE_HEAD)
if(!head?.dismember())
carbon_view.gib(DROP_ALL_REMAINS)
var/datum/effect_system/reagents_explosion/explosion = new(get_turf(carbon_view), 1, 1, 1)
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 207669735e0..565879a7c8c 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -81,7 +81,7 @@
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
- for(var/obj/item/bodypart/robotic_limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb as anything in carbon_target.get_bodyparts())
if(IS_ROBOTIC_LIMB(robotic_limb))
robotic_limb.receive_damage(500)
diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
index cac879740de..5d155281e5e 100644
--- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
@@ -29,10 +29,10 @@
/datum/heretic_knowledge/codex_morbus/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
. = ..()
var/mob/living/carbon/human/to_fuck_up = locate() in selected_atoms
- for(var/_limb in to_fuck_up.bodyparts)
+ for(var/_limb in to_fuck_up.get_bodyparts())
var/obj/item/bodypart/limb = _limb
limb.force_wound_upwards(/datum/wound/slash/flesh/critical)
- for(var/obj/item/bodypart/limb as anything in to_fuck_up.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in to_fuck_up.get_bodyparts())
to_fuck_up.cause_wound_of_type_and_severity(WOUND_BLUNT, limb, WOUND_SEVERITY_CRITICAL)
return TRUE
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index 830e6edae32..6647ee086bc 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -257,7 +257,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
// If it is, we will damage a random bodypart, and check that bodypart for its body type, to select between 'skin' or 'exterior'.
if(iscarbon(body))
var/mob/living/carbon/carbody = body
- var/obj/item/bodypart/bodypart = pick(carbody.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(carbody.get_bodyparts())
ripped_thing = bodypart
carbody.apply_damage(25, BRUTE, bodypart, sharpness = SHARP_EDGED)
diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm
index c0398b2b745..b878fd36bed 100644
--- a/code/modules/antagonists/heretic/magic/apetravulnera.dm
+++ b/code/modules/antagonists/heretic/magic/apetravulnera.dm
@@ -38,7 +38,7 @@
return FALSE
var/a_limb_got_damaged = FALSE
- for(var/obj/item/bodypart/bodypart in cast_on.bodyparts)
+ for(var/obj/item/bodypart/bodypart in cast_on.get_bodyparts())
if(bodypart.brute_dam < 15)
continue
a_limb_got_damaged = TRUE
@@ -47,7 +47,7 @@
if(!a_limb_got_damaged)
var/datum/wound/slash/crit_wound = new wound_type()
- crit_wound.apply_wound(pick(cast_on.bodyparts))
+ crit_wound.apply_wound(pick(cast_on.get_bodyparts()))
cast_on.visible_message(
span_danger("[cast_on]'s scratches and bruises are torn open by an unholy force!"),
diff --git a/code/modules/antagonists/heretic/magic/blood_cleave.dm b/code/modules/antagonists/heretic/magic/blood_cleave.dm
index 065286f490e..e0c9454ba33 100644
--- a/code/modules/antagonists/heretic/magic/blood_cleave.dm
+++ b/code/modules/antagonists/heretic/magic/blood_cleave.dm
@@ -44,7 +44,7 @@
span_danger("Your veins burst from within and unholy flame erupts from your blood!")
)
- var/obj/item/bodypart/bodypart = pick(victim.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(victim.get_bodyparts())
var/datum/wound/slash/flesh/crit_wound = new wound_type()
crit_wound.apply_wound(bodypart)
victim.apply_damage(20, BURN, wound_bonus = CANT_WOUND)
diff --git a/code/modules/antagonists/heretic/magic/blood_siphon.dm b/code/modules/antagonists/heretic/magic/blood_siphon.dm
index cb0b4ec355c..3ef5a7920f8 100644
--- a/code/modules/antagonists/heretic/magic/blood_siphon.dm
+++ b/code/modules/antagonists/heretic/magic/blood_siphon.dm
@@ -50,11 +50,11 @@
var/mob/living/carbon/carbon_target = cast_on
var/mob/living/carbon/carbon_user = owner
- for(var/obj/item/bodypart/bodypart as anything in carbon_user.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in carbon_user.get_bodyparts())
for(var/datum/wound/iter_wound as anything in bodypart.wounds)
if(prob(50))
continue
- var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in carbon_target.bodyparts
+ var/obj/item/bodypart/target_bodypart = carbon_target.get_bodypart(bodypart.body_zone)
if(!target_bodypart)
continue
iter_wound.remove_wound()
diff --git a/code/modules/antagonists/heretic/magic/crimson_cleave.dm b/code/modules/antagonists/heretic/magic/crimson_cleave.dm
index b64a5221a50..6ee02e2fea9 100644
--- a/code/modules/antagonists/heretic/magic/crimson_cleave.dm
+++ b/code/modules/antagonists/heretic/magic/crimson_cleave.dm
@@ -32,7 +32,7 @@
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/carbon_owner = owner
- for(var/obj/item/bodypart/limbs as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limbs as anything in carbon_owner.get_bodyparts())
for(var/datum/wound/iter_wound as anything in limbs.wounds)
iter_wound.remove_wound()
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index f3660e2f813..e4802049433 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -87,7 +87,7 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/drinker = owner
- for(var/obj/item/bodypart/potentially_wounded as anything in drinker.bodyparts)
+ for(var/obj/item/bodypart/potentially_wounded as anything in drinker.get_bodyparts())
for(var/datum/wound/found_wound as anything in potentially_wounded.wounds)
found_wound.remove_wound()
if(length(drinker.get_missing_limbs()))
@@ -102,7 +102,7 @@
carbie.adjust_brute_loss(-0.5 * seconds_between_ticks, updating_health = FALSE)
carbie.adjust_fire_loss(-0.5 * seconds_between_ticks, updating_health = FALSE)
- for(var/BP in carbie.bodyparts)
+ for(var/BP in carbie.get_bodyparts())
var/obj/item/bodypart/part = BP
for(var/W in part.wounds)
var/datum/wound/wound = W
diff --git a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
index 6f5786b809b..08d2292d677 100644
--- a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
+++ b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
@@ -307,7 +307,7 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/carbon_eater = owner
- for(var/obj/item/bodypart/wounded_limb as anything in carbon_eater.bodyparts)
+ for(var/obj/item/bodypart/wounded_limb as anything in carbon_eater.get_bodyparts())
for(var/datum/wound/to_cure as anything in wounded_limb.wounds)
to_cure.remove_wound()
break
@@ -543,7 +543,7 @@
var/mob/living/carbon/carbon_owner = source
if(passive_level < HERETIC_LEVEL_UPGRADE)
return
- for(var/obj/item/bodypart/wounded_limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/wounded_limb as anything in carbon_owner.get_bodyparts())
for(var/datum/wound/to_cure as anything in wounded_limb.wounds)
to_cure.remove_wound()
for(var/obj/item/organ/internal as anything in carbon_owner.organs)
diff --git a/code/modules/antagonists/heretic/status_effects/mark_effects.dm b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
index 465ad530d24..18b35b2eedc 100644
--- a/code/modules/antagonists/heretic/status_effects/mark_effects.dm
+++ b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
@@ -64,7 +64,7 @@
/datum/status_effect/eldritch/flesh/on_effect()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- var/obj/item/bodypart/bodypart = pick(human_owner.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(human_owner.get_bodyparts())
human_owner.cause_wound_of_type_and_severity(WOUND_SLASH, bodypart, WOUND_SEVERITY_SEVERE)
return ..()
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index d58fd398d7a..a1020c3c93e 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -663,7 +663,7 @@
/datum/spy_bounty/targets_person/some_item/limb_or_organ/find_desired_thing(mob/living/carbon/human/crewmember)
if(ispath(desired_type, /obj/item/bodypart))
- return locate(desired_type) in crewmember.bodyparts
+ return locate(desired_type) in crewmember.get_bodyparts()
if(ispath(desired_type, /obj/item/organ))
return locate(desired_type) in crewmember.organs
return null
diff --git a/code/modules/antagonists/voidwalker/voidwalker_traumas.dm b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
index 3deab1fd0d6..c6c2f3f2340 100644
--- a/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
@@ -45,7 +45,7 @@
RegisterSignal(owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(texture_limb)) //also catch new limbs being attached
RegisterSignal(owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(untexture_limb)) //and remove it from limbs if they go away
- for(var/obj/item/bodypart as anything in owner.bodyparts)
+ for(var/obj/item/bodypart as anything in owner.get_bodyparts())
texture_limb(owner, bodypart)
if(ishuman(owner))
@@ -75,7 +75,7 @@
var/mob/living/carbon/human/human = owner
human.physiology.brute_mod /= brute_mod
- for(var/obj/item/bodypart/bodypart as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in owner.get_bodyparts())
untexture_limb(owner, bodypart)
owner.update_body()
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
index a9047c79878..40f271a30f4 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
@@ -124,7 +124,7 @@
target_quirk.add_to_holder(target)
dna.copy_dna(target.dna, COPY_DNA_SE|COPY_DNA_SPECIES)
- for(var/obj/item/bodypart/limb as anything in target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in target.get_bodyparts())
limb.update_limb(is_creating = TRUE)
target.updateappearance(mutcolor_update = TRUE)
target.domutcheck()
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index c1829cb21cf..6d237f67f8d 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -329,7 +329,7 @@
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
var/mob/living/carbon/carbon_target_mob = target_living
- for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
+ for (var/bp in carbon_target_mob.get_bodyparts()) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)//we dont want to kill him, just teach em a lesson!
if(bodypart.dismember()) //Using the power of flextape i've sawed this man's limb in half!
@@ -342,7 +342,7 @@
organ_to_yeet.forceMove(turf_underneath) //Move the organ outta the body
organ_to_yeet.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(0.1 SECONDS)
- for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
+ for (var/bp in carbon_target_mob.get_bodyparts()) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs))
if (bodypart.dismember()) //Using the power of flextape i've sawed this man's bodypart in half!
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index 10236d9e415..849d1a21114 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -94,7 +94,7 @@
correct_legs[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
correct_legs[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
- for(var/obj/item/bodypart/old_part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/old_part as anything in target.get_bodyparts())
if(old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES)
continue
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 8f658884830..271322b1a49 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -452,7 +452,7 @@
return
if (isandroid(check))
return TRUE
- if (length(check.organs) < 6 || length(check.bodyparts) < 6)
+ if (length(check.organs) < 6 || length(check.get_missing_limbs()) > 1)
return FALSE
var/static/list/augmented_organ_slots = list(
@@ -468,7 +468,7 @@
continue
if (!IS_ROBOTIC_ORGAN(organ))
return FALSE
- for (var/obj/item/bodypart/bodypart as anything in check.bodyparts)
+ for (var/obj/item/bodypart/bodypart as anything in check.get_bodyparts())
if (!IS_ROBOTIC_LIMB(bodypart))
return FALSE
return TRUE
diff --git a/code/modules/fishing/fish/types/holographic.dm b/code/modules/fishing/fish/types/holographic.dm
index a4a10857d46..ae4f550ab85 100644
--- a/code/modules/fishing/fish/types/holographic.dm
+++ b/code/modules/fishing/fish/types/holographic.dm
@@ -115,7 +115,7 @@
if(!iscarbon(user))
return ..()
- for(var/obj/item/bodypart/limb in user.bodyparts)
+ for(var/obj/item/bodypart/limb in user.get_bodyparts())
limb.add_color_override(COLOR_WHITE, LIMB_COLOR_CS_SOURCE_SUICIDE)
limb.add_bodypart_overlay(new /datum/bodypart_overlay/texture/checkered(), update = FALSE)
diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm
index 8654d45538f..db02dcd7130 100644
--- a/code/modules/fishing/fish/types/ruins.dm
+++ b/code/modules/fishing/fish/types/ruins.dm
@@ -167,7 +167,7 @@
return SHAME
var/skin_tone
- for(var/obj/item/bodypart/to_wound as anything in user.bodyparts)
+ for(var/obj/item/bodypart/to_wound as anything in user.get_bodyparts())
if(to_wound == user.get_bodypart(BODY_ZONE_CHEST))
skin_tone = to_wound.species_color || skintone2hex(to_wound.skin_tone)
user.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_CRITICAL, WOUND_SEVERITY_CRITICAL)
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index 3422eee9944..0c661867370 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -234,7 +234,7 @@
var/mob/living/carbon/human/agent_whiskey = victim
var/drop_chance = 0
- for (var/obj/item/bodypart/limb as anything in agent_whiskey.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in agent_whiskey.get_bodyparts())
if (!limb.butcher_drops)
continue
diff --git a/code/modules/hallucination/blood_flow.dm b/code/modules/hallucination/blood_flow.dm
index 7805768c252..71dbb5c8acf 100644
--- a/code/modules/hallucination/blood_flow.dm
+++ b/code/modules/hallucination/blood_flow.dm
@@ -11,11 +11,11 @@
return FALSE
var/mob/living/carbon/carb_hallucinator = hallucinator
- if(!length(carb_hallucinator.bodyparts) || !carb_hallucinator.can_bleed())
+ var/list/bodyparts = carb_hallucinator.get_bodyparts()
+ if(!length(bodyparts) || !carb_hallucinator.can_bleed())
return FALSE
var/obj/item/bodypart/picked
- var/list/bodyparts = carb_hallucinator.bodyparts.Copy()
while(isnull(picked) && length(bodyparts))
picked = pick_n_take(bodyparts)
if(!picked.can_bleed())
diff --git a/code/modules/hallucination/screwy_health_doll.dm b/code/modules/hallucination/screwy_health_doll.dm
index 35f7356b4d4..2ab8a8c928a 100644
--- a/code/modules/hallucination/screwy_health_doll.dm
+++ b/code/modules/hallucination/screwy_health_doll.dm
@@ -50,7 +50,7 @@
/datum/hallucination/fake_health_doll/proc/add_fake_limb(obj/item/bodypart/specific_limb, severity)
var/mob/living/carbon/human/human_mob = hallucinator
- var/obj/item/bodypart/picked = specific_limb || pick(human_mob.bodyparts)
+ var/obj/item/bodypart/picked = specific_limb || pick(human_mob.get_bodyparts())
if(!(picked in bodyparts))
RegisterSignals(picked, list(COMSIG_QDELETING, COMSIG_BODYPART_REMOVED), PROC_REF(remove_bodypart))
RegisterSignal(picked, COMSIG_BODYPART_UPDATING_HEALTH_HUD, PROC_REF(on_bodypart_hud_update))
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 936028f86f0..899224b33f3 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -49,7 +49,7 @@
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 8c55dfcdce0..99b3eab2155 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -73,7 +73,7 @@
var/datum/prisoner_crime/crime = GLOB.prisoner_crimes[crime_name]
if (isnull(crime))
return
- var/list/limbs_to_tat = new_prisoner.bodyparts.Copy()
+ var/list/limbs_to_tat = new_prisoner.get_bodyparts()
for(var/i in 1 to crime.tattoos)
if(!length(SSpersistence.prison_tattoos_to_use) || visuals_only)
return
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 06c78c25f1c..d78fb4f94b0 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index bcc07dfd2d6..eea6d428c10 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -47,7 +47,7 @@
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 950f2c70a5c..07a83912f00 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -215,7 +215,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
return BLESSING_FAILED
var/mob/living/carbon/human/built_in_his_image = blessed
- for(var/obj/item/bodypart/bodypart as anything in built_in_his_image.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in built_in_his_image.get_bodyparts())
if(!IS_ORGANIC_LIMB(bodypart))
balloon_alert(user, "can't heal inorganic!")
return BLESSING_IGNORED
diff --git a/code/modules/lost_crew/damages/post_mortem.dm b/code/modules/lost_crew/damages/post_mortem.dm
index 5eeaabc3302..0e70b0cad62 100644
--- a/code/modules/lost_crew/damages/post_mortem.dm
+++ b/code/modules/lost_crew/damages/post_mortem.dm
@@ -36,7 +36,11 @@
/datum/corpse_damage/post_mortem/limb_loss/apply_to_body(mob/living/carbon/human/body, severity, list/saved_movables, list/datum/callback/on_revive_and_player_occupancy)
var/limbs_to_take = round(min_limbs + (max_limbs - min_limbs) * severity)
- var/list/limbs_we_can_take = body.bodyparts - body.get_bodypart(BODY_ZONE_HEAD) - body.get_bodypart(BODY_ZONE_CHEST)
+ var/list/limbs_we_can_take = list()
+ for(var/zone in GLOB.limb_zones)
+ var/bodypart = body.get_bodypart(zone)
+ if(bodypart)
+ limbs_we_can_take += bodypart
if(!limbs_we_can_take.len)
return
diff --git a/code/modules/lost_crew/lost_crew_manager.dm b/code/modules/lost_crew/lost_crew_manager.dm
index 1494204ad71..08d6d724681 100644
--- a/code/modules/lost_crew/lost_crew_manager.dm
+++ b/code/modules/lost_crew/lost_crew_manager.dm
@@ -36,9 +36,12 @@ GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new)
var/datum/corpse_damage_class/scenario = forced_class || pick_weight(scenarios)
scenario = new scenario ()
+ new_body.living_flags |= STOP_OVERLAY_UPDATE_BODY_PARTS
scenario.apply_character(new_body, protected_items, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.apply_injuries(new_body, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.death_lore += "I should get a formalized assignment!"
+ new_body.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
+ new_body.update_body_parts()
. = new_body
// so bodies can also be used for runes, morgue, etc
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index c3d55e7167b..4c57ced89bb 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -677,6 +677,7 @@
if(hud_used)
hud_used.build_hand_slots()
+ hud_used.healthdoll.update_body_zones()
//GetAllContents that is reasonable and not stupid
/mob/living/proc/get_all_gear(equipment_flags = INCLUDE_ACCESSORIES|INCLUDE_PROSTHETICS, recursive = TRUE)
diff --git a/code/modules/mob/living/basic/cult/constructs/harvester.dm b/code/modules/mob/living/basic/cult/constructs/harvester.dm
index 8fc1226ab09..1c36453b184 100644
--- a/code/modules/mob/living/basic/cult/constructs/harvester.dm
+++ b/code/modules/mob/living/basic/cult/constructs/harvester.dm
@@ -46,7 +46,7 @@
return ..()
var/mob/living/carbon/carbon_target = attack_target
- for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_target.get_bodyparts())
if(limb.body_part == HEAD || limb.body_part == CHEST)
continue
return ..() //if any arms or legs exist, attack
diff --git a/code/modules/mob/living/basic/farm_animals/goat/_goat.dm b/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
index 583542ff5ce..0f94efaf7fb 100644
--- a/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
+++ b/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
@@ -79,7 +79,7 @@
if(ishuman(living_target))
var/mob/living/carbon/human/plant_man = target
- edible_bodypart = pick(plant_man.bodyparts)
+ edible_bodypart = pick(plant_man.get_bodyparts())
edible_bodypart.dismember()
living_target.visible_message(
diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm
index 5d607c2c1a4..94bb50f026d 100644
--- a/code/modules/mob/living/basic/ruin_defender/flesh.dm
+++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm
@@ -114,7 +114,7 @@
return
var/list/zone_candidates = target.get_missing_limbs()
- for(var/obj/item/bodypart/bodypart in target.bodyparts)
+ for(var/obj/item/bodypart/bodypart in target.get_bodyparts())
if(bodypart.body_zone == BODY_ZONE_HEAD || bodypart.body_zone == BODY_ZONE_CHEST)
continue
if(HAS_TRAIT(bodypart, TRAIT_IGNORED_BY_LIVING_FLESH))
diff --git a/code/modules/mob/living/basic/space_fauna/lightgeist.dm b/code/modules/mob/living/basic/space_fauna/lightgeist.dm
index f59c5070bff..36ae135619c 100644
--- a/code/modules/mob/living/basic/space_fauna/lightgeist.dm
+++ b/code/modules/mob/living/basic/space_fauna/lightgeist.dm
@@ -103,7 +103,7 @@
if (!iscarbon(target))
return target.get_brute_loss() > 0 || target.get_fire_loss() > 0
var/mob/living/carbon/carbon_target = target
- for (var/obj/item/bodypart/part in carbon_target.bodyparts)
+ for (var/obj/item/bodypart/part in carbon_target.get_bodyparts())
if (!part.brute_dam && !part.burn_dam)
continue
if (!(part.bodytype & required_bodytype))
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 5b75da86443..f06bed87776 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -180,7 +180,7 @@
bleed(bleed_rate * seconds_per_tick)
bleed_warn(bleed_rate)
- for (var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for (var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if (bodypart.generic_bleedstacks)
bodypart.adjustBleedStacks(-1, 0)
@@ -263,7 +263,7 @@
/// Has each bodypart update its bleed/wound overlay icon states
/mob/living/carbon/proc/update_bodypart_bleed_overlays()
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
iter_part.update_part_wound_overlay()
/// Bleeds amount units of blood from the mob, sometimes creating a blood splatter on the floor.
@@ -290,7 +290,7 @@
return 0
. = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
. += bodypart.cached_bleed_rate
/mob/living/carbon/human/get_bleed_rate()
@@ -359,7 +359,7 @@
/mob/living/carbon/restore_blood()
. = ..()
- for(var/obj/item/bodypart/bodypart_to_restore as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart_to_restore as anything in get_bodyparts())
bodypart_to_restore.setBleedStacks(0)
/****************************************************
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index d1ee541429f..bc884201e83 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -425,8 +425,7 @@
return
var/total_burn = 0
var/total_brute = 0
- for(var/X in bodyparts) //hardcoded to streamline things a bit
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
total_brute += (BP.brute_dam * BP.body_damage_coeff)
total_burn += (BP.burn_dam * BP.body_damage_coeff)
set_health(round(maxHealth - get_oxy_loss() - get_tox_loss() - total_burn - total_brute, DAMAGE_PRECISION))
@@ -773,7 +772,7 @@
if(heal_flags & HEAL_LIMBS)
regenerate_limbs()
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
limb.remove_surgical_state(ALL)
if(heal_flags & (HEAL_REFRESH_ORGANS|HEAL_ORGANS))
@@ -911,13 +910,15 @@
switch(new_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
- set_num_legs(num_legs + 1)
- if(!new_bodypart.bodypart_disabled)
- set_usable_legs(usable_legs + 1)
+ if(!IS_STUMP(new_bodypart))
+ set_num_legs(num_legs + 1)
+ if(!new_bodypart.bodypart_disabled)
+ set_usable_legs(usable_legs + 1)
if(ARM_LEFT, ARM_RIGHT)
- set_num_hands(num_hands + 1)
- if(!new_bodypart.bodypart_disabled)
- set_usable_hands(usable_hands + 1)
+ if(!IS_STUMP(new_bodypart))
+ set_num_hands(num_hands + 1)
+ if(!new_bodypart.bodypart_disabled)
+ set_usable_hands(usable_hands + 1)
synchronize_bodytypes()
synchronize_bodyshapes()
@@ -938,13 +939,27 @@
switch(old_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
- set_num_legs(num_legs - 1)
- if(!old_bodypart.bodypart_disabled)
- set_usable_legs(usable_legs - 1)
+ if(!IS_STUMP(old_bodypart))
+ set_num_legs(num_legs - 1)
+ if(!old_bodypart.bodypart_disabled)
+ set_usable_legs(usable_legs - 1)
if(ARM_LEFT, ARM_RIGHT)
- set_num_hands(num_hands - 1)
- if(!old_bodypart.bodypart_disabled)
- set_usable_hands(usable_hands - 1)
+ if(!IS_STUMP(old_bodypart))
+ set_num_hands(num_hands - 1)
+ if(!old_bodypart.bodypart_disabled)
+ set_usable_hands(usable_hands - 1)
+
+ if(!special && old_bodypart.stump_typepath)
+ if(old_bodypart.type == old_bodypart.stump_typepath)
+ stack_trace("Attempted to replace a stump with a stump")
+ else
+ var/obj/item/bodypart/stump = new old_bodypart.stump_typepath()
+ stump.bodyshape = old_bodypart.bodyshape
+ stump.bodytype = old_bodypart.bodytype
+ if(!stump.try_attach_limb(src, special = TRUE))
+ // the only way this can happen is if the stump is rejected via signal
+ // not much we can do about that besides hope they know what they're doing
+ qdel(stump)
synchronize_bodytypes()
synchronize_bodyshapes()
@@ -952,7 +967,7 @@
///Updates the bodypart speed modifier based on our bodyparts.
/mob/living/carbon/proc/update_bodypart_speed_modifier()
var/final_modification = 0
- for(var/obj/item/bodypart/leg/bodypart in bodyparts)
+ for(var/obj/item/bodypart/leg/bodypart in get_bodyparts())
final_modification += bodypart.speed_modifier
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/bodypart, update = TRUE, multiplicative_slowdown = final_modification)
@@ -985,7 +1000,7 @@
return
var/list/limb_list = list()
if(edit_action == "remove")
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
limb_list += iter_part.body_zone
limb_list -= BODY_ZONE_CHEST
else
@@ -1019,7 +1034,7 @@
var/limb2add = input(usr, "Select a bodypart type to add", "Add/Replace Bodypart") as null|anything in sort_list(limbtypes)
var/obj/item/bodypart/new_bp = new limb2add()
if(new_bp.replace_limb(src))
- admin_ticket_log("key_name_admin(usr)] has replaced [src]'s [part.type] with [new_bp.type]")
+ admin_ticket_log("key_name_admin(usr)] has replaced [src]'s [part?.type || "missing limb"] with [new_bp.type]")
qdel(part)
else
to_chat(usr, "Failed to replace bodypart! They might be incompatible.")
@@ -1097,7 +1112,7 @@
/mob/living/carbon/proc/is_bleeding()
if(!CAN_HAVE_BLOOD(src))
return FALSE
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
if(part.cached_bleed_rate)
return TRUE
@@ -1107,7 +1122,7 @@
return FALSE
var/total_bleed_rate = 0
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
total_bleed_rate += part.cached_bleed_rate
return total_bleed_rate
@@ -1269,9 +1284,9 @@
/// Goes through the organs and bodyparts of the mob and updates their blood_dna_info, in case their blood type has changed (via set_species() or otherwise)
/mob/living/carbon/proc/update_cached_blood_dna_info()
var/list/blood_dna_info = get_blood_dna_list()
- for(var/obj/item/organ/organ in organs)
+ for(var/obj/item/organ/organ as anything in organs)
organ.blood_dna_info = blood_dna_info
- for(var/obj/item/bodypart/bodypart in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
bodypart.blood_dna_info = blood_dna_info
/// Setter for changing a mob's blood type
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 5dc3cbd7d70..497f133b117 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -453,7 +453,7 @@
return
var/embeds = FALSE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts())
for(var/obj/item/weapon as anything in limb.embedded_objects)
if(!embeds)
embeds = TRUE
@@ -562,8 +562,7 @@
/mob/living/carbon/get_organic_health()
. = health
- for (var/_limb in bodyparts)
- var/obj/item/bodypart/limb = _limb
+ for (var/obj/item/bodypart/limb as anything in get_bodyparts())
if (!IS_ORGANIC_LIMB(limb))
. += (limb.brute_dam * limb.body_damage_coeff) + (limb.burn_dam * limb.body_damage_coeff)
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 271eef3cd40..b759c290efe 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -290,7 +290,7 @@
remove_overlay(DAMAGE_LAYER)
var/mutable_appearance/damage_overlay
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
if(!iter_part.dmg_overlay_type)
continue
if(isnull(damage_overlay) && (iter_part.brutestate || iter_part.burnstate))
@@ -319,7 +319,7 @@
return
var/mutable_appearance/wound_overlay
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
if(iter_part.bleed_overlay_icon)
var/mutable_appearance/blood_overlay = mutable_appearance('icons/mob/effects/bleed_overlays.dmi', "blank", -WOUND_LAYER, appearance_flags = KEEP_TOGETHER)
blood_overlay.color = blood_type.get_wound_color(src)
@@ -463,38 +463,35 @@
/mob/living/carbon/proc/update_body_parts(update_limb_data)
update_damage_overlays()
update_wound_overlays()
- var/list/needs_update = list()
var/limb_count_update = 0
- for(var/obj/item/bodypart/limb as anything in bodyparts)
- limb.update_limb(is_creating = update_limb_data) //Update limb actually doesn't do much, get_limb_icon is the cpu eater.
+ var/list/new_limbs = list()
+ for(var/body_zone, limb_untyped in get_bodyparts_by_zones())
+ var/obj/item/bodypart/limb = limb_untyped
+ if(isnull(limb) || IS_STUMP(limb))
+ if(icon_render_keys[body_zone])
+ icon_render_keys -= body_zone
+ limb_count_update += 1
+ continue
- var/old_key = icon_render_keys?[limb.body_zone] //Checks the mob's icon render key list for the bodypart
- icon_render_keys[limb.body_zone] = (limb.is_husked) ? limb.generate_husk_key().Join() : limb.generate_icon_key().Join() //Generates a key for the current bodypart
+ // Update limb actually doesn't do much, get_limb_icon is the cpu eater.
+ limb.update_limb(is_creating = update_limb_data)
- if(icon_render_keys[limb.body_zone] != old_key) //If the keys match, that means the limb doesn't need to be redrawn
- needs_update += limb
+ var/old_key = icon_render_keys[limb.body_zone]
+ var/new_key = limb.get_cache_key()
- limb_count_update += length(needs_update)
- var/list/missing_bodyparts = get_missing_limbs()
- if(((dna ? dna.species.max_bodypart_count : BODYPARTS_DEFAULT_MAXIMUM) - icon_render_keys.len) != missing_bodyparts.len) //Checks to see if the target gained or lost any limbs.
- limb_count_update += 1
- for(var/missing_limb in missing_bodyparts)
- icon_render_keys -= missing_limb //Removes dismembered limbs from the key list
+ if(new_key == old_key)
+ new_limbs += limb_icon_cache[new_key]
+
+ else
+ limb_icon_cache[new_key] ||= limb.get_limb_icon(dropped = FALSE)
+ new_limbs += limb_icon_cache[new_key]
+ icon_render_keys[limb.body_zone] = new_key
+ limb_count_update += 1
. = limb_count_update
if(!.)
return
- //GENERATE NEW LIMBS
- var/list/new_limbs = list()
- for(var/obj/item/bodypart/limb as anything in bodyparts)
- if(limb in needs_update)
- var/bodypart_icon = limb.get_limb_icon(dropped = FALSE, update_on = src)
- new_limbs += bodypart_icon
- limb_icon_cache[icon_render_keys[limb.body_zone]] = bodypart_icon //Caches the icon with the bodypart key, as it is new
- else
- new_limbs += limb_icon_cache[icon_render_keys[limb.body_zone]] //Pulls existing sprites from the cache
-
remove_overlay(BODYPARTS_LAYER)
if(new_limbs.len)
@@ -508,6 +505,13 @@
/////////////////////////
// Limb Icon Cache 2.0 //
/////////////////////////
+
+/// Returns a string representing the bodyparts icon cache key
+/obj/item/bodypart/proc/get_cache_key()
+ if(is_husked)
+ return jointext(generate_husk_key(), "-")
+ return jointext(generate_icon_key(), "-")
+
/**
* Called from update_body_parts() these procs handle the limb icon cache.
* the limb icon cache adds an icon_render_key to a human mob, it represents:
@@ -522,20 +526,20 @@
RETURN_TYPE(/list)
. = list()
if(is_dimorphic)
- . += "[limb_gender]-"
- . += "[limb_id]"
- . += "-[body_zone]"
+ . += limb_gender
+ . += limb_id
+ . += body_zone
if(should_draw_greyscale && draw_color)
- . += "-[draw_color]"
+ . += draw_color
if(is_invisible)
- . += "-invisible"
+ . += "invisible"
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, is_husked))
continue
- . += "-[jointext(overlay.generate_icon_cache(), "-")]"
+ . += overlay.generate_icon_cache()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- . += "-[human_owner.mob_height]"
+ . += "[human_owner.mob_height]"
SEND_SIGNAL(src, COMSIG_BODYPART_GENERATE_ICON_KEY, .)
return .
@@ -543,24 +547,22 @@
/obj/item/bodypart/proc/generate_husk_key()
RETURN_TYPE(/list)
. = list()
- . += "[limb_id]-"
- . += "[husk_type]"
- . += "-husk"
- . += "-[body_zone]"
+ if(is_dimorphic)
+ . += limb_gender
+ . += limb_id
+ . += husk_type
+ . += "husk"
+ . += body_zone
if(is_invisible)
- . += "-invisible"
- var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list()
- if (LAZYLEN(blood_dna))
- . += "-[get_color_from_blood_list(blood_dna)]"
- else
- . += "-[BLOOD_COLOR_RED]"
+ . += "invisible"
+ . += "[LAZYLEN(blood_dna_info) ? get_color_from_blood_list(blood_dna_info) : BLOOD_COLOR_RED]"
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, TRUE))
continue
- . += "-[jointext(overlay.generate_icon_cache(), "-")]"
+ . += overlay.generate_icon_cache()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- . += "-[human_owner.mob_height]"
+ . += "[human_owner.mob_height]"
return .
/obj/item/bodypart/head/generate_icon_key()
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index 191b303f715..fc2a98df0c0 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -20,7 +20,7 @@
// ALso we'll automatically covnert string def zones into bodyparts to pass into parent call.
else if(!isbodypart(def_zone))
var/random_zone = check_zone(def_zone || get_random_valid_zone(def_zone))
- def_zone = get_bodypart(random_zone) || bodyparts[1]
+ def_zone = get_bodypart(random_zone) || get_bodypart()
. = ..()
// Taking brute or burn to bodyparts gives a damage flash
@@ -84,13 +84,13 @@
//These procs fetch a cumulative total damage from all bodyparts
/mob/living/carbon/get_brute_loss()
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
amount += bodypart.brute_dam
return round(amount, DAMAGE_PRECISION)
/mob/living/carbon/get_fire_loss()
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
amount += bodypart.burn_dam
return round(amount, DAMAGE_PRECISION)
@@ -104,7 +104,7 @@
*/
/mob/living/carbon/proc/get_brute_loss_for_type(required_bodytype = ALL)
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if(!(bodypart.bodytype & required_bodytype))
continue
amount += bodypart.brute_dam
@@ -119,7 +119,7 @@
*/
/mob/living/carbon/proc/get_fire_loss_for_type(required_bodytype = ALL)
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if(!(bodypart.bodytype & required_bodytype))
continue
amount += bodypart.burn_dam
@@ -239,8 +239,7 @@
///Returns a list of damaged bodyparts
/mob/living/carbon/proc/get_damaged_bodyparts(brute = FALSE, burn = FALSE, required_bodytype = NONE, target_zone = null)
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(!isnull(target_zone) && BP.body_zone != target_zone)
@@ -252,8 +251,7 @@
///Returns a list of damageable bodyparts
/mob/living/carbon/proc/get_damageable_bodyparts(required_bodytype)
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(BP.brute_dam + BP.burn_dam < BP.max_damage)
@@ -264,8 +262,7 @@
///Returns a list of bodyparts with wounds (in case someone has a wound on an otherwise fully healed limb)
/mob/living/carbon/proc/get_wounded_bodyparts(required_bodytype)
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(LAZYLEN(BP.wounds))
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 77611210d8c..292eda9c6ac 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -28,7 +28,7 @@
return ..()
/mob/living/carbon/get_gibs_type(drop_bitflags = NONE)
- var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST) || (length(bodyparts) ? bodyparts[1] : null)
+ var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST) || get_bodypart()
if (!istype(chest)) // what
return ..()
@@ -74,13 +74,22 @@
qdel(organ)
/mob/living/carbon/spread_bodyparts(drop_bitflags=NONE)
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
+ if(part.body_zone == BODY_ZONE_CHEST)
+ continue // never drop this
if(!(drop_bitflags & DROP_BRAIN) && part.body_zone == BODY_ZONE_HEAD)
- continue
- else if(part.body_zone == BODY_ZONE_CHEST)
- continue
- part.drop_limb()
+ continue // don't drop head if we aren't dropping a brain
+
+ var/list/leftover_organs = list()
+ for(var/obj/item/organ/leftover in part)
+ leftover_organs += leftover
+
+ part.drop_limb(TRUE)
part.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
+ // any organs that weren't throw out already about need to follow the bodypart out
+ for(var/obj/item/organ/leftover as anything in leftover_organs)
+ leftover.Remove(src, TRUE)
+ leftover.bodypart_insert(part)
/mob/living/carbon/set_suicide(suicide_state) //you thought that box trick was pretty clever, didn't you? well now hardmode is on, boyo.
. = ..()
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 946e009d4f8..279e65b8355 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -50,7 +50,7 @@
. += span_deadsay("It appears that [t_his] brain is missing...")
var/list/disabled = list()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
+ for(var/obj/item/bodypart/body_part as anything in get_bodyparts())
if(body_part.bodypart_disabled)
disabled += body_part
@@ -178,7 +178,7 @@
var/list/obj/item/bodypart/bleeding_limbs = list()
var/list/obj/item/bodypart/grasped_limbs = list()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
+ for(var/obj/item/bodypart/body_part as anything in get_bodyparts())
if(body_part.cached_bleed_rate)
bleeding_limbs += body_part.plaintext_zone
if(body_part.grasped_by)
@@ -338,7 +338,7 @@
var/list/seen_damage = list() // This looks like: ({Damage type} = list({Damage description for that damage type} = {number of times it has appeared}, ...), ...)
var/list/most_seen_damage = list() // This looks like: ({Damage type} = {Frequency of the most common description}, ...)
var/list/final_descriptions = list() // This looks like: ({Damage type} = {Most common damage description for that type}, ...)
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
for(var/damage_type in part.damage_examines)
var/damage_desc = part.damage_examines[damage_type]
if(!seen_damage[damage_type])
@@ -373,7 +373,7 @@
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
continue
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
- for(var/obj/item/bodypart/arm/part in bodyparts)
+ for(var/obj/item/bodypart/arm/part in get_bodyparts())
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
continue
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
@@ -475,7 +475,7 @@
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
continue
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
- for(var/obj/item/bodypart/arm/part in bodyparts)
+ for(var/obj/item/bodypart/arm/part in get_bodyparts())
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
continue
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
@@ -594,7 +594,7 @@
/mob/living/carbon/human/proc/get_mismatched_limb_text()
var/list/covered = get_covered_body_zones()
var/list/texts = list()
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
var/part_id = part.limb_id
var/obj/item/bodypart/expected_part = dna?.species?.bodypart_overrides[part.body_zone]
var/expected_id = initial(expected_part?.limb_id)
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 406b3eb8e26..75dbe2f24e9 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -189,6 +189,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
plural_form = "[name]\s"
if(!examine_limb_id)
examine_limb_id = id
+ // Carbons determine bodypart order by this list, so we need to make sure it's sorted properly
+ sortTim(bodypart_overrides, GLOBAL_PROC_REF(cmp_bodypart_by_body_part_asc), associative = TRUE)
return ..()
@@ -1270,7 +1272,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return
// Lets pick a random body part and check for an existing burn
- var/obj/item/bodypart/bodypart = pick(humi.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(humi.get_bodyparts())
var/datum/wound/existing_burn
for (var/datum/wound/iterated_wound as anything in bodypart.wounds)
var/datum/wound_pregen_data/pregen_data = iterated_wound.get_pregen_data()
@@ -1976,7 +1978,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
final_bodypart_overrides[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
final_bodypart_overrides[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
- for(var/obj/item/bodypart/old_part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/old_part as anything in target.get_bodyparts())
if((old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES) || (old_part.bodypart_flags & BODYPART_IMPLANTED))
continue
@@ -2048,7 +2050,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
/// Remove body markings
/datum/species/proc/remove_body_markings(mob/living/carbon/human/hooman)
- for(var/obj/item/bodypart/part as anything in hooman.bodyparts)
+ for(var/obj/item/bodypart/part as anything in hooman.get_bodyparts())
for(var/datum/bodypart_overlay/simple/body_marking/marking in part.bodypart_overlays)
part.remove_bodypart_overlay(marking)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index a49e73fc415..187e684d06e 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -192,8 +192,7 @@
var/status = ""
if(get_brute_loss())
to_chat(human_user, "Physical trauma analysis:")
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
var/brutedamage = BP.brute_dam
if(brutedamage > 0)
status = "received minor physical injuries."
@@ -208,8 +207,7 @@
to_chat(human_user, "[BP] appears to have [status]")
if(get_fire_loss())
to_chat(human_user, "Analysis of skin burns:")
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
var/burndamage = BP.burn_dam
if(burndamage > 0)
status = "signs of minor burns."
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index fedc4d6e58c..86b5d138354 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -13,8 +13,7 @@
//If a specific bodypart is targeted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
armorval += check_armor(BP, type)
organnum++
return (armorval/max(organnum, 1))
@@ -331,8 +330,7 @@
if(EXPLODE_DEVASTATE)
max_limb_loss = 4
probability = 50
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(prob(probability) && !prob(getarmor(BP, BOMB)) && BP.body_zone != BODY_ZONE_HEAD && BP.body_zone != BODY_ZONE_CHEST)
BP.receive_damage(INFINITY, wound_bonus = CANT_WOUND) //Capped by proc
BP.dismember()
@@ -557,10 +555,12 @@
combined_msg += span_notice("You check yourself for injuries.")
- var/list/missing = get_all_limbs()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
- missing -= body_part.body_zone
+ for(var/part_zone, body_part_untyped in get_bodyparts_by_zones())
+ var/obj/item/bodypart/body_part = body_part_untyped
+ if(isnull(body_part) || IS_STUMP(body_part))
+ combined_msg += span_boldannounce("↳ Your [parse_zone(body_part?.body_zone || part_zone)] is missing!")
+ continue
if(body_part.bodypart_flags & BODYPART_PSEUDOPART) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades
continue
@@ -568,9 +568,6 @@
if(bodypart_report)
combined_msg += "[span_notice("↳")] [bodypart_report]"
- for(var/t in missing)
- combined_msg += span_boldannounce("↳ Your [parse_zone(t)] is missing!")
-
var/tox = get_tox_loss() + (disgust / 5) + (HAS_TRAIT(src, TRAIT_SELF_AWARE) ? 0 : (rand(-3, 0) * 5))
switch(tox)
if(10 to 20)
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 7b0d8e7dd0e..15d8175ffcf 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -967,7 +967,7 @@ generate/load female uniform sprites matching all previously decided variables
// optimization - none of our limbs or organs have the desired shape
return .
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts())
var/checked_bodyshape = limb.bodyshape
// accounts for stuff like snouts
for(var/obj/item/organ/organ in limb)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 439ed75d544..10e9edfb715 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -413,6 +413,8 @@
new_bodypart = newBodyPart(BODY_ZONE_L_ARM)
new_bodypart.held_index = i
+ if(i >= 3) // start indexing them as right_arm2 and so on
+ new_bodypart.body_zone = "[new_bodypart.body_zone]_[ceil(i / 2)]"
new_bodypart.try_attach_limb(src, TRUE)
hand_bodyparts[i] = new_bodypart
..() //Don't redraw hands until we have organs for them
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 2d6754d2b95..bfa1ae5649b 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -67,7 +67,7 @@
var/obj/item/organ/heart/ethereal/ethereal_heart = new_ethereal.get_organ_slot(ORGAN_SLOT_HEART)
ethereal_heart.ethereal_color = default_color
- for(var/obj/item/bodypart/limb as anything in new_ethereal.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in new_ethereal.get_bodyparts())
if(limb.limb_id == SPECIES_ETHEREAL)
limb.update_limb(is_creating = TRUE)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index c2e5bb95d46..5fa890e834c 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -502,7 +502,7 @@
return COMPONENT_NO_EXPOSE_REAGENTS
/mob/living/carbon/proc/handle_bodyparts(seconds_per_tick)
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
. |= limb.on_life(seconds_per_tick)
/mob/living/carbon/proc/handle_organs(seconds_per_tick)
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 80189ae4902..32421516713 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -25,6 +25,13 @@
if(drop_bitflags & DROP_BODYPARTS)
spread_bodyparts(drop_bitflags)
+ // failsafe for if we fuck up and leave our brain behind. (other organs are replaceable so we can ignore them.)
+ var/obj/item/organ/brain/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
+ if((drop_bitflags & DROP_BRAIN) && !isnull(brain))
+ stack_trace("gib invoked with drop_brain() had their brain after spilling organs and bodyparts, meaning both failed!")
+ brain.Remove(src)
+ brain.forceMove(drop_location())
+
SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, drop_bitflags)
qdel(src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 25cc456dda3..d9466e62241 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2536,7 +2536,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
return
. = num_legs
num_legs = new_value
-
+ hud_used?.update_locked_slots()
///Proc to modify the value of usable_legs and hook behavior associated to this event.
/mob/living/proc/set_usable_legs(new_value)
@@ -2586,7 +2586,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
return
. = num_hands
num_hands = new_value
-
+ hud_used?.update_locked_slots()
///Proc to modify the value of usable_hands and hook behavior associated to this event.
/mob/living/proc/set_usable_hands(new_value)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 02e382c7117..40ee61766b8 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -62,7 +62,7 @@
/mob/living/carbon/get_random_valid_zone(base_zone, base_probability = 80, list/blacklisted_parts, even_weights, bypass_warning)
var/list/limbs = list()
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
var/limb_zone = part.body_zone //cache the zone since we're gonna check it a ton.
if(limb_zone in blacklisted_parts)
continue
diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm
index a252832dcd0..3c06aa13950 100644
--- a/code/modules/projectiles/ammunition/ballistic/rifle.dm
+++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm
@@ -161,7 +161,7 @@
/datum/embedding/rebar_healium/on_successful_embed(mob/living/carbon/victim, obj/item/bodypart/target_limb)
. = ..()
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
for(var/obj/item/ammo_casing/rebar/healium/other_rebar in limb.embedded_objects)
if (other_rebar == parent)
continue
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index bdbe041d2cf..79f5cfb4fd2 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -362,7 +362,7 @@
if (!iscarbon(user))
return BRUTELOSS
var/mob/living/carbon/suicider = user
- for (var/obj/item/bodypart/limb in suicider.bodyparts)
+ for (var/obj/item/bodypart/limb in suicider.get_bodyparts())
limb.dismember(BRUTE, silent = FALSE, wounding_type = WOUND_SLASH)
sleep(0.25 SECONDS)
diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
index 1b0dad125df..ba6ff560c9f 100644
--- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
@@ -1264,7 +1264,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
for (var/obj/item/organ/organ as anything in exposed_carbon.organs)
organ.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
- for (var/obj/item/bodypart/part as anything in exposed_carbon.bodyparts)
+ for (var/obj/item/bodypart/part as anything in exposed_carbon.get_bodyparts())
part.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
/datum/reagent/inverse/colorful_reagent/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
@@ -1313,22 +1313,22 @@ Basically, we fill the time between now and 2s from now with hands based off the
switch(current_cycle)
if(10)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_MODERATE)
to_chat(affected_mob, span_warning("Your legs start to cave in to your overwhelming gravity!"))
if(20)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_SEVERE)
to_chat(affected_mob, span_warning("Your bones fragment horribly as the gravity pounds on you!"))
if(30)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_CRITICAL)
to_chat(affected_mob, span_warning("The gravity of this situation makes your bones snap like popsicle sticks!"))
/datum/reagent/inverse/gravitum/overdose_start(mob/living/carbon/affected_mob, metabolization_ratio)
. = ..()
affected_mob.AddElement(/datum/element/squish, 120 SECONDS)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_SLASH, leg, WOUND_SEVERITY_SEVERE)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 7bed221fd53..1105c0a611c 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2358,7 +2358,7 @@
for (var/obj/item/organ/organ as anything in exposed_carbon.organs)
organ.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
- for (var/obj/item/bodypart/part as anything in exposed_carbon.bodyparts)
+ for (var/obj/item/bodypart/part as anything in exposed_carbon.get_bodyparts())
part.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
/datum/reagent/colorful_reagent/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index e30529bcd57..66096a368f4 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -90,7 +90,7 @@
. = ..()
if (!.)
return
- var/obj/item/bodypart/affecting = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/affecting = victim.get_bodypart(hit_zone) || victim.get_bodypart()
if (!IS_ORGANIC_LIMB(affecting))
return FALSE
return TRUE
diff --git a/code/modules/religion/festival/instrument_rites.dm b/code/modules/religion/festival/instrument_rites.dm
index e3f53c10fd8..3f03b47747b 100644
--- a/code/modules/religion/festival/instrument_rites.dm
+++ b/code/modules/religion/festival/instrument_rites.dm
@@ -176,7 +176,7 @@
listener.adjust_brute_loss(damage_dealt)
/datum/religion_rites/song_tuner/pain/finish_effect(mob/living/carbon/human/listener, atom/song_source)
- var/obj/item/bodypart/sliced_limb = pick(listener.bodyparts)
+ var/obj/item/bodypart/sliced_limb = pick(listener.get_bodyparts())
sliced_limb.force_wound_upwards(/datum/wound/slash/flesh/moderate/many_cuts)
/datum/religion_rites/song_tuner/lullaby
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index 05cf51ea433..d375a1c3882 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -111,7 +111,7 @@
return BLESSING_FAILED
var/mob/living/carbon/human/blessed = target
- for(var/obj/item/bodypart/bodypart as anything in blessed.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(bodypart))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
return BLESSING_IGNORED
@@ -280,7 +280,7 @@
return BLESSING_IGNORED
var/mob/living/carbon/human/blessed = blessed_living
- for(var/obj/item/bodypart/robolimb as anything in blessed.bodyparts)
+ for(var/obj/item/bodypart/robolimb as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(robolimb))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
return BLESSING_IGNORED
@@ -346,7 +346,7 @@
var/transferred = FALSE
var/list/hurt_limbs = target.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC) + target.get_wounded_bodyparts(BODYTYPE_ORGANIC)
var/list/chaplains_limbs = list()
- for(var/obj/item/bodypart/possible_limb in chaplain.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in chaplain.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
chaplains_limbs += possible_limb
diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm
index fbe0fe93070..c9ea83be5f0 100644
--- a/code/modules/religion/sparring/sparring_datum.dm
+++ b/code/modules/religion/sparring/sparring_datum.dm
@@ -216,7 +216,7 @@
if(PUNISHMENT_BRAND)
var/mob/living/carbon/human/branded = interfering
to_chat(interfering, span_warning("[GLOB.deity] brands your flesh for interfering with [chaplain]'s sparring match!!"))
- var/obj/item/bodypart/branded_limb = pick(branded.bodyparts)
+ var/obj/item/bodypart/branded_limb = pick(branded.get_bodyparts())
branded_limb.force_wound_upwards(/datum/wound/burn/flesh/severe/brand, wound_source = "divine intervention")
branded.emote("scream")
diff --git a/code/modules/research/xenobiology/crossbreeding/_misc.dm b/code/modules/research/xenobiology/crossbreeding/_misc.dm
index a8f22f5f980..a75df1542a6 100644
--- a/code/modules/research/xenobiology/crossbreeding/_misc.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_misc.dm
@@ -37,18 +37,11 @@ Slimecrossing Items
saved_part.old_part.heal_damage(INFINITY, INFINITY, null, FALSE)
saved_part.old_part.receive_damage(saved_part.brute_dam, saved_part.burn_dam, wound_bonus=CANT_WOUND)
dont_chop[zone] = TRUE
- for(var/_part in bodyparts)
- var/obj/item/bodypart/part = _part
- if(dont_chop[part.body_zone])
- continue
- part.drop_limb(TRUE)
/mob/living/carbon/proc/save_bodyparts()
var/list/datum/saved_bodypart/ret = list()
- for(var/_part in bodyparts)
- var/obj/item/bodypart/part = _part
+ for(var/obj/item/bodypart/part as anything in get_bodyparts(include_stumps = TRUE))
var/datum/saved_bodypart/saved_part = new(part)
-
ret[part.body_zone] = saved_part
return ret
diff --git a/code/modules/spells/spell_types/touch/scream_for_me.dm b/code/modules/spells/spell_types/touch/scream_for_me.dm
index d3c142d7039..2c951f1160d 100644
--- a/code/modules/spells/spell_types/touch/scream_for_me.dm
+++ b/code/modules/spells/spell_types/touch/scream_for_me.dm
@@ -28,7 +28,7 @@
return
var/mob/living/carbon/human/human_victim = victim
human_victim.emote("scream")
- for(var/obj/item/bodypart/to_wound as anything in human_victim.bodyparts)
+ for(var/obj/item/bodypart/to_wound as anything in human_victim.get_bodyparts())
human_victim.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_CRITICAL)
return TRUE
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index f266827d4e5..462c7067e17 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -231,6 +231,8 @@
var/static/list/butcher_drop_cache = list()
/// What state is the bodypart in for determining surgery availability
VAR_FINAL/surgery_state = NONE
+ /// Typepath of this limb as a stump
+ var/stump_typepath
/obj/item/bodypart/apply_fantasy_bonuses(bonus)
. = ..()
@@ -625,8 +627,7 @@
SHOULD_CALL_PARENT(TRUE)
var/atom/drop_loc = drop_location()
- if(IS_ORGANIC_LIMB(src))
- playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
+ var/play_sfx = FALSE
for(var/obj/item/organ/bodypart_organ in contents)
if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
@@ -643,10 +644,15 @@
if(drop_loc) //can be null if being deleted
bodypart_organ.forceMove(get_turf(drop_loc))
+ play_sfx = TRUE
if(drop_loc) //can be null during deletion
for(var/atom/movable/movable as anything in src)
movable.forceMove(drop_loc)
+ play_sfx = TRUE
+
+ if(play_sfx && IS_ORGANIC_LIMB(src))
+ playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
update_icon_dropped()
@@ -1261,7 +1267,7 @@
update_icon_dropped()
///Generates an /image for the limb to be used as an overlay
-/obj/item/bodypart/proc/get_limb_icon(dropped, mob/living/carbon/update_on)
+/obj/item/bodypart/proc/get_limb_icon(dropped)
SHOULD_CALL_PARENT(TRUE)
RETURN_TYPE(/list)
@@ -1275,7 +1281,7 @@
// Handles invisibility (not alpha or actual invisibility but invisibility)
if(is_invisible)
. += image(icon_invisible, "invisible_[body_zone]", -BODYPARTS_LAYER, dir = image_dir)
- SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped, update_on)
+ SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped)
return .
// Normal non-husk handling
@@ -1303,7 +1309,7 @@
if(brutestate)
// divided into two overlays: one that gets colored and one that doesn't.
var/image/brute_blood_overlay = image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, dir = SOUTH)
- brute_blood_overlay.color = get_color_from_blood_list(update_on ? update_on.get_blood_dna_list() : blood_dna_info) // living mobs can just get it fresh, dropped limbs use blood_dna_info
+ brute_blood_overlay.color = get_color_from_blood_list(blood_dna_info)
var/mutable_appearance/brute_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0_overlay", -DAMAGE_LAYER, appearance_flags = RESET_COLOR)
if(brute_damage_overlay)
brute_blood_overlay.overlays += brute_damage_overlay
@@ -1384,7 +1390,7 @@
for(var/datum/layer in .)
overlay.modify_bodypart_appearance(layer)
- SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped, update_on)
+ SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped)
return .
/obj/item/bodypart/proc/huskify_image(image/thing_to_husk)
@@ -1396,11 +1402,7 @@
husk_blood.blend_mode = BLEND_INSET_OVERLAY
husk_blood.dir = thing_to_husk.dir
husk_blood.layer = thing_to_husk.layer
- var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list()
- if (LAZYLEN(blood_dna))
- husk_blood.color = get_color_from_blood_list(blood_dna)
- else
- husk_blood.color = BLOOD_COLOR_RED
+ husk_blood.color = LAZYLEN(blood_dna_info) ? get_color_from_blood_list(blood_dna_info) : BLOOD_COLOR_RED
return husk_blood
///Add a bodypart overlay and call the appropriate update procs
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 2960dee5577..72108859d3e 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -17,7 +17,7 @@
if(!silent)
limb_owner.visible_message(span_danger("[limb_owner]'s [name] is violently dismembered!"))
INVOKE_ASYNC(limb_owner, TYPE_PROC_REF(/mob, emote), "scream")
- playsound(get_turf(limb_owner), 'sound/effects/dismember.ogg', 80, TRUE)
+ playsound(limb_owner, 'sound/effects/dismember.ogg', 80, TRUE)
limb_owner.add_mood_event("dismembered_[body_zone]", /datum/mood_event/dismembered, src)
limb_owner.add_mob_memory(/datum/memory/was_dismembered, lost_limb = src)
@@ -123,7 +123,7 @@
if(!special)
phantom_owner.hud_used?.update_locked_slots()
- if(bodypart_flags & BODYPART_PSEUDOPART)
+ if(bodypart_flags & (BODYPART_PSEUDOPART|BODYPART_STUMP))
drop_organs(phantom_owner) //Psuedoparts shouldn't have organs, but just in case
if(!QDELING(src)) // we might be removed as a part of something qdeling us
qdel(src)
@@ -281,6 +281,13 @@
if(!can_attach_limb(new_limb_owner, special))
return FALSE
+ var/obj/item/bodypart/existing_limb = new_limb_owner.get_bodypart(body_zone, include_stumps = TRUE)
+ if(existing_limb)
+ if(existing_limb.type != stump_typepath)
+ stack_trace("Attempted to attach a limb to [new_limb_owner] in zone [body_zone] where they already have a non-stump limb")
+ existing_limb.drop_limb(special = TRUE)
+ qdel(existing_limb)
+
SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_ATTACH_LIMB, src, special, lazy)
SEND_SIGNAL(src, COMSIG_BODYPART_ATTACHED, new_limb_owner, special, lazy)
new_limb_owner.add_bodypart(src)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 73a526361f0..88f4cc912f2 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -27,6 +27,7 @@
bodypart_trait_source = HEAD_TRAIT
butcher_replacement = /obj/item/bodypart/head/skeleton/nonfunctional
base_meat_amount = 0
+ stump_typepath = /obj/item/bodypart/head/stump
/// Do we show the information about missing organs upon being examined? Defaults to TRUE, useful for Dullahan heads.
var/show_organs_on_examine = TRUE
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index c5fc38a8b6c..afb7c18d1e3 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -1,15 +1,55 @@
-
-/mob/living/proc/get_bodypart(zone)
+/**
+ * Returns a bodypart of the specified zone that this mob has
+ *
+ * * zone: the zone to get.
+ * Defaults to chest, allowing for skilling zone nullchecks if you don't care what bodypart you get.
+ * * include_stumps: whether or not to consider stumps as valid bodyparts to return.
+ * Defaults to FALSE, meaning that if a limb is missing (is a stump), nothing will be returned.
+ *
+ * Returns a bodypart, or null.
+ */
+/mob/living/proc/get_bodypart(zone = BODY_ZONE_CHEST, include_stumps = FALSE)
return
-/mob/living/carbon/get_bodypart(zone)
+/mob/living/carbon/get_bodypart(zone = BODY_ZONE_CHEST, include_stumps = FALSE)
RETURN_TYPE(/obj/item/bodypart)
- if(!zone)
- zone = BODY_ZONE_CHEST
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
- if(bodypart.body_zone == zone)
- return bodypart
+ if(!include_stumps && IS_STUMP(bodypart))
+ continue
+ if(bodypart.body_zone != zone)
+ continue
+ return bodypart
+
+/**
+ * Returns all bodyparts this mob has, optionally including stumps.
+ *
+ * include_stumps: whether or not to consider stumps as valid bodyparts to return.
+ * Defaults to FALSE, meaning that if a limb is missing (is a stump), it won't be included in the returned list.
+ *
+ * Returns a list of bodyparts, which may be empty.
+ */
+/mob/living/proc/get_bodyparts(include_stumps = FALSE)
+ var/list/parts = list()
+ for(var/zone in get_all_limbs())
+ var/obj/item/bodypart/bodypart = get_bodypart(zone, include_stumps)
+ if(bodypart)
+ parts += bodypart
+
+ return parts
+
+/**
+ * Returns all bodyparts this mob has, indexed by their body zone
+ * Also includes stumps and nulls, so be sure to check for those if you use this proc.
+ * (Note: nulls will be very rare - as 95% of missing limbs are represented as stumps - but not impossible due to some edge cases)
+ *
+ * Returns a list of bodyparts indexed by their body zone
+ */
+/mob/living/proc/get_bodyparts_by_zones() as /list
+ var/list/parts = list()
+ for(var/zone in get_all_limbs())
+ parts[zone] = get_bodypart(zone)
+ return parts
///Returns TRUE/FALSE on whether the mob should have a limb in a given zone, used for species-restrictions.
/mob/living/carbon/proc/should_have_limb(zone)
@@ -128,13 +168,14 @@
///Returns a list of all limbs this mob should have.
/mob/living/carbon/get_all_limbs()
- if(dna)
- return dna.species.bodypart_overrides.Copy()
- return ..()
+ // gets the "normal list", ie chest-head-legs-arms. order matters for human rendering!
+ . = dna?.species?.bodypart_overrides.Copy() || ..()
+ // includes any additional adminbussed hands
+ for(var/obj/item/bodypart/hand in hand_bodyparts)
+ . |= hand.body_zone
///Returns a list of all missing limbs this mob should have on them, but don't.
/mob/living/carbon/proc/get_missing_limbs() as /list
- RETURN_TYPE(/list)
var/list/full = get_all_limbs()
for(var/zone in full)
if(get_bodypart(zone))
@@ -177,12 +218,12 @@
///Remove all embedded objects from all limbs on the carbon mob
/mob/living/carbon/proc/remove_all_embedded_objects()
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/embedded as anything in bodypart.embedded_objects)
remove_embedded_object(embedded)
/mob/living/carbon/proc/has_embedded_objects(include_harmless = FALSE)
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/embedded as anything in bodypart.embedded_objects)
if(!include_harmless && embedded.get_embed().is_harmless(consider_stamina = TRUE))
continue
@@ -228,7 +269,7 @@
/// Makes sure that the owner's bodytype flags match the flags of all of its parts and organs
/mob/living/carbon/proc/synchronize_bodytypes()
var/all_limb_flags = NONE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/organ/organ in limb)
all_limb_flags |= organ.external_bodytypes
all_limb_flags |= limb.bodytype
@@ -238,7 +279,7 @@
/// Makes sure that the owner's bodyshape flags match the flags of all of its parts and organs
/mob/living/carbon/proc/synchronize_bodyshapes()
var/all_limb_flags = NONE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/organ/organ in limb)
all_limb_flags |= organ.external_bodyshapes
all_limb_flags |= limb.bodyshape
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index c888271cfc2..7c112950265 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -149,6 +149,7 @@
/// Parent Type for arms, should not appear in game.
/obj/item/bodypart/arm
+ abstract_type = /obj/item/bodypart/arm
name = "arm"
desc = "Hey buddy give me a HAND and report this to the github because you shouldn't be seeing this."
abstract_type = /obj/item/bodypart/arm
@@ -324,6 +325,7 @@
px_y = 0
bodypart_trait_source = LEFT_ARM_TRAIT
butcher_replacement = /obj/item/bodypart/arm/left/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/arm/left/stump
/obj/item/bodypart/arm/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_ARM))
@@ -406,6 +408,7 @@
px_y = 0
bodypart_trait_source = RIGHT_ARM_TRAIT
butcher_replacement = /obj/item/bodypart/arm/right/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/arm/right/stump
/obj/item/bodypart/arm/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_ARM))
@@ -475,6 +478,7 @@
/// Parent Type for legs, should not appear in game.
/obj/item/bodypart/leg
+ abstract_type = /obj/item/bodypart/leg
name = "leg"
desc = "This item shouldn't exist. Talk about breaking a leg. Badum-Tss!"
abstract_type = /obj/item/bodypart/leg
@@ -565,6 +569,7 @@
can_be_disabled = TRUE
bodypart_trait_source = LEFT_LEG_TRAIT
butcher_replacement = /obj/item/bodypart/leg/left/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/leg/left/stump
/obj/item/bodypart/leg/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_LEG))
@@ -644,6 +649,7 @@
px_y = 12
bodypart_trait_source = RIGHT_LEG_TRAIT
butcher_replacement = /obj/item/bodypart/leg/right/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/leg/right/stump
/obj/item/bodypart/leg/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_LEG))
diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm
index 4d17428fc9b..fe5d9c6e14d 100644
--- a/code/modules/surgery/bodyparts/robot_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm
@@ -277,7 +277,7 @@
SIGNAL_HANDLER
var/all_robotic = TRUE
- for(var/obj/item/bodypart/part in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
all_robotic = all_robotic && IS_ROBOTIC_LIMB(part)
if(all_robotic)
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index acb4b65a821..41037f538c4 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -547,6 +547,7 @@
throwforce = 25 // It's also a potent weapon
show_organs_on_examine = FALSE
speech_span = null
+ stump_typepath = null
/obj/item/bodypart/head/dullahan/Entered(obj/item/organ/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
diff --git a/code/modules/surgery/bodyparts/stumps.dm b/code/modules/surgery/bodyparts/stumps.dm
new file mode 100644
index 00000000000..49e4cdf2955
--- /dev/null
+++ b/code/modules/surgery/bodyparts/stumps.dm
@@ -0,0 +1,72 @@
+// Invisible bodyparts that serve as placeholders for a missing limb
+// Used so we can add behavior to "missing limbs" without putting it on the mob itself
+
+/obj/item/bodypart/head/stump
+ limb_id = null
+ plaintext_zone = "neck stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+ head_flags = NONE
+ teeth_count = 0 // lol?
+
+/obj/item/bodypart/head/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ // these traits are largely redundant, as many places that check for disabled limbs filter stumps.
+ // however, marking them as unusable for the sake of completeness might make it easier to work with.
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/left/stump
+ limb_id = null
+ plaintext_zone = "left leg stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/leg/left/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/right/stump
+ limb_id = null
+ plaintext_zone = "right leg stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/leg/right/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/arm/left/stump
+ limb_id = null
+ plaintext_zone = "left arm stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/arm/left/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/arm/right/stump
+ limb_id = null
+ plaintext_zone = "right arm stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/arm/right/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
diff --git a/code/modules/surgery/operations/_operation.dm b/code/modules/surgery/operations/_operation.dm
index 95ad653cd3d..8d49b07c9f7 100644
--- a/code/modules/surgery/operations/_operation.dm
+++ b/code/modules/surgery/operations/_operation.dm
@@ -1297,6 +1297,9 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
abstract_type = /datum/surgery_operation/limb
/// Body type required to perform this operation
var/required_bodytype = NONE
+ /// If TRUE, this operation can be performed on stumps.
+ /// If FALSE, the target limb must be a full limb.
+ var/allow_stumps = FALSE
/datum/surgery_operation/limb/all_blocked_strings()
. = ..()
@@ -1308,7 +1311,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/datum/surgery_operation/limb/get_operation_target(atom/movable/operating_on, body_zone)
if (isliving(operating_on))
var/mob/living/patient = operating_on
- return patient.get_bodypart(deprecise_zone(body_zone))
+ return patient.get_bodypart(deprecise_zone(body_zone), allow_stumps)
if (!isbodypart(operating_on))
return null
return operating_on
diff --git a/code/modules/surgery/operations/operation_add_limb.dm b/code/modules/surgery/operations/operation_add_limb.dm
index 3f12368ff12..ab837aafbcb 100644
--- a/code/modules/surgery/operations/operation_add_limb.dm
+++ b/code/modules/surgery/operations/operation_add_limb.dm
@@ -1,7 +1,7 @@
#define OPERATION_REJECTION_DAMAGE "tox_damage"
// This surgery is so snowflake that it doesn't use any of the operation subtypes, it forges its own path
-/datum/surgery_operation/prosthetic_replacement
+/datum/surgery_operation/limb/prosthetic_replacement
name = "prosthetic replacement"
desc = "Replace a missing limb with a prosthetic (or arbitrary) item."
implements = list(
@@ -12,6 +12,7 @@
operation_flags = OPERATION_STANDING_ALLOWED | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NOTABLE | OPERATION_IGNORE_CLOTHES
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
+ allow_stumps = TRUE
/// List of items that are always allowed to be an arm replacement, even if they fail another requirement.
var/list/always_accepted_prosthetics = list(
/obj/item/chainsaw, // the OG, too large otherwise
@@ -21,25 +22,20 @@
/// Radial slice datums for every augment type
VAR_PRIVATE/list/cached_prosthetic_options
-/datum/surgery_operation/prosthetic_replacement/get_default_radial_image()
- return image(/obj/item/bodypart/chest)
-
-/datum/surgery_operation/prosthetic_replacement/get_recommended_tool()
+/datum/surgery_operation/limb/prosthetic_replacement/get_recommended_tool()
return "any limb / any item"
-/datum/surgery_operation/prosthetic_replacement/get_any_tool()
+/datum/surgery_operation/limb/prosthetic_replacement/get_any_tool()
return "Any suitable arm replacement"
-/datum/surgery_operation/prosthetic_replacement/all_required_strings()
- . = list()
- . += "operate on chest (target chest)"
- . += ..()
- . += "when the chest is prepared, target the zone of the limb you are attaching"
+/datum/surgery_operation/limb/prosthetic_replacement/all_required_strings()
+ . = ..()
+ . += "the limb must be missing / a stump"
-/datum/surgery_operation/prosthetic_replacement/any_required_strings()
+/datum/surgery_operation/limb/prosthetic_replacement/any_required_strings()
return list("arms may receive any suitable item in lieu of a replacement limb") + ..()
-/datum/surgery_operation/prosthetic_replacement/get_radial_options(obj/item/bodypart/chest/chest, obj/item/tool, operating_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/get_radial_options(obj/item/bodypart/chest/chest, obj/item/tool, operating_zone)
var/datum/radial_menu_choice/option = LAZYACCESS(cached_prosthetic_options, tool.type)
if(!option)
option = new()
@@ -50,32 +46,10 @@
return option
-/datum/surgery_operation/prosthetic_replacement/get_operation_target(atom/movable/operating_on, body_zone)
- if (!isliving(operating_on))
- return null
- var/mob/living/patient = operating_on
- // We always operate on the chest even if we're targeting left leg or w/e
- return patient.get_bodypart(BODY_ZONE_CHEST)
+/datum/surgery_operation/limb/prosthetic_replacement/state_check(obj/item/bodypart/limb)
+ return IS_STUMP(limb)
-/datum/surgery_operation/prosthetic_replacement/has_surgery_state(obj/item/bodypart/chest/chest, state)
- return LIMB_HAS_SURGERY_STATE(chest, state)
-
-/datum/surgery_operation/prosthetic_replacement/has_any_surgery_state(obj/item/bodypart/chest/chest, state)
- return LIMB_HAS_ANY_SURGERY_STATE(chest, state)
-
-/datum/surgery_operation/prosthetic_replacement/get_patient(obj/item/bodypart/chest/chest)
- return chest.owner
-
-/datum/surgery_operation/prosthetic_replacement/is_available(obj/item/bodypart/chest/chest, operated_zone)
- var/real_operated_zone = deprecise_zone(operated_zone)
- // Operate on the chest but target another zone
- if(!HAS_TRAIT(chest, TRAIT_READY_TO_OPERATE) || real_operated_zone == BODY_ZONE_CHEST)
- return FALSE
- if(chest.owner.get_bodypart(real_operated_zone))
- return FALSE
- return ..()
-
-/datum/surgery_operation/prosthetic_replacement/snowflake_check_availability(obj/item/bodypart/chest, mob/living/surgeon, obj/item/tool, operated_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/snowflake_check_availability(obj/item/bodypart/chest, mob/living/surgeon, obj/item/tool, operated_zone)
if(!surgeon.canUnEquip(tool))
return FALSE
var/real_operated_zone = deprecise_zone(operated_zone)
@@ -91,7 +65,7 @@
return FALSE
return TRUE
-/datum/surgery_operation/prosthetic_replacement/tool_check(obj/item/tool)
+/datum/surgery_operation/limb/prosthetic_replacement/tool_check(obj/item/tool)
if(tool.item_flags & (ABSTRACT|DROPDEL|HAND_ITEM))
return FALSE
if(isbodypart(tool))
@@ -104,41 +78,41 @@
return FALSE
return TRUE
-/datum/surgery_operation/prosthetic_replacement/pre_preop(atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
+/datum/surgery_operation/limb/prosthetic_replacement/pre_preop(atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
. = ..()
// always operate on absolute body zones
operation_args[OPERATION_TARGET_ZONE] = deprecise_zone(operation_args[OPERATION_TARGET_ZONE])
-/datum/surgery_operation/prosthetic_replacement/on_preop(obj/item/bodypart/chest/chest, mob/living/surgeon, obj/item/tool, list/operation_args)
- var/target_zone_readable = parse_zone(operation_args[OPERATION_TARGET_ZONE])
+/datum/surgery_operation/limb/prosthetic_replacement/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
surgeon,
- chest.owner,
- span_notice("You begin to replace [chest.owner]'s missing [target_zone_readable] with [tool]..."),
- span_notice("[surgeon] begins to replace [chest.owner]'s missing [target_zone_readable] with [tool]."),
- span_notice("[surgeon] begins to replace [chest.owner]'s missing [target_zone_readable]."),
+ limb.owner,
+ // "You begin to attach the right arm to john doe's right arm stump"
+ span_notice("You begin to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]."),
)
- display_pain(chest.owner, "You feel an uncomfortable sensation where your [target_zone_readable] should be!")
+ display_pain(limb.owner, "You feel an uncomfortable sensation where your [parse_zone(limb.body_zone)] should be!")
operation_args[OPERATION_REJECTION_DAMAGE] = 10
if(isbodypart(tool))
var/obj/item/bodypart/new_limb = tool
if(IS_ROBOTIC_LIMB(new_limb))
operation_args[OPERATION_REJECTION_DAMAGE] = 0
- else if(new_limb.check_for_frankenstein(chest.owner))
+ else if(new_limb.check_for_frankenstein(limb.owner))
operation_args[OPERATION_REJECTION_DAMAGE] = 30
-/datum/surgery_operation/prosthetic_replacement/on_success(obj/item/bodypart/chest/chest, mob/living/surgeon, obj/item/tool, list/operation_args)
+/datum/surgery_operation/limb/prosthetic_replacement/on_success(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
if(!surgeon.temporarilyRemoveItemFromInventory(tool))
return // should never happen
if(operation_args[OPERATION_REJECTION_DAMAGE] > 0)
- chest.owner.apply_damage(operation_args[OPERATION_REJECTION_DAMAGE], TOX)
+ limb.owner.apply_damage(operation_args[OPERATION_REJECTION_DAMAGE], TOX)
if(isbodypart(tool))
- handle_bodypart(chest.owner, surgeon, tool)
+ handle_bodypart(limb.owner, surgeon, tool)
return
- handle_arbitrary_prosthetic(chest.owner, surgeon, tool, operation_args[OPERATION_TARGET_ZONE])
+ handle_arbitrary_prosthetic(limb.owner, surgeon, tool, operation_args[OPERATION_TARGET_ZONE])
-/datum/surgery_operation/prosthetic_replacement/proc/handle_bodypart(mob/living/carbon/patient, mob/living/surgeon, obj/item/bodypart/bodypart_to_attach)
+/datum/surgery_operation/limb/prosthetic_replacement/proc/handle_bodypart(mob/living/carbon/patient, mob/living/surgeon, obj/item/bodypart/bodypart_to_attach)
bodypart_to_attach.try_attach_limb(patient)
if(bodypart_to_attach.check_for_frankenstein(patient))
bodypart_to_attach.bodypart_flags |= BODYPART_IMPLANTED
@@ -150,7 +124,7 @@
)
display_pain(patient, "You feel synthetic sensation wash from your [bodypart_to_attach.plaintext_zone], which you can feel again!", TRUE)
-/datum/surgery_operation/prosthetic_replacement/proc/handle_arbitrary_prosthetic(mob/living/carbon/patient, mob/living/surgeon, obj/item/thing_to_attach, target_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/proc/handle_arbitrary_prosthetic(mob/living/carbon/patient, mob/living/surgeon, obj/item/thing_to_attach, target_zone)
SSblackbox.record_feedback("tally", "arbitrary_prosthetic", 1, initial(thing_to_attach.name))
var/obj/item/bodypart/new_limb = patient.make_item_prosthetic(thing_to_attach, target_zone, 80)
new_limb.add_surgical_state(SURGERY_PROSTHETIC_UNSECURED)
@@ -187,8 +161,7 @@
span_notice("[surgeon] begins to [tool.singular_name] [limb] to [limb.owner]'s body."),
span_notice("[surgeon] begins to [tool.singular_name] something to [limb.owner]'s body."),
)
- var/obj/item/bodypart/chest = limb.owner.get_bodypart(BODY_ZONE_CHEST)
- display_pain(limb.owner, "[surgeon] begins to [tool.singular_name] [limb] to your body!", IS_ROBOTIC_LIMB(chest))
+ display_pain(limb.owner, "[surgeon] begins to [tool.singular_name] [limb] to your body!", IS_ROBOTIC_LIMB(limb))
/datum/surgery_operation/limb/secure_arbitrary_prosthetic/on_success(obj/item/bodypart/limb, mob/living/surgeon, obj/item/stack/medical/tool, list/operation_args)
display_results(
@@ -198,8 +171,7 @@
span_notice("[surgeon] finishes [tool.apply_verb] [limb] to [limb.owner]'s body."),
span_notice("[surgeon] finishes the [tool.apply_verb] procedure!"),
)
- var/obj/item/bodypart/chest = limb.owner.get_bodypart(BODY_ZONE_CHEST)
- display_pain(limb.owner, "You feel more secure as your prosthetic is firmly attached to your body!", IS_ROBOTIC_LIMB(chest))
+ display_pain(limb.owner, "You feel more secure as your prosthetic is firmly attached to your body!", IS_ROBOTIC_LIMB(limb))
limb.remove_surgical_state(SURGERY_PROSTHETIC_UNSECURED)
limb.AddComponent(/datum/component/item_as_prosthetic_limb, null, 0) // updates drop probability to zero
tool.use(1)
diff --git a/code/modules/surgery/operations/operation_generic.dm b/code/modules/surgery/operations/operation_generic.dm
index 8288971a4a5..fac2fcdc051 100644
--- a/code/modules/surgery/operations/operation_generic.dm
+++ b/code/modules/surgery/operations/operation_generic.dm
@@ -21,6 +21,7 @@
success_sound = 'sound/items/handling/surgery/scalpel2.ogg'
operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
any_surgery_states_blocked = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/// We can't cut mobs with this biostate
var/biostate_blacklist = BIO_CHITIN
@@ -115,6 +116,7 @@
preop_sound = 'sound/items/handling/surgery/retractor1.ogg'
success_sound = 'sound/items/handling/surgery/retractor2.ogg'
all_surgery_states_required = SURGERY_SKIN_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/retract_skin/get_default_radial_image()
return image(/obj/item/retractor)
@@ -165,6 +167,7 @@
/obj/item = 'sound/items/handling/surgery/cautery2.ogg',
)
any_surgery_states_required = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/close_skin/get_any_tool()
return "Any heat source"
@@ -225,6 +228,7 @@
time = 2.4 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_UNCLAMPED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/clamp_bleeders/get_default_radial_image()
return image(/obj/item/hemostat)
@@ -268,6 +272,7 @@
time = 2.4 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/unclamp_bleeders/get_default_radial_image()
return image(/obj/item/hemostat)
@@ -325,6 +330,7 @@
operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/saw_bones/get_any_tool()
return "Any sharp edged item with decent force"
@@ -381,6 +387,7 @@
time = 4 SECONDS
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_required = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/fix_bones/get_default_radial_image()
return image(/obj/item/stack/medical/bone_gel)
@@ -425,6 +432,7 @@
success_sound = 'sound/items/handling/surgery/organ2.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/drill_bones/get_any_tool()
return "Any sharp pointed item with decent force"
@@ -477,6 +485,7 @@
success_sound = 'sound/items/handling/surgery/organ1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_ORGANS_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/incise_organs/get_any_tool()
return "Any sharp edged item"
diff --git a/code/modules/surgery/operations/operation_generic_mechanic.dm b/code/modules/surgery/operations/operation_generic_mechanic.dm
index bbcdfd55fd2..54a97c7348c 100644
--- a/code/modules/surgery/operations/operation_generic_mechanic.dm
+++ b/code/modules/surgery/operations/operation_generic_mechanic.dm
@@ -16,6 +16,7 @@
preop_sound = 'sound/items/tools/screwdriver.ogg'
success_sound = 'sound/items/tools/screwdriver2.ogg'
any_surgery_states_blocked = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_incision/get_any_tool()
return "Any sharp item"
@@ -56,6 +57,7 @@
preop_sound = 'sound/items/tools/ratchet.ogg'
success_sound = 'sound/machines/airlock/doorclick.ogg'
all_surgery_states_required = SURGERY_SKIN_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_open/get_default_radial_image()
return image('icons/hud/screen_gen.dmi', "arrow_large_still")
@@ -93,6 +95,7 @@
preop_sound = 'sound/items/tools/screwdriver.ogg'
success_sound = 'sound/items/tools/screwdriver2.ogg'
any_surgery_states_required = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_close/get_any_tool()
return "Any sharp item"
@@ -137,6 +140,7 @@
success_sound = 'sound/items/taperecorder/taperecorder_close.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_ORGANS_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/prepare_electronics/get_default_radial_image()
return image(/obj/item/multitool)
@@ -170,6 +174,7 @@
preop_sound = 'sound/items/tools/ratchet.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanic_unwrench/get_default_radial_image()
return image(/obj/item/wrench)
@@ -198,10 +203,11 @@
TOOL_WRENCH = 1,
TOOL_RETRACTOR = 1.33,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/ratchet.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED | OPERATION_NO_PATIENT_REQUIRED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanic_wrench/state_check(obj/item/bodypart/limb)
return LIMB_HAS_BONES(limb)
diff --git a/code/modules/surgery/surgery_tools.dm b/code/modules/surgery/surgery_tools.dm
index ed1218394a7..69f97210b30 100644
--- a/code/modules/surgery/surgery_tools.dm
+++ b/code/modules/surgery/surgery_tools.dm
@@ -591,7 +591,7 @@
/obj/item/shears/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is pinching [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
var/timer = 1 SECONDS
- for(var/obj/item/bodypart/thing in user.bodyparts)
+ for(var/obj/item/bodypart/thing in user.get_bodyparts())
if(thing.body_part == CHEST)
continue
addtimer(CALLBACK(thing, TYPE_PROC_REF(/obj/item/bodypart/, dismember)), timer)
diff --git a/code/modules/unit_tests/limbsanity.dm b/code/modules/unit_tests/limbsanity.dm
index 6efec37ab76..13f6a4d7d35 100644
--- a/code/modules/unit_tests/limbsanity.dm
+++ b/code/modules/unit_tests/limbsanity.dm
@@ -1,8 +1,11 @@
/datum/unit_test/limbsanity
/datum/unit_test/limbsanity/Run()
- for(var/path in subtypesof(/obj/item/bodypart) - list(/obj/item/bodypart/arm, /obj/item/bodypart/leg)) /// removes the abstract items.
+ for(var/path in valid_subtypesof(/obj/item/bodypart)) /// removes the abstract items.
var/obj/item/bodypart/part = path
+ if(part::bodypart_flags & BODYPART_STUMP)
+ continue // stumps don't need to have icons
+
if(part::is_dimorphic)
if(!icon_exists(UNLINT(part::should_draw_greyscale ? part::icon_greyscale : part::icon_static), "[part::limb_id]_[part::body_zone]_m"))
TEST_FAIL("[path] does not have a valid icon for male variants")
diff --git a/code/modules/unit_tests/organ_bodypart_shuffle.dm b/code/modules/unit_tests/organ_bodypart_shuffle.dm
index e05c6bd0bd9..ffb2210756a 100644
--- a/code/modules/unit_tests/organ_bodypart_shuffle.dm
+++ b/code/modules/unit_tests/organ_bodypart_shuffle.dm
@@ -20,7 +20,7 @@
TEST_ASSERT_NULL(organ.bodypart_owner, "Organ '[organ.name] kept reference to bodypart after forceMove into nullspace.")
// 3. replace all bodyparts with new ones and place the previously removed organs into the new bodyparts
- for(var/obj/item/bodypart/bodypart as anything in hollow_boy.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in hollow_boy.get_bodyparts())
var/obj/item/bodypart/replacement = allocate(bodypart.type)
for(var/obj/item/organ/organ as anything in removed_organs)
if(replacement.body_zone != deprecise_zone(organ.zone))
@@ -35,3 +35,22 @@
TEST_ASSERT(organ in hollow_boy.organs, "Organ '[organ.name] was put in an empty bodypart that replaced a humans, but the organ did not come with.")
TEST_ASSERT(organ.owner == hollow_boy, "Organ '[organ.name]'s owner was not properly updated to the new human after being placed in a replacement bodypart.")
TEST_ASSERT(organ.bodypart_owner in hollow_boy.bodyparts, "Organ '[organ.name]'s bodypart_owner was not properly updated to the new bodypart after being placed in a replacement bodypart.")
+
+/// Tests that gibbing results in a head with a brain
+/datum/unit_test/gibbing_organ_transfer
+
+/datum/unit_test/gibbing_organ_transfer/Run()
+ var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
+ var/list/bodyparts_for_cleanup = dummy.bodyparts.Copy()
+ var/list/organs_for_cleanup = dummy.organs.Copy()
+ var/obj/item/organ/brain/original_brain = dummy.get_organ_slot(ORGAN_SLOT_BRAIN)
+ var/obj/item/bodypart/head/original_head = dummy.get_bodypart(BODY_ZONE_HEAD)
+ dummy.gib(ALL)
+
+ TEST_ASSERT(QDELETED(dummy), "Dummy was not deleted after gibbing.")
+ TEST_ASSERT(!QDELETED(original_head), "Original head was deleted after gibbing.")
+ TEST_ASSERT(!QDELETED(original_brain), "Original brain was deleted after gibbing.")
+ TEST_ASSERT(original_brain.loc == original_head, "Original brain was not transferred to the head after gibbing.")
+
+ QDEL_LIST(organs_for_cleanup)
+ QDEL_LIST(bodyparts_for_cleanup)
diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm
index f89f59a4cbf..a0691b16c87 100644
--- a/code/modules/unit_tests/spawn_humans.dm
+++ b/code/modules/unit_tests/spawn_humans.dm
@@ -14,3 +14,23 @@
TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_AGEUSIA, NO_TONGUE_TRAIT), "Dummy has ageusia on init, when it should've been removed by its default tongue.")
TEST_ASSERT(!dummy.is_blind_from(NO_EYES), "Dummy is blind on init, when it should've been removed by its default eyes.")
TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_DEAF, NO_EARS), "Dummy is deaf on init, when it should've been removed by its default ears.")
+
+/// Tests that we can change a mob's hand count without everything breaking
+/datum/unit_test/many_armed_humans
+
+/datum/unit_test/many_armed_humans/Run()
+ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent)
+ dummy.change_number_of_hands(4)
+
+/// Tests spawned humans have the correct bodypart order
+/datum/unit_test/human_bodypart_order
+
+/datum/unit_test/human_bodypart_order/Run()
+ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent)
+ var/list/obj/item/bodypart/bodyparts = dummy.get_bodyparts()
+ TEST_ASSERT(bodyparts[1].body_zone == BODY_ZONE_CHEST, "First bodypart in bodyparts list is not the chest, this is important for human rendering")
+ TEST_ASSERT(bodyparts[2].body_zone == BODY_ZONE_HEAD, "Second bodypart in bodyparts list is not the head, this is important for human rendering")
+
+ var/list/obj/item/bodypart/bodyparts_by_zone = dummy.get_bodyparts_by_zones()
+ TEST_ASSERT(bodyparts_by_zone[1] == BODY_ZONE_CHEST, "First bodypart in bodyparts_by_zone list is not the chest, this is important for human rendering")
+ TEST_ASSERT(bodyparts_by_zone[2] == BODY_ZONE_HEAD, "Second bodypart in bodyparts_by_zone list is not the head, this is important for human rendering")
diff --git a/code/modules/unit_tests/surgeries.dm b/code/modules/unit_tests/surgeries.dm
index 69bc29fb148..7a538da8fbb 100644
--- a/code/modules/unit_tests/surgeries.dm
+++ b/code/modules/unit_tests/surgeries.dm
@@ -57,9 +57,9 @@
TEST_ASSERT_EQUAL(bobs_head.real_name, "Bob", "Bob's head does not remember that it is from Bob")
// Put Bob's head onto Alice's body
- var/datum/surgery_operation/prosthetic_replacement/surgery = GLOB.operations.operations_by_typepath[__IMPLIED_TYPE__]
+ var/datum/surgery_operation/limb/prosthetic_replacement/surgery = GLOB.operations.operations_by_typepath[__IMPLIED_TYPE__]
user.put_in_active_hand(bobs_head)
- UNLINT(surgery.success(alice.get_bodypart(BODY_ZONE_CHEST), user, bobs_head, list()))
+ UNLINT(surgery.success(alice.get_bodypart(BODY_ZONE_HEAD, TRUE), user, bobs_head, list()))
TEST_ASSERT(!isnull(alice.get_bodypart(BODY_ZONE_HEAD)), "Alice has no head after prosthetic replacement")
TEST_ASSERT_EQUAL(alice.get_visible_name(), "Bob", "Bob's head was transplanted onto Alice's body, but their name is not Bob")
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index 0672586220c..6cff8a743f6 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -143,14 +143,16 @@
for(var/mob/living/carbon/carbon_occupant in occupants)
if(prob(35)) //Note: The randomstep on dump_mobs throws occupants into each other and often causes wounds regardless.
continue
- for(var/obj/item/bodypart/head/head_to_wound as anything in carbon_occupant.bodyparts)
- var/pick_mode = text2num(pick(list(
- "[WOUND_PICK_LOWEST_SEVERITY]",
- "[WOUND_PICK_HIGHEST_SEVERITY]"
- )))
- carbon_occupant.cause_wound_of_type_and_severity(WOUND_BLUNT, head_to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, pick_mode)
- carbon_occupant.playsound_local(src, 'sound/items/weapons/flash_ring.ogg', 50)
- carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
+ var/obj/item/bodypart/head/head_to_wound = carbon_occupant.get_bodypart(BODY_ZONE_HEAD)
+ if(isnull(head_to_wound))
+ return
+ var/pick_mode = text2num(pick(list(
+ "[WOUND_PICK_LOWEST_SEVERITY]",
+ "[WOUND_PICK_HIGHEST_SEVERITY]"
+ )))
+ carbon_occupant.cause_wound_of_type_and_severity(WOUND_BLUNT, head_to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, pick_mode)
+ carbon_occupant.playsound_local(src, 'sound/items/weapons/flash_ring.ogg', 50)
+ carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
hittarget_living.add_splatter_floor(small_drip = FALSE)
hittarget_living.adjust_brute_loss(200)
diff --git a/code/modules/vending/vendor/tilting.dm b/code/modules/vending/vendor/tilting.dm
index 1d7d899c0ea..971633c683b 100644
--- a/code/modules/vending/vendor/tilting.dm
+++ b/code/modules/vending/vendor/tilting.dm
@@ -180,7 +180,7 @@
if (!iscarbon(atom_target))
return FALSE
var/mob/living/carbon/carbon_target = atom_target
- for(var/obj/item/bodypart/squish_part in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/squish_part in carbon_target.get_bodyparts())
var/severity = pick(WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, WOUND_SEVERITY_CRITICAL)
if (!carbon_target.cause_wound_of_type_and_severity(WOUND_BLUNT, squish_part, severity, wound_source = "crushed by [src]"))
squish_part.receive_damage(brute = 30)
diff --git a/tgstation.dme b/tgstation.dme
index da8503ddcfc..67e316b9900 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -6434,6 +6434,7 @@
#include "code\modules\surgery\bodyparts\helpers.dm"
#include "code\modules\surgery\bodyparts\parts.dm"
#include "code\modules\surgery\bodyparts\robot_bodyparts.dm"
+#include "code\modules\surgery\bodyparts\stumps.dm"
#include "code\modules\surgery\bodyparts\worn_feature_offset.dm"
#include "code\modules\surgery\bodyparts\wounds.dm"
#include "code\modules\surgery\bodyparts\species_parts\android_parts.dm"