From 340332cf5b473543a48487efc5d68dded394f375 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 1 Jul 2024 18:23:36 -0400 Subject: [PATCH] Fix nukeops ending early if an operative dies at base (#29642) Fix nukeops ending early if an operative died at base --- .../Tests/GameRules/NukeOpsTest.cs | 46 ++++++++++++++----- .../GameTicking/Rules/NukeopsRuleSystem.cs | 24 +++++----- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index fc50d0bd33..7b3c994302 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -7,6 +7,7 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; using Content.Server.Pinpointer; using Content.Server.Roles; +using Content.Server.RoundEnd; using Content.Server.Shuttles.Components; using Content.Server.Station.Components; using Content.Shared.CCVar; @@ -49,6 +50,7 @@ public sealed class NukeOpsTest var roleSys = server.System(); var invSys = server.System(); var factionSys = server.System(); + var roundEndSys = server.System(); server.CfgMan.SetCVar(CCVars.GridFill, true); @@ -63,11 +65,11 @@ public sealed class NukeOpsTest // Opt into the nukies role. await pair.SetAntagPreference("NukeopsCommander", true); - await pair.SetAntagPreference( "NukeopsMedic", true, dummies[1].UserId); + await pair.SetAntagPreference("NukeopsMedic", true, dummies[1].UserId); // Initially, the players have no attached entities Assert.That(pair.Player?.AttachedEntity, Is.Null); - Assert.That(dummies.All(x => x.AttachedEntity == null)); + Assert.That(dummies.All(x => x.AttachedEntity == null)); // There are no grids or maps Assert.That(entMan.Count(), Is.Zero); @@ -150,7 +152,8 @@ public sealed class NukeOpsTest } // The game rule exists, and all the stations/shuttles/maps are properly initialized - var rule = entMan.AllComponents().Single().Component; + var rule = entMan.AllComponents().Single(); + var ruleComp = rule.Component; var gridsRule = entMan.AllComponents().Single().Component; foreach (var grid in gridsRule.MapGrids) { @@ -158,12 +161,14 @@ public sealed class NukeOpsTest Assert.That(entMan.HasComponent(grid)); Assert.That(entMan.HasComponent(grid)); } - Assert.That(entMan.EntityExists(rule.TargetStation)); + Assert.That(entMan.EntityExists(ruleComp.TargetStation)); - Assert.That(entMan.HasComponent(rule.TargetStation)); + Assert.That(entMan.HasComponent(ruleComp.TargetStation)); - var nukieShuttlEnt = entMan.AllComponents().FirstOrDefault().Uid; + var nukieShuttle = entMan.AllComponents().Single(); + var nukieShuttlEnt = nukieShuttle.Uid; Assert.That(entMan.EntityExists(nukieShuttlEnt)); + Assert.That(nukieShuttle.Component.AssociatedRule, Is.EqualTo(rule.Uid)); EntityUid? nukieStationEnt = null; foreach (var grid in gridsRule.MapGrids) @@ -179,12 +184,12 @@ public sealed class NukeOpsTest var nukieStation = entMan.GetComponent(nukieStationEnt!.Value); Assert.That(entMan.EntityExists(nukieStation.Station)); - Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); + Assert.That(nukieStation.Station, Is.Not.EqualTo(ruleComp.TargetStation)); Assert.That(server.MapMan.MapExists(gridsRule.Map)); var nukieMap = mapSys.GetMap(gridsRule.Map!.Value); - var targetStation = entMan.GetComponent(rule.TargetStation!.Value); + var targetStation = entMan.GetComponent(ruleComp.TargetStation!.Value); var targetGrid = targetStation.Grids.First(); var targetMap = entMan.GetComponent(targetGrid).MapUid!.Value; Assert.That(targetMap, Is.Not.EqualTo(nukieMap)); @@ -206,7 +211,7 @@ public sealed class NukeOpsTest Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized)); Assert.That(LifeStage(nukieStationEnt.Value), Is.GreaterThan(EntityLifeStage.Initialized)); Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized)); + Assert.That(LifeStage(ruleComp.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized)); // Make sure the player has hands. We've had fucking disarmed nukies before. Assert.That(entMan.HasComponent(player)); @@ -222,10 +227,10 @@ public sealed class NukeOpsTest } Assert.That(total, Is.GreaterThan(3)); - // Finally lets check the nukie commander passed basic training and figured out how to breathe. + // Check the nukie commander passed basic training and figured out how to breathe. var totalSeconds = 30; var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds); - int increment = 5; + var increment = 5; var resp = entMan.GetComponent(player); var damage = entMan.GetComponent(player); for (var tick = 0; tick < totalTicks; tick += increment) @@ -235,7 +240,24 @@ public sealed class NukeOpsTest Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); } - ticker.SetGamePreset((GamePresetPrototype?)null); + // Check that the round does not end prematurely when agents are deleted in the outpost + var nukies = dummyEnts.Where(entMan.HasComponent).Append(player).ToArray(); + await server.WaitAssertion(() => + { + for (var i = 0; i < nukies.Length - 1; i++) + { + entMan.DeleteEntity(nukies[i]); + Assert.That(roundEndSys.IsRoundEndRequested, Is.False, + $"The round ended, but {nukies.Length - i - 1} nukies are still alive!"); + } + // Delete the last nukie and make sure the round ends. + entMan.DeleteEntity(nukies[^1]); + + Assert.That(roundEndSys.IsRoundEndRequested, + "All nukies were deleted, but the round didn't end!"); + }); + + ticker.SetGamePreset((GamePresetPrototype?) null); await pair.CleanReturnAsync(); } } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 5c3459ef81..2a67e62e3f 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -57,13 +57,12 @@ public sealed class NukeopsRuleSystem : GameRuleSystem SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnOperativeZombified); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShuttleFTLAttempt); SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); SubscribeLocalEvent(OnAfterAntagEntSelected); + SubscribeLocalEvent(OnRuleLoadedGrids); } protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, @@ -256,17 +255,18 @@ public sealed class NukeopsRuleSystem : GameRuleSystem RemCompDeferred(uid, component); } - private void OnMapInit(Entity ent, ref MapInitEvent args) + private void OnRuleLoadedGrids(Entity ent, ref RuleLoadedGridsEvent args) { - var map = Transform(ent).MapID; - - var rules = EntityQueryEnumerator(); - while (rules.MoveNext(out var uid, out _, out var grids)) + // Check each nukie shuttle + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var shuttle)) { - if (map != grids.Map) - continue; - ent.Comp.AssociatedRule = uid; - break; + // Check if the shuttle's mapID is the one that just got loaded for this rule + if (Transform(uid).MapID == args.Map) + { + shuttle.AssociatedRule = ent; + break; + } } } @@ -376,7 +376,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (Transform(uid).MapID != Transform(outpost).MapID) // Will receive bonus TC only on their start outpost continue; - _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.Comp.WarTcAmountPerNukie } }, uid, component); + _store.TryAddCurrency(new() { { TelecrystalCurrencyPrototype, nukieRule.Comp.WarTcAmountPerNukie } }, uid, component); var msg = Loc.GetString("store-currency-war-boost-given", ("target", uid)); _popupSystem.PopupEntity(msg, uid); -- 2.51.2