Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.Appearance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ private void SetSex(Sex newSex)
break;
}

UpdateTTSVoicesControls(); // Art-TTS

UpdateGenderControls();
_markingsModel.SetOrganSexes(newSex);
ReloadPreview();
Expand Down
13 changes: 13 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,20 @@
<Control HorizontalExpand="True"/>
<OptionButton Name="SpawnPriorityButton" HorizontalAlignment="Right" />
</BoxContainer>
<!-- Art-TTS-Start -->
<BoxContainer HorizontalExpand="True"
Visible="False"
Name="TTSContainer">
<Label Text="{Loc 'humanoid-profile-editor-voice-label'}"/>
<Control HorizontalExpand="True"/>
<OptionButton Name="VoiceButton"
HorizontalAlignment="Right"/>
<Button Name="VoicePlayButton"
Text="{Loc 'humanoid-profile-editor-voice-play'}"
MaxWidth="80"/>
</BoxContainer>
<!-- Art-TTS-End -->
</BoxContainer>
<!-- Skin -->
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
<Label Text="{Loc 'humanoid-profile-editor-skin-color-label'}" />
Expand Down
12 changes: 12 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking;
using Content.Client.Sprite;
using Content.Shared._Art.ArtCVar; // Art-TTS
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Humanoid;
Expand Down Expand Up @@ -191,6 +192,14 @@ public HumanoidProfileEditor(

#endregion Gender

// Art-TTS End
if (configurationManager.GetCVar(ArtCVars.TTSClientEnabled))
{
TTSContainer.Visible = true;
InitializeVoice();
}
// Art-TTS End

RefreshSpecies();

SpeciesButton.OnItemSelected += args =>
Expand Down Expand Up @@ -288,6 +297,8 @@ public HumanoidProfileEditor(

RefreshFlavorText();

UpdateTTSVoicesControls(); // Art-TTS

#region Dummy

SpriteRotateLeft.OnPressed += _ =>
Expand Down Expand Up @@ -366,6 +377,7 @@ public void SetProfile(HumanoidCharacterProfile? profile, int? slot)
IsDirty = false;
JobOverride = null;

UpdateTTSVoicesControls(); // Art-TTS
UpdateNameEdit();
UpdateFlavorTextEdit();
UpdateSexControls();
Expand Down
2 changes: 2 additions & 0 deletions Content.Client/Options/UI/Tabs/AudioTab.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
<BoxContainer Orientation="Vertical" Margin="0 3 0 0">
<ui:OptionSlider Name="SliderVolumeMaster" Title="{Loc 'ui-options-master-volume'}"
Margin="0 0 0 8" />
<ui:OptionSlider Name="SliderVolumeTts" Title="Громкость TTS:"/> <!-- Art-TTS -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Локализуйте новые UI-строки через Loc.

На Line 11 и Line 19 добавлены хардкод-строки, что ломает единый i18n-поток для интерфейса настроек.

💡 Предлагаемый фикс
-                <ui:OptionSlider Name="SliderVolumeTts" Title="Громкость TTS:"/> <!-- Art-TTS  -->
+                <ui:OptionSlider Name="SliderVolumeTts" Title="{Loc 'ui-options-tts-volume'}"/> <!-- Art-TTS  -->
...
-                <CheckBox Name="TtsClientCheckBox" Text="Включить ТТС"/> <!-- Art-TTS -->
+                <CheckBox Name="TtsClientCheckBox" Text="{Loc 'ui-options-tts-enabled'}"/> <!-- Art-TTS -->

Also applies to: 19-19

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Client/Options/UI/Tabs/AudioTab.xaml` at line 11, Replace the
hard-coded Title text on the new UI controls (e.g., the OptionSlider named
SliderVolumeTts and the other control added at the same location) with the
localization helper (Loc) so strings flow through the i18n pipeline; update the
XAML Title attributes to use the Loc markup extension or binding (e.g.,
Title="{Loc Key=...}" or equivalent used elsewhere) and add matching resource
keys for those strings to the shared resource file so the UI reads localized
text instead of literal Russian text.

<ui:OptionSlider Name="SliderVolumeMidi" Title="{Loc 'ui-options-midi-volume'}" />
<ui:OptionSlider Name="SliderVolumeAmbientMusic" Title="{Loc 'ui-options-ambient-music-volume'}" />
<ui:OptionSlider Name="SliderVolumeAmbience" Title="{Loc 'ui-options-ambience-volume'}" />
<ui:OptionSlider Name="SliderVolumeLobby" Title="{Loc 'ui-options-lobby-volume'}" />
<ui:OptionSlider Name="SliderVolumeInterface" Title="{Loc 'ui-options-interface-volume'}" />
<ui:OptionSlider Name="SliderMaxAmbienceSounds" Title="{Loc 'ui-options-ambience-max-sounds'}"
Margin="0 0 0 8" />
<CheckBox Name="TtsClientCheckBox" Text="Включить ТТС"/> <!-- Art-TTS -->
<CheckBox Name="LobbyMusicCheckBox" Text="{Loc 'ui-options-lobby-music'}" />
<CheckBox Name="RestartSoundsCheckBox" Text="{Loc 'ui-options-restart-sounds'}" />
<CheckBox Name="EventMusicCheckBox" Text="{Loc 'ui-options-event-music'}" />
Expand Down
9 changes: 9 additions & 0 deletions Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Client.Administration.Managers;
using Content.Client.Audio;
using Content.Shared._Art.ArtCVar; // Art-TTS
using Content.Shared.CCVar;
using Robust.Client.Audio;
using Robust.Client.AutoGenerated;
Expand Down Expand Up @@ -28,6 +29,13 @@ public AudioTab()
scale: ContentAudioSystem.MasterVolumeMultiplier);
masterVolume.ImmediateValueChanged += OnMasterVolumeSliderChanged;

// Art-TTS Start
Control.AddOptionPercentSlider(
ArtCVars.TTSVolume,
SliderVolumeTts,
scale: ContentAudioSystem.TtsMultiplier);
// Art-TTS End

Control.AddOptionPercentSlider(
CVars.MidiVolume,
SliderVolumeMidi,
Expand Down Expand Up @@ -59,6 +67,7 @@ public AudioTab()
_cfg.GetCVar(CCVars.MinMaxAmbientSourcesConfigured),
_cfg.GetCVar(CCVars.MaxMaxAmbientSourcesConfigured));

Control.AddOptionCheckBox(ArtCVars.TTSClientEnabled, TtsClientCheckBox); // Art-TTS
Control.AddOptionCheckBox(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox);
Control.AddOptionCheckBox(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox);
Control.AddOptionCheckBox(CCVars.EventMusicEnabled, EventMusicCheckBox);
Expand Down
3 changes: 2 additions & 1 deletion Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ protected override void Open()

_window.OnNameChange += OnNameSelected;
_window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
_window.OnVoiceChange += voice => SendMessage(new VoiceMaskChangeVoiceMessage(voice)); // Art-TTS
_window.OnToggle += OnToggle;
_window.OnAccentToggle += OnAccentToggle;
}
Expand All @@ -52,7 +53,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
}

_window.UpdateState(cast.Name, cast.Verb, cast.Active, cast.AccentHide);
_window.UpdateState(cast.Name, cast.Voice, cast.Verb, cast.Active, cast.AccentHide); // Art-TTS
}

protected override void Dispose(bool disposing)
Expand Down
10 changes: 10 additions & 0 deletions Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,15 @@
</BoxContainer>
<Button Name="ToggleAccentButton" Text="{Loc 'voice-mask-name-change-accent-toggle'}" HorizontalExpand="True" ToggleMode="True" Margin="5"/>
<Button Name="ToggleButton" Text="{Loc 'voice-mask-name-change-toggle'}" HorizontalExpand="True" ToggleMode="True" Margin="5"/>
<!-- Art-TTS Start -->
<BoxContainer Orientation="Horizontal"
Margin="5"
Visible="False"
Name="TTSContainer">
<Label Text="{Loc 'voice-mask-voice-change-info'}"/>
<OptionButton Name="VoiceSelector"/>
<!-- Populated in LoadVerbs -->
</BoxContainer>
<!-- Art-TTS End -->
</BoxContainer>
</controls:FancyWindow>
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public VoiceMaskNameChangeWindow()
OnNameChange?.Invoke(NameSelector.Text);
};

ReloadVoices(); // Art-TTS

SpeechVerbSelector.OnItemSelected += args =>
{
OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
Expand Down Expand Up @@ -69,7 +71,7 @@ private void AddVerb(string name, string? verb)
SpeechVerbSelector.SelectId(id);
}

public void UpdateState(string name, string? verb, bool active, bool accentHide)
public void UpdateState(string name, string voice, string? verb, bool active, bool accentHide) // Art-TTS
{
NameSelector.Text = name;
_verb = verb;
Expand All @@ -84,5 +86,10 @@ public void UpdateState(string name, string? verb, bool active, bool accentHide)
break;
}
}
// Art-TTS Start
var voiceIdx = _voices.FindIndex(v => v.ID == voice);
if (voiceIdx != -1)
VoiceSelector.Select(voiceIdx);
// Art-TTS End
Comment on lines +89 to +93
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Поиск объявления поля _voices в файлах VoiceMaskNameChangeWindow
fd -e cs -e xaml 'VoiceMaskNameChangeWindow' --exec grep -l "_voices" {} \;
rg -n "_voices" --type cs -C 3

Repository: ss14-art/open-space

Length of output: 3171


🏁 Script executed:

wc -l Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs

Repository: ss14-art/open-space

Length of output: 124


🏁 Script executed:

cat -n Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs | head -50

Repository: ss14-art/open-space

Length of output: 1879


🏁 Script executed:

rg -n "private.*_voices|_voices\s*=" "Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs"

Repository: ss14-art/open-space

Length of output: 45


🏁 Script executed:

cat -n Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs | tail -50

Repository: ss14-art/open-space

Length of output: 1834


🏁 Script executed:

rg -n "ReloadVoices\|class VoiceMaskNameChangeWindow" "Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs"

Repository: ss14-art/open-space

Length of output: 45


🏁 Script executed:

fd -type f -name "*.cs" "Content.Client/VoiceMask/"

Repository: ss14-art/open-space

Length of output: 233


🏁 Script executed:

rg -l "VoiceMaskNameChangeWindow" "Content.Client/VoiceMask/"

Repository: ss14-art/open-space

Length of output: 177


🏁 Script executed:

cat Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs | head -100

Repository: ss14-art/open-space

Length of output: 1831


🏁 Script executed:

rg -n "_voices|ReloadVoices" "Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs"

Repository: ss14-art/open-space

Length of output: 45


🏁 Script executed:

rg -n "private.*ReloadVoices\|public.*ReloadVoices" "Content.Client/"

Repository: ss14-art/open-space

Length of output: 45


🏁 Script executed:

grep -n "ReloadVoices" "Content.Client/_Art/TTS/VoiceMaskNameChangeWindow.xaml.cs"

Repository: ss14-art/open-space

Length of output: 97


Поле _voices и метод ReloadVoices() не определены в этом классе.

Класс VoiceMaskNameChangeWindow в пространстве имён Content.Client.VoiceMask использует поле _voices (строка 90) и вызывает метод ReloadVoices() (строка 31), которые не объявлены в этом файле. Эти члены существуют в другом классе VoiceMaskNameChangeWindow в пространстве имён Content.Client._Art.TTS, но это отдельные классы, а не части одного partial класса. Необходимо добавить объявление поля _voices: List<TTSVoicePrototype> и реализацию метода ReloadVoices() в класс Content.Client.VoiceMask.VoiceMaskNameChangeWindow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs` around lines 89 -
93, Добавьте в класс Content.Client.VoiceMask.VoiceMaskNameChangeWindow поле
List<TTSVoicePrototype> _voices и реализуйте метод ReloadVoices() — метод должен
заполнять _voices (например, из того же источника, что и в
Content.Client._Art.TTS версия класса), обновлять элементы селектора
VoiceSelector (Items/ItemsSource) и корректно выставлять текущий выбранный
элемент; убедитесь, что добавлены нужные using (System.Collections.Generic и
пространство имён, где объявлен TTSVoicePrototype) и что имена методов/полей
соответствуют существующему коду (включая _voices, ReloadVoices, VoiceSelector,
VoiceMaskNameChangeWindow).

}
}
8 changes: 8 additions & 0 deletions Content.Client/_Art/TTS/ContentAudioSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Audio;

namespace Content.Client.Audio;

public sealed partial class ContentAudioSystem : SharedContentAudioSystem
{
public const float TtsMultiplier = 3f;
}
71 changes: 71 additions & 0 deletions Content.Client/_Art/TTS/HumanoidProfileEditor.TTS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Linq;
using Content.Client._Art.TTS;
using Content.Client.Lobby;
// using Content.Corvax.Interfaces.Shared;
using Content.Shared._Art.TTS;
using Content.Shared.Preferences;

namespace Content.Client.Lobby.UI;

public sealed partial class HumanoidProfileEditor
{
private List<TTSVoicePrototype> _voiceList = new();

private void InitializeVoice()
{
_voiceList = _prototypeManager
.EnumeratePrototypes<TTSVoicePrototype>()
.OrderBy(o => Loc.GetString(o.Name))
.OrderBy(o => ((o.Gender == "male") ? 0b01 : 0) + ((o.Gender == "female") ? 0b10 : 0))
.ToList();

VoiceButton.OnItemSelected += args =>
{
VoiceButton.SelectId(args.Id);
SetVoice(_voiceList[args.Id].ID);
};

VoicePlayButton.OnPressed += _ => PlayPreviewTTS();
}
Comment on lines +14 to +29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

InitializeVoice() не должен повторно навешивать обработчики.

Если этот метод вызывается больше одного раза, VoiceButton.OnItemSelected и VoicePlayButton.OnPressed начнут срабатывать по нескольку раз. Для preview это ещё и быстро упрётся в серверный rate limit.

💡 Предлагаемое исправление
+    private bool _voiceHandlersHooked;
+
     private void InitializeVoice()
     {
         _voiceList = _prototypeManager
             .EnumeratePrototypes<TTSVoicePrototype>()
             .OrderBy(o => Loc.GetString(o.Name))
             .OrderBy(o => ((o.Gender == "male") ? 0b01 : 0) + ((o.Gender == "female") ? 0b10 : 0))
             .ToList();
 
-        VoiceButton.OnItemSelected += args =>
+        if (_voiceHandlersHooked)
+            return;
+
+        VoiceButton.OnItemSelected += args =>
         {
             VoiceButton.SelectId(args.Id);
             SetVoice(_voiceList[args.Id].ID);
         };
 
         VoicePlayButton.OnPressed += _ => PlayPreviewTTS();
+        _voiceHandlersHooked = true;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void InitializeVoice()
{
_voiceList = _prototypeManager
.EnumeratePrototypes<TTSVoicePrototype>()
.OrderBy(o => Loc.GetString(o.Name))
.OrderBy(o => ((o.Gender == "male") ? 0b01 : 0) + ((o.Gender == "female") ? 0b10 : 0))
.ToList();
VoiceButton.OnItemSelected += args =>
{
VoiceButton.SelectId(args.Id);
SetVoice(_voiceList[args.Id].ID);
};
VoicePlayButton.OnPressed += _ => PlayPreviewTTS();
}
private bool _voiceHandlersHooked;
private void InitializeVoice()
{
_voiceList = _prototypeManager
.EnumeratePrototypes<TTSVoicePrototype>()
.OrderBy(o => Loc.GetString(o.Name))
.OrderBy(o => ((o.Gender == "male") ? 0b01 : 0) + ((o.Gender == "female") ? 0b10 : 0))
.ToList();
if (_voiceHandlersHooked)
return;
VoiceButton.OnItemSelected += args =>
{
VoiceButton.SelectId(args.Id);
SetVoice(_voiceList[args.Id].ID);
};
VoicePlayButton.OnPressed += _ => PlayPreviewTTS();
_voiceHandlersHooked = true;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Client/_Art/TTS/HumanoidProfileEditor.TTS.cs` around lines 14 - 29,
InitializeVoice currently adds anonymous handlers every call causing duplicate
event firing; fix by extracting the handlers into named methods (e.g.,
OnVoiceSelected which calls VoiceButton.SelectId(...) and
SetVoice(_voiceList[args.Id].ID), and OnVoicePlayPressed which calls
PlayPreviewTTS), then subscribe those methods once (VoiceButton.OnItemSelected
+= OnVoiceSelected; VoicePlayButton.OnPressed += OnVoicePlayPressed) or
explicitly unsubscribe before subscribing (VoiceButton.OnItemSelected -=
OnVoiceSelected; VoiceButton.OnItemSelected += OnVoiceSelected; same for
VoicePlayButton) so repeated InitializeVoice calls won't attach duplicate
delegates; leave the _voiceList population as-is.


private void UpdateTTSVoicesControls()
{
if (Profile is null)
return;

VoiceButton.Clear();

var firstVoiceChoiceId = 1;
for (var i = 0; i < _voiceList.Count; i++)
{
var voice = _voiceList[i];

var name = Loc.GetString(voice.Name);
VoiceButton.AddItem(name, i);

if (firstVoiceChoiceId == 1)
firstVoiceChoiceId = i;
}

var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice);
if (!VoiceButton.TrySelectId(voiceChoiceId) &&
VoiceButton.TrySelectId(firstVoiceChoiceId))
{
SetVoice(_voiceList[firstVoiceChoiceId].ID);
}
}

private void PlayPreviewTTS()
{
if (Profile is null)
return;

_entManager.System<TTSSystem>().RequestPreviewTTS(Profile.Voice);
}

private void SetVoice(string newVoice)
{
Profile = Profile?.WithVoice(newVoice);
IsDirty = true;
}
}
Loading
Loading