Beispiel #1
0
        /// <summary>
        ///     Tests a physBody against every other registered physBody.
        /// </summary>
        /// <param name="physBody">Body being tested.</param>
        /// <param name="colliderAABB">The AABB of the physBody being tested. This can be IPhysBody.WorldAABB, or a modified version of it.</param>
        /// <param name="results">An empty list that the function stores all colliding bodies inside of.</param>
        internal void DoCollisionTest(IPhysBody physBody, Box2 colliderAABB, List <IPhysBody> results)
        {
            foreach (var body in GetCollidablesForLocation(colliderAABB))
            {
                // TODO: Terrible hack to fix bullets crashing the server.
                // Should be handled with deferred physics events instead.
                if (body.Owner.Deleted)
                {
                    continue;
                }

                if (!body.CollisionEnabled)
                {
                    continue;
                }

                if ((physBody.CollisionMask & body.CollisionLayer) == 0x0)
                {
                    continue;
                }

                if (physBody.MapID != body.MapID ||
                    physBody == body ||
                    !colliderAABB.Intersects(body.WorldAABB))
                {
                    continue;
                }

                results.Add(body);
            }
        }
Beispiel #2
0
        public IEnumerable <IEntity> GetCollidingEntities(IPhysBody physBody, Vector2 offset, bool approximate = true)
        {
            var modifiers = physBody.Entity.GetAllComponents <ICollideSpecial>();

            foreach (var body in this[physBody.MapID].Query(physBody.WorldAABB, approximate))
            {
                if (body.Entity.Deleted)
                {
                    continue;
                }

                if (CollidesOnMask(physBody, body))
                {
                    var preventCollision = false;
                    var otherModifiers   = body.Entity.GetAllComponents <ICollideSpecial>();
                    foreach (var modifier in modifiers)
                    {
                        preventCollision |= modifier.PreventCollide(body);
                    }
                    foreach (var modifier in otherModifiers)
                    {
                        preventCollision |= modifier.PreventCollide(physBody);
                    }

                    if (preventCollision)
                    {
                        continue;
                    }
                    yield return(body.Entity);
                }
            }
        }
Beispiel #3
0
 /// <summary>
 ///     Adds a physBody to the manager.
 /// </summary>
 /// <param name="physBody"></param>
 public void AddBody(IPhysBody physBody)
 {
     if (!this[physBody.MapID].Add(physBody))
     {
         Logger.WarningS("phys", $"PhysicsBody already registered! {physBody.Entity}");
     }
 }
