diff --git a/Content.Client/ADT/Bark/Systems/HumanoidProfileEditor.Barks.cs b/Content.Client/ADT/Bark/Systems/HumanoidProfileEditor.Barks.cs new file mode 100644 index 0000000000..6a531fd198 --- /dev/null +++ b/Content.Client/ADT/Bark/Systems/HumanoidProfileEditor.Barks.cs @@ -0,0 +1,106 @@ +using System.Linq; +using Content.Client.ADT.SpeechBarks; +using Content.Client.Lobby; +using Content.Corvax.Interfaces.Shared; +using Content.Shared.Corvax.TTS; +using Content.Shared.Preferences; +using Content.Shared.ADT.SpeechBarks; + +namespace Content.Client.Lobby.UI; + +public sealed partial class HumanoidProfileEditor +{ + private List _barkList = new(); + + private void InitializeBarks() + { + _barkList = _prototypeManager + .EnumeratePrototypes() + .Where(o => o.RoundStart) + .OrderBy(o => Loc.GetString(o.Name)) + .ToList(); + + BarkProtoButton.OnItemSelected += args => + { + BarkProtoButton.SelectId(args.Id); + SetBarkProto(_barkList[args.Id].ID); + }; + + PitchEdit.OnTextChanged += args => + { + if (!float.TryParse(args.Text, out var newPitch)) + return; + + SetBarkPitch(newPitch); + }; + + DelayVariationMinEdit.OnTextChanged += args => + { + if (!float.TryParse(args.Text, out var newVar)) + return; + + SetBarkMinVariation(newVar); + }; + + DelayVariationMaxEdit.OnTextChanged += args => + { + if (!float.TryParse(args.Text, out var newVar)) + return; + + SetBarkMaxVariation(newVar); + }; + + BarkPlayButton.OnPressed += _ => PlayPreviewBark(); + + IoCManager.Instance!.TryResolveType(out _sponsorsMgr); + } + + private void UpdateBarkVoicesControls() + { + if (Profile is null) + return; + + BarkProtoButton.Clear(); + + PitchEdit.Text = Profile.BarkPitch.ToString(); + DelayVariationMinEdit.Text = Profile.BarkLowVar.ToString(); + DelayVariationMaxEdit.Text = Profile.BarkHighVar.ToString(); + + var firstVoiceChoiceId = 1; + for (var i = 0; i < _barkList.Count; i++) + { + var voice = _barkList[i]; + //if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex)) + // continue; + + var name = Loc.GetString(voice.Name); + BarkProtoButton.AddItem(name, i); + + if (firstVoiceChoiceId == 1) + firstVoiceChoiceId = i; + + if (_sponsorsMgr is null) + continue; + if (voice.SponsorOnly && _sponsorsMgr != null && + !_sponsorsMgr.GetClientPrototypes().Contains(voice.ID)) + { + BarkProtoButton.SetItemDisabled(VoiceButton.GetIdx(i), true); + } + } + + var voiceChoiceId = _barkList.FindIndex(x => x.ID == Profile.BarkProto); + if (!BarkProtoButton.TrySelectId(voiceChoiceId) && + BarkProtoButton.TrySelectId(firstVoiceChoiceId)) + { + SetBarkProto(_barkList[firstVoiceChoiceId].ID); + } + } + + private void PlayPreviewBark() + { + if (Profile is null) + return; + + _entManager.System().PlayDataPrewiew(Profile.BarkProto, Profile.BarkPitch, Profile.BarkLowVar, Profile.BarkHighVar); + } +} diff --git a/Content.Client/ADT/Bark/Systems/SpeechBarksSystem.cs b/Content.Client/ADT/Bark/Systems/SpeechBarksSystem.cs new file mode 100644 index 0000000000..e3df5c13bb --- /dev/null +++ b/Content.Client/ADT/Bark/Systems/SpeechBarksSystem.cs @@ -0,0 +1,142 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Content.Shared.ADT.SpeechBarks; +using Content.Shared.Chat; +using Content.Shared.Corvax.CCCVars; +using Robust.Client.Audio; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using System.Threading.Tasks; +using Robust.Client.ResourceManagement; +using Robust.Shared.Utility; +using Robust.Client.Player; +using Content.Shared.ADT.CCVar; + +namespace Content.Client.ADT.SpeechBarks; + +public sealed class SpeechBarksSystem : SharedSpeechBarksSystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly SharedChatSystem _chat = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + public override void Initialize() + { + base.Initialize(); + + _cfg.OnValueChanged(CCCVars.TTSVolume, OnVolumeChanged, true); + + SubscribeNetworkEvent(OnEntitySpoke); + } + + private readonly List _sampleText = + new() + { + "Тест мессЭдж 1.", + "Тест мессЭдж 2!", + "Тест мессЭдж 3?", + "Здесь был котя." + }; + + private const float MinimalVolume = -10f; + private float _volume = 0.0f; + private const float WhisperFade = 4f; + + private void OnVolumeChanged(float volume) + { + _volume = volume; + } + + private float AdjustVolume(bool isWhisper) + { + var volume = MinimalVolume + SharedAudioSystem.GainToVolume(_volume); + + if (isWhisper) + { + volume -= SharedAudioSystem.GainToVolume(WhisperFade); + } + + return volume; + } + + private float AdjustDistance(bool isWhisper) + { + return isWhisper ? SharedChatSystem.WhisperMuffledRange : SharedChatSystem.VoiceRange; + } + + private async void OnEntitySpoke(PlaySpeechBarksEvent ev) + { + if (_cfg.GetCVar(ADTCCVars.ReplaceTTSWithBarks) == false) + return; + + if (ev.Message == null) + return; + + if (ev.Source != null) + { + var audioParams = AudioParams.Default + .WithMaxDistance(AdjustDistance(ev.IsWhisper)) + .WithPitchScale(ev.Pitch) + .WithVolume(AdjustVolume(ev.IsWhisper)); + + if (ev.Message.EndsWith('!')) + audioParams = audioParams.WithRolloffFactor(1.4f); + //audioParams = audioParams.WithVolume(audioParams.Volume * 2.5f); + + var count = Math.Clamp((int)ev.Message.Length / 3f, 1, 15); + var message = ev.Message; + var audioResource = new AudioResource(); + string str = ev.Sound; + + var path = new ResPath(str); + audioResource.Load(IoCManager.Instance!, path); + + for (var i = 0; i < count; i++) + { + if (_player.LocalSession == null) + break; + + _audio.PlayEntity(audioResource.AudioStream, GetEntity(ev.Source.Value), audioParams.WithPitchScale(_random.NextFloat(ev.Pitch - 0.1f, ev.Pitch + 0.1f))); + + await Task.Delay(TimeSpan.FromSeconds(_random.NextFloat(ev.LowVar, ev.HighVar))); + } + } + } + + public async void PlayDataPrewiew(string protoId, float pitch, float lowVar, float highVar) + { + if (!_proto.TryIndex(protoId, out var proto)) + return; + + var message = _random.Pick(_sampleText); + + var audioParams = AudioParams.Default + .WithVolume(AdjustVolume(false)); + + var count = (int)message.Length / 3f; + var audioResource = new AudioResource(); + string str = proto.Sound; + + if (message.EndsWith('!')) + audioParams = audioParams.WithRolloffFactor(1.4f); + + var path = new ResPath(str); + audioResource.Load(IoCManager.Instance!, path); + + for (var i = 0; i < count; i++) + { + if (_player.LocalSession == null) + break; + + _audio.PlayGlobal(str, _player.LocalSession, audioParams.WithPitchScale(_random.NextFloat(pitch - 0.1f, pitch + 0.1f))); + + await Task.Delay(TimeSpan.FromSeconds(_random.NextFloat(lowVar, highVar))); + } + } + + +} diff --git a/Content.Client/Corvax/TTS/TTSSystem.cs b/Content.Client/Corvax/TTS/TTSSystem.cs index fc843fc873..2821b508e8 100644 --- a/Content.Client/Corvax/TTS/TTSSystem.cs +++ b/Content.Client/Corvax/TTS/TTSSystem.cs @@ -10,6 +10,7 @@ using Robust.Shared.Utility; using Content.Shared.ADT.Language; // ADT Languages using Robust.Shared.Player; +using Content.Shared.ADT.CCVar; namespace Content.Client.Corvax.TTS; @@ -73,6 +74,9 @@ private void OnPlayTTS(PlayTTSEvent ev) var filePath = new ResPath($"{_fileIdx++}.ogg"); + if (_cfg.GetCVar(ADTCCVars.ReplaceTTSWithBarks) == true) + return; + // Languages TTS support start var player = _playerManager.LocalSession?.AttachedEntity; if (player != null) diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 7cafca751d..7173b830f8 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -91,6 +91,26 @@ + + +