* Content side new physics structure

* BroadPhase outline done

* But we need to fix WorldAABB

* Fix static pvs AABB

* Fix import

* Rando fixes

* B is for balloon

* Change human mob hitbox to circle

* Decent movement

* Start adding friction to player controller

I think it's the best way to go about it to keep other objects somewhat consistent for physics.

* This baby can fit so many physics bugs in it.

* Slight mob mover optimisations.

* Player mover kinda works okay.

* Beginnings of testbed

* More testbed

* Circlestack bed

* Namespaces

* BB fixes

* Pull WorldAABB

* Joint pulling

* Semi-decent movement I guess.

* Pulling better

* Bullet controller + old movement

* im too dumb for this shit

* Use kinematic mob controller again

It's probably for the best TBH

* Stashed shitcode

* Remove SlipController

* In which movement code is entirely refactored

* Singularity fix

* Fix ApplyLinearImpulse

* MoveRelay fix

* Fix door collisions

* Disable subfloor collisions

Saves on broadphase a fair bit

* Re-implement ClimbController

* Zumzum's pressure

* Laggy item throwing

* Minor atmos change

* Some caching

* Optimise controllers

* Optimise CollideWith to hell and back

* Re-do throwing and tile friction

* Landing too

* Optimise controllers

* Move CCVars and other stuff swept is beautiful

* Cleanup a bunch of controllers

* Fix shooting and high pressure movement controller

* Flashing improvements

* Stuff and things

* Combat collisions

* Combat mode collisions

* Pulling distance joint again

* Cleanup physics interfaces

* More like scuffedularity

* Shit's fucked

* Haha tests go green

* Bigmoneycrab

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2021-03-01 03:11:29 +11:00
committed by GitHub
parent 9deee05279
commit 3e64fd56a1
211 changed files with 2602 additions and 2562 deletions

View File

@@ -166,6 +166,15 @@ namespace Content.Shared
public static readonly CVarDef<bool> ParallaxDebug =
CVarDef.Create("parallax.debug", false, CVar.CLIENTONLY);
/*
* Physics
*/
public static readonly CVarDef<float> TileFrictionModifier =
CVarDef.Create("physics.tilefriction", 15.0f);
public static readonly CVarDef<float> StopSpeed =
CVarDef.Create("physics.stopspeed", 0.1f);
/*
* Ambience

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Serialization;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.Construction.ConstructionConditions
{
@@ -34,7 +35,7 @@ namespace Content.Shared.Construction.ConstructionConditions
return false;
// now we need to check that user actually tries to build wallmount on a wall
var physics = IoCManager.Resolve<IPhysicsManager>();
var physics = EntitySystem.Get<SharedBroadPhaseSystem>();
var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int) CollisionGroup.Impassable);
var length = userToObject.Length;
var userToObjRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rUserToObj, maxLength: length,

View File

@@ -18,7 +18,7 @@ namespace Content.Shared.GameObjects.Components.Buckle
public sealed override uint? NetID => ContentNetIDs.BUCKLE;
[ComponentDependency] protected readonly IPhysicsComponent? Physics;
[ComponentDependency] protected readonly IPhysBody? Physics;
/// <summary>
/// The range from which this entity can buckle to a <see cref="SharedStrapComponent"/>.

View File

@@ -20,8 +20,8 @@ namespace Content.Shared.GameObjects.Components.Disposal
[ViewVariables]
public bool Anchored =>
!Owner.TryGetComponent(out IPhysicsComponent? physics) ||
physics.Anchored;
!Owner.TryGetComponent(out IPhysBody? physics) ||
physics.BodyType == BodyType.Static;
[Serializable, NetSerializable]
public enum Visuals
@@ -166,7 +166,7 @@ namespace Content.Shared.GameObjects.Components.Disposal
if (!Anchored)
return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide)
{
if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) {

View File

@@ -19,7 +19,7 @@ namespace Content.Shared.GameObjects.Components.Doors
protected readonly SharedAppearanceComponent? AppearanceComponent = null;
[ComponentDependency]
protected readonly IPhysicsComponent? PhysicsComponent = null;
protected readonly IPhysBody? PhysicsComponent = null;
[ViewVariables]
private DoorState _state = DoorState.Closed;

View File

@@ -21,8 +21,22 @@ namespace Content.Shared.GameObjects.Components.Mobs
get => _isInCombatMode;
set
{
if (_isInCombatMode == value) return;
_isInCombatMode = value;
Dirty();
// Regenerate physics contacts -> Can probably just selectively check
/* Still a bit jank so left disabled for now.
if (Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
{
if (value)
{
physicsComponent.WakeBody();
}
physicsComponent.RegenerateContacts();
}
*/
}
}
@@ -32,6 +46,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
get => _activeZone;
set
{
if (_activeZone == value) return;
_activeZone = value;
Dirty();
}

View File

@@ -0,0 +1,15 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.GameObjects.Components.Movement
{
public interface IMobMoverComponent : IComponent
{
EntityCoordinates LastPosition { get; set; }
public float StepSoundDistance { get; set; }
float GrabRange { get; set; }
}
}

View File

@@ -19,17 +19,6 @@ namespace Content.Shared.GameObjects.Components.Movement
/// </summary>
float CurrentSprintSpeed { get; }
/// <summary>
/// The movement speed (m/s) of the entity when it pushes off of a solid object in zero gravity.
/// </summary>
float CurrentPushSpeed { get; }
/// <summary>
/// How far an entity can reach (in meters) to grab hold of a solid object in zero gravity.
/// </summary>
float GrabRange { get; }
/// <summary>
/// Is the entity Sprinting (running)?
/// </summary>
@@ -40,10 +29,6 @@ namespace Content.Shared.GameObjects.Components.Movement
/// </summary>
(Vector2 walking, Vector2 sprinting) VelocityDir { get; }
EntityCoordinates LastPosition { get; set; }
float StepSoundDistance { get; set; }
/// <summary>
/// Toggles one of the four cardinal directions. Each of the four directions are
/// composed into a single direction vector, <see cref="SharedPlayerInputMoverComponent.VelocityDir"/>. Enabling
@@ -55,6 +40,5 @@ namespace Content.Shared.GameObjects.Components.Movement
void SetVelocityDirection(Direction direction, ushort subTick, bool enabled);
void SetSprinting(ushort subTick, bool walking);
}
}

View File

@@ -3,64 +3,89 @@ using System;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Movement
{
public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial
public abstract class SharedClimbingComponent : Component, IActionBlocker
{
public sealed override string Name => "Climbing";
public sealed override uint? NetID => ContentNetIDs.CLIMBING;
protected IPhysicsComponent? Body;
protected bool IsOnClimbableThisFrame;
protected bool OwnerIsTransitioning
protected bool IsOnClimbableThisFrame
{
get
{
if (Body != null && Body.TryGetController<ClimbController>(out var controller))
if (Body == null) return false;
foreach (var entity in Body.GetBodiesIntersecting())
{
return controller.IsActive;
if ((entity.CollisionLayer & (int) CollisionGroup.VaultImpassable) != 0) return true;
}
return false;
}
}
public abstract bool IsClimbing { get; set; }
bool IActionBlocker.CanMove() => !OwnerIsTransitioning;
bool IActionBlocker.CanChangeDirection() => !OwnerIsTransitioning;
bool ICollideSpecial.PreventCollide(IPhysBody collided)
[ViewVariables]
protected virtual bool OwnerIsTransitioning { get; set; }
[ComponentDependency] protected PhysicsComponent? Body;
protected TimeSpan StartClimbTime = TimeSpan.Zero;
/// <summary>
/// We'll launch the mob onto the table and give them at least this amount of time to be on it.
/// </summary>
protected const float BufferTime = 0.3f;
public virtual bool IsClimbing
{
if (((CollisionGroup)collided.CollisionLayer).HasFlag(CollisionGroup.VaultImpassable) && collided.Entity.HasComponent<IClimbable>())
get => _isClimbing;
set
{
IsOnClimbableThisFrame = true;
return IsClimbing;
}
if (_isClimbing == value) return;
_isClimbing = value;
return false;
ToggleVaultPassable(value);
}
}
public override void Initialize()
{
base.Initialize();
protected bool _isClimbing;
Owner.TryGetComponent(out Body);
// TODO: Layers need a re-work
private void ToggleVaultPassable(bool value)
{
// Hope the mob has one fixture
if (Body == null || Body.Deleted) return;
foreach (var fixture in Body.Fixtures)
{
if (value)
{
fixture.CollisionMask &= ~(int) CollisionGroup.VaultImpassable;
}
else
{
fixture.CollisionMask |= (int) CollisionGroup.VaultImpassable;
}
}
}
[Serializable, NetSerializable]
protected sealed class ClimbModeComponentState : ComponentState
{
public ClimbModeComponentState(bool climbing) : base(ContentNetIDs.CLIMBING)
public ClimbModeComponentState(bool climbing, bool isTransitioning) : base(ContentNetIDs.CLIMBING)
{
Climbing = climbing;
IsTransitioning = isTransitioning;
}
public bool Climbing { get; }
public bool IsTransitioning { get; }
}
}
}

View File

