diff --git a/Content.Client/Roles/RoleCodewordSystem.cs b/Content.Client/Roles/RoleCodewordSystem.cs deleted file mode 100644 index eea5319c03..0000000000 --- a/Content.Client/Roles/RoleCodewordSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Roles.RoleCodeword; - -namespace Content.Client.Roles; - -public sealed partial class RoleCodewordSystem : SharedRoleCodewordSystem; diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index 2140879aa9..97a55748d2 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -10,19 +10,17 @@ using Content.Client.Examine; using Content.Client.Gameplay; using Content.Client.Ghost; using Content.Client.Mind; -using Content.Client.Roles; -using Content.Client.Stylesheets; using Content.Client.UserInterface.Screens; using Content.Client.UserInterface.Systems.Chat.Widgets; using Content.Client.UserInterface.Systems.Gameplay; using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Chat; +using Content.Shared.Codewords; using Content.Shared.Damage.ForceSay; using Content.Shared.Decals; using Content.Shared.Input; using Content.Shared.Radio; -using Content.Shared.Roles.RoleCodeword; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; @@ -59,13 +57,12 @@ public sealed partial class ChatUIController : UIController [Dependency] private IGameTiming _timing = default!; [Dependency] private IReplayRecordingManager _replayRecording = default!; - [UISystemDependency] private readonly ExamineSystem? _examine = default; - [UISystemDependency] private readonly GhostSystem? _ghost = default; - [UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default; - [UISystemDependency] private readonly ChatSystem? _chatSys = default; - [UISystemDependency] private readonly TransformSystem? _transform = default; - [UISystemDependency] private readonly MindSystem? _mindSystem = default!; - [UISystemDependency] private readonly RoleCodewordSystem? _roleCodewordSystem = default!; + [UISystemDependency] private readonly ChatSystem _chatSys = default!; + [UISystemDependency] private readonly CodewordSystem _codeword = default!; + [UISystemDependency] private readonly ExamineSystem _examine = default!; + [UISystemDependency] private readonly GhostSystem _ghost = default!; + [UISystemDependency] private readonly TransformSystem _transform = default!; + [UISystemDependency] private readonly TypingIndicatorSystem _typingIndicator = default!; private SharedChatSystem? _sharedChatSys; private static readonly ProtoId ChatNamePalette = "ChatNames"; @@ -837,15 +834,11 @@ public sealed partial class ChatUIController : UIController } // Color any codewords for minds that have roles that use them - if (_player.LocalUser != null && _mindSystem != null && _roleCodewordSystem != null) + foreach (var data in _codeword.GetPlayerCodewords(_player.LocalUser)) { - if (_mindSystem.TryGetMind(_player.LocalUser.Value, out var mindId) && _ent.TryGetComponent(mindId, out RoleCodewordComponent? codewordComp)) + foreach (var codeword in data.Codewords) { - foreach (var (_, codewordData) in codewordComp.RoleCodewords) - { - foreach (string codeword in codewordData.Codewords) - msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, codeword, "color", codewordData.Color.ToHex()); - } + msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, codeword, "color", data.Color.ToHex()); } } diff --git a/Content.Server/Roles/RoleCodeword/RoleCodewordSystem.cs b/Content.Server/Roles/RoleCodeword/RoleCodewordSystem.cs deleted file mode 100644 index 3525e459bb..0000000000 --- a/Content.Server/Roles/RoleCodeword/RoleCodewordSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Roles.RoleCodeword; - -namespace Content.Server.Roles.RoleCodeword; - -public sealed partial class RoleCodewordSystem : SharedRoleCodewordSystem; diff --git a/Content.Shared/Codewords/CodewordSystem.cs b/Content.Shared/Codewords/CodewordSystem.cs index 5cdfa17963..d64d9090d0 100644 --- a/Content.Shared/Codewords/CodewordSystem.cs +++ b/Content.Shared/Codewords/CodewordSystem.cs @@ -1,20 +1,27 @@ -using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.GameTicking.Events; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using JetBrains.Annotations; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using static System.Linq.Enumerable; namespace Content.Shared.Codewords; /// -/// Gamerule that provides codewords for other gamerules that rely on them. +/// System that generates and handles the assignment of codewords. /// public sealed partial class CodewordSystem : EntitySystem { [Dependency] private ISharedAdminLogManager _adminLogger = default!; [Dependency] private IRobustRandom _random = default!; + [Dependency] private SharedMindSystem _mind = default!; + + [Dependency] private EntityQuery _codewordQuery; [SubscribeLocalEvent] private void OnRoundStart(RoundStartingEvent ev) @@ -80,4 +87,81 @@ public sealed partial class CodewordSystem : EntitySystem } return codewords; } + + /// + /// Returns all codewords a specific player knows + /// + /// Player account + /// All codewords known by this entity. + [PublicAPI] + public List GetPlayerCodewords(NetUserId? player) + { + return player != null && _mind.TryGetMind(player.Value, out var mind) + ? GetMindCodewords(mind.Value.AsNullable()) + : new List(); + } + + /// + /// Returns all codewords stored in a mind. + /// + /// Entity + /// All codewords known by this entity. + [PublicAPI] + public List GetEntityCodewords(Entity entity) + { + return _mind.TryGetMind(entity, out var uid, out var mind) + ? GetMindCodewords((uid, mind)) + : new List(); + } + + /// + /// Returns all codewords stored in a mind. + /// + /// Mind entity + /// All codewords known by this mind. + [PublicAPI] + public List GetMindCodewords(Entity mind) + { + var data = new List(); + if (!Resolve(mind, ref mind.Comp)) + return data; + + foreach (var role in mind.Comp.MindRoleContainer.ContainedEntities) + { + if (GetRoleCodewords(role) is not { } codewords) + continue; + + data.Add(codewords); + } + + return data; + } + + /// + /// Returns codewords associated with this role + /// + /// Role entity + /// Codewords for this role, if they exist. + [PublicAPI] + public CodewordsData? GetRoleCodewords(Entity role) + { + if (!_codewordQuery.Resolve(role, ref role.Comp, false)) + return null; + + return role.Comp.RoleCodewords; + } + + /// + /// Sets the codewords for a given mindrole entity. + /// + /// Mindrole entity. + /// Codewords + /// Color the codewords show as + [PublicAPI] + public void SetRoleCodewords(Entity ent, List codewords, Color color) + { + var data = new CodewordsData(color, codewords); + ent.Comp.RoleCodewords = data; + Dirty(ent); + } } diff --git a/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs b/Content.Shared/Codewords/RoleCodewordComponent.cs similarity index 73% rename from Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs rename to Content.Shared/Codewords/RoleCodewordComponent.cs index 222aec9bf7..ba23fb5005 100644 --- a/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs +++ b/Content.Shared/Codewords/RoleCodewordComponent.cs @@ -1,12 +1,12 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; -namespace Content.Shared.Roles.RoleCodeword; +namespace Content.Shared.Codewords; /// /// Used to display and highlight codewords in chat messages on the client. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedRoleCodewordSystem), Other = AccessPermissions.Read)] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CodewordSystem), Other = AccessPermissions.Read)] public sealed partial class RoleCodewordComponent : Component { /// @@ -14,15 +14,21 @@ public sealed partial class RoleCodewordComponent : Component /// Key string should be unique for the role. /// [DataField, AutoNetworkedField] - public Dictionary RoleCodewords = new(); + public CodewordsData RoleCodewords; } [DataDefinition, Serializable, NetSerializable] public partial struct CodewordsData { + /// + /// The Color these codewords appear as + /// [DataField] public Color Color; + /// + /// The Codewords!!! + /// [DataField] public List Codewords; diff --git a/Content.Shared/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Shared/GameTicking/Rules/TraitorRuleSystem.cs index 4b0485cb8d..b0e40aab57 100644 --- a/Content.Shared/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Shared/GameTicking/Rules/TraitorRuleSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.Random.Helpers; using Content.Shared.Roles; using Content.Shared.Roles.Components; using Content.Shared.Roles.Jobs; -using Content.Shared.Roles.RoleCodeword; using Robust.Shared.Random; namespace Content.Shared.GameTicking.Rules; @@ -24,7 +23,6 @@ public abstract partial class TraitorRuleSystem : GameRuleSystem((mindId, mind), out var traitorRole); - if (traitorRole is not null) + if (_roleSystem.MindHasRole((mindId, mind), out var traitorRole)) { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Add traitor briefing components"); EnsureComp(traitorRole.Value.Owner, out var briefingComp); briefingComp.Briefing = briefing; + + // The mind entity is stored in nullspace with a PVS override for the owner, so only they can see the codewords. + var codewordComp = EnsureComp(traitorRole.Value); + Codeword.SetRoleCodewords((traitorRole.Value, codewordComp), factionCodewords.ToList(), TraitorCodewordColor); } else { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - did not get traitor briefing"); } - var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found - - // The mind entity is stored in nullspace with a PVS override for the owner, so only they can see the codewords. - var codewordComp = EnsureComp(mindId); - _roleCodewordSystem.SetRoleCodewords((mindId, codewordComp), "traitor", factionCodewords.ToList(), color); - // Change the faction Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Change faction"); _npcFaction.RemoveFaction(traitor, rule.Comp.NanoTrasenFaction, false); diff --git a/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs b/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs deleted file mode 100644 index 345cfd4e21..0000000000 --- a/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Shared.Roles.RoleCodeword; - -public abstract class SharedRoleCodewordSystem : EntitySystem -{ - public void SetRoleCodewords(Entity ent, string key, List codewords, Color color) - { - var data = new CodewordsData(color, codewords); - ent.Comp.RoleCodewords[key] = data; - Dirty(ent); - } -}