]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Splits temperature damage processing into its own component (#30515)
authorTemporalOroboros <TemporalOroboros@gmail.com>
Wed, 24 Dec 2025 06:37:11 +0000 (22:37 -0800)
committerGitHub <noreply@github.com>
Wed, 24 Dec 2025 06:37:11 +0000 (06:37 +0000)
* Creates TemperatureDamageThresholdsComponent

* Obsolete TemperatureComponent fields

* Use TemperatureDamageThresholdsComponent
Moves all the uses of the relocated TemperatureComponent fields to use the TDTC versions

* Removes the obsolete TemperatureComponent fields

* Update YAML definitions

* Update doc comments

* Split TemperatureSystem
Creates TemperatureDamageSystem and moves the damage handling from TemperatureSystem

* Cull unused using statements

* Use component-based damage tick scheduling

* Fix temperature damage processing
Check was inverted resulting in things never starting to take temperature damage

* Poke tests

* Add TemperatureDamageThresholds to new prototypes

* Move TemperatureDamageThresholdsComponent to Shared
Parity with TemperatureComponent

* While I'm here
Fixes warning regarding obsolete ProtoId validator attribute

* Fix YAML errors

* Fix merge errors

* Rename TemperatureDamageThresholdsComponent -> TemperatureDamageComponent

* Use ContentHelpers.RoundToLevels for temperature alerts

* Fix YML

* A fuckton of cleanup

* working cleanup

* fix

* misc additions

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
27 files changed:
Content.Server/Temperature/Components/ContainerTemperatureComponent.cs [new file with mode: 0644]
Content.Server/Temperature/Components/ContainerTemperatureDamageThresholdsComponent.cs [deleted file]
Content.Server/Temperature/Systems/TemperatureSystem.Damage.cs [new file with mode: 0644]
Content.Server/Temperature/Systems/TemperatureSystem.cs
Content.Server/Zombies/ZombieSystem.Transform.cs
Content.Shared/Temperature/Components/TemperatureComponent.cs
Content.Shared/Temperature/Components/TemperatureDamageComponent.cs [new file with mode: 0644]
Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs
Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Resources/Prototypes/Entities/Mobs/NPCs/carp.yml
Resources/Prototypes/Entities/Mobs/NPCs/pets.yml
Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml
Resources/Prototypes/Entities/Mobs/NPCs/space.yml
Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml
Resources/Prototypes/Entities/Mobs/Player/dragon.yml
Resources/Prototypes/Entities/Mobs/Species/base.yml
Resources/Prototypes/Entities/Mobs/Species/moth.yml
Resources/Prototypes/Entities/Mobs/Species/reptilian.yml
Resources/Prototypes/Entities/Mobs/Species/vulpkanin.yml
Resources/Prototypes/Entities/Mobs/base.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml
Resources/Prototypes/Entities/Objects/Misc/ice_crust.yml
Resources/Prototypes/Entities/Objects/Misc/kudzu.yml
Resources/Prototypes/Entities/Objects/Misc/spaceshroom.yml
Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml
Resources/Prototypes/Entities/Structures/spider_web.yml

diff --git a/Content.Server/Temperature/Components/ContainerTemperatureComponent.cs b/Content.Server/Temperature/Components/ContainerTemperatureComponent.cs
new file mode 100644 (file)
index 0000000..7c7b1e8
--- /dev/null
@@ -0,0 +1,11 @@
+namespace Content.Server.Temperature.Components;
+
+[RegisterComponent]
+public sealed partial class ContainerTemperatureComponent : Component
+{
+    [DataField]
+    public float? HeatDamageThreshold;
+
+    [DataField]
+    public float? ColdDamageThreshold;
+}
diff --git a/Content.Server/Temperature/Components/ContainerTemperatureDamageThresholdsComponent.cs b/Content.Server/Temperature/Components/ContainerTemperatureDamageThresholdsComponent.cs
deleted file mode 100644 (file)
index 024b8a0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Content.Server.Temperature.Components;
-
-[RegisterComponent]
-public sealed partial class ContainerTemperatureDamageThresholdsComponent: Component
-{
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float? HeatDamageThreshold;
-
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float? ColdDamageThreshold;
-}
diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.Damage.cs b/Content.Server/Temperature/Systems/TemperatureSystem.Damage.cs
new file mode 100644 (file)
index 0000000..d436977
--- /dev/null
@@ -0,0 +1,279 @@
+using Content.Server.Administration.Logs;
+using Content.Server.Body.Components;
+using Content.Server.Temperature.Components;
+using Content.Shared.Alert;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
+using Content.Shared.Database;
+using Content.Shared.Rounding;
+using Content.Shared.Temperature;
+using Content.Shared.Temperature.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Temperature.Systems;
+
+/// <summary>
+/// Handles entities taking damage from being too hot or too cold.
+/// Also handles alerts relevant to the same.
+/// </summary>
+public sealed partial class TemperatureSystem
+{
+    [Dependency] private readonly AlertsSystem _alerts = default!;
+    [Dependency] private readonly DamageableSystem _damageable = default!;
+    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly IGameTiming _gameTiming = default!;
+
+    private EntityQuery<TemperatureDamageComponent> _tempDamageQuery;
+    private EntityQuery<ContainerTemperatureComponent> _containerTemperatureQuery;
+    private EntityQuery<ThermalRegulatorComponent> _thermalRegulatorQuery;
+
+    /// <summary>
+    ///     All the components that will have their damage updated at the end of the tick.
+    ///     This is done because both AtmosExposed and Flammable call ChangeHeat in the same tick, meaning
+    ///     that we need some mechanism to ensure it doesn't double-dip on damage for both calls.
+    /// </summary>
+    public HashSet<Entity<TemperatureDamageComponent>> ShouldUpdateDamage = new();
+
+    /// <summary>
+    /// Alert prototype for Temperature.
+    /// </summary>
+    public static readonly ProtoId<AlertCategoryPrototype> TemperatureAlertCategory = "Temperature";
+
+    /// <summary>
+    /// The maximum severity applicable to temperature alerts.
+    /// </summary>
+    public static readonly short MaxTemperatureAlertSeverity = 3;
+
+    /// <summary>
+    /// On a scale of 0. to 1. where 0. is the ideal temperature and 1. is a temperature damage threshold this is the point where the component starts raising temperature alerts.
+    /// </summary>
+    public static readonly float MinAlertTemperatureScale = 0.33f;
+
+    private void InitializeDamage()
+    {
+        SubscribeLocalEvent<AlertsComponent, OnTemperatureChangeEvent>(ServerAlert);
+
+        SubscribeLocalEvent<TemperatureDamageComponent, OnTemperatureChangeEvent>(EnqueueDamage);
+        SubscribeLocalEvent<TemperatureDamageComponent, EntityUnpausedEvent>(OnUnpaused);
+
+        // Allows overriding thresholds based on the parent's thresholds.
+        SubscribeLocalEvent<TemperatureDamageComponent, EntParentChangedMessage>(OnParentChange);
+        SubscribeLocalEvent<ContainerTemperatureComponent, ComponentStartup>(OnParentThresholdStartup);
+        SubscribeLocalEvent<ContainerTemperatureComponent, ComponentShutdown>(OnParentThresholdShutdown);
+
+        _tempDamageQuery = GetEntityQuery<TemperatureDamageComponent>();
+        _containerTemperatureQuery = GetEntityQuery<ContainerTemperatureComponent>();
+        _thermalRegulatorQuery = GetEntityQuery<ThermalRegulatorComponent>();
+    }
+
+    private void UpdateDamage()
+    {
+        foreach (var entity in ShouldUpdateDamage)
+        {
+            if (Deleted(entity) || Paused(entity))
+                continue;
+
+            var deltaTime = _gameTiming.CurTime - entity.Comp.LastUpdate;
+            if (entity.Comp.TakingDamage && deltaTime < entity.Comp.UpdateInterval)
+                continue;
+
+            ChangeDamage(entity, deltaTime);
+        }
+
+        ShouldUpdateDamage.Clear();
+    }
+
+    private void ChangeDamage(Entity<TemperatureDamageComponent> entity, TimeSpan deltaTime)
+    {
+        entity.Comp.LastUpdate = _gameTiming.CurTime;
+
+        if (!HasComp<DamageableComponent>(entity) || !TemperatureQuery.TryComp(entity, out var temperature))
+            return;
+
+        // See this link for where the scaling func comes from:
+        // https://www.desmos.com/calculator/0vknqtdvq9
+        // Based on a logistic curve, which caps out at MaxDamage
+        var heatK = 0.005;
+        var a = 1;
+        var y = entity.Comp.DamageCap;
+        var c = y * 2;
+
+        var heatDamageThreshold = entity.Comp.ParentHeatDamageThreshold ?? entity.Comp.HeatDamageThreshold;
+        var coldDamageThreshold = entity.Comp.ParentColdDamageThreshold ?? entity.Comp.ColdDamageThreshold;
+
+        if (temperature.CurrentTemperature >= heatDamageThreshold)
+        {
+            if (!entity.Comp.TakingDamage)
+            {
+                _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(entity):entity} started taking high temperature damage");
+                entity.Comp.TakingDamage = true;
+            }
+
+            var diff = Math.Abs(temperature.CurrentTemperature - heatDamageThreshold);
+            var tempDamage = c / (1 + a * Math.Pow(Math.E, -heatK * diff)) - y;
+            _damageable.TryChangeDamage(entity.Owner, entity.Comp.HeatDamage * tempDamage * deltaTime.TotalSeconds, ignoreResistances: true, interruptsDoAfters: false);
+        }
+        else if (temperature.CurrentTemperature <= coldDamageThreshold)
+        {
+            if (!entity.Comp.TakingDamage)
+            {
+                _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(entity):entity} started taking low temperature damage");
+                entity.Comp.TakingDamage = true;
+            }
+
+            var diff = Math.Abs(temperature.CurrentTemperature - coldDamageThreshold);
+            var tempDamage =
+                Math.Sqrt(diff * (Math.Pow(entity.Comp.DamageCap.Double(), 2) / coldDamageThreshold));
+            _damageable.TryChangeDamage(entity.Owner, entity.Comp.ColdDamage * tempDamage * deltaTime.TotalSeconds, ignoreResistances: true, interruptsDoAfters: false);
+        }
+        else if (entity.Comp.TakingDamage)
+        {
+            _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(entity):entity} stopped taking temperature damage");
+            entity.Comp.TakingDamage = false;
+        }
+    }
+
+    private void ServerAlert(Entity<AlertsComponent> entity, ref OnTemperatureChangeEvent args)
+    {
+        ProtoId<AlertPrototype> type;
+        float threshold;
+        float idealTemp;
+
+        if (!_tempDamageQuery.TryComp(entity, out var thresholds))
+        {
+            _alerts.ClearAlertCategory(entity.Owner, TemperatureAlertCategory);
+            return;
+        }
+
+        if (_thermalRegulatorQuery.TryComp(entity, out var regulator) &&
+            regulator.NormalBodyTemperature > thresholds.ColdDamageThreshold &&
+            regulator.NormalBodyTemperature < thresholds.HeatDamageThreshold)
+        {
+            idealTemp = regulator.NormalBodyTemperature;
+        }
+        else
+        {
+            idealTemp = (thresholds.ColdDamageThreshold + thresholds.HeatDamageThreshold) / 2;
+        }
+
+        if (args.CurrentTemperature <= idealTemp)
+        {
+            type = thresholds.ColdAlert;
+            threshold = thresholds.ColdDamageThreshold;
+        }
+        else
+        {
+            type = thresholds.HotAlert;
+            threshold = thresholds.HeatDamageThreshold;
+        }
+
+        // Calculates a scale where 0.0 is the ideal temperature and 1.0 is where temperature damage begins
+        // The cold and hot scales will differ in their range if the ideal temperature is not exactly halfway between the thresholds
+        var tempScale = (args.CurrentTemperature - idealTemp) / (threshold - idealTemp);
+        var alertLevel = (short)ContentHelpers.RoundToLevels(tempScale - MinAlertTemperatureScale, 1.00f - MinAlertTemperatureScale, MaxTemperatureAlertSeverity + 1);
+
+        if (alertLevel > 0)
+            _alerts.ShowAlert(entity.AsNullable(), type, alertLevel);
+        else
+            _alerts.ClearAlertCategory(entity.AsNullable(), TemperatureAlertCategory);
+    }
+
+    private void EnqueueDamage(Entity<TemperatureDamageComponent> ent, ref OnTemperatureChangeEvent args)
+    {
+        if (ShouldUpdateDamage.Add(ent) && !ent.Comp.TakingDamage)
+            ent.Comp.LastUpdate = _gameTiming.CurTime;
+    }
+
+    private void OnUnpaused(Entity<TemperatureDamageComponent> ent, ref EntityUnpausedEvent args)
+    {
+        ent.Comp.LastUpdate += args.PausedTime;
+    }
+
+    private void OnParentChange(Entity<TemperatureDamageComponent> entity, ref EntParentChangedMessage args)
+    {
+        // We only need to update thresholds if the thresholds changed for the entity's ancestors.
+        var oldThresholds = args.OldParent != null
+            ? RecalculateParentThresholds(args.OldParent.Value)
+            : (null, null);
+        var xform = Transform(entity.Owner);
+        var newThresholds = RecalculateParentThresholds(xform.ParentUid);
+
+        if (oldThresholds != newThresholds)
+            RecursiveThresholdUpdate((entity, entity.Comp, xform));
+    }
+
+    private void OnParentThresholdStartup(Entity<ContainerTemperatureComponent> entity, ref ComponentStartup args)
+    {
+        RecursiveThresholdUpdate(entity.Owner);
+    }
+
+    private void OnParentThresholdShutdown(Entity<ContainerTemperatureComponent> entity, ref ComponentShutdown args)
+    {
+        RecursiveThresholdUpdate(entity.Owner);
+    }
+
+    /// <summary>
+    /// Recalculate and apply parent thresholds for the root entity and all its children.
+    /// </summary>
+    /// <param name="root">The root entity we're currently updating</param>
+    private void RecursiveThresholdUpdate(Entity<TemperatureDamageComponent?, TransformComponent?> root)
+    {
+        RecalculateAndApplyParentThresholds(root);
+
+        var xform = root.Comp2 ?? Transform(root);
+        var enumerator = xform.ChildEnumerator;
+        while (enumerator.MoveNext(out var child))
+        {
+            RecursiveThresholdUpdate(child);
+        }
+    }
+
+    /// <summary>
+    /// Recalculate parent thresholds and apply them on the uid temperature component.
+    /// </summary>
+    /// <param name="entity">The entity whose temperature damage thresholds we're updating</param>
+    private void RecalculateAndApplyParentThresholds(Entity<TemperatureDamageComponent?> entity)
+    {
+        if (!_tempDamageQuery.Resolve(entity, ref entity.Comp, logMissing: false))
+            return;
+
+        var newThresholds = RecalculateParentThresholds(Transform(entity).ParentUid);
+        entity.Comp.ParentHeatDamageThreshold = newThresholds.Item1;
+        entity.Comp.ParentColdDamageThreshold = newThresholds.Item2;
+    }
+
+    /// <summary>
+    /// Recalculate Parent Heat/Cold DamageThreshold by recursively checking each ancestor and fetching the
+    /// maximum HeatDamageThreshold and the minimum ColdDamageThreshold if any exists (aka the best value for each).
+    /// </summary>
+    /// <param name="initialParentUid">parent we start with</param>
+    private (float?, float?) RecalculateParentThresholds(EntityUid initialParentUid)
+    {
+        // Recursively check parents for the best threshold available
+        var parentUid = initialParentUid;
+        float? newHeatThreshold = null;
+        float? newColdThreshold = null;
+        while (parentUid.IsValid())
+        {
+            if (_containerTemperatureQuery.TryComp(parentUid, out var newThresholds))
+            {
+                if (newThresholds.HeatDamageThreshold != null)
+                {
+                    newHeatThreshold = Math.Max(newThresholds.HeatDamageThreshold.Value,
+                        newHeatThreshold ?? 0);
+                }
+
+                if (newThresholds.ColdDamageThreshold != null)
+                {
+                    newColdThreshold = Math.Min(newThresholds.ColdDamageThreshold.Value,
+                        newColdThreshold ?? float.MaxValue);
+                }
+            }
+
+            parentUid = Transform(parentUid).ParentUid;
+        }
+
+        return (newHeatThreshold, newColdThreshold);
+    }
+}
index 928b6ae9b514140e525eafabb40ae14440fd80e2..484912c780e106e77e695c9780085776e74437a3 100644 (file)
@@ -1,64 +1,32 @@
-using System.Linq;
-using Content.Server.Administration.Logs;
 using Content.Server.Atmos.EntitySystems;
