diff --git a/Content.Client/ADT/NanoChat/StationAiNanoChatBoundUserInterface.cs b/Content.Client/ADT/NanoChat/StationAiNanoChatBoundUserInterface.cs new file mode 100644 index 00000000000..ea1254efeb2 --- /dev/null +++ b/Content.Client/ADT/NanoChat/StationAiNanoChatBoundUserInterface.cs @@ -0,0 +1,48 @@ +using Content.Client.ADT.CartridgeLoader.Cartridges; +using Content.Shared.ADT.CartridgeLoader.Cartridges; +using Content.Shared.ADT.NanoChat; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client.ADT.NanoChat; + +[UsedImplicitly] +public sealed class StationAiNanoChatBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + private StationAiNanoChatWindow? _window; + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + _window.Fragment.OnMessageSent += OnMessageSent; + } + + private void OnMessageSent(NanoChatUiMessageType type, uint? number, string? content, string? job) + { + SendMessage(new StationAiNanoChatUiMessage(type, number, content, job)); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not NanoChatUiState nanoChatState || _window == null) + return; + + _window.UpdateState(nanoChatState); + } + + protected override void Dispose(bool disposing) + { + if (_window != null) + { + _window.Fragment.OnMessageSent -= OnMessageSent; + _window.Dispose(); + _window = null; + } + + base.Dispose(disposing); + } +} diff --git a/Content.Client/ADT/NanoChat/StationAiNanoChatWindow.cs b/Content.Client/ADT/NanoChat/StationAiNanoChatWindow.cs new file mode 100644 index 00000000000..bbe60c8a2df --- /dev/null +++ b/Content.Client/ADT/NanoChat/StationAiNanoChatWindow.cs @@ -0,0 +1,26 @@ +using System.Numerics; +using Content.Client.ADT.CartridgeLoader.Cartridges; +using Content.Shared.ADT.CartridgeLoader.Cartridges; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; + +namespace Content.Client.ADT.NanoChat; + +public sealed class StationAiNanoChatWindow : DefaultWindow +{ + public readonly NanoChatUiFragment Fragment; + + public StationAiNanoChatWindow() + { + Title = Loc.GetString("station-ai-nanochat-window-title"); + MinSize = new Vector2(600, 400); + + Fragment = new NanoChatUiFragment(); + Contents.AddChild(Fragment); + } + + public void UpdateState(NanoChatUiState state) + { + Fragment.UpdateState(state); + } +} diff --git a/Content.Server/ADT/CartridgeLoader/Cartridges/NanoChatCartridgeSystem.cs b/Content.Server/ADT/CartridgeLoader/Cartridges/NanoChatCartridgeSystem.cs index aeb21361a06..8f852945117 100644 --- a/Content.Server/ADT/CartridgeLoader/Cartridges/NanoChatCartridgeSystem.cs +++ b/Content.Server/ADT/CartridgeLoader/Cartridges/NanoChatCartridgeSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.ADT.NanoChat; using Content.Server.Administration.Logs; using Content.Server.CartridgeLoader; using Content.Server.Power.Components; @@ -10,7 +11,9 @@ using Content.Shared.ADT.CartridgeLoader.Cartridges; using Content.Shared.ADT.NanoChat; using Content.Shared.PDA; +using Content.Shared.Radio; using Content.Shared.Radio.Components; +using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -135,32 +138,41 @@ private bool GetCardEntity( /// private void HandleNewChat(Entity card, NanoChatUiMessageEvent msg) { - if (msg.RecipientNumber == null || msg.Content == null || msg.RecipientNumber == card.Comp.Number) + NewChat(card, msg.RecipientNumber, msg.Content, msg.RecipientJob, msg.Actor); + } + + /// + /// Creates a new chat conversation on a card. + /// + [PublicAPI] + public void NewChat(Entity card, uint? recipientNumber, string? content, string? recipientJob, EntityUid actor) + { + if (recipientNumber == null || content == null || recipientNumber == card.Comp.Number) return; - var name = msg.Content; + var name = content; if (!string.IsNullOrWhiteSpace(name)) { name = name.Trim(); } - var jobTitle = msg.RecipientJob; + var jobTitle = recipientJob; if (!string.IsNullOrWhiteSpace(jobTitle)) { jobTitle = jobTitle.Trim(); } // Add new recipient - var recipient = new NanoChatRecipient(msg.RecipientNumber.Value, + var recipient = new NanoChatRecipient(recipientNumber.Value, name, jobTitle); // Initialize or update recipient - _nanoChat.SetRecipient((card, card.Comp), msg.RecipientNumber.Value, recipient); + _nanoChat.SetRecipient((card, card.Comp), recipientNumber.Value, recipient); _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(msg.Actor):user} created new NanoChat conversation with #{msg.RecipientNumber:D4} ({name})"); + $"{ToPrettyString(actor):user} created new NanoChat conversation with #{recipientNumber:D4} ({name})"); var recipientEv = new NanoChatRecipientUpdatedEvent(card); RaiseLocalEvent(ref recipientEv); @@ -172,16 +184,25 @@ private void HandleNewChat(Entity card, NanoChatUiMessage /// private void HandleSelectChat(Entity card, NanoChatUiMessageEvent msg) { - if (msg.RecipientNumber == null) + SelectChat(card, msg.RecipientNumber); + } + + /// + /// Selects a chat conversation on a card. + /// + [PublicAPI] + public void SelectChat(Entity card, uint? recipientNumber) + { + if (recipientNumber == null) return; - _nanoChat.SetCurrentChat((card, card.Comp), msg.RecipientNumber); + _nanoChat.SetCurrentChat((card, card.Comp), recipientNumber); // Clear unread flag when selecting chat - if (_nanoChat.GetRecipient((card, card.Comp), msg.RecipientNumber.Value) is { } recipient) + if (_nanoChat.GetRecipient((card, card.Comp), recipientNumber.Value) is { } recipient) { _nanoChat.SetRecipient((card, card.Comp), - msg.RecipientNumber.Value, + recipientNumber.Value, recipient with { HasUnread = false }); } } @@ -190,6 +211,15 @@ private void HandleSelectChat(Entity card, NanoChatUiMess /// Handles closing the current chat conversation. /// private void HandleCloseChat(Entity card) + { + CloseChat(card); + } + + /// + /// Closes the current chat conversation on a card. + /// + [PublicAPI] + public void CloseChat(Entity card) { _nanoChat.SetCurrentChat((card, card.Comp), null); } @@ -199,18 +229,27 @@ private void HandleCloseChat(Entity card) /// private void HandleDeleteChat(Entity card, NanoChatUiMessageEvent msg) { - if (msg.RecipientNumber == null || card.Comp.Number == null) + DeleteChat(card, msg.RecipientNumber, msg.Actor); + } + + /// + /// Deletes a chat conversation on a card. + /// + [PublicAPI] + public void DeleteChat(Entity card, uint? recipientNumber, EntityUid actor) + { + if (recipientNumber == null || card.Comp.Number == null) return; // Delete chat but keep the messages - var deleted = _nanoChat.TryDeleteChat((card, card.Comp), msg.RecipientNumber.Value, true); + var deleted = _nanoChat.TryDeleteChat((card, card.Comp), recipientNumber.Value, true); if (!deleted) return; _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(msg.Actor):user} deleted NanoChat conversation with #{msg.RecipientNumber:D4}"); + $"{ToPrettyString(actor):user} deleted NanoChat conversation with #{recipientNumber:D4}"); UpdateUIForCard(card); } @@ -219,12 +258,30 @@ private void HandleDeleteChat(Entity card, NanoChatUiMess /// Handles toggling notification mute state. /// private void HandleToggleMute(Entity card) + { + ToggleMute(card); + } + + /// + /// Toggles notification mute state on a card. + /// + [PublicAPI] + public void ToggleMute(Entity card) { _nanoChat.SetNotificationsMuted((card, card.Comp), !_nanoChat.GetNotificationsMuted((card, card.Comp))); UpdateUIForCard(card); } private void HandleToggleListNumber(Entity card) + { + ToggleListNumber(card); + } + + /// + /// Toggles card number visibility in the station directory. + /// + [PublicAPI] + public void ToggleListNumber(Entity card) { _nanoChat.SetListNumber((card, card.Comp), !_nanoChat.GetListNumber((card, card.Comp))); UpdateUIForAllCards(); @@ -237,13 +294,25 @@ private void HandleSendMessage(Entity cartridge, Entity card, NanoChatUiMessageEvent msg) { - if (msg.RecipientNumber == null || msg.Content == null || card.Comp.Number == null) + SendMessage(cartridge, card, msg.RecipientNumber, msg.Content, cartridge.Comp.RadioChannel); + } + + /// + /// Sends a NanoChat message from a card, attempting delivery through the radio channel. + /// + [PublicAPI] + public void SendMessage(EntityUid sender, + Entity card, + uint? recipientNumber, + string? content, + ProtoId channelId) + { + if (recipientNumber == null || content == null || card.Comp.Number == null) return; - if (!EnsureRecipientExists(card, msg.RecipientNumber.Value)) + if (!EnsureRecipientExists(card, recipientNumber.Value)) return; - var content = msg.Content; if (!string.IsNullOrWhiteSpace(content)) { content = FormattedMessage.EscapeText(content.Trim()); @@ -259,18 +328,18 @@ private void HandleSendMessage(Entity cartridge, ); // Attempt delivery - var (deliveryFailed, recipients) = AttemptMessageDelivery(cartridge, msg.RecipientNumber.Value); + var (deliveryFailed, recipients) = AttemptMessageDeliveryInternal(sender, recipientNumber.Value, channelId); // Update delivery status message = message with { DeliveryFailed = deliveryFailed }; // Store message in sender's outbox under recipient's number - _nanoChat.AddMessage((card, card.Comp), msg.RecipientNumber.Value, message); + _nanoChat.AddMessage((card, card.Comp), recipientNumber.Value, message); // Log message attempt var recipientsText = recipients.Count > 0 ? string.Join(", ", recipients.Select(r => ToPrettyString(r))) - : $"#{msg.RecipientNumber:D4}"; + : $"#{recipientNumber:D4}"; _adminLogger.Add(LogType.Chat, LogImpact.Low, @@ -300,17 +369,20 @@ private bool EnsureRecipientExists(Entity card, uint reci } /// - /// Attempts to deliver a message to recipients. + /// Attempts to deliver a message to recipients, including cards held by a station AI. /// - /// The sending cartridge entity + /// The sending entity (cartridge or station AI) /// The recipient's number + /// The radio channel used for delivery /// Tuple containing delivery status and recipients if found. - private (bool failed, List> recipient) AttemptMessageDelivery( - Entity sender, - uint recipientNumber) + [PublicAPI] + public (bool failed, List> recipient) AttemptMessageDeliveryInternal( + EntityUid sender, + uint recipientNumber, + ProtoId channelId) { // First verify we can send from this device - var channel = _prototype.Index(sender.Comp.RadioChannel); + var channel = _prototype.Index(channelId); var sendAttemptEvent = new RadioSendAttemptEvent(channel, sender); RaiseLocalEvent(ref sendAttemptEvent); if (sendAttemptEvent.Cancelled) @@ -331,11 +403,14 @@ private bool EnsureRecipientExists(Entity card, uint reci if (foundRecipients.Count == 0) return (true, foundRecipients); + var senderStation = _station.GetOwningStation(sender); + // Now check if any of these cards can receive var deliverableRecipients = new List>(); foreach (var recipient in foundRecipients) { // Find any cartridges that have this card + var foundCartridge = false; var cartridgeQuery = EntityQueryEnumerator(); while (cartridgeQuery.MoveNext(out var receiverUid, out var receiverCart, out _)) { @@ -344,7 +419,6 @@ private bool EnsureRecipientExists(Entity card, uint reci // Check if devices are on same station/map var recipientStation = _station.GetOwningStation(receiverUid); - var senderStation = _station.GetOwningStation(sender); // Both entities must be on a station if (recipientStation == null || senderStation == null) @@ -366,8 +440,38 @@ private bool EnsureRecipientExists(Entity card, uint reci // Found valid cartridge that can receive deliverableRecipients.Add(recipient); + foundCartridge = true; break; // Only need one valid cartridge per card } + + if (foundCartridge) + continue; + + // Cards held by a station AI (e.g. in a core) can receive without a cartridge + if (!HasComp(recipient.Owner)) + continue; + + var aiRecipientStation = _station.GetOwningStation(recipient.Owner); + + // Both entities must be on a station + if (aiRecipientStation == null || senderStation == null) + continue; + + // Must be on same map/station unless long range allowed + if (!channel.LongRange && aiRecipientStation != senderStation) + continue; + + // Needs telecomms + if (!HasActiveServer(senderStation.Value) || !HasActiveServer(aiRecipientStation.Value)) + continue; + + // Check if recipient can receive + var aiReceiveAttemptEv = new RadioReceiveAttemptEvent(channel, sender, recipient.Owner); + RaiseLocalEvent(ref aiReceiveAttemptEv); + if (aiReceiveAttemptEv.Cancelled) + continue; + + deliverableRecipients.Add(recipient); } return (deliverableRecipients.Count == 0, deliverableRecipients); @@ -397,7 +501,8 @@ private bool HasActiveServer(EntityUid station) /// The sender's card entity /// The recipient's card entity /// The to deliver - private void DeliverMessageToRecipient(Entity sender, + [PublicAPI] + public void DeliverMessageToRecipient(Entity sender, Entity recipient, NanoChatMessage message) { @@ -473,7 +578,8 @@ private void UpdateUIForCard(EntityUid cardUid) /// /// Updates the UI for all PDAs containing a NanoChat cartridge. /// - private void UpdateUIForAllCards() + [PublicAPI] + public void UpdateUIForAllCards() { // Find any PDA containing this card and update its UI var query = EntityQueryEnumerator(); @@ -487,7 +593,8 @@ private void UpdateUIForAllCards() /// /// Gets the for a given NanoChat number. /// - private NanoChatRecipient? GetCardInfo(uint number) + [PublicAPI] + public NanoChatRecipient? GetCardInfo(uint number) { // Find card with this number to get its info var query = EntityQueryEnumerator(); @@ -504,6 +611,11 @@ private void UpdateUIForAllCards() jobTitle = idCard.LocalizedJobTitle; name = idCard.FullName ?? name; } + else if (HasComp(uid)) + { + name = Name(uid); + jobTitle = Loc.GetString("job-name-station-ai"); + } return new NanoChatRecipient(number, name, jobTitle); } @@ -544,6 +656,16 @@ private void UpdateUI(Entity ent, EntityUid loader) contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName)); } } + + // Cards held by a station AI are visible in the directory as well + var aiQuery = AllEntityQuery(); + while (aiQuery.MoveNext(out var entityId, out var nanoChatCard, out _)) + { + if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(entityId) == station) + { + contacts.Add(new NanoChatRecipient(nanoChatNumber, Name(entityId))); + } + } contacts.Sort((contactA, contactB) => string.CompareOrdinal(contactA.Name, contactB.Name)); } else diff --git a/Content.Server/ADT/NanoChat/StationAiNanoChatComponent.cs b/Content.Server/ADT/NanoChat/StationAiNanoChatComponent.cs new file mode 100644 index 00000000000..211bd570842 --- /dev/null +++ b/Content.Server/ADT/NanoChat/StationAiNanoChatComponent.cs @@ -0,0 +1,27 @@ +using Content.Shared.Radio; +using Robust.Shared.Prototypes; + +namespace Content.Server.ADT.NanoChat; + +/// +/// Added to a station AI while it is held in a core (via the AiHeld prototype). +/// Marks its NanoChat card as deliverable without a PDA cartridge and grants the NanoChat action. +/// +[RegisterComponent] +public sealed partial class StationAiNanoChatComponent : Component +{ + /// + /// The action that opens the NanoChat UI. + /// + [DataField] + public EntProtoId Action = "ActionStationAiNanoChat"; + + [DataField] + public EntityUid? ActionEntity; + + /// + /// The required to send or receive messages. + /// + [DataField] + public ProtoId RadioChannel = "Common"; +} diff --git a/Content.Server/ADT/NanoChat/StationAiNanoChatSystem.cs b/Content.Server/ADT/NanoChat/StationAiNanoChatSystem.cs new file mode 100644 index 00000000000..3bd8ee6f71a --- /dev/null +++ b/Content.Server/ADT/NanoChat/StationAiNanoChatSystem.cs @@ -0,0 +1,142 @@ +using Content.Server.ADT.CartridgeLoader.Cartridges; +using Content.Server.Actions; +using Content.Server.Station.Systems; +using Content.Shared.Access.Components; +using Content.Shared.ADT.CartridgeLoader.Cartridges; +using Content.Shared.ADT.NanoChat; +using Robust.Server.GameObjects; +using Robust.Shared.Player; + +namespace Content.Server.ADT.NanoChat; + +public sealed class StationAiNanoChatSystem : EntitySystem +{ + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly ActionsSystem _action = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly NanoChatCartridgeSystem _nanoChatCartridge = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnAction); + SubscribeLocalEvent(OnUiOpened); + SubscribeLocalEvent(OnCardMessageReceived); + + Subs.BuiEvents(StationAiNanoChatUiKey.Key, subs => + { + subs.Event(OnMessage); + }); + } + + private void OnUiOpened(Entity ent, ref BoundUIOpenedEvent args) + { + if (!StationAiNanoChatUiKey.Key.Equals(args.UiKey)) + return; + + UpdateUi(ent); + } + + private void OnCardMessageReceived(Entity ent, ref NanoChatMessageReceivedEvent args) + { + if (!HasComp(ent.Owner)) + return; + + UpdateUi((ent.Owner, Comp(ent.Owner))); + } + + private void OnMapInit(EntityUid uid, StationAiNanoChatComponent component, MapInitEvent args) + { + _action.AddAction(uid, ref component.ActionEntity, component.Action); + } + + private void OnShutdown(EntityUid uid, StationAiNanoChatComponent component, ComponentShutdown args) + { + _action.RemoveAction(uid, component.ActionEntity); + } + + private void OnAction(EntityUid uid, StationAiNanoChatComponent comp, StationAiNanoChatActionEvent args) + { + if (args.Handled) + return; + + if (!TryComp(uid, out var actor)) + return; + + _uiSystem.TryOpenUi(uid, StationAiNanoChatUiKey.Key, actor.Owner); + args.Handled = true; + } + + private void OnMessage(Entity ent, ref StationAiNanoChatUiMessage msg) + { + if (!TryComp(ent.Owner, out var cardComp)) + return; + + var card = new Entity(ent.Owner, cardComp); + + switch (msg.Type) + { + case NanoChatUiMessageType.NewChat: + _nanoChatCartridge.NewChat(card, msg.RecipientNumber, msg.Content, msg.RecipientJob, msg.Actor); + break; + case NanoChatUiMessageType.SelectChat: + _nanoChatCartridge.SelectChat(card, msg.RecipientNumber); + break; + case NanoChatUiMessageType.CloseChat: + _nanoChatCartridge.CloseChat(card); + break; + case NanoChatUiMessageType.ToggleMute: + _nanoChatCartridge.ToggleMute(card); + break; + case NanoChatUiMessageType.DeleteChat: + _nanoChatCartridge.DeleteChat(card, msg.RecipientNumber, msg.Actor); + break; + case NanoChatUiMessageType.SendMessage: + _nanoChatCartridge.SendMessage(ent.Owner, card, msg.RecipientNumber, msg.Content, ent.Comp.RadioChannel); + break; + case NanoChatUiMessageType.ToggleListNumber: + _nanoChatCartridge.ToggleListNumber(card); + break; + } + + UpdateUi(ent); + } + + private void UpdateUi(Entity ent) + { + if (!TryComp(ent.Owner, out var card)) + return; + + List? contacts = null; + if (_station.GetOwningStation(ent.Owner) is { } station) + { + contacts = new List(); + + var query = AllEntityQuery(); + while (query.MoveNext(out var entityId, out var nanoChatCard, out var idCardComponent)) + { + if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && + idCardComponent.FullName is string fullName && + _station.GetOwningStation(entityId) == station) + { + contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName)); + } + } + contacts.Sort((contactA, contactB) => string.CompareOrdinal(contactA.Name, contactB.Name)); + } + + var state = new NanoChatUiState(card.Recipients, + card.Messages, + contacts, + card.CurrentChat, + card.Number ?? 0, + card.MaxRecipients, + card.NotificationsMuted, + card.ListNumber); + + _uiSystem.SetUiState(ent.Owner, StationAiNanoChatUiKey.Key, state); + } +} diff --git a/Content.Shared/ADT/NanoChat/SharedStationAiNanoChatSystem.cs b/Content.Shared/ADT/NanoChat/SharedStationAiNanoChatSystem.cs new file mode 100644 index 00000000000..027da130fd7 --- /dev/null +++ b/Content.Shared/ADT/NanoChat/SharedStationAiNanoChatSystem.cs @@ -0,0 +1,38 @@ +using Content.Shared.Actions; +using Content.Shared.ADT.CartridgeLoader.Cartridges; +using Robust.Shared.Serialization; + +namespace Content.Shared.ADT.NanoChat; + +[Serializable, NetSerializable] +public enum StationAiNanoChatUiKey : byte +{ + Key +} + +public sealed partial class StationAiNanoChatActionEvent : InstantActionEvent +{ +} + +[Serializable, NetSerializable] +public sealed class StationAiNanoChatUiMessage : BoundUserInterfaceMessage +{ + public readonly NanoChatUiMessageType Type; + + public readonly uint? RecipientNumber; + + public readonly string? Content; + + public readonly string? RecipientJob; + + public StationAiNanoChatUiMessage(NanoChatUiMessageType type, + uint? recipientNumber = null, + string? content = null, + string? recipientJob = null) + { + Type = type; + RecipientNumber = recipientNumber; + Content = content; + RecipientJob = recipientJob; + } +} diff --git a/Resources/Locale/ru-RU/ADT/silicons/station-ai.ftl b/Resources/Locale/ru-RU/ADT/silicons/station-ai.ftl index e4cacbe7830..5fa3f0b2f13 100644 --- a/Resources/Locale/ru-RU/ADT/silicons/station-ai.ftl +++ b/Resources/Locale/ru-RU/ADT/silicons/station-ai.ftl @@ -21,3 +21,6 @@ ent-ActionStationAiAtmosphericAlerts = Интерфейс атмосферной .desc = Просмотр интерфейса атмосферной сигнализации. ent-ActionStationAiInfo = Просмотреть информацию .desc = Просмотрите общую информацию о станции. +ent-ActionStationAiNanoChat = НаноМакс + .desc = Открыть мессенджер НаноМакс. +station-ai-nanochat-window-title = НаноМакс diff --git a/Resources/Prototypes/ADT/Actions/station_ai.yml b/Resources/Prototypes/ADT/Actions/station_ai.yml index 68d453b7736..1cff3776cbf 100644 --- a/Resources/Prototypes/ADT/Actions/station_ai.yml +++ b/Resources/Prototypes/ADT/Actions/station_ai.yml @@ -101,3 +101,14 @@ priority: -9 - type: InstantAction event: !type:ToggleRemoteDevicesScreenEvent + +- type: entity + id: ActionStationAiNanoChat + components: + - type: Action + icon: {sprite: ADT/Misc/program_icons.rsi, state: nanochat} + iconOn: {sprite: ADT/Misc/program_icons.rsi, state: nanochat} + keywords: [ "AI", "console", "interface" ] + priority: -4 + - type: InstantAction + event: !type:StationAiNanoChatActionEvent diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index c42673dd349..12c7613e577 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -76,6 +76,8 @@ type: AtmosAlertsComputerBoundUserInterface enum.StationAiInfoUiKey.Key: type: StationAiInfoBoundUserInterface + enum.StationAiNanoChatUiKey.Key: + type: StationAiNanoChatBoundUserInterface # ADT End - type: IntrinsicUI uis: @@ -94,6 +96,7 @@ toggleAction: ADTActionOpenRemoteDevicesMenu - type: AtmosAlertsComputer - type: StationAiInfo + - type: StationAiNanoChat - type: CrewManifestViewer ownerKey: enum.StationAiInfoUiKey.Key # ADT End @@ -538,6 +541,7 @@ - type: CollectiveMindRank rankName: collective-mind-sai-rank - type: StationAiBrain + - type: NanoChatCard # ADT-Tweak-End - type: StartingMindRole mindRole: "MindRoleSiliconBrain"