Message highlights (#2990)

* Merge pull request impstation/imp-station-14#468 from hivehum/highlight

Add text highlighting

(cherry picked from commit e442cc4bb20fbb4d5b970275c82cc87508e6f687)

* More cleanly separate auto-generated and manual highlights

Edge case that's not handled: Closing and reopening the game drops the list of auto-generated highlights

* Use LocalizedDatasets to make higlight lists for all jobs

* DeltaV-ification

Since the implementation is now fundamentally incompatible
with the (still open) upstream PR, move to our own cvars

* Try to untroll code

* Untroll

* Untroll for real this time

* Fix wrong count in ChatHighlightBorg

* Escape highlights

Depends on space-wizards/RobustToolbox/pull/5688

* add missing using directive

* Fix failing test

* Rename CVars to avoid clash with previous implementation

* Tidy some stuff up

* Show auto highlights

* Remove redundant check

---------

Co-authored-by: V <97265903+formlessnameless@users.noreply.github.com>
This commit is contained in:
Tobias Berger 2025-04-04 18:18:19 +02:00 committed by GitHub
parent f1195c7050
commit 788d43ad99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1057 additions and 16 deletions

View File

@ -31,9 +31,11 @@ public sealed class CharacterInfoSystem : EntitySystem
private void OnCharacterInfoEvent(CharacterInfoEvent msg, EntitySessionEventArgs args)
{
var entity = GetEntity(msg.NetEntity);
var data = new CharacterData(entity, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity));
// DeltaV - Refactored to use TryGetEntity instead of GetEntity because of message highlighting which requests character info too soon and I don't know how to delay it properly
if (!TryGetEntity(msg.NetEntity, out var entity))
return;
var data = new CharacterData(entity.Value, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity.Value));
OnCharacterUpdate?.Invoke(data);
}

View File

@ -44,7 +44,8 @@ using Content.Client.Nyanotrasen.Chat; //Nyano - Summary: chat namespace.
namespace Content.Client.UserInterface.Systems.Chat;
public sealed class ChatUIController : UIController
// DeltaV - Make partial to implement message highlighting
public sealed partial class ChatUIController : UIController
{
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IChatManager _manager = default!;
@ -245,6 +246,7 @@ public sealed class ChatUIController : UIController
_config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged);
InitializeChatHighlights(); // DeltaV - Message highlighting
}
public void OnScreenLoad()
@ -839,6 +841,14 @@ public sealed class ChatUIController : UIController
msg.WrappedMessage = SharedChatSystem.InjectTagInsideTag(msg, "Name", "color", GetNameColor(SharedChatSystem.GetStringInsideTag(msg, "Name")));
}
// DeltaV - Message highlighting start
// Color any words choosen by the client.
foreach (var highlight in _highlights)
{
msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, highlight, "color", _highlightsColor);
}
// DeltaV - Message highlighting end
// Color any codewords for minds that have roles that use them
if (_player.LocalUser != null && _mindSystem != null && _roleCodewordSystem != null)
{

View File

@ -1,10 +1,34 @@
<controls:ChannelFilterPopup
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls">
<PanelContainer Name="FilterPopupPanel" StyleClasses="BorderedWindowPanel">
<BoxContainer Orientation="Horizontal">
<Control MinSize="4 0"/>
<BoxContainer Name="FilterVBox" MinWidth="110" Margin="0 10" Orientation="Vertical" SeparationOverride="4"/>
<!-- DeltaV - Additional outer BoxContainer for changed layout including message highlighting -->
<BoxContainer Orientation="Horizontal" SeparationOverride="8" Margin="10 0">
<BoxContainer Orientation="Vertical" Margin="0 10">
<Label Text="{Loc 'hud-chatbox-select-channels'}"/>
<BoxContainer Name="FilterVBox" MinWidth="105" Orientation="Vertical" SeparationOverride="4"/>
</BoxContainer>
<!-- DeltaV - Message highlighting start -->
<BoxContainer Name="HighlightsVBox" MinWidth="120" Margin="0 10" Orientation="Vertical" SeparationOverride="4">
<Label Text="{Loc 'hud-chatbox-highlights'}"/>
<!-- Custom background for the TextEdit -->
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#323446"/>
</PanelContainer.PanelOverride>
<TextEdit Name="HighlightEdit" MinHeight="150" Margin="5 5"/>
</PanelContainer>
<Label Name="AutoHighlightLabel" Text="{Loc 'hud-chatbox-auto-highlights'}"/>
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#323446"/>
</PanelContainer.PanelOverride>
<TextEdit Name="AutoHighlightEdit" MinHeight="150" Margin="5 5" Editable="false" />
</PanelContainer>
<Button Name="HighlightButton" Text="{Loc 'hud-chatbox-highlights-button'}" ToolTip="{Loc 'hud-chatbox-highlights-tooltip'}"/>
</BoxContainer>
<!-- DeltaV - Message highlighting end -->
</BoxContainer>
</PanelContainer>
</controls:ChannelFilterPopup>

View File

@ -34,6 +34,7 @@ public sealed partial class ChannelFilterPopup : Popup
public ChannelFilterPopup()
{
RobustXamlLoader.Load(this);
InitializeChatHighlights(); // DeltaV - Message highlighting
}
public bool IsActive(ChatChannel channel)

View File

@ -36,8 +36,9 @@ public partial class ChatBox : UIWidget
ChatInput.Input.OnTextChanged += OnTextChanged;
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
ChatInput.FilterButton.Popup.OnChannelFilter += OnChannelFilter;
ChatInput.FilterButton.Popup.OnNewHighlights += OnNewHighlights; // DeltaV - Message highlighting
_controller = UserInterfaceManager.GetUIController<ChatUIController>();
_controller.OnAutoHighlightsUpdated += ChatInput.FilterButton.Popup.SetAutoHighlights; // DeltaV - Message highlighting
_controller.MessageAdded += OnMessageAdded;
_controller.RegisterChat(this);
}