-using Content.Server.Body.Components;
 using Content.Server.Temperature.Components;
-using Content.Shared.Alert;
 using Content.Shared.Atmos;
-using Content.Shared.Damage.Components;
-using Content.Shared.Damage.Systems;
-using Content.Shared.Database;
 using Content.Shared.Inventory;
 using Content.Shared.Rejuvenate;
 using Content.Shared.Temperature;
-using Robust.Shared.Prototypes;
 using Content.Shared.Projectiles;
 using Content.Shared.Temperature.Components;
 using Content.Shared.Temperature.Systems;
 
 namespace Content.Server.Temperature.Systems;
 
-public sealed class TemperatureSystem : SharedTemperatureSystem
+public sealed partial class TemperatureSystem : SharedTemperatureSystem
 {
-    [Dependency] private readonly AlertsSystem _alerts = default!;
     [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
-    [Dependency] private readonly DamageableSystem _damageable = default!;
-    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
-    [Dependency] private readonly TemperatureSystem _temperature = default!;
-
-    /// <summary>
-    ///     All the components that will have their damage updated at the end of the tick.
-    ///     This is done because both AtmosExposed and Flammable call ChangeHeat in the same tick, meaning
-    ///     that we need some mechanism to ensure it doesn't double dip on damage for both calls.
-    /// </summary>
-    public HashSet<Entity<TemperatureComponent>> ShouldUpdateDamage = new();
-
-    public float UpdateInterval = 1.0f;
-
-    private float _accumulatedFrametime;
-
-    public static readonly ProtoId<AlertCategoryPrototype> TemperatureAlertCategory = "Temperature";
 
     public override void Initialize()
     {
         base.Initialize();
 
-        SubscribeLocalEvent<TemperatureComponent, OnTemperatureChangeEvent>(EnqueueDamage);
         SubscribeLocalEvent<TemperatureComponent, AtmosExposedUpdateEvent>(OnAtmosExposedUpdate);
         SubscribeLocalEvent<TemperatureComponent, RejuvenateEvent>(OnRejuvenate);
-        SubscribeLocalEvent<AlertsComponent, OnTemperatureChangeEvent>(ServerAlert);
         Subs.SubscribeWithRelay<TemperatureProtectionComponent, ModifyChangedTemperatureEvent>(OnTemperatureChangeAttempt, held: false);
 
         SubscribeLocalEvent<InternalTemperatureComponent, MapInitEvent>(OnInit);
 
         SubscribeLocalEvent<ChangeTemperatureOnCollideComponent, ProjectileHitEvent>(ChangeTemperatureOnCollide);
 
-        // Allows overriding thresholds based on the parent's thresholds.
-        SubscribeLocalEvent<TemperatureComponent, EntParentChangedMessage>(OnParentChange);
-        SubscribeLocalEvent<ContainerTemperatureDamageThresholdsComponent, ComponentStartup>(
-            OnParentThresholdStartup);
-        SubscribeLocalEvent<ContainerTemperatureDamageThresholdsComponent, ComponentShutdown>(
-            OnParentThresholdShutdown);
+        InitializeDamage();
     }
 
     public override void Update(float frameTime)
@@ -88,49 +56,23 @@ public sealed class TemperatureSystem : SharedTemperatureSystem
             ForceChangeTemperature(uid, temp.CurrentTemperature - degrees, temp);
         }
 
-        UpdateDamage(frameTime);
-    }
-
-    private void UpdateDamage(float frameTime)
-    {
-        _accumulatedFrametime += frameTime;
-
-        if (_accumulatedFrametime < UpdateInterval)
-            return;
-        _accumulatedFrametime -= UpdateInterval;
-
-        if (!ShouldUpdateDamage.Any())
-            return;
-
-        foreach (var comp in ShouldUpdateDamage)
-        {
-            MetaDataComponent? metaData = null;
-
-            var uid = comp.Owner;
-            if (Deleted(uid, metaData) || Paused(uid, metaData))
-                continue;
-
-            ChangeDamage(uid, comp);
-        }
-
-        ShouldUpdateDamage.Clear();
+        UpdateDamage();
     }
 
     public void ForceChangeTemperature(EntityUid uid, float temp, TemperatureComponent? temperature = null)
     {
-        if (!Resolve(uid, ref temperature))
+        if (!TemperatureQuery.Resolve(uid, ref temperature))
             return;
 
-        float lastTemp = temperature.CurrentTemperature;
-        float delta = temperature.CurrentTemperature - temp;
+        var lastTemp = temperature.CurrentTemperature;
+        var delta = temperature.CurrentTemperature - temp;
         temperature.CurrentTemperature = temp;
-        RaiseLocalEvent(uid, new OnTemperatureChangeEvent(temperature.CurrentTemperature, lastTemp, delta),
-            true);
+        RaiseLocalEvent(uid, new OnTemperatureChangeEvent(temperature.CurrentTemperature, lastTemp, delta), broadcast: true);
     }
 
     public override void ChangeHeat(EntityUid uid, float heatAmount, bool ignoreHeatResistance = false, TemperatureComponent? temperature = null)
     {
-        if (!Resolve(uid, ref temperature, false))
+        if (!TemperatureQuery.Resolve(uid, ref temperature, false))
             return;
 
         if (!ignoreHeatResistance)
@@ -144,11 +86,10 @@ public sealed class TemperatureSystem : SharedTemperatureSystem
         temperature.CurrentTemperature += heatAmount / GetHeatCapacity(uid, temperature);
         float delta = temperature.CurrentTemperature - lastTemp;
 
-        RaiseLocalEvent(uid, new OnTemperatureChangeEvent(temperature.CurrentTemperature, lastTemp, delta), true);
+        RaiseLocalEvent(uid, new OnTemperatureChangeEvent(temperature.CurrentTemperature, lastTemp, delta), broadcast: true);
     }
 
