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

Model static fires equalizing with the local atmosphere #32760

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 11 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
37 changes: 24 additions & 13 deletions Content.Server/Atmos/Components/FlammableComponent.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,58 @@
using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Damage;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Prototypes;
using System.Linq;

namespace Content.Server.Atmos.Components
{
[RegisterComponent]
public sealed partial class FlammableComponent : Component
{
// An array which represents the atmos gases as mols, Specifies 1 mol of Oxygen.
private static float[] _standardOxygenMols = new float[] { 1f }.Concat(Enumerable.Repeat(0f, Atmospherics.AdjustedNumberOfGases - 1)).ToArray();

[DataField]
public bool Resisting;

[ViewVariables(VVAccess.ReadWrite)]
/// <summary>
/// A gas mix which specifies the minimum mols of each gas needed for the entity to burn. Set to null to burn without atmosphere.
/// </summary>
[DataField]
public GasMixture? FuelGasMix = new GasMixture(_standardOxygenMols, Atmospherics.T20C, 1f);

[DataField]
public bool OnFire;

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float FireStacks;

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float MaximumFireStacks = 10f;

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float MinimumFireStacks = -10f;

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public string FlammableFixtureID = "flammable";

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float MinIgnitionTemperature = 373.15f;

[ViewVariables(VVAccess.ReadWrite)]
/// <summary>
/// The peak temperature the entity will reach by burning alone.
/// </summary>
[DataField]
public float PeakFlameTemperature = 1500.00f; // vaguely how hot an incenerator gets.

[DataField]
public bool FireSpread { get; private set; } = false;

[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool CanResistFire { get; private set; } = false;

[DataField(required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier Damage = new(); // Empty by default, we don't want any funny NREs.

/// <summary>
Expand All @@ -56,31 +64,34 @@ public sealed partial class FlammableComponent : Component
/// <summary>
/// Should the component be set on fire by interactions with isHot entities
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool AlwaysCombustible = false;

/// <summary>
/// Can the component anyhow lose its FireStacks?
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool CanExtinguish = true;

/// <summary>
/// How many firestacks should be applied to component when being set on fire?
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float FirestacksOnIgnite = 2.0f;

/// <summary>
/// Determines how quickly the object will fade out. With positive values, the object will flare up instead of going out.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float FirestackFade = -0.1f;

[DataField]
public ProtoId<AlertPrototype> FireAlert = "Fire";

/// <summary>
/// A maximum quantity of gas to be added to the atmosphere every update while on fire.
/// </summary>
[DataField]
public GasMixture? EmissiveGasMix;
}
}
2 changes: 1 addition & 1 deletion Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public override void Update(float frameTime)
var query = EntityQueryEnumerator<AtmosExposedComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var transform))
{
var air = GetContainingMixture((uid, transform));
var air = GetContainingMixture((uid, transform), false, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

This would need to be in a separate PR if we make this change. I need to spend some time looking at this and see if exciting the mixture is really needed to get temperature to transfer correctly with AtmosExposed, but this may have significant detrimental performance impacts.

Copy link
Contributor

Choose a reason for hiding this comment

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

As a nitpick, please name the optional non-default arguments (e.g. GetContainingMixture((uid, transform), excite: true). This makes it a lot more apparent what the argument is, especially if someone is reviewing from a diff/GitHub/anything but Rider.

Copy link
Contributor Author

@IProduceWidgets IProduceWidgets Oct 15, 2024

Choose a reason for hiding this comment

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

When I tested it without exciting it did work, but then the room's gas mix could have different temperatures in different tiles that wouldn't change until something else excited the gas in the room.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would need to be in a separate PR if we make this change.

In the interest of fixing the bug, Ill move the gas emission stuff to another PR, I think for it to work I need burning to have temperature behave reasonably so it makes more sense to start there. I do agree that its kind of soaped into something else in supporting making static fires modeled now.


if (air == null)
continue;
Expand Down
46 changes: 39 additions & 7 deletions Content.Server/Atmos/EntitySystems/FlammableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Random;
using Content.Shared.Atmos.Piping.Binary.Components;

namespace Content.Server.Atmos.EntitySystems
{
Expand Down Expand Up @@ -82,6 +83,7 @@ public override void Initialize()
SubscribeLocalEvent<ExtinguishOnInteractComponent, ActivateInWorldEvent>(OnExtinguishActivateInWorld);

SubscribeLocalEvent<IgniteOnHeatDamageComponent, DamageChangedEvent>(OnDamageChanged);

IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
}

private void OnMeleeHit(EntityUid uid, IgniteOnMeleeHitComponent component, MeleeHitEvent args)
Expand Down Expand Up @@ -413,7 +415,6 @@ public override void Update(float frameTime)

_timer -= UpdateTime;

// TODO: This needs cleanup to take off the crust from TemperatureComponent and shit.
var query = EntityQueryEnumerator<FlammableComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var flammable, out _))
{
Expand All @@ -433,20 +434,51 @@ public override void Update(float frameTime)

if (flammable.FireStacks > 0)
{
var air = _atmosphereSystem.GetContainingMixture(uid);
var air = _atmosphereSystem.GetContainingMixture(uid, false, true);
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved

// If we're in an oxygenless environment, put the fire out.
if (air == null || air.GetMoles(Gas.Oxygen) < 1f)
if (flammable.FuelGasMix != null)
{
Extinguish(uid, flammable);
continue;
// If we're in no atmos extinguish.
if (air == null)
{
Extinguish(uid, flammable);
continue;
}

// If our local atmos doesnt have enough of the required gases extinguish.
if (!flammable.FuelGasMix.LEQMoles(air))
{
Extinguish(uid, flammable);
continue;
}
}

var source = EnsureComp<IgnitionSourceComponent>(uid);
_ignitionSourceSystem.SetIgnited((uid, source));

if (TryComp(uid, out TemperatureComponent? temp))
_temperatureSystem.ChangeHeat(uid, 12500 * flammable.FireStacks, false, temp);
{
// get hot
EnsureComp<AtmosExposedComponent>(uid); // required for the entity to ever cool down.
if (!_physicsQuery.TryComp(uid, out var physics))
continue;

var maxDeltaT = flammable.PeakFlameTemperature - temp.CurrentTemperature;
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
if (maxDeltaT > 0) // we dont want to burn and get colder.
{

var tempBump = MathF.Sqrt(maxDeltaT * temp.SpecificHeat * physics.Mass) * flammable.FireStacks; //monotonic function, slows as it reaches maxT
_temperatureSystem.ChangeHeat(uid, tempBump, false, temp);
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a really roundabout way to do _temperatureSystem.ForceChangeTemperature.

I still don't think this is necessarily the right way to do this, because it does not respect energy conservation in terms of energy supplied by the candle.

Copy link
Contributor Author

@IProduceWidgets IProduceWidgets Oct 15, 2024

Choose a reason for hiding this comment

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

I looked into this a bit more, if we do this (which is something like was originally there):
image
It does eventually equalize like we would expect, and so we can output a constant* amount of energy while burning into the room, and as long as something is burning the room will continue to get arbitrarily hotter.
However, as it turns out, most things contain a simply ludicrous amount of energy. irl we're talking like 15,000,000j (15megajoules) per kg of wood. In irl this doesnt turn your fireplace into a nuclear bomb because most of the heat is lost to the sky, but a spacestation is an enclosed box, so it conserves all the energy. This would mean if we used real values almost any fire is immediately ultra-deadly.

Understanding that, I think we have two alternatives.

  • We can go with a constant joule output, and just make up (much lower than irl) joule quantities for entities to make fires not immediately awful, but rooms will continue to heat if you burn more stuff.
  • We can do some sort of asymptoting function and have the room reach a maximum temperature based on whatever the burned entity is.

Both solutions seem fine to me, I do think the asymtote would be easier to maintain and harder to abuse.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, so superconduction is the thing that makes the station not an ideal enclosed box. The built-in "asymptoting function"

Copy link
Contributor Author

@IProduceWidgets IProduceWidgets Oct 15, 2024

Choose a reason for hiding this comment

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

Or I guess we can do a hybrid, and have it asymptote with a smaller minimum joule gain.

Copy link
Contributor Author

@IProduceWidgets IProduceWidgets Oct 15, 2024

Choose a reason for hiding this comment

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

Well hold on. That might be total joules contained in the entity. So then, ~3hr burn time, we would get something like 1,400j/s Thats still pretty high, but not as high.

Okay sure, lets try it just with no asymptote, and just see how it feels.

}

// release gas
if (air != null && flammable.EmissiveGasMix != null)
{
flammable.EmissiveGasMix.Temperature = air.Temperature;
var releasingGas = flammable.EmissiveGasMix.Clone(); // generate gas for the entity's entire burning lifespan, but we use ReleaseGasTo to handle temperature/pressure for us since those will change for other reasons.
_atmosphereSystem.ReleaseGasTo(releasingGas, air, releasingGas.Pressure); // doing it this way means that we dont need to track the entity damage values or know its destructible or mobstate thresholds.
} // the downside, and a possible later rework, is that you ""lose"" some gas if the candle is burning in an atmosphere where it can't emit due to pressure.
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
} // the yml is also slightly cumbersome, because we now specify a "maximum release mix per update (second)" instead of a total gasmix in the entity.

var ev = new GetFireProtectionEvent();
// let the thing on fire handle it
Expand Down
3 changes: 2 additions & 1 deletion Content.Server/Temperature/Systems/TemperatureSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ private void OnAtmosExposedUpdate(EntityUid uid, TemperatureComponent temperatur
var heatCapacity = GetHeatCapacity(uid, temperature);
var heat = temperatureDelta * (airHeatCapacity * heatCapacity /
(airHeatCapacity + heatCapacity));
ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature);
ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature); // move heat to entity
_atmosphere.AddHeat(args.GasMixture, -1 * heat * temperature.AtmosTemperatureTransferEfficiency); // remove heat from air
Comment on lines +163 to +164
Copy link
Contributor

Choose a reason for hiding this comment

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

Separate PR, because this is a bug fix to TemperatureSystem.

Copy link
Contributor Author

@IProduceWidgets IProduceWidgets Oct 15, 2024

Choose a reason for hiding this comment

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

Its necessary for temp-emitting fires.
Without it, temperatures cant equalize and run away.

}

public float GetHeatCapacity(EntityUid uid, TemperatureComponent? comp = null, PhysicsComponent? physics = null)
Expand Down
20 changes: 19 additions & 1 deletion Content.Shared/Atmos/GasMixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Content.Shared.Atmos.EntitySystems;
Expand Down Expand Up @@ -271,6 +271,24 @@ public bool Equals(GasMixture? other)
&& Volume.Equals(other.Volume);
}

/// <summary>
/// Returns True if supersetMix has more moles for all gases by mole quantity only.
/// </summary>
/// <param name="supersetMix"></param>
/// <param name="subsetMix"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
public bool LEQMoles(GasMixture supersetMix)
{
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < Atmospherics.AdjustedNumberOfGases; i++)
{
if (supersetMix.GetMoles(i) < GetMoles(i))
{
return false;
}
}
return true;
}

