Added Pain Numbness Trait (#34538)

* added pain-numbness component and system

* added numb as a trait that pulls the pain numbness component

* removed new event as mob threshold event as already being fired

* checked for MobThresholdsComponent first before running VerifyThresholds

* refacted force say to using LocalizedDatasetPrototype and added numb messages

* added severity check alert

* added comment for BeforeForceSayEvent

* removed space formatting

* changed Cancelled to CancelUpdate, fixed spacing and added two more damage-force-say-numb

* changed prefix damage-force-say-numb to 5 (whoops)
This commit is contained in:
Coolsurf6
2025-01-27 20:34:20 +10:00
committed by GitHub
parent 1b9d84a16e
commit 012c835559
14 changed files with 174 additions and 14 deletions

View File

@@ -3,6 +3,7 @@ using Content.Shared.FixedPoint;
using Content.Shared.Mobs; using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Traits.Assorted;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; using Robust.Client.Player;
@@ -94,7 +95,11 @@ public sealed class DamageOverlayUiController : UIController
{ {
case MobState.Alive: case MobState.Alive:
{ {
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage)) if (EntityManager.HasComponent<PainNumbnessComponent>(entity))
{
_overlay.BruteLevel = 0;
}
else if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
{ {
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float(); _overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
} }

View File

@@ -1,5 +1,6 @@
using Content.Shared.Bed.Sleep; using Content.Shared.Bed.Sleep;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.ForceSay; using Content.Shared.Damage.ForceSay;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Mobs; using Content.Shared.Mobs;
@@ -47,7 +48,7 @@ public sealed class DamageForceSaySystem : EntitySystem
} }
} }
private void TryForceSay(EntityUid uid, DamageForceSayComponent component, bool useSuffix=true, string? suffixOverride = null) private void TryForceSay(EntityUid uid, DamageForceSayComponent component, bool useSuffix=true)
{ {
if (!TryComp<ActorComponent>(uid, out var actor)) if (!TryComp<ActorComponent>(uid, out var actor))
return; return;
@@ -57,7 +58,13 @@ public sealed class DamageForceSaySystem : EntitySystem
_timing.CurTime < component.NextAllowedTime) _timing.CurTime < component.NextAllowedTime)
return; return;
var suffix = Loc.GetString(suffixOverride ?? component.ForceSayStringPrefix + _random.Next(1, component.ForceSayStringCount)); var ev = new BeforeForceSayEvent(component.ForceSayStringDataset);
RaiseLocalEvent(uid, ev);
if (!_prototype.TryIndex(ev.Prefix, out var prefixList))
return;
var suffix = Loc.GetString(_random.Pick(prefixList.Values));
// set cooldown & raise event // set cooldown & raise event
component.NextAllowedTime = _timing.CurTime + component.Cooldown; component.NextAllowedTime = _timing.CurTime + component.Cooldown;
@@ -80,7 +87,7 @@ public sealed class DamageForceSaySystem : EntitySystem
if (!args.FellAsleep) if (!args.FellAsleep)
return; return;
TryForceSay(uid, component, true, "damage-force-say-sleep"); TryForceSay(uid, component);
AllowNextSpeech(uid); AllowNextSpeech(uid);
} }

View File

@@ -1,6 +1,8 @@
using Content.Shared.Dataset;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Bed.Sleep; namespace Content.Shared.Bed.Sleep;
@@ -39,4 +41,11 @@ public sealed partial class SleepingComponent : Component
{ {
Params = AudioParams.Default.WithVariation(0.05f) Params = AudioParams.Default.WithVariation(0.05f)
}; };
/// <summary>
/// The fluent string prefix to use when picking a random suffix
/// This is only active for those who have the sleeping component
/// </summary>
[DataField]
public ProtoId<LocalizedDatasetPrototype> ForceSaySleepDataset = "ForceSaySleepDataset";
} }

View File