-    private void OnAtmosExposedUpdate(EntityUid uid, TemperatureComponent temperature,
-        ref AtmosExposedUpdateEvent args)
+    private void OnAtmosExposedUpdate(EntityUid uid, TemperatureComponent temperature, ref AtmosExposedUpdateEvent args)
     {
         var transform = args.Transform;
 
@@ -158,17 +99,18 @@ public sealed class TemperatureSystem : SharedTemperatureSystem
         var temperatureDelta = args.GasMixture.Temperature - temperature.CurrentTemperature;
         var airHeatCapacity = _atmosphere.GetHeatCapacity(args.GasMixture, false);
         var heatCapacity = GetHeatCapacity(uid, temperature);
+        // TODO ATMOS: This heat transfer formula is really really wrong, it needs to be pulled out. Pending on HeatContainers.
         var heat = temperatureDelta * (airHeatCapacity * heatCapacity /
                                        (airHeatCapacity + heatCapacity));
         ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature);
     }
 
-    private void OnInit(EntityUid uid, InternalTemperatureComponent comp, MapInitEvent args)
+    private void OnInit(Entity<InternalTemperatureComponent> entity, ref MapInitEvent args)
     {
-        if (!TryComp<TemperatureComponent>(uid, out var temp))
+        if (!TemperatureQuery.TryComp(entity, out var temp))
             return;
 
-        comp.Temperature = temp.CurrentTemperature;
+        entity.Comp.Temperature = temp.CurrentTemperature;
     }
 
     private void OnRejuvenate(EntityUid uid, TemperatureComponent comp, RejuvenateEvent args)
