Files
tbd-station-14/Content.Shared/_Offbrand/Wounds/BrainDamageSystem.cs
Janet Blackquill 173f24590f resimmed offmed
2025-10-08 20:49:29 -04:00

247 lines
8.4 KiB
C#

using Content.Shared.Body.Events;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction.Events;
using Content.Shared.Random.Helpers;
using Content.Shared.Rejuvenate;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._Offbrand.Wounds;
public sealed partial class BrainDamageSystem : EntitySystem
{
[Dependency] private readonly HeartSystem _heart = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BrainDamageComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<BrainDamageComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<BrainDamageComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<BrainDamageComponent, BaseVascularToneEvent>(OnBaseVascularTone);
SubscribeLocalEvent<BrainDamageOxygenationComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<BrainDamageOxygenationComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
}
private void OnSuicide(Entity<BrainDamageComponent> ent, ref SuicideEvent args)
{
KillBrain(ent.AsNullable());
args.Handled = true;
}
private void OnRejuvenate(Entity<BrainDamageComponent> ent, ref RejuvenateEvent args)
{
ent.Comp.Oxygen = ent.Comp.MaxOxygen;
ent.Comp.Damage = 0;
Dirty(ent);
var notifOxygen = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref notifOxygen);
var notifDamage = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notifDamage);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
private void OnMapInit(Entity<BrainDamageOxygenationComponent> ent, ref MapInitEvent args)
{
ent.Comp.LastUpdate = _timing.CurTime;
}
private void OnStartup(Entity<BrainDamageComponent> ent, ref ComponentStartup args)
{
var notifOxygen = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref notifOxygen);
var notifDamage = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notifDamage);
}
private void OnBaseVascularTone(Entity<BrainDamageComponent> ent, ref BaseVascularToneEvent args)
{
args.Tone *= 1f - ent.Comp.Damage.Float() / ent.Comp.MaxDamage.Float();
}
private void OnApplyMetabolicMultiplier(Entity<BrainDamageOxygenationComponent> ent, ref ApplyMetabolicMultiplierEvent args)
{
ent.Comp.UpdateIntervalMultiplier = args.Multiplier;
Dirty(ent);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var enumerator = EntityQueryEnumerator<BrainDamageComponent, BrainDamageOxygenationComponent, HeartrateComponent>();
while (enumerator.MoveNext(out var uid, out var brain, out var oxygenation, out var heartrate))
{
if (oxygenation.LastUpdate is not { } last || last + oxygenation.AdjustedUpdateInterval >= _timing.CurTime || brain.Damage >= brain.MaxDamage)
continue;
oxygenation.LastUpdate = _timing.CurTime;
DoUpdate((uid, brain, oxygenation, heartrate));
Dirty(uid, oxygenation);
}
}
public void KillBrain(Entity<BrainDamageComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
ent.Comp.Oxygen = 0;
ent.Comp.Damage = ent.Comp.MaxDamage;
Dirty(ent);
var notifOxygen = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref notifOxygen);
var notifDamage = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notifDamage);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
public void TryChangeBrainDamage(Entity<BrainDamageComponent?> ent, FixedPoint2 amount)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
ent.Comp.Damage = FixedPoint2.Clamp(ent.Comp.Damage + amount, FixedPoint2.Zero, ent.Comp.MaxDamage);
Dirty(ent);
var notif = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notif);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
public void TryChangeBrainOxygenation(Entity<BrainDamageComponent?> ent, FixedPoint2 amount)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
ent.Comp.Oxygen = FixedPoint2.Clamp(ent.Comp.Oxygen + amount, FixedPoint2.Zero, ent.Comp.MaxOxygen);
Dirty(ent);
var notif = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref notif);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
private void DoOxygen(Entity<BrainDamageComponent, BrainDamageOxygenationComponent, HeartrateComponent> ent, FixedPoint2 oxygenation, System.Random rand)
{
var depletionThreshold = ent.Comp2.OxygenDepletionThresholds.LowestMatch(oxygenation);
if (depletionThreshold is not (var chance, var amount))
{
if (ent.Comp1.Oxygen < ent.Comp1.MaxOxygen)
{
ent.Comp1.Oxygen = FixedPoint2.Min(ent.Comp1.Oxygen + ent.Comp2.OxygenRegeneration, ent.Comp1.MaxOxygen);
Dirty(ent.Owner, ent.Comp1);
var increased = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref increased);
}
return;
}
var evt = new BeforeDepleteBrainOxygen(chance);
RaiseLocalEvent(ent, ref evt);
if (!rand.Prob(evt.Chance))
return;
var newValue = FixedPoint2.Max(ent.Comp1.Oxygen - amount, FixedPoint2.Zero);
if (ent.Comp1.Oxygen == newValue)
return;
ent.Comp1.Oxygen = newValue;
Dirty(ent.Owner, ent.Comp1);
var notif = new AfterBrainOxygenChanged();
RaiseLocalEvent(ent, ref notif);
}
private void DoHeal(Entity<BrainDamageComponent, BrainDamageOxygenationComponent, HeartrateComponent> ent)
{
if (ent.Comp1.Damage == FixedPoint2.Zero)
return;
var evt = new BeforeHealBrainDamage(ent.Comp1.Damage < ent.Comp2.MaxPassivelyHealableDamage);
RaiseLocalEvent(ent, ref evt);
if (!evt.Heal)
return;
var newValue = FixedPoint2.Max(ent.Comp1.Damage - ent.Comp2.DamageHealing, FixedPoint2.Zero);
if (ent.Comp1.Damage == newValue)
return;
ent.Comp1.Damage = newValue;
Dirty(ent.Owner, ent.Comp1);
var notif = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notif);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
private void DoDamage(Entity<BrainDamageComponent, BrainDamageOxygenationComponent, HeartrateComponent> ent, FixedPoint2 oxygenation, System.Random rand)
{
var damageThreshold = ent.Comp2.OxygenationDamageThresholds.LowestMatch(oxygenation);
if (damageThreshold is not (var chance, (var atMost, var amount)) || ent.Comp1.Damage > atMost)
{
DoHeal(ent);
return;
}
var evt = new BeforeDealBrainDamage(chance);
RaiseLocalEvent(ent, ref evt);
if (!rand.Prob(evt.Chance))
return;
var newValue = FixedPoint2.Min(ent.Comp1.Damage + amount, ent.Comp1.MaxDamage);
if (ent.Comp1.Damage == newValue)
return;
ent.Comp1.Damage = newValue;
Dirty(ent.Owner, ent.Comp1);
var notif = new AfterBrainDamageChanged();
RaiseLocalEvent(ent, ref notif);
var overlays = new PotentiallyUpdateDamageOverlayEvent(ent);
RaiseLocalEvent(ent, ref overlays, true);
}
private void DoUpdate(Entity<BrainDamageComponent, BrainDamageOxygenationComponent, HeartrateComponent> ent)
{
var oxygenation = _heart.Spo2((ent.Owner, ent.Comp3));
var seed = SharedRandomExtensions.HashCodeCombine(new() { (int)_timing.CurTick.Value, GetNetEntity(ent).Id });
var rand = new System.Random(seed);
DoOxygen(ent, oxygenation, rand);
DoDamage(ent, oxygenation, rand);
}
public bool IsCritical(Entity<BrainDamageComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return false;
return ent.Comp.Oxygen == 0;
}
}