/// <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); } }
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); } } }
/// <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}"); } }
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); }
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); }
/// <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}"); } }
/// <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); }
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}"); } }
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); }
/// <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)); } }
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); }
/// <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); } }
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
public PreventCollideEvent(IPhysBody ourBody, IPhysBody otherBody) { BodyA = ourBody; BodyB = otherBody; }
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(); }
public bool IsColliding(IPhysBody body, Vector2 offset, bool approximate) { return(GetCollidingEntities(body, offset, approximate).Any()); }
/// <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); }
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); }