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 00000000000..1ca452de089 Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_digi_leg_test.png differ