Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add codeword highlighting #30092

Merged
merged 8 commits into from
Aug 23, 2024
8 changes: 8 additions & 0 deletions Content.Client/Roles/RoleCodewordSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Roles.RoleCodeword;

namespace Content.Client.Roles;

public sealed class RoleCodewordSystem : SharedRoleCodewordSystem
{

}
18 changes: 18 additions & 0 deletions Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
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;
Expand All @@ -20,6 +22,7 @@
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;
Expand Down Expand Up @@ -60,6 +63,8 @@ public sealed class ChatUIController : UIController
[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!;

[ValidatePrototypeId<ColorPalettePrototype>]
private const string ChatNamePalette = "ChatNames";
Expand Down Expand Up @@ -819,6 +824,19 @@ public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
msg.WrappedMessage = SharedChatSystem.InjectTagInsideTag(msg, "Name", "color", GetNameColor(SharedChatSystem.GetStringInsideTag(msg, "Name")));
}

// Color any codewords for minds that have roles that use them
if (_player.LocalUser != null && _mindSystem != null && _roleCodewordSystem != null)
{
if (_mindSystem.TryGetMind(_player.LocalUser.Value, out var mindId) && _ent.TryGetComponent(mindId, out RoleCodewordComponent? codewordComp))
{
foreach (var (_, codewordData) in codewordComp.RoleCodewords)
{
foreach (string codeword in codewordData.Codewords)
msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, codeword, "color", codewordData.Color.ToHex());
}
}
}

// Log all incoming chat to repopulate when filter is un-toggled
if (!msg.HideChat)
{
Expand Down
12 changes: 12 additions & 0 deletions Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
using Content.Shared.NPC.Systems;
using Content.Shared.Objectives.Components;
using Content.Shared.PDA;
using Content.Shared.Radio;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Roles.RoleCodeword;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using System.Linq;
Expand All @@ -21,6 +23,8 @@ namespace Content.Server.GameTicking.Rules;

public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{
private static readonly Color TraitorCodewordColor = Color.FromHex("#cc3b3b");

[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
Expand All @@ -29,6 +33,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly SharedRoleCodewordSystem _roleCodewordSystem = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -96,6 +101,7 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool

_antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code, issuer), null, component.GreetSoundNotification);


component.TraitorMinds.Add(mindId);

// Assign briefing
Expand All @@ -104,6 +110,12 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool
Briefing = briefing
}, mind, true);

// Send codewords to only the traitor client
var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found

RoleCodewordComponent codewordComp = EnsureComp<RoleCodewordComponent>(mindId);
_roleCodewordSystem.SetRoleCodewords(codewordComp, "traitor", component.Codewords.ToList(), color);

// Change the faction
_npcFaction.RemoveFaction(traitor, component.NanoTrasenFaction, false);
_npcFaction.AddFaction(traitor, component.SyndicateFaction);
Expand Down
8 changes: 8 additions & 0 deletions Content.Server/Roles/RoleCodeword/RoleCodewordSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Roles.RoleCodeword;

namespace Content.Server.Roles.RoleCodeword;

public sealed class RoleCodewordSystem : SharedRoleCodewordSystem
{

}
13 changes: 13 additions & 0 deletions Content.Shared/Chat/SharedChatSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Frozen;
using System.Text.RegularExpressions;
using Content.Shared.Popups;
using Content.Shared.Radio;
using Content.Shared.Speech;
Expand Down Expand Up @@ -237,6 +238,18 @@ public static string InjectTagInsideTag(ChatMessage message, string outerTag, st

return rawmsg;
}

/// <summary>
/// Injects a tag around all found instances of a specific string in a ChatMessage.
/// Excludes strings inside other tags and brackets.
/// </summary>
public static string InjectTagAroundString(ChatMessage message, string targetString, string tag, string? tagParameter)
{
var rawmsg = message.WrappedMessage;
rawmsg = Regex.Replace(rawmsg, "(?i)(" + targetString + ")(?-i)(?![^[]*])", $"[{tag}={tagParameter}]$1[/{tag}]");
return rawmsg;
}

public static string GetStringInsideTag(ChatMessage message, string tag)
{
var rawmsg = message.WrappedMessage;
Expand Down
37 changes: 37 additions & 0 deletions Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

namespace Content.Shared.Roles.RoleCodeword;

/// <summary>
/// Used to display and highlight codewords in chat messages on the client.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedRoleCodewordSystem), Other = AccessPermissions.Read)]
public sealed partial class RoleCodewordComponent : Component
{
/// <summary>
/// Contains the codewords tied to a role.
/// Key string should be unique for the role.
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<string, CodewordsData> RoleCodewords = new();

public override bool SessionSpecific => true;
}

[DataDefinition, Serializable, NetSerializable]
public partial struct CodewordsData
{
[DataField]
public Color Color;

[DataField]
public List<string> Codewords;

public CodewordsData(Color color, List<string> codewords)
{
Color = color;
Codewords = codewords;
}
}
49 changes: 49 additions & 0 deletions Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Content.Shared.Mind;
using Robust.Shared.GameStates;
using Robust.Shared.Player;

namespace Content.Shared.Roles.RoleCodeword;

public abstract class SharedRoleCodewordSystem : EntitySystem
{
[Dependency] private readonly SharedMindSystem _mindSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RoleCodewordComponent, ComponentGetStateAttemptEvent>(OnCodewordCompGetStateAttempt);
}

/// <summary>
/// Determines if a codeword component should be sent to the client.
/// </summary>
private void OnCodewordCompGetStateAttempt(EntityUid uid, RoleCodewordComponent comp, ref ComponentGetStateAttemptEvent args)
{
args.Cancelled = !CanGetState(args.Player, comp);
}

/// <summary>
/// The criteria that determine whether a codeword component should be sent to a client.
/// Sends the component if its owner is the player mind.
/// </summary>
/// <param name="player"> The Player the component will be sent to.</param>
/// <param name="comp"> The component being checked against</param>
/// <returns></returns>
private bool CanGetState(ICommonSession? player, RoleCodewordComponent comp)
{
if (!_mindSystem.TryGetMind(player, out EntityUid mindId, out var _))
return false;

if (!TryComp(mindId, out RoleCodewordComponent? playerComp) && comp != playerComp)
return false;

return true;
}

public void SetRoleCodewords(RoleCodewordComponent comp, string key, List<string> codewords, Color color)
{
var data = new CodewordsData(color, codewords);
comp.RoleCodewords[key] = data;
}
}
1 change: 0 additions & 1 deletion Resources/Prototypes/Datasets/adjectives.yml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@
- slow
- swift
- young
- Taste/Touch
- bitter
- delicious
- fresh
Expand Down
3 changes: 1 addition & 2 deletions Resources/Prototypes/Datasets/verbs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- type: dataset
- type: dataset
id: verbs
values:
- accept
Expand Down Expand Up @@ -616,7 +616,6 @@
- whine
- whip
- whirl
- whisper
- whistle
- wink
- wipe
Expand Down
Loading