diff --git a/Content.Client/Bed/SleepingSystem.cs b/Content.Client/Bed/SleepingSystem.cs deleted file mode 100644 index addf855bf3..0000000000 --- a/Content.Client/Bed/SleepingSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Server.Bed.Sleep; - -namespace Content.Client.Bed; - -public sealed class SleepingSystem : SharedSleepingSystem -{ - -} diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 49021c142f..ee43cff26d 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Actions; using Content.Server.Bed.Components; -using Content.Server.Bed.Sleep; using Content.Server.Body.Systems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs deleted file mode 100644 index 5e4f0eddb5..0000000000 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ /dev/null @@ -1,254 +0,0 @@ -using Content.Server.Popups; -using Content.Server.Sound; -using Content.Shared.Sound.Components; -using Content.Shared.Actions; -using Content.Shared.Audio; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage; -using Content.Shared.Examine; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Slippery; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public sealed class SleepingSystem : SharedSleepingSystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - [Dependency] private readonly EmitSoundSystem _emitSound = default!; - - [ValidatePrototypeId] public const string SleepActionId = "ActionSleep"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnSleepStateChanged); - SubscribeLocalEvent(OnDamageChanged); - SubscribeLocalEvent(OnSleepAction); - SubscribeLocalEvent(OnBedSleepAction); - SubscribeLocalEvent(OnWakeAction); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent>(AddWakeVerb); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnSlip); - SubscribeLocalEvent(OnConsciousAttempt); - SubscribeLocalEvent(OnInit); - } - - /// - /// when sleeping component is added or removed, we do some stuff with other components. - /// - private void OnSleepStateChanged(EntityUid uid, MobStateComponent component, SleepStateChangedEvent args) - { - if (args.FellAsleep) - { - // Expiring status effects would remove the components needed for sleeping - _statusEffectsSystem.TryRemoveStatusEffect(uid, "Stun"); - _statusEffectsSystem.TryRemoveStatusEffect(uid, "KnockedDown"); - - EnsureComp(uid); - EnsureComp(uid); - - if (TryComp(uid, out var sleepSound)) - { - var emitSound = EnsureComp(uid); - if (HasComp(uid)) - { - emitSound.Sound = sleepSound.Snore; - } - emitSound.MinInterval = sleepSound.Interval; - emitSound.MaxInterval = sleepSound.MaxInterval; - emitSound.PopUp = sleepSound.PopUp; - } - - return; - } - - RemComp(uid); - RemComp(uid); - RemComp(uid); - } - - /// - /// Wake up on taking an instance of damage at least the value of WakeThreshold. - /// - private void OnDamageChanged(EntityUid uid, SleepingComponent component, DamageChangedEvent args) - { - if (!args.DamageIncreased || args.DamageDelta == null) - return; - - if (args.DamageDelta.GetTotal() >= component.WakeThreshold) - TryWaking(uid, component); - } - - private void OnSleepAction(EntityUid uid, MobStateComponent component, SleepActionEvent args) - { - TrySleeping(uid); - } - - private void OnBedSleepAction(EntityUid uid, ActionsContainerComponent component, SleepActionEvent args) - { - TrySleeping(args.Performer); - } - - private void OnWakeAction(EntityUid uid, MobStateComponent component, WakeActionEvent args) - { - if (!TryWakeCooldown(uid)) - return; - - if (TryWaking(uid)) - args.Handled = true; - } - - /// - /// In crit, we wake up if we are not being forced to sleep. - /// And, you can't sleep when dead... - /// - private void OnMobStateChanged(EntityUid uid, SleepingComponent component, MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - { - RemComp(uid); - RemComp(uid); - return; - } - if (TryComp(uid, out var spam)) - _emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive); - } - - private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - AlternativeVerb verb = new() - { - Act = () => - { - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - }, - Text = Loc.GetString("action-name-wake"), - Priority = 2 - }; - - args.Verbs.Add(verb); - } - - /// - /// When you click on a sleeping person with an empty hand, try to wake them. - /// - private void OnInteractHand(EntityUid uid, SleepingComponent component, InteractHandEvent args) - { - args.Handled = true; - - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - } - - private void OnExamined(EntityUid uid, SleepingComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange) - { - args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(uid, EntityManager)))); - } - } - - private void OnSlip(EntityUid uid, SleepingComponent component, SlipAttemptEvent args) - { - args.Cancel(); - } - - private void OnConsciousAttempt(EntityUid uid, SleepingComponent component, ConsciousAttemptEvent args) - { - args.Cancel(); - } - - - private void OnInit(EntityUid uid, ForcedSleepingComponent component, ComponentInit args) - { - TrySleeping(uid); - } - - /// - /// Try sleeping. Only mobs can sleep. - /// - public bool TrySleeping(EntityUid uid) - { - if (!HasComp(uid)) - return false; - - var tryingToSleepEvent = new TryingToSleepEvent(uid); - RaiseLocalEvent(uid, ref tryingToSleepEvent); - if (tryingToSleepEvent.Cancelled) - return false; - - EnsureComp(uid); - return true; - } - - private bool TryWakeCooldown(EntityUid uid, SleepingComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - var curTime = _gameTiming.CurTime; - - if (curTime < component.CoolDownEnd) - { - return false; - } - - component.CoolDownEnd = curTime + component.Cooldown; - return true; - } - - /// - /// Try to wake up. - /// - public bool TryWaking(EntityUid uid, SleepingComponent? component = null, bool force = false, EntityUid? user = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - if (!force && HasComp(uid)) - { - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-failure", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true, Shared.Popups.PopupType.SmallCaution); - } - return false; - } - - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-success", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true); - } - RemComp(uid); - return true; - } - } -} diff --git a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs index 186fc91c46..bc61c5d141 100644 --- a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs +++ b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Damage.ForceSay; using Content.Shared.FixedPoint; diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs deleted file mode 100644 index c6248c88f7..0000000000 --- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage.ForceSay; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.Pointing; -using Content.Shared.Speech; -using Robust.Shared.Network; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public abstract class SharedSleepingSystem : EntitySystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly BlindableSystem _blindableSystem = default!; - - [ValidatePrototypeId] private const string WakeActionId = "ActionWake"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnSpeakAttempt); - SubscribeLocalEvent(OnSeeAttempt); - SubscribeLocalEvent(OnPointAttempt); - } - - - private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent args) - { - var ev = new SleepStateChangedEvent(true); - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - _actionsSystem.AddAction(uid, ref component.WakeAction, WakeActionId, uid); - - // TODO remove hardcoded time. - _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); - } - - private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args) - { - _actionsSystem.RemoveAction(uid, component.WakeAction); - var ev = new SleepStateChangedEvent(false); - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - } - - private void OnSpeakAttempt(EntityUid uid, SleepingComponent component, SpeakAttemptEvent args) - { - // TODO reduce duplication of this behavior with MobStateSystem somehow - if (HasComp(uid)) - { - RemCompDeferred(uid); - return; - } - - args.Cancel(); - } - - private void OnSeeAttempt(EntityUid uid, SleepingComponent component, CanSeeAttemptEvent args) - { - if (component.LifeStage <= ComponentLifeStage.Running) - args.Cancel(); - } - - private void OnPointAttempt(EntityUid uid, SleepingComponent component, PointAttemptEvent args) - { - args.Cancel(); - } - } -} - - -public sealed partial class SleepActionEvent : InstantActionEvent {} - -public sealed partial class WakeActionEvent : InstantActionEvent {} - -/// -/// Raised on an entity when they fall asleep or wake up. -/// -public sealed class SleepStateChangedEvent : EntityEventArgs -{ - public bool FellAsleep = false; - - public SleepStateChangedEvent(bool fellAsleep) - { - FellAsleep = fellAsleep; - } -} diff --git a/Content.Shared/Bed/Sleep/SleepingComponent.cs b/Content.Shared/Bed/Sleep/SleepingComponent.cs index cd468440f4..cbea0a0516 100644 --- a/Content.Shared/Bed/Sleep/SleepingComponent.cs +++ b/Content.Shared/Bed/Sleep/SleepingComponent.cs @@ -1,31 +1,42 @@ using Content.Shared.FixedPoint; +using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Bed.Sleep; /// /// Added to entities when they go to sleep. /// -[NetworkedComponent, RegisterComponent, AutoGenerateComponentPause(Dirty = true)] +[NetworkedComponent, RegisterComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause(Dirty = true)] public sealed partial class SleepingComponent : Component { /// /// How much damage of any type it takes to wake this entity. /// - [DataField("wakeThreshold")] + [DataField] public FixedPoint2 WakeThreshold = FixedPoint2.New(2); /// /// Cooldown time between users hand interaction. /// - [DataField("cooldown")] - [ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan Cooldown = TimeSpan.FromSeconds(1f); - [DataField("cooldownEnd", customTypeSerializer:typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan CoolDownEnd; + [DataField] + [AutoNetworkedField, AutoPausedField] + public TimeSpan CooldownEnd; - [DataField("wakeAction")] public EntityUid? WakeAction; + [DataField] + [AutoNetworkedField] + public EntityUid? WakeAction; + + /// + /// Sound to play when another player attempts to wake this entity. + /// + [DataField] + public SoundSpecifier WakeAttemptSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg") + { + Params = AudioParams.Default.WithVariation(0.05f) + }; } diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs new file mode 100644 index 0000000000..aac3e7bb18 --- /dev/null +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -0,0 +1,314 @@ +using Content.Shared.Actions; +using Content.Shared.Damage; +using Content.Shared.Damage.ForceSay; +using Content.Shared.Examine; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Pointing; +using Content.Shared.Popups; +using Content.Shared.Slippery; +using Content.Shared.Sound; +using Content.Shared.Sound.Components; +using Content.Shared.Speech; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.Bed.Sleep; + +public sealed partial class SleepingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly BlindableSystem _blindableSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + + public static readonly ProtoId SleepActionId = "ActionSleep"; + public static readonly ProtoId WakeActionId = "ActionWake"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBedSleepAction); + + SubscribeLocalEvent(OnSleepStateChanged); + SubscribeLocalEvent(OnWakeAction); + SubscribeLocalEvent(OnSleepAction); + + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnSpeakAttempt); + SubscribeLocalEvent(OnSeeAttempt); + SubscribeLocalEvent(OnPointAttempt); + SubscribeLocalEvent(OnSlip); + SubscribeLocalEvent(OnConsciousAttempt); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(AddWakeVerb); + SubscribeLocalEvent(OnInteractHand); + + SubscribeLocalEvent(OnInit); + } + + private void OnBedSleepAction(Entity ent, ref SleepActionEvent args) + { + TrySleeping(args.Performer); + } + + private void OnWakeAction(Entity ent, ref WakeActionEvent args) + { + if (TryWakeWithCooldown(ent.Owner)) + args.Handled = true; + } + + private void OnSleepAction(Entity ent, ref SleepActionEvent args) + { + TrySleeping((ent, ent.Comp)); + } + + /// + /// when sleeping component is added or removed, we do some stuff with other components. + /// + private void OnSleepStateChanged(Entity ent, ref SleepStateChangedEvent args) + { + if (args.FellAsleep) + { + // Expiring status effects would remove the components needed for sleeping + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "Stun"); + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "KnockedDown"); + + EnsureComp(ent); + EnsureComp(ent); + + if (TryComp(ent, out var sleepSound)) + { + var emitSound = EnsureComp(ent); + if (HasComp(ent)) + { + emitSound.Sound = sleepSound.Snore; + } + emitSound.MinInterval = sleepSound.Interval; + emitSound.MaxInterval = sleepSound.MaxInterval; + emitSound.PopUp = sleepSound.PopUp; + Dirty(ent.Owner, emitSound); + } + + return; + } + + RemComp(ent); + RemComp(ent); + RemComp(ent); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var ev = new SleepStateChangedEvent(true); + RaiseLocalEvent(ent, ref ev); + _blindableSystem.UpdateIsBlind(ent.Owner); + _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent); + + // TODO remove hardcoded time. + _actionsSystem.SetCooldown(ent.Comp.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); + } + + private void OnSpeakAttempt(Entity ent, ref SpeakAttemptEvent args) + { + // TODO reduce duplication of this behavior with MobStateSystem somehow + if (HasComp(ent)) + { + RemCompDeferred(ent); + return; + } + + args.Cancel(); + } + + private void OnSeeAttempt(Entity ent, ref CanSeeAttemptEvent args) + { + if (ent.Comp.LifeStage <= ComponentLifeStage.Running) + args.Cancel(); + } + + private void OnPointAttempt(Entity ent, ref PointAttemptEvent args) + { + args.Cancel(); + } + + private void OnSlip(Entity ent, ref SlipAttemptEvent args) + { + args.Cancel(); + } + + private void OnConsciousAttempt(Entity ent, ref ConsciousAttemptEvent args) + { + args.Cancel(); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (args.IsInDetailsRange) + { + args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(ent, EntityManager)))); + } + } + + private void AddWakeVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var target = args.Target; + var user = args.User; + AlternativeVerb verb = new() + { + Act = () => + { + TryWakeWithCooldown((ent, ent.Comp), user: user); + }, + Text = Loc.GetString("action-name-wake"), + Priority = 2 + }; + + args.Verbs.Add(verb); + } + + /// + /// When you click on a sleeping person with an empty hand, try to wake them. + /// + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + args.Handled = true; + + TryWakeWithCooldown((ent, ent.Comp), args.User); + } + + /// + /// Wake up on taking an instance of damage at least the value of WakeThreshold. + /// + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) + { + if (!args.DamageIncreased || args.DamageDelta == null) + return; + + if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold) + TryWaking((ent, ent.Comp)); + } + + /// + /// In crit, we wake up if we are not being forced to sleep. + /// And, you can't sleep when dead... + /// + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Dead) + { + RemComp(ent); + RemComp(ent); + return; + } + if (TryComp(ent, out var spam)) + _emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive); + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + TrySleeping(ent.Owner); + } + + private void Wake(Entity ent) + { + RemComp(ent); + _actionsSystem.RemoveAction(ent, ent.Comp.WakeAction); + + var ev = new SleepStateChangedEvent(false); + RaiseLocalEvent(ent, ref ev); + + _blindableSystem.UpdateIsBlind(ent.Owner); + } + + /// + /// Try sleeping. Only mobs can sleep. + /// + public bool TrySleeping(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false)) + return false; + + var tryingToSleepEvent = new TryingToSleepEvent(ent); + RaiseLocalEvent(ent, ref tryingToSleepEvent); + if (tryingToSleepEvent.Cancelled) + return false; + + EnsureComp(ent); + return true; + } + + /// + /// Tries to wake up , with a cooldown between attempts to prevent spam. + /// + public bool TryWakeWithCooldown(Entity ent, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + var curTime = _gameTiming.CurTime; + + if (curTime < ent.Comp.CooldownEnd) + return false; + + ent.Comp.CooldownEnd = curTime + ent.Comp.Cooldown; + Dirty(ent, ent.Comp); + return TryWaking(ent, user: user); + } + + /// + /// Try to wake up . + /// + public bool TryWaking(Entity ent, bool force = false, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + if (!force && HasComp(ent)) + { + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-failure", ("target", Identity.Entity(ent, EntityManager))), ent, user, PopupType.SmallCaution); + } + return false; + } + + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user); + } + + Wake((ent, ent.Comp)); + return true; + } +} + + +public sealed partial class SleepActionEvent : InstantActionEvent; + +public sealed partial class WakeActionEvent : InstantActionEvent; + +/// +/// Raised on an entity when they fall asleep or wake up. +/// +[ByRefEvent] +public record struct SleepStateChangedEvent(bool FellAsleep); diff --git a/Content.Server/Bed/Components/SnoringComponent.cs b/Content.Shared/Bed/Sleep/SnoringComponent.cs similarity index 54% rename from Content.Server/Bed/Components/SnoringComponent.cs rename to Content.Shared/Bed/Sleep/SnoringComponent.cs index 09f80327ba..2fe92951f0 100644 --- a/Content.Server/Bed/Components/SnoringComponent.cs +++ b/Content.Shared/Bed/Sleep/SnoringComponent.cs @@ -1,9 +1,11 @@ -namespace Content.Server.Bed.Sleep; +using Robust.Shared.GameStates; + +namespace Content.Shared.Bed.Sleep; /// /// This is used for the snoring trait. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class SnoringComponent : Component {