private void StartReassembly(EntityUid uid, BodyReassembleComponent component, float multiplier = 1f)
        {
            if (component.CancelToken != null)
            {
                return;
            }

            if (!GetNearbyParts(uid, component, out var partList))
            {
                return;
            }

            if (partList == null)
            {
                return;
            }

            var doAfterTime = component.DoAfterTime * multiplier;
            var cancelToken = new CancellationTokenSource();

            component.CancelToken = cancelToken;

            var doAfterEventArgs = new DoAfterEventArgs(component.Owner, doAfterTime, cancelToken.Token, component.Owner)
            {
                BreakOnTargetMove    = true,
                BreakOnUserMove      = true,
                BreakOnDamage        = true,
                BreakOnStun          = true,
                NeedHand             = false,
                TargetCancelledEvent = new ReassembleCancelledEvent(),
                TargetFinishedEvent  = new ReassembleCompleteEvent(uid, uid, partList),
            };

            _doAfterSystem.DoAfter(doAfterEventArgs);
        }
예제 #2
0
        public void StartOpeningStripper(EntityUid user, StrippableComponent component)
        {
            if (component.CancelTokens.ContainsKey(user))
            {
                return;
            }

            if (TryComp <ActorComponent>(user, out var actor))
            {
                if (component.Owner.GetUIOrNull(StrippingUiKey.Key)?.SessionHasOpen(actor.PlayerSession) == true)
                {
                    return;
                }
            }

            var token = new CancellationTokenSource();

            var doAfterArgs = new DoAfterEventArgs(user, component.OpenDelay, token.Token, component.Owner)
            {
                BreakOnStun          = true,
                BreakOnDamage        = true,
                BreakOnTargetMove    = true,
                BreakOnUserMove      = true,
                NeedHand             = true,
                TargetCancelledEvent = new OpenStrippingCancelledEvent(user),
                TargetFinishedEvent  = new OpenStrippingCompleteEvent(user),
            };

            component.CancelTokens[user] = token;
            _doAfterSystem.DoAfter(doAfterArgs);
        }
예제 #3
0
        public async Task TestFinished()
        {
            Task <DoAfterStatus> task = null;

            await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes });

            var server = pairTracker.Pair.Server;

            // That it finishes successfully
            await server.WaitPost(() =>
            {
                var tickTime   = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate;
                var mapManager = IoCManager.Resolve <IMapManager>();
                mapManager.CreateNewMapEntity(MapId.Nullspace);
                var entityManager = IoCManager.Resolve <IEntityManager>();
                var mob           = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
                var cancelToken   = new CancellationTokenSource();
                var args          = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token);
                task = EntitySystem.Get <DoAfterSystem>().WaitDoAfter(args);
            });

            await server.WaitRunTicks(1);

            Assert.That(task.Result == DoAfterStatus.Finished);

            await pairTracker.CleanReturnAsync();
        }
예제 #4
0
        public async Task TestCancelled()
        {
            Task <DoAfterStatus> task = null;

            await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes });

            var server = pairTracker.Pair.Server;

            await server.WaitPost(() =>
            {
                var tickTime   = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate;
                var mapManager = IoCManager.Resolve <IMapManager>();
                mapManager.CreateNewMapEntity(MapId.Nullspace);
                var entityManager = IoCManager.Resolve <IEntityManager>();
                var mob           = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
                var cancelToken   = new CancellationTokenSource();
                var args          = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token);
                task = EntitySystem.Get <DoAfterSystem>().WaitDoAfter(args);
                cancelToken.Cancel();
            });

            await server.WaitRunTicks(3);

            Assert.That(task.Status, Is.EqualTo(TaskStatus.RanToCompletion));
#pragma warning disable RA0004
            Assert.That(task.Result, Is.EqualTo(DoAfterStatus.Cancelled), $"Result was {task.Result}");