@@ -10,14 +10,12 @@ namespace Content.Shared.GameObjects.Components.Movement
public class SharedDummyInputMoverComponent : Component, IMoverComponent
{
public override string Name => "DummyInputMover";
public bool IgnorePaused => false;
public float CurrentWalkSpeed => 0f;
public float CurrentSprintSpeed => 0f;
public float CurrentPushSpeed => 0f;
public float GrabRange => 0f;
public bool Sprinting => false;
public (Vector2 walking, Vector2 sprinting) VelocityDir => (Vector2.Zero, Vector2.Zero);
public EntityCoordinates LastPosition { get; set; }
public float StepSoundDistance { get; set; }
public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled)
{

View File

@@ -5,7 +5,6 @@ using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
@@ -15,7 +14,9 @@ using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Movement
{
public abstract class SharedPlayerInputMoverComponent : Component, IMoverComponent, ICollideSpecial
[RegisterComponent]
[ComponentReference(typeof(IMoverComponent))]
public class SharedPlayerInputMoverComponent : Component, IMoverComponent
{
// This class has to be able to handle server TPS being lower than client FPS.
// While still having perfectly responsive movement client side.
@@ -39,8 +40,10 @@ namespace Content.Shared.GameObjects.Components.Movement
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public sealed override string Name => "PlayerInputMover";
public sealed override uint? NetID => ContentNetIDs.PLAYER_INPUT_MOVER;
[ComponentDependency] private readonly MovementSpeedModifierComponent? _movementSpeed = default!;
public override string Name => "PlayerInputMover";
public override uint? NetID => ContentNetIDs.PLAYER_INPUT_MOVER;
private GameTick _lastInputTick;
private ushort _lastInputSubTick;
@@ -49,36 +52,19 @@ namespace Content.Shared.GameObjects.Components.Movement
private MoveButtons _heldMoveButtons = MoveButtons.None;
public float CurrentWalkSpeed
// Don't serialize because it probably shouldn't change and it's a waste.
public bool IgnorePaused
{
get
{
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component))
{
return component.CurrentWalkSpeed;
}
return MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
}
get => _ignorePaused;
set => _ignorePaused = value;
}
public float CurrentSprintSpeed
{
get
{
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component))
{
return component.CurrentSprintSpeed;
}
private bool _ignorePaused;
return MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
}
}
public float CurrentWalkSpeed => _movementSpeed?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
public float CurrentSprintSpeed => _movementSpeed?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
[ViewVariables(VVAccess.ReadWrite)]
public float CurrentPushSpeed => 5;
[ViewVariables(VVAccess.ReadWrite)]
public float GrabRange => 0.2f;
public bool Sprinting => !HasFlag(_heldMoveButtons, MoveButtons.Walk);
/// <summary>
@@ -131,27 +117,24 @@ namespace Content.Shared.GameObjects.Components.Movement
}
}
public abstract EntityCoordinates LastPosition { get; set; }
public abstract float StepSoundDistance { get; set; }
/// <summary>
/// Whether or not the player can move diagonally.
/// </summary>
[ViewVariables]
public bool DiagonalMovementEnabled => _configurationManager.GetCVar<bool>(CCVars.GameDiagonalMovement);
/// <inheritdoc />
public override void OnAdd()
public override void ExposeData(ObjectSerializer serializer)
{
// This component requires that the entity has a IPhysicsComponent.
if (!Owner.HasComponent<IPhysicsComponent>())
Logger.Error(
$"[ECS] {Owner.Prototype?.Name} - {nameof(SharedPlayerInputMoverComponent)} requires" +
$" {nameof(IPhysicsComponent)}. ");
base.OnAdd();
base.ExposeData(serializer);
serializer.DataField(ref _ignorePaused, "ignorePaused", false);
}
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
Owner.EnsureComponentWarn<PhysicsComponent>();
}
/// <summary>
/// Toggles one of the four cardinal directions. Each of the four directions are
@@ -266,12 +249,6 @@ namespace Content.Shared.GameObjects.Components.Movement
return vec;
}
bool ICollideSpecial.PreventCollide(IPhysBody collidedWith)
{
// Don't collide with other mobs
return collidedWith.Entity.HasComponent<IBody>();
}
[Serializable, NetSerializable]
private sealed class MoverComponentState : ComponentState
{

View File

@@ -0,0 +1,100 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Movement
{
/// <summary>
/// The basic player mover with footsteps and grabbing
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IMobMoverComponent))]
public class SharedPlayerMobMoverComponent : Component, IMobMoverComponent, ICollideSpecial
{
public override string Name => "PlayerMobMover";
public override uint? NetID => ContentNetIDs.PLAYER_MOB_MOVER;
private float _stepSoundDistance;
private float _grabRange;
[ViewVariables(VVAccess.ReadWrite)]
public EntityCoordinates LastPosition { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public float StepSoundDistance
{
get => _stepSoundDistance;
set
{
if (MathHelper.CloseTo(_stepSoundDistance, value)) return;
_stepSoundDistance = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public float GrabRange
{
get => _grabRange;
set
{
if (MathHelper.CloseTo(_grabRange, value)) return;
_grabRange = value;
Dirty();
}
}
public override void Initialize()
{
base.Initialize();
if (!Owner.HasComponent<IMoverComponent>())
{
Owner.EnsureComponentWarn<SharedPlayerInputMoverComponent>();
}
}
public override ComponentState GetComponentState(ICommonSession session)
{
return new PlayerMobMoverComponentState(StepSoundDistance, GrabRange);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not PlayerMobMoverComponentState playerMoverState) return;
StepSoundDistance = playerMoverState.StepSoundDistance;
GrabRange = playerMoverState.GrabRange;
}
bool ICollideSpecial.PreventCollide(IPhysBody collidedWith)
{
// Don't collide with other mobs
// unless they have combat mode on
return collidedWith.Entity.HasComponent<IBody>(); /* &&
(!Owner.TryGetComponent(out SharedCombatModeComponent? ownerCombat) || !ownerCombat.IsInCombatMode) &&
(!collidedWith.Entity.TryGetComponent(out SharedCombatModeComponent? otherCombat) || !otherCombat.IsInCombatMode);
*/
}
[Serializable, NetSerializable]
private sealed class PlayerMobMoverComponentState : ComponentState
{
public float StepSoundDistance;
public float GrabRange;
public PlayerMobMoverComponentState(float stepSoundDistance, float grabRange) : base(ContentNetIDs.PLAYER_MOB_MOVER)
{
StepSoundDistance = stepSoundDistance;
GrabRange = grabRange;
}
}
}
}

View File

@@ -3,10 +3,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
using Content.Shared.Physics;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
@@ -53,14 +55,12 @@ namespace Content.Shared.GameObjects.Components.Movement
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool Slippery { get; set; }
private bool TrySlip(IEntity entity)
private bool TrySlip(IPhysBody ourBody, IPhysBody otherBody)
{
if (!Slippery
|| Owner.IsInContainer()
|| _slipped.Contains(entity.Uid)
|| !entity.TryGetComponent(out SharedStunnableComponent? stun)
|| !entity.TryGetComponent(out IPhysicsComponent? otherBody)
|| !Owner.TryGetComponent(out IPhysicsComponent? body))
|| _slipped.Contains(otherBody.Entity.Uid)
|| !otherBody.Entity.TryGetComponent(out SharedStunnableComponent? stun))
{
return false;
}
@@ -70,26 +70,22 @@ namespace Content.Shared.GameObjects.Components.Movement
return false;
}
var percentage = otherBody.WorldAABB.IntersectPercentage(body.WorldAABB);
var percentage = otherBody.GetWorldAABB().IntersectPercentage(ourBody.GetWorldAABB());
if (percentage < IntersectPercentage)
{
return false;
}
if (!EffectBlockerSystem.CanSlip(entity))
if (!EffectBlockerSystem.CanSlip(otherBody.Entity))
{
return false;
}
if (entity.TryGetComponent(out IPhysicsComponent? physics))
{
var controller = physics.EnsureController<SlipController>();
controller.LinearVelocity = physics.LinearVelocity * LaunchForwardsMultiplier;
}
otherBody.LinearVelocity *= LaunchForwardsMultiplier;
stun.Paralyze(5);
_slipped.Add(entity.Uid);
_slipped.Add(otherBody.Entity.Uid);
OnSlip();
@@ -98,9 +94,9 @@ namespace Content.Shared.GameObjects.Components.Movement
protected virtual void OnSlip() { }
public void CollideWith(IEntity collidedWith)
public void CollideWith(IPhysBody ourBody, IPhysBody otherBody)
{
TrySlip(collidedWith);
TrySlip(ourBody, otherBody);
}
public void Update()
@@ -114,10 +110,10 @@ namespace Content.Shared.GameObjects.Components.Movement
}
var entity = Owner.EntityManager.GetEntity(uid);
var physics = Owner.GetComponent<IPhysicsComponent>();
var otherPhysics = entity.GetComponent<IPhysicsComponent>();
var physics = Owner.GetComponent<IPhysBody>();
var otherPhysics = entity.GetComponent<IPhysBody>();
if (!physics.WorldAABB.Intersects(otherPhysics.WorldAABB))
if (!physics.GetWorldAABB().Intersects(otherPhysics.GetWorldAABB()))
{
_slipped.Remove(uid);
}
@@ -132,12 +128,12 @@ namespace Content.Shared.GameObjects.Components.Movement
physics.Hard = false;
var shape = physics.PhysicsShapes.FirstOrDefault();
var fixtures = physics.Fixtures.FirstOrDefault();
if (shape != null)
if (fixtures != null)
{
shape.CollisionLayer |= (int) CollisionGroup.SmallImpassable;
shape.CollisionMask = (int) CollisionGroup.None;
fixtures.CollisionLayer |= (int) CollisionGroup.SmallImpassable;
fixtures.CollisionMask = (int) CollisionGroup.None;
}
}

View File

@@ -0,0 +1,62 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Movement
{
[RegisterComponent]
public class SharedTileFrictionModifier : Component
{
public override string Name => "TileFrictionModifier";
/// <summary>
/// Multiply the tilefriction cvar by this to get the body's actual tilefriction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float Modifier
{
get => _modifier;
set
{
if (MathHelper.CloseTo(_modifier, value)) return;
_modifier = value;
}
}
private float _modifier;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, x => x.Modifier, "modifier", 1.0f);
}
public override ComponentState GetComponentState(ICommonSession session)
{
return new TileFrictionComponentState(_modifier);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not TileFrictionComponentState tileState) return;
_modifier = tileState.Modifier;
}
[NetSerializable, Serializable]
protected class TileFrictionComponentState : ComponentState
{
public float Modifier;
public TileFrictionComponentState(float modifier) : base(ContentNetIDs.TILE_FRICTION)
{
Modifier = modifier;
}
}
}
}

View File

