golobby for vanilla (#3715)

This commit is contained in:
shimorio 2026-09-03 22:31:15 +03:00 • committed by GitHub
parent 96be7c48c8
commit eacc62bbaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 270 additions and 4 deletions

View file

@ -0,0 +1,15 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'ghost-go-lobby-confirm-title'}"
Resizable="False">
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="420">
<RichTextLabel Name="WarningLabel" Margin="20 10 20 10"/>
<BoxContainer HorizontalExpand="True" HorizontalAlignment="Center">
<Button Name="ContinueButton"
Text="{Loc 'ghost-go-lobby-confirm-continue'}"
SetWidth="150"
Margin="0 10 0 10"/>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View file

@ -0,0 +1,25 @@
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Corvax.Ghost;
[GenerateTypedNameReferences]
public sealed partial class GhostGoLobbyConfirmWindow : FancyWindow
{
public event Action? ContinuePressed;
public GhostGoLobbyConfirmWindow()
{
RobustXamlLoader.Load(this);
WarningLabel.SetMessage(FormattedMessage.FromMarkupPermissive(Loc.GetString("ghost-go-lobby-confirm")));
ContinueButton.OnPressed += _ =>
{
ContinuePressed?.Invoke();
Close();
};
}
}

View file

@ -1,4 +1,5 @@
using Content.Client.Movement.Systems;
using Content.Shared.Corvax.Events;
using Content.Shared.Actions;
using Content.Shared.Ghost.Components;
using Content.Shared.Ghost.Systems;
@ -187,6 +188,11 @@ namespace Content.Client.Ghost
RaiseNetworkEvent(new GhostWarpsRequestEvent());
}
public void GhostGoLobby() // Corvax-GoLobby
{
RaiseNetworkEvent(new GhostGoLobbyEvent());
}
public void ReturnToBody()
{
var msg = new GhostReturnToBodyRequest();

View file

@ -1,11 +1,14 @@
using Content.Client.Corvax.Ghost;
using Content.Client.Gameplay;
using Content.Client.Ghost;
using Content.Client.UserInterface.Systems.Gameplay;
using Content.Client.UserInterface.Systems.Ghost.Widgets;
using Content.Shared.Corvax.CCCVars;
using Content.Shared.Ghost.Components;
using Content.Shared.Ghost.Systems;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Configuration;
namespace Content.Client.UserInterface.Systems.Ghost;
@ -13,11 +16,15 @@ namespace Content.Client.UserInterface.Systems.Ghost;
public sealed partial class GhostUIController : UIController, IOnSystemChanged<GhostSystem>
{
[Dependency] private IEntityNetworkManager _net = default!;
[Dependency] private IConfigurationManager _cfg = default!; // Corvax-GoLobby
[UISystemDependency] private readonly GhostSystem? _system = default;
private GhostGui? Gui => UIManager.GetActiveUIWidgetOrNull<GhostGui>();
private GhostGoLobbyConfirmWindow? _goLobbyConfirmWindow; // Corvax-GoLobby
private bool _goLobbyEnabled = true; // Corvax-GoLobby
public override void Initialize()
{
base.Initialize();
@ -25,6 +32,15 @@ public sealed partial class GhostUIController : UIController, IOnSystemChanged<G
var gameplayStateLoad = UIManager.GetUIController<GameplayStateLoadController>();
gameplayStateLoad.OnScreenLoad += OnScreenLoad;
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
// Corvax-GoLobby
_cfg.OnValueChanged(CCCVars.GhostGoLobbyEnabled, OnGoLobbyEnabledChanged, true);
}
private void OnGoLobbyEnabledChanged(bool enabled) // Corvax-GoLobby
{
_goLobbyEnabled = enabled;
UpdateGui();
}
private void OnScreenLoad()
@ -65,7 +81,7 @@ public sealed partial class GhostUIController : UIController, IOnSystemChanged<G
}
Gui.Visible = _system?.IsGhost ?? false;
Gui.Update(_system?.AvailableGhostRoleCount, _system?.Player?.CanReturnToBody);
Gui.Update(_system?.AvailableGhostRoleCount, _system?.Player?.CanReturnToBody, _goLobbyEnabled); // Corvax-GoLobby edit
}
private void OnPlayerRemoved(GhostComponent component)
@ -142,6 +158,7 @@ public sealed partial class GhostUIController : UIController, IOnSystemChanged<G
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
Gui.TargetWindow.OnWarpToRandomFollowedClicked += OnWarpToRandomFollowedClicked;
Gui.TargetWindow.OnWarpToRandomClicked += OnWarpToRandomClicked;
Gui.GhostGoLobbyPressed += GhostGoLobby; // Corvax-GoLobby
UpdateGui();
}
@ -155,6 +172,7 @@ public sealed partial class GhostUIController : UIController, IOnSystemChanged<G
Gui.ReturnToBodyPressed -= ReturnToBody;
Gui.GhostRolesPressed -= GhostRolesPressed;
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
Gui.GhostGoLobbyPressed -= GhostGoLobby; // Corvax-GoLobby
Gui.Hide();
}
@ -164,6 +182,22 @@ public sealed partial class GhostUIController : UIController, IOnSystemChanged<G
_system?.ReturnToBody();
}
// Corvax-Changes-Start
private void GhostGoLobby()
{
if (_goLobbyConfirmWindow is { Disposed: false })
{
_goLobbyConfirmWindow.MoveToFront();
return;
}
_goLobbyConfirmWindow = new GhostGoLobbyConfirmWindow();
_goLobbyConfirmWindow.ContinuePressed += () => _system?.GhostGoLobby();
_goLobbyConfirmWindow.OnClose += () => _goLobbyConfirmWindow = null;
_goLobbyConfirmWindow.OpenCentered();
}
// Corvax-Changes-End
private void RequestWarps()
{
_system?.RequestWarps();

View file

@ -1,9 +1,11 @@
<widgets:GhostGui xmlns="https://spacestation14.io"
<widgets:GhostGui xmlns="https://spacestation14.io"
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Ghost.Widgets"
HorizontalAlignment="Center">
<BoxContainer Orientation="Horizontal">
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
<Button Name="GhostRolesButton" />
<!-- Corvax-Changes: Added GhostGoLobbyButton -->
<Button Name="GhostGoLobbyButton" Text="{Loc ghost-go-lobby-button}" />
</BoxContainer>
</widgets:GhostGui>

View file

@ -14,6 +14,7 @@ public sealed partial class GhostGui : UIWidget
public event Action? RequestWarpsPressed;
public event Action? ReturnToBodyPressed;
public event Action? GhostRolesPressed;
public event Action? GhostGoLobbyPressed; // Corvax-GoLobby
private int _prevNumberRoles;
public GhostGui()
@ -28,6 +29,7 @@ public sealed partial class GhostGui : UIWidget
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesButton.StyleClasses.Remove(StyleClass.Negative);
GhostGoLobbyButton.OnPressed += _ => GhostGoLobbyPressed?.Invoke(); // Corvax-GoLobby
}
public void Hide()
@ -36,9 +38,10 @@ public sealed partial class GhostGui : UIWidget
Visible = false;
}
public void Update(int? roles, bool? canReturnToBody)
public void Update(int? roles, bool? canReturnToBody, bool? canGoLobby = true) // Corvax-GoLobby edit
{
ReturnToBodyButton.Disabled = !canReturnToBody ?? true;
GhostGoLobbyButton.Visible = canGoLobby ?? true; // Corvax-GoLobby
if (roles != null)
{

View file

@ -0,0 +1,13 @@
using Robust.Shared.Player;
namespace Content.Server._Corvax.Events;
public sealed class GhostJoinLobbyRequestEvent : EntityEventArgs
{
public readonly ICommonSession Session;
public GhostJoinLobbyRequestEvent(ICommonSession session)
{
Session = session;
}
}

View file

@ -0,0 +1,105 @@
using Content.Server._Corvax.Events;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.Mind;
using Content.Server.Players.PlayTimeTracking;
using Content.Server.Preferences.Managers;
using Content.Shared.Corvax.CCCVars;
using Content.Shared.Corvax.Events;
using Content.Shared.GameTicking;
using Content.Shared.Ghost.Components;
using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Popups;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Corvax.Ghost;
public sealed partial class GhostGoLobbySystem : EntitySystem
{
[Dependency] private IConfigurationManager _cfg = default!;
[Dependency] private IGameTiming _timing = default!;
[Dependency] private MindSystem _mind = default!;
[Dependency] private PlayTimeTrackingManager _playTime = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private IChatManager _chatManager = default!;
[Dependency] private IServerPreferencesManager _prefsManager = default!;
private bool _enabled;
private TimeSpan _requiredPlaytime;
private TimeSpan _deathTime;
private readonly Dictionary<NetUserId, HashSet<int>> _usedCharacterSlots = new();
public int GetSelectedSlot(NetUserId userId)
{
return _prefsManager.GetPreferences(userId).SelectedCharacterIndex;
}
public bool CanUseCharacter(NetUserId userId, int characterSlot)
{
return !_usedCharacterSlots.TryGetValue(userId, out var slots) || !slots.Contains(characterSlot);
}
public override void Initialize()
{
SubscribeNetworkEvent<GhostGoLobbyEvent>(OnGhostGoLobby);
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnRunLevelChanged);
Subs.CVar(_cfg, CCCVars.GhostGoLobbyEnabled, value => _enabled = value, true);
Subs.CVar(_cfg, CCCVars.GhostGoLobbyTimeHours, value => _requiredPlaytime = TimeSpan.FromHours(value), true);
Subs.CVar(_cfg, CCCVars.GhostGoLobbyDeathTimeMinutes, value => _deathTime = TimeSpan.FromMinutes(value), true);
}
private void OnRunLevelChanged(GameRunLevelChangedEvent ev)
{
if (ev.New == GameRunLevel.PreRoundLobby)
_usedCharacterSlots.Clear();
}
private void OnGhostGoLobby(GhostGoLobbyEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { Valid: true } attached)
return;
TryGhostGoLobby(attached, args.SenderSession);
}
private void TryGhostGoLobby(EntityUid uid, ICommonSession session)
{
if (!_enabled)
return;
var all = _playTime.GetOverallPlaytime(session);
if (all < _requiredPlaytime)
{
var remaining = (int) Math.Ceiling((_requiredPlaytime - all).TotalHours);
_popup.PopupEntity(Loc.GetString("ghost-go-lobby-playtime", ("hours", remaining)), uid, uid);
return;
}
if (!TryComp<GhostComponent>(uid, out var ghost))
return;
var timeSinceDeath = _timing.RealTime - ghost.TimeOfDeath;
if (_deathTime > TimeSpan.Zero && timeSinceDeath < _deathTime)
{
var remaining = (int) ((_deathTime - timeSinceDeath).TotalMinutes);
_popup.PopupEntity(Loc.GetString("ghost-go-lobby-deathtime", ("minutes", remaining)), uid, uid);
return;
}
var slot = GetSelectedSlot(session.UserId);
if (!_usedCharacterSlots.TryGetValue(session.UserId, out var slots))
_usedCharacterSlots[session.UserId] = slots = new HashSet<int>();
slots.Add(slot);
_mind.WipeMind(session);
RaiseLocalEvent(new GhostJoinLobbyRequestEvent(session));
}
}