#pragma warning restore RA0004

            await pairTracker.CleanReturnAsync();
        }
    private void OnInteractUsing(EntityUid uid, GatherableComponent component, InteractUsingEvent args)
    {
        if (!TryComp <GatheringToolComponent>(args.Used, out var tool) ||
            component.ToolWhitelist?.IsValid(args.Used) == false ||
            tool.GatheringEntities.TryGetValue(uid, out var cancelToken))
        {
            return;
        }

        // Can't gather too many entities at once.
        if (tool.MaxGatheringEntities < tool.GatheringEntities.Count + 1)
        {
            return;
        }

        cancelToken = new CancellationTokenSource();
        tool.GatheringEntities[uid] = cancelToken;

        var doAfter = new DoAfterEventArgs(args.User, tool.GatheringTime, cancelToken.Token, uid)
        {
            BreakOnDamage           = true,
            BreakOnStun             = true,
            BreakOnTargetMove       = true,
            BreakOnUserMove         = true,
            MovementThreshold       = 0.25f,
            BroadcastCancelledEvent = new GatheringDoafterCancel {
                Tool = args.Used, Resource = uid
            },
            TargetFinishedEvent = new GatheringDoafterSuccess {
                Tool = args.Used, Resource = uid, Player = args.User
            }
        };

        _doAfterSystem.DoAfter(doAfter);
    }
        public async Task TestCancelled()
        {
            Task <DoAfterStatus> task = null;
            var options = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var server = StartServerDummyTicker(options);

            server.Post(() =>
            {
                var tickTime   = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate;
                var mapManager = IoCManager.Resolve <IMapManager>();
                mapManager.CreateNewMapEntity(MapId.Nullspace);
                var entityManager = IoCManager.Resolve <IEntityManager>();
                var mob           = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
                var cancelToken   = new CancellationTokenSource();
                var args          = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token);
                task = EntitySystem.Get <DoAfterSystem>().DoAfter(args);
                cancelToken.Cancel();
            });

            await server.WaitRunTicks(3);

            Assert.That(task.Result == DoAfterStatus.Cancelled, $"Result was {task.Result}");
        }
        /// <summary>
        ///     Takes an item from a hand and places it in the user's active hand.
        /// </summary>
        private async void TakeItemFromHands(EntityUid user, string hand)
        {
            var hands     = _entities.GetComponent <HandsComponent>(Owner);
            var userHands = _entities.GetComponent <HandsComponent>(user);

            bool Check()
            {
                if (!hands.HasHand(hand))
                {
                    return(false);
                }

                if (!hands.TryGetItem(hand, out var heldItem))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner)));
                    return(false);
                }

                if (_entities.HasComponent <HandVirtualItemComponent>(heldItem.Owner))
                {
                    return(false);
                }

                if (!hands.CanDrop(hand, false))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            if (!hands.TryGetHeldEntity(hand, out var entity))
            {
                return;
            }

            hands.Drop(hand, false);
            userHands.PutInHandOrDrop(entity.Value);
            // hand update will trigger strippable update
        }
예제 #8
0
        private async void HandleAfterInteract(EntityUid uid, SpawnAfterInteractComponent component, AfterInteractEvent args)
        {
            if (string.IsNullOrEmpty(component.Prototype))
            {
                return;
            }
            if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(EntityManager), out var grid))
            {
                return;
            }
            if (!grid.TryGetTileRef(args.ClickLocation, out var tileRef))
            {
                return;
            }

            bool IsTileClear()
            {
                return(tileRef.Tile.IsEmpty == false && args.User.InRangeUnobstructed(args.ClickLocation, popup: true));
            }

            if (!IsTileClear())
            {
                return;
            }

            if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem))
            {
                var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime)
                {
                    BreakOnUserMove = true,
                    BreakOnStun     = true,
                    PostCheck       = IsTileClear,
                };
                var result = await doAfterSystem.DoAfter(doAfterArgs);

                if (result != DoAfterStatus.Finished)
                {
                    return;
                }
            }

            if (component.Deleted || component.Owner.Deleted)
            {
                return;
            }

            if (component.Owner.TryGetComponent <SharedStackComponent>(out var stackComp) &&
                component.RemoveOnInteract && !Get <StackSystem>().Use(uid, stackComp, 1))
            {
                return;
            }

            EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid));

            if (component.RemoveOnInteract && stackComp == null && !component.Owner.Deleted)
            {
                component.Owner.Delete();
            }
        }
        private async void HandleAfterInteract(EntityUid uid, SpawnAfterInteractComponent component, AfterInteractEvent args)
        {
            if (string.IsNullOrEmpty(component.Prototype))
            {
                return;
            }
            if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(EntityManager), out var grid))
            {
                return;
            }
            if (!grid.TryGetTileRef(args.ClickLocation, out var tileRef))
            {
                return;
            }

            bool IsTileClear()
            {
                return(tileRef.Tile.IsEmpty == false);
            }

            if (!IsTileClear())
            {
                return;
            }

            if (component.DoAfterTime > 0)
            {
                var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime)
                {
                    BreakOnUserMove = true,
                    BreakOnStun     = true,
                    PostCheck       = IsTileClear,
                };
                var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);

                if (result != DoAfterStatus.Finished)
                {
                    return;
                }
            }

            if (component.Deleted || Deleted(component.Owner))
            {
                return;
            }

            if (EntityManager.TryGetComponent <SharedStackComponent?>(component.Owner, out var stackComp) &&
                component.RemoveOnInteract && !_stackSystem.Use(uid, 1, stackComp))
            {
                return;
            }

            EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid));

            if (component.RemoveOnInteract && stackComp == null && !((!EntityManager.EntityExists(component.Owner) ? EntityLifeStage.Deleted : EntityManager.GetComponent <MetaDataComponent>(component.Owner).EntityLifeStage) >= EntityLifeStage.Deleted))
            {
                EntityManager.DeleteEntity(component.Owner);
            }
        }
예제 #10
0
        /// <summary>
        ///     Takes an item from the inventory and places it in the user's active hand.
        /// </summary>
        private async void TakeItemFromInventory(EntityUid user, string slot)
        {
            var inventory = _entities.GetComponent <InventoryComponent>(Owner);
            var userHands = _entities.GetComponent <HandsComponent>(user);
            var invSystem = EntitySystem.Get <InventorySystem>();

            bool Check()
            {
                if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
                {
                    return(false);
                }

                if (!invSystem.HasSlot(Owner, slot))
                {
                    return(false);
                }

                if (!invSystem.TryGetSlotEntity(Owner, slot, out var item))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner)));
                    return(false);
                }

                if (!invSystem.CanUnequip(user, Owner, slot, out _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            if (invSystem.TryGetSlotEntity(Owner, slot, out var item) && invSystem.TryUnequip(user, Owner, slot))
            {
                userHands.PutInHandOrDrop(item.Value);
            }

            UpdateState();
        }
        /// <summary>
        ///     Places item in user's active hand in one of the entity's hands.
        /// </summary>
        private async void PlaceActiveHandItemInHands(EntityUid user, string handName)
        {
            var hands     = _entities.GetComponent <HandsComponent>(Owner);
            var userHands = _entities.GetComponent <HandsComponent>(user);
            var sys       = _sysMan.GetEntitySystem <SharedHandsSystem>();

            bool Check()
            {
                if (userHands.ActiveHandEntity == null)
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
                    return(false);
                }

                if (!sys.CanDropHeld(user, userHands.ActiveHand !))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
                    return(false);
                }

                if (!hands.Hands.TryGetValue(handName, out var hand) ||
                    !sys.CanPickupToHand(Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            if (userHands.ActiveHandEntity is not EntityUid held)
            {
                return;
            }

            sys.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
            sys.TryPickup(Owner, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
            // hand update will trigger strippable update
        }
        /// <summary>
        ///     Takes an item from the inventory and places it in the user's active hand.
        /// </summary>
        private async void TakeItemFromInventory(EntityUid user, string slot)
        {
            var inventory = _entities.GetComponent <InventoryComponent>(Owner);
            var userHands = _entities.GetComponent <HandsComponent>(user);
            var invSystem = _sysMan.GetEntitySystem <InventorySystem>();

            bool Check()
            {
                if (!invSystem.HasSlot(Owner, slot))
                {
                    return(false);
                }

                if (!invSystem.TryGetSlotEntity(Owner, slot, out var item))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner)));
                    return(false);
                }

                if (!invSystem.CanUnequip(user, Owner, slot, out _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            if (invSystem.TryGetSlotEntity(Owner, slot, out var item) && invSystem.TryUnequip(user, Owner, slot))
            {
                // Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
                _entities.EventBus.RaiseLocalEvent(item.Value, new DroppedEvent(user));

                _sysMan.GetEntitySystem <SharedHandsSystem>().PickupOrDrop(user, item.Value);
            }

            UpdateState();
        }
        private async void TakeItemFromInventory(IEntity user, Slots slot)
        {
            var inventory = Owner.GetComponent <InventoryComponent>();
            var userHands = user.GetComponent <HandsComponent>();

            bool Check()
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    return(false);
                }

                if (!inventory.HasSlot(slot))
                {
                    return(false);
                }

                if (!inventory.TryGetSlotItem(slot, out ItemComponent itemToTake))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} {0:have} nothing there!", Owner));
                    return(false);
                }

                if (!inventory.CanUnequip(slot, false))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} cannot unequip that!", Owner));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            var item = inventory.GetSlotItem(slot);

            inventory.Unequip(slot, false);
            userHands.PutInHandOrDrop(item);
            UpdateSubscribed();
        }
        private async void TakeItemFromHands(IEntity user, string hand)
        {
            var hands     = Owner.GetComponent <HandsComponent>();
            var userHands = user.GetComponent <HandsComponent>();

            bool Check()
            {
                if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
                {
                    return(false);
                }

                if (!hands.HasHand(hand))
                {
                    return(false);
                }

                if (!hands.TryGetItem(hand, out var heldItem))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner)));
                    return(false);
                }

                if (!hands.CanDrop(hand, false))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            var item = hands.GetItem(hand);

            hands.Drop(hand, false);
            userHands.PutInHandOrDrop(item !);
            UpdateSubscribed();
        }
        /// <summary>
        ///     Takes an item from a hand and places it in the user's active hand.
        /// </summary>
        private async void TakeItemFromHands(EntityUid user, string handName)
        {
            var hands     = _entities.GetComponent <HandsComponent>(Owner);
            var userHands = _entities.GetComponent <HandsComponent>(user);
            var handSys   = _sysMan.GetEntitySystem <SharedHandsSystem>();

            bool Check()
            {
                if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null)
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner)));
                    return(false);
                }

                if (_entities.HasComponent <HandVirtualItemComponent>(hand.HeldEntity))
                {
                    return(false);
                }

                if (!handSys.CanDropHeld(Owner, hand, false))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not EntityUid held)
            {
                return;
            }

            handSys.TryDrop(Owner, hand, checkActionBlocker: false, handsComp: hands);
            handSys.PickupOrDrop(user, held, handsComp: userHands);
            // hand update will trigger strippable update
        }
        public async void AttemptDisassemble(EntityUid uid, EntityUid user, EntityUid target, DisassembleOnAltVerbComponent?component = null)
        {
            if (!Resolve(uid, ref component))
            {
                return;
            }
            if (string.IsNullOrEmpty(component.Prototype))
            {
                return;
            }

            if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem))
            {
                var doAfterArgs = new DoAfterEventArgs(user, component.DoAfterTime, component.TokenSource.Token)
                {
                    BreakOnUserMove = true,
                    BreakOnStun     = true,
                };
                var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

                if (result != DoAfterStatus.Finished)
                {
                    return;
                }
                component.TokenSource.Cancel();
            }

            if (component.Deleted || Deleted(component.Owner))
            {
                return;
            }

            if (!TryComp <TransformComponent>(component.Owner, out var transformComp))
            {
                return;
            }

            var entity = EntityManager.SpawnEntity(component.Prototype, transformComp.Coordinates);

            if (TryComp <HandsComponent?>(user, out var hands) &&
                TryComp <SharedItemComponent?>(entity, out var item))
            {
                hands.PutInHandOrDrop(item);
            }

            EntityManager.DeleteEntity(component.Owner);

            return;
        }