@@ -1,6 +1,7 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Portal
@@ -13,7 +14,7 @@ namespace Content.Shared.GameObjects.Components.Portal
{
base.OnAdd();
if (Owner.TryGetComponent<IPhysicsComponent>(out var physics))
if (Owner.TryGetComponent<IPhysBody>(out var physics))
{
physics.Hard = false;
}

View File

@@ -19,6 +19,8 @@ namespace Content.Shared.GameObjects.Components.Projectiles
get => _ignoreShooter;
set
{
if (_ignoreShooter == value) return;
_ignoreShooter = value;
Dirty();
}

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Physics.Dynamics.Joints;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Pulling
@@ -20,14 +21,16 @@ namespace Content.Shared.GameObjects.Components.Pulling
public override string Name => "Pullable";
public override uint? NetID => ContentNetIDs.PULLABLE;
[ComponentDependency] private readonly IPhysicsComponent? _physics = default!;
[ComponentDependency] private readonly PhysicsComponent? _physics = default!;
/// <summary>
/// Only set in Puller->set! Only set in unison with _pullerPhysics!
/// </summary>
private IEntity? _puller;
private IPhysicsComponent? _pullerPhysics;
public IPhysicsComponent? PullerPhysics => _pullerPhysics;
private IPhysBody? _pullerPhysics;
public IPhysBody? PullerPhysics => _pullerPhysics;
private DistanceJoint? _pullJoint = null;
/// <summary>
/// The current entity pulling this component.
@@ -62,7 +65,6 @@ namespace Content.Shared.GameObjects.Components.Pulling
oldPuller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
_physics.WakeBody();
_physics.TryRemoveController<PullController>();
}
// else-branch warning is handled below
}
@@ -70,7 +72,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
// Now that is settled, prepare to be pulled by a new object.
if (_physics == null)
{
Logger.WarningS("c.go.c.pulling", "Well now you've done it, haven't you? SharedPullableComponent on {0} didn't have an IPhysicsComponent.", Owner);
Logger.WarningS("c.go.c.pulling", "Well now you've done it, haven't you? SharedPullableComponent on {0} didn't have an IPhysBody.", Owner);
return;
}
@@ -83,7 +85,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
return;
}
if (!value.TryGetComponent<IPhysicsComponent>(out var valuePhysics))
if (!value.TryGetComponent<PhysicsComponent>(out var pullerPhysics))
{
return;
}
@@ -113,7 +115,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
// Continue with pulling process.
var pullAttempt = new PullAttemptMessage(valuePhysics, _physics);
var pullAttempt = new PullAttemptMessage(pullerPhysics, _physics);
value.SendMessage(null, pullAttempt);
@@ -133,9 +135,8 @@ namespace Content.Shared.GameObjects.Components.Pulling
_puller = value;
Dirty();
_pullerPhysics = valuePhysics;
_pullerPhysics = pullerPhysics;
_physics.EnsureController<PullController>().Manager = this;
var message = new PullStartedMessage(_pullerPhysics, _physics);
_puller.SendMessage(null, message);
@@ -143,7 +144,13 @@ namespace Content.Shared.GameObjects.Components.Pulling
_puller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
_physics.WakeBody();
_pullJoint = pullerPhysics.CreateDistanceJoint(_physics);
// _physics.BodyType = BodyType.Kinematic; // TODO: Need to consider their original bodytype
_pullJoint.CollideConnected = true;
_pullJoint.Length = 1.4f;
_pullJoint.MaxLength = 2.0f; // TODO hacky, we should consider ours and their bb
}
// Code here will not run if pulling a new object was attempted and failed because of the returns from the refactor.
}
@@ -212,6 +219,12 @@ namespace Content.Shared.GameObjects.Components.Pulling
return false;
}
if (_physics != null && _pullJoint != null)
{
_physics.RemoveJoint(_pullJoint);
}
_pullJoint = null;
Puller = null;
return true;
}
@@ -246,12 +259,16 @@ namespace Content.Shared.GameObjects.Components.Pulling
return false;
}
/*
if (!_physics.TryGetController(out PullController controller))
{
return false;
}
*/
return controller.TryMoveTo(Puller.Transform.Coordinates, to);
return true;
//return controller.TryMoveTo(Puller.Transform.Coordinates, to);
}
public override ComponentState GetComponentState(ICommonSession player)

View File

