Examine verbs + tooltip buttons (#6489)
This commit is contained in:
61
Content.Client/Examine/ExamineButton.cs
Normal file
61
Content.Client/Examine/ExamineButton.cs
Normal file
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buttons that show up in the examine tooltip to specify more detailed
|
||||||
|
/// ways to examine an item.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using Content.Client.Verbs;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
@@ -10,13 +10,8 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using static Content.Shared.Interaction.SharedInteractionSystem;
|
using static Content.Shared.Interaction.SharedInteractionSystem;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
@@ -29,6 +24,7 @@ namespace Content.Client.Examine
|
|||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
|
[Dependency] private readonly VerbSystem _verbSystem = default!;
|
||||||
|
|
||||||
public const string StyleClassEntityTooltip = "entity-tooltip";
|
public const string StyleClassEntityTooltip = "entity-tooltip";
|
||||||
|
|
||||||
@@ -41,7 +37,10 @@ namespace Content.Client.Examine
|
|||||||
{
|
{
|
||||||
UpdatesOutsidePrediction = true;
|
UpdatesOutsidePrediction = true;
|
||||||
|
|
||||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddExamineVerb);
|
SubscribeLocalEvent<GetVerbsEvent<ExamineVerb>>(AddExamineVerb);
|
||||||
|
|
||||||
|
SubscribeNetworkEvent<ExamineSystemMessages.ExamineInfoResponseMessage>(OnExamineInfoResponse);
|
||||||
|
SubscribeNetworkEvent<VerbsResponseEvent>(OnVerbsResponse);
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine, outsidePrediction: true))
|
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine, outsidePrediction: true))
|
||||||
@@ -90,40 +89,102 @@ namespace Content.Client.Examine
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddExamineVerb(GetVerbsEvent<Verb> args)
|
private void AddExamineVerb(GetVerbsEvent<ExamineVerb> args)
|
||||||
{
|
{
|
||||||
if (!CanExamine(args.User, args.Target))
|
if (!CanExamine(args.User, args.Target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Verb verb = new();
|
// Basic examine verb.
|
||||||
verb.Act = () => DoExamine(args.Target) ;
|
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.Text = Loc.GetString("examine-verb-name");
|
||||||
verb.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
|
verb.IconTexture = "/Textures/Interface/VerbIcons/examine.svg.192dpi.png";
|
||||||
|
verb.ShowOnExamineTooltip = false;
|
||||||
verb.ClientExclusive = true;
|
verb.ClientExclusive = true;
|
||||||
args.Verbs.Add(verb);
|
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<ExamineVerb>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCursor=true, bool openAtOldTooltip=true)
|
||||||
{
|
{
|
||||||
// Close any examine tooltip that might already be opened
|
// 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();
|
CloseTooltip();
|
||||||
|
|
||||||
// cache entity for Update function
|
// cache entity for Update function
|
||||||
_examinedEntity = entity;
|
_examinedEntity = target;
|
||||||
|
|
||||||
const float minWidth = 300;
|
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.
|
// Actually open the tooltip.
|
||||||
_examineTooltipOpen = new Popup { MaxWidth = 400 };
|
_examineTooltipOpen = new Popup { MaxWidth = 400 };
|
||||||
_userInterfaceManager.ModalRoot.AddChild(_examineTooltipOpen);
|
_userInterfaceManager.ModalRoot.AddChild(_examineTooltipOpen);
|
||||||
var panel = new PanelContainer();
|
var panel = new PanelContainer() { Name = "ExaminePopupPanel" };
|
||||||
panel.AddStyleClass(StyleClassEntityTooltip);
|
panel.AddStyleClass(StyleClassEntityTooltip);
|
||||||
panel.ModulateSelfOverride = Color.LightGray.WithAlpha(0.90f);
|
panel.ModulateSelfOverride = Color.LightGray.WithAlpha(0.90f);
|
||||||
_examineTooltipOpen.AddChild(panel);
|
_examineTooltipOpen.AddChild(panel);
|
||||||
|
|
||||||
var vBox = new BoxContainer
|
var vBox = new BoxContainer
|
||||||
{
|
{
|
||||||
|
Name = "ExaminePopupVbox",
|
||||||
Orientation = LayoutOrientation.Vertical
|
Orientation = LayoutOrientation.Vertical
|
||||||
};
|
};
|
||||||
panel.AddChild(vBox);
|
panel.AddChild(vBox);
|
||||||
@@ -133,16 +194,21 @@ namespace Content.Client.Examine
|
|||||||
Orientation = LayoutOrientation.Horizontal,
|
Orientation = LayoutOrientation.Horizontal,
|
||||||
SeparationOverride = 5
|
SeparationOverride = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
vBox.AddChild(hBox);
|
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
|
hBox.AddChild(new Label
|
||||||
{
|
{
|
||||||
Text = EntityManager.GetComponent<MetaDataComponent>(entity).EntityName,
|
Text = EntityManager.GetComponent<MetaDataComponent>(target).EntityName,
|
||||||
HorizontalExpand = true,
|
HorizontalExpand = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,52 +216,125 @@ namespace Content.Client.Examine
|
|||||||
var size = Vector2.ComponentMax((minWidth, 0), panel.DesiredSize);
|
var size = Vector2.ComponentMax((minWidth, 0), panel.DesiredSize);
|
||||||
|
|
||||||
_examineTooltipOpen.Open(UIBox2.FromDimensions(popupPos.Position, size));
|
_examineTooltipOpen.Open(UIBox2.FromDimensions(popupPos.Position, size));
|
||||||
|
}
|
||||||
|
|
||||||
FormattedMessage message;
|
/// <summary>
|
||||||
if (entity.IsClientSide())
|
/// Fills the examine tooltip with a message and buttons if applicable.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
return;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Ask server for extra examine info.
|
|
||||||
RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity));
|
|
||||||
|
|
||||||
ExamineSystemMessages.ExamineInfoResponseMessage response;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_requestCancelTokenSource = new CancellationTokenSource();
|
|
||||||
response =
|
|
||||||
await AwaitNetworkEvent<ExamineSystemMessages.ExamineInfoResponseMessage>(_requestCancelTokenSource
|
|
||||||
.Token);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_requestCancelTokenSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
message = response.Message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var msg in message.Tags.OfType<FormattedMessage.TagText>())
|
foreach (var msg in message.Tags.OfType<FormattedMessage.TagText>())
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(msg.Text)) continue;
|
if (string.IsNullOrWhiteSpace(msg.Text)) continue;
|
||||||
|
|
||||||
var richLabel = new RichTextLabel();
|
var richLabel = new RichTextLabel() { Margin = new Thickness(4, 4, 0, 4)};
|
||||||
richLabel.SetMessage(message);
|
richLabel.SetMessage(message);
|
||||||
vBox.AddChild(richLabel);
|
vBox.AddChild(richLabel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getVerbs)
|
||||||
|
{
|
||||||
|
// Get verbs
|
||||||
|
var set = _verbSystem.GetVerbs(target, player, typeof(ExamineVerb));
|
||||||
|
AddVerbsToTooltip(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddVerbsToTooltip(IEnumerable<Verb> 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()
|
private void CloseTooltip()
|
||||||
{
|
{
|
||||||
if (_examineTooltipOpen != null)
|
if (_examineTooltipOpen != null)
|
||||||
{
|
{
|
||||||
|
foreach (var control in _examineTooltipOpen.Children)
|
||||||
|
{
|
||||||
|
if (control is ExamineButton button)
|
||||||
|
{
|
||||||
|
button.OnPressed -= VerbButtonPressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
_examineTooltipOpen.Dispose();
|
_examineTooltipOpen.Dispose();
|
||||||
_examineTooltipOpen = null;
|
_examineTooltipOpen = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ namespace Content.Client.Stylesheets
|
|||||||
public static readonly Color ButtonColorContextPressed = Color.LightSlateGray;
|
public static readonly Color ButtonColorContextPressed = Color.LightSlateGray;
|
||||||
public static readonly Color ButtonColorContextDisabled = Color.Black;
|
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
|
//Used by the APC and SMES menus
|
||||||
public const string StyleClassPowerStateNone = "PowerStateNone";
|
public const string StyleClassPowerStateNone = "PowerStateNone";
|
||||||
public const string StyleClassPowerStateLow = "PowerStateLow";
|
public const string StyleClassPowerStateLow = "PowerStateLow";
|
||||||
@@ -654,6 +660,26 @@ namespace Content.Client.Stylesheets
|
|||||||
.Pseudo(ContainerButton.StylePseudoClassDisabled)
|
.Pseudo(ContainerButton.StylePseudoClassDisabled)
|
||||||
.Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDisabled),
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDisabled),
|
||||||
|
|
||||||
|
// Examine buttons
|
||||||
|
Element<ExamineButton>().Class(ExamineButton.StyleClassExamineButton)
|
||||||
|
.Prop(ContainerButton.StylePropertyStyleBox, buttonContext),
|
||||||
|
|
||||||
|
Element<ExamineButton>().Class(ExamineButton.StyleClassExamineButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassNormal)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContext),
|
||||||
|
|
||||||
|
Element<ExamineButton>().Class(ExamineButton.StyleClassExamineButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextHover),
|
||||||
|
|
||||||
|
Element<ExamineButton>().Class(ExamineButton.StyleClassExamineButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassPressed)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextPressed),
|
||||||
|
|
||||||
|
Element<ExamineButton>().Class(ExamineButton.StyleClassExamineButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassDisabled)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ExamineButtonColorContextDisabled),
|
||||||
|
|
||||||
// Thin buttons (No padding nor vertical margin)
|
// Thin buttons (No padding nor vertical margin)
|
||||||
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
.Prop(ContainerButton.StylePropertyStyleBox, buttonStorage),
|
.Prop(ContainerButton.StylePropertyStyleBox, buttonStorage),
|
||||||
|
|||||||
@@ -177,6 +177,14 @@ namespace Content.Client.Verbs
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asks the server to send back a list of server-side verbs, for the given verb type.
|
||||||
|
/// </summary>
|
||||||
|
public SortedSet<Verb> GetVerbs(EntityUid target, EntityUid user, Type type, bool force = false)
|
||||||
|
{
|
||||||
|
return GetVerbs(target, user, new List<Type>() { type }, force);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ask the server to send back a list of server-side verbs, and for now return an incomplete list of verbs
|
/// 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).
|
/// (only those defined locally).
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
@@ -25,6 +26,20 @@ namespace Content.Server.Examine
|
|||||||
SubscribeNetworkEvent<ExamineSystemMessages.RequestExamineInfoMessage>(ExamineInfoRequest);
|
SubscribeNetworkEvent<ExamineSystemMessages.RequestExamineInfoMessage>(ExamineInfoRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void SendExamineTooltip(EntityUid player, EntityUid target, FormattedMessage message, bool getVerbs, bool centerAtCursor)
|
||||||
|
{
|
||||||
|
if (!TryComp<ActorComponent>(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)
|
private void ExamineInfoRequest(ExamineSystemMessages.RequestExamineInfoMessage request, EntitySessionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
var player = (IPlayerSession) eventArgs.SenderSession;
|
var player = (IPlayerSession) eventArgs.SenderSession;
|
||||||
@@ -36,12 +51,12 @@ namespace Content.Server.Examine
|
|||||||
|| !CanExamine(playerEnt, request.EntityUid))
|
|| !CanExamine(playerEnt, request.EntityUid))
|
||||||
{
|
{
|
||||||
RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
|
RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
|
||||||
request.EntityUid, _entityNotFoundMessage), channel);
|
request.EntityUid, _entityNotFoundMessage, request.GetVerbs), channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = GetExamineText(request.EntityUid, player.AttachedEntity);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,11 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.NodeContainer;
|
using Content.Server.NodeContainer;
|
||||||
using Content.Server.NodeContainer.EntitySystems;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.Nodes;
|
|
||||||
using Content.Server.Power.Pow3r;
|
|
||||||
using Content.Server.Power.NodeGroups;
|
using Content.Server.Power.NodeGroups;
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Hands.Components;
|
|
||||||
using Content.Server.Tools;
|
using Content.Server.Tools;
|
||||||
using Content.Shared.Wires;
|
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Verbs;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Server.Power.EntitySystems
|
namespace Content.Server.Power.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -28,32 +14,41 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly ToolSystem _toolSystem = default!;
|
[Dependency] private readonly ToolSystem _toolSystem = default!;
|
||||||
[Dependency] private readonly PowerNetSystem _pnSystem = default!;
|
[Dependency] private readonly PowerNetSystem _pnSystem = default!;
|
||||||
|
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<CableComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<CableComponent, GetVerbsEvent<ExamineVerb>>(OnGetExamineVerbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExamined(EntityUid uid, CableComponent component, ExaminedEvent args)
|
private void OnGetExamineVerbs(EntityUid uid, CableComponent component, GetVerbsEvent<ExamineVerb> args)
|
||||||
{
|
{
|
||||||
// Must be in details range to try this.
|
// 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.
|
// 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.
|
var held = args.Using;
|
||||||
if (EntityManager.TryGetComponent<HandsComponent?>(args.Examiner, out var hands) && hands.TryGetActiveHand(out var hand))
|
|
||||||
|
// 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;
|
Disabled = !enabled,
|
||||||
// Pulsing is hardcoded here because I don't think it needs to be more complex than that right now.
|
Message = Loc.GetString("cable-multitool-system-verb-tooltip"),
|
||||||
// Update if I'm wrong.
|
Text = Loc.GetString("cable-multitool-system-verb-name"),
|
||||||
if (held != null && _toolSystem.HasQuality(held.Value, "Pulsing"))
|
Category = VerbCategory.Examine,
|
||||||
|
IconTexture = "/Textures/Interface/VerbIcons/zap.svg.192dpi.png",
|
||||||
|
Act = () =>
|
||||||
{
|
{
|
||||||
args.PushMarkup(GenerateCableMarkup(uid));
|
var markup = FormattedMessage.FromMarkup(GenerateCableMarkup(uid));
|
||||||
// args.PushFancyUpdatingPowerGraphs(uid);
|
_examineSystem.SendExamineTooltip(args.User, uid, markup, false, false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
args.Verbs.Add(verb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ namespace Content.Shared.Examine
|
|||||||
{
|
{
|
||||||
public readonly EntityUid EntityUid;
|
public readonly EntityUid EntityUid;
|
||||||
|
|
||||||
public RequestExamineInfoMessage(EntityUid entityUid)
|
public readonly bool GetVerbs;
|
||||||
|
|
||||||
|
public RequestExamineInfoMessage(EntityUid entityUid, bool getVerbs=false)
|
||||||
{
|
{
|
||||||
EntityUid = entityUid;
|
EntityUid = entityUid;
|
||||||
|
GetVerbs = getVerbs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,10 +27,18 @@ namespace Content.Shared.Examine
|
|||||||
public readonly EntityUid EntityUid;
|
public readonly EntityUid EntityUid;
|
||||||
public readonly FormattedMessage Message;
|
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;
|
EntityUid = entityUid;
|
||||||
Message = message;
|
Message = message;
|
||||||
|
GetVerbs = getVerbs;
|
||||||
|
CenterAtCursor = centerAtCursor;
|
||||||
|
OpenAtOldTooltip = openAtOldTooltip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,12 @@ namespace Content.Shared.Examine
|
|||||||
public const float ExamineRange = 16f;
|
public const float ExamineRange = 16f;
|
||||||
protected const float ExamineDetailsRange = 3f;
|
protected const float ExamineDetailsRange = 3f;
|
||||||
|
|
||||||
private bool IsInDetailsRange(EntityUid examiner, EntityUid entity)
|
/// <summary>
|
||||||
|
/// Creates a new examine tooltip with arbitrary info.
|
||||||
|
/// </summary>
|
||||||
|
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
|
// check if the mob is in ciritcal or dead
|
||||||
if (EntityManager.TryGetComponent(examiner, out MobStateComponent mobState) && mobState.IsIncapacitated())
|
if (EntityManager.TryGetComponent(examiner, out MobStateComponent mobState) && mobState.IsIncapacitated())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -15,6 +16,7 @@ namespace Content.Shared.Verbs
|
|||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
||||||
|
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -112,6 +114,13 @@ namespace Content.Shared.Verbs
|
|||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (types.Contains(typeof(ExamineVerb)))
|
||||||
|
{
|
||||||
|
var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract, canAccess);
|
||||||
|
RaiseLocalEvent(target, verbEvent);
|
||||||
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
|
}
|
||||||
|
|
||||||
// generic verbs
|
// generic verbs
|
||||||
if (types.Contains(typeof(Verb)))
|
if (types.Contains(typeof(Verb)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ namespace Content.Shared.Verbs
|
|||||||
{ typeof(InteractionVerb) },
|
{ typeof(InteractionVerb) },
|
||||||
{ typeof(AlternativeVerb) },
|
{ typeof(AlternativeVerb) },
|
||||||
{ typeof(ActivationVerb) },
|
{ typeof(ActivationVerb) },
|
||||||
|
{ typeof(ExamineVerb) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,4 +265,12 @@ namespace Content.Shared.Verbs
|
|||||||
TextStyleClass = DefaultTextStyleClass;
|
TextStyleClass = DefaultTextStyleClass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ExamineVerb : Verb
|
||||||
|
{
|
||||||
|
public override int TypePriority => 0;
|
||||||
|
|
||||||
|
public bool ShowOnExamineTooltip = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace Content.Shared.Verbs
|
|||||||
public static readonly VerbCategory Admin =
|
public static readonly VerbCategory Admin =
|
||||||
new("verb-categories-admin", "/Textures/Interface/character.svg.192dpi.png");
|
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 =
|
public static readonly VerbCategory Debug =
|
||||||
new("verb-categories-debug", "/Textures/Interface/VerbIcons/debug.svg.192dpi.png");
|
new("verb-categories-debug", "/Textures/Interface/VerbIcons/debug.svg.192dpi.png");
|
||||||
|
|
||||||
|
|||||||
@@ -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-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-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:
|
cable-multitool-system-statistics = Your multitool shows a list of statistics:
|
||||||
Current Supply: { POWERWATTS($supplyc) }
|
Current Supply: { POWERWATTS($supplyc) }
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
examine-system-entity-does-not-exist = That entity doesn't exist
|
examine-system-entity-does-not-exist = That entity doesn't exist
|
||||||
|
|
||||||
examine-verb-name = Examine
|
examine-verb-name = Basic
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ verb-self-target-pronoun = Yourself
|
|||||||
|
|
||||||
verb-categories-admin = Admin
|
verb-categories-admin = Admin
|
||||||
verb-categories-debug = Debug
|
verb-categories-debug = Debug
|
||||||
|
verb-categories-examine = Examine
|
||||||
verb-categories-eject = Eject
|
verb-categories-eject = Eject
|
||||||
verb-categories-insert = Insert
|
verb-categories-insert = Insert
|
||||||
verb-categories-buckle = Buckle
|
verb-categories-buckle = Buckle
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 78 120"><title>Shape</title><path d="M77.74 41.135L18.71 119.7l13.61-55.26-31.664.533L51.996 0 36.58 41.17l41.16-.035z" fill-rule="evenodd"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 200 B |
45
Resources/Textures/Interface/VerbIcons/zap.svg
Normal file
45
Resources/Textures/Interface/VerbIcons/zap.svg
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 78 120"
|
||||||
|
version="1.1"
|
||||||
|
id="svg838"
|
||||||
|
sodipodi:docname="zap.svg"
|
||||||
|
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs842" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview840"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="6.7333333"
|
||||||
|
inkscape:cx="38.985149"
|
||||||
|
inkscape:cy="60"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg838" />
|
||||||
|
<title
|
||||||
|
id="title834">Shape</title>
|
||||||
|
<path
|
||||||
|
d="M77.74 41.135L18.71 119.7l13.61-55.26-31.664.533L51.996 0 36.58 41.17l41.16-.035z"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path836" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;stroke-width:0.148515"
|
||||||
|
d="M 51.378887,0.73184174 C 40.143953,14.744322 29.068717,28.88831 17.91146,42.964424 12.173212,50.226506 6.434937,57.488835 0.69703357,64.751315 0.87039413,65.482357 2.2926888,64.858548 2.9358643,65.045854 12.65057,64.946339 22.363256,64.681025 32.078048,64.611212 27.766959,82.758698 23.158732,100.85441 18.812655,118.9952 c 0.352576,0.65852 0.81081,-0.45504 1.064259,-0.69471 C 39.157274,92.681045 58.40798,67.038989 77.672107,41.407216 77.485126,40.682352 76.078071,41.280749 75.433365,41.083445 62.560092,41.041386 49.686053,41.084375 36.813408,40.941077 41.744256,27.594276 46.828638,14.303459 51.699702,0.93517946 51.744557,0.75478192 51.513682,0.61789109 51.378887,0.73184174 Z"
|
||||||
|
id="path1271"
|
||||||
|
inkscape:export-filename="zap.svg.192dpi.png"
|
||||||
|
inkscape:export-xdpi="192"
|
||||||
|
inkscape:export-ydpi="192" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png
Normal file
BIN
Resources/Textures/Interface/VerbIcons/zap.svg.192dpi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
sample:
|
||||||
|
filter: true
|
||||||
Reference in New Issue
Block a user