using System; using System.Collections.Generic; using Content.Client.DoAfter.UI; using Content.Shared.DoAfter; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Network; using Robust.Shared.Players; using Robust.Shared.Timing; namespace Content.Client.DoAfter { [RegisterComponent] public sealed class DoAfterComponent : SharedDoAfterComponent { public override string Name => "DoAfter"; public IReadOnlyDictionary DoAfters => _doAfters; private readonly Dictionary _doAfters = new(); public readonly List<(TimeSpan CancelTime, ClientDoAfter Message)> CancelledDoAfters = new(); public DoAfterGui? Gui { get; set; } public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) { base.HandleNetworkMessage(message, netChannel, session); switch (message) { case CancelledDoAfterMessage msg: Cancel(msg.ID); break; } } public override void OnAdd() { base.OnAdd(); Enable(); } public override void OnRemove() { base.OnRemove(); Disable(); } /// /// For handling PVS so we dispose of controls if they go out of range /// public void Enable() { if (Gui?.Disposed == false) return; Gui = new DoAfterGui {AttachedEntity = Owner}; foreach (var (_, doAfter) in _doAfters) { Gui.AddDoAfter(doAfter); } foreach (var (_, cancelled) in CancelledDoAfters) { Gui.CancelDoAfter(cancelled.ID); } } public void Disable() { Gui?.Dispose(); Gui = null; } public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); if (curState is not DoAfterComponentState state) return; var toRemove = new List(); foreach (var (id, doAfter) in _doAfters) { var found = false; foreach (var clientdoAfter in state.DoAfters) { if (clientdoAfter.ID == id) { found = true; break; } } if (!found) { toRemove.Add(doAfter); } } foreach (var doAfter in toRemove) { Remove(doAfter); } foreach (var doAfter in state.DoAfters) { if (_doAfters.ContainsKey(doAfter.ID)) continue; _doAfters.Add(doAfter.ID, doAfter); } if (Gui == null || Gui.Disposed) return; foreach (var (_, doAfter) in _doAfters) { Gui.AddDoAfter(doAfter); } } /// /// Remove a DoAfter without showing a cancellation graphic. /// /// public void Remove(ClientDoAfter clientDoAfter) { _doAfters.Remove(clientDoAfter.ID); var found = false; for (var i = CancelledDoAfters.Count - 1; i >= 0; i--) { var cancelled = CancelledDoAfters[i]; if (cancelled.Message == clientDoAfter) { CancelledDoAfters.RemoveAt(i); found = true; break; } } if (!found) _doAfters.Remove(clientDoAfter.ID); Gui?.RemoveDoAfter(clientDoAfter.ID); } /// /// Mark a DoAfter as cancelled and show a cancellation graphic. /// /// Actual removal is handled by DoAfterEntitySystem. /// /// public void Cancel(byte id, TimeSpan? currentTime = null) { foreach (var (_, cancelled) in CancelledDoAfters) { if (cancelled.ID == id) return; } if (!_doAfters.ContainsKey(id)) return; var doAfterMessage = _doAfters[id]; currentTime ??= IoCManager.Resolve().CurTime; CancelledDoAfters.Add((currentTime.Value, doAfterMessage)); Gui?.CancelDoAfter(id); } } }