using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Prototypes;
+using Content.Shared.Inventory;
namespace Content.Client.Chat.TypingIndicator;
public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly InventorySystem _inventory = default!;
+
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
- if (!_prototypeManager.TryIndex<TypingIndicatorPrototype>(component.Prototype, out var proto))
+ var currentTypingIndicator = component.TypingIndicatorPrototype;
+
+ var evt = new BeforeShowTypingIndicatorEvent();
+
+ if (TryComp<InventoryComponent>(uid, out var inventoryComp))
+ _inventory.RelayEvent((uid, inventoryComp), ref evt);
+
+ var overrideIndicator = evt.GetMostRecentIndicator();
+
+ if (overrideIndicator != null)
+ currentTypingIndicator = overrideIndicator.Value;
+
+ if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
{
- Log.Error($"Unknown typing indicator id: {component.Prototype}");
+ Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}");
return;
}
using Content.Shared.ActionBlocker;
using Content.Shared.Clothing;
+using Content.Shared.Inventory;
using Robust.Shared.Player;
+using Robust.Shared.Timing;
namespace Content.Shared.Chat.TypingIndicator;
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
/// <summary>
/// Default ID of <see cref="TypingIndicatorPrototype"/>
SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
+ SubscribeLocalEvent<TypingIndicatorClothingComponent, InventoryRelayedEvent<BeforeShowTypingIndicatorEvent>>(BeforeShow);
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
}
SetTypingIndicatorEnabled(uid, false);
}
- private void OnGotEquipped(EntityUid uid, TypingIndicatorClothingComponent component, ClothingGotEquippedEvent args)
+ private void OnGotEquipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotEquippedEvent args)
{
- if (!TryComp<TypingIndicatorComponent>(args.Wearer, out var indicator))
- return;
-
- indicator.Prototype = component.Prototype;
+ entity.Comp.GotEquippedTime = _timing.CurTime;
}
- private void OnGotUnequipped(EntityUid uid, TypingIndicatorClothingComponent component, ClothingGotUnequippedEvent args)
+ private void OnGotUnequipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotUnequippedEvent args)
{
- if (!TryComp<TypingIndicatorComponent>(args.Wearer, out var indicator))
- return;
+ entity.Comp.GotEquippedTime = null;
+ }
- indicator.Prototype = InitialIndicatorId;
+ private void BeforeShow(Entity<TypingIndicatorClothingComponent> entity, ref InventoryRelayedEvent<BeforeShowTypingIndicatorEvent> args)
+ {
+ args.Args.TryUpdateTimeAndIndicator(entity.Comp.TypingIndicatorPrototype, entity.Comp.GotEquippedTime);
}
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
+++ /dev/null
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Chat.TypingIndicator;
-
-/// <summary>
-/// Networked event from client.
-/// Send to server when client started/stopped typing in chat input field.
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class TypingChangedEvent : EntityEventArgs
-{
- public readonly bool IsTyping;
-
- public TypingChangedEvent(bool isTyping)
- {
- IsTyping = isTyping;
- }
-}
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Chat.TypingIndicator;
-[RegisterComponent, NetworkedComponent]
+/// <summary>
+/// If an item is equipped to someones inventory (Anything but the pockets), and has this component
+/// the users typing indicator will be replaced by the prototype given in <c>TypingIndicatorPrototype</c>.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
[Access(typeof(SharedTypingIndicatorSystem))]
public sealed partial class TypingIndicatorClothingComponent : Component
{
+ /// <summary>
+ /// The typing indicator that will override the default typing indicator when the item is equipped to a users
+ /// inventory.
+ /// </summary>
[ViewVariables(VVAccess.ReadWrite)]
- [DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<TypingIndicatorPrototype>))]
- public string Prototype = default!;
+ [DataField("proto", required: true)]
+ public ProtoId<TypingIndicatorPrototype> TypingIndicatorPrototype = default!;
+
+ /// <summary>
+ /// This stores the time the item was equipped in someones inventory. If null, item is currently not equipped.
+ /// </summary>
+ [DataField, AutoPausedField]
+ public TimeSpan? GotEquippedTime = null;
}
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Prototypes;
namespace Content.Shared.Chat.TypingIndicator;
/// <summary>
/// Prototype id that store all visual info about typing indicator.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("proto", customTypeSerializer: typeof(PrototypeIdSerializer<TypingIndicatorPrototype>))]
- public string Prototype = SharedTypingIndicatorSystem.InitialIndicatorId;
+ [DataField("proto")]
+ public ProtoId<TypingIndicatorPrototype> TypingIndicatorPrototype = "default";
}
--- /dev/null
+using Robust.Shared.Serialization;
+using Content.Shared.Inventory;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.Manager.Exceptions;
+
+namespace Content.Shared.Chat.TypingIndicator;
+
+/// <summary>
+/// Networked event from client.
+/// Send to server when client started/stopped typing in chat input field.
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class TypingChangedEvent : EntityEventArgs
+{
+ public readonly bool IsTyping;
+
+ public TypingChangedEvent(bool isTyping)
+ {
+ IsTyping = isTyping;
+ }
+}
+
+/// <summary>
+/// This event will be broadcast right before displaying an entities typing indicator.
+/// If _overrideIndicator is not null after the event is finished it will be used.
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class BeforeShowTypingIndicatorEvent : IInventoryRelayEvent
+{
+ public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
+
+ private ProtoId<TypingIndicatorPrototype>? _overrideIndicator = null;
+ private TimeSpan? _latestEquipTime = null;
+ public BeforeShowTypingIndicatorEvent()
+ {
+ _overrideIndicator = null;
+ _latestEquipTime = null;
+ }
+ /// <summary>
+ /// Will only update the time and indicator if the given time is more recent than
+ /// the stored time or if the stored time is null.
+ /// </summary>
+ /// <returns>
+ /// True if the given time is more recent than the stored time, and false otherwise.
+ /// </returns>
+ public bool TryUpdateTimeAndIndicator(ProtoId<TypingIndicatorPrototype>? indicator, TimeSpan? equipTime)
+ {
+ if (equipTime != null && (_latestEquipTime == null || _latestEquipTime < equipTime))
+ {
+ _latestEquipTime = equipTime;
+ _overrideIndicator = indicator;
+ return true;
+ }
+ return false;
+ }
+ public ProtoId<TypingIndicatorPrototype>? GetMostRecentIndicator()
+ {
+ return _overrideIndicator;
+ }
+}