diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs index 01ce0efaae..db889e7e3b 100644 --- a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs @@ -36,11 +36,4 @@ public sealed partial class PullableComponent : Component [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] [AutoNetworkedField, DataField] public bool PrevFixedRotation; - - /// - /// Whether the entity is currently being actively pushed by the puller. - /// If true, the entity will be able to enter disposals upon colliding with them, and the like. - /// - [DataField, AutoNetworkedField] - public bool BeingActivelyPushed = false; } diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index c13baa28ef..1fc9b731bd 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -1,6 +1,5 @@ using Content.Shared.Movement.Pulling.Systems; using Robust.Shared.GameStates; -using Robust.Shared.Map; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Movement.Pulling.Components; @@ -12,17 +11,16 @@ namespace Content.Shared.Movement.Pulling.Components; [Access(typeof(PullingSystem))] public sealed partial class PullerComponent : Component { + // My raiding guild /// - /// Next time the puller change where they are pulling the target towards. + /// Next time the puller can throw what is being pulled. + /// Used to avoid spamming it for infinite spin + velocity. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] - public TimeSpan NextPushTargetChange; - - [DataField, AutoNetworkedField] - public TimeSpan NextPushStop; + public TimeSpan NextThrow; [DataField] - public TimeSpan PushChangeCooldown = TimeSpan.FromSeconds(0.1f), PushDuration = TimeSpan.FromSeconds(2f); + public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1); // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f; @@ -30,38 +28,14 @@ public sealed partial class PullerComponent : Component public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f; /// - /// Entity currently being pulled/pushed if applicable. + /// Entity currently being pulled if applicable. /// [AutoNetworkedField, DataField] public EntityUid? Pulling; - /// - /// The position the entity is currently being pulled towards. - /// - [AutoNetworkedField, DataField] - public EntityCoordinates? PushingTowards; - /// /// Does this entity need hands to be able to pull something? /// [DataField] public bool NeedsHands = true; - - /// - /// The maximum acceleration of pushing, in meters per second squared. - /// - [DataField] - public float PushAcceleration = 0.3f; - - /// - /// The maximum speed to which the pulled entity may be accelerated relative to the push direction, in meters per second. - /// - [DataField] - public float MaxPushSpeed = 1.2f; - - /// - /// The maximum distance between the puller and the point towards which the puller may attempt to pull it, in meters. - /// - [DataField] - public float MaxPushRange = 2f; } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index e6acabc33c..3c265d5a02 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -8,19 +8,16 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Interaction; -using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Systems; -using Content.Shared.Projectiles; using Content.Shared.Pulling.Events; using Content.Shared.Throwing; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; using Robust.Shared.Map; -using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -37,7 +34,6 @@ public sealed class PullingSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly MovementSpeedModifierSystem _modifierSystem = default!; @@ -47,7 +43,7 @@ public sealed class PullingSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _xformSys = default!; - [Dependency] private readonly ThrownItemSystem _thrownItem = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; public override void Initialize() { @@ -61,9 +57,7 @@ public override void Initialize() SubscribeLocalEvent(OnJointRemoved); SubscribeLocalEvent>(AddPullVerbs); SubscribeLocalEvent(OnPullableContainerInsert); - SubscribeLocalEvent(OnPullableCollide); - SubscribeLocalEvent(OnPullerMoveInput); SubscribeLocalEvent(OnPullerContainerInsert); SubscribeLocalEvent(OnPullerUnpaused); SubscribeLocalEvent(OnVirtualItemDeleted); @@ -75,86 +69,6 @@ public override void Initialize() .Register(); } - public override void Shutdown() - { - base.Shutdown(); - CommandBinds.Unregister(); - } - - public override void Update(float frameTime) - { - if (_net.IsClient) // Client cannot predict this - return; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var puller, out var pullerComp, out var pullerPhysics, out var pullerXForm)) - { - // If not pulling, reset the pushing cooldowns and exit - if (pullerComp.Pulling is not { } pulled || !TryComp(pulled, out var pulledComp)) - { - pullerComp.PushingTowards = null; - pullerComp.NextPushTargetChange = TimeSpan.Zero; - continue; - } - - pulledComp.BeingActivelyPushed = false; // Temporarily set to false; if the checks below pass, it will be set to true again - - // If pulling but the pullee is invalid or is on a different map, stop pulling - var pulledXForm = Transform(pulled); - if (!TryComp(pulled, out var pulledPhysics) - || pulledPhysics.BodyType == BodyType.Static - || pulledXForm.MapUid != pullerXForm.MapUid) - { - StopPulling(pulled, pulledComp); - continue; - } - - if (pullerComp.PushingTowards is null) - continue; - - // If pushing but the target position is invalid, or the push action has expired or finished, stop pushing - if (pullerComp.NextPushStop < _timing.CurTime - || !(pullerComp.PushingTowards.Value.ToMap(EntityManager, _xformSys) is var pushCoordinates) - || pushCoordinates.MapId != pulledXForm.MapID) - { - pullerComp.PushingTowards = null; - pullerComp.NextPushTargetChange = TimeSpan.Zero; - continue; - } - - // Actual force calculation. All the Vector2's below are in map coordinates. - var desiredDeltaPos = pushCoordinates.Position - Transform(pulled).Coordinates.ToMapPos(EntityManager, _xformSys); - if (desiredDeltaPos.LengthSquared() < 0.1f) - { - pullerComp.PushingTowards = null; - continue; - } - - var velocityAndDirectionAngle = new Angle(pulledPhysics.LinearVelocity) - new Angle(desiredDeltaPos); - var currentRelativeSpeed = pulledPhysics.LinearVelocity.Length() * (float) Math.Cos(velocityAndDirectionAngle.Theta); - var desiredAcceleration = MathF.Max(0f, pullerComp.MaxPushSpeed - currentRelativeSpeed); - - var desiredImpulse = pulledPhysics.Mass * desiredDeltaPos; - var maxSourceImpulse = MathF.Min(pullerComp.PushAcceleration, desiredAcceleration) * pullerPhysics.Mass; - var actualImpulse = desiredImpulse.LengthSquared() > maxSourceImpulse * maxSourceImpulse ? desiredDeltaPos.Normalized() * maxSourceImpulse : desiredImpulse; - - // Ideally we'd want to apply forces instead of impulses, however... - // We cannot use ApplyForce here because it will be cleared on the next physics substep which will render it ultimately useless - // The alternative is to run this function on every physics substep, but that is way too expensive for such a minor system - _physics.ApplyLinearImpulse(pulled, actualImpulse); - _physics.ApplyLinearImpulse(puller, -actualImpulse); - pulledComp.BeingActivelyPushed = true; - } - query.Dispose(); - } - - private void OnPullerMoveInput(EntityUid uid, PullerComponent component, ref MoveInputEvent args) - { - // Stop pushing - component.PushingTowards = null; - component.NextPushStop = TimeSpan.Zero; - } - private void OnPullerContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) { if (ent.Comp.Pulling == null) return; @@ -170,26 +84,15 @@ private void OnPullableContainerInsert(Entity ent, ref EntGot TryStopPull(ent.Owner, ent.Comp); } - private void OnPullableCollide(Entity ent, ref StartCollideEvent args) + public override void Shutdown() { - if (!ent.Comp.BeingActivelyPushed || args.OtherEntity == ent.Comp.Puller) - return; - - // This component isn't actually needed anywhere besides the thrownitemsyste`m itself, so we just fake it - var fakeThrown = new ThrownItemComponent() - { - Owner = ent.Owner, - Animate = false, - Landed = false, - PlayLandSound = false, - Thrower = ent.Comp.Puller - }; - _thrownItem.ThrowCollideInteraction(fakeThrown, ent, args.OtherEntity); + base.Shutdown(); + CommandBinds.Unregister(); } private void OnPullerUnpaused(EntityUid uid, PullerComponent component, ref EntityUnpausedEvent args) { - component.NextPushTargetChange += args.PausedTime; + component.NextThrow += args.PausedTime; } private void OnVirtualItemDeleted(EntityUid uid, PullerComponent component, VirtualItemDeletedEvent args) @@ -331,22 +234,31 @@ public bool IsPulled(EntityUid uid, PullableComponent? component = null) private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) { - if (session?.AttachedEntity is not { } player - || !player.IsValid() - || !TryComp(player, out var pullerComp)) + if (session?.AttachedEntity is not { } player || + !player.IsValid()) + { + return false; + } + + if (!TryComp(player, out var pullerComp)) return false; var pulled = pullerComp.Pulling; - if (!HasComp(pulled) - || _containerSystem.IsEntityInContainer(player) - || _timing.CurTime < pullerComp.NextPushTargetChange) + + if (!HasComp(pulled)) + return false; + + if (_containerSystem.IsEntityInContainer(player)) return false; - pullerComp.NextPushTargetChange = _timing.CurTime + pullerComp.PushChangeCooldown; - pullerComp.NextPushStop = _timing.CurTime + pullerComp.PushDuration; + // Cooldown buddy + if (_timing.CurTime < pullerComp.NextThrow) + return false; + + pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown; // Cap the distance - var range = pullerComp.MaxPushRange; + const float range = 2f; var fromUserCoords = coords.WithEntityId(player, EntityManager); var userCoords = new EntityCoordinates(player, Vector2.Zero); @@ -356,9 +268,8 @@ private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinate fromUserCoords = userCoords.Offset(userDirection.Normalized() * range); } - pullerComp.PushingTowards = fromUserCoords; Dirty(player, pullerComp); - + _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false, doSpin: false); return false; } diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index e0300fa503..6c9ec2049f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -25,8 +25,6 @@ - type: GhostHearing - type: Hands - type: Puller - pushAcceleration: 1000000 # Will still be capped in max speed - maxPushRange: 20 - type: CombatMode - type: Physics ignorePaused: true