From 6cb58e608b8a2c484f0064c1d53bf3bd186cbb71 Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Tue, 5 Oct 2021 14:29:03 +1100
Subject: [PATCH] ECS verbs and update context menu (#4594)
* Functioning ECS verbs
Currently only ID card console works.
* Changed verb types and allow ID card insertions
* Verb GUI sorting and verb networking
* More networking, and shared components
* Clientside verbs work now.
* Verb enums changed to bitmask flags
* Verb Categories redo
* Fix range check
* GasTank Verb
* Remove unnecessary bodypart verb
* Buckle Verb
* buckle & unbuckle verbs
* Updated range checks
* Item cabinet verbs
* Add range user override
* construction verb
* Chemistry machine verbs
* Climb Verb
* Generalise pulled entity verbs
* ViewVariables Verb
* rejuvenate, delete, sentient, control verbs
* Outfit verb
* inrangeunoccluded and tubedirection verbs
* attach-to verbs
* remove unused verbs and move VV
* Rename DebugVerbSystem
* Ghost role and pointing verbs
* Remove global verbs
* Allow verbs to raise events
* Changing categories and simplifying debug verbs
* Add rotate and flip verbs
* fix rejuvenate test
* redo context menu
* new Add Gas debug verb
* Add Set Temperature debug verb
* Uncuff verb
* Disposal unit verbs
* Add pickup verb
* lock/unlock verb
* Remove verb type, add specific verb events
* rename verb messages -> events
* Context menu displays verbs by interaction type
* Updated context menu HandleMove
previously, checked if entities moved 1 tile from click location.
Now checks if entities moved out of view.
Now you can actually right-click interact with yourself while walking!
* Misc Verb menu GUI changes
* Fix non-human/ghost verbs
* Update types and categories
* Allow non-ghost/human to open context menu
* configuration verb
* tagger verb
* Morgue Verbs
* Medical Scanner Verbs
* Fix solution refactor merge issues
* Fix context menu in-view check
* Remove prepare GUI
* Redo verb restrictions
* Fix context menu UI
* Disposal Verbs
* Spill verb
* Light verb
* Hand Held light verb
* power cell verbs
* storage verbs
and adding names to insert/eject
* Pulling verb
* Close context menu on verb execution
* Strip verb
* AmmoBox verb
* fix pull verb
* gun barrel verbs
revolver verb
energy weapon verbs
Bolt action verb
* Magazine gun barrel verbs
* Add charger verbs
* PDA verbs
* Transfer amount verb
* Add reagent verb
* make alt-click use ECS verbs
* Delete old verb files
* Magboot verb
* finalising tweaks
* context menu visibility changes
* code cleanup
* Update AdminAddReagentUI.cs
* Remove HasFlag
* Consistent verb keys
* Remove Linq, add comment
* Fix in-inventory check
* Update GUI text alignment and padding
* Added close-menu option
* Changed some "interaction" verbs to "activation"
* Remove verb keys, use sorted sets
* fix master merge
* update some verb text
* Undo Changes
Remove some new verbs that can be added later
undid some .ftl bugfixes, can and should be done separately
* fix merge
* Undo file rename
* fix merge
* Misc Cleanup
* remove contraction
* Fix keybinding issue
* fix comment
* merge fix
* fix merge
* fix merge
* fix merge
* fix merge
* fix open-close verbs
* adjust uncuff verb
* fix merge
and undo the renaming of SharedPullableComponent to PullableComponent. I'm tired of all of those merge conflicts
---
.../Administration/AdminVerbSystem.cs | 40 ++
.../ContextMenu/UI/ContextMenuElement.cs | 54 +-
.../ContextMenu/UI/ContextMenuPresenter.cs | 161 +++--
.../ContextMenu/UI/ContextMenuView.cs | 3 +-
.../ContextMenu/UI/ContextMenuViewGrouping.cs | 10 +-
Content.Client/Examine/ExamineSystem.cs | 18 +-
Content.Client/Examine/ExamineVerb.cs | 26 -
Content.Client/Input/ContentContexts.cs | 4 +-
.../Items/Managers/ItemSlotManager.cs | 2 +-
Content.Client/Pulling/PullableComponent.cs | 11 -
Content.Client/Pulling/PullingSystem.cs | 7 +-
.../Rotatable/RotatableComponent.cs | 11 -
Content.Client/Verbs/VerbMenuElement.cs | 246 ++++++++
Content.Client/Verbs/VerbSystem.cs | 571 ++++--------------
.../ViewVariables/ViewVariablesVerb.cs | 38 --
.../Tests/Commands/RejuvenateTest.cs | 6 +-
.../Components/IdCardConsoleComponent.cs | 131 ++--
Content.Server/Access/IdCardConsoleSystem.cs | 80 +++
.../Administration/AdminVerbSystem.cs | 188 ++++++
.../{Rejuvenate.cs => RejuvenateCommand.cs} | 35 +-
.../Administration/UI/AdminAddReagentEui.cs | 94 +++
.../Verbs/AdminAddReagentVerb.cs | 150 -----
.../Administration/Verbs/DeleteVerb.cs | 55 --
Content.Server/Alert/Click/StopBeingPulled.cs | 3 +-
Content.Server/Alert/Click/StopPulling.cs | 2 +-
.../Atmos/Components/GasTankComponent.cs | 32 -
.../Atmos/EntitySystems/GasTankSystem.cs | 21 +
.../Body/Commands/AttachBodyPartCommand.cs | 2 +-
Content.Server/Body/Part/BodyPartComponent.cs | 49 --
.../Buckle/Components/BuckleComponent.cs | 33 +-
.../Buckle/Components/StrapComponent.cs | 51 --
.../Buckle/{ => Systems}/BuckleSystem.cs | 25 +-
Content.Server/Buckle/Systems/StrapSystem.cs | 101 ++++
.../Cabinet/ItemCabinetComponent.cs | 51 --
Content.Server/Cabinet/ItemCabinetSystem.cs | 103 +++-
.../Components/ChemMasterComponent.cs | 43 +-
.../Components/ReagentDispenserComponent.cs | 53 +-
.../Components/SolutionTransferComponent.cs | 133 +---
.../EntitySystems/ChemMasterSystem.cs | 53 +-
.../EntitySystems/ReagentDispenserSystem.cs | 63 +-
.../EntitySystems/SolutionTransferSystem.cs | 68 +++
Content.Server/Climbing/ClimbSystem.cs | 27 +-
.../Climbing/Components/ClimbableComponent.cs | 26 +-
.../Clothing/Components/MagbootsComponent.cs | 20 -
Content.Server/Clothing/MagbootsSystem.cs | 29 +
.../Configurable/ConfigurationComponent.cs | 33 +-
.../Components/AnchorableComponent.cs | 3 +-
.../Components/ConstructionComponent.Verbs.cs | 49 --
.../Construction/ConstructionSystem.cs | 35 +-
.../Commands/HideContainedContextCommand.cs | 29 -
.../Cuffs/Components/CuffableComponent.cs | 26 -
Content.Server/Cuffs/CuffableSystem.cs | 26 +-
Content.Server/Damage/RejuvenateVerb.cs | 94 ---
.../Components/DisposalRouterComponent.cs | 28 +-
.../Components/DisposalTaggerComponent.cs | 29 +-
.../Tube/Components/DisposalTubeComponent.cs | 34 --
.../Disposal/Tube/DisposalTubeSystem.cs | 38 ++
.../Unit/Components/DisposalUnitComponent.cs | 78 ---
.../Unit/EntitySystems/DisposalUnitSystem.cs | 48 +-
.../Fluids/Components/SpillableComponent.cs | 56 --
Content.Server/Fluids/PuddleSystem.cs | 24 +
.../Ghost/Roles/MakeGhostRoleVerb.cs | 60 --
.../Hands/Components/HandsComponent.cs | 2 +-
.../Interaction/InRangeUnoccludedVerb.cs | 58 --
.../Interaction/InteractionSystem.cs | 45 +-
.../Components/InventoryComponent.cs | 42 --
Content.Server/Items/ItemComponent.cs | 32 -
Content.Server/Items/ItemSystem.cs | 39 ++
.../Components/ExpendableLightComponent.cs | 34 +-
.../Components/HandheldLightComponent.cs | 24 +-
.../UnpoweredFlashlightComponent.cs | 22 -
.../EntitySystems/ExpendableLightSystem.cs | 29 +-
.../EntitySystems/HandHeldLightSystem.cs | 18 +
.../UnpoweredFlashlightSystem.cs | 23 +
Content.Server/Lock/LockComponent.cs | 45 --
Content.Server/Lock/LockSystem.cs | 66 +-
.../Components/MedicalScannerComponent.cs | 46 --
.../Medical/MedicalScannerSystem.cs | 55 +-
.../Mind/Commands/MakeSentientCommand.cs | 2 +
Content.Server/Mind/Verbs/ControlMobVerb.cs | 62 --
Content.Server/Mind/Verbs/MakeSentientVerb.cs | 53 --
.../BodyBagEntityStorageComponent.cs | 24 -
.../CrematoriumEntityStorageComponent.cs | 23 -
Content.Server/Morgue/MorgueSystem.cs | 40 +-
Content.Server/PDA/PDAComponent.cs | 76 ---
Content.Server/PDA/PDASystem.cs | 3 +-
.../Pointing/EntitySystems/PointingSystem.cs | 23 +
Content.Server/Pointing/PointingVerb.cs | 53 --
.../Power/Components/BaseCharger.cs | 103 +---
.../Power/EntitySystems/BaseChargerSystem.cs | 51 ++
.../Components/PowerCellChargerComponent.cs | 2 +-
.../Components/PowerCellSlotComponent.cs | 39 --
Content.Server/PowerCell/PowerCellSystem.cs | 42 +-
Content.Server/Pulling/PullableComponent.cs | 63 --
Content.Server/Pulling/PullingSystem.cs | 4 +-
.../Rotatable/FlippableComponent.cs | 19 +
Content.Server/Rotatable/RotatableSystem.cs | 82 +++
.../Rotation/Components/FlippableComponent.cs | 60 --
.../Rotation/Components/RotatableComponent.cs | 75 ---
Content.Server/Rotation/HardRotateVerbs.cs | 42 --
.../Components/EntityStorageComponent.cs | 60 +-
.../Components/ServerStorageComponent.cs | 41 --
.../Storage/EntitySystems/StorageSystem.cs | 60 ++
Content.Server/Strip/StrippableComponent.cs | 29 +-
Content.Server/Strip/StrippableSystem.cs | 32 +
.../Components/TabletopGameComponent.cs | 29 -
Content.Server/Tabletop/TabletopSystem.cs | 24 +-
.../Verbs/AttachToGrandparentVerb.cs | 55 --
.../Transform/Verbs/AttachToGridVerb.cs | 54 --
.../Transform/Verbs/AttachToSelf.cs | 54 --
.../ToggleAllContextCommand.cs} | 8 +-
Content.Server/Verbs/VerbSystem.cs | 166 ++---
.../Ranged/Ammunition/AmmunitionSystem.cs | 32 +
.../Ammunition/Components/AmmoBoxComponent.cs | 33 +-
.../Weapon/Ranged/Barrels/BarrelSystem.cs | 149 +++++
.../Components/BoltActionBarrelComponent.cs | 44 --
.../Components/RevolverBarrelComponent.cs | 31 -
.../ServerBatteryBarrelComponent.cs | 43 +-
.../ServerMagazineBarrelComponent.cs | 182 ++----
.../Weapon/WeaponCapacitorChargerComponent.cs | 2 +-
.../Components/WieldableComponent.cs | 21 -
Content.Server/Wieldable/WieldableSystem.cs | 22 +-
.../Administration/AdminAddReagentEuiState.cs | 2 +-
.../EntitySystems/SolutionContainerSystem.cs | 2 +-
.../ItemSlot/SharedItemSlotsSystem.cs | 120 +++-
Content.Shared/Item/SharedItemComponent.cs | 4 +-
.../SharedExpendableLightComponent.cs | 2 +-
.../SharedMedicalScannerComponent.cs | 6 +-
...lableComponent.cs => PullableComponent.cs} | 3 +-
.../Pulling/Systems/SharedPullerSystem.cs | 2 +-
.../Pulling/Systems/SharedPullingSystem.cs | 32 +-
...ableComponent.cs => RotatableComponent.cs} | 3 +-
Content.Shared/Verbs/GlobalVerb.cs | 54 --
.../Verbs/HideContextMenuComponent.cs | 15 -
Content.Shared/Verbs/IShowContextMenu.cs | 9 -
.../Verbs/PlayerContainerVisibilityMessage.cs | 17 -
Content.Shared/Verbs/SharedVerbSystem.cs | 140 ++++-
Content.Shared/Verbs/Verb.cs | 232 ++++---
Content.Shared/Verbs/VerbBase.cs | 24 -
Content.Shared/Verbs/VerbCategories.cs | 18 -
Content.Shared/Verbs/VerbCategory.cs | 54 ++
Content.Shared/Verbs/VerbCategoryData.cs | 31 -
Content.Shared/Verbs/VerbData.cs | 68 ---
Content.Shared/Verbs/VerbEvents.cs | 211 +++++++
Content.Shared/Verbs/VerbSystemMessages.cs | 69 ---
Content.Shared/Verbs/VerbUtility.cs | 99 ---
Content.Shared/Verbs/VerbVisibility.cs | 24 -
.../components/id-card-console-component.ftl | 10 +-
.../body/components/bodypart-component.ftl | 3 -
.../buckle/components/buckle-component.ftl | 3 -
.../buckle/components/strap-component.ftl | 4 -
.../en-US/cabinet/item-cabinet-system.ftl | 8 -
.../components/chem-master-component.ftl | 3 -
.../solution-transfer-component.ftl | 6 +-
.../construction-component-verbs.ftl | 2 +-
.../disposal-mailing-unit-component.ftl | 4 +-
.../Locale/en-US/examine/examine-system.ftl | 4 +-
.../Locale/en-US/examine/examine-verb.ftl | 1 -
.../en-US/items/components/item-component.ftl | 6 +-
.../components/expendable-light-component.ftl | 1 +
.../components/handheld-light-component.ftl | 3 -
.../components/medical-scanner-component.ftl | 7 +-
.../components/power-cell-slot-component.ftl | 2 -
.../components/power-receiver-component.ftl | 8 -
.../components/entity-storage-component.ftl | 3 +-
.../storage/components/storage-component.ftl | 5 -
Resources/Locale/en-US/verbs/verb-system.ftl | 23 +-
.../server-battery-barrel-component.ftl | 4 -
.../server-magazine-barrel-component.ftl | 4 -
.../Entities/Markers/drag_shadow.yml | 10 +-
.../Prototypes/Entities/Markers/pointing.yml | 4 +-
.../Structures/Piping/Disposal/pipes.yml | 8 +-
.../Entities/Structures/Walls/low.yml | 4 +-
Resources/Prototypes/tags.yml | 3 +
Resources/keybinds.yml | 1 +
175 files changed, 3391 insertions(+), 4305 deletions(-)
create mode 100644 Content.Client/Administration/AdminVerbSystem.cs
delete mode 100644 Content.Client/Examine/ExamineVerb.cs
delete mode 100644 Content.Client/Pulling/PullableComponent.cs
delete mode 100644 Content.Client/Rotatable/RotatableComponent.cs
create mode 100644 Content.Client/Verbs/VerbMenuElement.cs
delete mode 100644 Content.Client/ViewVariables/ViewVariablesVerb.cs
create mode 100644 Content.Server/Access/IdCardConsoleSystem.cs
create mode 100644 Content.Server/Administration/AdminVerbSystem.cs
rename Content.Server/Administration/Commands/{Rejuvenate.cs => RejuvenateCommand.cs} (53%)
create mode 100644 Content.Server/Administration/UI/AdminAddReagentEui.cs
delete mode 100644 Content.Server/Administration/Verbs/AdminAddReagentVerb.cs
delete mode 100644 Content.Server/Administration/Verbs/DeleteVerb.cs
rename Content.Server/Buckle/{ => Systems}/BuckleSystem.cs (81%)
create mode 100644 Content.Server/Buckle/Systems/StrapSystem.cs
create mode 100644 Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs
create mode 100644 Content.Server/Clothing/MagbootsSystem.cs
delete mode 100644 Content.Server/Construction/Components/ConstructionComponent.Verbs.cs
delete mode 100644 Content.Server/Containers/Commands/HideContainedContextCommand.cs
delete mode 100644 Content.Server/Damage/RejuvenateVerb.cs
delete mode 100644 Content.Server/Ghost/Roles/MakeGhostRoleVerb.cs
delete mode 100644 Content.Server/Interaction/InRangeUnoccludedVerb.cs
create mode 100644 Content.Server/Items/ItemSystem.cs
delete mode 100644 Content.Server/Mind/Verbs/ControlMobVerb.cs
delete mode 100644 Content.Server/Mind/Verbs/MakeSentientVerb.cs
delete mode 100644 Content.Server/Pointing/PointingVerb.cs
delete mode 100644 Content.Server/Pulling/PullableComponent.cs
create mode 100644 Content.Server/Rotatable/FlippableComponent.cs
create mode 100644 Content.Server/Rotatable/RotatableSystem.cs
delete mode 100644 Content.Server/Rotation/Components/FlippableComponent.cs
delete mode 100644 Content.Server/Rotation/Components/RotatableComponent.cs
delete mode 100644 Content.Server/Rotation/HardRotateVerbs.cs
create mode 100644 Content.Server/Strip/StrippableSystem.cs
delete mode 100644 Content.Server/Transform/Verbs/AttachToGrandparentVerb.cs
delete mode 100644 Content.Server/Transform/Verbs/AttachToGridVerb.cs
delete mode 100644 Content.Server/Transform/Verbs/AttachToSelf.cs
rename Content.Server/{Containers/Commands/ShowContainedContextCommand.cs => Verbs/ToggleAllContextCommand.cs} (69%)
create mode 100644 Content.Server/Weapon/Ranged/Ammunition/AmmunitionSystem.cs
create mode 100644 Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs
rename Content.Shared/Pulling/Components/{SharedPullableComponent.cs => PullableComponent.cs} (97%)
rename Content.Shared/Rotatable/{SharedRotatableComponent.cs => RotatableComponent.cs} (91%)
delete mode 100644 Content.Shared/Verbs/GlobalVerb.cs
delete mode 100644 Content.Shared/Verbs/HideContextMenuComponent.cs
delete mode 100644 Content.Shared/Verbs/IShowContextMenu.cs
delete mode 100644 Content.Shared/Verbs/PlayerContainerVisibilityMessage.cs
delete mode 100644 Content.Shared/Verbs/VerbBase.cs
delete mode 100644 Content.Shared/Verbs/VerbCategories.cs
create mode 100644 Content.Shared/Verbs/VerbCategory.cs
delete mode 100644 Content.Shared/Verbs/VerbCategoryData.cs
delete mode 100644 Content.Shared/Verbs/VerbData.cs
create mode 100644 Content.Shared/Verbs/VerbEvents.cs
delete mode 100644 Content.Shared/Verbs/VerbSystemMessages.cs
delete mode 100644 Content.Shared/Verbs/VerbUtility.cs
delete mode 100644 Content.Shared/Verbs/VerbVisibility.cs
delete mode 100644 Resources/Locale/en-US/buckle/components/strap-component.ftl
delete mode 100644 Resources/Locale/en-US/examine/examine-verb.ftl
create mode 100644 Resources/Locale/en-US/light/components/expendable-light-component.ftl
delete mode 100644 Resources/Locale/en-US/storage/components/storage-component.ftl
delete mode 100644 Resources/Locale/en-US/weapons/ranged/barrels/components/server-battery-barrel-component.ftl
diff --git a/Content.Client/Administration/AdminVerbSystem.cs b/Content.Client/Administration/AdminVerbSystem.cs
new file mode 100644
index 0000000000..5cc3300483
--- /dev/null
+++ b/Content.Client/Administration/AdminVerbSystem.cs
@@ -0,0 +1,40 @@
+using Content.Client.Administration.UI.Tabs.AtmosTab;
+using Content.Shared.Verbs;
+using Robust.Client.Console;
+using Robust.Client.ViewVariables;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Map;
+
+namespace Content.Client.Verbs
+{
+ ///
+ /// Client-side admin verb system. These usually open some sort of UIs.
+ ///
+ class AdminVerbSystem : EntitySystem
+ {
+ [Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
+ [Dependency] private readonly IViewVariablesManager _viewVariablesManager = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(AddAdminVerbs);
+ }
+
+ private void AddAdminVerbs(GetOtherVerbsEvent args)
+ {
+ // Currently this is only the ViewVariables verb, but more admin-UI related verbs can be added here.
+
+ // View variables verbs
+ if (_clientConGroupController.CanViewVar())
+ {
+ Verb verb = new();
+ verb.Category = VerbCategory.Debug;
+ verb.Text = "View Variables";
+ verb.IconTexture = "/Textures/Interface/VerbIcons/vv.svg.192dpi.png";
+ verb.Act = () => _viewVariablesManager.OpenVV(args.Target);
+ args.Verbs.Add(verb);
+ }
+ }
+ }
+}
diff --git a/Content.Client/ContextMenu/UI/ContextMenuElement.cs b/Content.Client/ContextMenu/UI/ContextMenuElement.cs
index d2fa8b146d..b1d3b909ec 100644
--- a/Content.Client/ContextMenu/UI/ContextMenuElement.cs
+++ b/Content.Client/ContextMenu/UI/ContextMenuElement.cs
@@ -101,7 +101,6 @@ namespace Content.Client.ContextMenu.UI
public sealed class StackContextElement : ContextMenuElement
{
public event Action? OnExitedTree;
- public readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
public HashSet ContextEntities { get; }
public readonly StackContextElement? Pre;
@@ -176,40 +175,44 @@ namespace Content.Client.ContextMenu.UI
}
}
- public sealed class ContextMenuPopup : Robust.Client.UserInterface.Controls.Popup
+ public class ContextMenuPopup : Popup
{
- private static readonly Color DefaultColor = Color.FromHex("#1116");
- private static readonly Color MarginColor = Color.FromHex("#222E");
- private const int MaxItemsBeforeScroll = 10;
- private const int MarginSizeBetweenElements = 2;
+ public static readonly Color ButtonColor = Color.FromHex("#1119");
+ public static readonly Color BackgroundColor = Color.FromHex("#333E");
+
+ public const int MaxItemsBeforeScroll = 10;
+ public const int MarginSize = 2;
+ public const int ButtonHeight = 32;
public BoxContainer List { get; }
+ public ScrollContainer Scroll { get; }
public int Depth { get; }
public ContextMenuPopup(int depth = 0)
{
+ MaxHeight = MaxItemsBeforeScroll * (ButtonHeight + 2*MarginSize);
+
Depth = depth;
- AddChild(new ScrollContainer
+ List = new() { Orientation = LayoutOrientation.Vertical };
+ Scroll = new()
{
HScrollEnabled = false,
- Children = { new PanelContainer
- {
- Children = { (List = new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical
- }) },
- PanelOverride = new StyleBoxFlat { BackgroundColor = MarginColor }
- }}
+ Children = { List }
+ };
+ AddChild(new PanelContainer
+ {
+ Children = { Scroll },
+ PanelOverride = new StyleBoxFlat { BackgroundColor = BackgroundColor }
});
}
- public void AddToMenu(ContextMenuElement element)
+ public void AddToMenu(Control element)
{
List.AddChild(new PanelContainer
{
Children = { element },
- Margin = new Thickness(0,0,0, MarginSizeBetweenElements),
- PanelOverride = new StyleBoxFlat {BackgroundColor = DefaultColor}
+ Margin = new Thickness(MarginSize, MarginSize, MarginSize, MarginSize),
+ PanelOverride = new StyleBoxFlat { BackgroundColor = ButtonColor }
});
}
@@ -229,15 +232,18 @@ namespace Content.Client.ContextMenu.UI
return Vector2.Zero;
}
- List.Measure(availableSize);
- var listSize = List.DesiredSize;
+ Scroll.Measure(availableSize);
+ var size = List.DesiredSize;
- if (List.ChildCount < MaxItemsBeforeScroll)
+ // account for scroll bar width
+ if (size.Y > MaxHeight)
{
- return listSize;
+ // Scroll._vScrollBar is private and ScrollContainer gives no size information :/
+ // 10 = Scroll._vScrollBar.DesiredSize
+ size.X += 10;
}
- listSize.Y = MaxItemsBeforeScroll * 32 + MaxItemsBeforeScroll * MarginSizeBetweenElements;
- return listSize;
+
+ return size;
}
}
}
diff --git a/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs b/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs
index 1cc046b640..753f6c611b 100644
--- a/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs
+++ b/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs
@@ -6,10 +6,9 @@ using Content.Client.Interactable;
using Content.Client.Items.Managers;
using Content.Client.Verbs;
using Content.Client.Viewport;
-using Content.Shared;
using Content.Shared.CCVar;
using Content.Shared.Input;
-using Content.Shared.Verbs;
+using Content.Shared.Interaction.Helpers;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
@@ -40,21 +39,19 @@ namespace Content.Client.ContextMenu.UI
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
+ public static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
+ private CancellationTokenSource? _cancelHover;
+
private readonly IContextMenuView _contextMenuView;
private readonly VerbSystem _verbSystem;
- private bool _playerCanSeeThroughContainers;
-
private MapCoordinates _mapCoordinates;
- private CancellationTokenSource? _cancellationTokenSource;
public ContextMenuPresenter(VerbSystem verbSystem)
{
IoCManager.InjectDependencies(this);
_verbSystem = verbSystem;
- _verbSystem.ToggleContextMenu += SystemOnToggleContextMenu;
- _verbSystem.ToggleContainerVisibility += SystemOnToggleContainerVisibility;
_contextMenuView = new ContextMenuView();
_contextMenuView.OnKeyBindDownSingle += OnKeyBindDownSingle;
@@ -70,6 +67,10 @@ namespace Content.Client.ContextMenu.UI
_contextMenuView.OnCloseChildMenu += OnCloseChildMenu;
_cfg.OnValueChanged(CCVars.ContextMenuGroupingType, _contextMenuView.OnGroupingContextMenuChanged, true);
+
+ CommandBinds.Builder
+ .Bind(ContentKeyFunctions.OpenContextMenu, new PointerInputCmdHandler(HandleOpenContextMenu))
+ .Register();
}
#region View Events
@@ -92,13 +93,11 @@ namespace Content.Client.ContextMenu.UI
{
var realGlobalPosition = e.GlobalPosition;
- _cancellationTokenSource?.Cancel();
- _cancellationTokenSource = new();
+ _cancelHover?.Cancel();
+ _cancelHover = new();
- Timer.Spawn(e.HoverDelay, () =>
+ Timer.Spawn(HoverDelay, () =>
{
- _verbSystem.CloseGroupMenu();
-
if (_contextMenuView.Menus.Count == 0)
{
return;
@@ -111,7 +110,7 @@ namespace Content.Client.ContextMenu.UI
{
_contextMenuView.AddChildMenu(filteredEntities, realGlobalPosition, e);
}
- }, _cancellationTokenSource.Token);
+ }, _cancelHover.Token);
}
private void OnKeyBindDownStack(object? sender, (GUIBoundKeyEventArgs, StackContextElement) e)
@@ -169,10 +168,23 @@ namespace Content.Client.ContextMenu.UI
private void OnMouseEnteredSingle(object? sender, SingleContextElement e)
{
- _cancellationTokenSource?.Cancel();
+ // close other pop-ups after a short delay
+ _cancelHover?.Cancel();
+ _cancelHover = new();
+
+ Timer.Spawn(HoverDelay, () =>
+ {
+ if (_contextMenuView.Menus.Count == 0)
+ {
+ return;
+ }
+
+ OnCloseChildMenu(sender, e.ParentMenu?.Depth ?? 0);
+
+ }, _cancelHover.Token);
+
var entity = e.ContextEntity;
- _verbSystem.CloseGroupMenu();
OnCloseChildMenu(sender, e.ParentMenu?.Depth ?? 0);
@@ -251,91 +263,114 @@ namespace Content.Client.ContextMenu.UI
#endregion
#region Model Updates
- private void SystemOnToggleContainerVisibility(object? sender, bool args)
+ private bool HandleOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
- _playerCanSeeThroughContainers = args;
- }
-
- private void SystemOnToggleContextMenu(object? sender, PointerInputCmdHandler.PointerInputCmdArgs args)
- {
- if (_stateManager.CurrentState is not GameScreenBase)
+ if (args.State != BoundKeyState.Down)
{
- return;
+ return false;
}
- var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
- if (playerEntity == null)
+ if (_stateManager.CurrentState is not GameScreenBase)
{
- return;
+ return false;
+ }
+
+ var player = _playerManager.LocalPlayer?.ControlledEntity;
+ if (player == null)
+ {
+ return false;
}
_mapCoordinates = args.Coordinates.ToMap(_entityManager);
- if (!_verbSystem.TryGetContextEntities(playerEntity, _mapCoordinates, out var entities))
- {
- return;
- }
- entities = entities.Where(CanSeeOnContextMenu).ToList();
- if (entities.Count > 0)
+ if (!_verbSystem.TryGetContextEntities(player, _mapCoordinates, out var entities, ignoreVisibility: _verbSystem.CanSeeAllContext))
+ return false;
+
+ // do we need to do visiblity checks?
+ if (_verbSystem.CanSeeAllContext)
{
_contextMenuView.AddRootMenu(entities);
+ return true;
}
- }
- public void HandleMoveEvent(ref MoveEvent ev)
- {
- if (_contextMenuView.Elements.Count == 0) return;
- var entity = ev.Sender;
- if (_contextMenuView.Elements.ContainsKey(entity))
+ //visibility checks
+ player.TryGetContainer(out var playerContainer);
+ foreach (var entity in entities.ToList())
{
- if (!entity.Transform.MapPosition.InRange(_mapCoordinates, 1.0f))
+ if (!entity.TryGetComponent(out ISpriteComponent? spriteComponent) ||
+ !spriteComponent.Visible ||
+ !CanSeeContainerCheck(entity, playerContainer))
{
- _contextMenuView.RemoveEntity(entity);
+ entities.Remove(entity);
}
}
+
+ if (entities.Count == 0)
+ return false;
+
+ _contextMenuView.AddRootMenu(entities);
+ return true;
}
+ ///
+ /// Can the player see the entity through any entity containers?
+ ///
+ ///
+ /// This is similar to , except that we do not
+ /// allow the player to be the "parent" container and we allow for see-through containers (display cases).
+ ///
+ private bool CanSeeContainerCheck(IEntity entity, IContainer? playerContainer)
+ {
+ // is the player inside this entity?
+ if (playerContainer?.Owner == entity)
+ return true;
+
+ entity.TryGetContainer(out var entityContainer);
+
+ // are they in the same container (or none?)
+ if (playerContainer == entityContainer)
+ return true;
+
+ // Is the entity in a display case?
+ if (playerContainer == null && entityContainer!.ShowContents)
+ return true;
+
+ return false;
+ }
+
+ ///
+ /// Check that entities in the context menu are still visible. If not, remove them from the context menu.
+ ///
public void Update()
{
- if (_contextMenuView.Elements.Count == 0) return;
+ if (_contextMenuView.Elements.Count == 0)
+ return;
+
+ var player = _playerManager.LocalPlayer?.ControlledEntity;
+
+ if (player == null)
+ return;
foreach (var entity in _contextMenuView.Elements.Keys.ToList())
{
- if (entity.Deleted || !_playerCanSeeThroughContainers && entity.IsInContainer())
+ if (entity.Deleted || !_verbSystem.CanSeeAllContext && !player.InRangeUnOccluded(entity))
{
_contextMenuView.RemoveEntity(entity);
+ if (_verbSystem.CurrentTarget == entity.Uid)
+ _verbSystem.CloseVerbMenu();
}
}
}
#endregion
- private bool CanSeeOnContextMenu(IEntity entity)
- {
- if (!entity.TryGetComponent(out ISpriteComponent? spriteComponent) || !spriteComponent.Visible)
- {
- return false;
- }
-
- if (entity.GetAllComponents().Any(s => !s.ShowContextMenu(entity)))
- {
- return false;
- }
-
- return _playerCanSeeThroughContainers || !entity.TryGetContainer(out var container) || container.ShowContents;
- }
-
- private void CloseAllMenus()
+ public void CloseAllMenus()
{
_contextMenuView.CloseContextPopups();
- _verbSystem.CloseGroupMenu();
_verbSystem.CloseVerbMenu();
}
public void Dispose()
{
- _verbSystem.ToggleContextMenu -= SystemOnToggleContextMenu;
- _verbSystem.ToggleContainerVisibility -= SystemOnToggleContainerVisibility;
-
_contextMenuView.OnKeyBindDownSingle -= OnKeyBindDownSingle;
_contextMenuView.OnMouseEnteredSingle -= OnMouseEnteredSingle;
_contextMenuView.OnMouseExitedSingle -= OnMouseExitedSingle;
@@ -347,6 +382,8 @@ namespace Content.Client.ContextMenu.UI
_contextMenuView.OnExitedTree -= OnExitedTree;
_contextMenuView.OnCloseRootMenu -= OnCloseRootMenu;
_contextMenuView.OnCloseChildMenu -= OnCloseChildMenu;
+
+ CommandBinds.Unregister();
}
}
}
diff --git a/Content.Client/ContextMenu/UI/ContextMenuView.cs b/Content.Client/ContextMenu/UI/ContextMenuView.cs
index d3fe353091..1f25206931 100644
--- a/Content.Client/ContextMenu/UI/ContextMenuView.cs
+++ b/Content.Client/ContextMenu/UI/ContextMenuView.cs
@@ -1,4 +1,3 @@
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -73,7 +72,7 @@ namespace Content.Client.ContextMenu.UI
var entitySpriteStates = GroupEntities(entities);
var orderedStates = entitySpriteStates.ToList();
- orderedStates.Sort((x, y) => string.CompareOrdinal(x.First().Prototype!.Name, y.First().Prototype!.Name));
+ orderedStates.Sort((x, y) => string.CompareOrdinal(x.First().Prototype?.Name, y.First().Prototype?.Name));
AddToUI(orderedStates);
_userInterfaceManager.ModalRoot.AddChild(rootContextMenu);
diff --git a/Content.Client/ContextMenu/UI/ContextMenuViewGrouping.cs b/Content.Client/ContextMenu/UI/ContextMenuViewGrouping.cs
index 67556dbdc4..8175d18431 100644
--- a/Content.Client/ContextMenu/UI/ContextMenuViewGrouping.cs
+++ b/Content.Client/ContextMenu/UI/ContextMenuViewGrouping.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.GameObjects;
@@ -105,9 +105,13 @@ namespace Content.Client.ContextMenu.UI
public int GetHashCode(IEntity e)
{
var hash = EqualityComparer.Default.GetHashCode(e.Prototype?.ID!);
- foreach (var element in e.GetComponent().AllLayers.Where(obj => obj.Visible).Select(s => s.RsiState.Name))
+
+ if (e.TryGetComponent(out var sprite))
{
- hash ^= EqualityComparer.Default.GetHashCode(element!);
+ foreach (var element in sprite.AllLayers.Where(obj => obj.Visible).Select(s => s.RsiState.Name))
+ {
+ hash ^= EqualityComparer.Default.GetHashCode(element!);
+ }
}
return hash;
diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs
index 5d58fc47fd..4610587e26 100644
--- a/Content.Client/Examine/ExamineSystem.cs
+++ b/Content.Client/Examine/ExamineSystem.cs
@@ -1,8 +1,9 @@
-using System.Linq;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Content.Shared.Examine;
using Content.Shared.Input;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
@@ -12,6 +13,7 @@ using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
+using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
@@ -38,6 +40,8 @@ namespace Content.Client.Examine
{
IoCManager.InjectDependencies(this);
+ SubscribeLocalEvent(AddExamineVerb);
+
CommandBinds.Builder
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine))
.Register();
@@ -85,6 +89,18 @@ namespace Content.Client.Examine
return true;
}
+ private void AddExamineVerb(GetOtherVerbsEvent args)
+ {
+ if (!CanExamine(args.User, args.Target))
+ return;
+
+ Verb verb = new();
+ verb.Act = () => DoExamine(args.Target) ;
+ verb.Text = Loc.GetString("examine-verb-name");
+ verb.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
+ args.Verbs.Add(verb);
+ }
+
public async void DoExamine(IEntity entity)
{
// Close any examine tooltip that might already be opened
diff --git a/Content.Client/Examine/ExamineVerb.cs b/Content.Client/Examine/ExamineVerb.cs
deleted file mode 100644
index da6a9b47a7..0000000000
--- a/Content.Client/Examine/ExamineVerb.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Content.Shared.Verbs;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
-
-namespace Content.Client.Examine
-{
- [GlobalVerb]
- public class ExamineVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
-
- public override bool BlockedByContainers => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("examine-verb-name");
- data.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- EntitySystem.Get().DoExamine(target);
- }
- }
-}
diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs
index 3db6583d05..4a81755f24 100644
--- a/Content.Client/Input/ContentContexts.cs
+++ b/Content.Client/Input/ContentContexts.cs
@@ -26,6 +26,7 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.TakeScreenshot);
common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
common.AddFunction(ContentKeyFunctions.Point);
+ common.AddFunction(ContentKeyFunctions.OpenContextMenu);
var human = contexts.GetContext("human");
human.AddFunction(ContentKeyFunctions.SwapHands);
@@ -39,7 +40,6 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.TryPullObject);
human.AddFunction(ContentKeyFunctions.MovePulledObject);
human.AddFunction(ContentKeyFunctions.ReleasePulledObject);
- human.AddFunction(ContentKeyFunctions.OpenContextMenu);
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
@@ -82,7 +82,6 @@ namespace Content.Client.Input
aghost.AddFunction(EngineKeyFunctions.MoveLeft);
aghost.AddFunction(EngineKeyFunctions.MoveRight);
aghost.AddFunction(EngineKeyFunctions.Walk);
- aghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
aghost.AddFunction(ContentKeyFunctions.SwapHands);
aghost.AddFunction(ContentKeyFunctions.Drop);
aghost.AddFunction(ContentKeyFunctions.ThrowItemInHand);
@@ -93,7 +92,6 @@ namespace Content.Client.Input
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
ghost.AddFunction(EngineKeyFunctions.MoveRight);
ghost.AddFunction(EngineKeyFunctions.Walk);
- ghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
common.AddFunction(ContentKeyFunctions.OpenEntitySpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenSandboxWindow);
diff --git a/Content.Client/Items/Managers/ItemSlotManager.cs b/Content.Client/Items/Managers/ItemSlotManager.cs
index 9e0be4bda9..e131cb6b5a 100644
--- a/Content.Client/Items/Managers/ItemSlotManager.cs
+++ b/Content.Client/Items/Managers/ItemSlotManager.cs
@@ -78,7 +78,7 @@ namespace Content.Client.Items.Managers
else if (args.Function == ContentKeyFunctions.OpenContextMenu)
{
_entitySystemManager.GetEntitySystem()
- .OpenContextMenu(item, _uiMgr.ScreenToUIPosition(args.PointerLocation));
+ .OpenVerbMenu(item, _uiMgr.ScreenToUIPosition(args.PointerLocation));
}
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
{
diff --git a/Content.Client/Pulling/PullableComponent.cs b/Content.Client/Pulling/PullableComponent.cs
deleted file mode 100644
index ff39f07cc8..0000000000
--- a/Content.Client/Pulling/PullableComponent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Content.Shared.Pulling.Components;
-using Robust.Shared.GameObjects;
-
-namespace Content.Client.Pulling
-{
- [RegisterComponent]
- [ComponentReference(typeof(SharedPullableComponent))]
- public class PullableComponent : SharedPullableComponent
- {
- }
-}
diff --git a/Content.Client/Pulling/PullingSystem.cs b/Content.Client/Pulling/PullingSystem.cs
index 04135bb062..91ec1f5cd9 100644
--- a/Content.Client/Pulling/PullingSystem.cs
+++ b/Content.Client/Pulling/PullingSystem.cs
@@ -1,4 +1,5 @@
-using Content.Shared.Pulling;
+using Content.Shared.Pulling;
+using Content.Shared.Pulling.Components;
using JetBrains.Annotations;
using Robust.Client.Physics;
@@ -13,8 +14,8 @@ namespace Content.Client.Pulling
UpdatesAfter.Add(typeof(PhysicsSystem));
- SubscribeLocalEvent(OnPullableMove);
- SubscribeLocalEvent(OnPullableStopMove);
+ SubscribeLocalEvent(OnPullableMove);
+ SubscribeLocalEvent(OnPullableStopMove);
}
}
}
diff --git a/Content.Client/Rotatable/RotatableComponent.cs b/Content.Client/Rotatable/RotatableComponent.cs
deleted file mode 100644
index 5284292c8c..0000000000
--- a/Content.Client/Rotatable/RotatableComponent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Content.Shared.Rotatable;
-using Robust.Shared.GameObjects;
-
-namespace Content.Client.Rotatable
-{
- [RegisterComponent]
- [ComponentReference(typeof(SharedRotatableComponent))]
- public class RotatableComponent : SharedRotatableComponent
- {
- }
-}
diff --git a/Content.Client/Verbs/VerbMenuElement.cs b/Content.Client/Verbs/VerbMenuElement.cs
new file mode 100644
index 0000000000..b4681e82ac
--- /dev/null
+++ b/Content.Client/Verbs/VerbMenuElement.cs
@@ -0,0 +1,246 @@
+using System.Collections.Generic;
+using System.Threading;
+using Content.Client.ContextMenu.UI;
+using Content.Client.Resources;
+using Content.Shared.Verbs;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.Utility;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Maths;
+using Robust.Shared.Utility;
+using static Robust.Client.UserInterface.Controls.BoxContainer;
+using Timer = Robust.Shared.Timing.Timer;
+
+namespace Content.Client.Verbs
+{
+
+ ///
+ /// This pop-up appears when hovering over a verb category in the context menu.
+ ///
+ public sealed class VerbCategoryPopup : ContextMenuPopup
+ {
+ public VerbCategoryPopup(VerbSystem system, IEnumerable verbs, VerbType type, EntityUid target, bool drawOnlyIcons)
+ : base()
+ {
+ // Do any verbs have icons? If not, don't bother leaving space for icons in the pop-up.
+ var drawVerbIcons = false;
+ foreach (var verb in verbs)
+ {
+ if (verb.Icon != null)
+ {
+ drawVerbIcons = true;
+ break;
+ }
+ }
+
+ // If no verbs have icons. we cannot draw only icons
+ if (drawVerbIcons == false)
+ drawOnlyIcons = false;
+
+ // If we are drawing only icons, show them side by side
+ if (drawOnlyIcons)
+ List.Orientation = LayoutOrientation.Horizontal;
+
+ foreach (var verb in verbs)
+ {
+ AddToMenu(new VerbButton(system, verb, type, target, drawVerbIcons));
+ }
+ }
+ }
+
+ public sealed class VerbButton : BaseButton
+ {
+ public VerbButton(VerbSystem system, Verb verb, VerbType type, EntityUid target, bool drawIcons = true, bool categoryPrefix = false) : base()
+ {
+ Disabled = verb.Disabled;
+ ToolTip = verb.Tooltip;
+ TooltipDelay = 0.5f;
+
+ var buttonContents = new BoxContainer { Orientation = LayoutOrientation.Horizontal };
+
+ // maybe draw verb icons
+ if (drawIcons)
+ {
+ TextureRect icon = new()
+ {
+ MinSize = (ContextMenuPopup.ButtonHeight, ContextMenuPopup.ButtonHeight),
+ Stretch = TextureRect.StretchMode.KeepCentered,
+ TextureScale = (0.5f, 0.5f)
+ };
+
+ // Even though we are drawing icons, the icon for this specific verb may be null.
+ if (verb.Icon != null)
+ {
+ icon.Texture = verb.Icon.Frame0();
+ } else if (categoryPrefix && verb.Category?.Icon != null)
+ {
+ // we will use the category icon instead
+ icon.Texture = verb.Category.Icon.Frame0();
+ }
+
+ buttonContents.AddChild(icon);
+ }
+
+ // maybe add a label
+ if (verb.Text != string.Empty || categoryPrefix)
+ {
+ // First add a small bit of padding
+ buttonContents.AddChild(new Control { MinSize = (4, ContextMenuPopup.ButtonHeight) });
+
+ var label = new RichTextLabel();
+ var text = categoryPrefix ? verb.Category!.Text + " " + verb.Text : verb.Text;
+ label.SetMessage(FormattedMessage.FromMarkupPermissive(text.Trim()));
+ label.VerticalAlignment = VAlignment.Center;
+ buttonContents.AddChild(label);
+
+ // Then also add some padding after the text.
+ buttonContents.AddChild(new Control { MinSize = (4, ContextMenuPopup.ButtonHeight) });
+ }
+
+ AddChild(buttonContents);
+
+ if (Disabled)
+ return;
+
+ // give the button functionality!
+ OnPressed += _ =>
+ {
+ if (verb.CloseMenu)
+ system.ContextMenuPresenter.CloseAllMenus();
+ system.TryExecuteVerb(verb, target, type);
+ };
+ }
+
+ protected override void Draw(DrawingHandleScreen handle)
+ {
+ base.Draw(handle);
+
+ if (Disabled)
+ {
+ // use transparent-black rectangle to create a darker background.
+ handle.DrawRect(PixelSizeBox, new Color(0,0,0,155));
+ }
+ else if (DrawMode == DrawModeEnum.Hover)
+ {
+ // Draw a lighter shade of gray when hovered over
+ handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
+ }
+ }
+ }
+
+ public sealed class VerbCategoryButton : Control
+ {
+ private readonly VerbSystem _system;
+
+ private CancellationTokenSource? _openCancel;
+
+ ///
+ /// Whether or not to hide member verb text and just show icons.
+ ///
+ ///
+ /// If no members have icons, this option is ignored and text is shown anyways. Defaults to using .
+ ///
+ private readonly bool _drawOnlyIcons;
+
+ ///
+ /// The pop-up that appears when hovering over this verb group.
+ ///
+ private readonly VerbCategoryPopup _popup;
+
+ public VerbCategoryButton(VerbSystem system, VerbCategory category, IEnumerable verbs, VerbType type, EntityUid target, bool? drawOnlyIcons = null) : base()
+ {
+ _system = system;
+ _drawOnlyIcons = drawOnlyIcons ?? category.IconsOnly;
+
+ MouseFilter = MouseFilterMode.Stop;
+
+ // Contents of the button stored in this box container
+ var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal };
+
+ // First we add the icon for the verb group
+ var icon = new TextureRect
+ {
+ MinSize = (ContextMenuPopup.ButtonHeight, ContextMenuPopup.ButtonHeight),
+ TextureScale = (0.5f, 0.5f),
+ Stretch = TextureRect.StretchMode.KeepCentered,
+ };
+ if (category.Icon != null)
+ {
+ icon.Texture = category.Icon.Frame0();
+ }
+ box.AddChild(icon);
+
+ // Some padding before the text
+ box.AddChild(new Control { MinSize = (4, ContextMenuPopup.ButtonHeight) });
+
+ // Then we add the label
+ var label = new RichTextLabel();
+ label.SetMessage(FormattedMessage.FromMarkupPermissive(category.Text));
+ label.HorizontalExpand = true;
+ label.VerticalAlignment = VAlignment.Center;
+ box.AddChild(label);
+
+ // Then also add some padding after the text.
+ box.AddChild(new Control { MinSize = (4, ContextMenuPopup.ButtonHeight) });
+
+ // Then add the little ">" icon that tells you it's a group of verbs
+ box.AddChild(new TextureRect
+ {
+ Texture = IoCManager.Resolve()
+ .GetTexture("/Textures/Interface/VerbIcons/group.svg.192dpi.png"),
+ TextureScale = (0.5f, 0.5f),
+ Stretch = TextureRect.StretchMode.KeepCentered,
+ });
+
+ // The pop-up that appears when hovering over the button
+ _popup = new VerbCategoryPopup(_system, verbs, type, target, _drawOnlyIcons);
+ UserInterfaceManager.ModalRoot.AddChild(_popup);
+
+ AddChild(box);
+ }
+
+ protected override void Draw(DrawingHandleScreen handle)
+ {
+ base.Draw(handle);
+
+ if (this == UserInterfaceManager.CurrentlyHovered ||
+ _system.CurrentCategoryPopup == _popup)
+ {
+ handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
+ }
+ }
+
+ ///
+ /// Open a verb category pop-up after a short delay.
+ ///
+ protected override void MouseEntered()
+ {
+ base.MouseEntered();
+
+ _openCancel = new CancellationTokenSource();
+
+ Timer.Spawn(ContextMenuPresenter.HoverDelay, () =>
+ {
+ _system.CurrentCategoryPopup?.Close();
+ _system.CurrentCategoryPopup = _popup;
+ var upperRight = GlobalPosition + (Width + ContextMenuPopup.MarginSize, -ContextMenuPopup.MarginSize);
+ _popup.Open(UIBox2.FromDimensions(upperRight, (1, 1)), GlobalPosition);
+ }, _openCancel.Token);
+ }
+
+ ///
+ /// Cancel the delayed pop-up
+ ///
+ protected override void MouseExited()
+ {
+ base.MouseExited();
+
+ _openCancel?.Cancel();
+ _openCancel = null;
+ }
+ }
+}
diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs
index bc7ed79af0..eeace00254 100644
--- a/Content.Client/Verbs/VerbSystem.cs
+++ b/Content.Client/Verbs/VerbSystem.cs
@@ -1,535 +1,228 @@
-using System;
using System.Collections.Generic;
-using System.Reflection;
-using System.Threading;
+using System.Linq;
using Content.Client.ContextMenu.UI;
-using Content.Client.Resources;
using Content.Shared.GameTicking;
-using Content.Shared.Input;
using Content.Shared.Verbs;
using JetBrains.Annotations;
-using Robust.Client.Graphics;
using Robust.Client.Player;
-using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
-using Robust.Client.Utility;
using Robust.Shared.GameObjects;
-using Robust.Shared.Input;
-using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
-using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
-using Robust.Shared.Utility;
-using static Robust.Client.UserInterface.Controls.BoxContainer;
-using Timer = Robust.Shared.Timing.Timer;
namespace Content.Client.Verbs
{
[UsedImplicitly]
public sealed class VerbSystem : SharedVerbSystem
{
- [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
- public event EventHandler? ToggleContextMenu;
- public event EventHandler? ToggleContainerVisibility;
+ public ContextMenuPresenter ContextMenuPresenter = default!;
- private ContextMenuPresenter _contextMenuPresenter = default!;
- private VerbPopup? _currentVerbListRoot;
- private VerbPopup? _currentGroupList;
- private EntityUid _currentEntity;
+ public EntityUid CurrentTarget;
+ public ContextMenuPopup? CurrentVerbPopup;
+ public ContextMenuPopup? CurrentCategoryPopup;
+ public Dictionary> CurrentVerbs = new();
- // TODO: Move presenter out of the system
- // TODO: Separate the rest of the UI from the logic
+ ///
+ /// Whether to show all entities on the context menu.
+ ///
+ ///
+ /// Verb execution will only be affected if the server also agrees that this player can see the target
+ /// entity.
+ ///
+ public bool CanSeeAllContext = false;
+
+ // TODO VERBS Move presenter out of the system
+ // TODO VERBS Separate the rest of the UI from the logic
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent(Reset);
- SubscribeNetworkEvent(FillEntityPopup);
- SubscribeNetworkEvent(HandleContainerVisibilityMessage);
+ SubscribeNetworkEvent(HandleVerbResponse);
+ SubscribeNetworkEvent(SetSeeAllContext);
- _contextMenuPresenter = new ContextMenuPresenter(this);
- SubscribeLocalEvent(_contextMenuPresenter.HandleMoveEvent);
+ ContextMenuPresenter = new ContextMenuPresenter(this);
+ }
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.OpenContextMenu,
- new PointerInputCmdHandler(HandleOpenContextMenu))
- .Register();
+ private void Reset(RoundRestartCleanupEvent ev)
+ {
+ ContextMenuPresenter.CloseAllMenus();
}
public override void Shutdown()
{
base.Shutdown();
-
- _contextMenuPresenter?.Dispose();
-
- CommandBinds.Unregister();
- }
-
- public void Reset(RoundRestartCleanupEvent ev)
- {
- ToggleContainerVisibility?.Invoke(this, false);
- }
-
- private bool HandleOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
- {
- if (args.State == BoundKeyState.Down)
- {
- ToggleContextMenu?.Invoke(this, args);
- }
- return true;
- }
- private void HandleContainerVisibilityMessage(PlayerContainerVisibilityMessage ev)
- {
- ToggleContainerVisibility?.Invoke(this, ev.CanSeeThrough);
+ ContextMenuPresenter?.Dispose();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
- _contextMenuPresenter?.Update();
+ ContextMenuPresenter?.Update();
}
- public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
+ private void SetSeeAllContext(SetSeeAllContextEvent args)
{
- if (_currentVerbListRoot != null)
+ CanSeeAllContext = args.CanSeeAllContext;
+ }
+
+ ///
+ /// Execute actions associated with the given verb. If there are no defined actions, this will instead ask
+ /// the server to run the given verb.
+ ///
+ public void TryExecuteVerb(Verb verb, EntityUid target, VerbType verbType)
+ {
+ if (!TryExecuteVerb(verb))
+ RaiseNetworkEvent(new TryExecuteVerbEvent(target, verb, verbType));
+ }
+
+ public void OpenVerbMenu(IEntity target, ScreenCoordinates screenCoordinates)
+ {
+ if (CurrentVerbPopup != null)
{
CloseVerbMenu();
}
- _currentEntity = entity.Uid;
- _currentVerbListRoot = new VerbPopup();
- _userInterfaceManager.ModalRoot.AddChild(_currentVerbListRoot);
- _currentVerbListRoot.OnPopupHide += CloseVerbMenu;
+ var user = _playerManager.LocalPlayer?.ControlledEntity;
+ if (user == null)
+ return;
- if (!entity.Uid.IsClientSide())
+ CurrentTarget = target.Uid;
+
+ CurrentVerbPopup = new ContextMenuPopup();
+ _userInterfaceManager.ModalRoot.AddChild(CurrentVerbPopup);
+ CurrentVerbPopup.OnPopupHide += CloseVerbMenu;
+
+ CurrentVerbs = GetVerbs(target, user, VerbType.All);
+
+ if (!target.Uid.IsClientSide())
{
- _currentVerbListRoot.List.AddChild(new Label { Text = Loc.GetString("verb-system-waiting-on-server-text") });
- RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
+ CurrentVerbPopup.AddToMenu(new Label { Text = Loc.GetString("verb-system-waiting-on-server-text") });
+ RaiseNetworkEvent(new RequestServerVerbsEvent(CurrentTarget, VerbType.All));
}
+ // Show the menu
+ FillVerbPopup(CurrentVerbPopup);
var box = UIBox2.FromDimensions(screenCoordinates.Position, (1, 1));
- _currentVerbListRoot.Open(box);
+ CurrentVerbPopup.Open(box);
}
public void OnContextButtonPressed(IEntity entity)
{
- OpenContextMenu(entity, _userInterfaceManager.MousePositionScaled);
+ OpenVerbMenu(entity, _userInterfaceManager.MousePositionScaled);
}
- private void FillEntityPopup(VerbSystemMessages.VerbsResponseMessage msg)
+ private void HandleVerbResponse(VerbsResponseEvent msg)
{
- if (_currentEntity != msg.Entity || !EntityManager.TryGetEntity(_currentEntity, out var entity))
+ if (CurrentTarget != msg.Entity || CurrentVerbPopup == null)
{
return;
}
- DebugTools.AssertNotNull(_currentVerbListRoot);
-
- var buttons = new Dictionary>();
- var groupIcons = new Dictionary();
-
- var vBox = _currentVerbListRoot!.List;
- vBox.DisposeAllChildren();
-
- // Local variable so that scope capture ensures this is the correct value.
- var curEntity = _currentEntity;
-
- foreach (var data in msg.Verbs)
+ // This **should** not happen.
+ if (msg.Verbs == null)
{
- var list = buttons.GetOrNew(data.Category);
-
- if (data.CategoryIcon != null && !groupIcons.ContainsKey(data.Category))
- {
- groupIcons.Add(data.Category, data.CategoryIcon);
- }
-
- list.Add(new ListedVerbData(data.Text, !data.Available, data.Key, entity.ToString()!, () =>
- {
- RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(curEntity, data.Key));
- CloseAllMenus();
- }, data.Icon));
+ // update "waiting for server...".
+ CurrentVerbPopup.List.DisposeAllChildren();
+ CurrentVerbPopup.AddToMenu(new Label { Text = Loc.GetString("verb-system-null-server-response") });
+ FillVerbPopup(CurrentVerbPopup);
+ return;
}
- var user = GetUserEntity();
- //Get verbs, component dependent.
- foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
+ // Add the new server-side verbs.
+ foreach (var (verbType, verbSet) in msg.Verbs)
{
- if (!VerbUtility.VerbAccessChecks(user, entity, verb))
+ SortedSet sortedVerbs = new (verbSet);
+ if (!CurrentVerbs.TryAdd(verbType, sortedVerbs))
{
- continue;
+ CurrentVerbs[verbType].UnionWith(sortedVerbs);
}
-
- var verbData = verb.GetData(user, component);
-
- if (verbData.IsInvisible)
- continue;
-
- var list = buttons.GetOrNew(verbData.Category);
-
- if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
- {
- groupIcons.Add(verbData.Category, verbData.CategoryIcon);
- }
-
- list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, verb.ToString()!, entity.ToString()!,
- () => verb.Activate(user, component), verbData.Icon));
}
- //Get global verbs. Visible for all entities regardless of their components.
- foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly()))
+ // Clear currently shown verbs and show new ones
+ CurrentVerbPopup.List.DisposeAllChildren();
+ FillVerbPopup(CurrentVerbPopup);
+ }
+
+ private void FillVerbPopup(ContextMenuPopup popup)
+ {
+ if (CurrentTarget == EntityUid.Invalid)
+ return;
+
+ // Add verbs to pop-up, grouped by type. Order determined by how types are defined VerbTypes
+ var types = CurrentVerbs.Keys.ToList();
+ types.Sort();
+ foreach (var type in types)
{
- if (!VerbUtility.VerbAccessChecks(user, entity, globalVerb))
- {
- continue;
- }
-
- var verbData = globalVerb.GetData(user, entity);
-
- if (verbData.IsInvisible)
- continue;
-
- var list = buttons.GetOrNew(verbData.Category);
-
- if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
- {
- groupIcons.Add(verbData.Category, verbData.CategoryIcon);
- }
-
- list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, globalVerb.ToString()!,
- entity.ToString()!,
- () => globalVerb.Activate(user, entity), verbData.Icon));
+ AddVerbSet(popup, type);
}
- if (buttons.Count > 0)
- {
- var first = true;
- foreach (var (category, verbs) in buttons)
- {
- if (string.IsNullOrEmpty(category))
- continue;
-
- if (!first)
- {
- vBox.AddChild(new PanelContainer
- {
- MinSize = (0, 2),
- PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#333") }
- });
- }
-
- first = false;
-
- groupIcons.TryGetValue(category, out var icon);
-
- vBox.AddChild(CreateCategoryButton(category, verbs, icon));
- }
-
- if (buttons.ContainsKey(""))
- {
- buttons[""].Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
-
- foreach (var verb in buttons[""])
- {
- if (!first)
- {
- vBox.AddChild(new PanelContainer
- {
- MinSize = (0, 2),
- PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#333") }
- });
- }
-
- first = false;
-
- vBox.AddChild(CreateVerbButton(verb));
- }
- }
- }
- else
+ // Were the verb lists empty?
+ if (popup.List.ChildCount == 0)
{
var panel = new PanelContainer();
panel.AddChild(new Label { Text = Loc.GetString("verb-system-no-verbs-text") });
- vBox.AddChild(panel);
+ popup.AddChild(panel);
}
+
+ popup.InvalidateMeasure();
}
- private VerbButton CreateVerbButton(ListedVerbData data)
+ ///
+ /// Add a list of verbs to a BoxContainer. Iterates over the given verbs list and creates GUI buttons.
+ ///
+ private void AddVerbSet(ContextMenuPopup popup, VerbType type)
{
- var button = new VerbButton
- {
- Text = Loc.GetString(data.Text),
- Disabled = data.Disabled
- };
+ if (!CurrentVerbs.TryGetValue(type, out var verbSet) || verbSet.Count == 0)
+ return;
- if (data.Icon != null)
- {
- button.Icon = data.Icon.Frame0();
- }
+ HashSet listedCategories = new();
- if (!data.Disabled)
+ foreach (var verb in verbSet)
{
- button.OnPressed += _ =>
+ if (verb.Category == null)
{
- CloseAllMenus();
- try
- {
- data.Action.Invoke();
- }
- catch (Exception e)
- {
- Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", data.VerbName, data.OwnerName, e);
- }
- };
+ // Lone verb without a category. just create a button for it
+ popup.AddToMenu(new VerbButton(this, verb, type, CurrentTarget));
+ continue;
+ }
+
+ if (listedCategories.Contains(verb.Category.Text))
+ {
+ // This verb was already included in a verb-category button added by a previous verb
+ continue;
+ }
+
+ // Get the verbs in the category
+ var verbsInCategory = verbSet.Where(v => v.Category?.Text == verb.Category.Text);
+
+ popup.AddToMenu(
+ new VerbCategoryButton(this, verb.Category, verbsInCategory, type, CurrentTarget));
+ listedCategories.Add(verb.Category.Text);
+ continue;
+
}
-
- return button;
- }
-
- private Control CreateCategoryButton(string text, List verbButtons, SpriteSpecifier? icon)
- {
- verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
-
- return new VerbGroupButton(this, verbButtons, icon)
- {
- Text = Loc.GetString(text),
- };
}
public void CloseVerbMenu()
{
- _currentVerbListRoot?.Dispose();
- _currentVerbListRoot = null;
- _currentEntity = EntityUid.Invalid;
- }
-
- private void CloseAllMenus()
- {
- CloseVerbMenu();
- // CloseContextPopups();
- CloseGroupMenu();
- }
-
- public void CloseGroupMenu()
- {
- _currentGroupList?.Dispose();
- _currentGroupList = null;
- }
-
- private IEntity GetUserEntity()
- {
- return _playerManager.LocalPlayer!.ControlledEntity!;
- }
-
- private sealed class VerbPopup : Popup
- {
- public BoxContainer List { get; }
-
- public VerbPopup()
+ if (CurrentVerbPopup != null)
{
- AddChild(new PanelContainer
- {
- Children = {(List = new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical
- })},
- PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#111E")}
- });
- }
- }
-
- private sealed class VerbButton : BaseButton
- {
- private readonly Label _label;
- private readonly TextureRect _icon;
-
- public Texture? Icon
- {
- get => _icon.Texture;
- set => _icon.Texture = value;
+ CurrentVerbPopup.OnPopupHide -= CloseVerbMenu;
+ CurrentVerbPopup.Dispose();
+ CurrentVerbPopup = null;
}
- public string? Text
- {
- get => _label.Text;
- set => _label.Text = value;
- }
-
- public VerbButton()
- {
- AddChild(new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- (_icon = new TextureRect
- {
- MinSize = (32, 32),
- Stretch = TextureRect.StretchMode.KeepCentered,
- TextureScale = (0.5f, 0.5f)
- }),
- (_label = new Label()),
- // Padding
- new Control {MinSize = (8, 0)}
- }
- });
- }
-
- protected override void Draw(DrawingHandleScreen handle)
- {
- base.Draw(handle);
-
- if (DrawMode == DrawModeEnum.Hover)
- {
- handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
- }
- }
- }
-
- private sealed class VerbGroupButton : Control
- {
- private static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
-
- private readonly VerbSystem _system;
-
- private readonly Label _label;
- private readonly TextureRect _icon;
-
- private CancellationTokenSource? _openCancel;
-
- public List VerbButtons { get; }
-
- public string? Text
- {
- get => _label.Text;
- set => _label.Text = value;
- }
-
- public Texture? Icon
- {
- get => _icon.Texture;
- set => _icon.Texture = value;
- }
-
- public VerbGroupButton(VerbSystem system, List verbButtons, SpriteSpecifier? icon)
- {
- _system = system;
- VerbButtons = verbButtons;
-
- MouseFilter = MouseFilterMode.Stop;
-
- AddChild(new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- (_icon = new TextureRect
- {
- MinSize = (32, 32),
- TextureScale = (0.5f, 0.5f),
- Stretch = TextureRect.StretchMode.KeepCentered
- }),
-
- (_label = new Label { HorizontalExpand = true }),
-
- // Padding
- new Control {MinSize = (8, 0)},
-
- new TextureRect
- {
- Texture = IoCManager.Resolve()
- .GetTexture("/Textures/Interface/VerbIcons/group.svg.192dpi.png"),
- TextureScale = (0.5f, 0.5f),
- Stretch = TextureRect.StretchMode.KeepCentered,
- }
- }
- });
-
- if (icon != null)
- {
- _icon.Texture = icon.Frame0();
- }
- }
-
- protected override void Draw(DrawingHandleScreen handle)
- {
- base.Draw(handle);
-
- if (this == UserInterfaceManager.CurrentlyHovered)
- {
- handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
- }
- }
-
- protected override void MouseEntered()
- {
- base.MouseEntered();
-
- _openCancel = new CancellationTokenSource();
-
- Timer.Spawn(HoverDelay, () =>
- {
- if (_system._currentGroupList != null)
- {
- _system.CloseGroupMenu();
- }
-
- var popup = _system._currentGroupList = new VerbPopup();
-
- var first = true;
- foreach (var verb in VerbButtons)
- {
- if (!first)
- {
- popup.List.AddChild(new PanelContainer
- {
- MinSize = (0, 2),
- PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
- });
- }
-
- first = false;
-
- popup.List.AddChild(_system.CreateVerbButton(verb));
- }
-
- UserInterfaceManager.ModalRoot.AddChild(popup);
- popup.Open(UIBox2.FromDimensions(GlobalPosition + (Width, 0), (1, 1)), GlobalPosition);
- }, _openCancel.Token);
- }
-
- protected override void MouseExited()
- {
- base.MouseExited();
-
- _openCancel?.Cancel();
- _openCancel = null;
- }
- }
-
- private sealed class ListedVerbData
- {
- public string Text { get; }
- public bool Disabled { get; }
- public string VerbName { get; }
- public string OwnerName { get; }
- public SpriteSpecifier? Icon { get; }
- public Action Action { get; }
-
- public ListedVerbData(string text, bool disabled, string verbName, string ownerName,
- Action action, SpriteSpecifier? icon)
- {
- Text = text;
- Disabled = disabled;
- VerbName = verbName;
- OwnerName = ownerName;
- Action = action;
- Icon = icon;
- }
+ CurrentCategoryPopup?.Dispose();
+ CurrentCategoryPopup = null;
+ CurrentTarget = EntityUid.Invalid;
+ CurrentVerbs.Clear();
}
}
}
diff --git a/Content.Client/ViewVariables/ViewVariablesVerb.cs b/Content.Client/ViewVariables/ViewVariablesVerb.cs
deleted file mode 100644
index 2150dda02e..0000000000
--- a/Content.Client/ViewVariables/ViewVariablesVerb.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Content.Shared.Verbs;
-using Robust.Client.Console;
-using Robust.Client.ViewVariables;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-
-namespace Content.Client.ViewVariables
-{
- ///
- /// Global verb that opens a view variables window for the entity in question.
- ///
- [GlobalVerb]
- class ViewVariablesVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- var groupController = IoCManager.Resolve();
- if (!groupController.CanViewVar())
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = "View Variables";
- data.CategoryData = VerbCategories.Debug;
- data.IconTexture = "/Textures/Interface/VerbIcons/vv.svg.192dpi.png";
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- var vvm = IoCManager.Resolve();
- vvm.OpenVV(target);
- }
- }
-}
diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
index 0b242319cd..359cd2cfa7 100644
--- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
+++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Content.Server.Damage;
+using Content.Server.Administration.Commands;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.MobState;
@@ -12,7 +12,7 @@ using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Commands
{
[TestFixture]
- [TestOf(typeof(RejuvenateVerb))]
+ [TestOf(typeof(RejuvenateCommand))]
public class RejuvenateTest : ContentIntegrationTest
{
private const string Prototypes = @"
@@ -66,7 +66,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobState.IsIncapacitated, Is.True);
// Rejuvenate them
- RejuvenateVerb.PerformRejuvenate(human);
+ RejuvenateCommand.PerformRejuvenate(human);
// Check that it is alive and with no damage
Assert.That(mobState.IsAlive, Is.True);
diff --git a/Content.Server/Access/Components/IdCardConsoleComponent.cs b/Content.Server/Access/Components/IdCardConsoleComponent.cs
index 5ea5085974..db0e0dcf5f 100644
--- a/Content.Server/Access/Components/IdCardConsoleComponent.cs
+++ b/Content.Server/Access/Components/IdCardConsoleComponent.cs
@@ -1,16 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Content.Server.Hands.Components;
-using Content.Server.Items;
using Content.Server.Power.Components;
using Content.Server.UserInterface;
using Content.Shared.Access;
-using Content.Shared.ActionBlocker;
using Content.Shared.Acts;
+using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
@@ -28,21 +25,21 @@ namespace Content.Server.Access.Components
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- private ContainerSlot _privilegedIdContainer = default!;
- private ContainerSlot _targetIdContainer = default!;
+ public ContainerSlot PrivilegedIdContainer = default!;
+ public ContainerSlot TargetIdContainer = default!;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
[ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
- private bool PrivilegedIDEmpty => _privilegedIdContainer.ContainedEntities.Count < 1;
- private bool TargetIDEmpty => _targetIdContainer.ContainedEntities.Count < 1;
+ public bool PrivilegedIDEmpty => PrivilegedIdContainer.ContainedEntities.Count < 1;
+ public bool TargetIDEmpty => TargetIdContainer.ContainedEntities.Count < 1;
protected override void Initialize()
{
base.Initialize();
- _privilegedIdContainer = ContainerHelpers.EnsureContainer(Owner, $"{Name}-privilegedId");
- _targetIdContainer = ContainerHelpers.EnsureContainer(Owner, $"{Name}-targetId");
+ PrivilegedIdContainer = ContainerHelpers.EnsureContainer(Owner, $"{Name}-privilegedId");
+ TargetIdContainer = ContainerHelpers.EnsureContainer(Owner, $"{Name}-targetId");
Owner.EnsureComponentWarn();
Owner.EnsureComponentWarn();
@@ -68,10 +65,10 @@ namespace Content.Server.Access.Components
switch (msg.Button)
{
case UiButton.PrivilegedId:
- HandleId(obj.Session.AttachedEntity, _privilegedIdContainer);
+ HandleId(obj.Session.AttachedEntity, PrivilegedIdContainer);
break;
case UiButton.TargetId:
- HandleId(obj.Session.AttachedEntity, _targetIdContainer);
+ HandleId(obj.Session.AttachedEntity, TargetIdContainer);
break;
}
break;
@@ -84,7 +81,7 @@ namespace Content.Server.Access.Components
}
///
- /// Returns true if there is an ID in and said ID satisfies the requirements of .
+ /// Returns true if there is an ID in and said ID satisfies the requirements of .
///
private bool PrivilegedIdIsAuthorized()
{
@@ -93,22 +90,22 @@ namespace Content.Server.Access.Components
return true;
}
- var privilegedIdEntity = _privilegedIdContainer.ContainedEntity;
+ var privilegedIdEntity = PrivilegedIdContainer.ContainedEntity;
return privilegedIdEntity != null && reader.IsAllowed(privilegedIdEntity);
}
///
/// Called when the "Submit" button in the UI gets pressed.
- /// Writes data passed from the UI into the ID stored in , if present.
+ /// Writes data passed from the UI into the ID stored in , if present.
///
private void TryWriteToTargetId(string newFullName, string newJobTitle, List newAccessList)
{
- if (!PrivilegedIdIsAuthorized() || _targetIdContainer.ContainedEntity == null)
+ if (!PrivilegedIdIsAuthorized() || TargetIdContainer.ContainedEntity == null)
{
return;
}
- var targetIdEntity = _targetIdContainer.ContainedEntity;
+ var targetIdEntity = TargetIdContainer.ContainedEntity;
var targetIdComponent = targetIdEntity.GetComponent();
targetIdComponent.FullName = newFullName;
@@ -128,7 +125,7 @@ namespace Content.Server.Access.Components
///
private void HandleId(IEntity user, ContainerSlot container)
{
- if (!user.TryGetComponent(out IHandsComponent? hands))
+ if (!user.TryGetComponent(out SharedHandsComponent? hands))
{
Owner.PopupMessage(user, Loc.GetString("access-id-card-console-component-no-hands-error"));
return;
@@ -144,20 +141,15 @@ namespace Content.Server.Access.Components
}
}
- private void InsertIdFromHand(IEntity user, ContainerSlot container, IHandsComponent hands)
+ public void InsertIdFromHand(IEntity user, ContainerSlot container, SharedHandsComponent hands)
{
- var isId = hands.GetActiveHand?.Owner.HasComponent();
- if (isId != true)
- {
+ if (!hands.TryGetActiveHeldEntity(out var heldEntity))
return;
- }
- if (hands.ActiveHand == null)
- {
+ if (!heldEntity.HasComponent())
return;
- }
- if (!hands.TryPutHandIntoContainer(hands.ActiveHand, container))
+ if (!hands.TryPutHandIntoContainer(hands.ActiveHand!, container))
{
Owner.PopupMessage(user, Loc.GetString("access-id-card-console-component-cannot-let-go-error"));
return;
@@ -165,7 +157,7 @@ namespace Content.Server.Access.Components
UpdateUserInterface();
}
- private void PutIdInHand(ContainerSlot container, IHandsComponent hands)
+ public void PutIdInHand(ContainerSlot container, SharedHandsComponent hands)
{
var idEntity = container.ContainedEntity;
if (idEntity == null || !container.Remove(idEntity))
@@ -174,13 +166,13 @@ namespace Content.Server.Access.Components
}
UpdateUserInterface();
- hands.PutInHand(idEntity.GetComponent());
+ hands.TryPutInActiveHandOrAny(idEntity);
}
private void UpdateUserInterface()
{
- var isPrivilegedIdPresent = _privilegedIdContainer.ContainedEntity != null;
- var targetIdEntity = _targetIdContainer.ContainedEntity;
+ var isPrivilegedIdPresent = PrivilegedIdContainer.ContainedEntity != null;
+ var targetIdEntity = TargetIdContainer.ContainedEntity;
IdCardConsoleBoundUserInterfaceState newState;
// this could be prettier
if (targetIdEntity == null)
@@ -192,8 +184,8 @@ namespace Content.Server.Access.Components
null,
null,
null,
- _privilegedIdContainer.ContainedEntity?.Name ?? string.Empty,
- _targetIdContainer.ContainedEntity?.Name ?? string.Empty);
+ PrivilegedIdContainer.ContainedEntity?.Name ?? string.Empty,
+ TargetIdContainer.ContainedEntity?.Name ?? string.Empty);
}
else
{
@@ -206,8 +198,8 @@ namespace Content.Server.Access.Components
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(),
- _privilegedIdContainer.ContainedEntity?.Name ?? string.Empty,
- _targetIdContainer.ContainedEntity?.Name ?? string.Empty);
+ PrivilegedIdContainer.ContainedEntity?.Name ?? string.Empty,
+ TargetIdContainer.ContainedEntity?.Name ?? string.Empty);
}
UserInterface?.SetState(newState);
}
@@ -233,89 +225,34 @@ namespace Content.Server.Access.Components
return false;
}
- if (!item.HasComponent() || !user.TryGetComponent(out IHandsComponent? hand))
+ if (!item.HasComponent() || !user.TryGetComponent(out SharedHandsComponent? hand))
{
return false;
}
if (PrivilegedIDEmpty)
{
- InsertIdFromHand(user, _privilegedIdContainer, hand);
+ InsertIdFromHand(user, PrivilegedIdContainer, hand);
}
else if (TargetIDEmpty)
{
- InsertIdFromHand(user, _targetIdContainer, hand);
+ InsertIdFromHand(user, TargetIdContainer, hand);
}
UpdateUserInterface();
return true;
}
- [Verb]
- public sealed class EjectPrivilegedIDVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("access-eject-privileged-id-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- data.Visibility = component.PrivilegedIDEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
- }
-
- protected override void Activate(IEntity user, IdCardConsoleComponent component)
- {
- if (!user.TryGetComponent(out IHandsComponent? hand))
- {
- return;
- }
- component.PutIdInHand(component._privilegedIdContainer, hand);
- }
- }
-
- public sealed class EjectTargetIDVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("access-eject-target-id-verb-get-data-text");
- data.Visibility = component.TargetIDEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, IdCardConsoleComponent component)
- {
- if (!user.TryGetComponent(out IHandsComponent? hand))
- {
- return;
- }
- component.PutIdInHand(component._targetIdContainer, hand);
- }
- }
-
public void OnBreak(BreakageEventArgs eventArgs)
{
- var privileged = _privilegedIdContainer.ContainedEntity;
+ var privileged = PrivilegedIdContainer.ContainedEntity;
if (privileged != null)
- _privilegedIdContainer.Remove(privileged);
+ PrivilegedIdContainer.Remove(privileged);
- var target = _targetIdContainer.ContainedEntity;
+ var target = TargetIdContainer.ContainedEntity;
if (target != null)
- _targetIdContainer.Remove(target);
+ TargetIdContainer.Remove(target);
}
}
}
diff --git a/Content.Server/Access/IdCardConsoleSystem.cs b/Content.Server/Access/IdCardConsoleSystem.cs
new file mode 100644
index 0000000000..8dbc2d0972
--- /dev/null
+++ b/Content.Server/Access/IdCardConsoleSystem.cs
@@ -0,0 +1,80 @@
+using Content.Server.Access.Components;
+using Content.Shared.ActionBlocker;
+using Content.Shared.Verbs;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+
+namespace Content.Server.Access
+{
+ public class IdCardConsoleSystem : EntitySystem
+ {
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(AddInsertVerbs);
+ SubscribeLocalEvent(AddEjectVerbs);
+ }
+
+ private void AddInsertVerbs(EntityUid uid, IdCardConsoleComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Using == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ !args.Using.HasComponent() ||
+ !_actionBlockerSystem.CanDrop(args.User))
+ return;
+
+ // Can we insert a privileged ID?
+ if (component.PrivilegedIDEmpty)
+ {
+ Verb verb = new();
+ verb.Act = () => component.InsertIdFromHand(args.User, component.PrivilegedIdContainer, args.Hands!);
+ verb.Category = VerbCategory.Insert;
+ verb.Text = Loc.GetString("id-card-console-privileged-id");
+ args.Verbs.Add(verb);
+ }
+
+ // Can we insert a target ID?
+ if (component.TargetIDEmpty)
+ {
+ Verb verb = new();
+ verb.Act = () => component.InsertIdFromHand(args.User, component.TargetIdContainer, args.Hands!);
+ verb.Category = VerbCategory.Insert;
+ verb.Text = Loc.GetString("id-card-console-target-id");
+ args.Verbs.Add(verb);
+ }
+ }
+
+ private void AddEjectVerbs(EntityUid uid, IdCardConsoleComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (args.Hands == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ !_actionBlockerSystem.CanPickup(args.User))
+ return;
+
+ // Can we eject a privileged ID?
+ if (!component.PrivilegedIDEmpty)
+ {
+ Verb verb = new();
+ verb.Act = () => component.PutIdInHand(component.PrivilegedIdContainer, args.Hands);
+ verb.Category = VerbCategory.Eject;
+ verb.Text = Loc.GetString("id-card-console-privileged-id");
+ args.Verbs.Add(verb);
+ }
+
+ // Can we eject a target ID?
+ if (!component.TargetIDEmpty)
+ {
+ Verb verb = new();
+ verb.Act = () => component.PutIdInHand(component.TargetIdContainer, args.Hands);
+ verb.Category = VerbCategory.Eject;
+ verb.Text = Loc.GetString("id-card-console-target-id");
+ args.Verbs.Add(verb);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Administration/AdminVerbSystem.cs b/Content.Server/Administration/AdminVerbSystem.cs
new file mode 100644
index 0000000000..a9873ca246
--- /dev/null
+++ b/Content.Server/Administration/AdminVerbSystem.cs
@@ -0,0 +1,188 @@
+using Content.Server.Administration.Commands;
+using Content.Server.Administration.Managers;
+using Content.Server.Administration.UI;
+using Content.Server.Configurable;
+using Content.Server.Disposal.Tube.Components;
+using Content.Server.EUI;
+using Content.Server.Ghost.Roles;
+using Content.Server.Inventory.Components;
+using Content.Server.Mind.Commands;
+using Content.Server.Mind.Components;
+using Content.Server.Players;
+using Content.Server.Verbs;
+using Content.Shared.Administration;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Interaction.Helpers;
+using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Robust.Server.Console;
+using Robust.Server.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+
+namespace Content.Server.Administration
+{
+ ///
+ /// System to provide various global admin/debug verbs
+ ///
+ public class AdminVerbSystem : EntitySystem
+ {
+ [Dependency] private readonly IConGroupController _groupController = default!;
+ [Dependency] private readonly IAdminManager _adminManager = default!;
+ [Dependency] private readonly EuiManager _euiManager = default!;
+ [Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;
+ [Dependency] private readonly VerbSystem _verbSystem = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(AddDebugVerbs);
+ }
+
+ private void AddDebugVerbs(GetOtherVerbsEvent args)
+ {
+ if (!args.User.TryGetComponent(out var actor))
+ return;
+
+ var player = actor.PlayerSession;
+
+ // Delete verb
+ if (_groupController.CanCommand(player, "deleteentity"))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("delete-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/delete.svg.192dpi.png";
+ verb.Act = () => args.Target.Delete();
+ args.Verbs.Add(verb);
+ }
+
+ // Rejuvenate verb
+ if (_groupController.CanCommand(player, "rejuvenate"))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("rejuvenate-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png";
+ verb.Act = () => RejuvenateCommand.PerformRejuvenate(args.Target);
+ args.Verbs.Add(verb);
+ }
+
+ // Control mob verb
+ if (_groupController.CanCommand(player, "controlmob") &&
+ args.User != args.Target &&
+ args.User.HasComponent() &&
+ args.Target.TryGetComponent(out var targetMind))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("control-mob-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ // TODO VERB ICON control mob icon
+ verb.Act = () =>
+ {
+ targetMind.Mind?.TransferTo(null);
+ player.ContentData()?.Mind?.TransferTo(args.Target, ghostCheckOverride: true);
+ };
+ args.Verbs.Add(verb);
+ }
+
+ // Make Sentient verb
+ if (_groupController.CanCommand(player, "makesentient") &&
+ args.User != args.Target &&
+ !args.Target.HasComponent())
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("make-sentient-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/sentient.svg.192dpi.png";
+ verb.Act = () => MakeSentientCommand.MakeSentient(args.Target);
+ args.Verbs.Add(verb);
+ }
+
+ // Set clothing verb
+ if (_groupController.CanCommand(player, "setoutfit") &&
+ args.Target.HasComponent())
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("set-outfit-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png";
+ verb.Act = () => _euiManager.OpenEui(new SetOutfitEui(args.Target), player);
+ args.Verbs.Add(verb);
+ }
+
+ // In range unoccluded verb
+ if (_groupController.CanCommand(player, "inrangeunoccluded"))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("in-range-unoccluded-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png";
+ verb.Act = () =>
+ {
+ var message = args.User.InRangeUnOccluded(args.Target)
+ ? Loc.GetString("in-range-unoccluded-verb-on-activate-not-occluded")
+ : Loc.GetString("in-range-unoccluded-verb-on-activate-occluded");
+ args.Target.PopupMessage(args.User, message);
+ };
+ args.Verbs.Add(verb);
+ }
+
+ // Get Disposal tube direction verb
+ if (_groupController.CanCommand(player, "tubeconnections") &&
+ args.Target.TryGetComponent(out var tube))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("tube-direction-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png";
+ verb.Act = () => tube.PopupDirections(args.User);
+ args.Verbs.Add(verb);
+ }
+
+ // Make ghost role verb
+ if (_groupController.CanCommand(player, "makeghostrole") &&
+ !(args.Target.GetComponentOrNull()?.HasMind ?? false))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("make-ghost-role-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ // TODO VERB ICON add ghost icon
+ // Where is the national park service icon for haunted forests?
+ verb.Act = () => _ghostRoleSystem.OpenMakeGhostRoleEui(player, args.Target.Uid);
+ args.Verbs.Add(verb);
+ }
+
+ // Configuration verb. Is this even used for anything!?
+ if (_groupController.CanAdminMenu(player) &&
+ args.Target.TryGetComponent(out var config))
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("configure-verb-get-data-text");
+ verb.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
+ verb.Category = VerbCategory.Debug;
+ verb.Act = () => config.OpenUserInterface(actor);
+ args.Verbs.Add(verb);
+ }
+
+ // Add reagent verb
+ if (_adminManager.HasAdminFlag(player, AdminFlags.Fun) &&
+ args.Target.HasComponent())
+ {
+ Verb verb = new();
+ verb.Text = Loc.GetString("admin-add-reagent-verb-get-data-text");
+ verb.Category = VerbCategory.Debug;
+ verb.IconTexture = "/Textures/Interface/VerbIcons/spill.svg.192dpi.png";
+ verb.Act = () => _euiManager.OpenEui(new AdminAddReagentEui(args.Target), player);
+
+ // TODO CHEMISTRY
+ // Add reagent ui broke after solution refactor. Needs fixing
+ verb.Disabled = true;
+ verb.Tooltip = "Currently non functional after solution refactor.";
+ verb.Priority = -2;
+
+ args.Verbs.Add(verb);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Administration/Commands/Rejuvenate.cs b/Content.Server/Administration/Commands/RejuvenateCommand.cs
similarity index 53%
rename from Content.Server/Administration/Commands/Rejuvenate.cs
rename to Content.Server/Administration/Commands/RejuvenateCommand.cs
index 6ddee77ce1..12616f563c 100644
--- a/Content.Server/Administration/Commands/Rejuvenate.cs
+++ b/Content.Server/Administration/Commands/RejuvenateCommand.cs
@@ -1,5 +1,12 @@
-using Content.Server.Damage;
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Server.Nutrition.Components;
+using Content.Server.Nutrition.EntitySystems;
+using Content.Server.Stunnable.Components;
using Content.Shared.Administration;
+using Content.Shared.Damage;
+using Content.Shared.MobState;
+using Content.Shared.Nutrition.Components;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
@@ -9,7 +16,7 @@ using Robust.Shared.Localization;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Admin)]
- class Rejuvenate : IConsoleCommand
+ public class RejuvenateCommand : IConsoleCommand
{
public string Command => "rejuvenate";
@@ -28,7 +35,7 @@ namespace Content.Server.Administration.Commands
shell.WriteLine(Loc.GetString("rejuvenate-command-no-entity-attached-message"));
return;
}
- RejuvenateVerb.PerformRejuvenate(player.AttachedEntity);
+ PerformRejuvenate(player.AttachedEntity);
}
var entityManager = IoCManager.Resolve();
@@ -39,7 +46,27 @@ namespace Content.Server.Administration.Commands
shell.WriteLine(Loc.GetString("shell-could-not-find-entity",("entity", arg)));
continue;
}
- RejuvenateVerb.PerformRejuvenate(entity);
+ PerformRejuvenate(entity);
+ }
+ }
+
+ public static void PerformRejuvenate(IEntity target)
+ {
+ target.GetComponentOrNull()?.UpdateState(0);
+ target.GetComponentOrNull()?.ResetFood();
+ target.GetComponentOrNull()?.ResetThirst();
+ target.GetComponentOrNull()?.ResetStuns();
+
+ EntitySystem.Get().Extinguish(target.Uid);
+
+ if (target.TryGetComponent(out DamageableComponent? damageable))
+ {
+ EntitySystem.Get().SetAllDamage(damageable, 0);
+ }
+
+ if (target.TryGetComponent(out CreamPiedComponent? creamPied))
+ {
+ EntitySystem.Get().SetCreamPied(target.Uid, creamPied, false);
}
}
}
diff --git a/Content.Server/Administration/UI/AdminAddReagentEui.cs b/Content.Server/Administration/UI/AdminAddReagentEui.cs
new file mode 100644
index 0000000000..0f675cb5cc
--- /dev/null
+++ b/Content.Server/Administration/UI/AdminAddReagentEui.cs
@@ -0,0 +1,94 @@
+using Content.Server.Administration.Managers;
+using Content.Server.EUI;
+using Content.Shared.Administration;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Eui;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+
+namespace Content.Server.Administration.UI
+{
+ public sealed class AdminAddReagentEui : BaseEui
+ {
+ private readonly IEntity _target;
+ [Dependency] private readonly IAdminManager _adminManager = default!;
+
+ public AdminAddReagentEui(IEntity target)
+ {
+ _target = target;
+
+ IoCManager.InjectDependencies(this);
+ }
+
+ public override void Opened()
+ {
+ StateDirty();
+ }
+
+ public override EuiStateBase GetNewState()
+ {
+ if (EntitySystem.Get()
+ .TryGetSolution(_target, "default", out var container))
+ {
+ return new AdminAddReagentEuiState
+ {
+ CurVolume = container.CurrentVolume,
+ MaxVolume = container.MaxVolume
+ };
+ }
+
+ return new AdminAddReagentEuiState
+ {
+ CurVolume = ReagentUnit.Zero,
+ MaxVolume = ReagentUnit.Zero
+ };
+ }
+
+ public override void HandleMessage(EuiMessageBase msg)
+ {
+ switch (msg)
+ {
+ case AdminAddReagentEuiMsg.Close:
+ Close();
+ break;
+ case AdminAddReagentEuiMsg.DoAdd doAdd:
+ // Double check that user wasn't de-adminned in the mean time...
+ // Or the target was deleted.
+ if (!_adminManager.HasAdminFlag(Player, AdminFlags.Fun) || _target.Deleted)
+ {
+ Close();
+ return;
+ }
+
+ var id = doAdd.ReagentId;
+ var amount = doAdd.Amount;
+ var solutionsSys = EntitySystem.Get();
+
+ if (_target.TryGetComponent(out InjectableSolutionComponent? injectable)
+ && solutionsSys.TryGetSolution(_target, injectable.Name, out var targetSolution))
+ {
+ var solution = new Solution(id, amount);
+ solutionsSys.Inject(_target.Uid, targetSolution, solution);
+ }
+ else
+ {
+ //TODO decide how to find the solution
+ if (solutionsSys.TryGetSolution(_target, "default", out var solution))
+ {
+ solutionsSys.TryAddReagent(_target.Uid,solution, id, amount, out _);
+ }
+ }
+
+ StateDirty();
+
+ if (doAdd.CloseAfter)
+ Close();
+
+ break;
+ }
+ }
+ }
+}
diff --git a/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs b/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs
deleted file mode 100644
index 936ef2a5d8..0000000000
--- a/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using Content.Server.Administration.Managers;
-using Content.Server.EUI;
-using Content.Shared.Administration;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Eui;
-using Content.Shared.Verbs;
-using Robust.Server.GameObjects;
-using Robust.Server.Player;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Administration.Verbs
-{
- [GlobalVerb]
- internal sealed class AdminAddReagentVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- private const AdminFlags ReqFlags = AdminFlags.Fun;
-
- private static void OpenAddReagentMenu(IPlayerSession player, IEntity target)
- {
- var euiMgr = IoCManager.Resolve();
- euiMgr.OpenEui(new AdminAddReagentEui(target), player);
- }
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- // ISolutionInteractionsComponent doesn't exactly have an interface for "admin tries to refill this", so...
- // Still have a path for SolutionContainerComponent in case it doesn't allow direct refilling.
- if (!(target.HasComponent()
- && target.HasComponent()))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("admin-add-reagent-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/spill.svg.192dpi.png";
- data.CategoryData = VerbCategories.Debug;
- data.Visibility = VerbVisibility.Invisible;
-
- var adminManager = IoCManager.Resolve();
-
- if (user.TryGetComponent(out var player))
- {
- if (adminManager.HasAdminFlag(player.PlayerSession, ReqFlags))
- {
- data.Visibility = VerbVisibility.Visible;
- }
- }
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- var groupController = IoCManager.Resolve();
- if (user.TryGetComponent(out var player))
- {
- if (groupController.HasAdminFlag(player.PlayerSession, ReqFlags))
- OpenAddReagentMenu(player.PlayerSession, target);
- }
- }
-
- private sealed class AdminAddReagentEui : BaseEui
- {
- private readonly IEntity _target;
- [Dependency] private readonly IAdminManager _adminManager = default!;
-
- public AdminAddReagentEui(IEntity target)
- {
- _target = target;
-
- IoCManager.InjectDependencies(this);
- }
-
- public override void Opened()
- {
- StateDirty();
- }
-
- public override EuiStateBase GetNewState()
- {
- if (EntitySystem.Get()
- .TryGetSolution(_target, "default", out var container))
- {
- return new AdminAddReagentEuiState
- {
- CurVolume = container.CurrentVolume,
- MaxVolume = container.MaxVolume
- };
- }
-
- return new AdminAddReagentEuiState
- {
- CurVolume = ReagentUnit.Zero,
- MaxVolume = ReagentUnit.Zero
- };
- }
-
- public override void HandleMessage(EuiMessageBase msg)
- {
- switch (msg)
- {
- case AdminAddReagentEuiMsg.Close:
- Close();
- break;
- case AdminAddReagentEuiMsg.DoAdd doAdd:
- // Double check that user wasn't de-adminned in the mean time...
- // Or the target was deleted.
- if (!_adminManager.HasAdminFlag(Player, ReqFlags) || _target.Deleted)
- {
- Close();
- return;
- }
-
- var id = doAdd.ReagentId;
- var amount = doAdd.Amount;
- var solutionsSys = EntitySystem.Get();
-
- if (_target.TryGetComponent(out InjectableSolutionComponent? injectable)
- && solutionsSys.TryGetSolution(_target, injectable.Name, out var targetSolution))
- {
- var solution = new Solution(id, amount);
- solutionsSys.Inject(_target.Uid, targetSolution, solution);
- }
- else
- {
- //TODO decide how to find the solution
- if (solutionsSys.TryGetSolution(_target, "default", out var solution))
- {
- solutionsSys.TryAddReagent(_target.Uid,solution, id, amount, out _);
- }
- }
-
- StateDirty();
-
- if (doAdd.CloseAfter)
- Close();
-
- break;
- }
- }
- }
- }
-}
diff --git a/Content.Server/Administration/Verbs/DeleteVerb.cs b/Content.Server/Administration/Verbs/DeleteVerb.cs
deleted file mode 100644
index 262347fdda..0000000000
--- a/Content.Server/Administration/Verbs/DeleteVerb.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Content.Shared.Verbs;
-using Robust.Server.Console;
-using Robust.Server.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Administration.Verbs
-{
- [GlobalVerb]
- public class DeleteVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- var groupController = IoCManager.Resolve();
-
- if (!user.TryGetComponent(out ActorComponent? actor))
- {
- return;
- }
-
- if (!groupController.CanCommand(actor.PlayerSession, "deleteentity"))
- {
- return;
- }
-
- data.Text = Loc.GetString("delete-verb-get-data-text");
- data.CategoryData = VerbCategories.Debug;
- data.Visibility = VerbVisibility.Visible;
- data.IconTexture = "/Textures/Interface/VerbIcons/delete.svg.192dpi.png";
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- var groupController = IoCManager.Resolve();
-
- if (!user.TryGetComponent(out ActorComponent? actor))
- {
- return;
- }
-
- if (!groupController.CanCommand(actor.PlayerSession, "deleteentity"))
- {
- return;
- }
-
- target.Delete();
- }
- }
-}
diff --git a/Content.Server/Alert/Click/StopBeingPulled.cs b/Content.Server/Alert/Click/StopBeingPulled.cs
index 2b70e7e028..50d591cd30 100644
--- a/Content.Server/Alert/Click/StopBeingPulled.cs
+++ b/Content.Server/Alert/Click/StopBeingPulled.cs
@@ -16,10 +16,9 @@ namespace Content.Server.Alert.Click
{
public void AlertClicked(ClickAlertEventArgs args)
{
- var ps = EntitySystem.Get();
if (args.Player.TryGetComponent(out var playerPullable))
{
- ps.TryStopPull(playerPullable);
+ EntitySystem.Get().TryStopPull(playerPullable);
}
}
}
diff --git a/Content.Server/Alert/Click/StopPulling.cs b/Content.Server/Alert/Click/StopPulling.cs
index 37c68a742a..1e098733b4 100644
--- a/Content.Server/Alert/Click/StopPulling.cs
+++ b/Content.Server/Alert/Click/StopPulling.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Alert;
+using Content.Shared.Alert;
using Content.Shared.Pulling;
using Content.Shared.Pulling.Components;
using JetBrains.Annotations;
diff --git a/Content.Server/Atmos/Components/GasTankComponent.cs b/Content.Server/Atmos/Components/GasTankComponent.cs
index fcd1c0d46c..e0f0c6780d 100644
--- a/Content.Server/Atmos/Components/GasTankComponent.cs
+++ b/Content.Server/Atmos/Components/GasTankComponent.cs
@@ -16,7 +16,6 @@ using Content.Shared.DragDrop;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Sound;
-using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Player;
@@ -320,37 +319,6 @@ namespace Content.Server.Atmos.Components
{
DisconnectFromInternals(eventArgs.User);
}
-
- ///
- /// Open interaction window
- ///
- [Verb]
- private sealed class ControlVerb : Verb
- {
- public override bool RequireInteractionRange => true;
-
- protected override void GetData(IEntity user, GasTankComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
- if (!user.HasComponent())
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("control-verb-open-control-panel-text");
- }
-
- protected override void Activate(IEntity user, GasTankComponent component)
- {
- if (!user.TryGetComponent(out var actor))
- {
- return;
- }
-
- component.OpenInterface(actor.PlayerSession);
- }
- }
}
[UsedImplicitly]
diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
index 7c8cb0bfc8..3080367c8a 100644
--- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
+++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
@@ -1,7 +1,10 @@
using Content.Server.Atmos.Components;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
+using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
+using Robust.Shared.Localization;
namespace Content.Server.Atmos.EntitySystems
{
@@ -13,6 +16,24 @@ namespace Content.Server.Atmos.EntitySystems
private const float TimerDelay = 0.5f;
private float _timer = 0f;
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(AddOpenUIVerb);
+ }
+
+ private void AddOpenUIVerb(EntityUid uid, GasTankComponent component, GetActivationVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.User.TryGetComponent(out var actor))
+ return;
+
+ Verb verb = new();
+ verb.Act = () => component.OpenInterface(actor.PlayerSession);
+ verb.Text = Loc.GetString("control-verb-open-control-panel-text");
+ // TODO VERBS add "open UI" icon?
+ args.Verbs.Add(verb);
+ }
+
public override void Update(float frameTime)
{
base.Update(frameTime);
diff --git a/Content.Server/Body/Commands/AttachBodyPartCommand.cs b/Content.Server/Body/Commands/AttachBodyPartCommand.cs
index ba7e35c9d8..494c4bcfc2 100644
--- a/Content.Server/Body/Commands/AttachBodyPartCommand.cs
+++ b/Content.Server/Body/Commands/AttachBodyPartCommand.cs
@@ -99,7 +99,7 @@ namespace Content.Server.Body.Commands
return;
}
- body.SetPart($"{nameof(AttachBodyPartVerb)}-{partEntity.Uid}", part);
+ body.SetPart($"AttachBodyPartVerb-{partEntity.Uid}", part);
}
}
}
diff --git a/Content.Server/Body/Part/BodyPartComponent.cs b/Content.Server/Body/Part/BodyPartComponent.cs
index 63b09f2138..942bcc70e9 100644
--- a/Content.Server/Body/Part/BodyPartComponent.cs
+++ b/Content.Server/Body/Part/BodyPartComponent.cs
@@ -218,54 +218,5 @@ namespace Content.Server.Body.Part
break;
}
}
-
- [Verb]
- public class AttachBodyPartVerb : Verb
- {
- protected override void GetData(IEntity user, BodyPartComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (user == component.Owner)
- {
- return;
- }
-
- if (!user.TryGetComponent(out ActorComponent? actor))
- {
- return;
- }
-
- var groupController = IoCManager.Resolve();
-
- if (!groupController.CanCommand(actor.PlayerSession, "attachbodypart"))
- {
- return;
- }
-
- if (!user.TryGetComponent(out SharedBodyComponent? body))
- {
- return;
- }
-
- if (body.HasPart(component))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("attach-bodypart-verb-get-data-text");
- }
-
- protected override void Activate(IEntity user, BodyPartComponent component)
- {
- if (!user.TryGetComponent(out SharedBodyComponent? body))
- {
- return;
- }
-
- body.SetPart($"{nameof(AttachBodyPartVerb)}-{component.Owner.Uid}", component);
- }
- }
}
}
diff --git a/Content.Server/Buckle/Components/BuckleComponent.cs b/Content.Server/Buckle/Components/BuckleComponent.cs
index fe41940f2d..fc30eb1892 100644
--- a/Content.Server/Buckle/Components/BuckleComponent.cs
+++ b/Content.Server/Buckle/Components/BuckleComponent.cs
@@ -10,8 +10,9 @@ using Content.Shared.Buckle.Components;
using Content.Shared.Interaction.Helpers;
using Content.Shared.MobState.Components;
using Content.Shared.Popups;
+using Content.Shared.Pulling;
+using Content.Shared.Pulling.Components;
using Content.Shared.Standing;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
@@ -150,7 +151,7 @@ namespace Content.Server.Buckle.Components
}
}
- private bool CanBuckle(IEntity? user, IEntity to, [NotNullWhen(true)] out StrapComponent? strap)
+ public bool CanBuckle(IEntity? user, IEntity to, [NotNullWhen(true)] out StrapComponent? strap)
{
strap = null;
@@ -264,7 +265,7 @@ namespace Content.Server.Buckle.Components
SendMessage(new BuckleMessage(Owner, to));
- if (Owner.TryGetComponent(out PullableComponent? ownerPullable))
+ if (Owner.TryGetComponent(out SharedPullableComponent? ownerPullable))
{
if (ownerPullable.Puller != null)
{
@@ -272,7 +273,7 @@ namespace Content.Server.Buckle.Components
}
}
- if (to.TryGetComponent(out PullableComponent? toPullable))
+ if (to.TryGetComponent(out SharedPullableComponent? toPullable))
{
if (toPullable.Puller == Owner)
{
@@ -427,29 +428,5 @@ namespace Content.Server.Buckle.Components
IsOnStrapEntityThisFrame = false;
}
-
- ///
- /// Allows the unbuckling of the owning entity through a verb if
- /// anyone right clicks them.
- ///
- [Verb]
- private sealed class BuckleVerb : Verb
- {
- protected override void GetData(IEntity user, BuckleComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) || !component.Buckled)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("buckle-verb-unbuckle");
- }
-
- protected override void Activate(IEntity user, BuckleComponent component)
- {
- component.TryUnbuckle(user);
- }
- }
}
}
diff --git a/Content.Server/Buckle/Components/StrapComponent.cs b/Content.Server/Buckle/Components/StrapComponent.cs
index cf7622ea67..a94f1d6f28 100644
--- a/Content.Server/Buckle/Components/StrapComponent.cs
+++ b/Content.Server/Buckle/Components/StrapComponent.cs
@@ -6,13 +6,9 @@ using Content.Shared.Alert;
using Content.Shared.Buckle.Components;
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Interaction.Helpers;
using Content.Shared.Sound;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -173,53 +169,6 @@ namespace Content.Server.Buckle.Components
return buckle.ToggleBuckle(eventArgs.User, Owner);
}
- [Verb]
- private sealed class StrapVerb : Verb
- {
- protected override void GetData(IEntity user, StrapComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (!EntitySystem.Get().CanInteract(component.Owner) ||
- !user.TryGetComponent(out var buckle) ||
- buckle.BuckledTo != null && buckle.BuckledTo != component ||
- user == component.Owner)
- {
- return;
- }
-
- var parent = component.Owner.Transform.Parent;
- while (parent != null)
- {
- if (parent == user.Transform)
- {
- return;
- }
-
- parent = parent.Parent;
- }
-
- if (!user.InRangeUnobstructed(component, buckle.Range))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.IconTexture = buckle.BuckledTo == null ? "/Textures/Interface/VerbIcons/buckle.svg.192dpi.png" : "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png";
- data.Text = Loc.GetString(buckle.BuckledTo == null ? "strap-verb-get-data-text-buckle" : "strap-verb-get-data-text-unbuckle");
- }
-
- protected override void Activate(IEntity user, StrapComponent component)
- {
- if (!user.TryGetComponent(out var buckle))
- {
- return;
- }
-
- buckle.ToggleBuckle(user, component.Owner);
- }
- }
-
public override bool DragDropOn(DragDropEvent eventArgs)
{
if (!eventArgs.Dragged.TryGetComponent(out BuckleComponent? buckleComponent)) return false;
diff --git a/Content.Server/Buckle/BuckleSystem.cs b/Content.Server/Buckle/Systems/BuckleSystem.cs
similarity index 81%
rename from Content.Server/Buckle/BuckleSystem.cs
rename to Content.Server/Buckle/Systems/BuckleSystem.cs
index a9a32a4d7a..7e2ccac8ee 100644
--- a/Content.Server/Buckle/BuckleSystem.cs
+++ b/Content.Server/Buckle/Systems/BuckleSystem.cs
@@ -2,12 +2,14 @@ using Content.Server.Buckle.Components;
using Content.Server.Interaction;
using Content.Shared.Buckle;
using Content.Shared.Interaction;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
-namespace Content.Server.Buckle
+namespace Content.Server.Buckle.Systems
{
[UsedImplicitly]
internal sealed class BuckleSystem : SharedBuckleSystem
@@ -30,6 +32,27 @@ namespace Content.Server.Buckle
SubscribeLocalEvent(ContainerModifiedStrap);
SubscribeLocalEvent(HandleInteractHand);
+
+ SubscribeLocalEvent(AddUnbuckleVerb);
+ }
+
+ private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetInteractionVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract || !component.Buckled)
+ return;
+
+ Verb verb = new();
+ verb.Act = () => component.TryUnbuckle(args.User);
+ verb.Category = VerbCategory.Unbuckle;
+
+ if (args.Target == args.User && args.Using == null)
+ {
+ // A user is left clicking themselves with an empty hand, while buckled.
+ // It is very likely they are trying to unbuckle themselves.
+ verb.Priority = 1;
+ }
+
+ args.Verbs.Add(verb);
}
private void HandleInteractHand(EntityUid uid, BuckleComponent component, InteractHandEvent args)
diff --git a/Content.Server/Buckle/Systems/StrapSystem.cs b/Content.Server/Buckle/Systems/StrapSystem.cs
new file mode 100644
index 0000000000..1a4206ccf6
--- /dev/null
+++ b/Content.Server/Buckle/Systems/StrapSystem.cs
@@ -0,0 +1,101 @@
+using Content.Server.Buckle.Components;
+using Content.Server.Interaction;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction.Helpers;
+using Content.Shared.Verbs;
+using JetBrains.Annotations;
+using Robust.Server.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using System.Collections.Generic;
+
+namespace Content.Server.Buckle.Systems
+{
+ [UsedImplicitly]
+ internal sealed class StrapSystem : EntitySystem
+ {
+ [Dependency] InteractionSystem _interactionSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(AddStrapVerbs);
+ }
+
+ // TODO ECS BUCKLE/STRAP These 'Strap' verbs are an incestuous mess of buckle component and strap component
+ // functions. Whenever these are fully ECSed, maybe do it in a way that allows for these verbs to be handled in
+ // a sensible manner in a single system?
+
+ private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Hands == null || !args.CanAccess || !args.CanInteract)
+ return;
+
+ // Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this
+ // range can be set per-component, so we have to check a modified InRangeUnobstructed for every verb.
+
+ // Add unstrap verbs for every strapped entity.
+ foreach (var entity in component.BuckledEntities)
+ {
+ var buckledComp = entity.GetComponent();
+
+ if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range))
+ continue;
+
+ Verb verb = new();
+ verb.Act = () => buckledComp.TryUnbuckle(args.User);
+ verb.Category = VerbCategory.Unbuckle;
+ if (entity == args.User)
+ verb.Text = Loc.GetString("verb-self-target-pronoun");
+ else
+ verb.Text = entity.Name;
+
+ // In the event that you have more than once entity with the same name strapped to the same object,
+ // these two verbs will be identical according to Verb.CompareTo, and only one with actually be added to
+ // the verb list. However this should rarely ever be a problem. If it ever is, it could be fixed by
+ // appending an integer to verb.Text to distinguish the verbs.
+
+ args.Verbs.Add(verb);
+ }
+
+ // Add a verb to buckle the user.
+ if (args.User.TryGetComponent(out var buckle) &&
+ buckle.BuckledTo != component &&
+ args.User != component.Owner &&
+ component.HasSpace(buckle) &&
+ _interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckle.Range))
+ {
+ Verb verb = new();
+ verb.Act = () => buckle.TryBuckle(args.User, args.Target);
+ verb.Category = VerbCategory.Buckle;
+ verb.Text = Loc.GetString("verb-self-target-pronoun");
+ args.Verbs.Add(verb);
+ }
+
+ // If the user is currently holding/pulling an entity that can be buckled, add a verb for that.
+ if (args.Using != null &&
+ args.Using.TryGetComponent(out var usingBuckle) &&
+ component.HasSpace(usingBuckle) &&
+ _interactionSystem.InRangeUnobstructed(args.Using, args.Target, range: usingBuckle.Range))
+ {
+ // Check that the entity is unobstructed from the target (ignoring the user).
+ bool Ignored(IEntity entity) => entity == args.User || entity == args.Target || entity == args.Using;
+ if (!_interactionSystem.InRangeUnobstructed(args.Using, args.Target, usingBuckle.Range, predicate: Ignored))
+ return;
+
+ Verb verb = new();
+ verb.Act = () => usingBuckle.TryBuckle(args.User, args.Target);
+ verb.Category = VerbCategory.Buckle;
+ verb.Text = args.Using.Name;
+
+ // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
+ // just a held object, the user is probably just trying to sit down.
+ verb.Priority = args.Using.HasComponent() ? 1 : -1;
+
+ args.Verbs.Add(verb);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Cabinet/ItemCabinetComponent.cs b/Content.Server/Cabinet/ItemCabinetComponent.cs
index 3490dcbca0..fca1c34c50 100644
--- a/Content.Server/Cabinet/ItemCabinetComponent.cs
+++ b/Content.Server/Cabinet/ItemCabinetComponent.cs
@@ -1,11 +1,7 @@
-using Content.Shared.ActionBlocker;
-using Content.Shared.Interaction.Events;
using Content.Shared.Sound;
-using Content.Shared.Verbs;
using Content.Shared.Whitelist;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -50,52 +46,5 @@ namespace Content.Server.Cabinet
[ViewVariables]
[DataField("opened")]
public bool Opened { get; set; } = false;
-
- [Verb]
- public sealed class EjectItemFromCabinetVerb : Verb
- {
- protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
- {
- if (component.ItemContainer.ContainedEntity == null || !component.Opened || !EntitySystem.Get().CanInteract(user))
- data.Visibility = VerbVisibility.Invisible;
- else
- {
- data.Text = Loc.GetString("comp-item-cabinet-eject-verb-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- data.Visibility = VerbVisibility.Visible;
- }
- }
-
- protected override void Activate(IEntity user, ItemCabinetComponent component)
- {
- component.Owner.EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new TryEjectItemCabinetEvent(user), false);
- }
- }
-
- [Verb]
- public sealed class ToggleItemCabinetVerb : Verb
- {
- // Unlike lockers, you cannot open/close cabinets by clicking on them, as this usually removes their item
- // instead. So open/close is the alt-interact verb
-
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- data.Visibility = VerbVisibility.Invisible;
- else
- {
- data.Text = Loc.GetString(component.Opened ? "comp-item-cabinet-close-verb-text" : "comp-item-cabinet-open-verb-text");
- data.IconTexture = component.Opened ? "/Textures/Interface/VerbIcons/close.svg.192dpi.png" : "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
- data.Visibility = VerbVisibility.Visible;
- }
- }
-
- protected override void Activate(IEntity user, ItemCabinetComponent component)
- {
- component.Owner.EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new ToggleItemCabinetEvent(), false);
- }
- }
}
}
diff --git a/Content.Server/Cabinet/ItemCabinetSystem.cs b/Content.Server/Cabinet/ItemCabinetSystem.cs
index bc7b031e4d..47471633f1 100644
--- a/Content.Server/Cabinet/ItemCabinetSystem.cs
+++ b/Content.Server/Cabinet/ItemCabinetSystem.cs
@@ -1,12 +1,16 @@
using Content.Server.Hands.Components;
using Content.Server.Items;
+using Content.Shared.ActionBlocker;
using Content.Shared.Audio;
using Content.Shared.Cabinet;
+using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
+using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
@@ -14,6 +18,8 @@ namespace Content.Server.Cabinet
{
public class ItemCabinetSystem : EntitySystem
{
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+
public override void Initialize()
{
base.Initialize();
@@ -27,6 +33,70 @@ namespace Content.Server.Cabinet
SubscribeLocalEvent(OnTryEjectItemCabinet);
SubscribeLocalEvent(OnTryInsertItemCabinet);
SubscribeLocalEvent(OnToggleItemCabinet);
+
+ SubscribeLocalEvent(AddEjectInsertVerbs);
+ SubscribeLocalEvent(AddToggleOpenVerb);
+ }
+
+ private void AddToggleOpenVerb(EntityUid uid, ItemCabinetComponent component, GetActivationVerbsEvent args)
+ {
+ if (args.Hands == null || !args.CanAccess || !args.CanInteract)
+ return;
+
+ // Toggle open verb
+ Verb toggleVerb = new();
+ toggleVerb.Act = () => OnToggleItemCabinet(uid, component);
+ if (component.Opened)
+ {
+ toggleVerb.Text = Loc.GetString("verb-categories-close");
+ toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png";
+ }
+ else
+ {
+ toggleVerb.Text = Loc.GetString("verb-categories-open");
+ toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
+ }
+ args.Verbs.Add(toggleVerb);
+ }
+
+ private void AddEjectInsertVerbs(EntityUid uid, ItemCabinetComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Hands == null || !args.CanAccess || !args.CanInteract)
+ return;
+
+ // "Eject" item verb
+ if (component.Opened &&
+ component.ItemContainer.ContainedEntity != null &&
+ _actionBlockerSystem.CanPickup(args.User))
+ {
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ TakeItem(component, args.Hands, component.ItemContainer.ContainedEntity, args.User);
+ UpdateVisuals(component);
+ };
+ verb.Text = Loc.GetString("pick-up-verb-get-data-text");
+ verb.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
+ args.Verbs.Add(verb);
+ }
+
+ // Insert item verb
+ if (component.Opened &&
+ args.Using != null &&
+ _actionBlockerSystem.CanDrop(args.User) &&
+ (component.Whitelist?.IsValid(args.Using) ?? true) &&
+ component.ItemContainer.CanInsert(args.Using))
+ {
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ args.Hands.TryPutEntityIntoContainer(args.Using, component.ItemContainer);
+ UpdateVisuals(component);
+ };
+ verb.Category = VerbCategory.Insert;
+ verb.Text = args.Using.Name;
+ args.Verbs.Add(verb);
+ }
}
private void OnMapInitialize(EntityUid uid, ItemCabinetComponent comp, MapInitEvent args)
@@ -83,7 +153,7 @@ namespace Content.Server.Cabinet
///
/// Toggles the ItemCabinet's state.
///
- private void OnToggleItemCabinet(EntityUid uid, ItemCabinetComponent comp, ToggleItemCabinetEvent args)
+ private void OnToggleItemCabinet(EntityUid uid, ItemCabinetComponent comp, ToggleItemCabinetEvent? args = null)
{
comp.Opened = !comp.Opened;
ClickLatchSound(comp);
@@ -115,17 +185,10 @@ namespace Content.Server.Cabinet
{
if (comp.ItemContainer.ContainedEntity == null || args.Cancelled)
return;
- if (args.User.TryGetComponent(out HandsComponent? hands))
+ if (args.User.TryGetComponent(out SharedHandsComponent? hands))
{
-
- if (comp.ItemContainer.ContainedEntity.TryGetComponent(out var item))
- {
- comp.Owner.PopupMessage(args.User,
- Loc.GetString("comp-item-cabinet-successfully-taken",
- ("item", comp.ItemContainer.ContainedEntity),
- ("cabinet", comp.Owner)));
- hands.PutInHandOrDrop(item);
- }
+ // Put into hands
+ TakeItem(comp, hands, comp.ItemContainer.ContainedEntity, args.User);
}
else if (comp.ItemContainer.Remove(comp.ItemContainer.ContainedEntity))
{
@@ -134,6 +197,24 @@ namespace Content.Server.Cabinet
UpdateVisuals(comp);
}
+ ///
+ /// Tries to eject the ItemCabinet's item, either into the user's hands. Used by both and the eject verbs.
+ ///
+ private static void TakeItem(ItemCabinetComponent comp, SharedHandsComponent hands, IEntity containedEntity, IEntity user)
+ {
+ if (containedEntity.HasComponent())
+ {
+ if (!hands.TryPutInActiveHandOrAny(containedEntity))
+ containedEntity.Transform.Coordinates = hands.Owner.Transform.Coordinates;
+
+ comp.Owner.PopupMessage(user,
+ Loc.GetString("comp-item-cabinet-successfully-taken",
+ ("item", containedEntity),
+ ("cabinet", comp.Owner)));
+ }
+ }
+
private static void UpdateVisuals(ItemCabinetComponent comp)
{
if (comp.Owner.TryGetComponent(out SharedAppearanceComponent? appearance))
diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
index 5dd67f76d3..8ae20e0ec1 100644
--- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs
+++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
@@ -14,7 +14,6 @@ using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
using Content.Shared.Sound;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
@@ -38,10 +37,10 @@ namespace Content.Server.Chemistry.Components
public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing
{
[ViewVariables]
- private ContainerSlot _beakerContainer = default!;
+ public ContainerSlot BeakerContainer = default!;
[ViewVariables]
- private bool HasBeaker => _beakerContainer.ContainedEntity != null;
+ public bool HasBeaker => BeakerContainer.ContainedEntity != null;
[ViewVariables]
private bool _bufferModeTransfer = true;
@@ -74,7 +73,7 @@ namespace Content.Server.Chemistry.Components
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
}
- _beakerContainer =
+ BeakerContainer =
ContainerHelpers.EnsureContainer(Owner, $"{Name}-reagentContainerContainer");
_bufferSolution = EntitySystem.Get().EnsureSolution(Owner, SolutionName);
@@ -177,7 +176,7 @@ namespace Content.Server.Chemistry.Components
/// Returns a
private ChemMasterBoundUserInterfaceState GetUserInterfaceState()
{
- var beaker = _beakerContainer.ContainedEntity;
+ var beaker = BeakerContainer.ContainedEntity;
EntitySystem.Get().TryGetSolution(beaker, SolutionName, out var beakerSolution);
// TODO this is just a guess
if (beaker == null || beakerSolution == null)
@@ -204,17 +203,17 @@ namespace Content.Server.Chemistry.Components
/// If this component contains an entity with a , eject it.
/// Tries to eject into user's hands first, then ejects onto chem master if both hands are full.
///
- private void TryEject(IEntity user)
+ public void TryEject(IEntity user)
{
if (!HasBeaker)
return;
- var beaker = _beakerContainer.ContainedEntity;
+ var beaker = BeakerContainer.ContainedEntity;
if (beaker is null)
return;
- _beakerContainer.Remove(beaker);
+ BeakerContainer.Remove(beaker);
UpdateUserInterface();
if (!user.TryGetComponent(out var hands) ||
@@ -227,7 +226,7 @@ namespace Content.Server.Chemistry.Components
private void TransferReagent(string id, ReagentUnit amount, bool isBuffer)
{
if (!HasBeaker && _bufferModeTransfer) return;
- var beaker = _beakerContainer.ContainedEntity;
+ var beaker = BeakerContainer.ContainedEntity;
if (beaker is null)
return;
@@ -428,7 +427,7 @@ namespace Content.Server.Chemistry.Components
}
else
{
- _beakerContainer.Insert(activeHandEntity);
+ BeakerContainer.Insert(activeHandEntity);
UpdateUserInterface();
}
}
@@ -445,29 +444,5 @@ namespace Content.Server.Chemistry.Components
{
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
}
-
- [Verb]
- public sealed class EjectBeakerVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, ChemMasterComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("eject-beaker-verb-get-data-text");
- data.Visibility = component.HasBeaker ? VerbVisibility.Visible : VerbVisibility.Invisible;
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, ChemMasterComponent component)
- {
- component.TryEject(user);
- }
- }
}
}
diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
index ccb037c4a8..9d338e7132 100644
--- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
+++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
@@ -15,7 +15,6 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Sound;
-using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -46,13 +45,13 @@ namespace Content.Server.Chemistry.Components
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [ViewVariables] private ContainerSlot _beakerContainer = default!;
+ [ViewVariables] public ContainerSlot BeakerContainer = default!;
[ViewVariables] [DataField("pack")] private string _packPrototypeId = "";
[DataField("clickSound")]
private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
- [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
+ [ViewVariables] public bool HasBeaker => BeakerContainer.ContainedEntity != null;
[ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10);
[UsedImplicitly]
@@ -84,7 +83,7 @@ namespace Content.Server.Chemistry.Components
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
}
- _beakerContainer =
+ BeakerContainer =
ContainerHelpers.EnsureContainer(Owner, $"{Name}-reagentContainerContainer");
InitializeFromPrototype();
@@ -228,7 +227,7 @@ namespace Content.Server.Chemistry.Components
/// Returns a
private ReagentDispenserBoundUserInterfaceState GetUserInterfaceState()
{
- var beaker = _beakerContainer.ContainedEntity;
+ var beaker = BeakerContainer.ContainedEntity;
if (beaker == null || !beaker.TryGetComponent(out FitsInDispenserComponent? fits) ||
!EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var solution))
{
@@ -252,16 +251,16 @@ namespace Content.Server.Chemistry.Components
/// If this component contains an entity with a , eject it.
/// Tries to eject into user's hands first, then ejects onto dispenser if both hands are full.
///
- private void TryEject(IEntity user)
+ public void TryEject(IEntity user)
{
if (!HasBeaker)
return;
- var beaker = _beakerContainer.ContainedEntity;
+ var beaker = BeakerContainer.ContainedEntity;
if (beaker is null)
return;
- _beakerContainer.Remove(beaker);
+ BeakerContainer.Remove(beaker);
UpdateUserInterface();
if (!user.TryGetComponent(out var hands) ||
@@ -276,12 +275,12 @@ namespace Content.Server.Chemistry.Components
///
private void TryClear()
{
- if (!HasBeaker || !_beakerContainer.ContainedEntity!.TryGetComponent(out FitsInDispenserComponent? fits) ||
+ if (!HasBeaker || !BeakerContainer.ContainedEntity!.TryGetComponent(out FitsInDispenserComponent? fits) ||
!EntitySystem.Get()
- .TryGetSolution(_beakerContainer.ContainedEntity, fits.Solution, out var solution))
+ .TryGetSolution(BeakerContainer.ContainedEntity, fits.Solution, out var solution))
return;
- EntitySystem.Get().RemoveAllSolution(_beakerContainer.ContainedEntity!.Uid, solution);
+ EntitySystem.Get().RemoveAllSolution(BeakerContainer.ContainedEntity!.Uid, solution);
UpdateUserInterface();
}
@@ -294,12 +293,12 @@ namespace Content.Server.Chemistry.Components
{
if (!HasBeaker) return;
- if (_beakerContainer.ContainedEntity is not {} contained || !contained.TryGetComponent(out FitsInDispenserComponent? fits)
+ if (BeakerContainer.ContainedEntity is not {} contained || !contained.TryGetComponent(out FitsInDispenserComponent? fits)
|| !EntitySystem.Get()
- .TryGetSolution(_beakerContainer.ContainedEntity, fits.Solution, out var solution)) return;
+ .TryGetSolution(BeakerContainer.ContainedEntity, fits.Solution, out var solution)) return;
EntitySystem.Get()
- .TryAddReagent(_beakerContainer.ContainedEntity.Uid, solution, Inventory[dispenseIndex].ID, _dispenseAmount, out _);
+ .TryAddReagent(BeakerContainer.ContainedEntity.Uid, solution, Inventory[dispenseIndex].ID, _dispenseAmount, out _);
UpdateUserInterface();
}
@@ -360,7 +359,7 @@ namespace Content.Server.Chemistry.Components
return false;
}
- _beakerContainer.Insert(activeHandEntity);
+ BeakerContainer.Insert(activeHandEntity);
UpdateUserInterface();
return true;
@@ -378,30 +377,6 @@ namespace Content.Server.Chemistry.Components
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
}
- [Verb]
- public sealed class EjectBeakerVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, ReagentDispenserComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("eject-beaker-verb-get-data-text");
- data.Visibility = component.HasBeaker ? VerbVisibility.Visible : VerbVisibility.Invisible;
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, ReagentDispenserComponent component)
- {
- component.TryEject(user);
- }
- }
-
private class ReagentInventoryComparer : Comparer
{
public override int Compare(ReagentDispenserInventoryEntry x, ReagentDispenserInventoryEntry y)
diff --git a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs
index 52fef40479..1bbb255227 100644
--- a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs
+++ b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs
@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using Content.Server.UserInterface;
-using Content.Shared.ActionBlocker;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
@@ -10,7 +9,6 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
@@ -53,21 +51,6 @@ namespace Content.Server.Chemistry.Components
[ViewVariables(VVAccess.ReadWrite)]
public ReagentUnit MaximumTransferAmount { get; set; } = ReagentUnit.New(50);
- ///
- /// Subjectively, which transfer amount would be best for most activities given the maximum
- /// transfer amount.
- ///
- public ReagentUnit SubjectiveBestTransferAmount() =>
- MaximumTransferAmount.Int() switch
- {
- <= 5 => ReagentUnit.New(1),
- (> 5) and (<= 25) => ReagentUnit.New(5),
- (> 25) and (<= 50) => ReagentUnit.New(10),
- (> 50) and (<= 100) => ReagentUnit.New(20),
- (> 100) and (<= 500) => ReagentUnit.New(50),
- (> 500) => ReagentUnit.New(100)
- };
-
///
/// Can this entity take reagent from reagent tanks?
///
@@ -89,7 +72,7 @@ namespace Content.Server.Chemistry.Components
[ViewVariables(VVAccess.ReadWrite)]
public bool CanChangeTransferAmount { get; set; } = false;
- [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(TransferAmountUiKey.Key);
+ [ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(TransferAmountUiKey.Key);
protected override void Initialize()
{
@@ -209,119 +192,5 @@ namespace Content.Server.Chemistry.Components
return actualAmount;
}
-
- // TODO refactor when dynamic verbs are a thing
-
- [Verb]
- public sealed class MinimumTransferVerb : Verb
- {
- protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) || !component.CanChangeTransferAmount)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("comp-solution-transfer-verb-transfer-amount-min",
- ("amount", component.MinimumTransferAmount.Int()));
- data.CategoryData = VerbCategories.SetTransferAmount;
- }
-
- protected override void Activate(IEntity user, SolutionTransferComponent component)
- {
- component.TransferAmount = component.MinimumTransferAmount;
- user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
- ("amount", component.TransferAmount.Int())));
- }
- }
-
- [Verb]
- public sealed class DefaultTransferVerb : Verb
- {
- protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) || !component.CanChangeTransferAmount)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- var amt = component.SubjectiveBestTransferAmount();
- if (amt > component.MinimumTransferAmount && amt < component.MaximumTransferAmount)
- {
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("comp-solution-transfer-verb-transfer-amount-ideal",
- ("amount", amt.Int()));
- data.CategoryData = VerbCategories.SetTransferAmount;
- }
- else
- {
- data.Visibility = VerbVisibility.Invisible;
- }
- }
-
- protected override void Activate(IEntity user, SolutionTransferComponent component)
- {
- component.TransferAmount = component.SubjectiveBestTransferAmount();
- user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
- ("amount", component.TransferAmount.Int())));
- }
- }
-
- [Verb]
- public sealed class MaximumTransferVerb : Verb
- {
- protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) || !component.CanChangeTransferAmount)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("comp-solution-transfer-verb-transfer-amount-max",
- ("amount", component.MaximumTransferAmount));
- data.CategoryData = VerbCategories.SetTransferAmount;
- }
-
- protected override void Activate(IEntity user, SolutionTransferComponent component)
- {
- component.TransferAmount = component.MaximumTransferAmount;
- user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
- ("amount", component.TransferAmount.Int())));
- }
- }
-
- [Verb]
- public sealed class CustomTransferVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) || !component.CanChangeTransferAmount)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("comp-solution-transfer-verb-transfer-amount-custom");
- data.CategoryData = VerbCategories.SetTransferAmount;
- }
-
- protected override void Activate(IEntity user, SolutionTransferComponent component)
- {
- if (!user.TryGetComponent(out var actor))
- {
- return;
- }
-
- component.UserInterface?.Open(actor.PlayerSession);
- }
- }
}
}
diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
index 40f8f35753..50fc5c250d 100644
--- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
@@ -1,18 +1,69 @@
-using Content.Server.Chemistry.Components;
+using Content.Shared.Verbs;
+using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Robust.Shared.IoC;
+using Content.Shared.ActionBlocker;
namespace Content.Server.Chemistry.EntitySystems
{
[UsedImplicitly]
public class ChemMasterSystem : EntitySystem
{
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnSolutionChange);
+ SubscribeLocalEvent(AddInsertVerb);
+ SubscribeLocalEvent(AddEjectVerb);
+ }
+
+ // TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system? Maybe using something like the
+ // system mentioned in #4538? The code here is basically identical to the stuff in ChemDispenserSystem
+ private void AddEjectVerb(EntityUid uid, ChemMasterComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (args.Hands == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ !component.HasBeaker ||
+ !_actionBlockerSystem.CanPickup(args.User))
+ return;
+
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ component.TryEject(args.User);
+ component.UpdateUserInterface();
+ };
+ verb.Category = VerbCategory.Eject;
+ verb.Text = component.BeakerContainer.ContainedEntity!.Name;
+ args.Verbs.Add(verb);
+ }
+
+ private void AddInsertVerb(EntityUid uid, ChemMasterComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Using == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ component.HasBeaker ||
+ !args.Using.HasComponent() ||
+ !_actionBlockerSystem.CanDrop(args.User))
+ return;
+
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ component.BeakerContainer.Insert(args.Using);
+ component.UpdateUserInterface();
+ };
+ verb.Category = VerbCategory.Insert;
+ verb.Text = args.Using.Name;
+ args.Verbs.Add(verb);
}
private void OnSolutionChange(EntityUid uid, ChemMasterComponent component,
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index bcec570e1d..9a90dfaf0c 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -1,20 +1,81 @@
-using Content.Server.Chemistry.Components;
+using Content.Shared.Verbs;
+using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.ActionBlocker;
namespace Content.Server.Chemistry.EntitySystems
{
[UsedImplicitly]
public class ReagentDispenserSystem : EntitySystem
{
+ [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnSolutionChange);
+
+ SubscribeLocalEvent(AddEjectVerb);
+ SubscribeLocalEvent(AddInsertVerb);
}
+ // TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system? Maybe using something like the
+ // system mentioned in #4538? The code here is basically identical to the stuff in ChemDispenserSystem.
+ private void AddEjectVerb(EntityUid uid, ReagentDispenserComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (args.Hands == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ !component.HasBeaker ||
+ !_actionBlockerSystem.CanPickup(args.User))
+ return;
+
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ component.TryEject(args.User);
+ component.UpdateUserInterface();
+ };
+ verb.Category = VerbCategory.Eject;
+ verb.Text = component.BeakerContainer.ContainedEntity!.Name;
+
+ args.Verbs.Add(verb);
+ }
+
+ private void AddInsertVerb(EntityUid uid, ReagentDispenserComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Using == null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ component.HasBeaker ||
+ !args.Using.HasComponent() ||
+ !_actionBlockerSystem.CanDrop(args.User))
+ return;
+
+ if (!args.Using.HasComponent() ||
+ !_solutionContainerSystem.TryGetSolution(args.Using.Uid, "beaker", out _))
+ {
+ return;
+ }
+
+ Verb verb = new();
+ verb.Act = () =>
+ {
+ component.BeakerContainer.Insert(args.Using);
+ component.UpdateUserInterface();
+ };
+ verb.Category = VerbCategory.Insert;
+ verb.Text = args.Using.Name;
+ args.Verbs.Add(verb);
+ }
+
+
private void OnSolutionChange(EntityUid uid, ReagentDispenserComponent component, SolutionChangedEvent args)
{
component.UpdateUserInterface();
diff --git a/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs
new file mode 100644
index 0000000000..315b297d34
--- /dev/null
+++ b/Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs
@@ -0,0 +1,68 @@
+using Content.Shared.Verbs;
+using Content.Server.Chemistry.Components;
+using JetBrains.Annotations;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
+using Robust.Server.GameObjects;
+using System.Collections.Generic;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Popups;
+
+namespace Content.Server.Chemistry.EntitySystems
+{
+ [UsedImplicitly]
+ public class SolutionTransferSystem : EntitySystem
+ {
+ ///
+ /// Default transfer amounts for the set-transfer verb.
+ ///
+ public static readonly List DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000};
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(AddSetTransferVerbs);
+ }
+
+ private void AddSetTransferVerbs(EntityUid uid, SolutionTransferComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount)
+ return;
+
+ if (!args.User.TryGetComponent(out var actor))
+ return;
+
+ // Custom transfer verb
+ Verb custom = new();
+ custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount");
+ custom.Category = VerbCategory.SetTransferAmount;
+ custom.Act = () => component.UserInterface?.Open(actor.PlayerSession);
+ custom.Priority = 1;
+ args.Verbs.Add(custom);
+
+ // Add specific transfer verbs according to the container's size
+ var priority = 0;
+ foreach (var amount in DefaultTransferAmounts)
+ {
+ if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
+ continue;
+
+ Verb verb = new();
+ verb.Text = Loc.GetString("comp-solution-transfer-verb-amount", ("amount", amount));
+ verb.Category = VerbCategory.SetTransferAmount;
+ verb.Act = () =>
+ {
+ component.TransferAmount = ReagentUnit.New(amount);
+ args.User.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)));
+ };
+
+ // we want to sort by size, not alphabetically by the verb text.
+ verb.Priority = priority;
+ priority--;
+
+ args.Verbs.Add(verb);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Climbing/ClimbSystem.cs b/Content.Server/Climbing/ClimbSystem.cs
index f10268b15c..e36de443c3 100644
--- a/Content.Server/Climbing/ClimbSystem.cs
+++ b/Content.Server/Climbing/ClimbSystem.cs
@@ -1,10 +1,14 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using Content.Server.Climbing.Components;
+using Content.Shared.ActionBlocker;
using Content.Shared.Climbing;
using Content.Shared.GameTicking;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
namespace Content.Server.Climbing
{
@@ -13,11 +17,32 @@ namespace Content.Server.Climbing
{
private readonly HashSet _activeClimbers = new();
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(Reset);
+ SubscribeLocalEvent(AddClimbVerb);
+ }
+
+ private void AddClimbVerb(EntityUid uid, ClimbableComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanMove(args.User))
+ return;
+
+ // Check that the user climb.
+ if (!args.User.TryGetComponent(out ClimbingComponent? climbingComponent) ||
+ climbingComponent.IsClimbing)
+ return;
+
+ // Add a climb verb
+ Verb verb = new();
+ verb.Act = () => component.TryClimb(args.User);
+ verb.Text = Loc.GetString("comp-climbable-verb-climb");
+ // TODO VERBS ICON add a climbing icon?
+ args.Verbs.Add(verb);
}
public void AddActiveClimber(ClimbingComponent climbingComponent)
diff --git a/Content.Server/Climbing/Components/ClimbableComponent.cs b/Content.Server/Climbing/Components/ClimbableComponent.cs
index 90f218f7b1..2891b38c76 100644
--- a/Content.Server/Climbing/Components/ClimbableComponent.cs
+++ b/Content.Server/Climbing/Components/ClimbableComponent.cs
@@ -190,7 +190,7 @@ namespace Content.Server.Climbing.Components
}
}
- private async void TryClimb(IEntity user)
+ public async void TryClimb(IEntity user)
{
if (!user.TryGetComponent(out ClimbingComponent? climbingComponent) || climbingComponent.IsClimbing)
return;
@@ -234,29 +234,5 @@ namespace Content.Server.Climbing.Components
user.PopupMessage(selfMessage);
}
}
-
- ///
- /// Allows you to vault an object with the ClimbableComponent through right click
- ///
- [Verb]
- private sealed class ClimbVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, ClimbableComponent component, VerbData data)
- {
- if (!component.CanVault(user, component.Owner, out var _))
- {
- data.Visibility = VerbVisibility.Invisible;
- }
-
- data.Text = Loc.GetString("comp-climbable-verb-climb");
- }
-
- protected override void Activate(IEntity user, ClimbableComponent component)
- {
- component.TryClimb(user);
- }
- }
}
}
diff --git a/Content.Server/Clothing/Components/MagbootsComponent.cs b/Content.Server/Clothing/Components/MagbootsComponent.cs
index decec5873e..1360b6fa41 100644
--- a/Content.Server/Clothing/Components/MagbootsComponent.cs
+++ b/Content.Server/Clothing/Components/MagbootsComponent.cs
@@ -118,26 +118,6 @@ namespace Content.Server.Clothing.Components
{
return new MagbootsComponentState(On);
}
-
- [UsedImplicitly]
- public sealed class ToggleMagbootsVerb : Verb
- {
- protected override void GetData(IEntity user, MagbootsComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("toggle-magboots-verb-get-data-text");
- }
-
- protected override void Activate(IEntity user, MagbootsComponent component)
- {
- component.Toggle(user);
- }
- }
}
[UsedImplicitly]
diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs
new file mode 100644
index 0000000000..cc005a29b1
--- /dev/null
+++ b/Content.Server/Clothing/MagbootsSystem.cs
@@ -0,0 +1,29 @@
+using Content.Server.Clothing.Components;
+using Content.Shared.Verbs;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
+
+namespace Content.Server.Clothing
+{
+ public sealed class MagbootsSystem : EntitySystem
+ {
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(AddToggleVerb);
+ }
+
+ private void AddToggleVerb(EntityUid uid, MagbootsComponent component, GetActivationVerbsEvent args)
+ {
+ if (args.User == null || !args.CanAccess || !args.CanInteract)
+ return;
+
+ Verb verb = new();
+ verb.Text = Loc.GetString("toggle-magboots-verb-get-data-text");
+ verb.Act = () => component.On = !component.On;
+ // TODO VERB ICON add toggle icon? maybe a computer on/off symbol?
+ args.Verbs.Add(verb);
+ }
+ }
+}
diff --git a/Content.Server/Configurable/ConfigurationComponent.cs b/Content.Server/Configurable/ConfigurationComponent.cs
index 0aeafeeabf..808d75e5ce 100644
--- a/Content.Server/Configurable/ConfigurationComponent.cs
+++ b/Content.Server/Configurable/ConfigurationComponent.cs
@@ -7,13 +7,8 @@ using Content.Server.UserInterface;
using Content.Shared.Configurable;
using Content.Shared.Interaction;
using Content.Shared.Tool;
-using Content.Shared.Verbs;
-using Robust.Server.Console;
using Robust.Server.GameObjects;
-using Robust.Server.Player;
using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -117,7 +112,7 @@ namespace Content.Server.Configurable
UserInterface?.SetState(new ConfigurationBoundUserInterfaceState(_config));
}
- private void OpenUserInterface(ActorComponent actor)
+ public void OpenUserInterface(ActorComponent actor)
{
UpdateUserInterface();
UserInterface?.Open(actor.PlayerSession);
@@ -130,31 +125,5 @@ namespace Content.Server.Configurable
configuration.Add(list[index], value);
}
}
-
- [Verb]
- public sealed class ConfigureVerb : Verb
- {
- protected override void GetData(IEntity user, ConfigurationComponent component, VerbData data)
- {
- var session = user.PlayerSession();
- var groupController = IoCManager.Resolve();
- if (session == null || !groupController.CanAdminMenu(session))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("configure-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, ConfigurationComponent component)
- {
- if (user.TryGetComponent(out ActorComponent? actor))
- {
- component.OpenUserInterface(actor);
- }
- }
- }
}
}
diff --git a/Content.Server/Construction/Components/AnchorableComponent.cs b/Content.Server/Construction/Components/AnchorableComponent.cs
index 3862bb39c7..2926635144 100644
--- a/Content.Server/Construction/Components/AnchorableComponent.cs
+++ b/Content.Server/Construction/Components/AnchorableComponent.cs
@@ -4,6 +4,7 @@ using Content.Server.Coordinates.Helpers;
using Content.Server.Pulling;
using Content.Server.Tools.Components;
using Content.Shared.Interaction;
+using Content.Shared.Pulling.Components;
using Content.Shared.Tool;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
@@ -75,7 +76,7 @@ namespace Content.Server.Construction.Components
var rot = Owner.Transform.LocalRotation;
Owner.Transform.LocalRotation = Math.Round(rot / (Math.PI / 2)) * (Math.PI / 2);
- if (Owner.TryGetComponent(out PullableComponent? pullableComponent))
+ if (Owner.TryGetComponent(out SharedPullableComponent? pullableComponent))
{
if (pullableComponent.Puller != null)
{
diff --git a/Content.Server/Construction/Components/ConstructionComponent.Verbs.cs b/Content.Server/Construction/Components/ConstructionComponent.Verbs.cs
deleted file mode 100644
index b7920cb832..0000000000
--- a/Content.Server/Construction/Components/ConstructionComponent.Verbs.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Content.Shared.ActionBlocker;
-using Content.Shared.Popups;
-using Content.Shared.Verbs;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Construction.Components
-{
- public partial class ConstructionComponent
- {
- [Verb]
- public sealed class DeconstructibleVerb : Verb
- {
- protected override void GetData(IEntity user, ConstructionComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- if (((component.Target != null) && (component.Target.Name == component.DeconstructionNodeIdentifier)) ||
- ((component.Node != null) && (component.Node.Name == component.DeconstructionNodeIdentifier)))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.CategoryData = VerbCategories.Construction;
- data.Text = Loc.GetString("deconstructible-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, ConstructionComponent component)
- {
- component.SetNewTarget(component.DeconstructionNodeIdentifier);
- if (component.Target == null)
- {
- // Maybe check, but on the flip-side a better solution might be to not make it undeconstructible in the first place, no?
- component.Owner.PopupMessage(user, Loc.GetString("deconstructible-verb-activate-no-target-text"));
- }
- else
- {
- component.Owner.PopupMessage(user, Loc.GetString("deconstructible-verb-activate-text"));
- }
- }
- }
- }
-}
diff --git a/Content.Server/Construction/ConstructionSystem.cs b/Content.Server/Construction/ConstructionSystem.cs
index e45f840309..2fa3757b7c 100644
--- a/Content.Server/Construction/ConstructionSystem.cs
+++ b/Content.Server/Construction/ConstructionSystem.cs
@@ -17,6 +17,7 @@ using Content.Shared.Coordinates;
using Content.Shared.Examine;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
@@ -50,9 +51,42 @@ namespace Content.Server.Construction
SubscribeNetworkEvent(HandleStartStructureConstruction);
SubscribeNetworkEvent(HandleStartItemConstruction);
+ SubscribeLocalEvent(AddDeconstructVerb);
SubscribeLocalEvent(HandleConstructionExamined);
}
+ private void AddDeconstructVerb(EntityUid uid, ConstructionComponent component, GetOtherVerbsEvent args)
+ {
+ if (!args.CanAccess)
+ return;
+
+ if (component.Target?.Name == component.DeconstructionNodeIdentifier ||
+ component.Node?.Name == component.DeconstructionNodeIdentifier)
+ return;
+
+ Verb verb = new();
+ //verb.Category = VerbCategories.Construction;
+ //TODO VERBS add more construction verbs? Until then, removing construction category
+ verb.Text = Loc.GetString("deconstructible-verb-begin-deconstruct");
+ verb.IconTexture = "/Textures/Interface/hammer_scaled.svg.192dpi.png";
+
+ verb.Act = () =>
+ {
+ component.SetNewTarget(component.DeconstructionNodeIdentifier);
+ if (component.Target == null)
+ {
+ // Maybe check, but on the flip-side a better solution might be to not make it undeconstructible in the first place, no?
+ component.Owner.PopupMessage(args.User, Loc.GetString("deconstructible-verb-activate-no-target-text"));
+ }
+ else
+ {
+ component.Owner.PopupMessage(args.User, Loc.GetString("deconstructible-verb-activate-text"));
+ }
+ };
+
+ args.Verbs.Add(verb);
+ }
+
private void HandleConstructionExamined(EntityUid uid, ConstructionComponent component, ExaminedEvent args)
{
if (component.Target != null)
@@ -101,7 +135,6 @@ namespace Content.Server.Construction
list[0].DoExamine(args);
}
-
}
private IEnumerable EnumerateNearby(IEntity user)
diff --git a/Content.Server/Containers/Commands/HideContainedContextCommand.cs b/Content.Server/Containers/Commands/HideContainedContextCommand.cs
deleted file mode 100644
index 8ec47ad4bf..0000000000
--- a/Content.Server/Containers/Commands/HideContainedContextCommand.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Content.Server.Administration;
-using Content.Server.Verbs;
-using Content.Shared.Administration;
-using Robust.Server.Player;
-using Robust.Shared.Console;
-using Robust.Shared.GameObjects;
-
-namespace Content.Server.Containers.Commands
-{
- [AdminCommand(AdminFlags.Debug)]
- public class HideContainedContextCommand : IConsoleCommand
- {
- public string Command => "hidecontainedcontext";
- public string Description => $"Reverts the effects of {ShowContainedContextCommand.CommandName}";
- public string Help => $"{Command}";
-
- public void Execute(IConsoleShell shell, string argStr, string[] args)
- {
- var player = shell.Player as IPlayerSession;
- if (player == null)
- {
- shell.WriteLine("You need to be a player to use this command.");
- return;
- }
-
- EntitySystem.Get().RemoveContainerVisibility(player);
- }
- }
-}
diff --git a/Content.Server/Cuffs/Components/CuffableComponent.cs b/Content.Server/Cuffs/Components/CuffableComponent.cs
index 90db2f9b76..1ab0564cc3 100644
--- a/Content.Server/Cuffs/Components/CuffableComponent.cs
+++ b/Content.Server/Cuffs/Components/CuffableComponent.cs
@@ -313,31 +313,5 @@ namespace Content.Server.Cuffs.Components
return;
}
-
- ///
- /// Allows the uncuffing of a cuffed person. Used by other people and by the component owner to break out of cuffs.
- ///
- [Verb]
- private sealed class UncuffVerb : Verb
- {
- protected override void GetData(IEntity user, CuffableComponent component, VerbData data)
- {
- if ((user != component.Owner && !EntitySystem.Get().CanInteract(user)) || component.CuffedHandCount == 0)
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("uncuff-verb-get-data-text");
- }
-
- protected override void Activate(IEntity user, CuffableComponent component)
- {
- if (component.CuffedHandCount > 0)
- {
- component.TryUncuff(user);
- }
- }
- }
}
}
diff --git a/Content.Server/Cuffs/CuffableSystem.cs b/Content.Server/Cuffs/CuffableSystem.cs
index dbbbe75efc..a031212903 100644
--- a/Content.Server/Cuffs/CuffableSystem.cs
+++ b/Content.Server/Cuffs/CuffableSystem.cs
@@ -2,13 +2,14 @@ using Content.Server.Cuffs.Components;
using Content.Server.Hands.Components;
using Content.Shared.Hands.Components;
using Content.Shared.ActionBlocker;
-using Content.Shared.MobState;
using Content.Shared.Cuffs;
using Content.Shared.Popups;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.IoC;
+using Content.Shared.MobState;
using Robust.Shared.Player;
namespace Content.Server.Cuffs
@@ -25,6 +26,27 @@ namespace Content.Server.Cuffs
SubscribeLocalEvent(OnHandCountChanged);
SubscribeLocalEvent(OnUncuffAttempt);
+
+ SubscribeLocalEvent(AddUncuffVerb);
+ }
+
+ private void AddUncuffVerb(EntityUid uid, CuffableComponent component, GetOtherVerbsEvent args)
+ {
+ // Can the user access the cuffs, and is there even anything to uncuff?
+ if (!args.CanAccess || component.CuffedHandCount == 0)
+ return;
+
+ // We only check can interact if the user is not uncuffing themselves. As a result, the verb will show up
+ // when the user is incapacitated & trying to uncuff themselves, but TryUncuff() will still fail when
+ // attempted.
+ if (args.User != args.Target && !args.CanInteract)
+ return;
+
+ Verb verb = new();
+ verb.Act = () => component.TryUncuff(args.User);
+ verb.Text = Loc.GetString("uncuff-verb-get-data-text");
+ //TODO VERB ICON add uncuffing symbol? may re-use the alert symbol showing that you are currently cuffed?
+ args.Verbs.Add(verb);
}
private void OnUncuffAttempt(UncuffAttemptEvent args)
@@ -43,6 +65,7 @@ namespace Content.Server.Cuffs
// This is because the CanInteract blocking of the cuffs prevents self-uncuff.
if (args.User == args.Target)
{
+ // This UncuffAttemptEvent check should probably be In MobStateSystem, not here?
if (userEntity.TryGetComponent(out var state))
{
// Manually check this.
@@ -54,6 +77,7 @@ namespace Content.Server.Cuffs
else
{
// Uh... let it go through???
+ // TODO CUFFABLE/STUN add UncuffAttemptEvent subscription to StunSystem
}
}
else
diff --git a/Content.Server/Damage/RejuvenateVerb.cs b/Content.Server/Damage/RejuvenateVerb.cs
deleted file mode 100644
index 0462f1af28..0000000000
--- a/Content.Server/Damage/RejuvenateVerb.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Nutrition.Components;
-using Content.Server.Nutrition.EntitySystems;
-using Content.Server.Stunnable.Components;
-using Content.Shared.Damage;
-using Content.Shared.MobState;
-using Content.Shared.Nutrition.Components;
-using Content.Shared.Verbs;
-using Robust.Server.Console;
-using Robust.Server.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Damage
-{
- ///
- /// Completely removes all damage from the DamageableComponent (heals the mob).
- ///
- [GlobalVerb]
- public class RejuvenateVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- data.Text = Loc.GetString("rejuvenate-verb-get-data-text");
- data.CategoryData = VerbCategories.Debug;
- data.Visibility = VerbVisibility.Invisible;
- data.IconTexture = "/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png";
-
- var groupController = IoCManager.Resolve();
-
- if (user.TryGetComponent(out var player))
- {
- if (!target.HasComponent() && !target.HasComponent() &&
- !target.HasComponent())
- {
- return;
- }
-
- if (groupController.CanCommand(player.PlayerSession, "rejuvenate"))
- {
- data.Visibility = VerbVisibility.Visible;
- }
- }
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- var groupController = IoCManager.Resolve();
- if (user.TryGetComponent(out var player))
- {
- if (groupController.CanCommand(player.PlayerSession, "rejuvenate"))
- PerformRejuvenate(target);
- }
- }
-
- public static void PerformRejuvenate(IEntity target)
- {
- if (target.TryGetComponent(out DamageableComponent? damageable))
- {
- EntitySystem.Get().SetAllDamage(damageable, 0);
- }
-
- if (target.TryGetComponent(out HungerComponent? hunger))
- {
- hunger.ResetFood();
- }
-
- if (target.TryGetComponent(out ThirstComponent? thirst))
- {
- thirst.ResetThirst();
- }
-
- if (target.TryGetComponent(out StunnableComponent? stun))
- {
- stun.ResetStuns();
- }
-
- if (target.TryGetComponent(out FlammableComponent? flammable))
- {
- EntitySystem.Get().Extinguish(target.Uid, flammable);
- }
-
- if (target.TryGetComponent(out CreamPiedComponent? creamPied))
- {
- EntitySystem.Get().SetCreamPied(target.Uid, creamPied, false);
- }
- }
- }
-}
diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
index 3a973d2395..47b720a6d1 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
@@ -185,36 +185,10 @@ namespace Content.Server.Disposal.Tube.Components
base.OnRemove();
}
- private void OpenUserInterface(ActorComponent actor)
+ public void OpenUserInterface(ActorComponent actor)
{
UpdateUserInterface();
UserInterface?.Open(actor.PlayerSession);
}
-
- [Verb]
- public sealed class ConfigureVerb : Verb
- {
- protected override void GetData(IEntity user, DisposalRouterComponent component, VerbData data)
- {
- var session = user.PlayerSession();
- var groupController = IoCManager.Resolve();
- if (session == null || !groupController.CanAdminMenu(session))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("configure-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, DisposalRouterComponent component)
- {
- if (user.TryGetComponent(out ActorComponent? actor))
- {
- component.OpenUserInterface(actor);
- }
- }
- }
}
}
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
index 2cf41cbf4a..eb44c289eb 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
@@ -150,34 +150,7 @@ namespace Content.Server.Disposal.Tube.Components
base.OnRemove();
UserInterface?.CloseAll();
}
-
- [Verb]
- public sealed class ConfigureVerb : Verb
- {
- protected override void GetData(IEntity user, DisposalTaggerComponent component, VerbData data)
- {
-
- var groupController = IoCManager.Resolve();
- if (!user.TryGetComponent(out ActorComponent? actor) || !groupController.CanAdminMenu(actor.PlayerSession))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("configure-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, DisposalTaggerComponent component)
- {
- if (user.TryGetComponent(out ActorComponent? actor))
- {
- component.OpenUserInterface(actor);
- }
- }
- }
-
- private void OpenUserInterface(ActorComponent actor)
+ public void OpenUserInterface(ActorComponent actor)
{
UpdateUserInterface();
UserInterface?.Open(actor.PlayerSession);
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
index 89adacf96f..01ead3d2cf 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
@@ -252,39 +252,5 @@ namespace Content.Server.Disposal.Tube.Components
Disconnect();
UpdateVisualState();
}
-
- [Verb]
- private sealed class TubeDirectionsVerb : Verb
- {
- protected override void GetData(IEntity user, IDisposalTubeComponent component, VerbData data)
- {
- data.Text = Loc.GetString("tube-direction-verb-get-data-text");
- data.CategoryData = VerbCategories.Debug;
- data.Visibility = VerbVisibility.Invisible;
-
- var groupController = IoCManager.Resolve();
-
- if (user.TryGetComponent(out var player))
- {
- if (groupController.CanCommand(player.PlayerSession, "tubeconnections"))
- {
- data.Visibility = VerbVisibility.Visible;
- }
- }
- }
-
- protected override void Activate(IEntity user, IDisposalTubeComponent component)
- {
- var groupController = IoCManager.Resolve();
-
- if (user.TryGetComponent(out var player))
- {
- if (groupController.CanCommand(player.PlayerSession, "tubeconnections"))
- {
- component.PopupDirections(user);
- }
- }
- }
- }
}
}
diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs
index 4d0697ae6b..7454b4ba1e 100644
--- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs
+++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs
@@ -1,8 +1,11 @@
using Content.Server.Disposal.Tube.Components;
using Content.Shared.Movement;
+using Content.Shared.Verbs;
+using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
+using Robust.Shared.Localization;
using Robust.Shared.Player;
using Robust.Shared.Timing;
@@ -17,7 +20,42 @@ namespace Content.Server.Disposal.Tube
base.Initialize();
SubscribeLocalEvent(BodyTypeChanged);
+
SubscribeLocalEvent(OnRelayMovement);
+ SubscribeLocalEvent(AddOpenUIVerbs);
+ SubscribeLocalEvent(AddOpenUIVerbs);
+ }
+
+ private void AddOpenUIVerbs(EntityUid uid, DisposalTaggerComponent component, GetInteractionVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ if (!args.User.TryGetComponent(out var actor))
+ return;
+ var player = actor.PlayerSession;
+
+ Verb verb = new();
+ verb.Text = Loc.GetString("configure-verb-get-data-text");
+ verb.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
+ verb.Act = () => component.OpenUserInterface(actor);
+ args.Verbs.Add(verb);
+ }
+
+ private void AddOpenUIVerbs(EntityUid uid, DisposalRouterComponent component, GetInteractionVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ if (!args.User.TryGetComponent(out var actor))
+ return;
+ var player = actor.PlayerSession;
+
+ Verb verb = new();
+ verb.Text = Loc.GetString("configure-verb-get-data-text");
+ verb.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
+ verb.Act = () => component.OpenUserInterface(actor);
+ args.Verbs.Add(verb);
}
private void OnRelayMovement(EntityUid uid, DisposalTubeComponent component, RelayMovementEntityEvent args)
diff --git a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs
index f14f0999b7..37ffb69a57 100644
--- a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs
+++ b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs
@@ -193,84 +193,6 @@ namespace Content.Server.Disposal.Unit.Components
return true;
}
- [Verb]
- private sealed class SelfInsertVerb : Verb
- {
- protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (!EntitySystem.Get().CanInteract(user) ||
- component.ContainedEntities.Contains(user))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("disposal-self-insert-verb-get-data-text");
- }
-
- protected override void Activate(IEntity user, DisposalUnitComponent component)
- {
- _ = component.TryInsert(user, user);
- }
- }
-
- [Verb]
- private sealed class FlushVerb : Verb
- {
- protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (!EntitySystem.Get().CanInteract(user) ||
- component.ContainedEntities.Contains(user))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("disposal-flush-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, DisposalUnitComponent component)
- {
- EntitySystem.Get().Engage(component);
- }
- }
-
- [Verb]
- private sealed class EjectVerb : Verb
- {
- public override bool AlternativeInteraction => true;
-
- protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (!EntitySystem.Get().CanInteract(user) ||
- component.ContainedEntities.Contains(user))
- {
- return;
- }
-
- // Only show verb if actually containing any entities.
- if (component.ContainedEntities.Count > 0)
- data.Visibility = VerbVisibility.Visible;
- else
- data.Visibility = VerbVisibility.Invisible;
-
- data.Text = Loc.GetString("disposal-eject-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, DisposalUnitComponent component)
- {
- EntitySystem.Get().TryEjectContents(component);
- }
- }
-
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
EntitySystem.Get().TryEjectContents(this);
diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
index 9757ed2d9e..a8cd0c5935 100644
--- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
+++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
@@ -26,6 +26,7 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
+using Content.Shared.Verbs;
namespace Content.Server.Disposal.Unit.EntitySystems
{
@@ -33,7 +34,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
-
+ [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
private readonly List _activeDisposals = new();
@@ -58,6 +59,51 @@ namespace Content.Server.Disposal.Unit.EntitySystems
SubscribeLocalEvent(HandleActivate);
SubscribeLocalEvent(HandleInteractHand);
SubscribeLocalEvent(HandleInteractUsing);
+
+ // Verbs
+ SubscribeLocalEvent(AddFlushEjectVerbs);
+ SubscribeLocalEvent(AddClimbInsideVerb);
+ }
+ private void AddFlushEjectVerbs(EntityUid uid, DisposalUnitComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract || component.ContainedEntities.Count == 0)
+ return;
+
+ // Verbs to flush the unit
+ Verb flushVerb = new();
+ flushVerb.Act = () => Engage(component);
+ flushVerb.Text = Loc.GetString("disposal-flush-verb-get-data-text");
+ flushVerb.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png";
+ flushVerb.Priority = 1;
+ args.Verbs.Add(flushVerb);
+
+ // Verb to eject the contents
+ Verb ejectVerb = new();
+ ejectVerb.Act = () => TryEjectContents(component);
+ ejectVerb.Category = VerbCategory.Eject;
+ ejectVerb.Text = Loc.GetString("disposal-eject-verb-contents");
+ args.Verbs.Add(ejectVerb);
+ }
+
+ private void AddClimbInsideVerb(EntityUid uid, DisposalUnitComponent component, GetOtherVerbsEvent args)
+ {
+ // This is not an interaction, activation, or alternative verb type because unfortunately most users are
+ // unwilling to accept that this is where they belong and don't want to accidentally climb inside.
+ if (!args.CanAccess ||
+ !args.CanInteract ||
+ component.ContainedEntities.Contains(args.User) ||
+ !_actionBlockerSystem.CanMove(args.User))
+ return;
+
+ // Add verb to climb inside of the unit,
+ Verb verb = new();
+ verb.Act = () => component.TryInsert(args.User, args.User);
+ verb.Text = Loc.GetString("disposal-self-insert-verb-get-data-text");
+ // TODO VERN ICON
+ // TODO VERB CATEGORY
+ // create a verb category for "enter"?
+ // See also, medical scanner. Also maybe add verbs for entering lockers/body bags?
+ args.Verbs.Add(verb);
}
public override void Update(float frameTime)
diff --git a/Content.Server/Fluids/Components/SpillableComponent.cs b/Content.Server/Fluids/Components/SpillableComponent.cs
index 0a2ac3dd93..7b0af8c69c 100644
--- a/Content.Server/Fluids/Components/SpillableComponent.cs
+++ b/Content.Server/Fluids/Components/SpillableComponent.cs
@@ -1,12 +1,6 @@
-using Content.Shared.ActionBlocker;
-using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
using Content.Shared.Interaction;
-using Content.Shared.Popups;
-using Content.Shared.Verbs;
using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Fluids.Components
@@ -19,56 +13,6 @@ namespace Content.Server.Fluids.Components
[DataField("solution")]
public string SolutionName = "puddle";
- ///
- /// Transfers solution from the held container to the floor.
- ///
- [Verb]
- private sealed class SpillTargetVerb : Verb
- {
- protected override void GetData(IEntity user, SpillableComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) ||
- !EntitySystem.Get()
- .TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("spill-target-verb-get-data-text");
- data.Visibility = solutionComponent.DrainAvailable > ReagentUnit.Zero
- ? VerbVisibility.Visible
- : VerbVisibility.Disabled;
- }
-
- protected override void Activate(IEntity user, SpillableComponent component)
- {
- var solutionsSys = EntitySystem.Get();
- if (component.Owner.HasComponent())
- {
- if (solutionsSys.TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent))
- {
- if (solutionComponent.DrainAvailable <= 0)
- {
- user.PopupMessage(user,
- Loc.GetString("spill-target-verb-activate-is-empty-message", ("owner", component.Owner)));
- }
-
- // Need this as when we split the component's owner may be deleted
- EntitySystem.Get()
- .Drain(component.Owner.Uid, solutionComponent, solutionComponent.DrainAvailable)
- .SpillAt(component.Owner.Transform.Coordinates, "PuddleSmear");
- }
- else
- {
- user.PopupMessage(user,
- Loc.GetString("spill-target-verb-activate-cannot-drain-message",
- ("owner", component.Owner)));
- }
- }
- }
- }
-
void IDropped.Dropped(DroppedEventArgs eventArgs)
{
if (!eventArgs.Intentional
diff --git a/Content.Server/Fluids/PuddleSystem.cs b/Content.Server/Fluids/PuddleSystem.cs
index c019f12a09..b526b7825c 100644
--- a/Content.Server/Fluids/PuddleSystem.cs
+++ b/Content.Server/Fluids/PuddleSystem.cs
@@ -1,4 +1,7 @@
using Content.Server.Fluids.Components;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Verbs;
using Content.Shared.Examine;
using Content.Shared.Slippery;
using JetBrains.Annotations;
@@ -13,12 +16,14 @@ namespace Content.Server.Fluids
internal sealed class PuddleSystem : EntitySystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize()
{
base.Initialize();
_mapManager.TileChanged += HandleTileChanged;
+ SubscribeLocalEvent(AddSpillVerb);
SubscribeLocalEvent(HandlePuddleExamined);
}
@@ -28,6 +33,25 @@ namespace Content.Server.Fluids
_mapManager.TileChanged -= HandleTileChanged;
}
+ private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetOtherVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ if (!_solutionContainerSystem.TryGetDrainableSolution(args.Target.Uid, out var solution))
+ return;
+
+ if (solution.DrainAvailable == ReagentUnit.Zero)
+ return;
+
+ Verb verb = new();
+ verb.Text = Loc.GetString("spill-target-verb-get-data-text");
+ // TODO VERB ICONS spill icon? pouring out a glass/beaker?
+ verb.Act = () => _solutionContainerSystem.SplitSolution(args.Target.Uid,
+ solution, solution.DrainAvailable).SpillAt(args.Target.Transform.Coordinates, "PuddleSmear");
+ args.Verbs.Add(verb);
+ }
+
private void HandlePuddleExamined(EntityUid uid, PuddleComponent component, ExaminedEvent args)
{
if (EntityManager.TryGetComponent(uid, out var slippery) && slippery.Slippery)
diff --git a/Content.Server/Ghost/Roles/MakeGhostRoleVerb.cs b/Content.Server/Ghost/Roles/MakeGhostRoleVerb.cs
deleted file mode 100644
index 32b424e9ce..0000000000
--- a/Content.Server/Ghost/Roles/MakeGhostRoleVerb.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using Content.Server.Mind.Components;
-using Content.Shared.Verbs;
-using Robust.Server.Console;
-using Robust.Server.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Ghost.Roles
-{
- [GlobalVerb]
- public class MakeGhostRoleVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- var groupController = IoCManager.Resolve();
-
- if (target.TryGetComponent(out MindComponent? mind) &&
- mind.HasMind)
- {
- return;
- }
-
- if (!user.TryGetComponent(out ActorComponent? actor) ||
- !groupController.CanCommand(actor.PlayerSession, "makeghostrole"))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("make-ghost-role-verb-get-data-text");
- data.CategoryData = VerbCategories.Debug;
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- var groupController = IoCManager.Resolve();
-
- if (target.TryGetComponent(out MindComponent? mind) &&
- mind.HasMind)
- {
- return;
- }
-
- if (!user.TryGetComponent(out ActorComponent? actor) ||
- !groupController.CanCommand(actor.PlayerSession, "makeghostrole"))
- {
- return;
- }
-
- var ghostRoleSystem = EntitySystem.Get();
- ghostRoleSystem.OpenMakeGhostRoleEui(actor.PlayerSession, target.Uid);
- }
- }
-}
diff --git a/Content.Server/Hands/Components/HandsComponent.cs b/Content.Server/Hands/Components/HandsComponent.cs
index fb93535cde..a92c6e0ad7 100644
--- a/Content.Server/Hands/Components/HandsComponent.cs
+++ b/Content.Server/Hands/Components/HandsComponent.cs
@@ -163,7 +163,7 @@ namespace Content.Server.Hands.Components
{
// What is this API??
if (!Owner.TryGetComponent(out SharedPullerComponent? puller)
- || puller.Pulling == null || !puller.Pulling.TryGetComponent(out PullableComponent? pullable))
+ || puller.Pulling == null || !puller.Pulling.TryGetComponent(out SharedPullableComponent? pullable))
return false;
return _entitySystemManager.GetEntitySystem().TryStopPull(pullable);
diff --git a/Content.Server/Interaction/InRangeUnoccludedVerb.cs b/Content.Server/Interaction/InRangeUnoccludedVerb.cs
deleted file mode 100644
index 00a06f229b..0000000000
--- a/Content.Server/Interaction/InRangeUnoccludedVerb.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using Content.Shared.Interaction.Helpers;
-using Content.Shared.Popups;
-using Content.Shared.Verbs;
-using Robust.Server.Console;
-using Robust.Server.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-
-namespace Content.Server.Interaction
-{
- [GlobalVerb]
- public class InRangeUnoccludedVerb : GlobalVerb
- {
- public override bool RequireInteractionRange => false;
-
- public override void GetData(IEntity user, IEntity target, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
-
- if (!user.TryGetComponent(out ActorComponent? actor))
- {
- return;
- }
-
- var groupController = IoCManager.Resolve();
- if (!groupController.CanCommand(actor.PlayerSession, "inrangeunoccluded"))
- {
- return;
- }
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("in-range-unoccluded-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/information.svg.192dpi.png";
- data.CategoryData = VerbCategories.Debug;
- }
-
- public override void Activate(IEntity user, IEntity target)
- {
- if (!user.TryGetComponent(out ActorComponent? actor))
- {
- return;
- }
-
- var groupController = IoCManager.Resolve();
- if (!groupController.CanCommand(actor.PlayerSession, "inrangeunoccluded"))
- {
- return;
- }
-
- var message = user.InRangeUnOccluded(target)
- ? Loc.GetString("in-range-unoccluded-verb-on-activate-not-occluded")
- : Loc.GetString("in-range-unoccluded-verb-on-activate-occluded");
-
- target.PopupMessage(user, message);
- }
- }
-}
diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs
index 32e55c5c35..ecc47406bf 100644
--- a/Content.Server/Interaction/InteractionSystem.cs
+++ b/Content.Server/Interaction/InteractionSystem.cs
@@ -8,6 +8,7 @@ using Content.Server.Hands.Components;
using Content.Server.Items;
using Content.Server.Pulling;
using Content.Server.Timing;
+using Content.Server.Verbs;
using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop;
using Content.Shared.Hands;
@@ -17,6 +18,7 @@ using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Inventory;
using Content.Shared.Popups;
+using Content.Shared.Pulling.Components;
using Content.Shared.Rotatable;
using Content.Shared.Throwing;
using Content.Shared.Verbs;
@@ -47,6 +49,7 @@ namespace Content.Server.Interaction
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+ [Dependency] private readonly VerbSystem _verbSystem = default!;
[Dependency] private readonly PullingSystem _pullSystem = default!;
public override void Initialize()
@@ -311,7 +314,7 @@ namespace Content.Server.Interaction
if (!InRangeUnobstructed(userEntity, pulledObject, popup: true))
return false;
- if (!pulledObject.TryGetComponent(out PullableComponent? pull))
+ if (!pulledObject.TryGetComponent(out SharedPullableComponent? pull))
return false;
return _pullSystem.TogglePull(userEntity, pull);
@@ -380,13 +383,7 @@ namespace Content.Server.Interaction
{
// We are close to the nearby object.
if (altInteract)
- // We are trying to use alternative interactions. Perform alternative interactions, using context
- // menu verbs.
-
- // Verbs can be triggered with an item in the hand, but currently there are no verbs that depend on
- // the currently held item. Maybe this if statement should be changed to
- // (altInteract && (item == null || item == target)).
- // Note that item == target will happen when alt-clicking the item currently in your hands.
+ // Perform alternative interactions, using context menu verbs.
AltInteract(user, target);
else if (item != null && item != target)
// We are performing a standard interaction with an item, and the target isn't the same as the item
@@ -428,7 +425,7 @@ namespace Content.Server.Interaction
if (user.TryGetComponent(out BuckleComponent? buckle) && (buckle.BuckledTo != null))
{
// We're buckled to another object. Is that object rotatable?
- if (buckle.BuckledTo!.Owner.TryGetComponent(out SharedRotatableComponent? rotatable) && rotatable.RotateWhileAnchored)
+ if (buckle.BuckledTo!.Owner.TryGetComponent(out RotatableComponent? rotatable) && rotatable.RotateWhileAnchored)
{
// Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel".
// (Since the user being buckled to it holds it down with their weight.)
@@ -511,36 +508,20 @@ namespace Content.Server.Interaction
/// Alternative interactions on an entity.
///
///
- /// Uses the context menu verb list, and acts out the first verb marked as an alternative interaction. Note
- /// that this does not have any checks to see whether this interaction is valid, as these are all done in
+ /// Uses the context menu verb list, and acts out the highest priority alternative interaction verb.
///
public void AltInteract(IEntity user, IEntity target)
{
- // TODO VERB SYSTEM when ECS-ing verbs and re-writing VerbUtility.GetVerbs, maybe sort verbs by some
- // priority property, such that which verbs appear first is more predictable?.
+ // Get list of alt-interact verbs
+ GetAlternativeVerbsEvent getVerbEvent = new(user, target);
+ RaiseLocalEvent(target.Uid, getVerbEvent);
- // Iterate through list of verbs that apply to target. We do not include global verbs here. If in the future
- // alt click should also support global verbs, this needs to be changed.
- foreach (var (component, verb) in VerbUtility.GetVerbs(target))
+ foreach (var verb in getVerbEvent.Verbs)
{
- // Check that the verb marked as an alternative interaction?
- if (!verb.AlternativeInteraction)
+ if (verb.Disabled)
continue;
- // Can the verb be acted out?
- if (!VerbUtility.VerbAccessChecks(user, target, verb))
- continue;
-
- // Is the verb currently enabled?
- var verbData = verb.GetData(user, component);
- if (verbData.IsInvisible || verbData.IsDisabled)
- continue;
-
- // Act out the verb. Note that, if there is more than one AlternativeInteraction verb, only the first
- // one is activated. The priority is effectively determined by the order in which VerbUtility.GetVerbs()
- // returns the verbs.
- verb.Activate(user, component);
+ _verbSystem.TryExecuteVerb(verb);
break;
}
}
diff --git a/Content.Server/Inventory/Components/InventoryComponent.cs b/Content.Server/Inventory/Components/InventoryComponent.cs
index c6c9bb5cb3..b6c56dfeb6 100644
--- a/Content.Server/Inventory/Components/InventoryComponent.cs
+++ b/Content.Server/Inventory/Components/InventoryComponent.cs
@@ -614,47 +614,5 @@ namespace Content.Server.Inventory.Components
return false;
}
-
- [Verb]
- private sealed class SetOutfitVerb : Verb
- {
- public override bool RequireInteractionRange => false;
- public override bool BlockedByContainers => false;
-
- protected override void GetData(IEntity user, InventoryComponent component, VerbData data)
- {
- data.Visibility = VerbVisibility.Invisible;
- if (!CanCommand(user))
- return;
-
- data.Visibility = VerbVisibility.Visible;
- data.Text = Loc.GetString("set-outfit-verb-get-data-text");
- data.CategoryData = VerbCategories.Debug;
- data.IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, InventoryComponent component)
- {
- if (!CanCommand(user))
- return;
-
- var target = component.Owner;
-
- var entityId = target.Uid.ToString();
-
- var command = new SetOutfitCommand();
- var host = IoCManager.Resolve();
- var args = new string[] {entityId};
- var session = user.PlayerSession();
- command.Execute(new ConsoleShell(host, session), $"{command.Command} {entityId}", args);
- }
-
- private static bool CanCommand(IEntity user)
- {
- var groupController = IoCManager.Resolve();
- return user.TryGetComponent(out var player) &&
- groupController.CanCommand(player.PlayerSession, "setoutfit");
- }
- }
}
}
diff --git a/Content.Server/Items/ItemComponent.cs b/Content.Server/Items/ItemComponent.cs
index 02d5f8eeca..63c5fad6a2 100644
--- a/Content.Server/Items/ItemComponent.cs
+++ b/Content.Server/Items/ItemComponent.cs
@@ -1,12 +1,6 @@
-using Content.Server.Hands.Components;
-using Content.Shared.ActionBlocker;
-using Content.Shared.Interaction.Events;
using Content.Shared.Item;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
-using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
-using Robust.Shared.Localization;
namespace Content.Server.Items
{
@@ -29,32 +23,6 @@ namespace Content.Server.Items
component.Visible = false;
}
}
-
- [Verb]
- public sealed class PickUpVerb : Verb
- {
- protected override void GetData(IEntity user, ItemComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user) ||
- component.Owner.IsInContainer() ||
- !component.CanPickup(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- data.Text = Loc.GetString("pick-up-verb-get-data-text");
- data.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
- }
-
- protected override void Activate(IEntity user, ItemComponent component)
- {
- if (user.TryGetComponent(out HandsComponent? hands) && !hands.IsHolding(component.Owner))
- {
- hands.PutInHand(component);
- }
- }
- }
}
}
diff --git a/Content.Server/Items/ItemSystem.cs b/Content.Server/Items/ItemSystem.cs
new file mode 100644
index 0000000000..2f03fb5338
--- /dev/null
+++ b/Content.Server/Items/ItemSystem.cs
@@ -0,0 +1,39 @@
+using Content.Shared.Item;
+using Content.Shared.Verbs;
+using Robust.Shared.Containers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
+
+namespace Content.Server.Items
+{
+ public class ItemSystem : EntitySystem
+ {
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(AddPickupVerb);
+ }
+
+ private void AddPickupVerb(EntityUid uid, SharedItemComponent component, GetInteractionVerbsEvent args)
+ {
+ if (args.Hands == null ||
+ args.Using != null ||
+ !args.CanAccess ||
+ !args.CanInteract ||
+ !component.CanPickup(args.User, popup: false))
+ return;
+
+ Verb verb = new();
+ verb.Act = () => args.Hands.TryPutInActiveHandOrAny(args.Target);
+ verb.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
+
+ // if the item already in the user's inventory, change the text
+ if (args.Target.TryGetContainer(out var container) && container.Owner == args.User)
+ verb.Text = Loc.GetString("pick-up-verb-get-data-text-inventory");
+ else
+ verb.Text = Loc.GetString("pick-up-verb-get-data-text");
+
+ args.Verbs.Add(verb);
+ }
+ }
+}
diff --git a/Content.Server/Light/Components/ExpendableLightComponent.cs b/Content.Server/Light/Components/ExpendableLightComponent.cs
index 1eb5a47b15..7fbe58c860 100644
--- a/Content.Server/Light/Components/ExpendableLightComponent.cs
+++ b/Content.Server/Light/Components/ExpendableLightComponent.cs
@@ -1,11 +1,7 @@
using Content.Server.Clothing.Components;
using Content.Server.Items;
-using Content.Server.Sound.Components;
-using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
using Content.Shared.Light.Component;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
@@ -52,7 +48,7 @@ namespace Content.Server.Light.Components
///
/// Enables the light if it is not active. Once active it cannot be turned off.
///
- private bool TryActivate()
+ public bool TryActivate()
{
if (!Activated && CurrentState == ExpendableLightState.BrandNew)
{
@@ -173,33 +169,5 @@ namespace Content.Server.Light.Components
}
}
}
-
- [Verb]
- public sealed class ActivateVerb : Verb
- {
- protected override void GetData(IEntity user, ExpendableLightComponent component, VerbData data)
- {
- if (!EntitySystem.Get().CanInteract(user))
- {
- data.Visibility = VerbVisibility.Invisible;
- return;
- }
-
- if (component.CurrentState == ExpendableLightState.BrandNew)
- {
- data.Text = "Activate";
- data.Visibility = VerbVisibility.Visible;
- }
- else
- {
- data.Visibility = VerbVisibility.Invisible;
- }
- }
-
- protected override void Activate(IEntity user, ExpendableLightComponent component)
- {
- component.TryActivate();
- }
- }
}
}
diff --git a/Content.Server/Light/Components/HandheldLightComponent.cs b/Content.Server/Light/Components/HandheldLightComponent.cs
index 0b74f9e056..4ee50002c7 100644
--- a/Content.Server/Light/Components/HandheldLightComponent.cs
+++ b/Content.Server/Light/Components/HandheldLightComponent.cs
@@ -107,7 +107,7 @@ namespace Content.Server.Light.Components
return Activated ? TurnOff() : TurnOn(user);
}
- private bool TurnOff(bool makeNoise = true)
+ public bool TurnOff(bool makeNoise = true)
{
if (!Activated)
{
@@ -127,7 +127,7 @@ namespace Content.Server.Light.Components
return true;
}
- private bool TurnOn(IEntity user)
+ public bool TurnOn(IEntity user)
{
if (Activated)
{
@@ -243,26 +243,6 @@ namespace Content.Server.Light.Components
{
return new HandheldLightComponentState(GetLevel());
}
-
- [Verb]
- public sealed class ToggleLightVerb : Verb
- {
- protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data)
- {
- if (!EntitySystem.Get