diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 903fd1ff3f..41f582cde8 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -15,10 +15,12 @@ using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Stacks; +using Content.Shared.Standing; using Content.Shared.Throwing; using Robust.Shared.GameStates; using Robust.Shared.Input.Binding; using Robust.Shared.Map; +using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -53,6 +55,8 @@ namespace Content.Server.Hands.Systems SubscribeLocalEvent(OnExploded); + SubscribeLocalEvent(OnDropHandItems); + CommandBinds.Builder .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem)) .Register(); @@ -228,6 +232,36 @@ namespace Content.Server.Hands.Systems return true; } + private void OnDropHandItems(Entity entity, ref DropHandItemsEvent args) + { + var direction = EntityManager.TryGetComponent(entity, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero; + var dropAngle = _random.NextFloat(0.8f, 1.2f); + + var fellEvent = new FellDownEvent(entity); + RaiseLocalEvent(entity, fellEvent, false); + + var worldRotation = TransformSystem.GetWorldRotation(entity).ToVec(); + foreach (var hand in entity.Comp.Hands.Values) + { + if (hand.HeldEntity is not EntityUid held) + continue; + + var throwAttempt = new FellDownThrowAttemptEvent(entity); + RaiseLocalEvent(hand.HeldEntity.Value, ref throwAttempt); + + if (throwAttempt.Cancelled) + continue; + + if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp)) + continue; + + _throwingSystem.TryThrow(held, + _random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50), + 0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f), + entity, 0); + } + } + #endregion } } diff --git a/Content.Server/Standing/StandingStateSystem.cs b/Content.Server/Standing/StandingStateSystem.cs deleted file mode 100644 index bf9a4e4bea..0000000000 --- a/Content.Server/Standing/StandingStateSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Numerics; -using Content.Shared.Hands.Components; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Standing; -using Content.Shared.Throwing; -using Robust.Shared.Physics.Components; -using Robust.Shared.Random; - -namespace Content.Server.Standing; - -public sealed class StandingStateSystem : EntitySystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly ThrowingSystem _throwingSystem = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - - private void FallOver(EntityUid uid, StandingStateComponent component, DropHandItemsEvent args) - { - var direction = EntityManager.TryGetComponent(uid, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero; - var dropAngle = _random.NextFloat(0.8f, 1.2f); - - var fellEvent = new FellDownEvent(uid); - RaiseLocalEvent(uid, fellEvent, false); - - if (!TryComp(uid, out HandsComponent? handsComp)) - return; - - var worldRotation = _transformSystem.GetWorldRotation(uid).ToVec(); - foreach (var hand in handsComp.Hands.Values) - { - if (hand.HeldEntity is not EntityUid held) - continue; - - if (!_handsSystem.TryDrop(uid, hand, null, checkActionBlocker: false, handsComp: handsComp)) - continue; - - _throwingSystem.TryThrow(held, - _random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50), - 0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f), - uid, 0); - } - } - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(FallOver); - } - -} - - /// - /// Raised after an entity falls down. - /// - public sealed class FellDownEvent : EntityEventArgs - { - public EntityUid Uid { get; } - public FellDownEvent(EntityUid uid) - { - Uid = uid; - } - } diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 8d9be9ab77..c534f47955 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,172 +1,191 @@ using Content.Shared.Hands.Components; using Content.Shared.Physics; using Content.Shared.Rotation; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; -namespace Content.Shared.Standing +namespace Content.Shared.Standing; + +public sealed class StandingStateSystem : EntitySystem { - public sealed class StandingStateSystem : EntitySystem + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + + // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. + private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; + + public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + if (!Resolve(uid, ref standingState, false)) + return false; - // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. - private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; + return !standingState.Standing; + } - public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) - { - if (!Resolve(uid, ref standingState, false)) - return false; + public bool Down(EntityUid uid, + bool playSound = true, + bool dropHeldItems = true, + bool force = false, + StandingStateComponent? standingState = null, + AppearanceComponent? appearance = null, + HandsComponent? hands = null) + { + // TODO: This should actually log missing comps... + if (!Resolve(uid, ref standingState, false)) + return false; - return !standingState.Standing; - } - - public bool Down(EntityUid uid, - bool playSound = true, - bool dropHeldItems = true, - bool force = false, - StandingStateComponent? standingState = null, - AppearanceComponent? appearance = null, - HandsComponent? hands = null) - { - // TODO: This should actually log missing comps... - if (!Resolve(uid, ref standingState, false)) - return false; - - // Optional component. - Resolve(uid, ref appearance, ref hands, false); - - if (!standingState.Standing) - return true; - - // This is just to avoid most callers doing this manually saving boilerplate - // 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to. - // We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway - // and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent. - if (dropHeldItems && hands != null) - { - RaiseLocalEvent(uid, new DropHandItemsEvent(), false); - } - - if (!force) - { - var msg = new DownAttemptEvent(); - RaiseLocalEvent(uid, msg, false); - - if (msg.Cancelled) - return false; - } - - standingState.Standing = false; - Dirty(uid, standingState); - RaiseLocalEvent(uid, new DownedEvent(), false); - - // Seemed like the best place to put it - _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); - - // Change collision masks to allow going under certain entities like flaps and tables - if (TryComp(uid, out FixturesComponent? fixtureComponent)) - { - foreach (var (key, fixture) in fixtureComponent.Fixtures) - { - if ((fixture.CollisionMask & StandingCollisionLayer) == 0) - continue; - - standingState.ChangedFixtures.Add(key); - _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent); - } - } - - // check if component was just added or streamed to client - // if true, no need to play sound - mob was down before player could seen that - if (standingState.LifeStage <= ComponentLifeStage.Starting) - return true; - - if (playSound) - { - _audio.PlayPredicted(standingState.DownSound, uid, uid); - } + // Optional component. + Resolve(uid, ref appearance, ref hands, false); + if (!standingState.Standing) return true; - } - public bool Stand(EntityUid uid, - StandingStateComponent? standingState = null, - AppearanceComponent? appearance = null, - bool force = false) + // This is just to avoid most callers doing this manually saving boilerplate + // 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to. + // We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway + // and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent. + if (dropHeldItems && hands != null) { - // TODO: This should actually log missing comps... - if (!Resolve(uid, ref standingState, false)) - return false; - - // Optional component. - Resolve(uid, ref appearance, false); - - if (standingState.Standing) - return true; - - if (!force) - { - var msg = new StandAttemptEvent(); - RaiseLocalEvent(uid, msg, false); - - if (msg.Cancelled) - return false; - } - - standingState.Standing = true; - Dirty(uid, standingState); - RaiseLocalEvent(uid, new StoodEvent(), false); - - _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); - - if (TryComp(uid, out FixturesComponent? fixtureComponent)) - { - foreach (var key in standingState.ChangedFixtures) - { - if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture)) - _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent); - } - } - standingState.ChangedFixtures.Clear(); - - return true; + var ev = new DropHandItemsEvent(); + RaiseLocalEvent(uid, ref ev, false); } + + if (!force) + { + var msg = new DownAttemptEvent(); + RaiseLocalEvent(uid, msg, false); + + if (msg.Cancelled) + return false; + } + + standingState.Standing = false; + Dirty(uid, standingState); + RaiseLocalEvent(uid, new DownedEvent(), false); + + // Seemed like the best place to put it + _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); + + // Change collision masks to allow going under certain entities like flaps and tables + if (TryComp(uid, out FixturesComponent? fixtureComponent)) + { + foreach (var (key, fixture) in fixtureComponent.Fixtures) + { + if ((fixture.CollisionMask & StandingCollisionLayer) == 0) + continue; + + standingState.ChangedFixtures.Add(key); + _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent); + } + } + + // check if component was just added or streamed to client + // if true, no need to play sound - mob was down before player could seen that + if (standingState.LifeStage <= ComponentLifeStage.Starting) + return true; + + if (playSound) + { + _audio.PlayPredicted(standingState.DownSound, uid, uid); + } + + return true; } - public sealed class DropHandItemsEvent : EventArgs + public bool Stand(EntityUid uid, + StandingStateComponent? standingState = null, + AppearanceComponent? appearance = null, + bool force = false) { - } + // TODO: This should actually log missing comps... + if (!Resolve(uid, ref standingState, false)) + return false; - /// - /// Subscribe if you can potentially block a down attempt. - /// - public sealed class DownAttemptEvent : CancellableEntityEventArgs - { - } + // Optional component. + Resolve(uid, ref appearance, false); - /// - /// Subscribe if you can potentially block a stand attempt. - /// - public sealed class StandAttemptEvent : CancellableEntityEventArgs - { - } + if (standingState.Standing) + return true; - /// - /// Raised when an entity becomes standing - /// - public sealed class StoodEvent : EntityEventArgs - { - } + if (!force) + { + var msg = new StandAttemptEvent(); + RaiseLocalEvent(uid, msg, false); - /// - /// Raised when an entity is not standing - /// - public sealed class DownedEvent : EntityEventArgs - { + if (msg.Cancelled) + return false; + } + + standingState.Standing = true; + Dirty(uid, standingState); + RaiseLocalEvent(uid, new StoodEvent(), false); + + _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); + + if (TryComp(uid, out FixturesComponent? fixtureComponent)) + { + foreach (var key in standingState.ChangedFixtures) + { + if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture)) + _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent); + } + } + standingState.ChangedFixtures.Clear(); + + return true; } } + +[ByRefEvent] +public record struct DropHandItemsEvent(); + +/// +/// Subscribe if you can potentially block a down attempt. +/// +public sealed class DownAttemptEvent : CancellableEntityEventArgs +{ +} + +/// +/// Subscribe if you can potentially block a stand attempt. +/// +public sealed class StandAttemptEvent : CancellableEntityEventArgs +{ +} + +/// +/// Raised when an entity becomes standing +/// +public sealed class StoodEvent : EntityEventArgs +{ +} + +/// +/// Raised when an entity is not standing +/// +public sealed class DownedEvent : EntityEventArgs +{ +} + +/// +/// Raised after an entity falls down. +/// +public sealed class FellDownEvent : EntityEventArgs +{ + public EntityUid Uid { get; } + + public FellDownEvent(EntityUid uid) + { + Uid = uid; + } +} + +/// +/// Raised on the entity being thrown due to the holder falling down. +/// +[ByRefEvent] +public record struct FellDownThrowAttemptEvent(EntityUid Thrower, bool Cancelled = false); + +