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:
DrSmugleaf
2023-09-28 07:48:59 -07:00
committed by GitHub
parent 78091fb11d
commit 2f4b2cc887
7 changed files with 132 additions and 86 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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