Beispiel #4
0
        private static void UpdatePosition(IPhysBody body, float frameTime)
        {
            var ent = body.Entity;

            if (!body.CanMove() || (body.LinearVelocity.LengthSquared < Epsilon && MathF.Abs(body.AngularVelocity) < Epsilon))
            {
                return;
            }

            if (body.LinearVelocity != Vector2.Zero)
            {
                var entityMoveMessage = new EntityMovementMessage();
                ent.SendMessage(ent.Transform, entityMoveMessage);

                if (ContainerHelpers.IsInContainer(ent))
                {
                    var relayEntityMoveMessage = new RelayMovementEntityMessage(ent);
                    ent.Transform.Parent !.Owner.SendMessage(ent.Transform, relayEntityMoveMessage);
                    // This prevents redundant messages from being sent if solveIterations > 1 and also simulates the entity "colliding" against the locker door when it opens.
                    body.LinearVelocity = Vector2.Zero;
                }
            }

            body.WorldRotation += body.AngularVelocity * frameTime;
            body.WorldPosition += body.LinearVelocity * frameTime;
        }
        /// <summary>
        ///     Tests a physBody against every other registered physBody.
        /// </summary>
        /// <param name="physBody">Body being tested.</param>
        /// <param name="colliderAABB">The AABB of the physBody being tested. This can be IPhysBody.WorldAABB, or a modified version of it.</param>
        /// <param name="results">An empty list that the function stores all colliding bodies inside of.</param>
        internal bool DoCollisionTest(IPhysBody physBody, Box2 colliderAABB, List <IPhysBody> results)
        {
            // TODO: Terrible hack to fix bullets crashing the server.
            // Should be handled with deferred physics events instead.
            if (physBody.Owner.Deleted)
            {
                return(false);
            }

            var any = false;

            foreach (var body in this[physBody.MapID].Query(colliderAABB))
            {
                // TODO: Terrible hack to fix bullets crashing the server.
                // Should be handled with deferred physics events instead.
                if (body.Owner.Deleted)
                {
                    continue;
                }

                if (TestMask(physBody, body))
                {
                    results.Add(body);
                }

                any = true;
            }

            return(any);
        }
        /// <summary>
        ///     Tests a physBody against every other registered physBody.
        /// </summary>
        /// <param name="physBody">Body being tested.</param>
        /// <param name="colliderAABB">The AABB of the physBody being tested. This can be IPhysBody.WorldAABB, or a modified version of it.</param>
        /// <param name="results">An empty list that the function stores all colliding bodies inside of.</param>
        internal void DoCollisionTest(IPhysBody physBody, Box2 colliderAABB, List <IPhysBody> results)
        {
            // TODO: Terrible hack to fix bullets crashing the server.
            // Should be handled with deferred physics events instead.
            if (physBody.Owner.Deleted)
            {
                return;
            }

            _broadphase.Query((delegate(int id)
            {
                var body = _broadphase.GetProxy(id).Body;

                // TODO: Terrible hack to fix bullets crashing the server.
                // Should be handled with deferred physics events instead.
                if (body.Owner.Deleted)
                {
                    return(true);
                }

                if (TestMask(physBody, body))
                {
                    results.Add(body);
                }

                return(true);
            }), colliderAABB);
        }
#pragma warning restore 649

        /// <summary>
        ///     Removes a physBody from the manager
        /// </summary>
        /// <param name="physBody"></param>
        public void RemoveBody(IPhysBody physBody)
        {
            var removeAttempted = false;
            var removed         = false;

            if (physBody.Owner.Deleted || physBody.Owner.Transform.Deleted)
            {
                removeAttempted = true;
                foreach (var mapId in _mapManager.GetAllMapIds())
                {
                    removed = this[mapId].Remove(physBody);

                    if (removed)
                    {
                        break;
                    }
                }
            }

            if (!removed)
            {
                try
                {
                    removed = this[physBody.MapID].Remove(physBody);
                }
                catch (InvalidOperationException ioex)
                {
                    removeAttempted = true;
                    // TODO: TryGetMapId or something
                    foreach (var mapId in _mapManager.GetAllMapIds())
                    {
                        removed = this[mapId].Remove(physBody);

                        if (removed)
                        {
                            break;
                        }
                    }
                }
            }

            if (!removed)
            {
                foreach (var mapId in _mapManager.GetAllMapIds())
                {
                    removed = this[mapId].Remove(physBody);

                    if (removed)
                    {
                        break;
                    }
                }
            }

            if (!removed)
            {
                Logger.WarningS("phys", $"Trying to remove unregistered PhysicsBody! {physBody.Owner}");
            }
        }
 /// <summary>
 /// Special collision override, can be used to give custom behaviors deciding when to collide
 /// </summary>
 /// <param name="collidedwith"></param>
 /// <returns></returns>
 bool ICollideSpecial.PreventCollide(IPhysBody collidedwith)
 {
     // Don't collid with other mobs
     if (collidedwith.Owner.TryGetComponent <SpeciesComponent>(out var collidedSpeciesComponent))
     {
         return(true);
     }
     return(false);
 }
        private void HandleCollisionMessage(IPhysBody ourBody, IPhysBody otherBody, float frameTime, Manifold manifold)
        {
            if (otherBody.BodyType != BodyType.Dynamic) return;

            var normal = manifold.LocalNormal;

            if (!ourBody.Entity.TryGetComponent(out IMobMoverComponent? mobMover) || normal == Vector2.Zero) return;

            otherBody.ApplyLinearImpulse(-normal * mobMover.PushStrength * frameTime);
        }