View File

@ -0,0 +1,7 @@
<Control xmlns="https://spacestation14.io">
<BoxContainer Orientation="Vertical">
<Label Name="TitleLabel" Access="Public" />
<Label Name="ExampleLabel" Access="Public" />
<ColorSelectorSliders Name="Slider" Access="Public" HorizontalExpand="True" />
</BoxContainer>
</Control>

View File

@ -0,0 +1,30 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
namespace Content.Client._DV.Options.UI;
/// <summary>
/// Standard UI control used for color sliders in the options menu. Intended for use with <see cref="OptionsTabControlRow"/>.
/// </summary>
/// <seealso cref="OptionsTabControlRow.AddOptionColorSlider"/>
[GenerateTypedNameReferences]
public sealed partial class OptionColorSlider : Control
{
/// <summary>
/// The text describing what this slider affects.
/// </summary>
public string? Title
{
get => TitleLabel.Text;
set => TitleLabel.Text = value;
}
/// <summary>
/// The example text showing the current color of the slider.
/// </summary>
public string? Example
{
get => ExampleLabel.Text;
set => ExampleLabel.Text = value;
}
}

View File

@ -0,0 +1,20 @@
using Content.Client._DV.Options.UI;
using Robust.Shared.Configuration;
namespace Content.Client.Options.UI;
public sealed partial class OptionsTabControlRow
{
/// <summary>
/// Add a color slider option, backed by a simple string CVar.
/// </summary>
/// <param name="cVar">The CVar represented by the slider.</param>
/// <param name="slider">The UI control for the option.</param>
/// <returns>The option instance backing the added option.</returns>
public OptionColorSliderCVar AddOptionColorSlider(
CVarDef<string> cVar,
OptionColorSlider slider)
{
return AddOption(new OptionColorSliderCVar(this, _cfg, cVar, slider));
}
}

View File

@ -0,0 +1,56 @@
using Content.Client.Options.UI;
using Robust.Shared.Configuration;
namespace Content.Client._DV.Options.UI;
/// <summary>
/// Implementation of a CVar option that simply corresponds with a string <see cref="OptionColorSlider"/>.
/// </summary>
/// <seealso cref="OptionsTabControlRow"/>
public sealed class OptionColorSliderCVar : BaseOptionCVar<string>
{
private readonly OptionColorSlider _slider;
protected override string Value
{
get => _slider.Slider.Color.ToHex();
set
{
_slider.Slider.Color = Color.FromHex(value);
UpdateLabelColor();
}
}
/// <summary>
/// Creates a new instance of this type.
/// </summary>
/// <remarks>
/// <para>
/// It is generally more convenient to call overloads on <see cref="OptionsTabControlRow"/>
/// such as <see cref="OptionsTabControlRow.AddOptionPercentSlider"/> instead of instantiating this type directly.
/// </para>
/// </remarks>
/// <param name="controller">The control row that owns this option.</param>
/// <param name="cfg">The configuration manager to get and set values from.</param>
/// <param name="cVar">The CVar that is being controlled by this option.</param>
/// <param name="slider">The UI control for the option.</param>
public OptionColorSliderCVar(
OptionsTabControlRow controller,
IConfigurationManager cfg,
CVarDef<string> cVar,
OptionColorSlider slider) : base(controller, cfg, cVar)
{
_slider = slider;
slider.Slider.OnColorChanged += _ =>
{
ValueChanged();
UpdateLabelColor();
};
}
private void UpdateLabelColor()
{
_slider.ExampleLabel.FontColorOverride = Color.FromHex(Value);
}
}

View File

