/// <summary>
        ///     Tries to throw the entity if it has a physics component, otherwise does nothing.
        /// </summary>
        /// <param name="entity">The entity being thrown.</param>
        /// <param name="direction">A vector pointing from the entity to its destination.</param>
        /// <param name="strength">How much the direction vector should be multiplied for velocity.</param>
        /// <param name="user"></param>
        /// <param name="pushbackRatio">The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced</param>
        internal static void TryThrow(this EntityUid entity, Vector2 direction, float strength = 1.0f, EntityUid?user = null, float pushbackRatio = 10.0f)
        {
            var entities = IoCManager.Resolve <IEntityManager>();

            if (entities.GetComponent <MetaDataComponent>(entity).EntityDeleted ||
                strength <= 0f ||
                !entities.TryGetComponent(entity, out PhysicsComponent? physicsComponent))
            {
                return;
            }

            if (physicsComponent.BodyType == BodyType.Static)
            {
                Logger.Warning("Tried to throw entity {entity} but can't throw static bodies!");
                return;
            }

            if (entities.HasComponent <MobStateComponent>(entity))
            {
                Logger.Warning("Throwing not supported for mobs!");
                return;
            }

            var comp = entity.EnsureComponent <ThrownItemComponent>();

            if (entities.HasComponent <SharedItemComponent>(entity))
            {
                comp.Thrower = user;
                // Give it a l'il spin.
                if (!entity.HasTag("NoSpinOnThrow"))
                {
                    physicsComponent.ApplyAngularImpulse(ThrowAngularImpulse);
                }
                else if (direction != Vector2.Zero)
                {
                    entities.GetComponent <TransformComponent>(entity).LocalRotation = direction.ToWorldAngle() - Math.PI;
                }

                if (user != null)
                {
                    EntitySystem.Get <InteractionSystem>().ThrownInteraction(user.Value, entity);
                }
            }

            var impulseVector = direction.Normalized * strength * physicsComponent.Mass;

            physicsComponent.ApplyLinearImpulse(impulseVector);

            // Estimate time to arrival so we can apply OnGround status and slow it much faster.
            var time = (direction / strength).Length;

            if (time < FlyTime)
            {
                physicsComponent.BodyStatus = BodyStatus.OnGround;
                EntitySystem.Get <ThrownItemSystem>().LandComponent(comp);
            }
            else
            {
                physicsComponent.BodyStatus = BodyStatus.InAir;

                Timer.Spawn(TimeSpan.FromSeconds(time - FlyTime), () =>
                {
                    if (physicsComponent.Deleted)
                    {
                        return;
                    }
                    physicsComponent.BodyStatus = BodyStatus.OnGround;
                    EntitySystem.Get <ThrownItemSystem>().LandComponent(comp);
                });
            }

            // Give thrower an impulse in the other direction
            if (user != null && pushbackRatio > 0.0f && entities.TryGetComponent(user.Value, out IPhysBody? body))
            {
                var msg = new ThrowPushbackAttemptEvent();
                entities.EventBus.RaiseLocalEvent(body.Owner, msg);

                if (!msg.Cancelled)
                {
                    body.ApplyLinearImpulse(-impulseVector * pushbackRatio);
                }
            }
        }
