+++ /dev/null
-using Content.Server.Bed.Sleep;
-
-namespace Content.Client.Bed;
-
-public sealed class SleepingSystem : SharedSleepingSystem
-{
-
-}
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;
+++ /dev/null
-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<EntityPrototype>] public const string SleepActionId = "ActionSleep";
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<MobStateComponent, SleepStateChangedEvent>(OnSleepStateChanged);
- SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged);
- SubscribeLocalEvent<MobStateComponent, SleepActionEvent>(OnSleepAction);
- SubscribeLocalEvent<ActionsContainerComponent, SleepActionEvent>(OnBedSleepAction);
- SubscribeLocalEvent<MobStateComponent, WakeActionEvent>(OnWakeAction);
- SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged);
- SubscribeLocalEvent<SleepingComponent, GetVerbsEvent<AlternativeVerb>>(AddWakeVerb);
- SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
- SubscribeLocalEvent<SleepingComponent, ExaminedEvent>(OnExamined);
- SubscribeLocalEvent<SleepingComponent, SlipAttemptEvent>(OnSlip);
- SubscribeLocalEvent<SleepingComponent, ConsciousAttemptEvent>(OnConsciousAttempt);
- SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit);
- }
-
- /// <summary>
- /// when sleeping component is added or removed, we do some stuff with other components.
- /// </summary>
- 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<StunnedComponent>(uid);
- EnsureComp<KnockedDownComponent>(uid);
-
- if (TryComp<SleepEmitSoundComponent>(uid, out var sleepSound))
- {
- var emitSound = EnsureComp<SpamEmitSoundComponent>(uid);
- if (HasComp<SnoringComponent>(uid))
- {
- emitSound.Sound = sleepSound.Snore;
- }
- emitSound.MinInterval = sleepSound.Interval;
- emitSound.MaxInterval = sleepSound.MaxInterval;
- emitSound.PopUp = sleepSound.PopUp;
- }
-
- return;
- }
-
- RemComp<StunnedComponent>(uid);
- RemComp<KnockedDownComponent>(uid);
- RemComp<SpamEmitSoundComponent>(uid);
- }
-
- /// <summary>
- /// Wake up on taking an instance of damage at least the value of WakeThreshold.
- /// </summary>
- 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;
- }
-
- /// <summary>
- /// In crit, we wake up if we are not being forced to sleep.
- /// And, you can't sleep when dead...
- /// </summary>
- private void OnMobStateChanged(EntityUid uid, SleepingComponent component, MobStateChangedEvent args)
- {
- if (args.NewMobState == MobState.Dead)
- {
- RemComp<SpamEmitSoundComponent>(uid);
- RemComp<SleepingComponent>(uid);
- return;
- }
- if (TryComp<SpamEmitSoundComponent>(uid, out var spam))
- _emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive);
- }
-
- private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent<AlternativeVerb> 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);
- }
-
- /// <summary>
- /// When you click on a sleeping person with an empty hand, try to wake them.
- /// </summary>
- 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);
- }
-
- /// <summary>
- /// Try sleeping. Only mobs can sleep.
- /// </summary>
- public bool TrySleeping(EntityUid uid)
- {
- if (!HasComp<MobStateComponent>(uid))
- return false;
-
- var tryingToSleepEvent = new TryingToSleepEvent(uid);
- RaiseLocalEvent(uid, ref tryingToSleepEvent);
- if (tryingToSleepEvent.Cancelled)
- return false;
-
- EnsureComp<SleepingComponent>(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;
- }
-
- /// <summary>
- /// Try to wake up.
- /// </summary>
- 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<ForcedSleepingComponent>(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<SleepingComponent>(uid);
- return true;
- }
- }
-}
+using Content.Shared.Bed.Sleep;
using Content.Shared.Damage;
using Content.Shared.Damage.ForceSay;
using Content.Shared.FixedPoint;
+++ /dev/null
-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<EntityPrototype>] private const string WakeActionId = "ActionWake";
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<SleepingComponent, ComponentShutdown>(OnShutdown);
- SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
- SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
- SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(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<AllowNextCritSpeechComponent>(uid))
- {
- RemCompDeferred<AllowNextCritSpeechComponent>(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 {}
-
-/// <summary>
-/// Raised on an entity when they fall asleep or wake up.
-/// </summary>
-public sealed class SleepStateChangedEvent : EntityEventArgs
-{
- public bool FellAsleep = false;
-
- public SleepStateChangedEvent(bool fellAsleep)
- {
- FellAsleep = fellAsleep;
- }
-}
using Content.Shared.FixedPoint;
+using Robust.Shared.Audio;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Bed.Sleep;
/// <summary>
/// Added to entities when they go to sleep.
/// </summary>
-[NetworkedComponent, RegisterComponent, AutoGenerateComponentPause(Dirty = true)]
+[NetworkedComponent, RegisterComponent]
+[AutoGenerateComponentState, AutoGenerateComponentPause(Dirty = true)]
public sealed partial class SleepingComponent : Component
{
/// <summary>
/// How much damage of any type it takes to wake this entity.
/// </summary>
- [DataField("wakeThreshold")]
+ [DataField]
public FixedPoint2 WakeThreshold = FixedPoint2.New(2);
/// <summary>
/// Cooldown time between users hand interaction.
/// </summary>
- [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;
+
+ /// <summary>
+ /// Sound to play when another player attempts to wake this entity.
+ /// </summary>
+ [DataField]
+ public SoundSpecifier WakeAttemptSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg")
+ {
+ Params = AudioParams.Default.WithVariation(0.05f)
+ };
}
--- /dev/null
+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<EntityPrototype> SleepActionId = "ActionSleep";
+ public static readonly ProtoId<EntityPrototype> WakeActionId = "ActionWake";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<ActionsContainerComponent, SleepActionEvent>(OnBedSleepAction);
+
+ SubscribeLocalEvent<MobStateComponent, SleepStateChangedEvent>(OnSleepStateChanged);
+ SubscribeLocalEvent<MobStateComponent, WakeActionEvent>(OnWakeAction);
+ SubscribeLocalEvent<MobStateComponent, SleepActionEvent>(OnSleepAction);
+
+ SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged);
+ SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged);
+ SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
+ SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
+ SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt);
+ SubscribeLocalEvent<SleepingComponent, SlipAttemptEvent>(OnSlip);
+ SubscribeLocalEvent<SleepingComponent, ConsciousAttemptEvent>(OnConsciousAttempt);
+ SubscribeLocalEvent<SleepingComponent, ExaminedEvent>(OnExamined);
+ SubscribeLocalEvent<SleepingComponent, GetVerbsEvent<AlternativeVerb>>(AddWakeVerb);
+ SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
+
+ SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit);
+ }
+
+ private void OnBedSleepAction(Entity<ActionsContainerComponent> ent, ref SleepActionEvent args)
+ {
+ TrySleeping(args.Performer);
+ }
+
+ private void OnWakeAction(Entity<MobStateComponent> ent, ref WakeActionEvent args)
+ {
+ if (TryWakeWithCooldown(ent.Owner))
+ args.Handled = true;
+ }
+
+ private void OnSleepAction(Entity<MobStateComponent> ent, ref SleepActionEvent args)
+ {
+ TrySleeping((ent, ent.Comp));
+ }
+
+ /// <summary>
+ /// when sleeping component is added or removed, we do some stuff with other components.
+ /// </summary>
+ private void OnSleepStateChanged(Entity<MobStateComponent> 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<StunnedComponent>(ent);
+ EnsureComp<KnockedDownComponent>(ent);
+
+ if (TryComp<SleepEmitSoundComponent>(ent, out var sleepSound))
+ {
+ var emitSound = EnsureComp<SpamEmitSoundComponent>(ent);
+ if (HasComp<SnoringComponent>(ent))
+ {
+ emitSound.Sound = sleepSound.Snore;
+ }
+ emitSound.MinInterval = sleepSound.Interval;
+ emitSound.MaxInterval = sleepSound.MaxInterval;
+ emitSound.PopUp = sleepSound.PopUp;
+ Dirty(ent.Owner, emitSound);
+ }
+
+ return;
+ }
+
+ RemComp<StunnedComponent>(ent);
+ RemComp<KnockedDownComponent>(ent);
+ RemComp<SpamEmitSoundComponent>(ent);
+ }
+
+ private void OnMapInit(Entity<SleepingComponent> 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<SleepingComponent> ent, ref SpeakAttemptEvent args)
+ {
+ // TODO reduce duplication of this behavior with MobStateSystem somehow
+ if (HasComp<AllowNextCritSpeechComponent>(ent))
+ {
+ RemCompDeferred<AllowNextCritSpeechComponent>(ent);
+ return;
+ }
+
+ args.Cancel();
+ }
+
+ private void OnSeeAttempt(Entity<SleepingComponent> ent, ref CanSeeAttemptEvent args)
+ {
+ if (ent.Comp.LifeStage <= ComponentLifeStage.Running)
+ args.Cancel();
+ }
+
+ private void OnPointAttempt(Entity<SleepingComponent> ent, ref PointAttemptEvent args)
+ {
+ args.Cancel();
+ }
+
+ private void OnSlip(Entity<SleepingComponent> ent, ref SlipAttemptEvent args)
+ {
+ args.Cancel();
+ }
+
+ private void OnConsciousAttempt(Entity<SleepingComponent> ent, ref ConsciousAttemptEvent args)
+ {
+ args.Cancel();
+ }
+
+ private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args)
+ {
+ if (args.IsInDetailsRange)
+ {
+ args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(ent, EntityManager))));
+ }
+ }
+
+ private void AddWakeVerb(Entity<SleepingComponent> ent, ref GetVerbsEvent<AlternativeVerb> 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);
+ }
+
+ /// <summary>
+ /// When you click on a sleeping person with an empty hand, try to wake them.
+ /// </summary>
+ private void OnInteractHand(Entity<SleepingComponent> ent, ref InteractHandEvent args)
+ {
+ args.Handled = true;
+
+ TryWakeWithCooldown((ent, ent.Comp), args.User);
+ }
+
+ /// <summary>
+ /// Wake up on taking an instance of damage at least the value of WakeThreshold.
+ /// </summary>
+ private void OnDamageChanged(Entity<SleepingComponent> ent, ref DamageChangedEvent args)
+ {
+ if (!args.DamageIncreased || args.DamageDelta == null)
+ return;
+
+ if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold)
+ TryWaking((ent, ent.Comp));
+ }
+
+ /// <summary>
+ /// In crit, we wake up if we are not being forced to sleep.
+ /// And, you can't sleep when dead...
+ /// </summary>
+ private void OnMobStateChanged(Entity<SleepingComponent> ent, ref MobStateChangedEvent args)
+ {
+ if (args.NewMobState == MobState.Dead)
+ {
+ RemComp<SpamEmitSoundComponent>(ent);
+ RemComp<SleepingComponent>(ent);
+ return;
+ }
+ if (TryComp<SpamEmitSoundComponent>(ent, out var spam))
+ _emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive);
+ }
+
+ private void OnInit(Entity<ForcedSleepingComponent> ent, ref ComponentInit args)
+ {
+ TrySleeping(ent.Owner);
+ }
+
+ private void Wake(Entity<SleepingComponent> ent)
+ {
+ RemComp<SleepingComponent>(ent);
+ _actionsSystem.RemoveAction(ent, ent.Comp.WakeAction);
+
+ var ev = new SleepStateChangedEvent(false);
+ RaiseLocalEvent(ent, ref ev);
+
+ _blindableSystem.UpdateIsBlind(ent.Owner);
+ }
+
+ /// <summary>
+ /// Try sleeping. Only mobs can sleep.
+ /// </summary>
+ public bool TrySleeping(Entity<MobStateComponent?> 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<SleepingComponent>(ent);
+ return true;
+ }
+
+ /// <summary>
+ /// Tries to wake up <paramref name="ent"/>, with a cooldown between attempts to prevent spam.
+ /// </summary>
+ public bool TryWakeWithCooldown(Entity<SleepingComponent?> 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);
+ }
+
+ /// <summary>
+ /// Try to wake up <paramref name="ent"/>.
+ /// </summary>
+ public bool TryWaking(Entity<SleepingComponent?> ent, bool force = false, EntityUid? user = null)
+ {
+ if (!Resolve(ent, ref ent.Comp, false))
+ return false;
+
+ if (!force && HasComp<ForcedSleepingComponent>(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;
+
+/// <summary>
+/// Raised on an entity when they fall asleep or wake up.
+/// </summary>
+[ByRefEvent]
+public record struct SleepStateChangedEvent(bool FellAsleep);
-namespace Content.Server.Bed.Sleep;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Bed.Sleep;
/// <summary>
/// This is used for the snoring trait.
/// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
public sealed partial class SnoringComponent : Component
{