@ -3,7 +3,8 @@
xmlns:tabs="clr-namespace:Content.Client._DV.Options.UI.Tabs"
xmlns:xNamespace="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:Content.Client.Stylesheets"
xmlns:ui="clr-namespace:Content.Client.Options.UI">
xmlns:ui="clr-namespace:Content.Client.Options.UI"
xmlns:dvui="clr-namespace:Content.Client._DV.Options.UI">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Vertical" Margin="8 8 8 8" VerticalExpand="True">
<Label Text="{Loc 'ui-options-general-forknotice'}"
@ -13,6 +14,10 @@
FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="DisableFiltersCheckBox" Text="{Loc 'ui-options-no-filters'}" />
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
<dvui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
Example="{Loc 'ui-options-highlights-color-example'}"/>
</BoxContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />
</BoxContainer>

View File

@ -1,22 +1,22 @@
using Content.Shared._DV.CCVars;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
namespace Content.Client._DV.Options.UI.Tabs;
[GenerateTypedNameReferences]
public sealed partial class DeltaTab : Control
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
public DeltaTab()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
Control.AddOptionCheckBox(DCCVars.NoVisionFilters, DisableFiltersCheckBox);
Control.AddOptionCheckBox(DCCVars.ChatAutoFillHighlights, AutoFillHighlightsCheckBox);
Control.AddOptionColorSlider(DCCVars.ChatHighlightsColor, HighlightsColorSlider);
Control.Initialize();
}
}

View File

@ -0,0 +1,118 @@
using System.Linq;
using System.Text.RegularExpressions;
using Content.Client.CharacterInfo;
using Content.Shared._DV.CCVars;
using Content.Shared.Dataset;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using static Content.Client.CharacterInfo.CharacterInfoSystem;
namespace Content.Client.UserInterface.Systems.Chat;
public sealed partial class ChatUIController : IOnSystemChanged<CharacterInfoSystem>
{
/// <summary>
/// Gets Invoked whenever the autofilled highlights have changed.
/// Used to populate the preview in the channel selector window.
/// </summary>
public event Action<string>? OnAutoHighlightsUpdated;
[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
/// <summary>
/// A list of words to be highlighted in the chatbox.
/// User-specified.
/// </summary>
private readonly List<string> _highlights = [];
/// <summary>
/// A list of words to be highlighted in the chatbox.
/// Auto-generated from users's character information.
/// </summary>
private readonly List<string> _autoHighlights = [];
/// <summary>
/// The color (hex) in witch the words will be highlighted as.
/// </summary>
private string? _highlightsColor;
private bool _autoFillHighlightsEnabled;
private void InitializeChatHighlights()
{
_player.LocalPlayerAttached += _ => _characterInfo.RequestCharacterInfo();
_player.LocalPlayerDetached += _ => _characterInfo.RequestCharacterInfo();
_config.OnValueChanged(DCCVars.ChatAutoFillHighlights, value => { _autoFillHighlightsEnabled = value; UpdateHighlights(); });
_autoFillHighlightsEnabled = _config.GetCVar(DCCVars.ChatAutoFillHighlights);
_config.OnValueChanged(DCCVars.ChatHighlightsColor, value => _highlightsColor = value);
_highlightsColor = _config.GetCVar(DCCVars.ChatHighlightsColor);
_config.OnValueChanged(DCCVars.ChatHighlights, UpdateHighlights);
UpdateHighlights(_config.GetCVar(DCCVars.ChatHighlights));
}
public void OnSystemLoaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate += UpdateAutoHighlights;
}
public void OnSystemUnloaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate -= UpdateAutoHighlights;
}
private void UpdateAutoHighlights(CharacterData data)
{
var (_, job, _, _, entityName) = data;
_autoHighlights.Clear();
// If the character has a normal name (eg. "Name Surname" and not "Name Initial Surname" or a particular species name)
// subdivide it so that the name and surname individually get highlighted.
if (entityName.Count(c => c == ' ') == 1)
_autoHighlights.AddRange(entityName.Split(' '));
_autoHighlights.Add(entityName);
var jobKey = "ChatHighlight" + job.Replace(" ", "");
if (_prototypeManager.TryIndex<LocalizedDatasetPrototype>(jobKey, out var jobMatches))
_autoHighlights.AddRange(jobMatches.Values.Select(Loc.GetString));
else
_sawmill.Debug("Missing LocalizedDataset for Job: " + jobKey);
UpdateHighlights();
}
public void UpdateHighlights(string? newHighlights = null)
{
var configuredHighlights = _config.GetCVar(DCCVars.ChatHighlights);
var highlights = newHighlights ?? configuredHighlights;
// Save the newly provided list of highlights if different.
if (newHighlights is not null && !string.Equals(configuredHighlights, highlights, StringComparison.CurrentCultureIgnoreCase))
{
_config.SetCVar(DCCVars.ChatHighlights, highlights);
_config.SaveToFile();
}
var effectiveAutoHighlights = _autoFillHighlightsEnabled
? string.Join("\n", _autoHighlights)
: string.Empty;
OnAutoHighlightsUpdated?.Invoke(effectiveAutoHighlights);
var allHighlights = _autoFillHighlightsEnabled
? highlights.Split("\n").Concat(_autoHighlights)
: highlights.Split("\n");
_highlights.Clear();
// Use `"` as layman symbol for Regex `\b`, ignore all other special sequences
// (Without that escape, a name like `Robert'); DROP TABLE users; --` breaks all messsages)
// Turn `\` into `\\` or else it'll escape the tags inside the actual chat message for reasons I can barely intuit but not explain.
_highlights.AddRange(allHighlights.Select(highlight => Regex.Escape(highlight.Replace(@"\", @"\\")).Replace("\"", "\\b")));
// Arrange the list in descending order so that when highlighting,
// the full word (eg. "Security") appears before the abbreviation (eg. "Sec").
_highlights.Sort((x, y) => y.Length.CompareTo(x.Length));
}
}

