]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
antag objective issuing refactor (#28216)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Sat, 25 May 2024 20:14:48 +0000 (20:14 +0000)
committerGitHub <noreply@github.com>
Sat, 25 May 2024 20:14:48 +0000 (16:14 -0400)
* add AntagObjectives from GenericAntag

* add AntagRandomObjectives that traitor and thief can use

* make ObjectivesSystem use initial character name which AntagSelection passes

* make thief and traitor use AntagRandomObjectives

* remove now unused locale

* make sleeper agents rule use baseTraitorRule

* restore dragon rule oop

* bandaid for genericantag

* real

* typo

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
18 files changed:
Content.Server/Antag/AntagObjectivesSystem.cs [new file with mode: 0644]
Content.Server/Antag/AntagRandomObjectivesSystem.cs [new file with mode: 0644]
Content.Server/Antag/AntagSelectionSystem.API.cs
Content.Server/Antag/AntagSelectionSystem.cs
Content.Server/Antag/Components/AntagObjectivesComponent.cs [new file with mode: 0644]
Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs [new file with mode: 0644]
Content.Server/Antag/Components/AntagSelectionComponent.cs
Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs
Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs
Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs
Content.Server/GameTicking/Rules/ThiefRuleSystem.cs
Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
Content.Server/Objectives/ObjectivesSystem.cs
Resources/Locale/en-US/objectives/round-end.ftl
Resources/Prototypes/GameRules/events.yml
Resources/Prototypes/GameRules/midround.yml
Resources/Prototypes/GameRules/roundstart.yml
Resources/Prototypes/Objectives/objectiveGroups.yml

diff --git a/Content.Server/Antag/AntagObjectivesSystem.cs b/Content.Server/Antag/AntagObjectivesSystem.cs
new file mode 100644 (file)
index 0000000..5aa31f6
--- /dev/null
@@ -0,0 +1,35 @@
+using Content.Server.Antag.Components;
+using Content.Server.Objectives;
+using Content.Shared.Mind;
+using Content.Shared.Objectives.Systems;
+
+namespace Content.Server.Antag;
+
+/// <summary>
+/// Adds fixed objectives to an antag made with <c>AntagObjectivesComponent</c>.
+/// </summary>
+public sealed class AntagObjectivesSystem : EntitySystem
+{
+    [Dependency] private readonly SharedMindSystem _mind = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<AntagObjectivesComponent, AfterAntagEntitySelectedEvent>(OnAntagSelected);
+    }
+
+    private void OnAntagSelected(Entity<AntagObjectivesComponent> ent, ref AfterAntagEntitySelectedEvent args)
+    {
+        if (!_mind.TryGetMind(args.Session, out var mindId, out var mind))
+        {
+            Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!");
+            return;
+        }
+
+        foreach (var id in ent.Comp.Objectives)
+        {
+            _mind.TryAddObjective(mindId, mind, id);
+        }
+    }
+}
diff --git a/Content.Server/Antag/AntagRandomObjectivesSystem.cs b/Content.Server/Antag/AntagRandomObjectivesSystem.cs
new file mode 100644 (file)
index 0000000..c935b8c
--- /dev/null
@@ -0,0 +1,52 @@
+using Content.Server.Antag.Components;
+using Content.Server.Objectives;
+using Content.Shared.Mind;
+using Content.Shared.Objectives.Components;
+using Content.Shared.Objectives.Systems;
+using Robust.Shared.Random;
+
+namespace Content.Server.Antag;
+
+/// <summary>
+/// Adds fixed objectives to an antag made with <c>AntagRandomObjectivesComponent</c>.
+/// </summary>
+public sealed class AntagRandomObjectivesSystem : EntitySystem
+{
+    [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly SharedMindSystem _mind = default!;
+    [Dependency] private readonly ObjectivesSystem _objectives = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<AntagRandomObjectivesComponent, AfterAntagEntitySelectedEvent>(OnAntagSelected);
+    }
+
+    private void OnAntagSelected(Entity<AntagRandomObjectivesComponent> ent, ref AfterAntagEntitySelectedEvent args)
+    {
+        if (!_mind.TryGetMind(args.Session, out var mindId, out var mind))
+        {
+            Log.Error($"Antag {ToPrettyString(args.EntityUid):player} was selected by {ToPrettyString(ent):rule} but had no mind attached!");
+            return;
+        }
+
+        var difficulty = 0f;
+        foreach (var set in ent.Comp.Sets)
+        {
+            if (!_random.Prob(set.Prob))
+                continue;
+
+            for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++)
+            {
+                if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective)
+                    continue;
+
+                _mind.AddObjective(mindId, mind, objective);
+                var adding = Comp<ObjectiveComponent>(objective).Difficulty;
+                difficulty += adding;
+                Log.Debug($"Added objective {ToPrettyString(objective):objective} to {ToPrettyString(args.EntityUid):player} with {adding} difficulty");
+            }
+        }
+    }
+}
index 6acd17a35b273f236ed427d355f4af50c953dd8d..d8554e3fb22a4b2ef29d6e25cb1aa138baa9e238 100644 (file)
@@ -124,7 +124,7 @@ public sealed partial class AntagSelectionSystem
     }
 
     /// <remarks>
