From 5df674622c8d3f79aeefa9e06e47dd6583e6f6e0 Mon Sep 17 00:00:00 2001 From: Geeves <22774890+Geevies@users.noreply.github.com> Date: Sun, 16 Aug 2026 18:19:53 +0200 Subject: [PATCH] Looping Sound Channel (#22918) * Added the option to manually change the looping sound channel volume in the SFX Preferences tab. AI usage disclosure: The code for this was created in-part using GPT 5.6 Sol. --------- Signed-off-by: Geeves <22774890+Geevies@users.noreply.github.com> Co-authored-by: VMSolidus --- .../V023__looping_sound_volume.sql | 1 + code/__DEFINES/sound.dm | 3 +++ code/datums/looping_sounds/_looping_sound.dm | 3 ++- code/game/sound/sound.dm | 18 +++++++++++------- .../preference_setup/global/02_settings.dm | 10 ++++++++-- code/modules/client/preferences.dm | 1 + code/modules/client/preferences_ambience.dm | 15 ++++++++++++++- .../geeves-looping_sound_channels.yml | 6 ++++++ 8 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 SQL/migrate-2023/V023__looping_sound_volume.sql create mode 100644 html/changelogs/geeves-looping_sound_channels.yml diff --git a/SQL/migrate-2023/V023__looping_sound_volume.sql b/SQL/migrate-2023/V023__looping_sound_volume.sql new file mode 100644 index 00000000000..3f167a8e937 --- /dev/null +++ b/SQL/migrate-2023/V023__looping_sound_volume.sql @@ -0,0 +1 @@ +ALTER TABLE `ss13_player_preferences` ADD COLUMN `looping_sound_volume` INT(11) NOT NULL DEFAULT 100 AFTER `lobby_music_vol`; diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 6ab17b50a01..7f28c6fde31 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -14,6 +14,9 @@ #define CHANNEL_INSTRUMENTS 1012 #define CHANNEL_MOB_SOUNDS 1011 +// Client preference volume channels. These are separate from BYOND's playback channels above. +#define SOUND_VOLUME_CHANNEL_LOOPING 1 + /// Default range of a sound. #define SOUND_RANGE 17 #define MEDIUM_RANGE_SOUND_EXTRARANGE -5 diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index 54185efdb8d..34f6fd43867 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -165,7 +165,8 @@ pressure_affected = pressure_affected, ignore_walls = ignore_walls, falloff_distance = falloff_distance, - use_reverb = use_reverb + use_reverb = use_reverb, + volume_channel = SOUND_VOLUME_CHANNEL_LOOPING ) /// Returns the sound we should now be playing. diff --git a/code/game/sound/sound.dm b/code/game/sound/sound.dm index 2eb542aa438..d93f9b06c67 100644 --- a/code/game/sound/sound.dm +++ b/code/game/sound/sound.dm @@ -41,8 +41,9 @@ * * * required_preferences - What preference is required to be on on the client, for the sound to play * * required_asfx_toggles - What toggles are required to be on on the client, for the sound to play + * * volume_channel - The client preference volume channel to apply to the sound */ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE, required_preferences, required_asfx_toggles) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE, required_preferences, required_asfx_toggles, volume_channel) if(isarea(source)) CRASH("playsound(): source is an area") @@ -109,9 +110,9 @@ if(get_dist(listening_mob, turf_source) <= maxdistance) //Aurora snowflake, if we don't ignore the walls, account for wall-like obstacles to dampen the sound if(ignore_walls) - listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) + listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb, volume_channel) else - adjust_sound_based_on_path_obstacles(listening_mob, turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, use_reverb) + adjust_sound_based_on_path_obstacles(listening_mob, turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, use_reverb, volume_channel) . += listening_mob @@ -122,7 +123,7 @@ * * Use this to tweak what happens with the sound along the path from the emitter to the receiver of said sound */ -/proc/adjust_sound_based_on_path_obstacles(mob/listening_mob, turf/turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, use_reverb) +/proc/adjust_sound_based_on_path_obstacles(mob/listening_mob, turf/turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, use_reverb, volume_channel) var/turf/inbetween_turf = get_turf(listening_mob) for(var/step_counter in 1 to get_dist(listening_mob, turf_source)) @@ -141,13 +142,17 @@ if(vol <= 0) return - listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) + listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb, volume_channel) -/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) +/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, volume_channel) if(!client || !can_hear()) return + if (client.prefs) + switch (volume_channel) + if (SOUND_VOLUME_CHANNEL_LOOPING) vol *= client.prefs.looping_sound_volume / 100 + if(!sound_to_use) sound_to_use = sound(get_sfx(soundin)) @@ -243,4 +248,3 @@ return soundin var/datum/sound_effect/sfx = SSsounds.sfx_datum_by_key[soundin] return sfx?.return_sfx() || soundin - diff --git a/code/modules/client/preference_setup/global/02_settings.dm b/code/modules/client/preference_setup/global/02_settings.dm index f568f7590a9..a8e6d81c425 100644 --- a/code/modules/client/preference_setup/global/02_settings.dm +++ b/code/modules/client/preference_setup/global/02_settings.dm @@ -9,6 +9,7 @@ S["sfx_toggles"] >> pref.sfx_toggles S["toggles_secondary"] >> pref.toggles_secondary S["lobby_music_vol"] >> pref.lobby_music_vol + S["looping_sound_volume"] >> pref.looping_sound_volume /datum/category_item/player_setup_item/player_global/settings/save_preferences(var/savefile/S) S["lastchangelog"] << pref.lastchangelog @@ -17,6 +18,7 @@ S["sfx_toggles"] << pref.sfx_toggles S["toggles_secondary"] << pref.toggles_secondary S["lobby_music_vol"] << pref.lobby_music_vol + S["looping_sound_volume"] << pref.looping_sound_volume /datum/category_item/player_setup_item/player_global/settings/gather_load_query() return list( @@ -27,7 +29,8 @@ "toggles", "sfx_toggles", "toggles_secondary", - "lobby_music_vol" + "lobby_music_vol", + "looping_sound_volume" ), "args" = list("ckey") ) @@ -46,6 +49,7 @@ "ckey" = 1, "toggles_secondary", "lobby_music_vol", + "looping_sound_volume", ) ) @@ -57,7 +61,8 @@ "toggles" = pref.toggles, "sfx_toggles" = pref.sfx_toggles, "toggles_secondary" = pref.toggles_secondary, - "lobby_music_vol" = pref.lobby_music_vol + "lobby_music_vol" = pref.lobby_music_vol, + "looping_sound_volume" = pref.looping_sound_volume ) /datum/category_item/player_setup_item/player_global/settings/sanitize_preferences(var/sql_load = 0) @@ -71,6 +76,7 @@ pref.sfx_toggles = sanitize_integer(text2num(pref.sfx_toggles), 0, BITFIELDMAX, initial(pref.toggles)) pref.toggles_secondary = sanitize_integer(text2num(pref.toggles_secondary), 0, BITFIELDMAX, initial(pref.toggles_secondary)) pref.lobby_music_vol = sanitize_integer(text2num(pref.lobby_music_vol), 0, BITFIELDMAX, initial(pref.lobby_music_vol)) + pref.looping_sound_volume = sanitize_integer(text2num(pref.looping_sound_volume), 0, 100, initial(pref.looping_sound_volume)) /datum/category_item/player_setup_item/player_global/settings/content(mob/user) var/list/dat = list( diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index ed3d511060a..cd3db128af8 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -33,6 +33,7 @@ GLOBAL_LIST_EMPTY_TYPED(preferences_datums, /datum/preferences) var/tgui_say_light_mode = FALSE var/ui_scale = TRUE var/lobby_music_vol = 85 + var/looping_sound_volume = 100 //Style for popup tooltips var/tooltip_style = "Midnight" diff --git a/code/modules/client/preferences_ambience.dm b/code/modules/client/preferences_ambience.dm index 8aca95e8129..98cd4223d8e 100644 --- a/code/modules/client/preferences_ambience.dm +++ b/code/modules/client/preferences_ambience.dm @@ -14,7 +14,8 @@ GLOBAL_LIST_INIT(sfx_toggles, list( /client/proc/toggle_dropsounds, /client/proc/toggle_arcadesounds, /client/proc/toggle_radiosounds, - /client/proc/toggle_instrumentsounds + /client/proc/toggle_instrumentsounds, + /client/proc/toggle_looping_sounds )) /client/var/has_sfx_verbs = FALSE @@ -147,3 +148,15 @@ GLOBAL_LIST_INIT(sfx_toggles, list( prefs.sfx_toggles ^= ASFX_INSTRUMENT prefs.save_preferences() to_chat(src, SPAN_INFO("You will [(prefs.sfx_toggles & ASFX_INSTRUMENT) ? "now" : "no longer"] hear instrument sounds.")) + +/client/proc/toggle_looping_sounds() + set name = "Set Looping SFX Volume" + set category = "SFX Preferences" + set desc = "Sets the volume of looping sound effects" + + var/new_volume = tgui_input_number(src, "Choose looping sound effect volume, 0 to mute", "Looping SFX Volume", prefs.looping_sound_volume, 100, 0, 0, TRUE) + if(isnull(new_volume)) + return + prefs.looping_sound_volume = new_volume + prefs.save_preferences() + to_chat(src, SPAN_INFO("Looping sound effect volume set to [new_volume]%.")) diff --git a/html/changelogs/geeves-looping_sound_channels.yml b/html/changelogs/geeves-looping_sound_channels.yml new file mode 100644 index 00000000000..e3ed62f2415 --- /dev/null +++ b/html/changelogs/geeves-looping_sound_channels.yml @@ -0,0 +1,6 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "Added the option to manually change the looping sound channel volume in the SFX Preferences tab."