]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add fax admin panel (#15215)
authorMorb <14136326+Morb0@users.noreply.github.com>
Mon, 17 Apr 2023 06:20:57 +0000 (23:20 -0700)
committerGitHub <noreply@github.com>
Mon, 17 Apr 2023 06:20:57 +0000 (23:20 -0700)
13 files changed:
Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
Content.Client/Fax/AdminUI/AdminFaxEui.cs [new file with mode: 0644]
Content.Client/Fax/AdminUI/AdminFaxWindow.xaml [new file with mode: 0644]
Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs [new file with mode: 0644]
Content.Client/Fax/UI/FaxBoundUi.cs
Content.Client/Fax/UI/FaxWindow.xaml.cs
Content.Server/Administration/Commands/FaxUiCommand.cs [new file with mode: 0644]
Content.Server/Fax/AdminUI/AdminFaxEui.cs [new file with mode: 0644]
Content.Server/Fax/FaxMachineComponent.cs
Content.Server/Fax/FaxSystem.cs
Content.Shared/Fax/AdminFaxEui.cs [new file with mode: 0644]
Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
Resources/Locale/en-US/fax/fax-admin.ftl [new file with mode: 0644]

index 2573a2cf69608491804c560a7dab543ed114ea18..547e8d01a9399421610aacf802cac631e3582325 100644 (file)
@@ -15,6 +15,7 @@
             <cc:CommandButton Command="announceui" Text="{Loc admin-player-actions-window-announce}"/>
             <cc:UICommandButton Command="callshuttle" Text="{Loc admin-player-actions-window-shuttle}" WindowType="{x:Type at:AdminShuttleWindow}"/>
             <cc:CommandButton Command="adminlogs" Text="{Loc admin-player-actions-window-admin-logs}"/>
+            <cc:CommandButton Command="faxui" Text="{Loc admin-player-actions-window-admin-fax}"/>
         </GridContainer>
     </BoxContainer>
 </Control>
diff --git a/Content.Client/Fax/AdminUI/AdminFaxEui.cs b/Content.Client/Fax/AdminUI/AdminFaxEui.cs
new file mode 100644 (file)
index 0000000..06d0f71
--- /dev/null
@@ -0,0 +1,37 @@
+using Content.Client.Eui;
+using Content.Shared.Eui;
+using Content.Shared.Fax;
+using JetBrains.Annotations;
+
+namespace Content.Client.Fax.AdminUI;
+
+[UsedImplicitly]
+public sealed class AdminFaxEui : BaseEui
+{
+    private readonly AdminFaxWindow _window;
+
+    public AdminFaxEui()
+    {
+        _window = new AdminFaxWindow();
+        _window.OnClose += () => SendMessage(new AdminFaxEuiMsg.Close());
+        _window.OnFollowFax += uid => SendMessage(new AdminFaxEuiMsg.Follow(uid));
+        _window.OnMessageSend += args => SendMessage(new AdminFaxEuiMsg.Send(args.uid, args.title, args.from, args.message, args.stamp));
+    }
+
+    public override void Opened()
+    {
+        _window.OpenCentered();
+    }
+
+    public override void Closed()
+    {
+        _window.Close();
+    }
+
+    public override void HandleState(EuiStateBase state)
+    {
+        if (state is not AdminFaxEuiState cast)
+            return;
+        _window.PopulateFaxes(cast.Entries);
+    }
+}
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
new file mode 100644 (file)
index 0000000..0c148a9
--- /dev/null
@@ -0,0 +1,27 @@
+<DefaultWindow xmlns="https://spacestation14.io"
+               Title="{Loc admin-fax-title}"
+               MinWidth="400">
+    <BoxContainer Orientation="Vertical" VerticalExpand="True">
+        <BoxContainer Orientation="Horizontal">
+            <Label Text="{Loc admin-fax-fax}" />
+            <Control MinWidth="4" />
+            <OptionButton Name="FaxSelector" HorizontalExpand="True" />
+            <Control MinWidth="4" />
+            <Button Name="FollowButton" Text="{Loc admin-fax-follow}" />
+        </BoxContainer>
+        <Control MinHeight="5" />
+        <LineEdit Name="TitleEdit" HorizontalExpand="True" PlaceHolder="{Loc admin-fax-title-placeholder}"></LineEdit>
+        <Control MinHeight="5" />
+        <LineEdit Name="FromEdit" HorizontalExpand="True" PlaceHolder="{Loc admin-fax-from-placeholder}"></LineEdit>
+        <Control MinHeight="5" />
+        <TextEdit Name="MessageEdit" HorizontalExpand="True" MinHeight="200"></TextEdit>
+        <Control MinHeight="5" />
+        <BoxContainer Orientation="Horizontal">
+            <Label Text="{Loc admin-fax-stamp}" />
+            <Control MinWidth="5" />
+            <OptionButton Name="StampSelector" HorizontalExpand="True" />
+        </BoxContainer>
+        <Control MinHeight="10" />
+        <Button Name="SendButton" Text="{Loc admin-fax-send}"></Button>
+    </BoxContainer>
+</DefaultWindow>
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
new file mode 100644 (file)
index 0000000..207a39c
--- /dev/null
@@ -0,0 +1,95 @@
+using Content.Shared.Fax;
+using Robust.Client.AutoGenerated;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Fax.AdminUI;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminFaxWindow : DefaultWindow
+{
+    private const string StampsRsiPath = "/Textures/Objects/Misc/bureaucracy.rsi";
+
+    public Action<(EntityUid uid, string title, string from, string message, string stamp)>? OnMessageSend;
+    public Action<EntityUid>? OnFollowFax;
+
+    public AdminFaxWindow()
+    {
+        RobustXamlLoader.Load(this);
+        IoCManager.InjectDependencies(this);
+
+        PopulateStamps();
+
+        FaxSelector.OnItemSelected += args => FaxSelector.SelectId(args.Id);
+        StampSelector.OnItemSelected += args => StampSelector.SelectId(args.Id);
+        FollowButton.OnPressed += FollowFax;
+        SendButton.OnPressed += SendMessage;
+
+        var loc = IoCManager.Resolve<ILocalizationManager>();
+        MessageEdit.Placeholder = new Rope.Leaf(loc.GetString("admin-fax-message-placeholder")); // TextEdit work only with Nodes
+    }
+
+    public void PopulateFaxes(List<AdminFaxEntry> faxes)
+    {
+        for (var i = 0; i < faxes.Count; i++)
+        {
+            var fax = faxes[i];
+            FaxSelector.AddItem($"{fax.Name} ({fax.Address})", i);
+            FaxSelector.SetItemMetadata(i, fax.Uid);
+        }
+    }
+
+    private void PopulateStamps()
+    {
+        var rsi = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(StampsRsiPath).RSI;
+        using (var enumerator = rsi.GetEnumerator())
+        {
+            var i = 0;
+            while (enumerator.MoveNext())
+            {
+                var state = enumerator.Current;
+                var stateName = state.StateId.Name!;
+                if (!stateName.StartsWith("paper_stamp-"))
+                    continue;
+
+                StampSelector.AddItem(stateName, i);
+                StampSelector.SetItemMetadata(i, stateName);
+                i++;
+            }
+        }
+    }
+
+    private void FollowFax(BaseButton.ButtonEventArgs obj)
+    {
+        var faxUid = (EntityUid?) FaxSelector.SelectedMetadata;
+        if (faxUid == null)
+            return;
+
+        OnFollowFax?.Invoke(faxUid.Value);
+    }
+
+    private void SendMessage(BaseButton.ButtonEventArgs obj)
+    {
+        var faxUid = (EntityUid?) FaxSelector.SelectedMetadata;
+        if (faxUid == null)
+            return;
+
+        var stamp = (string?) StampSelector.SelectedMetadata;
+        if (stamp == null)
+            return;
+
+        var title = TitleEdit.Text;
+        if (string.IsNullOrEmpty(title))
+            return;
+
+        var message = Rope.Collapse(MessageEdit.TextRope);
+        if (string.IsNullOrEmpty(message))
+            return;
+
+        var from = FromEdit.Text;
+        OnMessageSend?.Invoke((faxUid.Value, title, from, message, stamp));
+    }
+}
index f6d31e78dbfae788dc9fbfbdeca0d8b3e5f49bfe..19a48a3a34230555428a163729027df5fc13dbb4 100644 (file)
@@ -1,12 +1,14 @@
 using Content.Shared.Fax;
+using JetBrains.Annotations;
 using Robust.Client.GameObjects;
 
 namespace Content.Client.Fax.UI;
 
+[UsedImplicitly]
 public sealed class FaxBoundUi : BoundUserInterface
 {
     private FaxWindow? _window;
-    
+
     public FaxBoundUi(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
     {
     }
@@ -42,7 +44,7 @@ public sealed class FaxBoundUi : BoundUserInterface
     protected override void UpdateState(BoundUserInterfaceState state)
     {
         base.UpdateState(state);
-        
+
         if (_window == null || state is not FaxUiState cast)
             return;
 
index 56c53d162cf1ee98a120b6dc3694a1ac5be88e13..759a8865ecbdb430b28c2704a05805e9d2696afe 100644 (file)
@@ -1,7 +1,6 @@
 using System.Linq;
 using Content.Shared.Fax;
 using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
 
diff --git a/Content.Server/Administration/Commands/FaxUiCommand.cs b/Content.Server/Administration/Commands/FaxUiCommand.cs
new file mode 100644 (file)
index 0000000..b06d36d
--- /dev/null
@@ -0,0 +1,31 @@
+using Content.Server.EUI;
+using Content.Server.Fax.AdminUI;
+using Content.Shared.Administration;
+using Robust.Server.Player;
+using Robust.Shared.Console;
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class FaxUiCommand : IConsoleCommand
+{
+    public string Command => "faxui";
+
+    public string Description => Loc.GetString("cmd-faxui-desc");
+    public string Help => Loc.GetString("cmd-faxui-help");
+
+    public void Execute(IConsoleShell shell, string argStr, string[] args)
+    {
+        var player = shell.Player as IPlayerSession;
+        if (player == null)
+        {
+            shell.WriteLine("shell-only-players-can-run-this-command");
+            return;
+        }
+
+        var eui = IoCManager.Resolve<EuiManager>();
+        var ui = new AdminFaxEui();
+        eui.OpenEui(ui, player);
+    }
+}
+
diff --git a/Content.Server/Fax/AdminUI/AdminFaxEui.cs b/Content.Server/Fax/AdminUI/AdminFaxEui.cs
new file mode 100644 (file)
index 0000000..87dd44a
--- /dev/null
@@ -0,0 +1,65 @@
+using Content.Server.DeviceNetwork.Components;
+using Content.Server.EUI;
+using Content.Server.Ghost.Components;
+using Content.Shared.Eui;
+using Content.Shared.Fax;
+using Content.Shared.Follower;
+
+namespace Content.Server.Fax.AdminUI;
+
+public sealed class AdminFaxEui : BaseEui
+{
+    [Dependency] private readonly IEntityManager _entityManager = default!;
+    private readonly FaxSystem _faxSystem;
+    private readonly FollowerSystem _followerSystem;
+
+    public AdminFaxEui()
+    {
+        IoCManager.InjectDependencies(this);
+        _faxSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<FaxSystem>();
+        _followerSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<FollowerSystem>();
+    }
+
+    public override void Opened()
+    {
+        StateDirty();
+    }
+
+    public override AdminFaxEuiState GetNewState()
+    {
+        var faxes = _entityManager.EntityQueryEnumerator<FaxMachineComponent, DeviceNetworkComponent>();
+        var entries = new List<AdminFaxEntry>();
+        while (faxes.MoveNext(out var uid, out var fax, out var device))
+        {
+            entries.Add(new AdminFaxEntry(uid, fax.FaxName, device.Address));
+        }
+        return new AdminFaxEuiState(entries);
+    }
+
+    public override void HandleMessage(EuiMessageBase msg)
+    {
+        switch (msg)
+        {
+            case AdminFaxEuiMsg.Close:
+            {
+                Close();
+                break;
+            }
+            case AdminFaxEuiMsg.Follow followData:
+            {
+                if (Player.AttachedEntity == null ||
+                    !_entityManager.HasComponent<GhostComponent>(Player.AttachedEntity.Value))
+                    return;
+
+                _followerSystem.StartFollowingEntity(Player.AttachedEntity.Value, followData.TargetFax);
+                break;
+            }
+            case AdminFaxEuiMsg.Send sendData:
+            {
+                var printout = new FaxPrintout(sendData.Content, sendData.Title, null, sendData.StampState, new() { sendData.From });
+                _faxSystem.Receive(sendData.Target, printout);
+                break;
+            }
+        }
+    }
+}
index edbc7576504aa6f33fa1fec2baa55874854e33fb..bdc97bc2450afa505304358c41a140a2a1c85caa 100644 (file)
@@ -142,7 +142,7 @@ public sealed class FaxPrintout
     {
     }
 
-    public FaxPrintout(string content, string name, string? prototypeId, string? stampState = null, List<string>? stampedBy = null)
+    public FaxPrintout(string content, string name, string? prototypeId = null, string? stampState = null, List<string>? stampedBy = null)
     {
         Content = content;
         Name = name;
index 2881dae02224e93a55a5bdb86ee8f4cdb5c9fb9d..26df47ce1b68b685d0998e7ccdc68ba67d427c90 100644 (file)
@@ -7,7 +7,6 @@ using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Paper;
 using Content.Server.Popups;
 using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
 using Content.Server.Tools;
 using Content.Server.UserInterface;
 using Content.Shared.Administration.Logs;
@@ -18,6 +17,7 @@ using Content.Shared.Emag.Systems;
 using Content.Shared.Fax;
 using Content.Shared.Interaction;
 using Robust.Server.GameObjects;
+using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 
@@ -38,7 +38,7 @@ public sealed class FaxSystem : EntitySystem
     [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
     [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
 
-    public const string PaperSlotId = "Paper";
+    private const string PaperSlotId = "Paper";
 
     public override void Initialize()
     {
@@ -69,29 +69,30 @@ public sealed class FaxSystem : EntitySystem
     {
         base.Update(frameTime);
 
-        foreach (var (comp, receiver) in EntityQuery<FaxMachineComponent, ApcPowerReceiverComponent>())
+        var query = EntityQueryEnumerator<FaxMachineComponent, ApcPowerReceiverComponent>();
+        while (query.MoveNext(out var uid, out var fax, out var receiver))
         {
             if (!receiver.Powered)
                 continue;
 
-            ProcessPrintingAnimation(frameTime, comp);
-            ProcessInsertingAnimation(frameTime, comp);
-            ProcessSendingTimeout(frameTime, comp);
+            ProcessPrintingAnimation(uid, frameTime, fax);
+            ProcessInsertingAnimation(uid, frameTime, fax);
+            ProcessSendingTimeout(uid, frameTime, fax);
         }
     }
 
-    private void ProcessPrintingAnimation(float frameTime, FaxMachineComponent comp)
+    private void ProcessPrintingAnimation(EntityUid uid, float frameTime, FaxMachineComponent comp)
     {
         if (comp.PrintingTimeRemaining > 0)
         {
             comp.PrintingTimeRemaining -= frameTime;
-            UpdateAppearance(comp.Owner, comp);
+            UpdateAppearance(uid, comp);
 
             var isAnimationEnd = comp.PrintingTimeRemaining <= 0;
             if (isAnimationEnd)
             {
-                SpawnPaperFromQueue(comp.Owner, comp);
-                UpdateUserInterface(comp.Owner, comp);
+                SpawnPaperFromQueue(uid, comp);
+                UpdateUserInterface(uid, comp);
             }
 
             return;
@@ -100,40 +101,40 @@ public sealed class FaxSystem : EntitySystem
         if (comp.PrintingQueue.Count > 0)
         {
             comp.PrintingTimeRemaining = comp.PrintingTime;
-            _audioSystem.PlayPvs(comp.PrintSound, comp.Owner);
+            _audioSystem.PlayPvs(comp.PrintSound, uid);
         }
     }
 
-    private void ProcessInsertingAnimation(float frameTime, FaxMachineComponent comp)
+    private void ProcessInsertingAnimation(EntityUid uid, float frameTime, FaxMachineComponent comp)
     {
         if (comp.InsertingTimeRemaining <= 0)
             return;
 
         comp.InsertingTimeRemaining -= frameTime;
-        UpdateAppearance(comp.Owner, comp);
+        UpdateAppearance(uid, comp);
 
         var isAnimationEnd = comp.InsertingTimeRemaining <= 0;
         if (isAnimationEnd)
         {
-            _itemSlotsSystem.SetLock(comp.Owner, comp.PaperSlot, false);
-            UpdateUserInterface(comp.Owner, comp);
+            _itemSlotsSystem.SetLock(uid, comp.PaperSlot, false);
+            UpdateUserInterface(uid, comp);
         }
     }
 
-    private void ProcessSendingTimeout(float frameTime, FaxMachineComponent comp)
+    private void ProcessSendingTimeout(EntityUid uid, float frameTime, FaxMachineComponent comp)
     {
         if (comp.SendTimeoutRemaining > 0)
         {
             comp.SendTimeoutRemaining -= frameTime;
 
             if (comp.SendTimeoutRemaining <= 0)
-                UpdateUserInterface(comp.Owner, comp);
+                UpdateUserInterface(uid, comp);
         }
     }
 
     private void OnComponentInit(EntityUid uid, FaxMachineComponent component, ComponentInit args)
     {
-        _itemSlotsSystem.AddItemSlot(uid, FaxSystem.PaperSlotId, component.PaperSlot);
+        _itemSlotsSystem.AddItemSlot(uid, PaperSlotId, component.PaperSlot);
         UpdateAppearance(uid, component);
     }
 
@@ -185,7 +186,7 @@ public sealed class FaxSystem : EntitySystem
         }
 
         if (isInsertInterrupted || isPrintInterrupted)
-            UpdateAppearance(component.Owner, component);
+            UpdateAppearance(uid, component);
 
         _itemSlotsSystem.SetLock(uid, component.PaperSlot, !args.Powered); // Lock slot when power is off
     }
@@ -425,14 +426,14 @@ public sealed class FaxSystem : EntitySystem
     ///     Accepts a new message and adds it to the queue to print
     ///     If has parameter "notifyAdmins" also output a special message to admin chat.
     /// </summary>
-    public void Receive(EntityUid uid, FaxPrintout printout, string? fromAddress, FaxMachineComponent? component = null)
+    public void Receive(EntityUid uid, FaxPrintout printout, string? fromAddress = null, FaxMachineComponent? component = null)
     {
         if (!Resolve(uid, ref component))
             return;
 
         var faxName = Loc.GetString("fax-machine-popup-source-unknown");
-        if (fromAddress != null && component.KnownFaxes.ContainsKey(fromAddress)) // If message received from unknown for fax address
-            faxName = component.KnownFaxes[fromAddress];
+        if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address
+            faxName = fax;
 
         _popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-received", ("from", faxName)), uid);
         _appearanceSystem.SetData(uid, FaxMachineVisuals.VisualState, FaxMachineVisualState.Printing);
@@ -476,6 +477,6 @@ public sealed class FaxSystem : EntitySystem
     private void NotifyAdmins(string faxName)
     {
         _chat.SendAdminAnnouncement(Loc.GetString("fax-machine-chat-notify", ("fax", faxName)));
-        _audioSystem.PlayGlobal("/Audio/Machines/high_tech_confirm.ogg", Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false);
+        _audioSystem.PlayGlobal("/Audio/Machines/high_tech_confirm.ogg", Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false, AudioParams.Default.WithVolume(-8f));
     }
 }
diff --git a/Content.Shared/Fax/AdminFaxEui.cs b/Content.Shared/Fax/AdminFaxEui.cs
new file mode 100644 (file)
index 0000000..cd59b4c
--- /dev/null
@@ -0,0 +1,68 @@
+using Content.Shared.Eui;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Fax;
+
+[Serializable, NetSerializable]
+public sealed class AdminFaxEuiState : EuiStateBase
+{
+    public List<AdminFaxEntry> Entries { get; }
+
+    public AdminFaxEuiState(List<AdminFaxEntry> entries)
+    {
+        Entries = entries;
+    }
+}
+
+[Serializable, NetSerializable]
+public sealed class AdminFaxEntry
+{
+    public EntityUid Uid { get; }
+    public string Name { get; }
+    public string Address { get; }
+
+    public AdminFaxEntry(EntityUid uid, string name, string address)
+    {
+        Uid = uid;
+        Name = name;
+        Address = address;
+    }
+}
+
+public static class AdminFaxEuiMsg
+{
+    [Serializable, NetSerializable]
+    public sealed class Close : EuiMessageBase
+    {
+    }
+
+    [Serializable, NetSerializable]
+    public sealed class Follow : EuiMessageBase
+    {
+        public EntityUid TargetFax { get; }
+
+        public Follow(EntityUid targetFax)
+        {
+            TargetFax = targetFax;
+        }
+    }
+
+    [Serializable, NetSerializable]
+    public sealed class Send : EuiMessageBase
+    {
+        public EntityUid Target { get; }
+        public string Title { get; }
+        public string From { get; }
+        public string Content { get; }
+        public string StampState { get; }
+
+        public Send(EntityUid target, string title, string from, string content, string stamp)
+        {
+            Target = target;
+            Title = title;
+            From = from;
+            Content = content;
+            StampState = stamp;
+        }
+    }
+}
index a03677a4b00aa990d98427cf3f3a5257b682fda8..57ef3e04b8768c422f705b43c0a27d5204ffac2b 100644 (file)
@@ -7,3 +7,4 @@ admin-player-actions-window-announce = Announce
 admin-player-actions-window-shuttle = (Re)call Shuttle
 admin-player-actions-window-admin-logs = Admin Logs
 admin-player-actions-window-admin-notes = Admin Notes
+admin-player-actions-window-admin-fax = Admin Fax
diff --git a/Resources/Locale/en-US/fax/fax-admin.ftl b/Resources/Locale/en-US/fax/fax-admin.ftl
new file mode 100644 (file)
index 0000000..c05b833
--- /dev/null
@@ -0,0 +1,13 @@
+# Command
+cmd-faxui-desc = Open admin window for sending faxes
+cmd-faxui-help = Usage: faxui
+
+# Window
+admin-fax-title = Admin Fax Manager
+admin-fax-fax = Fax:
+admin-fax-follow = Follow
+admin-fax-title-placeholder = Paper name...
+admin-fax-from-placeholder = From who...
+admin-fax-message-placeholder = Your message here...
+admin-fax-stamp = Stamp:
+admin-fax-send = Send