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>
This commit is contained in:
deltanedas
2024-05-25 20:14:48 +00:00
committed by GitHub
parent f1c70d69f2
commit a06ea58249
18 changed files with 231 additions and 153 deletions

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -124,7 +124,7 @@ public sealed partial class AntagSelectionSystem
} }
/// <remarks> /// <remarks>
/// Helper specifically for <see cref="ObjectivesTextGetInfoEvent"/> /// Helper to get just the mind entities and not names.
/// </remarks> /// </remarks>
public List<EntityUid> GetAntagMindEntityUids(Entity<AntagSelectionComponent?> ent) public List<EntityUid> GetAntagMindEntityUids(Entity<AntagSelectionComponent?> ent)
{ {

View File

@@ -7,6 +7,7 @@ using Content.Server.GameTicking.Rules;
using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Objectives;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Roles.Jobs; using Content.Server.Roles.Jobs;
@@ -50,6 +51,8 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
SubscribeLocalEvent<GhostRoleAntagSpawnerComponent, TakeGhostRoleEvent>(OnTakeGhostRole); SubscribeLocalEvent<GhostRoleAntagSpawnerComponent, TakeGhostRoleEvent>(OnTakeGhostRole);
SubscribeLocalEvent<AntagSelectionComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
SubscribeLocalEvent<RulePlayerSpawningEvent>(OnPlayerSpawning); SubscribeLocalEvent<RulePlayerSpawningEvent>(OnPlayerSpawning);
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnJobsAssigned); SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnJobsAssigned);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete); SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
@@ -423,6 +426,15 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
return true; 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> /// <summary>

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -42,6 +42,13 @@ public sealed partial class AntagSelectionComponent : Component
/// Is not serialized. /// Is not serialized.
/// </summary> /// </summary>
public HashSet<ICommonSession> SelectedSessions = new(); 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] [DataDefinition]

View File

@@ -8,23 +8,4 @@ namespace Content.Server.GameTicking.Rules.Components;
/// Stores data for <see cref="ThiefRuleSystem"/>. /// Stores data for <see cref="ThiefRuleSystem"/>.
/// </summary> /// </summary>
[RegisterComponent, Access(typeof(ThiefRuleSystem))] [RegisterComponent, Access(typeof(ThiefRuleSystem))]
public sealed partial class ThiefRuleComponent : Component 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;
}

View File

@@ -22,9 +22,6 @@ public sealed partial class TraitorRuleComponent : Component
[DataField] [DataField]
public ProtoId<NpcFactionPrototype> SyndicateFaction = "Syndicate"; public ProtoId<NpcFactionPrototype> SyndicateFaction = "Syndicate";
[DataField]
public ProtoId<WeightedRandomPrototype> ObjectiveGroup = "TraitorObjectiveGroups";
[DataField] [DataField]
public ProtoId<DatasetPrototype> CodewordAdjectives = "adjectives"; public ProtoId<DatasetPrototype> CodewordAdjectives = "adjectives";
@@ -72,7 +69,4 @@ public sealed partial class TraitorRuleComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public int StartingBalance = 20; public int StartingBalance = 20;
[DataField]
public int MaxDifficulty = 5;
} }

View File

@@ -1,6 +1,8 @@
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Server.Objectives; using Content.Server.Objectives;
using Content.Shared.Mind;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.GameTicking.Rules; 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) 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); args.AgentName = Loc.GetString(comp.AgentName);
} }
} }

View File

@@ -24,7 +24,6 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
SubscribeLocalEvent<ThiefRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagSelected); SubscribeLocalEvent<ThiefRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagSelected);
SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing); SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
SubscribeLocalEvent<ThiefRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
} }
private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args) private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
@@ -33,41 +32,9 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
return; return;
//Generate objectives //Generate objectives
GenerateObjectives(mindId, mind, ent);
_antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); _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 //Add mind briefing
private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args) 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"; briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
return briefing; 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");
}
} }

View File

@@ -31,15 +31,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
[Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly ObjectivesSystem _objectives = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!;
public const int MaxPicks = 20;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected); SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend); 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 //Grab the mind if it wasnt provided
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) 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.RemoveFaction(traitor, component.NanoTrasenFaction, false);
_npcFaction.AddFaction(traitor, component.SyndicateFaction); _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; return true;
} }
private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) // TODO: AntagCodewordsComponent
{
args.Minds = _antag.GetAntagMindEntityUids(uid);
args.AgentName = Loc.GetString("traitor-round-end-agent-name");
}
private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args)
{ {
args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); 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) private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();

View File

