using System.Threading; using Content.Shared.Interaction; using Content.Server.Storage.Components; using Content.Shared.Storage.Components; using Content.Shared.Verbs; using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.EntitySystems; using Content.Server.DoAfter; using Content.Shared.Placeable; using Robust.Shared.Containers; using Robust.Shared.Random; namespace Content.Server.Storage.EntitySystems { public sealed class DumpableSystem : EntitySystem { [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent>(AddDumpVerb); SubscribeLocalEvent>(AddUtilityVerbs); SubscribeLocalEvent(OnDumpCompleted); SubscribeLocalEvent(OnDumpCancelled); } private void OnAfterInteract(EntityUid uid, DumpableComponent component, AfterInteractEvent args) { if (!args.CanReach) return; if (!TryComp(args.Used, out var storage)) return; if (storage.StoredEntities == null || storage.StoredEntities.Count == 0) return; if (HasComp(args.Target) || HasComp(args.Target)) { StartDoAfter(uid, args.Target.Value, args.User, component, storage); return; } } private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) return; if (!TryComp(uid, out var storage) || storage.StoredEntities == null || storage.StoredEntities.Count == 0) return; AlternativeVerb verb = new() { Act = () => { StartDoAfter(uid, null, args.User, dumpable, storage, 0.6f); }, Text = Loc.GetString("dump-verb-name"), IconTexture = "/Textures/Interface/VerbIcons/drop.svg.192dpi.png", }; args.Verbs.Add(verb); } private void AddUtilityVerbs(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) return; if (!TryComp(uid, out var storage) || storage.StoredEntities == null || storage.StoredEntities.Count == 0) return; if (HasComp(args.Target)) { UtilityVerb verb = new() { Act = () => { StartDoAfter(uid, args.Target, args.User, dumpable, storage); }, Text = Loc.GetString("dump-disposal-verb-name", ("unit", args.Target)), IconEntity = uid }; args.Verbs.Add(verb); } if (HasComp(args.Target)) { UtilityVerb verb = new() { Act = () => { StartDoAfter(uid, args.Target, args.User, dumpable, storage); }, Text = Loc.GetString("dump-placeable-verb-name", ("surface", args.Target)), IconEntity = uid }; args.Verbs.Add(verb); } } public void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable, ServerStorageComponent storage, float multiplier = 1) { if (dumpable.CancelToken != null) { dumpable.CancelToken.Cancel(); dumpable.CancelToken = null; return; } if (storage.StoredEntities == null) return; float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * multiplier; dumpable.CancelToken = new CancellationTokenSource(); _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, dumpable.CancelToken.Token, target: targetUid) { BroadcastFinishedEvent = new DumpCompletedEvent(userUid, targetUid, storage.StoredEntities), BroadcastCancelledEvent = new DumpCancelledEvent(dumpable.Owner), BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnStun = true, NeedHand = true }); } private void OnDumpCompleted(DumpCompletedEvent args) { Queue dumpQueue = new(); foreach (var entity in args.StoredEntities) { dumpQueue.Enqueue(entity); } if (TryComp(args.Target, out var disposal)) { foreach (var entity in dumpQueue) { _disposalUnitSystem.DoInsertDisposalUnit(args.Target.Value, entity); } return; } foreach (var entity in dumpQueue) { var transform = Transform(entity); transform.AttachParentToContainerOrGrid(EntityManager); transform.LocalPosition = transform.LocalPosition + _random.NextVector2Box() / 2; transform.LocalRotation = _random.NextAngle(); } if (HasComp(args.Target)) { foreach (var entity in dumpQueue) { Transform(entity).LocalPosition = Transform(args.Target.Value).LocalPosition + _random.NextVector2Box() / 4; } return; } } private void OnDumpCancelled(DumpCancelledEvent args) { if (TryComp(args.Uid, out var dumpable)) dumpable.CancelToken = null; } private sealed class DumpCancelledEvent : EntityEventArgs { public readonly EntityUid Uid; public DumpCancelledEvent(EntityUid uid) { Uid = uid; } } private sealed class DumpCompletedEvent : EntityEventArgs { public EntityUid User { get; } public EntityUid? Target { get; } public IReadOnlyList StoredEntities { get; } public DumpCompletedEvent(EntityUid user, EntityUid? target, IReadOnlyList storedEntities) { User = user; Target = target; StoredEntities = storedEntities; } } } }