Organ damage refactor/cleanup (#93436)

## About The Pull Request
So, my original goal was just a refactor for the emissive overlays of
eyes, as a way to implement the specular emissive introduced by smartkar
some time ago, but somehow I found myself dragged into a bigger refactor
or cleanup of organ damage, thresholds, failures. One of the main
problem was that there were no procs called when a organ suffered enough
damage to fail or when recovering from failure. It'd just enable or
disable a bitflag, leaving it up to subtypes to decide how to tackle
organ failure their own ways: diverse, funky and sometimes incompatible.
More often than not relying on their very own "update_thingamajig" kinda
procs that run whenever the organ takes damage, rather than just when
the threshold is reached (low, high, failure. There are however a couple
organs with their own quirky thresholds, I let those slide).

There's also a bit of old code, especially for ears, with the
`AdjustEarDamage` and temporary deafness both predating the framework
for organ damage as far as I know. It really needed a coat of fresh
paint.

Oh, there were also more than a handful of organs that still heavily
relied on some ORGAN_TRAIT source instead of the `organ_traits` list and
the two add/remove procs `add_organ_trait` or `remove_organ_trait`. This
include organs that lose or gain specific traits when failing et
viceversa.

~~Lastly, felinids (and the halloween ghost species) having reflective
eyes. It's just a nod to the tapetum lucidum that animals with night
vision often have (including cats), which is why their eyes are a bit
brighter in the dark. Felinids however, do not have night vision (nor do
ghosts). This is merely cosmetic.~~ Cut out for the time being due to
issues with the specular emissive...

## Why It's Good For The Game
Refactoring / cleaning up old organ code.

## Changelog

🆑
refactor: Refactored organ damage code a little. Hopefully there won't
be issues (otherwise report them).
/🆑
This commit is contained in:
Ghom
2025-10-21 23:52:28 +02:00
committed by GitHub
parent 593fe47c1c
commit ca2cc70322
34 changed files with 422 additions and 388 deletions
+4
View File
@@ -550,6 +550,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CULT_HALO "cult_halo"
/// Their eyes glow an unnatural red colour. Currently used to set special examine text on humans. Does not guarantee the mob's eyes are coloured red, nor that there is any visible glow on their character sprite.
#define TRAIT_UNNATURAL_RED_GLOWY_EYES "unnatural_red_glowy_eyes"
/// Their eyes glow in the dark (aka emissive appearance)
#define TRAIT_LUMINESCENT_EYES "luminescent_eyes"
/// Their eyes reflect light, making them shine faintly in dim, shadowy areas. Kinda like nocturnal animals.
#define TRAIT_REFLECTIVE_EYES "reflective_eyes"
/// Their eyes are bloodshot. Currently used to set special examine text on humans. Examine text is overridden by TRAIT_UNNATURAL_RED_GLOWY_EYES.
#define TRAIT_BLOODSHOT_EYES "bloodshot_eyes"
/// This mob should never close UI even if it doesn't have a client
+3 -6
View File
@@ -23,8 +23,7 @@
victim.Stun(100)
victim.adjust_stutter(30 SECONDS)
victim.set_jitter_if_lower(1000 SECONDS)
var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(10, 15)
victim.sound_damage(10, 30 SECONDS)
to_chat(victim, "<font color='red' size='8'>HONK</font>")
var/obj/item/clothing/shoes/victim_shoes = victim.get_item_by_slot(ITEM_SLOT_FEET)
if(!victim_shoes || victim_shoes.fastening_type == SHOES_SLIPON)
@@ -35,13 +34,11 @@
victim.Paralyze(20)
victim.Stun(50)
victim.set_jitter_if_lower(500 SECONDS)
var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(7, 10)
victim.sound_damage(7, 20 SECONDS)
to_chat(victim, "<font color='red' size='5'>HONK</font>")
for(var/mob/living/carbon/victim in lightly_honked)
victim.Knockdown(20)
victim.set_jitter_if_lower(200 SECONDS)
var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(4, 5)
victim.sound_damage(4, 10 SECONDS)
to_chat(victim, "<font color='red' size='2'>HONK</font>")
+2
View File
@@ -372,6 +372,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_LITERATE" = TRAIT_LITERATE,
"TRAIT_LIVERLESS_METABOLISM" = TRAIT_LIVERLESS_METABOLISM,
"TRAIT_LOUD_BINARY" = TRAIT_LOUD_BINARY,
"TRAIT_LUMINESCENT_EYES" = TRAIT_LUMINESCENT_EYES,
"TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE,
"TRAIT_MAFIAINITIATE" = TRAIT_MAFIAINITIATE,
"TRAIT_MAGICALLY_GIFTED" = TRAIT_MAGICALLY_GIFTED,
@@ -498,6 +499,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_REAGENT_SCANNER" = TRAIT_REAGENT_SCANNER,
"TRAIT_RECENTLY_BLOCKED_MAGIC" = TRAIT_RECENTLY_BLOCKED_MAGIC,
"TRAIT_RECENTLY_TREATED" = TRAIT_RECENTLY_TREATED,
"TRAIT_REFLECTIVE_EYES" = TRAIT_REFLECTIVE_EYES,
"TRAIT_REGEN_SHIELD" = TRAIT_REGEN_SHIELD,
"TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER,
"TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING,
+2
View File
@@ -170,6 +170,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_LIMBATTACHMENT" = TRAIT_LIMBATTACHMENT,
"TRAIT_LITERATE" = TRAIT_LITERATE,
"TRAIT_LIVERLESS_METABOLISM" = TRAIT_LIVERLESS_METABOLISM,
"TRAIT_LUMINESCENT_EYES" = TRAIT_LUMINESCENT_EYES,
"TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE,
"TRAIT_MAGICALLY_GIFTED" = TRAIT_MAGICALLY_GIFTED,
"TRAIT_MARTIAL_ARTS_IMMUNE" = TRAIT_MARTIAL_ARTS_IMMUNE,
@@ -256,6 +257,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_QUICK_CARRY" = TRAIT_QUICK_CARRY,
"TRAIT_QUICKER_CARRY" = TRAIT_QUICKER_CARRY,
"TRAIT_RADIMMUNE" = TRAIT_RADIMMUNE,
"TRAIT_REFLECTIVE_EYES" = TRAIT_REFLECTIVE_EYES,
"TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING,
"TRAIT_RESISTCOLD" = TRAIT_RESISTCOLD,
"TRAIT_RESISTHEAT" = TRAIT_RESISTHEAT,
@@ -45,7 +45,6 @@
return
var/mob/living/carbon/infected_mob = advanced_disease.affected_mob
var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS)
switch(advanced_disease.stage)
if(3, 4)
@@ -53,15 +52,18 @@
to_chat(infected_mob, span_warning("[pick("You hear a ringing in your ear.", "Your ears pop.")]"))
if(5)
if(causes_permanent_deafness)
if(ears.damage < ears.maxHealth)
if(!HAS_TRAIT_FROM(infected_mob, TRAIT_DEAF, DISEASE_TRAIT))
to_chat(infected_mob, span_userdanger("Your ears pop painfully and start bleeding!"))
// Just absolutely murder me man
ears.apply_organ_damage(ears.maxHealth)
infected_mob.adjustOrganLoss(ORGAN_SLOT_EARS, INFINITY)
infected_mob.emote("scream")
ADD_TRAIT(infected_mob, TRAIT_DEAF, DISEASE_TRAIT)
else
to_chat(infected_mob, span_userdanger("Your ears pop and begin ringing loudly!"))
ears.deaf = min(20, ears.deaf + 15)
var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS)
var/deafness_to_add = min(40 SECONDS - ears.temporary_deafness, 30 SECONDS)
if(deafness_to_add > 0)
ears.adjust_temporary_deafness(deafness_to_add)
/datum/symptom/deafness/on_stage_change(datum/disease/advance/advanced_disease)
. = ..()
@@ -96,7 +96,8 @@
if(4, 5)
if(advanced_disease.has_required_infectious_organ(infected_mob, ORGAN_SLOT_EARS))
var/obj/item/organ/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS)
ears.adjustEarDamage(-4, -4)
ears?.apply_organ_damage(-4)
ears?.adjust_temporary_deafness(-8 SECONDS)
if(!advanced_disease.has_required_infectious_organ(infected_mob, ORGAN_SLOT_EYES))
return
+3 -2
View File
@@ -27,8 +27,9 @@
for(var/i in user_by_item)
var/mob/living/carbon/user = user_by_item[i]
var/obj/item/organ/ears/ears = user.get_organ_slot(ORGAN_SLOT_EARS)
if(!ears || !ears.damage || (ears.organ_flags & ORGAN_FAILING) || IS_ROBOTIC_ORGAN(ears))
if(!ears || (ears.organ_flags & ORGAN_FAILING) || IS_ROBOTIC_ORGAN(ears))
continue
ears.deaf = max(ears.deaf - 0.25 * seconds_per_tick, (ears.damage < ears.maxHealth ? 0 : 1)) // Do not clear deafness if our ears are too damaged
ears.adjust_temporary_deafness(-0.5 SECONDS * seconds_per_tick)
ears.apply_organ_damage(-0.025 * seconds_per_tick)
CHECK_TICK
@@ -18,9 +18,7 @@
if(iscarbon(M))
var/mob/living/carbon/C = M
if(!IS_CHANGELING(C))
var/obj/item/organ/ears/ears = C.get_organ_slot(ORGAN_SLOT_EARS)
if(ears)
ears.adjustEarDamage(0, 30)
C.sound_damage(deafen = 30 SECONDS)
C.adjust_confusion(25 SECONDS)
C.set_jitter_if_lower(100 SECONDS)
else
@@ -264,8 +264,7 @@
hearer.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic)
hearer.emote("scream")
hearer.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
var/obj/item/organ/ears/regret = hearer.get_organ_slot(ORGAN_SLOT_EARS)
regret?.adjustEarDamage(10,20)
hearer.sound_damage(10, 40 SECONDS)
return "[owner.p_Their()] lungs emit [span_hypnophrase(breath_noise)]"
/// It's full of worms
@@ -44,16 +44,14 @@
/// The duration of these effects are based on sanity, mainly for flavor but also to make it a weaker alpha strike
var/maximum_duration = 15 SECONDS
var/mind_gate_duration = ((SANITY_MAXIMUM - cast_on.mob_mood.sanity) / (SANITY_MAXIMUM - SANITY_INSANE)) * maximum_duration
var/mind_gate_duration = ((SANITY_MAXIMUM - cast_on.mob_mood.sanity) / (SANITY_MAXIMUM - SANITY_INSANE)) * maximum_duration + 1 SECONDS
to_chat(cast_on, span_warning("Your eyes cry out in pain, your ears bleed and your lips seal! THE MOON SMILES UPON YOU!"))
cast_on.adjust_temp_blindness(mind_gate_duration + 1 SECONDS)
cast_on.set_eye_blur_if_lower(mind_gate_duration + 2 SECONDS)
cast_on.adjust_temp_blindness(mind_gate_duration)
cast_on.set_eye_blur_if_lower(mind_gate_duration + 1 SECONDS)
var/obj/item/organ/ears/ears = cast_on.get_organ_slot(ORGAN_SLOT_EARS)
//adjustEarDamage takes deafness duration parameter in one unit per two seconds, instead of the normal time, so we divide by two seconds
ears?.adjustEarDamage(0, (mind_gate_duration + 1 SECONDS) / (2 SECONDS))
cast_on.sound_damage(0, mind_gate_duration)
cast_on.adjust_silence(mind_gate_duration + 1 SECONDS)
cast_on.adjust_silence(mind_gate_duration)
cast_on.add_mood_event("moon_smile", /datum/mood_event/moon_smile)
// Only knocksdown if the target has a low enough sanity
+1 -1
View File
@@ -740,7 +740,7 @@
else
var/obj/item/organ/ears/has_ears = human_examined.get_organ_slot(ORGAN_SLOT_EARS)
if(has_ears)
if(has_ears.deaf)
if(has_ears.temporary_deafness)
final_message += "\tDamaged eardrums in [examining.p_their()] ear canals."
else
final_message += "\tA set of [has_ears.damage ? "" : "healthy "][has_ears.name]."
+1 -1
View File
@@ -815,7 +815,7 @@
/obj/item/organ/ears/babbelfish/proc/on_drain_magic(mob/user)
to_chat(user, span_noticealien("Your [src] pop as they protect your mind from psychic phenomena!"))
adjustEarDamage(ddeaf = 20)
adjust_temporary_deafness(40 SECONDS)
/obj/item/organ/ears/babbelfish/proc/on_expire(mob/user)
to_chat(user, span_noticealien("Your [src] suddenly burst apart!"))
@@ -83,8 +83,7 @@
if(HAS_TRAIT(current_target, TRAIT_DEAF))
return
var/obj/item/organ/ears/target_ears = current_target.get_organ_slot(ORGAN_SLOT_EARS)
target_ears?.adjustEarDamage(0, 5)
sound_damage(deafen = 10 SECONDS)
/mob/living/basic/bot/honkbot/ui_data(mob/user)
var/list/data = ..()
@@ -31,14 +31,10 @@
. = ..()
animate_pulse()
/obj/item/organ/legion_tumour/apply_organ_damage(damage_amount, maximum, required_organ_flag)
var/was_failing = organ_flags & ORGAN_FAILING
. = ..()
if (was_failing != (organ_flags & ORGAN_FAILING))
animate_pulse()
/obj/item/organ/legion_tumour/on_begin_failure()
animate_pulse()
/obj/item/organ/legion_tumour/set_organ_damage(damage_amount, required_organ_flag)
. = ..()
/obj/item/organ/legion_tumour/on_failure_recovery()
animate_pulse()
/// Do a heartbeat animation depending on if we're failing or not
+26 -29
View File
@@ -25,8 +25,6 @@
var/mob/living/brain/brainmob = null
/// If it's a fake brain with no brainmob assigned. Feedback messages will be faked as if it does have a brainmob. See changelings & dullahans.
var/decoy_override = FALSE
/// Two variables necessary for calculating whether we get a brain trauma or not
var/damage_delta = 0
var/list/datum/brain_trauma/traumas = list()
@@ -345,34 +343,42 @@
owner.investigate_log("has been killed by brain damage.", INVESTIGATE_DEATHS)
owner.death()
/obj/item/organ/brain/check_damage_thresholds(mob/M)
/obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE)
. = ..()
var/delta_dam = . //for the sake of clarity
if(delta_dam <= 0 || damage < BRAIN_DAMAGE_MILD)
return
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
var/is_boosted = (owner && HAS_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST))
if(damage < BRAIN_DAMAGE_SEVERE)
return
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
if(prob(20 + (is_boosted * 30)))
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE)
else
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
/obj/item/organ/brain/check_damage_thresholds()
. = ..()
if(!owner)
return
// If we crossed blinking brain damage thresholds either way, update our blinking
if (owner && ((prev_damage > BRAIN_DAMAGE_ASYNC_BLINKING && damage < BRAIN_DAMAGE_ASYNC_BLINKING) || (prev_damage < BRAIN_DAMAGE_ASYNC_BLINKING && damage > BRAIN_DAMAGE_ASYNC_BLINKING)))
if((prev_damage >= BRAIN_DAMAGE_ASYNC_BLINKING && damage < BRAIN_DAMAGE_ASYNC_BLINKING) || (prev_damage < BRAIN_DAMAGE_ASYNC_BLINKING && damage >= BRAIN_DAMAGE_ASYNC_BLINKING))
var/obj/item/organ/eyes/eyes = owner.get_organ_slot(ORGAN_SLOT_EYES)
if(eyes?.blink_animation)
eyes.animate_eyelids(owner)
if(damage >= 60 && prev_damage < 60)
owner.add_mood_event("brain_damage", /datum/mood_event/brain_damage)
else if(prev_damage >= 60 && damage < 60)
owner.clear_mood_event("brain_damage")
// If we're not more injured than before, return without gambling for a trauma
if(damage <= prev_damage)
return
damage_delta = damage - prev_damage
if(damage > BRAIN_DAMAGE_MILD)
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
var/is_boosted = (owner && HAS_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST))
if(damage > BRAIN_DAMAGE_SEVERE)
if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
if(prob(20 + (is_boosted * 30)))
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE)
else
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
if (!owner || owner.stat > UNCONSCIOUS)
return
// Conscious or soft-crit
var/brain_message
if(prev_damage < BRAIN_DAMAGE_MILD && damage >= BRAIN_DAMAGE_MILD)
@@ -665,15 +671,6 @@
amount_cured++
return amount_cured
/obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE)
. = ..()
if(!owner)
return FALSE
if(damage >= 60)
owner.add_mood_event("brain_damage", /datum/mood_event/brain_damage)
else
owner.clear_mood_event("brain_damage")
/// This proc lets the mob's brain decide what bodypart to attack with in an unarmed strike.
/obj/item/organ/brain/proc/get_attacking_limb(mob/living/carbon/human/target)
var/obj/item/bodypart/arm/active_hand = owner.get_active_hand()
@@ -71,22 +71,19 @@ In all, this is a lot like the monkey code. /N
if(!. || QDELETED(src))
return FALSE
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
switch (severity)
if (EXPLODE_DEVASTATE)
gib(DROP_ALL_REMAINS)
if (EXPLODE_HEAVY)
take_overall_damage(60, 60)
if(ears)
ears.adjustEarDamage(30,120)
sound_damage(30, 240 SECONDS)
if(EXPLODE_LIGHT)
take_overall_damage(30,0)
if(prob(50))
Unconscious(20)
if(ears)
ears.adjustEarDamage(15,60)
sound_damage(15, 120 SECONDS)
return TRUE
@@ -16,6 +16,17 @@
if(isclothing(wear_mask)) //Mask
. += wear_mask.flash_protect
/mob/living/carbon/sound_damage(damage, deafen)
if(HAS_TRAIT(src, TRAIT_GODMODE))
return
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
if(QDELETED(ears))
return
if(damage)
ears.apply_organ_damage(damage * ears.damage_multiplier)
if(deafen)
ears.adjust_temporary_deafness(deafen)
/mob/living/carbon/get_ear_protection()
. = ..()
if(HAS_TRAIT(src, TRAIT_DEAF))
@@ -523,8 +534,8 @@
if(ears && (deafen_pwr || damage_pwr))
var/ear_damage = damage_pwr * effect_amount
var/deaf = deafen_pwr * effect_amount
ears.adjustEarDamage(ear_damage,deaf)
var/deaf = deafen_pwr * effect_amount * 2 SECONDS
sound_damage(ear_damage, deaf)
. = effect_amount //how soundbanged we are
SEND_SOUND(src, sound('sound/items/weapons/flash_ring.ogg',0,1,0,250))
@@ -20,7 +20,7 @@
if(OFFSET_HEAD)
update_worn_head()
if(OFFSET_FACE)
dna?.species?.update_face_offset(src) // updates eye and lipstick icon
update_face_offset() // updates eye and lipstick icon
update_worn_mask()
if(OFFSET_BELT)
update_worn_belt()
@@ -49,7 +49,6 @@
SEND_SIGNAL(src, COMSIG_CARBON_REMOVE_OVERLAY, cache_index, I)
/mob/living/carbon/update_body(is_creating = FALSE)
dna?.species.handle_body(src)
update_body_parts(is_creating)
/mob/living/carbon/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
@@ -504,6 +503,9 @@
apply_overlay(BODYPARTS_LAYER)
/mob/living/carbon/proc/update_face_offset()
return
/////////////////////////
// Limb Icon Cache 2.0 //
/////////////////////////
@@ -485,84 +485,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
human.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
/**
* Handles the body of a human
*
* Handles lipstick, having no eyes, eye color, undergarnments like underwear, undershirts, and socks, and body layers.
* Arguments:
* * species_human - Human, whoever we're handling the body for
*/
/datum/species/proc/handle_body(mob/living/carbon/human/species_human)
species_human.remove_overlay(BODY_LAYER)
species_human.remove_overlay(EYES_LAYER)
if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN))
return
if(!HAS_TRAIT(species_human, TRAIT_HUSK))
var/obj/item/bodypart/head/noggin = species_human.get_bodypart(BODY_ZONE_HEAD)
if(noggin?.head_flags & HEAD_EYESPRITES)
// eyes (missing eye sprites get handled by the head itself, but sadly we have to do this stupid shit here, for now)
var/obj/item/organ/eyes/eye_organ = species_human.get_organ_slot(ORGAN_SLOT_EYES)
if(eye_organ)
eye_organ.refresh(call_update = FALSE)
species_human.overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(species_human)
species_human.apply_overlay(EYES_LAYER)
if(HAS_TRAIT(species_human, TRAIT_NO_UNDERWEAR))
return
// Underwear, Undershirts & Socks
var/list/standing = list()
if(species_human.underwear)
var/datum/sprite_accessory/underwear/underwear = SSaccessories.underwear_list[species_human.underwear]
var/mutable_appearance/underwear_overlay
if(underwear)
if(species_human.dna.species.sexes && species_human.physique == FEMALE && (underwear.gender == MALE))
underwear_overlay = mutable_appearance(wear_female_version(underwear.icon_state, underwear.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER)
else
underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
if(!underwear.use_static)
underwear_overlay.color = species_human.underwear_color
standing += underwear_overlay
if(species_human.undershirt)
var/datum/sprite_accessory/undershirt/undershirt = SSaccessories.undershirt_list[species_human.undershirt]
if(undershirt)
var/mutable_appearance/working_shirt
if(species_human.dna.species.sexes && species_human.physique == FEMALE)
working_shirt = mutable_appearance(wear_female_version(undershirt.icon_state, undershirt.icon), layer = -BODY_LAYER)
else
working_shirt = mutable_appearance(undershirt.icon, undershirt.icon_state, layer = -BODY_LAYER)
standing += working_shirt
if(species_human.socks && species_human.num_legs >= 2 && !(species_human.bodyshape & BODYSHAPE_DIGITIGRADE))
var/datum/sprite_accessory/socks/socks = SSaccessories.socks_list[species_human.socks]
if(socks)
standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
if(standing.len)
species_human.overlays_standing[BODY_LAYER] = standing
species_human.apply_overlay(BODY_LAYER)
/// Updates face (as of now, only eye) offsets
/datum/species/proc/update_face_offset(mob/living/carbon/human/species_human)
var/list/eye_overlays = species_human.overlays_standing[EYES_LAYER]
species_human.remove_overlay(EYES_LAYER)
if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(species_human, TRAIT_HUSK) || !length(eye_overlays))
return
var/obj/item/bodypart/head/noggin = species_human.get_bodypart(BODY_ZONE_HEAD)
for (var/mutable_appearance/overlay as anything in eye_overlays)
overlay.pixel_w = 0
overlay.pixel_z = 0
noggin.worn_face_offset.apply_offset(overlay)
species_human.overlays_standing[EYES_LAYER] = eye_overlays
species_human.apply_overlay(EYES_LAYER)
// This exists so sprite accessories can still be per-layer without having to include that layer's
// number in their sprite name, which causes issues when those numbers change.
/datum/species/proc/mutant_bodyparts_layertext(layer)
@@ -271,7 +271,6 @@
//200 max knockdown for EXPLODE_HEAVY
//160 max knockdown for EXPLODE_LIGHT
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
switch (severity)
if (EXPLODE_DEVASTATE)
if(bomb_armor < EXPLODE_GIB_THRESHOLD) //gibs the mob if their bomb armor is lower than EXPLODE_GIB_THRESHOLD
@@ -299,8 +298,8 @@
brute_loss = 30*(2 - round(bomb_armor*0.01, 0.05))
burn_loss = brute_loss //damage gets reduced from 120 to up to 60 combined brute+burn
damage_clothes(200 - bomb_armor, BRUTE, BOMB)
if (ears && !HAS_TRAIT_FROM_ONLY(src, TRAIT_DEAF, EAR_DAMAGE))
ears.adjustEarDamage(30, 120)
if (!HAS_TRAIT_FROM(src, TRAIT_DEAF, EAR_DAMAGE))
sound_damage(30, 240 SECONDS)
Unconscious(20) //short amount of time for follow up attacks against elusive enemies like wizards
Knockdown(200 - (bomb_armor * 1.6)) //between ~4 and ~20 seconds of knockdown depending on bomb armor
@@ -309,8 +308,8 @@
if(bomb_armor)
brute_loss = 15*(2 - round(bomb_armor*0.01, 0.05))
damage_clothes(max(50 - bomb_armor, 0), BRUTE, BOMB)
if (ears && !HAS_TRAIT_FROM_ONLY(src, TRAIT_DEAF, EAR_DAMAGE))
ears.adjustEarDamage(15,60)
if (!HAS_TRAIT_FROM(src, TRAIT_DEAF, EAR_DAMAGE))
sound_damage(15, 120 SECONDS)
Knockdown(160 - (bomb_armor * 1.6)) //100 bomb armor will prevent knockdown altogether
take_overall_damage(brute_loss,burn_loss)
@@ -859,6 +859,82 @@ generate/load female uniform sprites matching all previously decided variables
observers = null
break
/mob/living/carbon/human/update_body(is_creating = FALSE)
update_eyes()
update_underwear()
return ..()
/mob/living/carbon/human/proc/update_underwear()
remove_overlay(BODY_LAYER)
if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
return
// Underwear, Undershirts & Socks
var/list/standing = list()
if(underwear)
var/datum/sprite_accessory/underwear/undie_accessory = SSaccessories.underwear_list[underwear]
var/mutable_appearance/underwear_overlay
if(undie_accessory)
if(dna.species.sexes && physique == FEMALE && (undie_accessory.gender == MALE))
underwear_overlay = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER)
else
underwear_overlay = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER)
if(!undie_accessory.use_static)
underwear_overlay.color = underwear_color
standing += underwear_overlay
if(undershirt)
var/datum/sprite_accessory/undershirt/undie_accessory = SSaccessories.undershirt_list[undershirt]
if(undie_accessory)
var/mutable_appearance/working_shirt
if(dna.species.sexes && physique == FEMALE)
working_shirt = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon), layer = -BODY_LAYER)
else
working_shirt = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, layer = -BODY_LAYER)
standing += working_shirt
if(socks && num_legs >= 2 && !(bodyshape & BODYSHAPE_DIGITIGRADE))
var/datum/sprite_accessory/socks/undie_accessory = SSaccessories.socks_list[socks]
if(undie_accessory)
standing += mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER)
if(standing.len)
overlays_standing[BODY_LAYER] = standing
apply_overlay(BODY_LAYER)
/mob/living/carbon/human/proc/update_eyes()
remove_overlay(EYES_LAYER)
if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
return
var/obj/item/bodypart/head/noggin = get_bodypart(BODY_ZONE_HEAD)
if(!(noggin?.head_flags & HEAD_EYESPRITES))
return
// eyes (missing eye sprites get handled by the head itself, but sadly we have to do this stupid shit here, for now)
var/obj/item/organ/eyes/eye_organ = get_organ_slot(ORGAN_SLOT_EYES)
if(eye_organ)
eye_organ.refresh(call_update = FALSE)
overlays_standing[EYES_LAYER] = eye_organ.generate_body_overlay(src)
apply_overlay(EYES_LAYER)
/// Updates face (as of now, only eye) offsets
/mob/living/carbon/human/update_face_offset()
var/list/eye_overlays = overlays_standing[EYES_LAYER]
if(HAS_TRAIT(src, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(src, TRAIT_HUSK) || !length(eye_overlays))
return
remove_overlay(EYES_LAYER)
var/obj/item/bodypart/head/noggin = get_bodypart(BODY_ZONE_HEAD)
for (var/mutable_appearance/overlay as anything in eye_overlays)
overlay.pixel_w = 0
overlay.pixel_z = 0
noggin.worn_face_offset.apply_offset(overlay)
overlays_standing[EYES_LAYER] = eye_overlays
apply_overlay(EYES_LAYER)
// Only renders the head of the human
/mob/living/carbon/human/proc/update_body_parts_head_only(update_limb_data)
if(!dna?.species)
@@ -6,6 +6,7 @@
mutantbrain = /obj/item/organ/brain/felinid
mutanttongue = /obj/item/organ/tongue/cat
mutantears = /obj/item/organ/ears/cat
mutanteyes = /obj/item/organ/eyes/felinid
mutant_organs = list(
/obj/item/organ/tail/cat = "Cat",
)
@@ -35,6 +35,10 @@
/mob/living/proc/get_eye_protection()
return 0
///A easy to use proc to apply both organ damage and temporary deafness at once, so you don't have to get the ears everytime.
/mob/living/proc/sound_damage(damage, deafen)
return
//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears)
/mob/living/proc/get_ear_protection()
var/turf/current_turf = get_turf(src)
@@ -829,7 +829,7 @@
if(eyes.apply_organ_damage(-2 * REM * seconds_per_tick * normalise_creation_purity(), required_organ_flag = affected_organ_flags))
. = UPDATE_MOB_HEALTH
// If our eyes are seriously damaged, we have a probability of causing eye blur while healing depending on purity
if(eyes.damaged && IS_ORGANIC_ORGAN(eyes) && SPT_PROB(16 - min(normalized_purity * 6, 12), seconds_per_tick))
if(eyes.damage >= eyes.low_threshold && IS_ORGANIC_ORGAN(eyes) && SPT_PROB(16 - min(normalized_purity * 6, 12), seconds_per_tick))
// While healing, gives some eye blur
if(affected_mob.is_blind_from(EYE_DAMAGE))
to_chat(affected_mob, span_warning("Your vision slowly returns..."))
@@ -929,7 +929,9 @@
var/obj/item/organ/ears/ears = affected_mob.get_organ_slot(ORGAN_SLOT_EARS)
if(!ears)
return
ears.adjustEarDamage(-4 * REM * seconds_per_tick * normalise_creation_purity(), -4 * REM * seconds_per_tick * normalise_creation_purity())
var/multiplier = REM * seconds_per_tick * normalise_creation_purity()
ears.apply_organ_damage(-4 * multiplier)
ears.adjust_temporary_deafness(-8 * multiplier)
return UPDATE_MOB_HEALTH
/datum/reagent/medicine/inacusiate/on_mob_delete(mob/living/affected_mob)
@@ -3230,8 +3230,7 @@
RegisterSignal(affected_human, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_removed))
var/obj/item/organ/eyes/eyes = affected_human.get_organ_slot(ORGAN_SLOT_EYES)
if (eyes && !IS_ROBOTIC_ORGAN(eyes))
prev_ignore_lighting = eyes.overlay_ignore_lighting
eyes.overlay_ignore_lighting = TRUE
ADD_TRAIT(affected_human, TRAIT_LUMINESCENT_EYES, REF(src))
/datum/reagent/luminescent_fluid/on_mob_end_metabolize(mob/living/affected_mob)
. = ..()
@@ -3243,7 +3242,7 @@
affected_human.remove_eye_color(EYE_COLOR_LUMINESCENT_PRIORITY)
var/obj/item/organ/eyes/eyes = affected_human.get_organ_slot(ORGAN_SLOT_EYES)
if (eyes && !IS_ROBOTIC_ORGAN(eyes) && !overdosed)
eyes.overlay_ignore_lighting = prev_ignore_lighting
REMOVE_TRAIT(affected_human, TRAIT_LUMINESCENT_EYES, REF(src))
/datum/reagent/luminescent_fluid/on_mob_life(mob/living/affected_mob, seconds_per_tick, times_fired)
. = ..()
@@ -3262,14 +3261,13 @@
SIGNAL_HANDLER
if (istype(new_eyes) && !IS_ROBOTIC_ORGAN(new_eyes))
prev_ignore_lighting = new_eyes.overlay_ignore_lighting
new_eyes.overlay_ignore_lighting = TRUE
ADD_TRAIT(source, TRAIT_LUMINESCENT_EYES, REF(src))
/datum/reagent/luminescent_fluid/proc/on_organ_removed(mob/living/source, obj/item/organ/eyes/old_eyes)
SIGNAL_HANDLER
if (istype(old_eyes) && !IS_ROBOTIC_ORGAN(old_eyes) && !overdosed)
old_eyes.overlay_ignore_lighting = prev_ignore_lighting
REMOVE_TRAIT(source, TRAIT_LUMINESCENT_EYES, REF(src))
/datum/reagent/luminescent_fluid/overdose_start(mob/living/affected_mob)
. = ..()
+30 -28
View File
@@ -176,35 +176,37 @@
. += get_hair_and_lips_icon(dropped)
// We need to get the eyes if we are dropped (ugh)
if(dropped)
var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src
// This is a bit of copy/paste code from eyes.dm:generate_body_overlay
if(eyes?.eye_icon_state && (head_flags & HEAD_EYESPRITES))
var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
var/image/eye_right = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
if(head_flags & HEAD_EYECOLOR)
if(eyes.eye_color_left)
eye_left.color = eyes.eye_color_left
if(eyes.eye_color_right)
eye_right.color = eyes.eye_color_right
if(eyes.overlay_ignore_lighting)
eye_left.overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, src, alpha = eye_left.alpha)
eye_right.overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, src, alpha = eye_right.alpha)
else if(blocks_emissive != EMISSIVE_BLOCK_NONE)
var/atom/location = loc || owner || src
eye_left.overlays += emissive_blocker(eye_left.icon, eye_left.icon_state, location, alpha = eye_left.alpha)
eye_right.overlays += emissive_blocker(eye_right.icon, eye_right.icon_state, location, alpha = eye_right.alpha)
if(worn_face_offset)
worn_face_offset.apply_offset(eye_left)
worn_face_offset.apply_offset(eye_right)
. += eye_left
. += eye_right
else if(!eyes && (head_flags & HEAD_EYEHOLES))
var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
worn_face_offset?.apply_offset(no_eyes)
. += no_eyes
if(!dropped)
return
var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src
// This is a bit of copy/paste code from eyes.dm:generate_body_overlay
if(eyes?.eye_icon_state && (head_flags & HEAD_EYESPRITES))
var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
var/image/eye_right = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
if(head_flags & HEAD_EYECOLOR)
if(eyes.eye_color_left)
eye_left.color = eyes.eye_color_left
if(eyes.eye_color_right)
eye_right.color = eyes.eye_color_right
return
var/list/emissive_overlays = eyes.get_emissive_overlays(eye_left, eye_right, src)
if(length(emissive_overlays))
eye_left.overlays += emissive_overlays[1] //left first
eye_right.overlays += emissive_overlays[2] //right second
else if(blocks_emissive != EMISSIVE_BLOCK_NONE)
var/atom/location = loc || owner || src
eye_left.overlays += emissive_blocker(eye_left.icon, eye_left.icon_state, location, alpha = eye_left.alpha)
eye_right.overlays += emissive_blocker(eye_right.icon, eye_right.icon_state, location, alpha = eye_right.alpha)
if(worn_face_offset)
worn_face_offset.apply_offset(eye_left)
worn_face_offset.apply_offset(eye_right)
. += eye_left
. += eye_right
else if(!eyes && (head_flags & HEAD_EYEHOLES))
var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
worn_face_offset?.apply_offset(no_eyes)
. += no_eyes
/obj/item/bodypart/head/Initialize(mapload)
. = ..()
+3 -1
View File
@@ -45,8 +45,10 @@
span_notice("[user] completes the surgery on [target]'s ears."),
)
display_pain(target, "Your head swims, but it seems like you can feel your hearing coming back!")
target_ears.deaf = (20) //deafness works off ticks, so this should work out to about 30-40s
target_ears.set_organ_damage(0)
///makes you temporarily deaf for a duration post-surgery
var/deaf_change = 40 SECONDS - target_ears.temporary_deafness
target_ears.adjust_temporary_deafness(deaf_change)
return ..()
/datum/surgery_step/fix_ears/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+61 -23
View File
@@ -117,7 +117,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/// Add a Trait to an organ that it will give its owner.
/obj/item/organ/proc/add_organ_trait(trait)
LAZYADD(organ_traits, trait)
LAZYOR(organ_traits, trait)
if(isnull(owner))
return
ADD_TRAIT(owner, trait, REF(src))
@@ -251,14 +251,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
damage = clamp(damage + damage_amount, 0, maximum)
SEND_SIGNAL(src, COMSIG_ORGAN_ADJUST_DAMAGE, damage_amount, maximum, required_organ_flag)
. = (prev_damage - damage) // return net damage
var/message = check_damage_thresholds(owner)
var/message = check_damage_thresholds()
prev_damage = damage
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
else
organ_flags &= ~ORGAN_FAILING
if(message && owner && owner.stat <= SOFT_CRIT)
to_chat(owner, message)
@@ -272,24 +267,66 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
* description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold.
* If we have, send the corresponding threshold message to the owner, if such a message exists.
*/
/obj/item/organ/proc/check_damage_thresholds(mob/organ_owner)
/obj/item/organ/proc/check_damage_thresholds()
SHOULD_CALL_PARENT(TRUE)
if(damage == prev_damage)
return
var/delta = damage - prev_damage
var/message = ""
if(delta > 0)
if(damage >= maxHealth)
return now_failing
if(damage > high_threshold && prev_damage <= high_threshold)
return high_threshold_passed
if(damage > low_threshold && prev_damage <= low_threshold)
return low_threshold_passed
else
if(prev_damage > low_threshold && damage <= low_threshold)
return low_threshold_cleared
if(prev_damage > high_threshold && damage <= high_threshold)
return high_threshold_cleared
if(prev_damage == maxHealth)
return now_fixed
on_low_damage_received()
message = low_threshold_passed
if(damage > high_threshold && prev_damage <= high_threshold)
on_high_damage_received()
message = high_threshold_passed
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
on_begin_failure()
message = now_failing
return message
if(prev_damage == maxHealth)
organ_flags &= ~ORGAN_FAILING
on_failure_recovery()
message = now_fixed
if(prev_damage > high_threshold && damage <= high_threshold)
on_high_damage_healed()
message = high_threshold_cleared
if(prev_damage > low_threshold && damage <= low_threshold)
on_low_damage_healed()
message = low_threshold_cleared
return message
/**
* Called when the damage surpasses the low damage threshold.
*
* This and other procs like this one merely exist to make it easier to keep a standard on
* damage thresholds for organs. This doesn't mean you cannot make custom thresholds for various stuff,
* and you're more than welcome to improve or refactor any portion of the code around these mechanics
*/
/obj/item/organ/proc/on_low_damage_received()
return
///Called when the damage goes below the low damage threshold
/obj/item/organ/proc/on_low_damage_healed()
return
///Called when the damage surpasses the high damage threshold
/obj/item/organ/proc/on_high_damage_received()
return
///Called when the damage goes below the high damage threshold
/obj/item/organ/proc/on_high_damage_healed()
return
///Called when the organ enters failing stage
/obj/item/organ/proc/on_begin_failure()
return
///Called when the organ recovers from failing stage
/obj/item/organ/proc/on_failure_recovery()
return
//Looking for brains?
//Try code/modules/mob/living/carbon/brain/brain_item.dm
@@ -313,9 +350,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
dna.species.regenerate_organs(src, replace_current = FALSE)
set_heartattack(FALSE)
// Ears have aditional vаr "deaf", need to update it too
// Ears have aditional var "deaf", need to update it too
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(0, -INFINITY) // full heal ears deafness
ears.adjust_temporary_deafness(-INFINITY)
return
@@ -352,7 +389,8 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
if(!ears)
ears = new()
ears.Insert(src)
ears.adjustEarDamage(-INFINITY, -INFINITY) // actually do: set_organ_damage(0) and deaf = 0
ears.set_organ_damage(0)
ears.adjust_temporary_deafness(-INFINITY)
///Organs don't die instantly, and neither should you when you get fucked up
/obj/item/organ/proc/handle_failing_organs(seconds_per_tick)
@@ -18,32 +18,36 @@
/// Eyecolor from the HUD
var/hud_color = "#3CB8A5"
/obj/item/organ/cyberimp/eyes/hud/Initialize(mapload)
. = ..()
if(toggled_on)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
/obj/item/organ/cyberimp/eyes/hud/proc/toggle_hud(mob/living/carbon/human/eye_owner)
if(toggled_on)
toggled_on = FALSE
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
remove_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud disabled")
if(hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY)
return
toggled_on = TRUE
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud enabled")
if(hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY)
/obj/item/organ/cyberimp/eyes/hud/on_mob_insert(mob/living/carbon/human/eye_owner, special = FALSE, movement_flags)
. = ..()
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = TRUE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/on_mob_remove(mob/living/carbon/human/eye_owner, special, movement_flags)
. = ..()
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = FALSE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/medical
@@ -14,8 +14,8 @@
now_fixed = span_info("Noise slowly begins filling your ears once more.")
low_threshold_cleared = span_info("The ringing in your ears has died down.")
/// `deaf` measures "ticks" of deafness. While > 0, the person is unable to hear anything.
var/deaf = 0
/// temporary deafness, measured in seconds. While > 0, the person is unable to hear anything.
var/temporary_deafness = 0
// `damage` in this case measures long term damage to the ears, if too high,
// the person will not have either `deaf` or `ear_damage` decrease
@@ -39,23 +39,20 @@
// no healing if failing
if(organ_flags & ORGAN_FAILING)
return
adjustEarDamage(0, -0.5 * seconds_per_tick)
adjust_temporary_deafness(-seconds_per_tick SECONDS)
if((damage > low_threshold) && SPT_PROB(damage / 60, seconds_per_tick))
adjustEarDamage(0, 4)
adjust_temporary_deafness(4 SECONDS)
SEND_SOUND(owner, sound('sound/items/weapons/flash_ring.ogg'))
/obj/item/organ/ears/apply_organ_damage(damage_amount, maximum, required_organ_flag)
. = ..()
update_temp_deafness()
/obj/item/organ/ears/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
update_temp_deafness()
if(temporary_deafness)
on_deafened()
/obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
if(temporary_deafness)
on_undeafened(organ_owner)
/obj/item/organ/ears/get_status_appendix(advanced, add_tooltips)
if(owner.stat == DEAD || !HAS_TRAIT(owner, TRAIT_DEAF))
@@ -65,7 +62,7 @@
return conditional_tooltip("Subject is permanently deaf.", "Irreparable under normal circumstances.", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, GENETIC_MUTATION))
return conditional_tooltip("Subject is genetically deaf.", "Use medication such as [/datum/reagent/medicine/mutadone::name].", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
if((organ_flags & ORGAN_FAILING) || HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
return conditional_tooltip("Subject is [(organ_flags & ORGAN_FAILING) ? "permanently": "temporarily"] deaf from ear damage.", "Repair surgically, use medication such as [/datum/reagent/medicine/inacusiate::name], or protect ears with earmuffs.", add_tooltips)
return "Subject is deaf."
@@ -73,52 +70,43 @@
// Always show if we have an appendix
return ..() || (owner.stat != DEAD && HAS_TRAIT(owner, TRAIT_DEAF))
/**
* Snowflake proc to handle temporary deafness
*
* * ddmg: Handles normal organ damage
* * ddeaf: Handles temporary deafness, 1 ddeaf = 2 seconds of deafness, by default (with no multiplier)
*/
/obj/item/organ/ears/proc/adjustEarDamage(ddmg = 0, ddeaf = 0)
if(HAS_TRAIT(owner, TRAIT_GODMODE))
update_temp_deafness()
///Adjust the temporary deafness of the person, up or down
/obj/item/organ/ears/proc/adjust_temporary_deafness(amount)
// organ failure makes us permanently deafened. Also, doesn't do anything if not in someone or during godmode
if(amount <= 0 || (owner && HAS_TRAIT(owner, TRAIT_GODMODE)))
return
var/mod_damage = ddmg > 0 ? (ddmg * damage_multiplier) : ddmg
if(mod_damage)
apply_organ_damage(mod_damage)
var/mod_deaf = ddeaf > 0 ? (ddeaf * damage_multiplier) : ddeaf
if(mod_deaf)
deaf = max(deaf + mod_deaf, 0)
update_temp_deafness()
temporary_deafness += max(amount * damage_multiplier, 0)
/// Updates status of deafness
/obj/item/organ/ears/proc/update_temp_deafness()
// if we're failing we always have at least some deaf stacks (and thus deafness)
if(organ_flags & ORGAN_FAILING)
deaf = max(deaf, 1 * damage_multiplier)
if(isnull(owner))
if(!owner)
return
if(HAS_TRAIT(owner, TRAIT_GODMODE))
deaf = 0
if(temporary_deafness && !HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_deafened()
else if(!temporary_deafness && HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_undeafened()
if(deaf > 0)
if(!HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
else
REMOVE_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(owner, COMSIG_MOB_SAY)
///Called when temporary deafness begins
/obj/item/organ/ears/proc/on_deafened()
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
///Called when temporary deafness reaches zero. Has to have an 'organ_owner' arg, because by the time it's called on 'on_mob_remove', owner is already null
/obj/item/organ/ears/proc/on_undeafened(mob/living/organ_owner = owner)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
/obj/item/organ/ears/on_begin_failure()
add_organ_trait(TRAIT_DEAF)
/obj/item/organ/ears/on_failure_recovery()
remove_organ_trait(TRAIT_DEAF)
/// Being deafened by loud noises makes you shout
/obj/item/organ/ears/proc/adjust_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
if(HAS_TRAIT_NOT_FROM(source, TRAIT_DEAF, EAR_DAMAGE))
return
if(HAS_TRAIT(source, TRAIT_SIGN_LANG))
if(HAS_TRAIT_NOT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE) || HAS_TRAIT(owner, TRAIT_SIGN_LANG))
return
var/message = speech_args[SPEECH_MESSAGE]
@@ -240,16 +228,9 @@
desc = "Allows the user to more easily hear whispers. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
/obj/item/organ/ears/cybernetic/whisper/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/whisper/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
organ_traits = list(TRAIT_GOOD_HEARING)
/obj/item/organ/ears/cybernetic/volume
name = "volume-adjusting cybernetic ears"
@@ -265,14 +246,7 @@
desc = "Through the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
/obj/item/organ/ears/cybernetic/xray/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/xray/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
organ_traits = list(TRAIT_XRAY_HEARING)
/obj/item/organ/ears/cybernetic/emp_act(severity)
. = ..()
@@ -22,8 +22,6 @@
/// Sight flags this eye pair imparts on its user.
var/sight_flags = NONE
/// changes how the eyes overlay is applied, makes it apply over the lighting layer
var/overlay_ignore_lighting = FALSE
/// How much innate tint these eyes have
var/tint = 0
/// How much innare flash protection these eyes have, usually paired with tint
@@ -52,8 +50,6 @@
/// Glasses cannot be worn over these eyes. Currently unused
var/no_glasses = FALSE
/// indication that the eyes are undergoing some negative effect
var/damaged = FALSE
/// Native FOV that will be applied if a config is enabled
var/native_fov = FOV_90_DEGREES
/// Scarring on this organ
@@ -82,8 +78,13 @@
/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(organ_flags & ORGAN_FAILING)
receiver.become_blind(EYE_DAMAGE)
if(damage >= low_threshold)
receiver.assign_nearsightedness(EYE_DAMAGE, damage >= high_threshold ? 3 : 2, TRUE)
receiver.cure_blind(NO_EYES)
apply_damaged_eye_effects()
// Ensures that non-player mobs get their eye colors assigned, as players get them from prefs
if (ishuman(receiver))
var/mob/living/carbon/human/as_human = receiver
@@ -91,9 +92,17 @@
eye_color_left = as_human.eye_color_left
if (!eye_color_right)
eye_color_right = as_human.eye_color_right
RegisterSignals(receiver, list(
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
), PROC_REF(on_shiny_eyes_trait_update))
refresh(receiver, call_update = TRUE)
RegisterSignal(receiver, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act))
RegisterSignal(receiver, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(on_face_wash))
if (scarring)
apply_scarring_effects()
@@ -134,19 +143,37 @@
if(!special)
human_owner.update_body()
// become blind (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
// Cure blindness from eye damage
organ_owner.cure_blind(EYE_DAMAGE)
organ_owner.cure_nearsighted(EYE_DAMAGE)
// Eye blind and temp blind go to, even if this is a bit of cheesy way to clear blindness
organ_owner.remove_status_effect(/datum/status_effect/eye_blur)
organ_owner.remove_status_effect(/datum/status_effect/temporary_blindness)
// Then become blind anyways (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
if (scarring)
owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
owner.cure_blind(EYE_SCARRING_TRAIT)
organ_owner.update_tint()
organ_owner.update_sight()
UnregisterSignal(organ_owner, list(COMSIG_ATOM_BULLET_ACT, COMSIG_COMPONENT_CLEAN_FACE_ACT))
UnregisterSignal(organ_owner, list(
COMSIG_ATOM_BULLET_ACT,
COMSIG_COMPONENT_CLEAN_FACE_ACT,
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
))
///Called whenever the luminescent and/or reflective eyes traits are added or removed
/obj/item/organ/eyes/proc/on_shiny_eyes_trait_update(mob/living/carbon/human/source)
SIGNAL_HANDLER
source.update_eyes()
/obj/item/organ/eyes/update_atom_colour()
. = ..()
@@ -267,9 +294,8 @@
var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -EYES_LAYER, parent)
var/list/overlays = list(eye_left, eye_right)
if(overlay_ignore_lighting && !(parent.obscured_slots & HIDEEYES))
overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, -EYES_LAYER, alpha = eye_left.alpha)
overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, -EYES_LAYER, alpha = eye_right.alpha)
if(!(parent.obscured_slots & HIDEEYES))
overlays += get_emissive_overlays(eye_left, eye_right, parent)
var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD)
@@ -299,6 +325,19 @@
return overlays
///Returns the two emissive overlays built for the left and right eyes, in order.
/obj/item/organ/eyes/proc/get_emissive_overlays(mutable_appearance/eye_left, mutable_appearance/eye_right, atom/spokesman)
var/list/return_list = list()
var/emissive_effect
if((owner && HAS_TRAIT(owner, TRAIT_LUMINESCENT_EYES)) || (TRAIT_LUMINESCENT_EYES in organ_traits))
emissive_effect = EMISSIVE_BLOOM
else if((owner && HAS_TRAIT(owner, TRAIT_REFLECTIVE_EYES)) || (TRAIT_REFLECTIVE_EYES in organ_traits))
emissive_effect = EMISSIVE_SPECULAR
if(emissive_effect)
return_list += emissive_appearance(eye_left.icon, eye_left.icon_state, spokesman, -EYES_LAYER, alpha = eye_left.alpha, effect_type = emissive_effect)
return_list += emissive_appearance(eye_right.icon, eye_right.icon_state, spokesman, -EYES_LAYER, alpha = eye_right.alpha, effect_type = emissive_effect)
return return_list
/obj/item/organ/eyes/update_overlays()
. = ..()
if (scarring & RIGHT_EYE_SCAR)
@@ -358,13 +397,6 @@
owner.cure_blind(EYE_SCARRING_TRAIT)
owner.update_body()
/obj/item/organ/eyes/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
if (scarring)
eye_owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
eye_owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
eye_owner.cure_blind(EYE_SCARRING_TRAIT)
#undef OFFSET_X
#undef OFFSET_Y
@@ -374,38 +406,28 @@
eye_color_left = initial(eye_color_left)
eye_color_right = initial(eye_color_right)
/obj/item/organ/eyes/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_damaged_eye_effects()
/// Applies effects to our owner based on how damaged our eyes are
/obj/item/organ/eyes/proc/apply_damaged_eye_effects()
// we're in healthy threshold, either try to heal (if damaged) or do nothing
if(damage <= low_threshold)
if(damaged)
damaged = FALSE
// clear nearsightedness from damage
owner.cure_nearsighted(EYE_DAMAGE)
// and cure blindness from damage
owner.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_low_damage_received()
if(damage >= high_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
//various degrees of "oh fuck my eyes", from "point a laser at your eye" to "staring at the Sun" intensities
// 50 - blind
// 49-31 - nearsighted (2 severity)
// 30-20 - nearsighted (1 severity)
if(organ_flags & ORGAN_FAILING)
// become blind from damage
owner.become_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_received()
owner?.assign_nearsightedness(EYE_DAMAGE, 3, TRUE)
else
// become nearsighted from damage
var/severity = damage > high_threshold ? 3 : 2
owner.assign_nearsightedness(EYE_DAMAGE, severity, TRUE)
/obj/item/organ/eyes/on_begin_failure()
owner?.become_blind(EYE_DAMAGE)
damaged = TRUE
/obj/item/organ/eyes/on_failure_recovery()
owner?.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_healed()
if(damage <= low_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
/obj/item/organ/eyes/on_low_damage_healed()
// clear nearsightedness from damage
owner?.cure_nearsighted(EYE_DAMAGE)
/obj/item/organ/eyes/feel_for_damage(self_aware)
// Eye damage has visual effects, so we don't really need to "feel" it when self-examining
@@ -681,16 +703,9 @@
eye_color_left = "#3cb8a5"
eye_color_right = "#3cb8a5"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
organ_traits = list(TRAIT_XRAY_VISION)
penlight_message = "replaced by small radiation emitters and detectors"
/obj/item/organ/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/xray/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
REMOVE_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/thermals
name = "thermal eyes"
desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
@@ -1004,17 +1019,25 @@
if(QDELETED(eye_owner) || !ishuman(eye_owner)) //Other carbon mobs don't have eye color.
return
if(!eye.light_on)
eye_icon_state = initial(eye_icon_state)
overlay_ignore_lighting = FALSE
else
overlay_ignore_lighting = TRUE
eye_icon_state = base_eye_state
var/obj/item/bodypart/head/head = eye_owner.get_bodypart(BODY_ZONE_HEAD) //if we have eyes we definently have a head anyway
var/previous_flags = head.head_flags
head.head_flags = previous_flags | HEAD_EYECOLOR
eye_owner.dna.species.handle_body(eye_owner)
head.head_flags |= HEAD_EYECOLOR
///enabling and disabling the TRAIT_LUMINESCENT_EYES trait already calls handle_eyes(), in that case, let's skip that call
var/skip_call = FALSE
if(!eye.light_on)
eye_icon_state = initial(eye_icon_state)
skip_call = HAS_TRAIT_FROM_ONLY(eye_owner, TRAIT_LUMINESCENT_EYES, REF(src))
remove_organ_trait(TRAIT_LUMINESCENT_EYES)
else
skip_call = !HAS_TRAIT(eye_owner, TRAIT_LUMINESCENT_EYES)
add_organ_trait(TRAIT_LUMINESCENT_EYES)
eye_icon_state = base_eye_state
if(!skip_call && ishuman(eye_owner))
var/mob/living/carbon/human/humie = eye_owner
humie.update_eyes()
head.head_flags = previous_flags
#undef MATCH_LIGHT_COLOR
@@ -1140,7 +1163,7 @@
eye_color_right = "#f74a4d"
eye_icon_state = "eyes_glow"
iris_overlay = null
overlay_ignore_lighting = TRUE
organ_traits = list(TRAIT_UNNATURAL_RED_GLOWY_EYES, TRAIT_LUMINESCENT_EYES)
flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
low_light_cutoff = list(5, 12, 20)
medium_light_cutoff = list(15, 20, 30)
@@ -1153,10 +1176,6 @@
apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area
return span_danger("[owner.p_Their()] eyes [penlight_message].")
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick, times_fired)
if(!owner.is_blind() && isturf(owner.loc) && owner.has_light_nearby(light_amount=0.5)) //we allow a little more than usual so we can produce light from the adapted eyes
to_chat(owner, span_danger("Your eyes! They burn in the light!"))
@@ -1166,10 +1185,6 @@
apply_organ_damage(-10) //heal quickly
. = ..()
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE, movement_flags)
REMOVE_TRAIT(unadapted, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
return ..()
/obj/item/organ/eyes/pod
name = "pod eyes"
desc = "Strangest salad you've ever seen."
@@ -1179,3 +1194,9 @@
iris_overlay = null
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
penlight_message = "are green and plant-like"
/obj/item/organ/eyes/felinid
name = "felinid eyes"
desc = "A pair of highly reflective eyes with slit pupils, like those of a cat."
pupils_name = "slit pupils"
penlight_message = "shine under the pearly light"
@@ -8,6 +8,7 @@
attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues")
attack_verb_simple = list("lick", "slobber", "slap", "french", "tongue")
voice_filter = ""
organ_traits = list(TRAIT_SPEAKS_CLEARLY)
/**
* A cached list of paths of all the languages this tongue is capable of speaking
*
@@ -51,6 +52,8 @@
// - then we cache it via string list
// this results in tongues with identical possible languages sharing a cached list instance
languages_possible = string_list(get_possible_languages())
if(!sense_of_taste)
add_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/examine(mob/user)
. = ..()
@@ -137,40 +140,24 @@
* ageusia from having a non-tasting tongue.
*/
REMOVE_TRAIT(receiver, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
apply_tongue_effects()
/obj/item/organ/tongue/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
temp_say_mod = ""
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
REMOVE_TRAIT(organ_owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
// Carbons by default start with NO_TONGUE_TRAIT caused TRAIT_AGEUSIA
ADD_TRAIT(organ_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
organ_owner.voice_filter = initial(organ_owner.voice_filter)
/obj/item/organ/tongue/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_tongue_effects()
/obj/item/organ/tongue/on_begin_failure()
remove_organ_trait(TRAIT_SPEAKS_CLEARLY)
add_organ_trait(TRAIT_AGEUSIA)
/// Applies effects to our owner based on how damaged our tongue is
/obj/item/organ/tongue/proc/apply_tongue_effects()
/obj/item/organ/tongue/on_failure_recovery()
add_organ_trait(TRAIT_SPEAKS_CLEARLY)
if(sense_of_taste)
//tongues can't taste food when they are failing
if(organ_flags & ORGAN_FAILING)
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
REMOVE_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
//tongues can't taste food when they lack a sense of taste
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
if(organ_flags & ORGAN_FAILING)
REMOVE_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
else
ADD_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
remove_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/could_speak_language(datum/language/language_path)
return (language_path in languages_possible)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 989 B

@@ -273,9 +273,7 @@
to_chat(M, "<font color='red' size='7'>HONK</font>")
M.SetSleeping(0)
M.adjust_stutter(40 SECONDS)
var/obj/item/organ/ears/ears = M.get_organ_slot(ORGAN_SLOT_EARS)
if(ears)
ears.adjustEarDamage(0, 30)
M.sound_damage(deafen = 30 SECONDS)
M.Paralyze(60)
if(prob(30))
M.Stun(200)