Fancy Verb Menu & Verb API Refactor (#928)

This commit is contained in:
Pieter-Jan Briers
2020-05-23 03:09:44 +02:00
committed by GitHub
parent 4527fc9e84
commit cad59d2cb4
33 changed files with 1099 additions and 399 deletions

View File

@@ -1,25 +1,37 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Threading;
using Content.Client.State; using Content.Client.State;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input; using Content.Shared.Input;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State; using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.GameObjects.EntitySystems
{ {
@@ -31,11 +43,21 @@ namespace Content.Client.GameObjects.EntitySystems
[Dependency] private readonly IEntityManager _entityManager; [Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IInputManager _inputManager; [Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IItemSlotManager _itemSlotManager;
[Dependency] private readonly IGameTiming _gameTiming;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
[Dependency] private readonly IResourceCache _resourceCache;
#pragma warning restore 649 #pragma warning restore 649
private VerbPopup _currentPopup; private EntityList _currentEntityList;
private VerbPopup _currentVerbListRoot;
private VerbPopup _currentGroupList;
private EntityUid _currentEntity; private EntityUid _currentEntity;
private bool IsAnyContextMenuOpen => _currentEntityList != null || _currentVerbListRoot != null;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -51,29 +73,28 @@ namespace Content.Client.GameObjects.EntitySystems
public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates) public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
{ {
if (_currentPopup != null) if (_currentVerbListRoot != null)
{ {
CloseContextMenu(); CloseVerbMenu();
} }
_currentEntity = entity.Uid; _currentEntity = entity.Uid;
_currentPopup = new VerbPopup(); _currentVerbListRoot = new VerbPopup();
_currentPopup.UserInterfaceManager.ModalRoot.AddChild(_currentPopup); _userInterfaceManager.ModalRoot.AddChild(_currentVerbListRoot);
_currentPopup.OnPopupHide += CloseContextMenu; _currentVerbListRoot.OnPopupHide += CloseVerbMenu;
_currentPopup.List.AddChild(new Label {Text = "Waiting on Server..."}); _currentVerbListRoot.List.AddChild(new Label {Text = "Waiting on Server..."});
RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity)); RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
var size = _currentPopup.List.CombinedMinimumSize; var box = UIBox2.FromDimensions(screenCoordinates.Position, (1, 1));
var box = UIBox2.FromDimensions(screenCoordinates.Position, size); _currentVerbListRoot.Open(box);
_currentPopup.Open(box);
} }
private bool OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args) private bool OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{ {
if (_currentPopup != null) if (IsAnyContextMenuOpen)
{ {
CloseContextMenu(); CloseAllMenus();
return true; return true;
} }
@@ -89,20 +110,29 @@ namespace Content.Client.GameObjects.EntitySystems
return false; return false;
} }
_currentPopup = new VerbPopup(); _currentEntityList = new EntityList();
_currentPopup.OnPopupHide += CloseContextMenu; _currentEntityList.OnPopupHide += CloseAllMenus;
foreach (var entity in entities) for (var i = 0; i < entities.Count; i++)
{ {
var button = new Button {Text = entity.Name}; if (i != 0)
_currentPopup.List.AddChild(button); {
button.OnPressed += _ => OnContextButtonPressed(entity); _currentEntityList.List.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
var entity = entities[i];
_currentEntityList.List.AddChild(new EntityButton(this, entity));
} }
_currentPopup.UserInterfaceManager.ModalRoot.AddChild(_currentPopup); _userInterfaceManager.ModalRoot.AddChild(_currentEntityList);
var size = _currentPopup.List.CombinedMinimumSize; var size = _currentEntityList.List.CombinedMinimumSize;
var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size); var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size);
_currentPopup.Open(box); _currentEntityList.Open(box);
return true; return true;
} }
@@ -119,28 +149,31 @@ namespace Content.Client.GameObjects.EntitySystems
return; return;
} }
DebugTools.AssertNotNull(_currentPopup); DebugTools.AssertNotNull(_currentVerbListRoot);
var buttons = new Dictionary<string, List<Button>>(); var buttons = new Dictionary<string, List<ListedVerbData>>();
var groupIcons = new Dictionary<string, SpriteSpecifier>();
var vBox = _currentPopup.List; var vBox = _currentVerbListRoot.List;
vBox.DisposeAllChildren(); vBox.DisposeAllChildren();
// Local variable so that scope capture ensures this is the correct value.
var curEntity = _currentEntity;
foreach (var data in msg.Verbs) foreach (var data in msg.Verbs)
{ {
var button = new Button {Text = data.Text, Disabled = !data.Available}; var list = buttons.GetOrNew(data.Category);
if (data.Available)
if (data.CategoryIcon != null && !groupIcons.ContainsKey(data.Category))
{ {
button.OnPressed += _ => groupIcons.Add(data.Category, data.CategoryIcon);
{
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(_currentEntity, data.Key));
CloseContextMenu();
};
} }
if(!buttons.ContainsKey(data.Category)) list.Add(new ListedVerbData(data.Text, !data.Available, data.Key, entity.ToString(), () =>
buttons[data.Category] = new List<Button>(); {
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(curEntity, data.Key));
buttons[data.Category].Add(button); CloseAllMenus();
}, data.Icon));
} }
var user = GetUserEntity(); var user = GetUserEntity();
@@ -150,55 +183,87 @@ namespace Content.Client.GameObjects.EntitySystems
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue; continue;
if (VerbUtility.IsVerbInvisible(verb, user, component, out var vis)) var verbData = verb.GetData(user, component);
if (verbData.IsInvisible)
continue; continue;
var disabled = vis != VerbVisibility.Visible; var list = buttons.GetOrNew(verbData.Category);
var category = verb.GetCategory(user, component);
if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
{
groupIcons.Add(verbData.Category, verbData.CategoryIcon);
}
if(!buttons.ContainsKey(category)) list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, verb.ToString(), entity.ToString(),
buttons[category] = new List<Button>(); () => verb.Activate(user, component), verbData.Icon));
buttons[category].Add(CreateVerbButton(verb.GetText(user, component), disabled, verb.ToString(),
entity.ToString(), () => verb.Activate(user, component)));
} }
//Get global verbs. Visible for all entities regardless of their components. //Get global verbs. Visible for all entities regardless of their components.
foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly())) foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly()))
{ {
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue; continue;
if (VerbUtility.IsVerbInvisible(globalVerb, user, entity, out var vis)) var verbData = globalVerb.GetData(user, entity);
if (verbData.IsInvisible)
continue; continue;
var disabled = vis != VerbVisibility.Visible; var list = buttons.GetOrNew(verbData.Category);
var category = globalVerb.GetCategory(user, entity);
if(!buttons.ContainsKey(category)) if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
buttons[category] = new List<Button>(); {
groupIcons.Add(verbData.Category, verbData.CategoryIcon);
}
buttons[category].Add(CreateVerbButton(globalVerb.GetText(user, entity), disabled, globalVerb.ToString(), list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, globalVerb.ToString(),
entity.ToString(), () => globalVerb.Activate(user, entity))); entity.ToString(),
() => globalVerb.Activate(user, entity), verbData.Icon));
} }
if (buttons.Count > 0) if (buttons.Count > 0)
{ {
var first = true;
foreach (var (category, verbs) in buttons) foreach (var (category, verbs) in buttons)
{ {
if (string.IsNullOrEmpty(category)) if (string.IsNullOrEmpty(category))
continue; continue;
vBox.AddChild(CreateCategoryButton(category, verbs)); if (!first)
{
vBox.AddChild(new PanelContainer
{
CustomMinimumSize = (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("")) if (buttons.ContainsKey(""))
{ {
buttons[""].Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal)); buttons[""].Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
foreach (var verb in buttons[""]) foreach (var verb in buttons[""])
{ {
vBox.AddChild(verb); if (!first)
{
vBox.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
first = false;
vBox.AddChild(CreateVerbButton(verb));
} }
} }
} }
@@ -210,69 +275,364 @@ namespace Content.Client.GameObjects.EntitySystems
} }
} }
private Button CreateVerbButton(string text, bool disabled, string verbName, string ownerName, Action action) private VerbButton CreateVerbButton(ListedVerbData data)
{ {
var button = new Button var button = new VerbButton
{ {
Text = text, Text = data.Text,
Disabled = disabled Disabled = data.Disabled
}; };
if (!disabled)
if (data.Icon != null)
{
button.Icon = data.Icon.Frame0();
}
if (!data.Disabled)
{ {
button.OnPressed += _ => button.OnPressed += _ =>
{ {
CloseContextMenu(); CloseAllMenus();
try try
{ {
action.Invoke(); data.Action.Invoke();
} }
catch (Exception e) catch (Exception e)
{ {
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", verbName, ownerName, e); Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", data.VerbName, data.OwnerName, e);
} }
}; };
} }
return button; return button;
} }
private Button CreateCategoryButton(string text, List<Button> verbButtons) private Control CreateCategoryButton(string text, List<ListedVerbData> verbButtons, SpriteSpecifier icon)
{ {
verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal)); verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
var button = new Button return new VerbGroupButton(this, verbButtons, icon)
{ {
Text = $"{text}...", Text = text,
}; };
button.OnPressed += _ =>
{
_currentPopup.List.DisposeAllChildren();
foreach (var verb in verbButtons)
{
_currentPopup.List.AddChild(verb);
}
};
return button;
} }
private void CloseContextMenu() private void CloseVerbMenu()
{ {
_currentPopup?.Dispose(); _currentVerbListRoot?.Dispose();
_currentPopup = null; _currentVerbListRoot = null;
_currentEntity = EntityUid.Invalid; _currentEntity = EntityUid.Invalid;
} }
private void CloseEntityList()
{
_currentEntityList?.Dispose();
_currentEntityList = null;
}
private void CloseAllMenus()
{
CloseVerbMenu();
CloseEntityList();
CloseGroupMenu();
}
private void CloseGroupMenu()
{
_currentGroupList?.Dispose();
_currentGroupList = null;
}
private IEntity GetUserEntity() private IEntity GetUserEntity()
{ {
return _playerManager.LocalPlayer.ControlledEntity; return _playerManager.LocalPlayer.ControlledEntity;
} }
private sealed class EntityList : Popup
{
public VBoxContainer List { get; }
public EntityList()
{
AddChild(new PanelContainer
{
Children = {(List = new VBoxContainer())},
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#111E")}
});
}
}
private sealed class VerbPopup : Popup private sealed class VerbPopup : Popup
{ {
public VBoxContainer List { get; } public VBoxContainer List { get; }
public VerbPopup() public VerbPopup()
{ {
AddChild(List = new VBoxContainer()); AddChild(new PanelContainer
{
Children = {(List = new VBoxContainer())},
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#111E")}
});
}
}
private sealed class EntityButton : Control
{
private readonly VerbSystem _master;
private readonly IEntity _entity;
public EntityButton(VerbSystem master, IEntity entity)
{
_master = master;
_entity = entity;
MouseFilter = MouseFilterMode.Stop;
var control = new HBoxContainer {SeparationOverride = 6};
if (entity.TryGetComponent(out ISpriteComponent sprite))
{
control.AddChild(new SpriteView {Sprite = sprite});
}
control.AddChild(new MarginContainer
{
MarginLeftOverride = 4,
MarginRightOverride = 4,
Children = {new Label {Text = entity.Name}}
});
AddChild(control);
}
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
{
base.KeyBindDown(args);
if (args.Function == ContentKeyFunctions.OpenContextMenu)
{
_master.OnContextButtonPressed(_entity);
return;
}
if (args.Function == EngineKeyFunctions.Use)
{
var inputSys = _master.EntitySystemManager.GetEntitySystem<InputSystem>();
var func = args.Function;
var funcId = _master._inputManager.NetworkBindMap.KeyFunctionID(args.Function);
var message = new FullInputCmdMessage(_master._gameTiming.CurTick, funcId, BoundKeyState.Down,
_entity.Transform.GridPosition,
args.PointerLocation, _entity.Uid);
// client side command handlers will always be sent the local player session.
var session = _master._playerManager.LocalPlayer.Session;
inputSys.HandleInputCommand(session, func, message);
_master.CloseAllMenus();
return;
}
if (_master._itemSlotManager.OnButtonPressed(args, _entity))
{
_master.CloseAllMenus();
}
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
if (UserInterfaceManager.CurrentlyHovered == this)
{
handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
}
}
}
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 HBoxContainer
{
Children =
{
(_icon = new TextureRect
{
CustomMinimumSize = (32, 32),
Stretch = TextureRect.StretchMode.KeepCentered
}),
(_label = new Label()),
// Padding
new Control {CustomMinimumSize = (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;
public List<ListedVerbData> VerbButtons { get; }
private readonly Label _label;
private readonly TextureRect _icon;
private CancellationTokenSource _openCancel;
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 HBoxContainer
{
Children =
{
(_icon = new TextureRect
{
CustomMinimumSize = (32, 32),
Stretch = TextureRect.StretchMode.KeepCentered
}),
(_label = new Label
{
SizeFlagsHorizontal = SizeFlags.FillExpand
}),
// Padding
new Control {CustomMinimumSize = (8, 0)},
new TextureRect
{
Texture = IoCManager.Resolve<IResourceCache>()
.GetTexture("/Textures/UserInterface/VerbIcons/group.svg.96dpi.png"),
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
{
CustomMinimumSize = (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;
} }
} }
} }

View File

@@ -12,17 +12,19 @@ namespace Content.Client.GlobalVerbs
[GlobalVerb] [GlobalVerb]
class ViewVariablesVerb : GlobalVerb class ViewVariablesVerb : GlobalVerb
{ {
public override string GetText(IEntity user, IEntity target) => "View variables";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false; public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target) public override void GetData(IEntity user, IEntity target, VerbData data)
{ {
var groupController = IoCManager.Resolve<IClientConGroupController>(); var groupController = IoCManager.Resolve<IClientConGroupController>();
if (groupController.CanViewVar()) if (!groupController.CanViewVar())
return VerbVisibility.Visible; {
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return;
}
data.Text = "View Variables";
data.CategoryData = VerbCategories.Debug;
} }
public override void Activate(IEntity user, IEntity target) public override void Activate(IEntity user, IEntity target)

View File

@@ -216,21 +216,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
[Verb] [Verb]
private sealed class FillTargetVerb : Verb<SolutionComponent> private sealed class FillTargetVerb : Verb<SolutionComponent>
{ {
protected override string GetText(IEntity user, SolutionComponent component) protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
{
if(!user.TryGetComponent<HandsComponent>(out var hands))
return "<I SHOULD BE INVISIBLE>";
if(hands.GetActiveHand == null)
return "<I SHOULD BE INVISIBLE>";
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
return $"Transfer liquid from [{heldEntityName}] to [{myName}].";
}
protected override VerbVisibility GetVisibility(IEntity user, SolutionComponent component)
{ {
if (user.TryGetComponent<HandsComponent>(out var hands)) if (user.TryGetComponent<HandsComponent>(out var hands))
{ {
@@ -238,13 +224,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
{ {
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution)) if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
{ {
if ((solution.Capabilities & SolutionCaps.PourOut) != 0 && (component.Capabilities & SolutionCaps.PourIn) != 0) if ((solution.Capabilities & SolutionCaps.PourOut) != 0 &&
return VerbVisibility.Visible; (component.Capabilities & SolutionCaps.PourIn) != 0)
{
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
data.Text= $"Transfer liquid from [{heldEntityName}] to [{myName}].";
return;
}
} }
} }
} }
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
} }
protected override void Activate(IEntity user, SolutionComponent component) protected override void Activate(IEntity user, SolutionComponent component)
@@ -269,7 +262,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
var transferSolution = handSolutionComp.SplitSolution(transferQuantity); var transferSolution = handSolutionComp.SplitSolution(transferQuantity);
component.TryAddSolution(transferSolution); component.TryAddSolution(transferSolution);
} }
} }
@@ -304,21 +296,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
[Verb] [Verb]
private sealed class EmptyTargetVerb : Verb<SolutionComponent> private sealed class EmptyTargetVerb : Verb<SolutionComponent>
{ {
protected override string GetText(IEntity user, SolutionComponent component) protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
{
if (!user.TryGetComponent<HandsComponent>(out var hands))
return "<I SHOULD BE INVISIBLE>";
if (hands.GetActiveHand == null)
return "<I SHOULD BE INVISIBLE>";
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
return $"Transfer liquid from [{myName}] to [{heldEntityName}].";
}
protected override VerbVisibility GetVisibility(IEntity user, SolutionComponent component)
{ {
if (user.TryGetComponent<HandsComponent>(out var hands)) if (user.TryGetComponent<HandsComponent>(out var hands))
{ {
@@ -326,13 +304,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
{ {
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution)) if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
{ {
if ((solution.Capabilities & SolutionCaps.PourIn) != 0 && (component.Capabilities & SolutionCaps.PourOut) != 0) if ((solution.Capabilities & SolutionCaps.PourIn) != 0 &&
return VerbVisibility.Visible; (component.Capabilities & SolutionCaps.PourOut) != 0)
{
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
data.Text = $"Transfer liquid from [{myName}] to [{heldEntityName}].";
return;
}
} }
} }
} }
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
} }
protected override void Activate(IEntity user, SolutionComponent component) protected override void Activate(IEntity user, SolutionComponent component)

View File

@@ -19,19 +19,18 @@ namespace Content.Server.GameObjects.Components.Fluids
[Verb] [Verb]
private sealed class FillTargetVerb : Verb<CanSpillComponent> private sealed class FillTargetVerb : Verb<CanSpillComponent>
{ {
protected override string GetText(IEntity user, CanSpillComponent component) protected override void GetData(IEntity user, CanSpillComponent component, VerbData data)
{ {
return "Spill liquid"; if (!component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
}
protected override VerbVisibility GetVisibility(IEntity user, CanSpillComponent component)
{
if (component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
{ {
return solutionComponent.CurrentVolume > ReagentUnit.Zero ? VerbVisibility.Visible : VerbVisibility.Disabled; data.Visibility = VerbVisibility.Invisible;
return;
} }
return VerbVisibility.Invisible; data.Text = "Spill liquid";
data.Visibility = solutionComponent.CurrentVolume > ReagentUnit.Zero
? VerbVisibility.Visible
: VerbVisibility.Disabled;
} }
protected override void Activate(IEntity user, CanSpillComponent component) protected override void Activate(IEntity user, CanSpillComponent component)

View File

@@ -250,14 +250,17 @@ namespace Content.Server.GameObjects.Components.Interactable
[Verb] [Verb]
public sealed class EjectCellVerb : Verb<HandheldLightComponent> public sealed class EjectCellVerb : Verb<HandheldLightComponent>
{ {
protected override string GetText(IEntity user, HandheldLightComponent component) protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data)
{ {
return component.Cell == null ? "Eject cell (cell missing)" : "Eject cell"; if (component.Cell == null)
} {
data.Text = "Eject cell (cell missing)";
protected override VerbVisibility GetVisibility(IEntity user, HandheldLightComponent component) data.Visibility = VerbVisibility.Disabled;
{ }
return component.Cell == null ? VerbVisibility.Disabled : VerbVisibility.Visible; else
{
data.Text = "Eject cell";
}
} }
protected override void Activate(IEntity user, HandheldLightComponent component) protected override void Activate(IEntity user, HandheldLightComponent component)

View File

@@ -328,16 +328,9 @@ namespace Content.Server.GameObjects.Components
[Verb] [Verb]
private sealed class LockToggleVerb : Verb<EntityStorageComponent> private sealed class LockToggleVerb : Verb<EntityStorageComponent>
{ {
/// <inheritdoc /> protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data)
protected override string GetText(IEntity user, EntityStorageComponent component)
{ {
return component._locked ? "Unlock" : "Lock"; data.Text = component._locked ? "Unlock" : "Lock";
}
/// <inheritdoc />
protected override VerbVisibility GetVisibility(IEntity user, EntityStorageComponent component)
{
return VerbVisibility.Visible;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -350,16 +343,15 @@ namespace Content.Server.GameObjects.Components
[Verb] [Verb]
private sealed class OpenToggleVerb : Verb<EntityStorageComponent> private sealed class OpenToggleVerb : Verb<EntityStorageComponent>
{ {
/// <inheritdoc /> protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data)
protected override string GetText(IEntity user, EntityStorageComponent component)
{ {
return component.Open ? "Close" : "Open"; if (component.NoDoor)
} {
data.Visibility = VerbVisibility.Invisible;
return;
}
/// <inheritdoc /> data.Text = component.Open ? "Close" : "Open";
protected override VerbVisibility GetVisibility(IEntity user, EntityStorageComponent component)
{
return component.NoDoor ? VerbVisibility.Invisible : VerbVisibility.Visible;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -113,23 +113,15 @@ namespace Content.Server.GameObjects
[Verb] [Verb]
public sealed class PickUpVerb : Verb<ItemComponent> public sealed class PickUpVerb : Verb<ItemComponent>
{ {
protected override string GetText(IEntity user, ItemComponent component) protected override void GetData(IEntity user, ItemComponent component, VerbData data)
{
if (user.TryGetComponent(out HandsComponent hands) && hands.IsHolding(component.Owner))
{
return "Pick Up (Already Holding)";
}
return "Pick Up";
}
protected override VerbVisibility GetVisibility(IEntity user, ItemComponent component)
{ {
if (ContainerHelpers.IsInContainer(component.Owner) || !component.CanPickup(user)) if (ContainerHelpers.IsInContainer(component.Owner) || !component.CanPickup(user))
{ {
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return;
} }
return VerbVisibility.Visible; data.Text = "Pick Up";
} }
protected override void Activate(IEntity user, ItemComponent component) protected override void Activate(IEntity user, ItemComponent component)

View File

@@ -128,14 +128,10 @@ namespace Content.Server.GameObjects.Components.Medical
[Verb] [Verb]
public sealed class EnterVerb : Verb<MedicalScannerComponent> public sealed class EnterVerb : Verb<MedicalScannerComponent>
{ {
protected override string GetText(IEntity user, MedicalScannerComponent component) protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
{ {
return "Enter"; data.Text = "Enter";
} data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, MedicalScannerComponent component) protected override void Activate(IEntity user, MedicalScannerComponent component)
@@ -147,14 +143,10 @@ namespace Content.Server.GameObjects.Components.Medical
[Verb] [Verb]
public sealed class EjectVerb : Verb<MedicalScannerComponent> public sealed class EjectVerb : Verb<MedicalScannerComponent>
{ {
protected override string GetText(IEntity user, MedicalScannerComponent component) protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
{ {
return "Eject"; data.Text = "Eject";
} data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
} }
protected override void Activate(IEntity user, MedicalScannerComponent component) protected override void Activate(IEntity user, MedicalScannerComponent component)

View File

@@ -58,28 +58,22 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb] [Verb]
private sealed class InsertVerb : Verb<PowerCellChargerComponent> private sealed class InsertVerb : Verb<PowerCellChargerComponent>
{ {
protected override string GetText(IEntity user, PowerCellChargerComponent component) protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component)
{ {
if (!user.TryGetComponent(out HandsComponent handsComponent)) if (!user.TryGetComponent(out HandsComponent handsComponent))
{ {
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return;
} }
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null) if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
{ {
return VerbVisibility.Disabled; data.Visibility = VerbVisibility.Disabled;
data.Text = "Insert";
return;
} }
return VerbVisibility.Visible; data.Text = $"Insert {handsComponent.GetActiveHand.Owner.Name}";
} }
protected override void Activate(IEntity user, PowerCellChargerComponent component) protected override void Activate(IEntity user, PowerCellChargerComponent component)
@@ -102,22 +96,16 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb] [Verb]
private sealed class EjectVerb : Verb<PowerCellChargerComponent> private sealed class EjectVerb : Verb<PowerCellChargerComponent>
{ {
protected override string GetText(IEntity user, PowerCellChargerComponent component) protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data)
{ {
if (component._container.ContainedEntity == null) if (component._container.ContainedEntity == null)
{ {
return "Eject"; data.Text = "Eject";
data.Visibility = VerbVisibility.Disabled;
return;
} }
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component) data.Text = $"Eject {component._container.ContainedEntity.Name}";
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, PowerCellChargerComponent component) protected override void Activate(IEntity user, PowerCellChargerComponent component)

View File

@@ -48,28 +48,27 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb] [Verb]
private sealed class InsertVerb : Verb<WeaponCapacitorChargerComponent> private sealed class InsertVerb : Verb<WeaponCapacitorChargerComponent>
{ {
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component) protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component)
{ {
if (!user.TryGetComponent(out HandsComponent handsComponent)) if (!user.TryGetComponent(out HandsComponent handsComponent))
{ {
return VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return;
} }
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null) if (handsComponent.GetActiveHand == null)
{ {
return VerbVisibility.Disabled; data.Visibility = VerbVisibility.Disabled;
data.Text = "Insert";
return;
} }
return VerbVisibility.Visible; if (component._container.ContainedEntity != null)
{
data.Visibility = VerbVisibility.Disabled;
}
data.Text = $"Insert {handsComponent.GetActiveHand.Owner.Name}";
} }
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component) protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)
@@ -92,22 +91,16 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb] [Verb]
private sealed class EjectVerb : Verb<WeaponCapacitorChargerComponent> private sealed class EjectVerb : Verb<WeaponCapacitorChargerComponent>
{ {
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component) protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data)
{ {
if (component._container.ContainedEntity == null) if (component._container.ContainedEntity == null)
{ {
return "Eject"; data.Visibility = VerbVisibility.Disabled;
data.Text = "Eject";
return;
} }
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component) data.Text = $"Eject {component._container.ContainedEntity.Name}";
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component) protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)

View File

@@ -35,16 +35,11 @@ namespace Content.Server.GameObjects.Components
[Verb] [Verb]
public sealed class RotateVerb : Verb<RotatableComponent> public sealed class RotateVerb : Verb<RotatableComponent>
{ {
protected override string GetText(IEntity user, RotatableComponent component) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
return "Rotate clockwise"; data.CategoryData = VerbCategories.Rotate;
} data.Text = "Rotate clockwise";
data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png";
protected override string GetCategory(IEntity user, RotatableComponent component) => "Rotate";
protected override VerbVisibility GetVisibility(IEntity user, RotatableComponent component)
{
return VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, RotatableComponent component) protected override void Activate(IEntity user, RotatableComponent component)
@@ -56,16 +51,11 @@ namespace Content.Server.GameObjects.Components
[Verb] [Verb]
public sealed class RotateCounterVerb : Verb<RotatableComponent> public sealed class RotateCounterVerb : Verb<RotatableComponent>
{ {
protected override string GetText(IEntity user, RotatableComponent component) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
return "Rotate counter-clockwise"; data.CategoryData = VerbCategories.Rotate;
} data.Text = "Rotate counter-clockwise";
data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png";
protected override string GetCategory(IEntity user, RotatableComponent component) => "Rotate";
protected override VerbVisibility GetVisibility(IEntity user, RotatableComponent component)
{
return VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, RotatableComponent component) protected override void Activate(IEntity user, RotatableComponent component)

View File

@@ -266,14 +266,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
[Verb] [Verb]
public sealed class EjectMagazineVerb : Verb<BallisticMagazineWeaponComponent> public sealed class EjectMagazineVerb : Verb<BallisticMagazineWeaponComponent>
{ {
protected override string GetText(IEntity user, BallisticMagazineWeaponComponent component) protected override void GetData(IEntity user, BallisticMagazineWeaponComponent component, VerbData data)
{ {
return component.Magazine == null ? "Eject magazine (magazine missing)" : "Eject magazine"; if (component.Magazine == null)
} {
data.Text = "Eject magazine (magazine missing)";
data.Visibility = VerbVisibility.Disabled;
return;
}
protected override VerbVisibility GetVisibility(IEntity user, BallisticMagazineWeaponComponent component) data.Text = "Eject magazine";
{
return component.Magazine == null ? VerbVisibility.Disabled : VerbVisibility.Visible;
} }
protected override void Activate(IEntity user, BallisticMagazineWeaponComponent component) protected override void Activate(IEntity user, BallisticMagazineWeaponComponent component)

View File

@@ -12,9 +12,9 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
public class VerbSystem : EntitySystem public class VerbSystem : EntitySystem
{ {
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager; [Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649 #pragma warning restore 649
public override void Initialize() public override void Initialize()
{ {
@@ -90,19 +90,19 @@ namespace Content.Server.GameObjects.EntitySystems
var userEntity = player.AttachedEntity; var userEntity = player.AttachedEntity;
var data = new List<VerbsResponseMessage.VerbData>(); var data = new List<VerbsResponseMessage.NetVerbData>();
//Get verbs, component dependent. //Get verbs, component dependent.
foreach (var (component, verb) in VerbUtility.GetVerbs(entity)) foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
{ {
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity)) if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue; continue;
if (VerbUtility.IsVerbInvisible(verb, userEntity, component, out var vis))
var verbData = verb.GetData(userEntity, component);
if (verbData.IsInvisible)
continue; continue;
// TODO: These keys being giant strings is inefficient as hell. // TODO: These keys being giant strings is inefficient as hell.
data.Add(new VerbsResponseMessage.VerbData(verb.GetText(userEntity, component), data.Add(new VerbsResponseMessage.NetVerbData(verbData, $"{component.GetType()}:{verb.GetType()}"));
$"{component.GetType()}:{verb.GetType()}", verb.GetCategory(userEntity, component),
vis == VerbVisibility.Visible));
} }
//Get global verbs. Visible for all entities regardless of their components. //Get global verbs. Visible for all entities regardless of their components.
@@ -110,14 +110,15 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity)) if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue; continue;
if (VerbUtility.IsVerbInvisible(globalVerb, userEntity, entity, out var vis))
var verbData = globalVerb.GetData(userEntity, entity);
if (verbData.IsInvisible)
continue; continue;
data.Add(new VerbsResponseMessage.VerbData(globalVerb.GetText(userEntity, entity), data.Add(new VerbsResponseMessage.NetVerbData(verbData, globalVerb.GetType().ToString()));
globalVerb.GetType().ToString(), globalVerb.GetCategory(userEntity, entity), vis == VerbVisibility.Visible));
} }
var response = new VerbsResponseMessage(data, req.EntityUid); var response = new VerbsResponseMessage(data.ToArray(), req.EntityUid);
RaiseNetworkEvent(response, player.ConnectedClient); RaiseNetworkEvent(response, player.ConnectedClient);
} }
} }

View File

@@ -1,53 +1,64 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.Components.Observer;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Server.GlobalVerbs namespace Content.Server.GlobalVerbs
{ {
[GlobalVerb] [GlobalVerb]
public class ControlMobVerb : GlobalVerb public class ControlMobVerb : GlobalVerb
{ {
public override string GetText(IEntity user, IEntity target) => "Control Mob";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false; public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target) public override void GetData(IEntity user, IEntity target, VerbData data)
{ {
data.Visibility = VerbVisibility.Invisible;
var groupController = IoCManager.Resolve<IConGroupController>(); var groupController = IoCManager.Resolve<IConGroupController>();
if (user == target) return VerbVisibility.Invisible; if (user == target)
{
return;
}
if (user.TryGetComponent<IActorComponent>(out var player)) if (user.TryGetComponent<IActorComponent>(out var player))
{ {
if (!user.HasComponent<MindComponent>() || !target.HasComponent<MindComponent>()) if (!user.HasComponent<MindComponent>() || !target.HasComponent<MindComponent>())
return VerbVisibility.Invisible; {
return;
}
if (groupController.CanCommand(player.playerSession, "controlmob")) if (groupController.CanCommand(player.playerSession, "controlmob"))
return VerbVisibility.Visible; {
data.Visibility = VerbVisibility.Visible;
data.Text = "Control Mob";
data.CategoryData = VerbCategories.Debug;
}
} }
return VerbVisibility.Invisible;
} }
public override void Activate(IEntity user, IEntity target) public override void Activate(IEntity user, IEntity target)
{ {
var userMind = user.GetComponent<IActorComponent>().playerSession.ContentData().Mind; var groupController = IoCManager.Resolve<IConGroupController>();
var player = user.GetComponent<IActorComponent>().playerSession;
if (!groupController.CanCommand(player, "controlmob"))
{
return;
}
var userMind = player.ContentData().Mind;
var targetMind = target.GetComponent<MindComponent>(); var targetMind = target.GetComponent<MindComponent>();
var oldEntity = userMind.CurrentEntity; var oldEntity = userMind.CurrentEntity;
targetMind.Mind?.TransferTo(null); targetMind.Mind?.TransferTo(null);
userMind.TransferTo(target); userMind.TransferTo(target);
if(oldEntity.HasComponent<GhostComponent>()) if (oldEntity.HasComponent<GhostComponent>())
oldEntity.Delete(); oldEntity.Delete();
} }
} }

View File

@@ -14,24 +14,29 @@ namespace Content.Server.GlobalVerbs
[GlobalVerb] [GlobalVerb]
class RejuvenateVerb : GlobalVerb class RejuvenateVerb : GlobalVerb
{ {
public override string GetText(IEntity user, IEntity target) => "Rejuvenate";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false; public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target) public override void GetData(IEntity user, IEntity target, VerbData data)
{ {
data.Text = "Rejuvenate";
data.CategoryData = VerbCategories.Debug;
data.Visibility = VerbVisibility.Invisible;
var groupController = IoCManager.Resolve<IConGroupController>(); var groupController = IoCManager.Resolve<IConGroupController>();
if (user.TryGetComponent<IActorComponent>(out var player)) if (user.TryGetComponent<IActorComponent>(out var player))
{ {
if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() && !target.HasComponent<ThirstComponent>()) if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() &&
return VerbVisibility.Invisible; !target.HasComponent<ThirstComponent>())
{
return;
}
if (groupController.CanCommand(player.playerSession, "rejuvenate")) if (groupController.CanCommand(player.playerSession, "rejuvenate"))
return VerbVisibility.Visible; {
data.Visibility = VerbVisibility.Visible;
}
} }
return VerbVisibility.Invisible;
} }
public override void Activate(IEntity user, IEntity target) public override void Activate(IEntity user, IEntity target)
@@ -42,7 +47,6 @@ namespace Content.Server.GlobalVerbs
if (groupController.CanCommand(player.playerSession, "rejuvenate")) if (groupController.CanCommand(player.playerSession, "rejuvenate"))
PerformRejuvenate(target); PerformRejuvenate(target);
} }
} }
public static void PerformRejuvenate(IEntity target) public static void PerformRejuvenate(IEntity target)
{ {

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.EntitySystemMessages namespace Content.Shared.GameObjects.EntitySystemMessages
{ {
@@ -21,29 +22,33 @@ namespace Content.Shared.GameObjects.EntitySystemMessages
[Serializable, NetSerializable] [Serializable, NetSerializable]
public class VerbsResponseMessage : EntitySystemMessage public class VerbsResponseMessage : EntitySystemMessage
{ {
public readonly List<VerbData> Verbs; public readonly NetVerbData[] Verbs;
public readonly EntityUid Entity; public readonly EntityUid Entity;
public VerbsResponseMessage(List<VerbData> verbs, EntityUid entity) public VerbsResponseMessage(NetVerbData[] verbs, EntityUid entity)
{ {
Verbs = verbs; Verbs = verbs;
Entity = entity; Entity = entity;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
public readonly struct VerbData public readonly struct NetVerbData
{ {
public readonly string Text; public readonly string Text;
public readonly string Key; public readonly string Key;
public readonly string Category; public readonly string Category;
public readonly SpriteSpecifier Icon;
public readonly SpriteSpecifier CategoryIcon;
public readonly bool Available; public readonly bool Available;
public VerbData(string text, string key, string category, bool available) public NetVerbData(VerbData data, string key)
{ {
Text = text; Text = data.Text;
Key = key; Key = key;
Category = category; Category = data.Category;
Available = available; CategoryIcon = data.CategoryIcon;
Icon = data.Icon;
Available = data.Visibility == VerbVisibility.Visible;
} }
} }
} }

View File

@@ -21,25 +21,16 @@ namespace Content.Shared.GameObjects
public virtual bool RequireInteractionRange => true; public virtual bool RequireInteractionRange => true;
/// <summary> /// <summary>
/// Gets the text string that will be shown to <paramref name="user"/> in the right click menu. /// Gets the visible verb data for the user.
/// </summary> /// </summary>
/// <remarks>
/// Implementations should write into <paramref name="data"/> to return their data.
/// </remarks>
/// <param name="user">The entity of the user opening this menu.</param> /// <param name="user">The entity of the user opening this menu.</param>
/// <param name="target">The entity this verb is being evaluated for.</param>
/// <param name="data">The data that must be filled in.</param>
/// <returns>The text string that is shown in the right click menu for this verb.</returns> /// <returns>The text string that is shown in the right click menu for this verb.</returns>
public abstract string GetText(IEntity user, IEntity target); public abstract void GetData(IEntity user, IEntity target, VerbData data);
/// <summary>
/// Gets the category of this verb.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <returns>The category of this verb.</returns>
public virtual string GetCategory(IEntity user, IEntity target) => "";
/// <summary>
/// Gets the visibility level of this verb in the right click menu.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <returns>The visibility level of the verb in the client's right click menu.</returns>
public abstract VerbVisibility GetVisibility(IEntity user, IEntity target);
/// <summary> /// <summary>
/// Invoked when this verb is activated from the right click menu. /// Invoked when this verb is activated from the right click menu.
@@ -47,6 +38,13 @@ namespace Content.Shared.GameObjects
/// <param name="user">The entity of the user opening this menu.</param> /// <param name="user">The entity of the user opening this menu.</param>
/// <param name="target">The entity that is being acted upon.</param> /// <param name="target">The entity that is being acted upon.</param>
public abstract void Activate(IEntity user, IEntity target); public abstract void Activate(IEntity user, IEntity target);
public VerbData GetData(IEntity user, IEntity target)
{
var data = new VerbData();
GetData(user, target, data);
return data;
}
} }
/// <summary> /// <summary>

View File

@@ -19,33 +19,20 @@ namespace Content.Shared.GameObjects
{ {
/// <summary> /// <summary>
/// If true, this verb requires the user to be inside within /// If true, this verb requires the user to be inside within
/// <see cref="InteractionRange"/> meters from the entity on which this verb resides. /// <see cref="VerbUtility.InteractionRange"/> meters from the entity on which this verb resides.
/// </summary> /// </summary>
public virtual bool RequireInteractionRange => true; public virtual bool RequireInteractionRange => true;
/// <summary> /// <summary>
/// Gets the text string that will be shown to <paramref name="user"/> in the right click menu. /// Gets the visible verb data for the user.
/// </summary> /// </summary>
/// <remarks>
/// Implementations should write into <paramref name="data"/> to return their data.
/// </remarks>
/// <param name="user">The entity of the user opening this menu.</param> /// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param> /// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The text string that is shown in the right click menu for this verb.</returns> /// <param name="data">The data that must be filled into.</param>
public abstract string GetText(IEntity user, IComponent component); protected abstract void GetData(IEntity user, IComponent component, VerbData data);
/// <summary>
/// Gets the category of this verb.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The category of this verb.</returns>
public virtual string GetCategory(IEntity user, IComponent component) => "";
/// <summary>
/// Gets the visibility level of this verb in the right click menu.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The visibility level of the verb in the client's right click menu.</returns>
public abstract VerbVisibility GetVisibility(IEntity user, IComponent component);
/// <summary> /// <summary>
/// Invoked when this verb is activated from the right click menu. /// Invoked when this verb is activated from the right click menu.
@@ -53,6 +40,13 @@ namespace Content.Shared.GameObjects
/// <param name="user">The entity of the user opening this menu.</param> /// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param> /// <param name="component">The component instance for which this verb is being loaded.</param>
public abstract void Activate(IEntity user, IComponent component); public abstract void Activate(IEntity user, IComponent component);
public VerbData GetData(IEntity user, IComponent component)
{
var data = new VerbData();
GetData(user, component, data);
return data;
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -64,28 +58,15 @@ namespace Content.Shared.GameObjects
public abstract class Verb<T> : Verb where T : IComponent public abstract class Verb<T> : Verb where T : IComponent
{ {
/// <summary> /// <summary>
/// Gets the text string that will be shown to <paramref name="user"/> in the right click menu. /// Gets the visible verb data for the user.
/// </summary> /// </summary>
/// <remarks>
/// Implementations should write into <paramref name="data"/> to return their data.
/// </remarks>
/// <param name="user">The entity of the user opening this menu.</param> /// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param> /// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The text string that is shown in the right click menu for this verb.</returns> /// <param name="data">The data that must be filled into.</param>
protected abstract string GetText(IEntity user, T component); protected abstract void GetData(IEntity user, T component, VerbData data);
/// <summary>
/// Gets the category of this verb.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The category of this verb.</returns>
protected virtual string GetCategory(IEntity user, T component) => "";
/// <summary>
/// Gets the visibility level of this verb in the right click menu.
/// </summary>
/// <param name="user">The entity of the user opening this menu.</param>
/// <param name="component">The component instance for which this verb is being loaded.</param>
/// <returns>The visibility level of the verb in the client's right click menu.</returns>
protected abstract VerbVisibility GetVisibility(IEntity user, T component);
/// <summary> /// <summary>
/// Invoked when this verb is activated from the right click menu. /// Invoked when this verb is activated from the right click menu.
@@ -94,22 +75,9 @@ namespace Content.Shared.GameObjects
/// <param name="component">The component instance for which this verb is being loaded.</param> /// <param name="component">The component instance for which this verb is being loaded.</param>
protected abstract void Activate(IEntity user, T component); protected abstract void Activate(IEntity user, T component);
/// <inheritdoc /> protected sealed override void GetData(IEntity user, IComponent component, VerbData data)
public sealed override string GetText(IEntity user, IComponent component)
{ {
return GetText(user, (T) component); GetData(user, (T) component, data);
}
/// <inheritdoc />
public sealed override string GetCategory(IEntity user, IComponent component)
{
return GetCategory(user, (T) component);
}
/// <inheritdoc />
public sealed override VerbVisibility GetVisibility(IEntity user, IComponent component)
{
return GetVisibility(user, (T) component);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -128,25 +96,4 @@ namespace Content.Shared.GameObjects
public sealed class VerbAttribute : Attribute public sealed class VerbAttribute : Attribute
{ {
} }
/// <summary>
/// Possible states of visibility for the verb in the right click menu.
/// </summary>
public enum VerbVisibility
{
/// <summary>
/// The verb will be listed in the right click menu.
/// </summary>
Visible,
/// <summary>
/// The verb will be listed, but it will be grayed out and unable to be clicked on.
/// </summary>
Disabled,
/// <summary>
/// The verb will not be listed in the right click menu.
/// </summary>
Invisible
}
} }

View File

@@ -0,0 +1,13 @@
namespace Content.Shared.GameObjects
{
/// <summary>
/// Standard verb categories.
/// </summary>
public static class VerbCategories
{
public static readonly VerbCategoryData Debug =
("Debug", "/Textures/UserInterface/VerbIcons/debug.svg.96dpi.png");
public static readonly VerbCategoryData Rotate = ("Rotate", null);
}
}

View File

@@ -0,0 +1,24 @@
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects
{
/// <summary>
/// Contains combined name and icon information for a verb category.
/// </summary>
public readonly struct VerbCategoryData
{
public VerbCategoryData(string name, SpriteSpecifier icon)
{
Name = name;
Icon = icon;
}
public string Name { get; }
public SpriteSpecifier Icon { get; }
public static implicit operator VerbCategoryData((string name, string icon) tuple)
{
return new VerbCategoryData(tuple.name, tuple.icon == null ? null : new SpriteSpecifier.Texture(new ResourcePath(tuple.icon)));
}
}
}

View File

@@ -0,0 +1,65 @@
using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects
{
/// <summary>
/// Stores visual data for a verb.
/// </summary>
/// <remarks>
/// An instance of this class gets instantiated by the verb system and should be filled in by implementations of
/// <see cref="Verb.GetData(IEntity, IComponent, VerbData)"/>.
/// </remarks>
public sealed class VerbData
{
/// <summary>
/// The text that the user sees on the verb button.
/// </summary>
public string Text { get; set; }
/// <summary>
/// Sprite of the icon that the user sees on the verb button.
/// </summary>
public SpriteSpecifier Icon { get; set; }
/// <summary>
/// Name of the category this button is under.
/// </summary>
public string Category { get; set; } = "";
/// <summary>
/// Sprite of the icon that the user sees on the verb button.
/// </summary>
public SpriteSpecifier CategoryIcon { get; set; }
/// <summary>
/// Whether this verb is visible, disabled (greyed out) or hidden.
/// </summary>
public VerbVisibility Visibility { get; set; } = VerbVisibility.Visible;
public bool IsInvisible => Visibility == VerbVisibility.Invisible;
public bool IsDisabled => Visibility == VerbVisibility.Disabled;
/// <summary>
/// Convenience property to set verb category and icon at once.
/// </summary>
[ValueProvider("Content.Shared.GameObjects.VerbCategories")]
public VerbCategoryData CategoryData
{
set
{
Category = value.Name;
CategoryIcon = value.Icon;
}
}
/// <summary>
/// Convenience property to set <see cref="Icon"/> to a raw texture path.
/// </summary>
public string IconTexture
{
set => Icon = new SpriteSpecifier.Texture(new ResourcePath(value));
}
}
}

View File

@@ -60,17 +60,5 @@ namespace Content.Shared.GameObjects
} }
return true; return true;
} }
public static bool IsVerbInvisible(Verb verb, IEntity user, IComponent target, out VerbVisibility visibility)
{
visibility = verb.GetVisibility(user, target);
return visibility == VerbVisibility.Invisible;
}
public static bool IsVerbInvisible(GlobalVerb verb, IEntity user, IEntity target, out VerbVisibility visibility)
{
visibility = verb.GetVisibility(user, target);
return visibility == VerbVisibility.Invisible;
}
} }
} }

View File

@@ -0,0 +1,23 @@
namespace Content.Shared.GameObjects
{
/// <summary>
/// Possible states of visibility for the verb in the right click menu.
/// </summary>
public enum VerbVisibility
{
/// <summary>
/// The verb will be listed in the right click menu.
/// </summary>
Visible,
/// <summary>
/// The verb will be listed, but it will be grayed out and unable to be clicked on.
/// </summary>
Disabled,
/// <summary>
/// The verb will not be listed in the right click menu.
/// </summary>
Invisible
}
}

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 6.3499998 6.3500002"
version="1.1"
id="svg8"
sodipodi:docname="debug.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
inkscape:export-filename="/home/pj/Projects/space-station-14/Resources/Textures/UserInterface/VerbIcons/debug.svg.96dpi.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#1e2121"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="23.470348"
inkscape:cx="10.081453"
inkscape:cy="13.861109"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:pagecheckerboard="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.35898988,-0.29466516)">
<path
transform="matrix(0.26458333,0,0,0.26458333,0.35898988,0.29466516)"
d="m 12,4.1367188 a 6.7719046,8.6036511 0 0 0 -6.7714844,8.6035152 6.7719046,8.6036511 0 0 0 5.4921874,8.449219 1.2499994,1.2499994 0 0 1 0,-0.04687 l 0.05859,-10.179687 a 1.2499994,1.2499994 0 0 1 1.253906,-1.2402348 1.2499994,1.2499994 0 0 1 1.240235,1.2558598 l -0.05078,10.179687 a 1.2499994,1.2499994 0 0 1 -0.002,0.04687 6.7719046,8.6036511 0 0 0 5.550781,-8.464844 A 6.7719046,8.6036511 0 0 0 12,4.1367188 Z"
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.1732;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path2786" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="M 2.8688267,4.6071132 1.3961568,5.9240289"
id="path2790" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 4.4712953,4.7414055 1.3160631,1.0882394 v 0"
id="path2792" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m 4.2344863,3.7587164 2.043264,-0.027483"
id="path2794" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 3.1795381,2.9022976 1.1255836,1.7708898"
id="path2796"
sodipodi:nodetypes="cc"
inkscape:transform-center-x="0.12808358"
inkscape:transform-center-y="0.13235299" />
<path
id="path2794-56"
d="M 0.81382462,3.7512129 2.8570887,3.72373"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1" />
<path
inkscape:transform-center-y="0.17965894"
inkscape:transform-center-x="-0.040563355"
sodipodi:nodetypes="cc"
id="path2796-2"
d="M 5.9376668,1.8134763 3.8659632,2.912047"
style="fill:none;stroke:#ffffff;stroke-width:0.74991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<ellipse
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
id="path3479"
cx="3.5339899"
cy="1.4727732"
rx="0.89334828"
ry="0.8606078" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/home/pj/Projects/space-station-14/Resources/Textures/UserInterface/VerbIcons/group.svg.96dpi.png"
sodipodi:docname="group.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg2892"
version="1.1"
viewBox="0 0 4.2333332 4.2333332"
height="16"
width="16">
<defs
id="defs2886" />
<sodipodi:namedview
units="px"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:window-height="1080"
inkscape:window-width="1920"
inkscape:pagecheckerboard="true"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="px"
inkscape:cy="7.7684689"
inkscape:cx="8.625185"
inkscape:zoom="38.76146"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata2889">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
id="path3455"
d="M 5.3128544,3.0306137 5.2510052,13.878976 12.710029,8.4980906 Z"
style="fill:none;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3457"
d="M 1.4374722,0.75584911 2.8315087,2.0357963 1.4125146,3.3656578"
style="fill:none;stroke:#ffffff;stroke-width:0.52916665;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/home/pj/Projects/space-station-14/Resources/Textures/UserInterface/VerbIcons/rotate_ccw.svg.96dpi.png"
sodipodi:docname="rotate_ccw.svg"
id="svg8"
version="1.1"
viewBox="0 0 8.4666665 8.4666669"
height="32"
width="32">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:window-height="1043"
inkscape:window-width="1920"
units="px"
inkscape:pagecheckerboard="true"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="a836"
inkscape:document-units="px"
inkscape:cy="22.023433"
inkscape:cx="5.8819795"
inkscape:zoom="11.617229"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<a
id="a836">
<path
id="path833"
style="fill:none;stroke:#ffffff;stroke-width:0.79375;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
d="M 6.8444093,4.4763634 C 6.1968058,3.7953131 5.2819769,3.3707564 4.267987,3.3707564 c -1.0092212,0 -1.9202122,0.4205733 -2.5672681,1.0960146"
sodipodi:nodetypes="csc" />
<path
sodipodi:nodetypes="ccc"
id="path1115"
d="M 2.0746212,2.5444979 1.7007189,4.4667709 3.1208546,5.1803407"
style="fill:none;stroke:#ffffff;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</a>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/home/pj/Projects/space-station-14/Resources/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png"
sodipodi:docname="rotate_cw.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 8.4666665 8.4666669"
height="32"
width="32">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:window-height="1043"
inkscape:window-width="1920"
units="px"
inkscape:pagecheckerboard="true"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="a836"
inkscape:document-units="px"
inkscape:cy="23.917177"
inkscape:cx="15.058009"
inkscape:zoom="11.617229"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<a
id="a836">
<path
id="path833"
style="fill:none;stroke:#ffffff;stroke-width:0.79375;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
d="m 1.5914553,4.4763634 c 0.6476035,-0.6810503 1.5624324,-1.105607 2.5764223,-1.105607 1.0092212,0 1.9202122,0.4205733 2.5672681,1.0960146"
sodipodi:nodetypes="csc" />
<path
sodipodi:nodetypes="ccc"
id="path1115"
d="M 6.3612434,2.5444979 6.7351457,4.4667709 5.31501,5.1803407"
style="fill:none;stroke:#ffffff;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</a>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@@ -4,6 +4,10 @@ binds:
type: state type: state
key: MouseLeft key: MouseLeft
canFocus: true canFocus: true
- function: CloseModals
type: state
key: Escape
priority: 10
- function: Use - function: Use
type: state type: state
key: MouseLeft key: MouseLeft
@@ -215,6 +219,7 @@ binds:
- function: TextReleaseFocus - function: TextReleaseFocus
type: state type: state
key: Escape key: Escape
priority: 15
- function: TextScrollToBottom - function: TextScrollToBottom
type: state type: state
key: PageDown key: PageDown

View File

@@ -47,6 +47,7 @@
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters&gt;&lt;Filter ModuleMask="Lidgren.Network" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /&gt;&lt;/ExcludeFilters&gt;&lt;/data&gt;</s:String> <s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters&gt;&lt;Filter ModuleMask="Lidgren.Network" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /&gt;&lt;/ExcludeFilters&gt;&lt;/data&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fluidsynth/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Fluidsynth/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=freepats/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=freepats/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kibibyte/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lerp/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Lerp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Noto/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Noto/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=occluder/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=occluder/@EntryIndexedValue">True</s:Boolean>