@@ -176,116 +118,6 @@ public sealed class TemperatureSystem : SharedTemperatureSystem
         ForceChangeTemperature(uid, Atmospherics.T20C, comp);
     }
 
-    private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureChangeEvent args)
-    {
-        ProtoId<AlertPrototype> type;
-        float threshold;
-        float idealTemp;
-
-        if (!TryComp<TemperatureComponent>(uid, out var temperature))
-        {
-            _alerts.ClearAlertCategory(uid, TemperatureAlertCategory);
-            return;
-        }
-
-        if (TryComp<ThermalRegulatorComponent>(uid, out var regulator) &&
-            regulator.NormalBodyTemperature > temperature.ColdDamageThreshold &&
-            regulator.NormalBodyTemperature < temperature.HeatDamageThreshold)
-        {
-            idealTemp = regulator.NormalBodyTemperature;
-        }
-        else
-        {
-            idealTemp = (temperature.ColdDamageThreshold + temperature.HeatDamageThreshold) / 2;
-        }
-
-        if (args.CurrentTemperature <= idealTemp)
-        {
-            type = temperature.ColdAlert;
-            threshold = temperature.ColdDamageThreshold;
-        }
-        else
-        {
-            type = temperature.HotAlert;
-            threshold = temperature.HeatDamageThreshold;
-        }
-
-        // Calculates a scale where 1.0 is the ideal temperature and 0.0 is where temperature damage begins
-        // The cold and hot scales will differ in their range if the ideal temperature is not exactly halfway between the thresholds
-        var tempScale = (args.CurrentTemperature - threshold) / (idealTemp - threshold);
-        switch (tempScale)
-        {
-            case <= 0f:
-                _alerts.ShowAlert(uid, type, 3);
-                break;
-
-            case <= 0.4f:
-                _alerts.ShowAlert(uid, type, 2);
-                break;
-
-            case <= 0.66f:
-                _alerts.ShowAlert(uid, type, 1);
-                break;
-
-            case > 0.66f:
-                _alerts.ClearAlertCategory(uid, TemperatureAlertCategory);
-                break;
-        }
-    }
-
-    private void EnqueueDamage(Entity<TemperatureComponent> temperature, ref OnTemperatureChangeEvent args)
-    {
-        ShouldUpdateDamage.Add(temperature);
-    }
-
-    private void ChangeDamage(EntityUid uid, TemperatureComponent temperature)
-    {
-        if (!HasComp<DamageableComponent>(uid))
-            return;
-
-        // See this link for where the scaling func comes from:
-        // https://www.desmos.com/calculator/0vknqtdvq9
-        // Based on a logistic curve, which caps out at MaxDamage
-        var heatK = 0.005;
-        var a = 1;
-        var y = temperature.DamageCap;
-        var c = y * 2;
-
-        var heatDamageThreshold = temperature.ParentHeatDamageThreshold ?? temperature.HeatDamageThreshold;
-        var coldDamageThreshold = temperature.ParentColdDamageThreshold ?? temperature.ColdDamageThreshold;
-
-        if (temperature.CurrentTemperature >= heatDamageThreshold)
-        {
-            if (!temperature.TakingDamage)
-            {
-                _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} started taking high temperature damage");
-                temperature.TakingDamage = true;
-            }
-
-            var diff = Math.Abs(temperature.CurrentTemperature - heatDamageThreshold);
-            var tempDamage = c / (1 + a * Math.Pow(Math.E, -heatK * diff)) - y;
-            _damageable.TryChangeDamage(uid, temperature.HeatDamage * tempDamage, ignoreResistances: true, interruptsDoAfters: false);
-        }
-        else if (temperature.CurrentTemperature <= coldDamageThreshold)
-        {
-            if (!temperature.TakingDamage)
-            {
-                _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} started taking low temperature damage");
-                temperature.TakingDamage = true;
-            }
-
-            var diff = Math.Abs(temperature.CurrentTemperature - coldDamageThreshold);
-            var tempDamage =
-                Math.Sqrt(diff * (Math.Pow(temperature.DamageCap.Double(), 2) / coldDamageThreshold));
-            _damageable.TryChangeDamage(uid, temperature.ColdDamage * tempDamage, ignoreResistances: true, interruptsDoAfters: false);
-        }
-        else if (temperature.TakingDamage)
-        {
-            _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} stopped taking temperature damage");
-            temperature.TakingDamage = false;
-        }
-    }
-
     private void OnTemperatureChangeAttempt(EntityUid uid, TemperatureProtectionComponent component, ModifyChangedTemperatureEvent args)
     {
         var coefficient = args.TemperatureDelta < 0
@@ -300,118 +132,6 @@ public sealed class TemperatureSystem : SharedTemperatureSystem
 
     private void ChangeTemperatureOnCollide(Entity<ChangeTemperatureOnCollideComponent> ent, ref ProjectileHitEvent args)
     {
-        _temperature.ChangeHeat(args.Target, ent.Comp.Heat, ent.Comp.IgnoreHeatResistance);// adjust the temperature
-    }
-
-    private void OnParentChange(EntityUid uid, TemperatureComponent component,
-        ref EntParentChangedMessage args)
-    {
-        var temperatureQuery = GetEntityQuery<TemperatureComponent>();
-        var transformQuery = GetEntityQuery<TransformComponent>();
-        var thresholdsQuery = GetEntityQuery<ContainerTemperatureDamageThresholdsComponent>();
-        // We only need to update thresholds if the thresholds changed for the entity's ancestors.
-        var oldThresholds = args.OldParent != null
-            ? RecalculateParentThresholds(args.OldParent.Value, transformQuery, thresholdsQuery)
-            : (null, null);
-        var newThresholds = RecalculateParentThresholds(transformQuery.GetComponent(uid).ParentUid, transformQuery, thresholdsQuery);
-
-        if (oldThresholds != newThresholds)
-        {
-            RecursiveThresholdUpdate(uid, temperatureQuery, transformQuery, thresholdsQuery);
-        }
-    }
-
-    private void OnParentThresholdStartup(EntityUid uid, ContainerTemperatureDamageThresholdsComponent component,
-        ComponentStartup args)
-    {
-        RecursiveThresholdUpdate(uid, GetEntityQuery<TemperatureComponent>(), GetEntityQuery<TransformComponent>(),
-            GetEntityQuery<ContainerTemperatureDamageThresholdsComponent>());
-    }
-
-    private void OnParentThresholdShutdown(EntityUid uid, ContainerTemperatureDamageThresholdsComponent component,
-        ComponentShutdown args)
-    {
-        RecursiveThresholdUpdate(uid, GetEntityQuery<TemperatureComponent>(), GetEntityQuery<TransformComponent>(),
-            GetEntityQuery<ContainerTemperatureDamageThresholdsComponent>());
-    }
-
-    /// <summary>
-    /// Recalculate and apply parent thresholds for the root entity and all its descendant.
-    /// </summary>
-    /// <param name="root"></param>
-    /// <param name="temperatureQuery"></param>
-    /// <param name="transformQuery"></param>
-    /// <param name="tempThresholdsQuery"></param>
-    private void RecursiveThresholdUpdate(EntityUid root, EntityQuery<TemperatureComponent> temperatureQuery,
-        EntityQuery<TransformComponent> transformQuery,
-        EntityQuery<ContainerTemperatureDamageThresholdsComponent> tempThresholdsQuery)
-    {
-        RecalculateAndApplyParentThresholds(root, temperatureQuery, transformQuery, tempThresholdsQuery);
-
-        var enumerator = Transform(root).ChildEnumerator;
-        while (enumerator.MoveNext(out var child))
-        {
-            RecursiveThresholdUpdate(child, temperatureQuery, transformQuery, tempThresholdsQuery);
-        }
-    }
-
-    /// <summary>
-    /// Recalculate parent thresholds and apply them on the uid temperature component.
-    /// </summary>
-    /// <param name="uid"></param>
-    /// <param name="temperatureQuery"></param>
-    /// <param name="transformQuery"></param>
-    /// <param name="tempThresholdsQuery"></param>
-    private void RecalculateAndApplyParentThresholds(EntityUid uid,
-        EntityQuery<TemperatureComponent> temperatureQuery, EntityQuery<TransformComponent> transformQuery,
-        EntityQuery<ContainerTemperatureDamageThresholdsComponent> tempThresholdsQuery)
-    {
-        if (!temperatureQuery.TryGetComponent(uid, out var temperature))
-        {
-            return;
-        }
-
-        var newThresholds = RecalculateParentThresholds(transformQuery.GetComponent(uid).ParentUid, transformQuery, tempThresholdsQuery);
-        temperature.ParentHeatDamageThreshold = newThresholds.Item1;
-        temperature.ParentColdDamageThreshold = newThresholds.Item2;
-    }
-
-    /// <summary>
-    /// Recalculate Parent Heat/Cold DamageThreshold by recursively checking each ancestor and fetching the
-    /// maximum HeatDamageThreshold and the minimum ColdDamageThreshold if any exists (aka the best value for each).
-    /// </summary>
-    /// <param name="initialParentUid"></param>
-    /// <param name="transformQuery"></param>
-    /// <param name="tempThresholdsQuery"></param>
-    private (float?, float?) RecalculateParentThresholds(
-        EntityUid initialParentUid,
-        EntityQuery<TransformComponent> transformQuery,
-        EntityQuery<ContainerTemperatureDamageThresholdsComponent> tempThresholdsQuery)
-    {
-        // Recursively check parents for the best threshold available
-        var parentUid = initialParentUid;
-        float? newHeatThreshold = null;
-        float? newColdThreshold = null;
-        while (parentUid.IsValid())
-        {
-            if (tempThresholdsQuery.TryGetComponent(parentUid, out var newThresholds))
-            {
-                if (newThresholds.HeatDamageThreshold != null)
-                {
-                    newHeatThreshold = Math.Max(newThresholds.HeatDamageThreshold.Value,
-                        newHeatThreshold ?? 0);
-                }
-
-                if (newThresholds.ColdDamageThreshold != null)
-                {
-                    newColdThreshold = Math.Min(newThresholds.ColdDamageThreshold.Value,
-                        newColdThreshold ?? float.MaxValue);
-                }
-            }
-
-            parentUid = transformQuery.GetComponent(parentUid).ParentUid;
-        }
-
-        return (newHeatThreshold, newColdThreshold);
+        ChangeHeat(args.Target, ent.Comp.Heat, ent.Comp.IgnoreHeatResistance);// adjust the temperature
     }
 }