-    /// Helper specifically for <see cref="ObjectivesTextGetInfoEvent"/>
+    /// Helper to get just the mind entities and not names.
     /// </remarks>
     public List<EntityUid> GetAntagMindEntityUids(Entity<AntagSelectionComponent?> ent)
     {
index 57df82d6fd9bd780d498211ad92ae08d92fd980a..1d1783ed8756d7249b9ce6156bf0b0fa429c2d7c 100644 (file)
@@ -7,6 +7,7 @@ using Content.Server.GameTicking.Rules;
 using Content.Server.Ghost.Roles;
 using Content.Server.Ghost.Roles.Components;
 using Content.Server.Mind;
+using Content.Server.Objectives;
 using Content.Server.Preferences.Managers;
 using Content.Server.Roles;
 using Content.Server.Roles.Jobs;
@@ -50,6 +51,8 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
 
         SubscribeLocalEvent<GhostRoleAntagSpawnerComponent, TakeGhostRoleEvent>(OnTakeGhostRole);
 
+        SubscribeLocalEvent<AntagSelectionComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
+
         SubscribeLocalEvent<RulePlayerSpawningEvent>(OnPlayerSpawning);
         SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnJobsAssigned);
         SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
@@ -423,6 +426,15 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
 
         return true;
     }
+
+    private void OnObjectivesTextGetInfo(Entity<AntagSelectionComponent> ent, ref ObjectivesTextGetInfoEvent args)
+    {
+        if (ent.Comp.AgentName is not {} name)
+            return;
+
+        args.Minds = ent.Comp.SelectedMinds;
+        args.AgentName = Loc.GetString(name);
+    }
 }
 
 /// <summary>
