diff --git a/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml b/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml
index 54aeffe72c..6199270e05 100644
--- a/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml
+++ b/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml
@@ -9,6 +9,7 @@
xmlns:hotbar="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Widgets"
+ xmlns:sss="clr-namespace:Content.Client._SSS.UserInterface.Widgets"
Name="DefaultHud"
VerticalExpand="False"
VerticalAlignment="Bottom"
@@ -30,4 +31,5 @@
+
diff --git a/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml.cs b/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml.cs
index 12f8422aeb..c51811e9b7 100644
--- a/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml.cs
+++ b/Content.Client/UserInterface/Screens/DefaultGameScreen.xaml.cs
@@ -23,6 +23,8 @@ public DefaultGameScreen()
SetAnchorAndMarginPreset(Chat, LayoutPreset.TopRight, margin: 10);
SetAnchorAndMarginPreset(Alerts, LayoutPreset.TopRight, margin: 10);
+ SetAnchorAndMarginPreset(SSSStatus, LayoutPreset.BottomRight, margin: 10);
+
Chat.OnResized += ChatOnResized;
Chat.OnChatResizeFinish += ChatOnResizeFinish;
diff --git a/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml b/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml
index 7f1d1bcd5b..c2d664d113 100644
--- a/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml
+++ b/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml
@@ -10,6 +10,7 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Widgets"
+ xmlns:sss="clr-namespace:Content.Client._SSS.UserInterface.Widgets"
Name="SeparatedChatHud"
VerticalExpand="False"
VerticalAlignment="Bottom"
@@ -25,6 +26,7 @@
+
diff --git a/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml.cs b/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml.cs
index e04d377d32..54bc8f5848 100644
--- a/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml.cs
+++ b/Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml.cs
@@ -24,6 +24,8 @@ public SeparatedChatGameScreen()
SetAnchorAndMarginPreset(Hotbar, LayoutPreset.BottomWide, margin: 5);
SetAnchorAndMarginPreset(Alerts, LayoutPreset.CenterRight, margin: 10);
+ SetAnchorAndMarginPreset(SSSStatus, LayoutPreset.BottomRight, margin: 10);
+
ScreenContainer.OnSplitResizeFinished += () =>
OnChatResized?.Invoke(new Vector2(ScreenContainer.SplitFraction, 0));
diff --git a/Content.Client/_SSS/UserInterface/SSSStatusUIController.cs b/Content.Client/_SSS/UserInterface/SSSStatusUIController.cs
new file mode 100644
index 0000000000..474e9bc431
--- /dev/null
+++ b/Content.Client/_SSS/UserInterface/SSSStatusUIController.cs
@@ -0,0 +1,253 @@
+using Content.Client._SSS.UserInterface.Widgets;
+using Content.Client.Gameplay;
+using Content.Client.GameTicking.Managers;
+using Content.Shared._SSS.SuspicionGameRule;
+using Content.Shared._SSS.SuspicionGameRule.Components;
+using Content.Shared.Damage;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
+using Robust.Client.Player;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controllers;
+using Robust.Shared.Timing;
+
+namespace Content.Client._SSS.UserInterface;
+
+public sealed class SSSStatusUIController : UIController, IOnSystemChanged, IOnStateChanged
+{
+ private ISawmill _log = default!;
+
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+
+ [UISystemDependency] private readonly MobThresholdSystem? _mobThreshold = default!;
+ [UISystemDependency] private readonly ClientGameTicker? _clientGameTicker = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _log = Logger.GetSawmill("StatusUI");
+
+ SubscribeNetworkEvent(UpdateTimerEnd);
+ SubscribeNetworkEvent(PreroundStarted);
+ SubscribeNetworkEvent(UpdateRoleDisplay);
+ SubscribeNetworkEvent(UpdatePlayerSpawn);
+ }
+
+ private TimeSpan _lastEndTime;
+ private TimeSpan _lastDrawnSeconds;
+
+ private (string, Color)? _queuedRole = null;
+ private (string, float, float)? _queuedHealth = null;
+
+ public override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+
+ // I LOVE THIS HACK I LOVE THIS HACK I LOVE THIS HACK I LOVE THIS HACK
+ SetRoleFromQueued();
+ SetHealthBarFromQueued();
+
+ // TODO: limit this to only update when the timer is not the same
+ if (_clientGameTicker is null)
+ return;
+
+ if (_lastEndTime == TimeSpan.MinValue)
+ return;
+
+ var drawTime = _lastEndTime.Subtract(_clientGameTicker.RoundDuration());
+ if ((int)drawTime.TotalSeconds != (int)_lastDrawnSeconds.TotalSeconds)
+ {
+ UpdateTimer(drawTime);
+ _lastDrawnSeconds = drawTime;
+ }
+ }
+
+ private SSSStatusGui? StatusUI => UIManager.GetActiveUIWidgetOrNull();
+
+ public void UpdateHealth(Entity ent)
+ {
+
+ if (!EntityManager.TryGetComponent(ent, out var mobState))
+ return;
+
+ if (mobState.CurrentState == MobState.Dead)
+ {
+ SetHealthBarUI(Loc.GetString("suspicion-status-ui-health-dead"), 0, 100);
+ return;
+ }
+
+ if (!EntityManager.TryGetComponent(ent, out var mobThresholds))
+ return;
+
+ if (_mobThreshold is null)
+ return;
+
+ var maxHp = _mobThreshold.GetThresholdForState(ent, MobState.Critical, mobThresholds);
+ var hp = Math.Max(Math.Ceiling((maxHp - ent.Comp.TotalDamage).Double()), 0);
+
+ SetHealthBar((float)hp, maxHp.Float());
+ }
+
+ private void SetHealthBarUI(string text, float value, float maxValue)
+ {
+ var ui = StatusUI;
+
+ if (ui == null)
+ {
+ _queuedHealth = (text, value, maxValue);
+ return;
+ }
+
+ ui.SetHealthBar(text, value, maxValue);
+ }
+
+ private bool SetHealthBarFromQueued()
+ {
+ if (_queuedHealth is null)
+ return false;
+
+ var ui = StatusUI;
+ if (ui is null)
+ return false;
+
+ var (text, value, maxValue) = _queuedHealth.Value;
+ ui.SetHealthBar(text, value, maxValue);
+ _queuedHealth = null;
+
+ return true;
+ }
+
+ private void SetHealthBar(float hp, float maxHp)
+ {
+ SetHealthBarUI($"\u2665 {hp}", hp, maxHp);
+ }
+
+ private void UpdateTimer(TimeSpan ts)
+ {
+ var ui = StatusUI;
+
+ if (ui == null)
+ return;
+
+ if (ts < TimeSpan.Zero)
+ {
+ ts = TimeSpan.Zero;
+ }
+
+ // nice job c#, TimeSpan.ToString doesn't support having no leading zeros
+ ui.TimerText.Text = $"{ts.Minutes}:{ts.Seconds:00}";
+ }
+
+ public void UpdateTimerEnd(SuspicionRuleTimerUpdate ev, EntitySessionEventArgs args)
+ {
+ _lastEndTime = ev.EndTime;
+ }
+
+ public void PreroundStarted(SuspicionRulePreroundStarted ev, EntitySessionEventArgs args)
+ {
+ _lastEndTime = ev.PreroundEndTime;
+ SetRoleToPreround();
+ }
+
+ private void SetRoleUI(string role, Color color)
+ {
+ var ui = StatusUI;
+
+ if (ui is null)
+ {
+ _queuedRole = (role, color);
+ return;
+ }
+
+ ui.SetRole(role, color);
+ }
+
+ private bool SetRoleFromQueued()
+ {
+ if (_queuedRole is null)
+ return false;
+
+ var ui = StatusUI;
+ if (ui is null)
+ return false;
+
+ var (role, color) = _queuedRole.Value;
+ ui.SetRole(role, color);
+ _queuedRole = null;
+
+ return true;
+ }
+
+ private void SetRoleToPreround()
+ {
+ SetRoleUI(Loc.GetString("suspicion-status-ui-role-preround"), Color.Gray);
+ }
+
+ private void SetRoleToObserbing()
+ {
+ SetRoleUI(Loc.GetString("suspicion-status-ui-role-obserbing"), Color.Gray);
+ }
+
+ public void UpdateRoleDisplay(SuspicionRuleUpdateRole ev, EntitySessionEventArgs args)
+ {
+ var roleName = Loc.GetString(ev.NewRole switch
+ {
+ SuspicionRole.Traitor => "roles-antag-suspicion-traitor-name",
+ SuspicionRole.Detective => "roles-antag-suspicion-detective-name",
+ SuspicionRole.Innocent => "roles-antag-suspicion-innocent-name",
+ _ => "roles-antag-suspicion-unknown",
+ });
+ SetRoleUI(roleName, Color.FromName(ev.NewRole.GetRoleColor()));
+ }
+
+ public void UpdatePlayerSpawn(SuspicionRulePlayerSpawn ev, EntitySessionEventArgs args)
+ {
+ if (ev.GameState == SuspicionGameState.Preparing)
+ {
+ SetRoleToPreround();
+
+ if (EntityManager.TryGetComponent(_playerManager.LocalEntity!.Value, out var damagable))
+ UpdateHealth((_playerManager.LocalEntity!.Value, damagable));
+ }
+ else
+ {
+ SetRoleToObserbing();
+ SetHealthBar(0, 100);
+ }
+
+ _lastEndTime = ev.EndTime;
+ }
+
+ public void OnSystemLoaded(SSSStatusUISystem system)
+ {
+ system.OnPlayerDamageChanged += UpdateHealth;
+ }
+
+ public void OnSystemUnloaded(SSSStatusUISystem system)
+ {
+ system.OnPlayerDamageChanged -= UpdateHealth;
+ }
+
+ public void OnStateEntered(GameplayState state)
+ {
+ if (EntityManager.TryGetComponent(_playerManager.LocalEntity!.Value, out var damagable))
+ UpdateHealth((_playerManager.LocalEntity!.Value, damagable));
+ else
+ SetHealthBar(0, 100);
+
+ UpdateTimer(TimeSpan.Zero);
+
+ SetRoleUI("-", Color.Black);
+ }
+
+ public void OnStateExited(GameplayState state)
+ {
+ SetHealthBarUI("-", 0, 100);
+
+ UpdateTimer(TimeSpan.Zero);
+
+ SetRoleUI("-", Color.Black);
+ }
+}
diff --git a/Content.Client/_SSS/UserInterface/SSSStatusUISystem.cs b/Content.Client/_SSS/UserInterface/SSSStatusUISystem.cs
new file mode 100644
index 0000000000..c54e94b7af
--- /dev/null
+++ b/Content.Client/_SSS/UserInterface/SSSStatusUISystem.cs
@@ -0,0 +1,28 @@
+using Content.Shared.Damage;
+using Robust.Shared.Player;
+
+namespace Content.Client._SSS.UserInterface;
+
+public sealed partial class SSSStatusUISystem : EntitySystem
+{
+ [Dependency] private readonly ISharedPlayerManager _playerManager = default!;
+
+ public event Action>? OnPlayerDamageChanged;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnDamageChanged);
+ }
+
+ private void OnDamageChanged(Entity ent, ref DamageChangedEvent args)
+ {
+ var (uid, damagable) = ent;
+
+ if (uid == _playerManager.LocalEntity)
+ {
+ OnPlayerDamageChanged?.Invoke(ent);
+ }
+ }
+}
diff --git a/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml b/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml
new file mode 100644
index 0000000000..e7e308d32c
--- /dev/null
+++ b/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml.cs b/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml.cs
new file mode 100644
index 0000000000..e66a8f525e
--- /dev/null
+++ b/Content.Client/_SSS/UserInterface/Widgets/SSSStatusGui.xaml.cs
@@ -0,0 +1,41 @@
+using Content.Client.Resources;
+using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client._SSS.UserInterface.Widgets;
+
+[GenerateTypedNameReferences]
+public sealed partial class SSSStatusGui : UIWidget
+{
+ private readonly StyleBoxTexture _roleStyleBox;
+
+ public SSSStatusGui()
+ {
+ RobustXamlLoader.Load(this);
+
+ var resourceCache = IoCManager.Resolve();
+
+ var styleBox = new StyleBoxTexture()
+ {
+ Texture = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"),
+ };
+ styleBox.SetPatchMargin(StyleBox.Margin.All, 10);
+ _roleStyleBox = styleBox;
+ }
+
+ public void SetHealthBar(string text, float value, float maxValue)
+ {
+ HealthNumber.Text = text;
+ HealthBar.MaxValue = maxValue;
+ HealthBar.Value = value;
+ }
+
+ public void SetRole(string role, Color color)
+ {
+ RoleBG.PanelOverride = new StyleBoxTexture(_roleStyleBox) { Modulate = color };
+ RoleText.Text = role;
+ }
+}
diff --git a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Rules.cs b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Rules.cs
index 61796df987..d8ab4a99ed 100644
--- a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Rules.cs
+++ b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Rules.cs
@@ -1,8 +1,10 @@
using System.Linq;
using Content.Server._SSS.GridMarker;
-using Content.Server._SSS.SuspicionGameRule.Components;
using Content.Server.Communications;
using Content.Server.Ghost;
+using Content.Shared._SSS;
+using Content.Shared._SSS.SuspicionGameRule;
+using Content.Shared._SSS.SuspicionGameRule.Components;
using Content.Shared.Chat;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
@@ -11,13 +13,10 @@
using Content.Shared.GameTicking.Components;
using Content.Shared.Ghost;
using Content.Shared.Hands.Components;
-using Content.Shared.Implants.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Overlays;
using Content.Shared.Popups;
-using Content.Shared.Store.Components;
-using Robust.Shared.Network;
namespace Content.Server._SSS.SuspicionGameRule;
@@ -64,6 +63,8 @@ private void OnMobStateChanged(EntityUid uid, SuspicionPlayerComponent component
sus.EndAt += TimeSpan.FromSeconds(sus.TimeAddedPerKill);
sus.AnnouncedTimeLeft.Clear();
+ RaiseNetworkEvent(new SuspicionRuleTimerUpdate(_gameTicker.RoundDuration() + sus.EndAt));
+
var allTraitors = FindAllOfType(SuspicionRole.Traitor);
// Ok this is fucking horrible
foreach (var traitor in allTraitors)
diff --git a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Spawning.cs b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Spawning.cs
index 1b7b3952f4..e119dd524a 100644
--- a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Spawning.cs
+++ b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Spawning.cs
@@ -1,5 +1,4 @@
using System.Linq;
-using Content.Server._SSS.SuspicionGameRule.Components;
using Content.Server.Administration.Commands;
using Content.Server.Atmos.Components;
using Content.Server.GameTicking;
@@ -9,6 +8,9 @@
using Content.Server.Roles;
using Content.Server.Temperature.Components;
using Content.Server.Traits.Assorted;
+using Content.Shared._SSS;
+using Content.Shared._SSS.SuspicionGameRule;
+using Content.Shared._SSS.SuspicionGameRule.Components;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Atmos.Rotting;
@@ -122,6 +124,8 @@ private void StartRound(EntityUid uid, SuspicionRuleComponent component, GameRul
Loc.GetString("traitor-briefing"),
Color.Red,
_traitorStartSound);
+
+ RaiseNetworkEvent(new SuspicionRuleUpdateRole(SuspicionRole.Traitor), ownedEntity.Value);
}
for (var i = traitorCount; i < traitorCount + detectiveCount; i++)
@@ -146,6 +150,7 @@ private void StartRound(EntityUid uid, SuspicionRuleComponent component, GameRul
Loc.GetString("detective-briefing"),
Color.Blue,
briefingSound:null);
+ RaiseNetworkEvent(new SuspicionRuleUpdateRole(SuspicionRole.Detective), ownedEntity.Value);
}
// Anyone who isn't a traitor will get the innocent role.
@@ -164,9 +169,14 @@ private void StartRound(EntityUid uid, SuspicionRuleComponent component, GameRul
Loc.GetString("innocent-briefing"),
briefingColor: Color.Green,
briefingSound:null);
+
+ RaiseNetworkEvent(new SuspicionRuleUpdateRole(SuspicionRole.Innocent), ownedEntity.Value);
}
_chatManager.DispatchServerAnnouncement($"The round has started. There are {traitorCount} traitors among us.");
+
+ // SIMYON WHY
+ RaiseNetworkEvent(new SuspicionRuleTimerUpdate(_gameTicker.RoundDuration() + component.EndAt));
}
private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)
@@ -176,6 +186,7 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)
.Select(p => new ProtoId(p.ID))
.ToArray();
+
var query = EntityQueryEnumerator();
while (query.MoveNext(out var uid, out var sus, out var gameRule))
{
@@ -188,9 +199,31 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)
_chatManager.DispatchServerMessage(ev.Player, "Sorry, the game has already started. You have been made an observer.");
GameTicker.SpawnObserver(ev.Player); // Players can't join mid-round.
ev.Handled = true;
+ if (sus.GameState == SuspicionGameState.InProgress)
+ {
+ RaiseNetworkEvent(new SuspicionRulePlayerSpawn()
+ {
+ GameState = SuspicionGameState.InProgress,
+ EndTime = _gameTicker.RoundDuration() + sus.EndAt,
+ }, ev.Player);
+ }
+ else if (sus.GameState == SuspicionGameState.PostRound)
+ {
+ RaiseNetworkEvent(new SuspicionRulePlayerSpawn()
+ {
+ GameState = SuspicionGameState.PostRound,
+ EndTime = TimeSpan.MinValue,
+ }, ev.Player);
+ }
return;
}
+ RaiseNetworkEvent(new SuspicionRulePlayerSpawn()
+ {
+ GameState = SuspicionGameState.Preparing,
+ EndTime = TimeSpan.FromSeconds(sus.PreparingDuration),
+ }, ev.Player);
+
var newMind = _mindSystem.CreateMind(ev.Player.UserId, ev.Profile.Name);
_mindSystem.SetUserId(newMind, ev.Player.UserId);
diff --git a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Utility.cs b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Utility.cs
index c95fea4ee7..5f56d83774 100644
--- a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Utility.cs
+++ b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.Utility.cs
@@ -1,4 +1,4 @@
-using Content.Server._SSS.SuspicionGameRule.Components;
+using Content.Shared._SSS.SuspicionGameRule.Components;
using Content.Shared.Chat;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
@@ -7,7 +7,6 @@
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Content.Shared.Store.Components;
-using Robust.Server.Containers;
using Robust.Shared.Map;
using Robust.Shared.Physics.Components;
diff --git a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.cs b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.cs
index 1fdaa5fd73..cd13d4f57e 100644
--- a/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.cs
+++ b/Content.Server/_SSS/SuspicionGameRule/SuspicionRuleSystem.cs
@@ -1,5 +1,4 @@
using System.Linq;
-using Content.Server._SSS.SuspicionGameRule.Components;
using Content.Server.Access.Systems;
using Content.Server.Administration.Systems;
using Content.Server.Antag;
@@ -17,6 +16,9 @@
using Content.Server.RoundEnd;
using Content.Server.Station.Systems;
using Content.Server.Store.Systems;
+using Content.Shared._SSS;
+using Content.Shared._SSS.SuspicionGameRule;
+using Content.Shared._SSS.SuspicionGameRule.Components;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.GameTicking.Components;
@@ -57,6 +59,7 @@ public sealed partial class SuspicionRuleSystem : GameRuleSystem _chatManager.DispatchServerAnnouncement("The round will start in 5 seconds."));
Timer.Spawn(TimeSpan.FromSeconds(component.PreparingDuration), () => StartRound(uid, component, gameRule));
Log.Debug("Starting a game of Suspicion.");
+
+ RaiseNetworkEvent(new SuspicionRulePreroundStarted(TimeSpan.FromSeconds(component.PreparingDuration)));
}
public override void Update(float frameTime)
diff --git a/Content.Server/_SSS/SuspicionRadarSystem.cs b/Content.Server/_SSS/SuspicionRadarSystem.cs
index 33709b0737..095b08ed7e 100644
--- a/Content.Server/_SSS/SuspicionRadarSystem.cs
+++ b/Content.Server/_SSS/SuspicionRadarSystem.cs
@@ -1,7 +1,8 @@
-using Content.Server._SSS.SuspicionGameRule.Components;
-using Content.Server.Mind;
+using Content.Server.Mind;
using Content.Server.Roles;
+using Content.Shared._SSS;
using Content.Shared._SSS.RadarOverlay;
+using Content.Shared._SSS.SuspicionGameRule.Components;
using Content.Shared.Implants.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
diff --git a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs
similarity index 71%
rename from Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs
rename to Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs
index ab54b20b35..7ea237d4a0 100644
--- a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs
+++ b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRadarComponent.cs
@@ -1,4 +1,4 @@
-namespace Content.Server._SSS.SuspicionGameRule.Components;
+namespace Content.Shared._SSS.SuspicionGameRule.Components;
[RegisterComponent]
public sealed partial class SuspicionRadarComponent : Component
diff --git a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs
similarity index 83%
rename from Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs
rename to Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs
index 39290aa13c..fb6d4ce15a 100644
--- a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs
+++ b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRoleComponent.cs
@@ -1,6 +1,7 @@
using Content.Shared.Roles;
+using Robust.Shared.Serialization;
-namespace Content.Server._SSS.SuspicionGameRule.Components;
+namespace Content.Shared._SSS.SuspicionGameRule.Components;
[RegisterComponent]
public sealed partial class SuspicionRoleComponent : BaseMindRoleComponent
@@ -23,6 +24,7 @@ public static string GetRoleColor(this SuspicionRole role)
}
}
+[Serializable, NetSerializable]
public enum SuspicionRole
{
Pending,
diff --git a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs
similarity index 95%
rename from Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs
rename to Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs
index 4cf80242a1..4b7267213e 100644
--- a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs
+++ b/Content.Shared/_SSS/SuspicionGameRule/Components/SuspicionRuleComponent.cs
@@ -1,13 +1,12 @@
using Content.Shared.NPC.Prototypes;
-using Content.Shared.Radio;
using Content.Shared.Roles;
-using Robust.Shared.Map;
using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server._SSS.SuspicionGameRule.Components;
+namespace Content.Shared._SSS.SuspicionGameRule.Components;
-[RegisterComponent, Access(typeof(SuspicionRuleSystem))]
+[RegisterComponent]
public sealed partial class SuspicionRuleComponent : Component
{
#region State management
diff --git a/Content.Shared/_SSS/SuspicionGameRule/SuspicionRuleEvents.cs b/Content.Shared/_SSS/SuspicionGameRule/SuspicionRuleEvents.cs
new file mode 100644
index 0000000000..dba69bbf74
--- /dev/null
+++ b/Content.Shared/_SSS/SuspicionGameRule/SuspicionRuleEvents.cs
@@ -0,0 +1,30 @@
+using Content.Shared._SSS.SuspicionGameRule.Components;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._SSS.SuspicionGameRule;
+
+
+[Serializable, NetSerializable]
+public sealed class SuspicionRuleTimerUpdate(TimeSpan endTime) : EntityEventArgs
+{
+ public TimeSpan EndTime = endTime;
+}
+
+[Serializable, NetSerializable]
+public sealed class SuspicionRulePreroundStarted(TimeSpan preroundEndTime) : EntityEventArgs
+{
+ public TimeSpan PreroundEndTime = preroundEndTime;
+}
+
+[Serializable, NetSerializable]
+public sealed class SuspicionRuleUpdateRole(SuspicionRole newRole) : EntityEventArgs
+{
+ public readonly SuspicionRole NewRole = newRole;
+}
+
+[Serializable, NetSerializable]
+public sealed class SuspicionRulePlayerSpawn : EntityEventArgs
+{
+ public SuspicionGameState GameState;
+ public TimeSpan EndTime;
+}
diff --git a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionPlayerComponent.cs b/Content.Shared/_SSS/SuspicionPlayerComponent.cs
similarity index 95%
rename from Content.Server/_SSS/SuspicionGameRule/Components/SuspicionPlayerComponent.cs
rename to Content.Shared/_SSS/SuspicionPlayerComponent.cs
index aaf87622b1..c98b262997 100644
--- a/Content.Server/_SSS/SuspicionGameRule/Components/SuspicionPlayerComponent.cs
+++ b/Content.Shared/_SSS/SuspicionPlayerComponent.cs
@@ -1,4 +1,4 @@
-namespace Content.Server._SSS;
+namespace Content.Shared._SSS;
///
/// Main component that marks a player "active" in a round of SSS.
diff --git a/Resources/Locale/en-US/_SSS/suspicion.ftl b/Resources/Locale/en-US/_SSS/suspicion.ftl
index 87fc82ad75..f8337cf629 100644
--- a/Resources/Locale/en-US/_SSS/suspicion.ftl
+++ b/Resources/Locale/en-US/_SSS/suspicion.ftl
@@ -14,3 +14,9 @@ roles-antag-suspicion-pending-name = Pending
roles-antag-suspicion-pending-objective = The game is still starting, wait for your role to be assigned.
roles-antag-suspicion-detective-name = Detective
roles-antag-suspicion-detective-objective = Discover and eliminate all traitors with your special tools.
+
+roles-antag-suspicion-unknown = Unknown
+
+suspicion-status-ui-health-dead = DEAD
+suspicion-status-ui-role-obserbing = Obserbing
+suspicion-status-ui-role-preround = Preround