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;
+
+ return !standingState.Standing;
+ }
- // 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 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;
- public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
- {
- if (!Resolve(uid, ref standingState, false))
- return false;
+ // Optional component.
+ Resolve(uid, ref appearance, ref hands, false);
- return !standingState.Standing;
+ 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)
+ {
+ var ev = new DropHandItemsEvent();
+ RaiseLocalEvent(uid, ref ev, false);
}
- public bool Down(EntityUid uid,
- bool playSound = true,
- bool dropHeldItems = true,
- bool force = false,
- StandingStateComponent? standingState = null,
- AppearanceComponent? appearance = null,
- HandsComponent? hands = null)
+ if (!force)
{
- // TODO: This should actually log missing comps...
- if (!Resolve(uid, ref standingState, false))
- return false;
+ var msg = new DownAttemptEvent();
+ RaiseLocalEvent(uid, msg, false);
- // Optional component.
- Resolve(uid, ref appearance, ref hands, false);
+ if (msg.Cancelled)
+ return false;
+ }
- if (!standingState.Standing)
- return true;
+ standingState.Standing = false;
+ Dirty(uid, standingState);
+ RaiseLocalEvent(uid, new DownedEvent(), 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)
- {
- RaiseLocalEvent(uid, new DropHandItemsEvent(), false);
- }
+ // Seemed like the best place to put it
+ _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
- if (!force)
+ // 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)
{
- var msg = new DownAttemptEvent();
- RaiseLocalEvent(uid, msg, false);
+ if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
+ continue;
- if (msg.Cancelled)
- return false;
+ standingState.ChangedFixtures.Add(key);
+ _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent);
}
+ }
- standingState.Standing = false;
- Dirty(uid, standingState);
- RaiseLocalEvent(uid, new DownedEvent(), false);
+ // 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;
- // Seemed like the best place to put it
- _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
+ if (playSound)
+ {
+ _audio.PlayPredicted(standingState.DownSound, uid, uid);
+ }
- // 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);
- }
- }
+ return true;
+ }
- // 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;
+ 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;
- if (playSound)
- {
- _audio.PlayPredicted(standingState.DownSound, uid, uid);
- }
+ // Optional component.
+ Resolve(uid, ref appearance, false);
+ if (standingState.Standing)
return true;
- }
- public bool Stand(EntityUid uid,
- StandingStateComponent? standingState = null,
- AppearanceComponent? appearance = null,
- bool force = false)
+ if (!force)
{
- // TODO: This should actually log missing comps...
- if (!Resolve(uid, ref standingState, false))
+ var msg = new StandAttemptEvent();
+ RaiseLocalEvent(uid, msg, false);
+
+ if (msg.Cancelled)
return false;
+ }
- // Optional component.
- Resolve(uid, ref appearance, false);
+ standingState.Standing = true;
+ Dirty(uid, standingState);
+ RaiseLocalEvent(uid, new StoodEvent(), false);
- if (standingState.Standing)
- return true;
+ _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
- if (!force)
+ if (TryComp(uid, out FixturesComponent? fixtureComponent))
+ {
+ foreach (var key in standingState.ChangedFixtures)
{
- var msg = new StandAttemptEvent();
- RaiseLocalEvent(uid, msg, false);
-
- if (msg.Cancelled)
- return false;
+ if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
+ _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
}
+ }
+ standingState.ChangedFixtures.Clear();
- standingState.Standing = true;
- Dirty(uid, standingState);
- RaiseLocalEvent(uid, new StoodEvent(), false);
-
- _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
+ return true;
+ }
+}
- 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();
+[ByRefEvent]
+public record struct DropHandItemsEvent();
- return true;
- }
- }
+/// <summary>
+/// Subscribe if you can potentially block a down attempt.
+/// </summary>
+public sealed class DownAttemptEvent : CancellableEntityEventArgs
+{
+}
- public sealed class DropHandItemsEvent : EventArgs
- {
- }
+/// <summary>
+/// Subscribe if you can potentially block a stand attempt.
+/// </summary>
+public sealed class StandAttemptEvent : CancellableEntityEventArgs
+{
+}
- /// <summary>
- /// Subscribe if you can potentially block a down attempt.
- /// </summary>
- public sealed class DownAttemptEvent : CancellableEntityEventArgs
- {
- }
+/// <summary>
+/// Raised when an entity becomes standing
+/// </summary>
+public sealed class StoodEvent : EntityEventArgs
+{
+}
- /// <summary>
- /// Subscribe if you can potentially block a stand attempt.
- /// </summary>
- public sealed class StandAttemptEvent : CancellableEntityEventArgs
- {
- }
+/// <summary>
+/// Raised when an entity is not standing
+/// </summary>
+public sealed class DownedEvent : EntityEventArgs
+{
+}
- /// <summary>
- /// Raised when an entity becomes standing
- /// </summary>
- public sealed class StoodEvent : EntityEventArgs
- {
- }
+/// <summary>
+/// Raised after an entity falls down.
+/// </summary>
+public sealed class FellDownEvent : EntityEventArgs
+{
+ public EntityUid Uid { get; }
- /// <summary>
- /// Raised when an entity is not standing
- /// </summary>
- public sealed class DownedEvent : EntityEventArgs
+ public FellDownEvent(EntityUid uid)
{
+ Uid = uid;
}
}
+
+/// <summary>
+/// Raised on the entity being thrown due to the holder falling down.
+/// </summary>
+[ByRefEvent]
+public record struct FellDownThrowAttemptEvent(EntityUid Thrower, bool Cancelled = false);
+
+