diff --git a/Content.Server/Antag/Components/AntagObjectivesComponent.cs b/Content.Server/Antag/Components/AntagObjectivesComponent.cs
new file mode 100644 (file)
index 0000000..357c138
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Server.Antag;
+using Content.Shared.Objectives.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Antag.Components;
+
+/// <summary>
+/// Gives antags selected by this rule a fixed list of objectives.
+/// </summary>
+[RegisterComponent, Access(typeof(AntagObjectivesSystem))]
+public sealed partial class AntagObjectivesComponent : Component
+{
+    /// <summary>
+    /// List of static objectives to give.
+    /// </summary>
+    [DataField(required: true)]
+    public List<EntProtoId<ObjectiveComponent>> Objectives = new();
+}
diff --git a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
new file mode 100644 (file)
index 0000000..9a551ac
--- /dev/null
@@ -0,0 +1,52 @@
+using Content.Server.Antag;
+using Content.Shared.Random;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Antag.Components;
+
+/// <summary>
+/// Gives antags selected by this rule a random list of objectives.
+/// </summary>
+[RegisterComponent, Access(typeof(AntagRandomObjectivesSystem))]
+public sealed partial class AntagRandomObjectivesComponent : Component
+{
+    /// <summary>
+    /// Each set of objectives to add.
+    /// </summary>
+    [DataField(required: true)]
+    public List<AntagObjectiveSet> Sets = new();
+
+    /// <summary>
+    /// If the total difficulty of the currently given objectives exceeds, no more will be given.
+    /// </summary>
+    [DataField(required: true)]
+    public float MaxDifficulty;
+}
+
+/// <summary>
+/// A set of objectives to try picking.
+/// Difficulty is checked over all sets, but each set has its own probability and pick count.
+/// </summary>
+[DataRecord]
+public record struct AntagObjectiveSet()
+{
+    /// <summary>
+    /// The grouping used by the objective system to pick random objectives.
+    /// First a group is picked from these, then an objective from that group.
+    /// </summary>
+    [DataField(required: true)]
+    public ProtoId<WeightedRandomPrototype> Groups = string.Empty;
+
+    /// <summary>
+    /// Probability of this set being used.
+    /// </summary>
+    [DataField]
+    public float Prob = 1f;
+
+    /// <summary>
+    /// Number of times to try picking objectives from this set.
+    /// Even if there is enough difficulty remaining, no more will be given after this.
+    /// </summary>
+    [DataField]
+    public int MaxPicks = 20;
+}
index 096be14049a7636087dec590fbcb729c4bcd531f..3464ee56662a04fa49d146d7431b16777473bea5 100644 (file)
@@ -42,6 +42,13 @@ public sealed partial class AntagSelectionComponent : Component
     /// Is not serialized.
     /// </summary>
     public HashSet<ICommonSession> SelectedSessions = new();
+
+    /// <summary>
+    /// Locale id for the name of the antag.
+    /// If this is set then the antag is listed in the round-end summary.
+    /// </summary>
+    [DataField]
+    public LocId? AgentName;
 }
 
 [DataDefinition]
index 01a078625ae3ff2ed583a03e51bb9d539be33b1a..6ad1e177755e0b4fed3c3eba2e88acb16a927b99 100644 (file)
@@ -8,23 +8,4 @@ namespace Content.Server.GameTicking.Rules.Components;
 /// Stores data for <see cref="ThiefRuleSystem"/>.
 /// </summary>
 [RegisterComponent, Access(typeof(ThiefRuleSystem))]
-public sealed partial class ThiefRuleComponent : Component
-{
-    [DataField]
-    public ProtoId<WeightedRandomPrototype> BigObjectiveGroup = "ThiefBigObjectiveGroups";
-
-    [DataField]
-    public ProtoId<WeightedRandomPrototype> SmallObjectiveGroup = "ThiefObjectiveGroups";
-
-    [DataField]
-    public ProtoId<WeightedRandomPrototype> EscapeObjectiveGroup = "ThiefEscapeObjectiveGroups";
-
-    [DataField]
-    public float BigObjectiveChance = 0.7f;
-
-    [DataField]
-    public float MaxObjectiveDifficulty = 2.5f;
-
-    [DataField]
-    public int MaxStealObjectives = 10;
-}
+public sealed partial class ThiefRuleComponent : Component;
index b85019d8afa61801e038d0793d2e7028f986594c..62f92963aa7b5da7d8f35b1b39aa6d2f7d9411cf 100644 (file)
@@ -22,9 +22,6 @@ public sealed partial class TraitorRuleComponent : Component
     [DataField]
     public ProtoId<NpcFactionPrototype> SyndicateFaction = "Syndicate";
 
-    [DataField]
-    public ProtoId<WeightedRandomPrototype> ObjectiveGroup = "TraitorObjectiveGroups";
-
     [DataField]
     public ProtoId<DatasetPrototype> CodewordAdjectives = "adjectives";
 
@@ -72,7 +69,4 @@ public sealed partial class TraitorRuleComponent : Component
     /// </summary>
     [DataField]
     public int StartingBalance = 20;
-
-    [DataField]
-    public int MaxDifficulty = 5;
 }
