From 627f6baef4e8683388b6c7aed1136875686bf0bd Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:29:30 -0500 Subject: [PATCH] Fix digi legs sprites getting stuck (#87254) ## About The Pull Request Fixes #86756 Fixes #67174 (isn't this already fixed? Oh well now it's really fixed because I tested it) Basically changed the random calls to `update_body_parts` with a bespoke proc which better explains what they are there to do Adds one missing call to head items (for `HIDESNOUT`) ## Changelog :cl: Melbert fix: Fixed digitigrade pants sprite not updating in accordance to some leg updates /:cl: # Conflicts: # code/modules/mob/living/carbon/human/human_update_icons.dm # code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm --- code/__DEFINES/vv.dm | 1 + .../jobs/job_types/assistant/assistant.dm | 9 ++- .../mob/living/carbon/carbon_update_icons.dm | 9 ++- .../living/carbon/human/human_update_icons.dm | 49 +++++++++++- code/modules/mob/mob.dm | 7 ++ .../species_parts/lizard_bodyparts.dm | 7 -- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/screenshot_digi.dm | 75 ++++++++++++++++++ .../screenshots/screenshot_digi_leg_test.png | Bin 0 -> 1692 bytes 9 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 code/modules/unit_tests/screenshot_digi.dm create mode 100644 code/modules/unit_tests/screenshots/screenshot_digi_leg_test.png diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index a83ef71ddc6..88f46a53fd9 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -127,6 +127,7 @@ #define VV_HK_GODMODE "godmode" #define VV_HK_DROP_ALL "dropall" #define VV_HK_REGEN_ICONS "regen_icons" +#define VV_HK_REGEN_ICONS_FULL "regen_icons_full" #define VV_HK_PLAYER_PANEL "player_panel" #define VV_HK_BUILDMODE "buildmode" #define VV_HK_DIRECT_CONTROL "direct_control" diff --git a/code/modules/jobs/job_types/assistant/assistant.dm b/code/modules/jobs/job_types/assistant/assistant.dm index 2de684c9d68..35fdebd1673 100644 --- a/code/modules/jobs/job_types/assistant/assistant.dm +++ b/code/modules/jobs/job_types/assistant/assistant.dm @@ -58,14 +58,16 @@ Assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/target) ..() + give_holiday_hat(target) + give_jumpsuit(target) + +/datum/outfit/job/assistant/proc/give_holiday_hat(mob/living/carbon/human/target) for(var/holidayname in GLOB.holidays) var/datum/holiday/holiday_today = GLOB.holidays[holidayname] var/obj/item/special_hat = holiday_today.holiday_hat if(prob(HOLIDAY_HAT_CHANCE) && !isnull(special_hat) && isnull(head)) head = special_hat - give_jumpsuit(target) - /datum/outfit/job/assistant/proc/give_jumpsuit(mob/living/carbon/human/target) // SKYRAT EDIT - Loadouts (we don't want jumpsuits to override the person's loadout item) if(modified_outfit_slots & ITEM_SLOT_ICLOTHING) @@ -90,6 +92,9 @@ Assistant /datum/outfit/job/assistant/consistent name = "Assistant - Consistent" +/datum/outfit/job/assistant/consistent/give_holiday_hat(mob/living/carbon/human/target) + return + /datum/outfit/job/assistant/consistent/give_jumpsuit(mob/living/carbon/human/target) uniform = /obj/item/clothing/under/color/grey diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 4293977edec..b33cb91787a 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -480,11 +480,12 @@ SEND_SIGNAL(src, COMSIG_ITEM_GET_WORN_OVERLAYS, ., standing, isinhands, icon_file) ///Checks to see if any bodyparts need to be redrawn, then does so. update_limb_data = TRUE redraws the limbs to conform to the owner. +///Returns an integer representing the number of limbs that were updated. /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 = FALSE + var/limb_count_update = 0 for(var/obj/item/bodypart/limb as anything in bodyparts) // SKYRAT EDIT BEGIN - Don't handle abstract limbs (Taurs, etc.) if(!limb.show_icon) @@ -498,13 +499,15 @@ 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 + 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 = TRUE + limb_count_update += 1 for(var/missing_limb in missing_bodyparts) icon_render_keys -= missing_limb //Removes dismembered limbs from the key list - if(!needs_update.len && !limb_count_update) + . = limb_count_update + if(!.) return //GENERATE NEW LIMBS 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 5cd38db5af7..f63941c58b1 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -158,8 +158,10 @@ There are several things that need to be remembered: var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_uniform_offset?.apply_offset(uniform_overlay) overlays_standing[UNIFORM_LAYER] = uniform_overlay - apply_overlay(UNIFORM_LAYER) + + apply_overlay(UNIFORM_LAYER) + check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_ICLOTHING) update_mutant_bodyparts() /mob/living/carbon/human/update_worn_id(update_obscured = TRUE) @@ -447,9 +449,7 @@ There are several things that need to be remembered: overlays_standing[SHOES_LAYER] = shoes_overlay apply_overlay(SHOES_LAYER) - - update_body_parts() - + check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_FEET) /mob/living/carbon/human/update_suit_storage(update_obscured = TRUE) remove_overlay(SUIT_STORE_LAYER) @@ -516,6 +516,7 @@ There are several things that need to be remembered: 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(update_obscured = TRUE) remove_overlay(BELT_LAYER) @@ -606,6 +607,7 @@ There are several things that need to be remembered: update_mutant_bodyparts() apply_overlay(SUIT_LAYER) + check_body_shape(BODYSHAPE_DIGITIGRADE, ITEM_SLOT_OCLOTHING) /mob/living/carbon/human/update_pockets() @@ -675,6 +677,7 @@ There are several things that need to be remembered: overlays_standing[FACEMASK_LAYER] = mask_overlay apply_overlay(FACEMASK_LAYER) + check_body_shape(BODYSHAPE_SNOUTED, ITEM_SLOT_MASK) update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout /mob/living/carbon/human/update_worn_back(update_obscured = TRUE) @@ -988,6 +991,44 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E update_worn_head() update_worn_mask() +/** + * 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 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()) + 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/mob.dm b/code/modules/mob/mob.dm index 138ab026989..5151bb72dc9 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1394,6 +1394,7 @@ VV_DROPDOWN_OPTION(VV_HK_GODMODE, "Toggle Godmode") VV_DROPDOWN_OPTION(VV_HK_DROP_ALL, "Drop Everything") VV_DROPDOWN_OPTION(VV_HK_REGEN_ICONS, "Regenerate Icons") + VV_DROPDOWN_OPTION(VV_HK_REGEN_ICONS_FULL, "Regenerate Icons & Clear Stuck Overlays") VV_DROPDOWN_OPTION(VV_HK_PLAYER_PANEL, "Show player panel") VV_DROPDOWN_OPTION(VV_HK_BUILDMODE, "Toggle Buildmode") VV_DROPDOWN_OPTION(VV_HK_DIRECT_CONTROL, "Assume Direct Control") @@ -1412,6 +1413,12 @@ return regenerate_icons() + if(href_list[VV_HK_REGEN_ICONS_FULL]) + if(!check_rights(NONE)) + return + cut_overlays() + regenerate_icons() + if(href_list[VV_HK_PLAYER_PANEL]) return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/show_player_panel, src) diff --git a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm index 0f7e2833dc8..30ef5abcc05 100644 --- a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm @@ -57,13 +57,7 @@ /obj/item/bodypart/leg/left/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE) . = ..() - var/old_id = limb_id limb_id = owner?.is_digitigrade_squished() ? SPECIES_LIZARD : BODYPART_ID_DIGITIGRADE - if(old_id != limb_id) - // Something unsquished / squished us so we need to go through and update everything that is affected - for(var/obj/item/thing as anything in owner?.get_equipped_items()) - if(thing.supports_variations_flags & DIGITIGRADE_VARIATIONS) - thing.update_slot_icon() /obj/item/bodypart/leg/right/digitigrade icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi' @@ -74,7 +68,6 @@ /obj/item/bodypart/leg/right/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE) . = ..() - var/old_id = limb_id limb_id = owner?.is_digitigrade_squished() ? SPECIES_LIZARD : BODYPART_ID_DIGITIGRADE if(old_id != limb_id) // Something unsquished / squished us so we need to go through and update everything that is affected diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index b1461334f06..492a1d40cb8 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -240,6 +240,7 @@ #include "say.dm" #include "screenshot_antag_icons.dm" #include "screenshot_basic.dm" +#include "screenshot_digi.dm" #include "screenshot_dynamic_human_icons.dm" #include "screenshot_high_luminosity_eyes.dm" #include "screenshot_humanoids.dm" diff --git a/code/modules/unit_tests/screenshot_digi.dm b/code/modules/unit_tests/screenshot_digi.dm new file mode 100644 index 00000000000..835b2501c87 --- /dev/null +++ b/code/modules/unit_tests/screenshot_digi.dm @@ -0,0 +1,75 @@ +/// Ensures digitigrade legs and clothing are displayed correctly in screenshots +/datum/unit_test/screenshot_digi + +/datum/unit_test/screenshot_digi/Run() + var/icon/finished_icon = icon('icons/effects/effects.dmi', "nothing") + var/mob/living/carbon/human/consistent/dummy = allocate(__IMPLIED_TYPE__) + + // screenshot test of just plain digitigrade legs. + // doubles as coverage that ashwalkers spawn with digitigrade legs (as they should be forced to do) + dummy.set_species(/datum/species/lizard/ashwalker) + TEST_ASSERT((dummy.bodyshape & BODYSHAPE_DIGITIGRADE), "Dummy (Ashwalker) should be digitigrade!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 1) + + // screenshot test of an assistant outfit + // covers digitigrade autogen'd legs + dummy.equipOutfit(/datum/outfit/job/assistant/consistent) + TEST_ASSERT(isclothing(dummy.w_uniform), "Dummy (Ashwalker) should be wearing a jumpsuit!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 2) + + // screenshot test of an EVA suit + // should hide the autogen'd legs + var/obj/item/clothing/suit/space/eva/suit = allocate(__IMPLIED_TYPE__) + dummy.equip_to_appropriate_slot(suit) + TEST_ASSERT_EQUAL(dummy.wear_suit, suit, "Dummy (Ashwalker) should be wearing the EVA suit!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 3) + + // screenshot test of holding an EVA suit + // should show the autogen'd legs once more + suit.attempt_pickup(dummy, skip_grav = TRUE) + TEST_ASSERT((suit in dummy.held_items), "Dummy (Ashwalker) should be holding the EVA suit!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 4) + + // screenshot of turning the ashwalker into a human + // this should correctly update the auto gen sprites and leg sprites + dummy.set_species(/datum/species/human) + TEST_ASSERT(!(dummy.bodyshape & BODYSHAPE_DIGITIGRADE), "Dummy (Human) should be not digitigrade!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 5) + + // screenshot test of turning the human back into an ashwalker + // this should correctly update the auto gen sprites and leg sprites again + dummy.set_species(/datum/species/lizard/ashwalker) + TEST_ASSERT((dummy.bodyshape & BODYSHAPE_DIGITIGRADE), "Dummy (Ashwalker) should be digitigrade again!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 6) + + + // screenshot test of putting the EVA suit back on. + // you'd think this is unnecessary but this is here to cover a bug where the suit works the first equip, but not the second + dummy.temporarilyRemoveItemFromInventory(suit) + dummy.equip_to_appropriate_slot(suit) + TEST_ASSERT_EQUAL(dummy.wear_suit, suit, "Dummy (Ashwalker) should be wearing the EVA suit again!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 7) + + // screenshot test of taking the EVA suit off + // should show the autogen'd legs once more + qdel(suit) + TEST_ASSERT_NULL(dummy.wear_suit, "Dummy (Ashwalker) should not be wearing the EVA suit!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 8) + + // finally, screenshot test of taking jumpsuit (everything) off + // which should test that the autogen legs disappear (here to cover a bug in which it does not disappear) + dummy.delete_equipment() + TEST_ASSERT_EQUAL(length(dummy.get_equipped_items()), 0, "Dummy (Ashwalker) should have no equipment!") + finished_icon = icon(finished_icon) + finished_icon.Insert(getFlatIcon(dummy, no_anim = TRUE), dir = SOUTH, frame = 9) + + // and upload + test_screenshot("leg_test", finished_icon) diff --git a/code/modules/unit_tests/screenshots/screenshot_digi_leg_test.png b/code/modules/unit_tests/screenshots/screenshot_digi_leg_test.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca452de089462485c7e4d6204dd14452995206d GIT binary patch literal 1692 zcmV;N24ne&P)rw$#_(iHo9{nVDi{bN~qe z00aQqo|5^|#{b~a>c6z%uco|vaY#u>VPau_fPRICgf4zbE-sm-rlk~Hn00D8l!;e7 zJv^;NEsZc6z`(#pMMQdecZ3FgcK`qY0d!JMQvg8b*k%9#0C0L#Sad{Xb7OL8aCB*J zZU6vyoKseCa&`CgQ*iP1gNKs3;=*g9m`BA zV@?171szF5K~!jg?O6?%nm7~p6&op`)UVDRLLAhF=f@+?ILMzxaW5h^Vf%upMLxN`=^f| zEcm_F>2kTGSxVbf>-%#8dI||M#?p+kuso7D&y-SG(rzs{^?G_JtUCnzUXa4`Jz-mB zrexIph9C$sE6&n*U#!tv0(0_ar0oP|5hqzp`S+@CS{hr+({ucr5Xs$YR03EFa~ z-fBVme^`5{?@h_`+yxDOKWO6@tHQ-F^!=cXzb=YmZD+XT{Q&5D_AW1OSGPr8STEp$ z_XD8s+556styV>8zXbe#fb{(x+mr=;HrDu;ydPlwzo9X3#J`|D*8fVCW!a1Ox5xTF z2-g7bX^-^(aJ*mt55=MWzXIN?|A*pG{}08X{vV2;>i?=X`oDUK(+bX?@tl5DuWNsz z|Eqdi)35sF5&62@9Px6UpW&$eQU4SEM=ivkf4w|nQLByJqG z-|PRXZhkz}4?mjoKHM)R=Ehcr;yKAVn6F70y z{-FPx$8Ak>?XeTz+zxth(EddK|9Whi=J8i2J}LKBzFTq7{v!RK(*rqw+MbpE_802^ ze6?Dy*NncaRsM?h7wiALEH|4Cqi=btj4p2fHp>70!GG=_4Dtt%|1jJiK>ovEe*pOp z>d_MsBf29AR{{Zs;NdF`M0p3`%u!2Cbb|HyxU`G4deApZg8|B-)y{0Er-NB#lwA8`I3`3J~13a`~&1a!2Cb*50L)=^Z&>{K>h>B|3m)(`46l&{}25GU!}2jo9I m$G7GGujMbe^8Z8qg?|9+m)1lQLf(M@0000