View file

@ -1,3 +1,4 @@
using Content.Server._Corvax.Events;
using Content.Corvax.Interfaces.Server;
using Content.Shared.Administration;
using Content.Shared.CCVar;
@ -24,6 +25,7 @@ namespace Content.Server.GameTicking
private void InitializePlayer()
{
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
SubscribeLocalEvent<GhostJoinLobbyRequestEvent>(OnGhostJoinLobbyRequest); // CorvaxGoob-GoLobby
}
private async void PlayerStatusChanged(object? sender, SessionStatusEventArgs args)
@ -227,6 +229,13 @@ namespace Content.Server.GameTicking
RaiseLocalEvent(new PlayerJoinedLobbyEvent(session));
}
// Corvax-Changes-Start
private void OnGhostJoinLobbyRequest(GhostJoinLobbyRequestEvent ev)
{
PlayerJoinLobby(ev.Session);
}
// Corvax-Changes-End
private void ReqWindowAttentionAll()
{
RaiseNetworkEvent(new RequestWindowAttentionEvent());

View file

@ -165,6 +165,15 @@ namespace Content.Server.GameTicking
if (DummyTicker)
return;
// Corvax-GoLobby-start
var selectedSlot = _ghostGoLobby.GetSelectedSlot(player.UserId);
if (!_ghostGoLobby.CanUseCharacter(player.UserId, selectedSlot))
{
_chatManager.DispatchServerMessage(player, Loc.GetString("ghost-go-lobby-used"));
return;
}
// Corvax-GoLobby-end
if (station == EntityUid.Invalid)
{
var stations = GetSpawnableStations();

View file

@ -5,6 +5,7 @@ using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.Database;
using Content.Server.Ghost;
using Content.Server.Corvax.Ghost; // Corvax-GoLobby
using Content.Server.Maps;
using Content.Server.Players.PlayTimeTracking;
using Content.Server.Preferences.Managers;
@ -50,6 +51,7 @@ namespace Content.Server.GameTicking
[Dependency] private MapLoaderSystem _loader = default!;
[Dependency] private SharedMapSystem _map = default!;
[Dependency] private GhostSystem _ghost = default!;
[Dependency] private GhostGoLobbySystem _ghostGoLobby = default!; // Corvax-GoLobby
[Dependency] private SharedMindSystem _mind = default!;
[Dependency] private PlayTimeTrackingSystem _playTimeTrackings = default!;
[Dependency] private PvsOverrideSystem _pvsOverride = default!;

View file

@ -1,4 +1,4 @@
using Content.Shared.Corvax.TTS;
using Content.Shared.Corvax.TTS;
using Robust.Shared.Configuration;
namespace Content.Shared.Corvax.CCCVars;
@ -134,4 +134,14 @@ public sealed class CCCVars
/// </summary>
public static readonly CVarDef<bool> StationGoal =
CVarDef.Create("game.station_goal", true, CVar.SERVERONLY);
public static readonly CVarDef<bool> GhostGoLobbyEnabled =
CVarDef.Create("ghost.go_lobby.enabled", false, CVar.SERVER | CVar.REPLICATED);
public static readonly CVarDef<float> GhostGoLobbyTimeHours =
CVarDef.Create("ghost.go_lobby.require_time", 25f, CVar.SERVERONLY);
public static readonly CVarDef<float> GhostGoLobbyDeathTimeMinutes =
CVarDef.Create("ghost.go_lobby.death_time", 15f, CVar.SERVERONLY);
}

View file

@ -0,0 +1,6 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Corvax.Events;
[Serializable, NetSerializable]
public sealed class GhostGoLobbyEvent : EntityEventArgs;

View file

@ -27,3 +27,6 @@ same_type_timeout = 300.0
timerrestart = 30
preset_enabled = false
restart_enabled = false
[ghost.go_lobby]
enabled = true

View file

@ -0,0 +1,12 @@
ghost-go-lobby-button = Return to lobby
ghost-go-lobby-deathtime = You can return to the lobby in {$minutes} minutes.
ghost-go-lobby-playtime = You need to play for another {$hours} hours.
ghost-go-lobby-used = Please choose a different character.
ghost-go-lobby-confirm-title = Returning to the Lobby
ghost-go-lobby-confirm = Are you sure you want to return to the lobby?
You have forgotten everything about your current character and do not remember anything you learned while you were a ghost.
You are allowed to remember general knowledge about the game e.g., how to cook, use items, etc.
You are [color=red]NOT[/color] allowed to remember or mention the name, appearance, etc., of your previous character.
ghost-go-lobby-confirm-continue = Continue

View file

@ -0,0 +1,12 @@
ghost-go-lobby-button = Вернуться в лобби
ghost-go-lobby-deathtime = Вернуться в лобби можно через {$minutes} мин.
ghost-go-lobby-playtime = Вам нужно наиграть еще {$hours} ч.
ghost-go-lobby-used = Выберите другого персонажа.
ghost-go-lobby-confirm-title = Возвращение в лобби
ghost-go-lobby-confirm = Вы уверены, что хотите вернуться в лобби?
Вы забыли всё о своём текущем персонаже и ничего не помните из того, что узнали, будучи призраком.
Вам разрешается помнить общие знания об игре: например, как готовить, использовать предметы и т.д.
Вам [color=red]НЕ[/color] разрешается помнить или говорить имя, внешность и т.д. вашего предыдущего персонажа.
ghost-go-lobby-confirm-continue = Продолжить