[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode()
{
Expand Down
10 changes: 10 additions & 0 deletions Resources/Prototypes/Catalog/Cargo/cargo_atmospherics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,13 @@
# cost: 15500
# category: cargoproduct-category-name-atmospherics
# group: market

- type: cargoProduct
id: AtmosphericsCandles
icon:
sprite: Objects/Misc/candles.rsi
state: candle-big
product: CrateCandlesAir
cost: 2500 # its ~ 32000 mols of Air mix potential. Cheap though because no cannister, and youve got to cool it and collect it to use it.
category: cargoproduct-category-name-atmospherics
group: market
11 changes: 11 additions & 0 deletions Resources/Prototypes/Catalog/Fills/Boxes/engineering.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- type: entity
name: air candle box
parent: BoxCandle
id: BoxCandleAir
components:
- type: StorageFill
contents:
- id: CandleGasNitrogen
amount: 9
- id: CandleGasOxygen
amount: 3
12 changes: 12 additions & 0 deletions Resources/Prototypes/Catalog/Fills/Crates/engineering.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,15 @@
- type: StorageFill
contents:
- id: SpaceHeaterFlatpack

- type: entity
id: CrateCandlesAir
parent: CrateEngineeringSecure
name: air candle crate
description: A crate containing 2 boxes of air gas candles.
components:
- type: StorageFill
contents:
- id: BoxCandleAir
amount: 2
- id: BoxFlare
2 changes: 1 addition & 1 deletion Resources/Prototypes/Entities/Objects/Misc/candles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
canResistFire: false
alwaysCombustible: true
canExtinguish: true
firestacksOnIgnite: 3.0
firestacksOnIgnite: 3.3
IProduceWidgets marked this conversation as resolved.
Show resolved Hide resolved
firestackFade: -0.01
damage:
types:
Expand Down
Loading
Loading