Skip to content

Commit

Permalink
Basic Soft-Crit Implementation (#1370)
Browse files Browse the repository at this point in the history
# Description

This PR adds a simple server configuration option for enabling basic
"Soft-Crit", and not much else because oh my god this system is horribly
complicated. When enabled, characters can crawl around very slowly while
in crit, and really not much else. This more or less mirrors how crit
affects character movement in SS13, where you can at least crawl to
relative safety while bleeding to death.

# Changelog

:cl:
- add: Added server config options for basic "Soft-Crit". When enabled,
characters who are critically injured can still slowly crawl, but are
otherwise still helpless and dying.
  • Loading branch information
VMSolidus authored Dec 26, 2024
1 parent bfb32cf commit dff8c69
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 7 deletions.
23 changes: 23 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2820,5 +2820,28 @@ public static readonly CVarDef<float>
/// </summary>
public static readonly CVarDef<bool> UseDynamicHostname =
CVarDef.Create("game.use_dynamic_hostname", false, CVar.SERVERONLY);

#region SoftCrit

/// <summary>
/// Used for basic Soft-Crit implementation. Entities are allowed to crawl when in crit, as this CVar intercepts the mover controller check for incapacitation,
/// and prevents it from stopping movement if this CVar is set to true and the user is Crit but Not Dead. This is only for movement,
/// you still can't stand up while crit, and you're still more or less helpless.
/// </summary>
public static readonly CVarDef<bool> AllowMovementWhileCrit =
CVarDef.Create("mobstate.allow_movement_while_crit", true, CVar.REPLICATED);

public static readonly CVarDef<bool> AllowTalkingWhileCrit =
CVarDef.Create("mobstate.allow_talking_while_crit", true, CVar.REPLICATED);

/// <summary>
/// Currently does nothing because I would have to figure out WHERE I would even put this check, and the mover controller is fairly complicated.
/// The goal is to make it so that attempting to move while in 'soft crit' can potentially cause further injury, causing you to die faster. Ideally there would be special
/// actions that can be performed in soft crit, such as applying pressure to your own injuries to slow down the bleedout, or other varieties of "Will To Live".
/// </summary>
public static readonly CVarDef<bool> DamageWhileCritMove =
CVarDef.Create("mobstate.damage_while_crit_move", false, CVar.REPLICATED);

#endregion
}
}
28 changes: 26 additions & 2 deletions Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Shared.Bed.Sleep;
using Content.Shared.Buckle.Components;
using Content.Shared.CCVar;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage.ForceSay;
using Content.Shared.Emoting;
Expand All @@ -16,17 +17,20 @@
using Content.Shared.Standing;
using Content.Shared.Strip.Components;
using Content.Shared.Throwing;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components;

namespace Content.Shared.Mobs.Systems;

public partial class MobStateSystem
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;

//General purpose event subscriptions. If you can avoid it register these events inside their own systems
private void SubscribeEvents()
{
SubscribeLocalEvent<MobStateComponent, BeforeGettingStrippedEvent>(OnGettingStripped);
SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(OnDirectionAttempt);
SubscribeLocalEvent<MobStateComponent, UseAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, AttackAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, ConsciousAttemptEvent>(CheckAct);
Expand All @@ -38,7 +42,7 @@ private void SubscribeEvents()
SubscribeLocalEvent<MobStateComponent, DropAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PickupAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, StartPullAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(OnMoveAttempt);
SubscribeLocalEvent<MobStateComponent, StandAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PointAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, TryingToSleepEvent>(OnSleepAttempt);
Expand All @@ -48,6 +52,23 @@ private void SubscribeEvents()
SubscribeLocalEvent<MobStateComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
}

private void OnDirectionAttempt(Entity<MobStateComponent> ent, ref ChangeDirectionAttemptEvent args)
{
if (ent.Comp.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

CheckAct(ent.Owner, ent.Comp, args);
}

private void OnMoveAttempt(Entity<MobStateComponent> ent, ref UpdateCanMoveEvent args)
{
if (ent.Comp.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

CheckAct(ent.Owner, ent.Comp, args);
}


private void OnUnbuckleAttempt(Entity<MobStateComponent> ent, ref UnbuckleAttemptEvent args)
{
// TODO is this necessary?
Expand Down Expand Up @@ -145,6 +166,9 @@ private void OnSpeakAttempt(EntityUid uid, MobStateComponent component, SpeakAtt
return;
}

if (component.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowTalkingWhileCrit))
return;

CheckAct(uid, component, args);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,11 @@ private void HandleDirChange(EntityUid entity, Direction dir, ushort subTick, bo
if (MoverQuery.TryGetComponent(entity, out var mover))
SetMoveInput(mover, MoveButtons.None);

if (!_mobState.IsIncapacitated(entity))
HandleDirChange(relayMover.RelayEntity, dir, subTick, state);
if (_mobState.IsDead(entity)
|| _mobState.IsCritical(entity) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

HandleDirChange(relayMover.RelayEntity, dir, subTick, state);

return;
}
Expand Down
7 changes: 4 additions & 3 deletions Content.Shared/Movement/Systems/SharedMoverController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ protected void HandleMobMovement(
var canMove = mover.CanMove;
if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget))
{
if (_mobState.IsIncapacitated(relayTarget.Source) ||
TryComp<SleepingComponent>(relayTarget.Source, out _) ||
!MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover))
if (_mobState.IsDead(relayTarget.Source)
|| TryComp<SleepingComponent>(relayTarget.Source, out _)
|| !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)
|| _mobState.IsCritical(relayTarget.Source) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
{
canMove = false;
}
Expand Down

0 comments on commit dff8c69

Please sign in to comment.