From 621e102c93aed90afce6ac719286a1f0fbd6eec9 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 16 Sep 2023 12:48:42 +0100 Subject: [PATCH] refactor nukeops rule a bit (#19792) Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Rules/Components/NukeopsRuleComponent.cs | 5 +- .../GameTicking/Rules/NukeopsRuleSystem.cs | 134 ++++++++++-------- .../game-presets/preset-nukeops.ftl | 3 +- 3 files changed, 81 insertions(+), 61 deletions(-) diff --git a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs index 8795bf1950..760b684e1a 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs @@ -154,11 +154,10 @@ public sealed partial class NukeopsRuleComponent : Component /// /// Players who played as an operative at some point in the round. - /// Stores the session as well as the entity name + /// Stores the mind as well as the entity name /// - /// todo: don't store sessions, dingus [DataField("operativePlayers")] - public Dictionary OperativePlayers = new(); + public Dictionary OperativePlayers = new(); [DataField("faction", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] public string Faction = default!; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 20d0871448..4787b7d7dc 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -55,29 +55,29 @@ namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IServerPreferencesManager _prefs = default!; - [Dependency] private readonly IChatManager _chatManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IPlayerManager _playerSystem = default!; - [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; - [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!; - [Dependency] private readonly StationSpawningSystem _stationSpawningSystem = default!; - [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly MapLoaderSystem _map = default!; - [Dependency] private readonly ShuttleSystem _shuttle = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - [Dependency] private readonly StoreSystem _storeSystem = default!; - [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly WarDeclaratorSystem _warDeclaratorSystem = default!; + [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly ShuttleSystem _shuttle = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; + [Dependency] private readonly StoreSystem _store = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly WarDeclaratorSystem _warDeclarator = default!; [ValidatePrototypeId] private const string TelecrystalCurrencyPrototype = "Telecrystal"; @@ -122,7 +122,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (!GameTicker.IsGameRuleAdded(ruleEnt, gameRule)) continue; - var found = nukeops.OperativePlayers.Values.Any(v => v.AttachedEntity == opUid); + var found = nukeops.OperativePlayers.Values.Any(v => v == opUid); if (found) { comps = (nukeops, gameRule); @@ -193,9 +193,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem var nukieRule = comps.Value.Item1; nukieRule.WarDeclaredTime = _gameTiming.CurTime; - _chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: announcementSound, colorOverride: colorOverride); + _chat.DispatchGlobalAnnouncement(msg, title, announcementSound: announcementSound, colorOverride: colorOverride); DistributeExtraTC(nukieRule); - _warDeclaratorSystem.RefreshAllUI(comps.Value.Item1, comps.Value.Item2); + _warDeclarator.RefreshAllUI(comps.Value.Item1, comps.Value.Item2); } private void DistributeExtraTC(NukeopsRuleComponent nukieRule) @@ -212,7 +212,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (Transform(uid).MapID != Transform(nukieRule.NukieOutpost.Value).MapID) // Will receive bonus TC only on their start outpost continue; - _storeSystem.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.WarTCAmountPerNukie } }, uid, component); + _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.WarTCAmountPerNukie } }, uid, component); var msg = Loc.GetString("store-currency-war-boost-given", ("target", uid)); _popupSystem.PopupEntity(msg, uid); @@ -228,13 +228,11 @@ public sealed class NukeopsRuleSystem : GameRuleSystem continue; // If entity has a prior mind attached, add them to the players list. - if (!_mindSystem.TryGetMind(uid, out _, out var mind)) + if (!_mind.TryGetMind(uid, out var mind, out _)) continue; - var session = mind?.Session; var name = MetaData(uid).EntityName; - if (session != null) - nukeops.OperativePlayers.Add(name, session); + nukeops.OperativePlayers.Add(name, mind); } } @@ -345,7 +343,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem while (query.MoveNext(out _, out var nukeops, out var actor)) { _chatManager.DispatchServerMessage(actor.PlayerSession, Loc.GetString("nukeops-welcome", ("station", component.TargetStation.Value))); - _audioSystem.PlayGlobal(nukeops.GreetSoundNotification, actor.PlayerSession); + _audio.PlayGlobal(nukeops.GreetSoundNotification, actor.PlayerSession); filter.AddPlayer(actor.PlayerSession); } } @@ -393,14 +391,28 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } var allAlive = true; - foreach (var (_, state) in EntityQuery()) + var mindQuery = GetEntityQuery(); + var mobStateQuery = GetEntityQuery(); + foreach (var (_, mindId) in component.OperativePlayers) { - if (state.CurrentState is MobState.Alive) + // mind got deleted somehow so ignore it + if (!mindQuery.TryGetComponent(mindId, out var mind)) continue; + // check if player got gibbed or ghosted or something - count as dead + if (mind.OwnedEntity != null && + // if the player somehow isn't a mob anymore that also counts as dead + mobStateQuery.TryGetComponent(mind.OwnedEntity.Value, out var mobState) && + // have to be alive, not crit or dead + mobState.CurrentState is MobState.Alive) + { + continue; + } + allAlive = false; break; } + // If all nuke ops were alive at the end of the round, // the nuke ops win. This is to prevent people from // running away the moment nuke ops appear. @@ -443,6 +455,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem private void OnRoundEndText(RoundEndTextAppendEvent ev) { + var mindQuery = GetEntityQuery(); foreach (var nukeops in EntityQuery()) { var winText = Loc.GetString($"nukeops-{nukeops.WinType.ToString().ToLower()}"); @@ -457,10 +470,16 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } ev.AddLine(Loc.GetString("nukeops-list-start")); - foreach (var (name, session) in nukeops.OperativePlayers) + foreach (var (name, mindId) in nukeops.OperativePlayers) { - var listing = Loc.GetString("nukeops-list-name", ("name", name), ("user", session.Name)); - ev.AddLine(listing); + if (mindQuery.TryGetComponent(mindId, out var mind) && mind.Session != null) + { + ev.AddLine(Loc.GetString("nukeops-list-name-user", ("name", name), ("user", mind.Session.Name))); + } + else + { + ev.AddLine(Loc.GetString("nukeops-list-name", ("name", name))); + } } } } @@ -548,7 +567,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem private void OnMobStateChanged(EntityUid uid, NukeOperativeComponent component, MobStateChangedEvent ev) { - if(ev.NewMobState == MobState.Dead) + if (ev.NewMobState == MobState.Dead) CheckRoundShouldEnd(); } @@ -601,7 +620,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } } - var numNukies = MathHelper.Clamp(_playerSystem.PlayerCount / playersPerOperative, 1, maxOperatives); + var numNukies = MathHelper.Clamp(_playerManager.PlayerCount / playersPerOperative, 1, maxOperatives); for (var i = 0; i < numNukies; i++) { @@ -693,11 +712,14 @@ public sealed class NukeopsRuleSystem : GameRuleSystem { ev.PlayerPool.Remove(session); GameTicker.PlayerJoinGame(session); + + if (!_mind.TryGetMind(session, out var mind, out _)) + continue; + var name = session.AttachedEntity == null ? string.Empty - : MetaData(session.AttachedEntity.Value).EntityName; - // TODO: Fix this being able to have duplicates - nukeops.OperativePlayers[name] = session; + : Name(session.AttachedEntity.Value); + nukeops.OperativePlayers[name] = mind; } } } @@ -733,7 +755,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args) { - if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + if (!_mind.TryGetMind(uid, out var mindId, out var mind)) return; foreach (var (nukeops, gameRule) in EntityQuery()) @@ -748,13 +770,11 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (mind.Session is not { } playerSession) return; - if (nukeops.OperativePlayers.ContainsValue(playerSession)) + if (nukeops.OperativePlayers.ContainsValue(mindId)) return; - var name = MetaData(uid).EntityName; - - nukeops.OperativePlayers.Add(name, playerSession); - _warDeclaratorSystem.RefreshAllUI(nukeops, gameRule); + nukeops.OperativePlayers.Add(Name(uid), mindId); + _warDeclarator.RefreshAllUI(nukeops, gameRule); if (GameTicker.RunLevel != GameRunLevel.InRound) return; @@ -764,7 +784,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem _chatManager.DispatchServerMessage(playerSession, Loc.GetString("nukeops-welcome", ("station", nukeops.TargetStation.Value))); // Notificate player about new role assignment - _audioSystem.PlayGlobal(component.GreetSoundNotification, playerSession); + _audio.PlayGlobal(component.GreetSoundNotification, playerSession); } } } @@ -866,11 +886,11 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (profile != null) { - _humanoidSystem.LoadProfile(mob, profile); + _humanoid.LoadProfile(mob, profile); } if (component.StartingGearPrototypes.TryGetValue(gear, out var gearPrototype)) - _stationSpawningSystem.EquipStartingGear(mob, gearPrototype, profile); + _stationSpawning.EquipStartingGear(mob, gearPrototype, profile); _npcFaction.RemoveFaction(mob, "NanoTrasen", false); _npcFaction.AddFaction(mob, "Syndicate"); @@ -885,7 +905,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem var spawns = new List(); // Forgive me for hardcoding prototypes - foreach (var (_, meta, xform) in EntityManager.EntityQuery(true)) + foreach (var (_, meta, xform) in EntityQuery(true)) { if (meta.EntityPrototype?.ID != component.SpawnPointPrototype) continue; @@ -899,7 +919,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (spawns.Count == 0) { - spawns.Add(EntityManager.GetComponent(outpostUid).Coordinates); + spawns.Add(Transform(outpostUid).Coordinates); Logger.WarningS("nukies", $"Fell back to default spawn for nukies!"); } @@ -917,17 +937,17 @@ public sealed class NukeopsRuleSystem : GameRuleSystem species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); } - var mob = EntityManager.SpawnEntity(species.Prototype, _random.Pick(spawns)); + var mob = Spawn(species.Prototype, _random.Pick(spawns)); SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile, component); - var newMind = _mindSystem.CreateMind(session.UserId, spawnDetails.Name); - _mindSystem.SetUserId(newMind, session.UserId); + var newMind = _mind.CreateMind(session.UserId, spawnDetails.Name); + _mind.SetUserId(newMind, session.UserId); _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = spawnDetails.Role }); - _mindSystem.TransferTo(newMind, mob); + _mind.TransferTo(newMind, mob); } else if (addSpawnPoints) { - var spawnPoint = EntityManager.SpawnEntity(component.GhostSpawnPointProto, _random.Pick(spawns)); + var spawnPoint = Spawn(component.GhostSpawnPointProto, _random.Pick(spawns)); var ghostRole = EnsureComp(spawnPoint); EnsureComp(spawnPoint); ghostRole.RoleName = Loc.GetString(nukeOpsAntag.Name); @@ -955,7 +975,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem var playersPerOperative = component.PlayersPerOperative; var maxOperatives = component.MaxOperatives; - var playerPool = _playerSystem.ServerSessions.ToList(); + var playerPool = _playerManager.ServerSessions.ToList(); var numNukies = MathHelper.Clamp(playerPool.Count / playersPerOperative, 1, maxOperatives); var operatives = new List(); @@ -1050,7 +1070,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem nukeops.LeftOutpost = true; if (TryGetRuleFromGrid(gridUid.Value, out var comps)) - _warDeclaratorSystem.RefreshAllUI(comps.Value.Item1, comps.Value.Item2); + _warDeclarator.RefreshAllUI(comps.Value.Item1, comps.Value.Item2); } } } @@ -1096,13 +1116,13 @@ public sealed class NukeopsRuleSystem : GameRuleSystem var query = EntityQuery(true); foreach (var (_, mindComp, metaData) in query) { - if (!mindComp.HasMind || !_mindSystem.TryGetSession(mindComp.Mind.Value, out var session)) + if (!mindComp.HasMind) continue; - component.OperativePlayers.Add(metaData.EntityName, session); + + component.OperativePlayers.Add(metaData.EntityName, mindComp.Mind.Value); } if (GameTicker.RunLevel == GameRunLevel.InRound) SpawnOperativesForGhostRoles(uid, component); } - } diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl index 09ee01a8f4..6864688157 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl @@ -24,7 +24,8 @@ nukeops-cond-somenukiesalive = Some nuclear operatives died. nukeops-cond-allnukiesalive = No nuclear operatives died. nukeops-list-start = The operatives were: -nukeops-list-name = - [color=White]{$name}[/color] ([color=gray]{$user}[/color]) +nukeops-list-name = - [color=White]{$name}[/color] +nukeops-list-name-user = - [color=White]{$name}[/color] ([color=gray]{$user}[/color]) nukeops-not-enough-ready-players = Not enough players readied up for the game! There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start Nukeops. nukeops-no-one-ready = No players readied up! Can't start Nukeops.