@@ -36,14 +36,14 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
private void OnRoundEndText(RoundEndTextAppendEvent ev) private void OnRoundEndText(RoundEndTextAppendEvent ev)
{ {
// go through each gamerule getting data for the roundend summary. // 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>(); var query = EntityQueryEnumerator<GameRuleComponent>();
while (query.MoveNext(out var uid, out var gameRule)) while (query.MoveNext(out var uid, out var gameRule))
{ {
if (!_gameTicker.IsGameRuleAdded(uid, gameRule)) if (!_gameTicker.IsGameRuleAdded(uid, gameRule))
continue; continue;
var info = new ObjectivesTextGetInfoEvent(new List<EntityUid>(), string.Empty); var info = new ObjectivesTextGetInfoEvent(new List<(EntityUid, string)>(), string.Empty);
RaiseLocalEvent(uid, ref info); RaiseLocalEvent(uid, ref info);
if (info.Minds.Count == 0) if (info.Minds.Count == 0)
continue; continue;
@@ -51,7 +51,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
// first group the gamerules by their agents, for example 2 different dragons // first group the gamerules by their agents, for example 2 different dragons
var agent = info.AgentName; var agent = info.AgentName;
if (!summaries.ContainsKey(agent)) if (!summaries.ContainsKey(agent))
summaries[agent] = new Dictionary<string, List<EntityUid>>(); summaries[agent] = new Dictionary<string, List<(EntityUid, string)>>();
var prepend = new ObjectivesTextPrependEvent(""); var prepend = new ObjectivesTextPrependEvent("");
RaiseLocalEvent(uid, ref prepend); RaiseLocalEvent(uid, ref prepend);
@@ -79,7 +79,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
foreach (var (_, minds) in summary) foreach (var (_, minds) in summary)
{ {
total += minds.Count; total += minds.Count;
totalInCustody += minds.Where(m => IsInCustody(m)).Count(); totalInCustody += minds.Where(pair => IsInCustody(pair.Item1)).Count();
} }
var result = new StringBuilder(); 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)>(); 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)) if (!TryComp<MindComponent>(mindId, out var mind))
continue;
var title = GetTitle(mindId, mind);
if (title == null)
continue; continue;
var title = GetTitle((mindId, mind), name);
var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty; var custody = IsInCustody(mindId, mind) ? Loc.GetString("objectives-in-custody") : string.Empty;
var objectives = mind.Objectives; var objectives = mind.Objectives;
@@ -238,34 +235,18 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
/// <summary> /// <summary>
/// Get the title for a player's mind used in round end. /// Get the title for a player's mind used in round end.
/// Pass in the original entity name which is shown alongside username.
/// </summary> /// </summary>
public string? GetTitle(EntityUid mindId, MindComponent? mind = null) public string GetTitle(Entity<MindComponent?> mind, string name)
{ {
if (!Resolve(mindId, ref mind)) if (Resolve(mind, ref mind.Comp) &&
return null; mind.Comp.OriginalOwnerUserId != null &&
_player.TryGetPlayerData(mind.Comp.OriginalOwnerUserId.Value, out var sessionData))
var name = mind.CharacterName;
var username = (string?) null;
if (mind.OriginalOwnerUserId != null &&
_player.TryGetPlayerData(mind.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)); 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. /// 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> /// </remarks>
[ByRefEvent] [ByRefEvent]
public record struct ObjectivesTextGetInfoEvent(List<EntityUid> Minds, string AgentName); public record struct ObjectivesTextGetInfoEvent(List<(EntityUid, string)> Minds, string AgentName);
/// <summary> /// <summary>
/// Raised on the game rule before text for each agent's objectives is added, letting you prepend something. /// Raised on the game rule before text for each agent's objectives is added, letting you prepend something.

View 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-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-named = [color=White]{$name}[/color] ([color=gray]{$user}[/color])
objectives-player-user = [color=gray]{$user}[/color]
objectives-player-named = [color=White]{$name}[/color] objectives-player-named = [color=White]{$name}[/color]
objectives-no-objectives = {$custody}{$title} was a {$agent}. objectives-no-objectives = {$custody}{$title} was a {$agent}.

View File

@@ -428,9 +428,9 @@
prototype: Nukeops prototype: Nukeops
- type: entity - type: entity
id: SleeperAgentsRule
parent: BaseGameRule
noSpawn: true noSpawn: true
parent: BaseTraitorRule
id: SleeperAgentsRule
components: components:
- type: StationEvent - type: StationEvent
earliestStart: 30 earliestStart: 30
@@ -441,7 +441,6 @@
startAudio: startAudio:
path: /Audio/Announcements/intercept.ogg path: /Audio/Announcements/intercept.ogg
- type: AlertLevelInterceptionRule - type: AlertLevelInterceptionRule
- type: TraitorRule
- type: AntagSelection - type: AntagSelection
definitions: definitions:
- prefRoles: [ Traitor ] - prefRoles: [ Traitor ]

View File

@@ -35,7 +35,19 @@
id: Thief id: Thief
components: components:
- type: ThiefRule - 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 - type: AntagSelection
agentName: thief-round-end-agent-name
definitions: definitions:
- prefRoles: [ Thief ] - prefRoles: [ Thief ]
maxRange: maxRange:
@@ -53,14 +65,3 @@
prototype: Thief prototype: Thief
briefing: briefing:
sound: "/Audio/Misc/thief_greeting.ogg" 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

View File

@@ -134,16 +134,30 @@
prototype: Nukeops prototype: Nukeops
- type: entity - type: entity
id: Traitor abstract: true
parent: BaseGameRule 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 noSpawn: true
parent: BaseTraitorRule
id: Traitor
components: components:
- type: GameRule - type: GameRule
minPlayers: 5 minPlayers: 5
delay: delay:
min: 240 min: 240
max: 420 max: 420
- type: TraitorRule
- type: AntagSelection - type: AntagSelection
definitions: definitions:
- prefRoles: [ Traitor ] - prefRoles: [ Traitor ]

View File

@@ -55,13 +55,6 @@
ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added
ThiefObjectiveGroupAnimal: 2 ThiefObjectiveGroupAnimal: 2
- type: weightedRandom
id: ThiefEscapeObjectiveGroups
weights:
ThiefObjectiveGroupEscape: 1
- type: weightedRandom - type: weightedRandom
id: ThiefObjectiveGroupCollection id: ThiefObjectiveGroupCollection
weights: weights: