diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3ee9cb364a..32e1759ed4 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -371,4 +371,15 @@ var/global/list/##LIST_NAME = list();\ #define FONT_HUGE(X) "[X]" -#define FONT_GIANT(X) "[X]" \ No newline at end of file +#define FONT_GIANT(X) "[X]" + +#define VOLUME_CHANNEL_MASTER "Master" +#define VOLUME_CHANNEL_AMBIENCE "Ambience" +#define VOLUME_CHANNEL_ALARMS "Alarms" + +// Make sure you update this or clients won't be able to adjust the channel +GLOBAL_LIST_INIT(all_volume_channels, list( + VOLUME_CHANNEL_MASTER, + VOLUME_CHANNEL_AMBIENCE, + VOLUME_CHANNEL_ALARMS, +)) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 8515f282c5..21e94e38d4 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -365,8 +365,11 @@ var/list/mob/living/forced_ambiance_list = new /area/proc/play_ambience(var/mob/living/L, initial = TRUE) // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) return - + if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) + return + + var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE) + // If we previously were in an area with force-played ambiance, stop it. if((L in forced_ambiance_list) && initial) L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) @@ -380,13 +383,14 @@ var/list/mob/living/forced_ambiance_list = new var/sound/chosen_ambiance = pick(forced_ambience) if(!istype(chosen_ambiance)) chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED) + chosen_ambiance.volume *= volume_mod L << chosen_ambiance else L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) else if(src.ambience.len && prob(35)) if((world.time >= L.client.time_last_ambience_played + 1 MINUTE)) var/sound = pick(ambience) - L << sound(sound, repeat = 0, wait = 0, volume = 50, channel = CHANNEL_AMBIENCE) + L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE) L.client.time_last_ambience_played = world.time /area/proc/gravitychange(var/gravitystate = 0) diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm index fa95fed959..a21982f7da 100644 --- a/code/game/machinery/fire_alarm.dm +++ b/code/game/machinery/fire_alarm.dm @@ -208,8 +208,7 @@ FIRE ALARM for(var/obj/machinery/firealarm/FA in area) fire_alarm.triggerAlarm(loc, FA, duration, hidden = alarms_hidden) update_icon() - playsound(src, 'sound/machines/airalarm.ogg', 25, 0, 4) - return + playsound(src, 'sound/machines/airalarm.ogg', 25, 0, 4, volume_channel = VOLUME_CHANNEL_ALARMS) /obj/machinery/firealarm/proc/set_security_level(var/newlevel) if(seclevel != newlevel) diff --git a/code/game/sound.dm b/code/game/sound.dm index c6918beba9..5364d10105 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -1,4 +1,4 @@ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null, volume_channel = null) if(isarea(source)) throw EXCEPTION("playsound(): source is an area") return @@ -23,9 +23,9 @@ if(distance <= maxdistance) if(T && T.z == turf_source.z) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference) +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference, volume_channel = null) if(!client || ear_deaf > 0) return if(preference && !client.is_preference_enabled(preference)) @@ -36,6 +36,11 @@ S.wait = 0 //No queue S.channel = channel || open_sound_channel() + + // I'm not sure if you can modify S.volume, but I'd rather not try to find out what + // horrible things lurk in BYOND's internals, so we're just gonna do vol *= + vol *= client.get_preference_volume_channel(volume_channel) + vol *= client.get_preference_volume_channel(VOLUME_CHANNEL_MASTER) S.volume = vol if(vary) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 163096ecda..2e20f5d531 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -24,6 +24,7 @@ var/time_died_as_mouse = null //when the client last died as a mouse var/datum/tooltip/tooltips = null var/datum/chatOutput/chatOutput + // var/datum/volume_panel/volume_panel = null // Initialized by /client/verb/volume_panel() var/chatOutputLoadedAt var/adminhelped = 0 diff --git a/code/modules/client/preference_setup/volume_sliders/01_volume.dm b/code/modules/client/preference_setup/volume_sliders/01_volume.dm new file mode 100644 index 0000000000..4458573ab2 --- /dev/null +++ b/code/modules/client/preference_setup/volume_sliders/01_volume.dm @@ -0,0 +1,107 @@ +/datum/category_group/player_setup_category/volume_sliders + name = "Sound" + sort_order = 7 + category_item_type = /datum/category_item/player_setup_item/volume_sliders + +/datum/category_item/player_setup_item/volume_sliders/volume + name = "General Volume" + sort_order = 1 + +/datum/category_item/player_setup_item/volume_sliders/volume/load_preferences(var/savefile/S) + S["volume_channels"] >> pref.volume_channels + +/datum/category_item/player_setup_item/volume_sliders/volume/save_preferences(var/savefile/S) + S["volume_channels"] << pref.volume_channels + +/datum/category_item/player_setup_item/volume_sliders/volume/sanitize_preferences() + if(isnull(pref.volume_channels)) + pref.volume_channels = list() + + for(var/channel in pref.volume_channels) + if(!(channel in GLOB.all_volume_channels)) + // Channel no longer exists, yeet + pref.volume_channels.Remove(channel) + + for(var/channel in GLOB.all_volume_channels) + if(!(channel in pref.volume_channels)) + pref.volume_channels["[channel]"] = 1 + else + pref.volume_channels["[channel]"] = clamp(pref.volume_channels["[channel]"], 0, 2) + +/datum/category_item/player_setup_item/volume_sliders/volume/content(var/mob/user) + . += "Volume Settings
" + for(var/channel in pref.volume_channels) + . += "[channel]: [pref.volume_channels[channel] * 100]%
" + . += "
" + +/datum/category_item/player_setup_item/volume_sliders/volume/OnTopic(var/href, var/list/href_list, var/mob/user) + if(href_list["change_volume"]) + if(CanUseTopic(user)) + var/channel = href_list["change_volume"] + if(!(channel in pref.volume_channels)) + pref.volume_channels["[channel]"] = 1 + var/value = input("Choose your volume for [channel] (0-200%)", "[channel] volume", (pref.volume_channels[channel] * 100)) + if(isnum(value)) + value = CLAMP(value, 0, 200) + pref.volume_channels["[channel]"] = (value / 100) + return TOPIC_REFRESH + return ..() + +/mob/proc/get_preference_volume_channel(volume_channel) + if(!client) + return 0 + return client.get_preference_volume_channel(volume_channel) + +/client/proc/get_preference_volume_channel(volume_channel) + if(!volume_channel || !prefs) + return 1 + if(!(volume_channel in prefs.volume_channels)) + prefs.volume_channels["[volume_channel]"] = 1 + return prefs.volume_channels["[volume_channel]"] + + +// Neat little volume adjuster thing in case you don't wanna touch preferences by hand you lazy fuck +// Currently unimplemented due to lack of TGUI v4 +// /datum/volume_panel +// /datum/volume_panel/tgui_state(mob/user) +// return GLOB.tgui_always_state + +// /datum/volume_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) +// ui = SStgui.try_update_ui(user, src, ui) +// if(!ui) +// ui = new(user, src, "VolumePanel", "Volume Panel") +// ui.open() + +// /datum/volume_panel/tgui_data(mob/user) +// if(!user.client || !user.client.prefs) +// return list("error" = TRUE) + +// var/list/data = ..() +// data["volume_channels"] = user.client.prefs.volume_channels +// return data + +// /datum/volume_panel/tgui_act(action, params) +// if(..()) +// return TRUE + +// if(!usr?.client?.prefs) +// return TRUE + +// var/datum/preferences/P = usr.client.prefs +// switch(action) +// if("adjust_volume") +// var/channel = params["channel"] +// if(channel in P.volume_channels) +// P.volume_channels["[channel]"] = clamp(params["vol"], 0, 2) +// SScharacter_setup.queue_preferences_save(P) +// return TRUE + +// /client/verb/volume_panel() +// set name = "Volume Panel" +// set category = "Preferences" +// set desc = "Allows you to adjust volume levels on the fly." + +// if(!volume_panel) +// volume_panel = new(src) + +// volume_panel.tgui_interact(mob) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 7a6e709582..522c3f2ba1 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -138,6 +138,8 @@ datum/preferences var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel. var/multilingual_mode = 0 // Default behaviour, delimiter-key-space, delimiter-key-delimiter, off + var/list/volume_channels = list() + /datum/preferences/New(client/C) player_setup = new(src) diff --git a/polaris.dme b/polaris.dme index 3d5b9bf6b7..3f833491a6 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1553,6 +1553,7 @@ #include "code\modules\client\preference_setup\skills\skills.dm" #include "code\modules\client\preference_setup\traits\trait_defines.dm" #include "code\modules\client\preference_setup\traits\traits.dm" +#include "code\modules\client\preference_setup\volume_sliders\01_volume.dm" #include "code\modules\clothing\chameleon.dm" #include "code\modules\clothing\clothing.dm" #include "code\modules\clothing\clothing_accessories.dm" diff --git a/tgui/packages/tgui/interfaces/VolumePanel.js b/tgui/packages/tgui/interfaces/VolumePanel.js new file mode 100644 index 0000000000..33bc883fb0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/VolumePanel.js @@ -0,0 +1,38 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, Slider, Section } from "../components"; +import { Window } from "../layouts"; + +export const VolumePanel = (props, context) => { + const { act, data } = useBackend(context); + + const { + volume_channels, + } = data; + + return ( + + +
+ + {Object.keys(volume_channels).map(key => ( + + act("adjust_volume", { channel: key, vol: (val / 100) })} /> +
+
+
+ ); +}; \ No newline at end of file