From 0bc3c517070ba81ae88e4249d520f4d850c0ebc1 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:54:05 +1000 Subject: [PATCH] Contact QOL stuff (#5385) * Contact QOL stuff Only just made the enumerator version need to test with AI branch had an IEnumerable before. * Fix --- .../Physics/Dynamics/Contacts/Contact.cs | 23 +++++ .../Physics/Systems/FixtureSystem.cs | 1 + .../Physics/Systems/SharedBroadphaseSystem.cs | 2 +- .../Systems/SharedPhysicsSystem.Contacts.cs | 93 +++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs b/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs index 6bb2f8e8779..e588fc93952 100644 --- a/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs +++ b/Robust.Shared/Physics/Dynamics/Contacts/Contact.cs @@ -349,6 +349,29 @@ public override int GetHashCode() // TODO: Need to suss this out return HashCode.Combine(EntityA, EntityB); } + + /// + /// Gets the other ent for this contact. + /// + public EntityUid OtherEnt(EntityUid uid) + { + if (uid == EntityA) + return EntityB; + else if (uid == EntityB) + return EntityA; + + throw new InvalidOperationException(); + } + + public (string Id, Fixture) OtherFixture(EntityUid uid) + { + if (uid == EntityA) + return (FixtureBId, FixtureB!); + else if (uid == EntityB) + return (FixtureAId, FixtureA!); + + throw new InvalidOperationException(); + } } [Flags] diff --git a/Robust.Shared/Physics/Systems/FixtureSystem.cs b/Robust.Shared/Physics/Systems/FixtureSystem.cs index c07c6831f08..ac22c96cc6a 100644 --- a/Robust.Shared/Physics/Systems/FixtureSystem.cs +++ b/Robust.Shared/Physics/Systems/FixtureSystem.cs @@ -116,6 +116,7 @@ internal void CreateFixture( // Don't need to ResetMassData as FixtureUpdate already does it. Dirty(uid, manager); } + // TODO: Set newcontacts to true. } diff --git a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs index ba1a80e029c..c57b0fe1a30 100644 --- a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs +++ b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs @@ -416,7 +416,7 @@ public void RegenerateContacts(EntityUid uid, PhysicsComponent body, FixturesCom } } - private void TouchProxies(EntityUid mapId, Matrix3x2 broadphaseMatrix, Fixture fixture) + internal void TouchProxies(EntityUid mapId, Matrix3x2 broadphaseMatrix, Fixture fixture) { foreach (var proxy in fixture.Proxies) { diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs index e98839a50c6..f7cee57fa47 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs @@ -30,7 +30,10 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Numerics; +using JetBrains.Annotations; using Microsoft.Extensions.ObjectPool; using Robust.Shared.GameObjects; using Robust.Shared.Maths; @@ -741,6 +744,96 @@ protected bool ShouldCollide( return true; } + + /// + /// Will destroy all contacts and queue for rebuild. + /// Useful if you have one that may no longer be relevant and don't want to destroy it directly. + /// + public void RegenerateContacts(Entity entity) + { + if (!PhysicsQuery.Resolve(entity.Owner, ref entity.Comp)) + return; + + _broadphase.RegenerateContacts(entity.Owner, entity.Comp); + } + + /// + /// Returns the number of touching contacts this entity has. + /// + /// Fixture we should ignore if applicable + [Pure] + public int GetTouchingContacts(Entity entity, string? ignoredFixtureId = null) + { + if (!_fixturesQuery.Resolve(entity.Owner, ref entity.Comp)) + return 0; + + var count = 0; + + foreach (var (id, fixture) in entity.Comp.Fixtures) + { + if (ignoredFixtureId == id) + continue; + + foreach (var contact in fixture.Contacts.Values) + { + if (!contact.IsTouching) + continue; + + count++; + } + } + + return count; + } + + /// + /// Returns all of this entity's contacts. + /// + [Pure] + public ContactEnumerator GetContacts(Entity entity) + { + _fixturesQuery.Resolve(entity.Owner, ref entity.Comp); + return new ContactEnumerator(entity.Comp); + } +} + +public record struct ContactEnumerator +{ + public static readonly ContactEnumerator Empty = new(null); + + private Dictionary.ValueCollection.Enumerator _fixtureEnumerator; + private Dictionary.ValueCollection.Enumerator _contactEnumerator; + + public ContactEnumerator(FixturesComponent? fixtures) + { + if (fixtures == null || fixtures.Fixtures.Count == 0) + { + this = Empty; + return; + } + + _fixtureEnumerator = fixtures.Fixtures.Values.GetEnumerator(); + _fixtureEnumerator.MoveNext(); + _contactEnumerator = _fixtureEnumerator.Current.Contacts.Values.GetEnumerator(); + } + + public bool MoveNext([NotNullWhen(true)] out Contact? contact) + { + if (!_contactEnumerator.MoveNext()) + { + if (!_fixtureEnumerator.MoveNext()) + { + contact = null; + return false; + } + + _contactEnumerator = _fixtureEnumerator.Current.Contacts.Values.GetEnumerator(); + return MoveNext(out contact); + } + + contact = _contactEnumerator.Current; + return true; + } } internal enum ContactStatus : byte