Make codewords mind role based (#46167)

* make codewords mind role based

* whoop
This commit is contained in:
Princess Cheeseballs 2026-09-30 06:53:09 +00:00 • committed by GitHub
parent f61de36a2e
commit 09b80ab993
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 110 additions and 53 deletions

View file

@ -1,5 +0,0 @@
using Content.Shared.Roles.RoleCodeword;
namespace Content.Client.Roles;
public sealed partial class RoleCodewordSystem : SharedRoleCodewordSystem;

View file

@ -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<ColorPalettePrototype> 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());
}
}

View file

@ -1,5 +0,0 @@
using Content.Shared.Roles.RoleCodeword;
namespace Content.Server.Roles.RoleCodeword;
public sealed partial class RoleCodewordSystem : SharedRoleCodewordSystem;

View file

@ -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;
/// <summary>
/// Gamerule that provides codewords for other gamerules that rely on them.
/// System that generates and handles the assignment of codewords.
/// </summary>
public sealed partial class CodewordSystem : EntitySystem
{
[Dependency] private ISharedAdminLogManager _adminLogger = default!;
[Dependency] private IRobustRandom _random = default!;
[Dependency] private SharedMindSystem _mind = default!;
[Dependency] private EntityQuery<RoleCodewordComponent> _codewordQuery;
[SubscribeLocalEvent]
private void OnRoundStart(RoundStartingEvent ev)
@ -80,4 +87,81 @@ public sealed partial class CodewordSystem : EntitySystem
}
return codewords;
}
/// <summary>
/// Returns all codewords a specific player knows
/// </summary>
/// <param name="player">Player account</param>
/// <returns>All codewords known by this entity.</returns>
[PublicAPI]
public List<CodewordsData> GetPlayerCodewords(NetUserId? player)
{
return player != null && _mind.TryGetMind(player.Value, out var mind)
? GetMindCodewords(mind.Value.AsNullable())
: new List<CodewordsData>();
}
/// <summary>
/// Returns all codewords stored in a mind.
/// </summary>
/// <param name="entity">Entity</param>
/// <returns>All codewords known by this entity.</returns>
[PublicAPI]
public List<CodewordsData> GetEntityCodewords(Entity<MindContainerComponent?> entity)
{
return _mind.TryGetMind(entity, out var uid, out var mind)
? GetMindCodewords((uid, mind))
: new List<CodewordsData>();
}
/// <summary>
/// Returns all codewords stored in a mind.
/// </summary>
/// <param name="mind">Mind entity</param>
/// <returns>All codewords known by this mind.</returns>
[PublicAPI]
public List<CodewordsData> GetMindCodewords(Entity<MindComponent?> mind)
{
var data = new List<CodewordsData>();
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;
}
/// <summary>
/// Returns codewords associated with this role
/// </summary>
/// <param name="role">Role entity</param>
/// <returns>Codewords for this role, if they exist.</returns>
[PublicAPI]
public CodewordsData? GetRoleCodewords(Entity<RoleCodewordComponent?> role)
{
if (!_codewordQuery.Resolve(role, ref role.Comp, false))
return null;
return role.Comp.RoleCodewords;
}
/// <summary>
/// Sets the codewords for a given mindrole entity.
/// </summary>
/// <param name="ent">Mindrole entity.</param>
/// <param name="codewords">Codewords</param>
/// <param name="color">Color the codewords show as</param>
[PublicAPI]
public void SetRoleCodewords(Entity<RoleCodewordComponent> ent, List<string> codewords, Color color)
{
var data = new CodewordsData(color, codewords);
ent.Comp.RoleCodewords = data;
Dirty(ent);
}
}

View file

@ -1,12 +1,12 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Roles.RoleCodeword;
namespace Content.Shared.Codewords;
/// <summary>
/// Used to display and highlight codewords in chat messages on the client.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedRoleCodewordSystem), Other = AccessPermissions.Read)]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CodewordSystem), Other = AccessPermissions.Read)]
public sealed partial class RoleCodewordComponent : Component
{
/// <summary>
@ -14,15 +14,21 @@ public sealed partial class RoleCodewordComponent : Component
/// Key string should be unique for the role.
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<string, CodewordsData> RoleCodewords = new();
public CodewordsData RoleCodewords;
}
[DataDefinition, Serializable, NetSerializable]
public partial struct CodewordsData
{
/// <summary>
/// The Color these codewords appear as
/// </summary>
[DataField]
public Color Color;
/// <summary>
/// The Codewords!!!
/// </summary>
[DataField]
public List<string> Codewords;

View file

@ -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<TraitorRuleComp
[Dependency] private NpcFactionSystem _npcFaction = default!;
[Dependency] private SharedJobSystem _jobs = default!;
[Dependency] private SharedMindSystem _mindSystem = default!;
[Dependency] private SharedRoleCodewordSystem _roleCodewordSystem = default!;
[Dependency] private SharedRoleSystem _roleSystem = default!;
private static readonly Color TraitorCodewordColor = Color.FromHex("#cc3b3b");
@ -102,24 +100,21 @@ public abstract partial class TraitorRuleSystem : GameRuleSystem<TraitorRuleComp
//Since this provides neither an antag/job prototype, nor antag status/roletype,
//and is intrinsically related to the traitor role
//it does not need to be a separate Mind Role Entity
_roleSystem.MindHasRole<TraitorRoleComponent>((mindId, mind), out var traitorRole);
if (traitorRole is not null)
if (_roleSystem.MindHasRole<TraitorRoleComponent>((mindId, mind), out var traitorRole))
{
Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Add traitor briefing components");
EnsureComp<RoleBriefingComponent>(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<RoleCodewordComponent>(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<RoleCodewordComponent>(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);

View file

@ -1,11 +0,0 @@
namespace Content.Shared.Roles.RoleCodeword;
public abstract class SharedRoleCodewordSystem : EntitySystem
{
public void SetRoleCodewords(Entity<RoleCodewordComponent> ent, string key, List<string> codewords, Color color)
{
var data = new CodewordsData(color, codewords);
ent.Comp.RoleCodewords[key] = data;
Dirty(ent);
}
}