diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 1bcdb99839..5dffd35ad4 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -51,7 +51,9 @@ public sealed class TraitorRuleSystem : GameRuleSystem SubscribeLocalEvent(OnStartAttempt); SubscribeLocalEvent(OnPlayersSpawned); SubscribeLocalEvent(HandleLatejoin); - SubscribeLocalEvent(OnRoundEndText); + + SubscribeLocalEvent(OnObjectivesTextGetInfo); + SubscribeLocalEvent(OnObjectivesTextPrepend); } protected override void ActiveTick(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, float frameTime) @@ -367,90 +369,15 @@ public sealed class TraitorRuleSystem : GameRuleSystem } } - private void OnRoundEndText(RoundEndTextAppendEvent ev) + private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var traitor, out var gameRule)) - { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - continue; + args.Minds = comp.TraitorMinds; + args.AgentName = Loc.GetString("traitor-round-end-agent-name"); + } - var result = Loc.GetString("traitor-round-end-result", ("traitorCount", traitor.TraitorMinds.Count)); - - result += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", traitor.Codewords))) + - "\n"; - - foreach (var mindId in traitor.TraitorMinds) - { - if (!TryComp(mindId, out MindComponent? mind)) - continue; - - var name = mind.CharacterName; - _mindSystem.TryGetSession(mindId, out var session); - var username = session?.Name; - - var objectives = mind.AllObjectives.ToArray(); - if (objectives.Length == 0) - { - if (username != null) - { - if (name == null) - result += "\n" + Loc.GetString("traitor-user-was-a-traitor", ("user", username)); - else - result += "\n" + Loc.GetString("traitor-user-was-a-traitor-named", ("user", username), - ("name", name)); - } - else if (name != null) - result += "\n" + Loc.GetString("traitor-was-a-traitor-named", ("name", name)); - - continue; - } - - if (username != null) - { - if (name == null) - result += "\n" + Loc.GetString("traitor-user-was-a-traitor-with-objectives", - ("user", username)); - else - result += "\n" + Loc.GetString("traitor-user-was-a-traitor-with-objectives-named", - ("user", username), ("name", name)); - } - else if (name != null) - result += "\n" + Loc.GetString("traitor-was-a-traitor-with-objectives-named", ("name", name)); - - foreach (var objectiveGroup in objectives.GroupBy(o => o.Prototype.Issuer)) - { - result += "\n" + Loc.GetString($"preset-traitor-objective-issuer-{objectiveGroup.Key}"); - - foreach (var objective in objectiveGroup) - { - foreach (var condition in objective.Conditions) - { - var progress = condition.Progress; - if (progress > 0.99f) - { - result += "\n- " + Loc.GetString( - "traitor-objective-condition-success", - ("condition", condition.Title), - ("markupColor", "green") - ); - } - else - { - result += "\n- " + Loc.GetString( - "traitor-objective-condition-fail", - ("condition", condition.Title), - ("progress", (int) (progress * 100)), - ("markupColor", "red") - ); - } - } - } - } - } - - ev.AddLine(result); - } + private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) + { + args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind) diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index ce6cb9a1f6..86cc58438a 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -1,16 +1,124 @@ -using Content.Shared.Mind; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Shared.Mind; using Content.Shared.Objectives; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using System.Linq; namespace Content.Server.Objectives; public sealed class ObjectivesSystem : EntitySystem { + [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MindSystem _mind = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRoundEndText); + } + + /// + /// Adds objective text for each game rule's players on round end. + /// + private void OnRoundEndText(RoundEndTextAppendEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var gameRule)) + { + if (!_gameTicker.IsGameRuleAdded(uid, gameRule)) + continue; + + var info = new ObjectivesTextGetInfoEvent(new List(), string.Empty); + RaiseLocalEvent(uid, ref info); + if (info.Minds.Count == 0) + continue; + + var agent = info.AgentName; + var result = Loc.GetString("objectives-round-end-result", ("count", info.Minds.Count), ("agent", agent)); + var prepend = new ObjectivesTextPrependEvent(result); + RaiseLocalEvent(uid, ref prepend); + // space between the start text and player list + result = prepend.Text + "\n"; + + foreach (var mindId in info.Minds) + { + if (!TryComp(mindId, out MindComponent? mind)) + continue; + + var name = mind.CharacterName; + _mind.TryGetSession(mindId, out var session); + var username = session?.Name; + + string title; + if (username != null) + { + if (name != null) + title = Loc.GetString("objectives-player-user-named", ("user", username), ("name", name)); + else + title = Loc.GetString("objectives-player-user", ("user", username)); + } + else + { + // nothing to identify the player by, just give up + if (name == null) + continue; + + title = Loc.GetString("objectives-player-named", ("name", name)); + } + + result += "\n"; + + var objectives = mind.AllObjectives.ToArray(); + if (objectives.Length == 0) + { + result += Loc.GetString("objectives-no-objectives", ("title", title), ("agent", agent)); + continue; + } + + result += Loc.GetString("objectives-with-objectives", ("title", title), ("agent", agent)); + + foreach (var objectiveGroup in objectives.GroupBy(o => o.Prototype.Issuer)) + { + result += "\n" + Loc.GetString($"objective-issuer-{objectiveGroup.Key}"); + + foreach (var objective in objectiveGroup) + { + foreach (var condition in objective.Conditions) + { + var progress = condition.Progress; + if (progress > 0.99f) + { + result += "\n- " + Loc.GetString( + "objectives-condition-success", + ("condition", condition.Title), + ("markupColor", "green") + ); + } + else + { + result += "\n- " + Loc.GetString( + "objectives-condition-fail", + ("condition", condition.Title), + ("progress", (int) (progress * 100)), + ("markupColor", "red") + ); + } + } + } + } + } + + ev.AddLine(result + "\n"); + } + } public ObjectivePrototype? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) { @@ -43,3 +151,20 @@ public sealed class ObjectivesSystem : EntitySystem return null; } } + +/// +/// Raised on the game rule to get info for any objectives. +/// If its minds list is set then the players will have their objectives shown in the round end text. +/// AgentName is the generic name for a player in the list. +/// +/// +/// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler. +/// +[ByRefEvent] +public record struct ObjectivesTextGetInfoEvent(List Minds, string AgentName); + +/// +/// Raised on the game rule before text for each agent's objectives is added, letting you prepend something. +/// +[ByRefEvent] +public record struct ObjectivesTextPrependEvent(string Text); diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl index 4459cf5f90..7f515fc97d 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl @@ -1,30 +1,11 @@ - ## Traitor -# Shown at the end of a round of Traitor -traitor-round-end-result = {$traitorCount -> - [one] There was one traitor. - *[other] There were {$traitorCount} traitors. -} - traitor-round-end-codewords = The codewords were: [color=White]{$codewords}[/color] +traitor-round-end-agent-name = traitor + +objective-issuer-syndicate = [color=#87cefa]The Syndicate[/color] # Shown at the end of a round of Traitor -traitor-user-was-a-traitor = [color=gray]{$user}[/color] was a traitor. -traitor-user-was-a-traitor-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) was a traitor. -traitor-was-a-traitor-named = [color=White]{$name}[/color] was a traitor. - -traitor-user-was-a-traitor-with-objectives = [color=gray]{$user}[/color] was a traitor who had the following objectives: -traitor-user-was-a-traitor-with-objectives-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) was a traitor who had the following objectives: -traitor-was-a-traitor-with-objectives-named = [color=White]{$name}[/color] was a traitor who had the following objectives: - -preset-traitor-objective-issuer-syndicate = [color=#87cefa]The Syndicate[/color] - -# Shown at the end of a round of Traitor -traitor-objective-condition-success = {$condition} | [color={$markupColor}]Success![/color] - -# Shown at the end of a round of Traitor -traitor-objective-condition-fail = {$condition} | [color={$markupColor}]Failure![/color] ({$progress}%) traitor-title = Traitor traitor-description = There are traitors among us... diff --git a/Resources/Locale/en-US/objectives/round-end.ftl b/Resources/Locale/en-US/objectives/round-end.ftl new file mode 100644 index 0000000000..c5ef2aacff --- /dev/null +++ b/Resources/Locale/en-US/objectives/round-end.ftl @@ -0,0 +1,14 @@ +objectives-round-end-result = {$count -> + [one] There was one {$agent}. + *[other] There were {$count} {MAKEPLURAL($agent)}. +} + +objectives-player-user-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color]) +objectives-player-user = [color=gray]{$user}[/color] +objectives-player-named = [color=White]{$name}[/color] + +objectives-no-objectives = {$title} was a {$agent}. +objectives-with-objectives = {$title} was a {$agent} who had the following objectives: + +objectives-condition-success = {$condition} | [color={$markupColor}]Success![/color] +objectives-condition-fail = {$condition} | [color={$markupColor}]Failure![/color] ({$progress}%)