index 81bdda706bdfcdb836d7463b549bee7a0d0783dd..0367aa1460ce4a87ba8a1f5b2bcf08a14c304a54 100644 (file)
@@ -1,6 +1,8 @@
 using Content.Server.GameTicking.Rules.Components;
 using Content.Server.Objectives;
+using Content.Shared.Mind;
 using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 
 namespace Content.Server.GameTicking.Rules;
 
@@ -47,7 +49,8 @@ public sealed class GenericAntagRuleSystem : GameRuleSystem<GenericAntagRuleComp
 
     private void OnObjectivesTextGetInfo(EntityUid uid, GenericAntagRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
     {
-        args.Minds = comp.Minds;
+        // just temporary until this is deleted
+        args.Minds = comp.Minds.Select(mindId => (mindId, Comp<MindComponent>(mindId).CharacterName ?? "?")).ToList();
         args.AgentName = Loc.GetString(comp.AgentName);
     }
 }
index 083085fa0d8e4ea75e43b142a66300a81b79a23c..faec4a9e9ca2bfb0f7ac8fb6c1b03ca4ef8a1215 100644 (file)
@@ -24,7 +24,6 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
         SubscribeLocalEvent<ThiefRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagSelected);
 
         SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
-        SubscribeLocalEvent<ThiefRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
     }
 
     private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
@@ -33,41 +32,9 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
             return;
 
         //Generate objectives
-        GenerateObjectives(mindId, mind, ent);
         _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null);
     }
 
-    private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule)
-    {
-        // Give thieves their objectives
-        var difficulty = 0f;
-
-        if (_random.Prob(thiefRule.BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal)
-        {
-            var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.BigObjectiveGroup);
-            if (objective != null)
-            {
-                _mindSystem.AddObjective(mindId, mind, objective.Value);
-                difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
-            }
-        }
-
-        for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++)  // Many small objectives
-        {
-            var objective = _objectives.GetRandomObjective(mindId, mind, thiefRule.SmallObjectiveGroup);
-            if (objective == null)
-                continue;
-
-            _mindSystem.AddObjective(mindId, mind, objective.Value);
-            difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
-        }
-
-        //Escape target
-        var escapeObjective = _objectives.GetRandomObjective(mindId, mind, thiefRule.EscapeObjectiveGroup);
-        if (escapeObjective != null)
-            _mindSystem.AddObjective(mindId, mind, escapeObjective.Value);
-    }
-
     //Add mind briefing
     private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args)
     {
@@ -87,10 +54,4 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
         briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
         return briefing;
     }
-
-    private void OnObjectivesTextGetInfo(Entity<ThiefRuleComponent> ent, ref ObjectivesTextGetInfoEvent args)
-    {
-        args.Minds = _antag.GetAntagMindEntityUids(ent.Owner);
-        args.AgentName = Loc.GetString("thief-round-end-agent-name");
-    }
 }
index 1c894a460cca0d663951457e0647cd70724584e8..29de85a42e50e03ad2aa65e2fa365527e3ab8f21 100644 (file)
@@ -31,15 +31,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
     [Dependency] private readonly SharedJobSystem _jobs = default!;
     [Dependency] private readonly ObjectivesSystem _objectives = default!;
 
-    public const int MaxPicks = 20;
-
     public override void Initialize()
     {
         base.Initialize();
 
         SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
 
-        SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
         SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
     }
 
@@ -67,7 +64,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
         }
     }
 
-    public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true)
+    public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true)
     {
         //Grab the mind if it wasnt provided
         if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
@@ -112,37 +109,16 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
         _npcFaction.RemoveFaction(traitor, component.NanoTrasenFaction, false);
         _npcFaction.AddFaction(traitor, component.SyndicateFaction);
 
-        // Give traitors their objectives
-        if (giveObjectives)
-        {
-            var difficulty = 0f;
-            for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++)
-            {
-                var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup);
-                if (objective == null)
-                    continue;
-
-                _mindSystem.AddObjective(mindId, mind, objective.Value);
-                var adding = Comp<ObjectiveComponent>(objective.Value).Difficulty;
-                difficulty += adding;
-                Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty");
-            }
-        }
-
         return true;
     }
 
