Rewrites speech sounds + preferences (#22622)

* all one commit here we go

* rename

* Update voice_types.dm

* type preference based on species

* Update voice_types.dm

* Update _species.dm

* changes

* Update voice_types.dm

* Update speech_sounds.dm
This commit is contained in:
Molti
2024-09-24 22:12:08 -05:00
committed by GitHub
parent 91a45bfeaa
commit 6292a7cba3
14 changed files with 263 additions and 44 deletions

View File

@@ -128,3 +128,6 @@
#define MAX_FLAVOR_LEN 4096 //double the maximum message length.
/// Default volume of speech sound effects (this is actually multiplied by 5 when used)
#define DEFAULT_SPEECH_VOLUME 60

View File

@@ -16,27 +16,3 @@
#define SPECIES_VOX "vox"
#define BUTT_SPRITE_VOX "vox"
GLOBAL_REAL_VAR(list/voice_type2sound = list(
"1" = list(
"1" = sound('goon/sound/speak_1.ogg'),
"!" = sound('goon/sound/speak_1_exclaim.ogg'),
"?" = sound('goon/sound/speak_1_ask.ogg')
),
"2" = list(
"2" = sound('goon/sound/speak_2.ogg'),
"!" = sound('goon/sound/speak_2_exclaim.ogg'),
"?" = sound('goon/sound/speak_2_ask.ogg')
),
"3" = list(
"3" = sound('goon/sound/speak_3.ogg'),
"!" = sound('goon/sound/speak_3_exclaim.ogg'),
"?" = sound('goon/sound/speak_3_ask.ogg')
),
"4" = list(
"4" = sound('goon/sound/speak_4.ogg'),
"!" = sound('goon/sound/speak_4_exclaim.ogg'),
"?" = sound('goon/sound/speak_4_ask.ogg')
),
))

View File

@@ -0,0 +1,14 @@
/// Middleware for voice types
/// Generate list of valid voice_types for the species
/datum/preference_middleware/voice_type/get_ui_data()
. = list()
.["available_voices"] = list()
var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
var/species_id = initial(species_type.id)
for(var/i in GLOB.voice_types)
var/datum/voice/test = GLOB.voice_types[i]
if(test.can_use(species_id))
.["available_voices"] += test.name

View File

@@ -0,0 +1,64 @@
/datum/preference/toggle/speech_hear
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "speech_hear"
savefile_identifier = PREFERENCE_PLAYER
/// Controls the volume at which the user hears in-person Speech sounds
/datum/preference/numeric/speech_volume
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "speech_volume"
savefile_identifier = PREFERENCE_PLAYER
minimum = 0
maximum = 100
/datum/preference/numeric/speech_volume/create_default_value()
return DEFAULT_SPEECH_VOLUME
/datum/preference/choiced/voice_type
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "voice_type"
/datum/preference/choiced/voice_type/init_possible_values()
return GLOB.voice_types
/datum/preference/choiced/voice_type/compile_constant_data()
var/list/data = ..()
data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.voice_types
return data
//this doesn't yet prevent people from taking invalid voices, but it'll stop you from having one to start
/datum/preference/choiced/voice_type/create_informed_default_value(datum/preferences/preferences)
var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
var/species_id = initial(species_type.id)
var/list/valid = list()
for(var/i in GLOB.voice_types)
var/datum/voice/test = GLOB.voice_types[i]
if(test.can_use(species_id))
valid |= i
if(length(valid))
return pick(valid)
return pick(GLOB.voice_types)
/datum/preference/choiced/voice_type/apply_to_human(mob/living/carbon/human/target, value)
target.voice_type = GLOB.voice_types[value]
/datum/preference/choiced/voice_type/deserialize(input, datum/preferences/preferences)
var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
var/species_id = initial(species_type.id)
var/datum/voice/test = GLOB.voice_types[input]
if (!test || !test.can_use(species_id))
return create_informed_default_value(preferences)
return ..(input, preferences)

View File

@@ -514,6 +514,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
C.regenerate_icons()
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
if(!(C.voice_type?.can_use(id)))
C.voice_type = get_random_valid_voice(id)
/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
if(C.dna.species.exotic_bloodtype)

View File

@@ -11,8 +11,6 @@
GLOB.mob_living_list += src
if(startDead)
death(FALSE)
if(!voice_type)
voice_type = pick(voice_type2sound) //yogs edit (stolen from monke)
/mob/living/prepare_huds()
..()

View File

@@ -233,22 +233,6 @@ GLOBAL_LIST_INIT(special_radio_keys, list(
spans |= SPAN_ITALICS
send_speech(message, message_range, src, bubble_type, spans, language, message_mods)
//yogs edit (stolen from monkestation)
///Play a sound to indicate we just spoke
if(client && !HAS_TRAIT(src, TRAIT_SIGN_LANG))
var/ending = copytext_char(message, -1)
var/sound/speak_sound
if(SPAN_HELIUM in spans)
speak_sound = sound('sound/effects/mousesqueek.ogg')
else if(ending == "?")
speak_sound = voice_type2sound[voice_type]["?"]
else if(ending == "!")
speak_sound = voice_type2sound[voice_type]["!"]
else
speak_sound = voice_type2sound[voice_type][voice_type]
playsound(src, speak_sound, 300, 1, SHORT_RANGE_SOUND_EXTRARANGE-2, falloff_exponent = 1, pressure_affected = FALSE, ignore_walls = FALSE, use_reverb = FALSE)
//yogs change end
return on_say_success(message,message_range,succumbed, spans, language, message_mods)//Yogs
@@ -318,6 +302,15 @@ GLOBAL_LIST_INIT(special_radio_keys, list(
eavesdropping = stars(message)
eavesrendered = compose_message(src, message_language, eavesdropping, , spans, message_mods)
//yogs edit (stolen from monkestation and moved)
///Play a sound to indicate we just spoke
var/sound/speak_sound
if(client && voice_type && istype(voice_type))
speak_sound = voice_type.get_sound(src, message, spans)
//yogs change end
var/rendered = compose_message(src, message_language, message, , spans, message_mods)
for(var/_AM in listening)
var/atom/movable/AM = _AM
@@ -325,6 +318,12 @@ GLOBAL_LIST_INIT(special_radio_keys, list(
AM.Hear(eavesrendered, src, message_language, eavesdropping, , spans, message_mods)
else
AM.Hear(rendered, src, message_language, message, , spans, message_mods)
if(speak_sound && ismob(AM))
var/mob/hearing_mob = AM
if(hearing_mob.client?.prefs?.read_preference(/datum/preference/toggle/speech_hear) && hearing_mob.has_language(message_language))
var/volume = hearing_mob.client?.prefs?.read_preference(/datum/preference/numeric/speech_volume) || DEFAULT_SPEECH_VOLUME
volume *= 6 //the sounds are actually really quiet, we just reduced the numbers shown in preferences to not confuse players
hearing_mob.playsound_local(src, speak_sound, volume, TRUE, pressure_affected = FALSE, use_reverb = FALSE) //virtualspeaker handles radio stuff
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
//speech bubble

View File

@@ -175,6 +175,8 @@ export type PreferencesMenuData = {
earned_skillcapes: string[];
available_voices: string[];
window: Window;
};

View File

@@ -147,7 +147,7 @@ export type FeatureChoicedServerData = {
export type FeatureChoiced = Feature<string, string, FeatureChoicedServerData>;
const capitalizeFirstLetter = (text: string) => (
export const capitalizeFirstLetter = (text: string) => (
text.toString().charAt(0).toUpperCase() + text.toString().slice(1)
);

View File

@@ -0,0 +1,33 @@
import { useBackend } from "../../../../../backend";
import { PreferencesMenuData } from "../../../data";
import { sortStrings } from "common/collections";
import { StandardizedDropdown, capitalizeFirstLetter, FeatureChoiced, FeatureChoicedServerData, FeatureValueProps } from "../base";
const VoiceDropdownInput = (
props: FeatureValueProps<string, string, FeatureChoicedServerData> & {
disabled?: boolean,
}, context) => {
const serverData = props.serverData;
if (!serverData) {
return null;
}
const { data } = useBackend<PreferencesMenuData>(context);
const displayNames = serverData.display_names || Object.fromEntries(serverData.choices.map(choice => [choice, capitalizeFirstLetter(choice)]));
let choices = sortStrings(data.available_voices);
return (<StandardizedDropdown
choices={choices}
disabled={props.disabled}
displayNames={displayNames}
onSetValue={props.handleSetValue}
value={props.value}
/>);
};
export const voice_type: FeatureChoiced = {
name: "Voice",
component: VoiceDropdownInput,
};

View File

@@ -0,0 +1,29 @@
import { Feature, FeatureNumberInput, CheckboxInput, FeatureToggle } from "../base";
export const speech_hear: FeatureToggle = {
name: "Hear speech sounds",
category: "SOUND",
description: "If turned off, speech sounds will be muted for you.",
component: CheckboxInput,
};
export const speech_volume: Feature<number> = {
name: "Speech volume",
category: "SOUND",
description: "Controls the volume at which you hear in-person speech sounds.",
component: FeatureNumberInput,
};
// export const speech_hear_radio: FeatureToggle = {
// name: "Hear radio speech",
// category: "SOUND",
// description: "If turned off, speech sounds over radio channels will be muted for you.",
// component: CheckboxInput,
// };
// export const speech_volume_radio: Feature<number> = {
// name: "Speech radio volume",
// category: "SOUND",
// description: "Controls the volume at which you hear speech sounds over radio channels.",
// component: FeatureNumberInput,
// };

View File

@@ -2319,6 +2319,7 @@
#include "code\modules\client\preferences\skin_tone.dm"
#include "code\modules\client\preferences\sounds.dm"
#include "code\modules\client\preferences\species.dm"
#include "code\modules\client\preferences\speech_sounds.dm"
#include "code\modules\client\preferences\tgui.dm"
#include "code\modules\client\preferences\tooltips.dm"
#include "code\modules\client\preferences\ui_style.dm"
@@ -2335,6 +2336,7 @@
#include "code\modules\client\preferences\middleware\random.dm"
#include "code\modules\client\preferences\middleware\skillcapes.dm"
#include "code\modules\client\preferences\middleware\species.dm"
#include "code\modules\client\preferences\middleware\voice_type.dm"
#include "code\modules\client\preferences\migrations\legacy_mood_migration.dm"
#include "code\modules\client\preferences\migrations\tgui_prefs_migration.dm"
#include "code\modules\client\preferences\species_features\basic.dm"
@@ -4478,6 +4480,7 @@
#include "yogstation\code\modules\mob\living\life.dm"
#include "yogstation\code\modules\mob\living\living.dm"
#include "yogstation\code\modules\mob\living\living_defines.dm"
#include "yogstation\code\modules\mob\living\voice_types.dm"
#include "yogstation\code\modules\mob\living\brain\brain_item.dm"
#include "yogstation\code\modules\mob\living\brain\MMI.dm"
#include "yogstation\code\modules\mob\living\carbon\carbon.dm"

View File

@@ -2,4 +2,4 @@
var/ignores_capitalism = FALSE // Decides whether vending machines will just always give them shit for free or not
///The talk chime set to use when speaking.
var/voice_type
var/datum/voice/voice_type

View File

@@ -0,0 +1,96 @@
GLOBAL_LIST_INIT(voice_types, generate_voice_types()) //we have a list of all voice types that players reference to, rather than creating a new one for each character
/proc/generate_voice_types()
. = list()
for(var/path in subtypesof(/datum/voice))
var/datum/voice/new_type = new path()
.[new_type.name] = new_type
/// Returns a voice datum that is valid for the input species
/proc/get_random_valid_voice(species)
var/list/valid = list()
for(var/i in GLOB.voice_types)
var/datum/voice/test = GLOB.voice_types[i]
if(test.can_use(species))
valid |= i
var/voice = GLOB.voice_types[pick(GLOB.voice_types)]
if(length(valid))
voice = GLOB.voice_types[pick(valid)]
return voice
/datum/voice
var/name = "debug"
/// list of sounds used any time it isn't a question or exclamation
var/list/normal
/// list of sounds used for when adding ! at the end
var/list/exclamation
/// list of sounds used for when adding ? at the end
var/list/question
/// set this to false if you don't want people to be able to select it
var/selectable = TRUE
///if this is initialized, a species needs to be on this list to pick it (unused)
var/list/species_whitelist
///needs to not be on this list to pick it (unused)
var/list/species_blacklist
/datum/voice/proc/get_sound(mob/living/speaker, message, list/spans)
if(HAS_TRAIT(speaker, TRAIT_SIGN_LANG))
return
if(SPAN_HELIUM in spans) //lol, squeeky
return 'sound/effects/mousesqueek.ogg'
var/ending = copytext_char(message, -1)
if(ending == "?" && length(question))
return pick(question)
if(ending == "!" && length(exclamation))
return pick(exclamation)
if(!length(normal))
return
//otherwise, just pick one of the regular ones
return pick(normal)
/datum/voice/proc/can_use(species)
if(!selectable)
return FALSE
if(species_blacklist && (species in species_blacklist))
return FALSE
if(species_whitelist && !(species in species_whitelist))
return FALSE
return TRUE
/datum/voice/high
name = "High pitched"
normal = list('goon/sound/speak_2.ogg')
exclamation = list('goon/sound/speak_2_exclaim.ogg')
question = list('goon/sound/speak_2_ask.ogg')
/datum/voice/middle
name = "Medium pitched"
normal = list('goon/sound/speak_1.ogg')
exclamation = list('goon/sound/speak_1_exclaim.ogg')
question = list('goon/sound/speak_1_ask.ogg')
/datum/voice/low
name = "Low pitched"
normal = list('goon/sound/speak_4.ogg')
exclamation = list('goon/sound/speak_4_exclaim.ogg')
question = list('goon/sound/speak_4_ask.ogg')
/datum/voice/wonky
name = "Wonky"
normal = list('goon/sound/speak_3.ogg')
exclamation = list('goon/sound/speak_3_exclaim.ogg')
question = list('goon/sound/speak_3_ask.ogg')