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

🆑 Melbert
fix: Fixed digitigrade pants sprite not updating in accordance to some
leg updates
/🆑
# Conflicts:
#	code/modules/mob/living/carbon/human/human_update_icons.dm
#	code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
This commit is contained in:
MrMelbert
2024-10-17 13:29:30 -05:00
committed by lessthanthree
parent 199ce751bb
commit 627f6baef4
9 changed files with 142 additions and 16 deletions
+1
View File
@@ -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"
@@ -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
@@ -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
@@ -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.
+7
View File
@@ -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)
@@ -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
+1
View File
@@ -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"
@@ -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)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB