Adds even more smites and a bunch of tools. (#9825)

* Adds three new smites, headstand, locker stuff, and reptilian species swap.

* Localize all the smites.

* save work

* More smites...

* Final tweaks.

* oops

* !PLEH

* Adds disarm prone and improved hand removal options.

* fix chances.

* take out the trash.

* Add some admin TRICKS instead of more smites.

* oop

* Implements the admin test arena and associated trick.

* Tricks for granting/revoking access.

* e

* mfw

* Implement quick dialogs, for when you don't want to spend 20 minutes writing a simple dialog prompt.

* Forgot the rejuv icon.

* E

* docs

* augh

* Add rename/redescribe buttons.

* Adds objects menu, implements a couple tricks for stations.

* 1984

* Adds a trick for effectively infinite power.

* fixes some icon uggo.

* a

* HALT!

* Pause/unpause buttons.

* Forgor the textures.

* they broke every bone in their body.

* i added more

* more battery actions, touch up battery icon.

* Address reviews.
This commit is contained in:
Moony
2022-07-21 17:30:00 -05:00
committed by GitHub
parent ec18f438bc
commit f98df73fae
76 changed files with 3708 additions and 88 deletions

View File

@@ -0,0 +1,188 @@
using Content.Client.Administration.Managers;
using Content.Client.Administration.UI;
using Content.Client.Administration.UI.Tabs.ObjectsTab;
using Content.Client.Administration.UI.Tabs.PlayerTab;
using Content.Client.HUD;
using Content.Client.Verbs;
using Content.Shared.Input;
using Robust.Client.Console;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Network;
namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem
{
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IGameHud _gameHud = default!;
[Dependency] private readonly IClientAdminManager _clientAdminManager = default!;
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
[Dependency] private readonly VerbSystem _verbSystem = default!;
private AdminMenuWindow? _window;
private readonly List<BaseWindow> _commandWindows = new();
private void InitializeMenu()
{
// Reset the AdminMenu Window on disconnect
_netManager.Disconnect += (_, _) => ResetWindow();
_inputManager.SetInputCommand(ContentKeyFunctions.OpenAdminMenu,
InputCmdHandler.FromDelegate(_ => Toggle()));
_clientAdminManager.AdminStatusUpdated += () =>
{
// when status changes, show the top button if we can open admin menu.
// if we can't or we lost admin status, close it and hide the button.
_gameHud.AdminButtonVisible = CanOpen();
if (!_gameHud.AdminButtonVisible)
{
Close();
}
};
_gameHud.AdminButtonToggled += (open) =>
{
if (open)
{
TryOpen();
}
else
{
Close();
}
};
_gameHud.AdminButtonVisible = CanOpen();
_gameHud.AdminButtonDown = false;
}
public void ResetWindow()
{
_window?.Close();
_window?.Dispose();
_window = null;
foreach (var window in _commandWindows)
{
window.Close();
window.Dispose();
}
_commandWindows.Clear();
}
public void OpenCommand(BaseWindow window)
{
_commandWindows.Add(window);
window.OpenCentered();
}
public void Open()
{
if (_window == null)
{
_window = new AdminMenuWindow();
_window.OnClose += Close;
}
_window.PlayerTabControl.OnEntryPressed += PlayerTabEntryPressed;
_window.ObjectsTabControl.OnEntryPressed += ObjectsTabEntryPressed;
_window.OpenCentered();
}
public void Close()
{
if (_window != null)
{
_window.PlayerTabControl.OnEntryPressed -= PlayerTabEntryPressed;
_window.ObjectsTabControl.OnEntryPressed -= ObjectsTabEntryPressed;
}
_window?.Close();
foreach (var window in _commandWindows)
window?.Dispose();
_commandWindows.Clear();
}
/// <summary>
/// Checks if the player can open the window
/// </summary>
/// <returns>True if the player is allowed</returns>
public bool CanOpen()
{
return _clientConGroupController.CanAdminMenu();
}
/// <summary>
/// Checks if the player can open the window and tries to open it
/// </summary>
public void TryOpen()
{
if (CanOpen())
Open();
}
public void Toggle()
{
if (_window != null && _window.IsOpen)
{
Close();
}
else
{
TryOpen();
}
}
private void PlayerTabEntryPressed(BaseButton.ButtonEventArgs args)
{
if (args.Button is not PlayerTabEntry button
|| button.PlayerUid == null)
return;
var uid = button.PlayerUid.Value;
var function = args.Event.Function;
if (function == EngineKeyFunctions.UIClick)
_clientConsoleHost.ExecuteCommand($"vv {uid}");
else if (function == ContentKeyFunctions.OpenContextMenu)
_verbSystem.VerbMenu.OpenVerbMenu(uid, true);
else
return;
args.Event.Handle();
}
private void ObjectsTabEntryPressed(BaseButton.ButtonEventArgs args)
{
if (args.Button is not ObjectsTabEntry button)
return;
var uid = button.AssocEntity;
var function = args.Event.Function;
if (function == EngineKeyFunctions.UIClick)
_clientConsoleHost.ExecuteCommand($"vv {uid}");
else if (function == ContentKeyFunctions.OpenContextMenu)
_verbSystem.VerbMenu.OpenVerbMenu(uid, true);
else
return;
args.Event.Handle();
}
}
}

View File

@@ -0,0 +1,45 @@
using Content.Client.Administration.Managers;
using Robust.Client.Graphics;
namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem
{
[Dependency] private readonly IClientAdminManager _adminManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
private AdminNameOverlay _adminNameOverlay = default!;
public event Action? OverlayEnabled;
public event Action? OverlayDisabled;
private void InitializeOverlay()
{
_adminNameOverlay = new AdminNameOverlay(this, _entityManager, _eyeManager, _resourceCache, _entityLookup);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
}
private void ShutdownOverlay()
{
_adminManager.AdminStatusUpdated -= OnAdminStatusUpdated;
}
private void OnAdminStatusUpdated()
{
AdminOverlayOff();
}
public void AdminOverlayOn()
{
if (_overlayManager.HasOverlay<AdminNameOverlay>()) return;
_overlayManager.AddOverlay(_adminNameOverlay);
OverlayEnabled?.Invoke();
}
public void AdminOverlayOff()
{
_overlayManager.RemoveOverlay<AdminNameOverlay>();
OverlayDisabled?.Invoke();
}
}
}

View File

@@ -0,0 +1,71 @@
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Administration.Events;
using Content.Shared.GameTicking;
using Robust.Shared.Network;
namespace Content.Client.Administration.Systems
{
public sealed partial class AdminSystem : EntitySystem
{
public event Action<List<PlayerInfo>>? PlayerListChanged;
private Dictionary<NetUserId, PlayerInfo>? _playerList;
public IReadOnlyList<PlayerInfo> PlayerList
{
get
{
if (_playerList != null) return _playerList.Values.ToList();
return new List<PlayerInfo>();
}
}
public override void Initialize()
{
base.Initialize();
InitializeOverlay();
InitializeMenu();
SubscribeNetworkEvent<FullPlayerListEvent>(OnPlayerListChanged);
SubscribeNetworkEvent<PlayerInfoChangedEvent>(OnPlayerInfoChanged);
SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
}
public override void Shutdown()
{
base.Shutdown();
ShutdownOverlay();
}
private void OnRoundRestartCleanup(RoundRestartCleanupEvent msg, EntitySessionEventArgs args)
{
if (_playerList == null)
return;
foreach (var (id, playerInfo) in _playerList.ToArray())
{
if (playerInfo.Connected)
continue;
_playerList.Remove(id);
}
PlayerListChanged?.Invoke(_playerList.Values.ToList());
}
private void OnPlayerInfoChanged(PlayerInfoChangedEvent ev)
{
if(ev.PlayerInfo == null) return;
if (_playerList == null) _playerList = new();
_playerList[ev.PlayerInfo.SessionId] = ev.PlayerInfo;
PlayerListChanged?.Invoke(_playerList.Values.ToList());
}
private void OnPlayerListChanged(FullPlayerListEvent msg)
{
_playerList = msg.PlayersInfo.ToDictionary(x => x.SessionId, x => x);
PlayerListChanged?.Invoke(msg.PlayersInfo);
}
}
}

View File

@@ -0,0 +1,36 @@
using Content.Shared.Verbs;
using Robust.Client.Console;
namespace Content.Client.Administration.Systems
{
/// <summary>
/// Client-side admin verb system. These usually open some sort of UIs.
/// </summary>
sealed class AdminVerbSystem : EntitySystem
{
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
public override void Initialize()
{
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
}
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
{
// Currently this is only the ViewVariables verb, but more admin-UI related verbs can be added here.
// View variables verbs
if (_clientConGroupController.CanViewVar())
{
Verb verb = new();
verb.Category = VerbCategory.Debug;
verb.Text = "View Variables";
verb.IconTexture = "/Textures/Interface/VerbIcons/vv.svg.192dpi.png";
verb.Act = () => _clientConsoleHost.ExecuteCommand($"vv {args.Target}");
verb.ClientExclusive = true; // opening VV window is client-side. Don't ask server to run this verb.
args.Verbs.Add(verb);
}
}
}
}

View File

@@ -0,0 +1,152 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Content.Client.Administration.Managers;
using Content.Client.Administration.UI;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.HUD;
using Content.Shared.Administration;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Audio;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Content.Client.Administration.Systems
{
[UsedImplicitly]
public sealed class BwoinkSystem : SharedBwoinkSystem
{
[Dependency] private readonly IClientAdminManager _adminManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IGameHud _hud = default!;
private BwoinkWindow? _adminWindow;
private DefaultWindow? _plainWindow;
private readonly Dictionary<NetUserId, BwoinkPanel> _activePanelMap = new();
public bool IsOpen => (_adminWindow?.IsOpen ?? false) || (_plainWindow?.IsOpen ?? false);
protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySessionEventArgs eventArgs)
{
base.OnBwoinkTextMessage(message, eventArgs);
LogBwoink(message);
// Actual line
var window = EnsurePanel(message.ChannelId);
window.ReceiveLine(message);
// Play a sound if we didn't send it
var localPlayer = _playerManager.LocalPlayer;
if (localPlayer?.UserId != message.TrueSender)
{
SoundSystem.Play("/Audio/Effects/adminhelp.ogg", Filter.Local());
_clyde.RequestWindowAttention();
}
// If they're not an admin force it open so they read
// If it's admin-admin messaging then eh.
if (!_adminManager.HasFlag(AdminFlags.Adminhelp))
_plainWindow?.Open();
else
{
_adminWindow?.OnBwoink(message.ChannelId);
if (_adminWindow?.IsOpen != true)
_hud.SetInfoRed(true);
}
}
public bool TryGetChannel(NetUserId ch, [NotNullWhen(true)] out BwoinkPanel? bp) => _activePanelMap.TryGetValue(ch, out bp);
private BwoinkPanel EnsureAdmin(NetUserId channelId)
{
_adminWindow ??= new BwoinkWindow(this);
if (!_activePanelMap.TryGetValue(channelId, out var existingPanel))
{
_activePanelMap[channelId] = existingPanel = new BwoinkPanel(this, channelId);
existingPanel.Visible = false;
if (!_adminWindow.BwoinkArea.Children.Contains(existingPanel))
_adminWindow.BwoinkArea.AddChild(existingPanel);
}
return existingPanel;
}
private BwoinkPanel EnsurePlain(NetUserId channelId)
{
BwoinkPanel bp;
if (_plainWindow is null)
{
bp = new BwoinkPanel(this, channelId);
_plainWindow = new DefaultWindow()
{
TitleClass="windowTitleAlert",
HeaderClass="windowHeaderAlert",
Title=Loc.GetString("bwoink-user-title"),
SetSize=(400, 200),
};
_plainWindow.Contents.AddChild(bp);
}
else
{
bp = (BwoinkPanel) _plainWindow.Contents.GetChild(0);
}
return bp;
}
public BwoinkPanel EnsurePanel(NetUserId channelId)
{
if (_adminManager.HasFlag(AdminFlags.Adminhelp))
return EnsureAdmin(channelId);
return EnsurePlain(channelId);
}
public void Open(NetUserId? channelId = null)
{
if (channelId == null)
{
var localPlayer = _playerManager.LocalPlayer;
if (localPlayer != null)
Open(localPlayer.UserId);
return;
}
_hud.SetInfoRed(false);
if (_adminManager.HasFlag(AdminFlags.Adminhelp))
{
SelectChannel(channelId.Value);
_adminWindow?.Open();
return;
}
EnsurePlain(channelId.Value);
_plainWindow?.Open();
}
public void Close()
{
_adminWindow?.Close();
_plainWindow?.Close();
}
private void SelectChannel(NetUserId uid)
{
_adminWindow ??= new BwoinkWindow(this);
_adminWindow.SelectChannel(uid);
}
public void Send(NetUserId channelId, string text)
{
// Reuse the channel ID as the 'true sender'.
// Server will ignore this and if someone makes it not ignore this (which is bad, allows impersonation!!!), that will help.
RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text));
}
}
}

View File

@@ -0,0 +1,35 @@
using Content.Client.Administration.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Administration.Systems;
public sealed class HeadstandSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<HeadstandComponent, ComponentStartup>(OnHeadstandAdded);
SubscribeLocalEvent<HeadstandComponent, ComponentShutdown>(OnHeadstandRemoved);
}
private void OnHeadstandAdded(EntityUid uid, HeadstandComponent component, ComponentStartup args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
foreach (var layer in sprite.AllLayers)
{
layer.Rotation += Angle.FromDegrees(180.0f);
}
}
private void OnHeadstandRemoved(EntityUid uid, HeadstandComponent component, ComponentShutdown args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
foreach (var layer in sprite.AllLayers)
{
layer.Rotation -= Angle.FromDegrees(180.0f);
}
}
}

View File

@@ -0,0 +1,47 @@
using Content.Client.Administration.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Administration.Systems;
public sealed class KillSignSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<KillSignComponent, ComponentStartup>(KillSignAdded);
SubscribeLocalEvent<KillSignComponent, ComponentShutdown>(KillSignRemoved);
}
private void KillSignRemoved(EntityUid uid, KillSignComponent component, ComponentShutdown args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
if (!sprite.LayerMapTryGet(KillSignKey.Key, out var layer))
return;
sprite.RemoveLayer(layer);
}
private void KillSignAdded(EntityUid uid, KillSignComponent component, ComponentStartup args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
if (sprite.LayerMapTryGet(KillSignKey.Key, out var _))
return;
var adj = sprite.Bounds.Height / 2 + ((1.0f/32) * 6.0f);
var layer = sprite.AddLayer(new SpriteSpecifier.Rsi(new ResourcePath("Objects/Misc/killsign.rsi"), "sign"));
sprite.LayerMapSet(KillSignKey.Key, layer);
sprite.LayerSetOffset(layer, new Vector2(0.0f, adj));
sprite.LayerSetShader(layer, "unshaded");
}
private enum KillSignKey
{
Key,
}
}