Add active and ended game rule components, generic TryRoundStartAttempt and minPlayers field (#20564)
* Improve active game rule querying, add generic try round start attempt method, move minPlayers to GameRuleComponent * Nukeops todo and cleanup * Remove Active field * Add EndedGameRuleComponent
This commit is contained in:
@@ -94,7 +94,7 @@ public sealed partial class GameTicker
|
|||||||
ruleData ??= EnsureComp<GameRuleComponent>(ruleEntity);
|
ruleData ??= EnsureComp<GameRuleComponent>(ruleEntity);
|
||||||
|
|
||||||
// can't start an already active rule
|
// can't start an already active rule
|
||||||
if (ruleData.Active || ruleData.Ended)
|
if (HasComp<ActiveGameRuleComponent>(ruleEntity) || HasComp<EndedGameRuleComponent>(ruleEntity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
|
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
|
||||||
@@ -103,8 +103,9 @@ public sealed partial class GameTicker
|
|||||||
_allPreviousGameRules.Add((RoundDuration(), id));
|
_allPreviousGameRules.Add((RoundDuration(), id));
|
||||||
_sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}");
|
_sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}");
|
||||||
|
|
||||||
ruleData.Active = true;
|
EnsureComp<ActiveGameRuleComponent>(ruleEntity);
|
||||||
ruleData.ActivatedAt = _gameTiming.CurTime;
|
ruleData.ActivatedAt = _gameTiming.CurTime;
|
||||||
|
|
||||||
var ev = new GameRuleStartedEvent(ruleEntity, id);
|
var ev = new GameRuleStartedEvent(ruleEntity, id);
|
||||||
RaiseLocalEvent(ruleEntity, ref ev, true);
|
RaiseLocalEvent(ruleEntity, ref ev, true);
|
||||||
return true;
|
return true;
|
||||||
@@ -120,14 +121,15 @@ public sealed partial class GameTicker
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// don't end it multiple times
|
// don't end it multiple times
|
||||||
if (ruleData.Ended)
|
if (HasComp<EndedGameRuleComponent>(ruleEntity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
|
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ruleData.Active = false;
|
RemComp<ActiveGameRuleComponent>(ruleEntity);
|
||||||
ruleData.Ended = true;
|
EnsureComp<EndedGameRuleComponent>(ruleEntity);
|
||||||
|
|
||||||
_sawmill.Info($"Ended game rule {ToPrettyString(ruleEntity)}");
|
_sawmill.Info($"Ended game rule {ToPrettyString(ruleEntity)}");
|
||||||
|
|
||||||
var ev = new GameRuleEndedEvent(ruleEntity, id);
|
var ev = new GameRuleEndedEvent(ruleEntity, id);
|
||||||
@@ -137,7 +139,7 @@ public sealed partial class GameTicker
|
|||||||
|
|
||||||
public bool IsGameRuleAdded(EntityUid ruleEntity, GameRuleComponent? component = null)
|
public bool IsGameRuleAdded(EntityUid ruleEntity, GameRuleComponent? component = null)
|
||||||
{
|
{
|
||||||
return Resolve(ruleEntity, ref component) && !component.Ended;
|
return Resolve(ruleEntity, ref component) && !HasComp<EndedGameRuleComponent>(ruleEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsGameRuleAdded(string rule)
|
public bool IsGameRuleAdded(string rule)
|
||||||
@@ -153,7 +155,7 @@ public sealed partial class GameTicker
|
|||||||
|
|
||||||
public bool IsGameRuleActive(EntityUid ruleEntity, GameRuleComponent? component = null)
|
public bool IsGameRuleActive(EntityUid ruleEntity, GameRuleComponent? component = null)
|
||||||
{
|
{
|
||||||
return Resolve(ruleEntity, ref component) && component.Active;
|
return Resolve(ruleEntity, ref component) && HasComp<ActiveGameRuleComponent>(ruleEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsGameRuleActive(string rule)
|
public bool IsGameRuleActive(string rule)
|
||||||
@@ -193,11 +195,10 @@ public sealed partial class GameTicker
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<EntityUid> GetActiveGameRules()
|
public IEnumerable<EntityUid> GetActiveGameRules()
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<GameRuleComponent>();
|
var query = EntityQueryEnumerator<ActiveGameRuleComponent, GameRuleComponent>();
|
||||||
while (query.MoveNext(out var uid, out var ruleData))
|
while (query.MoveNext(out var uid, out _, out _))
|
||||||
{
|
{
|
||||||
if (ruleData.Active)
|
yield return uid;
|
||||||
yield return uid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Content.Server.GameTicking.Rules.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to game rules before <see cref="GameRuleStartedEvent"/> and removed before <see cref="GameRuleEndedEvent"/>.
|
||||||
|
/// Mutually exclusive with <seealso cref="EndedGameRuleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class ActiveGameRuleComponent : Component
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Content.Server.GameTicking.Rules.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to game rules before <see cref="GameRuleEndedEvent"/>.
|
||||||
|
/// Mutually exclusive with <seealso cref="ActiveGameRuleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class EndedGameRuleComponent : Component
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -9,25 +9,17 @@ namespace Content.Server.GameTicking.Rules.Components;
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed partial class GameRuleComponent : Component
|
public sealed partial class GameRuleComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether or not the rule is active.
|
|
||||||
/// Is enabled after <see cref="GameRuleStartedEvent"/> and disabled after <see cref="GameRuleEndedEvent"/>
|
|
||||||
/// </summary>
|
|
||||||
[DataField("active")]
|
|
||||||
public bool Active;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Game time when game rule was activated
|
/// Game time when game rule was activated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("activatedAt", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan ActivatedAt;
|
public TimeSpan ActivatedAt;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not the gamerule finished.
|
/// The minimum amount of players needed for this game rule.
|
||||||
/// Used for tracking whether a non-active gamerule has been started before.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("ended")]
|
[DataField]
|
||||||
public bool Ended;
|
public int MinPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Content.Server.StationEvents.Events;
|
|||||||
using Content.Shared.Dataset;
|
using Content.Shared.Dataset;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
@@ -16,79 +15,80 @@ namespace Content.Server.GameTicking.Rules.Components;
|
|||||||
[RegisterComponent, Access(typeof(NukeopsRuleSystem), typeof(LoneOpsSpawnRule))]
|
[RegisterComponent, Access(typeof(NukeopsRuleSystem), typeof(LoneOpsSpawnRule))]
|
||||||
public sealed partial class NukeopsRuleComponent : Component
|
public sealed partial class NukeopsRuleComponent : Component
|
||||||
{
|
{
|
||||||
|
// TODO Replace with GameRuleComponent.minPlayers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum needed amount of players
|
/// The minimum needed amount of players
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("minPlayers")]
|
[DataField]
|
||||||
public int MinPlayers = 20;
|
public int MinPlayers = 20;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
[DataField("playersPerOperative")]
|
[DataField]
|
||||||
public int PlayersPerOperative = 10;
|
public int PlayersPerOperative = 10;
|
||||||
|
|
||||||
[DataField("maxOps")]
|
[DataField]
|
||||||
public int MaxOperatives = 5;
|
public int MaxOps = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// What will happen if all of the nuclear operatives will die. Used by LoneOpsSpawn event.
|
/// What will happen if all of the nuclear operatives will die. Used by LoneOpsSpawn event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("roundEndBehavior")]
|
[DataField]
|
||||||
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.ShuttleCall;
|
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.ShuttleCall;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text for shuttle call if RoundEndBehavior is ShuttleCall.
|
/// Text for shuttle call if RoundEndBehavior is ShuttleCall.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("roundEndTextSender")]
|
[DataField]
|
||||||
public string RoundEndTextSender = "comms-console-announcement-title-centcom";
|
public string RoundEndTextSender = "comms-console-announcement-title-centcom";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text for shuttle call if RoundEndBehavior is ShuttleCall.
|
/// Text for shuttle call if RoundEndBehavior is ShuttleCall.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("roundEndTextShuttleCall")]
|
[DataField]
|
||||||
public string RoundEndTextShuttleCall = "nuke-ops-no-more-threat-announcement-shuttle-call";
|
public string RoundEndTextShuttleCall = "nuke-ops-no-more-threat-announcement-shuttle-call";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text for announcement if RoundEndBehavior is ShuttleCall. Used if shuttle is already called
|
/// Text for announcement if RoundEndBehavior is ShuttleCall. Used if shuttle is already called
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("roundEndTextAnnouncement")]
|
[DataField]
|
||||||
public string RoundEndTextAnnouncement = "nuke-ops-no-more-threat-announcement";
|
public string RoundEndTextAnnouncement = "nuke-ops-no-more-threat-announcement";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time to emergency shuttle to arrive if RoundEndBehavior is ShuttleCall.
|
/// Time to emergency shuttle to arrive if RoundEndBehavior is ShuttleCall.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("evacShuttleTime")]
|
[DataField]
|
||||||
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(10);
|
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(10);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not to spawn the nuclear operative outpost. Used by LoneOpsSpawn event.
|
/// Whether or not to spawn the nuclear operative outpost. Used by LoneOpsSpawn event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("spawnOutpost")]
|
[DataField]
|
||||||
public bool SpawnOutpost = true;
|
public bool SpawnOutpost = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not nukie left their outpost
|
/// Whether or not nukie left their outpost
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("leftOutpost")]
|
[DataField]
|
||||||
public bool LeftOutpost = false;
|
public bool LeftOutpost;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables opportunity to get extra TC for war declaration
|
/// Enables opportunity to get extra TC for war declaration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("canEnableWarOps")]
|
[DataField]
|
||||||
public bool CanEnableWarOps = true;
|
public bool CanEnableWarOps = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates time when war has been declared, null if not declared
|
/// Indicates time when war has been declared, null if not declared
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("warDeclaredTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan? WarDeclaredTime;
|
public TimeSpan? WarDeclaredTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This amount of TC will be given to each nukie
|
/// This amount of TC will be given to each nukie
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("warTCAmountPerNukie")]
|
[DataField]
|
||||||
public int WarTCAmountPerNukie = 40;
|
public int WarTCAmountPerNukie = 40;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,55 +100,55 @@ public sealed partial class NukeopsRuleComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare
|
/// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("warNukieArriveDelay")]
|
[DataField]
|
||||||
public TimeSpan? WarNukieArriveDelay = TimeSpan.FromMinutes(15);
|
public TimeSpan? WarNukieArriveDelay = TimeSpan.FromMinutes(15);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimal operatives count for war declaration
|
/// Minimal operatives count for war declaration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("warDeclarationMinOps")]
|
[DataField]
|
||||||
public int WarDeclarationMinOps = 4;
|
public int WarDeclarationMinOps = 4;
|
||||||
|
|
||||||
[DataField("spawnPointProto", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string SpawnPointPrototype = "SpawnPointNukies";
|
public EntProtoId SpawnPointProto = "SpawnPointNukies";
|
||||||
|
|
||||||
[DataField("ghostSpawnPointProto", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string GhostSpawnPointProto = "SpawnPointGhostNukeOperative";
|
public EntProtoId GhostSpawnPointProto = "SpawnPointGhostNukeOperative";
|
||||||
|
|
||||||
[DataField("commanderRoleProto", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
[DataField]
|
||||||
public string CommanderRolePrototype = "NukeopsCommander";
|
public ProtoId<AntagPrototype> CommanderRoleProto = "NukeopsCommander";
|
||||||
|
|
||||||
[DataField("operativeRoleProto", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
[DataField]
|
||||||
public string OperativeRoleProto = "Nukeops";
|
public ProtoId<AntagPrototype> OperativeRoleProto = "Nukeops";
|
||||||
|
|
||||||
[DataField("medicRoleProto", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
[DataField]
|
||||||
public string MedicRoleProto = "NukeopsMedic";
|
public ProtoId<AntagPrototype> MedicRoleProto = "NukeopsMedic";
|
||||||
|
|
||||||
[DataField("commanderStartingGearProto", customTypeSerializer: typeof(PrototypeIdSerializer<StartingGearPrototype>))]
|
[DataField]
|
||||||
public string CommanderStartGearPrototype = "SyndicateCommanderGearFull";
|
public ProtoId<StartingGearPrototype> CommanderStartGearProto = "SyndicateCommanderGearFull";
|
||||||
|
|
||||||
[DataField("medicStartGearProto", customTypeSerializer: typeof(PrototypeIdSerializer<StartingGearPrototype>))]
|
[DataField]
|
||||||
public string MedicStartGearPrototype = "SyndicateOperativeMedicFull";
|
public ProtoId<StartingGearPrototype> MedicStartGearProto = "SyndicateOperativeMedicFull";
|
||||||
|
|
||||||
[DataField("operativeStartGearProto", customTypeSerializer: typeof(PrototypeIdSerializer<StartingGearPrototype>))]
|
[DataField]
|
||||||
public string OperativeStartGearPrototype = "SyndicateOperativeGearFull";
|
public ProtoId<StartingGearPrototype> OperativeStartGearProto = "SyndicateOperativeGearFull";
|
||||||
|
|
||||||
[DataField("eliteNames", customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
||||||
public string EliteNames = "SyndicateNamesElite";
|
public string EliteNames = "SyndicateNamesElite";
|
||||||
|
|
||||||
[DataField("normalNames", customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<DatasetPrototype>))]
|
||||||
public string NormalNames = "SyndicateNamesNormal";
|
public string NormalNames = "SyndicateNamesNormal";
|
||||||
|
|
||||||
[DataField("outpostMap", customTypeSerializer: typeof(ResPathSerializer))]
|
[DataField(customTypeSerializer: typeof(ResPathSerializer))]
|
||||||
public ResPath NukieOutpostMap = new("/Maps/nukieplanet.yml");
|
public ResPath OutpostMap = new("/Maps/nukieplanet.yml");
|
||||||
|
|
||||||
[DataField("shuttleMap", customTypeSerializer: typeof(ResPathSerializer))]
|
[DataField(customTypeSerializer: typeof(ResPathSerializer))]
|
||||||
public ResPath NukieShuttleMap = new("/Maps/infiltrator.yml");
|
public ResPath ShuttleMap = new("/Maps/infiltrator.yml");
|
||||||
|
|
||||||
[DataField("winType")]
|
[DataField]
|
||||||
public WinType WinType = WinType.Neutral;
|
public WinType WinType = WinType.Neutral;
|
||||||
|
|
||||||
[DataField("winConditions")]
|
[DataField]
|
||||||
public List<WinCondition> WinConditions = new ();
|
public List<WinCondition> WinConditions = new ();
|
||||||
|
|
||||||
public MapId? NukiePlanet;
|
public MapId? NukiePlanet;
|
||||||
@@ -162,30 +162,30 @@ public sealed partial class NukeopsRuleComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached starting gear prototypes.
|
/// Cached starting gear prototypes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("startingGearPrototypes")]
|
[DataField]
|
||||||
public Dictionary<string, StartingGearPrototype> StartingGearPrototypes = new ();
|
public Dictionary<string, StartingGearPrototype> StartingGearPrototypes = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached operator name prototypes.
|
/// Cached operator name prototypes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("operativeNames")]
|
[DataField]
|
||||||
public Dictionary<string, List<string>> OperativeNames = new();
|
public Dictionary<string, List<string>> OperativeNames = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data to be used in <see cref="OnMindAdded"/> for an operative once the Mind has been added.
|
/// Data to be used in <see cref="OnMindAdded"/> for an operative once the Mind has been added.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("operativeMindPendingData")]
|
[DataField]
|
||||||
public Dictionary<EntityUid, string> OperativeMindPendingData = new();
|
public Dictionary<EntityUid, string> OperativeMindPendingData = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Players who played as an operative at some point in the round.
|
/// Players who played as an operative at some point in the round.
|
||||||
/// Stores the mind as well as the entity name
|
/// Stores the mind as well as the entity name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("operativePlayers")]
|
[DataField]
|
||||||
public Dictionary<string, EntityUid> OperativePlayers = new();
|
public Dictionary<string, EntityUid> OperativePlayers = new();
|
||||||
|
|
||||||
[DataField("faction", customTypeSerializer: typeof(PrototypeIdSerializer<NpcFactionPrototype>), required: true)]
|
[DataField(required: true)]
|
||||||
public string Faction = default!;
|
public ProtoId<NpcFactionPrototype> Faction = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WinType : byte
|
public enum WinType : byte
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
using Content.Server.Chat.Managers;
|
||||||
using Content.Server.GameTicking.Rules.Components;
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.Rules;
|
namespace Content.Server.GameTicking.Rules;
|
||||||
|
|
||||||
public abstract partial class GameRuleSystem<T> : EntitySystem where T : Component
|
public abstract partial class GameRuleSystem<T> : EntitySystem where T : Component
|
||||||
{
|
{
|
||||||
|
[Dependency] protected readonly IChatManager ChatManager = default!;
|
||||||
[Dependency] protected readonly GameTicker GameTicker = default!;
|
[Dependency] protected readonly GameTicker GameTicker = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -36,6 +38,7 @@ public abstract partial class GameRuleSystem<T> : EntitySystem where T : Compone
|
|||||||
Ended(uid, component, ruleData, args);
|
Ended(uid, component, ruleData, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the gamerule is added
|
/// Called when the gamerule is added
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -68,6 +71,36 @@ public abstract partial class GameRuleSystem<T> : EntitySystem where T : Compone
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected EntityQueryEnumerator<ActiveGameRuleComponent, T, GameRuleComponent> QueryActiveRules()
|
||||||
|
{
|
||||||
|
return EntityQueryEnumerator<ActiveGameRuleComponent, T, GameRuleComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool TryRoundStartAttempt(RoundStartAttemptEvent ev, string localizedPresetName)
|
||||||
|
{
|
||||||
|
var query = EntityQueryEnumerator<ActiveGameRuleComponent, T, GameRuleComponent>();
|
||||||
|
while (query.MoveNext(out _, out _, out _, out var gameRule))
|
||||||
|
{
|
||||||
|
var minPlayers = gameRule.MinPlayers;
|
||||||
|
if (!ev.Forced && ev.Players.Length < minPlayers)
|
||||||
|
{
|
||||||
|
ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players",
|
||||||
|
("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers),
|
||||||
|
("presetName", localizedPresetName)));
|
||||||
|
ev.Cancel();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.Players.Length == 0)
|
||||||
|
{
|
||||||
|
ChatManager.DispatchServerAnnouncement(Loc.GetString("preset-no-one-ready"));
|
||||||
|
ev.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !ev.Cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|||||||
@@ -595,7 +595,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
|
|
||||||
// Basically copied verbatim from traitor code
|
// Basically copied verbatim from traitor code
|
||||||
var playersPerOperative = nukeops.PlayersPerOperative;
|
var playersPerOperative = nukeops.PlayersPerOperative;
|
||||||
var maxOperatives = nukeops.MaxOperatives;
|
var maxOperatives = nukeops.MaxOps;
|
||||||
|
|
||||||
// Dear lord what is happening HERE.
|
// Dear lord what is happening HERE.
|
||||||
var everyone = new List<IPlayerSession>(ev.PlayerPool);
|
var everyone = new List<IPlayerSession>(ev.PlayerPool);
|
||||||
@@ -614,15 +614,15 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
}
|
}
|
||||||
|
|
||||||
var profile = ev.Profiles[player.UserId];
|
var profile = ev.Profiles[player.UserId];
|
||||||
if (profile.AntagPreferences.Contains(nukeops.OperativeRoleProto))
|
if (profile.AntagPreferences.Contains(nukeops.OperativeRoleProto.Id))
|
||||||
{
|
{
|
||||||
prefList.Add(player);
|
prefList.Add(player);
|
||||||
}
|
}
|
||||||
if (profile.AntagPreferences.Contains(nukeops.MedicRoleProto))
|
if (profile.AntagPreferences.Contains(nukeops.MedicRoleProto.Id))
|
||||||
{
|
{
|
||||||
medPrefList.Add(player);
|
medPrefList.Add(player);
|
||||||
}
|
}
|
||||||
if (profile.AntagPreferences.Contains(nukeops.CommanderRolePrototype))
|
if (profile.AntagPreferences.Contains(nukeops.CommanderRoleProto.Id))
|
||||||
{
|
{
|
||||||
cmdrPrefList.Add(player);
|
cmdrPrefList.Add(player);
|
||||||
}
|
}
|
||||||
@@ -808,8 +808,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
if (!component.SpawnOutpost)
|
if (!component.SpawnOutpost)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var path = component.NukieOutpostMap;
|
var path = component.OutpostMap;
|
||||||
var shuttlePath = component.NukieShuttleMap;
|
var shuttlePath = component.ShuttleMap;
|
||||||
|
|
||||||
var mapId = _mapManager.CreateMap();
|
var mapId = _mapManager.CreateMap();
|
||||||
var options = new MapLoadOptions
|
var options = new MapLoadOptions
|
||||||
@@ -866,18 +866,18 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
name = Loc.GetString("nukeops-role-commander") + " " + _random.PickAndTake(component.OperativeNames[component.EliteNames]);
|
name = Loc.GetString("nukeops-role-commander") + " " + _random.PickAndTake(component.OperativeNames[component.EliteNames]);
|
||||||
role = component.CommanderRolePrototype;
|
role = component.CommanderRoleProto;
|
||||||
gear = component.CommanderStartGearPrototype;
|
gear = component.CommanderStartGearProto;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
name = Loc.GetString("nukeops-role-agent") + " " + _random.PickAndTake(component.OperativeNames[component.NormalNames]);
|
name = Loc.GetString("nukeops-role-agent") + " " + _random.PickAndTake(component.OperativeNames[component.NormalNames]);
|
||||||
role = component.MedicRoleProto;
|
role = component.MedicRoleProto;
|
||||||
gear = component.MedicStartGearPrototype;
|
gear = component.MedicStartGearProto;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
name = Loc.GetString("nukeops-role-operator") + " " + _random.PickAndTake(component.OperativeNames[component.NormalNames]);
|
name = Loc.GetString("nukeops-role-operator") + " " + _random.PickAndTake(component.OperativeNames[component.NormalNames]);
|
||||||
role = component.OperativeRoleProto;
|
role = component.OperativeRoleProto;
|
||||||
gear = component.OperativeStartGearPrototype;
|
gear = component.OperativeStartGearProto;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,7 +915,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
// Forgive me for hardcoding prototypes
|
// Forgive me for hardcoding prototypes
|
||||||
foreach (var (_, meta, xform) in EntityQuery<SpawnPointComponent, MetaDataComponent, TransformComponent>(true))
|
foreach (var (_, meta, xform) in EntityQuery<SpawnPointComponent, MetaDataComponent, TransformComponent>(true))
|
||||||
{
|
{
|
||||||
if (meta.EntityPrototype?.ID != component.SpawnPointPrototype)
|
if (meta.EntityPrototype?.ID != component.SpawnPointProto.Id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (xform.ParentUid != component.NukieOutpost)
|
if (xform.ParentUid != component.NukieOutpost)
|
||||||
@@ -981,7 +981,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
}
|
}
|
||||||
// Basically copied verbatim from traitor code
|
// Basically copied verbatim from traitor code
|
||||||
var playersPerOperative = component.PlayersPerOperative;
|
var playersPerOperative = component.PlayersPerOperative;
|
||||||
var maxOperatives = component.MaxOperatives;
|
var maxOperatives = component.MaxOps;
|
||||||
|
|
||||||
var playerPool = _playerManager.ServerSessions.ToList();
|
var playerPool = _playerManager.ServerSessions.ToList();
|
||||||
var numNukies = MathHelper.Clamp(playerPool.Count / playersPerOperative, 1, maxOperatives);
|
var numNukies = MathHelper.Clamp(playerPool.Count / playersPerOperative, 1, maxOperatives);
|
||||||
@@ -1115,9 +1115,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||||||
// TODO: Loot table or something
|
// TODO: Loot table or something
|
||||||
foreach (var proto in new[]
|
foreach (var proto in new[]
|
||||||
{
|
{
|
||||||
component.CommanderStartGearPrototype,
|
component.CommanderStartGearProto,
|
||||||
component.MedicStartGearPrototype,
|
component.MedicStartGearProto,
|
||||||
component.OperativeStartGearPrototype
|
component.OperativeStartGearProto
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
component.StartingGearPrototypes.Add(proto, _prototypeManager.Index<StartingGearPrototype>(proto));
|
component.StartingGearPrototypes.Add(proto, _prototypeManager.Index<StartingGearPrototype>(proto));
|
||||||
|
|||||||
Reference in New Issue
Block a user