index b4aee77cef653a825b13b916fc120a7905199e0c..f39487bb2455f329c8c1b0d3f14fdb83435cb202 100644 (file)
@@ -242,7 +242,7 @@ public sealed partial class ZombieSystem
         _mind.MakeSentient(target);
 
         //Make the zombie not die in the cold. Good for space zombies
-        if (TryComp<TemperatureComponent>(target, out var tempComp))
+        if (TryComp<TemperatureDamageComponent>(target, out var tempComp))
             tempComp.ColdDamage.ClampMax(0);
 
         //Heals the zombie from all the damage it took while human
index b73d1f77d10e8d78276b9237c5fdb986bcbc4ed2..fd79c4b4b230565d95c8d4c7e5d24322dcbd01f5 100644 (file)
@@ -1,15 +1,10 @@
-using Content.Shared.Alert;
 using Content.Shared.Atmos;
-using Content.Shared.Damage;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Temperature.Components;
 
 /// <summary>
 /// Handles changing temperature,
-/// informing others of the current temperature,
-/// and taking fire damage from high temperature.
+/// informing others of the current temperature.
 /// </summary>
 [RegisterComponent]
 public sealed partial class TemperatureComponent : Component
@@ -20,24 +15,6 @@ public sealed partial class TemperatureComponent : Component
     [DataField, ViewVariables(VVAccess.ReadWrite)]
     public float CurrentTemperature = Atmospherics.T20C;
 
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float HeatDamageThreshold = 360f;
-
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float ColdDamageThreshold = 260f;
-
-    /// <summary>
-    /// Overrides HeatDamageThreshold if the entity's within a parent with the TemperatureDamageThresholdsComponent component.
-    /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float? ParentHeatDamageThreshold;
-
-    /// <summary>
-    /// Overrides ColdDamageThreshold if the entity's within a parent with the TemperatureDamageThresholdsComponent component.
-    /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float? ParentColdDamageThreshold;
-
     /// <summary>
     /// Heat capacity per kg of mass.
     /// </summary>
