From a9d863485b5f9bb6fb330dc8b1d4486f08f4a9bb Mon Sep 17 00:00:00 2001 From: 13spacemen <46101244+13spacemen@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:39:28 +0500 Subject: [PATCH] footprints are now based on shoes and then legs, monkey legs make pawprints, fixed blood overlay leaking bug, added keep together helpers, renamed icon states in footprints.dmi --- code/__DEFINES/cleaning.dm | 5 ++ code/__DEFINES/flags.dm | 15 ++++ code/__HELPERS/type2type.dm | 5 +- code/datums/components/bloodysoles.dm | 19 +++-- code/datums/elements/decals/blood.dm | 1 + .../effects/decals/cleanable/humans.dm | 70 ++++++++++++++---- code/modules/clothing/shoes/_shoes.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 3 + .../mob/living/carbon/carbon_defines.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 7 +- code/modules/surgery/bodyparts/parts.dm | 6 ++ icons/effects/footprints.dmi | Bin 3946 -> 4063 bytes 12 files changed, 109 insertions(+), 26 deletions(-) diff --git a/code/__DEFINES/cleaning.dm b/code/__DEFINES/cleaning.dm index 365cd09a58ec..9d99384337cf 100644 --- a/code/__DEFINES/cleaning.dm +++ b/code/__DEFINES/cleaning.dm @@ -34,3 +34,8 @@ #define CLEAN_SCRUB (CLEAN_WASH | CLEAN_TYPE_FINGERPRINTS | CLEAN_TYPE_FIBERS | CLEAN_TYPE_HARD_DECAL) #define CLEAN_RAD CLEAN_TYPE_RADIATION #define CLEAN_ALL (ALL & ~CLEAN_TYPE_WEAK) + +// Footprint sprites to use when making footprints in blood, oil, etc. +#define FOOTPRINT_SPRITE_SHOES "shoes" +#define FOOTPRINT_SPRITE_PAWS "paws" +#define FOOTPRINT_SPRITE_CLAWS "claws" diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 0586b90352b9..dbfb47efccff 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -197,6 +197,21 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define AA_TARGET_SEE_APPEARANCE (1<<0) #define AA_MATCH_TARGET_OVERLAYS (1<<1) +#define KEEP_TOGETHER_ORIGINAL "keep_together_original" + +//setter for KEEP_TOGETHER to allow for multiple sources to set and unset it +#define ADD_KEEP_TOGETHER(x, source)\ + if ((x.appearance_flags & KEEP_TOGETHER) && !HAS_TRAIT(x, TRAIT_KEEP_TOGETHER)) ADD_TRAIT(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL); \ + ADD_TRAIT(x, TRAIT_KEEP_TOGETHER, source);\ + x.appearance_flags |= KEEP_TOGETHER + +#define REMOVE_KEEP_TOGETHER(x, source)\ + REMOVE_TRAIT(x, TRAIT_KEEP_TOGETHER, source);\ + if(HAS_TRAIT_FROM_ONLY(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL))\ + REMOVE_TRAIT(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL);\ + else if(!HAS_TRAIT(x, TRAIT_KEEP_TOGETHER))\ + x.appearance_flags &= ~KEEP_TOGETHER + //religious_tool flags #define RELIGION_TOOL_INVOKE (1<<0) #define RELIGION_TOOL_SACRIFICE (1<<1) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index ac89b9855de4..6bab5f05dbff 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -143,8 +143,9 @@ if(337.5 to 360) return NORTH -/proc/angle2dir_cardinal(angle) - switch(round(angle, 0.1)) +/proc/angle2dir_cardinal(degree) + degree = SIMPLIFY_DEGREES(degree) + switch(round(degree, 0.1)) if(315.5 to 360, 0 to 45.5) return NORTH if(45.6 to 135.5) diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm index d05043312d7f..77a842eed651 100644 --- a/code/datums/components/bloodysoles.dm +++ b/code/datums/components/bloodysoles.dm @@ -21,6 +21,8 @@ /// The world.time when we last picked up blood var/last_pickup + var/footprint_sprite = FOOTPRINT_SPRITE_SHOES + /datum/component/bloodysoles/Initialize() if(!isclothing(parent)) return COMPONENT_INCOMPATIBLE @@ -69,9 +71,9 @@ //Find a blood decal on a turf that matches our last_blood_state -/datum/component/bloodysoles/proc/find_pool_by_blood_state(turf/turfLoc, typeFilter = null) +/datum/component/bloodysoles/proc/find_pool_by_blood_state(turf/turfLoc, typeFilter = null, footprint_sprite) for(var/obj/effect/decal/cleanable/blood/pool in turfLoc) - if(pool.blood_state == last_blood_state && (!typeFilter || istype(pool, typeFilter))) + if(pool.blood_state == last_blood_state && pool.footprint_sprite == footprint_sprite && (!typeFilter || istype(pool, typeFilter))) return pool @@ -123,24 +125,24 @@ Used to make bloody footprints on the ground return var/half_our_blood = bloody_shoes[last_blood_state] / 2 - + var/footprint_sprite = wielder.get_footprint_sprite() // Add footprints in old loc if we have enough cream if(half_our_blood >= BLOOD_FOOTPRINTS_MIN) var/turf/oldLocTurf = get_turf(OldLoc) - var/obj/effect/decal/cleanable/blood/footprints/oldLocFP = find_pool_by_blood_state(oldLocTurf, /obj/effect/decal/cleanable/blood/footprints) + var/obj/effect/decal/cleanable/blood/footprints/oldLocFP = find_pool_by_blood_state(oldLocTurf, /obj/effect/decal/cleanable/blood/footprints, footprint_sprite) if(oldLocFP) // Footprints found in the tile we left, add us to it add_parent_to_footprint(oldLocFP) if (!(oldLocFP.exited_dirs & wielder.dir)) oldLocFP.exited_dirs |= wielder.dir oldLocFP.update_icon() - else if(find_pool_by_blood_state(oldLocTurf)) + else if(find_pool_by_blood_state(oldLocTurf, footprint_sprite = footprint_sprite)) // No footprints in the tile we left, but there was some other blood pool there. Add exit footprints on it bloody_shoes[last_blood_state] -= half_our_blood update_icon() - oldLocFP = new(oldLocTurf) + oldLocFP = new(oldLocTurf, footprint_sprite) if(!QDELETED(oldLocFP)) ///prints merged oldLocFP.blood_state = last_blood_state oldLocFP.exited_dirs |= wielder.dir @@ -160,7 +162,7 @@ Used to make bloody footprints on the ground bloody_shoes[last_blood_state] -= half_our_blood update_icon() - var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom)) + var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom), footprint_sprite) if(!QDELETED(FP)) ///prints merged FP.blood_state = last_blood_state FP.entered_dirs |= wielder.dir @@ -220,7 +222,8 @@ Like its parent but can be applied to carbon mobs instead of clothing items return COMPONENT_INCOMPATIBLE parent_atom = parent wielder = parent - + if(footprint_sprite) + src.footprint_sprite = footprint_sprite if(!bloody_feet) bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER) diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm index f496f76adf1e..e3bad18e8704 100644 --- a/code/datums/elements/decals/blood.dm +++ b/code/datums/elements/decals/blood.dm @@ -13,6 +13,7 @@ /datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, source) var/obj/item/I = source + ADD_KEEP_TOGETHER(I, "item_blood_overlay") if(!_color) _color = COLOR_BLOOD var/icon = I.icon diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index f700c847f6ee..d885f080cab0 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -11,6 +11,7 @@ var/dryname = "dried blood" //when the blood lasts long enough, it becomes dry and gets a new name var/drydesc = "Looks like it's been here a while. Eew." //as above var/drytime = 0 + var/footprint_sprite = null /obj/effect/decal/cleanable/blood/Initialize(mapload) . = ..() @@ -205,9 +206,8 @@ /obj/effect/decal/cleanable/blood/footprints name = "footprints" icon = 'icons/effects/footprints.dmi' - icon_state = "nothingwhatsoever" desc = "WHOSE FOOTPRINTS ARE THESE?" - icon_state = "blood1" + icon_state = "blood_shoes_enter" random_icon_states = null blood_state = BLOOD_STATE_HUMAN //the icon state to load images from var/entered_dirs = 0 @@ -220,28 +220,72 @@ dryname = "dried footprints" drydesc = "HMM... SOMEONE WAS HERE!" +/obj/effect/decal/cleanable/blood/footprints/Initialize(mapload, footprint_sprite) + src.footprint_sprite = footprint_sprite + . = ..() + icon_state = "" //All of the footprint visuals come from overlays + if(mapload) + entered_dirs |= dir //Keep the same appearance as in the map editor + update_appearance(mapload ? (ALL) : (UPDATE_NAME | UPDATE_DESC)) + +//Rotate all of the footprint directions too +/obj/effect/decal/cleanable/blood/footprints/setDir(newdir) + if(dir == newdir) + return ..() + + var/ang_change = dir2angle(newdir) - dir2angle(dir) + var/old_entered_dirs = entered_dirs + var/old_exited_dirs = exited_dirs + entered_dirs = 0 + exited_dirs = 0 + + for(var/Ddir in GLOB.cardinals) + if(old_entered_dirs & Ddir) + entered_dirs |= angle2dir_cardinal(dir2angle(Ddir) + ang_change) + if(old_exited_dirs & Ddir) + exited_dirs |= angle2dir_cardinal(dir2angle(Ddir) + ang_change) + + update_appearance() + return ..() + +/obj/effect/decal/cleanable/blood/footprints/update_name(updates) + switch(footprint_sprite) + if(FOOTPRINT_SPRITE_CLAWS) + name = "clawprints" + if(FOOTPRINT_SPRITE_SHOES) + name = "footprints" + if(FOOTPRINT_SPRITE_PAWS) + name = "pawprints" + dryname = "dried [name]" + return ..() + +/obj/effect/decal/cleanable/blood/footprints/update_desc(updates) + desc = "WHOSE [uppertext(name)] ARE THESE?" + return ..() + +/obj/effect/decal/cleanable/blood/footprints/update_icon() + . = ..() + alpha = min(BLOODY_FOOTPRINT_BASE_ALPHA + (255 - BLOODY_FOOTPRINT_BASE_ALPHA) * bloodiness / (BLOOD_ITEM_MAX / 2), 255) + /obj/effect/decal/cleanable/blood/footprints/update_overlays() . = ..() for(var/Ddir in GLOB.cardinals) if(entered_dirs & Ddir) - var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["entered-[blood_state]-[Ddir]"] + var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["entered-[footprint_sprite]-[blood_state]-[Ddir]"] if(!bloodstep_overlay) - GLOB.bloody_footprints_cache["entered-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]1", dir = Ddir) + GLOB.bloody_footprints_cache["entered-[footprint_sprite]-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]_[footprint_sprite]_enter", dir = Ddir) . += bloodstep_overlay if(exited_dirs & Ddir) - var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["exited-[blood_state]-[Ddir]"] + var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["exited-[footprint_sprite]-[blood_state]-[Ddir]"] if(!bloodstep_overlay) - GLOB.bloody_footprints_cache["exited-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]2", dir = Ddir) + GLOB.bloody_footprints_cache["exited-[footprint_sprite]-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]_[footprint_sprite]_exit", dir = Ddir) . += bloodstep_overlay - alpha = min(BLOODY_FOOTPRINT_BASE_ALPHA + (255 - BLOODY_FOOTPRINT_BASE_ALPHA) * bloodiness / (BLOOD_ITEM_MAX / 2), 255) - - /obj/effect/decal/cleanable/blood/footprints/examine(mob/user) . = ..() if((shoe_types.len + species_types.len) > 0) - . += "You recognise the footprints as belonging to:" + . += "You recognise the [name] as belonging to:" for(var/sole in shoe_types) var/obj/item/clothing/item = sole var/article = initial(item.gender) == PLURAL ? "Some" : "A" @@ -251,14 +295,14 @@ if(species == "unknown") . += "Some feet." else if(species == "monkey") - . += "[icon2html('icons/mob/monkey.dmi', user, "monkey1")] Some monkey feet." + . += "[icon2html('icons/mob/monkey.dmi', user, "monkey1")] Some monkey paws." else if(species == "human") . += "[icon2html('icons/mob/human_parts.dmi', user, "default_human_l_leg")] Some human feet." else . += "[icon2html('icons/mob/human_parts.dmi', user, "[species]_l_leg")] Some [species] feet." -/obj/effect/decal/cleanable/blood/footprints/replace_decal(obj/effect/decal/cleanable/C) - if(blood_state != C.blood_state) //We only replace footprints of the same type as us +/obj/effect/decal/cleanable/blood/footprints/replace_decal(obj/effect/decal/cleanable/blood/blood_decal) + if(blood_state != blood_decal.blood_state || footprint_sprite != blood_decal.footprint_sprite) //We only replace footprints of the same type as us return FALSE return ..() diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 253dd925eb8f..ed25bd294c73 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -9,7 +9,7 @@ slot_flags = ITEM_SLOT_FEET slowdown = SHOES_SLOWDOWN - + var/footprint_sprite = FOOTPRINT_SPRITE_SHOES var/offset = 0 var/equipped_before_drop = FALSE var/xenoshoe = NO_DIGIT // Check for if shoes can be worn by straight legs (NO_DIGIT) which is default, both / hybrid (EITHER_STYLE), or digitigrade only (YES_DIGIT) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 6fe67f54b776..1fe00caa4707 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -984,6 +984,9 @@ var/obj/item/organ/I = X I.Insert(src) +/mob/living/carbon/proc/get_footprint_sprite() + return FOOTPRINT_SPRITE_PAWS + /mob/living/carbon/vv_get_dropdown() . = ..() VV_DROPDOWN_SEPERATOR diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 39c2d4f18ec9..8223aeb52ba3 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -40,7 +40,7 @@ var/obj/item/clothing/head = null var/obj/item/clothing/gloves = null //only used by humans - var/obj/item/clothing/shoes = null //only used by humans. + var/obj/item/clothing/shoes/shoes = null //only used by humans. var/obj/item/clothing/glasses/glasses = null //only used by humans. var/obj/item/clothing/ears = null //only used by humans. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index be2381ed21bd..b8ccb11b772c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -35,7 +35,7 @@ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(clean_face)) AddComponent(/datum/component/personal_crafting) AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6) - AddComponent(/datum/component/bloodysoles/feet) + AddComponent(/datum/component/bloodysoles/feet, FOOTPRINT_SPRITE_SHOES) /mob/living/carbon/human/proc/setup_human_dna() //initialize dna. for spawned humans; overwritten by other code @@ -547,6 +547,11 @@ // Might need re-wording. to_chat(user, span_alert("There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].")) +/mob/living/carbon/human/get_footprint_sprite() + var/obj/item/bodypart/l_leg/left_leg = get_bodypart(BODY_ZONE_L_LEG) + var/obj/item/bodypart/r_leg/right_leg = get_bodypart(BODY_ZONE_R_LEG) + return shoes?.footprint_sprite || left_leg?.footprint_sprite || right_leg?.footprint_sprite + /mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) if(judgement_criteria & JUDGE_EMAGGED) return 10 //Everyone is a criminal! diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index 64bf4b260469..688e71d8d8cf 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -259,6 +259,8 @@ px_y = 12 max_stamina_damage = 50 can_be_disabled = TRUE + /// Used by the bloodysoles component to make footprints + var/footprint_sprite = FOOTPRINT_SPRITE_SHOES /obj/item/bodypart/l_leg/set_owner(new_owner) . = ..() @@ -315,6 +317,7 @@ animal_origin = MONKEY_BODYPART wound_resistance = -10 px_y = 4 + footprint_sprite = FOOTPRINT_SPRITE_PAWS /obj/item/bodypart/l_leg/alien icon = 'icons/mob/animal_parts.dmi' @@ -348,6 +351,8 @@ px_y = 12 max_stamina_damage = 50 can_be_disabled = TRUE + /// Used by the bloodysoles component to make footprints + var/footprint_sprite = FOOTPRINT_SPRITE_SHOES /obj/item/bodypart/r_leg/set_owner(new_owner) . = ..() @@ -404,6 +409,7 @@ animal_origin = MONKEY_BODYPART wound_resistance = -10 px_y = 4 + footprint_sprite = FOOTPRINT_SPRITE_PAWS /obj/item/bodypart/r_leg/alien icon = 'icons/mob/animal_parts.dmi' diff --git a/icons/effects/footprints.dmi b/icons/effects/footprints.dmi index dc8ddb4e734127034886a629cf2cb6cfadfe6fac..29fdbe8eb0dd0f4e61c11a8fc5b02a14f70942e5 100644 GIT binary patch delta 4057 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 x0-qWN>b<3b2PahTEe)7)sNVbY5UTf