Communications Console: The ECSining (#8374)

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Veritius
2022-06-03 21:37:35 +10:00
committed by GitHub
parent c6d20e6ccf
commit 237cb3d0b4
33 changed files with 608 additions and 249 deletions

View File

@@ -23,8 +23,7 @@ namespace Content.Client.Communications.UI
public string CurrentLevel { get; private set; } = default!; public string CurrentLevel { get; private set; } = default!;
public int Countdown => _expectedCountdownTime == null public int Countdown => _expectedCountdownTime == null ? 0 : Math.Max((int)_expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0);
? 0 : Math.Max((int)_expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0);
private TimeSpan? _expectedCountdownTime; private TimeSpan? _expectedCountdownTime;
public CommunicationsConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) public CommunicationsConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)

View File

@@ -25,12 +25,12 @@ namespace Content.Client.Communications.UI
SetSize = MinSize = (600, 400); SetSize = MinSize = (600, 400);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
Title = Loc.GetString("communicationsconsole-menu-title"); Title = Loc.GetString("comms-console-menu-title");
Owner = owner; Owner = owner;
_messageInput = new LineEdit _messageInput = new LineEdit
{ {
PlaceHolder = Loc.GetString("communicationsconsole-menu-announcement-placeholder"), PlaceHolder = Loc.GetString("comms-console-menu-announcement-placeholder"),
HorizontalExpand = true, HorizontalExpand = true,
SizeFlagsStretchRatio = 1 SizeFlagsStretchRatio = 1
}; };
@@ -127,11 +127,11 @@ namespace Content.Client.Communications.UI
if (!Owner.CountdownStarted) if (!Owner.CountdownStarted)
{ {
_countdownLabel.SetMessage(""); _countdownLabel.SetMessage("");
EmergencyShuttleButton.Text = Loc.GetString("communicationsconsole-menu-call-shuttle"); EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-call-shuttle");
return; return;
} }
EmergencyShuttleButton.Text = Loc.GetString("communicationsconsole-menu-recall-shuttle"); EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle");
_countdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s"); _countdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s");
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Shared.Administration; using Content.Shared.Administration;
@@ -10,6 +11,7 @@ namespace Content.Server.Administration.UI
{ {
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
public AdminAnnounceEui() public AdminAnnounceEui()
{ {
@@ -45,8 +47,9 @@ namespace Content.Server.Administration.UI
case AdminAnnounceType.Server: case AdminAnnounceType.Server:
_chatManager.DispatchServerAnnouncement(doAnnounce.Announcement); _chatManager.DispatchServerAnnouncement(doAnnounce.Announcement);
break; break;
// TODO: Per-station announcement support
case AdminAnnounceType.Station: case AdminAnnounceType.Station:
_chatManager.DispatchStationAnnouncement(doAnnounce.Announcement, doAnnounce.Announcer, colorOverride: Color.Gold); _chatSystem.DispatchGlobalStationAnnouncement(doAnnounce.Announcement, doAnnounce.Announcer, colorOverride: Color.Gold);
break; break;
} }

View File

@@ -27,7 +27,8 @@ public sealed class AlertLevelComponent : Component
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] public bool IsLevelLocked = false; [ViewVariables(VVAccess.ReadWrite)] public bool IsLevelLocked = false;
[ViewVariables] public const float Delay = 300; [ViewVariables] public const float Delay = 30;
[ViewVariables] public float CurrentDelay = 0; [ViewVariables] public float CurrentDelay = 0;
[ViewVariables] public bool ActiveDelay; [ViewVariables] public bool ActiveDelay;

View File

@@ -1,9 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Chat;
using Content.Server.Chat.Managers;
using Content.Server.Station.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.AlertLevel;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -13,7 +10,7 @@ namespace Content.Server.AlertLevel;
public sealed class AlertLevelSystem : EntitySystem public sealed class AlertLevelSystem : EntitySystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly StationSystem _stationSystem = default!;
// Until stations are a prototype, this is how it's going to have to be. // Until stations are a prototype, this is how it's going to have to be.
@@ -52,7 +49,7 @@ public sealed class AlertLevelSystem : EntitySystem
continue; continue;
} }
alert.CurrentDelay--; alert.CurrentDelay -= time;
} }
} }
@@ -186,11 +183,11 @@ public sealed class AlertLevelSystem : EntitySystem
if (announce) if (announce)
{ {
_chatManager.DispatchStationAnnouncement(announcementFull, playDefaultSound: playDefault, _chatSystem.DispatchStationAnnouncement(station, announcementFull, playDefaultSound: playDefault,
colorOverride: detail.Color, sender: stationName); colorOverride: detail.Color, sender: stationName);
} }
RaiseLocalEvent(new AlertLevelChangedEvent(level)); RaiseLocalEvent(new AlertLevelChangedEvent(station, level));
} }
} }
@@ -202,10 +199,12 @@ public sealed class AlertLevelPrototypeReloadedEvent : EntityEventArgs
public sealed class AlertLevelChangedEvent : EntityEventArgs public sealed class AlertLevelChangedEvent : EntityEventArgs
{ {
public EntityUid Station { get; }
public string AlertLevel { get; } public string AlertLevel { get; }
public AlertLevelChangedEvent(string alertLevel) public AlertLevelChangedEvent(EntityUid station, string alertLevel)
{ {
Station = station;
AlertLevel = alertLevel; AlertLevel = alertLevel;
} }
} }

View File

@@ -1,5 +1,5 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Chat.Managers; using Content.Server.Chat;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -13,7 +13,7 @@ namespace Content.Server.Announcements
public string Help => $"{Command} <sender> <message> or {Command} <message> to send announcement as centcomm."; public string Help => $"{Command} <sender> <message> or {Command} <message> to send announcement as centcomm.";
public void Execute(IConsoleShell shell, string argStr, string[] args) public void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
var chat = IoCManager.Resolve<IChatManager>(); var chat = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
if (args.Length == 0) if (args.Length == 0)
{ {
@@ -23,12 +23,12 @@ namespace Content.Server.Announcements
if (args.Length == 1) if (args.Length == 1)
{ {
chat.DispatchStationAnnouncement(args[0], colorOverride: Color.Gold); chat.DispatchGlobalStationAnnouncement(args[0], colorOverride: Color.Gold);
} }
else else
{ {
var message = string.Join(' ', new ArraySegment<string>(args, 1, args.Length-1)); var message = string.Join(' ', new ArraySegment<string>(args, 1, args.Length-1));
chat.DispatchStationAnnouncement(message, args[0], colorOverride: Color.Gold); chat.DispatchGlobalStationAnnouncement(message, args[0], colorOverride: Color.Gold);
} }
shell.WriteLine("Sent!"); shell.WriteLine("Sent!");
} }

View File