@@ -49,31 +26,4 @@ public sealed partial class TemperatureComponent : Component
     /// </summary>
     [DataField, ViewVariables(VVAccess.ReadWrite)]
     public float AtmosTemperatureTransferEfficiency = 0.1f;
-
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public DamageSpecifier ColdDamage = new();
-
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public DamageSpecifier HeatDamage = new();
-
-    /// <summary>
-    /// Temperature won't do more than this amount of damage per second.
-    /// </summary>
-    /// <remarks>
-    /// Okay it genuinely reaches this basically immediately for a plasma fire.
-    /// </remarks>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public FixedPoint2 DamageCap = FixedPoint2.New(8);
-
-    /// <summary>
-    /// Used to keep track of when damage starts/stops. Useful for logs.
-    /// </summary>
-    [DataField]
-    public bool TakingDamage;
-
-    [DataField]
-    public ProtoId<AlertPrototype> HotAlert = "Hot";
-
-    [DataField]
-    public ProtoId<AlertPrototype> ColdAlert = "Cold";
 }
diff --git a/Content.Shared/Temperature/Components/TemperatureDamageComponent.cs b/Content.Shared/Temperature/Components/TemperatureDamageComponent.cs
new file mode 100644 (file)
index 0000000..a9543e3
--- /dev/null
@@ -0,0 +1,94 @@
+using Content.Shared.Alert;
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Temperature.Components;
+
+/// <summary>
+/// Handles taking damage from being excessively hot/cold.
+/// Also handles alerts about being too hot or too cold.
+/// </summary>
+[RegisterComponent]
+public sealed partial class TemperatureDamageComponent : Component
+{
+    /// <summary>
+    /// The temperature above which the entity will start taking damage from being too hot.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float HeatDamageThreshold = 360f;
+
+    /// <summary>
+    /// The temperature below which the entity will start taking damage from being too cold.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float ColdDamageThreshold = 260f;
+
+    /// <summary>
+    /// Overrides HeatDamageThreshold if the entity's within a parent with the ContainerTemperatureDamageThresholdsComponent component.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float? ParentHeatDamageThreshold;
+
+    /// <summary>
+    /// Overrides ColdDamageThreshold if the entity's within a parent with the ContainerTemperatureDamageThresholdsComponent component.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float? ParentColdDamageThreshold;
+
+    /// <summary>
+    /// The base damage that this entity will take if it's too cold.
+    /// Will be scaled according to how cold it is.
+    /// The scaling maxes out at <see cref="DamageCap"/> times this damage per second.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public DamageSpecifier ColdDamage = new();
+
+    /// <summary>
+    /// The base damage that this entity will take per second if it's too hot.
+    /// Will be scaled according to how hot it is.
+    /// The scaling maxes out at <see cref="DamageCap"/> times this damage per second.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public DamageSpecifier HeatDamage = new();
+
+    /// <summary>
+    /// Temperature won't do more than this multiple of the base overheating/overcooling damage per seond.
+    /// </summary>
+    /// <remarks>
+    /// Okay it genuinely reaches this basically immediately for a plasma fire.
+    /// </remarks>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public FixedPoint2 DamageCap = FixedPoint2.New(8);
+
+    /// <summary>
+    /// Used to keep track of when damage starts/stops. Useful for logs.
+    /// </summary>
+    [DataField]
+    public bool TakingDamage;
+
+    /// <summary>
+    /// The id of the alert thrown when the entity is too hot.
+    /// </summary>
+    [DataField]
+    public ProtoId<AlertPrototype> HotAlert = "Hot";
+
+    /// <summary>
+    /// The id of the alert thrown when the entity is too cold.
+    /// </summary>
+    [DataField]
+    public ProtoId<AlertPrototype> ColdAlert = "Cold";
+
+    /// <summary>
+    /// The last time this entity processed temperature damage.
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+    public TimeSpan LastUpdate;
+
+    /// <summary>
+    /// The time interval between temperature damage ticks for this entity.
+    /// </summary>
+    [DataField]
+    public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1.0);
+}
index 9561c6dfe14607022609586ec3f0689588da0241..216349d6f5912c57a48ab23b9f1a2abc8d08c68e 100644 (file)
@@ -16,6 +16,8 @@ public abstract class SharedTemperatureSystem : EntitySystem
     [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
 
+    protected EntityQuery<TemperatureComponent> TemperatureQuery;
+
     /// <summary>
     /// Band-aid for unpredicted atmos. Delays the application for a short period so that laggy clients can get the replicated temperature.
     /// </summary>
@@ -27,6 +29,8 @@ public abstract class SharedTemperatureSystem : EntitySystem
 
         SubscribeLocalEvent<TemperatureSpeedComponent, OnTemperatureChangeEvent>(OnTemperatureChanged);
         SubscribeLocalEvent<TemperatureSpeedComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
+
+        TemperatureQuery = GetEntityQuery<TemperatureComponent>();
     }
 
     private void OnTemperatureChanged(Entity<TemperatureSpeedComponent> ent, ref OnTemperatureChangeEvent args)
