diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index 62f59572f57..a8af2cc5c66 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -60,6 +60,11 @@ /// Called from bodypart being removed /obj/item/bodypart/proc/drop_limb(mob/living/carbon/old_owner, special, dismembered) #define COMSIG_BODYPART_REMOVED "bodypart_removed" +/// From /obj/item/bodypart/proc/update_limb(): (dropping_limb, is_creating) +#define COMSIG_BODYPART_UPDATED "bodypart_updated" +/// From /datum/component/butchering/create_replacement_limb(): (replacement limb) +#define COMSIG_BODYPART_BUTCHERED "bodypart_butchered" + /// Sent to a limb when something *attempts* to change its surgery state (old_state, new_state, changed_states) #define COMSIG_BODYPART_UPDATING_SURGERY_STATE "bodypart_updating_surgery_state" @@ -212,3 +217,5 @@ #define COMSIG_START_DREAMING "start_dreaming" /// A mob has finished dreaming: (datum/dream/finished_dream) #define COMSIG_END_DREAMING "end_dreaming" +/// From /mob/living/carbon/item_coverage_changed: (added_slots, removed_slots) +#define COMSIG_CARBON_ITEM_COVERAGE_CHANGED "carbon_item_coverage_changed" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 67a1dc8b747..0f4a28a1f94 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -172,6 +172,8 @@ #define BODYTYPE_SHADOW (1<<7) //This limb is a ghost limb and can phase through walls. #define BODYTYPE_GHOST (1<<8) +/// Analagous to BODYSHAPE_DIGITIGRADE, though this one is not removed if the mob's shape changed +#define BODYTYPE_DIGITIGRADE (1<<9) // Bodyshape defines for how things can be worn, i.e., what "shape" the mob sprite is ///The limb fits the human mold. This is not meant to be literal, if the sprite "fits" on a human, it is "humanoid", regardless of origin. diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index bdc41329a74..0ccaa013943 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -318,6 +318,7 @@ wound.remove_wound() wound.apply_wound(replacement, silent = TRUE) + SEND_SIGNAL(target, COMSIG_BODYPART_BUTCHERED, replacement) return replacement /datum/component/butchering/proc/start_butcher(obj/item/source, mob/living/target, mob/living/user) diff --git a/code/datums/components/digitigrade_linb.dm b/code/datums/components/digitigrade_linb.dm new file mode 100644 index 00000000000..6b7776474b5 --- /dev/null +++ b/code/datums/components/digitigrade_linb.dm @@ -0,0 +1,142 @@ +/// Updates the limb id of a bodypart if the mob is wearing digitigrade squishing clothing +/datum/component/digitigrade_limb + /// Id when wearing digitigrade squishing clothing + var/squashed_id + /// Id when not wearing digitigrade squishing clothing + var/free_id + + /// Lazylist of refs that are squishing this limb + VAR_PRIVATE/list/squashing_us + +/datum/component/digitigrade_limb/Initialize(squashed_id, free_id) + if(!istype(parent, /obj/item/bodypart/leg)) + return COMPONENT_INCOMPATIBLE + + var/obj/item/bodypart/limb = parent + limb.bodytype |= BODYTYPE_DIGITIGRADE + + src.squashed_id = squashed_id + src.free_id = free_id || initial(limb.limb_id) + + RegisterSignal(parent, COMSIG_BODYPART_UPDATED, PROC_REF(update_limb_id_comsig)) + RegisterSignal(parent, COMSIG_BODYPART_ATTACHED, PROC_REF(on_attach)) + RegisterSignal(parent, COMSIG_BODYPART_REMOVED, PROC_REF(on_remove)) + RegisterSignal(parent, COMSIG_BODYPART_BUTCHERED, PROC_REF(on_butchered)) + + if(ishuman(limb.owner)) + on_attach(limb, limb.owner) + +/datum/component/digitigrade_limb/Destroy() + UnregisterSignal(parent, COMSIG_BODYPART_UPDATED) + UnregisterSignal(parent, COMSIG_BODYPART_ATTACHED) + UnregisterSignal(parent, COMSIG_BODYPART_REMOVED) + + UnregisterSignal(parent, COMSIG_BODYPART_BUTCHERED) + var/obj/item/bodypart/limb = parent + if(!QDELING(parent) && ishuman(limb.owner)) + on_remove(limb, limb.owner) + limb.bodytype &= ~BODYTYPE_DIGITIGRADE + return ..() + +/datum/component/digitigrade_limb/proc/on_attach(obj/item/bodypart/limb, mob/living/carbon/new_limb_owner) + SIGNAL_HANDLER + + RegisterSignal(new_limb_owner, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(equipped_item)) + RegisterSignal(new_limb_owner, COMSIG_MOB_DROPPED_ITEM, PROC_REF(unequipped_item)) + RegisterSignal(new_limb_owner, COMSIG_CARBON_ITEM_COVERAGE_CHANGED, PROC_REF(coverage_changed)) + for(var/obj/item/equipped as anything in new_limb_owner.get_equipped_items()) + if(equipped_item(new_limb_owner, equipped, new_limb_owner.get_slot_by_item(equipped))) + LAZYOR(squashing_us, REF(equipped)) + +/datum/component/digitigrade_limb/proc/on_remove(obj/item/bodypart/limb, mob/living/carbon/old_limb_owner) + SIGNAL_HANDLER + + UnregisterSignal(old_limb_owner, COMSIG_MOB_EQUIPPED_ITEM) + UnregisterSignal(old_limb_owner, COMSIG_MOB_DROPPED_ITEM) + UnregisterSignal(old_limb_owner, COMSIG_CARBON_ITEM_COVERAGE_CHANGED) + LAZYNULL(squashing_us) + update_limb_id() + +/datum/component/digitigrade_limb/proc/equipped_item(mob/living/carbon/equipper, obj/item/equipped_item, slot) + SIGNAL_HANDLER + + if((slot & equipped_item.slot_flags) && item_squishes_limb(equipped_item, equipper)) + LAZYOR(squashing_us, REF(equipped_item)) + update_limb_id() + +/datum/component/digitigrade_limb/proc/unequipped_item(mob/living/carbon/equipper, obj/item/equipped_item) + SIGNAL_HANDLER + + LAZYREMOVE(squashing_us, REF(equipped_item)) + update_limb_id() + +// Snowflake case for updating whether our jumpsuit should squish us +/datum/component/digitigrade_limb/proc/coverage_changed(mob/living/carbon/equipper, added_slots, removed_slots) + SIGNAL_HANDLER + + if(!((added_slots|removed_slots) & HIDEJUMPSUIT)) + return + + var/obj/item/clothing/uniform = equipper.get_item_by_slot(ITEM_SLOT_ICLOTHING) + if(isnull(uniform)) + return + + var/uniform_ref = REF(uniform) + if(uniform_ref in squashing_us) + if(added_slots & HIDEJUMPSUIT) + LAZYREMOVE(squashing_us, uniform_ref) + update_limb_id() + + else + if((removed_slots & HIDEJUMPSUIT) && item_squishes_limb(uniform, equipper)) + LAZYOR(squashing_us, uniform_ref) + update_limb_id() + +/datum/component/digitigrade_limb/proc/item_squishes_limb(obj/item/equipped_item, mob/living/carbon/equipper) + if(!(equipped_item.body_parts_covered & (LEGS|FEET))) + return FALSE + + switch(equipper.get_slot_by_item(equipped_item)) + if(ITEM_SLOT_FEET, ITEM_SLOT_OCLOTHING) + return !(equipped_item.supports_variations_flags & DIGITIGRADE_VARIATIONS) + if(ITEM_SLOT_ICLOTHING) // If the jumpsuit is obscured, it shouldn't contribute to squishing + return !(equipped_item.supports_variations_flags & DIGITIGRADE_VARIATIONS) && !(equipper.obscured_slots & HIDEJUMPSUIT) + + return FALSE + +/// This is just ran on update_limb() to ensure we always have the correct ID +/datum/component/digitigrade_limb/proc/update_limb_id_comsig() + SIGNAL_HANDLER + update_limb_id(sprite_update = FALSE) + +/// Digitigrade limbs that are butchered add the component to the replacement limb +/datum/component/digitigrade_limb/proc/on_butchered(datum/source, obj/item/bodypart/replacement) + SIGNAL_HANDLER + squashed_id = "[initial(replacement.limb_id)]_[BODYPART_ID_DIGITIGRADE]" + free_id = initial(replacement.limb_id) + replacement.TakeComponent(src) + +/datum/component/digitigrade_limb/proc/update_limb_id(sprite_update = TRUE) + var/obj/item/bodypart/limb = parent + var/old_id = limb.limb_id + if(LAZYLEN(squashing_us)) + limb.limb_id = squashed_id + limb.remove_bodyshape(BODYSHAPE_DIGITIGRADE) + + else + limb.limb_id = free_id + limb.add_bodyshape(BODYSHAPE_DIGITIGRADE) + + if(!sprite_update || old_id == limb.limb_id) + return + + if(isnull(limb.owner)) + limb.update_icon_dropped() + return + + // Ensures any items that with a variation are updated + for(var/obj/item/thing as anything in limb.owner.get_equipped_items(INCLUDE_PROSTHETICS|INCLUDE_ABSTRACT)) + if(thing.supports_variations_flags & DIGITIGRADE_VARIATIONS) + thing.update_slot_icon() + // Updates underwear and mob sprites + limb.owner.update_body() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 8c011445492..07f8d0e89ed 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -270,7 +270,7 @@ if(IS_RIGHT_INDEX(get_held_index_of_item(I))) icon_file = I.righthand_file - hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE, bodyshape = bodyshape) return hands /mob/living/carbon/get_fire_overlay(stacks, on_fire) @@ -344,7 +344,7 @@ if(wear_mask) if(!(obscured_slots & HIDEMASK)) - overlays_standing[FACEMASK_LAYER] = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/clothing/mask.dmi') + overlays_standing[FACEMASK_LAYER] = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/clothing/mask.dmi', bodyshape = bodyshape) update_hud_wear_mask(wear_mask) apply_overlay(FACEMASK_LAYER) @@ -358,7 +358,7 @@ if(wear_neck) if(!(obscured_slots & HIDENECK)) - overlays_standing[NECK_LAYER] = wear_neck.build_worn_icon(default_layer = NECK_LAYER, default_icon_file = 'icons/mob/clothing/neck.dmi') + overlays_standing[NECK_LAYER] = wear_neck.build_worn_icon(default_layer = NECK_LAYER, default_icon_file = 'icons/mob/clothing/neck.dmi', bodyshape = bodyshape) update_hud_neck(wear_neck) apply_overlay(NECK_LAYER) @@ -371,7 +371,7 @@ inv.update_appearance() if(back) - overlays_standing[BACK_LAYER] = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = 'icons/mob/clothing/back.dmi') + overlays_standing[BACK_LAYER] = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = 'icons/mob/clothing/back.dmi', bodyshape = bodyshape) update_hud_back(back) apply_overlay(BACK_LAYER) @@ -399,7 +399,7 @@ if(head) if(!(obscured_slots & HIDEHEADGEAR)) - overlays_standing[HEAD_LAYER] = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = 'icons/mob/clothing/head/default.dmi') + overlays_standing[HEAD_LAYER] = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = 'icons/mob/clothing/head/default.dmi', bodyshape = bodyshape) update_hud_head(head) apply_overlay(HEAD_LAYER) diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 72f9968d250..996a9e3ca84 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -593,7 +593,7 @@ GLOBAL_LIST_EMPTY(features_by_species) if(ITEM_SLOT_FEET) if(H.num_legs < 2) return FALSE - if((H.bodyshape & BODYSHAPE_DIGITIGRADE) && !(I.item_flags & IGNORE_DIGITIGRADE)) + if((H.bodytype & BODYTYPE_DIGITIGRADE) && !(I.item_flags & IGNORE_DIGITIGRADE)) if(!(I.supports_variations_flags & DIGITIGRADE_VARIATIONS)) if(!disable_warning) to_chat(H, span_warning("The footwear around here isn't compatible with your feet!")) 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 e11b01fb53b..dce12788dec 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -120,6 +120,7 @@ There are several things that need to be remembered: female_uniform = woman ? uniform.female_sprite_flags : null, override_state = target_overlay, override_file = handled_by_bodyshape ? icon_file : null, + bodyshape = bodyshape, ) var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) @@ -127,7 +128,6 @@ There are several things that need to be remembered: overlays_standing[UNIFORM_LAYER] = uniform_overlay apply_overlay(UNIFORM_LAYER) - check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_ICLOTHING) /mob/living/carbon/human/update_worn_id() remove_overlay(ID_LAYER) @@ -147,7 +147,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/id.dmi' - id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = icon_file) + id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) if(!id_overlay) return @@ -193,7 +193,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/hands.dmi' - var/mutable_appearance/gloves_overlay = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = icon_file) + var/mutable_appearance/gloves_overlay = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) var/feature_y_offset = 0 //needs to be typed, hand_bodyparts can have nulls @@ -240,7 +240,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/eyes.dmi' - var/mutable_appearance/glasses_overlay = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = icon_file) + var/mutable_appearance/glasses_overlay = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) my_head.worn_glasses_offset?.apply_offset(glasses_overlay) overlays_standing[GLASSES_LAYER] = glasses_overlay apply_overlay(GLASSES_LAYER) @@ -266,7 +266,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/ears.dmi' - var/mutable_appearance/ears_overlay = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = icon_file) + var/mutable_appearance/ears_overlay = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) my_head.worn_ears_offset?.apply_offset(ears_overlay) overlays_standing[EARS_LAYER] = ears_overlay apply_overlay(EARS_LAYER) @@ -287,7 +287,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/neck.dmi' - var/mutable_appearance/neck_overlay = worn_item.build_worn_icon(default_layer = NECK_LAYER, default_icon_file = icon_file) + var/mutable_appearance/neck_overlay = worn_item.build_worn_icon(default_layer = NECK_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_neck_offset?.apply_offset(neck_overlay) overlays_standing[NECK_LAYER] = neck_overlay @@ -313,7 +313,7 @@ There are several things that need to be remembered: var/icon_file = DEFAULT_SHOES_FILE - var/mutable_appearance/shoes_overlay = shoes.build_worn_icon(default_layer = SHOES_LAYER, default_icon_file = icon_file) + var/mutable_appearance/shoes_overlay = shoes.build_worn_icon(default_layer = SHOES_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) if(!shoes_overlay) return @@ -330,7 +330,6 @@ There are several things that need to be remembered: overlays_standing[SHOES_LAYER] = shoes_overlay apply_overlay(SHOES_LAYER) - check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_FEET) /mob/living/carbon/human/update_suit_storage() remove_overlay(SUIT_STORE_LAYER) @@ -367,13 +366,12 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/head/default.dmi' - var/mutable_appearance/head_overlay = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = icon_file) + var/mutable_appearance/head_overlay = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) my_head?.worn_head_offset?.apply_offset(head_overlay) overlays_standing[HEAD_LAYER] = head_overlay apply_overlay(HEAD_LAYER) - check_body_shape(BODYSHAPE_SNOUTED, ITEM_SLOT_HEAD) /mob/living/carbon/human/update_worn_belt() remove_overlay(BELT_LAYER) @@ -391,7 +389,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/belt.dmi' - var/mutable_appearance/belt_overlay = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = icon_file) + var/mutable_appearance/belt_overlay = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_belt_offset?.apply_offset(belt_overlay) overlays_standing[BELT_LAYER] = belt_overlay @@ -414,13 +412,12 @@ There are several things that need to be remembered: var/icon_file = DEFAULT_SUIT_FILE - var/mutable_appearance/suit_overlay = wear_suit.build_worn_icon(default_layer = SUIT_LAYER, default_icon_file = icon_file) + var/mutable_appearance/suit_overlay = wear_suit.build_worn_icon(default_layer = SUIT_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_suit_offset?.apply_offset(suit_overlay) overlays_standing[SUIT_LAYER] = suit_overlay apply_overlay(SUIT_LAYER) - check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_OCLOTHING) /mob/living/carbon/human/update_pockets() if(client && hud_used) @@ -463,12 +460,11 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/mask.dmi' - var/mutable_appearance/mask_overlay = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = icon_file) + var/mutable_appearance/mask_overlay = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) my_head.worn_mask_offset?.apply_offset(mask_overlay) overlays_standing[FACEMASK_LAYER] = mask_overlay apply_overlay(FACEMASK_LAYER) - check_body_shape(BODYSHAPE_SNOUTED, ITEM_SLOT_MASK) /mob/living/carbon/human/update_worn_back() remove_overlay(BACK_LAYER) @@ -487,7 +483,7 @@ There are several things that need to be remembered: var/icon_file = 'icons/mob/clothing/back.dmi' - back_overlay = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = icon_file) + back_overlay = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = icon_file, bodyshape = bodyshape) if(!back_overlay) return @@ -520,7 +516,7 @@ There are several things that need to be remembered: var/mutable_appearance/hand_overlay var/icon_file = IS_RIGHT_INDEX(held_index) ? worn_item.righthand_file : worn_item.lefthand_file - hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE, bodyshape = bodyshape) var/obj/item/bodypart/arm/held_in_hand = hand_bodyparts[held_index] held_in_hand?.held_hand_offset?.apply_offset(hand_overlay) @@ -758,6 +754,7 @@ generate/load female uniform sprites matching all previously decided variables female_uniform = NO_FEMALE_UNIFORM, override_state = null, override_file = null, + bodyshape = NONE, ) //Find a valid icon_state from variables+arguments @@ -767,9 +764,6 @@ generate/load female uniform sprites matching all previously decided variables //Find a valid layer from variables+arguments var/layer2use = alternate_worn_layer || default_layer - var/mob/living/carbon/wearer = loc - var/is_digi = istype(wearer) && (wearer.bodyshape & BODYSHAPE_DIGITIGRADE) && !wearer.is_digitigrade_squished() - var/mutable_appearance/draw_target // MA of the item itself, not the final result var/icon/building_icon // used to construct an icon across multiple procs before converting it to MA if(female_uniform) @@ -779,7 +773,7 @@ generate/load female uniform sprites matching all previously decided variables type = female_uniform, greyscale_colors = greyscale_colors, ) - if(!isinhands && is_digi && (supports_variations_flags & CLOTHING_DIGITIGRADE_MASK)) + if(!isinhands && (bodyshape & BODYSHAPE_DIGITIGRADE) && (supports_variations_flags & CLOTHING_DIGITIGRADE_MASK)) building_icon = wear_digi_version( base_icon = building_icon || icon(file2use, t_state), item = src, @@ -879,22 +873,21 @@ generate/load female uniform sprites matching all previously decided variables return . // Underwear, Undershirts & Socks - var/active_bodyshapes = get_active_bodyshapes() if(underwear) var/datum/sprite_accessory/clothing/underwear/undie_accessory = SSaccessories.underwear_list[underwear] - var/mutable_appearance/underwear_overlay = undie_accessory?.make_appearance(underwear_color, physique, active_bodyshapes) + var/mutable_appearance/underwear_overlay = undie_accessory?.make_appearance(underwear_color, physique, bodyshape) if(underwear_overlay) . += underwear_overlay if(undershirt) var/datum/sprite_accessory/clothing/undershirt/shirt_accessory = SSaccessories.undershirt_list[undershirt] - var/mutable_appearance/shirt_overlay = shirt_accessory?.make_appearance(null, physique, active_bodyshapes) + var/mutable_appearance/shirt_overlay = shirt_accessory?.make_appearance(null, physique, bodyshape) if(shirt_overlay) . += shirt_overlay if(socks && num_legs >= 2 && !(bodyshape & BODYSHAPE_DIGITIGRADE)) var/datum/sprite_accessory/clothing/socks/sock_accessory = SSaccessories.socks_list[socks] - var/mutable_appearance/socks_overlay = sock_accessory?.make_appearance(null, physique, active_bodyshapes) + var/mutable_appearance/socks_overlay = sock_accessory?.make_appearance(null, physique, bodyshape) if(socks_overlay) . += socks_overlay @@ -970,44 +963,6 @@ generate/load female uniform sprites matching all previously decided variables update_eyes() update_hair() -/** - * Used to perform regular updates to the limbs of humans with special bodyshapes - * - * * check_shapes: The bodyshapes to check for. - * Any limbs or organs which share this shape, will be updated. - * * ignore_slots: The slots to ignore when updating the limbs. - * This is useful for things like digitigrade legs, where we can skip some slots that we're already updating. - * - * return an integer, the number of limbs updated - */ -/mob/living/carbon/human/proc/check_body_shape(check_shapes = BODYSHAPE_DIGITIGRADE|BODYSHAPE_SNOUTED, ignore_slots = NONE) - . = 0 - if(!(bodyshape & check_shapes)) - // optimization - none of our limbs or organs have the desired shape - return . - - 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) - checked_bodyshape |= organ.external_bodyshapes - - // any limb needs to be updated, so stop here and do it - if(checked_bodyshape & check_shapes) - . = update_body_parts() - break - - if(!.) - return - // hardcoding this here until bodypart updating is more sane - // we need to update clothing items that may have been affected by bodyshape updates - if(check_shapes & BODYSHAPE_DIGITIGRADE) - for(var/obj/item/thing as anything in get_equipped_items(INCLUDE_PROSTHETICS|INCLUDE_ABSTRACT)) - if(thing.slot_flags & ignore_slots) - continue - if(thing.supports_variations_flags & DIGITIGRADE_VARIATIONS) - thing.update_slot_icon() - // Hooks into human apply overlay so that we can modify all overlays applied through standing overlays to our height system. // Some of our overlays will be passed through a displacement filter to make our mob look taller or shorter. // Some overlays can't be displaced as they're too close to the edge of the sprite or cross the middle point in a weird way. diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index 049b0ccb42a..1f761d9dfd2 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -260,7 +260,10 @@ * * removed_slots - slots that were removed from obscured_slots */ /mob/living/carbon/proc/item_coverage_changed(added_slots, removed_slots) + SEND_SIGNAL(src, COMSIG_CARBON_ITEM_COVERAGE_CHANGED, added_slots, removed_slots) update_clothing(hidden_slots_to_inventory_slots(added_slots|removed_slots)) + if((added_slots|removed_slots) & HIDESNOUT) + synchronize_bodyshapes() if((added_slots|removed_slots) & (HIDEHAIR|HIDEFACIALHAIR)) update_hair() if((added_slots|removed_slots) & HIDEEYES) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 4d11d2b0f93..dad783c27e9 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -1146,6 +1146,8 @@ /obj/item/bodypart/proc/update_limb(dropping_limb = FALSE, is_creating = FALSE) SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_BODYPART_UPDATED, dropping_limb, is_creating) + if(IS_ORGANIC_LIMB(src)) // Try to add a cached blood type data, we must do it in here because for some reason DNA gets initialized AFTER the mob's limbs are created. // Should be fine as this gets called before all the important stuff happens @@ -1783,6 +1785,22 @@ return REMOVE_TRAIT(owner, old_trait, bodypart_trait_source) +/// Add a bodyshape to the bodypart, then synchronize with the owner if necessary +/obj/item/bodypart/proc/add_bodyshape(new_shape) + if(bodyshape & new_shape) + return + + bodyshape |= new_shape + owner?.synchronize_bodyshapes() + +/// Remove a bodyshape from the bodypart, then synchronize with the owner if necessary +/obj/item/bodypart/proc/remove_bodyshape(old_shape) + if(!(bodyshape & old_shape)) + return + + bodyshape &= ~old_shape + owner?.synchronize_bodyshapes() + /// Add one or multiple surgical states to the bodypart /obj/item/bodypart/proc/add_surgical_state(new_states) if(!new_states) diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 258162453a7..c3e8319120d 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -284,17 +284,10 @@ all_limb_flags |= organ.external_bodyshapes all_limb_flags |= limb.bodyshape - bodyshape = all_limb_flags + if(obscured_slots & HIDESNOUT) + all_limb_flags &= ~BODYSHAPE_SNOUTED -/// Get all bodyshapes but filter out bodyshapes that are currently being hidden -/mob/living/carbon/proc/get_active_bodyshapes() - var/active_shapes = bodyshape - // future todo: both of these are state based, maybe we can just remove relevant bodyshapes directly. would remove the need for this proc - if((active_shapes & BODYSHAPE_DIGITIGRADE) && is_digitigrade_squished()) - active_shapes &= ~BODYSHAPE_DIGITIGRADE - if((active_shapes & BODYSHAPE_SNOUTED) && (obscured_slots & HIDESNOUT)) - active_shapes &= ~BODYSHAPE_SNOUTED - return active_shapes + bodyshape = all_limb_flags /proc/skintone2hex(skin_tone) . = 0 diff --git a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm index 45889011818..00858221c05 100644 --- a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm @@ -51,50 +51,26 @@ icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi' limb_id = SPECIES_LIZARD -/// Checks if this mob is wearing anything that does not have a valid sprite set for digitigrade legs -/// (In other words, is the mob's digitigrade body squished by its clothing?) -/mob/living/carbon/proc/is_digitigrade_squished() - return FALSE - -/mob/living/carbon/human/is_digitigrade_squished() - var/obj/item/clothing/shoes/worn_shoes = shoes - var/obj/item/clothing/under/worn_suit = wear_suit - var/obj/item/clothing/under/worn_uniform = w_uniform - - var/uniform_compatible = isnull(worn_uniform) \ - || (worn_uniform.supports_variations_flags & DIGITIGRADE_VARIATIONS) \ - || !(worn_uniform.body_parts_covered & LEGS) \ - || (obscured_slots & HIDEJUMPSUIT) // If suit hides our jumpsuit, it doesn't matter if it squishes - - var/suit_compatible = isnull(worn_suit) \ - || (worn_suit.supports_variations_flags & DIGITIGRADE_VARIATIONS) \ - || !(worn_suit.body_parts_covered & LEGS) - - var/shoes_compatible = isnull(worn_shoes) \ - || (worn_shoes.supports_variations_flags & DIGITIGRADE_VARIATIONS) - - return !uniform_compatible || !suit_compatible || !shoes_compatible - /obj/item/bodypart/leg/left/digitigrade icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi' limb_id = BODYPART_ID_DIGITIGRADE species_id = SPECIES_LIZARD - bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE + bodyshape = BODYSHAPE_HUMANOID footprint_sprite = FOOTPRINT_SPRITE_CLAWS footstep_type = FOOTSTEP_MOB_CLAW -/obj/item/bodypart/leg/left/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE) +/obj/item/bodypart/leg/left/digitigrade/Initialize(mapload) . = ..() - limb_id = owner?.is_digitigrade_squished() ? SPECIES_LIZARD : BODYPART_ID_DIGITIGRADE + AddComponent(/datum/component/digitigrade_limb, SPECIES_LIZARD, initial(limb_id)) /obj/item/bodypart/leg/right/digitigrade icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi' limb_id = BODYPART_ID_DIGITIGRADE species_id = SPECIES_LIZARD - bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE + bodyshape = BODYSHAPE_HUMANOID footprint_sprite = FOOTPRINT_SPRITE_CLAWS footstep_type = FOOTSTEP_MOB_CLAW -/obj/item/bodypart/leg/right/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE) +/obj/item/bodypart/leg/right/digitigrade/Initialize(mapload) . = ..() - limb_id = owner?.is_digitigrade_squished() ? SPECIES_LIZARD : BODYPART_ID_DIGITIGRADE + AddComponent(/datum/component/digitigrade_limb, SPECIES_LIZARD, initial(limb_id)) diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index 41037f538c4..888e9e790a8 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -481,10 +481,6 @@ . = ..() AddElement(/datum/element/blood_limb_overlay) -/obj/item/bodypart/leg/left/skeleton/nonfunctional/update_limb(dropping_limb = FALSE, is_creating = FALSE) - . = ..() - limb_id = ((bodyshape & BODYSHAPE_DIGITIGRADE) && owner?.is_digitigrade_squished()) ? initial(limb_id) : "[initial(limb_id)]_[BODYPART_ID_DIGITIGRADE]" - /obj/item/bodypart/leg/right/skeleton/nonfunctional limb_id = BODYPART_ID_BONE disabling_threshold_percentage = 0 @@ -493,10 +489,6 @@ . = ..() AddElement(/datum/element/blood_limb_overlay) -/obj/item/bodypart/leg/right/skeleton/nonfunctional/update_limb(dropping_limb = FALSE, is_creating = FALSE) - . = ..() - limb_id = ((bodyshape & BODYSHAPE_DIGITIGRADE) && owner?.is_digitigrade_squished()) ? initial(limb_id) : "[initial(limb_id)]_[BODYPART_ID_DIGITIGRADE]" - ///MUSHROOM /obj/item/bodypart/head/mushroom limb_id = SPECIES_MUSHROOM diff --git a/tgstation.dme b/tgstation.dme index 444b015fbed..802f7ba7817 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1187,6 +1187,7 @@ #include "code\datums\components\defaceable.dm" #include "code\datums\components\dejavu.dm" #include "code\datums\components\deployable.dm" +#include "code\datums\components\digitigrade_linb.dm" #include "code\datums\components\direct_explosive_trap.dm" #include "code\datums\components\earprotection.dm" #include "code\datums\components\echolocation.dm"