diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index a5fbc5131d1..99d0335065b 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -17,6 +17,44 @@ #define MAX_INSTRUMENT_CHANNELS (128 * 6) +/// This is the lowest volume that can be used by playsound otherwise it gets ignored +/// Most sounds around 10 volume can barely be heard. Almost all sounds at 5 volume or below are inaudible +/// This is to prevent sound being spammed at really low volumes due to distance calculations +/// Recommend setting this to anywhere from 10-3 (or 0 to disable any sound minimum volume restrictions) +/// Ex. For a 70 volume sound, 17 tile range, 3 exponent, 2 falloff_distance: +/// Setting SOUND_AUDIBLE_VOLUME_MIN to 0 for the above will result in 17x17 radius (289 turfs) +/// Setting SOUND_AUDIBLE_VOLUME_MIN to 5 for the above will result in 14x14 radius (196 turfs) +/// Setting SOUND_AUDIBLE_VOLUME_MIN to 10 for the above will result in 11x11 radius (121 turfs) +#define SOUND_AUDIBLE_VOLUME_MIN 3 + +/* Calculates the max distance of a sound based on audible volume + * + * Note - you should NEVER pass in a volume that is lower than SOUND_AUDIBLE_VOLUME_MIN otherwise distance will be insanely large (like +250,000) + * + * Arguments: + * * volume: The initial volume of the sound being played + * * max_distance: The range of the sound in tiles (technically not real max distance since the furthest areas gets pruned due to SOUND_AUDIBLE_VOLUME_MIN) + * * falloff_distance: Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. + * * falloff_exponent: Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. + * Returns: The max distance of a sound based on audible volume range + */ +#define CALCULATE_MAX_SOUND_AUDIBLE_DISTANCE(volume, max_distance, falloff_distance, falloff_exponent)\ + floor(((((-(max(max_distance - falloff_distance, 0) ** (1 / falloff_exponent)) / volume) * (SOUND_AUDIBLE_VOLUME_MIN - volume)) ** falloff_exponent) + falloff_distance)) + +/* Calculates the volume of a sound based on distance + * + * https://www.desmos.com/calculator/sqdfl8ipgf + * + * Arguments: + * * volume: The initial volume of the sound being played + * * distance: How far away the sound is in tiles from the source + * * falloff_distance: Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. + * * falloff_exponent: Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. + * Returns: The max distance of a sound based on audible volume range + */ +#define CALCULATE_SOUND_VOLUME(volume, distance, max_distance, falloff_distance, falloff_exponent)\ + ((max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * volume) + ///Default range of a sound. #define SOUND_RANGE 17 #define MEDIUM_RANGE_SOUND_EXTRARANGE -5 diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 54c2c3d80db..c1957c3581a 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -921,6 +921,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai // Debug traits /// This object has light debugging tools attached to it #define TRAIT_LIGHTING_DEBUGGED "lighting_debugged" +/// This object has sound debugging tools attached to it +#define TRAIT_SOUND_DEBUGGED "sound_debugged" /// Gives you the Shifty Eyes quirk, rarely making people who examine you think you examined them back even when you didn't #define TRAIT_SHIFTY_EYES "shifty_eyes" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 8aa0ae2e11e..42932244758 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -492,6 +492,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SNOB" = TRAIT_SNOB, "TRAIT_SOFTSPOKEN" = TRAIT_SOFTSPOKEN, "TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT, + "TRAIT_SOUND_DEBUGGED" = TRAIT_SOUND_DEBUGGED, "TRAIT_SPACEWALK" = TRAIT_SPACEWALK, "TRAIT_SPARRING" = TRAIT_SPARRING, "TRAIT_SPEAKS_CLEARLY" = TRAIT_SPEAKS_CLEARLY, diff --git a/code/game/objects/items/storage/boxes/engineering_boxes.dm b/code/game/objects/items/storage/boxes/engineering_boxes.dm index 09f641ec315..b30aaed9f68 100644 --- a/code/game/objects/items/storage/boxes/engineering_boxes.dm +++ b/code/game/objects/items/storage/boxes/engineering_boxes.dm @@ -82,6 +82,7 @@ /obj/item/storage/box/material=1, /obj/item/uplink/debug=1, /obj/item/uplink/nuclear/debug=1, + /obj/item/clothing/ears/earmuffs/debug = 1, ) generate_items_inside(items_inside,src) diff --git a/code/game/sound.dm b/code/game/sound.dm index f460847bbcd..5f79ca9e3db 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -51,6 +51,9 @@ if (!turf_source || !soundin || !vol) return + if(vol < SOUND_AUDIBLE_VOLUME_MIN) // never let sound go below SOUND_AUDIBLE_VOLUME_MIN or bad things will happen + return + //allocate a channel if necessary now so its the same for everyone channel = channel || SSsounds.random_available_channel() @@ -64,8 +67,9 @@ var/turf/above_turf = GET_TURF_ABOVE(turf_source) var/turf/below_turf = GET_TURF_BELOW(turf_source) - if(ignore_walls) + var/audible_distance = CALCULATE_MAX_SOUND_AUDIBLE_DISTANCE(vol, maxdistance, falloff_distance, falloff_exponent) + if(ignore_walls) if(above_turf && istransparentturf(above_turf)) listeners += SSmobs.clients_by_zlevel[above_turf.z] @@ -73,16 +77,16 @@ listeners += SSmobs.clients_by_zlevel[below_turf.z] else //these sounds don't carry through walls - listeners = get_hearers_in_view(maxdistance, turf_source) + listeners = get_hearers_in_view(audible_distance, turf_source) if(above_turf && istransparentturf(above_turf)) - listeners += get_hearers_in_view(maxdistance, above_turf) + listeners += get_hearers_in_view(audible_distance, above_turf) if(below_turf && istransparentturf(turf_source)) - listeners += get_hearers_in_view(maxdistance, below_turf) + listeners += get_hearers_in_view(audible_distance, below_turf) for(var/mob/listening_mob in listeners | SSmobs.dead_players_by_zlevel[source_z])//observers always hear through walls - if(get_dist(listening_mob, turf_source) <= maxdistance) + if(get_dist(listening_mob, turf_source) <= audible_distance) listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) . += listening_mob @@ -122,15 +126,16 @@ else sound_to_use.frequency = get_rand_frequency() + var/distance = 0 + if(isturf(turf_source)) var/turf/turf_loc = get_turf(src) //sound volume falloff with distance - var/distance = get_dist(turf_loc, turf_source) * distance_multiplier + distance = get_dist(turf_loc, turf_source) * distance_multiplier if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff. - sound_to_use.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * sound_to_use.volume - //https://www.desmos.com/calculator/sqdfl8ipgf + sound_to_use.volume -= CALCULATE_SOUND_VOLUME(vol, distance, max_distance, falloff_distance, falloff_exponent) if(pressure_affected) //Atmosphere affects sound @@ -151,7 +156,7 @@ sound_to_use.volume *= pressure_factor //End Atmosphere affecting sound - if(sound_to_use.volume <= 0) + if(sound_to_use.volume < SOUND_AUDIBLE_VOLUME_MIN) return //No sound var/dx = turf_source.x - turf_loc.x // Hearing from the right/left @@ -176,6 +181,9 @@ sound_to_use.echo[3] = 0 //Room setting, 0 means normal reverb sound_to_use.echo[4] = 0 //RoomHF setting, 0 means normal reverb. + if(HAS_TRAIT(src, TRAIT_SOUND_DEBUGGED)) + to_chat(src, span_admin("Max Range-[max_distance] Distance-[distance] Vol-[round(sound_to_use.volume, 0.01)] Sound-[sound_to_use.file]")) + SEND_SOUND(src, sound_to_use) /proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S) diff --git a/code/modules/clothing/ears/_ears.dm b/code/modules/clothing/ears/_ears.dm index 7520e6e769b..c1898c0ab9d 100644 --- a/code/modules/clothing/ears/_ears.dm +++ b/code/modules/clothing/ears/_ears.dm @@ -27,3 +27,8 @@ AddElement(/datum/element/earhealing) AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS)) AddComponent(/datum/component/adjust_fishing_difficulty, -2) + +/obj/item/clothing/ears/earmuffs/debug + name = "debug earmuffs" + desc = "Wearing these sends a chat message for every sound played. Walking to ignore footsteps is highly recommended." + clothing_traits = list(TRAIT_SOUND_DEBUGGED)