Refactor antag rule code (#23445)
* Initial Pass, Rev, Thief * Zombie initial pass * Rebase, Traitor * Nukeops, More overloads * Revert RevolutionaryRuleComponent * Use TryRoundStartAttempt, Rewrite nukie spawning * Comments, Add task scheduler to GameRuleSystem * Zombie initial testing done * Sort methods, rework GameRuleTask * Add CCVar, Initial testing continues * Might as well get rid of the obsolete logging * Oops, i dont know how to log apparently * Suggested formatting fixes * Suggested changes * Fix merge issues * Minor optimisation * Allowed thief to choose other antags * Review changes * Spawn items on floor first, then inserting * minor tweaks * Shift as much as possible to ProtoId<> * Remove unneeded * Add exclusive antag attribute * Fix merge issues * Minor formatting fix * Convert to struct * Cleanup * Review cleanup (need to test a lot) * Some fixes, (mostly) tested * oop * Pass tests (for real) --------- Co-authored-by: Rainfall <rainfey0+git@gmail.com> Co-authored-by: AJCM <AJCM@tutanota.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@ public sealed partial class NukeOperativeSpawnerComponent : Component
|
||||
[DataField("name", required:true)]
|
||||
public string OperativeName = default!;
|
||||
|
||||
[DataField("rolePrototype", customTypeSerializer:typeof(PrototypeIdSerializer<AntagPrototype>), required:true)]
|
||||
public string OperativeRolePrototype = default!;
|
||||
|
||||
[DataField("startingGearPrototype", customTypeSerializer:typeof(PrototypeIdSerializer<StartingGearPrototype>), required:true)]
|
||||
public string OperativeStartingGear = default!;
|
||||
[DataField]
|
||||
public NukeopSpawnPreset SpawnDetails = default!;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.StationEvents.Events;
|
||||
@@ -16,15 +17,8 @@ namespace Content.Server.GameTicking.Rules.Components;
|
||||
[RegisterComponent, Access(typeof(NukeopsRuleSystem), typeof(LoneOpsSpawnRule))]
|
||||
public sealed partial class NukeopsRuleComponent : Component
|
||||
{
|
||||
// TODO Replace with GameRuleComponent.minPlayers
|
||||
/// <summary>
|
||||
/// The minimum needed amount of players
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinPlayers = 20;
|
||||
|
||||
/// <summary>
|
||||
/// This INCLUDES the operatives. So a value of 3 is satisfied by 2 players & 1 operative
|
||||
/// This INCLUDES the operatives. So a value of 3 is satisfied by 2 players & 1 operative
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int PlayersPerOperative = 10;
|
||||
@@ -92,17 +86,11 @@ public sealed partial class NukeopsRuleComponent : Component
|
||||
[DataField]
|
||||
public int WarTCAmountPerNukie = 40;
|
||||
|
||||
/// <summary>
|
||||
/// Time allowed for declaration of war
|
||||
/// </summary>
|
||||
[DataField("warDeclarationDelay")]
|
||||
public TimeSpan WarDeclarationDelay = TimeSpan.FromMinutes(6);
|
||||
|
||||
/// <summary>
|
||||
/// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan? WarNukieArriveDelay = TimeSpan.FromMinutes(15);
|
||||
public TimeSpan WarNukieArriveDelay = TimeSpan.FromMinutes(15);
|
||||
|
||||
/// <summary>
|
||||
/// Minimal operatives count for war declaration
|
||||
@@ -116,38 +104,11 @@ public sealed partial class NukeopsRuleComponent : Component
|
||||
[DataField]
|
||||
public EntProtoId GhostSpawnPointProto = "SpawnPointGhostNukeOperative";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> CommanderRoleProto = "NukeopsCommander";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> OperativeRoleProto = "Nukeops";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> MedicRoleProto = "NukeopsMedic";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<StartingGearPrototype> CommanderStartGearProto = "SyndicateCommanderGearFull";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<StartingGearPrototype> MedicStartGearProto = "SyndicateOperativeMedicFull";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<StartingGearPrototype> OperativeStartGearProto = "SyndicateOperativeGearFull";
|
||||
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
||||
public string EliteNames = "SyndicateNamesElite";
|
||||
|
||||
[DataField]
|
||||
public string OperationName = "Test Operation";
|
||||
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
||||
public string NormalNames = "SyndicateNamesNormal";
|
||||
|
||||
[DataField(customTypeSerializer: typeof(ResPathSerializer))]
|
||||
public ResPath OutpostMap = new("/Maps/nukieplanet.yml");
|
||||
|
||||
[DataField(customTypeSerializer: typeof(ResPathSerializer))]
|
||||
public ResPath ShuttleMap = new("/Maps/infiltrator.yml");
|
||||
[DataField]
|
||||
public ProtoId<GameMapPrototype> OutpostMapPrototype = "NukieOutpost";
|
||||
|
||||
[DataField]
|
||||
public WinType WinType = WinType.Neutral;
|
||||
@@ -163,33 +124,53 @@ public sealed partial class NukeopsRuleComponent : Component
|
||||
public EntityUid? NukieShuttle;
|
||||
public EntityUid? TargetStation;
|
||||
|
||||
/// <summary>
|
||||
/// Cached starting gear prototypes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, StartingGearPrototype> StartingGearPrototypes = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Cached operator name prototypes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, List<string>> OperativeNames = new();
|
||||
|
||||
/// <summary>
|
||||
/// Data to be used in <see cref="OnMindAdded"/> for an operative once the Mind has been added.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<EntityUid, string> OperativeMindPendingData = new();
|
||||
|
||||
/// <summary>
|
||||
/// Players who played as an operative at some point in the round.
|
||||
/// Stores the mind as well as the entity name
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, EntityUid> OperativePlayers = new();
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<NpcFactionPrototype> Faction = default!;
|
||||
|
||||
[DataField]
|
||||
public NukeopSpawnPreset CommanderSpawnDetails = new() { AntagRoleProto = "NukeopsCommander", GearProto = "SyndicateCommanderGearFull", NamePrefix = "nukeops-role-commander", NameList = "SyndicateNamesElite" };
|
||||
|
||||
[DataField]
|
||||
public NukeopSpawnPreset AgentSpawnDetails = new() { AntagRoleProto = "NukeopsMedic", GearProto = "SyndicateOperativeMedicFull", NamePrefix = "nukeops-role-agent", NameList = "SyndicateNamesNormal" };
|
||||
|
||||
[DataField]
|
||||
public NukeopSpawnPreset OperativeSpawnDetails = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the presets for each operative type
|
||||
/// Ie Commander, Agent and Operative
|
||||
/// </summary>
|
||||
[DataDefinition, Serializable]
|
||||
public sealed partial class NukeopSpawnPreset
|
||||
{
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> AntagRoleProto = "Nukeops";
|
||||
|
||||
/// <summary>
|
||||
/// The equipment set this operative will be given when spawned
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<StartingGearPrototype> GearProto = "SyndicateOperativeGearFull";
|
||||
|
||||
/// <summary>
|
||||
/// The name prefix, ie "Agent"
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId NamePrefix = "nukeops-role-operator";
|
||||
|
||||
/// <summary>
|
||||
/// The entity name suffix will be chosen from this list randomly
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<DatasetPrototype> NameList = "SyndicateNamesNormal";
|
||||
}
|
||||
|
||||
public enum WinType : byte
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
@@ -32,15 +31,6 @@ public sealed partial class RevolutionaryRuleComponent : Component
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> HeadRevPrototypeId = "HeadRev";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> RevPrototypeId = "Rev";
|
||||
|
||||
/// <summary>
|
||||
/// Sound that plays when you are chosen as Rev. (Placeholder until I find something cool I guess)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier HeadRevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Min players needed for Revolutionary gamemode to start.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Preferences;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
@@ -12,6 +11,18 @@ namespace Content.Server.GameTicking.Rules.Components;
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// Add a Pacified comp to thieves
|
||||
/// </summary>
|
||||
@@ -27,8 +38,6 @@ public sealed partial class ThiefRuleComponent : Component
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> ThiefPrototypeId = "Thief";
|
||||
|
||||
public Dictionary<ICommonSession, HumanoidCharacterProfile> StartCandidates = new();
|
||||
|
||||
[DataField]
|
||||
public float MaxObjectiveDifficulty = 2.5f;
|
||||
|
||||
@@ -39,7 +48,7 @@ public sealed partial class ThiefRuleComponent : Component
|
||||
/// Things that will be given to thieves
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntProtoId> StarterItems = new List<EntProtoId> { "ToolboxThief", "ClothingHandsChameleonThief" }; //TO DO - replace to chameleon thieving gloves whem merg
|
||||
public List<EntProtoId> StarterItems = new() { "ToolboxThief", "ClothingHandsChameleonThief" };
|
||||
|
||||
/// <summary>
|
||||
/// All Thieves created by this rule
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
@@ -11,8 +13,23 @@ public sealed partial class TraitorRuleComponent : Component
|
||||
{
|
||||
public readonly List<EntityUid> TraitorMinds = new();
|
||||
|
||||
[DataField("traitorPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string TraitorPrototypeId = "Traitor";
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> TraitorPrototypeId = "Traitor";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<NpcFactionPrototype> NanoTrasenFaction = "NanoTrasen";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<NpcFactionPrototype> SyndicateFaction = "Syndicate";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> ObjectiveGroup = "TraitorObjectiveGroups";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<DatasetPrototype> CodewordAdjectives = "adjectives";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<DatasetPrototype> CodewordVerbs = "verbs";
|
||||
|
||||
public int TotalTraitors => TraitorMinds.Count;
|
||||
public string[] Codewords = new string[3];
|
||||
@@ -20,17 +37,24 @@ public sealed partial class TraitorRuleComponent : Component
|
||||
public enum SelectionState
|
||||
{
|
||||
WaitingForSpawn = 0,
|
||||
ReadyToSelect = 1,
|
||||
SelectionMade = 2,
|
||||
ReadyToStart = 1,
|
||||
Started = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current state of the rule
|
||||
/// </summary>
|
||||
public SelectionState SelectionStatus = SelectionState.WaitingForSpawn;
|
||||
public TimeSpan AnnounceAt = TimeSpan.Zero;
|
||||
public Dictionary<ICommonSession, HumanoidCharacterProfile> StartCandidates = new();
|
||||
|
||||
/// <summary>
|
||||
/// When should traitors be selected and the announcement made
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan? AnnounceAt;
|
||||
|
||||
/// <summary>
|
||||
/// Path to antagonist alert sound.
|
||||
/// </summary>
|
||||
[DataField("greetSoundNotification")]
|
||||
[DataField]
|
||||
public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg");
|
||||
}
|
||||
|
||||
@@ -2,98 +2,85 @@ using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(ZombieRuleSystem))]
|
||||
public sealed partial class ZombieRuleComponent : Component
|
||||
{
|
||||
[DataField("initialInfectedNames")]
|
||||
[DataField]
|
||||
public Dictionary<string, string> InitialInfectedNames = new();
|
||||
|
||||
[DataField("patientZeroPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string PatientZeroPrototypeId = "InitialInfected";
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the initial infected have been chosen.
|
||||
/// </summary>
|
||||
[DataField("infectedChosen")]
|
||||
public bool InfectedChosen;
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype> PatientZeroPrototypeId = "InitialInfected";
|
||||
|
||||
/// <summary>
|
||||
/// When the round will next check for round end.
|
||||
/// </summary>
|
||||
[DataField("nextRoundEndCheck", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextRoundEndCheck;
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan? NextRoundEndCheck;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time between each check for the end of the round.
|
||||
/// </summary>
|
||||
[DataField("endCheckDelay")]
|
||||
[DataField]
|
||||
public TimeSpan EndCheckDelay = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the initial infected will be chosen.
|
||||
/// </summary>
|
||||
[DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan? StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of time after the round starts that the initial infected will be chosen.
|
||||
/// </summary>
|
||||
[DataField("minStartDelay")]
|
||||
[DataField]
|
||||
public TimeSpan MinStartDelay = TimeSpan.FromMinutes(10);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time after the round starts that the initial infected will be chosen.
|
||||
/// </summary>
|
||||
[DataField("maxStartDelay")]
|
||||
[DataField]
|
||||
public TimeSpan MaxStartDelay = TimeSpan.FromMinutes(15);
|
||||
|
||||
/// <summary>
|
||||
/// The sound that plays when someone becomes an initial infected.
|
||||
/// todo: this should have a unique sound instead of reusing the zombie one.
|
||||
/// </summary>
|
||||
[DataField("initialInfectedSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier InitialInfectedSound = new SoundPathSpecifier("/Audio/Ambience/Antag/zombie_start.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of time initial infected have before they start taking infection damage.
|
||||
/// </summary>
|
||||
[DataField("minInitialInfectedGrace")]
|
||||
[DataField]
|
||||
public TimeSpan MinInitialInfectedGrace = TimeSpan.FromMinutes(12.5f);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time initial infected have before they start taking damage.
|
||||
/// </summary>
|
||||
[DataField("maxInitialInfectedGrace")]
|
||||
[DataField]
|
||||
public TimeSpan MaxInitialInfectedGrace = TimeSpan.FromMinutes(15f);
|
||||
|
||||
/// <summary>
|
||||
/// How many players for each initial infected.
|
||||
/// </summary>
|
||||
[DataField("playersPerInfected")]
|
||||
[DataField]
|
||||
public int PlayersPerInfected = 10;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of initial infected.
|
||||
/// </summary>
|
||||
[DataField("maxInitialInfected")]
|
||||
[DataField]
|
||||
public int MaxInitialInfected = 6;
|
||||
|
||||
/// <summary>
|
||||
/// After this amount of the crew become zombies, the shuttle will be automatically called.
|
||||
/// </summary>
|
||||
[DataField("zombieShuttleCallPercentage")]
|
||||
[DataField]
|
||||
public float ZombieShuttleCallPercentage = 0.7f;
|
||||
|
||||
/// <summary>
|
||||
/// Have we called the evac shuttle yet?
|
||||
/// </summary>
|
||||
[DataField("shuttleCalled")]
|
||||
public bool ShuttleCalled;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string ZombifySelfActionPrototype = "ActionTurnUndead";
|
||||
[DataField]
|
||||
public EntProtoId ZombifySelfActionPrototype = "ActionTurnUndead";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user