Beispiel #10
0
        public float CalculatePenetration(IPhysBody target, IPhysBody source)
        {
            var manifold = target.WorldAABB.Intersect(source.WorldAABB);

            if (manifold.IsEmpty())
            {
                return(0.0f);
            }
            return(manifold.Height > manifold.Width ? manifold.Width : manifold.Height);
        }
        bool ICollideSpecial.PreventCollide(IPhysBody collidedwith)
        {
            if (collidedwith.Entity.Uid == LastEntityBuckledTo)
            {
                IsOnStrapEntityThisFrame = true;
                return(Buckled || DontCollide);
            }

            return(false);
        }
        bool ICollideSpecial.PreventCollide(IPhysBody collided)
        {
            if (((CollisionGroup)collided.CollisionLayer).HasFlag(CollisionGroup.VaultImpassable) && collided.Entity.HasComponent <IClimbable>())
            {
                IsOnClimbableThisFrame = true;
                return(IsClimbing);
            }

            return(false);
        }
Beispiel #13
0
 /// <summary>
 ///     Adds a physBody to the manager.
 /// </summary>
 /// <param name="physBody"></param>
 public void AddBody(IPhysBody physBody)
 {
     if (!_bodies.Contains(physBody))
     {
         _bodies.Add(physBody);
     }
     else
     {
         Logger.WarningS("phys", $"PhysicsBody already registered! {physBody.Owner}");
     }
 }
Beispiel #14
0
 /// <summary>
 ///     Removes a physBody from the manager
 /// </summary>
 /// <param name="physBody"></param>
 public void RemoveBody(IPhysBody physBody)
 {
     if (_bodies.Contains(physBody))
     {
         _bodies.Remove(physBody);
     }
     else
     {
         Logger.WarningS("phys", $"Trying to remove unregistered PhysicsBody! {physBody.Owner}");
     }
 }
 /// <summary>
 ///     Raises collision events on the thrown and target entities.
 /// </summary>
 public void ThrowCollideInteraction(EntityUid?user, IPhysBody thrown, IPhysBody target)
 {
     if (user is not null)
     {
         _adminLogger.Add(LogType.ThrowHit, LogImpact.Low,
                          $"{ToPrettyString(thrown.Owner):thrown} thrown by {ToPrettyString(user.Value):thrower} hit {ToPrettyString(target.Owner):target}.");
     }
     // TODO: Just pass in the bodies directly
     RaiseLocalEvent(target.Owner, new ThrowHitByEvent(user, thrown.Owner, target.Owner), true);
     RaiseLocalEvent(thrown.Owner, new ThrowDoHitEvent(user, thrown.Owner, target.Owner), true);
 }
        private float GetTileFriction(IPhysBody body)
        {
            // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events
            if (body.BodyStatus == BodyStatus.InAir || body.Owner.IsWeightless(_physicsManager) || !_mapManager.TryGetGrid(body.Owner.Transform.GridID, out var grid))
            {
                return(0.0f);
            }

            var tile    = grid.GetTileRef(body.Owner.Transform.Coordinates);
            var tileDef = _tileDefinitionManager[tile.Tile.TypeId];

            return(tileDef.Friction);
        }
Beispiel #17
0
        private (float, float) GetInvMass(IPhysBody bodyA, IPhysBody bodyB)
        {
            // God this is shitcodey but uhhhh we need to snowflake KinematicController for nice collisions.
            // TODO: Might need more finagling with the kinematic bodytype
            switch (bodyA.BodyType)
            {
            case BodyType.Kinematic:
            case BodyType.Static:
                return(bodyA.InvMass, bodyB.InvMass);

            case BodyType.KinematicController:
                switch (bodyB.BodyType)
                {
                case BodyType.Kinematic:
                case BodyType.Static:
                    return(bodyA.InvMass, bodyB.InvMass);

                case BodyType.Dynamic:
                    return(bodyA.InvMass, 0f);

                case BodyType.KinematicController:
                    return(0f, 0f);

                default:
                    throw new ArgumentOutOfRangeException();
                }

            case BodyType.Dynamic:
                switch (bodyB.BodyType)
                {
                case BodyType.Kinematic:
                case BodyType.Static:
                case BodyType.Dynamic:
                    return(bodyA.InvMass, bodyB.InvMass);

                case BodyType.KinematicController:
                    return(0f, bodyB.InvMass);

                default:
                    throw new ArgumentOutOfRangeException();
                }

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        /// <summary>
        ///     Adds a physBody to the manager.
        /// </summary>
        /// <param name="physBody"></param>
        public void AddBody(IPhysBody physBody)
        {
            if (!_bodies.Contains(physBody))
            {
                _bodies.Add(physBody);

                var proxy = new BodyProxy()
                {
                    Body = physBody
                };

                physBody.ProxyId = _broadphase.AddProxy(proxy);
            }
            else
            {
                Logger.WarningS("phys", $"PhysicsBody already registered! {physBody.Owner}");
            }
        }
Beispiel #19
0
        public static bool CollidesOnMask(IPhysBody a, IPhysBody b)
        {
            if (a == b)
            {
                return(false);
            }

            if (!a.CanCollide || !b.CanCollide)
            {
                return(false);
            }

            if ((a.CollisionMask & b.CollisionLayer) == 0x0 &&
                (b.CollisionMask & a.CollisionLayer) == 0x0)
            {
                return(false);
            }

            return(true);
        }
        private static bool TestMask(IPhysBody a, IPhysBody b)
        {
            if (a == b)
            {
                return(false);
            }

            if (!a.CollisionEnabled || !b.CollisionEnabled)
            {
                return(false);
            }

            if ((a.CollisionMask & b.CollisionLayer) == 0x0 &&
                (b.CollisionMask & a.CollisionLayer) == 0x0)
            {
                return(false);
            }

            return(true);
        }
        bool ICollideSpecial.PreventCollide(IPhysBody collided)
        {
            if (IsExiting(collided.Entity))
            {
                return(true);
            }
            if (!Owner.TryGetComponent(out IContainerManager manager))
            {
                return(false);
            }

            if (manager.ContainsEntity(collided.Entity))
            {
                if (!_intersecting.Contains(collided.Entity))
                {
                    _intersecting.Add(collided.Entity);
                }
                return(true);
            }
            return(false);
        }
Beispiel #22
0
        /// <summary>
        ///     Calculates the normal vector for two colliding bodies
        /// </summary>
        /// <param name="target"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static Vector2 CalculateNormal(IPhysBody target, IPhysBody source)
        {
            var manifold = target.WorldAABB.Intersect(source.WorldAABB);

            if (manifold.IsEmpty())
            {
                return(Vector2.Zero);
            }
            if (manifold.Height > manifold.Width)
            {
                // X is the axis of seperation
                var leftDist  = source.WorldAABB.Right - target.WorldAABB.Left;
                var rightDist = target.WorldAABB.Right - source.WorldAABB.Left;
                return(new Vector2(leftDist > rightDist ? 1 : -1, 0));
            }
            else
            {
                // Y is the axis of seperation
                var bottomDist = source.WorldAABB.Top - target.WorldAABB.Bottom;
                var topDist    = target.WorldAABB.Top - source.WorldAABB.Bottom;
                return(new Vector2(0, bottomDist > topDist ? 1 : -1));
            }
        }
Beispiel #23
0
        private bool TrySlip(SlipperyComponent component, IPhysBody ourBody, IPhysBody otherBody)
        {
            if (!component.Slippery ||
                component.Owner.IsInContainer() ||
                component.Slipped.Contains(otherBody.Owner.Uid) ||
                !otherBody.Owner.TryGetComponent(out SharedStunnableComponent? stun))
            {
                return(false);
            }

            if (otherBody.LinearVelocity.Length < component.RequiredSlipSpeed || stun.KnockedDown)
            {
                return(false);
            }

            var percentage = otherBody.GetWorldAABB().IntersectPercentage(ourBody.GetWorldAABB());

            if (percentage < component.IntersectPercentage)
            {
                return(false);
            }

            if (!EffectBlockerSystem.CanSlip(otherBody.Owner))
            {
                return(false);
            }

            otherBody.LinearVelocity *= component.LaunchForwardsMultiplier;

            stun.Paralyze(5);
            component.Slipped.Add(otherBody.Owner.Uid);
            component.Dirty();

            PlaySound(component);

            return(true);
        }
Beispiel #24
0
        /// <summary>
        ///     Tests a physBody against every other registered physBody.
        /// </summary>
        /// <param name="physBody">Body being tested.</param>
        /// <param name="colliderAABB">The AABB of the physBody being tested. This can be IPhysBody.WorldAABB, or a modified version of it.</param>
        /// <param name="results">An empty list that the function stores all colliding bodies inside of.</param>
        internal void DoCollisionTest(IPhysBody physBody, Box2 colliderAABB, List <IPhysBody> results)
        {
            foreach (var body in GetCollidablesForLocation(colliderAABB))
            {
                if (!body.CollisionEnabled)
                {
                    continue;
                }

                if ((physBody.CollisionMask & body.CollisionLayer) == 0x0)
                {
                    continue;
                }

                if (physBody.MapID != body.MapID ||
                    physBody == body ||
                    !colliderAABB.Intersects(body.WorldAABB))
                {
                    continue;
                }

                results.Add(body);
            }
        }
Beispiel #25
0
 void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
Beispiel #26
0
 public PreventCollideEvent(IPhysBody ourBody, IPhysBody otherBody)
 {
     BodyA = ourBody;
     BodyB = otherBody;
 }
Beispiel #27
0
        public async Task AirlockBlockTest()
        {
            await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes });

            var server = pairTracker.Pair.Server;

            await server.WaitIdleAsync();

            var mapManager    = server.ResolveDependency <IMapManager>();
            var entityManager = server.ResolveDependency <IEntityManager>();

            IPhysBody     physBody      = null;
            EntityUid     physicsDummy  = default;
            EntityUid     airlock       = default;
            DoorComponent doorComponent = null;

            var physicsDummyStartingX = -1;

            await server.WaitAssertion(() =>
            {
                var mapId = mapManager.CreateMap();

                var humanCoordinates = new MapCoordinates((physicsDummyStartingX, 0), mapId);
                physicsDummy         = entityManager.SpawnEntity("PhysicsDummy", humanCoordinates);

                airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates((0, 0), mapId));

                Assert.True(entityManager.TryGetComponent(physicsDummy, out physBody));

                Assert.True(entityManager.TryGetComponent(airlock, out doorComponent));
                Assert.That(doorComponent.State, Is.EqualTo(DoorState.Closed));
            });

            await server.WaitIdleAsync();

            // Push the human towards the airlock
            await server.WaitAssertion(() => Assert.That(physBody != null));

            await server.WaitPost(() => physBody.LinearVelocity = (0.5f, 0));

            for (var i = 0; i < 240; i += 10)
            {
                // Keep the airlock awake so they collide
                await server.WaitPost(() => entityManager.GetComponent <IPhysBody>(airlock).WakeBody());

                await server.WaitRunTicks(10);

                await server.WaitIdleAsync();
            }

            // Sanity check
            // Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor
            // If you see this yell at me in discord so I can continue to pretend this didn't happen.
            // REMINDER THAT I STILL HAVE TO FIX THIS TEST EVERY OTHER PHYSICS PR
            // Assert.That(physicsDummy.Transform.MapPosition.X, Is.GreaterThan(physicsDummyStartingX));

            // Blocked by the airlock
            await server.WaitAssertion(() => Assert.That(Math.Abs(entityManager.GetComponent <TransformComponent>(physicsDummy).MapPosition.X - 1) > 0.01f));

            await pairTracker.CleanReturnAsync();
        }