예제 #17
0
        private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke)
        {
            nuke.DisarmCancelToken = new();
            var doafter = new DoAfterEventArgs(user, nuke.DisarmDoafterLength, nuke.DisarmCancelToken.Value, uid)
            {
                TargetCancelledEvent = new NukeDisarmCancelledEvent(),
                TargetFinishedEvent  = new NukeDisarmSuccessEvent(),
                BreakOnDamage        = true,
                BreakOnStun          = true,
                BreakOnTargetMove    = true,
                BreakOnUserMove      = true,
                NeedHand             = true,
            };

            _doAfterSystem.DoAfter(doafter);
            _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user,
                                Filter.Entities(user), PopupType.LargeCaution);
        }
        private async void HandleActivateInWorld(EntityUid uid, DisassembleOnActivateComponent component, ActivateInWorldEvent args)
        {
            if (string.IsNullOrEmpty(component.Prototype))
            {
                return;
            }
            if (!args.User.InRangeUnobstructed(args.Target))
            {
                return;
            }

            if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem))
            {
                var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime, component.TokenSource.Token)
                {
                    BreakOnUserMove = true,
                    BreakOnStun     = true,
                };
                var result = await doAfterSystem.WaitDoAfter(doAfterArgs);

                if (result != DoAfterStatus.Finished)
                {
                    return;
                }
                component.TokenSource.Cancel();
            }

            if (component.Deleted || Deleted(component.Owner))
            {
                return;
            }

            var entity = EntityManager.SpawnEntity(component.Prototype, EntityManager.GetComponent <TransformComponent>(component.Owner).Coordinates);

            if (EntityManager.TryGetComponent <HandsComponent?>(args.User, out var hands) &&
                EntityManager.TryGetComponent <ItemComponent?>(entity, out var item))
            {
                hands.PutInHandOrDrop(item);
            }

            EntityManager.DeleteEntity(component.Owner);

            return;
        }
    private void AttemptEscape(EntityUid user, EntityUid container, CanEscapeInventoryComponent component)
    {
        component.CancelToken = new();
        var doAfterEventArgs = new DoAfterEventArgs(user, component.ResistTime, component.CancelToken.Token, container)
        {
            BreakOnTargetMove  = false,
            BreakOnUserMove    = false,
            BreakOnDamage      = true,
            BreakOnStun        = true,
            NeedHand           = false,
            UserFinishedEvent  = new EscapeDoAfterComplete(),
            UserCancelledEvent = new EscapeDoAfterCancel(),
        };

        component.IsResisting = true;
        _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, Filter.Entities(user));
        _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, Filter.Entities(container));
        _doAfterSystem.DoAfter(doAfterEventArgs);
    }
