Skip to content

Commit

Permalink
Feat/better cryo (new-frontiers-14#706)
Browse files Browse the repository at this point in the history
* Allowed to put others to cryosleep; added a do-after for this exact purpose

* Added drag & drop support + made it wheelchair friendly

* minor fixes

* Fix those pesky errors I've made

* Implement returning from cryo

* New cryosleep events, various changes

* Refuse to accept "passengers" when someone goes cryo

* Add a way to disable returning from cryo on a server level

* Delete bodies after a certain time

* Fix popups
  • Loading branch information
Mnemotechnician authored Dec 17, 2023
1 parent 77b0b29 commit f2d6c0d
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 28 deletions.
107 changes: 107 additions & 0 deletions Content.Client/CryoSleep/CryosleepWakeupWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Configuration;
using static Content.Shared.CryoSleep.SharedCryoSleepSystem;

namespace Content.Client.CryoSleep;

public sealed class CryosleepWakeupWindow : DefaultWindow, IEntityEventSubscriber
{
[Dependency] private readonly EntityManager _entityManager = default!;

public RichTextLabel Label;
public Button DenyButton;
public Button AcceptButton;

public CryosleepWakeupWindow()
{
IoCManager.InjectDependencies(this);

Title = Loc.GetString("cryo-wakeup-window-title");

Contents.AddChild(new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Children =
{
new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Children =
{
(Label = new RichTextLabel()
{
HorizontalExpand = true,
MaxWidth = 300,
StyleClasses = { }
}),
new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
Align = BoxContainer.AlignMode.Center,
Children =
{
(AcceptButton = new Button
{
Text = Loc.GetString("cryo-wakeup-window-accept-button"),
}),

(new Control()
{
MinSize = new Vector2i(20, 0)
}),

(DenyButton = new Button
{
Text = Loc.GetString("cryo-wakeup-window-deny-button"),
})
}
},
}
},
}
});

_entityManager.EventBus.SubscribeEvent<WakeupRequestMessage.Response>(EventSource.Network, this, OnWakeupResponse);
DenyButton.OnPressed += _ => Close();
AcceptButton.OnPressed += _ => OnAccept();
}

protected override void Opened()
{
Label.SetMessage(Loc.GetString("cryo-wakeup-window-rules"));
DenyButton.Disabled = false;
AcceptButton.Disabled = false;
}

private void OnAccept()
{
var message = new WakeupRequestMessage();
_entityManager.EntityNetManager?.SendSystemNetworkMessage(message);

// Disable the buttons to make the user wait for a response
AcceptButton.Disabled = true;
DenyButton.Disabled = true;
}

