1.0.4c-zio4: chat labels in tags.json

The dead marker and the (T)/(CT)/(SPEC)/(NONE) team-chat labels come from a new optional "chat"
section in tags.json, with colour tags; "" drops a label, and missing keys keep the old defaults.
This commit is contained in:
Astra 2026-09-28 20:29:29 +01:00
parent 3e652f9f6f
commit 8b01af2d14
2 changed files with 50 additions and 9 deletions

View file

@ -27,19 +27,31 @@ public class Tag
public bool AffectsChat => Prefix != "" || NickColor != "" || MessageColor != "";
}
// Labels CS2-Tags adds to the chat lines it prints, from tags.json's "chat" section. Empty leaves
// the label out.
public class ChatLabels
{
[JsonPropertyName("dead")] public string Dead { get; set; } = "{White}☠";
[JsonPropertyName("team_t")] public string TeamT { get; set; } = "{Yellow}(T)";
[JsonPropertyName("team_ct")] public string TeamCT { get; set; } = "{Blue}(CT)";
[JsonPropertyName("team_spec")] public string TeamSpec { get; set; } = "(SPEC)";
[JsonPropertyName("team_none")] public string TeamNone { get; set; } = "(NONE)";
}
[MinimumApiVersion(375)]
public class CS2_Tags : BasePlugin
{
public override string ModuleName => "CS2-Tags";
public override string ModuleDescription => "Add player tags easily in cs2 game";
public override string ModuleAuthor => "daffyy";
public override string ModuleVersion => "1.0.4c-zio3";
public override string ModuleVersion => "1.0.4c-zio4";
private static string TagsPath => Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp",
"configs", "plugins", "CS2-Tags", "tags.json");
// Checked in file order after the SteamID64 entries, first match wins, so list higher ranks first.
private List<KeyValuePair<string, Tag>> _tags = new();
private ChatLabels _labels = new();
public override void Load(bool hotReload)
{
@ -68,6 +80,7 @@ public class CS2_Tags : BasePlugin
var root = JsonNode.Parse(File.ReadAllText(TagsPath),
documentOptions: new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true });
var labels = root?["chat"]?.Deserialize<ChatLabels>() ?? new ChatLabels();
var tags = new List<KeyValuePair<string, Tag>>();
if (root?["tags"] is JsonObject tagsObject)
{
@ -80,6 +93,7 @@ public class CS2_Tags : BasePlugin
}
_tags = tags;
_labels = labels;
Logger.LogInformation("Loaded {Count} tags from {Path}", _tags.Count, TagsPath);
}
catch (Exception ex)
@ -160,7 +174,7 @@ public class CS2_Tags : BasePlugin
var tag = FindTag(player);
if (tag == null || !tag.AffectsChat) return HookResult.Continue;
var line = $"{TeamName(player.TeamNum)} {ChatColors.Default}{FormatChat(player, tag, message, player.TeamNum)}";
var line = $"{Label(TeamLabel(player.TeamNum), player.TeamNum)}{FormatChat(player, tag, message, player.TeamNum)}";
foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false } && p.TeamNum == player.TeamNum))
{
p.PrintToChat($" {line}");
@ -171,7 +185,7 @@ public class CS2_Tags : BasePlugin
private string FormatChat(CCSPlayerController player, Tag tag, string message, int teamNum)
{
string deadIcon = player.TeamNum > (int)CsTeam.Spectator && !player.PawnIsAlive ? $"{ChatColors.White}☠ {ChatColors.Default}" : "";
string deadIcon = player.TeamNum > (int)CsTeam.Spectator && !player.PawnIsAlive ? Label(_labels.Dead, teamNum) : "";
string prefix = tag.Prefix == "" ? "" : ReplaceTags(tag.Prefix, teamNum).TrimEnd() + " ";
string nickColor = tag.NickColor == "" ? TeamColor(teamNum) : ReplaceTags(tag.NickColor, teamNum);
string messageColor = tag.MessageColor == "" ? ChatColors.Default.ToString() : ReplaceTags(tag.MessageColor, teamNum);
@ -224,17 +238,23 @@ public class CS2_Tags : BasePlugin
Utilities.SetStateChanged(player, "CCSPlayerController", "m_szClan");
}
private static string TeamName(int teamNum)
private string TeamLabel(int teamNum)
{
return teamNum switch
{
(int)CsTeam.Spectator => "(SPEC)",
(int)CsTeam.Terrorist => $"{ChatColors.Yellow}(T)",
(int)CsTeam.CounterTerrorist => $"{ChatColors.Blue}(CT)",
_ => "(NONE)",
(int)CsTeam.Spectator => _labels.TeamSpec,
(int)CsTeam.Terrorist => _labels.TeamT,
(int)CsTeam.CounterTerrorist => _labels.TeamCT,
_ => _labels.TeamNone,
};
}
// A label from the "chat" section with its colour tags replaced and a space after it, or nothing.
private static string Label(string label, int teamNum)
{
return label == "" ? "" : $"{ReplaceTags(label, teamNum).TrimEnd()} {ChatColors.Default}";
}
private static string TeamColor(int teamNum)
{
return teamNum switch
@ -264,6 +284,13 @@ public class CS2_Tags : BasePlugin
// the rank out. No brackets there: CS2 adds its own around the clan tag.
private const string DefaultTagsJson = """
{
"chat": {
"dead": "{White}☠",
"team_t": "{Yellow}(T)",
"team_ct": "{Blue}(CT)",
"team_spec": "(SPEC)",
"team_none": "(NONE)"
},
"tags": {
"#rank/owner": { "prefix": "{Default}[{Lime}O{Default}]", "nick_color": "", "message_color": "", "scoreboard": "Owner" },
"#rank/senioradmin": { "prefix": "{Default}[{Lime}SA{Default}]", "nick_color": "", "message_color": "", "scoreboard": "Sen.Admin" },

View file

@ -57,6 +57,20 @@ Fields (all optional, empty means "not set"):
- `scoreboard` - clan tag on the scoreboard, without brackets (CS2 adds them); empty leaves the
player's own clan tag.
The optional `chat` section sets the labels CS2-Tags adds to the chat lines it prints (only for
players whose tag changes their chat). Leave a key out for its default, or set it to `""` to drop
the label:
```jsonc
"chat": {
"dead": "{White}☠", // before a dead player's line
"team_t": "{Yellow}(T)", // start of team chat, per team
"team_ct": "{Blue}(CT)",
"team_spec": "(SPEC)",
"team_none": "(NONE)"
}
```
Colours: any `ChatColors` field name in braces, case-insensitive, e.g. `{Red}`, `{LightBlue}`,
`{Gold}`, `{Grey}`, plus `{TEAMCOLOR}`.
@ -77,4 +91,4 @@ FORGEJO_TOKEN=... ./release.sh v<ModuleVersion>
`release.sh` rebuilds from the committed source and publishes `CS2-Tags-<tag>.tar.gz`, laid out
like `game/csgo/`, to https://git.zio.sh/cs2/CS2-Tags. The tag must equal `v<ModuleVersion>`
(`v1.0.4c-zio3` now).
(`v1.0.4c-zio4` now).