From cd9ca53d13eab023729bbfd4d2dd14ac675b284a Mon Sep 17 00:00:00 2001 From: Dani Glore Date: Wed, 15 Jan 2025 20:32:23 -0500 Subject: [PATCH 1/2] [MODULAR] Add: Voice Actor Quirk (#4529) * Add Voice Actor quirk, action, and prefs * Fix vet requirement and gain_text * Fix missing apply_to_human() implementations * Fix typos * Remove medical record text from voice actor quirk * Fix voice actor pref validation and speaker list generation * Add quirk_constant_data for voice actor quirk * Add extra vars for original voice * Fix pref validation, fix action isAvailable * Refine quirk description * Fix activation bug, fix background icon, treeshake vars, add balloon alert and chat messages * Fix argument * Simplify json file loading * Cleaning up, fix action bugs, fix button icon bug * Update modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm * Update modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm --------- Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> --- .../code/alter_voice_action.dm | 78 +++++++++++++++++++ .../code/voice_actor_preferences.dm | 43 ++++++++++ .../code/voice_actor_quirk.dm | 22 ++++++ tgstation.dme | 3 + .../nova/voice_actor.tsx | 13 ++++ 5 files changed, 159 insertions(+) create mode 100644 modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm create mode 100644 modular_nova/modules/voice_actor_quirk/code/voice_actor_preferences.dm create mode 100644 modular_nova/modules/voice_actor_quirk/code/voice_actor_quirk.dm create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/nova/voice_actor.tsx diff --git a/modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm b/modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm new file mode 100644 index 00000000000..eb6f04594d3 --- /dev/null +++ b/modular_nova/modules/voice_actor_quirk/code/alter_voice_action.dm @@ -0,0 +1,78 @@ +///Action for voice_actor quirk +/datum/action/innate/alter_voice + name = "Swap Voice" + button_icon = 'icons/mob/actions/actions_spells.dmi' + button_icon_state = "swap" + check_flags = AB_CHECK_CONSCIOUS + /// The primary voice, aka the initial voice + var/primary_voice + /// The pitch of the primary voice + var/primary_pitch = 0 + /// The secondary voice that can be swapped to/from at will + var/secondary_voice + /// The secondary voice's pitch + var/secondary_pitch = 0 + +///Sets up the voice and pitch variables. +/datum/action/innate/alter_voice/proc/setup_second_voice(mob/actor) + if(!actor.client) + return + // Set up secondary pitch + secondary_pitch = actor.client.prefs.read_preference(/datum/preference/numeric/voice_actor_pitch) + // Set up secondary voice + var/speaker = actor.client.prefs.read_preference(/datum/preference/choiced/voice_actor) + var/list/available_speakers + if(SStts.tts_enabled) + available_speakers = SStts.available_speakers + else if(fexists("data/cached_tts_voices.json")) + available_speakers = json_decode(rustg_file_read("data/cached_tts_voices.json")) + else + return + if((speaker == "Random") || !(speaker in available_speakers)) + secondary_voice = pick(available_speakers) + else + secondary_voice = speaker + +///Swaps between primary_voice and secondary_voice +/datum/action/innate/alter_voice/proc/swap_voice() + // If client didn't exist when action was granted, it should exist now. + if(isnull(secondary_voice)) + setup_second_voice(owner) + // Block activation if second voice failed to load. + if(!active && isnull(secondary_voice)) + to_chat(owner, span_userdanger("You can't remember your second voice at the moment. (Please consider reporting this on github!)")) + return + active = !active + if(active) + owner.voice = secondary_voice + owner.pitch = secondary_pitch + to_chat(owner, span_green("You are now voice acting.")) + else + owner.voice = primary_voice + owner.pitch = primary_pitch + to_chat(owner, span_green("You have stopped voice acting.")) + owner.balloon_alert(owner, "voice changed") + build_all_button_icons(UPDATE_BUTTON_BACKGROUND) + +/datum/action/innate/alter_voice/Grant(mob/grant_to) + . = ..() + if(grant_to != owner) + return + // Set up primary pitch + if(SStts.pitch_enabled) + primary_pitch = grant_to.pitch + // Set up primary voice + primary_voice = grant_to.voice + setup_second_voice(grant_to) + +/datum/action/innate/alter_voice/Remove(mob/remove_from) + if(remove_from == owner) + remove_from.voice = primary_voice + remove_from.pitch = primary_pitch + return ..() + +/datum/action/innate/alter_voice/Activate() + swap_voice() + +/datum/action/innate/alter_voice/Deactivate() + swap_voice() diff --git a/modular_nova/modules/voice_actor_quirk/code/voice_actor_preferences.dm b/modular_nova/modules/voice_actor_quirk/code/voice_actor_preferences.dm new file mode 100644 index 00000000000..714ff520f4d --- /dev/null +++ b/modular_nova/modules/voice_actor_quirk/code/voice_actor_preferences.dm @@ -0,0 +1,43 @@ +/datum/preference/choiced/voice_actor + savefile_key = "voice_actor" + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + +/datum/preference/choiced/voice_actor/apply_to_human() + return + +/datum/preference/choiced/voice_actor/create_default_value() + return "Random" + +/datum/preference/choiced/voice_actor/is_valid(value) + if(value == "Random") + return TRUE + if(value in get_choices()) + return TRUE + return FALSE + +/datum/preference/choiced/voice_actor/init_possible_values() + if(SStts.tts_enabled) + var/list/speakers = SStts.available_speakers + speakers.Insert(1, "Random") + return speakers + if(fexists("data/cached_tts_voices.json")) + var/list/cached_voices = json_decode(rustg_file_read("data/cached_tts_voices.json")) + if(cached_voices) + cached_voices.Insert(1, "Random") + return cached_voices + return list("Random") + +/datum/preference/numeric/voice_actor_pitch + savefile_key = "voice_actor_pitch" + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + minimum = -12 + maximum = 12 + +/datum/preference/numeric/voice_actor_pitch/apply_to_human() + return + +/datum/preference/numeric/voice_actor_pitch/create_default_value() + return 0 diff --git a/modular_nova/modules/voice_actor_quirk/code/voice_actor_quirk.dm b/modular_nova/modules/voice_actor_quirk/code/voice_actor_quirk.dm new file mode 100644 index 00000000000..921583bee64 --- /dev/null +++ b/modular_nova/modules/voice_actor_quirk/code/voice_actor_quirk.dm @@ -0,0 +1,22 @@ +/datum/quirk/voice_actor + name = "Voice Actor" + desc = "You are able to swap between two different TTS voices." + icon = FA_ICON_MICROPHONE_LINES + gain_text = span_notice("You are reminded of how your other voice sounds.") + lose_text = span_warning("You suddenly forget what your other voice sounds like!") + medical_record_text = "" + value = 4 + quirk_flags = QUIRK_HUMAN_ONLY + +/datum/quirk_constant_data/voice_actor + associated_typepath = /datum/quirk/voice_actor + customization_options = list(/datum/preference/choiced/voice_actor, /datum/preference/numeric/voice_actor_pitch) + +/datum/quirk/voice_actor/add(client/client_source) + var/datum/action/innate/alter_voice/voice_action = new + voice_action.Grant(quirk_holder) + +/datum/quirk/voice_actor/remove() + var/datum/action/action_to_remove = locate(/datum/action/innate/alter_voice) in quirk_holder.actions + if(action_to_remove) + qdel(action_to_remove) diff --git a/tgstation.dme b/tgstation.dme index 2859d760539..088e91be446 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -8788,6 +8788,9 @@ #include "modular_nova\modules\verbs\code\subtle.dm" #include "modular_nova\modules\veteran_only\code\job_types.dm" #include "modular_nova\modules\veteran_only\code\species_types.dm" +#include "modular_nova\modules\voice_actor_quirk\code\alter_voice_action.dm" +#include "modular_nova\modules\voice_actor_quirk\code\voice_actor_preferences.dm" +#include "modular_nova\modules\voice_actor_quirk\code\voice_actor_quirk.dm" #include "modular_nova\modules\vox_sprites\code\color.dm" #include "modular_nova\modules\vox_sprites\code\head.dm" #include "modular_nova\modules\vox_sprites\code\security.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/nova/voice_actor.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/nova/voice_actor.tsx new file mode 100644 index 00000000000..f8efd378aa2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/nova/voice_actor.tsx @@ -0,0 +1,13 @@ +// THIS IS A NOVA SECTOR UI FILE +import { Feature, FeatureChoiced, FeatureNumberInput } from '../../base'; +import { FeatureDropdownInput } from '../../dropdowns'; + +export const voice_actor: FeatureChoiced = { + name: 'Second Voice', + component: FeatureDropdownInput, +}; + +export const voice_actor_pitch: Feature = { + name: 'Second Pitch', + component: FeatureNumberInput, +}; From c91f7f3349d72646edcc24fc72ab85a2cd210412 Mon Sep 17 00:00:00 2001 From: StealsThePRs Date: Thu, 16 Jan 2025 04:32:46 +0300 Subject: [PATCH 2/2] [MIRROR] [MODULAR] Add: Voice Actor Quirk