private void OnWakeupResponse(WakeupRequestMessage.Response response)
{
if (response.Status == ReturnToBodyStatus.Success)
{
Close();
return;
}

// Failure
DenyButton.Disabled = false;
if (response.Status == ReturnToBodyStatus.Occupied)
Label.SetMessage(Loc.GetString("cryo-wakeup-result-occupied"));
else if (response.Status == ReturnToBodyStatus.CryopodMissing)
Label.SetMessage(Loc.GetString("cryo-wakeup-result-no-cryopod"));
else if (response.Status == ReturnToBodyStatus.BodyMissing)
Label.SetMessage(Loc.GetString("cryo-wakeup-result-no-body"));
else if (response.Status == ReturnToBodyStatus.Disabled)
Label.SetMessage(Loc.GetString("cryo-wakeup-result-disabled"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
[UISystemDependency] private readonly GhostSystem? _system = default;

private GhostGui? Gui => UIManager.GetActiveUIWidgetOrNull<GhostGui>();
private bool _canUncryo = true; // Frontier. TODO: find a reliable way to update this, for now it just stays active all the time

public override void Initialize()
{
Expand Down Expand Up @@ -75,7 +76,8 @@ public void UpdateGui()
Gui.Visible = _system?.IsGhost ?? false;
Gui.Update(_system?.AvailableGhostRoleCount, _system?.Player?.CanReturnToBody,
_system?.Player?.TimeOfDeath,
_cfg.GetCVar(NF14CVars.RespawnTime));
_cfg.GetCVar(NF14CVars.RespawnTime),
_canUncryo && _cfg.GetCVar(NF14CVars.CryoReturnEnabled));
}

private void UpdateRespawn(TimeSpan? timeOfDeath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
<Button Name="GhostRolesButton" />
<Button Name="GhostRespawnButton" Text="Respawn" />
<Button Name="GhostRespawnButton" Text="{Loc ghost-gui-respawn}" />
<Button Name="CryosleepReturnButton" Text="{Loc ghost-gui-uncryo}" />
</BoxContainer>
</widgets:GhostGui>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Client.CryoSleep;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Ghost.Controls;
using Robust.Client.AutoGenerated;
Expand All @@ -21,6 +22,7 @@ public sealed partial class GhostGui : UIWidget

public GhostTargetWindow TargetWindow { get; }
public GhostRespawnRulesWindow RulesWindow { get; }
public CryosleepWakeupWindow CryosleepWakeupWindow { get; } // Frontier

public event Action? RequestWarpsPressed;
public event Action? ReturnToBodyPressed;
Expand All @@ -33,6 +35,7 @@ public GhostGui()

TargetWindow = new GhostTargetWindow();
RulesWindow = new GhostRespawnRulesWindow();
CryosleepWakeupWindow = new CryosleepWakeupWindow();
RulesWindow.RespawnButton.OnPressed += _ => GhostRespawnPressed?.Invoke();

MouseFilter = MouseFilterMode.Ignore;
Expand All @@ -41,6 +44,7 @@ public GhostGui()
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
GhostRespawnButton.OnPressed += _ => RulesWindow.OpenCentered();
CryosleepReturnButton.OnPressed += _ => CryosleepWakeupWindow.OpenCentered(); // Frontier
}

public void Hide()
Expand All @@ -58,7 +62,7 @@ public void UpdateRespawn(TimeSpan? todd)
}
}

public void Update(int? roles, bool? canReturnToBody, TimeSpan? timeOfDeath, float minTimeToRespawn)
public void Update(int? roles, bool? canReturnToBody, TimeSpan? timeOfDeath, float minTimeToRespawn, bool canUncryo)
{
ReturnToBodyButton.Disabled = !canReturnToBody ?? true;
_timeOfDeath = timeOfDeath;
Expand All @@ -78,6 +82,8 @@ public void Update(int? roles, bool? canReturnToBody, TimeSpan? timeOfDeath, flo
}

TargetWindow.Populate();

CryosleepReturnButton.Disabled = !canUncryo;
}

protected override void FrameUpdate(FrameEventArgs args)
Expand Down
5 changes: 5 additions & 0 deletions Content.Server/_NF/CryoSleep/CryoSleepComponent.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.Containers;

Expand All @@ -13,4 +14,8 @@ public sealed partial class CryoSleepComponent : Component
[DataField("leaveSound")]
public SoundSpecifier LeaveSound = new SoundPathSpecifier("/Audio/Effects/radpulse1.ogg");