@@ -87,7 +91,7 @@ public abstract class SharedTemperatureSystem : EntitySystem
 
     public float GetHeatCapacity(EntityUid uid, TemperatureComponent? comp = null, PhysicsComponent? physics = null)
     {
-        if (!Resolve(uid, ref comp) || !Resolve(uid, ref physics, false) || physics.FixturesMass <= 0)
+        if (!TemperatureQuery.Resolve(uid, ref comp) || !Resolve(uid, ref physics, false) || physics.FixturesMass <= 0)
         {
             return Atmospherics.MinimumHeatCapacity;
         }
index 3685f891b617917d809957767464f6cbad234c1b..39c319f068c38aa921facae601911d0eacd2687b 100644 (file)
         Slash: 5
         Piercing: 4
   - type: Temperature
-    heatDamageThreshold: 360
-    coldDamageThreshold: 285
     currentTemperature: 310.15
     specificHeat: 42
+  - type: TemperatureDamage
+    heatDamageThreshold: 360
+    coldDamageThreshold: 285
   - type: Sprite
     drawdepth: Mobs
     layers:
     factions:
     - Passive
   - type: Temperature
-    heatDamageThreshold: 335
-    coldDamageThreshold: 230
     currentTemperature: 310.15
     specificHeat: 46
+  - type: TemperatureDamage
+    heatDamageThreshold: 335
+    coldDamageThreshold: 230
     coldDamage:
       types:
         Cold : 0.05 #per second, scales with temperature & other constants
     damageContainer: BiologicalMetaphysical
     damageModifierSet: Infernal
   - type: Temperature
+    currentTemperature: 310.15
+    specificHeat: 42
+  - type: TemperatureDamage
     heatDamageThreshold: 4000 #They come from hell, so..
     coldDamageThreshold: 260
-    currentTemperature: 310.15
     coldDamage:
       types:
         Cold : 1 #per second, scales with temperature & other constants
-    specificHeat: 42
     heatDamage:
       types:
         Heat : 1 #per second, scales with temperature & other constants
       Dead:
         Base: spacecat_dead
   - type: Temperature
+  - type: TemperatureDamage
     heatDamageThreshold: 423
     coldDamageThreshold: 0
   - type: Tag
index 1547e36339429ce416bab4b5466371d69e8c7c05..1eb22649e7db87cc1d39c5daf6d5d10066a4acbb 100644 (file)
       damage:
        types: {}
     - type: Temperature
+    - type: TemperatureDamage
       heatDamageThreshold: 1200
 - type: entity
   id: MobCarpDungeon
index ccb5c3aebb1030e68c75b75027cf31ae1a8b938a..b6e53b794db33cc5aa64bf37a3b214bdce74297d 100644 (file)
         Bloodloss:
           -0.8
   - type: Temperature
+  - type: TemperatureDamage
     heatDamageThreshold: 800
     coldDamageThreshold: 0
   - type: MeleeWeapon
index 8f84e1496ae0079d2a6ed0b899cdaed5eaa2c34c..883103adcc09335b5809769d0107904e2375274f 100644 (file)
       60: Critical
       125: Dead
   - type: Temperature
-    heatDamageThreshold: 360
-    coldDamageThreshold: 285
     currentTemperature: 310.15
     specificHeat: 42
+  - type: TemperatureDamage
+    heatDamageThreshold: 360
+    coldDamageThreshold: 285
   # Good eatin', you monster.
   - type: Butcherable
     butcheringType: Spike
index 7a51d257ef4a2ab8e668b3bf27ee6fe0cb3807b4..e57e0d3d97a865f355439a1de101ad73968c1666 100644 (file)
@@ -45,6 +45,7 @@
         Quantity: 300
   - type: CombatMode
   - type: Temperature
+  - type: TemperatureDamage
     heatDamageThreshold: 500
     coldDamageThreshold: 0
   - type: MeleeWeapon
     sprite: Mobs/Effects/onfire.rsi
     normalState: Mouse_burning
   - type: Temperature
+  - type: TemperatureDamage
     heatDamageThreshold: 500
     coldDamageThreshold: 0
   - type: Reactive
index 58e0088828b76b04b6bd9a4318f481f9b521ea28..33653ed20774d7d798ed336add6dae9d30b9222d 100644 (file)
   - type: TypingIndicator
     proto: alien
   - type: Temperature
+    currentTemperature: 310.15
+  - type: TemperatureDamage
     heatDamageThreshold: 360
     coldDamageThreshold: -150
-    currentTemperature: 310.15
   - type: Tag
     tags:
       - CannotSuicide
   - type: TypingIndicator
     proto: alien
   - type: Temperature
+    currentTemperature: 310.15
+  - type: TemperatureDamage
     heatDamageThreshold: 360
     coldDamageThreshold: -150
-    currentTemperature: 310.15
   - type: NoSlip
   - type: Perishable #Ummmm the acid kills a lot of the bacteria or something
     molsPerSecondPerUnitMass: 0.0005
index 07bff11974f622771e2d73fec8cce2a496ab2169..5aa7f05f0ea64fbc1cf02adeb86ec13f7ba03312 100644 (file)
     - RadiationProtection
     - Adrenaline
   - type: Temperature
+  - type: TemperatureDamage
     heatDamageThreshold: 2400
   - type: Metabolizer
     solutionOnBody: false
index a0ace57c47ec9e94bf59ec37b8ef45594264bdc2..4df9ddcaacced8fb47254daf2d11cae802a16abc 100644 (file)
   - type: Blindable
   # Other
   - type: Temperature
-    heatDamageThreshold: 325
-    coldDamageThreshold: 260
     currentTemperature: 310.15
     specificHeat: 42
+  - type: TemperatureDamage
+    heatDamageThreshold: 325
+    coldDamageThreshold: 260
     coldDamage:
       types:
         Cold: 0.1 #per second, scales with temperature & other constants
index 83031d61d94bbecdf007ddb5f6267165e474f422..6d7ba02f5f05a3ee72ed77ae6ec2165aa762d394 100644 (file)
     damage:
       types:
         Heat: 2.5 # moths burn more easily
-  - type: Temperature # Moths hate the heat and thrive in the cold.
-    heatDamageThreshold: 320
-    coldDamageThreshold: 230
+  - type: Temperature
     currentTemperature: 310.15
     specificHeat: 46
+  - type: TemperatureDamage # Moths hate the heat and thrive in the cold.
+    heatDamageThreshold: 320
+    coldDamageThreshold: 230
     coldDamage:
       types:
         Cold : 0.05 #per second, scales with temperature & other constants
index 67d0cfba80666a99721e29a226a92ba35b17e411..55bb32e3a33ace1438cf30afe1cd5100c789d410 100644 (file)
       types:
         Slash: 5
   - type: Temperature
-    heatDamageThreshold: 400
-    coldDamageThreshold: 285
     currentTemperature: 310.15
     specificHeat: 42
+  - type: TemperatureDamage
+    heatDamageThreshold: 400
+    coldDamageThreshold: 285
     coldDamage:
       types:
         Cold : 0.1 #per second, scales with temperature & other constants
index a80d20627fbbd161bdf4d94215b8ecb5647da446..535d3ba327d699214b19e825f087ae8a2e0e5795 100644 (file)
         Piercing: 2
         Slash: 3
   - type: Temperature # Same as moth temperatures until below is solved.
+    specificHeat: 44
+  - type: TemperatureDamage
     heatDamageThreshold: 320 # TODO: 315 when there is a way to make the temperature alert not blink to the side of the screen and disappear when you "sweat" at 39C.
     coldDamageThreshold: 230 # TODO: 220 when the above is solved.
-    specificHeat: 44
     coldDamage:
       types:
         Cold: 0.05 # Per second, scales with temperature & other constants
index a21ca14c5db3bc5e9891c40e7285e162fb6e9b6d..26f9215247459ec78bcafa5f030ed902ca72b2cb 100644 (file)
   components:
   - type: AtmosExposed
   - type: Temperature
+    currentTemperature: 310.15
+    specificHeat: 42
+  - type: TemperatureDamage
     heatDamageThreshold: 325
     coldDamageThreshold: 0
-    currentTemperature: 310.15
     coldDamage: #per second, scales with temperature & other constants
       types:
         Cold : 0.1
-    specificHeat: 42
     heatDamage: #per second, scales with temperature & other constants
       types:
         Heat : 1.5
     normalBodyTemperature: 310.15
     thermalRegulationTemperatureThreshold: 2
   - type: Temperature
-    heatDamageThreshold: 325
-    coldDamageThreshold: 260
     currentTemperature: 310.15
     specificHeat: 42
+  - type: TemperatureDamage
+    heatDamageThreshold: 325
+    coldDamageThreshold: 260
     coldDamage:
       types:
         Cold: 1 #per second, scales with temperature & other constants
index 25e40d12dc498fb1597e3157020fb4cc25d6b049..e19859f16e69d68a819fb38a7dd523bfafa21427 100644 (file)
   # all below are for egg cooking/exploding
   - type: AtmosExposed
   - type: Temperature
+  - type: TemperatureDamage
+  - type: InternalTemperature
+    # ~1mm shell and ~1cm of albumen
+    thickness: 0.011
+    area: 0.04
+    # conductivity of egg shell based on a paper from Romanoff and Romanoff (1949)
+    conductivity: 0.456
 
 # Splat
 - type: entity
   - type: Temperature
     # preserve temperature from the boiling step
     currentTemperature: 344
+  - type: TemperatureDamage
index adc3b373d4e16304254d8a7f685f789cb0730fb1..41c8f658857a50bcd6a0e57c63166d66f846c2fe 100644 (file)
@@ -42,6 +42,7 @@
   - type: AtmosExposed
   - type: Temperature
     currentTemperature: 290
+  - type: TemperatureDamage
   # required for cooking to work
   - type: InternalTemperature
     thickness: 0.02
index ccca7d1af1882676214588c8d4da00ff738b3439..514443f9a107b4034dc3c4c2f541ba891f6e9f3d 100644 (file)
@@ -45,6 +45,7 @@
         - !type:DoActsBehavior
           acts: [ "Destruction" ]
     - type: Temperature
+    - type: TemperatureDamage
       heatDamage:
         types:
           Heat: 5
index 157a9616ab1e0673a936c82bd30ec30fdc9bbce2..1f1b9a9c6ab694fb949d50510a7b90ce4bb38ed2 100644 (file)
@@ -56,6 +56,7 @@
         - !type:DoActsBehavior
           acts: [ "Destruction" ]
     - type: Temperature
+    - type: TemperatureDamage
       heatDamage:
         types:
           Heat: 5
           Cold: -1.0
           Blunt: -0.5 # Needs to be balanced (approx 3x) with vacuum damage to stall but not kill Kudzu
     - type: Temperature
+    - type: TemperatureDamage
       heatDamage:
         types:
           Heat: 10
index e73d13e2cbec0f49f6d2d0119a54fb985ad4040a..6e96dbbadcaa52913cc13e2af659b2a57b91e6a6 100644 (file)
@@ -89,6 +89,7 @@
   - type: AtmosExposed
   - type: Temperature
     currentTemperature: 290
+  - type: TemperatureDamage
   - type: InternalTemperature
     thickness: 0.02
     area: 0.02
index d147417f9ef88d33764099439fdde5c8c4cc4f02..4d50a8b6ed01974228bd613de5588bc1de61ae36 100644 (file)
   - type: CryoPodAir
   - type: Climbable # so that ejected bodies don't get stuck
     vaultable: false
-  - type: ContainerTemperatureDamageThresholds
+  - type: ContainerTemperature
     coldDamageThreshold: 10
   - type: GuideHelp
     guides:
index 73e5f5f03214418a6d676c2d92961adba10302dd..b09e022a3bdf6c6abc2035384d35cea91c5c5969 100644 (file)
@@ -24,6 +24,7 @@
   - type: Damageable
     damageModifierSet: Wood
   - type: Temperature
+  - type: TemperatureDamage
     heatDamage:
       types:
         Heat: 5