Skip to content

Commit

Permalink
work
Browse files Browse the repository at this point in the history
  • Loading branch information
rhailrake committed Dec 21, 2024
1 parent daf2a8a commit f5cf021
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 141 deletions.
35 changes: 18 additions & 17 deletions Content.Shared/Standing/StandingStateComponent.cs
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
}
21 changes: 21 additions & 0 deletions Content.Shared/Standing/StandingStateEvents.cs
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
{
}
210 changes: 86 additions & 124 deletions Content.Shared/Standing/StandingStateSystem.cs
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
}

0 comments on commit f5cf021

Please sign in to comment.