/// <summary>
/// The ID of the latest DoAfter event associated with this entity. May be null if there's no DoAfter going on.
/// </summary>
public DoAfterId? CryosleepDoAfter = null;
}
22 changes: 15 additions & 7 deletions Content.Server/_NF/CryoSleep/CryoSleepEui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,36 @@ namespace Content.Server.CryoSleep;
public sealed class CryoSleepEui : BaseEui
{
private readonly CryoSleepSystem _cryoSystem;
private readonly EntityUid _mind;
private readonly EntityUid _body;
private readonly EntityUid _cryopod;

public CryoSleepEui(EntityUid mind, CryoSleepSystem cryoSys)
public CryoSleepEui(EntityUid body, EntityUid cryopod, CryoSleepSystem cryoSys)
{
_mind = mind;
_body = body;
_cryopod = cryopod;
_cryoSystem = cryoSys;
}

public override void HandleMessage(EuiMessageBase msg)
{
base.HandleMessage(msg);

if (msg is not AcceptCryoChoiceMessage choice ||
choice.Button == AcceptCryoUiButton.Deny)
if (msg is not AcceptCryoChoiceMessage choice)
{
Close();
return;
}

if (_mind is { Valid: true } body)
if (_body is { Valid: true })
{
_cryoSystem.CryoStoreBody(body);
if (choice.Button == AcceptCryoUiButton.Accept)
{
_cryoSystem.CryoStoreBody(_body, _cryopod);
}
else
{
_cryoSystem.EjectBody(_cryopod, body: _body);
}
}

Close();
Expand Down
101 changes: 101 additions & 0 deletions Content.Server/_NF/CryoSleep/CryoSleepSystem.Returning.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System.Threading;
using Content.Server.Administration.Logs;
using Content.Server.GameTicking;
using Content.Shared.Bed.Sleep;
using Content.Shared.Database;
using Content.Shared.Ghost;
using Content.Shared.Mind;
using Content.Shared.NF14.CCVar;
using Content.Shared.Players;
using Robust.Shared.Configuration;
using Robust.Shared.Network;

namespace Content.Server.CryoSleep;

public sealed partial class CryoSleepSystem
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;

private void InitReturning()
{
SubscribeNetworkEvent<WakeupRequestMessage>(OnWakeupMessage);
SubscribeNetworkEvent<GetStatusMessage>(OnGetStatusMessage);
SubscribeLocalEvent<PlayerJoinedLobbyEvent>(e => ResetCryosleepState(e.PlayerSession.UserId));
SubscribeLocalEvent<PlayerBeforeSpawnEvent>(e => ResetCryosleepState(e.Player.UserId));
}

private void OnWakeupMessage(WakeupRequestMessage message, EntitySessionEventArgs session)
{
var entity = session.SenderSession.GetMind();

var result = entity == null || !TryComp<MindComponent>(entity, out var mind)
? ReturnToBodyStatus.NotAGhost
: TryReturnToBody(mind);

var msg = new WakeupRequestMessage.Response(result);
RaiseNetworkEvent(msg, session.SenderSession);
}

public void OnGetStatusMessage(GetStatusMessage message, EntitySessionEventArgs args)
{
var msg = new GetStatusMessage.Response(HasCryosleepingBody(args.SenderSession.UserId));
RaiseNetworkEvent(msg, args.SenderSession);
}

/// <summary>
/// Returns the mind to the original body, if any. The mind must be possessing a ghost, unless [force] is true.
/// </summary>
public ReturnToBodyStatus TryReturnToBody(MindComponent mind, bool force = false)
{
if (!_configurationManager.GetCVar(NF14CVars.CryoReturnEnabled))
return ReturnToBodyStatus.Disabled;

var id = mind.UserId;
if (id == null || !_storedBodies.TryGetValue(id.Value, out var storedBody))
return ReturnToBodyStatus.BodyMissing;

if (!force && (mind.CurrentEntity is not { Valid: true } ghost || !HasComp<GhostComponent>(ghost)))
return ReturnToBodyStatus.NotAGhost;

var cryopod = storedBody!.Value.Cryopod;
if (!Exists(cryopod) || Deleted(cryopod) || !TryComp<CryoSleepComponent>(cryopod, out var cryoComp))
return ReturnToBodyStatus.CryopodMissing;

var body = storedBody.Value.Body;
if (IsOccupied(cryoComp) || !cryoComp.BodyContainer.Insert(body, EntityManager))
return ReturnToBodyStatus.Occupied;

_storedBodies.Remove(id.Value);
_mind.ControlMob(id.Value, body);
// Force the mob to sleep
var sleep = EnsureComp<SleepingComponent>(body);
sleep.CoolDownEnd = TimeSpan.FromSeconds(5);

_popup.PopupEntity(Loc.GetString("cryopod-wake-up", ("entity", body)), body);

RaiseLocalEvent(body, new CryosleepWakeUpEvent(storedBody.Value.Cryopod, id), true);

_adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"{id.Value} has returned from cryosleep!");
return ReturnToBodyStatus.Success;
}

/// <summary>
/// Removes the body of the given user from the cryosleep dictionary, making them unable to return to it.
/// Also actually deletes the body if it's still on that map.
/// </summary>
public void ResetCryosleepState(NetUserId id)
{
var body = _storedBodies.GetValueOrDefault(id, null);

if (body != null && _storedBodies.Remove(id) && Transform(body!.Value.Body).ParentUid == _storageMap)
{
QueueDel(body.Value.Body);
}
}

public bool HasCryosleepingBody(NetUserId id)
{
return _storedBodies.ContainsKey(id);
}
}
Loading

0 comments on commit f2d6c0d

Please sign in to comment.