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 @@ -Shape \ 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 @@ + + + + + Shape + + + 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