-
-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
125 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,25 @@ | ||
using Robust.Shared.Audio; | ||
using Robust.Shared.GameStates; | ||
using Robust.Shared.Serialization; | ||
|
||
namespace Content.Shared.Standing | ||
namespace Content.Shared.Standing; | ||
|
||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] | ||
public sealed partial class StandingStateComponent : Component | ||
{ | ||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] | ||
[Access(typeof(StandingStateSystem))] | ||
public sealed partial class StandingStateComponent : Component | ||
{ | ||
[ViewVariables(VVAccess.ReadWrite)] | ||
[DataField] | ||
public SoundSpecifier? DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall"); | ||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] | ||
public SoundSpecifier DownSound { get; set; } = new SoundCollectionSpecifier("BodyFall"); | ||
|
||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)] | ||
public StandingState CurrentState = StandingState.Standing; | ||
|
||
[DataField, AutoNetworkedField] | ||
public bool Standing { get; set; } = true; | ||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)] | ||
public List<string> ChangedFixtures = new(); | ||
} | ||
|
||
/// <summary> | ||
/// List of fixtures that had their collision mask changed when the entity was downed. | ||
/// Required for re-adding the collision mask. | ||
/// </summary> | ||
[DataField, AutoNetworkedField] | ||
public List<string> ChangedFixtures = new(); | ||
} | ||
[Serializable, NetSerializable] | ||
public enum StandingState : byte | ||
{ | ||
Standing = 0, | ||
Laying = 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
namespace Content.Shared.Standing; | ||
|
||
public sealed class DropHandItemsEvent : EventArgs | ||
{ | ||
} | ||
|
||
public sealed class DownAttemptEvent : CancellableEntityEventArgs | ||
{ | ||
} | ||
|
||
public sealed class StandAttemptEvent : CancellableEntityEventArgs | ||
{ | ||
} | ||
|
||
public sealed class StoodEvent : EntityEventArgs | ||
{ | ||
} | ||
|
||
public sealed class DownedEvent : EntityEventArgs | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,172 +1,134 @@ | ||
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!; | ||
[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; | ||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; | ||
|
||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) | ||
{ | ||
if (!Resolve(uid, ref standingState, false)) | ||
return false; | ||
public bool Stand(EntityUid uid, | ||
StandingStateComponent? standingState = null, | ||
AppearanceComponent? appearance = null, | ||
bool force = false) | ||
{ | ||
if (!Resolve(uid, ref standingState, false)) | ||
return false; | ||
|
||
return !standingState.Standing; | ||
} | ||
Resolve(uid, ref appearance, 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 (IsStanding(uid, standingState)) | ||
return true; | ||
|
||
if (!force) | ||
{ | ||
// TODO: This should actually log missing comps... | ||
if (!Resolve(uid, ref standingState, false)) | ||
var msg = new StandAttemptEvent(); | ||
RaiseLocalEvent(uid, msg); | ||
|
||
if (msg.Cancelled) | ||
return false; | ||
} | ||
|
||
// Optional component. | ||
Resolve(uid, ref appearance, ref hands, false); | ||
standingState.CurrentState = StandingState.Standing; | ||
Dirty(uid, standingState); | ||
RaiseLocalEvent(uid, new StoodEvent()); | ||
|
||
if (!standingState.Standing) | ||
return true; | ||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); | ||
|
||
// 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) | ||
if (TryComp(uid, out FixturesComponent? fixtureComponent)) | ||
{ | ||
foreach (var key in standingState.ChangedFixtures) | ||
{ | ||
var msg = new DownAttemptEvent(); | ||
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.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); | ||
standingState.ChangedFixtures.Clear(); | ||
|
||
// 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 Down(EntityUid uid, | ||
bool playSound = true, | ||
bool dropHeldItems = true, | ||
bool force = false, | ||
StandingStateComponent? standingState = null, | ||
AppearanceComponent? appearance = null, | ||
HandsComponent? hands = null) | ||
{ | ||
if (!Resolve(uid, ref standingState, false)) | ||
return false; | ||
|
||
if (playSound) | ||
{ | ||
_audio.PlayPredicted(standingState.DownSound, uid, uid); | ||
} | ||
Resolve(uid, ref appearance, ref hands, false); | ||
|
||
if (IsDown(uid, standingState)) | ||
return true; | ||
} | ||
|
||
public bool Stand(EntityUid uid, | ||
StandingStateComponent? standingState = null, | ||
AppearanceComponent? appearance = null, | ||
bool force = false) | ||
if (dropHeldItems && hands != null) | ||
RaiseLocalEvent(uid, new DropHandItemsEvent()); | ||
|
||
if (!force) | ||
{ | ||
// TODO: This should actually log missing comps... | ||
if (!Resolve(uid, ref standingState, false)) | ||
var msg = new DownAttemptEvent(); | ||
RaiseLocalEvent(uid, msg); | ||
|
||
if (msg.Cancelled) | ||
return false; | ||
} | ||
|
||
// Optional component. | ||
Resolve(uid, ref appearance, false); | ||
standingState.CurrentState = StandingState.Laying; | ||
Dirty(uid, standingState); | ||
RaiseLocalEvent(uid, new DownedEvent()); | ||
|
||
if (standingState.Standing) | ||
return true; | ||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); | ||
|
||
if (!force) | ||
if (TryComp(uid, out FixturesComponent? fixtureComponent)) | ||
{ | ||
foreach (var (key, fixture) in fixtureComponent.Fixtures) | ||
{ | ||
var msg = new StandAttemptEvent(); | ||
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 = true; | ||
Dirty(uid, standingState); | ||
RaiseLocalEvent(uid, new StoodEvent(), false); | ||
if (standingState.LifeStage <= ComponentLifeStage.Starting) | ||
return true; | ||
|
||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); | ||
if (playSound) | ||
_audio.PlayPredicted(standingState.DownSound, uid, uid); | ||
|
||
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; | ||
} | ||
return true; | ||
} | ||
|
||
public sealed class DropHandItemsEvent : EventArgs | ||
{ | ||
} | ||
#region Helpers | ||
|
||
/// <summary> | ||
/// Subscribe if you can potentially block a down attempt. | ||
/// </summary> | ||
public sealed class DownAttemptEvent : CancellableEntityEventArgs | ||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) | ||
{ | ||
} | ||
if (!Resolve(uid, ref standingState, false)) | ||
return false; | ||
|
||
/// <summary> | ||
/// Subscribe if you can potentially block a stand attempt. | ||
/// </summary> | ||
public sealed class StandAttemptEvent : CancellableEntityEventArgs | ||
{ | ||
return standingState.CurrentState == StandingState.Laying; | ||
} | ||
|
||
/// <summary> | ||
/// Raised when an entity becomes standing | ||
/// </summary> | ||
public sealed class StoodEvent : EntityEventArgs | ||
public bool IsStanding(EntityUid uid, StandingStateComponent? standingState = null) | ||
{ | ||
} | ||
if (!Resolve(uid, ref standingState, false)) | ||
return false; | ||
|
||
/// <summary> | ||
/// Raised when an entity is not standing | ||
/// </summary> | ||
public sealed class DownedEvent : EntityEventArgs | ||
{ | ||
return standingState.CurrentState == StandingState.Standing; | ||
} | ||
|
||
#endregion | ||
} |