Repository: SerbiaStrong-220/space-station-14
Pull request #2925: Telepathy tweaks
Author: login: SkaldetSkaeg
Labels: name: Changes: C#
description: Изменение затронуло файлы ".cs"
Created: 2025-05-14T11:28:28Z | Merged: 2025-05-19T10:39:35Z

<!-- ЭТО ШАБЛОН ВАШЕГО PULL REQUEST. Текст между стрелками - это комментарии - они не будут видны в PR. -->

## Описание PR
<!-- Ниже опишите ваш Pull Request. Что он изменяет? На что еще это может повлиять? Постарайтесь описать все внесённые вами изменения! -->
Внимание, работает дилетант, который впервые трогает TTS.
Добавил TTS для телепатии + добавил логи для админов, так как их просто нету.

**Медиа**
<!-- Если приемлемо, добавьте скриншоты для демонстрации вашего PR. Если ваш PR представляет собой визуальное изменение, добавьте
скриншоты, иначе он может быть закрыт.

Ссылки на медиа (изображения/видео) будут добавлены в список изменений на дискорд сервере (не более 10 ссылок каждого вида и 8 МБ суммарного веса (для каждого вида)).
Всё что записано между тегами "CLIgnore" будет игнорироваться при отправке списка изменений.-->

<!--CLIgnore-->
**Проверки**
<!-- Выполнение всех следующих действий, если это приемлемо для вида изменений сильно ускорит разбор вашего PR -->
- [x] PR полностью завершён и мне не нужна помощь чтобы его закончить.
- [x] Я **ознакомился** с [наставлениями по работе с репозиторием](https://serbiastrong-220.github.io/ss220-docs/development/ss220-guidelines/) и следовал им при создании PR'а.
- [x] Я внимательно просмотрел все свои изменения и багов в них не нашёл.
- [x] Я запускал локальный сервер со своими изменениями и всё протестировал.
- [x] Я добавил скриншот/видео демонстрации PR в игре, **или** этот PR этого не требует.
<!--/CLIgnore-->

**Изменения**
<!--
Здесь вы можете написать список изменений, который будет автоматически добавлен в игру и на дискорд сервер, когда ваш PR будет принят.

В журнал изменений следует помещать только то, что действительно важно игрокам.

В списке изменений тип значка не является часть предложения, поэтому явно указывайте - Добавлен, Удалён, Изменён.
плохо: - add: Новый инструмент для инженеров
хорошо: - add: Добавлен новый инструмент для инженеров

Вы можете указать своё имя после символа :cl: именно оно будет отображаться в журнале изменений (иначе будет использоваться ваше имя на GitHub)
Например: :cl: Ian

-->
:cl:
- add: Добавлен TTS для телепатии (пока только для тех, кто умеет говорить)



Diff:
diff --git a/Content.Server/SS220/TTS/TTSManager.cs b/Content.Server/SS220/TTS/TTSManager.cs
index 1f97136d6d031..6e274dc3a7c36 100644
--- a/Content.Server/SS220/TTS/TTSManager.cs
+++ b/Content.Server/SS220/TTS/TTSManager.cs
@@ -129,6 +129,11 @@ public void Initialize()
                     requestUrl += "&effect=announce";
                 }
 
+                if (kind == TtsKind.Telepathy)
+                {
+                    requestUrl += "&effect=announce";
+                }
+
                 var httpRequest = new HttpRequestMessage(HttpMethod.Get, requestUrl);
                 var httpResponse = await _httpClient.SendAsync(httpRequest, cts.Token);
                 if (!httpResponse.IsSuccessStatusCode)
diff --git a/Content.Server/SS220/TTS/TTSSystem.cs b/Content.Server/SS220/TTS/TTSSystem.cs
index 1f61f7f7cde7e..25b5a7410a998 100644
--- a/Content.Server/SS220/TTS/TTSSystem.cs
+++ b/Content.Server/SS220/TTS/TTSSystem.cs
@@ -52,6 +52,7 @@ public override void Initialize()
         SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(OnEntitySpoke);
         SubscribeLocalEvent<RadioSpokeEvent>(OnRadioReceiveEvent);
         SubscribeLocalEvent<AnnouncementSpokeEvent>(OnAnnouncementSpoke);
