Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mob collisions #34580

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
66 changes: 66 additions & 0 deletions Content.Client/Movement/Systems/MobCollisionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Numerics;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Client.Player;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Timing;

namespace Content.Client.Movement.Systems;

public sealed class MobCollisionSystem : SharedMobCollisionSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;

public override void Update(float frameTime)
{
base.Update(frameTime);

if (!_timing.IsFirstTimePredicted)
return;

var player = _player.LocalEntity;

if (!HasComp<MobCollisionComponent>(player) || !TryComp(player, out PhysicsComponent? physics))
return;

if (physics.ContactCount == 0)
return;

var ourTransform = _physics.GetPhysicsTransform(player.Value);
var contacts = _physics.GetContacts(player.Value);
var direction = Vector2.Zero;

while (contacts.MoveNext(out var contact))
{
if (!contact.IsTouching)
continue;

var other = contact.OtherEnt(player.Value);

if (!HasComp<MobCollisionComponent>(other))
continue;

// TODO: Get overlap amount
var otherTransform = _physics.GetRelativePhysicsTransform(ourTransform, other);

var diff = otherTransform.Position;

var penDepth = MathF.Max(0f, 0.7f - diff.LengthSquared());

// Need the push strength proportional to penetration depth.
direction += penDepth * diff.Normalized() * 1f * frameTime;
}

if (direction == Vector2.Zero)
return;

RaisePredictiveEvent(new MobCollisionMessage()
{
Direction = direction,
});
}
}
8 changes: 8 additions & 0 deletions Content.Server/Movement/Systems/MobCollisionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Movement.Systems;

namespace Content.Server.Movement.Systems;

public sealed class MobCollisionSystem : SharedMobCollisionSystem
{

}
14 changes: 14 additions & 0 deletions Content.Shared/Movement/Components/MobCollisionComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Robust.Shared.GameStates;
using Robust.Shared.Physics.Collision.Shapes;

namespace Content.Shared.Movement.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class MobCollisionComponent : Component
{
/// <summary>
/// Shape to give this entity for mob collisions.
/// </summary>
[DataField, AutoNetworkedField]
public IPhysShape Shape = new PhysShapeCircle(radius: 0.35f);
}
48 changes: 48 additions & 0 deletions Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Numerics;
using Content.Shared.Movement.Components;
using Content.Shared.Physics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Serialization;

namespace Content.Shared.Movement.Systems;

public abstract class SharedMobCollisionSystem : EntitySystem
{
[Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;

public override void Initialize()
{
base.Initialize();
SubscribeAllEvent<MobCollisionMessage>(OnCollision);
SubscribeLocalEvent<MobCollisionComponent, ComponentStartup>(OnCollisionStartup);
}

private void OnCollisionStartup(Entity<MobCollisionComponent> ent, ref ComponentStartup args)
{
_fixtures.TryCreateFixture(ent.Owner,
ent.Comp.Shape,
"mob_collision",
hard: false,
collisionLayer: (int) CollisionGroup.MidImpassable,
collisionMask: (int) CollisionGroup.MidImpassable);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should layer/mask be configurable on the component, so flying mobs could ignore ground mobs but still collide with other flying mobs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using flammable as it's supposed to be a generic non-hard event subscriber though still not entirely happy with it.

If I ever get around to my planned collision layer refactor I might revisit it or if we have mobs that don't have flammable that also need pushing.

}

private void OnCollision(MobCollisionMessage msg, EntitySessionEventArgs args)
{
var player = args.SenderSession.AttachedEntity;

if (!HasComp<MobCollisionComponent>(player) || !TryComp(player.Value, out TransformComponent? xform))
return;

// TODO: Validation
_xformSystem.SetLocalPosition(player.Value, xform.LocalPosition + msg.Direction);
}

[Serializable, NetSerializable]
protected sealed class MobCollisionMessage : EntityEventArgs
{
public Vector2 Direction;
}
}
Loading