private bool CheckRangeAndSetFacing(EntityCoordinates target, IEntity player)
        {
            // ensure it's within their clickable range
            var targetWorldPos = target.ToMapPos(EntityManager);
            var rangeBox       = new Box2(player.Transform.WorldPosition, player.Transform.WorldPosition)
                                 .Enlarged(_entityManager.MaxUpdateRange);

            if (!rangeBox.Contains(targetWorldPos))
            {
                Logger.DebugS("action", "user {0} attempted to" +
                              " perform target action further than allowed range",
                              player.Name);
                return(false);
            }

            if (!ActionBlockerSystem.CanChangeDirection(player))
            {
                return(true);
            }

            // don't set facing unless they clicked far enough away
            var diff = targetWorldPos - player.Transform.WorldPosition;

            if (diff.LengthSquared > 0.01f)
            {
                player.Transform.LocalRotation = new Angle(diff);
            }

            return(true);
        }
Beispiel #2
0
        private void FaceClickCoordinates(IEntity user, EntityCoordinates coordinates)
        {
            var diff = coordinates.ToMapPos(EntityManager) - user.Transform.MapPosition.Position;

            if (diff.LengthSquared <= 0.01f)
            {
                return;
            }
            var diffAngle = Angle.FromWorldVec(diff);

            if (_actionBlockerSystem.CanChangeDirection(user))
            {
                user.Transform.WorldRotation = diffAngle;
            }
            else
            {
                if (user.TryGetComponent(out BuckleComponent? buckle) && (buckle.BuckledTo != null))
                {
                    // We're buckled to another object. Is that object rotatable?
                    if (buckle.BuckledTo !.Owner.TryGetComponent(out RotatableComponent? rotatable) && rotatable.RotateWhileAnchored)
                    {
                        // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel".
                        // (Since the user being buckled to it holds it down with their weight.)
                        // This is logically equivalent to RotateWhileAnchored.
                        // Barstools and office chairs have independent wheels, while regular chairs don't.
                        rotatable.Owner.Transform.LocalRotation = diffAngle;
                    }
                }
            }
        }
Beispiel #3
0
 public bool TryFaceAngle(EntityUid user, Angle diffAngle)
 {
     if (_actionBlockerSystem.CanChangeDirection(user))
     {
         EntityManager.GetComponent <TransformComponent>(user).WorldRotation = diffAngle;
         return(true);
     }
     else
     {
         if (EntityManager.TryGetComponent(user, out SharedBuckleComponent? buckle) && buckle.Buckled)
         {
             var suid = buckle.LastEntityBuckledTo;
             if (suid != null)
             {
                 // We're buckled to another object. Is that object rotatable?
                 if (EntityManager.TryGetComponent <RotatableComponent>(suid.Value !, out var rotatable) && rotatable.RotateWhileAnchored)
                 {
                     // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel".
                     // (Since the user being buckled to it holds it down with their weight.)
                     // This is logically equivalent to RotateWhileAnchored.
                     // Barstools and office chairs have independent wheels, while regular chairs don't.
                     EntityManager.GetComponent <TransformComponent>(rotatable.Owner).WorldRotation = diffAngle;
                     return(true);
                 }
             }
         }
     }
     return(false);
 }