View File

@ -0,0 +1,40 @@
using Content.Shared._DV.CCVars;
using Robust.Shared.Configuration;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.UserInterface.Systems.Chat.Controls;
public sealed partial class ChannelFilterPopup
{
public event Action<string>? OnNewHighlights;
private void InitializeChatHighlights()
{
HighlightButton.OnPressed += HighlightsEntered;
HighlightEdit.Placeholder = new Rope.Leaf(Loc.GetString("hud-chatbox-highlights-placeholder"));
// Load highlights if any were saved.
var cfg = IoCManager.Resolve<IConfigurationManager>();
var highlights = cfg.GetCVar(DCCVars.ChatHighlights);
if (!string.IsNullOrEmpty(highlights))
{
HighlightEdit.TextRope = new Rope.Leaf(highlights);
}
}
public void SetAutoHighlights(string autoHighlights)
{
var anyAutohighlights = !string.IsNullOrWhiteSpace(autoHighlights);
AutoHighlightEdit.Visible = anyAutohighlights;
AutoHighlightLabel.Visible = anyAutohighlights;
AutoHighlightEdit.TextRope = new Rope.Leaf(autoHighlights);
}
private void HighlightsEntered(ButtonEventArgs args)
{
OnNewHighlights?.Invoke(Rope.Collapse(HighlightEdit.TextRope));
}
}

View File

@ -0,0 +1,9 @@
namespace Content.Client.UserInterface.Systems.Chat.Widgets;
public partial class ChatBox
{
private void OnNewHighlights(string highlights)
{
_controller.UpdateHighlights(highlights);
}
}

View File

