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"