Beispiel #4
0
        /// <summary>
        ///     Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>.
        /// </summary>
        /// <param name="thrownEnt">The entity to throw.</param>
        /// <param name="throwForce">
        /// The force to throw the entity with.
        /// Total impulse applied is equal to this force applied for one second.
        /// </param>
        /// <param name="targetLoc">
        /// The target location to throw at.
        /// This is only used to calculate a direction,
        /// actual distance is purely determined by <paramref name="throwForce"/>.
        /// </param>
        /// <param name="sourceLoc">
        /// The position to start the throw from.
        /// </param>
        /// <param name="spread">
        /// If true, slightly spread the actual throw angle.
        /// </param>
        /// <param name="throwSourceEnt">
        /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in.
        /// </param>
        public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
        {
            if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp))
            {
                return;
            }

            var entityManager = IoCManager.Resolve <IEntityManager>();

            colComp.CanCollide = true;
            // I can now collide with player, so that i can do damage.

            if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp))
            {
                projComp = thrownEnt.AddComponent <ThrownItemComponent>();

                if (colComp.PhysicsShapes.Count == 0)
                {
                    colComp.PhysicsShapes.Add(new PhysShapeAabb());
                }

                colComp.PhysicsShapes[0].CollisionMask |= (int)CollisionGroup.ThrownItem;
                colComp.Status = BodyStatus.InAir;
            }
            var angle = new Angle(targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager));

            if (spread)
            {
                var spreadRandom = IoCManager.Resolve <IRobustRandom>();
                angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3));
            }

            if (throwSourceEnt != null)
            {
                projComp.User = throwSourceEnt;
                projComp.IgnoreEntity(throwSourceEnt);

                if (ActionBlockerSystem.CanChangeDirection(throwSourceEnt))
                {
                    throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle();
                }
            }

            // scaling is handled elsewhere, this is just multiplying by 60 independent of timing as a fix until elsewhere values are updated
            var spd = throwForce * 60;

            projComp.StartThrow(angle.ToVec(), spd);

            if (throwSourceEnt != null &&
                throwSourceEnt.TryGetComponent <IPhysicsComponent>(out var physics))
            {
                if (throwSourceEnt.IsWeightless())
                {
                    // We don't check for surrounding entities,
                    // so you'll still get knocked around if you're hugging the station wall in zero g.
                    // I got kinda lazy is the reason why. Also it makes a bit of sense.
                    // If somebody wants they can come along and make it so magboots completely hold you still.
                    // Would be a cool incentive to use them.
                    const float throwFactor = 0.2f; // Break Newton's Third Law for better gameplay
                    var         mover       = physics.EnsureController <ThrowKnockbackController>();
                    mover.Push(-angle.ToVec(), spd * throwFactor);
                }
            }
        }
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var server = StartServerDummyTicker();

            IEntity         human  = null;
            IEntity         chair  = null;
            BuckleComponent buckle = null;
            StrapComponent  strap  = null;

            await server.WaitAssertion(() =>
            {
                var mapManager = IoCManager.Resolve <IMapManager>();

                mapManager.CreateNewMapEntity(MapId.Nullspace);

                var entityManager = IoCManager.Resolve <IEntityManager>();

                human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
                chair = entityManager.SpawnEntity("ChairWood", MapCoordinates.Nullspace);

                // Default state, unbuckled
                Assert.True(human.TryGetComponent(out buckle));
                Assert.NotNull(buckle);
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Default state, no buckled entities, strap
                Assert.True(chair.TryGetComponent(out strap));
                Assert.NotNull(strap);
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Side effects of buckling
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.NotNull(buckle.BuckledTo);
                Assert.True(buckle.Buckled);
                Assert.True(((BuckleComponentState)buckle.GetComponentState()).Buckled);
                Assert.False(ActionBlockerSystem.CanMove(human));
                Assert.False(ActionBlockerSystem.CanChangeDirection(human));
                Assert.False(EffectBlockerSystem.CanFall(human));
                Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition));

                // Side effects of buckling for the strap
                Assert.That(strap.BuckledEntities, Does.Contain(human));
                Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
                Assert.Positive(strap.OccupiedSize);

                // Trying to buckle while already buckled fails
                Assert.False(buckle.TryBuckle(human, chair));

                // Trying to unbuckle too quickly fails
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Unbuckle, strap
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Re-buckling has no cooldown
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // On cooldown
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.False(buckle.Buckled);

                // Move away from the chair
                human.Transform.WorldPosition += (1000, 1000);

                // Out of range
                Assert.False(buckle.TryBuckle(human, chair));
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));

                // Move near the chair
                human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0);

                // In range
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // Force unbuckle
                Assert.True(buckle.TryUnbuckle(human, true));
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Re-buckle
                Assert.True(buckle.TryBuckle(human, chair));

                // Move away from the chair
                human.Transform.WorldPosition += (1, 0);
            });

            await server.WaitRunTicks(1);

            await server.WaitAssertion(() =>
            {
                // No longer buckled
                Assert.False(buckle.Buckled);
                Assert.Null(buckle.BuckledTo);
                Assert.IsEmpty(strap.BuckledEntities);
            });
        }
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var cOptions = new ClientIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var sOptions = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };

            var(client, server) = await StartConnectedServerClientPair(cOptions, sOptions);

            IEntity         human  = null;
            IEntity         chair  = null;
            BuckleComponent buckle = null;
            StrapComponent  strap  = null;

            await server.WaitAssertion(() =>
            {
                var mapManager    = IoCManager.Resolve <IMapManager>();
                var entityManager = IoCManager.Resolve <IEntityManager>();

                var gridId      = new GridId(1);
                var grid        = mapManager.GetGrid(gridId);
                var coordinates = grid.GridEntityId.ToCoordinates();

                human = entityManager.SpawnEntity(BuckleDummyId, coordinates);
                chair = entityManager.SpawnEntity(StrapDummyId, coordinates);

                // Default state, unbuckled
                Assert.True(human.TryGetComponent(out buckle));
                Assert.NotNull(buckle);
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Default state, no buckled entities, strap
                Assert.True(chair.TryGetComponent(out strap));
                Assert.NotNull(strap);
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Side effects of buckling
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.NotNull(buckle.BuckledTo);
                Assert.True(buckle.Buckled);

                var player = IoCManager.Resolve <IPlayerManager>().GetAllPlayers().Single();
                Assert.True(((BuckleComponentState)buckle.GetComponentState(player)).Buckled);
                Assert.False(ActionBlockerSystem.CanMove(human));
                Assert.False(ActionBlockerSystem.CanChangeDirection(human));
                Assert.False(EffectBlockerSystem.CanFall(human));
                Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition));

                // Side effects of buckling for the strap
                Assert.That(strap.BuckledEntities, Does.Contain(human));
                Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
                Assert.Positive(strap.OccupiedSize);

                // Trying to buckle while already buckled fails
                Assert.False(buckle.TryBuckle(human, chair));

                // Trying to unbuckle too quickly fails
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Unbuckle, strap
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Re-buckling has no cooldown
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // On cooldown
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.False(buckle.Buckled);

                // Move away from the chair
                human.Transform.WorldPosition += (1000, 1000);

                // Out of range
                Assert.False(buckle.TryBuckle(human, chair));
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));

                // Move near the chair
                human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0);

                // In range
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // Force unbuckle
                Assert.True(buckle.TryUnbuckle(human, true));
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Re-buckle
                Assert.True(buckle.TryBuckle(human, chair));

                // Move away from the chair
                human.Transform.WorldPosition += (1, 0);
            });

            await server.WaitRunTicks(1);

            await server.WaitAssertion(() =>
            {
                // No longer buckled
                Assert.False(buckle.Buckled);
                Assert.Null(buckle.BuckledTo);
                Assert.IsEmpty(strap.BuckledEntities);
            });
        }