@@ -1,6 +1,7 @@
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Buckle.Components; using Content.Shared.Buckle.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.ForceSay; using Content.Shared.Damage.ForceSay;
using Content.Shared.Emoting; using Content.Shared.Emoting;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -18,6 +19,7 @@ using Content.Shared.Sound.Components;
using Content.Shared.Speech; using Content.Shared.Speech;
using Content.Shared.StatusEffect; using Content.Shared.StatusEffect;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared.Traits.Assorted;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -63,6 +65,8 @@ public sealed partial class SleepingSystem : EntitySystem
SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit); SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt); SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
SubscribeLocalEvent<SleepingComponent, EmoteAttemptEvent>(OnEmoteAttempt); SubscribeLocalEvent<SleepingComponent, EmoteAttemptEvent>(OnEmoteAttempt);
SubscribeLocalEvent<SleepingComponent, BeforeForceSayEvent>(OnChangeForceSay, after: new []{typeof(PainNumbnessSystem)});
} }
private void OnUnbuckleAttempt(Entity<SleepingComponent> ent, ref UnbuckleAttemptEvent args) private void OnUnbuckleAttempt(Entity<SleepingComponent> ent, ref UnbuckleAttemptEvent args)
@@ -317,6 +321,11 @@ public sealed partial class SleepingSystem : EntitySystem
{ {
args.Cancel(); args.Cancel();
} }
private void OnChangeForceSay(Entity<SleepingComponent> ent, ref BeforeForceSayEvent args)
{
args.Prefix = ent.Comp.ForceSaySleepDataset;
}
} }

View File

@@ -0,0 +1,14 @@
using Content.Shared.Dataset;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Damage.Events;
/// <summary>
/// Event for interrupting and changing the prefix for when an entity is being forced to say something
/// </summary>
[Serializable, NetSerializable]
public sealed class BeforeForceSayEvent(ProtoId<LocalizedDatasetPrototype> prefixDataset) : EntityEventArgs
{
public ProtoId<LocalizedDatasetPrototype> Prefix = prefixDataset;
}

View File

@@ -1,8 +1,8 @@
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.Dataset;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.Damage.ForceSay; namespace Content.Shared.Damage.ForceSay;
@@ -30,14 +30,7 @@ public sealed partial class DamageForceSayComponent : Component
/// The fluent string prefix to use when picking a random suffix /// The fluent string prefix to use when picking a random suffix
/// </summary> /// </summary>
[DataField] [DataField]
public string ForceSayStringPrefix = "damage-force-say-"; public ProtoId<LocalizedDatasetPrototype> ForceSayStringDataset = "ForceSayStringDataset";
/// <summary>
/// The number of suffixes that exist for use with <see cref="ForceSayStringPrefix"/>.
/// i.e. (prefix)-1 through (prefix)-(count)
/// </summary>
[DataField]
public int ForceSayStringCount = 7;
/// <summary> /// <summary>
/// The amount of total damage between <see cref="ValidDamageGroups"/> that needs to be taken before /// The amount of total damage between <see cref="ValidDamageGroups"/> that needs to be taken before

View File

@@ -0,0 +1,16 @@
using Content.Shared.Alert;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Mobs.Events;
/// <summary>
/// Event for allowing the interrupting and change of the mob threshold severity alert
/// </summary>
[Serializable, NetSerializable]
public sealed class BeforeAlertSeverityCheckEvent(ProtoId<AlertPrototype> currentAlert, short severity) : EntityEventArgs
{
public bool CancelUpdate = false;
public ProtoId<AlertPrototype> CurrentAlert = currentAlert;
public short Severity = severity;
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Events;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Mobs.Systems; namespace Content.Shared.Mobs.Systems;
@@ -391,6 +392,16 @@ public sealed class MobThresholdSystem : EntitySystem
if (alertPrototype.SupportsSeverity) if (alertPrototype.SupportsSeverity)
{ {
var severity = _alerts.GetMinSeverity(currentAlert); var severity = _alerts.GetMinSeverity(currentAlert);
var ev = new BeforeAlertSeverityCheckEvent(currentAlert, severity);
RaiseLocalEvent(target, ev);
if (ev.CancelUpdate)
{
_alerts.ShowAlert(target, ev.CurrentAlert, ev.Severity);
return;
}
if (TryGetNextState(target, currentMobState, out var nextState, threshold) && if (TryGetNextState(target, currentMobState, out var nextState, threshold) &&
TryGetPercentageForState(target, nextState.Value, damageable.TotalDamage, out var percentage)) TryGetPercentageForState(target, nextState.Value, damageable.TotalDamage, out var percentage))
{ {

View File

@@ -0,0 +1,16 @@
using Content.Shared.Dataset;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Traits.Assorted;
[RegisterComponent, NetworkedComponent]
public sealed partial class PainNumbnessComponent : Component
{
/// <summary>
/// The fluent string prefix to use when picking a random suffix
/// This is only active for those who have the pain numbness component
/// </summary>
[DataField]
public ProtoId<LocalizedDatasetPrototype> ForceSayNumbDataset = "ForceSayNumbDataset";
}

View File

@@ -0,0 +1,46 @@
using Content.Shared.Damage.Events;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Events;
using Content.Shared.Mobs.Systems;
namespace Content.Shared.Traits.Assorted;
public sealed class PainNumbnessSystem : EntitySystem
{
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<PainNumbnessComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<PainNumbnessComponent, ComponentRemove>(OnComponentRemove);
SubscribeLocalEvent<PainNumbnessComponent, BeforeForceSayEvent>(OnChangeForceSay);
SubscribeLocalEvent<PainNumbnessComponent, BeforeAlertSeverityCheckEvent>(OnAlertSeverityCheck);
}
private void OnComponentRemove(EntityUid uid, PainNumbnessComponent component, ComponentRemove args)
{
if (!HasComp<MobThresholdsComponent>(uid))
return;
_mobThresholdSystem.VerifyThresholds(uid);
}
private void OnComponentInit(EntityUid uid, PainNumbnessComponent component, ComponentInit args)
{
if (!HasComp<MobThresholdsComponent>(uid))
return;
_mobThresholdSystem.VerifyThresholds(uid);
}
private void OnChangeForceSay(Entity<PainNumbnessComponent> ent, ref BeforeForceSayEvent args)
{
args.Prefix = ent.Comp.ForceSayNumbDataset;
}
private void OnAlertSeverityCheck(Entity<PainNumbnessComponent> ent, ref BeforeAlertSeverityCheckEvent args)
{
if (args.CurrentAlert == "HumanHealth")
args.CancelUpdate = true;
}
}

View File

@@ -9,4 +9,10 @@ damage-force-say-5 = OW!
damage-force-say-6 = URGH! damage-force-say-6 = URGH!
damage-force-say-7 = HRNK! damage-force-say-7 = HRNK!
damage-force-say-sleep = zzz... damage-force-say-sleep-1 = zzz...
damage-force-say-numb-1 = oh-
damage-force-say-numb-2 = ow-
damage-force-say-numb-3 = oof-
damage-force-say-numb-4 = ah-
damage-force-say-numb-5 = ugh-

View File

@@ -59,3 +59,6 @@ trait-french-desc = Your accent seems to have a certain «je ne sais quoi».
trait-spanish-name = Spanish accent trait-spanish-name = Spanish accent
trait-spanish-desc = Hola señor, donde esta la biblioteca. trait-spanish-desc = Hola señor, donde esta la biblioteca.
trait-painnumbness-name = Numb
trait-painnumbness-desc = You lack any sense of feeling pain, being unaware of how hurt you may be.

View File

@@ -0,0 +1,17 @@
- type: localizedDataset
id: ForceSayStringDataset
values:
prefix: damage-force-say-
count: 7
- type: localizedDataset
id: ForceSaySleepDataset
values:
prefix: damage-force-say-sleep-
count: 1
- type: localizedDataset
id: ForceSayNumbDataset
values:
prefix: damage-force-say-numb-
count: 5

View File

@@ -64,3 +64,11 @@
maxSoundDistance: 7 maxSoundDistance: 7
sounds: sounds:
collection: Paracusia collection: Paracusia
- type: trait
id: PainNumbness
name: trait-painnumbness-name
description: trait-painnumbness-desc
category: Disabilities
components:
- type: PainNumbness