예제 #20
0
    private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
    {
        if (component.CancelToken != null)
        {
            return;
        }

        component.CancelToken = new CancellationTokenSource();

        var doAfterEventArgs = new DoAfterEventArgs(args.User, component.LearnTime, component.CancelToken.Token, uid)
        {
            BreakOnTargetMove    = true,
            BreakOnUserMove      = true,
            BreakOnDamage        = true,
            BreakOnStun          = true,
            NeedHand             = true, //What, are you going to read with your eyes only??
            TargetFinishedEvent  = new LearnDoAfterComplete(args.User),
            TargetCancelledEvent = new LearnDoAfterCancel(),
        };

        _doAfter.DoAfter(doAfterEventArgs);
    }
        public async Task TestFinished()
        {
            Task <DoAfterStatus> task = null;
            var server = StartServerDummyTicker();

            // That it finishes successfully
            server.Post(() =>
            {
                var tickTime   = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate;
                var mapManager = IoCManager.Resolve <IMapManager>();
                mapManager.CreateNewMapEntity(MapId.Nullspace);
                var entityManager = IoCManager.Resolve <IEntityManager>();
                var mob           = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
                var cancelToken   = new CancellationTokenSource();
                var args          = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token);
                task = EntitySystem.Get <DoAfterSystem>().DoAfter(args);
            });

            await server.WaitRunTicks(1);

            Assert.That(task.Result == DoAfterStatus.Finished);
        }
    private void AttemptResist(EntityUid user, EntityUid target, EntityStorageComponent?storageComponent = null, ResistLockerComponent?resistLockerComponent = null)
    {
        if (!Resolve(target, ref storageComponent, ref resistLockerComponent))
        {
            return;
        }

        resistLockerComponent.CancelToken = new();
        var doAfterEventArgs = new DoAfterEventArgs(user, resistLockerComponent.ResistTime, resistLockerComponent.CancelToken.Token, target)
        {
            BreakOnTargetMove    = false,
            BreakOnUserMove      = true,
            BreakOnDamage        = true,
            BreakOnStun          = true,
            NeedHand             = false, //No hands 'cause we be kickin'
            TargetFinishedEvent  = new ResistDoAfterComplete(user, target),
            TargetCancelledEvent = new ResistDoAfterCancelled(user)
        };

        resistLockerComponent.IsResisting = true;
        _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-start-resisting"), user, Filter.Entities(user));
        _doAfterSystem.DoAfter(doAfterEventArgs);
    }
예제 #23
0
        async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
        {
            // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad.
            if (!eventArgs.ClickLocation.IsValid(Owner.EntityManager) || !eventArgs.ClickLocation.GetGridId(Owner.EntityManager).IsValid())
            {
                return(false);
            }

            //No changing mode mid-RCD
            var startingMode = _mode;

            var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager));
            var tile    = mapGrid.GetTileRef(eventArgs.ClickLocation);
            var snapPos = mapGrid.TileIndicesFor(eventArgs.ClickLocation);

            //Using an RCD isn't instantaneous
            var cancelToken      = new CancellationTokenSource();
            var doAfterEventArgs = new DoAfterEventArgs(eventArgs.User, _delay, cancelToken.Token, eventArgs.Target)
            {
                BreakOnDamage = true,
                BreakOnStun   = true,
                NeedHand      = true,
                ExtraCheck    = () => IsRCDStillValid(eventArgs, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here
            };

            var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs);

            if (result == DoAfterStatus.Cancelled)
            {
                return(true);
            }

            switch (_mode)
            {
            //Floor mode just needs the tile to be a space tile (subFloor)
            case RcdMode.Floors:
                mapGrid.SetTile(eventArgs.ClickLocation, new Robust.Shared.Map.Tile(_tileDefinitionManager["floor_steel"].TileId));
                break;

            //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check.
            case RcdMode.Deconstruct:
                if (!tile.IsBlockedTurf(true))     //Delete the turf
                {
                    mapGrid.SetTile(snapPos, Robust.Shared.Map.Tile.Empty);
                }
                else     //Delete what the user targeted
                {
                    eventArgs.Target?.Delete();
                }
                break;

            //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code.
            case RcdMode.Walls:
                var ent = _serverEntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos));
                ent.Transform.LocalRotation = Angle.Zero;     // Walls always need to point south.
                break;

            case RcdMode.Airlock:
                var airlock = _serverEntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos));
                airlock.Transform.LocalRotation = Owner.Transform.LocalRotation;     //Now apply icon smoothing.
                break;

            default:
                return(true);    //I don't know why this would happen, but sure I guess. Get out of here invalid state!
            }

            SoundSystem.Play(Filter.Pvs(Owner), _successSound.GetSound(), Owner);
            _ammo--;
            return(true);
        }
        private async void PlaceActiveHandItemInHands(IEntity user, string hand)
        {
            var hands     = Owner.GetComponent <HandsComponent>();
            var userHands = user.GetComponent <HandsComponent>();
            var item      = userHands.GetActiveHand;

            bool Check()
            {
                if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
                {
                    return(false);
                }

                if (item == null)
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
                    return(false);
                }

                if (!userHands.CanDrop(userHands.ActiveHand !))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
                    return(false);
                }

                if (!hands.HasHand(hand))
                {
                    return(false);
                }

                if (hands.TryGetItem(hand, out var _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied-message", ("owner", Owner)));
                    return(false);
                }

                if (!hands.CanPickupEntity(hand, item.Owner, checkActionBlocker: false))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            userHands.Drop(hand);
            hands.TryPickupEntity(hand, item !.Owner, checkActionBlocker: false);
            UpdateSubscribed();
        }
        private async void PlaceActiveHandItemInInventory(IEntity user, Slots slot)
        {
            var inventory = Owner.GetComponent <InventoryComponent>();
            var userHands = user.GetComponent <HandsComponent>();
            var item      = userHands.GetActiveHand;

            bool Check()
            {
                if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
                {
                    return(false);
                }

                if (item == null)
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
                    return(false);
                }

                if (!userHands.CanDrop(userHands.ActiveHand !))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
                    return(false);
                }

                if (!inventory.HasSlot(slot))
                {
                    return(false);
                }

                if (inventory.TryGetSlotItem(slot, out ItemComponent _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied", ("owner", Owner)));
                    return(false);
                }

                if (!inventory.CanEquip(slot, item, false))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message", ("owner", Owner)));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            userHands.Drop(item !.Owner, false);
            inventory.Equip(slot, item !.Owner, false);

            UpdateSubscribed();
        }
        private async void PlaceActiveHandItemInHands(IEntity user, string hand)
        {
            var hands     = Owner.GetComponent <HandsComponent>();
            var userHands = user.GetComponent <HandsComponent>();
            var item      = userHands.GetActiveHand;

            bool Check()
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    return(false);
                }

                if (item == null)
                {
                    user.PopupMessageCursor(Loc.GetString("You aren't holding anything!"));
                    return(false);
                }

                if (!userHands.CanDrop(userHands.ActiveHand !))
                {
                    user.PopupMessageCursor(Loc.GetString("You can't drop that!"));
                    return(false);
                }

                if (!hands.HasHand(hand))
                {
                    return(false);
                }

                if (hands.TryGetItem(hand, out var _))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} already {0:have} something there!", Owner));
                    return(false);
                }

                if (!hands.CanPutInHand(item, hand, false))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} cannot put that there!", Owner));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            userHands.Drop(hand, false);
            hands.PutInHand(item !, hand, false, false);
            UpdateSubscribed();
        }
예제 #27
0
        async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
        {
            //No changing mode mid-RCD
            var startingMode = _mode;

            var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager));
            var tile    = mapGrid.GetTileRef(eventArgs.ClickLocation);
            var snapPos = mapGrid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center);

            //Using an RCD isn't instantaneous
            var cancelToken      = new CancellationTokenSource();
            var doAfterEventArgs = new DoAfterEventArgs(eventArgs.User, _delay, cancelToken.Token, eventArgs.Target)
            {
                BreakOnDamage = true,
                BreakOnStun   = true,
                NeedHand      = true,
                ExtraCheck    = () => IsRCDStillValid(eventArgs, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here
            };

            var result = await doAfterSystem.DoAfter(doAfterEventArgs);

            if (result == DoAfterStatus.Cancelled)
            {
                return(true);
            }

            switch (_mode)
            {
            //Floor mode just needs the tile to be a space tile (subFloor)
            case RcdMode.Floors:
                mapGrid.SetTile(eventArgs.ClickLocation, new Tile(_tileDefinitionManager["floor_steel"].TileId));
                break;

            //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check.
            case RcdMode.Deconstruct:
                if (!tile.IsBlockedTurf(true))     //Delete the turf
                {
                    mapGrid.SetTile(snapPos, Tile.Empty);
                }
                else     //Delete what the user targeted
                {
                    eventArgs.Target.Delete();
                }
                break;

            //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code.
            case RcdMode.Walls:
                var ent = _serverEntityManager.SpawnEntity("solid_wall", mapGrid.GridTileToLocal(snapPos));
                ent.Transform.LocalRotation = Owner.Transform.LocalRotation;     //Now apply icon smoothing.
                break;

            case RcdMode.Airlock:
                var airlock = _serverEntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos));
                airlock.Transform.LocalRotation = Owner.Transform.LocalRotation;     //Now apply icon smoothing.
                break;

            default:
                return(true);    //I don't know why this would happen, but sure I guess. Get out of here invalid state!
            }

            _entitySystemManager.GetEntitySystem <AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", Owner);
            _ammo--;
            return(true);
        }