+        SubscribeLocalEvent<TelepathySpokeEvent>(OnTelepathySpoke);
         SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
         SubscribeLocalEvent<TTSComponent, MapInitEvent>(OnInit);
 
@@ -467,6 +468,48 @@ private async void HandleRadio(RadioEventReceiver[] receivers, string message, s
         }
     }
 
+    private async void OnTelepathySpoke(TelepathySpokeEvent args)
+    {
+        if (args.Receivers.Length == 0)
+            return;
+
+        if (!TryComp(args.Source, out TTSComponent? senderComponent))
+            return;
+
+        var voiceId = senderComponent.VoicePrototypeId;
+        if (voiceId == null)
+            return;
+
+        if (TryGetVoiceMaskUid(args.Source, out var maskUid))
+        {
+            var voiceEv = new TransformSpeakerVoiceEvent(maskUid.Value, voiceId);
+            RaiseLocalEvent(maskUid.Value, voiceEv);
+            voiceId = voiceEv.VoiceId;
+        }
+
+        if (!GetVoicePrototype(voiceId, out var protoVoice))
+        {
+            return;
+        }
+
+        using var soundData = await GenerateTts(args.Message, protoVoice.Speaker, TtsKind.Telepathy);
+        if (soundData is null)
+            return;
+
+        foreach (var receiver in args.Receivers)
+        {
+            if (!_playerManager.TryGetSessionByEntity(receiver, out var session)
+                || !soundData.TryGetValue(out var audioData))
+                continue;
+            _netManager.ServerSendMessage(new MsgPlayTts
+            {
+                Data = audioData,
+                SourceUid = GetNetEntity(receiver),
+                Kind = TtsKind.Telepathy
+            }, session.Channel);
+        }
+    }
+
     private async Task<ReferenceCounter<TtsAudioData>.Handle?> GenerateTts(string text, string speaker, TtsKind kind)
     {
         try
diff --git a/Content.Server/SS220/Telepathy/TelepathySystem.cs b/Content.Server/SS220/Telepathy/TelepathySystem.cs
index 8d03334d56242..271604eff5939 100644
--- a/Content.Server/SS220/Telepathy/TelepathySystem.cs
+++ b/Content.Server/SS220/Telepathy/TelepathySystem.cs
@@ -1,8 +1,12 @@
 // © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
 
+using Content.Server.Administration.Logs;
+using Content.Server.Chat.Systems;
 using Content.Shared.Chat;
+using Content.Shared.Database;
 using Content.Shared.GameTicking;
 using Content.Shared.SS220.Telepathy;
+using Content.Shared.SS220.TTS;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
@@ -15,6 +19,7 @@ namespace Content.Server.SS220.Telepathy;
 /// </summary>
 public sealed class TelepathySystem : EntitySystem
 {
+    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
     [Dependency] private readonly INetManager _netMan = default!;
     [Dependency] private readonly IEntityManager _entityManager = default!;
     [Dependency] private readonly IPrototypeManager _prototype = default!;
@@ -111,21 +116,39 @@ private ProtoId<TelepathyChannelPrototype> MakeNewDynamicChannel(string? nameLoc
     private void SendMessageToEveryoneWithRightChannel(ProtoId<TelepathyChannelPrototype> rightTelepathyChannel, string message, EntityUid? senderUid)
     {
         ChannelParameters? channelParameters = null;
+
         if (_dynamicChannels.TryGetValue(rightTelepathyChannel, out var dynamicParameters))
             channelParameters = dynamicParameters;
+
         if (_prototype.TryIndex(rightTelepathyChannel, out var prototype))
             channelParameters = prototype.ChannelParameters;
+
         if (channelParameters == null)
         {
             Log.Error($"Tried to send message with incorrect {nameof(TelepathyChannelPrototype)} proto id. id was: {rightTelepathyChannel}");
             return;
         }
 
+        List<EntityUid> telephatyTtsRecievers = [];
+
         var telepathyQuery = EntityQueryEnumerator<TelepathyComponent>();
         while (telepathyQuery.MoveNext(out var receiverUid, out var receiverTelepathy))
         {
             if (rightTelepathyChannel == receiverTelepathy.TelepathyChannelPrototype || receiverTelepathy.ReceiveAllChannels)
+            {
                 SendMessageToChat(receiverUid, message, senderUid, channelParameters);
+                telephatyTtsRecievers.Add(receiverUid);
+            }
+        }
+
+        if (senderUid != null)
+            _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Telepathy message were send from {ToPrettyString(senderUid):user}: {message}, telepathy channel: {rightTelepathyChannel.Id}");
+        else
+            _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Announce telepathy message: {message}, were send in telepathy channel: {rightTelepathyChannel.Id}");
+
+        if (senderUid != null && HasComp<TTSComponent>(senderUid))
+        {
+            RaiseLocalEvent(new TelepathySpokeEvent(senderUid.Value, message, [.. telephatyTtsRecievers]));
         }
     }
 
diff --git a/Content.Shared/SS220/TTS/SharedTtsEvents.cs b/Content.Shared/SS220/TTS/SharedTtsEvents.cs
new file mode 100644
index 0000000000000..83db19e67cdcf
--- /dev/null
+++ b/Content.Shared/SS220/TTS/SharedTtsEvents.cs
@@ -0,0 +1,10 @@
+// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
+
+namespace Content.Shared.SS220.TTS;
+
+public sealed class TelepathySpokeEvent(EntityUid source, string message, EntityUid[] receivers) : EntityEventArgs
+{
+    public readonly EntityUid Source = source;
+    public readonly string Message = message;
+    public readonly EntityUid[] Receivers = receivers;
+}
diff --git a/Content.Shared/SS220/TTS/TtsKind.cs b/Content.Shared/SS220/TTS/TtsKind.cs
index f2516e94d8ea2..da70dfb526e20 100644
--- a/Content.Shared/SS220/TTS/TtsKind.cs
+++ b/Content.Shared/SS220/TTS/TtsKind.cs
@@ -8,4 +8,5 @@ public enum TtsKind
     Radio,
     Whisper,
     Announce,
+    Telepathy,
 }


Reviews:
user: login: stalengd
body: \+ проверить работу на локалке
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T18:09:30Z
user: login: SkaldetSkaeg
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-18T11:13:17Z
user: login: UrPrice
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-18T13:48:45Z
user: login: stalengd
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-19T10:38:05Z

Review comments:
diff_hunk: @@ -111,21 +118,39 @@ private ProtoId<TelepathyChannelPrototype> MakeNewDynamicChannel(string? nameLoc
     private void SendMessageToEveryoneWithRightChannel(ProtoId<TelepathyChannelPrototype> rightTelepathyChannel, string message, EntityUid? senderUid)
     {
         ChannelParameters? channelParameters = null;
+
         if (_dynamicChannels.TryGetValue(rightTelepathyChannel, out var dynamicParameters))
             channelParameters = dynamicParameters;
+
         if (_prototype.TryIndex(rightTelepathyChannel, out var prototype))
             channelParameters = prototype.ChannelParameters;
+
         if (channelParameters == null)
         {
             Log.Error($"Tried to send message with incorrect {nameof(TelepathyChannelPrototype)} proto id. id was: {rightTelepathyChannel}");
             return;
         }
 
+        List<EntityUid> telephatyTTSrecievers = [];
path: Content.Server/SS220/Telepathy/TelepathySystem.cs
user: login: stalengd
body: ```suggestion
        List<EntityUid> telepathyTtsRecievers = [];
```
created_at: 2025-05-17T18:04:33Z
author_association: COLLABORATOR
path: Content.Shared/SS220/TTS/SharedTTSEvents.cs
user: login: stalengd
body: Переименовать файл в `SharedTtsEvents.cs`
created_at: 2025-05-17T18:07:10Z
author_association: COLLABORATOR
diff_hunk: @@ -1,12 +1,18 @@
 // © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
 
+using Content.Server.Administration.Logs;
+using Content.Server.Chat.Systems;
 using Content.Shared.Chat;
+using Content.Shared.Database;
 using Content.Shared.GameTicking;
 using Content.Shared.SS220.Telepathy;
+using Content.Shared.SS220.TTS;
+using Npgsql.Replication.PgOutput.Messages;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Xml.Linq;
path: Content.Server/SS220/Telepathy/TelepathySystem.cs
user: login: stalengd
body: Здесь точно нужны `Npgsql.Replication.PgOutput.Messages` и `System.Xml.Linq`?
created_at: 2025-05-17T18:08:53Z
author_association: COLLABORATOR
diff_hunk: @@ -1,12 +1,18 @@
 // © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
 
+using Content.Server.Administration.Logs;
+using Content.Server.Chat.Systems;
 using Content.Shared.Chat;
+using Content.Shared.Database;
 using Content.Shared.GameTicking;
 using Content.Shared.SS220.Telepathy;
+using Content.Shared.SS220.TTS;
+using Npgsql.Replication.PgOutput.Messages;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Xml.Linq;
path: Content.Server/SS220/Telepathy/TelepathySystem.cs
user: login: SkaldetSkaeg
body: > Здесь точно нужны `Npgsql.Replication.PgOutput.Messages` и `System.Xml.Linq`?

хз, что мне срет визуалка этими либами
created_at: 2025-05-18T11:13:16Z
author_association: NONE

PR comments:
user: login: UrPrice
created_at: 2025-05-16T08:15:52Z
body: Там стандартное звучание ттс для телепатии? Или оно как-то отличается от стандартов? 

У всех объектов с возможной телепатией прописан ттс?
author_association: COLLABORATOR
user: login: SkaldetSkaeg
created_at: 2025-05-16T08:42:42Z
body: > Там стандартное звучание ттс для телепатии? Или оно как-то отличается от стандартов?
> 
> У всех объектов с возможной телепатией прописан ттс?

Сделал так, как понял: только для тех у кого есть ТТС и без эффектов. 
Это лучше чем вобще ничего, может потом пойму как доработать.
author_association: NONE
user: login: UrPrice
created_at: 2025-05-16T12:35:59Z
body: > > Там стандартное звучание ттс для телепатии? Или оно как-то отличается от стандартов?
> > У всех объектов с возможной телепатией прописан ттс?
> 
> Сделал так, как понял: только для тех у кого есть ТТС и без эффектов. Это лучше чем вобще ничего, может потом пойму как доработать.

Посмотри как аннонусер работает. хотели вроде его эффект накинуть с каким-нибудь звуком. Можешь просто эффект без доп. звуков, и чуть приглушенный сам ТТС. Ибо телепатия звучит из ниоткуда, ей надо быть немного выделяющейся.
author_association: COLLABORATOR
user: login: SkaldetSkaeg
created_at: 2025-05-16T14:41:39Z
body: > Посмотри как аннонусер работает. хотели вроде его эффект накинуть с каким-нибудь звуком. Можешь просто эффект без доп. звуков, и чуть приглушенный сам ТТС. Ибо телепатия звучит из ниоткуда, ей надо быть немного выделяющейся.

не совсем уверен как адекватно сделать "с каким-нибудь звуком", вобще эту штуку не собирался трогать так как не знаю, а поломать можно легко.

Про эффекты -- нет полного списка, могу разве что по аналогии с имеющимися накинуть
author_association: NONE
user: login: UrPrice
created_at: 2025-05-17T11:10:54Z
body: Ну, потому я и написал

> Можешь просто эффект без доп. звуков

И потому же я также подсветил

> Посмотри как аннонусер работает

Чтобы сделать по аналогии
author_association: COLLABORATOR
user: login: SkaldetSkaeg
created_at: 2025-05-18T11:26:16Z
body: > + проверить работу на локалке

проверил, работает. Единственное эффекты не почувствовал, возможно на локалке не воспроизводятся
author_association: NONE
user: login: SkaldetSkaeg
created_at: 2025-05-18T12:31:16Z
body: эффекты послушал, только анонс +- норм звучит, всё доделано
author_association: NONE