Adds hand labelers (#4903)
* Adds hand labelers * Removes unnecessary thing * Docs * Reverts some changes * Addresses comments and adds inhand sprites * Addressed comments Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com>
This commit is contained in:
@@ -285,7 +285,9 @@ namespace Content.Client.Entry
|
|||||||
"DeviceNetworkConnection",
|
"DeviceNetworkConnection",
|
||||||
"WiredNetworkConnection",
|
"WiredNetworkConnection",
|
||||||
"WirelessNetworkConnection",
|
"WirelessNetworkConnection",
|
||||||
"GhostRadio"
|
"HandLabeler",
|
||||||
|
"Label",
|
||||||
|
"GhostRadio",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Content.Shared.HandLabeler;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.HandLabeler.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a <see cref="HandLabelerWindow"/> and updates it when new server messages are received.
|
||||||
|
/// </summary>
|
||||||
|
public class HandLabelerBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private HandLabelerWindow? _window;
|
||||||
|
|
||||||
|
public HandLabelerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
_window = new HandLabelerWindow();
|
||||||
|
if (State != null)
|
||||||
|
UpdateState(State);
|
||||||
|
|
||||||
|
_window.OpenCentered();
|
||||||
|
|
||||||
|
_window.OnClose += Close;
|
||||||
|
_window.OnLabelEntered += OnLabelChanged;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLabelChanged(string newLabel)
|
||||||
|
{
|
||||||
|
SendMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the UI state based on server-sent info
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state"></param>
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window.SetCurrentLabel(cast.CurrentLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing) return;
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
Content.Client/HandLabeler/UI/HandLabelerWindow.xaml
Normal file
8
Content.Client/HandLabeler/UI/HandLabelerWindow.xaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<SS14Window xmlns="https://spacestation14.io"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Hand Labeler">
|
||||||
|
<BoxContainer Orientation="Vertical" SeparationOverride="4" MinWidth="150">
|
||||||
|
<Label Name="CurrentTextLabel" Text="{Loc 'hand-labeler-current-text-label'}" />
|
||||||
|
<LineEdit Name="LabelLineEdit" />
|
||||||
|
</BoxContainer>
|
||||||
|
</SS14Window>
|
||||||
26
Content.Client/HandLabeler/UI/HandLabelerWindow.xaml.cs
Normal file
26
Content.Client/HandLabeler/UI/HandLabelerWindow.xaml.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.HandLabeler.UI
|
||||||
|
{
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public partial class HandLabelerWindow : SS14Window
|
||||||
|
{
|
||||||
|
public event Action<string>? OnLabelEntered;
|
||||||
|
|
||||||
|
public HandLabelerWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
LabelLineEdit.OnTextEntered += e => OnLabelEntered?.Invoke(e.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCurrentLabel(string label)
|
||||||
|
{
|
||||||
|
LabelLineEdit.Text = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.Items;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.HandLabeler.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class HandLabelerComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "HandLabeler";
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("assignedLabel")]
|
||||||
|
public string AssignedLabel { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("maxLabelChars")]
|
||||||
|
public int MaxLabelChars { get; set; } = 50;
|
||||||
|
|
||||||
|
[DataField("whitelist")]
|
||||||
|
public EntityWhitelist Whitelist = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Content.Server/HandLabeler/Components/LabelComponent.cs
Normal file
18
Content.Server/HandLabeler/Components/LabelComponent.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.HandLabeler.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class LabelComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Label";
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("currentLabel")]
|
||||||
|
public string? CurrentLabel { get; set; }
|
||||||
|
|
||||||
|
public string? OriginalName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
110
Content.Server/HandLabeler/HandLabelerSystem.cs
Normal file
110
Content.Server/HandLabeler/HandLabelerSystem.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using Content.Server.HandLabeler.Components;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.HandLabeler;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Server.HandLabeler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hand labeler system that lets an object apply labels to objects with the <see cref="LabelComponent"/> .
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class HandLabelerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandLabelerComponent, AfterInteractEvent>(AfterInteractOn);
|
||||||
|
SubscribeLocalEvent<HandLabelerComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
// Bound UI subscriptions
|
||||||
|
SubscribeLocalEvent<HandLabelerComponent, HandLabelerLabelChangedMessage>(OnHandLabelerLabelChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AfterInteractOn(EntityUid uid, HandLabelerComponent handLabeler, AfterInteractEvent args)
|
||||||
|
{
|
||||||
|
if (args.Target == null || !handLabeler.Whitelist.IsValid(args.Target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AddLabelTo(uid, handLabeler, args.Target, out string? result);
|
||||||
|
if (result != null)
|
||||||
|
handLabeler.Owner.PopupMessage(args.User, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddLabelTo(EntityUid uid, HandLabelerComponent? handLabeler, IEntity target, out string? result)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref handLabeler))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelComponent label = target.EnsureComponent<LabelComponent>();
|
||||||
|
|
||||||
|
if (label.OriginalName != null)
|
||||||
|
target.Name = label.OriginalName;
|
||||||
|
label.OriginalName = null;
|
||||||
|
|
||||||
|
if (handLabeler.AssignedLabel == string.Empty)
|
||||||
|
{
|
||||||
|
label.CurrentLabel = null;
|
||||||
|
result = Loc.GetString("hand-labeler-successfully-removed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.OriginalName = target.Name;
|
||||||
|
target.Name += $" ({handLabeler.AssignedLabel})";
|
||||||
|
label.CurrentLabel = handLabeler.AssignedLabel;
|
||||||
|
result = Loc.GetString("hand-labeler-successfully-applied");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUseInHand(EntityUid uid, HandLabelerComponent handLabeler, UseInHandEvent args)
|
||||||
|
{
|
||||||
|
if (!args.User.TryGetComponent(out ActorComponent? actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
handLabeler.Owner.GetUIOrNull(HandLabelerUiKey.Key)?.Open(actor.PlayerSession);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckInteract(ICommonSession session)
|
||||||
|
{
|
||||||
|
if (session.AttachedEntity is not { } entity
|
||||||
|
|| !Get<ActionBlockerSystem>().CanInteract(entity)
|
||||||
|
|| !Get<ActionBlockerSystem>().CanUse(entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args)
|
||||||
|
{
|
||||||
|
if (!CheckInteract(args.Session))
|
||||||
|
return;
|
||||||
|
|
||||||
|
handLabeler.AssignedLabel = args.Label.Trim().Substring(0, Math.Min(handLabeler.MaxLabelChars, args.Label.Length));
|
||||||
|
DirtyUI(uid, handLabeler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DirtyUI(EntityUid uid,
|
||||||
|
HandLabelerComponent? handLabeler = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref handLabeler))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_userInterfaceSystem.TrySetUiState(uid, HandLabelerUiKey.Key,
|
||||||
|
new HandLabelerBoundUserInterfaceState(handLabeler.AssignedLabel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Content.Server/HandLabeler/LabelSystem.cs
Normal file
36
Content.Server/HandLabeler/LabelSystem.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Server.HandLabeler.Components;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.HandLabeler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A system that lets players see the contents of a label on an object.
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class LabelSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<LabelComponent, ExaminedEvent>(OnExamine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamine(EntityUid uid, LabelComponent? label, ExaminedEvent args)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref label))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (label.CurrentLabel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = new FormattedMessage();
|
||||||
|
message.AddText(Loc.GetString("hand-labeler-has-label", ("label", label.CurrentLabel)));
|
||||||
|
args.PushMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Content.Shared/HandLabeler/SharedHandLabelerComponent.cs
Normal file
41
Content.Shared/HandLabeler/SharedHandLabelerComponent.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.HandLabeler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Key representing which <see cref="BoundUserInterface"/> is currently open.
|
||||||
|
/// Useful when there are multiple UI for an object. Here it's future-proofing only.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum HandLabelerUiKey
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="HandLabelerComponent"/> state that can be sent to the client
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class HandLabelerBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public string CurrentLabel { get; }
|
||||||
|
|
||||||
|
public HandLabelerBoundUserInterfaceState(string currentLabel)
|
||||||
|
{
|
||||||
|
CurrentLabel = currentLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class HandLabelerLabelChangedMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public HandLabelerLabelChangedMessage(string label)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Resources/Locale/en-US/hand-labeler/hand-labeler.ftl
Normal file
11
Resources/Locale/en-US/hand-labeler/hand-labeler.ftl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# The content of the label in the UI above the text entry input.
|
||||||
|
hand-labeler-current-text-label = Label:
|
||||||
|
|
||||||
|
# When the hand labeler applies a label successfully
|
||||||
|
hand-labeler-successfully-applied = Applied label successfully
|
||||||
|
|
||||||
|
# When the hand labeler removes a label successfully
|
||||||
|
hand-labeler-successfully-removed = Removed label successfully
|
||||||
|
|
||||||
|
# Appended to the description of an object with a label on input
|
||||||
|
hand-labeler-has-label = This object has a label on it, which reads '{$label}'
|
||||||
23
Resources/Prototypes/Entities/Objects/Tools/hand_labeler.yml
Normal file
23
Resources/Prototypes/Entities/Objects/Tools/hand_labeler.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: HandLabeler
|
||||||
|
name: hand labeler
|
||||||
|
description: A hand labeler, used to label items and objects.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Tools/hand_labeler.rsi
|
||||||
|
state: hand_labeler
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Tools/hand_labeler.rsi
|
||||||
|
- type: UseDelay
|
||||||
|
delay: 2.0
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.HandLabelerUiKey.Key
|
||||||
|
type: HandLabelerBoundUserInterface
|
||||||
|
- type: HandLabeler
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- Item
|
||||||
|
tags:
|
||||||
|
- Structure
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
mask:
|
mask:
|
||||||
- Impassable
|
- Impassable
|
||||||
- type: Pullable
|
- type: Pullable
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Structure
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
# This means that it's not anchored on spawn.
|
# This means that it's not anchored on spawn.
|
||||||
@@ -46,3 +49,6 @@
|
|||||||
mask:
|
mask:
|
||||||
- VaultImpassable
|
- VaultImpassable
|
||||||
- type: Anchorable
|
- type: Anchorable
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: Structure
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 273 B |
Binary file not shown.
|
After Width: | Height: | Size: 636 B |
Binary file not shown.
|
After Width: | Height: | Size: 648 B |
22
Resources/Textures/Objects/Tools/hand_labeler.rsi/meta.json
Normal file
22
Resources/Textures/Objects/Tools/hand_labeler.rsi/meta.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/tgstation/tgstation at commit 44636483b7b2868b3e42c92205539f11f6d7968f. Inhand sprites by Macoron.",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hand_labeler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user