diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs index ca528e99b2..64f59c91cb 100644 --- a/Content.Client/Ghost/GhostSystem.cs +++ b/Content.Client/Ghost/GhostSystem.cs @@ -16,6 +16,10 @@ namespace Content.Client.Ghost [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IGameHud _gameHud = default!; + // Changes to this value are manually propagated. + // No good way to get an event into the UI. + public int AvailableGhostRoleCount { get; private set; } = 0; + private bool _ghostVisibility; private bool GhostVisibility @@ -51,6 +55,7 @@ namespace Content.Client.Ghost SubscribeLocalEvent(OnGhostPlayerDetach); SubscribeNetworkEvent(OnGhostWarpsResponse); + SubscribeNetworkEvent(OnUpdateGhostRoleCount); } private void OnGhostInit(EntityUid uid, GhostComponent component, ComponentInit args) @@ -78,7 +83,7 @@ namespace Content.Client.Ghost // I hate UI I hate UI I Hate UI if (component.Gui == null) { - component.Gui = new GhostGui(component, EntityManager.EntityNetManager!); + component.Gui = new GhostGui(component, this, EntityManager.EntityNetManager!); component.Gui.Update(); } @@ -113,5 +118,12 @@ namespace Content.Client.Ghost window.Populate(); } } + + private void OnUpdateGhostRoleCount(GhostUpdateGhostRoleCountEvent msg) + { + AvailableGhostRoleCount = msg.AvailableGhostRoles; + foreach (var ghost in EntityManager.EntityQuery(true)) + ghost.Gui?.Update(); + } } } diff --git a/Content.Client/Ghost/UI/GhostGui.cs b/Content.Client/Ghost/UI/GhostGui.cs index 509a83b087..fa3141a060 100644 --- a/Content.Client/Ghost/UI/GhostGui.cs +++ b/Content.Client/Ghost/UI/GhostGui.cs @@ -1,3 +1,4 @@ +using Content.Client.Stylesheets; using Content.Shared.Ghost; using Robust.Client.Console; using Robust.Client.UserInterface; @@ -13,16 +14,18 @@ namespace Content.Client.Ghost.UI { private readonly Button _returnToBody = new() {Text = Loc.GetString("ghost-gui-return-to-body-button") }; private readonly Button _ghostWarp = new() {Text = Loc.GetString("ghost-gui-ghost-warp-button") }; - private readonly Button _ghostRoles = new() {Text = Loc.GetString("ghost-gui-ghost-roles-button") }; + private readonly Button _ghostRoles = new(); private readonly GhostComponent _owner; + private readonly GhostSystem _system; public GhostTargetWindow? TargetWindow { get; } - public GhostGui(GhostComponent owner, IEntityNetworkManager eventBus) + public GhostGui(GhostComponent owner, GhostSystem system, IEntityNetworkManager eventBus) { IoCManager.InjectDependencies(this); _owner = owner; + _system = system; TargetWindow = new GhostTargetWindow(eventBus); @@ -57,6 +60,15 @@ namespace Content.Client.Ghost.UI public void Update() { _returnToBody.Disabled = !_owner.CanReturnToBody; + _ghostRoles.Text = Loc.GetString("ghost-gui-ghost-roles-button", ("count", _system.AvailableGhostRoleCount)); + if (_system.AvailableGhostRoleCount != 0) + { + _ghostRoles.StyleClasses.Add(StyleBase.ButtonCaution); + } + else + { + _ghostRoles.StyleClasses.Remove(StyleBase.ButtonCaution); + } TargetWindow?.Populate(); } diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 6c60c2dd44..85594ac420 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.UI; using Content.Shared.GameTicking; using Content.Shared.Ghost.Roles; +using Content.Shared.Ghost; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -13,6 +14,7 @@ using Robust.Shared.Console; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.ViewVariables; +using Robust.Shared.Enums; namespace Content.Server.Ghost.Roles { @@ -20,8 +22,10 @@ namespace Content.Server.Ghost.Roles public class GhostRoleSystem : EntitySystem { [Dependency] private readonly EuiManager _euiManager = default!; + [Dependency] private IPlayerManager _playerManager = default!; private uint _nextRoleIdentifier = 0; + private bool _needsUpdateGhostRoleCount = true; private readonly Dictionary _ghostRoles = new(); private readonly Dictionary _openUis = new(); private readonly Dictionary _openMakeGhostRoleUis = new(); @@ -35,6 +39,15 @@ namespace Content.Server.Ghost.Roles SubscribeLocalEvent(Reset); SubscribeLocalEvent(OnPlayerAttached); + + _playerManager.PlayerStatusChanged += PlayerStatusChanged; + } + + public override void Shutdown() + { + base.Shutdown(); + + _playerManager.PlayerStatusChanged -= PlayerStatusChanged; } private uint GetNextRoleIdentifier() @@ -91,6 +104,31 @@ namespace Content.Server.Ghost.Roles { eui.StateDirty(); } + // Note that this, like the EUIs, is deferred. + // This is for roughly the same reasons, too: + // Someone might spawn a ton of ghost roles at once. + _needsUpdateGhostRoleCount = true; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + if (_needsUpdateGhostRoleCount) + { + _needsUpdateGhostRoleCount = false; + var response = new GhostUpdateGhostRoleCountEvent(_ghostRoles.Count); + foreach (var player in _playerManager.GetAllPlayers()) + RaiseNetworkEvent(response, player.ConnectedClient); + } + } + + private void PlayerStatusChanged(object? blah, SessionStatusEventArgs args) + { + if (args.NewStatus == SessionStatus.InGame) + { + var response = new GhostUpdateGhostRoleCountEvent(_ghostRoles.Count); + RaiseNetworkEvent(response, args.Session.ConnectedClient); + } } public void RegisterGhostRole(GhostRoleComponent role) diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index fadf894e0d..46e4dfa908 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -113,4 +113,15 @@ namespace Content.Shared.Ghost public class GhostReturnToBodyRequest : EntityEventArgs { } + + [Serializable, NetSerializable] + public class GhostUpdateGhostRoleCountEvent : EntityEventArgs + { + public int AvailableGhostRoles { get; } + + public GhostUpdateGhostRoleCountEvent(int v) + { + AvailableGhostRoles = v; + } + } } diff --git a/Resources/Locale/en-US/ghost/ghost-gui.ftl b/Resources/Locale/en-US/ghost/ghost-gui.ftl index 345e07b93a..468867c284 100644 --- a/Resources/Locale/en-US/ghost/ghost-gui.ftl +++ b/Resources/Locale/en-US/ghost/ghost-gui.ftl @@ -1,6 +1,6 @@ ghost-gui-return-to-body-button = Return to body ghost-gui-ghost-warp-button = Ghost Warp -ghost-gui-ghost-roles-button = Ghost Roles +ghost-gui-ghost-roles-button = Ghost Roles ({$count}) ghost-target-window-title = Ghost Warp ghost-target-window-current-button = Warp: {$name}