@@ -14,8 +14,8 @@ namespace Content.Shared.GameObjects
public const uint STORAGE = 1005;
public const uint INVENTORY = 1006;
public const uint POWER_DEBUG_TOOL = 1007;
// 1008
// 1009
public const uint PLAYER_MOB_MOVER = 1008;
public const uint TILE_FRICTION = 1009;
public const uint RANGED_WEAPON = 1010;
public const uint CAMERA_RECOIL = 1011;
public const uint SOUND = 1012;
@@ -60,7 +60,7 @@ namespace Content.Shared.GameObjects
public const uint FLASHABLE = 1051;
public const uint BUCKLE = 1052;
public const uint PROJECTILE = 1053;
public const uint THROWN_ITEM = 1054;
// 1054
public const uint STRAP = 1055;
public const uint DISPOSABLE = 1056;
public const uint GAS_ANALYZER = 1057;

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.GameObjects.EntitySystems
{
@@ -18,8 +19,6 @@ namespace Content.Shared.GameObjects.EntitySystems
[UsedImplicitly]
public class SharedInteractionSystem : EntitySystem
{
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
public const float InteractionRange = 2;
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
@@ -49,7 +48,7 @@ namespace Content.Shared.GameObjects.EntitySystems
predicate ??= _ => false;
var ray = new CollisionRay(origin.Position, dir.Normalized, collisionMask);
var rayResults = _physicsManager.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
if (rayResults.Count == 0) return dir.Length;
return (rayResults[0].HitPos - origin.Position).Length;
@@ -125,7 +124,7 @@ namespace Content.Shared.GameObjects.EntitySystems
predicate ??= _ => false;
var ray = new CollisionRay(origin.Position, dir.Normalized, (int) collisionMask);
var rayResults = _physicsManager.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
if (rayResults.Count == 0) return true;
@@ -133,12 +132,12 @@ namespace Content.Shared.GameObjects.EntitySystems
foreach (var result in rayResults)
{
if (!result.HitEntity.TryGetComponent(out IPhysicsComponent p))
if (!result.HitEntity.TryGetComponent(out IPhysBody p))
{
continue;
}
var bBox = p.WorldAABB;
var bBox = p.GetWorldAABB();
if (bBox.Contains(origin.Position) || bBox.Contains(other.Position))
{

View File

@@ -1,10 +1,11 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Physics;
using Content.Shared.Physics.Pull;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
@@ -15,11 +16,11 @@ using Robust.Shared.Players;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedMoverSystem : EntitySystem
/// <summary>
/// Handles converting inputs into movement.
/// </summary>
public sealed class SharedMoverSystem : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] protected readonly IPhysicsManager PhysicsManager = default!;
public override void Initialize()
{
base.Initialize();
@@ -45,109 +46,9 @@ namespace Content.Shared.GameObjects.EntitySystems
base.Shutdown();
}
//TODO: reorganize this to make more logical sense
protected void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, IPhysicsComponent physics)
{
physics.EnsureController<MoverController>();
var weightless = transform.Owner.IsWeightless();
if (weightless)
{
// No gravity: is our entity touching anything?
var touching = IsAroundCollider(transform, mover, physics);
if (!touching)
{
transform.LocalRotation = physics.LinearVelocity.GetDir().ToAngle();
return;
}
}
// TODO: movement check.
var (walkDir, sprintDir) = mover.VelocityDir;
var combined = walkDir + sprintDir;
if (combined.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner) && !weightless)
{
if (physics.TryGetController(out MoverController controller))
{
controller.StopMoving();
}
}
else if (ActionBlockerSystem.CanMove(mover.Owner))
{
if (weightless)
{
if (physics.TryGetController(out MoverController controller))
{
controller.Push(combined, mover.CurrentPushSpeed);
}
transform.LocalRotation = physics.LinearVelocity.GetDir().ToAngle();
return;
}
var total = walkDir * mover.CurrentWalkSpeed + sprintDir * mover.CurrentSprintSpeed;
{
if (physics.TryGetController(out MoverController controller))
{
controller.Move(total, 1);
}
}
transform.LocalRotation = total.GetDir().ToAngle();
HandleFootsteps(mover);
}
}
protected virtual void HandleFootsteps(IMoverComponent mover)
{
}
private bool IsAroundCollider(ITransformComponent transform, IMoverComponent mover,
IPhysicsComponent collider)
{
foreach (var entity in _entityManager.GetEntitiesInRange(transform.Owner, mover.GrabRange, true))
{
if (entity == transform.Owner)
{
continue; // Don't try to push off of yourself!
}
if (!entity.TryGetComponent<IPhysicsComponent>(out var otherCollider) ||
!otherCollider.CanCollide ||
(collider.CollisionMask & otherCollider.CollisionLayer) == 0)
{
continue;
}
// Don't count pulled entities
if (otherCollider.HasController<PullController>())
{
continue;
}
// TODO: Item check.
var touching = ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0
|| (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) // Ensure collision
&& !entity.HasComponent<IItemComponent>(); // This can't be an item
if (touching)
{
return true;
}
}
return false;
}
private static void HandleDirChange(ICommonSession? session, Direction dir, ushort subTick, bool state)
{
if (!TryGetAttachedComponent<IMoverComponent>(session, out var moverComp))
if (!TryGetAttachedComponent<SharedPlayerInputMoverComponent>(session, out var moverComp))
return;
var owner = session?.AttachedEntity;
@@ -158,6 +59,15 @@ namespace Content.Shared.GameObjects.EntitySystems
{
comp.MoveInputPressed(session);
}
// For stuff like "Moving out of locker" or the likes
if (owner.IsInContainer() &&
(!owner.TryGetComponent(out IMobStateComponent? mobState) ||
mobState.IsAlive()))
{
var relayEntityMoveMessage = new RelayMovementEntityMessage(owner);
owner.Transform.Parent!.Owner.SendMessage(owner.Transform, relayEntityMoveMessage);
}
}
moverComp.SetVelocityDirection(dir, subTick, state);

View File

@@ -8,6 +8,7 @@ using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Players;
namespace Content.Shared.GameObjects.EntitySystems
@@ -58,7 +59,7 @@ namespace Content.Shared.GameObjects.EntitySystems
return;
}
if (!pulled.TryGetComponent(out IPhysicsComponent? physics))
if (!pulled.TryGetComponent(out IPhysBody? physics))
{
return;
}

View File

@@ -18,7 +18,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
/// <summary>
/// The entity that threw <see cref="Thrown"/> and hit <see cref="Target"/>.
/// </summary>
public IEntity User { get; }
public IEntity? User { get; }
/// <summary>
/// The entity thrown by <see cref="User"/> that hit <see cref="Target"/>
@@ -29,14 +29,12 @@ namespace Content.Shared.Interfaces.GameObjects.Components
/// The entity hit with <see cref="Thrown"/> by <see cref="User"/>
/// </summary>
public IEntity Target { get; }
public EntityCoordinates Location { get; }
public ThrowCollideEventArgs(IEntity user, IEntity thrown, IEntity target, EntityCoordinates location)
public ThrowCollideEventArgs(IEntity? user, IEntity thrown, IEntity target)
{
User = user;
Thrown = thrown;
Target = target;
Location = location;
}
}
@@ -50,7 +48,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
/// <summary>
/// The entity that threw <see cref="Thrown"/>.
/// </summary>
public IEntity User { get; }
public IEntity? User { get; }
/// <summary>
/// The entity thrown by <see cref="User"/> that hit <see cref="Target"/>
@@ -61,14 +59,12 @@ namespace Content.Shared.Interfaces.GameObjects.Components
/// The entity hit with <see cref="Thrown"/> by <see cref="User"/>
/// </summary>
public IEntity Target { get; }
public EntityCoordinates Location { get; }
public ThrowCollideMessage(IEntity user, IEntity thrown, IEntity target, EntityCoordinates location)
public ThrowCollideMessage(IEntity? user, IEntity thrown, IEntity target)
{
User = user;
Thrown = thrown;
Target = target;
Location = location;
}
}
}

View File