예제 #28
0
        private async void OnAfterInteract(EntityUid uid, RCDComponent rcd, AfterInteractEvent args)
        {
            if (args.Handled || !args.CanReach)
            {
                return;
            }

            if (rcd.CancelToken != null)
            {
                rcd.CancelToken?.Cancel();
                rcd.CancelToken = null;
                args.Handled    = true;
                return;
            }

            if (!args.ClickLocation.IsValid(EntityManager))
            {
                return;
            }

            var clickLocationMod = args.ClickLocation;

            // Initial validity check
            if (!clickLocationMod.IsValid(EntityManager))
            {
                return;
            }
            // Try to fix it (i.e. if clicking on space)
            // Note: Ideally there'd be a better way, but there isn't right now.
            var gridID = clickLocationMod.GetGridId(EntityManager);

            if (!gridID.IsValid())
            {
                clickLocationMod = clickLocationMod.AlignWithClosestGridTile();
                gridID           = clickLocationMod.GetGridId(EntityManager);
            }
            // Check if fixing it failed / get final grid ID
            if (!gridID.IsValid())
            {
                return;
            }

            var mapGrid = _mapManager.GetGrid(gridID);
            var tile    = mapGrid.GetTileRef(clickLocationMod);
            var snapPos = mapGrid.TileIndicesFor(clickLocationMod);

            //No changing mode mid-RCD
            var startingMode = rcd.Mode;

            args.Handled = true;
            var user = args.User;

            //Using an RCD isn't instantaneous
            rcd.CancelToken = new CancellationTokenSource();
            var doAfterEventArgs = new DoAfterEventArgs(user, rcd.Delay, rcd.CancelToken.Token, args.Target)
            {
                BreakOnDamage = true,
                BreakOnStun   = true,
                NeedHand      = true,
                ExtraCheck    = () => IsRCDStillValid(rcd, args, mapGrid, tile, startingMode) //All of the sanity checks are here
            };

            var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs);

            rcd.CancelToken = null;

            if (result == DoAfterStatus.Cancelled)
            {
                return;
            }

            switch (rcd.Mode)
            {
            //Floor mode just needs the tile to be a space tile (subFloor)
            case RcdMode.Floors:
                mapGrid.SetTile(snapPos, new Tile(_tileDefinitionManager["floor_steel"].TileId));
                _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} {snapPos} to floor_steel");
                break;

            //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check.
            case RcdMode.Deconstruct:
                if (!tile.IsBlockedTurf(true))     //Delete the turf
                {
                    mapGrid.SetTile(snapPos, Tile.Empty);
                    _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} tile: {snapPos} to space");
                }
                else     //Delete what the user targeted
                {
                    if (args.Target is { Valid : true } target)
                    {
                        _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to delete {ToPrettyString(target):target}");
                        QueueDel(target);
                    }
                }
                break;

            //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile,
            // thus we early return to avoid the tile set code.
            case RcdMode.Walls:
                var ent = EntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos));
                Transform(ent).LocalRotation = Angle.Zero;     // Walls always need to point south.
                _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(ent)} at {snapPos} on grid {mapGrid.Index}");
                break;

            case RcdMode.Airlock:
                var airlock = EntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos));
                Transform(airlock).LocalRotation = Transform(rcd.Owner).LocalRotation;     //Now apply icon smoothing.
                _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(airlock)} at {snapPos} on grid {mapGrid.Index}");
                break;

            default:
                args.Handled = true;
                return;     //I don't know why this would happen, but sure I guess. Get out of here invalid state!
            }

            SoundSystem.Play(Filter.Pvs(uid, entityManager: EntityManager), rcd.SuccessSound.GetSound(), rcd.Owner);
            rcd.CurrentAmmo--;
            args.Handled = true;
        }
예제 #29
0
        /// <summary>
        /// Attempt to uncuff a cuffed entity. Can be called by the cuffed entity, or another entity trying to help uncuff them.
        /// If the uncuffing succeeds, the cuffs will drop on the floor.
        /// </summary>
        /// <param name="user">The cuffed entity</param>
        /// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param>
        public async void TryUncuff(IEntity user, IEntity cuffsToRemove = null)
        {
            var isOwner = user == Owner;

            if (cuffsToRemove == null)
            {
                cuffsToRemove = LastAddedCuffs;
            }
            else
            {
                if (!_container.ContainedEntities.Contains(cuffsToRemove))
                {
                    Logger.Warning("A user is trying to remove handcuffs that aren't in the owner's container. This should never happen!");
                }
            }

            if (!cuffsToRemove.TryGetComponent <HandcuffComponent>(out var cuff))
            {
                Logger.Warning($"A user is trying to remove handcuffs without a {nameof(HandcuffComponent)}. This should never happen!");
                return;
            }

            if (!isOwner && !ActionBlockerSystem.CanInteract(user))
            {
                user.PopupMessage(user, Loc.GetString("You can't do that!"));
                return;
            }

            if (!isOwner && user.InRangeUnobstructed(Owner, _interactRange))
            {
                user.PopupMessage(user, Loc.GetString("You are too far away to remove the cuffs."));
                return;
            }

            if (!cuffsToRemove.InRangeUnobstructed(Owner, _interactRange))
            {
                Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!");
                return;
            }

            user.PopupMessage(user, Loc.GetString("You start removing the cuffs."));

            var audio = EntitySystem.Get <AudioSystem>();

            audio.PlayFromEntity(isOwner ? cuff.StartBreakoutSound : cuff.StartUncuffSound, Owner);

            var uncuffTime       = isOwner ? cuff.BreakoutTime : cuff.UncuffTime;
            var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime)
            {
                BreakOnUserMove = true,
                BreakOnDamage   = true,
                BreakOnStun     = true,
                NeedHand        = true
            };

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();
            var result        = await doAfterSystem.DoAfter(doAfterEventArgs);

            if (result != DoAfterStatus.Cancelled)
            {
                audio.PlayFromEntity(cuff.EndUncuffSound, Owner);

                _container.ForceRemove(cuffsToRemove);
                cuffsToRemove.Transform.AttachToGridOrMap();
                cuffsToRemove.Transform.WorldPosition = Owner.Transform.WorldPosition;

                if (cuff.BreakOnRemove)
                {
                    cuff.Broken = true;

                    cuffsToRemove.Name        = cuff.BrokenName;
                    cuffsToRemove.Description = cuff.BrokenDesc;

                    if (cuffsToRemove.TryGetComponent <SpriteComponent>(out var sprite))
                    {
                        sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state?
                    }
                }

                CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
                OnCuffedStateChanged.Invoke();
                UpdateStatusEffect();
                Dirty();

                if (CuffedHandCount == 0)
                {
                    _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs."));

                    if (!isOwner)
                    {
                        _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} uncuffs your hands.", user));
                    }
                }
                else
                {
                    if (!isOwner)
                    {
                        _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of {1:theName}'s hands remain cuffed.", CuffedHandCount, user));
                        _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} removes your cuffs. {1} of your hands remain cuffed.", user, CuffedHandCount));
                    }
                    else
                    {
                        _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of your hands remain cuffed.", CuffedHandCount));
                    }
                }
            }
            else
            {
                _notifyManager.PopupMessage(user, user, Loc.GetString("You fail to remove the cuffs."));
            }

            return;
        }
