+++ /dev/null
-using Content.Shared.Bed;
-using Robust.Client.GameObjects;
-
-namespace Content.Client.Bed;
-
-public sealed class StasisBedSystem : VisualizerSystem<StasisBedVisualsComponent>
-{
- protected override void OnAppearanceChange(EntityUid uid, StasisBedVisualsComponent component, ref AppearanceChangeEvent args)
- {
- if (args.Sprite != null
- && AppearanceSystem.TryGetData<bool>(uid, StasisBedVisuals.IsOn, out var isOn, args.Component))
- {
- SpriteSystem.LayerSetVisible((uid, args.Sprite), StasisBedVisualLayers.IsOn, isOn);
- }
- }
-}
-
-public enum StasisBedVisualLayers : byte
-{
- IsOn,
-}
--- /dev/null
+using Content.Shared.Body.Systems;
+
+namespace Content.Client.Body.Systems;
+
+/// <inheritdoc/>
+public sealed class MetabolizerSystem : SharedMetabolizerSystem;
-using Content.Server.Bed.Components;
-using Content.Server.Power.EntitySystems;
using Content.Shared.Bed;
using Content.Shared.Bed.Components;
using Content.Shared.Bed.Sleep;
-using Content.Shared.Body.Components;
-using Content.Shared.Body.Events;
using Content.Shared.Buckle.Components;
using Content.Shared.Damage;
-using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Systems;
-using Content.Shared.Power;
namespace Content.Server.Bed
{
public sealed class BedSystem : SharedBedSystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
- [Dependency] private readonly EmagSystem _emag = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
private EntityQuery<SleepingComponent> _sleepingQuery;
base.Initialize();
_sleepingQuery = GetEntityQuery<SleepingComponent>();
-
- SubscribeLocalEvent<StasisBedComponent, StrappedEvent>(OnStasisStrapped);
- SubscribeLocalEvent<StasisBedComponent, UnstrappedEvent>(OnStasisUnstrapped);
- SubscribeLocalEvent<StasisBedComponent, PowerChangedEvent>(OnPowerChanged);
- SubscribeLocalEvent<StasisBedComponent, GotEmaggedEvent>(OnEmagged);
}
public override void Update(float frameTime)
}
}
}
-
- private void UpdateAppearance(EntityUid uid, bool isOn)
- {
- _appearance.SetData(uid, StasisBedVisuals.IsOn, isOn);
- }
-
- private void OnStasisStrapped(Entity<StasisBedComponent> bed, ref StrappedEvent args)
- {
- if (!HasComp<BodyComponent>(args.Buckle) || !this.IsPowered(bed, EntityManager))
- return;
-
- var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, true);
- RaiseLocalEvent(args.Buckle, ref metabolicEvent);
- }
-
- private void OnStasisUnstrapped(Entity<StasisBedComponent> bed, ref UnstrappedEvent args)
- {
- if (!HasComp<BodyComponent>(args.Buckle) || !this.IsPowered(bed, EntityManager))
- return;
-
- var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, false);
- RaiseLocalEvent(args.Buckle, ref metabolicEvent);
- }
-
- private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args)
- {
- UpdateAppearance(uid, args.Powered);
- UpdateMetabolisms(uid, component, args.Powered);
- }
-
- private void OnEmagged(EntityUid uid, StasisBedComponent component, ref GotEmaggedEvent args)
- {
- if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
- return;
-
- if (_emag.CheckFlag(uid, EmagType.Interaction))
- return;
-
- // Reset any metabolisms first so they receive the multiplier correctly
- UpdateMetabolisms(uid, component, false);
- component.Multiplier = 1 / component.Multiplier;
- UpdateMetabolisms(uid, component, true);
- args.Handled = true;
- }
-
- private void UpdateMetabolisms(EntityUid uid, StasisBedComponent component, bool shouldApply)
- {
- if (!TryComp<StrapComponent>(uid, out var strap) || strap.BuckledEntities.Count == 0)
- return;
-
- foreach (var buckledEntity in strap.BuckledEntities)
- {
- var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply);
- RaiseLocalEvent(buckledEntity, ref metabolicEvent);
- }
- }
}
}
+++ /dev/null
-namespace Content.Server.Bed.Components
-{
- [RegisterComponent]
- public sealed partial class StasisBedComponent : Component
- {
- /// <summary>
- /// What the metabolic update rate will be multiplied by (higher = slower metabolism)
- /// </summary>
- [ViewVariables(VVAccess.ReadOnly)] // Writing is is not supported. ApplyMetabolicMultiplierEvent needs to be refactored first
- [DataField]
- public float Multiplier = 10f;
- }
-}
[DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
+ /// <summary>
+ /// Multiplier applied to <see cref="UpdateInterval"/> for adjusting based on metabolic rate multiplier.
+ /// </summary>
+ [DataField]
+ public float UpdateIntervalMultiplier = 1f;
+
+ /// <summary>
+ /// Adjusted update interval based off of the multiplier value.
+ /// </summary>
+ [ViewVariables]
+ public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier;
+
/// <summary>
/// From which solution will this metabolizer attempt to metabolize chemicals
/// </summary>
/// </summary>
[DataField]
[Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
- public HashSet<ProtoId<MetabolizerTypePrototype>>? MetabolizerTypes = null;
+ public HashSet<ProtoId<MetabolizerTypePrototype>>? MetabolizerTypes;
/// <summary>
/// Should this metabolizer remove chemicals that have no metabolisms defined?
/// As a stop-gap, basically.
/// </summary>
[DataField]
- public bool RemoveEmpty = false;
+ public bool RemoveEmpty;
/// <summary>
/// How many reagents can this metabolizer process at once?
/// A list of metabolism groups that this metabolizer will act on, in order of precedence.
/// </summary>
[DataField("groups")]
- public List<MetabolismGroupEntry>? MetabolismGroups = default!;
+ public List<MetabolismGroupEntry>? MetabolismGroups;
}
/// <summary>
public sealed partial class MetabolismGroupEntry
{
[DataField(required: true)]
- public ProtoId<MetabolismGroupPrototype> Id = default!;
+ public ProtoId<MetabolismGroupPrototype> Id;
[DataField("rateModifier")]
public FixedPoint2 MetabolismRateModifier = 1.0;
using Content.Server.Body.Systems;
-using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Damage;
namespace Content.Server.Body.Components
{
- [RegisterComponent, Access(typeof(RespiratorSystem))]
+ [RegisterComponent, Access(typeof(RespiratorSystem)), AutoGenerateComponentPause]
public sealed partial class RespiratorComponent : Component
{
/// <summary>
/// <summary>
/// The next time that this body will inhale or exhale.
/// </summary>
- [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan NextUpdate;
/// <summary>
[DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
+ /// <summary>
+ /// Multiplier applied to <see cref="UpdateInterval"/> for adjusting based on metabolic rate multiplier.
+ /// </summary>
+ [DataField]
+ public float UpdateIntervalMultiplier = 1f;
+
+ /// <summary>
+ /// Adjusted update interval based off of the multiplier value.
+ /// </summary>
+ [ViewVariables]
+ public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier;
+
/// <summary>
/// Saturation level. Reduced by UpdateInterval each tick.
/// Can be thought of as 'how many seconds you have until you start suffocating' in this configuration.
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Events;
using Content.Shared.Body.Organ;
+using Content.Shared.Body.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
namespace Content.Server.Body.Systems
{
- public sealed class MetabolizerSystem : EntitySystem
+ /// <inheritdoc/>
+ public sealed class MetabolizerSystem : SharedMetabolizerSystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private void OnMapInit(Entity<MetabolizerComponent> ent, ref MapInitEvent args)
{
- ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.AdjustedUpdateInterval;
}
private void OnUnpaused(Entity<MetabolizerComponent> ent, ref EntityUnpausedEvent args)
}
}
- private void OnApplyMetabolicMultiplier(
- Entity<MetabolizerComponent> ent,
- ref ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(Entity<MetabolizerComponent> ent, ref ApplyMetabolicMultiplierEvent args)
{
- // TODO REFACTOR THIS
- // This will slowly drift over time due to floating point errors.
- // Instead, raise an event with the base rates and allow modifiers to get applied to it.
- if (args.Apply)
- {
- ent.Comp.UpdateInterval *= args.Multiplier;
- return;
- }
-
- ent.Comp.UpdateInterval /= args.Multiplier;
+ ent.Comp.UpdateIntervalMultiplier = args.Multiplier;
}
public override void Update(float frameTime)
if (_gameTiming.CurTime < metab.NextUpdate)
continue;
- metab.NextUpdate += metab.UpdateInterval;
+ metab.NextUpdate += metab.AdjustedUpdateInterval;
TryMetabolize((uid, metab));
}
}
// We want to process lung reagents before we inhale new reagents.
UpdatesAfter.Add(typeof(MetabolizerSystem));
SubscribeLocalEvent<RespiratorComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<RespiratorComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BodyComponent, InhaledGasEvent>(OnGasInhaled);
SubscribeLocalEvent<BodyComponent, ExhaledGasEvent>(OnGasExhaled);
private void OnMapInit(Entity<RespiratorComponent> ent, ref MapInitEvent args)
{
- ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
- }
-
- private void OnUnpaused(Entity<RespiratorComponent> ent, ref EntityUnpausedEvent args)
- {
- ent.Comp.NextUpdate += args.PausedTime;
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.AdjustedUpdateInterval;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
- var query = EntityQueryEnumerator<RespiratorComponent, BodyComponent>();
- while (query.MoveNext(out var uid, out var respirator, out var body))
+ var query = EntityQueryEnumerator<RespiratorComponent>();
+ while (query.MoveNext(out var uid, out var respirator))
{
if (_gameTiming.CurTime < respirator.NextUpdate)
continue;
- respirator.NextUpdate += respirator.UpdateInterval;
+ respirator.NextUpdate += respirator.AdjustedUpdateInterval;
if (_mobState.IsDead(uid))
continue;
Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
}
- private void OnApplyMetabolicMultiplier(
- Entity<RespiratorComponent> ent,
- ref ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(Entity<RespiratorComponent> ent, ref ApplyMetabolicMultiplierEvent args)
{
- // TODO REFACTOR THIS
- // This will slowly drift over time due to floating point errors.
- // Instead, raise an event with the base rates and allow modifiers to get applied to it.
- if (args.Apply)
- {
- ent.Comp.UpdateInterval *= args.Multiplier;
- ent.Comp.Saturation *= args.Multiplier;
- ent.Comp.MaxSaturation *= args.Multiplier;
- ent.Comp.MinSaturation *= args.Multiplier;
- return;
- }
-
- // This way we don't have to worry about it breaking if the stasis bed component is destroyed
- ent.Comp.UpdateInterval /= args.Multiplier;
- ent.Comp.Saturation /= args.Multiplier;
- ent.Comp.MaxSaturation /= args.Multiplier;
- ent.Comp.MinSaturation /= args.Multiplier;
+ ent.Comp.UpdateIntervalMultiplier = args.Multiplier;
}
private void OnGasInhaled(Entity<BodyComponent> entity, ref InhaledGasEvent args)
UpdatesAfter.Add(typeof(NodeGroupSystem));
_solver = new(_cfg.GetCVar(CCVars.DebugPow3rDisableParallel));
+ SubscribeLocalEvent<ApcPowerReceiverComponent, MapInitEvent>(ApcPowerReceiverMapInit);
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentInit>(ApcPowerReceiverInit);
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentShutdown>(ApcPowerReceiverShutdown);
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentRemove>(ApcPowerReceiverRemove);
_solver = new(val);
}
+ private void ApcPowerReceiverMapInit(Entity<ApcPowerReceiverComponent> ent, ref MapInitEvent args)
+ {
+ _appearance.SetData(ent, PowerDeviceVisuals.Powered, ent.Comp.Powered);
+ }
+
private void ApcPowerReceiverInit(EntityUid uid, ApcPowerReceiverComponent component, ComponentInit args)
{
AllocLoad(component.NetworkLoad);
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Bed.Components;
+
+/// <summary>
+/// Tracking component added to entities buckled to stasis beds.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedBedSystem))]
+public sealed partial class StasisBedBuckledComponent : Component;
--- /dev/null
+using Content.Shared.Buckle.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Bed.Components;
+
+/// <summary>
+/// A <see cref="StrapComponent"/> that modifies a strapped entity's metabolic rate by the given multiplier
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedBedSystem))]
+public sealed partial class StasisBedComponent : Component
+{
+ /// <summary>
+ /// What the metabolic update rate will be multiplied by (higher = slower metabolism)
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public float Multiplier = 10f;
+}
using Content.Shared.Actions;
using Content.Shared.Bed.Components;
using Content.Shared.Bed.Sleep;
+using Content.Shared.Body.Events;
+using Content.Shared.Body.Systems;
using Content.Shared.Buckle.Components;
+using Content.Shared.Emag.Systems;
+using Content.Shared.Power;
+using Content.Shared.Power.EntitySystems;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] private readonly ActionContainerSystem _actConts = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
+ [Dependency] private readonly EmagSystem _emag = default!;
+ [Dependency] private readonly SharedMetabolizerSystem _metabolizer = default!;
+ [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
public override void Initialize()
SubscribeLocalEvent<HealOnBuckleComponent, MapInitEvent>(OnHealMapInit);
SubscribeLocalEvent<HealOnBuckleComponent, StrappedEvent>(OnStrapped);
SubscribeLocalEvent<HealOnBuckleComponent, UnstrappedEvent>(OnUnstrapped);
+
+ SubscribeLocalEvent<StasisBedComponent, StrappedEvent>(OnStasisStrapped);
+ SubscribeLocalEvent<StasisBedComponent, UnstrappedEvent>(OnStasisUnstrapped);
+ SubscribeLocalEvent<StasisBedComponent, GotEmaggedEvent>(OnStasisEmagged);
+ SubscribeLocalEvent<StasisBedComponent, PowerChangedEvent>(OnPowerChanged);
+ SubscribeLocalEvent<StasisBedBuckledComponent, GetMetabolicMultiplierEvent>(OnStasisGetMetabolicMultiplier);
}
private void OnHealMapInit(Entity<HealOnBuckleComponent> ent, ref MapInitEvent args)
_sleepingSystem.TryWaking(args.Buckle.Owner);
RemComp<HealOnBuckleHealingComponent>(bed);
}
+
+ private void OnStasisStrapped(Entity<StasisBedComponent> ent, ref StrappedEvent args)
+ {
+ EnsureComp<StasisBedBuckledComponent>(args.Buckle);
+ _metabolizer.UpdateMetabolicMultiplier(args.Buckle);
+ }
+
+ private void OnStasisUnstrapped(Entity<StasisBedComponent> ent, ref UnstrappedEvent args)
+ {
+ RemComp<StasisBedBuckledComponent>(ent);
+ _metabolizer.UpdateMetabolicMultiplier(args.Buckle);
+ }
+
+ private void OnStasisEmagged(Entity<StasisBedComponent> ent, ref GotEmaggedEvent args)
+ {
+ if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
+ return;
+
+ if (_emag.CheckFlag(ent, EmagType.Interaction))
+ return;
+
+ ent.Comp.Multiplier = 1f / ent.Comp.Multiplier;
+ UpdateMetabolisms(ent.Owner);
+ Dirty(ent);
+
+ args.Handled = true;
+ }
+
+ private void OnPowerChanged(Entity<StasisBedComponent> ent, ref PowerChangedEvent args)
+ {
+ UpdateMetabolisms(ent.Owner);
+ }
+
+ private void OnStasisGetMetabolicMultiplier(Entity<StasisBedBuckledComponent> ent, ref GetMetabolicMultiplierEvent args)
+ {
+ if (!TryComp<BuckleComponent>(ent, out var buckle) || buckle.BuckledTo is not { } buckledTo)
+ return;
+
+ if (!TryComp<StasisBedComponent>(buckledTo, out var stasis))
+ return;
+
+ if (!_powerReceiver.IsPowered(buckledTo))
+ return;
+
+ args.Multiplier *= stasis.Multiplier;
+ }
+
+ protected void UpdateMetabolisms(Entity<StrapComponent?> ent)
+ {
+ if (!Resolve(ent, ref ent.Comp, false))
+ return;
+
+ foreach (var buckledEntity in ent.Comp.BuckledEntities)
+ {
+ _metabolizer.UpdateMetabolicMultiplier(buckledEntity);
+ }
+ }
}
+++ /dev/null
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Bed
-{
- [Serializable, NetSerializable]
- public enum StasisBedVisuals : byte
- {
- IsOn,
- }
-}
[DataField, AutoNetworkedField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3);
+ /// <summary>
+ /// Multiplier applied to <see cref="UpdateInterval"/> for adjusting based on metabolic rate multiplier.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public float UpdateIntervalMultiplier = 1f;
+
+ /// <summary>
+ /// Adjusted update interval based off of the multiplier value.
+ /// </summary>
+ [ViewVariables]
+ public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier;
+
/// <summary>
/// How much is this entity currently bleeding?
/// Higher numbers mean more blood lost every tick.
[DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
+ /// <summary>
+ /// Multiplier applied to <see cref="UpdateInterval"/> for adjusting based on metabolic rate multiplier.
+ /// </summary>
+ [DataField]
+ public float UpdateIntervalMultiplier = 1f;
+
+ /// <summary>
+ /// Adjusted update interval based off of the multiplier value.
+ /// </summary>
+ [ViewVariables]
+ public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier;
+
/// <summary>
/// The solution inside of this stomach this transfers reagents to the body.
/// </summary>
+++ /dev/null
-namespace Content.Shared.Body.Events;
-
-// TODO REFACTOR THIS
-// This will cause rates to slowly drift over time due to floating point errors.
-// Instead, the system that raised this should trigger an update and subscribe to get-modifier events.
-[ByRefEvent]
-public readonly record struct ApplyMetabolicMultiplierEvent(
- EntityUid Uid,
- float Multiplier,
- bool Apply)
-{
- /// <summary>
- /// The entity whose metabolism is being modified.
- /// </summary>
- public readonly EntityUid Uid = Uid;
-
- /// <summary>
- /// What the metabolism's update rate will be multiplied by.
- /// </summary>
- public readonly float Multiplier = Multiplier;
-
- /// <summary>
- /// If true, apply the multiplier. If false, revert it.
- /// </summary>
- public readonly bool Apply = Apply;
-}
--- /dev/null
+namespace Content.Shared.Body.Events;
+
+/// <summary>
+/// Raised on an entity to determine their metabolic multiplier.
+/// </summary>
+[ByRefEvent]
+public record struct GetMetabolicMultiplierEvent()
+{
+ /// <summary>
+ /// What the metabolism's update rate will be multiplied by.
+ /// </summary>
+ public float Multiplier = 1f;
+}
+
+/// <summary>
+/// Raised on an entity to apply their metabolic multiplier to relevant systems.
+/// Note that you should be storing this value as to not accrue precision errors when it's modified.
+/// </summary>
+[ByRefEvent]
+public readonly record struct ApplyMetabolicMultiplierEvent(float Multiplier)
+{
+ /// <summary>
+ /// What the metabolism's update rate will be multiplied by.
+ /// </summary>
+ public readonly float Multiplier = Multiplier;
+}
if (curTime < bloodstream.NextUpdate)
continue;
- bloodstream.NextUpdate += bloodstream.UpdateInterval;
+ bloodstream.NextUpdate += bloodstream.AdjustedUpdateInterval;
DirtyField(uid, bloodstream, nameof(BloodstreamComponent.NextUpdate)); // needs to be dirtied on the client so it can be rerolled during prediction
if (!SolutionContainer.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
// Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out
_drunkSystem.TryApplyDrunkenness(
uid,
- (float)bloodstream.UpdateInterval.TotalSeconds * 2,
+ (float)bloodstream.AdjustedUpdateInterval.TotalSeconds * 2,
applySlur: false);
- _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false);
+ _stutteringSystem.DoStutter(uid, bloodstream.AdjustedUpdateInterval * 2, refresh: false);
// storing the drunk and stutter time so we can remove it independently from other effects additions
- bloodstream.StatusTime += bloodstream.UpdateInterval * 2;
+ bloodstream.StatusTime += bloodstream.AdjustedUpdateInterval * 2;
DirtyField(uid, bloodstream, nameof(BloodstreamComponent.StatusTime));
}
else if (!_mobStateSystem.IsDead(uid))
private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
{
- ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.UpdateInterval;
+ ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.AdjustedUpdateInterval;
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.NextUpdate));
}
private void OnApplyMetabolicMultiplier(Entity<BloodstreamComponent> ent, ref ApplyMetabolicMultiplierEvent args)
{
- // TODO REFACTOR THIS
- // This will slowly drift over time due to floating point errors.
- // Instead, raise an event with the base rates and allow modifiers to get applied to it.
- if (args.Apply)
- ent.Comp.UpdateInterval *= args.Multiplier;
- else
- ent.Comp.UpdateInterval /= args.Multiplier;
-
- DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.UpdateInterval));
+ ent.Comp.UpdateIntervalMultiplier = args.Multiplier;
+ DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.UpdateIntervalMultiplier));
}
private void OnRejuvenate(Entity<BloodstreamComponent> ent, ref RejuvenateEvent args)
--- /dev/null
+using Content.Shared.Body.Events;
+
+namespace Content.Shared.Body.Systems;
+
+public abstract class SharedMetabolizerSystem : EntitySystem
+{
+ /// <summary>
+ /// Updates the metabolic rate multiplier for a given entity,
+ /// raising both <see cref="GetMetabolicMultiplierEvent"/> to determine what the multiplier is and <see cref="ApplyMetabolicMultiplierEvent"/> to update relevant components.
+ /// </summary>
+ /// <param name="uid"></param>
+ public void UpdateMetabolicMultiplier(EntityUid uid)
+ {
+ var getEv = new GetMetabolicMultiplierEvent();
+ RaiseLocalEvent(uid, ref getEv);
+
+ var applyEv = new ApplyMetabolicMultiplierEvent(getEv.Multiplier);
+ RaiseLocalEvent(uid, ref applyEv);
+ }
+}
private void OnMapInit(Entity<StomachComponent> ent, ref MapInitEvent args)
{
- ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.AdjustedUpdateInterval;
}
private void OnUnpaused(Entity<StomachComponent> ent, ref EntityUnpausedEvent args)
if (_gameTiming.CurTime < stomach.NextUpdate)
continue;
- stomach.NextUpdate += stomach.UpdateInterval;
+ stomach.NextUpdate += stomach.AdjustedUpdateInterval;
// Get our solutions
if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
var queue = new RemQueue<StomachComponent.ReagentDelta>();
foreach (var delta in stomach.ReagentDeltas)
{
- delta.Increment(stomach.UpdateInterval);
+ delta.Increment(stomach.AdjustedUpdateInterval);
if (delta.Lifetime > stomach.DigestionDelay)
{
if (stomachSolution.TryGetReagent(delta.ReagentQuantity.Reagent, out var reagent))
}
}
- private void OnApplyMetabolicMultiplier(
- Entity<StomachComponent> ent,
- ref ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(Entity<StomachComponent> ent, ref ApplyMetabolicMultiplierEvent args)
{
- if (args.Apply)
- {
- ent.Comp.UpdateInterval *= args.Multiplier;
- return;
- }
-
- // This way we don't have to worry about it breaking if the stasis bed component is destroyed
- ent.Comp.UpdateInterval /= args.Multiplier;
+ ent.Comp.UpdateIntervalMultiplier = args.Multiplier;
}
public bool CanTransferSolution(
- state: icon
- state: unlit
shader: unshaded
- map: ["enum.StasisBedVisualLayers.IsOn"]
- - type: StasisBedVisuals
+ map: ["unlit"]
+ - type: GenericVisualizer
+ visuals:
+ enum.PowerDeviceVisuals.Powered:
+ unlit:
+ True: { visible: true }
+ False: { visible: false }
- type: Appearance
- type: ApcPowerReceiver
powerLoad: 1000