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:
35
Content.Server/Antag/AntagObjectivesSystem.cs
Normal file
35
Content.Server/Antag/AntagObjectivesSystem.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Content.Server/Antag/AntagRandomObjectivesSystem.cs
Normal file
52
Content.Server/Antag/AntagRandomObjectivesSystem.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
18
Content.Server/Antag/Components/AntagObjectivesComponent.cs
Normal file
18
Content.Server/Antag/Components/AntagObjectivesComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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}.
|
||||||
|
|||||||
@@ -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 ]
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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 ]
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user