From e4f533111f06995d57d4eb65887f3252f1353d3a Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:19:56 -0600 Subject: [PATCH] Deafness is now solely tracked by trait (#95029) ## About The Pull Request Deletes `can_hear`, replaces it with trait-checking deafness. The only two non-trait sources of deafness (hardcrit and lacking ears) were refactored into using the trait. ## Why It's Good For The Game Many places inconsistently check for the deaf trait rather than use can_hear which meant behavior was not consistent. Some code would treat "do we lack ears?" as being deaf, some would not. This unifies all the behavior so being deaf means you're deaf everywhere. It also means we can now easily react to gaining and losing deafness via signal, where before we could not react to it without hooking the trait, organ remove, AND stat change. Which no one did, of course, because who would ever think to do that? ## Changelog :cl: Melbert refactor: Refactored how deafness is tracked. Please report any weird interactions with sounds, like messages or sfx being missing. fix: Lacking ears and being in hard crit now consistently treats you as "being deaf". This affects a few minor interactions like empath, the jukebox, and sleeping. /:cl: --- code/__DEFINES/traits/sources.dm | 1 + code/__HELPERS/honkerblast.dm | 2 +- code/__HELPERS/priority_announce.dm | 2 +- code/controllers/subsystem/ambience.dm | 4 ++-- code/datums/brain_damage/hypnosis.dm | 2 +- code/datums/brain_damage/mild.dm | 2 +- code/datums/brain_damage/severe.dm | 2 +- code/datums/brain_damage/split_personality.dm | 2 +- code/datums/components/codeword_hearing.dm | 2 +- code/datums/components/fearful/sources/phobia.dm | 2 +- code/datums/emotes.dm | 2 +- code/datums/status_effects/buffs.dm | 2 +- code/datums/status_effects/debuffs/debuffs.dm | 2 +- code/datums/voice_of_god_command.dm | 2 +- code/game/machinery/wall_vitals.dm | 2 +- code/game/objects/items/clown_items.dm | 2 +- code/game/objects/items/devices/radio/radio.dm | 2 +- code/game/sound/sound.dm | 2 +- code/modules/client/preferences/sounds.dm | 5 +++++ code/modules/clothing/neck/_neck.dm | 2 +- code/modules/economy/account.dm | 6 +++--- code/modules/fishing/fish/types/rift.dm | 2 +- code/modules/hallucination/battle.dm | 2 +- code/modules/hallucination/fake_sound.dm | 2 +- code/modules/hallucination/station_message.dm | 2 +- code/modules/mob/living/carbon/carbon_defense.dm | 13 +------------ code/modules/mob/living/carbon/human/human.dm | 2 ++ code/modules/mob/living/carbon/init_signals.dm | 4 ++++ code/modules/mob/living/carbon/life.dm | 12 +++--------- code/modules/mob/living/living.dm | 9 ++++----- code/modules/mob/living/living_say.dm | 4 ++-- code/modules/mob/living/silicon/ai/ai_say.dm | 2 +- code/modules/mob/login.dm | 2 +- code/modules/mob/mob.dm | 4 ++-- code/modules/mob/mob_helpers.dm | 4 ---- .../impure_reagents/impure_medicine_reagents.dm | 6 +++--- .../chemistry/reagents/medicine_reagents.dm | 4 ++-- code/modules/surgery/organs/internal/ears/_ears.dm | 5 +++++ code/modules/unit_tests/spawn_humans.dm | 1 + .../vehicles/mecha/equipment/weapons/weapons.dm | 2 +- 40 files changed, 64 insertions(+), 68 deletions(-) diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 5973fb25111..e0cd34d8065 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -123,6 +123,7 @@ #define GENETICS_SPELL "genetics_spell" #define EYES_COVERED "eyes_covered" #define NO_EYES "no_eyes" +#define NO_EARS "no_ears" #define HYPNOCHAIR_TRAIT "hypnochair" #define FLASHLIGHT_EYES "flashlight_eyes" #define IMPURE_OCULINE "impure_oculine" diff --git a/code/__HELPERS/honkerblast.dm b/code/__HELPERS/honkerblast.dm index e76d8178228..c3926d421b0 100644 --- a/code/__HELPERS/honkerblast.dm +++ b/code/__HELPERS/honkerblast.dm @@ -8,7 +8,7 @@ playsound(origin_turf, 'sound/items/airhorn/airhorn.ogg', 100, TRUE) for(var/mob/living/carbon/victim in hearers(max(light_range, medium_range, heavy_range), origin_turf)) - if(!victim.can_hear()) + if(HAS_TRAIT(victim, TRAIT_DEAF)) continue var/distance = get_dist(origin_turf, victim.loc) if(distance <= heavy_range) diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm index bbb0d9db5ba..e1be4c7ad05 100644 --- a/code/__HELPERS/priority_announce.dm +++ b/code/__HELPERS/priority_announce.dm @@ -192,7 +192,7 @@ var/datum/callback/should_play_sound_callback = astype(should_play_sound) for(var/mob/target in players) - if(isnewplayer(target) || !target.can_hear()) + if(isnewplayer(target) || HAS_TRAIT(target, TRAIT_DEAF)) continue to_chat(target, announcement) diff --git a/code/controllers/subsystem/ambience.dm b/code/controllers/subsystem/ambience.dm index f49604b058a..88db3ffcc33 100644 --- a/code/controllers/subsystem/ambience.dm +++ b/code/controllers/subsystem/ambience.dm @@ -27,7 +27,7 @@ SUBSYSTEM_DEF(ambience) client_old_areas -= client_iterator continue - if(!client_mob.can_hear()) //WHAT? I CAN'T HEAR YOU + if(HAS_TRAIT(client_mob, TRAIT_DEAF)) //WHAT? I CAN'T HEAR YOU continue //Check to see if the client-mob is in a valid area @@ -126,7 +126,7 @@ SUBSYSTEM_DEF(ambience) client.current_ambient_sound = null return - if(!can_hear()) // Can the mob hear? + if(HAS_TRAIT(src, TRAIT_DEAF)) // Can the mob hear? SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = CHANNEL_BUZZ)) client.current_ambient_sound = null return diff --git a/code/datums/brain_damage/hypnosis.dm b/code/datums/brain_damage/hypnosis.dm index 05c98b0ec30..d789a7d142c 100644 --- a/code/datums/brain_damage/hypnosis.dm +++ b/code/datums/brain_damage/hypnosis.dm @@ -76,6 +76,6 @@ ) /datum/brain_trauma/hypnosis/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return hearing_args[HEARING_RAW_MESSAGE] = target_phrase.Replace(hearing_args[HEARING_RAW_MESSAGE], span_hypnophrase("$1")) diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 3db89c03880..993ddea4ebb 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -256,7 +256,7 @@ var/list/speak_dejavu = list() /datum/brain_trauma/mild/mind_echo/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return if(hear_dejavu.len >= 5) diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index 522bc2479c6..3e6f0e6691b 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -294,7 +294,7 @@ owner.remove_status_effect(/datum/status_effect/trance) /datum/brain_trauma/severe/hypnotic_trigger/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return var/regex/reg = new("(\\b[REGEX_QUOTE(trigger_phrase)]\\b)","ig") diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index 57c94e60b1d..3cedb77dcdb 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -235,7 +235,7 @@ return //no random switching /datum/brain_trauma/severe/split_personality/brainwashing/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return var/message = hearing_args[HEARING_RAW_MESSAGE] diff --git a/code/datums/components/codeword_hearing.dm b/code/datums/components/codeword_hearing.dm index 0b171d9492e..a8ac051ab1c 100644 --- a/code/datums/components/codeword_hearing.dm +++ b/code/datums/components/codeword_hearing.dm @@ -42,7 +42,7 @@ return // don't skip codewords when owner speaks - if(!owner.can_hear() || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return var/message = hearing_args[HEARING_RAW_MESSAGE] diff --git a/code/datums/components/fearful/sources/phobia.dm b/code/datums/components/fearful/sources/phobia.dm index e9303867769..155e949f298 100644 --- a/code/datums/components/fearful/sources/phobia.dm +++ b/code/datums/components/fearful/sources/phobia.dm @@ -101,7 +101,7 @@ return // Words can't trigger you if you can't hear them *taps head* - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return if(trigger_regex.Find(hearing_args[HEARING_RAW_MESSAGE])) diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 2d5dbf84ea6..bfda885aba9 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -134,7 +134,7 @@ if(isnull(viewer.client)) continue if(!is_important && viewer != user && (!is_visual || !is_audible)) - if(is_audible && !viewer.can_hear()) + if(is_audible && HAS_TRAIT(viewer, TRAIT_DEAF)) continue if(is_visual && viewer.is_blind()) continue diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index cad5efca459..50a2e229273 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -369,7 +369,7 @@ status_type = STATUS_EFFECT_REFRESH /datum/status_effect/good_music/tick(seconds_between_ticks) - if(owner.can_hear()) + if(!HAS_TRAIT(owner, TRAIT_DEAF)) owner.adjust_dizzy(-4 SECONDS) owner.adjust_jitter(-4 SECONDS) owner.adjust_confusion(-1 SECONDS) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 556a8e2e4dc..5d390a8035b 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -604,7 +604,7 @@ /datum/status_effect/trance/proc/hypnotize(datum/source, list/hearing_args) SIGNAL_HANDLER - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return var/mob/hearing_speaker = hearing_args[HEARING_SPEAKER] diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm index 1b701432076..a49c7cb6c34 100644 --- a/code/datums/voice_of_god_command.dm +++ b/code/datums/voice_of_god_command.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands()) var/to_remove_string var/list/candidates = get_hearers_in_view(8, user) - (include_speaker ? null : user) for(var/mob/living/candidate in candidates) - if(candidate.stat != DEAD && candidate.can_hear()) + if(candidate.stat != DEAD && !HAS_TRAIT(candidate, TRAIT_DEAF)) if(candidate.can_block_magic(MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND, charge_cost = 0)) to_chat(user, span_userdanger("Something's wrong! [candidate] seems to be resisting your commands.")) continue diff --git a/code/game/machinery/wall_vitals.dm b/code/game/machinery/wall_vitals.dm index c175fc98f48..51790a1e7bc 100644 --- a/code/game/machinery/wall_vitals.dm +++ b/code/game/machinery/wall_vitals.dm @@ -453,7 +453,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/vitals_reader/advanced, 32) /obj/machinery/vitals_reader/proc/beep_message(message) for(var/mob/viewer as anything in viewers(src)) - if(isnull(viewer.client) || !viewer.can_hear()) + if(isnull(viewer.client) || HAS_TRAIT(viewer, TRAIT_DEAF)) continue if(!viewer.runechat_prefs_check(viewer, EMOTE_MESSAGE)) continue diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index c08b1c3fcd2..9b2537e205b 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -266,7 +266,7 @@ return var/turf/T = get_turf(src) for(M in ohearers(7, T)) - if(M.can_hear()) + if(!HAS_TRAIT(M, TRAIT_DEAF)) M.emote("flip") COOLDOWN_START(src, golden_horn_cooldown, 1 SECONDS) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index c7c9c94f8e3..9eb377ca8f9 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -369,7 +369,7 @@ if(isliving(talking_movable)) var/mob/living/talking_living = talking_movable var/volume_modifier = (talking_living.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_radio_noise)) - if(radio_noise && talking_living.can_hear() && volume_modifier && signal.frequency != FREQ_COMMON && !LAZYACCESS(message_mods, MODE_SEQUENTIAL) && COOLDOWN_FINISHED(src, audio_cooldown)) + if(radio_noise && !HAS_TRAIT(talking_living, TRAIT_DEAF) && volume_modifier && signal.frequency != FREQ_COMMON && !LAZYACCESS(message_mods, MODE_SEQUENTIAL) && COOLDOWN_FINISHED(src, audio_cooldown)) COOLDOWN_START(src, audio_cooldown, 0.5 SECONDS) var/sound/radio_noise = sound('sound/items/radio/radio_talk.ogg', volume = volume_modifier) radio_noise.frequency = get_rand_frequency_low_range() diff --git a/code/game/sound/sound.dm b/code/game/sound/sound.dm index a3d086eac3a..451ee6d60a4 100644 --- a/code/game/sound/sound.dm +++ b/code/game/sound/sound.dm @@ -98,7 +98,7 @@ * * volume_preference - Optional: Will be checked to modify the volume of the sound. */ /mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/sound_to_use, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE, datum/preference/numeric/volume/volume_preference = null) - if(!client || !can_hear()) + if(!client || HAS_TRAIT(src, TRAIT_DEAF)) return if(!sound_to_use) diff --git a/code/modules/client/preferences/sounds.dm b/code/modules/client/preferences/sounds.dm index 3fa9c16ca34..e6434afdd32 100644 --- a/code/modules/client/preferences/sounds.dm +++ b/code/modules/client/preferences/sounds.dm @@ -20,6 +20,11 @@ savefile_key = "sound_breathing" savefile_identifier = PREFERENCE_PLAYER +/datum/preference/toggle/sound_breathing/apply_to_client_updated(client/client, value) + var/mob/living/carbon/carbon_mob = client.mob + if(istype(carbon_mob) && !value) + carbon_mob.breathing_loop.stop() + /// Controls hearing announcement sounds /datum/preference/toggle/sound_announcements category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 49f5a7412b7..7d6df307725 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -270,7 +270,7 @@ switch (body_part) if(BODY_ZONE_CHEST)//Listening to the chest user.visible_message(span_notice("[user] places [src] against [carbon_patient]'s [body_part] and listens attentively."), ignored_mobs = user) - if(!user.can_hear()) + if(HAS_TRAIT(user, TRAIT_DEAF)) to_chat(user, span_notice("You place [src] against [carbon_patient]'s [body_part]. Fat load of good it does you though, since you can't hear.")) return else diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm index 797dd9d3856..5c0f8b3fe9d 100644 --- a/code/modules/economy/account.dm +++ b/code/modules/economy/account.dm @@ -244,7 +244,7 @@ if(!card_holder.client || (!(get_chat_toggles(card_holder.client) & CHAT_BANKCARD) && !force)) return - if(card_holder.can_hear()) + if(!HAS_TRAIT(card_holder, TRAIT_DEAF)) card_holder.playsound_local(get_turf(card_holder), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(card_holder, "[icon2html(icon_source, card_holder)] [span_notice("[message]")]") else if(isturf(card.loc)) //If on the ground @@ -252,7 +252,7 @@ for(var/mob/potential_hearer in hearers(1,card_location)) if(!potential_hearer.client || (!(get_chat_toggles(potential_hearer.client) & CHAT_BANKCARD) && !force)) continue - if(potential_hearer.can_hear()) + if(!HAS_TRAIT(potential_hearer, TRAIT_DEAF)) potential_hearer.playsound_local(card_location, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]") else @@ -262,7 +262,7 @@ continue if(!sound_atom) sound_atom = card.drop_location() //in case we're inside a bodybag in a crate or something. doing this here to only process it if there's a valid mob who can hear the sound. - if(potential_hearer.can_hear()) + if(!HAS_TRAIT(potential_hearer, TRAIT_DEAF)) potential_hearer.playsound_local(get_turf(sound_atom), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]") diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm index 641336f0dbe..626be1ecde0 100644 --- a/code/modules/fishing/fish/types/rift.dm +++ b/code/modules/fishing/fish/types/rift.dm @@ -636,7 +636,7 @@ to_chat(screeched, span_notice("You resist the psychic wail!")) continue var/power = 1 - if(!screeched.can_hear()) // bit weaker if deaf. but its still psychic + if(HAS_TRAIT(screeched, TRAIT_DEAF)) // bit weaker if deaf. but its still psychic power *= 0.5 var/affect_time = 15 SECONDS * power // it really fucks you up diff --git a/code/modules/hallucination/battle.dm b/code/modules/hallucination/battle.dm index 985709b90d0..60e2555e387 100644 --- a/code/modules/hallucination/battle.dm +++ b/code/modules/hallucination/battle.dm @@ -5,7 +5,7 @@ hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/battle/start() - if(!hallucinator.can_hear()) + if(HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE // for subtypes diff --git a/code/modules/hallucination/fake_sound.dm b/code/modules/hallucination/fake_sound.dm index 7b2e2ee8825..b7809548630 100644 --- a/code/modules/hallucination/fake_sound.dm +++ b/code/modules/hallucination/fake_sound.dm @@ -11,7 +11,7 @@ var/sound_type /datum/hallucination/fake_sound/start() - if(!hallucinator.can_hear()) + if(HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE var/sound_to_play = islist(sound_type) ? pick(sound_type) : sound_type diff --git a/code/modules/hallucination/station_message.dm b/code/modules/hallucination/station_message.dm index a8bddedcdad..33f20cb1014 100644 --- a/code/modules/hallucination/station_message.dm +++ b/code/modules/hallucination/station_message.dm @@ -8,7 +8,7 @@ var/require_hearing = TRUE /datum/hallucination/station_message/start() - if(require_hearing && !hallucinator.can_hear()) + if(require_hearing && HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE if(do_fake_alert() == CANCEL_FAKE_ALERT) return FALSE diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 5dac58d3123..5dc3cbd7d70 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -30,9 +30,7 @@ /mob/living/carbon/get_ear_protection(ignore_deafness = FALSE) var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) - if(!ears) - return INFINITY - return ..() + ears.bang_protect + return ..() + ears?.bang_protect /mob/living/carbon/is_mouth_covered(check_flags = ALL) if((check_flags & ITEM_SLOT_HEAD) && head && (head.flags_cover & HEADCOVERSMOUTH)) @@ -535,15 +533,6 @@ if(hit_clothes) hit_clothes.take_damage(damage_amount, damage_type, damage_flag, 0) -/mob/living/carbon/can_hear() - . = FALSE - var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) - if(ears && !HAS_TRAIT(src, TRAIT_DEAF)) - . = TRUE - if(health <= hardcrit_threshold && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)) - . = FALSE - - /mob/living/carbon/adjust_oxy_loss(amount, updating_health = TRUE, forced, required_biotype, required_respiration_type) if(!forced && HAS_TRAIT(src, TRAIT_NOBREATH)) amount = min(amount, 0) //Prevents oxy damage but not healing diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index f527b70d6bd..32c7dfd139e 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -55,6 +55,8 @@ /mob/living/carbon/human/proc/setup_organless_effects() // All start without eyes, and get them via set species become_blind(NO_EYES) + // And no ears, and get them via set species + ADD_TRAIT(src, TRAIT_DEAF, NO_EARS) // Mobs cannot taste anything without a tongue; the tongue organ removes this on Insert ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index a46acd8c520..da1cf59de61 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -141,3 +141,7 @@ SIGNAL_HANDLER cure_trauma_type(/datum/brain_trauma/severe/split_personality, TRAUMA_LIMIT_ABSOLUTE) + +/mob/living/carbon/on_hearing_loss(datum/source) + . = ..() + breathing_loop.stop() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 4076cf77c86..91ab5cc761d 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -119,19 +119,13 @@ loc_as_obj.handle_internal_lifeform(src,0) if(check_breath(breath) && is_on_internals) - try_breathing_sound(breath) + // successful breath from internals, try to play the breathing sound + if(!HAS_TRAIT(src, TRAIT_DEAF) && client?.prefs?.read_preference(/datum/preference/toggle/sound_breathing)) + breathing_loop.start() if(breath) loc.assume_air(breath) -//Tries to play the carbon a breathing sound when using internals, also invokes check_breath -/mob/living/carbon/proc/try_breathing_sound(breath) - var/should_be_on = canon_client?.prefs?.read_preference(/datum/preference/toggle/sound_breathing) - if(should_be_on && !breathing_loop.timer_id && canon_client?.mob.can_hear()) - breathing_loop.start() - else if((!should_be_on && breathing_loop.timer_id) || !canon_client?.mob.can_hear()) - breathing_loop.stop() - /mob/living/carbon/proc/has_smoke_protection() if(HAS_TRAIT(src, TRAIT_NOBREATH)) return TRUE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5f0374d08ea..93fbabee070 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2418,7 +2418,9 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(HARD_CRIT) if(stat != UNCONSCIOUS) cure_blind(UNCONSCIOUS_TRAIT) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) if(DEAD) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) remove_from_dead_mob_list() add_to_alive_mob_list() switch(stat) //Current stat. @@ -2446,17 +2448,14 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(. != UNCONSCIOUS) become_blind(UNCONSCIOUS_TRAIT) ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) log_combat(src, src, "entered hard crit") if(DEAD) REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) remove_from_alive_mob_list() add_to_dead_mob_list() log_combat(src, src, "died") - if(!can_hear()) - stop_sound_channel(CHANNEL_AMBIENCE) - refresh_looping_ambience() - - ///Reports the event of the change in value of the buckled variable. /mob/living/proc/set_buckled(new_buckled) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 28db8499a66..3bd7fcc8221 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -306,7 +306,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( // But we can still see them speak if(speaker_is_signing) deaf_message = "[span_name("[speaker]")] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar." - else if(can_hear()) // If we can't hear we want to continue to the default deaf message + else if(!HAS_TRAIT(src, TRAIT_DEAF)) // If we can't hear we want to continue to the default deaf message if(isliving(speaker)) var/mob/living/living_speaker = speaker var/mouth_hidden = living_speaker.is_mouth_covered() || HAS_TRAIT(living_speaker, TRAIT_FACE_COVERED) @@ -360,7 +360,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( deaf_type = MSG_AUDIBLE // Since you should be able to hear yourself without looking // Create map text prior to modifying message for goonchat - if (use_runechat && can_hear()) + if (use_runechat && !HAS_TRAIT(src, TRAIT_DEAF)) if (message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) create_chat_message(speaker, null, message_mods[MODE_CUSTOM_SAY_EMOTE], spans, EMOTE_MESSAGE) else diff --git a/code/modules/mob/living/silicon/ai/ai_say.dm b/code/modules/mob/living/silicon/ai/ai_say.dm index 26f2cc2f64a..88abc47ab37 100644 --- a/code/modules/mob/living/silicon/ai/ai_say.dm +++ b/code/modules/mob/living/silicon/ai/ai_say.dm @@ -171,7 +171,7 @@ // Play voice for all mobs in the z level for(var/mob/player_mob as anything in GLOB.player_list) var/pref_volume = safe_read_pref(player_mob.client, /datum/preference/numeric/volume/sound_ai_vox) - if(!player_mob.can_hear() || !pref_volume) + if(HAS_TRAIT(player_mob, TRAIT_DEAF) || !pref_volume) continue var/turf/player_turf = get_turf(player_mob) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index d3217bb736f..691a36a8b65 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -100,7 +100,7 @@ update_mouse_pointer() update_ambience_area(get_area(src)) - if(!can_hear()) + if(HAS_TRAIT(src, TRAIT_DEAF)) stop_sound_channel(CHANNEL_AMBIENCE) if(client) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 3796d8711d1..2b3759948c1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -243,7 +243,7 @@ type = alt_type . = FALSE - if(type & MSG_AUDIBLE && !can_hear())//Hearing related + if(type & MSG_AUDIBLE && HAS_TRAIT(src, TRAIT_DEAF))//Hearing related if(!alt_msg) return FALSE else @@ -369,7 +369,7 @@ continue if(self_message && hearing_mob == src) continue - if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(hearing_mob, audible_message_flags) && hearing_mob.can_hear()) + if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(hearing_mob, audible_message_flags) && !HAS_TRAIT(hearing_mob, TRAIT_DEAF)) hearing_mob.create_chat_message(src, raw_message = raw_msg, runechat_flags = audible_message_flags) hearing_mob.show_message(message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index d48bbd9ac23..02e382c7117 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -403,10 +403,6 @@ var/mob/living/T = pick(nearby_mobs) ClickOn(T) -///Can the mob hear -/mob/proc/can_hear() - return !HAS_TRAIT(src, TRAIT_DEAF) - /** * Get the list of keywords for policy config * diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm index 76caad943ff..13f14d3af2c 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm @@ -767,18 +767,18 @@ Basically, we fill the time between now and 2s from now with hands based off the . = ..() random_span = pick("clown", "small", "big", "hypnophrase", "alien", "cult", "alert", "danger", "emote", "yell", "brass", "sans", "papyrus", "robot", "his_grace", "phobia") RegisterSignal(affected_mob, COMSIG_MOVABLE_HEAR, PROC_REF(owner_hear)) - to_chat(affected_mob, span_warning("Your hearing seems to be a bit off[affected_mob.can_hear() ? "!" : " - wait, that's normal."]")) + to_chat(affected_mob, span_warning("Your hearing seems to be a bit off[!HAS_TRAIT(affected_mob, TRAIT_DEAF) ? "!" : " - wait, that's normal."]")) /datum/reagent/impurity/inacusiate/on_mob_end_metabolize(mob/living/affected_mob) . = ..() UnregisterSignal(affected_mob, COMSIG_MOVABLE_HEAR) - to_chat(affected_mob, span_notice("You start hearing things normally again[affected_mob.can_hear() ? "" : " - no, wait, no you don't"].")) + to_chat(affected_mob, span_notice("You start hearing things normally again[!HAS_TRAIT(affected_mob, TRAIT_DEAF) ? "" : " - no, wait, no you don't"].")) /datum/reagent/impurity/inacusiate/proc/owner_hear(mob/living/owner, list/hearing_args) SIGNAL_HANDLER // don't skip messages that the owner says or can't understand (since they still make sounds) - if(!owner.can_hear()) + if(HAS_TRAIT(owner, TRAIT_DEAF)) return // not technically hearing var/atom/movable/speaker = hearing_args[HEARING_SPEAKER] diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 0c57cf86b0e..57e21242b34 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -929,7 +929,7 @@ . = ..() if(creation_purity >= 1) ADD_TRAIT(affected_mob, TRAIT_GOOD_HEARING, type) - if(affected_mob.can_hear()) + if(!HAS_TRAIT(affected_mob, TRAIT_DEAF)) to_chat(affected_mob, span_nicegreen("You can feel your hearing drastically improve!")) /datum/reagent/medicine/inacusiate/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) @@ -945,7 +945,7 @@ /datum/reagent/medicine/inacusiate/on_mob_delete(mob/living/affected_mob) . = ..() REMOVE_TRAIT(affected_mob, TRAIT_GOOD_HEARING, type) - if(affected_mob.can_hear()) + if(!HAS_TRAIT(affected_mob, TRAIT_DEAF)) to_chat(affected_mob, span_notice("Your hearing returns to its normal acuity.")) /datum/reagent/medicine/atropine diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index fb086e8bd36..e1259f5c82d 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -48,11 +48,16 @@ . = ..() if(temporary_deafness) on_deafened() + REMOVE_TRAIT(organ_owner, TRAIT_DEAF, NO_EARS) /obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(temporary_deafness) on_undeafened(organ_owner) + // Do not apply with special flag, even if it would ultimately be redundant by new ears being hot-swapped in. + // This is so we don't trip signal_addtrait when hot-swapping ears, which could cause inappropriate behavior like nuking sound effects. + if(!special) + ADD_TRAIT(organ_owner, TRAIT_DEAF, NO_EARS) /obj/item/organ/ears/get_status_appendix(advanced, add_tooltips) if(owner.stat == DEAD || !HAS_TRAIT(owner, TRAIT_DEAF)) diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm index e90473a7ba0..f89f59a4cbf 100644 --- a/code/modules/unit_tests/spawn_humans.dm +++ b/code/modules/unit_tests/spawn_humans.dm @@ -13,3 +13,4 @@ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent) TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_AGEUSIA, NO_TONGUE_TRAIT), "Dummy has ageusia on init, when it should've been removed by its default tongue.") TEST_ASSERT(!dummy.is_blind_from(NO_EYES), "Dummy is blind on init, when it should've been removed by its default eyes.") + TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_DEAF, NO_EARS), "Dummy is deaf on init, when it should've been removed by its default ears.") diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm index 7e68aaf91cb..d85189ea8ea 100644 --- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm +++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm @@ -265,7 +265,7 @@ playsound(chassis, 'sound/items/airhorn/airhorn.ogg', 100, TRUE) to_chat(source, "[icon2html(src, source)]HONK") for(var/mob/living/carbon/M in ohearers(6, chassis)) - if(!M.can_hear()) + if(HAS_TRAIT(M, TRAIT_DEAF)) continue var/turf/turf_check = get_turf(M) if(isspaceturf(turf_check) && !turf_check.Adjacent(src)) //in space nobody can hear you honk.