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
This commit is contained in:
40
Content.Client/Administration/AdminVerbSystem.cs
Normal file
40
Content.Client/Administration/AdminVerbSystem.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side admin verb system. These usually open some sort of UIs.
|
||||
/// </summary>
|
||||
class AdminVerbSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
||||
[Dependency] private readonly IViewVariablesManager _viewVariablesManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetOtherVerbsEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IEntity> 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 }
|
||||
};
|
||||
AddChild(new PanelContainer
|
||||
{
|
||||
Children = { (List = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}) },
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = MarginColor }
|
||||
}}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ContextMenuPresenter>();
|
||||
}
|
||||
|
||||
#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;
|
||||
if (args.State != BoundKeyState.Down)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SystemOnToggleContextMenu(object? sender, PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
if (_stateManager.CurrentState is not GameScreenBase)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (playerEntity == null)
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
//visibility checks
|
||||
player.TryGetContainer(out var playerContainer);
|
||||
foreach (var entity in entities.ToList())
|
||||
{
|
||||
if (!entity.TryGetComponent(out ISpriteComponent? spriteComponent) ||
|
||||
!spriteComponent.Visible ||
|
||||
!CanSeeContainerCheck(entity, playerContainer))
|
||||
{
|
||||
entities.Remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleMoveEvent(ref MoveEvent ev)
|
||||
{
|
||||
if (_contextMenuView.Elements.Count == 0) return;
|
||||
var entity = ev.Sender;
|
||||
if (_contextMenuView.Elements.ContainsKey(entity))
|
||||
{
|
||||
if (!entity.Transform.MapPosition.InRange(_mapCoordinates, 1.0f))
|
||||
{
|
||||
_contextMenuView.RemoveEntity(entity);
|
||||
}
|
||||
}
|
||||
if (entities.Count == 0)
|
||||
return false;
|
||||
|
||||
_contextMenuView.AddRootMenu(entities);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the player see the entity through any entity containers?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is similar to <see cref="ContainerHelpers.IsInSameOrParentContainer()"/>, except that we do not
|
||||
/// allow the player to be the "parent" container and we allow for see-through containers (display cases).
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that entities in the context menu are still visible. If not, remove them from the context menu.
|
||||
/// </summary>
|
||||
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<IShowContextMenu>().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<ContextMenuPresenter>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -105,10 +105,14 @@ namespace Content.Client.ContextMenu.UI
|
||||
public int GetHashCode(IEntity e)
|
||||
{
|
||||
var hash = EqualityComparer<string>.Default.GetHashCode(e.Prototype?.ID!);
|
||||
foreach (var element in e.GetComponent<ISpriteComponent>().AllLayers.Where(obj => obj.Visible).Select(s => s.RsiState.Name))
|
||||
|
||||
if (e.TryGetComponent<ISpriteComponent>(out var sprite))
|
||||
{
|
||||
foreach (var element in sprite.AllLayers.Where(obj => obj.Visible).Select(s => s.RsiState.Name))
|
||||
{
|
||||
hash ^= EqualityComparer<string>.Default.GetHashCode(element!);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -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<GetOtherVerbsEvent>(AddExamineVerb);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine))
|
||||
.Register<ExamineSystem>();
|
||||
@@ -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
|
||||
|
||||
@@ -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<ExamineSystem>().DoExamine(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Content.Client.Items.Managers
|
||||
else if (args.Function == ContentKeyFunctions.OpenContextMenu)
|
||||
{
|
||||
_entitySystemManager.GetEntitySystem<VerbSystem>()
|
||||
.OpenContextMenu(item, _uiMgr.ScreenToUIPosition(args.PointerLocation));
|
||||
.OpenVerbMenu(item, _uiMgr.ScreenToUIPosition(args.PointerLocation));
|
||||
}
|
||||
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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<PullableComponent, PullableMoveMessage>(OnPullableMove);
|
||||
SubscribeLocalEvent<PullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullableMoveMessage>(OnPullableMove);
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using Content.Shared.Rotatable;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Rotatable
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedRotatableComponent))]
|
||||
public class RotatableComponent : SharedRotatableComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
246
Content.Client/Verbs/VerbMenuElement.cs
Normal file
246
Content.Client/Verbs/VerbMenuElement.cs
Normal file
@@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This pop-up appears when hovering over a verb category in the context menu.
|
||||
/// </summary>
|
||||
public sealed class VerbCategoryPopup : ContextMenuPopup
|
||||
{
|
||||
public VerbCategoryPopup(VerbSystem system, IEnumerable<Verb> 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;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to hide member verb text and just show icons.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If no members have icons, this option is ignored and text is shown anyways. Defaults to using <see cref="VerbCategory.IconsOnly"/>.
|
||||
/// </remarks>
|
||||
private readonly bool _drawOnlyIcons;
|
||||
|
||||
/// <summary>
|
||||
/// The pop-up that appears when hovering over this verb group.
|
||||
/// </summary>
|
||||
private readonly VerbCategoryPopup _popup;
|
||||
|
||||
public VerbCategoryButton(VerbSystem system, VerbCategory category, IEnumerable<Verb> 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<IResourceCache>()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a verb category pop-up after a short delay.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the delayed pop-up
|
||||
/// </summary>
|
||||
protected override void MouseExited()
|
||||
{
|
||||
base.MouseExited();
|
||||
|
||||
_openCancel?.Cancel();
|
||||
_openCancel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PointerInputCmdHandler.PointerInputCmdArgs>? ToggleContextMenu;
|
||||
public event EventHandler<bool>? 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<VerbType, SortedSet<Verb>> CurrentVerbs = new();
|
||||
|
||||
// TODO: Move presenter out of the system
|
||||
// TODO: Separate the rest of the UI from the logic
|
||||
/// <summary>
|
||||
/// Whether to show all entities on the context menu.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Verb execution will only be affected if the server also agrees that this player can see the target
|
||||
/// entity.
|
||||
/// </remarks>
|
||||
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<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeNetworkEvent<VerbSystemMessages.VerbsResponseMessage>(FillEntityPopup);
|
||||
SubscribeNetworkEvent<PlayerContainerVisibilityMessage>(HandleContainerVisibilityMessage);
|
||||
SubscribeNetworkEvent<VerbsResponseEvent>(HandleVerbResponse);
|
||||
SubscribeNetworkEvent<SetSeeAllContextEvent>(SetSeeAllContext);
|
||||
|
||||
_contextMenuPresenter = new ContextMenuPresenter(this);
|
||||
SubscribeLocalEvent<MoveEvent>(_contextMenuPresenter.HandleMoveEvent);
|
||||
ContextMenuPresenter = new ContextMenuPresenter(this);
|
||||
}
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.OpenContextMenu,
|
||||
new PointerInputCmdHandler(HandleOpenContextMenu))
|
||||
.Register<VerbSystem>();
|
||||
private void Reset(RoundRestartCleanupEvent ev)
|
||||
{
|
||||
ContextMenuPresenter.CloseAllMenus();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_contextMenuPresenter?.Dispose();
|
||||
|
||||
CommandBinds.Unregister<VerbSystem>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute actions associated with the given verb. If there are no defined actions, this will instead ask
|
||||
/// the server to run the given verb.
|
||||
/// </summary>
|
||||
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<string, List<ListedVerbData>>();
|
||||
var groupIcons = new Dictionary<string, SpriteSpecifier>();
|
||||
|
||||
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);
|
||||
// update "waiting for server...".
|
||||
CurrentVerbPopup.List.DisposeAllChildren();
|
||||
CurrentVerbPopup.AddToMenu(new Label { Text = Loc.GetString("verb-system-null-server-response") });
|
||||
FillVerbPopup(CurrentVerbPopup);
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(new ListedVerbData(data.Text, !data.Available, data.Key, entity.ToString()!, () =>
|
||||
// Add the new server-side verbs.
|
||||
foreach (var (verbType, verbSet) in msg.Verbs)
|
||||
{
|
||||
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(curEntity, data.Key));
|
||||
CloseAllMenus();
|
||||
}, data.Icon));
|
||||
}
|
||||
|
||||
var user = GetUserEntity();
|
||||
//Get verbs, component dependent.
|
||||
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||
SortedSet<Verb> sortedVerbs = new (verbSet);
|
||||
if (!CurrentVerbs.TryAdd(verbType, sortedVerbs))
|
||||
{
|
||||
if (!VerbUtility.VerbAccessChecks(user, entity, verb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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()))
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
CurrentVerbs[verbType].UnionWith(sortedVerbs);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear currently shown verbs and show new ones
|
||||
CurrentVerbPopup.List.DisposeAllChildren();
|
||||
FillVerbPopup(CurrentVerbPopup);
|
||||
}
|
||||
else
|
||||
|
||||
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)
|
||||
{
|
||||
AddVerbSet(popup, type);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
private VerbButton CreateVerbButton(ListedVerbData data)
|
||||
{
|
||||
var button = new VerbButton
|
||||
{
|
||||
Text = Loc.GetString(data.Text),
|
||||
Disabled = data.Disabled
|
||||
};
|
||||
|
||||
if (data.Icon != null)
|
||||
{
|
||||
button.Icon = data.Icon.Frame0();
|
||||
popup.InvalidateMeasure();
|
||||
}
|
||||
|
||||
if (!data.Disabled)
|
||||
/// <summary>
|
||||
/// Add a list of verbs to a BoxContainer. Iterates over the given verbs list and creates GUI buttons.
|
||||
/// </summary>
|
||||
private void AddVerbSet(ContextMenuPopup popup, VerbType type)
|
||||
{
|
||||
button.OnPressed += _ =>
|
||||
if (!CurrentVerbs.TryGetValue(type, out var verbSet) || verbSet.Count == 0)
|
||||
return;
|
||||
|
||||
HashSet<string> listedCategories = new();
|
||||
|
||||
foreach (var verb in verbSet)
|
||||
{
|
||||
CloseAllMenus();
|
||||
try
|
||||
if (verb.Category == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return button;
|
||||
if (listedCategories.Contains(verb.Category.Text))
|
||||
{
|
||||
// This verb was already included in a verb-category button added by a previous verb
|
||||
continue;
|
||||
}
|
||||
|
||||
private Control CreateCategoryButton(string text, List<ListedVerbData> verbButtons, SpriteSpecifier? icon)
|
||||
{
|
||||
verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
|
||||
// Get the verbs in the category
|
||||
var verbsInCategory = verbSet.Where(v => v.Category?.Text == verb.Category.Text);
|
||||
|
||||
return new VerbGroupButton(this, verbButtons, icon)
|
||||
{
|
||||
Text = Loc.GetString(text),
|
||||
};
|
||||
popup.AddToMenu(
|
||||
new VerbCategoryButton(this, verb.Category, verbsInCategory, type, CurrentTarget));
|
||||
listedCategories.Add(verb.Category.Text);
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseVerbMenu()
|
||||
{
|
||||
_currentVerbListRoot?.Dispose();
|
||||
_currentVerbListRoot = null;
|
||||
_currentEntity = EntityUid.Invalid;
|
||||
if (CurrentVerbPopup != null)
|
||||
{
|
||||
CurrentVerbPopup.OnPopupHide -= CloseVerbMenu;
|
||||
CurrentVerbPopup.Dispose();
|
||||
CurrentVerbPopup = null;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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<ListedVerbData> 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<ListedVerbData> 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<IResourceCache>()
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Global verb that opens a view variables window for the entity in question.
|
||||
/// </summary>
|
||||
[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<IClientConGroupController>();
|
||||
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<IViewVariablesManager>();
|
||||
vvm.OpenVV(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ContainerSlot>(Owner, $"{Name}-privilegedId");
|
||||
_targetIdContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-targetId");
|
||||
PrivilegedIdContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-privilegedId");
|
||||
TargetIdContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-targetId");
|
||||
|
||||
Owner.EnsureComponentWarn<AccessReader>();
|
||||
Owner.EnsureComponentWarn<ServerUserInterfaceComponent>();
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is an ID in <see cref="_privilegedIdContainer"/> and said ID satisfies the requirements of <see cref="AccessReader"/>.
|
||||
/// Returns true if there is an ID in <see cref="PrivilegedIdContainer"/> and said ID satisfies the requirements of <see cref="AccessReader"/>.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the "Submit" button in the UI gets pressed.
|
||||
/// Writes data passed from the UI into the ID stored in <see cref="_targetIdContainer"/>, if present.
|
||||
/// Writes data passed from the UI into the ID stored in <see cref="TargetIdContainer"/>, if present.
|
||||
/// </summary>
|
||||
private void TryWriteToTargetId(string newFullName, string newJobTitle, List<string> newAccessList)
|
||||
{
|
||||
if (!PrivilegedIdIsAuthorized() || _targetIdContainer.ContainedEntity == null)
|
||||
if (!PrivilegedIdIsAuthorized() || TargetIdContainer.ContainedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetIdEntity = _targetIdContainer.ContainedEntity;
|
||||
var targetIdEntity = TargetIdContainer.ContainedEntity;
|
||||
|
||||
var targetIdComponent = targetIdEntity.GetComponent<IdCardComponent>();
|
||||
targetIdComponent.FullName = newFullName;
|
||||
@@ -128,7 +125,7 @@ namespace Content.Server.Access.Components
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
var isId = hands.GetActiveHand?.Owner.HasComponent<IdCardComponent>();
|
||||
if (isId != true)
|
||||
public void InsertIdFromHand(IEntity user, ContainerSlot container, SharedHandsComponent hands)
|
||||
{
|
||||
if (!hands.TryGetActiveHeldEntity(out var heldEntity))
|
||||
return;
|
||||
}
|
||||
|
||||
if (hands.ActiveHand == null)
|
||||
{
|
||||
if (!heldEntity.HasComponent<IdCardComponent>())
|
||||
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<ItemComponent>());
|
||||
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<IdCardComponent>() || !user.TryGetComponent(out IHandsComponent? hand))
|
||||
if (!item.HasComponent<IdCardComponent>() || !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<IdCardConsoleComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<IdCardConsoleComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, IdCardConsoleComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
Content.Server/Access/IdCardConsoleSystem.cs
Normal file
80
Content.Server/Access/IdCardConsoleSystem.cs
Normal file
@@ -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<IdCardConsoleComponent, GetInteractionVerbsEvent>(AddInsertVerbs);
|
||||
SubscribeLocalEvent<IdCardConsoleComponent, GetAlternativeVerbsEvent>(AddEjectVerbs);
|
||||
}
|
||||
|
||||
private void AddInsertVerbs(EntityUid uid, IdCardConsoleComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Using == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
!args.Using.HasComponent<IdCardComponent>() ||
|
||||
!_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
188
Content.Server/Administration/AdminVerbSystem.cs
Normal file
188
Content.Server/Administration/AdminVerbSystem.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// System to provide various global admin/debug verbs
|
||||
/// </summary>
|
||||
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<GetOtherVerbsEvent>(AddDebugVerbs);
|
||||
}
|
||||
|
||||
private void AddDebugVerbs(GetOtherVerbsEvent args)
|
||||
{
|
||||
if (!args.User.TryGetComponent<ActorComponent>(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<MindComponent>() &&
|
||||
args.Target.TryGetComponent<MindComponent>(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<MindComponent>())
|
||||
{
|
||||
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<InventoryComponent>())
|
||||
{
|
||||
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<IDisposalTubeComponent>(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<MindComponent>()?.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<ConfigurationComponent>(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<SolutionContainerManagerComponent>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IEntityManager>();
|
||||
@@ -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<IMobStateComponent>()?.UpdateState(0);
|
||||
target.GetComponentOrNull<HungerComponent>()?.ResetFood();
|
||||
target.GetComponentOrNull<ThirstComponent>()?.ResetThirst();
|
||||
target.GetComponentOrNull<StunnableComponent>()?.ResetStuns();
|
||||
|
||||
EntitySystem.Get<FlammableSystem>().Extinguish(target.Uid);
|
||||
|
||||
if (target.TryGetComponent(out DamageableComponent? damageable))
|
||||
{
|
||||
EntitySystem.Get<DamageableSystem>().SetAllDamage(damageable, 0);
|
||||
}
|
||||
|
||||
if (target.TryGetComponent(out CreamPiedComponent? creamPied))
|
||||
{
|
||||
EntitySystem.Get<CreamPieSystem>().SetCreamPied(target.Uid, creamPied, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Content.Server/Administration/UI/AdminAddReagentEui.cs
Normal file
94
Content.Server/Administration/UI/AdminAddReagentEui.cs
Normal file
@@ -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<SolutionContainerSystem>()
|
||||
.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<SolutionContainerSystem>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<EuiManager>();
|
||||
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<SolutionContainerManagerComponent>()
|
||||
&& target.HasComponent<InjectableSolutionComponent>()))
|
||||
{
|
||||
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<IAdminManager>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(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<IAdminManager>();
|
||||
if (user.TryGetComponent<ActorComponent>(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<SolutionContainerSystem>()
|
||||
.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<SolutionContainerSystem>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IConGroupController>();
|
||||
|
||||
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<IConGroupController>();
|
||||
|
||||
if (!user.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!groupController.CanCommand(actor.PlayerSession, "deleteentity"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,9 @@ namespace Content.Server.Alert.Click
|
||||
{
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
var ps = EntitySystem.Get<SharedPullingSystem>();
|
||||
if (args.Player.TryGetComponent<SharedPullableComponent>(out var playerPullable))
|
||||
{
|
||||
ps.TryStopPull(playerPullable);
|
||||
EntitySystem.Get<SharedPullingSystem>().TryStopPull(playerPullable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Pulling;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open interaction window
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class ControlVerb : Verb<GasTankComponent>
|
||||
{
|
||||
public override bool RequireInteractionRange => true;
|
||||
|
||||
protected override void GetData(IEntity user, GasTankComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
if (!user.HasComponent<ActorComponent>())
|
||||
{
|
||||
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<ActorComponent>(out var actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.OpenInterface(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
|
||||
@@ -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<GasTankComponent, GetActivationVerbsEvent>(AddOpenUIVerb);
|
||||
}
|
||||
|
||||
private void AddOpenUIVerb(EntityUid uid, GasTankComponent component, GetActivationVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.User.TryGetComponent<ActorComponent>(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);
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Content.Server.Body.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
body.SetPart($"{nameof(AttachBodyPartVerb)}-{partEntity.Uid}", part);
|
||||
body.SetPart($"AttachBodyPartVerb-{partEntity.Uid}", part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,54 +218,5 @@ namespace Content.Server.Body.Part
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public class AttachBodyPartVerb : Verb<BodyPartComponent>
|
||||
{
|
||||
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<IConGroupController>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the unbuckling of the owning entity through a verb if
|
||||
/// anyone right clicks them.
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class BuckleVerb : Verb<BuckleComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, BuckleComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<StrapComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, StrapComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(component.Owner) ||
|
||||
!user.TryGetComponent<BuckleComponent>(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<BuckleComponent>(out var buckle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
buckle.ToggleBuckle(user, component.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!eventArgs.Dragged.TryGetComponent(out BuckleComponent? buckleComponent)) return false;
|
||||
|
||||
@@ -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<StrapComponent, EntRemovedFromContainerMessage>(ContainerModifiedStrap);
|
||||
|
||||
SubscribeLocalEvent<BuckleComponent, InteractHandEvent>(HandleInteractHand);
|
||||
|
||||
SubscribeLocalEvent<BuckleComponent, GetInteractionVerbsEvent>(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)
|
||||
101
Content.Server/Buckle/Systems/StrapSystem.cs
Normal file
101
Content.Server/Buckle/Systems/StrapSystem.cs
Normal file
@@ -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<StrapComponent, GetInteractionVerbsEvent>(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<BuckleComponent>();
|
||||
|
||||
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<BuckleComponent>(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<BuckleComponent>(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<ActorComponent>() ? 1 : -1;
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ItemCabinetComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
|
||||
{
|
||||
if (component.ItemContainer.ContainedEntity == null || !component.Opened || !EntitySystem.Get<ActionBlockerSystem>().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<ItemCabinetComponent>
|
||||
{
|
||||
// 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<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ItemCabinetComponent, TryEjectItemCabinetEvent>(OnTryEjectItemCabinet);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, TryInsertItemCabinetEvent>(OnTryInsertItemCabinet);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ToggleItemCabinetEvent>(OnToggleItemCabinet);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, GetInteractionVerbsEvent>(AddEjectInsertVerbs);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, GetActivationVerbsEvent>(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
|
||||
/// <summary>
|
||||
/// Toggles the ItemCabinet's state.
|
||||
/// </summary>
|
||||
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<ItemComponent>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to eject the ItemCabinet's item, either into the user's hands. Used by both <see
|
||||
/// cref="OnTryEjectItemCabinet"/> and the eject verbs.
|
||||
/// </summary>
|
||||
private static void TakeItem(ItemCabinetComponent comp, SharedHandsComponent hands, IEntity containedEntity, IEntity user)
|
||||
{
|
||||
if (containedEntity.HasComponent<ItemComponent>())
|
||||
{
|
||||
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))
|
||||
|
||||
@@ -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<ContainerSlot>(Owner, $"{Name}-reagentContainerContainer");
|
||||
|
||||
_bufferSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, SolutionName);
|
||||
@@ -177,7 +176,7 @@ namespace Content.Server.Chemistry.Components
|
||||
/// <returns>Returns a <see cref="SharedChemMasterComponent.ChemMasterBoundUserInterfaceState"/></returns>
|
||||
private ChemMasterBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
var beaker = BeakerContainer.ContainedEntity;
|
||||
EntitySystem.Get<SolutionContainerSystem>().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 <see cref="Solution"/>, eject it.
|
||||
/// Tries to eject into user's hands first, then ejects onto chem master if both hands are full.
|
||||
/// </summary>
|
||||
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<HandsComponent>(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<ChemMasterComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, ChemMasterComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ContainerSlot>(Owner, $"{Name}-reagentContainerContainer");
|
||||
|
||||
InitializeFromPrototype();
|
||||
@@ -228,7 +227,7 @@ namespace Content.Server.Chemistry.Components
|
||||
/// <returns>Returns a <see cref="SharedReagentDispenserComponent.ReagentDispenserBoundUserInterfaceState"/></returns>
|
||||
private ReagentDispenserBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
var beaker = BeakerContainer.ContainedEntity;
|
||||
if (beaker == null || !beaker.TryGetComponent(out FitsInDispenserComponent? fits) ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(beaker, fits.Solution, out var solution))
|
||||
{
|
||||
@@ -252,16 +251,16 @@ namespace Content.Server.Chemistry.Components
|
||||
/// If this component contains an entity with a <see cref="SolutionHolder"/>, eject it.
|
||||
/// Tries to eject into user's hands first, then ejects onto dispenser if both hands are full.
|
||||
/// </summary>
|
||||
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<HandsComponent>(out var hands) ||
|
||||
@@ -276,12 +275,12 @@ namespace Content.Server.Chemistry.Components
|
||||
/// </summary>
|
||||
private void TryClear()
|
||||
{
|
||||
if (!HasBeaker || !_beakerContainer.ContainedEntity!.TryGetComponent(out FitsInDispenserComponent? fits) ||
|
||||
if (!HasBeaker || !BeakerContainer.ContainedEntity!.TryGetComponent(out FitsInDispenserComponent? fits) ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(_beakerContainer.ContainedEntity, fits.Solution, out var solution))
|
||||
.TryGetSolution(BeakerContainer.ContainedEntity, fits.Solution, out var solution))
|
||||
return;
|
||||
|
||||
EntitySystem.Get<SolutionContainerSystem>().RemoveAllSolution(_beakerContainer.ContainedEntity!.Uid, solution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().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<SolutionContainerSystem>()
|
||||
.TryGetSolution(_beakerContainer.ContainedEntity, fits.Solution, out var solution)) return;
|
||||
.TryGetSolution(BeakerContainer.ContainedEntity, fits.Solution, out var solution)) return;
|
||||
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.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<ReagentDispenserComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, ReagentDispenserComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<ReagentDispenserInventoryEntry>
|
||||
{
|
||||
public override int Compare(ReagentDispenserInventoryEntry x, ReagentDispenserInventoryEntry y)
|
||||
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Subjectively, which transfer amount would be best for most activities given the maximum
|
||||
/// transfer amount.
|
||||
/// </summary>
|
||||
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)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Can this entity take reagent from reagent tanks?
|
||||
/// </summary>
|
||||
@@ -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<SolutionTransferComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<SolutionTransferComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<SolutionTransferComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<SolutionTransferComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, SolutionTransferComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<ActorComponent>(out var actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ChemMasterComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<ChemMasterComponent, GetInteractionVerbsEvent>(AddInsertVerb);
|
||||
SubscribeLocalEvent<ChemMasterComponent, GetAlternativeVerbsEvent>(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<FitsInDispenserComponent>() ||
|
||||
!_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,
|
||||
|
||||
@@ -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<ReagentDispenserComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, GetAlternativeVerbsEvent>(AddEjectVerb);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, GetInteractionVerbsEvent>(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<FitsInDispenserComponent>() ||
|
||||
!_actionBlockerSystem.CanDrop(args.User))
|
||||
return;
|
||||
|
||||
if (!args.Using.HasComponent<FitsInDispenserComponent>() ||
|
||||
!_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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Default transfer amounts for the set-transfer verb.
|
||||
/// </summary>
|
||||
public static readonly List<int> DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionTransferComponent, GetAlternativeVerbsEvent>(AddSetTransferVerbs);
|
||||
}
|
||||
|
||||
private void AddSetTransferVerbs(EntityUid uid, SolutionTransferComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount)
|
||||
return;
|
||||
|
||||
if (!args.User.TryGetComponent<ActorComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ClimbingComponent> _activeClimbers = new();
|
||||
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<ClimbableComponent, GetAlternativeVerbsEvent>(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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to vault an object with the ClimbableComponent through right click
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class ClimbVerb : Verb<ClimbableComponent>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,26 +118,6 @@ namespace Content.Server.Clothing.Components
|
||||
{
|
||||
return new MagbootsComponentState(On);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class ToggleMagbootsVerb : Verb<MagbootsComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, MagbootsComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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]
|
||||
|
||||
29
Content.Server/Clothing/MagbootsSystem.cs
Normal file
29
Content.Server/Clothing/MagbootsSystem.cs
Normal file
@@ -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<MagbootsComponent, GetActivationVerbsEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ConfigurationComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ConfigurationComponent component, VerbData data)
|
||||
{
|
||||
var session = user.PlayerSession();
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<ConstructionComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ConstructionComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<TryStartStructureConstructionMessage>(HandleStartStructureConstruction);
|
||||
SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction);
|
||||
SubscribeLocalEvent<ConstructionComponent, GetOtherVerbsEvent>(AddDeconstructVerb);
|
||||
SubscribeLocalEvent<ConstructionComponent, ExaminedEvent>(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<IEntity> EnumerateNearby(IEntity user)
|
||||
|
||||
@@ -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<VerbSystem>().RemoveContainerVisibility(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,31 +313,5 @@ namespace Content.Server.Cuffs.Components
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the uncuffing of a cuffed person. Used by other people and by the component owner to break out of cuffs.
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class UncuffVerb : Verb<CuffableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, CuffableComponent component, VerbData data)
|
||||
{
|
||||
if ((user != component.Owner && !EntitySystem.Get<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<HandCountChangedEvent>(OnHandCountChanged);
|
||||
SubscribeLocalEvent<UncuffAttemptEvent>(OnUncuffAttempt);
|
||||
|
||||
SubscribeLocalEvent<CuffableComponent, GetOtherVerbsEvent>(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<IMobStateComponent>(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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Completely removes all damage from the DamageableComponent (heals the mob).
|
||||
/// </summary>
|
||||
[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<IConGroupController>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(out var player))
|
||||
{
|
||||
if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() &&
|
||||
!target.HasComponent<ThirstComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupController.CanCommand(player.PlayerSession, "rejuvenate"))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
if (user.TryGetComponent<ActorComponent>(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<DamageableSystem>().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<FlammableSystem>().Extinguish(target.Uid, flammable);
|
||||
}
|
||||
|
||||
if (target.TryGetComponent(out CreamPiedComponent? creamPied))
|
||||
{
|
||||
EntitySystem.Get<CreamPieSystem>().SetCreamPied(target.Uid, creamPied, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DisposalRouterComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalRouterComponent component, VerbData data)
|
||||
{
|
||||
var session = user.PlayerSession();
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,34 +150,7 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
base.OnRemove();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ConfigureVerb : Verb<DisposalTaggerComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalTaggerComponent component, VerbData data)
|
||||
{
|
||||
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
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);
|
||||
|
||||
@@ -252,39 +252,5 @@ namespace Content.Server.Disposal.Tube.Components
|
||||
Disconnect();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class TubeDirectionsVerb : Verb<IDisposalTubeComponent>
|
||||
{
|
||||
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<IConGroupController>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(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<IConGroupController>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(out var player))
|
||||
{
|
||||
if (groupController.CanCommand(player.PlayerSession, "tubeconnections"))
|
||||
{
|
||||
component.PopupDirections(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DisposalTubeComponent, PhysicsBodyTypeChangedEvent>(BodyTypeChanged);
|
||||
|
||||
SubscribeLocalEvent<DisposalTubeComponent, RelayMovementEntityEvent>(OnRelayMovement);
|
||||
SubscribeLocalEvent<DisposalTaggerComponent, GetInteractionVerbsEvent>(AddOpenUIVerbs);
|
||||
SubscribeLocalEvent<DisposalRouterComponent, GetInteractionVerbsEvent>(AddOpenUIVerbs);
|
||||
}
|
||||
|
||||
private void AddOpenUIVerbs(EntityUid uid, DisposalTaggerComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!args.User.TryGetComponent<ActorComponent>(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<ActorComponent>(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)
|
||||
|
||||
@@ -193,84 +193,6 @@ namespace Content.Server.Disposal.Unit.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class SelfInsertVerb : Verb<DisposalUnitComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<DisposalUnitComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<DisposalUnitSystem>().Engage(component);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class EjectVerb : Verb<DisposalUnitComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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<DisposalUnitSystem>().TryEjectContents(component);
|
||||
}
|
||||
}
|
||||
|
||||
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
|
||||
{
|
||||
EntitySystem.Get<DisposalUnitSystem>().TryEjectContents(this);
|
||||
|
||||
@@ -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<DisposalUnitComponent> _activeDisposals = new();
|
||||
@@ -58,6 +59,51 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
SubscribeLocalEvent<DisposalUnitComponent, ActivateInWorldEvent>(HandleActivate);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, InteractHandEvent>(HandleInteractHand);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, InteractUsingEvent>(HandleInteractUsing);
|
||||
|
||||
// Verbs
|
||||
SubscribeLocalEvent<DisposalUnitComponent, GetAlternativeVerbsEvent>(AddFlushEjectVerbs);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, GetOtherVerbsEvent>(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)
|
||||
|
||||
@@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// Transfers solution from the held container to the floor.
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class SpillTargetVerb : Verb<SpillableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, SpillableComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.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<SolutionContainerSystem>();
|
||||
if (component.Owner.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
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<SolutionContainerSystem>()
|
||||
.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
|
||||
|
||||
@@ -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<SpillableComponent, GetOtherVerbsEvent>(AddSpillVerb);
|
||||
SubscribeLocalEvent<PuddleComponent, ExaminedEvent>(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<SlipperyComponent>(uid, out var slippery) && slippery.Slippery)
|
||||
|
||||
@@ -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<IConGroupController>();
|
||||
|
||||
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<IConGroupController>();
|
||||
|
||||
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>();
|
||||
ghostRoleSystem.OpenMakeGhostRoleEui(actor.PlayerSession, target.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PullingSystem>().TryStopPull(pullable);
|
||||
|
||||
@@ -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<IConGroupController>();
|
||||
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<IConGroupController>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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 <see
|
||||
/// cref="UserInteraction(IEntity, EntityCoordinates, EntityUid, bool)"/>
|
||||
/// Uses the context menu verb list, and acts out the highest priority alternative interaction verb.
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,47 +614,5 @@ namespace Content.Server.Inventory.Components
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class SetOutfitVerb : Verb<InventoryComponent>
|
||||
{
|
||||
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<IServerConsoleHost>();
|
||||
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<IConGroupController>();
|
||||
return user.TryGetComponent<ActorComponent>(out var player) &&
|
||||
groupController.CanCommand(player.PlayerSession, "setoutfit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ItemComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ItemComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
Content.Server/Items/ItemSystem.cs
Normal file
39
Content.Server/Items/ItemSystem.cs
Normal file
@@ -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<SharedItemComponent, GetInteractionVerbsEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Enables the light if it is not active. Once active it cannot be turned off.
|
||||
/// </summary>
|
||||
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<ExpendableLightComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ExpendableLightComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<HandheldLightComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("toggle-light-verb-get-data-text");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, HandheldLightComponent component)
|
||||
{
|
||||
component.ToggleStatus(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -22,23 +18,5 @@ namespace Content.Server.Light.Components
|
||||
public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Items/flashlight_pda.ogg");
|
||||
|
||||
[ViewVariables] public bool LightOn = false;
|
||||
|
||||
[Verb]
|
||||
public sealed class ToggleFlashlightVerb : Verb<UnpoweredFlashlightComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, UnpoweredFlashlightComponent component, VerbData data)
|
||||
{
|
||||
var canInteract = EntitySystem.Get<ActionBlockerSystem>().CanInteract(user);
|
||||
|
||||
data.Visibility = canInteract ? VerbVisibility.Visible : VerbVisibility.Invisible;
|
||||
data.Text = Loc.GetString("toggle-flashlight-verb-get-data-text");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, UnpoweredFlashlightComponent component)
|
||||
{
|
||||
EntitySystem.Get<UnpoweredFlashlightSystem>().ToggleLight(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Shared.Light.Component;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
@@ -14,5 +17,29 @@ namespace Content.Server.Light.EntitySystems
|
||||
light.Update(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExpendableLightComponent, GetActivationVerbsEvent>(AddIgniteVerb);
|
||||
}
|
||||
|
||||
private void AddIgniteVerb(EntityUid uid, ExpendableLightComponent component, GetActivationVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (component.CurrentState != ExpendableLightState.BrandNew)
|
||||
return;
|
||||
|
||||
// Ignite the flare or make the glowstick glow.
|
||||
// Also hot damn, those are some shitty glowsticks, we need to get a refund.
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("expendable-light-start-verb");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
verb.Act = () => component.TryActivate();
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
@@ -18,6 +20,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ActivateHandheldLightMessage>(HandleActivate);
|
||||
SubscribeLocalEvent<DeactivateHandheldLightMessage>(HandleDeactivate);
|
||||
SubscribeLocalEvent<HandheldLightComponent, GetActivationVerbsEvent>(AddToggleLightVerb);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -44,5 +47,20 @@ namespace Content.Server.Light.EntitySystems
|
||||
handheld.OnUpdate(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetActivationVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("verb-toggle-light");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
verb.Act = component.Activated
|
||||
? () => component.TurnOff()
|
||||
: () => component.TurnOn(args.User);
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.Events;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using System;
|
||||
|
||||
@@ -11,6 +13,27 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
public class UnpoweredFlashlightSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<UnpoweredFlashlightComponent, GetActivationVerbsEvent>(AddToggleLightVerbs);
|
||||
}
|
||||
|
||||
private void AddToggleLightVerbs(EntityUid uid, UnpoweredFlashlightComponent component, GetActivationVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("toggle-flashlight-verb-get-data-text");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
verb.Act = () => ToggleLight(component);
|
||||
verb.Priority = -1; // For things like PDA's, Open-UI and other verbs that should be higher priority.
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
public void ToggleLight(UnpoweredFlashlightComponent flashlight)
|
||||
{
|
||||
if (!flashlight.Owner.TryGetComponent(out PointLightComponent? light))
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
using Content.Server.Lock;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -23,44 +17,5 @@ namespace Content.Server.Storage.Components
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockOnClick")] public bool LockOnClick { get; set; } = false;
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("unlockingSound")] public SoundSpecifier UnlockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound")] public SoundSpecifier LockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||
|
||||
[Verb]
|
||||
private sealed class ToggleLockVerb : Verb<LockComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, LockComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
|
||||
component.Owner.TryGetComponent(out EntityStorageComponent? entityStorageComponent) && entityStorageComponent.Open)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, LockComponent component)
|
||||
{
|
||||
// Do checks
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
|
||||
!user.InRangeUnobstructed(component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Call relevant entity system
|
||||
var lockSystem = user.EntityManager.EntitySysManager.GetEntitySystem<LockSystem>();
|
||||
if (component.Locked)
|
||||
{
|
||||
lockSystem.DoUnlock(component.Owner.Uid, user, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
lockSystem.DoLock(component.Owner.Uid, user, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -26,6 +27,7 @@ namespace Content.Server.Lock
|
||||
SubscribeLocalEvent<LockComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<LockComponent, ActivateInWorldEvent>(OnActivated);
|
||||
SubscribeLocalEvent<LockComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<LockComponent, GetAlternativeVerbsEvent>(AddToggleLockVerb);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args)
|
||||
@@ -38,14 +40,17 @@ namespace Content.Server.Lock
|
||||
|
||||
private void OnActivated(EntityUid uid, LockComponent lockComp, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// Only attempt an unlock by default on Activate
|
||||
if (lockComp.Locked)
|
||||
{
|
||||
DoUnlock(uid, args.User, lockComp);
|
||||
args.Handled = TryUnlock(uid, args.User, lockComp);
|
||||
}
|
||||
else if (lockComp.LockOnClick)
|
||||
{
|
||||
DoLock(uid, args.User, lockComp);
|
||||
args.Handled = TryLock(uid, args.User, lockComp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,16 +62,20 @@ namespace Content.Server.Lock
|
||||
("entityName", lockComp.Owner.Name)));
|
||||
}
|
||||
|
||||
public bool DoLock(EntityUid uid, IEntity user, LockComponent? lockComp = null)
|
||||
public bool TryLock(EntityUid uid, IEntity user, LockComponent? lockComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref lockComp))
|
||||
return false;
|
||||
|
||||
if (!HasUserAccess(uid, user))
|
||||
if (!CanToggleLock(uid, user, quiet: false))
|
||||
return false;
|
||||
|
||||
if (!HasUserAccess(uid, user, quiet: false))
|
||||
return false;
|
||||
|
||||
lockComp.Owner.PopupMessage(user, Loc.GetString("lock-comp-do-lock-success", ("entityName",lockComp.Owner.Name)));
|
||||
lockComp.Locked = true;
|
||||
|
||||
if(lockComp.LockSound != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(lockComp.Owner), lockComp.LockSound.GetSound(), lockComp.Owner, AudioParams.Default.WithVolume(-5));
|
||||
@@ -82,16 +91,20 @@ namespace Content.Server.Lock
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DoUnlock(EntityUid uid, IEntity user, LockComponent? lockComp = null)
|
||||
public bool TryUnlock(EntityUid uid, IEntity user, LockComponent? lockComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref lockComp))
|
||||
return false;
|
||||
|
||||
if (!HasUserAccess(uid, user))
|
||||
if (!CanToggleLock(uid, user, quiet: false))
|
||||
return false;
|
||||
|
||||
if (!HasUserAccess(uid, user, quiet: false))
|
||||
return false;
|
||||
|
||||
lockComp.Owner.PopupMessage(user, Loc.GetString("lock-comp-do-unlock-success", ("entityName", lockComp.Owner.Name)));
|
||||
lockComp.Locked = false;
|
||||
|
||||
if(lockComp.UnlockSound != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(lockComp.Owner), lockComp.UnlockSound.GetSound(), lockComp.Owner, AudioParams.Default.WithVolume(-5));
|
||||
@@ -104,11 +117,26 @@ namespace Content.Server.Lock
|
||||
|
||||
RaiseLocalEvent(lockComp.Owner.Uid, new LockToggledEvent(false));
|
||||
|
||||
// To stop EntityStorageComponent from opening right after the container gets unlocked
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HasUserAccess(EntityUid uid, IEntity user, AccessReader? reader = null)
|
||||
public bool CanToggleLock(EntityUid uid, IEntity user, EntityStorageComponent? storage = null, bool quiet = true)
|
||||
{
|
||||
if (!Resolve(uid, ref storage))
|
||||
return true;
|
||||
|
||||
// Cannot lock if the entity is currently opened.
|
||||
if (storage.Open)
|
||||
return false;
|
||||
|
||||
// Cannot (un)lock from the inside. Maybe a bad idea? Security jocks could trap nerds in lockers?
|
||||
if (storage.Contents.Contains(user))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HasUserAccess(EntityUid uid, IEntity user, AccessReader? reader = null, bool quiet = true)
|
||||
{
|
||||
// Not having an AccessComponent means you get free access. woo!
|
||||
if (!Resolve(uid, ref reader))
|
||||
@@ -116,12 +144,26 @@ namespace Content.Server.Lock
|
||||
|
||||
if (!reader.IsAllowed(user))
|
||||
{
|
||||
if (!quiet)
|
||||
reader.Owner.PopupMessage(user, Loc.GetString("lock-comp-has-user-access-fail"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || !CanToggleLock(uid, args.User))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = component.Locked ?
|
||||
() => TryUnlock(uid, args.User, component) :
|
||||
() => TryLock(uid, args.User, component);
|
||||
verb.Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock");
|
||||
// TODO VERB ICONS need padlock open/close icons.
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -167,51 +166,6 @@ namespace Content.Server.Medical.Components
|
||||
UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EnterVerb : Verb<MedicalScannerComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("enter-verb-get-data-text");
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
||||
{
|
||||
component.InsertBody(user);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectVerb : Verb<MedicalScannerComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("medical-scanner-eject-verb-get-data-text");
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
||||
{
|
||||
component.EjectBody();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertBody(IEntity user)
|
||||
{
|
||||
_bodyContainer.Insert(user);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Content.Server.Medical.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Movement;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Medical
|
||||
@@ -11,13 +13,64 @@ namespace Content.Server.Medical
|
||||
[UsedImplicitly]
|
||||
internal sealed class MedicalScannerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MedicalScannerComponent, RelayMovementEntityEvent>(OnRelayMovement);
|
||||
SubscribeLocalEvent<MedicalScannerComponent, GetInteractionVerbsEvent>(AddInsertOtherVerb);
|
||||
SubscribeLocalEvent<MedicalScannerComponent, GetAlternativeVerbsEvent>(AddAlternativeVerbs);
|
||||
}
|
||||
|
||||
private void AddInsertOtherVerb(EntityUid uid, MedicalScannerComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Using == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
component.IsOccupied ||
|
||||
!component.CanInsert(args.Using))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => component.InsertBody(args.Using);
|
||||
verb.Category = VerbCategory.Insert;
|
||||
verb.Text = args.Using.Name;
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddAlternativeVerbs(EntityUid uid, MedicalScannerComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
// Eject verb
|
||||
if (component.IsOccupied)
|
||||
{
|
||||
Verb verb = new();
|
||||
verb.Act = () => component.EjectBody();
|
||||
verb.Category = VerbCategory.Eject;
|
||||
verb.Text = Loc.GetString("medical-scanner-verb-noun-occupant");
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
// Self-insert verb
|
||||
if (!component.IsOccupied &&
|
||||
component.CanInsert(args.User) &&
|
||||
_actionBlockerSystem.CanMove(args.User))
|
||||
{
|
||||
Verb verb = new();
|
||||
verb.Act = () => component.InsertBody(args.User);
|
||||
verb.Text = Loc.GetString("medical-scanner-verb-enter");
|
||||
// TODO VERN ICON
|
||||
// TODO VERB CATEGORY
|
||||
// create a verb category for "enter"?
|
||||
// See also, disposal unit. Also maybe add verbs for entering lockers/body bags?
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent component, RelayMovementEntityEvent args)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.AI.Components;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Emoting;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Shared.Console;
|
||||
@@ -59,6 +60,7 @@ namespace Content.Server.Mind.Commands
|
||||
entity.EnsureComponent<SharedPlayerMobMoverComponent>();
|
||||
entity.EnsureComponent<SharedSpeechComponent>();
|
||||
entity.EnsureComponent<SharedEmotingComponent>();
|
||||
entity.EnsureComponent<ExaminerComponent>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Players;
|
||||
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.Mind.Verbs
|
||||
{
|
||||
[GlobalVerb]
|
||||
public class ControlMobVerb : 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<IConGroupController>();
|
||||
if (user == target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(out var player))
|
||||
{
|
||||
if (!user.HasComponent<MindComponent>() || !target.HasComponent<MindComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupController.CanCommand(player.PlayerSession, "controlmob"))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("control-mob-verb-get-data-text");
|
||||
data.CategoryData = VerbCategories.Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
|
||||
var player = user.GetComponent<ActorComponent>().PlayerSession;
|
||||
if (!groupController.CanCommand(player, "controlmob"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var userMind = player.ContentData()?.Mind;
|
||||
|
||||
var targetMind = target.GetComponent<MindComponent>();
|
||||
|
||||
targetMind.Mind?.TransferTo(null);
|
||||
userMind?.TransferTo(target, ghostCheckOverride: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using Content.Server.Mind.Commands;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Mind.Verbs
|
||||
{
|
||||
[GlobalVerb]
|
||||
public class MakeSentientVerb : 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<IConGroupController>();
|
||||
|
||||
if (user == target || target.HasComponent<MindComponent>())
|
||||
return;
|
||||
|
||||
var player = user.GetComponent<ActorComponent>().PlayerSession;
|
||||
if (groupController.CanCommand(player, "makesentient"))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("make-sentient-verb-get-data-text");
|
||||
data.CategoryData = VerbCategories.Debug;
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/sentient.svg.192dpi.png";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
|
||||
var player = user.GetComponent<ActorComponent>().PlayerSession;
|
||||
if (!groupController.CanCommand(player, "makesentient"))
|
||||
return;
|
||||
|
||||
var host = IoCManager.Resolve<IServerConsoleHost>();
|
||||
var cmd = new MakeSentientCommand();
|
||||
var uidStr = target.Uid.ToString();
|
||||
cmd.Execute(new ConsoleShell(host, player), $"{cmd.Command} {uidStr}",
|
||||
new[] {uidStr});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,12 @@ using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.Paper;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Morgue;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -98,27 +96,5 @@ namespace Content.Server.Morgue.Components
|
||||
_appearance?.SetData(BodyBagVisuals.Label, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Verb]
|
||||
private sealed class RemoveLabelVerb : Verb<BodyBagEntityStorageComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, BodyBagEntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || component.LabelContainer?.ContainedEntity == null)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("remove-label-verb-get-data-text");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Activate(IEntity user, BodyBagEntityStorageComponent component)
|
||||
{
|
||||
component.RemoveLabel(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ using Content.Server.GameTicking;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Morgue;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -155,26 +153,5 @@ namespace Content.Server.Morgue.Components
|
||||
|
||||
return SuicideKind.Heat;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class CremateVerb : Verb<CrematoriumEntityStorageComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, CrematoriumEntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || component.Cooking || component.Open)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("cremate-verb-get-data-text");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Activate(IEntity user, CrematoriumEntityStorageComponent component)
|
||||
{
|
||||
component.TryCremate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Server.Morgue.Components;
|
||||
using Content.Server.Morgue.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Morgue
|
||||
{
|
||||
@@ -10,6 +12,42 @@ namespace Content.Server.Morgue
|
||||
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CrematoriumEntityStorageComponent, GetAlternativeVerbsEvent>(AddCremateVerb);
|
||||
SubscribeLocalEvent<BodyBagEntityStorageComponent, GetAlternativeVerbsEvent>(AddRemoveLabelVerb);
|
||||
}
|
||||
|
||||
private void AddCremateVerb(EntityUid uid, CrematoriumEntityStorageComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || component.Cooking || component.Open)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("cremate-verb-get-data-text");
|
||||
// TODO VERB ICON add flame/burn symbol?
|
||||
verb.Act = () => component.TryCremate();
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This adds the "remove label" verb to the list of verbs. Yes, this is a stupid function name, but it's
|
||||
/// consistent with other get-verb event handlers.
|
||||
/// </summary>
|
||||
private void AddRemoveLabelVerb(EntityUid uid, BodyBagEntityStorageComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract || component.LabelContainer?.ContainedEntity == null)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("remove-label-verb-get-data-text");
|
||||
// TODO VERB ICON Add cancel/X icon? or maybe just use the pick-up or eject icon?
|
||||
verb.Act = () => component.RemoveLabel(args.User);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
_accumulatedFrameTime += frameTime;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -52,76 +47,5 @@ namespace Content.Server.PDA
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
// TODO: replace me with dynamic verbs for ItemSlotsSystem
|
||||
#region Verbs
|
||||
[Verb]
|
||||
public sealed class EjectPenVerb : Verb<PDAComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!component.Owner.TryGetComponent(out SharedItemSlotsComponent? slots))
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return;
|
||||
|
||||
var item = EntitySystem.Get<SharedItemSlotsSystem>().PeekItemInSlot(slots, PenSlotName);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("eject-item-verb-text-default", ("item", item.Name));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent pda)
|
||||
{
|
||||
var entityManager = pda.Owner.EntityManager;
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
{
|
||||
entityManager.EntitySysManager.GetEntitySystem<SharedItemSlotsSystem>().
|
||||
TryEjectContent(itemSlots, PenSlotName, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectIDVerb : Verb<PDAComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!component.Owner.TryGetComponent(out SharedItemSlotsComponent? slots))
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return;
|
||||
|
||||
var item = EntitySystem.Get<SharedItemSlotsSystem>().PeekItemInSlot(slots, IDSlotName);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("eject-item-verb-text-default", ("item", item.Name));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent pda)
|
||||
{
|
||||
var entityManager = pda.Owner.EntityManager;
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
{
|
||||
entityManager.EntitySysManager.GetEntitySystem<SharedItemSlotsSystem>().
|
||||
TryEjectContent(itemSlots, IDSlotName, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.Light.Events;
|
||||
@@ -7,6 +6,7 @@ using Content.Server.Traitor.Uplink;
|
||||
using Content.Server.Traitor.Uplink.Components;
|
||||
using Content.Server.Traitor.Uplink.Systems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.PDA;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -24,6 +24,7 @@ namespace Content.Server.PDA
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PDAComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<PDAComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<PDAComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
@@ -185,6 +186,8 @@ namespace Content.Server.Pointing.EntitySystems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GetOtherVerbsEvent>(AddPointingVerb);
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
|
||||
CommandBinds.Builder
|
||||
@@ -192,6 +195,26 @@ namespace Content.Server.Pointing.EntitySystems
|
||||
.Register<PointingSystem>();
|
||||
}
|
||||
|
||||
private void AddPointingVerb(GetOtherVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null)
|
||||
return;
|
||||
|
||||
//Check if the object is already being pointed at
|
||||
if (args.Target.HasComponent<PointingArrowComponent>())
|
||||
return;
|
||||
|
||||
if (!args.User.TryGetComponent<ActorComponent>(out var actor) ||
|
||||
!InRange(args.User, args.Target.Transform.Coordinates))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("pointing-verb-get-data-text");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/point.svg.192dpi.png";
|
||||
verb.Act = () => TryPoint(actor.PlayerSession, args.Target.Transform.Coordinates, args.Target.Uid); ;
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using Content.Server.Pointing.Components;
|
||||
using Content.Server.Pointing.EntitySystems;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Pointing
|
||||
{
|
||||
/// <summary>
|
||||
/// Global verb that points at an entity.
|
||||
/// </summary>
|
||||
[GlobalVerb]
|
||||
public class PointingVerb : GlobalVerb
|
||||
{
|
||||
public override bool RequireInteractionRange => false;
|
||||
|
||||
public override void GetData(IEntity user, IEntity target, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/point.svg.192dpi.png";
|
||||
|
||||
if (!user.HasComponent<ActorComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<PointingSystem>().InRange(user, target.Transform.Coordinates))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.HasComponent<PointingArrowComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
|
||||
data.Text = Loc.GetString("pointing-verb-get-data-text");
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
if (!user.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<PointingSystem>().TryPoint(actor.PlayerSession, target.Transform.Coordinates, target.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,9 @@ using System.Threading.Tasks;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.Weapon.Ranged.Barrels.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -25,7 +23,9 @@ namespace Content.Server.Power.Components
|
||||
private BatteryComponent? _heldBattery;
|
||||
|
||||
[ViewVariables]
|
||||
private ContainerSlot _container = default!;
|
||||
public ContainerSlot Container = default!;
|
||||
|
||||
public bool HasCell => Container.ContainedEntity != null;
|
||||
|
||||
[ViewVariables]
|
||||
private CellChargerStatus _status;
|
||||
@@ -43,7 +43,7 @@ namespace Content.Server.Power.Components
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponent<ApcPowerReceiverComponent>();
|
||||
_container = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powerCellContainer");
|
||||
Container = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powerCellContainer");
|
||||
// Default state in the visualizer is OFF, so when this gets powered on during initialization it will generally show empty
|
||||
}
|
||||
|
||||
@@ -85,15 +85,15 @@ namespace Content.Server.Power.Components
|
||||
/// This will remove the item directly into the user's hand / floor
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
private void RemoveItem(IEntity user)
|
||||
public void RemoveItem(IEntity user)
|
||||
{
|
||||
var heldItem = _container.ContainedEntity;
|
||||
var heldItem = Container.ContainedEntity;
|
||||
if (heldItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_container.Remove(heldItem);
|
||||
Container.Remove(heldItem);
|
||||
_heldBattery = null;
|
||||
if (user.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
@@ -113,81 +113,6 @@ namespace Content.Server.Power.Components
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class InsertVerb : Verb<BaseCharger>
|
||||
{
|
||||
protected override void GetData(IEntity user, BaseCharger component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
if (!user.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
var heldItemName = Loc.GetString(handsComponent.GetActiveHand.Owner.Name);
|
||||
|
||||
data.Text = Loc.GetString("insert-verb-get-data-text", ("itemName", heldItemName));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/insert.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, BaseCharger component)
|
||||
{
|
||||
if (!user.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (handsComponent.GetActiveHand == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userItem = handsComponent.GetActiveHand.Owner;
|
||||
handsComponent.Drop(userItem);
|
||||
component.TryInsertItem(userItem);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class EjectVerb : Verb<BaseCharger>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, BaseCharger component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
if (component._container.ContainedEntity == null)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
var containerItemName = Loc.GetString(component._container.ContainedEntity.Name);
|
||||
|
||||
data.Text = Loc.GetString("eject-verb-get-data-text",("containerName", containerItemName));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, BaseCharger component)
|
||||
{
|
||||
component.RemoveItem(user);
|
||||
}
|
||||
}
|
||||
|
||||
private CellChargerStatus GetStatus()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) &&
|
||||
@@ -195,7 +120,7 @@ namespace Content.Server.Power.Components
|
||||
{
|
||||
return CellChargerStatus.Off;
|
||||
}
|
||||
if (_container.ContainedEntity == null)
|
||||
if (!HasCell)
|
||||
{
|
||||
return CellChargerStatus.Empty;
|
||||
}
|
||||
@@ -206,13 +131,13 @@ namespace Content.Server.Power.Components
|
||||
return CellChargerStatus.Charging;
|
||||
}
|
||||
|
||||
private bool TryInsertItem(IEntity entity)
|
||||
public bool TryInsertItem(IEntity entity)
|
||||
{
|
||||
if (!IsEntityCompatible(entity) || _container.ContainedEntity != null)
|
||||
if (!IsEntityCompatible(entity) || HasCell)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_container.Insert(entity))
|
||||
if (!Container.Insert(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -224,7 +149,7 @@ namespace Content.Server.Power.Components
|
||||
/// <summary>
|
||||
/// If the supplied entity should fit into the charger.
|
||||
/// </summary>
|
||||
protected abstract bool IsEntityCompatible(IEntity entity);
|
||||
public abstract bool IsEntityCompatible(IEntity entity);
|
||||
|
||||
protected abstract BatteryComponent? GetBatteryFrom(IEntity entity);
|
||||
|
||||
@@ -264,12 +189,12 @@ namespace Content.Server.Power.Components
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
appearance?.SetData(CellVisual.Occupied, _container.ContainedEntity != null);
|
||||
appearance?.SetData(CellVisual.Occupied, HasCell);
|
||||
}
|
||||
|
||||
public void OnUpdate(float frameTime) //todo: make single system for this
|
||||
{
|
||||
if (_status == CellChargerStatus.Empty || _status == CellChargerStatus.Charged || _container.ContainedEntity == null)
|
||||
if (_status == CellChargerStatus.Empty || _status == CellChargerStatus.Charged || !HasCell)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.PowerCell.Components;
|
||||
using Content.Server.Weapon;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class BaseChargerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerCellChargerComponent, GetAlternativeVerbsEvent>(AddEjectVerb);
|
||||
SubscribeLocalEvent<PowerCellChargerComponent, GetInteractionVerbsEvent>(AddInsertVerb);
|
||||
SubscribeLocalEvent<WeaponCapacitorChargerComponent, GetAlternativeVerbsEvent>(AddEjectVerb);
|
||||
SubscribeLocalEvent<WeaponCapacitorChargerComponent, GetInteractionVerbsEvent>(AddInsertVerb);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var comp in EntityManager.EntityQuery<BaseCharger>(true))
|
||||
@@ -14,5 +31,39 @@ namespace Content.Server.Power.EntitySystems
|
||||
comp.OnUpdate(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system?
|
||||
private void AddEjectVerb(EntityUid uid, BaseCharger component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
!component.HasCell ||
|
||||
!_actionBlockerSystem.CanPickup(args.User))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = component.Container.ContainedEntity!.Name;
|
||||
verb.Category = VerbCategory.Eject;
|
||||
verb.Act = () => component.RemoveItem(args.User);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddInsertVerb(EntityUid uid, BaseCharger component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Using == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
component.HasCell ||
|
||||
!component.IsEntityCompatible(args.Using) ||
|
||||
!_actionBlockerSystem.CanDrop(args.User))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = args.Using.Name;
|
||||
verb.Category = VerbCategory.Insert;
|
||||
verb.Act = () => component.TryInsertItem(args.Using);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Content.Server.PowerCell.Components
|
||||
{
|
||||
public override string Name => "PowerCellCharger";
|
||||
|
||||
protected override bool IsEntityCompatible(IEntity entity)
|
||||
public override bool IsEntityCompatible(IEntity entity)
|
||||
{
|
||||
return entity.HasComponent<BatteryComponent>();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -176,42 +173,6 @@ namespace Content.Server.PowerCell.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectCellVerb : Verb<PowerCellSlotComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, PowerCellSlotComponent component, VerbData data)
|
||||
{
|
||||
if (!component.ShowVerb || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Cell == null)
|
||||
{
|
||||
data.Text = Loc.GetString("power-cell-slot-component-no-cell");
|
||||
data.Visibility = VerbVisibility.Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Text = Loc.GetString("power-cell-slot-component-eject-cell");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
if (component.Cell == null || !component.CanRemoveCell)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PowerCellSlotComponent component)
|
||||
{
|
||||
component.EjectCell(user);
|
||||
}
|
||||
}
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
if (_startEmpty || _cellContainer.ContainedEntity != null)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.PowerCell.Components;
|
||||
using Content.Server.PowerCell.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -10,12 +12,50 @@ namespace Content.Server.PowerCell
|
||||
public class PowerCellSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerCellComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, GetAlternativeVerbsEvent>(AddEjectVerb);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, GetInteractionVerbsEvent>(AddInsertVerb);
|
||||
}
|
||||
|
||||
// TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system?
|
||||
private void AddEjectVerb(EntityUid uid, PowerCellSlotComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
!component.ShowVerb ||
|
||||
!component.HasCell ||
|
||||
!_actionBlockerSystem.CanPickup(args.User))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = component.Cell!.Name;
|
||||
verb.Category = VerbCategory.Eject;
|
||||
verb.Act = () => component.EjectCell(args.User);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddInsertVerb(EntityUid uid, PowerCellSlotComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Using == null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract ||
|
||||
component.HasCell ||
|
||||
!args.Using.HasComponent<PowerCellComponent>() ||
|
||||
!_actionBlockerSystem.CanDrop(args.User))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Text = args.Using.Name;
|
||||
verb.Category = VerbCategory.Insert;
|
||||
verb.Act = () => component.InsertCell(args.Using);
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, PowerCellComponent component, SolutionChangedEvent args)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Content.Shared.Pulling;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Server.Pulling
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedPullableComponent))]
|
||||
public class PullableComponent : SharedPullableComponent
|
||||
{
|
||||
[Verb]
|
||||
public class PullingVerb : Verb<PullableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PullableComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (user == component.Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.Transform.Coordinates.TryDistance(user.EntityManager, component.Owner.Transform.Coordinates, out var distance) ||
|
||||
distance > SharedInteractionSystem.InteractionRange)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.HasComponent<ISharedHandsComponent>() ||
|
||||
!user.TryGetComponent(out IPhysBody? userPhysics) ||
|
||||
!component.Owner.TryGetComponent(out IPhysBody? targetPhysics) ||
|
||||
targetPhysics.BodyType == BodyType.Static)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = component.Puller == userPhysics.Owner
|
||||
? Loc.GetString("pulling-verb-get-data-text-stop-pulling")
|
||||
: Loc.GetString("pulling-verb-get-data-text");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PullableComponent component)
|
||||
{
|
||||
// There used to be sanity checks here for no reason.
|
||||
// Why no reason? Because they're supposed to be performed in TryStartPull.
|
||||
if (component.Puller == user)
|
||||
{
|
||||
EntitySystem.Get<SharedPullingSystem>().TryStopPull(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
EntitySystem.Get<SharedPullingSystem>().TryStartPull(component.Owner, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ namespace Content.Server.Pulling
|
||||
|
||||
UpdatesAfter.Add(typeof(PhysicsSystem));
|
||||
|
||||
SubscribeLocalEvent<PullableComponent, PullableMoveMessage>(OnPullableMove);
|
||||
SubscribeLocalEvent<PullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullableMoveMessage>(OnPullableMove);
|
||||
SubscribeLocalEvent<SharedPullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
|
||||
|
||||
19
Content.Server/Rotatable/FlippableComponent.cs
Normal file
19
Content.Server/Rotatable/FlippableComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Rotatable
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class FlippableComponent : Component
|
||||
{
|
||||
public override string Name => "Flippable";
|
||||
|
||||
/// <summary>
|
||||
/// Entity to replace this entity with when the current one is 'flipped'.
|
||||
/// </summary>
|
||||
[DataField("mirrorEntity", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string MirrorEntity = default!;
|
||||
}
|
||||
}
|
||||
82
Content.Server/Rotatable/RotatableSystem.cs
Normal file
82
Content.Server/Rotatable/RotatableSystem.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rotatable;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Server.Rotatable
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles verbs for the <see cref="RotatableComponent"/> and <see cref="FlippableComponent"/> components.
|
||||
/// </summary>
|
||||
public class RotatableSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FlippableComponent, GetOtherVerbsEvent>(AddFlipVerb);
|
||||
SubscribeLocalEvent<RotatableComponent, GetOtherVerbsEvent>(AddRotateVerbs);
|
||||
}
|
||||
|
||||
private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetOtherVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || component.MirrorEntity == null)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => TryFlip(component, args.User);
|
||||
verb.Text = Loc.GetString("flippable-verb-get-data-text");
|
||||
// TODO VERB ICONS Add Uno reverse card style icon?
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetOtherVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
// Check if the object is anchored, and whether we are still allowed to rotate it.
|
||||
if (!component.RotateWhileAnchored &&
|
||||
component.Owner.TryGetComponent(out IPhysBody? physics) &&
|
||||
physics.BodyType == BodyType.Static)
|
||||
return;
|
||||
|
||||
// rotate clockwise
|
||||
Verb rotateCW = new();
|
||||
rotateCW.Act = () => component.Owner.Transform.LocalRotation += Angle.FromDegrees(-90);
|
||||
rotateCW.Category = VerbCategory.Rotate;
|
||||
rotateCW.IconTexture = "/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png";
|
||||
rotateCW.Priority = -2; // show CCW, then CW
|
||||
rotateCW.CloseMenu = false; // allow for easy double rotations.
|
||||
args.Verbs.Add(rotateCW);
|
||||
|
||||
// rotate counter-clockwise
|
||||
Verb rotateCCW = new();
|
||||
rotateCCW.Act = () => component.Owner.Transform.LocalRotation += Angle.FromDegrees(90);
|
||||
rotateCCW.Category = VerbCategory.Rotate;
|
||||
rotateCCW.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png";
|
||||
rotateCCW.Priority = -1;
|
||||
rotateCCW.CloseMenu = false; // allow for easy double rotations.
|
||||
args.Verbs.Add(rotateCCW);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace a flippable entity with it's flipped / mirror-symmetric entity.
|
||||
/// </summary>
|
||||
public static void TryFlip(FlippableComponent component, IEntity user)
|
||||
{
|
||||
// TODO FLIPPABLE Currently an entity needs to be un-anchored when flipping. But the newly spawned entity
|
||||
// defaults to being anchored (and spawns under floor tiles). Fix this?
|
||||
if (component.Owner.TryGetComponent(out IPhysBody? physics) &&
|
||||
physics.BodyType == BodyType.Static)
|
||||
{
|
||||
component.Owner.PopupMessage(user, Loc.GetString("flippable-component-try-flip-is-stuck"));
|
||||
return;
|
||||
}
|
||||
|
||||
component.Owner.EntityManager.SpawnEntity(component.MirrorEntity, component.Owner.Transform.Coordinates);
|
||||
component.Owner.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Rotation.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class FlippableComponent : Component
|
||||
{
|
||||
public override string Name => "Flippable";
|
||||
|
||||
|
||||
private string? _entity => _internalEntity ?? Owner.Prototype?.ID;
|
||||
|
||||
[DataField("entity")]
|
||||
private string? _internalEntity;
|
||||
|
||||
private void TryFlip(IEntity user)
|
||||
{
|
||||
if (Owner.TryGetComponent(out IPhysBody? physics) &&
|
||||
physics.BodyType == BodyType.Static)
|
||||
{
|
||||
Owner.PopupMessage(user, Loc.GetString("flippable-component-try-flip-is-stuck"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Owner.EntityManager.SpawnEntity(_entity, Owner.Transform.Coordinates);
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class FlippableVerb : Verb<FlippableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, FlippableComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("flippable-verb-get-data-text");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, FlippableComponent component)
|
||||
{
|
||||
component.TryFlip(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rotatable;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Server.Rotation.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedRotatableComponent))]
|
||||
public class RotatableComponent : SharedRotatableComponent
|
||||
{
|
||||
private void TryRotate(IEntity user, Angle angle)
|
||||
{
|
||||
if (!RotateWhileAnchored && Owner.TryGetComponent(out IPhysBody? physics))
|
||||
{
|
||||
if (physics.BodyType == BodyType.Static)
|
||||
{
|
||||
Owner.PopupMessage(user, Loc.GetString("rotatable-component-try-rotate-stuck"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Owner.Transform.LocalRotation += angle;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class RotateVerb : Verb<RotatableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysBody? physics) && physics.BodyType == BodyType.Static))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.CategoryData = VerbCategories.Rotate;
|
||||
data.Text = Loc.GetString("rotate-verb-get-data-text");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, RotatableComponent component)
|
||||
{
|
||||
component.TryRotate(user, Angle.FromDegrees(-90));
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class RotateCounterVerb : Verb<RotatableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysBody? physics) && physics.BodyType == BodyType.Static))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.CategoryData = VerbCategories.Rotate;
|
||||
data.Text = Loc.GetString("rotate-counter-verb-get-data-text");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, RotatableComponent component)
|
||||
{
|
||||
component.TryRotate(user, Angle.FromDegrees(90));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
namespace Content.Server.Rotation
|
||||
{
|
||||
// Mapping tools
|
||||
// Uncomment if you need them, I guess.
|
||||
|
||||
/*
|
||||
[GlobalVerb]
|
||||
public sealed class HardRotateCcwVerb : GlobalVerb
|
||||
{
|
||||
public override bool RequireInteractionRange => false;
|
||||
|
||||
public override void GetData(IEntity user, IEntity target, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = "Rotate CCW";
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png";
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
target.Transform.LocalRotation += Math.PI / 2;
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalVerb]
|
||||
public sealed class HardRotateCwVerb : GlobalVerb
|
||||
{
|
||||
public override bool RequireInteractionRange => false;
|
||||
|
||||
public override void GetData(IEntity user, IEntity target, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = "Rotate CW";
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png";
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
target.Transform.LocalRotation -= Math.PI / 2;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user