diff --git a/Content.Client/Examine/ExamineButton.cs b/Content.Client/Examine/ExamineButton.cs
new file mode 100644
index 0000000000..68c9ce36e8
--- /dev/null
+++ b/Content.Client/Examine/ExamineButton.cs
@@ -0,0 +1,61 @@
+using Content.Client.ContextMenu.UI;
+using Content.Client.Stylesheets;
+using Content.Shared.Verbs;
+using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Client.Utility;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Examine;
+
+///
+/// Buttons that show up in the examine tooltip to specify more detailed
+/// ways to examine an item.
+///
+public class ExamineButton : ContainerButton
+{
+ public const string StyleClassExamineButton = "examine-button";
+
+ public const int ElementHeight = 32;
+ public const int ElementWidth = 32;
+
+ private const int Thickness = 4;
+
+ public TextureRect Icon;
+
+ public ExamineVerb Verb;
+
+ public ExamineButton(ExamineVerb verb)
+ {
+ Margin = new Thickness(Thickness, Thickness, Thickness, Thickness);
+
+ SetOnlyStyleClass(StyleClassExamineButton);
+
+ Verb = verb;
+
+ if (verb.Disabled)
+ {
+ Disabled = true;
+ }
+
+ ToolTip = verb.Message;
+
+ Icon = new TextureRect
+ {
+ SetWidth = ElementWidth,
+ SetHeight = ElementHeight
+ };
+
+ if (verb.IconTexture != null)
+ {
+ var icon = new SpriteSpecifier.Texture(new ResourcePath(verb.IconTexture));
+
+ Icon.Texture = icon.Frame0();
+ Icon.Stretch = TextureRect.StretchMode.KeepAspectCentered;
+
+ AddChild(Icon);
+ }
+ }
+}
diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs
index e9201cebe0..4a8e2fc0c8 100644
--- a/Content.Client/Examine/ExamineSystem.cs
+++ b/Content.Client/Examine/ExamineSystem.cs
@@ -1,6 +1,6 @@
using System.Linq;
using System.Threading;
-using System.Threading.Tasks;
+using Content.Client.Verbs;
using Content.Shared.Examine;
using Content.Shared.Input;
using Content.Shared.Verbs;
@@ -10,13 +10,8 @@ using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
-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;
using Robust.Shared.Utility;
using static Content.Shared.Interaction.SharedInteractionSystem;
using static Robust.Client.UserInterface.Controls.BoxContainer;
@@ -29,6 +24,7 @@ namespace Content.Client.Examine
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
+ [Dependency] private readonly VerbSystem _verbSystem = default!;
public const string StyleClassEntityTooltip = "entity-tooltip";
@@ -41,7 +37,10 @@ namespace Content.Client.Examine
{
UpdatesOutsidePrediction = true;
- SubscribeLocalEvent>(AddExamineVerb);
+ SubscribeLocalEvent>(AddExamineVerb);
+
+ SubscribeNetworkEvent(OnExamineInfoResponse);
+ SubscribeNetworkEvent(OnVerbsResponse);
CommandBinds.Builder
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine, outsidePrediction: true))
@@ -90,40 +89,102 @@ namespace Content.Client.Examine
return true;
}
- private void AddExamineVerb(GetVerbsEvent args)
+ private void AddExamineVerb(GetVerbsEvent args)
{
if (!CanExamine(args.User, args.Target))
return;
- Verb verb = new();
- verb.Act = () => DoExamine(args.Target) ;
+ // Basic examine verb.
+ ExamineVerb verb = new();
+ verb.Category = VerbCategory.Examine;
+ verb.Priority = 10;
+ // Center it on the entity if they use the verb instead.
+ verb.Act = () => DoExamine(args.Target, false);
verb.Text = Loc.GetString("examine-verb-name");
verb.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
+ verb.ShowOnExamineTooltip = false;
verb.ClientExclusive = true;
args.Verbs.Add(verb);
}
- public async void DoExamine(EntityUid entity)
+ private void OnExamineInfoResponse(ExamineSystemMessages.ExamineInfoResponseMessage ev)
+ {
+ var player = _playerManager.LocalPlayer?.ControlledEntity;
+ if (player == null)
+ return;
+
+ // Tooltips coming in from the server generally prioritize
+ // opening at the old tooltip rather than the cursor/another entity,
+ // since there's probably one open already if it's coming in from the server.
+ OpenTooltip(player.Value, ev.EntityUid, ev.CenterAtCursor, ev.OpenAtOldTooltip);
+ UpdateTooltipInfo(player.Value, ev.EntityUid, ev.Message, ev.GetVerbs);
+ }
+
+ private void OnVerbsResponse(VerbsResponseEvent ev)
+ {
+ if (ev.Verbs == null || _examineTooltipOpen == null)
+ return;
+
+ var verbs = new List();
+
+ foreach (var verb in ev.Verbs)
+ {
+ if (verb is ExamineVerb ex)
+ verbs.Add(ex);
+ }
+
+ AddVerbsToTooltip(verbs);
+ }
+
+ public override void SendExamineTooltip(EntityUid player, EntityUid target, FormattedMessage message, bool getVerbs, bool centerAtCursor)
+ {
+ OpenTooltip(player, target, centerAtCursor, false);
+ UpdateTooltipInfo(player, target, message, getVerbs);
+ }
+
+ ///
+ /// Opens the tooltip window and sets spriteview/name/etc, but does
+ /// not fill it with information. This is done when the server sends examine info/verbs,
+ /// or immediately if it's entirely clientside.
+ ///
+ public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCursor=true, bool openAtOldTooltip=true)
{
// Close any examine tooltip that might already be opened
+ // Before we do that, save its position. We'll prioritize opening any new popups there if
+ // openAtOldTooltip is true.
+ var oldTooltipPos = _examineTooltipOpen?.ScreenCoordinates;
CloseTooltip();
// cache entity for Update function
- _examinedEntity = entity;
+ _examinedEntity = target;
const float minWidth = 300;
- var popupPos = _userInterfaceManager.MousePositionScaled;
+ ScreenCoordinates popupPos;
+
+ if (openAtOldTooltip && oldTooltipPos != null)
+ {
+ popupPos = oldTooltipPos.Value;
+ }
+ else if (centeredOnCursor)
+ {
+ popupPos = _userInterfaceManager.MousePositionScaled;
+ }
+ else
+ {
+ popupPos = _eyeManager.CoordinatesToScreen(Transform(target).Coordinates);
+ }
// Actually open the tooltip.
_examineTooltipOpen = new Popup { MaxWidth = 400 };
_userInterfaceManager.ModalRoot.AddChild(_examineTooltipOpen);
- var panel = new PanelContainer();
+ var panel = new PanelContainer() { Name = "ExaminePopupPanel" };
panel.AddStyleClass(StyleClassEntityTooltip);
panel.ModulateSelfOverride = Color.LightGray.WithAlpha(0.90f);
_examineTooltipOpen.AddChild(panel);
var vBox = new BoxContainer
{
+ Name = "ExaminePopupVbox",
Orientation = LayoutOrientation.Vertical
};
panel.AddChild(vBox);
@@ -133,16 +194,21 @@ namespace Content.Client.Examine
Orientation = LayoutOrientation.Horizontal,
SeparationOverride = 5
};
+
vBox.AddChild(hBox);
- if (EntityManager.TryGetComponent(entity, out ISpriteComponent? sprite))
+ if (EntityManager.TryGetComponent(target, out ISpriteComponent? sprite))
{
- hBox.AddChild(new SpriteView { Sprite = sprite, OverrideDirection = Direction.South });
+ hBox.AddChild(new SpriteView
+ {
+ Sprite = sprite, OverrideDirection = Direction.South,
+ Margin = new Thickness(2, 0, 2, 0),
+ });
}
hBox.AddChild(new Label
{
- Text = EntityManager.GetComponent(entity).EntityName,
+ Text = EntityManager.GetComponent(target).EntityName,
HorizontalExpand = true,
});
@@ -150,52 +216,125 @@ namespace Content.Client.Examine
var size = Vector2.ComponentMax((minWidth, 0), panel.DesiredSize);
_examineTooltipOpen.Open(UIBox2.FromDimensions(popupPos.Position, size));
+ }
- FormattedMessage message;
- if (entity.IsClientSide())
+ ///
+ /// Fills the examine tooltip with a message and buttons if applicable.
+ ///
+ public void UpdateTooltipInfo(EntityUid player, EntityUid target, FormattedMessage message, bool getVerbs = true)
+ {
+ var vBox = _examineTooltipOpen?.GetChild(0).GetChild(0);
+ if (vBox == null)
{
- message = GetExamineText(entity, _playerManager.LocalPlayer?.ControlledEntity);
- }
- else
- {
- // Ask server for extra examine info.
- RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity));
-
- ExamineSystemMessages.ExamineInfoResponseMessage response;
- try
- {
- _requestCancelTokenSource = new CancellationTokenSource();
- response =
- await AwaitNetworkEvent(_requestCancelTokenSource
- .Token);
- }
- catch (TaskCanceledException)
- {
- return;
- }
- finally
- {
- _requestCancelTokenSource = null;
- }
-
- message = response.Message;
+ return;
}
foreach (var msg in message.Tags.OfType())
{
if (string.IsNullOrWhiteSpace(msg.Text)) continue;
- var richLabel = new RichTextLabel();
+ var richLabel = new RichTextLabel() { Margin = new Thickness(4, 4, 0, 4)};
richLabel.SetMessage(message);
vBox.AddChild(richLabel);
break;
}
+
+ if (getVerbs)
+ {
+ // Get verbs
+ var set = _verbSystem.GetVerbs(target, player, typeof(ExamineVerb));
+ AddVerbsToTooltip(set);
+ }
+ }
+
+ private void AddVerbsToTooltip(IEnumerable verbs)
+ {
+ if (_examineTooltipOpen == null)
+ return;
+
+ var buttonsHBox = new BoxContainer
+ {
+ Name = "ExamineButtonsHBox",
+ Orientation = LayoutOrientation.Horizontal,
+ HorizontalAlignment = Control.HAlignment.Right,
+ VerticalAlignment = Control.VAlignment.Bottom,
+ };
+
+ // Examine button time
+ foreach (var verb in verbs)
+ {
+ if (verb is not ExamineVerb examine)
+ continue;
+
+ if (examine.IconTexture == null)
+ continue;
+
+ if (!examine.ShowOnExamineTooltip)
+ continue;
+
+ var button = new ExamineButton(examine);
+
+ button.OnPressed += VerbButtonPressed;
+ buttonsHBox.AddChild(button);
+ }
+
+ var vbox = _examineTooltipOpen?.GetChild(0).GetChild(0);
+ if (vbox == null)
+ {
+ buttonsHBox.Dispose();
+ return;
+ }
+
+ // Remove any existing buttons hbox, in case we generated it from the client
+ // then received ones from the server
+ var hbox = vbox.Children.Where(c => c.Name == "ExamineButtonsHBox").ToArray();
+ if (hbox.Any())
+ {
+ vbox.Children.Remove(hbox.First());
+ }
+ vbox.AddChild(buttonsHBox);
+ }
+
+ public void VerbButtonPressed(BaseButton.ButtonEventArgs obj)
+ {
+ if (obj.Button is ExamineButton button)
+ {
+ _verbSystem.ExecuteVerb(_examinedEntity, button.Verb);
+ }
+ }
+
+ public void DoExamine(EntityUid entity, bool centeredOnCursor=true)
+ {
+ var playerEnt = _playerManager.LocalPlayer?.ControlledEntity;
+ if (playerEnt == null)
+ return;
+
+ FormattedMessage message;
+ OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false);
+ if (entity.IsClientSide())
+ {
+ message = GetExamineText(entity, playerEnt);
+
+ UpdateTooltipInfo(playerEnt.Value, entity, message);
+ }
+ else
+ {
+ // Ask server for extra examine info.
+ RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity, true));
+ }
}
private void CloseTooltip()
{
if (_examineTooltipOpen != null)
{
+ foreach (var control in _examineTooltipOpen.Children)
+ {
+ if (control is ExamineButton button)
+ {
+ button.OnPressed -= VerbButtonPressed;
+ }
+ }
_examineTooltipOpen.Dispose();
_examineTooltipOpen = null;
}
diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs
index 67d73c1225..4e29394781 100644
--- a/Content.Client/Stylesheets/StyleNano.cs
+++ b/Content.Client/Stylesheets/StyleNano.cs
@@ -99,6 +99,12 @@ namespace Content.Client.Stylesheets
public static readonly Color ButtonColorContextPressed = Color.LightSlateGray;
public static readonly Color ButtonColorContextDisabled = Color.Black;
+ // Examine button colors
+ public static readonly Color ExamineButtonColorContext = Color.Transparent;
+ public static readonly Color ExamineButtonColorContextHover = Color.DarkSlateGray;
+ public static readonly Color ExamineButtonColorContextPressed = Color.LightSlateGray;
+ public static readonly Color ExamineButtonColorContextDisabled = Color.FromHex("#5A5A5A");
+
//Used by the APC and SMES menus
public const string StyleClassPowerStateNone = "PowerStateNone";
public const string StyleClassPowerStateLow = "PowerStateLow";
@@ -654,6 +660,26 @@ namespace Content.Client.Stylesheets
.Pseudo(ContainerButton.StylePseudoClassDisabled)
.Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDisabled),
+ // Examine buttons
+ Element().Class(ExamineButton.StyleClassExamineButton)
+ .Prop(ContainerButton.StylePropertyStyleBox, buttonContext),
+
+ Element().Class(ExamineButton.StyleClassExamineButton)
+ .Pseudo(ContainerButton.StylePseudoClassNormal)
+ .Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContext),
+
+ Element().Class(ExamineButton.StyleClassExamineButton)
+ .Pseudo(ContainerButton.StylePseudoClassHover)
+ .Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextHover),
+
+ Element().Class(ExamineButton.StyleClassExamineButton)
+ .Pseudo(ContainerButton.StylePseudoClassPressed)
+ .Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextPressed),
+
+ Element().Class(ExamineButton.StyleClassExamineButton)
+ .Pseudo(ContainerButton.StylePseudoClassDisabled)
+ .Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextDisabled),
+
// Thin buttons (No padding nor vertical margin)
Element().Class(StyleClassStorageButton)
.Prop(ContainerButton.StylePropertyStyleBox, buttonStorage),
diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs
index 11567ea3bf..0c62d7bdd0 100644
--- a/Content.Client/Verbs/VerbSystem.cs
+++ b/Content.Client/Verbs/VerbSystem.cs
@@ -177,6 +177,14 @@ namespace Content.Client.Verbs
return true;
}
+ ///
+ /// Asks the server to send back a list of server-side verbs, for the given verb type.
+ ///
+ public SortedSet GetVerbs(EntityUid target, EntityUid user, Type type, bool force = false)
+ {
+ return GetVerbs(target, user, new List() { type }, force);
+ }
+
///
/// Ask the server to send back a list of server-side verbs, and for now return an incomplete list of verbs
/// (only those defined locally).
diff --git a/Content.Server/Examine/ExamineSystem.cs b/Content.Server/Examine/ExamineSystem.cs
index 5bf6e4a6bd..c51c61ac8a 100644
--- a/Content.Server/Examine/ExamineSystem.cs
+++ b/Content.Server/Examine/ExamineSystem.cs
@@ -1,5 +1,6 @@
using Content.Shared.Examine;
using JetBrains.Annotations;
+using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
@@ -25,6 +26,20 @@ namespace Content.Server.Examine
SubscribeNetworkEvent(ExamineInfoRequest);
}
+ public override void SendExamineTooltip(EntityUid player, EntityUid target, FormattedMessage message, bool getVerbs, bool centerAtCursor)
+ {
+ if (!TryComp(player, out var actor))
+ return;
+
+ var session = actor.PlayerSession;
+
+ var ev = new ExamineSystemMessages.ExamineInfoResponseMessage(
+ target, message, getVerbs, centerAtCursor
+ );
+
+ RaiseNetworkEvent(ev, session.ConnectedClient);
+ }
+
private void ExamineInfoRequest(ExamineSystemMessages.RequestExamineInfoMessage request, EntitySessionEventArgs eventArgs)
{
var player = (IPlayerSession) eventArgs.SenderSession;
@@ -36,12 +51,12 @@ namespace Content.Server.Examine
|| !CanExamine(playerEnt, request.EntityUid))
{
RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
- request.EntityUid, _entityNotFoundMessage), channel);
+ request.EntityUid, _entityNotFoundMessage, request.GetVerbs), channel);
return;
}
var text = GetExamineText(request.EntityUid, player.AttachedEntity);
- RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(request.EntityUid, text), channel);
+ RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(request.EntityUid, text, request.GetVerbs), channel);
}
}
}
diff --git a/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs b/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs
index 998268e24a..75d624c944 100644
--- a/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs
+++ b/Content.Server/Power/EntitySystems/CableMultitoolSystem.cs
@@ -1,25 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
using Content.Server.NodeContainer;
-using Content.Server.NodeContainer.EntitySystems;
using Content.Server.Power.Components;
-using Content.Server.Power.Nodes;
-using Content.Server.Power.Pow3r;
using Content.Server.Power.NodeGroups;
-using Content.Server.Power.EntitySystems;
-using Content.Server.Hands.Components;
using Content.Server.Tools;
-using Content.Shared.Wires;
using Content.Shared.Examine;
-using Content.Shared.Hands.Components;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Map;
-using Robust.Shared.Maths;
-using Robust.Shared.Localization;
+using Robust.Shared.Utility;
namespace Content.Server.Power.EntitySystems
{
@@ -28,32 +14,41 @@ namespace Content.Server.Power.EntitySystems
{
[Dependency] private readonly ToolSystem _toolSystem = default!;
[Dependency] private readonly PowerNetSystem _pnSystem = default!;
+ [Dependency] private readonly ExamineSystemShared _examineSystem = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent(OnExamined);
+ SubscribeLocalEvent>(OnGetExamineVerbs);
}
- private void OnExamined(EntityUid uid, CableComponent component, ExaminedEvent args)
+ private void OnGetExamineVerbs(EntityUid uid, CableComponent component, GetVerbsEvent args)
{
// Must be in details range to try this.
// Theoretically there should be a separate range at which a multitool works, but this does just fine.
- if (args.IsInDetailsRange)
+ if (_examineSystem.IsInDetailsRange(args.User, args.Target))
{
- // Determine if they are holding a multitool.
- if (EntityManager.TryGetComponent(args.Examiner, out var hands) && hands.TryGetActiveHand(out var hand))
+ var held = args.Using;
+
+ // Pulsing is hardcoded here because I don't think it needs to be more complex than that right now.
+ // Update if I'm wrong.
+ var enabled = held != null && _toolSystem.HasQuality(held.Value, "Pulsing");
+ var verb = new ExamineVerb
{
- var held = hand.HeldEntity;
- // Pulsing is hardcoded here because I don't think it needs to be more complex than that right now.
- // Update if I'm wrong.
- if (held != null && _toolSystem.HasQuality(held.Value, "Pulsing"))
+ Disabled = !enabled,
+ Message = Loc.GetString("cable-multitool-system-verb-tooltip"),
+ Text = Loc.GetString("cable-multitool-system-verb-name"),
+ Category = VerbCategory.Examine,
+ IconTexture = "/Textures/Interface/VerbIcons/zap.svg.192dpi.png",
+ Act = () =>
{
- args.PushMarkup(GenerateCableMarkup(uid));
- // args.PushFancyUpdatingPowerGraphs(uid);
+ var markup = FormattedMessage.FromMarkup(GenerateCableMarkup(uid));
+ _examineSystem.SendExamineTooltip(args.User, uid, markup, false, false);
}
- }
+ };
+
+ args.Verbs.Add(verb);
}
}
diff --git a/Content.Shared/Examine/ExamineSystemMessages.cs b/Content.Shared/Examine/ExamineSystemMessages.cs
index c8e0d41bdb..3928f142d5 100644
--- a/Content.Shared/Examine/ExamineSystemMessages.cs
+++ b/Content.Shared/Examine/ExamineSystemMessages.cs
@@ -12,9 +12,12 @@ namespace Content.Shared.Examine
{
public readonly EntityUid EntityUid;
- public RequestExamineInfoMessage(EntityUid entityUid)
+ public readonly bool GetVerbs;
+
+ public RequestExamineInfoMessage(EntityUid entityUid, bool getVerbs=false)
{
EntityUid = entityUid;
+ GetVerbs = getVerbs;
}
}
@@ -24,10 +27,18 @@ namespace Content.Shared.Examine
public readonly EntityUid EntityUid;
public readonly FormattedMessage Message;
- public ExamineInfoResponseMessage(EntityUid entityUid, FormattedMessage message)
+ public readonly bool GetVerbs;
+ public readonly bool CenterAtCursor;
+ public readonly bool OpenAtOldTooltip;
+
+ public ExamineInfoResponseMessage(EntityUid entityUid, FormattedMessage message,
+ bool getVerbs=false, bool centerAtCursor=true, bool openAtOldTooltip=true)
{
EntityUid = entityUid;
Message = message;
+ GetVerbs = getVerbs;
+ CenterAtCursor = centerAtCursor;
+ OpenAtOldTooltip = openAtOldTooltip;
}
}
}
diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs
index 832d2c1f8b..9ea12a774a 100644
--- a/Content.Shared/Examine/ExamineSystemShared.cs
+++ b/Content.Shared/Examine/ExamineSystemShared.cs
@@ -54,7 +54,12 @@ namespace Content.Shared.Examine
public const float ExamineRange = 16f;
protected const float ExamineDetailsRange = 3f;
- private bool IsInDetailsRange(EntityUid examiner, EntityUid entity)
+ ///
+ /// Creates a new examine tooltip with arbitrary info.
+ ///
+ public abstract void SendExamineTooltip(EntityUid player, EntityUid target, FormattedMessage message, bool getVerbs, bool centerAtCursor);
+
+ public bool IsInDetailsRange(EntityUid examiner, EntityUid entity)
{
// check if the mob is in ciritcal or dead
if (EntityManager.TryGetComponent(examiner, out MobStateComponent mobState) && mobState.IsIncapacitated())
diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs
index f19b264a63..caa590dda1 100644
--- a/Content.Shared/Verbs/SharedVerbSystem.cs
+++ b/Content.Shared/Verbs/SharedVerbSystem.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Content.Shared.ActionBlocker;
+using Content.Shared.Examine;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Robust.Shared.Containers;
@@ -15,6 +16,7 @@ namespace Content.Shared.Verbs
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
+ [Dependency] private readonly ExamineSystemShared _examineSystem = default!;
public override void Initialize()
{
@@ -112,6 +114,13 @@ namespace Content.Shared.Verbs
verbs.UnionWith(verbEvent.Verbs);
}
+ if (types.Contains(typeof(ExamineVerb)))
+ {
+ var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract, canAccess);
+ RaiseLocalEvent(target, verbEvent);
+ verbs.UnionWith(verbEvent.Verbs);
+ }
+
// generic verbs
if (types.Contains(typeof(Verb)))
{
diff --git a/Content.Shared/Verbs/Verb.cs b/Content.Shared/Verbs/Verb.cs
index a2a7e14a4e..ce21a4bc1f 100644
--- a/Content.Shared/Verbs/Verb.cs
+++ b/Content.Shared/Verbs/Verb.cs
@@ -202,6 +202,7 @@ namespace Content.Shared.Verbs
{ typeof(InteractionVerb) },
{ typeof(AlternativeVerb) },
{ typeof(ActivationVerb) },
+ { typeof(ExamineVerb) }
};
}
@@ -264,4 +265,12 @@ namespace Content.Shared.Verbs
TextStyleClass = DefaultTextStyleClass;
}
}
+
+ [Serializable, NetSerializable]
+ public sealed class ExamineVerb : Verb
+ {
+ public override int TypePriority => 0;
+
+ public bool ShowOnExamineTooltip = true;
+ }
}
diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs
index dbc30eced3..07417a4894 100644
--- a/Content.Shared/Verbs/VerbCategory.cs
+++ b/Content.Shared/Verbs/VerbCategory.cs
@@ -34,6 +34,9 @@ namespace Content.Shared.Verbs
public static readonly VerbCategory Admin =
new("verb-categories-admin", "/Textures/Interface/character.svg.192dpi.png");
+ public static readonly VerbCategory Examine =
+ new("verb-categories-examine", "/Textures/Interface/VerbIcons/examine.svg.192dpi.png");
+
public static readonly VerbCategory Debug =
new("verb-categories-debug", "/Textures/Interface/VerbIcons/debug.svg.192dpi.png");
diff --git a/Resources/Locale/en-US/cable/cable-multitool-system.ftl b/Resources/Locale/en-US/cable/cable-multitool-system.ftl
index 6b15bcbd00..491844dd1e 100644
--- a/Resources/Locale/en-US/cable/cable-multitool-system.ftl
+++ b/Resources/Locale/en-US/cable/cable-multitool-system.ftl
@@ -1,5 +1,7 @@
cable-multitool-system-internal-error-no-power-node = Your multitool reads, "INTERNAL ERROR: NOT A POWER CABLE".
cable-multitool-system-internal-error-missing-component = Your multitool reads, "INTERNAL ERROR: CABLE ABNORMAL".
+cable-multitool-system-verb-name = Power
+cable-multitool-system-verb-tooltip = Requires multitool
cable-multitool-system-statistics = Your multitool shows a list of statistics:
Current Supply: { POWERWATTS($supplyc) }
diff --git a/Resources/Locale/en-US/examine/examine-system.ftl b/Resources/Locale/en-US/examine/examine-system.ftl
index 0337e9646c..74d806bcf9 100644
--- a/Resources/Locale/en-US/examine/examine-system.ftl
+++ b/Resources/Locale/en-US/examine/examine-system.ftl
@@ -2,4 +2,4 @@
examine-system-entity-does-not-exist = That entity doesn't exist
-examine-verb-name = Examine
\ No newline at end of file
+examine-verb-name = Basic
diff --git a/Resources/Locale/en-US/verbs/verb-system.ftl b/Resources/Locale/en-US/verbs/verb-system.ftl
index ec9ff7763d..622813050d 100644
--- a/Resources/Locale/en-US/verbs/verb-system.ftl
+++ b/Resources/Locale/en-US/verbs/verb-system.ftl
@@ -11,6 +11,7 @@ verb-self-target-pronoun = Yourself
verb-categories-admin = Admin
verb-categories-debug = Debug
+verb-categories-examine = Examine
verb-categories-eject = Eject
verb-categories-insert = Insert
verb-categories-buckle = Buckle
diff --git a/Resources/Textures/Interface/VerbIcons/Spare/zap.svg b/Resources/Textures/Interface/VerbIcons/Spare/zap.svg
deleted file mode 100644
index a51b13e80d..0000000000
--- a/Resources/Textures/Interface/VerbIcons/Spare/zap.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/Resources/Textures/Interface/VerbIcons/zap.svg b/Resources/Textures/Interface/VerbIcons/zap.svg
new file mode 100644
index 0000000000..c0e358d248
--- /dev/null
+++ b/Resources/Textures/Interface/VerbIcons/zap.svg
@@ -0,0 +1,45 @@
+
+
diff --git a/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png b/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png
new file mode 100644
index 0000000000..ba03bed967
Binary files /dev/null and b/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png differ
diff --git a/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png.yml b/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png.yml
new file mode 100644
index 0000000000..5c43e23305
--- /dev/null
+++ b/Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png.yml
@@ -0,0 +1,2 @@
+sample:
+ filter: true