From 9244abe669763d01e2959e512cec4e952e8d164b Mon Sep 17 00:00:00 2001 From: Dev Date: Thu, 18 Jan 2024 14:35:59 +0000 Subject: [PATCH] Add experimental patch to make PMCs spawn at pmc spawn positions --- project/Aki.Debugging/Aki.Debugging.csproj | 1 + project/Aki.Debugging/AkiDebuggingPlugin.cs | 2 + .../Patches/PMCBotSpawnLocationPatch.cs | 101 ++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 project/Aki.Debugging/Patches/PMCBotSpawnLocationPatch.cs diff --git a/project/Aki.Debugging/Aki.Debugging.csproj b/project/Aki.Debugging/Aki.Debugging.csproj index d422fc3..9b822fc 100644 --- a/project/Aki.Debugging/Aki.Debugging.csproj +++ b/project/Aki.Debugging/Aki.Debugging.csproj @@ -28,6 +28,7 @@ + diff --git a/project/Aki.Debugging/AkiDebuggingPlugin.cs b/project/Aki.Debugging/AkiDebuggingPlugin.cs index b1f67e8..5d323a1 100644 --- a/project/Aki.Debugging/AkiDebuggingPlugin.cs +++ b/project/Aki.Debugging/AkiDebuggingPlugin.cs @@ -35,6 +35,8 @@ namespace Aki.Debugging // Debug command patches, can be disabled later new BTRDebugCommandPatch().Enable(); new BTRDebugDataPatch().Enable(); + + new PMCBotSpawnLocationPatch().Enable(); } catch (Exception ex) { diff --git a/project/Aki.Debugging/Patches/PMCBotSpawnLocationPatch.cs b/project/Aki.Debugging/Patches/PMCBotSpawnLocationPatch.cs new file mode 100644 index 0000000..c999752 --- /dev/null +++ b/project/Aki.Debugging/Patches/PMCBotSpawnLocationPatch.cs @@ -0,0 +1,101 @@ +using System.Linq; +using System.Reflection; +using Aki.Reflection.Patching; +using EFT; +using EFT.UI; +using HarmonyLib; +using System.Collections.Generic; +using EFT.Game.Spawning; +using System; +using Aki.PrePatch; + +namespace Aki.Debugging.Patches +{ + + // TODO: Instantiation of this is fairly slow, need to find best way to cache it + public class SptSpawnHelper + { + private readonly List playerSpawnPoints; + private readonly Random _rnd = new Random(); + private readonly GStruct379 _spawnSettings = new GStruct379(); + + public SptSpawnHelper() + { + IEnumerable locationSpawnPoints = GClass2922.CreateFromScene(); + + var playerSpawns = locationSpawnPoints.Where(x => x.Categories.HasFlag(ESpawnCategoryMask.Player)).ToList(); + this.playerSpawnPoints = locationSpawnPoints.Where(x => x.Categories.HasFlag(ESpawnCategoryMask.Player)).ToList(); + } + + public void PrintSpawnPoints() + { + foreach (var spawnPoint in playerSpawnPoints) + { + ConsoleScreen.Log("[AKI PMC Bot spawn] Spawn point " + spawnPoint.Id + " location is " + spawnPoint.Position.ToString()); + } + } + + public ISpawnPoint SelectSpawnPoint() + { + // TODO: Select spawn points more intelligently + return this.playerSpawnPoints[_rnd.Next(this.playerSpawnPoints.Count)]; + } + + public List SelectSpawnPoints(int count) + { + // TODO: Fine-grained spawn selection + if (count > this.playerSpawnPoints.Count()) + { + ConsoleScreen.Log($"[AKI PMC Bot spawn] Wanted ${count} but only {this.playerSpawnPoints.Count()} found, returning all"); + return this.playerSpawnPoints; + } + return this.playerSpawnPoints.OrderBy(x => _rnd.Next()).Take(count).ToList(); + } + } + + + public class PMCBotSpawnLocationPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(BotSpawner), "TryToSpawnInZoneInner"); + } + + [PatchPrefix] + public static bool PatchPrefix(GClass1468 __instance, GClass588 data) + { + + var firstBotRole = data.Profiles[0].Info.Settings.Role; + if ((int)firstBotRole != AkiBotsPrePatcher.sptBearValue || (int)firstBotRole != AkiBotsPrePatcher.sptUsecValue) + { + ConsoleScreen.Log("[AKI PMC Bot spawn] Spawning a set of Scavs. Skipping..."); + return true; + } + + var helper = new SptSpawnHelper(); + var newSpawns = helper.SelectSpawnPoints(data.Count); + + for (int i = 0; i < data.Count; i++) + { + ConsoleScreen.Log($"[AKI PMC Bot spawn] Trying to spawn bot {i}"); + var currentSpawnData = data.Separate(1); + + // Unset group settings + // TODO: Allow for PMC bot groups? + currentSpawnData.SpawnParams.ShallBeGroup = null; + var spawnPointDetails = newSpawns[i]; + var currentZone = __instance.GetClosestZone(spawnPointDetails.Position, out float _); + + // CorePointId of player spawns seems to always be 0. Bots will not activate properly if this ID is used + // TODO: Verify if CorePointId of 1 is acceptable in all cases + + ConsoleScreen.Log($"[AKI PMC Bot spawn] spawn point chosen: {spawnPointDetails.Name} Core point id was: {spawnPointDetails.CorePointId}"); + currentSpawnData.AddPosition(spawnPointDetails.Position, spawnPointDetails.CorePointId); + + __instance.SpawnBotsInZoneOnPositions(newSpawns.GetRange(i, 1), currentZone, currentSpawnData); + } + + return false; + } + } +} \ No newline at end of file