Example #2
0
        public async Task TagComponentTest()
        {
            var options = new ServerContentIntegrationOption {
                ExtraPrototypes = Prototypes
            };
            var server = StartServer(options);

            await server.WaitIdleAsync();

            var sMapManager       = server.ResolveDependency <IMapManager>();
            var sEntityManager    = server.ResolveDependency <IEntityManager>();
            var sPrototypeManager = server.ResolveDependency <IPrototypeManager>();

            EntityUid    sTagDummy     = default;
            TagComponent sTagComponent = null !;

            await server.WaitPost(() =>
            {
                sMapManager.CreateNewMapEntity(MapId.Nullspace);
                sTagDummy     = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace);
                sTagComponent = IoCManager.Resolve <IEntityManager>().GetComponent <TagComponent>(sTagDummy);
            });

            await server.WaitAssertion(() =>
            {
                // Has one tag, the starting tag
                Assert.That(sTagComponent.Tags.Count, Is.EqualTo(1));
                sPrototypeManager.Index <TagPrototype>(StartingTag);
                Assert.That(sTagComponent.Tags, Contains.Item(StartingTag));

                // Single
                Assert.True(sTagDummy.HasTag(StartingTag));
                Assert.True(sTagComponent.HasTag(StartingTag));

                // Any
                Assert.True(sTagDummy.HasAnyTag(StartingTag));
                Assert.True(sTagComponent.HasAnyTag(StartingTag));

                // All
                Assert.True(sTagDummy.HasAllTags(StartingTag));
                Assert.True(sTagComponent.HasAllTags(StartingTag));

                // Does not have the added tag
                var addedTagPrototype = sPrototypeManager.Index <TagPrototype>(AddedTag);
                Assert.That(sTagComponent.Tags, Does.Not.Contains(addedTagPrototype));

                // Single
                Assert.False(sTagDummy.HasTag(AddedTag));
                Assert.False(sTagComponent.HasTag(AddedTag));

                // Any
                Assert.False(sTagDummy.HasAnyTag(AddedTag));
                Assert.False(sTagComponent.HasAnyTag(AddedTag));

                // All
                Assert.False(sTagDummy.HasAllTags(AddedTag));
                Assert.False(sTagComponent.HasAllTags(AddedTag));

                // Does not have the unused tag
                var unusedTagPrototype = sPrototypeManager.Index <TagPrototype>(UnusedTag);
                Assert.That(sTagComponent.Tags, Does.Not.Contains(unusedTagPrototype));

                // Single
                Assert.False(sTagDummy.HasTag(UnusedTag));
                Assert.False(sTagComponent.HasTag(UnusedTag));

                // Any
                Assert.False(sTagDummy.HasAnyTag(UnusedTag));
                Assert.False(sTagComponent.HasAnyTag(UnusedTag));

                // All
                Assert.False(sTagDummy.HasAllTags(UnusedTag));
                Assert.False(sTagComponent.HasAllTags(UnusedTag));

                // Throws when checking for an unregistered tag
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sPrototypeManager.Index <TagPrototype>(UnregisteredTag);
                });

                // Single
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagDummy.HasTag(UnregisteredTag);
                });
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagComponent.HasTag(UnregisteredTag);
                });

                // Any
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagDummy.HasAnyTag(UnregisteredTag);
                });
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagComponent.HasAnyTag(UnregisteredTag);
                });

                // All
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagDummy.HasAllTags(UnregisteredTag);
                });
                Assert.Throws <UnknownPrototypeException>(() =>
                {
                    sTagComponent.HasAllTags(UnregisteredTag);
                });

                // Cannot add the starting tag again
                Assert.That(sTagComponent.AddTag(StartingTag), Is.False);
                Assert.That(sTagComponent.AddTags(StartingTag, StartingTag), Is.False);
                Assert.That(sTagComponent.AddTags(new List <string> {
                    StartingTag, StartingTag
                }), Is.False);

                // Has the starting tag
                Assert.That(sTagComponent.HasTag(StartingTag), Is.True);
                Assert.That(sTagComponent.HasAllTags(StartingTag, StartingTag), Is.True);
                Assert.That(sTagComponent.HasAllTags(new List <string> {
                    StartingTag, StartingTag
                }), Is.True);
                Assert.That(sTagComponent.HasAnyTag(StartingTag, StartingTag), Is.True);
                Assert.That(sTagComponent.HasAnyTag(new List <string> {
                    StartingTag, StartingTag
                }), Is.True);

                // Does not have the added tag yet
                Assert.That(sTagComponent.HasTag(AddedTag), Is.False);
                Assert.That(sTagComponent.HasAllTags(AddedTag, AddedTag), Is.False);
                Assert.That(sTagComponent.HasAllTags(new List <string> {
                    AddedTag, AddedTag
                }), Is.False);
                Assert.That(sTagComponent.HasAnyTag(AddedTag, AddedTag), Is.False);
                Assert.That(sTagComponent.HasAnyTag(new List <string> {
                    AddedTag, AddedTag
                }), Is.False);

                // Has a combination of the two tags
                Assert.That(sTagComponent.HasAnyTag(StartingTag, AddedTag), Is.True);
                Assert.That(sTagComponent.HasAnyTag(new List <string> {
                    StartingTag, AddedTag
                }), Is.True);

                // Does not have both tags
                Assert.That(sTagComponent.HasAllTags(StartingTag, AddedTag), Is.False);
                Assert.That(sTagComponent.HasAllTags(new List <string> {
                    StartingTag, AddedTag
                }), Is.False);

                // Cannot remove a tag that does not exist
                Assert.That(sTagComponent.RemoveTag(AddedTag), Is.False);
                Assert.That(sTagComponent.RemoveTags(AddedTag, AddedTag), Is.False);
                Assert.That(sTagComponent.RemoveTags(new List <string> {
                    AddedTag, AddedTag
                }), Is.False);

                // Can add the new tag
                Assert.That(sTagComponent.AddTag(AddedTag), Is.True);

                // Cannot add it twice
                Assert.That(sTagComponent.AddTag(AddedTag), Is.False);

                // Cannot add existing tags
                Assert.That(sTagComponent.AddTags(StartingTag, AddedTag), Is.False);
                Assert.That(sTagComponent.AddTags(new List <string> {
                    StartingTag, AddedTag
                }), Is.False);

                // Now has two tags
                Assert.That(sTagComponent.Tags.Count, Is.EqualTo(2));

                // Has both tags
                Assert.That(sTagComponent.HasTag(StartingTag), Is.True);
                Assert.That(sTagComponent.HasTag(AddedTag), Is.True);
                Assert.That(sTagComponent.HasAllTags(StartingTag, StartingTag), Is.True);
                Assert.That(sTagComponent.HasAllTags(AddedTag, StartingTag), Is.True);
                Assert.That(sTagComponent.HasAllTags(new List <string> {
                    StartingTag, AddedTag
                }), Is.True);
                Assert.That(sTagComponent.HasAllTags(new List <string> {
                    AddedTag, StartingTag
                }), Is.True);
                Assert.That(sTagComponent.HasAnyTag(StartingTag, AddedTag), Is.True);
                Assert.That(sTagComponent.HasAnyTag(AddedTag, StartingTag), Is.True);

                // Remove the existing starting tag
                Assert.That(sTagComponent.RemoveTag(StartingTag), Is.True);

                // Remove the existing added tag
                Assert.That(sTagComponent.RemoveTags(AddedTag, AddedTag), Is.True);

                // No tags left to remove
                Assert.That(sTagComponent.RemoveTags(new List <string> {
                    StartingTag, AddedTag
                }), Is.False);

                // No tags left in the component
                Assert.That(sTagComponent.Tags, Is.Empty);
            });
        }