From 922bd0cb9212932a58033841313f5a027bf2ca73 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Tue, 4 Nov 2025 21:22:33 +0100 Subject: [PATCH] Refactor get_ear_protection() and soundbang_act() to be less bs (#93472) so, the current `COMSIG_CARBON_SOUNDBANG` signal is kinda lame because: 1) it's relegated to carbon mobs 2) it means ear protection equipment actually has no effect when `get_ear_protection` is called instead. 3) It's sent in more than one place in the code So I replaced it with another signal, this one properly sent in `get_ear_protection`, and therefore `soundbang_act` as well. What's also lame is that the code for both `get_ear_protection` and `soundbang_act` can be easily moved to the living type, and it wouldn't make a huge impact aside causing simple mobs to be stunned very briefly by flashbangs since they cannot be knocked down anyway. The less the code is specific to a subtype, the easier it's to work with, the fewer the paradigms etc. Another lame thing is some of the code being kinda old. Also I wanted to fix an issue with the shriek ling ability detailed in #93401. I wanted to make the ability more consistent with how other sound-related abilities work by making it use `soundbang_act`, but with an intensity higher than any other feature so far, beside point-blank flashbangs (holding on standing on one). I've also buffed the "ear protection" offered by vacuums from 1 to 3, meaning nothing but point-blank flashbangs will go through. Yes, this means shrieks are not good in a vacuum because sound generally propagate in absence of molecules that can transmit the vibration... at least IRL. The game's an approximation anyway, but it's still inconsistent, like a fuckton of other things that are even more outside of the scope of this PR. Well, at least it affects simple/basic mobs too now. ## Why It's Good For The Game This will fix #93401 and make some code less old and bad. ## Changelog :cl: refactor: refactored code for ear/soundbang protection. Flashbangs and other things can now affect simple/basic mobs as well, not just carbon and occasionally silicons. balance: Aliens are no longer impervious to soundbang_act. Still very resistant to it due to their hardcoded ear protection balance: How the resonant shriek changeling ability works is now slightly more consistent with the ear/soundbang protection, meaning it won't work well in a vacuum, but at least it works on simple mobs now. fix: Everyone can now hear a changeling shriek, unless they're already deafened or in a vacuum. /:cl: --- .../signals/signals_mob/signals_mob_carbon.dm | 2 - .../signals/signals_mob/signals_mob_living.dm | 4 ++ code/__DEFINES/mobs.dm | 25 +++++++++- code/datums/components/earprotection.dm | 4 +- code/game/objects/items/grenades/flashbang.dm | 22 ++++----- code/game/objects/items/grenades/hypno.dm | 46 ++++++++---------- .../game/objects/items/robot/items/generic.dm | 24 +++++----- .../antagonists/changeling/powers/shriek.dm | 31 ++++++------ .../antagonists/malf_ai/malf_ai_modules.dm | 6 ++- code/modules/fishing/fish/types/rift.dm | 2 +- code/modules/mob/living/brain/brain.dm | 6 +-- .../mob/living/carbon/alien/alien_defense.dm | 10 ++-- .../mob/living/carbon/carbon_defense.dm | 44 ++--------------- code/modules/mob/living/living_defense.dm | 47 ++++++++++++++++--- .../mob/living/silicon/silicon_defense.dm | 4 +- code/modules/reagents/chemistry/recipes.dm | 4 +- .../chemistry/recipes/pyrotechnics.dm | 8 ++-- .../surgery/organs/internal/ears/_ears.dm | 4 +- code/modules/unit_tests/combat.dm | 12 +++++ 19 files changed, 165 insertions(+), 140 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index c8835caed3e..b80f546693d 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -60,8 +60,6 @@ /// Called from bodypart being removed /obj/item/bodypart/proc/drop_limb(mob/living/carbon/old_owner, special, dismembered) #define COMSIG_BODYPART_REMOVED "bodypart_removed" -///from base of mob/living/carbon/soundbang_act(): (list(intensity)) -#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" ///from /item/organ/proc/Insert() (/obj/item/organ/) #define COMSIG_CARBON_GAIN_ORGAN "carbon_gain_organ" ///from /item/organ/proc/Remove() (/obj/item/organ/) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index 40d0f1ae3bd..5d32a627b40 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -318,6 +318,10 @@ /// From /obj/machinery/gibber/startgibbing(): (mob/living/user, /obj/machinery/gibber, list/results) #define COMSIG_LIVING_GIBBER_ACT "living_gibber_act" +/// From /mob/living/get_eye_protection() (list/reflist) +#define COMSIG_LIVING_GET_EAR_PROTECTION "living_get_ear_protection" + #define EAR_PROTECTION_ARG 1 + /// Sent to the mob when their mind is slaved #define COMSIG_MOB_ENSLAVED_TO "mob_enslaved_to" /// From /obj/item/proc/attack_atom: (mob/living/attacker, atom/attacked, list/modifiers) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 7cdd886694d..10c87301114 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -479,7 +479,7 @@ #define REM REAGENTS_EFFECT_MULTIPLIER //! Shorthand for the above define for ease of use in equations and the like // Eye protection -// THese values are additive to determine your overall flash protection. +// These values are additive to determine your overall flash protection. #define FLASH_PROTECTION_HYPER_SENSITIVE -2 #define FLASH_PROTECTION_SENSITIVE -1 #define FLASH_PROTECTION_NONE 0 @@ -488,6 +488,29 @@ #define FLASH_PROTECTION_WELDER_SENSITIVE 3 #define FLASH_PROTECTION_WELDER_HYPER_SENSITIVE 4 +/** + * Ear protection + * These values are additive to determine your overall ear/soundbang protection + */ +#define EAR_PROTECTION_NONE 0 +#define EAR_PROTECTION_NORMAL 1 +#define EAR_PROTECTION_HEAVY 2 +#define EAR_PROTECTION_VACUUM 3 +#define EAR_PROTECTION_FULL INFINITY + +/** + * Soundbang defines + * These values are used as argument to determine the strength of the soundbang_act call + */ +///Soundbang strength for most things like flashbangs, honkblasts and harm control modules +#define SOUNDBANG_NORMAL 1 +///Soundbang strength for things like flashbangs in proximity and emagged harm alarm megaphones, cannot be countered by standard ear protection equipment +#define SOUNDBANG_STRONG 2 +///Soundbang strength for things like changeling shrieks, which can affect robots and aliens as well. +#define SOUNDBANG_MASSIVE 3 +///Soundbang strength for anything that cannot be stopped unless you're stacked on multiple effects and equipment to counter it (or are simply deaf) +#define SOUNDBANG_OVERWHELMING 4 + // AI Toggles #define AI_CAMERA_LUMINOSITY 5 #define AI_VOX // Comment out if you don't want VOX to be enabled and have players download the voice sounds. diff --git a/code/datums/components/earprotection.dm b/code/datums/components/earprotection.dm index 542a1d0c60f..382f3b1db2b 100644 --- a/code/datums/components/earprotection.dm +++ b/code/datums/components/earprotection.dm @@ -1,5 +1,5 @@ /datum/component/wearertargeting/earprotection - signals = list(COMSIG_CARBON_SOUNDBANG) + signals = list(COMSIG_LIVING_GET_EAR_PROTECTION) mobtype = /mob/living/carbon proctype = PROC_REF(reducebang) var/reduce_amount = 1 @@ -11,4 +11,4 @@ src.reduce_amount = reduce_amount /datum/component/wearertargeting/earprotection/proc/reducebang(datum/source, list/reflist) - reflist[1] -= reduce_amount + reflist[EAR_PROTECTION_ARG] -= reduce_amount diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm index 41d370d7bdd..cc320f6928e 100644 --- a/code/game/objects/items/grenades/flashbang.dm +++ b/code/game/objects/items/grenades/flashbang.dm @@ -46,7 +46,7 @@ if(living_mob.stat == DEAD) //They're dead! return living_mob.show_message(span_warning("BANG"), MSG_AUDIBLE) - var/distance = max(0, get_dist(get_turf(src), turf)) + var/distance = get_dist(get_turf(src), turf) var/sweetspot_range = clamp(CEILING(flashbang_range/sweetspot_divider, 1), 0, flashbang_range) //Flash @@ -61,19 +61,19 @@ living_mob.dropItemToGround(living_mob.get_inactive_held_item()) //Bang - if(!distance || loc == living_mob || loc == living_mob.loc) - living_mob.soundbang_act(2, 20 SECONDS, 10, 15) + if(!distance) + living_mob.soundbang_act(SOUNDBANG_OVERWHELMING, 20 SECONDS, 10, 15) return if(distance <= 1) // Adds more stun as to not prime n' pull (#45381) - living_mob.soundbang_act(2, 3 SECONDS, 5) + living_mob.soundbang_act(SOUNDBANG_STRONG, 3 SECONDS, 5) return if(distance <= sweetspot_range) - living_mob.soundbang_act(1, max(200 / max(1, distance), 60), rand(0, 5)) + living_mob.soundbang_act(SOUNDBANG_NORMAL, max(20 SECONDS / max(1, distance), 60), rand(0, 5)) return - if(!living_mob.soundbang_act(1, 0, rand(0, 2))) + if(!living_mob.soundbang_act(SOUNDBANG_NORMAL, 0, rand(0, 2))) return living_mob.adjust_staggered_up_to(max(200/max(1, distance), 5), 10 SECONDS) @@ -133,17 +133,17 @@ if(living_mob.stat == DEAD) //They're dead! return living_mob.show_message(span_warning("POP"), MSG_AUDIBLE) - var/distance = max(0, get_dist(get_turf(src), turf)) + var/distance = get_dist(get_turf(src), turf) //Flash if(living_mob.flash_act(affect_silicon = 1)) living_mob.Paralyze(max(10/max(1, distance), 5)) living_mob.Knockdown(max(100/max(1, distance), 60)) //Bang - if(!distance || loc == living_mob || loc == living_mob.loc) - living_mob.Paralyze(20) - living_mob.Knockdown(200) - living_mob.soundbang_act(1, 200, 10, 15) + if(!distance) + living_mob.Paralyze(2 SECONDS) + living_mob.Knockdown(20 SECONDS) + living_mob.soundbang_act(SOUNDBANG_NORMAL, 200, 10, 15) if(living_mob.apply_damages(brute = 10, burn = 10)) to_chat(living_mob, span_userdanger("The blast from \the [src] bruises and burns you!")) diff --git a/code/game/objects/items/grenades/hypno.dm b/code/game/objects/items/grenades/hypno.dm index d77049419ab..13bdf7351ab 100644 --- a/code/game/objects/items/grenades/hypno.dm +++ b/code/game/objects/items/grenades/hypno.dm @@ -32,46 +32,38 @@ /obj/item/grenade/hypnotic/proc/bang(turf/turf, mob/living/living_mob) if(living_mob.stat == DEAD) //They're dead! return - var/distance = max(0, get_dist(get_turf(src), turf)) + var/distance = get_dist(get_turf(src), turf) //Bang var/hypno_sound = FALSE //Hearing protection check - if(iscarbon(living_mob)) - var/mob/living/carbon/target = living_mob - var/list/reflist = list(1) - SEND_SIGNAL(target, COMSIG_CARBON_SOUNDBANG, reflist) - var/intensity = reflist[1] - var/ear_safety = target.get_ear_protection() - var/effect_amount = intensity - ear_safety - if(effect_amount > 0) - hypno_sound = TRUE + if(living_mob.get_ear_protection() < 0) + hypno_sound = TRUE - if(!distance || loc == living_mob || loc == living_mob.loc) - living_mob.Paralyze(10) - living_mob.Knockdown(100) + if(!distance) + living_mob.Paralyze(1 SECONDS) + living_mob.Knockdown(10 SECONDS) to_chat(living_mob, span_hypnophrase("The sound echoes in your brain...")) living_mob.adjust_hallucinations(150 SECONDS) else if(distance <= 1) - living_mob.Paralyze(5) - living_mob.Knockdown(30) + living_mob.Paralyze(0.5 SECONDS) + living_mob.Knockdown(3 SECONDS) if(hypno_sound) to_chat(living_mob, span_hypnophrase("The sound echoes in your brain...")) living_mob.adjust_hallucinations(150 SECONDS) //Flash - if(living_mob.flash_act(affect_silicon = 1)) - living_mob.Paralyze(max(10/max(1, distance), 5)) - living_mob.Knockdown(max(100/max(1, distance), 40)) - if(iscarbon(living_mob)) - var/mob/living/carbon/target = living_mob - if(target.hypnosis_vulnerable()) //The sound causes the necessary conditions unless the target has mindshield or hearing protection - target.apply_status_effect(/datum/status_effect/trance, 100, TRUE) - else - to_chat(target, span_hypnophrase("The light is so pretty...")) - target.adjust_drowsiness_up_to(20 SECONDS, 40 SECONDS) - target.adjust_confusion_up_to(10 SECONDS, 20 SECONDS) - target.adjust_dizzy_up_to(20 SECONDS, 40 SECONDS) + if(!living_mob.flash_act(affect_silicon = TRUE)) + return + living_mob.Paralyze(max(1 SECONDS / (distance || 1), 0.5 SECONDS)) + living_mob.Knockdown(max(10 SECONDS / (distance || 1), 4 SECONDS)) + if(living_mob.hypnosis_vulnerable()) //The sound causes the necessary conditions unless the target has mindshield or hearing protection + living_mob.apply_status_effect(/datum/status_effect/trance, 10 SECONDS, TRUE) + return + to_chat(living_mob, span_hypnophrase("The light is so pretty...")) + living_mob.adjust_drowsiness_up_to(20 SECONDS, 40 SECONDS) + living_mob.adjust_confusion_up_to(10 SECONDS, 20 SECONDS) + living_mob.adjust_dizzy_up_to(20 SECONDS, 40 SECONDS) diff --git a/code/game/objects/items/robot/items/generic.dm b/code/game/objects/items/robot/items/generic.dm index d282ae01b7f..d709be2067b 100644 --- a/code/game/objects/items/robot/items/generic.dm +++ b/code/game/objects/items/robot/items/generic.dm @@ -373,7 +373,7 @@ span_danger("The siren pierces your hearing!"), ) for(var/mob/living/carbon/carbon in get_hearers_in_view(9, user)) - if(carbon.get_ear_protection()) + if(carbon.get_ear_protection() > 0) continue carbon.adjust_confusion(6 SECONDS) @@ -386,18 +386,20 @@ to_chat(robot_user.connected_ai, "
[span_notice("NOTICE - Peacekeeping 'HARM ALARM' used by: [user]")]
") else user.audible_message("BZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZT") - for(var/mob/living/carbon/carbon in get_hearers_in_view(9, user)) - var/bang_effect = carbon.soundbang_act(2, 0, 0, 5) + for(var/mob/living/living in get_hearers_in_view(9, user)) + var/bang_effect = living.soundbang_act(SOUNDBANG_STRONG, 0, 0, 5) switch(bang_effect) + if(0) + continue if(1) - carbon.adjust_confusion(5 SECONDS) - carbon.adjust_stutter(20 SECONDS) - carbon.adjust_jitter(20 SECONDS) - if(2) - carbon.Paralyze(40) - carbon.adjust_confusion(10 SECONDS) - carbon.adjust_stutter(30 SECONDS) - carbon.adjust_jitter(50 SECONDS) + living.adjust_confusion(5 SECONDS) + living.adjust_stutter(20 SECONDS) + living.adjust_jitter(20 SECONDS) + else + living.Paralyze(4 SECONDS) + living.adjust_confusion(10 SECONDS) + living.adjust_stutter(30 SECONDS) + living.adjust_jitter(50 SECONDS) playsound(get_turf(src), 'sound/machines/warning-buzzer.ogg', 130, 3) COOLDOWN_START(src, alarm_cooldown, HARM_ALARM_NO_SAFETY_COOLDOWN) user.log_message("used an emagged Cyborg Harm Alarm", LOG_ATTACK) diff --git a/code/modules/antagonists/changeling/powers/shriek.dm b/code/modules/antagonists/changeling/powers/shriek.dm index 2a704e0baac..80ce0f232aa 100644 --- a/code/modules/antagonists/changeling/powers/shriek.dm +++ b/code/modules/antagonists/changeling/powers/shriek.dm @@ -1,6 +1,7 @@ /datum/action/changeling/resonant_shriek name = "Resonant Shriek" - desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses humans, causing them to lose some control over their movements. Best used to stop prey from escaping. Costs 20 chemicals." + desc = "Our lungs and vocal cords shift, allowing us to emit a noise that deafens and confuses non-changelings, causing them to lose some control over their movements. \ + Best used to stop prey from escaping. Doesn't work well in a vacuum. Costs 20 chemicals." helptext = "Emits a high-frequency sound that confuses and deafens humans to hamper their movement, blows out nearby lights and overloads cyborg sensors." button_icon_state = "resonant_shriek" chemical_cost = 20 @@ -14,23 +15,19 @@ if(user.movement_type & VENTCRAWLING) user.balloon_alert(user, "can't shriek in pipes!") return FALSE - for(var/mob/living/M in get_hearers_in_view(4, user)) - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(!IS_CHANGELING(C)) - C.sound_damage(deafen = 30 SECONDS) - C.adjust_confusion(25 SECONDS) - C.set_jitter_if_lower(100 SECONDS) - else - SEND_SOUND(C, sound('sound/effects/screech.ogg')) + playsound(user, 'sound/effects/screech.ogg', 100) + for(var/mob/living/living in get_hearers_in_view(4, user)) + if(IS_CHANGELING(living) || !living.soundbang_act(SOUNDBANG_MASSIVE, stun_pwr = 0, damage_pwr = 0, deafen_pwr = 1 MINUTES, ignore_deafness = TRUE, send_sound = FALSE)) + continue + if(issilicon(living)) + living.Paralyze(rand(10 SECONDS, 20 SECONDS)) + continue + living.adjust_confusion(25 SECONDS) + living.set_jitter_if_lower(100 SECONDS) - if(issilicon(M)) - SEND_SOUND(M, sound('sound/items/weapons/flash.ogg')) - M.Paralyze(rand(100,200)) - - for(var/obj/machinery/light/L in range(4, user)) - L.on = TRUE - L.break_light_tube() + for(var/obj/machinery/light/light in range(4, user)) + light.on = TRUE + light.break_light_tube() stoplag() return TRUE diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm index 9ebaf05b1bc..433d87fcbc3 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm @@ -630,11 +630,13 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf)) continue found_intercom.audible_message(message = "[found_intercom] crackles for a split second.", hearing_distance = 3) playsound(found_intercom, 'sound/items/airhorn/airhorn.ogg', 100, TRUE) - for(var/mob/living/carbon/honk_victim in ohearers(6, found_intercom)) + for(var/mob/living/honk_victim in ohearers(6, found_intercom)) + if(issilicon(honk_victim)) + continue var/turf/victim_turf = get_turf(honk_victim) if(isspaceturf(victim_turf) && !victim_turf.Adjacent(found_intercom)) //Prevents getting honked in space continue - if(honk_victim.soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 30, deafen_pwr = 60)) //Ear protection will prevent these effects + if(honk_victim.soundbang_act(SOUNDBANG_NORMAL, stun_pwr = 20, damage_pwr = 30, deafen_pwr = 60)) //Ear protection will prevent these effects honk_victim.set_jitter_if_lower(120 SECONDS) to_chat(honk_victim, span_clown("HOOOOONK!")) diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm index 87e83ccae9b..011a1736379 100644 --- a/code/modules/fishing/fish/types/rift.dm +++ b/code/modules/fishing/fish/types/rift.dm @@ -705,7 +705,7 @@ now_fixed = span_noticealien("The psychic noise starts to fade.") low_threshold_cleared = span_noticealien("The whispers leave you alone.") - bang_protect = 5 + bang_protect = EAR_PROTECTION_VACUUM damage_multiplier = 0.1 visual = TRUE /// Overlay for the mob sprite because actual organ overlays are a fucking unusable nightmare diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 54fbb132db2..fc8e60b63e0 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -58,10 +58,10 @@ return /mob/living/brain/get_eye_protection()//no eyes - return 2 + return FLASH_PROTECTION_WELDER -/mob/living/brain/get_ear_protection()//no ears - return 2 +/mob/living/brain/get_ear_protection(ignore_deafness = FALSE) + return ..() + EAR_PROTECTION_HEAVY /mob/living/brain/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) return // no eyes, no flashing diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index e0892cbaf8e..3d613d250b7 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -1,9 +1,9 @@ /mob/living/carbon/alien/get_eye_protection() - return ..() + 2 //potential cyber implants + natural eye protection + return ..() + FLASH_PROTECTION_WELDER //potential cyber implants + natural eye protection -/mob/living/carbon/alien/get_ear_protection() - return 2 //no ears +/mob/living/carbon/alien/get_ear_protection(ignore_deafness = FALSE) + return ..() + EAR_PROTECTION_HEAVY //no ears /mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) ..(AM, skipcatch = TRUE, hitpush = FALSE) @@ -87,10 +87,6 @@ In all, this is a lot like the monkey code. /N return TRUE - -/mob/living/carbon/alien/soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15) - return 0 - /mob/living/carbon/alien/acid_act(acidpwr, acid_volume) return FALSE//aliens are immune to acid. diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 24011e06e88..6ed5be63713 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -27,15 +27,11 @@ if(deafen) ears.adjust_temporary_deafness(deafen) -/mob/living/carbon/get_ear_protection() - . = ..() - if(HAS_TRAIT(src, TRAIT_DEAF)) - return INFINITY //For all my homies that can not hear in the world - var/obj/item/organ/ears/E = get_organ_slot(ORGAN_SLOT_EARS) - if(!E) +/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 - else - . += E.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)) @@ -517,38 +513,6 @@ else if(damage == 0 && prob(20)) // just enough protection to_chat(src, span_notice("Something bright flashes in the corner of your vision!")) - -/mob/living/carbon/soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15) - var/list/reflist = list(intensity) // Need to wrap this in a list so we can pass a reference - SEND_SIGNAL(src, COMSIG_CARBON_SOUNDBANG, reflist) - intensity = reflist[1] - var/ear_safety = get_ear_protection() - var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) - var/effect_amount = intensity - ear_safety - if(effect_amount <= 0) - return FALSE - - if(stun_pwr) - Paralyze((stun_pwr*effect_amount)*0.1) - Knockdown(stun_pwr*effect_amount) - - if(ears && (deafen_pwr || damage_pwr)) - var/ear_damage = damage_pwr * effect_amount - 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)) - - if(ears.damage < 5) - return - if(ears.damage >= 15 && prob(ears.damage - 5)) - to_chat(src, span_userdanger("You can't hear anything!")) - // Makes you deaf, enough that you need a proper source of healing, it won't self heal - // you need earmuffs, inacusiate, or replacement - ears.set_organ_damage(ears.maxHealth) - to_chat(src, span_warning("Your ears start to ring[ears.damage >= 15 ? " badly!":"!"]")) - /mob/living/carbon/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone) if(damage_type != BRUTE && damage_type != BURN) return diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 821ad98dd78..e1cb74d5095 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -40,13 +40,18 @@ 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() +/mob/living/proc/get_ear_protection(ignore_deafness = FALSE) + if(!ignore_deafness && HAS_TRAIT(src, TRAIT_DEAF)) + return INFINITY //For all my homies that can not hear in the world + var/list/sig_protection = list(0) + SEND_SIGNAL(src, COMSIG_LIVING_GET_EAR_PROTECTION, sig_protection) + var/protection = sig_protection[EAR_PROTECTION_ARG] var/turf/current_turf = get_turf(src) var/datum/gas_mixture/environment = current_turf.return_air() - var/pressure = environment ? environment.return_pressure() : 0 + var/pressure = environment?.return_pressure() if(pressure < SOUND_MINIMUM_PRESSURE) //space is empty - return 1 - return 0 + protection += EAR_PROTECTION_VACUUM + return protection /** * Checks if our mob has their mouth covered. @@ -654,8 +659,38 @@ return TRUE //called when the mob receives a loud bang -/mob/living/proc/soundbang_act() - return FALSE +/mob/living/proc/soundbang_act(intensity = SOUNDBANG_NORMAL, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15, ignore_deafness = FALSE, send_sound = TRUE) + var/protection = get_ear_protection(ignore_deafness) + if(protection >= intensity) + return FALSE + + ///The amplitude of the effect is reduced by sound protection, while weakness only makes it worse. + var/effect_amount = protection > 0 ? 1 - (protection/intensity) : 1 - protection + if(stun_pwr) + Paralyze(stun_pwr * effect_amount * 0.1) + Knockdown(stun_pwr * effect_amount) + + var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) + + . = effect_amount //how soundbanged we are + if(!ears || !(deafen_pwr || damage_pwr)) + return + + var/ear_damage = damage_pwr * effect_amount + var/deaf = deafen_pwr * effect_amount + sound_damage(ear_damage, deaf) + + if(send_sound) + SEND_SOUND(src, sound('sound/items/weapons/flash_ring.ogg',0, 1, 0, 250)) + + if(ears.damage >= 15 && prob(ears.damage - 5)) + to_chat(src, span_userdanger("You can't hear anything!")) + // Makes you deaf, enough that you need a proper source of healing, it won't self heal + // you need earmuffs, inacusiate, or replacement + ears.set_organ_damage(ears.maxHealth) + else if(ears.damage >= 5) + to_chat(src, span_warning("Your ears start to ring[ears.damage >= 15 ? " badly!":"!"]")) + //to damage the clothes worn by a mob /mob/living/proc/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone) diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index 25cdc81fda8..0a5e3191190 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -2,8 +2,8 @@ /mob/living/silicon/grippedby(mob/living/carbon/user, instant = FALSE) return //can't upgrade a simple pull into a more aggressive grab. -/mob/living/silicon/get_ear_protection()//no ears - return 2 +/mob/living/silicon/get_ear_protection(ignore_deafness = FALSE) + return ..() + EAR_PROTECTION_HEAVY //no ears /mob/living/silicon/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers) . = ..() diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index f1b3dfdbff2..9cd650d67ff 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -348,8 +348,8 @@ /datum/chemical_reaction/proc/explode_deafen(datum/reagents/holder, datum/equilibrium/equilibrium, power = 3, stun = 20, range = 2) var/location = get_turf(holder.my_atom) playsound(location, 'sound/effects/bang.ogg', 25, TRUE) - for(var/mob/living/carbon/carbon_mob in get_hearers_in_view(range, location)) - carbon_mob.soundbang_act(1, stun, power) + for(var/mob/living/living in get_hearers_in_view(range, location)) + living.soundbang_act(SOUNDBANG_NORMAL, stun, power) //Spews out the inverse of the chems in the beaker of the products/reactants only /datum/chemical_reaction/proc/explode_invert_smoke(datum/reagents/holder, datum/equilibrium/equilibrium, force_range = 0, clear_products = TRUE, clear_reactants = TRUE, accept_impure = TRUE) diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index a10a6902ccb..c891b012d56 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -414,8 +414,8 @@ holder.remove_reagent(/datum/reagent/sonic_powder, created_volume * 3) var/location = get_turf(holder.my_atom) playsound(location, 'sound/effects/bang.ogg', 25, TRUE) - for(var/mob/living/carbon/C in get_hearers_in_view(created_volume/3, location)) - C.soundbang_act(1, 100, rand(0, 5)) + for(var/mob/living/living in get_hearers_in_view(created_volume/3, location)) + living.soundbang_act(1, 10 SECONDS, rand(0, 5)) /datum/chemical_reaction/sonic_powder_deafen required_reagents = list(/datum/reagent/sonic_powder = 1) @@ -425,8 +425,8 @@ /datum/chemical_reaction/sonic_powder_deafen/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) var/location = get_turf(holder.my_atom) playsound(location, 'sound/effects/bang.ogg', 25, TRUE) - for(var/mob/living/carbon/C in get_hearers_in_view(created_volume/10, location)) - C.soundbang_act(1, 100, rand(0, 5)) + for(var/mob/living/living in get_hearers_in_view(created_volume/10, location)) + living.soundbang_act(1, 10 SECONDS, rand(0, 5)) /datum/chemical_reaction/phlogiston results = list(/datum/reagent/phlogiston = 3) diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index 10f2c5af0c1..83871e9d73b 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -22,7 +22,7 @@ // without external aid (earmuffs, drugs) /// Resistance against loud noises - var/bang_protect = 0 + var/bang_protect = EAR_PROTECTION_NONE /// Multiplier for both long term and short term ear damage var/damage_multiplier = 1 @@ -276,7 +276,7 @@ name = "volume-adjusting cybernetic ears" icon_state = "ears-c-u" desc = "Advanced cybernetic ears capable of dampening loud noises to protect their user." - bang_protect = 1 + bang_protect = EAR_PROTECTION_NORMAL damage_multiplier = 0.5 // "X-ray ears" that let you hear through walls diff --git a/code/modules/unit_tests/combat.dm b/code/modules/unit_tests/combat.dm index d645df98cbf..4ce7b5abdd6 100644 --- a/code/modules/unit_tests/combat.dm +++ b/code/modules/unit_tests/combat.dm @@ -138,3 +138,15 @@ attacker.set_species(/datum/species/monkey) attacker.ClickOn(victim) TEST_ASSERT_NOTEQUAL(victim.getBruteLoss(), 0, "Victim took no brute damage from being bit by a handcuffed monkey, which is incorrect, as it's a bite attack") + +/// Tests that soundbang_act (and therefore sound_damage) works correctly +/datum/unit_test/soundbang + +/datum/unit_test/soundbang/Run() + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human/consistent) + victim.soundbang_act(intensity = SOUNDBANG_NORMAL, damage_pwr = 10, deafen_pwr = 20 SECONDS) + TEST_ASSERT_EQUAL(victim.get_organ_loss(ORGAN_SLOT_EARS), 10, "victim didn't take the right amount of ears damage") + TEST_ASSERT(HAS_TRAIT_FROM(victim, TRAIT_DEAF, EAR_DAMAGE), "victim wasn't temporarily deafened") + var/obj/item/organ/ears/ears = victim.get_organ_slot(ORGAN_SLOT_EARS) + ears.adjust_temporary_deafness(-20 SECONDS) + TEST_ASSERT(!HAS_TRAIT_FROM(victim, TRAIT_DEAF, EAR_DAMAGE), "victim hasn't recovered from temprorary deafness")