fix: ТТС снова выдает правильные голоса
This commit is contained in:
@@ -972,7 +972,8 @@ public sealed partial class ChangelingSystem
|
|||||||
ClonePerson(polymorphEntity.Value, transformData.AppearanceComponent, polyAppearance);
|
ClonePerson(polymorphEntity.Value, transformData.AppearanceComponent, polyAppearance);
|
||||||
TransferDna(polymorphEntity.Value, transformData.Dna);
|
TransferDna(polymorphEntity.Value, transformData.Dna);
|
||||||
|
|
||||||
_humanoidAppearance.SetTTSVoice(polymorphEntity.Value, transformData.AppearanceComponent.Voice, polyAppearance);
|
_humanoidAppearance.SetTTSVoice(polymorphEntity.Value, transformData.AppearanceComponent.Voice,
|
||||||
|
humanoid: polyAppearance);
|
||||||
|
|
||||||
if (!TryComp<MetaDataComponent>(polymorphEntity.Value, out var meta))
|
if (!TryComp<MetaDataComponent>(polymorphEntity.Value, out var meta))
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -40,15 +40,15 @@ public sealed partial class HumanoidAppearanceSystem : SharedHumanoidAppearanceS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetHumanoid.Species = sourceHumanoid.Species;
|
SetSpecies(target, sourceHumanoid.Species, false, targetHumanoid);
|
||||||
targetHumanoid.SkinColor = sourceHumanoid.SkinColor;
|
SetSkinColor(target, sourceHumanoid.SkinColor, false, true, targetHumanoid);
|
||||||
targetHumanoid.EyeColor = sourceHumanoid.EyeColor;
|
targetHumanoid.EyeColor = sourceHumanoid.EyeColor;
|
||||||
targetHumanoid.Age = sourceHumanoid.Age;
|
targetHumanoid.Age = sourceHumanoid.Age;
|
||||||
SetSex(target, sourceHumanoid.Sex, false, targetHumanoid);
|
|
||||||
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
|
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
|
||||||
targetHumanoid.MarkingSet = new(sourceHumanoid.MarkingSet);
|
targetHumanoid.MarkingSet = new(sourceHumanoid.MarkingSet);
|
||||||
targetHumanoid.BodyType = sourceHumanoid.BodyType;
|
SetSex(target, sourceHumanoid.Sex, false, targetHumanoid);
|
||||||
SetTTSVoice(target, sourceHumanoid.Voice, targetHumanoid);
|
SetBodyType(target, sourceHumanoid.BodyType, false, targetHumanoid);
|
||||||
|
SetTTSVoice(target, sourceHumanoid.Voice, false, targetHumanoid);
|
||||||
|
|
||||||
targetHumanoid.Gender = sourceHumanoid.Gender;
|
targetHumanoid.Gender = sourceHumanoid.Gender;
|
||||||
if (TryComp<GrammarComponent>(target, out var grammar))
|
if (TryComp<GrammarComponent>(target, out var grammar))
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using Content.Server._White.TTS;
|
|
||||||
using Content.Shared.Humanoid;
|
|
||||||
|
|
||||||
namespace Content.Server.Humanoid;
|
|
||||||
|
|
||||||
public sealed partial class HumanoidAppearanceSystem
|
|
||||||
{
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public void SetTTSVoice(EntityUid uid, string voiceId, HumanoidAppearanceComponent humanoid)
|
|
||||||
{
|
|
||||||
if (!TryComp<TTSComponent>(uid, out var comp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
humanoid.Voice = voiceId;
|
|
||||||
comp.VoicePrototypeId = voiceId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
namespace Content.Server._White.TTS;
|
namespace Content.Server._White.TTS;
|
||||||
|
|
||||||
public sealed class TTSAnnouncementEvent : EntityEventArgs
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public sealed class TTSAnnouncementEvent(string message, string voiceId, EntityUid source, bool global)
|
||||||
|
: EntityEventArgs
|
||||||
{
|
{
|
||||||
public readonly string Message;
|
public readonly string Message = message;
|
||||||
public readonly bool Global;
|
public readonly bool Global = global;
|
||||||
public readonly string VoiceId;
|
public readonly string VoiceId = voiceId;
|
||||||
public readonly EntityUid Source;
|
public readonly EntityUid Source = source;
|
||||||
|
}
|
||||||
public TTSAnnouncementEvent(string message, string voiceId, EntityUid source , bool global)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
Global = global;
|
|
||||||
VoiceId = voiceId;
|
|
||||||
Source = source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using Content.Shared._White.TTS;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Server._White.TTS;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Apply TTS for entity chat say messages
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public sealed partial class TTSComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Prototype of used voice for TTS.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("voice", customTypeSerializer:typeof(PrototypeIdSerializer<TTSVoicePrototype>))]
|
|
||||||
public string VoicePrototypeId { get; set; } = "Eugene";
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ using System.Text.Json.Serialization;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using Content.Shared.CCVar;
|
|
||||||
using Content.Shared._White;
|
using Content.Shared._White;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -18,9 +17,9 @@ public sealed class TTSManager
|
|||||||
private static readonly Histogram RequestTimings = Metrics.CreateHistogram(
|
private static readonly Histogram RequestTimings = Metrics.CreateHistogram(
|
||||||
"tts_req_timings",
|
"tts_req_timings",
|
||||||
"Timings of TTS API requests",
|
"Timings of TTS API requests",
|
||||||
new HistogramConfiguration()
|
new HistogramConfiguration
|
||||||
{
|
{
|
||||||
LabelNames = new[] {"type"},
|
LabelNames = new[] { "type" },
|
||||||
Buckets = Histogram.ExponentialBuckets(.1, 1.5, 10),
|
Buckets = Histogram.ExponentialBuckets(.1, 1.5, 10),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +54,12 @@ public sealed class TTSManager
|
|||||||
/// <param name="text">SSML formatted text</param>
|
/// <param name="text">SSML formatted text</param>
|
||||||
/// <returns>OGG audio bytes</returns>
|
/// <returns>OGG audio bytes</returns>
|
||||||
/// <exception cref="Exception">Throws if url or token CCVar not set or http request failed</exception>
|
/// <exception cref="Exception">Throws if url or token CCVar not set or http request failed</exception>
|
||||||
public async Task<byte[]?> ConvertTextToSpeech(string speaker, string text, string pitch, string rate, string? effect = null)
|
public async Task<byte[]?> ConvertTextToSpeech(
|
||||||
|
string speaker,
|
||||||
|
string text,
|
||||||
|
string pitch,
|
||||||
|
string rate,
|
||||||
|
string? effect = null)
|
||||||
{
|
{
|
||||||
var url = _cfg.GetCVar(WhiteCVars.TtsApiUrl);
|
var url = _cfg.GetCVar(WhiteCVars.TtsApiUrl);
|
||||||
var maxCacheSize = _cfg.GetCVar(WhiteCVars.TtsMaxCacheSize);
|
var maxCacheSize = _cfg.GetCVar(WhiteCVars.TtsMaxCacheSize);
|
||||||
@@ -96,7 +100,7 @@ public sealed class TTSManager
|
|||||||
|
|
||||||
var soundData = await response.Content.ReadAsByteArrayAsync(cts.Token);
|
var soundData = await response.Content.ReadAsByteArrayAsync(cts.Token);
|
||||||
|
|
||||||
if(_cache.Count > maxCacheSize)
|
if (_cache.Count > maxCacheSize)
|
||||||
{
|
{
|
||||||
_cache.Remove(_cache.Last().Key);
|
_cache.Remove(_cache.Last().Key);
|
||||||
}
|
}
|
||||||
@@ -104,7 +108,9 @@ public sealed class TTSManager
|
|||||||
_cache.Add(cacheKey, soundData);
|
_cache.Add(cacheKey, soundData);
|
||||||
CachedCount.Inc();
|
CachedCount.Inc();
|
||||||
|
|
||||||
_sawmill.Debug($"Generated new sound for '{text}' speech by '{speaker}' speaker ({soundData.Length} bytes)");
|
_sawmill.Debug(
|
||||||
|
$"Generated new sound for '{text}' speech by '{speaker}' speaker ({soundData.Length} bytes)");
|
||||||
|
|
||||||
RequestTimings.WithLabels("Success").Observe((DateTime.UtcNow - reqTime).TotalSeconds);
|
RequestTimings.WithLabels("Success").Observe((DateTime.UtcNow - reqTime).TotalSeconds);
|
||||||
|
|
||||||
return soundData;
|
return soundData;
|
||||||
@@ -149,7 +155,7 @@ public sealed class TTSManager
|
|||||||
private string GenerateCacheKey(string speaker, string text)
|
private string GenerateCacheKey(string speaker, string text)
|
||||||
{
|
{
|
||||||
var key = $"{speaker}/{text}";
|
var key = $"{speaker}/{text}";
|
||||||
byte[] keyData = Encoding.UTF8.GetBytes(key);
|
var keyData = Encoding.UTF8.GetBytes(key);
|
||||||
var sha256 = System.Security.Cryptography.SHA256.Create();
|
var sha256 = System.Security.Cryptography.SHA256.Create();
|
||||||
var bytes = sha256.ComputeHash(keyData);
|
var bytes = sha256.ComputeHash(keyData);
|
||||||
return Convert.ToHexString(bytes);
|
return Convert.ToHexString(bytes);
|
||||||
@@ -187,4 +193,4 @@ public sealed class TTSManager
|
|||||||
[JsonPropertyName("audio")]
|
[JsonPropertyName("audio")]
|
||||||
public string Audio { get; set; }
|
public string Audio { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ 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;
|
||||||
@@ -31,7 +30,6 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
[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 StationSystem _stationSystem = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = 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
|
||||||
@@ -44,7 +42,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
_cfg.OnValueChanged(WhiteCVars.TtsApiUrl, url => _apiUrl = url, true);
|
_cfg.OnValueChanged(WhiteCVars.TtsApiUrl, url => _apiUrl = url, true);
|
||||||
|
|
||||||
SubscribeLocalEvent<TransformSpeechEvent>(OnTransformSpeech);
|
SubscribeLocalEvent<TransformSpeechEvent>(OnTransformSpeech);
|
||||||
SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(OnEntitySpoke);
|
SubscribeLocalEvent<SharedTTSComponent, EntitySpokeEvent>(OnEntitySpoke);
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
||||||
|
|
||||||
SubscribeLocalEvent<TTSAnnouncementEvent>(OnAnnounceRequest);
|
SubscribeLocalEvent<TTSAnnouncementEvent>(OnAnnounceRequest);
|
||||||
@@ -80,37 +78,37 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var player in filter.Recipients)
|
foreach (var player in filter.Recipients)
|
||||||
{
|
{
|
||||||
if (player.AttachedEntity != null)
|
if (player.AttachedEntity == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get emergency lights in range to broadcast from
|
||||||
|
var entities = _lookup.GetEntitiesInRange(player.AttachedEntity.Value, 30f)
|
||||||
|
.Where(HasComp<EmergencyLightComponent>)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (entities.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get closest emergency light
|
||||||
|
var entity = entities.First();
|
||||||
|
var range = new Vector2(100f);
|
||||||
|
|
||||||
|
foreach (var item in entities)
|
||||||
{
|
{
|
||||||
// Get emergency lights in range to broadcast from
|
var itemSource = _xforms.GetWorldPosition(Transform(item));
|
||||||
var entities = _lookup.GetEntitiesInRange(player.AttachedEntity.Value, 30f)
|
var playerSource = _xforms.GetWorldPosition(Transform(player.AttachedEntity.Value));
|
||||||
.Where(HasComp<EmergencyLightComponent>)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (entities.Count == 0)
|
var distance = playerSource - itemSource;
|
||||||
return;
|
|
||||||
|
|
||||||
// Get closest emergency light
|
if (range.Length() > distance.Length())
|
||||||
var entity = entities.First();
|
|
||||||
var range = new Vector2(100f);
|
|
||||||
|
|
||||||
foreach (var item in entities)
|
|
||||||
{
|
{
|
||||||
var itemSource = _xforms.GetWorldPosition(Transform(item));
|
range = distance;
|
||||||
var playerSource = _xforms.GetWorldPosition(Transform(player.AttachedEntity.Value));
|
entity = item;
|
||||||
|
|
||||||
var distance = playerSource - itemSource;
|
|
||||||
|
|
||||||
if (range.Length() > distance.Length())
|
|
||||||
{
|
|
||||||
range = distance;
|
|
||||||
entity = item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(entity), soundData, true), Filter.SinglePlayer(player),
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(entity), soundData, true), Filter.SinglePlayer(player),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +119,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_playerManager.TryGetSessionByChannel(ev.MsgChannel, out var session) ||
|
if (!_playerManager.TryGetSessionByChannel(ev.MsgChannel, out var session) ||
|
||||||
!_prototypeManager.TryIndex<TTSVoicePrototype>(ev.VoiceId, out var protoVoice))
|
!_prototypeManager.TryIndex(ev.VoiceId, out var protoVoice))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var soundData = await GenerateTTS(ev.Uid, ev.Text, protoVoice.Speaker);
|
var soundData = await GenerateTTS(ev.Uid, ev.Text, protoVoice.Speaker);
|
||||||
@@ -129,7 +127,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(ev.Uid), soundData, false), Filter.SinglePlayer(session), false);
|
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(ev.Uid), soundData, false), Filter.SinglePlayer(session), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnEntitySpoke(EntityUid uid, TTSComponent component, EntitySpokeEvent args)
|
private async void OnEntitySpoke(EntityUid uid, SharedTTSComponent component, EntitySpokeEvent args)
|
||||||
{
|
{
|
||||||
if (!_isEnabled ||
|
if (!_isEnabled ||
|
||||||
args.Message.Length > MaxMessageChars)
|
args.Message.Length > MaxMessageChars)
|
||||||
@@ -145,7 +143,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
RaiseLocalEvent(uid, voiceEv);
|
RaiseLocalEvent(uid, voiceEv);
|
||||||
voiceId = voiceEv.VoiceId;
|
voiceId = voiceEv.VoiceId;
|
||||||
|
|
||||||
if (!_prototypeManager.TryIndex<TTSVoicePrototype>(voiceId, out var protoVoice))
|
if (!_prototypeManager.TryIndex(voiceId, out var protoVoice))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var message = FormattedMessage.RemoveMarkup(args.Message);
|
var message = FormattedMessage.RemoveMarkup(args.Message);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Robust.Shared.Enums;
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
namespace Content.Shared.Humanoid;
|
||||||
@@ -73,14 +72,17 @@ public sealed partial class HumanoidAppearanceComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current body type.
|
/// Current body type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("bodyType"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public ProtoId<BodyTypePrototype> BodyType = SharedHumanoidAppearanceSystem.DefaultBodyType;
|
public ProtoId<BodyTypePrototype> BodyType = SharedHumanoidAppearanceSystem.DefaultBodyType;
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public Color EyeColor = Color.Brown;
|
public Color EyeColor = Color.Brown;
|
||||||
|
|
||||||
[DataField("voice", customTypeSerializer: typeof(PrototypeIdSerializer<TTSVoicePrototype>)), AutoNetworkedField]
|
/// <summary>
|
||||||
public string Voice { get; set; } = SharedHumanoidAppearanceSystem.DefaultVoice;
|
/// Current TTS voice.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public ProtoId<TTSVoicePrototype> Voice { get; set; } = SharedHumanoidAppearanceSystem.DefaultVoice;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hair color of this humanoid. Used to avoid looping through all markings
|
/// Hair color of this humanoid. Used to avoid looping through all markings
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Shared._White.TTS;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
@@ -28,10 +29,13 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
[ValidatePrototypeId<SpeciesPrototype>]
|
[ValidatePrototypeId<SpeciesPrototype>]
|
||||||
public const string DefaultSpecies = "Human";
|
public const string DefaultSpecies = "Human";
|
||||||
|
|
||||||
|
[ValidatePrototypeId<BodyTypePrototype>]
|
||||||
public const string DefaultBodyType = "HumanNormal";
|
public const string DefaultBodyType = "HumanNormal";
|
||||||
|
|
||||||
|
[ValidatePrototypeId<TTSVoicePrototype>]
|
||||||
public const string DefaultVoice = "Eugene";
|
public const string DefaultVoice = "Eugene";
|
||||||
|
|
||||||
public static readonly Dictionary<Sex, string> DefaultSexVoice = new()
|
public static readonly Dictionary<Sex, ProtoId<TTSVoicePrototype>> DefaultSexVoice = new()
|
||||||
{
|
{
|
||||||
{ Sex.Male, "Eugene" },
|
{ Sex.Male, "Eugene" },
|
||||||
{ Sex.Female, "Kseniya" },
|
{ Sex.Female, "Kseniya" },
|
||||||
@@ -75,7 +79,8 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
var species = GetSpeciesRepresentation(component.Species).ToLower();
|
var species = GetSpeciesRepresentation(component.Species).ToLower();
|
||||||
var age = GetAgeRepresentation(component.Species, component.Age);
|
var age = GetAgeRepresentation(component.Species, component.Age);
|
||||||
|
|
||||||
args.PushText(Loc.GetString("humanoid-appearance-component-examine", ("user", identity), ("age", age), ("species", species)));
|
args.PushText(Loc.GetString("humanoid-appearance-component-examine", ("user", identity), ("age", age),
|
||||||
|
("species", species)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -244,6 +249,36 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a humanoid mob's body yupe. This will change their base sprites.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The humanoid mob's UID.</param>
|
||||||
|
/// <param name="voiceId">The tts voice to set the mob to.</param>
|
||||||
|
/// <param name="sync">Whether to immediately synchronize this to the humanoid mob, or not.</param>
|
||||||
|
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public void SetTTSVoice(
|
||||||
|
EntityUid uid,
|
||||||
|
ProtoId<TTSVoicePrototype> voiceId,
|
||||||
|
bool sync = true,
|
||||||
|
HumanoidAppearanceComponent? humanoid = null)
|
||||||
|
{
|
||||||
|
if (!TryComp<SharedTTSComponent>(uid, out var comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref humanoid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
humanoid.Voice = voiceId;
|
||||||
|
comp.VoicePrototypeId = voiceId;
|
||||||
|
if (sync)
|
||||||
|
{
|
||||||
|
Dirty(uid, humanoid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the base layer ID of this humanoid mob. A humanoid mob's 'base layer' is
|
/// Sets the base layer ID of this humanoid mob. A humanoid mob's 'base layer' is
|
||||||
/// the skin sprite that is applied to the mob's sprite upon appearance refresh.
|
/// the skin sprite that is applied to the mob's sprite upon appearance refresh.
|
||||||
@@ -353,7 +388,8 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
humanoid.EyeColor = profile.Appearance.EyeColor;
|
humanoid.EyeColor = profile.Appearance.EyeColor;
|
||||||
|
|
||||||
SetSkinColor(uid, profile.Appearance.SkinColor, false);
|
SetSkinColor(uid, profile.Appearance.SkinColor, false);
|
||||||
SetBodyType(uid, profile.BodyType, false);
|
SetBodyType(uid, profile.BodyType, false, humanoid);
|
||||||
|
SetTTSVoice(uid, profile.Voice, false, humanoid);
|
||||||
|
|
||||||
humanoid.MarkingSet.Clear();
|
humanoid.MarkingSet.Clear();
|
||||||
|
|
||||||
@@ -545,4 +581,4 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
|
|
||||||
return Loc.GetString("identity-age-old");
|
return Loc.GetString("identity-age-old");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Lidgren.Network;
|
using Lidgren.Network;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared._White.TTS;
|
namespace Content.Shared._White.TTS;
|
||||||
@@ -10,8 +11,8 @@ public sealed class MsgRequestTTS : NetMessage
|
|||||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||||
|
|
||||||
public EntityUid Uid { get; set; } = EntityUid.Invalid;
|
public EntityUid Uid { get; set; } = EntityUid.Invalid;
|
||||||
public string Text { get; set; } = String.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
public string VoiceId { get; set; } = String.Empty;
|
public ProtoId<TTSVoicePrototype> VoiceId { get; set; } = string.Empty;
|
||||||
|
|
||||||
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,16 +4,11 @@ namespace Content.Shared._White.TTS;
|
|||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public sealed class PlayTTSEvent : EntityEventArgs
|
public sealed class PlayTTSEvent(NetEntity uid, byte[] data, bool boostVolume) : EntityEventArgs
|
||||||
{
|
{
|
||||||
public NetEntity Uid { get; }
|
public NetEntity Uid { get; } = uid;
|
||||||
public byte[] Data { get; }
|
|
||||||
public bool BoostVolume { get; }
|
|
||||||
|
|
||||||
public PlayTTSEvent(NetEntity uid, byte[] data, bool boostVolume)
|
public byte[] Data { get; } = data;
|
||||||
{
|
|
||||||
Uid = uid;
|
public bool BoostVolume { get; } = boostVolume;
|
||||||
Data = data;
|
}
|
||||||
BoostVolume = boostVolume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,12 +4,7 @@ namespace Content.Shared._White.TTS;
|
|||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public sealed class RequestTTSEvent : EntityEventArgs
|
public sealed class RequestTTSEvent(string text) : EntityEventArgs
|
||||||
{
|
{
|
||||||
public string Text { get; }
|
public string Text { get; } = text;
|
||||||
|
}
|
||||||
public RequestTTSEvent(string text)
|
|
||||||
{
|
|
||||||
Text = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
Content.Shared/_White/TTS/SharedTTSComponent.cs
Normal file
17
Content.Shared/_White/TTS/SharedTTSComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared._White.TTS;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply TTS for entity chat say messages
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, AutoGenerateComponentState]
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public sealed partial class SharedTTSComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype of used voice for TTS.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||||
|
public ProtoId<TTSVoicePrototype> VoicePrototypeId { get; set; } = "Eugene";
|
||||||
|
}
|
||||||
@@ -3,12 +3,7 @@
|
|||||||
namespace Content.Shared.VoiceMask;
|
namespace Content.Shared.VoiceMask;
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class VoiceMaskChangeVoiceMessage : BoundUserInterfaceMessage
|
public sealed class VoiceMaskChangeVoiceMessage(string voice) : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
public string Voice { get; }
|
public string Voice { get; } = voice;
|
||||||
|
}
|
||||||
public VoiceMaskChangeVoiceMessage(string voice)
|
|
||||||
{
|
|
||||||
Voice = voice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Preferences;
|
|
||||||
|
|
||||||
namespace Content.Shared._White.TTS;
|
namespace Content.Shared._White.TTS;
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
public sealed class TTSPitchRateSystem : EntitySystem
|
public sealed class TTSPitchRateSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -10,28 +10,27 @@ namespace Content.Shared._White.TTS;
|
|||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public sealed class TTSVoicePrototype : IPrototype
|
public sealed class TTSVoicePrototype : IPrototype
|
||||||
{
|
{
|
||||||
[IdDataFieldAttribute]
|
[IdDataField]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
[DataField("name")]
|
[DataField]
|
||||||
public string Name { get; } = string.Empty;
|
public string Name { get; } = string.Empty;
|
||||||
|
|
||||||
[DataField("sex", required: true)]
|
[DataField(required: true)]
|
||||||
public Sex Sex { get; } = default!;
|
public Sex Sex { get; }
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite), DataField(required: true)]
|
||||||
[DataField("speaker", required: true)]
|
|
||||||
public string Speaker { get; } = string.Empty;
|
public string Speaker { get; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the species is available "at round start" (In the character editor)
|
/// Whether the species is available "at round start" (In the character editor)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("roundStart")]
|
[DataField]
|
||||||
public bool RoundStart { get; } = true;
|
public bool RoundStart { get; } = true;
|
||||||
|
|
||||||
[DataField("sponsorOnly")]
|
[DataField]
|
||||||
public bool SponsorOnly { get; } = false;
|
public bool SponsorOnly { get; }
|
||||||
|
|
||||||
[DataField("borgVoice")]
|
[DataField]
|
||||||
public bool BorgVoice { get; } = false;
|
public bool BorgVoice { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user