-    private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
-    {
-        args.Minds = _antag.GetAntagMindEntityUids(uid);
-        args.AgentName = Loc.GetString("traitor-round-end-agent-name");
-    }
-
+    // TODO: AntagCodewordsComponent
     private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args)
     {
         args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
     }
 
+    // TODO: figure out how to handle this? add priority to briefing event?
     private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null)
     {
         var sb = new StringBuilder();
index 47fe4eb5f889ca85c908ebbe0b79d1a4c275cf56..f8ecc22828e527842fb957b83a6e11dd35e38b19 100644 (file)
@@ -36,14 +36,14 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
     private void OnRoundEndText(RoundEndTextAppendEvent ev)
     {
         // go through each gamerule getting data for the roundend summary.
-        var summaries = new Dictionary<string, Dictionary<string, List<EntityUid>>>();
+        var summaries = new Dictionary<string, Dictionary<string, List<(EntityUid, string)>>>();
         var query = EntityQueryEnumerator<GameRuleComponent>();
         while (query.MoveNext(out var uid, out var gameRule))
         {
             if (!_gameTicker.IsGameRuleAdded(uid, gameRule))
                 continue;
 
-            var info = new ObjectivesTextGetInfoEvent(new List<EntityUid>(), string.Empty);
+            var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty);
             RaiseLocalEvent(uid, ref info);
             if (info.Minds.Count == 0)
                 continue;
@@ -51,7 +51,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
             // first group the gamerules by their agents, for example 2 different dragons
             var agent = info.AgentName;
             if (!summaries.ContainsKey(agent))
-                summaries[agent] = new Dictionary<string, List<EntityUid>>();
+                summaries[agent] = new Dictionary<string, List<(EntityUid, string)>>();
 
             var prepend = new ObjectivesTextPrependEvent("");
             RaiseLocalEvent(uid, ref prepend);
@@ -79,7 +79,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
             foreach (var (_, minds) in summary)
             {
                 total += minds.Count;
-                totalInCustody += minds.Where(m => IsInCustody(m)).Count();
+                totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count();
             }
 
             var result = new StringBuilder();
@@ -104,19 +104,16 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
         }
     }
 
-    private void AddSummary(StringBuilder result, string agent, List<EntityUid> minds)
+    private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds)
     {
         var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>();
 
-        foreach (var mindId in minds)
+        foreach (var (mindId, name) in minds)
         {
-            if (!TryComp(mindId, out MindComponent? mind))
-                continue;
-
-            var title = GetTitle(mindId, mind);
-            if (title == null)
+            if (!TryComp<MindComponent>(mindId, out var mind))
                 continue;
 
+            var title = GetTitle((mindId, mind), name);
             var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty;
 
             var objectives = mind.Objectives;
@@ -238,34 +235,18 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
 
     /// <summary>
     /// Get the title for a player's mind used in round end.
+    /// Pass in the original entity name which is shown alongside username.
     /// </summary>
-    public string? GetTitle(EntityUid mindId, MindComponent? mind = null)
+    public string GetTitle(Entity<MindComponent?> mind, string name)
     {
-        if (!Resolve(mindId, ref mind))
-            return null;
-
-        var name = mind.CharacterName;
-        var username = (string?) null;
-
-        if (mind.OriginalOwnerUserId != null &&
-            _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData))
+        if (Resolve(mind, ref mind.Comp) &&
+            mind.Comp.OriginalOwnerUserId != null &&
+            _player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData))
         {
-            username = sessionData.UserName;
+            var username = sessionData.UserName;
+            return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name));
         }
 
-
-        if (username != null)
-        {
-            if (name != null)
-                return Loc.GetString("objectives-player-user-named", ("user", username), ("name", name));
-
-            return Loc.GetString("objectives-player-user", ("user", username));
-        }
-
-        // nothing to identify the player by, just give up
-        if (name == null)
-            return null;
-
         return Loc.GetString("objectives-player-named", ("name", name));
     }
 }
