Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pipe pressure damage #20931

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions Content.Server/NodeContainer/NodeGroups/PipeNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Content.Shared.Damage;
using Robust.Shared.Random;
using Robust.Shared.Utility;

namespace Content.Server.NodeContainer.NodeGroups
Expand All @@ -21,6 +23,9 @@ public sealed class PipeNet : BaseNodeGroup, IPipeNet
[ViewVariables] public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C};

[ViewVariables] private AtmosphereSystem? _atmosphereSystem;
[ViewVariables] private DamageableSystem? _damage;
[ViewVariables] private IEntityManager? _entMan;
[ViewVariables] private IRobustRandom? _random;

public EntityUid? Grid { get; private set; }

Expand All @@ -38,11 +43,53 @@ public override void Initialize(Node sourceNode, IEntityManager entMan)

_atmosphereSystem = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
_atmosphereSystem.AddPipeNet(Grid.Value, this);
_damage = entMan.EntitySysManager.GetEntitySystem<DamageableSystem>();
_entMan = entMan;
_random = IoCManager.Resolve<IRobustRandom>();
}

/// <summary>
/// Calculate pressure damage for pipe. There is no damage if the pressure is below MaxPressure,
/// and damage scales exponentially beyond that.
/// </summary>
private int PressureDamage(PipeNode pipe)
{
const float tau = 10; // number of atmos ticks to break pipe at nominal overpressure
var diff = pipe.Air.Pressure - pipe.MaxPressure;
const float alpha = 100/tau;
return diff > 0 ? (int)(alpha*float.Exp(diff / pipe.MaxPressure)) : 0;
Comment on lines +57 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More descriptive names might improve maintainability. Something like:

Suggested change
const float tau = 10; // number of atmos ticks to break pipe at nominal overpressure
var diff = pipe.Air.Pressure - pipe.MaxPressure;
const float alpha = 100/tau;
return diff > 0 ? (int)(alpha*float.Exp(diff / pipe.MaxPressure)) : 0;
const float ticksToBreak = 10; // number of atmos ticks to break pipe at nominal overpressure
var diff = pipe.Air.Pressure - pipe.MaxPressure;
const float nominalDamage = 100 / ticksToBreak;
return diff > 0 ? (int)(nominalDamage * float.Exp(diff / pipe.MaxPressure)) : 0;

}

public void Update()
{
_atmosphereSystem?.React(Air, this);

// Check each pipe node for overpressure and apply damage if needed
foreach (var node in Nodes)
{
if (node is PipeNode pipe && pipe.MaxPressure > 0)
{
// Prefer damaging pipes that are already damaged. This means that only one pipe
// fails instead of the whole pipenet bursting at the same time.
const float baseChance = 0.5f;
float p = baseChance;
if (_entMan != null && _entMan.TryGetComponent<DamageableComponent>(pipe.Owner, out var damage))
{
p += (float)damage.TotalDamage * (1 - baseChance);
}

if (_random != null && _random.Prob(1-p))
continue;

int dam = PressureDamage(pipe);
if (dam > 0)
{
var dspec = new DamageSpecifier();
dspec.DamageDict.Add("Structural", dam);
_damage?.TryChangeDamage(pipe.Owner, dspec);
}
Comment on lines +72 to +90
Copy link
Contributor

@veprolet veprolet Feb 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea was to linearly scale the probability of damage between .5 and 1. with the accumulated damage increasing between 0 and destruction. This however scales the probability directly with the actual value of total damage. This leads to probabilities wildly above 1 and understandably triggers debug assertions in the PRNG code. Ignoring the other potential issues, I believe the code was intended to look something like this (with more verbose names again):

Suggested change
// Prefer damaging pipes that are already damaged. This means that only one pipe
// fails instead of the whole pipenet bursting at the same time.
const float baseChance = 0.5f;
float p = baseChance;
if (_entMan != null && _entMan.TryGetComponent<DamageableComponent>(pipe.Owner, out var damage))
{
p += (float)damage.TotalDamage * (1 - baseChance);
}
if (_random != null && _random.Prob(1-p))
continue;
int dam = PressureDamage(pipe);
if (dam > 0)
{
var dspec = new DamageSpecifier();
dspec.DamageDict.Add("Structural", dam);
_damage?.TryChangeDamage(pipe.Owner, dspec);
}
// Prefer damaging pipes that are already damaged. This means that only one pipe
// fails instead of the whole pipenet bursting at the same time.
const float baseChance = 0.5f;
float prob = baseChance;
if (_entMan != null
&& _entMan.TryGetComponent<DamageableComponent>(pipe.Owner, out var damage)
&& _destroy != null
&& _destroy.TryGetDestroyedAt(pipe.Owner, out var maxDamage))
{
var damageRatio = (float)damage.TotalDamage / (float)maxDamage;
prob += damageRatio * (1 - baseChance);
}
if (_random != null && _random.Prob(1 - prob))
continue;
int damageInflicted = PressureDamage(pipe);
if (damageInflicted > 0)
{
var damageSpec = new DamageSpecifier();
damageSpec.DamageDict.Add("Structural", damageInflicted);
_damage?.TryChangeDamage(pipe.Owner, damageSpec);
}

Where _destroy is of course:

        [ViewVariables] private DestructibleSystem? _destroy;

}
}
}

public override void LoadNodes(List<Node> groupNodes)
Expand Down
6 changes: 6 additions & 0 deletions Content.Server/NodeContainer/Nodes/PipeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ public GasMixture Air

private const float DefaultVolume = 200f;

/// <summary>
/// Pressure beyond which this pipe node starts taking damage. Set to zero for no pressure damage.
/// </summary>
[DataField]
public float MaxPressure = 0;

public override void Initialize(EntityUid owner, IEntityManager entMan)
{
base.Initialize(owner, entMan);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: Longitudinal
maxPressure: 6750
- type: Sprite
layers:
- state: pipeStraight
Expand Down Expand Up @@ -137,6 +138,7 @@
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: SWBend
maxPressure: 6750
- type: Sprite
layers:
- state: pipeBend
Expand Down Expand Up @@ -181,6 +183,7 @@
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: TSouth
maxPressure: 6750
- type: Sprite
layers:
- state: pipeTJunction
Expand Down Expand Up @@ -223,6 +226,7 @@
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: Fourway
maxPressure: 6750
- type: Sprite
layers:
- state: pipeFourway
Expand Down
Loading