@@ -8,12 +8,15 @@ using Content.Server.Headset;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Radio.EntitySystems; using Content.Server.Radio.EntitySystems;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Network; using Robust.Shared.Network;
@@ -41,9 +44,11 @@ public sealed class ChatSystem : SharedChatSystem
[Dependency] private readonly ListeningSystem _listener = default!; [Dependency] private readonly ListeningSystem _listener = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
private const int VoiceRange = 7; // how far voice goes in world units private const int VoiceRange = 7; // how far voice goes in world units
private const int WhisperRange = 2; // how far whisper goes in world units private const int WhisperRange = 2; // how far whisper goes in world units
private const string AnnouncementSound = "/Audio/Announcements/announce.ogg";
private bool _loocEnabled = true; private bool _loocEnabled = true;
private readonly bool _adminLoocEnabled = true; private readonly bool _adminLoocEnabled = true;
@@ -140,6 +145,67 @@ public sealed class ChatSystem : SharedChatSystem
} }
} }
#region Announcements
/// <summary>
/// Dispatches an announcement to all stations
/// </summary>
/// <param name="message">The contents of the message</param>
/// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param>
/// <param name="playDefaultSound">Play the announcement sound</param>
/// <param name="colorOverride">Optional color for the announcement message</param>
public void DispatchGlobalStationAnnouncement(string message, string sender = "Central Command",
bool playDefaultSound = true, Color? colorOverride = null)
{
var messageWrap = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender));
_chatManager.ChatMessageToAll(ChatChannel.Radio, message, messageWrap);
if (playDefaultSound)
{
SoundSystem.Play(Filter.Broadcast(), AnnouncementSound, AudioParams.Default.WithVolume(-2f));
}
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Global station announcement from {sender}: {message}");
}
/// <summary>
/// Dispatches an announcement on a specific station
/// </summary>
/// <param name="source">The entity making the announcement (used to determine the station)</param>
/// <param name="message">The contents of the message</param>
/// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param>
/// <param name="playDefaultSound">Play the announcement sound</param>
/// <param name="colorOverride">Optional color for the announcement message</param>
public void DispatchStationAnnouncement(EntityUid source, string message, string sender = "Central Command", bool playDefaultSound = true, Color? colorOverride = null)
{
var messageWrap = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender));
var station = _stationSystem.GetOwningStation(source);
var filter = Filter.Empty();
if (station != null)
{
if (!EntityManager.TryGetComponent<StationDataComponent>(station, out var stationDataComp)) return;
foreach (var gridEnt in stationDataComp.Grids)
{
filter.AddInGrid(gridEnt);
}
}
else
{
filter = Filter.Pvs(source, entityManager: EntityManager);
}
_chatManager.ChatMessageToManyFiltered(filter, ChatChannel.Radio, message, messageWrap, source, true, colorOverride);
if (playDefaultSound)
{
SoundSystem.Play(filter, AnnouncementSound, AudioParams.Default.WithVolume(-2f));
}
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement on {station} from {sender}: {message}");
}
#endregion
#region Private API #region Private API
private void SendEntitySpeak(EntityUid source, string message, bool hideChat = false) private void SendEntitySpeak(EntityUid source, string message, bool hideChat = false)

View File