@@ -279,7 +260,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
 /// The objectives system already checks if the game rule is added so you don't need to check that in this event's handler.
 /// </remarks>
 [ByRefEvent]
-public record struct ObjectivesTextGetInfoEvent(List<EntityUid> Minds, string AgentName);
+public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName);
 
 /// <summary>
 /// Raised on the game rule before text for each agent's objectives is added, letting you prepend something.
index b4314b2caff91dac583c9d3f1f77c8136f8c0d2c..3da81fc96404536cfa28dc0369fef2108a9fac73 100644 (file)
@@ -6,7 +6,6 @@ objectives-round-end-result = {$count ->
 objectives-round-end-result-in-custody = {$custody} out of {$count} {MAKEPLURAL($agent)} were in custody.
 
 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 = {$custody}{$title} was a {$agent}.
index 3b9ad5aadf2c304f3b6493739dcffe279798ae08..593122eee55838914d7da1d2d461f341f3cbcfb1 100644 (file)
         prototype: Nukeops
 
 - type: entity
-  id: SleeperAgentsRule
-  parent: BaseGameRule
   noSpawn: true
+  parent: BaseTraitorRule
+  id: SleeperAgentsRule
   components:
   - type: StationEvent
     earliestStart: 30
     startAudio:
       path: /Audio/Announcements/intercept.ogg
   - type: AlertLevelInterceptionRule
-  - type: TraitorRule
   - type: AntagSelection
     definitions:
     - prefRoles: [ Traitor ]
index 5a4cde31018bd32f37015bfcf5d0e4910bfee601..1b58ada64855dcf73f16dd5b9c42679ee4bd93b5 100644 (file)
   id: Thief
   components:
   - type: ThiefRule
+  - type: AntagObjectives
+    objectives:
+    - EscapeThiefShuttleObjective
+  - type: AntagRandomObjectives
+    sets:
+    - groups: ThiefBigObjectiveGroups
+      prob: 0.7
+      maxPicks: 1
+    - groups: ThiefObjectiveGroups
+      maxPicks: 10
+    maxDifficulty: 2.5
   - type: AntagSelection
+    agentName: thief-round-end-agent-name
     definitions:
     - prefRoles: [ Thief ]
       maxRange:
         prototype: Thief
       briefing:
         sound: "/Audio/Misc/thief_greeting.ogg"
-
-#- type: entity
-#  noSpawn: true
-#  parent: BaseGameRule
-#  id: Exterminator
-#  components:
-#  - type: GenericAntagRule
-#    agentName: terminator-round-end-agent-name
-#    objectives:
-#    - TerminateObjective
-#    - ShutDownObjective
index d87e7ccbe76d9b30d480d20f8165601ea8e6bf58..be5f617c8dc8bd3ab87450c262ed74c8948825a7 100644 (file)
         prototype: Nukeops
 
 - type: entity
-  id: Traitor
+  abstract: true
   parent: BaseGameRule
+  id: BaseTraitorRule
+  components:
+  - type: TraitorRule
+  # TODO: codewords in yml
+  # TODO: uplink in yml
+  - type: AntagRandomObjectives
+    sets:
+    - groups: TraitorObjectiveGroups
+    maxDifficulty: 5
+  - type: AntagSelection
+    agentName: traitor-round-end-agent-name
+
+- type: entity
   noSpawn: true
+  parent: BaseTraitorRule
+  id: Traitor
   components:
   - type: GameRule
     minPlayers: 5
     delay:
       min: 240
       max: 420
-  - type: TraitorRule
   - type: AntagSelection
     definitions:
     - prefRoles: [ Traitor ]
index ff126eb5d162f70cdd8b282caeb2bfda8f73dba9..bb74c92da3b5ad008b9eccc29e034c696ecdf827 100644 (file)
     ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added
     ThiefObjectiveGroupAnimal: 2
 
-- type: weightedRandom
-  id: ThiefEscapeObjectiveGroups
-  weights:
-    ThiefObjectiveGroupEscape: 1
-
-
-
 - type: weightedRandom
   id: ThiefObjectiveGroupCollection
   weights: