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.
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Administration.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared.Administration.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
}
|
||||
154
Content.Client/Administration/QuickDialogSystem.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
|
||||
/// <summary>
|
||||
/// This handles the client portion of quick dialogs.
|
||||
/// </summary>
|
||||
public sealed class QuickDialogSystem : EntitySystem
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeNetworkEvent<QuickDialogOpenEvent>(OpenDialog);
|
||||
}
|
||||
|
||||
private void OpenDialog(QuickDialogOpenEvent ev)
|
||||
{
|
||||
var window = new FancyWindow()
|
||||
{
|
||||
Title = ev.Title
|
||||
};
|
||||
|
||||
var entryContainer = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8),
|
||||
};
|
||||
|
||||
var promptsDict = new Dictionary<string, LineEdit>();
|
||||
|
||||
foreach (var entry in ev.Prompts)
|
||||
{
|
||||
var entryBox = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
entryBox.AddChild(new Label { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
|
||||
var edit = new LineEdit() { HorizontalExpand = true};
|
||||
entryBox.AddChild(edit);
|
||||
switch (entry.Type)
|
||||
{
|
||||
case QuickDialogEntryType.Integer:
|
||||
edit.IsValid += VerifyInt;
|
||||
edit.PlaceHolder = "Integer..";
|
||||
break;
|
||||
case QuickDialogEntryType.Float:
|
||||
edit.IsValid += VerifyFloat;
|
||||
edit.PlaceHolder = "Float..";
|
||||
break;
|
||||
case QuickDialogEntryType.ShortText:
|
||||
edit.IsValid += VerifyShortText;
|
||||
edit.PlaceHolder = "Short text..";
|
||||
break;
|
||||
case QuickDialogEntryType.LongText:
|
||||
edit.IsValid += VerifyLongText;
|
||||
edit.PlaceHolder = "Long text..";
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
promptsDict.Add(entry.FieldId, edit);
|
||||
entryContainer.AddChild(entryBox);
|
||||
}
|
||||
|
||||
var buttonsBox = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = Control.HAlignment.Center,
|
||||
};
|
||||
|
||||
var alreadyReplied = false;
|
||||
|
||||
if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
|
||||
{
|
||||
var okButton = new Button()
|
||||
{
|
||||
Text = "Ok",
|
||||
};
|
||||
|
||||
okButton.OnPressed += _ =>
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
|
||||
QuickDialogButtonFlag.OkButton));
|
||||
alreadyReplied = true;
|
||||
window.Close();
|
||||
};
|
||||
|
||||
buttonsBox.AddChild(okButton);
|
||||
}
|
||||
|
||||
if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
|
||||
{
|
||||
var cancelButton = new Button()
|
||||
{
|
||||
Text = "Cancel",
|
||||
};
|
||||
|
||||
cancelButton.OnPressed += _ =>
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
new(),
|
||||
QuickDialogButtonFlag.CancelButton));
|
||||
alreadyReplied = true;
|
||||
window.Close();
|
||||
};
|
||||
|
||||
buttonsBox.AddChild(cancelButton);
|
||||
}
|
||||
|
||||
window.OnClose += () =>
|
||||
{
|
||||
if (!alreadyReplied)
|
||||
{
|
||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||
new(),
|
||||
QuickDialogButtonFlag.CancelButton));
|
||||
}
|
||||
};
|
||||
|
||||
entryContainer.AddChild(buttonsBox);
|
||||
|
||||
window.ContentsContainer.AddChild(entryContainer);
|
||||
|
||||
window.MinWidth *= 2; // Just double it.
|
||||
|
||||
window.OpenCentered();
|
||||
}
|
||||
|
||||
private bool VerifyInt(string input)
|
||||
{
|
||||
return int.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyFloat(string input)
|
||||
{
|
||||
return float.TryParse(input, out var _);
|
||||
}
|
||||
|
||||
private bool VerifyShortText(string input)
|
||||
{
|
||||
return input.Length <= 100;
|
||||
}
|
||||
|
||||
private bool VerifyLongText(string input)
|
||||
{
|
||||
return input.Length <= 2000;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
@@ -11,13 +11,11 @@ using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.Administration
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
public sealed partial class AdminSystem
|
||||
{
|
||||
@@ -101,13 +99,18 @@ namespace Content.Client.Administration
|
||||
}
|
||||
|
||||
_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)
|
||||
@@ -163,5 +166,23 @@ namespace Content.Client.Administration
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.Administration
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
public sealed partial class AdminSystem
|
||||
{
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Events;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.Administration
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
public sealed partial class AdminSystem : EntitySystem
|
||||
{
|
||||
@@ -1,10 +1,7 @@
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.Verbs
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side admin verb system. These usually open some sort of UIs.
|
||||
@@ -1,7 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Administration.UI;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
@@ -9,16 +7,13 @@ using Content.Client.HUD;
|
||||
using Content.Shared.Administration;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Administration
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BwoinkSystem : SharedBwoinkSystem
|
||||
35
Content.Client/Administration/Systems/HeadstandSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class KillSignSystem : EntitySystem
|
||||
{
|
||||
@@ -4,7 +4,8 @@
|
||||
xmlns:adminbusTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminbusTab"
|
||||
xmlns:atmosTab="clr-namespace:Content.Client.Administration.UI.Tabs.AtmosTab"
|
||||
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab">
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab">
|
||||
<TabContainer Name="MasterTabContainer">
|
||||
<adminTab:AdminTab />
|
||||
<adminbusTab:AdminbusTab />
|
||||
@@ -12,5 +13,6 @@
|
||||
<tabs:RoundTab />
|
||||
<tabs:ServerTab />
|
||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||
</TabContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Content.Client.Administration.UI
|
||||
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-objects-tab"));
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.Administration.UI.Tabs.AdminTab;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Object type:}" />
|
||||
<OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="ObjectList">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Station;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
|
||||
private readonly List<ObjectsTabEntry> _objects = new();
|
||||
private List<ObjectsTabSelection> _selections = new();
|
||||
|
||||
public event Action<BaseButton.ButtonEventArgs>? OnEntryPressed;
|
||||
|
||||
public ObjectsTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
ObjectTypeOptions.OnItemSelected += ev =>
|
||||
{
|
||||
ObjectTypeOptions.SelectId(ev.Id);
|
||||
RefreshObjectList(_selections[ev.Id]);
|
||||
};
|
||||
|
||||
foreach (var type in Enum.GetValues(typeof(ObjectsTabSelection)))
|
||||
{
|
||||
_selections.Add((ObjectsTabSelection)type!);
|
||||
ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type!)!);
|
||||
}
|
||||
|
||||
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
|
||||
}
|
||||
|
||||
private void RefreshObjectList(ObjectsTabSelection selection)
|
||||
{
|
||||
var entities = selection switch
|
||||
{
|
||||
ObjectsTabSelection.Stations => _entityManager.EntitySysManager.GetEntitySystem<StationSystem>().Stations.ToList(),
|
||||
ObjectsTabSelection.Grids => _entityManager.EntityQuery<IMapGridComponent>(true).Select(x => x.Owner).ToList(),
|
||||
ObjectsTabSelection.Maps => _entityManager.EntityQuery<IMapComponent>(true).Select(x => x.Owner).ToList(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(selection), selection, null)
|
||||
};
|
||||
|
||||
foreach (var control in _objects)
|
||||
{
|
||||
ObjectList.RemoveChild(control);
|
||||
}
|
||||
|
||||
_objects.Clear();
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var ctrl = new ObjectsTabEntry(_entityManager.GetComponent<MetaDataComponent>(entity).EntityName, entity);
|
||||
_objects.Add(ctrl);
|
||||
ObjectList.AddChild(ctrl);
|
||||
ctrl.OnPressed += args => OnEntryPressed?.Invoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
private enum ObjectsTabSelection
|
||||
{
|
||||
Grids,
|
||||
Maps,
|
||||
Stations,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<ContainerButton xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
EnableAllKeybinds="True">
|
||||
<PanelContainer Name="BackgroundColorPanel"/>
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="NameLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="EIDLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</ContainerButton>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabEntry : ContainerButton
|
||||
{
|
||||
public EntityUid AssocEntity;
|
||||
|
||||
public ObjectsTabEntry(string name, EntityUid euid)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
AssocEntity = euid;
|
||||
EIDLabel.Text = euid.ToString();
|
||||
NameLabel.Text = name;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Client.Administration;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using Content.Client.HUD.UI;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Administration;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Targeting;
|
||||
using Content.Shared.CCVar;
|
||||
|
||||
32
Content.Client/Station/StationSystem.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Content.Shared.Station;
|
||||
|
||||
namespace Content.Client.Station;
|
||||
|
||||
/// <summary>
|
||||
/// This handles letting the client know stations are a thing. Only really used by an admin menu.
|
||||
/// </summary>
|
||||
public sealed class StationSystem : EntitySystem
|
||||
{
|
||||
|
||||
private readonly HashSet<EntityUid> _stations = new();
|
||||
|
||||
/// <summary>
|
||||
/// All stations that currently exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
|
||||
/// </remarks>
|
||||
public IReadOnlySet<EntityUid> Stations => _stations;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
|
||||
}
|
||||
|
||||
private void StationsUpdated(StationsUpdatedEvent ev)
|
||||
{
|
||||
_stations.Clear();
|
||||
_stations.UnionWith(ev.Stations);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface"
|
||||
MouseFilter="Stop"
|
||||
MinWidth="200" MinHeight="150">
|
||||
@@ -15,6 +16,6 @@
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<Control Name="ContentsContainer" Margin="0 2" RectClipContent="True" VerticalExpand="true" />
|
||||
<Control Access="Public" Name="ContentsContainer" Margin="0 2" RectClipContent="True" VerticalExpand="true" />
|
||||
</BoxContainer>
|
||||
</ui:FancyWindow>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server.Administration.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for forcing someone to be disarmed 100% of the time.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DisarmProneComponent : Component { }
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared.Administration.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.Administration.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Administration.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the admin map-wide/station-wide/grid-wide infinite power trick.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class StationInfiniteBatteryTargetComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
176
Content.Server/Administration/QuickDialogSystem.OpenDialog.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using Content.Shared.Administration;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.Administration;
|
||||
|
||||
public sealed partial class QuickDialogSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens a dialog for the given client, allowing them to enter in the desired data.
|
||||
/// </summary>
|
||||
/// <param name="session">Client to show a dialog for.</param>
|
||||
/// <param name="title">Title of the dialog.</param>
|
||||
/// <param name="prompt">The prompt.</param>
|
||||
/// <param name="okAction">The action to execute upon Ok being pressed.</param>
|
||||
/// <param name="cancelAction">The action to execute upon the dialog being cancelled.</param>
|
||||
/// <typeparam name="T1">Type of the input.</typeparam>
|
||||
[PublicAPI]
|
||||
public void OpenDialog<T1>(IPlayerSession session, string title, string prompt, Action<T1> okAction,
|
||||
Action? cancelAction = null)
|
||||
{
|
||||
OpenDialogInternal(
|
||||
session,
|
||||
title,
|
||||
new List<QuickDialogEntry>
|
||||
{
|
||||
new("1", TypeToEntryType(typeof(T1)), prompt)
|
||||
},
|
||||
QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton,
|
||||
(ev =>
|
||||
{
|
||||
if (TryParseQuickDialog<T1>(TypeToEntryType(typeof(T1)), ev.Responses["1"], out var v1))
|
||||
okAction.Invoke(v1);
|
||||
else
|
||||
{
|
||||
session.ConnectedClient.Disconnect("Replied with invalid quick dialog data.");
|
||||
cancelAction?.Invoke();
|
||||
}
|
||||
}),
|
||||
cancelAction ?? (() => { })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog for the given client, allowing them to enter in the desired data.
|
||||
/// </summary>
|
||||
/// <param name="session">Client to show a dialog for.</param>
|
||||
/// <param name="title">Title of the dialog.</param>
|
||||
/// <param name="prompt1">The first prompt.</param>
|
||||
/// <param name="prompt2">The second prompt.</param>
|
||||
/// <param name="okAction">The action to execute upon Ok being pressed.</param>
|
||||
/// <param name="cancelAction">The action to execute upon the dialog being cancelled.</param>
|
||||
/// <typeparam name="T1">Type of the first input.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second input.</typeparam>
|
||||
[PublicAPI]
|
||||
public void OpenDialog<T1, T2>(IPlayerSession session, string title, string prompt1, string prompt2,
|
||||
Action<T1, T2> okAction, Action? cancelAction = null)
|
||||
{
|
||||
OpenDialogInternal(
|
||||
session,
|
||||
title,
|
||||
new List<QuickDialogEntry>
|
||||
{
|
||||
new("1", TypeToEntryType(typeof(T1)), prompt1),
|
||||
new("2", TypeToEntryType(typeof(T2)), prompt2)
|
||||
},
|
||||
QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton,
|
||||
(ev =>
|
||||
{
|
||||
|
||||
if (TryParseQuickDialog<T1>(TypeToEntryType(typeof(T1)), ev.Responses["1"], out var v1) &&
|
||||
TryParseQuickDialog<T2>(TypeToEntryType(typeof(T2)), ev.Responses["2"], out var v2)
|
||||
)
|
||||
okAction.Invoke(v1, v2);
|
||||
else
|
||||
{
|
||||
session.ConnectedClient.Disconnect("Replied with invalid quick dialog data.");
|
||||
cancelAction?.Invoke();
|
||||
}
|
||||
}),
|
||||
cancelAction ?? (() => { })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog for the given client, allowing them to enter in the desired data.
|
||||
/// </summary>
|
||||
/// <param name="session">Client to show a dialog for.</param>
|
||||
/// <param name="title">Title of the dialog.</param>
|
||||
/// <param name="prompt1">The first prompt.</param>
|
||||
/// <param name="prompt2">The second prompt.</param>
|
||||
/// <param name="prompt3">The third prompt.</param>
|
||||
/// <param name="okAction">The action to execute upon Ok being pressed.</param>
|
||||
/// <param name="cancelAction">The action to execute upon the dialog being cancelled.</param>
|
||||
/// <typeparam name="T1">Type of the first input.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second input.</typeparam>
|
||||
/// <typeparam name="T3">Type of the third input.</typeparam>
|
||||
[PublicAPI]
|
||||
public void OpenDialog<T1, T2, T3>(IPlayerSession session, string title, string prompt1, string prompt2,
|
||||
string prompt3, Action<T1, T2, T3> okAction, Action? cancelAction = null)
|
||||
{
|
||||
OpenDialogInternal(
|
||||
session,
|
||||
title,
|
||||
new List<QuickDialogEntry>
|
||||
{
|
||||
new("1", TypeToEntryType(typeof(T1)), prompt1),
|
||||
new("2", TypeToEntryType(typeof(T2)), prompt2),
|
||||
new("3", TypeToEntryType(typeof(T3)), prompt3)
|
||||
},
|
||||
QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton,
|
||||
(ev =>
|
||||
{
|
||||
if (TryParseQuickDialog<T1>(TypeToEntryType(typeof(T1)), ev.Responses["1"], out var v1) &&
|
||||
TryParseQuickDialog<T2>(TypeToEntryType(typeof(T2)), ev.Responses["2"], out var v2) &&
|
||||
TryParseQuickDialog<T3>(TypeToEntryType(typeof(T3)), ev.Responses["3"], out var v3)
|
||||
)
|
||||
okAction.Invoke(v1, v2, v3);
|
||||
else
|
||||
{
|
||||
session.ConnectedClient.Disconnect("Replied with invalid quick dialog data.");
|
||||
cancelAction?.Invoke();
|
||||
}
|
||||
}),
|
||||
cancelAction ?? (() => { })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog for the given client, allowing them to enter in the desired data.
|
||||
/// </summary>
|
||||
/// <param name="session">Client to show a dialog for.</param>
|
||||
/// <param name="title">Title of the dialog.</param>
|
||||
/// <param name="prompt1">The first prompt.</param>
|
||||
/// <param name="prompt2">The second prompt.</param>
|
||||
/// <param name="prompt3">The third prompt.</param>
|
||||
/// <param name="prompt4">The fourth prompt.</param>
|
||||
/// <param name="okAction">The action to execute upon Ok being pressed.</param>
|
||||
/// <param name="cancelAction">The action to execute upon the dialog being cancelled.</param>
|
||||
/// <typeparam name="T1">Type of the first input.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second input.</typeparam>
|
||||
/// <typeparam name="T3">Type of the third input.</typeparam>
|
||||
/// <typeparam name="T4">Type of the fourth input.</typeparam>
|
||||
[PublicAPI]
|
||||
public void OpenDialog<T1, T2, T3, T4>(IPlayerSession session, string title, string prompt1, string prompt2,
|
||||
string prompt3, string prompt4, Action<T1, T2, T3, T4> okAction, Action? cancelAction = null)
|
||||
{
|
||||
OpenDialogInternal(
|
||||
session,
|
||||
title,
|
||||
new List<QuickDialogEntry>
|
||||
{
|
||||
new("1", TypeToEntryType(typeof(T1)), prompt1),
|
||||
new("2", TypeToEntryType(typeof(T2)), prompt2),
|
||||
new("3", TypeToEntryType(typeof(T3)), prompt3),
|
||||
new("4", TypeToEntryType(typeof(T4)), prompt4),
|
||||
},
|
||||
QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton,
|
||||
(ev =>
|
||||
{
|
||||
if (TryParseQuickDialog<T1>(TypeToEntryType(typeof(T1)), ev.Responses["1"], out var v1) &&
|
||||
TryParseQuickDialog<T2>(TypeToEntryType(typeof(T2)), ev.Responses["2"], out var v2) &&
|
||||
TryParseQuickDialog<T3>(TypeToEntryType(typeof(T3)), ev.Responses["3"], out var v3) &&
|
||||
TryParseQuickDialog<T4>(TypeToEntryType(typeof(T4)), ev.Responses["4"], out var v4)
|
||||
)
|
||||
okAction.Invoke(v1, v2, v3, v4);
|
||||
else
|
||||
{
|
||||
session.ConnectedClient.Disconnect("Replied with invalid quick dialog data.");
|
||||
cancelAction?.Invoke();
|
||||
}
|
||||
}),
|
||||
cancelAction ?? (() => { })
|
||||
);
|
||||
}
|
||||
}
|
||||
176
Content.Server/Administration/QuickDialogSystem.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Administration;
|
||||
|
||||
/// <summary>
|
||||
/// This handles the server portion of quick dialogs, including opening them.
|
||||
/// </summary>
|
||||
public sealed partial class QuickDialogSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the success/cancel actions for a dialog.
|
||||
/// </summary>
|
||||
private readonly Dictionary<int, (Action<QuickDialogResponseEvent> okAction, Action cancelAction)> _openDialogs = new();
|
||||
private readonly Dictionary<NetUserId, List<int>> _openDialogsByUser = new();
|
||||
|
||||
private int _nextDialogId = 1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
||||
|
||||
SubscribeNetworkEvent<QuickDialogResponseEvent>(Handler);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_playerManager.PlayerStatusChanged -= PlayerManagerOnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private void Handler(QuickDialogResponseEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!_openDialogs.ContainsKey(msg.DialogId) || !_openDialogsByUser[args.SenderSession.UserId].Contains(msg.DialogId))
|
||||
{
|
||||
args.SenderSession.ConnectedClient.Disconnect($"Replied with invalid quick dialog data with id {msg.DialogId}.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.ButtonPressed)
|
||||
{
|
||||
case QuickDialogButtonFlag.OkButton:
|
||||
_openDialogs[msg.DialogId].okAction.Invoke(msg);
|
||||
break;
|
||||
case QuickDialogButtonFlag.CancelButton:
|
||||
_openDialogs[msg.DialogId].cancelAction.Invoke();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
_openDialogs.Remove(msg.DialogId);
|
||||
_openDialogsByUser[args.SenderSession.UserId].Remove(msg.DialogId);
|
||||
}
|
||||
|
||||
private int GetDialogId()
|
||||
{
|
||||
return _nextDialogId++;
|
||||
}
|
||||
|
||||
private void PlayerManagerOnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Disconnected && e.NewStatus != SessionStatus.Zombie)
|
||||
return;
|
||||
|
||||
var user = e.Session.UserId;
|
||||
|
||||
if (!_openDialogsByUser.ContainsKey(user))
|
||||
return;
|
||||
|
||||
foreach (var dialogId in _openDialogsByUser[user])
|
||||
{
|
||||
_openDialogs[dialogId].cancelAction.Invoke();
|
||||
_openDialogs.Remove(dialogId);
|
||||
}
|
||||
|
||||
_openDialogsByUser.Remove(user);
|
||||
}
|
||||
|
||||
private void OpenDialogInternal(IPlayerSession session, string title, List<QuickDialogEntry> entries, QuickDialogButtonFlag buttons, Action<QuickDialogResponseEvent> okAction, Action cancelAction)
|
||||
{
|
||||
var did = GetDialogId();
|
||||
RaiseNetworkEvent(
|
||||
new QuickDialogOpenEvent(
|
||||
title,
|
||||
entries,
|
||||
did,
|
||||
buttons),
|
||||
Filter.SinglePlayer(session)
|
||||
);
|
||||
|
||||
_openDialogs.Add(did, (okAction, cancelAction));
|
||||
if (!_openDialogsByUser.ContainsKey(session.UserId))
|
||||
_openDialogsByUser.Add(session.UserId, new List<int>());
|
||||
|
||||
_openDialogsByUser[session.UserId].Add(did);
|
||||
}
|
||||
|
||||
private bool TryParseQuickDialog<T>(QuickDialogEntryType entryType, string input, [NotNullWhen(true)] out T? output)
|
||||
{
|
||||
switch (entryType)
|
||||
{
|
||||
case QuickDialogEntryType.Integer:
|
||||
{
|
||||
var result = int.TryParse(input, out var val);
|
||||
output = (T?) (object?) val;
|
||||
return result;
|
||||
}
|
||||
case QuickDialogEntryType.Float:
|
||||
{
|
||||
var result = float.TryParse(input, out var val);
|
||||
output = (T?) (object?) val;
|
||||
return result;
|
||||
}
|
||||
case QuickDialogEntryType.ShortText:
|
||||
{
|
||||
if (input.Length > 100)
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = (T?) (object?) input;
|
||||
return output is not null;
|
||||
}
|
||||
case QuickDialogEntryType.LongText:
|
||||
{
|
||||
if (input.Length > 2000)
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = (T?) (object?) input;
|
||||
return output is not null;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(entryType), entryType, null);
|
||||
}
|
||||
}
|
||||
|
||||
private QuickDialogEntryType TypeToEntryType(Type T)
|
||||
{
|
||||
if (T == typeof(int) || T == typeof(uint) || T == typeof(long) || T == typeof(ulong))
|
||||
{
|
||||
return QuickDialogEntryType.Integer;
|
||||
}
|
||||
else if (T == typeof(float) || T == typeof(double))
|
||||
{
|
||||
return QuickDialogEntryType.Float;
|
||||
}
|
||||
else if (T == typeof(string)) // People are more likely to notice the input box is too short than they are to notice it's too long.
|
||||
{
|
||||
return QuickDialogEntryType.ShortText;
|
||||
} else if (T == typeof(LongString))
|
||||
{
|
||||
return QuickDialogEntryType.LongText;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Tried to open a dialog with unsupported type {T}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A type used with quick dialogs to indicate you want a large entry window for text and not a short one.
|
||||
/// </summary>
|
||||
/// <param name="String">The string retrieved.</param>
|
||||
public record struct LongString(string String);
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Linq;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// This handles the administrative test arena maps, and loading them.
|
||||
/// </summary>
|
||||
public sealed class AdminTestArenaSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapLoader _mapLoader = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
|
||||
|
||||
public Dictionary<NetUserId, EntityUid> ArenaMap { get; private set; } = new();
|
||||
public Dictionary<NetUserId, EntityUid> ArenaGrid { get; private set; } = new();
|
||||
|
||||
public (EntityUid, EntityUid) AssertArenaLoaded(IPlayerSession admin)
|
||||
{
|
||||
if (ArenaMap.TryGetValue(admin.UserId, out var arenaMap) && !Deleted(arenaMap) && !Terminating(arenaMap))
|
||||
return (arenaMap, ArenaGrid[admin.UserId]);
|
||||
|
||||
ArenaMap[admin.UserId] = _mapManager.GetMapEntityId(_mapManager.CreateMap());
|
||||
var (grids, _) = _mapLoader.LoadMap(Comp<MapComponent>(ArenaMap[admin.UserId]).WorldMap, ArenaMapPath);
|
||||
ArenaGrid[admin.UserId] = grids.First();
|
||||
|
||||
return (ArenaMap[admin.UserId], ArenaGrid[admin.UserId]);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,12 @@ using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Server.Pointing.Components;
|
||||
using Content.Server.Polymorph.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Server.Storage.EntitySystems;
|
||||
using Content.Server.Tabletop;
|
||||
using Content.Server.Tabletop.Components;
|
||||
using Content.Server.Tools.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
@@ -50,20 +54,22 @@ namespace Content.Server.Administration.Systems;
|
||||
public sealed partial class AdminVerbSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly GhostKickManager _ghostKickManager = default!;
|
||||
[Dependency] private readonly PolymorphableSystem _polymorphableSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
|
||||
[Dependency] private readonly CreamPieSystem _creamPieSystem = default!;
|
||||
[Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
|
||||
[Dependency] private readonly TabletopSystem _tabletopSystem = default!;
|
||||
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
[Dependency] private readonly GodmodeSystem _godmodeSystem = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly VomitSystem _vomitSystem = default!;
|
||||
[Dependency] private readonly CreamPieSystem _creamPieSystem = default!;
|
||||
[Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
|
||||
[Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
|
||||
[Dependency] private readonly EntityStorageSystem _entityStorageSystem = default!;
|
||||
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
[Dependency] private readonly GhostKickManager _ghostKickManager = default!;
|
||||
[Dependency] private readonly GodmodeSystem _godmodeSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly PolymorphableSystem _polymorphableSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly TabletopSystem _tabletopSystem = default!;
|
||||
[Dependency] private readonly VomitSystem _vomitSystem = default!;
|
||||
[Dependency] private readonly WeldableSystem _weldableSystem = default!;
|
||||
|
||||
// All smite verbs have names so invokeverb works.
|
||||
private void AddSmiteVerbs(GetVerbsEvent<Verb> args)
|
||||
@@ -76,6 +82,10 @@ public sealed partial class AdminVerbSystem
|
||||
if (!_adminManager.HasAdminFlag(player, AdminFlags.Fun))
|
||||
return;
|
||||
|
||||
// 1984.
|
||||
if (HasComp<IMapComponent>(args.Target) || HasComp<IMapGridComponent>(args.Target))
|
||||
return;
|
||||
|
||||
Verb explode = new()
|
||||
{
|
||||
Text = "Explode",
|
||||
@@ -95,7 +105,7 @@ public sealed partial class AdminVerbSystem
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Explode them.",
|
||||
Message = Loc.GetString("admin-smite-explode-description")
|
||||
};
|
||||
args.Verbs.Add(explode);
|
||||
|
||||
@@ -121,7 +131,7 @@ public sealed partial class AdminVerbSystem
|
||||
xform.WorldRotation = Angle.Zero;
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Banishment to the Chess Dimension.",
|
||||
Message = Loc.GetString("admin-smite-chess-dimension-description")
|
||||
};
|
||||
args.Verbs.Add(chess);
|
||||
|
||||
@@ -144,7 +154,7 @@ public sealed partial class AdminVerbSystem
|
||||
Filter.PvsExcept(args.Target), PopupType.MediumCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Makes them burn.",
|
||||
Message = Loc.GetString("admin-smite-set-alight-description")
|
||||
};
|
||||
args.Verbs.Add(flames);
|
||||
}
|
||||
@@ -159,10 +169,24 @@ public sealed partial class AdminVerbSystem
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminMonkeySmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Monkey mode.",
|
||||
Message = Loc.GetString("admin-smite-monkeyify-description")
|
||||
};
|
||||
args.Verbs.Add(monkey);
|
||||
|
||||
Verb disposalBin = new()
|
||||
{
|
||||
Text = "Garbage Can",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Structures/Piping/disposal.rsi/disposal.png",
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminDisposalsSmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-garbage-can-description")
|
||||
};
|
||||
args.Verbs.Add(disposalBin);
|
||||
|
||||
if (TryComp<DiseaseCarrierComponent>(args.Target, out var carrier))
|
||||
{
|
||||
Verb lungCancer = new()
|
||||
@@ -176,7 +200,7 @@ public sealed partial class AdminVerbSystem
|
||||
1.0f, true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Stage IIIA Lung Cancer, for when they really like the hit show Breaking Bad.",
|
||||
Message = Loc.GetString("admin-smite-lung-cancer-description")
|
||||
};
|
||||
args.Verbs.Add(lungCancer);
|
||||
}
|
||||
@@ -192,11 +216,11 @@ public sealed partial class AdminVerbSystem
|
||||
Act = () =>
|
||||
{
|
||||
int damageToDeal;
|
||||
var critState = mobState._highestToLowestStates.Where(x => x.Value.IsCritical()).FirstOrNull();
|
||||
var critState = mobState._highestToLowestStates.Where(x => x.Value == DamageState.Critical).FirstOrNull();
|
||||
if (critState is null)
|
||||
{
|
||||
// We can't crit them so try killing them.
|
||||
var deadState = mobState._highestToLowestStates.Where(x => x.Value.IsDead()).FirstOrNull();
|
||||
var deadState = mobState._highestToLowestStates.Where(x => x.Value == DamageState.Dead).FirstOrNull();
|
||||
if (deadState is null)
|
||||
return; // whelp.
|
||||
|
||||
@@ -225,7 +249,7 @@ public sealed partial class AdminVerbSystem
|
||||
TimeSpan.FromSeconds(30), true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Electrocutes them, rendering anything they were wearing useless.",
|
||||
Message = Loc.GetString("admin-smite-electrocute-description")
|
||||
};
|
||||
args.Verbs.Add(hardElectrocute);
|
||||
}
|
||||
@@ -242,7 +266,7 @@ public sealed partial class AdminVerbSystem
|
||||
_creamPieSystem.SetCreamPied(args.Target, creamPied, true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "A cream pie, condensed into a button.",
|
||||
Message = Loc.GetString("admin-smite-creampie-description")
|
||||
};
|
||||
args.Verbs.Add(creamPie);
|
||||
}
|
||||
@@ -264,7 +288,7 @@ public sealed partial class AdminVerbSystem
|
||||
Filter.PvsExcept(args.Target), PopupType.MediumCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Removes their blood. All of it.",
|
||||
Message = Loc.GetString("admin-smite-remove-blood-description")
|
||||
};
|
||||
args.Verbs.Add(bloodRemoval);
|
||||
}
|
||||
@@ -297,15 +321,15 @@ public sealed partial class AdminVerbSystem
|
||||
Filter.PvsExcept(args.Target), PopupType.MediumCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Causes them to vomit, including their internal organs.",
|
||||
Message = Loc.GetString("admin-smite-vomit-organs-description")
|
||||
};
|
||||
args.Verbs.Add(vomitOrgans);
|
||||
|
||||
Verb handRemoval = new()
|
||||
Verb handsRemoval = new()
|
||||
{
|
||||
Text = "Remove hands",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/fist.svg.192dpi.png",
|
||||
IconTexture = "/Textures/Interface/AdminActions/remove-hands.png",
|
||||
Act = () =>
|
||||
{
|
||||
var baseXform = Transform(args.Target);
|
||||
@@ -320,9 +344,81 @@ public sealed partial class AdminVerbSystem
|
||||
Filter.PvsExcept(args.Target), PopupType.Medium);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Removes the target's hands.",
|
||||
Message = Loc.GetString("admin-smite-remove-hands-description")
|
||||
};
|
||||
args.Verbs.Add(handsRemoval);
|
||||
|
||||
Verb handRemoval = new()
|
||||
{
|
||||
Text = "Remove hands",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/remove-hand.png",
|
||||
Act = () =>
|
||||
{
|
||||
var baseXform = Transform(args.Target);
|
||||
foreach (var part in body.GetPartsOfType(BodyPartType.Hand))
|
||||
{
|
||||
body.RemovePart(part);
|
||||
Transform(part.Owner).Coordinates = baseXform.Coordinates;
|
||||
break;
|
||||
}
|
||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-hands-self"), args.Target,
|
||||
Filter.Entities(args.Target), PopupType.LargeCaution);
|
||||
_popupSystem.PopupCoordinates(Loc.GetString("admin-smite-remove-hands-others", ("name", args.Target)), baseXform.Coordinates,
|
||||
Filter.PvsExcept(args.Target), PopupType.Medium);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-remove-hand-description")
|
||||
};
|
||||
args.Verbs.Add(handRemoval);
|
||||
|
||||
Verb stomachRemoval = new()
|
||||
{
|
||||
Text = "Stomach Removal",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/stomach.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var part in body.Parts)
|
||||
{
|
||||
foreach (var mechanism in part.Key.Mechanisms)
|
||||
{
|
||||
if (HasComp<StomachComponent>(mechanism.Owner))
|
||||
QueueDel(mechanism.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-stomach-removal-self"), args.Target,
|
||||
Filter.Entities(args.Target), PopupType.LargeCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-stomach-removal-description"),
|
||||
};
|
||||
args.Verbs.Add(stomachRemoval);
|
||||
|
||||
Verb lungRemoval = new()
|
||||
{
|
||||
Text = "Lungs Removal",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/lung-r.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var part in body.Parts)
|
||||
{
|
||||
foreach (var mechanism in part.Key.Mechanisms)
|
||||
{
|
||||
if (HasComp<LungComponent>(mechanism.Owner))
|
||||
QueueDel(mechanism.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-lung-removal-self"), args.Target,
|
||||
Filter.Entities(args.Target), PopupType.LargeCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-lung-removal-description"),
|
||||
};
|
||||
args.Verbs.Add(lungRemoval);
|
||||
}
|
||||
|
||||
if (TryComp<PhysicsComponent>(args.Target, out var physics))
|
||||
@@ -353,8 +449,7 @@ public sealed partial class AdminVerbSystem
|
||||
physics.AngularDamping = 0.0f;
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message =
|
||||
"Turns them into a super bouncy ball, flinging them around until they clip through the station into the abyss.",
|
||||
Message = Loc.GetString("admin-smite-pinball-description")
|
||||
};
|
||||
args.Verbs.Add(pinball);
|
||||
|
||||
@@ -382,7 +477,7 @@ public sealed partial class AdminVerbSystem
|
||||
physics.AngularDamping = 0.0f;
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Banishes them into the depths of space by turning on no-clip and tossing them.",
|
||||
Message = Loc.GetString("admin-smite-yeet-description")
|
||||
};
|
||||
args.Verbs.Add(yeet);
|
||||
}
|
||||
@@ -397,10 +492,24 @@ public sealed partial class AdminVerbSystem
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminBreadSmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "It turns them into bread. Really. That's all it does.",
|
||||
Message = Loc.GetString("admin-smite-become-bread-description")
|
||||
};
|
||||
args.Verbs.Add(bread);
|
||||
|
||||
Verb mouse = new()
|
||||
{
|
||||
Text = "Become Mouse",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Mobs/Animals/mouse.rsi/icon-0.png",
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminMouseSmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-become-mouse-description")
|
||||
};
|
||||
args.Verbs.Add(mouse);
|
||||
|
||||
if (TryComp<ActorComponent>(args.Target, out var actorComponent))
|
||||
{
|
||||
Verb ghostKick = new()
|
||||
@@ -413,7 +522,7 @@ public sealed partial class AdminVerbSystem
|
||||
_ghostKickManager.DoDisconnect(actorComponent.PlayerSession.ConnectedClient, "Smitten.");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Silently kicks the user, dropping their connection.",
|
||||
Message = Loc.GetString("admin-smite-ghostkick-description")
|
||||
};
|
||||
args.Verbs.Add(ghostKick);
|
||||
}
|
||||
@@ -432,7 +541,7 @@ public sealed partial class AdminVerbSystem
|
||||
_inventorySystem.TryEquip(args.Target, ears, "head", true, true, false, inventory);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Forcibly adds cat ears. There is no escape.",
|
||||
Message = Loc.GetString("admin-smite-nyanify-description")
|
||||
};
|
||||
args.Verbs.Add(nyanify);
|
||||
|
||||
@@ -446,7 +555,7 @@ public sealed partial class AdminVerbSystem
|
||||
EnsureComp<KillSignComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Marks a player for death by their fellows.",
|
||||
Message = Loc.GetString("admin-smite-kill-sign-description")
|
||||
};
|
||||
args.Verbs.Add(killSign);
|
||||
|
||||
@@ -466,9 +575,30 @@ public sealed partial class AdminVerbSystem
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Clowns them. The suit cannot be removed.",
|
||||
Message = Loc.GetString("admin-smite-clown-description")
|
||||
};
|
||||
args.Verbs.Add(clown);
|
||||
|
||||
Verb maiden = new()
|
||||
{
|
||||
Text = "Maid",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
SetOutfitCommand.SetOutfit(args.Target, "JanitorMaidGear", EntityManager, (_, clothing) =>
|
||||
{
|
||||
if (HasComp<ClothingComponent>(clothing))
|
||||
EnsureComp<UnremoveableComponent>(clothing);
|
||||
EnsureComp<ClumsyComponent>(args.Target);
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-maid-description")
|
||||
};
|
||||
args.Verbs.Add(maiden);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Verb angerPointingArrows = new()
|
||||
@@ -481,7 +611,7 @@ public sealed partial class AdminVerbSystem
|
||||
EnsureComp<PointingArrowAngeringComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Angers the pointing arrows, causing them to assault this entity.",
|
||||
Message = Loc.GetString("admin-smite-anger-pointing-arrows-description")
|
||||
};
|
||||
args.Verbs.Add(angerPointingArrows);
|
||||
|
||||
@@ -498,7 +628,7 @@ public sealed partial class AdminVerbSystem
|
||||
Filter.Pvs(args.Target), PopupType.LargeCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Reduces the target to a small pile of ash.",
|
||||
Message = Loc.GetString("admin-smite-dust-description"),
|
||||
};
|
||||
args.Verbs.Add(dust);
|
||||
|
||||
@@ -512,7 +642,7 @@ public sealed partial class AdminVerbSystem
|
||||
EnsureComp<BufferingComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Causes the target to randomly start buffering, freezing them in place for a short timespan while they load.",
|
||||
Message = Loc.GetString("admin-smite-buffering-description"),
|
||||
};
|
||||
args.Verbs.Add(youtubeVideoSimulation);
|
||||
|
||||
@@ -526,7 +656,7 @@ public sealed partial class AdminVerbSystem
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminInstrumentSmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "It turns them into a supersynth. Really. That's all it does.",
|
||||
Message = Loc.GetString("admin-smite-become-instrument-description"),
|
||||
};
|
||||
args.Verbs.Add(instrumentation);
|
||||
|
||||
@@ -539,10 +669,146 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
var grav = EnsureComp<MovementIgnoreGravityComponent>(args.Target);
|
||||
grav.Weightless = true;
|
||||
|
||||
Dirty(grav);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = "Grants them anti-gravity.",
|
||||
Message = Loc.GetString("admin-smite-remove-gravity-description"),
|
||||
};
|
||||
args.Verbs.Add(noGravity);
|
||||
|
||||
Verb reptilian = new()
|
||||
{
|
||||
Text = "Reptilian Species Swap",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Objects/Fun/toys.rsi/plushie_lizard.png",
|
||||
Act = () =>
|
||||
{
|
||||
_polymorphableSystem.PolymorphEntity(args.Target, "AdminLizardSmite");
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-reptilian-species-swap-description"),
|
||||
};
|
||||
args.Verbs.Add(reptilian);
|
||||
|
||||
Verb locker = new()
|
||||
{
|
||||
Text = "Locker stuff",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Structures/Storage/closet.rsi/generic.png",
|
||||
Act = () =>
|
||||
{
|
||||
var xform = Transform(args.Target);
|
||||
var locker = Spawn("ClosetMaintenance", xform.Coordinates);
|
||||
if (TryComp<EntityStorageComponent>(locker, out var storage))
|
||||
{
|
||||
_entityStorageSystem.ToggleOpen(args.Target, locker, storage);
|
||||
_entityStorageSystem.Insert(args.Target, locker, storage);
|
||||
_entityStorageSystem.ToggleOpen(args.Target, locker, storage);
|
||||
}
|
||||
_weldableSystem.ForceWeldedState(locker, true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-locker-stuff-description"),
|
||||
};
|
||||
args.Verbs.Add(locker);
|
||||
|
||||
Verb headstand = new()
|
||||
{
|
||||
Text = "Headstand",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png",
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<HeadstandComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-headstand-description"),
|
||||
};
|
||||
args.Verbs.Add(headstand);
|
||||
|
||||
Verb zoomIn = new()
|
||||
{
|
||||
Text = "Zoom in",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/zoom.png",
|
||||
Act = () =>
|
||||
{
|
||||
var eye = EnsureComp<EyeComponent>(args.Target);
|
||||
|
||||
eye.Zoom *= Vector2.One * 0.2f;
|
||||
|
||||
Dirty(eye);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-zoom-in-description"),
|
||||
};
|
||||
args.Verbs.Add(zoomIn);
|
||||
|
||||
Verb flipEye = new()
|
||||
{
|
||||
Text = "Flip eye",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/flip.png",
|
||||
Act = () =>
|
||||
{
|
||||
var eye = EnsureComp<EyeComponent>(args.Target);
|
||||
|
||||
eye.Zoom *= -1;
|
||||
|
||||
Dirty(eye);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-flip-eye-description"),
|
||||
};
|
||||
args.Verbs.Add(flipEye);
|
||||
|
||||
Verb runWalkSwap = new()
|
||||
{
|
||||
Text = "Run Walk Swap",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/run-walk-swap.png",
|
||||
Act = () =>
|
||||
{
|
||||
var movementSpeed = EnsureComp<MovementSpeedModifierComponent>(args.Target);
|
||||
(movementSpeed.BaseSprintSpeed, movementSpeed.BaseWalkSpeed) = (movementSpeed.BaseWalkSpeed, movementSpeed.BaseSprintSpeed);
|
||||
|
||||
Dirty(movementSpeed);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-run-walk-swap-prompt"), args.Target,
|
||||
Filter.Entities(args.Target), PopupType.LargeCaution);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-run-walk-swap-description"),
|
||||
};
|
||||
args.Verbs.Add(runWalkSwap);
|
||||
|
||||
Verb backwardsAccent = new()
|
||||
{
|
||||
Text = "Speak Backwards",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/AdminActions/help-backwards.png",
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<BackwardsAccentComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-speak-backwards-description"),
|
||||
};
|
||||
args.Verbs.Add(backwardsAccent);
|
||||
|
||||
Verb disarmProne = new()
|
||||
{
|
||||
Text = "Disarm Prone",
|
||||
Category = VerbCategory.Smite,
|
||||
IconTexture = "/Textures/Interface/Actions/disarm.png",
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<DisarmProneComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-disarm-prone-description"),
|
||||
};
|
||||
args.Verbs.Add(disarmProne);
|
||||
}
|
||||
}
|
||||
|
||||
928
Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
Normal file
@@ -0,0 +1,928 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Administration.Components;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Doors.Components;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
public sealed partial class AdminVerbSystem
|
||||
{
|
||||
[Dependency] private readonly AirlockSystem _airlockSystem = default!;
|
||||
[Dependency] private readonly StackSystem _stackSystem = default!;
|
||||
[Dependency] private readonly AccessSystem _accessSystem = default!;
|
||||
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly QuickDialogSystem _quickDialog = default!;
|
||||
[Dependency] private readonly AdminTestArenaSystem _adminTestArenaSystem = default!;
|
||||
[Dependency] private readonly StationJobsSystem _stationJobsSystem = default!;
|
||||
[Dependency] private readonly JointSystem _jointSystem = default!;
|
||||
|
||||
private void AddTricksVerbs(GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent<ActorComponent?>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
var player = actor.PlayerSession;
|
||||
|
||||
if (!_adminManager.HasAdminFlag(player, AdminFlags.Admin))
|
||||
return;
|
||||
|
||||
if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))
|
||||
{
|
||||
if (TryComp<AirlockComponent>(args.Target, out var airlock))
|
||||
{
|
||||
Verb bolt = new()
|
||||
{
|
||||
Text = airlock.BoltsDown ? "Unbolt" : "Bolt",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = airlock.BoltsDown
|
||||
? "/Textures/Interface/AdminActions/unbolt.png"
|
||||
: "/Textures/Interface/AdminActions/bolt.png",
|
||||
Act = () =>
|
||||
{
|
||||
airlock.SetBoltsWithAudio(!airlock.BoltsDown);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString(airlock.BoltsDown
|
||||
? "admin-trick-unbolt-description"
|
||||
: "admin-trick-bolt-description"),
|
||||
Priority = (int) (airlock.BoltsDown ? TricksVerbPriorities.Unbolt : TricksVerbPriorities.Bolt),
|
||||
|
||||
};
|
||||
args.Verbs.Add(bolt);
|
||||
|
||||
Verb emergencyAccess = new()
|
||||
{
|
||||
Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/emergency_access.png",
|
||||
Act = () =>
|
||||
{
|
||||
_airlockSystem.ToggleEmergencyAccess(airlock);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString(airlock.EmergencyAccess
|
||||
? "admin-trick-emergency-access-off-description"
|
||||
: "admin-trick-emergency-access-on-description"),
|
||||
Priority = (int) (airlock.EmergencyAccess
|
||||
? TricksVerbPriorities.EmergencyAccessOff
|
||||
: TricksVerbPriorities.EmergencyAccessOn),
|
||||
};
|
||||
args.Verbs.Add(emergencyAccess);
|
||||
}
|
||||
|
||||
if (HasComp<DamageableComponent>(args.Target))
|
||||
{
|
||||
Verb rejuvenate = new()
|
||||
{
|
||||
Text = "Rejuvenate",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rejuvenate.png",
|
||||
Act = () =>
|
||||
{
|
||||
RejuvenateCommand.PerformRejuvenate(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-rejuvenate-description"),
|
||||
Priority = (int) TricksVerbPriorities.Rejuvenate,
|
||||
};
|
||||
args.Verbs.Add(rejuvenate);
|
||||
}
|
||||
|
||||
if (!_godmodeSystem.HasGodmode(args.Target))
|
||||
{
|
||||
Verb makeIndestructible = new()
|
||||
{
|
||||
Text = "Make Indestructible",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/plus.svg.192dpi.png",
|
||||
Act = () =>
|
||||
{
|
||||
_godmodeSystem.EnableGodmode(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-make-indestructible-description"),
|
||||
Priority = (int) TricksVerbPriorities.MakeIndestructible,
|
||||
};
|
||||
args.Verbs.Add(makeIndestructible);
|
||||
}
|
||||
else
|
||||
{
|
||||
Verb makeVulnerable = new()
|
||||
{
|
||||
Text = "Make Vulnerable",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/plus.svg.192dpi.png",
|
||||
Act = () =>
|
||||
{
|
||||
_godmodeSystem.DisableGodmode(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-make-vulnerable-description"),
|
||||
Priority = (int) TricksVerbPriorities.MakeVulnerable,
|
||||
};
|
||||
args.Verbs.Add(makeVulnerable);
|
||||
}
|
||||
|
||||
if (TryComp<BatteryComponent>(args.Target, out var battery))
|
||||
{
|
||||
Verb refillBattery = new()
|
||||
{
|
||||
Text = "Refill Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
battery.CurrentCharge = battery.MaxCharge;
|
||||
Dirty(battery);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-refill-battery-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillBattery,
|
||||
};
|
||||
args.Verbs.Add(refillBattery);
|
||||
|
||||
Verb drainBattery = new()
|
||||
{
|
||||
Text = "Drain Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/drain_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
battery.CurrentCharge = 0;
|
||||
Dirty(battery);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-drain-battery-description"),
|
||||
Priority = (int) TricksVerbPriorities.DrainBattery,
|
||||
};
|
||||
args.Verbs.Add(drainBattery);
|
||||
|
||||
Verb infiniteBattery = new()
|
||||
{
|
||||
Text = "Infinite Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/infinite_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
|
||||
recharger.AutoRecharge = true;
|
||||
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
|
||||
Priority = (int) TricksVerbPriorities.InfiniteBattery,
|
||||
};
|
||||
args.Verbs.Add(infiniteBattery);
|
||||
}
|
||||
|
||||
if (TryComp<AnchorableComponent>(args.Target, out var anchor))
|
||||
{
|
||||
Verb blockUnanchor = new()
|
||||
{
|
||||
Text = "Block Unanchoring",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/anchor.svg.192dpi.png",
|
||||
Act = () =>
|
||||
{
|
||||
RemComp(args.Target, anchor);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-block-unanchoring-description"),
|
||||
Priority = (int) TricksVerbPriorities.BlockUnanchoring,
|
||||
};
|
||||
args.Verbs.Add(blockUnanchor);
|
||||
}
|
||||
|
||||
if (TryComp<GasTankComponent>(args.Target, out var tank))
|
||||
{
|
||||
Verb refillInternalsO2 = new()
|
||||
{
|
||||
Text = "Refill Internals Oxygen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/oxygen.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Oxygen, tank);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillOxygen,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsO2);
|
||||
|
||||
Verb refillInternalsN2 = new()
|
||||
{
|
||||
Text = "Refill Internals Nitrogen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/red.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Nitrogen, tank);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillNitrogen,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsN2);
|
||||
|
||||
Verb refillInternalsPlasma = new()
|
||||
{
|
||||
Text = "Refill Internals Plasma",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/plasma.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
RefillGasTank(args.Target, Gas.Plasma, tank);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillPlasma,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsPlasma);
|
||||
}
|
||||
|
||||
if (TryComp<InventoryComponent>(args.Target, out var inventory))
|
||||
{
|
||||
Verb refillInternalsO2 = new()
|
||||
{
|
||||
Text = "Refill Internals Oxygen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/oxygen.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
{
|
||||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Oxygen, tank);
|
||||
}
|
||||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Oxygen, tank);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillOxygen,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsO2);
|
||||
|
||||
Verb refillInternalsN2 = new()
|
||||
{
|
||||
Text = "Refill Internals Nitrogen",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/red.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
{
|
||||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Nitrogen, tank);
|
||||
}
|
||||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Nitrogen, tank);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillNitrogen,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsN2);
|
||||
|
||||
Verb refillInternalsPlasma = new()
|
||||
{
|
||||
Text = "Refill Internals Plasma",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Tanks/plasma.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var slot in _inventorySystem.GetSlots(args.Target))
|
||||
{
|
||||
if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<GasTankComponent>(entity, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(entity.Value, Gas.Plasma, tank);
|
||||
}
|
||||
|
||||
foreach (var held in _handsSystem.EnumerateHeld(args.Target))
|
||||
{
|
||||
if (!TryComp<GasTankComponent>(held, out var tank))
|
||||
continue;
|
||||
|
||||
RefillGasTank(held, Gas.Plasma, tank);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillPlasma,
|
||||
};
|
||||
args.Verbs.Add(refillInternalsPlasma);
|
||||
}
|
||||
|
||||
Verb sendToTestArena = new()
|
||||
{
|
||||
Text = "Send to test arena",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png",
|
||||
Act = () =>
|
||||
{
|
||||
var (_, arenaGrid) = _adminTestArenaSystem.AssertArenaLoaded(player);
|
||||
|
||||
Transform(args.Target).Coordinates = new EntityCoordinates(arenaGrid, Vector2.One);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-send-to-test-arena-description"),
|
||||
Priority = (int) TricksVerbPriorities.SendToTestArena,
|
||||
};
|
||||
args.Verbs.Add(sendToTestArena);
|
||||
|
||||
var activeId = FindActiveId(args.Target);
|
||||
|
||||
if (activeId is not null)
|
||||
{
|
||||
Verb grantAllAccess = new()
|
||||
{
|
||||
Text = "Grant All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/centcom.png",
|
||||
Act = () =>
|
||||
{
|
||||
GiveAllAccess(activeId.Value);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-grant-all-access-description"),
|
||||
Priority = (int) TricksVerbPriorities.GrantAllAccess,
|
||||
};
|
||||
args.Verbs.Add(grantAllAccess);
|
||||
|
||||
Verb revokeAllAccess = new()
|
||||
{
|
||||
Text = "Revoke All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/default.png",
|
||||
Act = () =>
|
||||
{
|
||||
RevokeAllAccess(activeId.Value);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
|
||||
Priority = (int) TricksVerbPriorities.RevokeAllAccess,
|
||||
};
|
||||
args.Verbs.Add(revokeAllAccess);
|
||||
}
|
||||
|
||||
if (HasComp<AccessComponent>(args.Target))
|
||||
{
|
||||
Verb grantAllAccess = new()
|
||||
{
|
||||
Text = "Grant All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/centcom.png",
|
||||
Act = () =>
|
||||
{
|
||||
GiveAllAccess(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-grant-all-access-description"),
|
||||
Priority = (int) TricksVerbPriorities.GrantAllAccess,
|
||||
};
|
||||
args.Verbs.Add(grantAllAccess);
|
||||
|
||||
Verb revokeAllAccess = new()
|
||||
{
|
||||
Text = "Revoke All Access",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Misc/id_cards.rsi/default.png",
|
||||
Act = () =>
|
||||
{
|
||||
RevokeAllAccess(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
|
||||
Priority = (int) TricksVerbPriorities.RevokeAllAccess,
|
||||
};
|
||||
args.Verbs.Add(revokeAllAccess);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<StackComponent>(args.Target, out var stack))
|
||||
{
|
||||
Verb adjustStack = new()
|
||||
{
|
||||
Text = "Adjust Stack",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/adjust-stack.png",
|
||||
Act = () =>
|
||||
{
|
||||
// Unbounded intentionally.
|
||||
_quickDialog.OpenDialog(player, "Adjust stack", $"Amount (max {stack.MaxCount})", (int newAmount) =>
|
||||
{
|
||||
_stackSystem.SetCount(args.Target, newAmount, stack);
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-adjust-stack-description"),
|
||||
Priority = (int) TricksVerbPriorities.AdjustStack,
|
||||
};
|
||||
args.Verbs.Add(adjustStack);
|
||||
|
||||
Verb fillStack = new()
|
||||
{
|
||||
Text = "Fill Stack",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill-stack.png",
|
||||
Act = () =>
|
||||
{
|
||||
_stackSystem.SetCount(args.Target, stack.MaxCount, stack);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-fill-stack-description"),
|
||||
Priority = (int) TricksVerbPriorities.FillStack,
|
||||
};
|
||||
args.Verbs.Add(fillStack);
|
||||
}
|
||||
|
||||
Verb rename = new()
|
||||
{
|
||||
Text = "Rename",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rename.png",
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Rename", "Name", (string newName) =>
|
||||
{
|
||||
MetaData(args.Target).EntityName = newName;
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-rename-description"),
|
||||
Priority = (int) TricksVerbPriorities.Rename,
|
||||
};
|
||||
args.Verbs.Add(rename);
|
||||
|
||||
Verb redescribe = new()
|
||||
{
|
||||
Text = "Redescribe",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/redescribe.png",
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Redescribe", "Description", (LongString newDescription) =>
|
||||
{
|
||||
MetaData(args.Target).EntityDescription = newDescription.String;
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-redescribe-description"),
|
||||
Priority = (int) TricksVerbPriorities.Redescribe,
|
||||
};
|
||||
args.Verbs.Add(redescribe);
|
||||
|
||||
Verb renameAndRedescribe = new()
|
||||
{
|
||||
Text = "Redescribe",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/rename_and_redescribe.png",
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Rename & Redescribe", "Name", "Description",
|
||||
(string newName, LongString newDescription) =>
|
||||
{
|
||||
var meta = MetaData(args.Target);
|
||||
meta.EntityName = newName;
|
||||
meta.EntityDescription = newDescription.String;
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-rename-and-redescribe-description"),
|
||||
Priority = (int) TricksVerbPriorities.RenameAndRedescribe,
|
||||
};
|
||||
args.Verbs.Add(renameAndRedescribe);
|
||||
|
||||
if (TryComp<StationDataComponent>(args.Target, out var stationData))
|
||||
{
|
||||
if (_adminManager.HasAdminFlag(player, AdminFlags.Round))
|
||||
{
|
||||
Verb barJobSlots = new()
|
||||
{
|
||||
Text = "Bar job slots",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/bar_jobslots.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var (job, _) in _stationJobsSystem.GetJobs(args.Target))
|
||||
{
|
||||
_stationJobsSystem.TrySetJobSlot(args.Target, job, 0, true);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-bar-job-slots-description"),
|
||||
Priority = (int) TricksVerbPriorities.BarJobSlots,
|
||||
};
|
||||
args.Verbs.Add(barJobSlots);
|
||||
}
|
||||
|
||||
Verb locateCargoShuttle = new()
|
||||
{
|
||||
Text = "Locate Cargo Shuttle",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Clothing/Head/Soft/cargosoft.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
var shuttle = Comp<StationCargoOrderDatabaseComponent>(args.Target).Shuttle;
|
||||
|
||||
if (shuttle is null)
|
||||
return;
|
||||
|
||||
Transform(args.User).Coordinates = new EntityCoordinates(shuttle.Value, Vector2.Zero);
|
||||
},
|
||||
Impact = LogImpact.Low,
|
||||
Message = Loc.GetString("admin-trick-locate-cargo-shuttle-description"),
|
||||
Priority = (int) TricksVerbPriorities.LocateCargoShuttle,
|
||||
};
|
||||
args.Verbs.Add(locateCargoShuttle);
|
||||
}
|
||||
|
||||
if (TryGetGridChildren(args.Target, out var childEnum))
|
||||
{
|
||||
Verb refillBattery = new()
|
||||
{
|
||||
Text = "Refill Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/fill_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var ent in childEnum)
|
||||
{
|
||||
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
|
||||
continue;
|
||||
var battery = EnsureComp<BatteryComponent>(ent);
|
||||
battery.CurrentCharge = battery.MaxCharge;
|
||||
Dirty(battery);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-refill-battery-description"),
|
||||
Priority = (int) TricksVerbPriorities.RefillBattery,
|
||||
};
|
||||
args.Verbs.Add(refillBattery);
|
||||
|
||||
Verb drainBattery = new()
|
||||
{
|
||||
Text = "Drain Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/drain_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
foreach (var ent in childEnum)
|
||||
{
|
||||
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
|
||||
continue;
|
||||
var battery = EnsureComp<BatteryComponent>(ent);
|
||||
battery.CurrentCharge = 0;
|
||||
Dirty(battery);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-drain-battery-description"),
|
||||
Priority = (int) TricksVerbPriorities.DrainBattery,
|
||||
};
|
||||
args.Verbs.Add(drainBattery);
|
||||
|
||||
Verb infiniteBattery = new()
|
||||
{
|
||||
Text = "Infinite Battery",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/infinite_battery.png",
|
||||
Act = () =>
|
||||
{
|
||||
// this kills the sloth
|
||||
foreach (var ent in childEnum)
|
||||
{
|
||||
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
|
||||
continue;
|
||||
|
||||
var recharger = EnsureComp<BatterySelfRechargerComponent>(ent);
|
||||
var battery = EnsureComp<BatteryComponent>(ent);
|
||||
|
||||
recharger.AutoRecharge = true;
|
||||
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-infinite-battery-description"),
|
||||
Priority = (int) TricksVerbPriorities.InfiniteBattery,
|
||||
};
|
||||
args.Verbs.Add(infiniteBattery);
|
||||
}
|
||||
|
||||
if (TryComp<PhysicsComponent>(args.Target, out var physics))
|
||||
{
|
||||
Verb haltMovement = new()
|
||||
{
|
||||
Text = "Halt Movement",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/halt.png",
|
||||
Act = () =>
|
||||
{
|
||||
physics.LinearVelocity = Vector2.Zero;
|
||||
physics.AngularVelocity = 0.0f;
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-halt-movement-description"),
|
||||
Priority = (int) TricksVerbPriorities.HaltMovement,
|
||||
};
|
||||
args.Verbs.Add(haltMovement);
|
||||
}
|
||||
|
||||
if (TryComp<IMapComponent>(args.Target, out var map))
|
||||
{
|
||||
if (_adminManager.HasAdminFlag(player, AdminFlags.Mapping))
|
||||
{
|
||||
if (_mapManager.IsMapPaused(map.WorldMap))
|
||||
{
|
||||
Verb unpauseMap = new()
|
||||
{
|
||||
Text = "Unpause Map",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/play.png",
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.WorldMap, false);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-unpause-map-description"),
|
||||
Priority = (int) TricksVerbPriorities.Unpause,
|
||||
};
|
||||
args.Verbs.Add(unpauseMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
Verb pauseMap = new()
|
||||
{
|
||||
Text = "Pause Map",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/pause.png",
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.WorldMap, true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-pause-map-description"),
|
||||
Priority = (int) TricksVerbPriorities.Pause,
|
||||
};
|
||||
args.Verbs.Add(pauseMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<JointComponent>(args.Target, out var joints))
|
||||
{
|
||||
Verb snapJoints = new()
|
||||
{
|
||||
Text = "Snap Joints",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Interface/AdminActions/snap_joints.png",
|
||||
Act = () =>
|
||||
{
|
||||
_jointSystem.ClearJoints(joints);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-snap-joints-description"),
|
||||
Priority = (int) TricksVerbPriorities.SnapJoints,
|
||||
};
|
||||
args.Verbs.Add(snapJoints);
|
||||
}
|
||||
|
||||
if (TryComp<GunComponent>(args.Target, out var gun))
|
||||
{
|
||||
Verb minigunFire = new()
|
||||
{
|
||||
Text = "Make Minigun",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi/icon.png",
|
||||
Act = () =>
|
||||
{
|
||||
gun.FireRate = 15;
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-minigun-fire-description"),
|
||||
Priority = (int) TricksVerbPriorities.MakeMinigun,
|
||||
};
|
||||
args.Verbs.Add(minigunFire);
|
||||
}
|
||||
|
||||
if (TryComp<BallisticAmmoProviderComponent>(args.Target, out var ballisticAmmo))
|
||||
{
|
||||
Verb setCapacity = new()
|
||||
{
|
||||
Text = "Set Bullet Amount",
|
||||
Category = VerbCategory.Tricks,
|
||||
IconTexture = "/Textures/Objects/Fun/caps.rsi/mag-6.png",
|
||||
Act = () =>
|
||||
{
|
||||
_quickDialog.OpenDialog(player, "Set Bullet Amount", $"Amount (max {ballisticAmmo.Capacity}):", (int amount) =>
|
||||
{
|
||||
ballisticAmmo.UnspawnedCount = amount;
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-set-bullet-amount-description"),
|
||||
Priority = (int) TricksVerbPriorities.SetBulletAmount,
|
||||
};
|
||||
args.Verbs.Add(setCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefillGasTank(EntityUid tank, Gas gasType, GasTankComponent? tankComponent)
|
||||
{
|
||||
if (!Resolve(tank, ref tankComponent))
|
||||
return;
|
||||
|
||||
var mixSize = tankComponent.Air.Volume;
|
||||
var newMix = new GasMixture(mixSize);
|
||||
newMix.SetMoles(gasType, (1000.0f * mixSize) / (Atmospherics.R * Atmospherics.T20C)); // Fill the tank to 1000KPA.
|
||||
newMix.Temperature = Atmospherics.T20C;
|
||||
tankComponent.Air = newMix;
|
||||
}
|
||||
|
||||
private bool TryGetGridChildren(EntityUid target, [NotNullWhen(true)] out IEnumerable<EntityUid>? enumerator)
|
||||
{
|
||||
if (!HasComp<IMapComponent>(target) && !HasComp<IMapGridComponent>(target) &&
|
||||
!HasComp<StationDataComponent>(target))
|
||||
{
|
||||
enumerator = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
enumerator = GetGridChildrenInner(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ew. This finds everything supposedly on a grid.
|
||||
private IEnumerable<EntityUid> GetGridChildrenInner(EntityUid target)
|
||||
{
|
||||
if (TryComp<StationDataComponent>(target, out var station))
|
||||
{
|
||||
foreach (var grid in station.Grids)
|
||||
{
|
||||
foreach (var ent in Transform(grid).ChildEntities)
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
else if (HasComp<IMapComponent>(target))
|
||||
{
|
||||
foreach (var possibleGrid in Transform(target).ChildEntities)
|
||||
{
|
||||
foreach (var ent in Transform(possibleGrid).ChildEntities)
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var ent in Transform(target).ChildEntities)
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityUid? FindActiveId(EntityUid target)
|
||||
{
|
||||
if (_inventorySystem.TryGetSlotEntity(target, "id", out var slotEntity))
|
||||
{
|
||||
if (HasComp<AccessComponent>(slotEntity))
|
||||
{
|
||||
return slotEntity.Value;
|
||||
}
|
||||
else if (TryComp<PDAComponent>(slotEntity, out var pda))
|
||||
{
|
||||
if (pda.ContainedID != null)
|
||||
{
|
||||
return pda.ContainedID.Owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (TryComp<HandsComponent>(target, out var hands))
|
||||
{
|
||||
foreach (var held in _handsSystem.EnumerateHeld(target, hands))
|
||||
{
|
||||
if (HasComp<AccessComponent>(held))
|
||||
{
|
||||
return held;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void GiveAllAccess(EntityUid entity)
|
||||
{
|
||||
var allAccess = _prototypeManager
|
||||
.EnumeratePrototypes<AccessLevelPrototype>()
|
||||
.Select(p => p.ID).ToArray();
|
||||
|
||||
_accessSystem.TrySetTags(entity, allAccess);
|
||||
}
|
||||
|
||||
private void RevokeAllAccess(EntityUid entity)
|
||||
{
|
||||
_accessSystem.TrySetTags(entity, new string[]{});
|
||||
}
|
||||
|
||||
public enum TricksVerbPriorities
|
||||
{
|
||||
Bolt = 0,
|
||||
Unbolt = 0,
|
||||
EmergencyAccessOn = -1, // These are separate intentionally for `invokeverb` shenanigans.
|
||||
EmergencyAccessOff = -1,
|
||||
MakeIndestructible = -2,
|
||||
MakeVulnerable = -2,
|
||||
BlockUnanchoring = -3,
|
||||
RefillBattery = -4,
|
||||
DrainBattery = -5,
|
||||
RefillOxygen = -6,
|
||||
RefillNitrogen = -7,
|
||||
RefillPlasma = -8,
|
||||
SendToTestArena = -9,
|
||||
GrantAllAccess = -10,
|
||||
RevokeAllAccess = -11,
|
||||
Rejuvenate = -12,
|
||||
AdjustStack = -13,
|
||||
FillStack = -14,
|
||||
Rename = -15,
|
||||
Redescribe = -16,
|
||||
RenameAndRedescribe = -17,
|
||||
BarJobSlots = -18,
|
||||
LocateCargoShuttle = -19,
|
||||
InfiniteBattery = -20,
|
||||
HaltMovement = -21,
|
||||
Unpause = -22,
|
||||
Pause = -23,
|
||||
SnapJoints = -24,
|
||||
MakeMinigun = -25,
|
||||
SetBulletAmount = -26,
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ namespace Content.Server.Administration.Systems
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddDebugVerbs);
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddSmiteVerbs);
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddTricksVerbs);
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAntagVerbs);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Actions.Events;
|
||||
using Content.Server.Administration.Components;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.CombatMode.Disarm;
|
||||
using Content.Server.Hands.Components;
|
||||
@@ -100,7 +101,7 @@ namespace Content.Server.CombatMode
|
||||
SoundSystem.Play(component.DisarmSuccessSound.GetSound(), filterAll, args.Performer, AudioHelpers.WithVariation(0.025f));
|
||||
_adminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(args.Performer):user} used disarm on {ToPrettyString(args.Target):target}");
|
||||
|
||||
var eventArgs = new DisarmedEvent() { Target = args.Target, Source = args.Performer, PushProbability = chance };
|
||||
var eventArgs = new DisarmedEvent() { Target = args.Target, Source = args.Performer, PushProbability = HasComp<DisarmProneComponent>(args.Target) ? 1.0f : chance };
|
||||
RaiseLocalEvent(args.Target, eventArgs, true);
|
||||
}
|
||||
|
||||
@@ -108,6 +109,13 @@ namespace Content.Server.CombatMode
|
||||
private float CalculateDisarmChance(EntityUid disarmer, EntityUid disarmed, EntityUid? inTargetHand, SharedCombatModeComponent disarmerComp)
|
||||
{
|
||||
float healthMod = 0;
|
||||
|
||||
if (HasComp<DisarmProneComponent>(disarmer))
|
||||
return 1.0f;
|
||||
|
||||
if (HasComp<DisarmProneComponent>(disarmed))
|
||||
return 0.0f;
|
||||
|
||||
if (TryComp<DamageableComponent>(disarmer, out var disarmerDamage) && TryComp<DamageableComponent>(disarmed, out var disarmedDamage))
|
||||
{
|
||||
// I wanted this to consider their mob state thresholds too but I'm not touching that shitcode after having a go at this.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Components;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
@@ -147,6 +148,9 @@ namespace Content.Server.Cuffs.Components
|
||||
cuffTime = MathF.Max(0.1f, cuffTime - StunBonus);
|
||||
}
|
||||
|
||||
if (_entities.HasComponent<DisarmProneComponent>(target))
|
||||
cuffTime = 0.0f; // cuff them instantly.
|
||||
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, cuffTime, default, target)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
|
||||
@@ -222,10 +222,6 @@ public sealed partial class StationJobsSystem : EntitySystem
|
||||
UpdateJobsAvailable();
|
||||
return true;
|
||||
case true:
|
||||
// Job is unlimited so just say we adjusted it and do nothing.
|
||||
if (jobList[jobPrototypeId] == null)
|
||||
return true;
|
||||
|
||||
stationJobs.TotalJobs += amount - (int)jobList[jobPrototypeId]!.Value;
|
||||
|
||||
jobList[jobPrototypeId] = (uint)amount;
|
||||
|
||||
@@ -5,10 +5,12 @@ using Content.Server.Chat.Systems;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Station;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
@@ -63,6 +65,22 @@ public sealed class StationSystem : EntitySystem
|
||||
_configurationManager.OnValueChanged(CCVars.StationOffset, x => _randomStationOffset = x, true);
|
||||
_configurationManager.OnValueChanged(CCVars.MaxStationOffset, x => _maxRandomStationOffset = x, true);
|
||||
_configurationManager.OnValueChanged(CCVars.StationRotation, x => _randomStationRotation = x, true);
|
||||
|
||||
_player.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_player.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus == SessionStatus.Connected)
|
||||
{
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), Filter.SinglePlayer(e.Session));
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
@@ -70,11 +88,15 @@ public sealed class StationSystem : EntitySystem
|
||||
private void OnStationStartup(EntityUid uid, StationDataComponent component, ComponentAdd args)
|
||||
{
|
||||
_stations.Add(uid);
|
||||
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), Filter.Broadcast());
|
||||
}
|
||||
|
||||
private void OnStationDeleted(EntityUid uid, StationDataComponent component, ComponentShutdown args)
|
||||
{
|
||||
_stations.Remove(uid);
|
||||
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), Filter.Broadcast());
|
||||
}
|
||||
|
||||
private void OnPreGameMapLoad(PreGameMapLoad ev)
|
||||
|
||||
@@ -96,6 +96,18 @@ public sealed class WeldableSystem : EntitySystem
|
||||
appearance.SetData(WeldableVisuals.IsWelded, component.IsWelded);
|
||||
}
|
||||
|
||||
public void ForceWeldedState(EntityUid uid, bool state, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
component.IsWelded = state;
|
||||
|
||||
RaiseLocalEvent(uid, new WeldableChangedEvent(component.IsWelded));
|
||||
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
public void SetWeldingTime(EntityUid uid, TimeSpan time, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Shared.Administration.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Flips the target's sprite on it's head, so they do a headstand.
|
||||
/// </summary>
|
||||
public abstract class SharedHeadstandComponent : Component { }
|
||||
129
Content.Shared/Administration/QuickDialogOpenEvent.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration;
|
||||
|
||||
/// <summary>
|
||||
/// A networked event raised when the server wants to open a quick dialog.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class QuickDialogOpenEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The title of the dialog.
|
||||
/// </summary>
|
||||
public string Title;
|
||||
|
||||
/// <summary>
|
||||
/// The internal dialog ID.
|
||||
/// </summary>
|
||||
public int DialogId;
|
||||
|
||||
/// <summary>
|
||||
/// The prompts to show the user.
|
||||
/// </summary>
|
||||
public List<QuickDialogEntry> Prompts;
|
||||
|
||||
/// <summary>
|
||||
/// The buttons presented for the user.
|
||||
/// </summary>
|
||||
public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton;
|
||||
|
||||
public QuickDialogOpenEvent(string title, List<QuickDialogEntry> prompts, int dialogId, QuickDialogButtonFlag buttons)
|
||||
{
|
||||
Title = title;
|
||||
Prompts = prompts;
|
||||
Buttons = buttons;
|
||||
DialogId = dialogId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A networked event raised when the client replies to a quick dialog.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class QuickDialogResponseEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The internal dialog ID.
|
||||
/// </summary>
|
||||
public int DialogId;
|
||||
|
||||
/// <summary>
|
||||
/// The responses to the prompts.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Responses;
|
||||
|
||||
/// <summary>
|
||||
/// The button pressed when responding.
|
||||
/// </summary>
|
||||
public QuickDialogButtonFlag ButtonPressed;
|
||||
|
||||
public QuickDialogResponseEvent(int dialogId, Dictionary<string, string> responses, QuickDialogButtonFlag buttonPressed)
|
||||
{
|
||||
DialogId = dialogId;
|
||||
Responses = responses;
|
||||
ButtonPressed = buttonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An entry in a quick dialog.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class QuickDialogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the dialog field.
|
||||
/// </summary>
|
||||
public string FieldId;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the field, for checks.
|
||||
/// </summary>
|
||||
public QuickDialogEntryType Type;
|
||||
|
||||
/// <summary>
|
||||
/// The prompt to show the user.
|
||||
/// </summary>
|
||||
public string Prompt;
|
||||
|
||||
public QuickDialogEntry(string fieldId, QuickDialogEntryType type, string prompt)
|
||||
{
|
||||
FieldId = fieldId;
|
||||
Type = type;
|
||||
Prompt = prompt;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The buttons available in a quick dialog.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum QuickDialogButtonFlag
|
||||
{
|
||||
OkButton = 1,
|
||||
CancelButton = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entry types for a quick dialog.
|
||||
/// </summary>
|
||||
public enum QuickDialogEntryType
|
||||
{
|
||||
/// <summary>
|
||||
/// Any integer.
|
||||
/// </summary>
|
||||
Integer,
|
||||
/// <summary>
|
||||
/// Any floating point value.
|
||||
/// </summary>
|
||||
Float,
|
||||
/// <summary>
|
||||
/// Maximum of 100 characters string.
|
||||
/// </summary>
|
||||
ShortText,
|
||||
/// <summary>
|
||||
/// Maximum of 2,000 characters string.
|
||||
/// </summary>
|
||||
LongText,
|
||||
}
|
||||
14
Content.Shared/Station/StationsUpdatedEvent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Station;
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public sealed class StationsUpdatedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly HashSet<EntityUid> Stations;
|
||||
|
||||
public StationsUpdatedEvent(HashSet<EntityUid> stations)
|
||||
{
|
||||
Stations = stations;
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,9 @@ namespace Content.Shared.Verbs
|
||||
new("verb-categories-rotate", "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png", iconsOnly: true) { Columns = 5 };
|
||||
|
||||
public static readonly VerbCategory Smite =
|
||||
new("verb-categories-smite", "/Textures/Interface/VerbIcons/smite.svg.192dpi.png", iconsOnly: true) { Columns = 5 };
|
||||
new("verb-categories-smite", "/Textures/Interface/VerbIcons/smite.svg.192dpi.png", iconsOnly: true) { Columns = 6 };
|
||||
public static readonly VerbCategory Tricks =
|
||||
new("verb-categories-tricks", "/Textures/Interface/AdminActions/tricks.png", iconsOnly: true) { Columns = 5 };
|
||||
|
||||
public static readonly VerbCategory SetTransferAmount =
|
||||
new("verb-categories-transfer", "/Textures/Interface/VerbIcons/spill.svg.192dpi.png");
|
||||
|
||||
@@ -9,3 +9,82 @@ admin-smite-vomit-organs-others = {CAPITALIZE($name)} vomits up their organs!
|
||||
admin-smite-remove-hands-self = Your hands fall off!
|
||||
admin-smite-remove-hands-other = {CAPITALIZE($name)}'s hands fall off!
|
||||
admin-smite-turned-ash-other = {CAPITALISE($name)} turns into a pile of ash!
|
||||
admin-smite-stomach-removal-self = Your stomach feels hollow...
|
||||
admin-smite-run-walk-swap-prompt = You have to press shift to run!
|
||||
admin-smite-lung-removal-self = You can't breath!
|
||||
|
||||
|
||||
## Smite descriptions
|
||||
|
||||
admin-smite-explode-description = Explode them.
|
||||
admin-smite-chess-dimension-description = Banishment to the Chess Dimension.
|
||||
admin-smite-set-alight-description = Makes them burn.
|
||||
admin-smite-monkeyify-description = Turns the target into a monkey.
|
||||
admin-smite-lung-cancer-description = Stage IIIA Lung Cancer, for when they really like the hit show Breaking Bad.
|
||||
admin-smite-electrocute-description = Electrocutes them, rendering anything they were wearing useless.
|
||||
admin-smite-creampie-description = A creampie, condensed into a button.
|
||||
admin-smite-remove-blood-description = Removes all of their blood, messily.
|
||||
admin-smite-vomit-organs-description = Causes them to vomit, organs included.
|
||||
admin-smite-remove-hands-description = Removes their hands.
|
||||
admin-smite-pinball-description = Turns them into a super bouncy ball, flinging them around until they clip through the station into the abyss.
|
||||
admin-smite-yeet-description = Banishes them into the depths of space by turning on no-clip and tossing them.
|
||||
admin-smite-become-bread-description = It turns them into bread. Really, that's all it does.
|
||||
admin-smite-ghostkick-description = Silently kicks the user, dropping their connection.
|
||||
admin-smite-nyanify-description = Forcibly add cat ears, there is no escape.
|
||||
admin-smite-kill-sign-description = Marks a player for death by their fellows.
|
||||
admin-smite-clown-description = Clowns them. The suit cannot be removed.
|
||||
admin-smite-anger-pointing-arrows-description = Angers the pointing arrows, causing them to assault this entity explosively.
|
||||
admin-smite-dust-description = Reduces the target to a small pile of ash.
|
||||
admin-smite-buffering-description = Causes the target to randomly start buffering, freezing them in place for a short timespan while they load.
|
||||
admin-smite-become-instrument-description = It turns them into a supersynth. Really. That's all it does.
|
||||
admin-smite-remove-gravity-description = Grants them anti-gravity.
|
||||
admin-smite-reptilian-species-swap-description = It changes their species to Reptilian. Useful for people who were being space racist.
|
||||
admin-smite-locker-stuff-description = Stuffs them in a (welded) locker.
|
||||
admin-smite-headstand-description = Vertically flips their sprite.
|
||||
admin-smite-plasma-internals-description = Replaces the contents of their internals with plasma.
|
||||
admin-smite-become-mouse-description = They become a rat. Ratatouille.
|
||||
admin-smite-maid-description = Forcibly converts them into a janitorial cat maid. This is actual torture for some players, use it wisely.
|
||||
admin-smite-zoom-in-description = Zooms in their view so that they can no longer see their surroundings.
|
||||
admin-smite-flip-eye-description = Flips their view, effectively reversing their controls and making the game annoying to play.
|
||||
admin-smite-run-walk-swap-description = Swaps running and walking, forcing them to hold shift to move fast.
|
||||
admin-smite-stomach-removal-description = Removes their stomach, rendering them unable to eat.
|
||||
admin-smite-speak-backwards-description = Forces them to speak backwards, so they can't call for help.
|
||||
admin-smite-lung-removal-description = Removes their lungs, drowning them.
|
||||
admin-smite-remove-hand-description = Removes only one of their hands instead of all of them.
|
||||
admin-smite-disarm-prone-description = Makes them get disarmed 100% of the time and cuffed instantly.
|
||||
admin-smite-garbage-can-description = Turn them into a garbage bin to emphasize what they remind you of.
|
||||
|
||||
|
||||
## Tricks descriptions
|
||||
|
||||
admin-trick-unbolt-description = Unbolts the targetted airlock.
|
||||
admin-trick-bolt-description = Bolts the targetted airlock.
|
||||
admin-trick-emergency-access-on-description = Turns on emergency access for the targetted airlock.
|
||||
admin-trick-emergency-access-off-description = Turns off emergency access for the targetted airlock.
|
||||
admin-trick-make-indestructible-description = Makes the given object indestructible, effectively godmode.
|
||||
admin-trick-make-vulnerable-description = Makes the given object vulnerable again, turning off godmode.
|
||||
admin-trick-block-unanchoring-description = Prevents unanchoring the given object.
|
||||
admin-trick-refill-battery-description = Refills the internal battery of the given object.
|
||||
admin-trick-drain-battery-description = Empties the internal battery of the given object.
|
||||
admin-trick-internals-refill-oxygen-description = Refills oxygen in the target tank or target's internals.
|
||||
admin-trick-internals-refill-nitrogen-description = Refills nitrogen in the target tank or target's internals.
|
||||
admin-trick-internals-refill-plasma-description = Refills plasma in the target tank or target's internals.
|
||||
admin-trick-send-to-test-arena-description = Sends an object to the admin testing arena. This arena is per-admin.
|
||||
admin-trick-grant-all-access-description = Grants the target all access.
|
||||
admin-trick-revoke-all-access-description = Revokes all accesses on the target.
|
||||
admin-trick-rejuvenate-description = Rejuvenates the target, healing them of all ailments.
|
||||
admin-trick-adjust-stack-description = Adjusts the contents of a stack of items to the given value.
|
||||
admin-trick-fill-stack-description = Refills a stack of items to max.
|
||||
admin-trick-rename-description = Renames the given object. Note this is not equivalent to the `rename` command and won't fix their ID.
|
||||
admin-trick-redescribe-description = Redescribes the given object.
|
||||
admin-trick-rename-and-redescribe-description = Convenient bundle of both rename and redescribe into one button.
|
||||
admin-trick-bar-job-slots-description = Closes all job slots on the station, such that nobody can join it.
|
||||
admin-trick-locate-cargo-shuttle-description = Teleports you directly to the station's cargo shuttle, if it exists.
|
||||
admin-trick-infinite-battery-description = Reconfigures the SMESes and substations on the grid/station/map to self-recharge rapidly.
|
||||
admin-trick-infinite-battery-object-description = Reconfigures the item so that it's battery rapidly refills.
|
||||
admin-trick-halt-movement-description = Halts the movement of the target object, at least until something moves it again.
|
||||
admin-trick-unpause-map-description = Unpause the selected map. NOTE THIS CAN CAUSE BAD BEHAVIOR WITH STORAGE MAPS!
|
||||
admin-trick-pause-map-description = Pause the selected map. Note this doesn't entirely stop player movement!
|
||||
admin-trick-snap-joints-description = Remove all physics joints from an object. Unfortunately does not snap every bone in their body.
|
||||
admin-trick-minigun-fire-description = Makes the targetted gun fire like a minigun (very fast).
|
||||
admin-trick-set-bullet-amount-description = Quickly set the amount of unspawned bullets in a gun.
|
||||
|
||||
@@ -7,3 +7,4 @@ admin-menu-atmos-tab = Atmos
|
||||
admin-menu-round-tab = Round
|
||||
admin-menu-server-tab = Server
|
||||
admin-menu-players-tab = Players
|
||||
admin-menu-objects-tab = Objects
|
||||
|
||||
@@ -18,6 +18,7 @@ verb-categories-buckle = Buckle
|
||||
verb-categories-unbuckle = Unbuckle
|
||||
verb-categories-rotate = Rotate
|
||||
verb-categories-smite = Smite
|
||||
verb-categories-tricks = Tricks
|
||||
verb-categories-transfer = Set Transfer Amount
|
||||
verb-categories-split = Split
|
||||
verb-categories-instrument-style = Instrument Style
|
||||
|
||||
1324
Resources/Maps/Test/admin_test_arena.yml
Normal file
@@ -91,6 +91,7 @@
|
||||
max: 1
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: StationInfiniteBatteryTarget
|
||||
|
||||
# APCs in use
|
||||
- type: entity
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
containers:
|
||||
machine_parts: !type:Container
|
||||
machine_board: !type:Container
|
||||
- type: StationInfiniteBatteryTarget
|
||||
|
||||
# SMES' in use
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
containers:
|
||||
machine_parts: !type:Container
|
||||
machine_board: !type:Container
|
||||
- type: StationInfiniteBatteryTarget
|
||||
|
||||
# Compact Wall Substation Base
|
||||
- type: entity
|
||||
@@ -161,6 +162,7 @@
|
||||
maxIntensity: 50
|
||||
intensitySlope: 2
|
||||
totalIntensity: 100
|
||||
- type: StationInfiniteBatteryTarget
|
||||
|
||||
# Substations in use
|
||||
|
||||
|
||||
37
Resources/Prototypes/Polymorphs/admin.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
- type: polymorph
|
||||
id: AdminLizardSmite
|
||||
entity: MobReptilian
|
||||
forced: true
|
||||
transferName: true
|
||||
transferHumanoidAppearance: true
|
||||
inventory: Transfer
|
||||
|
||||
- type: polymorph
|
||||
id: AdminMonkeySmite
|
||||
entity: MobMonkey
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminBreadSmite
|
||||
entity: FoodBreadPlain
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminInstrumentSmite
|
||||
entity: SuperSynthesizerInstrument
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminMouseSmite
|
||||
entity: MobMouse
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminDisposalsSmite
|
||||
entity: DisposalUnit
|
||||
forced: true
|
||||
inventory: Drop
|
||||
@@ -46,24 +46,6 @@
|
||||
transferHumanoidAppearance: true
|
||||
inventory: Transfer
|
||||
|
||||
- type: polymorph
|
||||
id: AdminMonkeySmite
|
||||
entity: MobMonkey
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminBreadSmite
|
||||
entity: FoodBreadPlain
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AdminInstrumentSmite
|
||||
entity: SuperSynthesizerInstrument
|
||||
forced: true
|
||||
inventory: Drop
|
||||
|
||||
- type: polymorph
|
||||
id: AMIVMorph
|
||||
entity: MobMonkey
|
||||
|
||||
@@ -27,3 +27,16 @@
|
||||
innerclothingskirt: ClothingUniformJumpskirtJanitor
|
||||
satchel: ClothingBackpackSatchelFilled
|
||||
duffelbag: ClothingBackpackDuffelFilled
|
||||
|
||||
- type: startingGear
|
||||
id: JanitorMaidGear
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtJanimaid
|
||||
back: ClothingBackpackFilled
|
||||
id: JanitorPDA
|
||||
head: ClothingHeadHatCatEars
|
||||
ears: ClothingHeadsetService
|
||||
belt: ClothingBeltJanitorFilled
|
||||
innerclothingskirt: ClothingUniformJumpskirtJanimaid
|
||||
satchel: ClothingBackpackSatchelFilled
|
||||
duffelbag: ClothingBackpackDuffelFilled
|
||||
|
||||
BIN
Resources/Textures/Interface/AdminActions/adjust-stack.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
Resources/Textures/Interface/AdminActions/bar_jobslots.png
Normal file
|
After Width: | Height: | Size: 307 B |
BIN
Resources/Textures/Interface/AdminActions/bolt.png
Normal file
|
After Width: | Height: | Size: 267 B |
BIN
Resources/Textures/Interface/AdminActions/drain_battery.png
Normal file
|
After Width: | Height: | Size: 226 B |
BIN
Resources/Textures/Interface/AdminActions/emergency_access.png
Normal file
|
After Width: | Height: | Size: 238 B |
BIN
Resources/Textures/Interface/AdminActions/fill-stack.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
Resources/Textures/Interface/AdminActions/fill_battery.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
Resources/Textures/Interface/AdminActions/flip.png
Normal file
|
After Width: | Height: | Size: 264 B |
BIN
Resources/Textures/Interface/AdminActions/halt.png
Normal file
|
After Width: | Height: | Size: 145 B |
BIN
Resources/Textures/Interface/AdminActions/help-backwards.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
Resources/Textures/Interface/AdminActions/infinite_battery.png
Normal file
|
After Width: | Height: | Size: 240 B |
BIN
Resources/Textures/Interface/AdminActions/pause.png
Normal file
|
After Width: | Height: | Size: 128 B |
BIN
Resources/Textures/Interface/AdminActions/play.png
Normal file
|
After Width: | Height: | Size: 198 B |
BIN
Resources/Textures/Interface/AdminActions/redescribe.png
Normal file
|
After Width: | Height: | Size: 212 B |
BIN
Resources/Textures/Interface/AdminActions/rejuvenate.png
Normal file
|
After Width: | Height: | Size: 253 B |
BIN
Resources/Textures/Interface/AdminActions/remove-hand.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
Resources/Textures/Interface/AdminActions/remove-hands.png
Normal file
|
After Width: | Height: | Size: 234 B |
BIN
Resources/Textures/Interface/AdminActions/rename.png
Normal file
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 292 B |
BIN
Resources/Textures/Interface/AdminActions/run-walk-swap.png
Normal file
|
After Width: | Height: | Size: 302 B |
BIN
Resources/Textures/Interface/AdminActions/snap_joints.png
Normal file
|
After Width: | Height: | Size: 290 B |
BIN
Resources/Textures/Interface/AdminActions/unbolt.png
Normal file
|
After Width: | Height: | Size: 272 B |
BIN
Resources/Textures/Interface/AdminActions/zoom.png
Normal file
|
After Width: | Height: | Size: 270 B |
@@ -540,6 +540,7 @@ public sealed class $CLASS$ : Shared$CLASS$ {
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=prefs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Protolathe/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pullable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Redescribe/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reparenting/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=roundstart/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ruinable/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||