@@ -3,13 +3,14 @@ using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.MoMMI; using Content.Server.MoMMI;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.Station.Systems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Database; using Content.Shared.Database;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -35,6 +36,10 @@ namespace Content.Server.Chat.Managers
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; [Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private StationSystem _stationSystem = default!;
/// <summary> /// <summary>
/// The maximum length a player-sent message can be sent /// The maximum length a player-sent message can be sent
@@ -46,6 +51,7 @@ namespace Content.Server.Chat.Managers
public void Initialize() public void Initialize()
{ {
_stationSystem = _entityManager.EntitySysManager.GetEntitySystem<StationSystem>();
_netManager.RegisterNetMessage<MsgChatMessage>(); _netManager.RegisterNetMessage<MsgChatMessage>();
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
@@ -79,18 +85,6 @@ namespace Content.Server.Chat.Managers
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server announcement: {message}"); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server announcement: {message}");
} }
public void DispatchStationAnnouncement(string message, string sender = "Central Command", bool playDefaultSound = true, Color? colorOverride = null)
{
var messageWrap = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender));
ChatMessageToAll(ChatChannel.Radio, message, messageWrap, colorOverride);
if (playDefaultSound)
{
SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/announce.ogg", AudioParams.Default.WithVolume(-2f));
}
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement from {sender}: {message}");
}
public void DispatchServerMessage(IPlayerSession player, string message) public void DispatchServerMessage(IPlayerSession player, string message)
{ {
var messageWrap = Loc.GetString("chat-manager-server-wrap-message"); var messageWrap = Loc.GetString("chat-manager-server-wrap-message");
@@ -235,6 +229,20 @@ namespace Content.Server.Chat.Managers
_netManager.ServerSendToMany(msg, clients); _netManager.ServerSendToMany(msg, clients);
} }
public void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string messageWrap, EntityUid source,
bool hideChat, Color? colorOverride = null)
{
if (!filter.Recipients.Any()) return;
var clients = new List<INetChannel>();
foreach (var recipient in filter.Recipients)
{
clients.Add(recipient.ConnectedClient);
}
ChatMessageToMany(channel, message, messageWrap, source, hideChat, clients);
}
public void ChatMessageToAll(ChatChannel channel, string message, string messageWrap, Color? colorOverride = null) public void ChatMessageToAll(ChatChannel channel, string message, string messageWrap, Color? colorOverride = null)
{ {
var msg = new MsgChatMessage(); var msg = new MsgChatMessage();

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chat; using Content.Shared.Chat;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Content.Server.Chat.Managers namespace Content.Server.Chat.Managers
{ {
@@ -15,16 +16,6 @@ namespace Content.Server.Chat.Managers
/// <param name="colorOverride">Override the color of the message being sent.</param> /// <param name="colorOverride">Override the color of the message being sent.</param>
void DispatchServerAnnouncement(string message, Color? colorOverride = null); void DispatchServerAnnouncement(string message, Color? colorOverride = null);
/// <summary>
/// Station announcement to every player
/// </summary>
/// <param name="message"></param>
/// <param name="sender"></param>
/// <param name="playDefaultSound">If the default 'PA' sound should be played.</param>
/// <param name="colorOverride">Override the color of the message being sent.</param>
void DispatchStationAnnouncement(string message, string sender = "CentComm", bool playDefaultSound = true,
Color? colorOverride = null);
void DispatchServerMessage(IPlayerSession player, string message); void DispatchServerMessage(IPlayerSession player, string message);
void TrySendOOCMessage(IPlayerSession player, string message, OOCChatType type); void TrySendOOCMessage(IPlayerSession player, string message, OOCChatType type);
@@ -36,6 +27,7 @@ namespace Content.Server.Chat.Managers
INetChannel client); INetChannel client);
void ChatMessageToMany(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, void ChatMessageToMany(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat,
List<INetChannel> clients); List<INetChannel> clients);
void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, Color? colorOverride);
void ChatMessageToAll(ChatChannel channel, string message, string messageWrap, Color? colorOverride = null); void ChatMessageToAll(ChatChannel channel, string message, string messageWrap, Color? colorOverride = null);
bool MessageCharacterLimit(IPlayerSession player, string message); bool MessageCharacterLimit(IPlayerSession player, string message);

View File

@@ -1,157 +1,59 @@
using System.Globalization;
using System.Linq;
using System.Threading;
using Content.Server.Access.Systems;
using Content.Server.AlertLevel;
using Content.Server.Chat.Managers;
using Content.Server.Power.Components;
using Content.Server.RoundEnd;
using Content.Server.Station.Systems;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.Communications; using Content.Shared.Communications;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Timing;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.Communications namespace Content.Server.Communications
{ {
// TODO: ECS
[RegisterComponent] [RegisterComponent]
public sealed class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent, IEntityEventSubscriber public sealed class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; /// <summary>
[Dependency] private readonly IChatManager _chatManager = default!; /// Remaining cooldown between making announcements.
[Dependency] private readonly IEntityManager _entities = default!; /// </summary>
[Dependency] private readonly IEntityManager _entityManager = default!; [ViewVariables]
[Dependency] private readonly IEntitySystemManager _sysMan = default!; public float AnnouncementCooldownRemaining;
private bool Powered => !_entities.TryGetComponent(Owner, out ApcPowerReceiverComponent? receiver) || receiver.Powered; /// <summary>
/// Has the UI already been refreshed after the announcement
/// </summary>
[ViewVariables]
public bool AlreadyRefreshed = false;
private RoundEndSystem RoundEndSystem => EntitySystem.Get<RoundEndSystem>(); /// <summary>
private AlertLevelSystem AlertLevelSystem => EntitySystem.Get<AlertLevelSystem>(); /// Fluent ID for the announcement title
private StationSystem StationSystem => EntitySystem.Get<StationSystem>(); /// If a Fluent ID isn't found, just uses the raw string
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("title", required: true)]
public string AnnouncementDisplayName = "comms-console-announcement-title-station";
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(CommunicationsConsoleUiKey.Key); /// <summary>
/// Announcement color
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("color")]
public Color AnnouncementColor = Color.Gold;
public TimeSpan LastAnnounceTime { get; private set; } = TimeSpan.Zero; /// <summary>
public TimeSpan AnnounceCooldown { get; } = TimeSpan.FromSeconds(90); /// Time in seconds between announcement delays on a per-console basis
private CancellationTokenSource _announceCooldownEndedTokenSource = new(); /// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("delay")]
public int DelayBetweenAnnouncements = 90;
protected override void Initialize() /// <summary>
{ /// Can call or recall the shuttle
base.Initialize(); /// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canShuttle")]
public bool CanCallShuttle = true;
if (UserInterface != null) /// <summary>
{ /// Announce on all grids (for nukies)
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; /// </summary>
} [DataField("global")]
public bool AnnounceGlobal = false;
_entityManager.EventBus.SubscribeEvent<RoundEndSystemChangedEvent>(EventSource.Local, this, (s) => UpdateBoundInterface()); public BoundUserInterface? UserInterface => Owner.GetUIOrNull(CommunicationsConsoleUiKey.Key);
_entityManager.EventBus.SubscribeEvent<AlertLevelChangedEvent>(EventSource.Local, this, _ => UpdateBoundInterface());
_entityManager.EventBus.SubscribeEvent<AlertLevelDelayFinishedEvent>(EventSource.Local, this, _ => UpdateBoundInterface());
}
protected override void Startup()
{
base.Startup();
UpdateBoundInterface();
}
private void UpdateBoundInterface()
{
if (!Deleted)
{
var system = RoundEndSystem;
List<string>? levels = null;
string currentLevel = default!;
float currentDelay = 0;
var stationUid = StationSystem.GetOwningStation(Owner);
if (stationUid != null)
{
if (_entityManager.TryGetComponent(stationUid.Value, out AlertLevelComponent? alerts)
&& alerts.AlertLevels != null)
{
if (alerts.IsSelectable)
{
levels = new();
foreach (var (id, detail) in alerts.AlertLevels.Levels)
{
if (detail.Selectable)
{
levels.Add(id);
}
}
}
currentLevel = alerts.CurrentLevel;
currentDelay = AlertLevelSystem.GetAlertLevelDelay(stationUid.Value, alerts);
}
}
UserInterface?.SetState(new CommunicationsConsoleInterfaceState(CanAnnounce(), system.CanCall(), levels, currentLevel, currentDelay, system.ExpectedCountdownEnd));
}
}
public bool CanAnnounce()
{
if (LastAnnounceTime == TimeSpan.Zero)
{
return true;
}
return _gameTiming.CurTime >= LastAnnounceTime + AnnounceCooldown;
}
protected override void OnRemove()
{
_entityManager.EventBus.UnsubscribeEvent<RoundEndSystemChangedEvent>(EventSource.Local, this);
base.OnRemove();
}
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
{
switch (obj.Message)
{
case CommunicationsConsoleCallEmergencyShuttleMessage _:
RoundEndSystem.RequestRoundEnd(obj.Session.AttachedEntity);
break;
case CommunicationsConsoleRecallEmergencyShuttleMessage _:
RoundEndSystem.CancelRoundEndCountdown(obj.Session.AttachedEntity);
break;
case CommunicationsConsoleAnnounceMessage msg:
if (!CanAnnounce())
{
return;
}
_announceCooldownEndedTokenSource.Cancel();
_announceCooldownEndedTokenSource = new CancellationTokenSource();
LastAnnounceTime = _gameTiming.CurTime;
Timer.Spawn(AnnounceCooldown, UpdateBoundInterface, _announceCooldownEndedTokenSource.Token);
UpdateBoundInterface();
var message = msg.Message.Length <= 256 ? msg.Message.Trim() : $"{msg.Message.Trim().Substring(0, 256)}...";
var sys = _sysMan.GetEntitySystem<IdCardSystem>();
var author = "Unknown";
if (obj.Session.AttachedEntity is {Valid: true} mob && sys.TryFindIdCard(mob, out var id))
{
author = $"{id.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(id.JobTitle ?? string.Empty)})".Trim();
}
message += $"\nSent by {author}";
_chatManager.DispatchStationAnnouncement(message, "Communications Console", colorOverride: Color.Gold);
break;
case CommunicationsConsoleSelectAlertLevelMessage alertMsg:
var stationUid = StationSystem.GetOwningStation(Owner);
if (stationUid != null)
{
AlertLevelSystem.SetLevel(stationUid.Value, alertMsg.Level, true, true);
}
break;
}
}
} }
} }

View File

@@ -0,0 +1,233 @@
using System.Globalization;
using Content.Server.Access.Systems;
using Content.Server.AlertLevel;
using Content.Server.Chat;
using Content.Server.Chat.Managers;
using Content.Server.Popups;
using Content.Server.RoundEnd;
using Content.Server.Station.Systems;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Communications;
using Robust.Shared.Player;
namespace Content.Server.Communications
{
public sealed class CommunicationsConsoleSystem : EntitySystem
{
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly IdCardSystem _idCardSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
private const int MaxMessageLength = 256;
public override void Initialize()
{
// All events that refresh the BUI
SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
SubscribeLocalEvent<CommunicationsConsoleComponent, ComponentInit>((_, comp, _) => UpdateBoundUserInterface(comp));
SubscribeLocalEvent<RoundEndSystemChangedEvent>(_ => OnGenericBroadcastEvent());
SubscribeLocalEvent<AlertLevelDelayFinishedEvent>(_ => OnGenericBroadcastEvent());
// Messages from the BUI
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleSelectAlertLevelMessage>(OnSelectAlertLevelMessage);
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleAnnounceMessage>(OnAnnounceMessage);
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage);
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage);
}
public override void Update(float frameTime)
{
foreach (var comp in EntityQuery<CommunicationsConsoleComponent>())
{
// TODO: Find a less ass way of refreshing the UI
if (comp.AlreadyRefreshed) continue;
if (comp.AnnouncementCooldownRemaining <= 0f)
{
UpdateBoundUserInterface(comp);
comp.AlreadyRefreshed = true;
continue;
}
comp.AnnouncementCooldownRemaining -= frameTime;
}
base.Update(frameTime);
}
/// <summary>
/// Update the UI of every comms console.
/// </summary>
private void OnGenericBroadcastEvent()
{
foreach (var comp in EntityQuery<CommunicationsConsoleComponent>())
{
UpdateBoundUserInterface(comp);
}
}
/// <summary>
/// Updates all comms consoles belonging to the station that the alert level was set on
/// </summary>
/// <param name="args">Alert level changed event arguments</param>
private void OnAlertLevelChanged(AlertLevelChangedEvent args)
{
foreach (var comp in EntityQuery<CommunicationsConsoleComponent>())
{
var entStation = _stationSystem.GetOwningStation(comp.Owner);
if (args.Station == entStation)
{
UpdateBoundUserInterface(comp);
}
}
}
private void UpdateBoundUserInterface(CommunicationsConsoleComponent comp)
{
var uid = comp.Owner;
var stationUid = _stationSystem.GetOwningStation(uid);
List<string>? levels = null;
string currentLevel = default!;
float currentDelay = 0;
if (stationUid != null)
{
if (TryComp(stationUid.Value, out AlertLevelComponent? alertComp) &&
alertComp.AlertLevels != null)
{
if (alertComp.IsSelectable)
{
levels = new();
foreach (var (id, detail) in alertComp.AlertLevels.Levels)
{
if (detail.Selectable)
{
levels.Add(id);
}
}
}
currentLevel = alertComp.CurrentLevel;
currentDelay = _alertLevelSystem.GetAlertLevelDelay(stationUid.Value, alertComp);
}
}
comp.UserInterface?.SetState(
new CommunicationsConsoleInterfaceState(
CanAnnounce(comp),
CanCall(comp),
levels,
currentLevel,
currentDelay,
_roundEndSystem.ExpectedCountdownEnd
)
);
}
private bool CanAnnounce(CommunicationsConsoleComponent comp)
{
return comp.AnnouncementCooldownRemaining <= 0f;
}
private bool CanUse(EntityUid user, EntityUid console)
{
if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent) && accessReaderComponent.Enabled)
{
return _accessReaderSystem.IsAllowed(user, accessReaderComponent);
}
return true;
}
private bool CanCall(CommunicationsConsoleComponent comp)
{
return comp.CanCallShuttle && _roundEndSystem.CanCall();
}
private void OnSelectAlertLevelMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleSelectAlertLevelMessage message)
{
if (message.Session.AttachedEntity is not {Valid: true} mob) return;
if (!CanUse(mob, uid))
{
_popupSystem.PopupCursor(Loc.GetString("comms-console-permission-denied"), Filter.Entities(mob));
return;
}
var stationUid = _stationSystem.GetOwningStation(uid);
if (stationUid != null)
{
_alertLevelSystem.SetLevel(stationUid.Value, message.Level, true, true);
}
}
private void OnAnnounceMessage(EntityUid uid, CommunicationsConsoleComponent comp,
CommunicationsConsoleAnnounceMessage message)
{
var msg = message.Message.Length <= MaxMessageLength ? message.Message.Trim() : $"{message.Message.Trim().Substring(0, MaxMessageLength)}...";
var author = Loc.GetString("comms-console-announcement-unknown-sender");
if (message.Session.AttachedEntity is {Valid: true} mob)
{
if (!CanAnnounce(comp))
{
return;
}
if (!CanUse(mob, uid))
{
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, Filter.Entities(mob));
return;
}
if (_idCardSystem.TryFindIdCard(mob, out var id))
{
author = $"{id.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(id.JobTitle ?? string.Empty)})".Trim();
}
}
comp.AnnouncementCooldownRemaining = comp.DelayBetweenAnnouncements;
comp.AlreadyRefreshed = false;
UpdateBoundUserInterface(comp);
// allow admemes with vv
Loc.TryGetString(comp.AnnouncementDisplayName, out var title);
title ??= comp.AnnouncementDisplayName;
msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + author;
if (comp.AnnounceGlobal)
{
_chatSystem.DispatchGlobalStationAnnouncement(msg, title, colorOverride: comp.AnnouncementColor);
}
else
{
_chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.AnnouncementColor);
}
}
private void OnCallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleCallEmergencyShuttleMessage message)
{
if (!comp.CanCallShuttle) return;
if (message.Session.AttachedEntity is not {Valid: true} mob) return;
if (!CanUse(mob, uid))
{
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, Filter.Entities(mob));
return;
}
_roundEndSystem.RequestRoundEnd(uid);
}
private void OnRecallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleRecallEmergencyShuttleMessage message)
{
if (!comp.CanCallShuttle) return;
if (message.Session.AttachedEntity is not {Valid: true} mob) return;
if (!CanUse(mob, uid))
{
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, Filter.Entities(mob));
return;
}
_roundEndSystem.CancelRoundEndCountdown(uid);
}
}
}

View File

@@ -474,7 +474,7 @@ namespace Content.Server.GameTicking
if (!proto.GamePresets.Contains(Preset.ID)) continue; if (!proto.GamePresets.Contains(Preset.ID)) continue;
if (proto.Message != null) if (proto.Message != null)
_chatManager.DispatchStationAnnouncement(Loc.GetString(proto.Message), playDefaultSound: false); _chatSystem.DispatchGlobalStationAnnouncement(Loc.GetString(proto.Message), playDefaultSound: true);
if (proto.Sound != null) if (proto.Sound != null)
SoundSystem.Play(Filter.Broadcast(), proto.Sound.GetSound()); SoundSystem.Play(Filter.Broadcast(), proto.Sound.GetSound());

View File

@@ -157,8 +157,9 @@ namespace Content.Server.GameTicking
if (lateJoin) if (lateJoin)
{ {
_chatManager.DispatchStationAnnouncement(Loc.GetString( _chatSystem.DispatchStationAnnouncement(station,
"latejoin-arrival-announcement", Loc.GetString(
"latejoin-arrival-announcement",
("character", character.Name), ("character", character.Name),
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name)) ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name))
), Loc.GetString("latejoin-arrival-sender"), ), Loc.GetString("latejoin-arrival-sender"),

View File

@@ -1,5 +1,6 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.Database; using Content.Server.Database;
using Content.Server.Ghost; using Content.Server.Ghost;
@@ -114,6 +115,7 @@ namespace Content.Server.GameTicking
[Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly GhostSystem _ghosts = default!; [Dependency] private readonly GhostSystem _ghosts = default!;
[Dependency] private readonly RoleBanManager _roleBanManager = default!; [Dependency] private readonly RoleBanManager _roleBanManager = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly ServerUpdateManager _serverUpdates = default!; [Dependency] private readonly ServerUpdateManager _serverUpdates = default!;
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Server.Chat.Managers; using Content.Server.Chat;
using Content.Server.Communications; using Content.Server.Communications;
using Content.Server.Station.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -12,7 +13,8 @@ namespace Content.Server.Nuke
public sealed class NukeCodeSystem : EntitySystem public sealed class NukeCodeSystem : EntitySystem
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
private const int CodeLength = 6; private const int CodeLength = 6;
public string Code { get; private set; } = default!; public string Code { get; private set; } = default!;
@@ -73,10 +75,11 @@ namespace Content.Server.Nuke
wasSent = true; wasSent = true;
} }
// TODO: Allow selecting a station for nuke codes
if (wasSent) if (wasSent)
{ {
var msg = Loc.GetString("nuke-component-announcement-send-codes"); var msg = Loc.GetString("nuke-component-announcement-send-codes");
_chat.DispatchStationAnnouncement(msg, colorOverride: Color.Red); _chatSystem.DispatchGlobalStationAnnouncement(msg, colorOverride: Color.Red);
} }
return wasSent; return wasSent;

View File

@@ -1,4 +1,5 @@
using Content.Server.AlertLevel; using Content.Server.AlertLevel;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.Coordinates.Helpers; using Content.Server.Coordinates.Helpers;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
@@ -24,7 +25,7 @@ namespace Content.Server.Nuke
[Dependency] private readonly ExplosionSystem _explosions = default!; [Dependency] private readonly ExplosionSystem _explosions = default!;
[Dependency] private readonly AlertLevelSystem _alertLevel = default!; [Dependency] private readonly AlertLevelSystem _alertLevel = default!;
[Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly ChatSystem _chatSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -340,7 +341,7 @@ namespace Content.Server.Nuke
var announcement = Loc.GetString("nuke-component-announcement-armed", var announcement = Loc.GetString("nuke-component-announcement-armed",
("time", (int) component.RemainingTime)); ("time", (int) component.RemainingTime));
var sender = Loc.GetString("nuke-component-announcement-sender"); var sender = Loc.GetString("nuke-component-announcement-sender");
_chat.DispatchStationAnnouncement(announcement, sender, false, Color.Red); _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false, Color.Red);
// todo: move it to announcements system // todo: move it to announcements system
SoundSystem.Play(Filter.Broadcast(), component.ArmSound.GetSound()); SoundSystem.Play(Filter.Broadcast(), component.ArmSound.GetSound());
@@ -369,7 +370,7 @@ namespace Content.Server.Nuke
// warn a crew // warn a crew
var announcement = Loc.GetString("nuke-component-announcement-unarmed"); var announcement = Loc.GetString("nuke-component-announcement-unarmed");
var sender = Loc.GetString("nuke-component-announcement-sender"); var sender = Loc.GetString("nuke-component-announcement-sender");
_chat.DispatchStationAnnouncement(announcement, sender, false); _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false);
// todo: move it to announcements system // todo: move it to announcements system
SoundSystem.Play(Filter.Broadcast(), component.DisarmSound.GetSound()); SoundSystem.Play(Filter.Broadcast(), component.DisarmSound.GetSound());

View File

@@ -1,3 +1,4 @@
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Shared.Roles; using Content.Shared.Roles;
@@ -31,17 +32,25 @@ namespace Content.Server.Roles
if (Mind.TryGetSession(out var session)) if (Mind.TryGetSession(out var session))
{ {
var chat = IoCManager.Resolve<IChatManager>(); var chatMgr = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name", ("jobName", Name))); var chatSys = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name", ("jobName", Name)));
if(Prototype.RequireAdminNotify) if(Prototype.RequireAdminNotify)
chat.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify")); chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify"));
chat.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning", ("jobName", Name), ("supervisors", Prototype.Supervisors))); chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning", ("jobName", Name), ("supervisors", Prototype.Supervisors)));
if(Prototype.JoinNotifyCrew && Mind.CharacterName != null) if(Prototype.JoinNotifyCrew && Mind.CharacterName != null)
chat.DispatchStationAnnouncement(Loc.GetString("job-greet-join-notify-crew", ("jobName", Name), ("characterName", Mind.CharacterName)), {
Loc.GetString("job-greet-join-notify-crew-announcer"), false); if (Mind.OwnedEntity != null)
{
chatSys.DispatchStationAnnouncement(Mind.OwnedEntity.Value,
Loc.GetString("job-greet-join-notify-crew", ("jobName", Name),
("characterName", Mind.CharacterName)),
Loc.GetString("job-greet-join-notify-crew-announcer"), false);
}
}
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Threading; using System.Threading;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Shared.Database; using Content.Shared.Database;
@@ -15,6 +16,7 @@ namespace Content.Server.RoundEnd
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
@@ -80,7 +82,7 @@ namespace Content.Server.RoundEnd
_adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called"); _adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called");
} }
_chatManager.DispatchStationAnnouncement(Loc.GetString("round-end-system-shuttle-called-announcement",("minutes", countdownTime.Minutes)), Loc.GetString("Station"), false, Color.Gold); _chatSystem.DispatchGlobalStationAnnouncement(Loc.GetString("round-end-system-shuttle-called-announcement",("minutes", countdownTime.Minutes)), Loc.GetString("Station"), false, Color.Gold);
SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/shuttlecalled.ogg"); SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/shuttlecalled.ogg");
@@ -109,7 +111,7 @@ namespace Content.Server.RoundEnd
_adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled"); _adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled");
} }
_chatManager.DispatchStationAnnouncement(Loc.GetString("round-end-system-shuttle-recalled-announcement"), _chatSystem.DispatchGlobalStationAnnouncement(Loc.GetString("round-end-system-shuttle-recalled-announcement"),
Loc.GetString("Station"), false, colorOverride: Color.Gold); Loc.GetString("Station"), false, colorOverride: Color.Gold);
SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/shuttlerecalled.ogg"); SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/shuttlerecalled.ogg");

View File

@@ -0,0 +1,14 @@
namespace Content.Server.Salvage
{
/// <summary>
/// A grid spawned by a salvage magnet.
/// </summary>
[RegisterComponent]
public sealed class SalvageGridComponent : Component
{
/// <summary>
/// The magnet that spawned this grid.
/// </summary>
public SalvageMagnetComponent? SpawnerMagnet;
}
}

View File

@@ -12,24 +12,28 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using Content.Server.Chat;
using Content.Server.Station.Systems;
namespace Content.Server.Salvage namespace Content.Server.Salvage
{ {
public sealed class SalvageSystem : EntitySystem public sealed class SalvageSystem : EntitySystem
{ {
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IMapLoader _mapLoader = default!; [Dependency] private readonly IMapLoader _mapLoader = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
private static readonly TimeSpan AttachingTime = TimeSpan.FromSeconds(30); private static readonly TimeSpan AttachingTime = TimeSpan.FromSeconds(30);
private static readonly TimeSpan HoldTime = TimeSpan.FromMinutes(4); private static readonly TimeSpan HoldTime = TimeSpan.FromMinutes(4);
private static readonly TimeSpan DetachingTime = TimeSpan.FromSeconds(30); private static readonly TimeSpan DetachingTime = TimeSpan.FromSeconds(30);
private static readonly TimeSpan CooldownTime = TimeSpan.FromMinutes(1); private static readonly TimeSpan CooldownTime = TimeSpan.FromMinutes(1);
// TODO: This is probably not compatible with multi-station
private readonly Dictionary<GridId, SalvageGridState> _salvageGridStates = new(); private readonly Dictionary<GridId, SalvageGridState> _salvageGridStates = new();
public override void Initialize() public override void Initialize()
@@ -58,7 +62,9 @@ namespace Content.Server.Salvage
// If we ever want to give magnets names, and announce them individually, we would need to loop this, before removing it. // If we ever want to give magnets names, and announce them individually, we would need to loop this, before removing it.
if (_salvageGridStates.Remove(ev.GridId)) if (_salvageGridStates.Remove(ev.GridId))
{ {
Report("salvage-system-announcement-spawn-magnet-lost"); var gridUid = _mapManager.GetGridEuid(ev.GridId);
if (EntityManager.TryGetComponent<SalvageGridComponent>(gridUid, out var salvComp) && salvComp.SpawnerMagnet != null)
Report(salvComp.SpawnerMagnet.Owner, "salvage-system-announcement-spawn-magnet-lost");
// For the very unlikely possibility that the salvage magnet was on a salvage, we will not return here // For the very unlikely possibility that the salvage magnet was on a salvage, we will not return here
} }
foreach(var gridState in _salvageGridStates) foreach(var gridState in _salvageGridStates)
@@ -85,16 +91,16 @@ namespace Content.Server.Salvage
return; return;
} }
salvageGridState.ActiveMagnets.Remove(component); salvageGridState.ActiveMagnets.Remove(component);
Report("salvage-system-announcement-spawn-magnet-lost"); Report(uid, "salvage-system-announcement-spawn-magnet-lost");
if (component.AttachedEntity.HasValue) if (component.AttachedEntity.HasValue)
{ {
SafeDeleteSalvage(component.AttachedEntity.Value); SafeDeleteSalvage(component.AttachedEntity.Value);
component.AttachedEntity = null; component.AttachedEntity = null;
Report("salvage-system-announcement-lost"); Report(uid, "salvage-system-announcement-lost");
} }
else if (component.MagnetState is { StateType: MagnetStateType.Attaching }) else if (component.MagnetState is { StateType: MagnetStateType.Attaching })
{ {
Report("salvage-system-announcement-spawn-no-debris-available"); Report(uid, "salvage-system-announcement-spawn-no-debris-available");
} }
component.MagnetState = MagnetState.Inactive; component.MagnetState = MagnetState.Inactive;
} }
@@ -157,7 +163,7 @@ namespace Content.Server.Salvage
} }
gridState.ActiveMagnets.Add(component); gridState.ActiveMagnets.Add(component);
component.MagnetState = new MagnetState(MagnetStateType.Attaching, gridState.CurrentTime + AttachingTime); component.MagnetState = new MagnetState(MagnetStateType.Attaching, gridState.CurrentTime + AttachingTime);
Report("salvage-system-report-activate-success"); Report(component.Owner, "salvage-system-report-activate-success");
break; break;
case MagnetStateType.Attaching: case MagnetStateType.Attaching:
case MagnetStateType.Holding: case MagnetStateType.Holding:
@@ -260,7 +266,7 @@ namespace Content.Server.Salvage
if (map == null) if (map == null)
{ {
Report("salvage-system-announcement-spawn-no-debris-available"); Report(component.Owner, "salvage-system-announcement-spawn-no-debris-available");
return false; return false;
} }
@@ -272,22 +278,24 @@ namespace Content.Server.Salvage
var (_, gridId) = _mapLoader.LoadBlueprint(spl.MapId, map.MapPath.ToString(), opts); var (_, gridId) = _mapLoader.LoadBlueprint(spl.MapId, map.MapPath.ToString(), opts);
if (gridId == null) if (gridId == null)
{ {
Report("salvage-system-announcement-spawn-debris-disintegrated"); Report(component.Owner, "salvage-system-announcement-spawn-debris-disintegrated");
return false; return false;
} }
var salvageEntityId = _mapManager.GetGridEuid(gridId.Value); var salvageEntityId = _mapManager.GetGridEuid(gridId.Value);
component.AttachedEntity = salvageEntityId; component.AttachedEntity = salvageEntityId;
var gridcomp = EntityManager.EnsureComponent<SalvageGridComponent>(salvageEntityId);
gridcomp.SpawnerMagnet = component;
var pulledTransform = EntityManager.GetComponent<TransformComponent>(salvageEntityId); var pulledTransform = EntityManager.GetComponent<TransformComponent>(salvageEntityId);
pulledTransform.WorldRotation = spAngle; pulledTransform.WorldRotation = spAngle;
Report("salvage-system-announcement-arrived", ("timeLeft", HoldTime.TotalSeconds)); Report(component.Owner, "salvage-system-announcement-arrived", ("timeLeft", HoldTime.TotalSeconds));
return true; return true;
} }
private void Report(string messageKey) => private void Report(EntityUid source, string messageKey) =>
_chatManager.DispatchStationAnnouncement(Loc.GetString(messageKey), Loc.GetString("salvage-system-announcement-source"), colorOverride: Color.Orange, playDefaultSound: false); _chatSystem.DispatchStationAnnouncement(source, Loc.GetString(messageKey), Loc.GetString("salvage-system-announcement-source"), colorOverride: Color.Orange, playDefaultSound: false);
private void Report(string messageKey, params (string, object)[] args) => private void Report(EntityUid source, string messageKey, params (string, object)[] args) =>
_chatManager.DispatchStationAnnouncement(Loc.GetString(messageKey, args), Loc.GetString("salvage-system-announcement-source"), colorOverride: Color.Orange, playDefaultSound: false); _chatSystem.DispatchStationAnnouncement(source, Loc.GetString(messageKey, args), Loc.GetString("salvage-system-announcement-source"), colorOverride: Color.Orange, playDefaultSound: false);
private void Transition(SalvageMagnetComponent magnet, TimeSpan currentTime) private void Transition(SalvageMagnetComponent magnet, TimeSpan currentTime)
{ {
@@ -304,7 +312,7 @@ namespace Content.Server.Salvage
} }
break; break;
case MagnetStateType.Holding: case MagnetStateType.Holding:
Report("salvage-system-announcement-losing", ("timeLeft", DetachingTime.TotalSeconds)); Report(magnet.Owner, "salvage-system-announcement-losing", ("timeLeft", DetachingTime.TotalSeconds));
magnet.MagnetState = new MagnetState(MagnetStateType.Detaching, currentTime + DetachingTime); magnet.MagnetState = new MagnetState(MagnetStateType.Detaching, currentTime + DetachingTime);
break; break;
case MagnetStateType.Detaching: case MagnetStateType.Detaching:
@@ -316,7 +324,7 @@ namespace Content.Server.Salvage
{ {
Logger.ErrorS("salvage", "Salvage detaching was expecting attached entity but it was null"); Logger.ErrorS("salvage", "Salvage detaching was expecting attached entity but it was null");
} }
Report("salvage-system-announcement-lost"); Report(magnet.Owner, "salvage-system-announcement-lost");
magnet.MagnetState = new MagnetState(MagnetStateType.CoolingDown, currentTime + CooldownTime); magnet.MagnetState = new MagnetState(MagnetStateType.CoolingDown, currentTime + CooldownTime);
break; break;
case MagnetStateType.CoolingDown: case MagnetStateType.CoolingDown:

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Station.Components; using Content.Server.Station.Components;
@@ -23,6 +24,7 @@ public sealed class StationSystem : EntitySystem
[Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
@@ -268,7 +270,7 @@ public sealed class StationSystem : EntitySystem
if (loud) if (loud)
{ {
_chatManager.DispatchStationAnnouncement($"The station {oldName} has been renamed to {name}."); _chatSystem.DispatchStationAnnouncement(station, $"The station {oldName} has been renamed to {name}.");
} }
RaiseLocalEvent(station, new StationRenamedEvent(oldName, name)); RaiseLocalEvent(station, new StationRenamedEvent(oldName, name));

View File

@@ -1,6 +1,7 @@
using Content.Server.Chat.Managers; using Content.Server.Chat;
using Content.Server.Disease.Components; using Content.Server.Disease.Components;
using Content.Server.Disease; using Content.Server.Disease;
using Content.Server.Station.Systems;
using Content.Shared.Disease; using Content.Shared.Disease;
using Content.Shared.MobState.Components; using Content.Shared.MobState.Components;
using Content.Shared.Sound; using Content.Shared.Sound;
@@ -17,7 +18,6 @@ public sealed class DiseaseOutbreak : StationEvent
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
/// <summary> /// <summary>
/// Disease prototypes I decided were not too deadly for a random event /// Disease prototypes I decided were not too deadly for a random event
@@ -36,6 +36,7 @@ public sealed class DiseaseOutbreak : StationEvent
public override SoundSpecifier? StartAudio => new SoundPathSpecifier("/Audio/Announcements/outbreak7.ogg"); public override SoundSpecifier? StartAudio => new SoundPathSpecifier("/Audio/Announcements/outbreak7.ogg");
protected override float EndAfter => 1.0f; protected override float EndAfter => 1.0f;
/// <summary> /// <summary>
/// Finds 2-5 random, alive entities that can host diseases /// Finds 2-5 random, alive entities that can host diseases
/// and gives them a randomly selected disease. /// and gives them a randomly selected disease.
@@ -44,6 +45,7 @@ public sealed class DiseaseOutbreak : StationEvent
public override void Startup() public override void Startup()
{ {
base.Startup(); base.Startup();
HashSet<EntityUid> stationsToNotify = new();
List<DiseaseCarrierComponent> aliveList = new(); List<DiseaseCarrierComponent> aliveList = new();
foreach (var (carrier, mobState) in _entityManager.EntityQuery<DiseaseCarrierComponent, MobStateComponent>()) foreach (var (carrier, mobState) in _entityManager.EntityQuery<DiseaseCarrierComponent, MobStateComponent>())
{ {
@@ -61,15 +63,25 @@ public sealed class DiseaseOutbreak : StationEvent
return; return;
var diseaseSystem = EntitySystem.Get<DiseaseSystem>(); var diseaseSystem = EntitySystem.Get<DiseaseSystem>();
/// Now we give it to people in the list of living disease carriers earlier var stationSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<StationSystem>();
var chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
// Now we give it to people in the list of living disease carriers earlier
foreach (var target in aliveList) foreach (var target in aliveList)
{ {
if (toInfect-- == 0) if (toInfect-- == 0)
break; break;
diseaseSystem.TryAddDisease(target.Owner, disease, target); diseaseSystem.TryAddDisease(target.Owner, disease, target);
var station = stationSystem.GetOwningStation(target.Owner);
if(station == null) continue;
stationsToNotify.Add((EntityUid) station);
}
foreach (var station in stationsToNotify)
{
chatSystem.DispatchStationAnnouncement(station, Loc.GetString("station-event-disease-outbreak-announcement"),
playDefaultSound: false, colorOverride: Color.YellowGreen);
} }
_chatManager.DispatchStationAnnouncement(Loc.GetString("station-event-disease-outbreak-announcement"),
playDefaultSound: false, colorOverride: Color.YellowGreen);
} }
} }

View File

@@ -1,7 +1,8 @@
using System.Linq; using System.Linq;
using Content.Server.Chat.Managers; using Content.Server.Chat;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind.Commands; using Content.Server.Mind.Commands;
using Content.Server.Station.Systems;
using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Components;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -11,7 +12,6 @@ public sealed class RandomSentience : StationEvent
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
public override string Name => "RandomSentience"; public override string Name => "RandomSentience";
@@ -22,6 +22,7 @@ public sealed class RandomSentience : StationEvent
public override void Startup() public override void Startup()
{ {
base.Startup(); base.Startup();
HashSet<EntityUid> stationsToNotify = new();
var targetList = _entityManager.EntityQuery<SentienceTargetComponent>().ToList(); var targetList = _entityManager.EntityQuery<SentienceTargetComponent>().ToList();
_random.Shuffle(targetList); _random.Shuffle(targetList);
@@ -49,13 +50,26 @@ public sealed class RandomSentience : StationEvent
var kind1 = groupList.Count > 0 ? groupList[0] : "???"; var kind1 = groupList.Count > 0 ? groupList[0] : "???";
var kind2 = groupList.Count > 1 ? groupList[1] : "???"; var kind2 = groupList.Count > 1 ? groupList[1] : "???";
var kind3 = groupList.Count > 2 ? groupList[2] : "???"; var kind3 = groupList.Count > 2 ? groupList[2] : "???";
_chatManager.DispatchStationAnnouncement(
Loc.GetString("station-event-random-sentience-announcement", var stationSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<StationSystem>();
("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), var chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
("data", Loc.GetString($"random-sentience-event-data-{_random.Next(1, 6)}")), foreach (var target in targetList)
("strength", Loc.GetString($"random-sentience-event-strength-{_random.Next(1, 8)}"))), {
var station = stationSystem.GetOwningStation(target.Owner);
if(station == null) continue;
stationsToNotify.Add((EntityUid) station);
}
foreach (var station in stationsToNotify)
{
chatSystem.DispatchStationAnnouncement(
(EntityUid) station,
Loc.GetString("station-event-random-sentience-announcement",
("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count),
("data", Loc.GetString($"random-sentience-event-data-{_random.Next(1, 6)}")),
("strength", Loc.GetString($"random-sentience-event-strength-{_random.Next(1, 8)}"))),
playDefaultSound: false, playDefaultSound: false,
colorOverride: Color.Gold colorOverride: Color.Gold
); );
}
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Chat;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Station.Components; using Content.Server.Station.Components;
@@ -140,8 +141,8 @@ namespace Content.Server.StationEvents.Events
if (StartAnnouncement != null) if (StartAnnouncement != null)
{ {
var chatManager = IoCManager.Resolve<IChatManager>(); var chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
chatManager.DispatchStationAnnouncement(StartAnnouncement, playDefaultSound: false, colorOverride: Color.Gold); chatSystem.DispatchGlobalStationAnnouncement(StartAnnouncement, playDefaultSound: false, colorOverride: Color.Gold);
} }
if (StartAudio != null) if (StartAudio != null)
@@ -163,8 +164,8 @@ namespace Content.Server.StationEvents.Events
if (EndAnnouncement != null) if (EndAnnouncement != null)
{ {
var chatManager = IoCManager.Resolve<IChatManager>(); var chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
chatManager.DispatchStationAnnouncement(EndAnnouncement, playDefaultSound: false, colorOverride: Color.Gold); chatSystem.DispatchGlobalStationAnnouncement(EndAnnouncement, playDefaultSound: false, colorOverride: Color.Gold);
} }
if (EndAudio != null) if (EndAudio != null)

View File

@@ -1,6 +1,7 @@
using Content.Server.Chat;
using Robust.Shared.Random; using Robust.Shared.Random;
using Content.Server.Chat.Managers;
using Content.Server.Disease.Zombie.Components; using Content.Server.Disease.Zombie.Components;
using Content.Server.Station.Systems;
using Content.Shared.MobState.Components; using Content.Shared.MobState.Components;
using Content.Shared.Sound; using Content.Shared.Sound;
@@ -13,7 +14,6 @@ namespace Content.Server.StationEvents.Events
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
public override string Name => "ZombieOutbreak"; public override string Name => "ZombieOutbreak";
public override int EarliestStart => 50; public override int EarliestStart => 50;
@@ -31,6 +31,7 @@ namespace Content.Server.StationEvents.Events
public override void Startup() public override void Startup()
{ {
base.Startup(); base.Startup();
HashSet<EntityUid> stationsToNotify = new();
List<MobStateComponent> deadList = new(); List<MobStateComponent> deadList = new();
foreach (var mobState in _entityManager.EntityQuery<MobStateComponent>()) foreach (var mobState in _entityManager.EntityQuery<MobStateComponent>())
{ {
@@ -41,16 +42,25 @@ namespace Content.Server.StationEvents.Events
var toInfect = _random.Next(1, 3); var toInfect = _random.Next(1, 3);
/// Now we give it to people in the list of dead entities earlier. // Now we give it to people in the list of dead entities earlier.
var stationSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<StationSystem>();
var chatSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ChatSystem>();
foreach (var target in deadList) foreach (var target in deadList)
{ {
if (toInfect-- == 0) if (toInfect-- == 0)
break; break;
_entityManager.EnsureComponent<DiseaseZombieComponent>(target.Owner); _entityManager.EnsureComponent<DiseaseZombieComponent>(target.Owner);
var station = stationSystem.GetOwningStation(target.Owner);
if(station == null) continue;
stationsToNotify.Add((EntityUid) station);
}
foreach (var station in stationsToNotify)
{
chatSystem.DispatchStationAnnouncement((EntityUid) station, Loc.GetString("station-event-zombie-outbreak-announcement"),
playDefaultSound: false, colorOverride: Color.DarkMagenta);
} }
_chatManager.DispatchStationAnnouncement(Loc.GetString("station-event-zombie-outbreak-announcement"),
playDefaultSound: false, colorOverride: Color.DarkMagenta);
} }
} }
} }

View File

@@ -8,8 +8,11 @@ namespace Content.Shared.Administration
Station, Station,
Server, Server,
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class AdminAnnounceEuiState : EuiStateBase {} public sealed class AdminAnnounceEuiState : EuiStateBase
{
}
public static class AdminAnnounceEuiMsg public static class AdminAnnounceEuiMsg
{ {

View File

@@ -1,4 +1,17 @@
communicationsconsole-menu-title = Communications Console # User interface
communicationsconsole-menu-announcement-placeholder = Announcement comms-console-menu-title = Communications Console
communicationsconsole-menu-call-shuttle = Call emergency shuttle comms-console-menu-announcement-placeholder = Announcement
communicationsconsole-menu-recall-shuttle = Recall emergency shuttle comms-console-menu-call-shuttle = Call emergency shuttle
comms-console-menu-recall-shuttle = Recall emergency shuttle
# Popup
comms-console-permission-denied = Permission denied
# Placeholder values
comms-console-announcement-sent-by = Sent by
comms-console-announcement-unknown-sender = Unknown
# Comms console variant titles
comms-console-announcement-title-station = Communications Console
comms-console-announcement-title-centcom = Central Command
comms-console-announcement-title-nukie = Syndicate Nuclear Operative

View File

@@ -97,6 +97,8 @@
event: !type:ToggleIntrinsicUIEvent event: !type:ToggleIntrinsicUIEvent
- type: SolarControlConsole # look ma i AM the computer! - type: SolarControlConsole # look ma i AM the computer!
- type: CommunicationsConsole - type: CommunicationsConsole
title: communicationsconsole-announcement-title-centcom
color: "#228b22"
- type: RadarConsole - type: RadarConsole
- type: CargoConsole - type: CargoConsole
- type: CargoOrderDatabase - type: CargoOrderDatabase

View File

@@ -146,6 +146,15 @@
- type: ComputerBoard - type: ComputerBoard
prototype: ComputerComms prototype: ComputerComms
- type: entity
parent: BaseComputerCircuitboard
id: SyndicateCommsComputerCircuitboard
name: syndicate communications computer board
description: A computer printed circuit board for a syndicate communications console
components:
- type: ComputerBoard
prototype: SyndicateComputerComms
- type: entity - type: entity
parent: BaseComputerCircuitboard parent: BaseComputerCircuitboard
id: RadarConsoleCircuitboard id: RadarConsoleCircuitboard

View File

@@ -278,7 +278,10 @@
- type: ComputerVisualizer - type: ComputerVisualizer
key: generic_key key: generic_key
screen: comm screen: comm
- type: AccessReader
access: [[ "Command" ]]
- type: CommunicationsConsole - type: CommunicationsConsole
title: comms-console-announcement-title-station
- type: ActivatableUI - type: ActivatableUI
key: enum.CommunicationsConsoleUiKey.Key key: enum.CommunicationsConsoleUiKey.Key
- type: ActivatableUIRequiresPower - type: ActivatableUIRequiresPower
@@ -293,6 +296,31 @@
energy: 1.6 energy: 1.6
color: "#3c5eb5" color: "#3c5eb5"
- type: entity
parent: ComputerComms
id: SyndicateComputerComms
name: syndicate communications computer
description: This can be used for various important functions. Still under development.
components:
- type: Appearance
visuals:
- type: ComputerVisualizer
key: syndie_key
screen: comm_syndie
- type: AccessReader
access: [[ "NuclearOperative" ]]
- type: CommunicationsConsole
title: comms-console-announcement-title-nukie
color: "#ff0000"
canShuttle: false
global: true #announce to everyone they're about to fuck shit up
- type: Computer
board: SyndicateCommsComputerCircuitboard
- type: PointLight
radius: 1.5
energy: 1.6
color: "#f71713"
- type: entity - type: entity
parent: ComputerBase parent: ComputerBase
id: ComputerSolarControl id: ComputerSolarControl

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"license": "CC-BY-SA-3.0", "license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894", "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894. comm_syndie made by Veritius, based on comm.",
"size": { "size": {
"x": 32, "x": 32,
"y": 32 "y": 32
@@ -305,6 +305,28 @@
] ]
] ]
}, },
{
"name": "comm_syndie",
"directions": 4,
"delays": [
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
],
[
0.1,
0.1
]
]
},
{ {
"name": "command", "name": "command",
"directions": 4, "directions": 4,