From a06ea58249889febd1268d9a67397636d69bae57 Mon Sep 17 00:00:00 2001
From: deltanedas <39013340+deltanedas@users.noreply.github.com>
Date: Sat, 25 May 2024 20:14:48 +0000
Subject: [PATCH] antag objective issuing refactor (#28216)
* 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>
---
Content.Server/Antag/AntagObjectivesSystem.cs | 35 +++++++++++++
.../Antag/AntagRandomObjectivesSystem.cs | 52 +++++++++++++++++++
.../Antag/AntagSelectionSystem.API.cs | 2 +-
Content.Server/Antag/AntagSelectionSystem.cs | 12 +++++
.../Components/AntagObjectivesComponent.cs | 18 +++++++
.../AntagRandomObjectivesComponent.cs | 52 +++++++++++++++++++
.../Components/AntagSelectionComponent.cs | 7 +++
.../Rules/Components/ThiefRuleComponent.cs | 21 +-------
.../Rules/Components/TraitorRuleComponent.cs | 6 ---
.../Rules/GenericAntagRuleSystem.cs | 5 +-
.../GameTicking/Rules/ThiefRuleSystem.cs | 39 --------------
.../GameTicking/Rules/TraitorRuleSystem.cs | 30 ++---------
Content.Server/Objectives/ObjectivesSystem.cs | 51 ++++++------------
.../Locale/en-US/objectives/round-end.ftl | 1 -
Resources/Prototypes/GameRules/events.yml | 5 +-
Resources/Prototypes/GameRules/midround.yml | 23 ++++----
Resources/Prototypes/GameRules/roundstart.yml | 18 ++++++-
.../Prototypes/Objectives/objectiveGroups.yml | 7 ---
18 files changed, 231 insertions(+), 153 deletions(-)
create mode 100644 Content.Server/Antag/AntagObjectivesSystem.cs
create mode 100644 Content.Server/Antag/AntagRandomObjectivesSystem.cs
create mode 100644 Content.Server/Antag/Components/AntagObjectivesComponent.cs
create mode 100644 Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
diff --git a/Content.Server/Antag/AntagObjectivesSystem.cs b/Content.Server/Antag/AntagObjectivesSystem.cs
new file mode 100644
index 0000000000..5aa31f66f6
--- /dev/null
+++ b/Content.Server/Antag/AntagObjectivesSystem.cs
@@ -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;
+
+///
+/// Adds fixed objectives to an antag made with AntagObjectivesComponent.
+///
+public sealed class AntagObjectivesSystem : EntitySystem
+{
+ [Dependency] private readonly SharedMindSystem _mind = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnAntagSelected);
+ }
+
+ private void OnAntagSelected(Entity 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
index 0000000000..c935b8c064
--- /dev/null
+++ b/Content.Server/Antag/AntagRandomObjectivesSystem.cs
@@ -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;
+
+///
+/// Adds fixed objectives to an antag made with AntagRandomObjectivesComponent.
+///
+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(OnAntagSelected);
+ }
+
+ private void OnAntagSelected(Entity 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(objective).Difficulty;
+ difficulty += adding;
+ Log.Debug($"Added objective {ToPrettyString(objective):objective} to {ToPrettyString(args.EntityUid):player} with {adding} difficulty");
+ }
+ }
+ }
+}
diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs
index 6acd17a35b..d8554e3fb2 100644
--- a/Content.Server/Antag/AntagSelectionSystem.API.cs
+++ b/Content.Server/Antag/AntagSelectionSystem.API.cs
@@ -124,7 +124,7 @@ public sealed partial class AntagSelectionSystem
}
///
- /// Helper specifically for
+ /// Helper to get just the mind entities and not names.
///
public List GetAntagMindEntityUids(Entity ent)
{
diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs
index 57df82d6fd..1d1783ed87 100644
--- a/Content.Server/Antag/AntagSelectionSystem.cs
+++ b/Content.Server/Antag/AntagSelectionSystem.cs
@@ -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(OnTakeGhostRole);
+ SubscribeLocalEvent(OnObjectivesTextGetInfo);
+
SubscribeLocalEvent(OnPlayerSpawning);
SubscribeLocalEvent(OnJobsAssigned);
SubscribeLocalEvent(OnSpawnComplete);
@@ -423,6 +426,15 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem ent, ref ObjectivesTextGetInfoEvent args)
+ {
+ if (ent.Comp.AgentName is not {} name)
+ return;
+
+ args.Minds = ent.Comp.SelectedMinds;
+ args.AgentName = Loc.GetString(name);
+ }
}
///
diff --git a/Content.Server/Antag/Components/AntagObjectivesComponent.cs b/Content.Server/Antag/Components/AntagObjectivesComponent.cs
new file mode 100644
index 0000000000..357c138f46
--- /dev/null
+++ b/Content.Server/Antag/Components/AntagObjectivesComponent.cs
@@ -0,0 +1,18 @@
+using Content.Server.Antag;
+using Content.Shared.Objectives.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Antag.Components;
+
+///
+/// Gives antags selected by this rule a fixed list of objectives.
+///
+[RegisterComponent, Access(typeof(AntagObjectivesSystem))]
+public sealed partial class AntagObjectivesComponent : Component
+{
+ ///
+ /// List of static objectives to give.
+ ///
+ [DataField(required: true)]
+ public List> Objectives = new();
+}
diff --git a/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
new file mode 100644
index 0000000000..9a551acc49
--- /dev/null
+++ b/Content.Server/Antag/Components/AntagRandomObjectivesComponent.cs
@@ -0,0 +1,52 @@
+using Content.Server.Antag;
+using Content.Shared.Random;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Antag.Components;
+
+///
+/// Gives antags selected by this rule a random list of objectives.
+///
+[RegisterComponent, Access(typeof(AntagRandomObjectivesSystem))]
+public sealed partial class AntagRandomObjectivesComponent : Component
+{
+ ///
+ /// Each set of objectives to add.
+ ///
+ [DataField(required: true)]
+ public List Sets = new();
+
+ ///
+ /// If the total difficulty of the currently given objectives exceeds, no more will be given.
+ ///
+ [DataField(required: true)]
+ public float MaxDifficulty;
+}
+
+///
+/// A set of objectives to try picking.
+/// Difficulty is checked over all sets, but each set has its own probability and pick count.
+///
+[DataRecord]
+public record struct AntagObjectiveSet()
+{
+ ///
+ /// The grouping used by the objective system to pick random objectives.
+ /// First a group is picked from these, then an objective from that group.
+ ///
+ [DataField(required: true)]
+ public ProtoId Groups = string.Empty;
+
+ ///
+ /// Probability of this set being used.
+ ///
+ [DataField]
+ public float Prob = 1f;
+
+ ///
+ /// Number of times to try picking objectives from this set.
+ /// Even if there is enough difficulty remaining, no more will be given after this.
+ ///
+ [DataField]
+ public int MaxPicks = 20;
+}
diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs
index 096be14049..3464ee5666 100644
--- a/Content.Server/Antag/Components/AntagSelectionComponent.cs
+++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs
@@ -42,6 +42,13 @@ public sealed partial class AntagSelectionComponent : Component
/// Is not serialized.
///
public HashSet SelectedSessions = new();
+
+ ///
+ /// Locale id for the name of the antag.
+ /// If this is set then the antag is listed in the round-end summary.
+ ///
+ [DataField]
+ public LocId? AgentName;
}
[DataDefinition]
diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs
index 01a078625a..6ad1e17775 100644
--- a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs
+++ b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs
@@ -8,23 +8,4 @@ namespace Content.Server.GameTicking.Rules.Components;
/// Stores data for .
///
[RegisterComponent, Access(typeof(ThiefRuleSystem))]
-public sealed partial class ThiefRuleComponent : Component
-{
- [DataField]
- public ProtoId BigObjectiveGroup = "ThiefBigObjectiveGroups";
-
- [DataField]
- public ProtoId SmallObjectiveGroup = "ThiefObjectiveGroups";
-
- [DataField]
- public ProtoId 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;
diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs
index b85019d8af..62f92963aa 100644
--- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs
+++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs
@@ -22,9 +22,6 @@ public sealed partial class TraitorRuleComponent : Component
[DataField]
public ProtoId SyndicateFaction = "Syndicate";
- [DataField]
- public ProtoId ObjectiveGroup = "TraitorObjectiveGroups";
-
[DataField]
public ProtoId CodewordAdjectives = "adjectives";
@@ -72,7 +69,4 @@ public sealed partial class TraitorRuleComponent : Component
///
[DataField]
public int StartingBalance = 20;
-
- [DataField]
- public int MaxDifficulty = 5;
}
diff --git a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs
index 81bdda706b..0367aa1460 100644
--- a/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/GenericAntagRuleSystem.cs
@@ -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 (mindId, Comp(mindId).CharacterName ?? "?")).ToList();
args.AgentName = Loc.GetString(comp.AgentName);
}
}
diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs
index 083085fa0d..faec4a9e9c 100644
--- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs
@@ -24,7 +24,6 @@ public sealed class ThiefRuleSystem : GameRuleSystem
SubscribeLocalEvent(AfterAntagSelected);
SubscribeLocalEvent(OnGetBriefing);
- SubscribeLocalEvent(OnObjectivesTextGetInfo);
}
private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args)
@@ -33,41 +32,9 @@ public sealed class ThiefRuleSystem : GameRuleSystem
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(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(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 thief, ref GetBriefingEvent args)
{
@@ -87,10 +54,4 @@ public sealed class ThiefRuleSystem : GameRuleSystem
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
return briefing;
}
-
- private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args)
- {
- args.Minds = _antag.GetAntagMindEntityUids(ent.Owner);
- args.AgentName = Loc.GetString("thief-round-end-agent-name");
- }
}
diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
index 1c894a460c..29de85a42e 100644
--- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
@@ -31,15 +31,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly ObjectivesSystem _objectives = default!;
- public const int MaxPicks = 20;
-
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(AfterEntitySelected);
- SubscribeLocalEvent(OnObjectivesTextGetInfo);
SubscribeLocalEvent(OnObjectivesTextPrepend);
}
@@ -67,7 +64,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem
}
}
- 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
_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(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();
diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs
index 47fe4eb5f8..f8ecc22828 100644
--- a/Content.Server/Objectives/ObjectivesSystem.cs
+++ b/Content.Server/Objectives/ObjectivesSystem.cs
@@ -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>>();
+ var summaries = new Dictionary>>();
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);
+ 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>();
+ summaries[agent] = new Dictionary>();
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 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(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
///
/// Get the title for a player's mind used in round end.
+ /// Pass in the original entity name which is shown alongside username.
///
- public string? GetTitle(EntityUid mindId, MindComponent? mind = null)
+ public string GetTitle(Entity 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.
///
[ByRefEvent]
-public record struct ObjectivesTextGetInfoEvent(List Minds, string AgentName);
+public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName);
///
/// Raised on the game rule before text for each agent's objectives is added, letting you prepend something.
diff --git a/Resources/Locale/en-US/objectives/round-end.ftl b/Resources/Locale/en-US/objectives/round-end.ftl
index b4314b2caf..3da81fc964 100644
--- a/Resources/Locale/en-US/objectives/round-end.ftl
+++ b/Resources/Locale/en-US/objectives/round-end.ftl
@@ -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}.
diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml
index 3b9ad5aadf..593122eee5 100644
--- a/Resources/Prototypes/GameRules/events.yml
+++ b/Resources/Prototypes/GameRules/events.yml
@@ -428,9 +428,9 @@
prototype: Nukeops
- type: entity
- id: SleeperAgentsRule
- parent: BaseGameRule
noSpawn: true
+ parent: BaseTraitorRule
+ id: SleeperAgentsRule
components:
- type: StationEvent
earliestStart: 30
@@ -441,7 +441,6 @@
startAudio:
path: /Audio/Announcements/intercept.ogg
- type: AlertLevelInterceptionRule
- - type: TraitorRule
- type: AntagSelection
definitions:
- prefRoles: [ Traitor ]
diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml
index 5a4cde3101..1b58ada648 100644
--- a/Resources/Prototypes/GameRules/midround.yml
+++ b/Resources/Prototypes/GameRules/midround.yml
@@ -35,7 +35,19 @@
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:
@@ -53,14 +65,3 @@
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
diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml
index d87e7ccbe7..be5f617c8d 100644
--- a/Resources/Prototypes/GameRules/roundstart.yml
+++ b/Resources/Prototypes/GameRules/roundstart.yml
@@ -134,16 +134,30 @@
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 ]
diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml
index ff126eb5d1..bb74c92da3 100644
--- a/Resources/Prototypes/Objectives/objectiveGroups.yml
+++ b/Resources/Prototypes/Objectives/objectiveGroups.yml
@@ -55,13 +55,6 @@
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: