diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 8f122c5be7c..254dee79055 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -228,6 +228,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_PROFOUND_FISHER "profound_fisher" /// This trait lets you evaluate someone's fitness level against your own #define TRAIT_EXAMINE_FITNESS "reveal_power_level" +/// These mobs have particularly hygienic tongues +#define TRAIT_WOUND_LICKER "wound_licker" /// Added to a mob, allows that mob to experience flavour-based moodlets when examining food #define TRAIT_REMOTE_TASTING "remote_tasting" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 779b56ce01d..28100977dc0 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -498,6 +498,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_WINE_TASTER" = TRAIT_WINE_TASTER, "TRAIT_WING_BUFFET" = TRAIT_WING_BUFFET, "TRAIT_WING_BUFFET_TIRED" = TRAIT_WING_BUFFET_TIRED, + "TRAIT_WOUND_LICKER" = TRAIT_WOUND_LICKER, "TRAIT_XENO_HOST" = TRAIT_XENO_HOST, "TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE, "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING, diff --git a/code/datums/status_effects/debuffs/slime/slimed.dm b/code/datums/status_effects/debuffs/slime/slimed.dm index 15632277f3d..6c2c0fb5be3 100644 --- a/code/datums/status_effects/debuffs/slime/slimed.dm +++ b/code/datums/status_effects/debuffs/slime/slimed.dm @@ -5,9 +5,42 @@ /atom/movable/screen/alert/status_effect/slimed name = "Covered in Slime" - desc = "You are covered in slime and it's eating away at you! Find a way to wash it off!" + desc = "You are covered in slime and it's eating away at you! Click to start cleaning it off, or find a faster way to wash it away!" icon_state = "slimed" +/atom/movable/screen/alert/status_effect/slimed/Click() + . = ..() + if (!.) + return FALSE + if (!can_wash()) + return FALSE + INVOKE_ASYNC(src, PROC_REF(remove_slime)) + return TRUE + +/// Confirm that we are capable of washing off slime +/atom/movable/screen/alert/status_effect/slimed/proc/can_wash() + var/mob/living/living_owner = owner + if (!living_owner.can_resist()) + return FALSE + if (DOING_INTERACTION_WITH_TARGET(owner, owner)) + return FALSE + if (locate(/datum/status_effect/fire_handler/wet_stacks) in living_owner.status_effects) + return FALSE // Don't double dip with washing + return TRUE + +/// Try to get rid of it +/atom/movable/screen/alert/status_effect/slimed/proc/remove_slime() + owner.balloon_alert(owner, "cleaning off slime...") + var/datum/status_effect/slimed/slime_effect = owner.has_status_effect(/datum/status_effect/slimed) + while (!QDELETED(src) && !isnull(slime_effect)) + if (!can_wash()) + return + var/clean_interval = HAS_TRAIT(owner, TRAIT_WOUND_LICKER) ? 1.2 SECONDS : 1.5 SECONDS + owner.Shake(2, 0, duration = clean_interval * 0.8, shake_interval = 0.05 SECONDS) + if (!do_after(owner, clean_interval, owner)) + return + slime_effect.remove_stacks() + /datum/status_effect/slimed id = "slimed" tick_interval = 3 SECONDS @@ -32,6 +65,16 @@ to_chat(owner, span_userdanger("You have been covered in a thick layer of slime! Find a way to wash it off!")) return ..() +/datum/status_effect/slimed/proc/remove_stacks(stacks_to_remove = 1) + slime_stacks -= stacks_to_remove // lose 1 stack per second + if(slime_stacks <= 0) + to_chat(owner, span_notice("You manage to wash off the layer of slime completely.")) + qdel(src) + return + + if(prob(10)) + to_chat(owner,span_warning("The layer of slime is slowly getting thinner.")) + /datum/status_effect/slimed/tick(seconds_between_ticks) // remove from the mob once we have dealt enough damage if(owner.get_organic_health() <= MIN_HEALTH) @@ -42,20 +85,11 @@ // handle washing slime off var/datum/status_effect/fire_handler/wet_stacks/wetness = locate() in owner.status_effects if(istype(wetness) && wetness.stacks > (MIN_WATER_STACKS * seconds_between_ticks)) - slime_stacks -= seconds_between_ticks // lose 1 stack per second wetness.adjust_stacks(-5 * seconds_between_ticks) - - // got rid of it + remove_stacks(seconds_between_ticks) // 1 per second if(slime_stacks <= 0) - to_chat(owner, span_notice("You manage to wash off the layer of slime completely.")) - qdel(src) return - if(SPT_PROB(10, seconds_between_ticks)) - to_chat(owner,span_warning("The layer of slime is slowly getting thinner as it's washing off your skin.")) - - return - // otherwise deal brute damage owner.apply_damage(rand(2,4) * seconds_between_ticks, damagetype = BRUTE) diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index 980cb712484..0e370c2ffca 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -189,7 +189,7 @@ return suture(I, user) /datum/wound/slash/flesh/try_handling(mob/living/carbon/human/user) - if(user.pulling != victim || user.zone_selected != limb.body_zone || !isfelinid(user) || !victim.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)) + if(user.pulling != victim || user.zone_selected != limb.body_zone || !HAS_TRAIT(user, TRAIT_WOUND_LICKER) || !victim.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)) return FALSE if(DOING_INTERACTION_WITH_TARGET(user, victim)) to_chat(user, span_warning("You're already interacting with [victim]!")) diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm index e452c156474..b7a947f00e3 100644 --- a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm +++ b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm @@ -55,6 +55,7 @@ /mob/living/basic/mining/wolf/Initialize(mapload) . = ..() + ADD_TRAIT(src, TRAIT_WOUND_LICKER, INNATE_TRAIT) AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) AddElement(/datum/element/ai_flee_while_injured) AddElement(/datum/element/ai_retaliate) diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm index 3c2612d9a62..400bd9ac9f1 100644 --- a/code/modules/mob/living/basic/pets/cat/cat.dm +++ b/code/modules/mob/living/basic/pets/cat/cat.dm @@ -58,7 +58,7 @@ AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW) add_cell_sample() add_verb(src, /mob/living/proc/toggle_resting) - add_traits(list(TRAIT_CATLIKE_GRACE, TRAIT_VENTCRAWLER_ALWAYS), INNATE_TRAIT) + add_traits(list(TRAIT_CATLIKE_GRACE, TRAIT_VENTCRAWLER_ALWAYS, TRAIT_WOUND_LICKER), INNATE_TRAIT) ai_controller.set_blackboard_key(BB_HUNTABLE_PREY, typecacheof(huntable_items)) if(can_breed) add_breeding_component() diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm index f513795e7f6..5cd970575ec 100644 --- a/code/modules/mob/living/basic/pets/dog/_dog.dm +++ b/code/modules/mob/living/basic/pets/dog/_dog.dm @@ -47,6 +47,7 @@ /mob/living/basic/pet/dog/Initialize(mapload) . = ..() + ADD_TRAIT(src, TRAIT_WOUND_LICKER, INNATE_TRAIT) AddElement(/datum/element/pet_bonus, "woofs happily!") AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) AddElement(/datum/element/unfriend_attacker, untamed_reaction = "%SOURCE% fixes %TARGET% with a look of betrayal.") diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index c01b5938124..a47639fe0e6 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -617,6 +617,7 @@ GLOBAL_LIST_INIT(english_to_zombie, list()) say_mod = "meows" liked_foodtypes = SEAFOOD | ORANGES | BUGS | GORE disliked_foodtypes = GROSS | CLOTH | RAW + organ_traits = list(TRAIT_WOUND_LICKER) /obj/item/organ/internal/tongue/jelly name = "jelly tongue"