@@ -10,6 +10,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.Maps
{
@@ -181,7 +182,7 @@ namespace Content.Shared.Maps
/// </summary>
public static bool IsBlockedTurf(this TileRef turf, bool filterMobs)
{
var physics = IoCManager.Resolve<IPhysicsManager>();
var physics = EntitySystem.Get<SharedBroadPhaseSystem>();
var worldBox = GetWorldTileBox(turf);

View File

@@ -1,17 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class BulletController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -1,89 +0,0 @@
#nullable enable
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
/// <summary>
/// Movement controller used by the climb system. Lerps the player from A to B.
/// Also does checks to make sure the player isn't blocked.
/// </summary>
public class ClimbController : VirtualController
{
private Vector2? _movingTo = null;
private Vector2 _lastKnownPosition = default;
private int _numTicksBlocked = 0;
/// <summary>
/// If 5 ticks have passed and our position has not changed then something is blocking us.
/// </summary>
public bool IsBlocked => _numTicksBlocked > 5 || _isMovingWrongDirection;
/// <summary>
/// If the controller is currently moving the player somewhere, it is considered active.
/// </summary>
public bool IsActive => _movingTo.HasValue;
private float _initialDist = default;
private bool _isMovingWrongDirection = false;
public void TryMoveTo(Vector2 from, Vector2 to)
{
if (ControlledComponent == null)
{
return;
}
_initialDist = (from - to).Length;
_numTicksBlocked = 0;
_lastKnownPosition = from;
_movingTo = to;
_isMovingWrongDirection = false;
}
public override void UpdateAfterProcessing()
{
base.UpdateAfterProcessing();
if (ControlledComponent == null || _movingTo == null)
{
return;
}
ControlledComponent.WakeBody();
if ((ControlledComponent.Owner.Transform.WorldPosition - _lastKnownPosition).Length <= 0.05f)
{
_numTicksBlocked++;
}
else
{
_numTicksBlocked = 0;
}
_lastKnownPosition = ControlledComponent.Owner.Transform.WorldPosition;
if ((ControlledComponent.Owner.Transform.WorldPosition - _movingTo.Value).Length <= 0.1f)
{
_movingTo = null;
}
if (_movingTo.HasValue)
{
var dist = (_lastKnownPosition - _movingTo.Value).Length;
if (dist > _initialDist)
{
_isMovingWrongDirection = true;
}
var diff = _movingTo.Value - ControlledComponent.Owner.Transform.WorldPosition;
LinearVelocity = diff.Normalized * 5;
}
else
{
LinearVelocity = Vector2.Zero;
}
}
}
}

View File

@@ -2,6 +2,7 @@
using System;
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization;
using RobustPhysics = Robust.Shared.Physics;
@@ -11,7 +12,7 @@ namespace Content.Shared.Physics
/// Defined collision groups for the physics system.
/// </summary>
[Flags, PublicAPI]
[FlagsFor(typeof(RobustPhysics.CollisionLayer)), FlagsFor(typeof(RobustPhysics.CollisionMask))]
[FlagsFor(typeof(CollisionLayer)), FlagsFor(typeof(CollisionMask))]
public enum CollisionGroup
{
None = 0,

View File

@@ -1,10 +0,0 @@
#nullable enable
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class ContainmentFieldCollisionController : VirtualController
{
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
using Robust.Shared.Maths;
namespace Content.Shared.Physics
{
public class ContainmentFieldRepellController : FrictionController
{
public void Repell(Direction dir, float speed)
{
LinearVelocity = dir.ToVec() * speed;
}
}
}

View File

@@ -0,0 +1,142 @@
#nullable enable
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Pulling;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Physics.Controllers;
namespace Content.Shared.Physics.Controllers
{
/// <summary>
/// Handles player and NPC mob movement.
/// NPCs are handled server-side only.
/// </summary>
public abstract class SharedMoverController : VirtualController
{
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
public override void Initialize()
{
base.Initialize();
_broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
}
/// <summary>
/// A generic kinematic mover for entities.
/// </summary>
protected void HandleKinematicMovement(IMoverComponent mover, PhysicsComponent physicsComponent)
{
var (walkDir, sprintDir) = mover.VelocityDir;
// Regular movement.
// Target velocity.
var total = (walkDir * mover.CurrentWalkSpeed + sprintDir * mover.CurrentSprintSpeed);
if (total != Vector2.Zero)
{
mover.Owner.Transform.LocalRotation = total.GetDir().ToAngle();
}
physicsComponent.LinearVelocity = total;
}
/// <summary>
/// Movement while considering actionblockers, weightlessness, etc.
/// </summary>
/// <param name="mover"></param>
/// <param name="physicsComponent"></param>
/// <param name="mobMover"></param>
protected void HandleMobMovement(IMoverComponent mover, PhysicsComponent physicsComponent, IMobMoverComponent mobMover)
{
// TODO: Look at https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/CharacterControllers.html?highlight=controller as it has some adviceo n kinematic controllersx
if (!UseMobMovement(_broadPhaseSystem, physicsComponent, _physicsManager))
{
return;
}
var transform = mover.Owner.Transform;
var (walkDir, sprintDir) = mover.VelocityDir;
var weightless = transform.Owner.IsWeightless(_physicsManager);
// Handle wall-pushes.
if (weightless)
{
// No gravity: is our entity touching anything?
var touching = IsAroundCollider(_broadPhaseSystem, transform, mobMover, physicsComponent);
if (!touching)
{
transform.LocalRotation = physicsComponent.LinearVelocity.GetDir().ToAngle();
return;
}
}
// Regular movement.
// Target velocity.
var total = (walkDir * mover.CurrentWalkSpeed + sprintDir * mover.CurrentSprintSpeed);
if (total != Vector2.Zero)
{
// This should have its event run during island solver soooo
transform.DeferUpdates = true;
transform.LocalRotation = total.GetDir().ToAngle();
HandleFootsteps(mover, mobMover);
}
physicsComponent.LinearVelocity = total;
}
public static bool UseMobMovement(SharedBroadPhaseSystem broadPhaseSystem, PhysicsComponent body, IPhysicsManager? physicsManager = null)
{
return (body.BodyStatus == BodyStatus.OnGround) &
body.Owner.HasComponent<IMobStateComponent>() &&
ActionBlockerSystem.CanMove(body.Owner) &&
(!body.Owner.IsWeightless(physicsManager) ||
body.Owner.TryGetComponent(out SharedPlayerMobMoverComponent? mover) &&
IsAroundCollider(broadPhaseSystem, body.Owner.Transform, mover, body));
}
/// <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.Entity.TryGetComponent(out SharedPullableComponent? pullable) && pullable.BeingPulled))
{
continue;
}
return true;
}
return false;
}
// TODO: Need a predicted client version that only plays for our own entity and then have server-side ignore our session (for that entity only)
protected virtual void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover) {}
}
}

View File

@@ -0,0 +1,103 @@
using System;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using JetBrains.Annotations;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Dynamics;
#nullable enable
namespace Content.Shared.Physics.Controllers
{
public sealed class SharedTileFrictionController : VirtualController
{
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
private float _stopSpeed;
private float _frictionModifier;
public override void Initialize()
{
base.Initialize();
_broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
_frictionModifier = _configManager.GetCVar(CCVars.TileFrictionModifier);
_configManager.OnValueChanged(CCVars.TileFrictionModifier, value => _frictionModifier = value);
_stopSpeed = _configManager.GetCVar(CCVars.StopSpeed);
_configManager.OnValueChanged(CCVars.StopSpeed, value => _stopSpeed = value);
}
public override void UpdateBeforeMapSolve(bool prediction, PhysicsMap map, float frameTime)
{
base.UpdateBeforeMapSolve(prediction, map, frameTime);
foreach (var body in map.AwakeBodies)
{
var speed = body.LinearVelocity.Length;
if (speed <= 0.0f || body.BodyStatus == BodyStatus.InAir) continue;
// This is the *actual* amount that speed will drop by, we just do some multiplication around it to be easier.
var drop = 0.0f;
float control;
// Only apply friction when it's not a mob (or the mob doesn't have control).
if (SharedMoverController.UseMobMovement(_broadPhaseSystem, body, _physicsManager)) continue;
var surfaceFriction = GetTileFriction(body);
var bodyModifier = body.Owner.GetComponentOrNull<SharedTileFrictionModifier>()?.Modifier ?? 1.0f;
var friction = _frictionModifier * surfaceFriction * bodyModifier;
if (friction > 0.0f)
{
// TBH I can't really tell if this makes a difference, player movement is fucking hard.
if (!prediction)
{
control = speed < _stopSpeed ? _stopSpeed : speed;
}
else
{
control = speed;
}
drop += control * friction * frameTime;
}
var newSpeed = MathF.Max(0.0f, speed - drop);
newSpeed /= speed;
body.LinearVelocity *= newSpeed;
}
}
[Pure]
private float GetTileFriction(IPhysBody body)
{
if (body.BodyStatus == BodyStatus.InAir || body.Entity.Transform.GridID == GridId.Invalid)
return 0.0f;
var transform = body.Owner.Transform;
var coords = transform.Coordinates;
var grid = _mapManager.GetGrid(coords.GetGridId(body.Owner.EntityManager));
var tile = grid.GetTileRef(coords);
var tileDef = _tileDefinitionManager[tile.Tile.TypeId];
return tileDef.Friction;
}
}
}

