mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-02-14 23:14:45 +01:00
Splits temperature damage processing into its own component (#30515)
* 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>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.Temperature.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ContainerTemperatureComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float? HeatDamageThreshold;
|
||||
|
||||
[DataField]
|
||||
public float? ColdDamageThreshold;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
279
Content.Server/Temperature/Systems/TemperatureSystem.Damage.cs
Normal file
279
Content.Server/Temperature/Systems/TemperatureSystem.Damage.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1610,10 +1610,11 @@
|
||||
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:
|
||||
@@ -2368,10 +2369,11 @@
|
||||
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
|
||||
@@ -3055,13 +3057,14 @@
|
||||
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
|
||||
@@ -3256,6 +3259,7 @@
|
||||
Dead:
|
||||
Base: spacecat_dead
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 423
|
||||
coldDamageThreshold: 0
|
||||
- type: Tag
|
||||
|
||||
@@ -185,6 +185,7 @@
|
||||
damage:
|
||||
types: {}
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 1200
|
||||
- type: entity
|
||||
id: MobCarpDungeon
|
||||
|
||||
@@ -833,6 +833,7 @@
|
||||
Bloodloss:
|
||||
-0.8
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 800
|
||||
coldDamageThreshold: 0
|
||||
- type: MeleeWeapon
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
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
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
Quantity: 300
|
||||
- type: CombatMode
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 500
|
||||
coldDamageThreshold: 0
|
||||
- type: MeleeWeapon
|
||||
@@ -512,6 +513,7 @@
|
||||
sprite: Mobs/Effects/onfire.rsi
|
||||
normalState: Mouse_burning
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 500
|
||||
coldDamageThreshold: 0
|
||||
- type: Reactive
|
||||
|
||||
@@ -107,9 +107,10 @@
|
||||
- type: TypingIndicator
|
||||
proto: alien
|
||||
- type: Temperature
|
||||
currentTemperature: 310.15
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 360
|
||||
coldDamageThreshold: -150
|
||||
currentTemperature: 310.15
|
||||
- type: Tag
|
||||
tags:
|
||||
- CannotSuicide
|
||||
@@ -524,9 +525,10 @@
|
||||
- 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
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
- RadiationProtection
|
||||
- Adrenaline
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamageThreshold: 2400
|
||||
- type: Metabolizer
|
||||
solutionOnBody: false
|
||||
|
||||
@@ -239,10 +239,11 @@
|
||||
- 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
|
||||
|
||||
@@ -58,11 +58,12 @@
|
||||
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
|
||||
|
||||
@@ -54,10 +54,11 @@
|
||||
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
|
||||
|
||||
@@ -34,9 +34,10 @@
|
||||
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
|
||||
|
||||
@@ -169,13 +169,14 @@
|
||||
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
|
||||
@@ -204,10 +205,11 @@
|
||||
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
|
||||
|
||||
@@ -61,6 +61,13 @@
|
||||
# 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
|
||||
@@ -128,3 +135,4 @@
|
||||
- type: Temperature
|
||||
# preserve temperature from the boiling step
|
||||
currentTemperature: 344
|
||||
- type: TemperatureDamage
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
- type: AtmosExposed
|
||||
- type: Temperature
|
||||
currentTemperature: 290
|
||||
- type: TemperatureDamage
|
||||
# required for cooking to work
|
||||
- type: InternalTemperature
|
||||
thickness: 0.02
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamage:
|
||||
types:
|
||||
Heat: 5
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamage:
|
||||
types:
|
||||
Heat: 5
|
||||
@@ -226,6 +227,7 @@
|
||||
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
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
- type: AtmosExposed
|
||||
- type: Temperature
|
||||
currentTemperature: 290
|
||||
- type: TemperatureDamage
|
||||
- type: InternalTemperature
|
||||
thickness: 0.02
|
||||
area: 0.02
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
- type: CryoPodAir
|
||||
- type: Climbable # so that ejected bodies don't get stuck
|
||||
vaultable: false
|
||||
- type: ContainerTemperatureDamageThresholds
|
||||
- type: ContainerTemperature
|
||||
coldDamageThreshold: 10
|
||||
- type: GuideHelp
|
||||
guides:
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
- type: Damageable
|
||||
damageModifierSet: Wood
|
||||
- type: Temperature
|
||||
- type: TemperatureDamage
|
||||
heatDamage:
|
||||
types:
|
||||
Heat: 5
|
||||
|
||||
Reference in New Issue
Block a user