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; /// /// Supports typing indicators on entities. /// public abstract class SharedTypingIndicatorSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IGameTiming _timing = default!; /// /// Default ID of /// [ValidatePrototypeId] public const string InitialIndicatorId = "default"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent>(BeforeShow); SubscribeAllEvent(OnTypingChanged); } private void OnPlayerAttached(PlayerAttachedEvent ev) { // when player poses entity we want to make sure that there is typing indicator EnsureComp(ev.Entity); // we also need appearance component to sync visual state EnsureComp(ev.Entity); } private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args) { // player left entity body - hide typing indicator SetTypingIndicatorEnabled(uid, false); } private void OnGotEquipped(Entity entity, ref ClothingGotEquippedEvent args) { entity.Comp.GotEquippedTime = _timing.CurTime; } private void OnGotUnequipped(Entity entity, ref ClothingGotUnequippedEvent args) { entity.Comp.GotEquippedTime = null; } private void BeforeShow(Entity entity, ref InventoryRelayedEvent args) { args.Args.TryUpdateTimeAndIndicator(entity.Comp.TypingIndicatorPrototype, entity.Comp.GotEquippedTime); } private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args) { var uid = args.SenderSession.AttachedEntity; if (!Exists(uid)) { Log.Warning($"Client {args.SenderSession} sent TypingChangedEvent without an attached entity."); return; } // check if this entity can speak or emote if (!_actionBlocker.CanEmote(uid.Value) && !_actionBlocker.CanSpeak(uid.Value)) { // nah, make sure that typing indicator is disabled SetTypingIndicatorEnabled(uid.Value, false); return; } SetTypingIndicatorEnabled(uid.Value, ev.IsTyping); } private void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null) { if (!Resolve(uid, ref appearance, false)) return; _appearance.SetData(uid, TypingIndicatorVisuals.IsTyping, isEnabled, appearance); } }