using Content.Server.Body.Components; using Content.Server.Temperature.Systems; using Content.Shared.ActionBlocker; using Content.Shared.Temperature.Components; using Content.Shared.Mobs.Systems; using Robust.Shared.Timing; namespace Content.Server.Body.Systems; public sealed partial class ThermalRegulatorSystem : EntitySystem { [Dependency] private IGameTiming _gameTiming = default!; [Dependency] private TemperatureSystem _tempSys = default!; [Dependency] private ActionBlockerSystem _actionBlockerSys = default!; [Dependency] private MobStateSystem _mobState = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUnpaused); } private void OnMapInit(Entity ent, ref MapInitEvent args) { ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; } private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) { ent.Comp.NextUpdate += args.PausedTime; } public override void Update(float frameTime) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var regulator)) { if (_gameTiming.CurTime < regulator.NextUpdate) continue; regulator.NextUpdate += regulator.UpdateInterval; // The dead shouldn't be able to regulate their own body temperature. if (_mobState.IsDead(uid)) continue; ProcessThermalRegulation((uid, regulator)); } } /// /// Processes thermal regulation for a mob /// private void ProcessThermalRegulation(Entity ent) { if (!Resolve(ent, ref ent.Comp2, logMissing: false)) return; // TODO: Why do we have two datafields for this if they are only ever used once here? var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat; // implicit heat regulation var tempDiff = Math.Abs(ent.Comp2.Temperature - ent.Comp1.NormalBodyTemperature); var heatCapacity = ent.Comp2.HeatCapacity; var targetHeat = tempDiff * heatCapacity; if (ent.Comp2.Temperature > ent.Comp1.NormalBodyTemperature) { totalMetabolismTempChange -= Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } else { totalMetabolismTempChange += Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } _tempSys.ChangeHeat((ent, ent.Comp2), totalMetabolismTempChange, ignoreHeatResistance: true); // recalc difference and target heat tempDiff = Math.Abs(ent.Comp2.Temperature - ent.Comp1.NormalBodyTemperature); targetHeat = tempDiff * heatCapacity; // if body temperature is not within comfortable, thermal regulation // processes starts if (tempDiff < ent.Comp1.ThermalRegulationTemperatureThreshold) return; if (ent.Comp2.Temperature > ent.Comp1.NormalBodyTemperature) { if (!_actionBlockerSys.CanSweat(ent)) return; _tempSys.ChangeHeat((ent, ent.Comp2), -Math.Min(targetHeat, ent.Comp1.SweatHeatRegulation), ignoreHeatResistance: true); } else { if (!_actionBlockerSys.CanShiver(ent)) return; _tempSys.ChangeHeat((ent, ent.Comp2), Math.Min(targetHeat, ent.Comp1.ShiveringHeatRegulation), ignoreHeatResistance: true); } } }