View File

@@ -1,62 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class ConveyedController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Move(Vector2 velocityDirection, float speed, Vector2 itemRelativeToConveyor)
{
if (ControlledComponent?.Owner.IsWeightless() ?? false)
{
return;
}
if (ControlledComponent?.Status == BodyStatus.InAir)
{
return;
}
LinearVelocity = velocityDirection * speed;
//gravitating item towards center
//http://csharphelper.com/blog/2016/09/find-the-shortest-distance-between-a-point-and-a-line-segment-in-c/
Vector2 centerPoint;
var t = 0f;
if (velocityDirection.Length > 0) //if velocitydirection is 0, this calculation will divide by 0
{
t = Vector2.Dot(itemRelativeToConveyor, velocityDirection) /
Vector2.Dot(velocityDirection, velocityDirection);
}
if (t < 0)
{
centerPoint = new Vector2();
}
else if(t > 1)
{
centerPoint = velocityDirection;
}
else
{
centerPoint = velocityDirection * t;
}
var delta = centerPoint - itemRelativeToConveyor;
LinearVelocity += delta * (4 * delta.Length);
}
public override void UpdateAfterProcessing()
{
base.UpdateAfterProcessing();
LinearVelocity = Vector2.Zero;
}
}
}

View File

@@ -1,24 +0,0 @@
#nullable enable
using System;
using Robust.Shared.IoC;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public abstract class FrictionController : VirtualController
{
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
public override void UpdateAfterProcessing()
{
base.UpdateAfterProcessing();
if (ControlledComponent != null && !_physicsManager.IsWeightless(ControlledComponent.Owner.Transform.Coordinates))
{
LinearVelocity *= 0.85f;
if (MathF.Abs(LinearVelocity.Length) < 1f)
Stop();
}
}
}
}

View File

@@ -1,33 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class MoverController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Move(Vector2 velocityDirection, float speed)
{
if (ControlledComponent?.Owner.IsWeightless() ?? false)
{
return;
}
Push(velocityDirection, speed);
}
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
public void StopMoving()
{
LinearVelocity = Vector2.Zero;
}
}
}

View File

@@ -1,11 +1,11 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Shared.Physics.Pull
{
public class PullAttemptMessage : PullMessage
{
public PullAttemptMessage(IPhysicsComponent puller, IPhysicsComponent pulled) : base(puller, pulled) { }
public PullAttemptMessage(IPhysBody puller, IPhysBody pulled) : base(puller, pulled) { }
public bool Cancelled { get; set; }
}

View File

@@ -1,190 +0,0 @@
#nullable enable
using System;
using System.Linq;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Content.Shared.GameObjects.Components.Pulling;
using static Content.Shared.GameObjects.EntitySystems.SharedInteractionSystem;
namespace Content.Shared.Physics.Pull
{
/// <summary>
/// This is applied upon a Pullable object when that object is being pulled.
/// It lives only to serve that Pullable object.
/// </summary>
public class PullController : VirtualController
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
private const float DistBeforeStopPull = InteractionRange;
private const float StopMoveThreshold = 0.25f;
/// <summary>
/// The managing SharedPullableComponent of this PullController.
/// MUST BE SET! If you go attaching PullControllers yourself, YOU ARE DOING IT WRONG.
/// If you get a crash based on such, then, well, see previous note.
/// This is set by the SharedPullableComponent attaching the PullController.
/// </summary>
public SharedPullableComponent Manager = default!;
private EntityCoordinates? _movingTo;
public EntityCoordinates? MovingTo
{
get => _movingTo;
set
{
if (_movingTo == value || ControlledComponent == null)
{
return;
}
_movingTo = value;
ControlledComponent.WakeBody();
}
}
private bool PullerMovingTowardsPulled()
{
var _puller = Manager.PullerPhysics;
if (_puller == null)
{
return false;
}
if (ControlledComponent == null)
{
return false;
}
if (_puller.LinearVelocity.EqualsApprox(Vector2.Zero))
{
return false;
}
var pullerTransform = _puller.Owner.Transform;
var origin = pullerTransform.Coordinates.Position;
var velocity = _puller.LinearVelocity.Normalized;
var mapId = pullerTransform.MapPosition.MapId;
var ray = new CollisionRay(origin, velocity, (int) CollisionGroup.AllMask);
bool Predicate(IEntity e) => e != ControlledComponent.Owner;
var rayResults =
_physicsManager.IntersectRayWithPredicate(mapId, ray, DistBeforeStopPull, Predicate);
return rayResults.Any();
}
public bool TryMoveTo(EntityCoordinates from, EntityCoordinates to)
{
var _puller = Manager.PullerPhysics;
if (_puller == null || ControlledComponent == null)
{
return false;
}
if (!_puller.Owner.Transform.Coordinates.InRange(_entityManager, from, InteractionRange))
{
return false;
}
if (!_puller.Owner.Transform.Coordinates.InRange(_entityManager, to, InteractionRange))
{
return false;
}
if (!from.InRange(_entityManager, to, InteractionRange))
{
return false;
}
if (from.Position.EqualsApprox(to.Position))
{
return false;
}
if (!_puller.Owner.Transform.Coordinates.TryDistance(_entityManager, to, out var distance) ||
Math.Sqrt(distance) > DistBeforeStopPull ||
Math.Sqrt(distance) < StopMoveThreshold)
{
return false;
}
MovingTo = to;
return true;
}
public override void UpdateBeforeProcessing()
{
var _puller = Manager.PullerPhysics;
if (_puller == null || ControlledComponent == null)
{
return;
}
if (!_puller.Owner.IsInSameOrNoContainer(ControlledComponent.Owner))
{
Manager.Puller = null;
return;
}
var distance = _puller.Owner.Transform.WorldPosition - ControlledComponent.Owner.Transform.WorldPosition;
if (distance.Length > DistBeforeStopPull)
{
Manager.Puller = null;
}
else if (MovingTo.HasValue)
{
var diff = MovingTo.Value.Position - ControlledComponent.Owner.Transform.Coordinates.Position;
LinearVelocity = diff.Normalized * 5;
}
else
{
if (PullerMovingTowardsPulled())
{
LinearVelocity = Vector2.Zero;
return;
}
var distanceAbs = Vector2.Abs(distance);
var totalAabb = _puller.AABB.Size + ControlledComponent.AABB.Size / 2;
if (distanceAbs.X < totalAabb.X && distanceAbs.Y < totalAabb.Y)
{
LinearVelocity = Vector2.Zero;
return;
}
LinearVelocity = distance.Normalized * _puller.LinearVelocity.Length * 1.5f;
}
}
public override void UpdateAfterProcessing()
{
base.UpdateAfterProcessing();
if (ControlledComponent == null)
{
MovingTo = null;
return;
}
if (MovingTo != null &&
ControlledComponent.Owner.Transform.Coordinates.Position.EqualsApprox(MovingTo.Value.Position, 0.01))
{
MovingTo = null;
}
if (LinearVelocity != Vector2.Zero)
{
var angle = LinearVelocity.ToAngle();
ControlledComponent.Owner.Transform.LocalRotation = angle;
}
}
}
}

View File

@@ -1,14 +1,15 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Shared.Physics.Pull
{
public class PullMessage : ComponentMessage
{
public readonly IPhysicsComponent Puller;
public readonly IPhysicsComponent Pulled;
public readonly IPhysBody Puller;
public readonly IPhysBody Pulled;
protected PullMessage(IPhysicsComponent puller, IPhysicsComponent pulled)
protected PullMessage(IPhysBody puller, IPhysBody pulled)
{
Puller = puller;
Pulled = pulled;

View File

@@ -1,11 +1,11 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Shared.Physics.Pull
{
public class PullStartedMessage : PullMessage
{
public PullStartedMessage(IPhysicsComponent puller, IPhysicsComponent pulled) :
public PullStartedMessage(IPhysBody puller, IPhysBody pulled) :
base(puller, pulled)
{
}

View File

@@ -1,11 +1,11 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Shared.Physics.Pull
{
public class PullStoppedMessage : PullMessage
{
public PullStoppedMessage(IPhysicsComponent puller, IPhysicsComponent pulled) : base(puller, pulled)
public PullStoppedMessage(IPhysBody puller, IPhysBody pulled) : base(puller, pulled)
{
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class ShuttleController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -1,18 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class SingularityController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -1,22 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Singularity
{
public class SingularityPullController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void StopPull()
{
LinearVelocity = Vector2.Zero;
}
public void Pull(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -1,44 +0,0 @@
#nullable enable
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class SlipController : VirtualController
{
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
public SlipController()
{
IoCManager.InjectDependencies(this);
}
private float Decay { get; set; } = 0.95f;
public override void UpdateAfterProcessing()
{
if (ControlledComponent == null)
{
return;
}
if (_physicsManager.IsWeightless(ControlledComponent.Owner.Transform.Coordinates))
{
if (ControlledComponent.IsColliding(Vector2.Zero, false))
{
Stop();
}
return;
}
LinearVelocity *= Decay;
if (LinearVelocity.Length < 0.001)
{
Stop();
}
}
}
}

View File

@@ -1,50 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class ThrowKnockbackController : VirtualController
{
public ThrowKnockbackController()
{
IoCManager.InjectDependencies(this);
}
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
private float Decay { get; set; } = 0.95f;
public override void UpdateAfterProcessing()
{
if (ControlledComponent == null)
{
return;
}
if (ControlledComponent.Owner.IsWeightless())
{
if (ActionBlockerSystem.CanMove(ControlledComponent.Owner)
&& ControlledComponent.IsColliding(Vector2.Zero, false))
{
Stop();
}
return;
}
LinearVelocity *= Decay;
if (LinearVelocity.Length < 0.001)
{
Stop();
}
}
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class ThrownController : VirtualController
{
public override IPhysicsComponent? ControlledComponent { protected get; set; }
public void Push(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -1,14 +0,0 @@
#nullable enable
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Shared.Physics
{
public class VaporController : VirtualController
{
public void Move(Vector2 velocityDirection, float speed)
{
LinearVelocity = velocityDirection * speed;
}
}
}

View File

@@ -6,6 +6,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.Utility
{
@@ -17,9 +18,9 @@ namespace Content.Shared.Utility
EntityCoordinates coordinates,
CollisionGroup collisionLayer,
in Box2? box = null,
IPhysicsManager? physicsManager = null)
SharedBroadPhaseSystem? physicsManager = null)
{
physicsManager ??= IoCManager.Resolve<IPhysicsManager>();
physicsManager ??= EntitySystem.Get<SharedBroadPhaseSystem>();
var mapCoordinates = coordinates.ToMap(entityManager);
return entityManager.SpawnIfUnobstructed(prototypeName, mapCoordinates, collisionLayer, box, physicsManager);
@@ -31,18 +32,19 @@ namespace Content.Shared.Utility
MapCoordinates coordinates,
CollisionGroup collisionLayer,
in Box2? box = null,
IPhysicsManager? physicsManager = null)
SharedBroadPhaseSystem? collision = null)
{
var boxOrDefault = box.GetValueOrDefault(Box2.UnitCentered);
physicsManager ??= IoCManager.Resolve<IPhysicsManager>();
collision ??= EntitySystem.Get<SharedBroadPhaseSystem>();
foreach (var body in physicsManager.GetCollidingEntities(coordinates.MapId, in boxOrDefault))
foreach (var body in collision.GetCollidingEntities(coordinates.MapId, in boxOrDefault))
{
if (!body.Hard)
{
continue;
}
// TODO: wtf fix this
if (collisionLayer == 0 || (body.CollisionMask & (int) collisionLayer) == 0)
{
continue;
@@ -61,7 +63,7 @@ namespace Content.Shared.Utility
CollisionGroup collisionLayer,
[NotNullWhen(true)] out IEntity? entity,
Box2? box = null,
IPhysicsManager? physicsManager = null)
SharedBroadPhaseSystem? physicsManager = null)
{
entity = entityManager.SpawnIfUnobstructed(prototypeName, coordinates, collisionLayer, box, physicsManager);
@@ -75,7 +77,7 @@ namespace Content.Shared.Utility
CollisionGroup collisionLayer,
[NotNullWhen(true)] out IEntity? entity,
in Box2? box = null,
IPhysicsManager? physicsManager = null)
SharedBroadPhaseSystem? physicsManager = null)
{
entity = entityManager.SpawnIfUnobstructed(prototypeName, coordinates, collisionLayer, box, physicsManager);