@ -162,6 +162,33 @@ public sealed class DCCVars
public static readonly CVarDef<bool> EnableBacktoBack =
CVarDef.Create("game.disable_preset_test", false, CVar.SERVERONLY);
/// <summary>
/// A string containing a list of newline-separated strings to be highlighted in the chat.
/// </summary>
public static readonly CVarDef<string> ChatHighlights =
CVarDef.Create("deltav.chat.highlights",
"",
CVar.CLIENTONLY | CVar.ARCHIVE,
"A list of newline-separated strings to be highlighted in the chat.");
/// <summary>
/// An option to toggle the automatic filling of the highlights with the character's info, if available.
/// </summary>
public static readonly CVarDef<bool> ChatAutoFillHighlights =
CVarDef.Create("deltav.chat.auto_fill_highlights",
false,
CVar.CLIENTONLY | CVar.ARCHIVE,
"Toggles automatically filling the highlights with the character's information.");
/// <summary>
/// The color in which the highlights will be displayed.
/// </summary>
public static readonly CVarDef<string> ChatHighlightsColor =
CVarDef.Create("deltav.chat.highlights_color",
"#17FFC1FF",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color in which the highlights will be displayed.");
/* Laying down combat */
/// <summary>

View File

@ -0,0 +1,341 @@
# These IDs are generated from job IDs
# The corresponding prototypes are located in Resources/Prototypes/Catalog/_DV/Chat/highlights.yml
# if you add or remove a string here, update the protoype
###### Centcomm
highlight-centcomm-agent-1 = Centcomm
highlight-centcomm-agent-2 = Central Command
highlight-centcomm-agent-3 = Death Squad
highlight-centcomm-agent-4 = DeathSquad
highlight-centcomm-official-1 = Centcomm
highlight-centcomm-official-2 = Central Command
highlight-centcomm-quarantine-officer-1 = Centcomm
highlight-centcomm-quarantine-officer-2 = Central Command
highlight-centcomm-quarantine-officer-3 = "CBURN"
###### ERT
highlight-ert-leader-1 = "ERT Leader
highlight-ert-leader-2 = Leader
highlight-ert-leader-3 = "ERT"
highlight-ert-chaplain-1 = Chaplain
highlight-ert-chaplain-2 = "Chap"
highlight-ert-chaplain-3 = Chapel
highlight-ert-chaplain-4 = "ERT"
highlight-ert-engineer-1 = Station Engineer
highlight-ert-engineer-2 = Engineering
highlight-ert-engineer-3 = Engineer
highlight-ert-engineer-4 = "Engi"
highlight-ert-engineer-5 = "ERT"
highlight-ert-janitor-1 = Janitorial
highlight-ert-janitor-2 = Janitor
highlight-ert-janitor-3 = "Jani"
highlight-ert-janitor-4 = "ERT"
highlight-ert-medic-1 = Medical Doctor
highlight-ert-medic-2 = Doctor
highlight-ert-medic-3 = "Doc"
highlight-ert-medic-4 = MedBay
highlight-ert-medic-5 = "Med"
highlight-ert-medic-6 = Medic
highlight-ert-medic-7 = "ERT"
highlight-ert-security-1 = Security Officer
highlight-ert-security-2 = Secoff
highlight-ert-security-3 = Officer
highlight-ert-security-4 = Security
highlight-ert-security-5 = "Sec"
highlight-ert-security-6 = "ERT"
###### Command
highlight-captain-1 = Captain
highlight-captain-2 = "Cap"
highlight-captain-3 = Command
highlight-chief-justice-1 = Chief justice
highlight-chief-justice-2 = "CJ"
highlight-chief-justice-3 = Justice
highlight-chief-justice-4 = Command
highlight-chief-engineer-1 = Chief Engineer
highlight-chief-engineer-2 = "CE"
highlight-chief-engineer-3 = Engineering
highlight-chief-engineer-4 = Engineer
highlight-chief-engineer-5 = "Engi"
highlight-chief-engineer-6 = Command
highlight-chief-medical-officer-1 = Chief Medical Officer
highlight-chief-medical-officer-2 = "CMO"
highlight-chief-medical-officer-3 = MedBay
highlight-chief-medical-officer-4 = "Med"
highlight-chief-medical-officer-5 = Command
highlight-head-of-personnel-1 = Head Of Personnel
highlight-head-of-personnel-2 = "HoP"
highlight-head-of-personnel-3 = Service
highlight-head-of-personnel-4 = Command
highlight-head-of-security-1 = Head of Security
highlight-head-of-security-2 = "HoS"
highlight-head-of-security-3 = Security
highlight-head-of-security-4 = "Sec"
highlight-head-of-security-5 = Command
highlight-logistics-officer-1 = Logistics Officer
highlight-logistics-officer-2 = "LO"
highlight-logistics-officer-3 = Logistics
highlight-logistics-officer-4 = Logi
highlight-logistics-officer-5 = Command
highlight-mystagogue-1 = Mystagogue
highlight-mystagogue-2 = Mysta
highlight-mystagogue-3 = "MG"
highlight-mystagogue-4 = Epistemics
highlight-mystagogue-5 = "Epi"
highlight-mystagogue-6 = Command
highlight-administrative-assistant-1 = Administrative Assistant
highlight-administrative-assistant-2 = "AA"
highlight-administrative-assistant-3 = Command
###### Security
highlight-detective-1 = Detective
highlight-detective-2 = "Det"
highlight-detective-3 = Security
highlight-detective-4 = "Sec"
highlight-security-cadet-1 = Security Cadet
highlight-security-cadet-2 = Cadet
highlight-security-cadet-3 = Security
highlight-security-cadet-4 = "Sec"
highlight-security-officer-1 = Security Officer
highlight-security-officer-2 = Secoff
highlight-security-officer-3 = Officer
highlight-security-officer-4 = Security
highlight-security-officer-5 = "Sec"
highlight-warden-1 = Warden
highlight-warden-2 = "Ward"
highlight-warden-3 = Security
highlight-warden-4 = "Sec"
highlight-warden-5 = Brig
highlight-prison-guard-1 = Prison Guard
highlight-prison-guard-2 = Prison
highlight-prison-guard-3 = Guard
highlight-prison-guard-4 = Security
highlight-prison-guard-5 = "Sec"
highlight-prison-guard-6 = Brig
highlight-corpsman-1 = Corpsman
highlight-corpsman-2 = "Corps"
highlight-corpsman-3 = Brigmed
highlight-corpsman-4 = "Sec"
highlight-corpsman-5 = Security
###### Station-specific
highlight-boxer-1 = Boxer
highlight-gladiator-1 = Gladiator
highlight-martial-artist-1 = Martial Artist
highlight-martial-artist-2 = Martial
highlight-prisoner-1 = Prisoner
highlight-psychologist-1 = Psychologist
highlight-psychologist-2 = Psychology
highlight-psychologist-3 = Psychodoc
highlight-psychologist-4 = "Psych"
highlight-reporter-1 = Reporter
highlight-reporter-2 = Journalist
highlight-zookeeper-1 = Zookeeper
highlight-zookeeper-2 = Zookeep
###### Engineering
highlight-atmospheric-technician-1 = Atmospheric Technician
highlight-atmospheric-technician-2 = Atmos
highlight-atmospheric-technician-3 = Atmospheric
highlight-atmospheric-technician-4 = Atmosian
highlight-station-engineer-1 = Station Engineer
highlight-station-engineer-2 = Engineering
highlight-station-engineer-3 = Engineer
highlight-station-engineer-4 = "Engi"
highlight-technical-assistant-1 = Technical Assistant
highlight-technical-assistant-2 = Tech Assistant
highlight-technical-assistant-3 = Engineering
highlight-technical-assistant-4 = Engineer
highlight-technical-assistant-5 = "Engi"
###### Epistemics
highlight-scientist-1 = Scientist
highlight-scientist-2 = Epistemics
highlight-scientist-3 = "Epi"
highlight-roboticist-1 = Roboticist
highlight-roboticist-2 = Epistemics
highlight-roboticist-3 = "Epi"
highlight-research-assistant-1 = Research Assistant
highlight-research-assistant-2 = Epistemics
highlight-research-assistant-3 = "Epi"
highlight-psionic-mantis-1 = "Mantis"
highlight-psionic-mantis-2 = Psionic Mantis
###### Justice
highlight-clerk-1 = Clerk
highlight-attorney-1 = Lawyer
highlight-attorney-2 = Attorney
highlight-attorney-3 = Defense
highlight-prosecutor-1 = Lawyer
highlight-prosecutor-2 = Prosecutor
highlight-prosecutor-3 = Prosecution
highlight-prosecutor-4 = "Prosec"
###### Logistics
highlight-cargo-technician-1 = Cargo Technician
highlight-cargo-technician-2 = Cargo Tech
highlight-cargo-technician-3 = Cargo
highlight-cargo-technician-4 = "CT"
highlight-cargo-technician-5 = "Logi"
highlight-cargo-technician-6 = Logistics
highlight-cargo-assistant-1 = Cargo Assistant
highlight-cargo-assistant-2 = "CA"
highlight-cargo-assistant-3 = "Logi"
highlight-cargo-assistant-4 = Logistics
highlight-salvage-specialist-1 = Salvage Specialist
highlight-salvage-specialist-2 = Salvager
highlight-salvage-specialist-3 = Salvage
highlight-salvage-specialist-4 = "Salv"
highlight-salvage-specialist-5 = Miner
highlight-salvage-specialist-6 = "Logi"
highlight-salvage-specialist-7 = Logistics
highlight-courier-1 = Courier
highlight-courier-2 = Mailman
highlight-courier-3 = Logistics
highlight-courier-4 = "Logi"
###### Medical
highlight-chemist-1 = Chemist
highlight-chemist-2 = Chemistry
highlight-chemist-3 = MedBay
highlight-chemist-4 = "Chem"
highlight-chemist-5 = "Medical"
highlight-medical-doctor-1 = Medical Doctor
highlight-medical-doctor-2 = Doctor
highlight-medical-doctor-3 = "Doc"
highlight-medical-doctor-4 = MedBay
highlight-medical-doctor-5 = "Med"
highlight-medical-doctor-6 = "Medical"
highlight-medical-intern-1 = Medical Intern
highlight-medical-intern-2 = Intern
highlight-medical-intern-3 = MedBay
highlight-medical-intern-4 = "Med"
highlight-medical-intern-5 = "Medical"
highlight-paramedic-1 = Paramedic
highlight-paramedic-2 = "Para"
highlight-paramedic-3 = MedBay
highlight-paramedic-4 = "Med"
highlight-paramedic-5 = "Medical"
highlight-surgeon-1 = Surgeon
highlight-surgeon-2 = "Med"
highlight-surgeon-3 = MedBay
highlight-surgeon-4 = Medical
###### Civilian
highlight-bartender-1 = Bartender
highlight-bartender-2 = Barkeeper
highlight-bartender-3 = Barkeep
highlight-bartender-4 = "Bar"
highlight-bartender-5 = Tender
highlight-botanist-1 = Botanist
highlight-botanist-2 = Botany
highlight-botanist-3 = Hydroponics
highlight-chaplain-1 = Chaplain
highlight-chaplain-2 = "Chap"
highlight-chaplain-3 = Chapel
highlight-chef-1 = Chef
highlight-chef-2 = "Cook"
highlight-chef-3 = Kitchen
highlight-clown-1 = Clown
highlight-clown-2 = Jester
highlight-janitor-1 = Janitorial
highlight-janitor-2 = Janitor
highlight-janitor-3 = "Jani"
highlight-lawyer-1 = Lawyer
highlight-lawyer-2 = Attorney
highlight-librarian-1 = Librarian
highlight-librarian-2 = Library
highlight-mime-1 = Mime
highlight-musician-1 = Musician
highlight-musician-2 = Music
highlight-passenger-1 = Passenger
highlight-visitor-1 = Visitor
highlight-service-worker-1 = Service Worker
###### Silicon
highlight-personal-ai-1 = Personal AI
highlight-personal-ai-2 = "pAI"
highlight-cyborg-1 = Cyborg
highlight-cyborg-2 = Borgie
highlight-cyborg-3 = Borg
highlight-medical-cyborg-1 = Cyborg
highlight-medical-cyborg-2 = Borgie
highlight-medical-cyborg-3 = Borg
highlight-medical-cyborg-4 = Medical
highlight-medical-cyborg-5 = Medborg
highlight-medical-cyborg-6 = Medical Cyborg
highlight-security-cyborg-1 = Cyborg
highlight-security-cyborg-2 = Borgie
highlight-security-cyborg-3 = Borg
highlight-security-cyborg-4 = Security
highlight-security-cyborg-5 = Secborg
highlight-security-cyborg-6 = Security Cyborg
highlight-station-ai-1 = Station AI
highlight-station-ai-2 = "AI"

View File

@ -0,0 +1,11 @@
hud-chatbox-select-channels = Channels:
hud-chatbox-auto-highlights = Automatic Highlights:
hud-chatbox-highlights = Highlights:
hud-chatbox-highlights-button = Submit
hud-chatbox-highlights-tooltip = The words need to be separated by a newline,
if wrapped around " they will be highlighted
only if separated by spaces.
hud-chatbox-highlights-placeholder = Urist
"Para"
Bridge

View File

@ -13,3 +13,8 @@ ui-options-function-nano-chat-navigate-down-unread = Navigate down to next unrea
## DeltaV staff chats
ui-options-function-open-staff-chats-deltav = Open admin help and curator chat
## DeltaV implementation of message highlights
ui-options-auto-fill-highlights = Auto-fill the highlights with the character's information
ui-options-highlights-color = Highlights color:
ui-options-highlights-color-example = This is an highlighted text.

View File

@ -0,0 +1,334 @@
# The IDs are generated from job names
# the Prefixes are generated job IDs
# The corresponding strings are located in Resources/Locale/en-US/_DV/chat/highlights.ftl
- type: localizedDataset
id: ChatHighlightAdministrativeAssistant
values:
prefix: highlight-administrative-assistant-
count: 3
- type: localizedDataset
id: ChatHighlightAtmosphericTechnician
values:
prefix: highlight-atmospheric-technician-
count: 4
- type: localizedDataset
id: ChatHighlightBartender
values:
prefix: highlight-bartender-
count: 5
- type: localizedDataset
id: ChatHighlightBorg
values:
prefix: highlight-cyborg-
count: 3
- type: localizedDataset
id: ChatHighlightBotanist
values:
prefix: highlight-botanist-
count: 3
- type: localizedDataset
id: ChatHighlightBoxer
values:
prefix: highlight-boxer-
count: 1
- type: localizedDataset
id: ChatHighlightBrigmedic
values:
prefix: highlight-corpsman-
count: 5
- type: localizedDataset
id: ChatHighlightCaptain
values:
prefix: highlight-captain-
count: 3
- type: localizedDataset
id: ChatHighlightCargoAssistant
values:
prefix: highlight-cargo-assistant-
count: 4
- type: localizedDataset
id: ChatHighlightCargoTechnician
values:
prefix: highlight-cargo-technician-
count: 6
- type: localizedDataset
id: ChatHighlightCBURN
values:
prefix: highlight-centcomm-quarantine-officer-
count: 3
- type: localizedDataset
id: ChatHighlightCentralCommandOfficial
values:
prefix: highlight-centcomm-official-
count: 2
- type: localizedDataset
id: ChatHighlightChaplain
values:
prefix: highlight-chaplain-
count: 3
- type: localizedDataset
id: ChatHighlightChef
values:
prefix: highlight-chef-
count: 3
- type: localizedDataset
id: ChatHighlightChemist
values:
prefix: highlight-chemist-
count: 5
- type: localizedDataset
id: ChatHighlightChiefEngineer
values:
prefix: highlight-chief-engineer-
count: 6
- type: localizedDataset
id: ChatHighlightChiefJustice
values:
prefix: highlight-chief-justice-
count: 4
- type: localizedDataset
id: ChatHighlightChiefMedicalOfficer
values:
prefix: highlight-chief-medical-officer-
count: 5
- type: localizedDataset
id: ChatHighlightClerk
values:
prefix: highlight-clerk-
count: 1
- type: localizedDataset
id: ChatHighlightClown
values:
prefix: highlight-clown-
count: 2
- type: localizedDataset
id: ChatHighlightCourier
values:
prefix: highlight-courier-
count: 4
- type: localizedDataset
id: ChatHighlightDeathSquad
values:
prefix: highlight-centcomm-agent-
count: 4
- type: localizedDataset
id: ChatHighlightDetective
values:
prefix: highlight-detective-
count: 4
- type: localizedDataset
id: ChatHighlightERTChaplain
values:
prefix: highlight-ert-chaplain-
count: 4
- type: localizedDataset
id: ChatHighlightERTEngineer
values:
prefix: highlight-ert-engineer-
count: 5
- type: localizedDataset
id: ChatHighlightERTJanitor
values:
prefix: highlight-ert-janitor-
count: 4
- type: localizedDataset
id: ChatHighlightERTLeader
values:
prefix: highlight-ert-leader-
count: 3
- type: localizedDataset
id: ChatHighlightERTMedical
values:
prefix: highlight-ert-medic-
count: 7
- type: localizedDataset
id: ChatHighlightERTSecurity
values:
prefix: highlight-ert-security-
count: 6
- type: localizedDataset
id: ChatHighlightForensicMantis
values:
prefix: highlight-psionic-mantis-
count: 2
- type: localizedDataset
id: ChatHighlightGladiator
values:
prefix: highlight-gladiator-
count: 1
- type: localizedDataset
id: ChatHighlightHeadOfPersonnel
values:
prefix: highlight-head-of-personnel-
count: 4
- type: localizedDataset
id: ChatHighlightHeadOfSecurity
values:
prefix: highlight-head-of-security-
count: 5
- type: localizedDataset
id: ChatHighlightJanitor
values:
prefix: highlight-janitor-
count: 3
- type: localizedDataset
id: ChatHighlightLawyer
values:
prefix: highlight-attorney-
count: 3
- type: localizedDataset
id: ChatHighlightLibrarian
values:
prefix: highlight-librarian-
count: 2
- type: localizedDataset
id: ChatHighlightMartialArtist
values:
prefix: highlight-martial-artist-
count: 2
- type: localizedDataset
id: ChatHighlightMedicalBorg
values:
prefix: highlight-medical-cyborg-
count: 6
- type: localizedDataset
id: ChatHighlightMedicalDoctor
values:
prefix: highlight-medical-doctor-
count: 6
- type: localizedDataset
id: ChatHighlightMedicalIntern
values:
prefix: highlight-medical-intern-
count: 5
- type: localizedDataset
id: ChatHighlightMime
values:
prefix: highlight-mime-
count: 1
- type: localizedDataset
id: ChatHighlightMusician
values:
prefix: highlight-musician-
count: 2
- type: localizedDataset
id: ChatHighlightParamedic
values:
prefix: highlight-paramedic-
count: 5
- type: localizedDataset
id: ChatHighlightPassenger
values:
prefix: highlight-passenger-
count: 1
- type: localizedDataset
id: ChatHighlightPrisoner
values:
prefix: highlight-prisoner-
count: 1
- type: localizedDataset
id: ChatHighlightPrisonGuard
values:
prefix: highlight-prison-guard-
count: 6
- type: localizedDataset
id: ChatHighlightProsecutor
values:
prefix: highlight-prosecutor-
count: 4
- type: localizedDataset
id: ChatHighlightPsychologist
values:
prefix: highlight-psychologist-
count: 4
- type: localizedDataset
id: ChatHighlightQuartermaster
values:
prefix: highlight-logistics-officer-
count: 5
- type: localizedDataset
id: ChatHighlightReporter
values:
prefix: highlight-reporter-
count: 2
- type: localizedDataset
id: ChatHighlightResearchAssistant
values:
prefix: highlight-research-assistant-
count: 3
- type: localizedDataset
id: ChatHighlightResearchDirector
values:
prefix: highlight-mystagogue-
count: 6
- type: localizedDataset
id: ChatHighlightRoboticist
values:
prefix: highlight-roboticist-
count: 3
- type: localizedDataset
id: ChatHighlightSalvageSpecialist
values:
prefix: highlight-salvage-specialist-
count: 7
- type: localizedDataset
id: ChatHighlightScientist
values:
prefix: highlight-scientist-
count: 3
- type: localizedDataset
id: ChatHighlightSecurityBorg
values:
prefix: highlight-security-cyborg-
count: 6
- type: localizedDataset
id: ChatHighlightSecurityCadet
values:
prefix: highlight-security-cadet-
count: 4
- type: localizedDataset
id: ChatHighlightSecurityOfficer
values:
prefix: highlight-security-officer-
count: 5
- type: localizedDataset
id: ChatHighlightServiceWorker
values:
prefix: highlight-service-worker-
count: 1
- type: localizedDataset
id: ChatHighlightStationAi
values:
prefix: highlight-station-ai-
count: 2
- type: localizedDataset
id: ChatHighlightStationEngineer
values:
prefix: highlight-station-engineer-
count: 4
- type: localizedDataset
id: ChatHighlightSurgeon
values:
prefix: highlight-surgeon-
count: 4
- type: localizedDataset
id: ChatHighlightTechnicalAssistant
values:
prefix: highlight-technical-assistant-
count: 5
- type: localizedDataset
id: ChatHighlightVisitor
values:
prefix: highlight-visitor-
count: 1
- type: localizedDataset
id: ChatHighlightWarden
values:
prefix: highlight-warden-
count: 5
- type: localizedDataset
id: ChatHighlightZookeeper
values:
prefix: highlight-zookeeper-
count: 2