[feat] TTS
# Conflicts: # Content.Client/Options/UI/Tabs/AudioTab.xaml.cs # Content.Client/Preferences/UI/HumanoidProfileEditor.xaml # Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs # Content.Server/Database/ServerDbBase.cs # Content.Server/Entry/EntryPoint.cs # Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.cs # Content.Server/IoC/ServerContentIoC.cs # Content.Server/VoiceMask/VoiceMaskSystem.cs # Resources/Prototypes/Entities/Mobs/Species/base.yml
This commit is contained in:
@@ -61,6 +61,19 @@
|
||||
<Label Name="AmbienceVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-tts-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="TtsVolumeSlider"
|
||||
MinValue="0"
|
||||
MaxValue="200"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="TtsVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-lobby-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.Audio;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -36,6 +37,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged;
|
||||
AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged;
|
||||
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
|
||||
TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged;
|
||||
InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
|
||||
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
|
||||
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
|
||||
@@ -58,9 +60,22 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AmbienceVolumeSlider.OnValueChanged -= OnAmbienceVolumeSliderChanged;
|
||||
LobbyVolumeSlider.OnValueChanged -= OnLobbyVolumeSliderChanged;
|
||||
InterfaceVolumeSlider.OnValueChanged -= OnInterfaceVolumeSliderChanged;
|
||||
|
||||
//WD-EDIT
|
||||
TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged;
|
||||
//WD-EDIT
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
||||
//TTS-Start
|
||||
private void OnTtsVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
//TTS-End
|
||||
|
||||
private void OnLobbyVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
@@ -132,6 +147,11 @@ namespace Content.Client.Options.UI.Tabs
|
||||
_cfg.SetCVar(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.EventMusicEnabled, EventMusicCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox.Pressed);
|
||||
|
||||
//WD-EDIT
|
||||
_cfg.SetCVar(WhiteCVars.TtsVolume, LV100ToDB(TtsVolumeSlider.Value));
|
||||
//WD-EDIT
|
||||
|
||||
_cfg.SaveToFile();
|
||||
UpdateChanges();
|
||||
}
|
||||
@@ -156,9 +176,30 @@ namespace Content.Client.Options.UI.Tabs
|
||||
RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
|
||||
EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
|
||||
AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled);
|
||||
|
||||
//WD-EDIT
|
||||
TtsVolumeSlider.Value = DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume));
|
||||
//WD-EDIT
|
||||
|
||||
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
// Note: Rather than moving these functions somewhere, instead switch MidiManager to using linear units rather than dB
|
||||
// Do be sure to rename the setting though
|
||||
private float DBToLV100(float db, float multiplier = 1f)
|
||||
{
|
||||
var weh = (float) (Math.Pow(10, db / 10) * 100 / multiplier);
|
||||
return weh;
|
||||
}
|
||||
|
||||
private float LV100ToDB(float lv100, float multiplier = 1f)
|
||||
{
|
||||
// Saving negative infinity doesn't work, so use -10000000 instead (MidiManager does it)
|
||||
var weh = MathF.Max(-10000000, (float) (Math.Log(lv100 * multiplier / 100, 10) * 10));
|
||||
return weh;
|
||||
}
|
||||
|
||||
private void UpdateChanges()
|
||||
{
|
||||
// y'all need jesus.
|
||||
@@ -177,11 +218,18 @@ namespace Content.Client.Options.UI.Tabs
|
||||
|
||||
var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources);
|
||||
var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled);
|
||||
|
||||
//WD-EDIT
|
||||
var isTtsVolumeSame =
|
||||
Math.Abs(TtsVolumeSlider.Value - DBToLV100(_cfg.GetCVar(WhiteCVars.TtsVolume))) < 0.01f;
|
||||
//WD-EDIT
|
||||
|
||||
var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
|
||||
var isEventSame = EventMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.EventMusicEnabled);
|
||||
var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled);
|
||||
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
|
||||
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
||||
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
||||
isEverythingSame = isEverythingSame && isTtsVolumeSame; //WD-EDIT
|
||||
ApplyButton.Disabled = isEverythingSame;
|
||||
ResetButton.Disabled = isEverythingSame;
|
||||
MasterVolumeLabel.Text =
|
||||
@@ -197,6 +245,11 @@ namespace Content.Client.Options.UI.Tabs
|
||||
InterfaceVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", InterfaceVolumeSlider.Value / 100));
|
||||
AmbienceSoundsLabel.Text = ((int)AmbienceSoundsSlider.Value).ToString();
|
||||
|
||||
//WD-EDIT
|
||||
TtsVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100));
|
||||
//WD-EDIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,12 @@
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CPronounsButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- TTS -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-voice-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CVoiceButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Show clothing -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-clothing'}" />
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Content.Client.Preferences.UI
|
||||
private Button _saveButton => CSaveButton;
|
||||
private OptionButton _sexButton => CSexButton;
|
||||
private OptionButton _genderButton => CPronounsButton;
|
||||
|
||||
//WD-EDIT
|
||||
private OptionButton _voiceButton => CVoiceButton;
|
||||
//WD-EDIT
|
||||
|
||||
private Slider _skinColor => CSkin;
|
||||
private OptionButton _clothingButton => CClothingButton;
|
||||
private OptionButton _backpackButton => CBackpackButton;
|
||||
@@ -172,6 +177,14 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
#endregion Gender
|
||||
|
||||
//TTS-Start
|
||||
#region Voice
|
||||
|
||||
InitializeVoice();
|
||||
|
||||
#endregion
|
||||
//TTS-End
|
||||
|
||||
#region Species
|
||||
|
||||
_speciesList = prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart).ToList();
|
||||
@@ -748,9 +761,18 @@ namespace Content.Client.Preferences.UI
|
||||
}
|
||||
UpdateGenderControls();
|
||||
CMarkings.SetSex(newSex);
|
||||
UpdateTTSVoicesControls(); //WD-EDIT
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
//WD-EDIT
|
||||
private void SetVoice(string newVoice)
|
||||
{
|
||||
Profile = Profile?.WithVoice(newVoice);
|
||||
IsDirty = true;
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
private void SetGender(Gender newGender)
|
||||
{
|
||||
Profile = Profile?.WithGender(newGender);
|
||||
@@ -1112,6 +1134,10 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateCMarkingsHair();
|
||||
UpdateCMarkingsFacialHair();
|
||||
|
||||
//WD-EDIT
|
||||
UpdateTTSVoicesControls();
|
||||
//WD-EDIT
|
||||
|
||||
_preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnNameChange += OnNameSelected;
|
||||
_window.OnVoiceChange += (value) => SendMessage(new VoiceMaskChangeVoiceMessage(value));
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
@@ -35,7 +36,8 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateState(cast.Name);
|
||||
_window.UpdateState(cast.Name, cast.Voice);
|
||||
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -7,5 +7,9 @@
|
||||
<LineEdit Name="NameSelector" HorizontalExpand="True" />
|
||||
<Button Name="NameSelectorSet" Text="{Loc 'voice-mask-name-change-set'}" />
|
||||
</BoxContainer>
|
||||
<Label Text="{Loc 'voice-mask-voice-change-info'}" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<OptionButton Name="VoiceSelector" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.White.TTS;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.VoiceMask;
|
||||
|
||||
@@ -9,6 +12,9 @@ public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
|
||||
{
|
||||
public Action<string>? OnNameChange;
|
||||
|
||||
private readonly List<TTSVoicePrototype> _voices; // TTS
|
||||
public Action<string>? OnVoiceChange;
|
||||
|
||||
public VoiceMaskNameChangeWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -17,10 +23,31 @@ public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
|
||||
{
|
||||
OnNameChange!(NameSelector.Text);
|
||||
};
|
||||
|
||||
VoiceSelector.OnItemSelected += args =>
|
||||
{
|
||||
VoiceSelector.SelectId(args.Id);
|
||||
if (VoiceSelector.SelectedMetadata != null)
|
||||
OnVoiceChange!((string)VoiceSelector.SelectedMetadata);
|
||||
};
|
||||
_voices = IoCManager
|
||||
.Resolve<IPrototypeManager>()
|
||||
.EnumeratePrototypes<TTSVoicePrototype>()
|
||||
.Where(o => o.RoundStart)
|
||||
.ToList();
|
||||
for (var i = 0; i < _voices.Count; i++)
|
||||
{
|
||||
var name = Loc.GetString(_voices[i].Name);
|
||||
VoiceSelector.AddItem(name);
|
||||
VoiceSelector.SetItemMetadata(i, _voices[i].ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(string name)
|
||||
public void UpdateState(string name, string voice)
|
||||
{
|
||||
NameSelector.Text = name;
|
||||
var voiceIdx = _voices.FindIndex(v => v.ID == voice);
|
||||
if (voiceIdx != -1)
|
||||
VoiceSelector.Select(voiceIdx);
|
||||
}
|
||||
}
|
||||
|
||||
61
Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs
Normal file
61
Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Linq;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.White.TTS;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
public sealed partial class HumanoidProfileEditor
|
||||
{
|
||||
private List<TTSVoicePrototype> _voiceList = default!;
|
||||
|
||||
private void InitializeVoice()
|
||||
{
|
||||
_voiceList = _prototypeManager.EnumeratePrototypes<TTSVoicePrototype>().Where(o => o.RoundStart).ToList();
|
||||
|
||||
_voiceButton.OnItemSelected += args =>
|
||||
{
|
||||
_voiceButton.SelectId(args.Id);
|
||||
SetVoice(_voiceList[args.Id].ID);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void UpdateTTSVoicesControls()
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var sponsorsManager = IoCManager.Resolve<SponsorsManager>();
|
||||
|
||||
_voiceButton.Clear();
|
||||
|
||||
var firstVoiceChoiceId = 1;
|
||||
for (var i = 0; i < _voiceList.Count; i++)
|
||||
{
|
||||
var voice = _voiceList[i];
|
||||
if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex))
|
||||
continue;
|
||||
|
||||
var name = Loc.GetString(voice.Name);
|
||||
_voiceButton.AddItem(name, i);
|
||||
|
||||
if (firstVoiceChoiceId == 1)
|
||||
firstVoiceChoiceId = i;
|
||||
|
||||
if (voice.SponsorOnly &&
|
||||
sponsorsManager.TryGetInfo(out var sponsor) &&
|
||||
!sponsor.AllowedMarkings.Contains(voice.ID))
|
||||
{
|
||||
_voiceButton.SetItemDisabled(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice);
|
||||
if (!_voiceButton.TrySelectId(voiceChoiceId) &&
|
||||
_voiceButton.TrySelectId(firstVoiceChoiceId))
|
||||
{
|
||||
SetVoice(_voiceList[firstVoiceChoiceId].ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
213
Content.Client/White/TTS/TTSSystem.cs
Normal file
213
Content.Client/White/TTS/TTSSystem.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Shared.White.TTS;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.White;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio.Sources;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Client.White.TTS;
|
||||
|
||||
/// <summary>
|
||||
/// Plays TTS audio in world
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public sealed class TTSSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAudioManager _audioSystem = default!;
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _broadPhase = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private float _volume = 0.0f;
|
||||
|
||||
private readonly HashSet<AudioStream> _currentStreams = new();
|
||||
private readonly Dictionary<EntityUid, Queue<AudioStream>> _entityQueues = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_sawmill = Logger.GetSawmill("tts");
|
||||
_cfg.OnValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged, true);
|
||||
SubscribeNetworkEvent<PlayTTSEvent>(OnPlayTTS);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_cfg.UnsubValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged);
|
||||
EndStreams();
|
||||
}
|
||||
|
||||
// Little bit of duplication logic from AudioSystem
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
var streamToRemove = new HashSet<AudioStream>();
|
||||
|
||||
var ourPos = _eye.CurrentEye.Position.Position;
|
||||
foreach (var stream in _currentStreams)
|
||||
{
|
||||
if (!stream.Source.Playing ||
|
||||
!_entity.TryGetComponent<MetaDataComponent>(stream.Uid, out var meta) ||
|
||||
Deleted(stream.Uid, meta) ||
|
||||
!_entity.TryGetComponent<TransformComponent>(stream.Uid, out var xform))
|
||||
{
|
||||
stream.Source.Dispose();
|
||||
streamToRemove.Add(stream);
|
||||
continue;
|
||||
}
|
||||
|
||||
var mapPos = xform.MapPosition;
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
{
|
||||
stream.Source.Position = mapPos.Position;
|
||||
}
|
||||
|
||||
if (mapPos.MapId == _eye.CurrentMap)
|
||||
{
|
||||
var collisionMask = (int) CollisionGroup.Impassable;
|
||||
var sourceRelative = ourPos - mapPos.Position;
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length() > 0)
|
||||
{
|
||||
occlusion = _broadPhase.IntersectRayPenetration(mapPos.MapId,
|
||||
new CollisionRay(mapPos.Position, sourceRelative.Normalized(), collisionMask),
|
||||
sourceRelative.Length(), stream.Uid);
|
||||
}
|
||||
|
||||
stream.Source.Occlusion = occlusion;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var audioStream in streamToRemove)
|
||||
{
|
||||
_currentStreams.Remove(audioStream);
|
||||
ProcessEntityQueue(audioStream.Uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTtsVolumeChanged(float volume)
|
||||
{
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
private void OnPlayTTS(PlayTTSEvent ev)
|
||||
{
|
||||
if (!TryCreateAudioSource(ev.Data, out var source))
|
||||
return;
|
||||
|
||||
var stream = new AudioStream(GetEntity(ev.Uid), source);
|
||||
AddEntityStreamToQueue(stream);
|
||||
}
|
||||
|
||||
public void PlayCustomText(string text)
|
||||
{
|
||||
RaiseNetworkEvent(new RequestTTSEvent(text));
|
||||
}
|
||||
|
||||
private bool TryCreateAudioSource(byte[] data, [NotNullWhen(true)] out IAudioSource? source)
|
||||
{
|
||||
var dataStream = new MemoryStream(data) { Position = 0 };
|
||||
var audioStream = _audioSystem.LoadAudioWav(dataStream);
|
||||
source = _audioSystem.CreateAudioSource(audioStream);
|
||||
if (source == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
source.Volume = _volume;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddEntityStreamToQueue(AudioStream stream)
|
||||
{
|
||||
if (_entityQueues.TryGetValue(stream.Uid, out var queue))
|
||||
{
|
||||
queue.Enqueue(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
_entityQueues.Add(stream.Uid, new Queue<AudioStream>(new[] { stream }));
|
||||
|
||||
if (!IsEntityCurrentlyPlayStream(stream.Uid))
|
||||
ProcessEntityQueue(stream.Uid);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsEntityCurrentlyPlayStream(EntityUid uid)
|
||||
{
|
||||
return _currentStreams.Any(s => s.Uid == uid);
|
||||
}
|
||||
|
||||
private void ProcessEntityQueue(EntityUid uid)
|
||||
{
|
||||
if (TryTakeEntityStreamFromQueue(uid, out var stream))
|
||||
PlayEntity(stream);
|
||||
}
|
||||
|
||||
private bool TryTakeEntityStreamFromQueue(EntityUid uid, [NotNullWhen(true)] out AudioStream? stream)
|
||||
{
|
||||
if (_entityQueues.TryGetValue(uid, out var queue))
|
||||
{
|
||||
stream = queue.Dequeue();
|
||||
if (queue.Count == 0)
|
||||
_entityQueues.Remove(uid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PlayEntity(AudioStream stream)
|
||||
{
|
||||
if (!_entity.TryGetComponent<TransformComponent>(stream.Uid, out var xform))
|
||||
return;
|
||||
|
||||
stream.Source.Position = xform.WorldPosition;
|
||||
stream.Source.StartPlaying();
|
||||
_currentStreams.Add(stream);
|
||||
}
|
||||
|
||||
public void StopAllStreams()
|
||||
{
|
||||
foreach (var stream in _currentStreams)
|
||||
{
|
||||
stream.Source.StopPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
private void EndStreams()
|
||||
{
|
||||
foreach (var stream in _currentStreams)
|
||||
{
|
||||
stream.Source.StopPlaying();
|
||||
stream.Source.Dispose();
|
||||
}
|
||||
|
||||
_currentStreams.Clear();
|
||||
_entityQueues.Clear();
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private sealed class AudioStream
|
||||
{
|
||||
public EntityUid Uid { get; }
|
||||
|
||||
public IAudioSource Source { get; }
|
||||
|
||||
public AudioStream(EntityUid uid, IAudioSource source)
|
||||
{
|
||||
Uid = uid;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user