[Feat] TTS borgs & announcements (#342)
* tts for borgs * tts for announcements
This commit is contained in:
@@ -55,5 +55,10 @@ namespace Content.Server.Communications
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg");
|
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg");
|
||||||
|
|
||||||
|
//WD-start
|
||||||
|
[DataField("ttsVoiceId")]
|
||||||
|
public string TtsVoiceId = "Glados";
|
||||||
|
//WD-end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Content.Server.RoundEnd;
|
|||||||
using Content.Server.Shuttles.Systems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Server.Station.Components;
|
using Content.Server.Station.Components;
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.White.TTS;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
@@ -287,6 +288,11 @@ namespace Content.Server.Communications
|
|||||||
}
|
}
|
||||||
_chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color);
|
_chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color);
|
||||||
|
|
||||||
|
//WD-start
|
||||||
|
var ttsEv = new TTSAnnouncementEvent(message.Message, comp.TtsVoiceId, uid, comp.Global);
|
||||||
|
RaiseLocalEvent(ttsEv);
|
||||||
|
//WD-end
|
||||||
|
|
||||||
if (message.Session.AttachedEntity != null)
|
if (message.Session.AttachedEntity != null)
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following station announcement: {msg}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following station announcement: {msg}");
|
||||||
}
|
}
|
||||||
|
|||||||
17
Content.Server/White/TTS/TTSAnnouncementEvent.cs
Normal file
17
Content.Server/White/TTS/TTSAnnouncementEvent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Content.Server.White.TTS;
|
||||||
|
|
||||||
|
public sealed class TTSAnnouncementEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly string Message;
|
||||||
|
public readonly bool Global;
|
||||||
|
public readonly string VoiceId;
|
||||||
|
public readonly EntityUid Source;
|
||||||
|
|
||||||
|
public TTSAnnouncementEvent(string message, string voiceId, EntityUid source , bool global)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Global = global;
|
||||||
|
VoiceId = voiceId;
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Server.Light.Components;
|
||||||
|
using Content.Server.Station.Components;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
using Content.Shared.White.TTS;
|
using Content.Shared.White.TTS;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.White;
|
using Content.Shared.White;
|
||||||
|
using Robust.Server.Audio;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
@@ -24,6 +29,9 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
[Dependency] private readonly IServerNetManager _netMgr = default!;
|
[Dependency] private readonly IServerNetManager _netMgr = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly TTSPitchRateSystem _ttsPitchRateSystem = default!;
|
[Dependency] private readonly TTSPitchRateSystem _ttsPitchRateSystem = default!;
|
||||||
|
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||||
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
|
||||||
private const int MaxMessageChars = 100 * 2; // same as SingleBubbleCharLimit * 2
|
private const int MaxMessageChars = 100 * 2; // same as SingleBubbleCharLimit * 2
|
||||||
private bool _isEnabled = false;
|
private bool _isEnabled = false;
|
||||||
@@ -38,9 +46,73 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(OnEntitySpoke);
|
SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(OnEntitySpoke);
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TTSAnnouncementEvent>(OnAnnounceRequest);
|
||||||
|
|
||||||
_netMgr.RegisterNetMessage<MsgRequestTTS>(OnRequestTTS);
|
_netMgr.RegisterNetMessage<MsgRequestTTS>(OnRequestTTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnAnnounceRequest(TTSAnnouncementEvent ev)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex<TTSVoicePrototype>(ev.VoiceId, out var ttsPrototype))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = FormattedMessage.RemoveMarkup(ev.Message);
|
||||||
|
var soundData = await GenerateTTS(null, message, ttsPrototype.Speaker);
|
||||||
|
|
||||||
|
if (soundData == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Filter filter;
|
||||||
|
if (ev.Global)
|
||||||
|
filter = Filter.Broadcast();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var station = _stationSystem.GetOwningStation(ev.Source);
|
||||||
|
if (station == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!EntityManager.TryGetComponent<StationDataComponent>(station, out var stationDataComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
filter = _stationSystem.GetInStation(stationDataComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var player in filter.Recipients)
|
||||||
|
{
|
||||||
|
if (player.AttachedEntity != null)
|
||||||
|
{
|
||||||
|
// Get emergency lights in range to broadcast from
|
||||||
|
var entities = _lookup.GetEntitiesInRange(player.AttachedEntity.Value, 20f)
|
||||||
|
.Where(HasComp<EmergencyLightComponent>)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (entities.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get closest emergency light
|
||||||
|
var entity = entities.First();
|
||||||
|
var range = 100f;
|
||||||
|
|
||||||
|
foreach (var item in entities)
|
||||||
|
{
|
||||||
|
var itemSource = _xforms.GetWorldPosition(Transform(item));
|
||||||
|
var playerSource = _xforms.GetWorldPosition(Transform(player.AttachedEntity.Value));
|
||||||
|
|
||||||
|
var distance = playerSource.Length() - itemSource.Length();
|
||||||
|
|
||||||
|
if (range > distance)
|
||||||
|
{
|
||||||
|
range = distance;
|
||||||
|
entity = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(entity), soundData), Filter.SinglePlayer(player),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnRequestTTS(MsgRequestTTS ev)
|
private async void OnRequestTTS(MsgRequestTTS ev)
|
||||||
{
|
{
|
||||||
var url = _cfg.GetCVar(WhiteCVars.TTSApiUrl);
|
var url = _cfg.GetCVar(WhiteCVars.TTSApiUrl);
|
||||||
@@ -138,7 +210,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
_ttsManager.ResetCache();
|
_ttsManager.ResetCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<byte[]?> GenerateTTS(EntityUid uid, string text, string speaker, string? speechPitch = null, string? speechRate = null)
|
private async Task<byte[]?> GenerateTTS(EntityUid? uid, string text, string speaker, string? speechPitch = null, string? speechRate = null)
|
||||||
{
|
{
|
||||||
var textSanitized = Sanitize(text);
|
var textSanitized = Sanitize(text);
|
||||||
if (textSanitized == "")
|
if (textSanitized == "")
|
||||||
@@ -148,7 +220,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
string rate;
|
string rate;
|
||||||
if (speechPitch == null || speechRate == null)
|
if (speechPitch == null || speechRate == null)
|
||||||
{
|
{
|
||||||
if (!_ttsPitchRateSystem.TryGetPitchRate(uid, out var pitchRate))
|
if (uid == null || !_ttsPitchRateSystem.TryGetPitchRate(uid.Value, out var pitchRate))
|
||||||
{
|
{
|
||||||
pitch = "medium";
|
pitch = "medium";
|
||||||
rate = "medium";
|
rate = "medium";
|
||||||
|
|||||||
@@ -31,4 +31,7 @@ public sealed class TTSVoicePrototype : IPrototype
|
|||||||
|
|
||||||
[DataField("sponsorOnly")]
|
[DataField("sponsorOnly")]
|
||||||
public bool SponsorOnly { get; } = false;
|
public bool SponsorOnly { get; } = false;
|
||||||
|
|
||||||
|
[DataField("borgVoice")]
|
||||||
|
public bool BorgVoice { get; } = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,25 +25,17 @@ tts-voice-name-gman = G-Man
|
|||||||
tts-voice-name-obama = Обама
|
tts-voice-name-obama = Обама
|
||||||
tts-voice-name-trump = Трамп
|
tts-voice-name-trump = Трамп
|
||||||
tts-voice-name-briman = Брин
|
tts-voice-name-briman = Брин
|
||||||
tts-voice-name-kleiner-alt = Кляйнер (Альт.)
|
|
||||||
tts-voice-name-father-grigori = Отец Григорий
|
tts-voice-name-father-grigori = Отец Григорий
|
||||||
tts-voice-name-vance = Вэнс
|
tts-voice-name-vance = Вэнс
|
||||||
tts-voice-name-barni = Барни
|
tts-voice-name-barni = Барни
|
||||||
tts-voice-name-gman-alt = G-Man (Альт.)
|
|
||||||
tts-voice-name-alyx = Аликс
|
tts-voice-name-alyx = Аликс
|
||||||
tts-voice-name-mossman = Моссман
|
tts-voice-name-mossman = Моссман
|
||||||
tts-voice-name-bandit = Бандит
|
tts-voice-name-bandit = Бандит
|
||||||
tts-voice-name-papich-alt = Папич (Альт.)
|
|
||||||
tts-voice-name-bebey-alt = Бэбэй (Альт.)
|
|
||||||
tts-voice-name-glados-alt = Гладос (Альт.)
|
|
||||||
tts-voice-name-geralt = Геральт
|
tts-voice-name-geralt = Геральт
|
||||||
tts-voice-name-triss = Трисс
|
tts-voice-name-triss = Трисс
|
||||||
tts-voice-name-kodlak-white-mane = Кодлак Белая Грива
|
tts-voice-name-kodlak-white-mane = Кодлак Белая Грива
|
||||||
tts-voice-name-cicero = Цицерон
|
tts-voice-name-cicero = Цицерон
|
||||||
tts-voice-name-sheogorath = Шеогорат
|
tts-voice-name-sheogorath = Шеогорат
|
||||||
tts-voice-name-adventure-core-alt = Модуль Приключений (Альт.)
|
|
||||||
tts-voice-name-fact-core-alt = Модуль Фактов (Альт.)
|
|
||||||
tts-voice-name-space-core-alt = Модуль Космоса (Альт.)
|
|
||||||
tts-voice-name-turret-floor = Турель
|
tts-voice-name-turret-floor = Турель
|
||||||
tts-voice-name-cirilla = Цири
|
tts-voice-name-cirilla = Цири
|
||||||
tts-voice-name-lambert = Ламберт
|
tts-voice-name-lambert = Ламберт
|
||||||
|
|||||||
@@ -69,12 +69,15 @@
|
|||||||
name: tts-voice-name-glados
|
name: tts-voice-name-glados
|
||||||
sex: Female
|
sex: Female
|
||||||
speaker: glados
|
speaker: glados
|
||||||
|
roundStart: false
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Sentrybot
|
id: Sentrybot
|
||||||
name: tts-voice-name-sentrybot
|
name: tts-voice-name-sentrybot
|
||||||
sex: Male
|
sex: Male
|
||||||
speaker: sentrybot
|
speaker: sentrybot
|
||||||
|
roundStart: false
|
||||||
|
borgVoice: true
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Mana
|
id: Mana
|
||||||
@@ -105,18 +108,24 @@
|
|||||||
name: tts-voice-name-adventure-core
|
name: tts-voice-name-adventure-core
|
||||||
sex: Male
|
sex: Male
|
||||||
speaker: adventure_core
|
speaker: adventure_core
|
||||||
|
roundStart: false
|
||||||
|
borgVoice: true
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: SpaceCore
|
id: SpaceCore
|
||||||
name: tts-voice-name-space-core
|
name: tts-voice-name-space-core
|
||||||
sex: Male
|
sex: Male
|
||||||
speaker: space_core
|
speaker: space_core
|
||||||
|
roundStart: false
|
||||||
|
borgVoice: true
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: FactCore
|
id: FactCore
|
||||||
name: tts-voice-name-fact-core
|
name: tts-voice-name-fact-core
|
||||||
sex: Male
|
sex: Male
|
||||||
speaker: fact_core
|
speaker: fact_core
|
||||||
|
roundStart: false
|
||||||
|
borgVoice: true
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Kleiner
|
id: Kleiner
|
||||||
@@ -160,12 +169,6 @@
|
|||||||
sex: Male
|
sex: Male
|
||||||
speaker: briman
|
speaker: briman
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: KleinerAlt
|
|
||||||
name: tts-voice-name-kleiner-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: kleiner_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: FatherGrigori
|
id: FatherGrigori
|
||||||
name: tts-voice-name-father-grigori
|
name: tts-voice-name-father-grigori
|
||||||
@@ -184,12 +187,6 @@
|
|||||||
sex: Male
|
sex: Male
|
||||||
speaker: barni
|
speaker: barni
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: GmanAlt
|
|
||||||
name: tts-voice-name-gman-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: gman_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Alyx
|
id: Alyx
|
||||||
name: tts-voice-name-alyx
|
name: tts-voice-name-alyx
|
||||||
@@ -208,24 +205,6 @@
|
|||||||
sex: Male
|
sex: Male
|
||||||
speaker: bandit
|
speaker: bandit
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: PapichAlt
|
|
||||||
name: tts-voice-name-papich-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: papich_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: BebeyAlt
|
|
||||||
name: tts-voice-name-bebey-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: bebey_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: GladosAlt
|
|
||||||
name: tts-voice-name-glados-alt
|
|
||||||
sex: Female
|
|
||||||
speaker: glados_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Geralt
|
id: Geralt
|
||||||
name: tts-voice-name-geralt
|
name: tts-voice-name-geralt
|
||||||
@@ -256,29 +235,13 @@
|
|||||||
sex: Male
|
sex: Male
|
||||||
speaker: sheogorath
|
speaker: sheogorath
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: AdventureCoreAlt
|
|
||||||
name: tts-voice-name-adventure-core-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: adventure_core_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: FactCoreAlt
|
|
||||||
name: tts-voice-name-fact-core-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: fact_core_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
|
||||||
id: SpaceCoreAlt
|
|
||||||
name: tts-voice-name-space-core-alt
|
|
||||||
sex: Male
|
|
||||||
speaker: space_core_alt
|
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: TurretFloor
|
id: TurretFloor
|
||||||
name: tts-voice-name-turret-floor
|
name: tts-voice-name-turret-floor
|
||||||
sex: Female
|
sex: Female
|
||||||
speaker: turret_floor
|
speaker: turret_floor
|
||||||
|
roundStart: false
|
||||||
|
borgVoice: true
|
||||||
|
|
||||||
- type: ttsVoice
|
- type: ttsVoice
|
||||||
id: Cirilla
|
id: Cirilla
|
||||||
|
|||||||
Reference in New Issue
Block a user