diff --git a/Content.Server/Construction/ConstructionSystem.Interactions.cs b/Content.Server/Construction/ConstructionSystem.Interactions.cs index ad7b2a11b0..c844dd2c9a 100644 --- a/Content.Server/Construction/ConstructionSystem.Interactions.cs +++ b/Content.Server/Construction/ConstructionSystem.Interactions.cs @@ -2,7 +2,6 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Construction.Components; using Content.Server.Temperature.Components; -using Content.Server.Temperature.Systems; using Content.Shared.Construction; using Content.Shared.Construction.Components; using Content.Shared.Construction.EntitySystems; @@ -11,6 +10,7 @@ using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Prying.Systems; using Content.Shared.Radio.EntitySystems; +using Content.Shared.Temperature; using Content.Shared.Tools.Systems; using Robust.Shared.Containers; using Robust.Shared.Utility; diff --git a/Content.Server/Temperature/Components/TemperatureComponent.cs b/Content.Server/Temperature/Components/TemperatureComponent.cs index 3bfa12f269..ae2c373b14 100644 --- a/Content.Server/Temperature/Components/TemperatureComponent.cs +++ b/Content.Server/Temperature/Components/TemperatureComponent.cs @@ -1,4 +1,3 @@ -using Content.Server.Temperature.Systems; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; @@ -51,15 +50,6 @@ public sealed partial class TemperatureComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public float AtmosTemperatureTransferEfficiency = 0.1f; - [Obsolete("Use system method")] - public float HeatCapacity - { - get - { - return IoCManager.Resolve().System().GetHeatCapacity(Owner, this); - } - } - [DataField, ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier ColdDamage = new(); @@ -71,7 +61,7 @@ public sealed partial class TemperatureComponent : Component /// /// /// Okay it genuinely reaches this basically immediately for a plasma fire. - /// + /// [DataField, ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 DamageCap = FixedPoint2.New(8); @@ -79,7 +69,7 @@ public sealed partial class TemperatureComponent : Component /// Used to keep track of when damage starts/stops. Useful for logs. /// [DataField] - public bool TakingDamage = false; + public bool TakingDamage; [DataField] public ProtoId HotAlert = "Hot"; diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index d33bf6e025..8b6fa107cc 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -412,17 +412,3 @@ public sealed class TemperatureSystem : EntitySystem return (newHeatThreshold, newColdThreshold); } } - -public sealed class OnTemperatureChangeEvent : EntityEventArgs -{ - public float CurrentTemperature { get; } - public float LastTemperature { get; } - public float TemperatureDelta { get; } - - public OnTemperatureChangeEvent(float current, float last, float delta) - { - CurrentTemperature = current; - LastTemperature = last; - TemperatureDelta = delta; - } -} diff --git a/Content.Shared/Temperature/Components/TemperatureSpeedComponent.cs b/Content.Shared/Temperature/Components/TemperatureSpeedComponent.cs new file mode 100644 index 0000000000..74ba2a0274 --- /dev/null +++ b/Content.Shared/Temperature/Components/TemperatureSpeedComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Temperature.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Temperature.Components; + +/// +/// This is used for an entity that varies in speed based on current temperature. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedTemperatureSystem)), AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class TemperatureSpeedComponent : Component +{ + /// + /// Pairs of temperature thresholds to applied slowdown values. + /// + [DataField] + public Dictionary Thresholds = new(); + + /// + /// The current speed modifier from we reached. + /// Stored and networked so that the client doesn't mispredict temperature + /// + [DataField, AutoNetworkedField] + public float? CurrentSpeedModifier; + + /// + /// The time at which the temperature slowdown is updated. + /// + [DataField, AutoNetworkedField, AutoPausedField] + public TimeSpan? NextSlowdownUpdate; +} diff --git a/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs new file mode 100644 index 0000000000..efea2df5af --- /dev/null +++ b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs @@ -0,0 +1,80 @@ +using System.Linq; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Temperature.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Temperature.Systems; + +/// +/// This handles predicting temperature based speedup. +/// +public sealed class SharedTemperatureSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + + /// + /// Band-aid for unpredicted atmos. Delays the application for a short period so that laggy clients can get the replicated temperature. + /// + private static readonly TimeSpan SlowdownApplicationDelay = TimeSpan.FromSeconds(1f); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTemperatureChanged); + SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); + } + + private void OnTemperatureChanged(Entity ent, ref OnTemperatureChangeEvent args) + { + foreach (var (threshold, modifier) in ent.Comp.Thresholds) + { + if (args.CurrentTemperature < threshold && args.LastTemperature > threshold || + args.CurrentTemperature > threshold && args.LastTemperature < threshold) + { + ent.Comp.NextSlowdownUpdate = _timing.CurTime + SlowdownApplicationDelay; + ent.Comp.CurrentSpeedModifier = modifier; + Dirty(ent); + break; + } + } + + var maxThreshold = ent.Comp.Thresholds.Max(p => p.Key); + if (args.CurrentTemperature > maxThreshold && args.LastTemperature < maxThreshold) + { + ent.Comp.NextSlowdownUpdate = _timing.CurTime + SlowdownApplicationDelay; + ent.Comp.CurrentSpeedModifier = null; + Dirty(ent); + } + } + + private void OnRefreshMovementSpeedModifiers(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + // Don't update speed and mispredict while we're compensating for lag. + if (ent.Comp.NextSlowdownUpdate != null || ent.Comp.CurrentSpeedModifier == null) + return; + + args.ModifySpeed(ent.Comp.CurrentSpeedModifier.Value, ent.Comp.CurrentSpeedModifier.Value); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var temp, out var movement)) + { + if (temp.NextSlowdownUpdate == null) + continue; + + if (_timing.CurTime < temp.NextSlowdownUpdate) + continue; + + temp.NextSlowdownUpdate = null; + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid, movement); + Dirty(uid, temp); + } + } +} diff --git a/Content.Shared/Temperature/TemperatureEvents.cs b/Content.Shared/Temperature/TemperatureEvents.cs index ac12224868..7a26d07e30 100644 --- a/Content.Shared/Temperature/TemperatureEvents.cs +++ b/Content.Shared/Temperature/TemperatureEvents.cs @@ -13,3 +13,18 @@ public sealed class ModifyChangedTemperatureEvent : EntityEventArgs, IInventoryR TemperatureDelta = temperature; } } + +public sealed class OnTemperatureChangeEvent : EntityEventArgs +{ + public readonly float CurrentTemperature; + public readonly float LastTemperature; + public readonly float TemperatureDelta; + + public OnTemperatureChangeEvent(float current, float last, float delta) + { + CurrentTemperature = current; + LastTemperature = last; + TemperatureDelta = delta; + } +} + diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 99aa2d4bc8..af309e93ad 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -249,6 +249,11 @@ heatDamage: types: Heat: 1.5 #per second, scales with temperature & other constants + - type: TemperatureSpeed + thresholds: + 293: 0.8 + 280: 0.6 + 260: 0.4 - type: ThermalRegulator metabolismHeat: 800 radiatedHeat: 100 diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index 153d5cccd3..da0dca740c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -64,6 +64,11 @@ heatDamage: types: Heat : 3 #per second, scales with temperature & other constants + - type: TemperatureSpeed + thresholds: + 289: 0.8 + 275: 0.6 + 250: 0.4 - type: Sprite # sprite again because we want different layer ordering noRot: true drawdepth: Mobs diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index 1c9eaa2ec6..c023dc1c25 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -59,6 +59,11 @@ heatDamage: types: Heat : 1.5 #per second, scales with temperature & other constants + - type: TemperatureSpeed + thresholds: + 301: 0.8 + 295: 0.6 + 285: 0.4 - type: Wagging - type: Inventory speciesId: reptilian