Files
wwdpublic/Content.Server/Standing/LayingDownSystem.cs
Mnemotechnican a2c1687260 Lay Down via Keybind (#530)
# Description
Adds a way to lay down/crawl using a keybind (R by default) similarly to
ss13. It has the same effects as falling down after slipping or buckling
to a bed, except you don't drop items while doing so and can (very
slowly) move around. This opens new gameplay and roleplay possibilities.

You can only toggle standing/laying once in 2.5 seconds (this cooldown
is to prevent pro gamers from spamming it). It shows a small popup to
everyone. If the attempt fails for whatever reason - being buckled,
sleeping, stunned, or anything else - another popup is shown that's only
visible to you.

It's been tested and made sure that the system works correctly with
buckling, sleeping, being stunned, and shocked.

<details><summary><h1>Media</h1></summary>
<p>

18 mb recording won't fit on github:


https://cdn.discordapp.com/attachments/1255902264309321851/1260354667578261504/weeee-2024-07-10_00.57.23.mp4?ex=668f0441&is=668db2c1&hm=d338a3499bf47780a66b7ba96d5e8830d8cb4167064423b8983b2d0144b7aa88&

</p>
</details>

---

# Changelog

🆑
- add: You can now lie down and stand up at will! The default keybind
for it is "R", but it can be changed in settings.

---------

Signed-off-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com>
Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com>
2024-07-17 22:33:16 -04:00

102 lines
4.2 KiB
C#

using Content.Shared.ActionBlocker;
using Content.Shared.Input;
using Content.Shared.Movement.Systems;
using Content.Shared.Popups;
using Content.Shared.Standing;
using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Standing;
/// <remarks>Unfortunately cannot be shared because some standing conditions are server-side only</remarks>
public sealed class LayingDownSystem : EntitySystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly Shared.Standing.StandingStateSystem _standing = default!; // WHY IS THERE TWO DIFFERENT STANDING SYSTEMS?!
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
CommandBinds.Builder
.Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding, handle: false, outsidePrediction: false))
.Register<LayingDownSystem>();
SubscribeLocalEvent<LayingDownComponent, StoodEvent>(DoRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, DownedEvent>(DoRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, EntParentChangedMessage>(OnParentChanged);
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<LayingDownSystem>();
}
private void DoRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, object args)
{
_movement.RefreshMovementSpeedModifiers(uid);
}
private void OnRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (TryComp<StandingStateComponent>(uid, out var standingState) && standingState.Standing)
return;
args.ModifySpeed(component.DownedSpeedMultiplier, component.DownedSpeedMultiplier);
}
private void OnParentChanged(EntityUid uid, LayingDownComponent component, EntParentChangedMessage args)
{
// If the entity is not on a grid, try to make it stand up to avoid issues
if (!TryComp<StandingStateComponent>(uid, out var standingState)
|| standingState.Standing
|| Transform(uid).GridUid != null)
return;
_standing.Stand(uid, standingState);
}
private void ToggleStanding(ICommonSession? session)
{
if (session is not { AttachedEntity: { Valid: true } uid } playerSession
|| !Exists(uid)
|| !TryComp<StandingStateComponent>(uid, out var standingState)
|| !TryComp<LayingDownComponent>(uid, out var layingDown))
return;
// If successful, show popup to self and others. Otherwise, only to self.
if (ToggleStandingImpl(uid, standingState, layingDown, out var popupBranch))
{
_popups.PopupEntity(Loc.GetString($"laying-comp-{popupBranch}-other", ("entity", uid)), uid, Filter.PvsExcept(uid), true);
layingDown.NextToggleAttempt = _timing.CurTime + layingDown.Cooldown;
}
_popups.PopupEntity(Loc.GetString($"laying-comp-{popupBranch}-self", ("entity", uid)), uid, uid);
}
private bool ToggleStandingImpl(EntityUid uid, StandingStateComponent standingState, LayingDownComponent layingDown, out string popupBranch)
{
var success = layingDown.NextToggleAttempt <= _timing.CurTime;
if (_standing.IsDown(uid, standingState))
{
success = success && _standing.Stand(uid, standingState, force: false);
popupBranch = success ? "stand-success" : "stand-fail";
}
else
{
success = success && Transform(uid).GridUid != null; // Do not allow laying down when not on a surface.
success = success && _standing.Down(uid, standingState: standingState, playSound: true, dropHeldItems: false);
popupBranch = success ? "lay-success" : "lay-fail";
}
return success;
}
}