예제 #30
0
        /// <summary>
        /// Allows a user to pick up entities by clicking them, or pick up all entities in a certain radius
        /// around a click.
        /// </summary>
        /// <returns></returns>
        private async void AfterInteract(EntityUid uid, ServerStorageComponent storageComp, AfterInteractEvent eventArgs)
        {
            if (!eventArgs.CanReach)
            {
                return;
            }

            if (storageComp.CancelToken != null)
            {
                storageComp.CancelToken.Cancel();
                storageComp.CancelToken = null;
                return;
            }

            // Pick up all entities in a radius around the clicked location.
            // The last half of the if is because carpets exist and this is terrible
            if (storageComp.AreaInsert && (eventArgs.Target == null || !HasComp <ItemComponent>(eventArgs.Target.Value)))
            {
                var validStorables = new List <EntityUid>();
                foreach (var entity in _entityLookupSystem.GetEntitiesInRange(eventArgs.ClickLocation, storageComp.AreaInsertRadius, LookupFlags.None))
                {
                    if (entity == eventArgs.User ||
                        !HasComp <ItemComponent>(entity) ||
                        !_interactionSystem.InRangeUnobstructed(eventArgs.User, entity))
                    {
                        continue;
                    }

                    validStorables.Add(entity);
                }

                //If there's only one then let's be generous
                if (validStorables.Count > 1)
                {
                    storageComp.CancelToken = new CancellationTokenSource();
                    var doAfterArgs = new DoAfterEventArgs(eventArgs.User, 0.2f * validStorables.Count, storageComp.CancelToken.Token, uid)
                    {
                        BreakOnStun     = true,
                        BreakOnDamage   = true,
                        BreakOnUserMove = true,
                        NeedHand        = true,
                    };

                    await _doAfterSystem.WaitDoAfter(doAfterArgs);
                }

                // TODO: Make it use the event DoAfter
                var successfullyInserted          = new List <EntityUid>();
                var successfullyInsertedPositions = new List <EntityCoordinates>();
                foreach (var entity in validStorables)
                {
                    // Check again, situation may have changed for some entities, but we'll still pick up any that are valid
                    if (_containerSystem.IsEntityInContainer(entity) ||
                        entity == eventArgs.User ||
                        !HasComp <ItemComponent>(entity))
                    {
                        continue;
                    }

                    if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(entity, out var transformEnt))
                    {
                        var position = EntityCoordinates.FromMap(transformOwner.Parent?.Owner ?? uid, transformEnt.MapPosition);

                        if (PlayerInsertEntityInWorld(uid, eventArgs.User, entity, storageComp))
                        {
                            successfullyInserted.Add(entity);
                            successfullyInsertedPositions.Add(position);
                        }
                    }
                }

                // If we picked up atleast one thing, play a sound and do a cool animation!
                if (successfullyInserted.Count > 0)
                {
                    if (storageComp.StorageInsertSound is not null)
                    {
                        SoundSystem.Play(storageComp.StorageInsertSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, AudioParams.Default);
                    }
                    RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, successfullyInserted, successfullyInsertedPositions));
                }
                return;
            }
            // Pick up the clicked entity
            else if (storageComp.QuickInsert)
            {
                if (eventArgs.Target is not {
                    Valid : true
                } target)
                {
                    return;
                }

                if (_containerSystem.IsEntityInContainer(target) ||
                    target == eventArgs.User ||
                    !HasComp <ItemComponent>(target))
                {
                    return;
                }

                if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(target, out var transformEnt))
                {
                    var parent = transformOwner.ParentUid;

                    var position = EntityCoordinates.FromMap(
                        parent.IsValid() ? parent : uid,
                        transformEnt.MapPosition);
                    if (PlayerInsertEntityInWorld(uid, eventArgs.User, target, storageComp))
                    {
                        RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid,
                                                                            new List <EntityUid> {
                            target
                        },
                                                                            new List <EntityCoordinates> {
                            position
                        }));
                    }
                }
            }
            return;
        }