Beispiel #28
0
 public bool IsColliding(IPhysBody body, Vector2 offset, bool approximate)
 {
     return(GetCollidingEntities(body, offset, approximate).Any());
 }
Beispiel #29
0
        /// <summary>
        ///     Used for weightlessness to determine if we are near a wall.
        /// </summary>
        /// <param name="broadPhaseSystem"></param>
        /// <param name="transform"></param>
        /// <param name="mover"></param>
        /// <param name="collider"></param>
        /// <returns></returns>
        public static bool IsAroundCollider(SharedBroadphaseSystem broadPhaseSystem, ITransformComponent transform, IMobMoverComponent mover, IPhysBody collider)
        {
            var enlargedAABB = collider.GetWorldAABB().Enlarged(mover.GrabRange);

            foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB))
            {
                if (otherCollider == collider)
                {
                    continue;                            // Don't try to push off of yourself!
                }
                // Only allow pushing off of anchored things that have collision.
                if (otherCollider.BodyType != BodyType.Static ||
                    !otherCollider.CanCollide ||
                    ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 &&
                     (otherCollider.CollisionMask & collider.CollisionLayer) == 0) ||
                    (otherCollider.Owner.TryGetComponent(out SharedPullableComponent? pullable) && pullable.BeingPulled))
                {
                    continue;
                }

                return(true);
            }

            return(false);
        }
Beispiel #30
0
        public async Task AirlockBlockTest()
        {
            var options = new ServerContentIntegrationOption
            {
                ExtraPrototypes = Prototypes
            };
            var server = StartServer(options);

            await server.WaitIdleAsync();

            var mapManager    = server.ResolveDependency <IMapManager>();
            var entityManager = server.ResolveDependency <IEntityManager>();

            IPhysBody           physBody      = null;
            IEntity             physicsDummy  = null;
            IEntity             airlock       = null;
            ServerDoorComponent doorComponent = null;

            var physicsDummyStartingX = -1;

            server.Assert(() =>
            {
                var mapId = new MapId(1);
                mapManager.CreateNewMapEntity(mapId);

                var humanCoordinates = new MapCoordinates((physicsDummyStartingX, 0), mapId);
                physicsDummy         = entityManager.SpawnEntity("PhysicsDummy", humanCoordinates);

                airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates((0, 0), mapId));

                Assert.True(physicsDummy.TryGetComponent(out physBody));

                Assert.True(airlock.TryGetComponent(out doorComponent));
                Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed));
            });

            await server.WaitIdleAsync();

            // Push the human towards the airlock
            Assert.That(physBody != null);
            physBody.LinearVelocity = (0.5f, 0);

            for (var i = 0; i < 240; i += 10)
            {
                // Keep the airlock awake so they collide
                airlock.GetComponent <IPhysBody>().WakeBody();


                await server.WaitRunTicks(10);

                await server.WaitIdleAsync();
            }

            // Sanity check
            // Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor
            // If you see this yell at me in discord so I can continue to pretend this didn't happen.
            // Assert.That(physicsDummy.Transform.MapPosition.X, Is.GreaterThan(physicsDummyStartingX));

            // Blocked by the airlock
            Assert.That(Math.Abs(physicsDummy.Transform.MapPosition.X - 1) > 0.01f);
        }