mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-18 14:07:53 +03:00
# Description This reworks the narcolepsy system: - Before you actually fall asleep, you have a chance to see a warning popup and prepare for the nap. - If you go to sleep normally, and spend more time sleeping than the minimum time of a narcolepsy incident (10 seconds normally), your narcolepsy timer will reset, allowing you to make sure you won't fall asleep mid important work. - Your narcolepsy timer no longer decreases while you are sleeping. - However, if you do fall asleep for any reason while being close to a natural narcolepsy incident (close enough for a warning to be shown - 25 seconds by default), a narcolepsy incident will happen immediately. - When you wake up after sleeping enough to reset the narcolepsy timer, a popup is shown. <details><summary><h1>Media</h1></summary> <p> https://github.com/user-attachments/assets/600bb287-1919-4f55-9672-313b55189aaa  </p> </details> # Changelog 🆑 - add: Narcolepsy has been reworked. You can now know when you're about to fall asleep, and can choose to go to sleep willingly to reset the narcolepsy timer and avoid an incident.
118 lines
4.6 KiB
C#
118 lines
4.6 KiB
C#
using Content.Server.Chat.Managers;
|
|
using Content.Shared.Bed.Sleep;
|
|
using Content.Shared.Chat;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.StatusEffect;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Server.Traits.Assorted;
|
|
|
|
/// <summary>
|
|
/// This handles narcolepsy, causing the affected to fall asleep uncontrollably at a random interval.
|
|
/// </summary>
|
|
public sealed class NarcolepsySystem : EntitySystem
|
|
{
|
|
[ValidatePrototypeId<StatusEffectPrototype>]
|
|
private const string StatusEffectKey = "ForcedSleep"; // Same one used by N2O and other sleep chems.
|
|
|
|
[Dependency] private readonly IChatManager _chatMan = default!;
|
|
[Dependency] private readonly SharedPopupSystem _popups = default!;
|
|
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
|
|
/// <inheritdoc/>
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<NarcolepsyComponent, ComponentStartup>(SetupNarcolepsy);
|
|
SubscribeLocalEvent<NarcolepsyComponent, SleepStateChangedEvent>(OnSleepChanged);
|
|
}
|
|
|
|
private void SetupNarcolepsy(EntityUid uid, NarcolepsyComponent component, ComponentStartup args)
|
|
{
|
|
PrepareNextIncident((uid, component));
|
|
}
|
|
|
|
private void OnSleepChanged(Entity<NarcolepsyComponent> ent, ref SleepStateChangedEvent args)
|
|
{
|
|
// When falling asleep while an incident is nigh, force it to happen immediately.
|
|
if (args.FellAsleep)
|
|
{
|
|
if (ent.Comp.NextIncidentTime < ent.Comp.TimeBeforeWarning)
|
|
StartIncident(ent);
|
|
return;
|
|
}
|
|
|
|
// When waking up after sleeping for at least the minimum time of an incident, reset the incident timer and show a popup.
|
|
if (args.TimeSlept is null || args.TimeSlept.Value.TotalSeconds < ent.Comp.DurationOfIncident.X)
|
|
return;
|
|
|
|
ShowRandomPopup(ent, ent.Comp.WakeupLocaleBase, ent.Comp.WakeupLocaleCount);
|
|
PrepareNextIncident(ent);
|
|
}
|
|
|
|
public void AdjustNarcolepsyTimer(EntityUid uid, float setTime, NarcolepsyComponent? narcolepsy = null)
|
|
{
|
|
if (!Resolve(uid, ref narcolepsy, false) || narcolepsy.NextIncidentTime > setTime)
|
|
return;
|
|
|
|
narcolepsy.NextIncidentTime = setTime;
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
var query = EntityQueryEnumerator<NarcolepsyComponent>();
|
|
while (query.MoveNext(out var uid, out var narcolepsy))
|
|
{
|
|
if (HasComp<SleepingComponent>(uid))
|
|
continue;
|
|
|
|
narcolepsy.NextIncidentTime -= frameTime;
|
|
if (narcolepsy.NextIncidentTime <= narcolepsy.TimeBeforeWarning && narcolepsy.NextIncidentTime < narcolepsy.LastWarningRollTime - 1f)
|
|
{
|
|
// Roll for showing a popup. There should really be a class for doing this.
|
|
narcolepsy.LastWarningRollTime = narcolepsy.NextIncidentTime;
|
|
if (_random.Prob(narcolepsy.WarningChancePerSecond))
|
|
{
|
|
ShowRandomPopup(uid, narcolepsy.WarningLocaleBase, narcolepsy.WakeupLocaleCount);
|
|
narcolepsy.LastWarningRollTime = 0f; // Do not show any more popups for the upcoming incident
|
|
}
|
|
}
|
|
|
|
if (narcolepsy.NextIncidentTime >= 0)
|
|
continue;
|
|
|
|
StartIncident((uid, narcolepsy));
|
|
}
|
|
}
|
|
|
|
public void StartIncident(Entity<NarcolepsyComponent> ent)
|
|
{
|
|
var duration = _random.NextFloat(ent.Comp.DurationOfIncident.X, ent.Comp.DurationOfIncident.Y);
|
|
PrepareNextIncident(ent, duration);
|
|
|
|
_statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(ent, StatusEffectKey, TimeSpan.FromSeconds(duration), false);
|
|
}
|
|
|
|
private void PrepareNextIncident(Entity<NarcolepsyComponent> ent, float startingFrom = 0f)
|
|
{
|
|
var time = _random.NextFloat(ent.Comp.TimeBetweenIncidents.X, ent.Comp.TimeBetweenIncidents.Y);
|
|
ent.Comp.NextIncidentTime = startingFrom + time;
|
|
ent.Comp.LastWarningRollTime = float.MaxValue;
|
|
}
|
|
|
|
private void ShowRandomPopup(EntityUid uid, string prefix, int count)
|
|
{
|
|
if (count <= 0 || !TryComp<ActorComponent>(uid, out var actor))
|
|
return;
|
|
|
|
var popup = Loc.GetString($"{prefix}-{_random.Next(1, count + 1)}");
|
|
_popups.PopupEntity(popup, uid, uid, PopupType.MediumCaution);
|
|
// This should use ChatChannel.Visual, but it's not displayed on the client.
|
|
_chatMan.ChatMessageToOne(ChatChannel.Notifications, popup, popup, uid, false,
|
|
actor.PlayerSession.Channel, Color.IndianRed);
|
|
}
|
|
}
|