From ca68fbe818902318eb6666298527f1c638782728 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?=
<6766154+Zumorica@users.noreply.github.com>
Date: Thu, 13 Aug 2020 14:18:26 +0200
Subject: [PATCH] Add heat conduction (#1653)
---
Content.Server/Atmos/GasMixture.cs | 29 ++-
.../Atmos/IGridAtmosphereComponent.cs | 12 ++
Content.Server/Atmos/TileAtmosphere.cs | 176 +++++++++++++++++-
.../Atmos/GridAtmosphereComponent.cs | 41 +++-
Content.Shared/Atmos/Atmospherics.cs | 13 ++
Content.Shared/Maps/ContentTileDefinition.cs | 10 +
Content.Shared/Maps/TurfHelpers.cs | 28 +++
SpaceStation14.sln.DotSettings | 1 +
8 files changed, 301 insertions(+), 9 deletions(-)
diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs
index 1455402779..f886acad84 100644
--- a/Content.Server/Atmos/GasMixture.cs
+++ b/Content.Server/Atmos/GasMixture.cs
@@ -330,7 +330,7 @@ namespace Content.Server.Atmos
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void TemperatureShare(GasMixture sharer, float conductionCoefficient)
+ public float TemperatureShare(GasMixture sharer, float conductionCoefficient)
{
var temperatureDelta = TemperatureArchived - sharer.TemperatureArchived;
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
@@ -349,6 +349,30 @@ namespace Content.Server.Atmos
sharer.Temperature = MathF.Abs(MathF.Max(sharer.Temperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
}
}
+
+ return sharer.Temperature;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float TemperatureShare(float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity)
+ {
+ var temperatureDelta = TemperatureArchived - sharerTemperature;
+ if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
+ {
+ var heatCapacity = HeatCapacityArchived;
+
+ if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
+ {
+ var heat = conductionCoefficient * temperatureDelta * (heatCapacity * sharerHeatCapacity / (heatCapacity + sharerHeatCapacity));
+
+ if (!Immutable)
+ Temperature = MathF.Abs(MathF.Max(Temperature - heat / heatCapacity, Atmospherics.TCMB));
+
+ sharerTemperature = MathF.Abs(MathF.Max(sharerTemperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
+ }
+ }
+
+ return sharerTemperature;
}
public enum GasCompareResult
@@ -357,6 +381,9 @@ namespace Content.Server.Atmos
TemperatureExchange = -1,
}
+ ///
+ /// Compares sample to self to see if within acceptable ranges that group processing may be enabled.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GasCompareResult Compare(GasMixture sample)
{
diff --git a/Content.Server/Atmos/IGridAtmosphereComponent.cs b/Content.Server/Atmos/IGridAtmosphereComponent.cs
index 5fdd25b2e3..430c674bce 100644
--- a/Content.Server/Atmos/IGridAtmosphereComponent.cs
+++ b/Content.Server/Atmos/IGridAtmosphereComponent.cs
@@ -66,6 +66,18 @@ namespace Content.Server.Atmos
///
void RemoveHotspotTile(TileAtmosphere tile);
+ ///
+ /// Marks a tile as superconductive so it can be processed.
+ ///
+ ///
+ void AddSuperconductivityTile(TileAtmosphere tile);
+
+ ///
+ /// Removes a tile from the superconductivity processing list.
+ ///
+ ///
+ void RemoveSuperconductivityTile(TileAtmosphere tile);
+
///
/// Marks a tile has having high pressure differences that need to be equalized.
///
diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs
index dfb3ad675f..69d74ce995 100644
--- a/Content.Server/Atmos/TileAtmosphere.cs
+++ b/Content.Server/Atmos/TileAtmosphere.cs
@@ -7,6 +7,7 @@ using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Shared.Atmos;
using Content.Shared.Audio;
+using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
@@ -27,9 +28,21 @@ namespace Content.Server.Atmos
[Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!;
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!;
+ [ViewVariables]
private int _archivedCycle = 0;
+
+ [ViewVariables]
private int _currentCycle = 0;
+ [ViewVariables]
+ private static GasTileOverlaySystem _gasTileOverlaySystem;
+
+ [ViewVariables]
+ private float _temperature = Atmospherics.T20C;
+
+ [ViewVariables]
+ private float _temperatureArchived = Atmospherics.T20C;
+
// I know this being static is evil, but I seriously can't come up with a better solution to sound spam.
private static int _soundCooldown = 0;
@@ -39,6 +52,12 @@ namespace Content.Server.Atmos
[ViewVariables]
public float PressureDifference { get; set; } = 0;
+ [ViewVariables(VVAccess.ReadWrite)]
+ public float HeatCapacity { get; set; } = 1f;
+
+ [ViewVariables]
+ public float ThermalConductivity => Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.05f;
+
[ViewVariables]
public bool Excited { get; set; } = false;
@@ -54,11 +73,15 @@ namespace Content.Server.Atmos
[ViewVariables]
public Hotspot Hotspot;
+ [ViewVariables]
private Direction _pressureDirection;
[ViewVariables]
public GridId GridIndex { get; }
+ [ViewVariables]
+ public TileRef? Tile => GridIndices.GetTileRef(GridIndex);
+
[ViewVariables]
public MapIndices GridIndices { get; }
@@ -68,6 +91,9 @@ namespace Content.Server.Atmos
[ViewVariables]
public GasMixture Air { get; set; }
+ [ViewVariables]
+ public bool BlocksAir => _gridAtmosphereComponent.IsAirBlocked(GridIndices);
+
public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, MapIndices gridIndices, GasMixture mixture = null)
{
IoCManager.InjectDependencies(this);
@@ -80,8 +106,9 @@ namespace Content.Server.Atmos
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Archive(int fireCount)
{
- _archivedCycle = fireCount;
Air?.Archive();
+ _archivedCycle = fireCount;
+ _temperatureArchived = _temperature;
}
public void HotspotExpose(float exposedTemperature, float exposedVolume, bool soh = false)
@@ -704,10 +731,150 @@ namespace Content.Server.Atmos
// TODO ATMOS Let all entities in this tile know about the fire?
}
+ private bool ConsiderSuperconductivity()
+ {
+ if (ThermalConductivity == 0f)
+ return false;
+
+ _gridAtmosphereComponent.AddSuperconductivityTile(this);
+ return true;
+ }
+
private bool ConsiderSuperconductivity(bool starting)
{
- // TODO ATMOS
- return false;
+ if (Air.Temperature < (starting
+ ? Atmospherics.MinimumTemperatureStartSuperConduction
+ : Atmospherics.MinimumTemperatureForSuperconduction))
+ return false;
+
+ return !(Air.HeatCapacity < Atmospherics.MCellWithRatio) && ConsiderSuperconductivity();
+ }
+
+ public void Superconduct()
+ {
+ var directions = ConductivityDirections();
+ var adjacentTiles = _gridAtmosphereComponent.GetAdjacentTiles(GridIndices, true);
+
+ if (directions.Length > 0)
+ {
+ foreach (var direction in directions)
+ {
+ if (!adjacentTiles.TryGetValue(direction, out var adjacent)) continue;
+
+ if (adjacent.ThermalConductivity == 0f)
+ continue;
+
+ if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter)
+ adjacent.Archive(_gridAtmosphereComponent.UpdateCounter);
+
+ adjacent.NeighborConductWithSource(this);
+
+ adjacent.ConsiderSuperconductivity();
+ }
+ }
+
+ RadiateToSpace();
+
+ FinishSuperconduction();
+ }
+
+ private void FinishSuperconduction()
+ {
+ // Conduct with air on my tile if I have it
+ if (!BlocksAir)
+ {
+ _temperature = Air.TemperatureShare(ThermalConductivity, _temperature, HeatCapacity);
+ }
+
+ FinishSuperconduction(BlocksAir ? _temperature : Air.Temperature);
+ }
+
+ private void FinishSuperconduction(float temperature)
+ {
+ // Make sure it's still hot enough to continue conducting.
+ if (temperature < Atmospherics.MinimumTemperatureForSuperconduction)
+ {
+ _gridAtmosphereComponent.RemoveSuperconductivityTile(this);
+ }
+ }
+
+ private void NeighborConductWithSource(TileAtmosphere other)
+ {
+ if (BlocksAir)
+ {
+ if (!other.BlocksAir)
+ {
+ other.TemperatureShareOpenToSolid(this);
+ }
+ else
+ {
+ other.TemperatureShareMutualSolid(this, ThermalConductivity);
+ }
+
+ TemperatureExpose(null, _temperature, _gridAtmosphereComponent.GetVolumeForCells(1));
+ return;
+ }
+
+ if (!other.BlocksAir)
+ {
+ other.Air.TemperatureShare(Air, Atmospherics.WindowHeatTransferCoefficient);
+ }
+ else
+ {
+ TemperatureShareOpenToSolid(other);
+ }
+
+ _gridAtmosphereComponent.AddActiveTile(this);
+ }
+
+ private void TemperatureShareOpenToSolid(TileAtmosphere other)
+ {
+ other._temperature =
+ Air.TemperatureShare(other.ThermalConductivity, other._temperature, other.HeatCapacity);
+ }
+
+ private void TemperatureShareMutualSolid(TileAtmosphere other, float conductionCoefficient)
+ {
+ var deltaTemperature = (_temperatureArchived - other._temperatureArchived);
+ if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider
+ && HeatCapacity != 0f && other.HeatCapacity != 0f)
+ {
+ var heat = conductionCoefficient * deltaTemperature *
+ (HeatCapacity * other.HeatCapacity / (HeatCapacity + other.HeatCapacity));
+
+ _temperature -= heat / HeatCapacity;
+ other._temperature += heat / other.HeatCapacity;
+ }
+ }
+
+ public void RadiateToSpace()
+ {
+ // Considering 0ÂșC as the break even point for radiation in and out.
+ if (_temperature > Atmospherics.T0C)
+ {
+ // Hardcoded space temperature.
+ var deltaTemperature = (_temperatureArchived - Atmospherics.TCMB);
+ if ((HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
+ {
+ var heat = ThermalConductivity * deltaTemperature * (HeatCapacity *
+ Atmospherics.HeatCapacityVacuum / (HeatCapacity + Atmospherics.HeatCapacityVacuum));
+
+ _temperature -= heat;
+ }
+ }
+ }
+
+ public Direction[] ConductivityDirections()
+ {
+ if(BlocksAir)
+ {
+ if(_archivedCycle < _gridAtmosphereComponent.UpdateCounter)
+ Archive(_gridAtmosphereComponent.UpdateCounter);
+ return Cardinal;
+ }
+
+ // TODO ATMOS check if this is correct
+ return Cardinal;
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -820,7 +987,6 @@ namespace Content.Server.Atmos
//throw new NotImplementedException();
}
-
private void React()
{
// TODO ATMOS I think this is enough? gotta make sure...
@@ -880,8 +1046,6 @@ namespace Content.Server.Atmos
Direction.North, Direction.East, Direction.South, Direction.West
};
- private static GasTileOverlaySystem _gasTileOverlaySystem;
-
public void TemperatureExpose(GasMixture mixture, float temperature, float cellVolume)
{
// TODO ATMOS do this
diff --git a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
index ea05c19642..c7207ffa4e 100644
--- a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
+++ b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
@@ -65,6 +65,9 @@ namespace Content.Server.GameObjects.Components.Atmos
[ViewVariables]
private readonly HashSet _hotspotTiles = new HashSet(1000);
+ [ViewVariables]
+ private readonly HashSet _superconductivityTiles = new HashSet(1000);
+
[ViewVariables]
private readonly HashSet _invalidatedCoords = new HashSet(1000);
@@ -81,6 +84,7 @@ namespace Content.Server.GameObjects.Components.Atmos
ExcitedGroups,
HighPressureDelta,
Hotspots,
+ Superconductivity,
}
///
@@ -237,6 +241,18 @@ namespace Content.Server.GameObjects.Components.Atmos
_hotspotTiles.Remove(tile);
}
+ public void AddSuperconductivityTile(TileAtmosphere tile)
+ {
+ if (tile?.GridIndex != _grid.Index) return;
+ _superconductivityTiles.Add(tile);
+ }
+
+ public void RemoveSuperconductivityTile(TileAtmosphere tile)
+ {
+ if (tile == null) return;
+ _superconductivityTiles.Remove(tile);
+ }
+
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHighPressureDelta(TileAtmosphere tile)
@@ -302,14 +318,14 @@ namespace Content.Server.GameObjects.Components.Atmos
return _grid.GetTileRef(indices).Tile.IsEmpty;
}
- public Dictionary GetAdjacentTiles(MapIndices indices)
+ public Dictionary GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false)
{
var sides = new Dictionary();
foreach (var dir in Cardinal)
{
var side = indices.Offset(dir);
var tile = GetTile(side);
- if(tile?.Air != null)
+ if(tile?.Air != null || includeAirBlocked)
sides[dir] = tile;
}
@@ -361,6 +377,10 @@ namespace Content.Server.GameObjects.Components.Atmos
break;
case ProcessState.Hotspots:
ProcessHotspots();
+ _state = ProcessState.Superconductivity;
+ break;
+ case ProcessState.Superconductivity:
+ ProcessSuperconductivity();
_state = ProcessState.TileEqualize;
break;
}
@@ -463,6 +483,23 @@ namespace Content.Server.GameObjects.Components.Atmos
}
}
+ private void ProcessSuperconductivity()
+ {
+ _stopwatch.Restart();
+
+ var number = 0;
+ foreach (var superconductivity in _superconductivityTiles.ToArray())
+ {
+ superconductivity.Superconduct();
+
+ if (number++ < LagCheckIterations) continue;
+ number = 0;
+ // Process the rest next time.
+ if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
+ return;
+ }
+ }
+
private AirtightComponent GetObstructingComponent(MapIndices indices)
{
foreach (var v in _grid.GetSnapGridCell(indices, SnapGridOffset.Center))
diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs
index 4659ab2441..2b1e2a5dc6 100644
--- a/Content.Shared/Atmos/Atmospherics.cs
+++ b/Content.Shared/Atmos/Atmospherics.cs
@@ -63,6 +63,11 @@ namespace Content.Shared.Atmos
///
public const float MolesCellStandard = (OneAtmosphere * CellVolume / (T20C * R));
+ ///
+ /// Compared against for superconduction.
+ ///
+ public const float MCellWithRatio = (MolesCellStandard * 0.005f);
+
public const float OxygenStandard = 0.21f;
public const float NitrogenStandard = 0.79f;
@@ -83,6 +88,11 @@ namespace Content.Shared.Atmos
public const float OpenHeatTransferCoefficient = 0.4f;
+ ///
+ /// Hack to make vacuums cold, sacrificing realism for gameplay.
+ ///
+ public const float HeatCapacityVacuum = 7000f;
+
///
/// Ratio of air that must move to/from a tile to reset group processing
///
@@ -116,6 +126,7 @@ namespace Content.Shared.Atmos
/// Minimum temperature for starting superconduction.
///
public const float MinimumTemperatureStartSuperConduction = (T20C + 200f);
+ public const float MinimumTemperatureForSuperconduction = (T20C + 10f);
///
/// Minimum heat capacity.
@@ -214,6 +225,8 @@ namespace Content.Shared.Atmos
/// so it just applies this flat value).
///
public const int LowPressureDamage = 4;
+
+ public const float WindowHeatTransferCoefficient = 0.1f;
}
///
diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs
index 419a1bd812..c00e62fa87 100644
--- a/Content.Shared/Maps/ContentTileDefinition.cs
+++ b/Content.Shared/Maps/ContentTileDefinition.cs
@@ -26,6 +26,7 @@ namespace Content.Shared.Maps
public bool CanCrowbar { get; private set; }
public string FootstepSounds { get; private set; }
public float Friction { get; set; }
+ public float ThermalConductivity { get; set; }
public string ItemDropPrototypeName { get; private set; }
public void AssignTileId(ushort id)
@@ -68,6 +69,15 @@ namespace Content.Shared.Maps
Friction = 0;
}
+ if (mapping.TryGetNode("thermalConductivity", out node))
+ {
+ ThermalConductivity = node.AsFloat();
+ }
+ else
+ {
+ ThermalConductivity = 0.05f;
+ }
+
if (mapping.TryGetNode("item_drop", out node))
{
ItemDropPrototypeName = node.ToString();
diff --git a/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs
index 1f81bc6f8f..aa60e7168c 100644
--- a/Content.Shared/Maps/TurfHelpers.cs
+++ b/Content.Shared/Maps/TurfHelpers.cs
@@ -12,6 +12,34 @@ namespace Content.Shared.Maps
{
public static class TurfHelpers
{
+ ///
+ /// Returns the content tile definition for a tile.
+ ///
+ public static ContentTileDefinition GetContentTileDefinition(this Tile tile)
+ {
+ var tileDefinitionManager = IoCManager.Resolve();
+ return (ContentTileDefinition)tileDefinitionManager[tile.TypeId];
+ }
+
+ ///
+ /// Attempts to get the turf at map indices with grid id or null if no such turf is found.
+ ///
+ public static TileRef? GetTileRef(this MapIndices mapIndices, GridId gridId)
+ {
+ if (!gridId.IsValid())
+ return null;
+
+ var mapManager = IoCManager.Resolve();
+
+ if (!mapManager.TryGetGrid(gridId, out var grid))
+ return null;
+
+ if (!grid.TryGetTileRef(mapIndices, out var tile))
+ return null;
+
+ return tile;
+ }
+
///
/// Attempts to get the turf at a certain coordinates or null if no such turf is found.
///
diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings
index bd8ed2aff7..f9a53158a2 100644
--- a/SpaceStation14.sln.DotSettings
+++ b/SpaceStation14.sln.DotSettings
@@ -72,6 +72,7 @@
True
True
True
+ True
True
True
True