diff --git a/README.md b/README.md index 1409165..c929865 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ git config --local user.email "USERNAME@SOMETHING.com" ## Requirements -- Escape From Tarkov 23043 +- Escape From Tarkov 24605 - BepInEx 5.4.19 - Visual Studio Code - .NET 6 SDK diff --git a/project/Aki.Core/Patches/TransportPrefixPatch.cs b/project/Aki.Core/Patches/TransportPrefixPatch.cs index 0f83261..2d2fc0a 100644 --- a/project/Aki.Core/Patches/TransportPrefixPatch.cs +++ b/project/Aki.Core/Patches/TransportPrefixPatch.cs @@ -15,7 +15,7 @@ namespace Aki.Core.Patches { try { - var type = PatchConstants.EftTypes.Single(t => t.Name == "Class229"); + var type = PatchConstants.EftTypes.Single(t => t.Name == "Class226"); var value = Traverse.Create(type).Field("TransportPrefixes").GetValue>(); value[ETransportProtocolType.HTTPS] = "http://"; value[ETransportProtocolType.WSS] = "ws://"; @@ -34,7 +34,7 @@ namespace Aki.Core.Patches } [PatchPrefix] - private static bool PatchPrefix(ref GStruct22 legacyParams) + private static bool PatchPrefix(ref GStruct21 legacyParams) { //Console.WriteLine($"Original url {legacyParams.Url}"); legacyParams.Url = legacyParams.Url diff --git a/project/Aki.Custom/Airdrops/Utils/AirdropUtil.cs b/project/Aki.Custom/Airdrops/Utils/AirdropUtil.cs index fbe3e98..4efc73b 100644 --- a/project/Aki.Custom/Airdrops/Utils/AirdropUtil.cs +++ b/project/Aki.Custom/Airdrops/Utils/AirdropUtil.cs @@ -27,52 +27,57 @@ namespace Aki.Custom.Airdrops.Utils return 100; } - string location = gameWorld.RegisteredPlayers[0].Location; + // Get players current location + string playerLocation = gameWorld.MainPlayer.Location; - int result = 25; - switch (location.ToLower()) + int result = 0; + switch (playerLocation.ToLower()) { case "bigmap": - { - result = config.AirdropChancePercent.Bigmap; - break; - } + { + result = config.AirdropChancePercent.Bigmap; + break; + } case "interchange": - { - result = config.AirdropChancePercent.Interchange; - break; - } + { + result = config.AirdropChancePercent.Interchange; + break; + } case "rezervbase": - { - result = config.AirdropChancePercent.Reserve; - break; - } + { + result = config.AirdropChancePercent.Reserve; + break; + } case "shoreline": - { - result = config.AirdropChancePercent.Shoreline; - break; - } + { + result = config.AirdropChancePercent.Shoreline; + break; + } case "woods": - { - result = config.AirdropChancePercent.Woods; - break; - } + { + result = config.AirdropChancePercent.Woods; + break; + } case "lighthouse": - { - result = config.AirdropChancePercent.Lighthouse; - break; - } + { + result = config.AirdropChancePercent.Lighthouse; + break; + } case "tarkovstreets": - { - result = config.AirdropChancePercent.TarkovStreets; + { + result = config.AirdropChancePercent.TarkovStreets; + break; + } + default: + Debug.LogError($"[AKI-AIRDROPS]: Map with name {playerLocation} not handled, defaulting spawn chance to 25%"); + result = 25; break; - } } return result; } - public static bool ShouldAirdropOccur(int dropChance, List airdropPoints) + private static bool ShouldAirdropOccur(int dropChance, List airdropPoints) { return airdropPoints.Count > 0 && Random.Range(0, 100) <= dropChance; } @@ -81,15 +86,16 @@ namespace Aki.Custom.Airdrops.Utils { var serverConfig = GetConfigFromServer(); var allAirdropPoints = LocationScene.GetAll().ToList(); - var playerPosition = gameWorld.RegisteredPlayers[0].Position; + var playerPosition = gameWorld.MainPlayer.Position; var flareAirdropPoints = new List(); var dropChance = ChanceToSpawn(gameWorld, serverConfig, isFlare); + var flareSpawnRadiusDistance = 100f; if (isFlare && allAirdropPoints.Count > 0) { foreach (AirdropPoint point in allAirdropPoints) { - if (Vector3.Distance(playerPosition, point.transform.position) <= 100f) + if (Vector3.Distance(playerPosition, point.transform.position) <= flareSpawnRadiusDistance) { flareAirdropPoints.Add(point); } @@ -98,7 +104,7 @@ namespace Aki.Custom.Airdrops.Utils if (flareAirdropPoints.Count == 0 && isFlare) { - Debug.LogError($"[AKI-AIRDROPS]: Airdrop called in by flare, Unable to find an airdropPoint within 100m, defaulting to normal drop"); + Debug.LogError($"[AKI-AIRDROPS]: Airdrop called in by flare, Unable to find an airdropPoint within {flareSpawnRadiusDistance}m, defaulting to normal drop"); flareAirdropPoints.Add(allAirdropPoints.OrderBy(_ => Guid.NewGuid()).FirstOrDefault()); } diff --git a/project/Aki.Custom/AkiCustomPlugin.cs b/project/Aki.Custom/AkiCustomPlugin.cs index 50092bd..d3fa849 100644 --- a/project/Aki.Custom/AkiCustomPlugin.cs +++ b/project/Aki.Custom/AkiCustomPlugin.cs @@ -25,7 +25,8 @@ namespace Aki.Custom new BotDifficultyPatch().Enable(); new CoreDifficultyPatch().Enable(); new OfflineRaidMenuPatch().Enable(); - new RaidSettingsWindowPatch().Enable(); + // Fixed in live, no need for patch + //new RaidSettingsWindowPatch().Enable(); new OfflineRaidSettingsMenuPatch().Enable(); new SessionIdPatch().Enable(); new VersionLabelPatch().Enable(); diff --git a/project/Aki.Custom/Patches/CustomAiPatch.cs b/project/Aki.Custom/Patches/CustomAiPatch.cs index 29d1b89..b6aba2c 100644 --- a/project/Aki.Custom/Patches/CustomAiPatch.cs +++ b/project/Aki.Custom/Patches/CustomAiPatch.cs @@ -98,7 +98,7 @@ namespace Aki.Custom.Patches { var gameWorld = Singleton.Instance; - return gameWorld.RegisteredPlayers[0].Location; + return gameWorld.MainPlayer.Location; } private static bool CacheIsStale() diff --git a/project/Aki.Custom/Patches/EasyAssetsPatch.cs b/project/Aki.Custom/Patches/EasyAssetsPatch.cs index 0597b36..9f45526 100644 --- a/project/Aki.Custom/Patches/EasyAssetsPatch.cs +++ b/project/Aki.Custom/Patches/EasyAssetsPatch.cs @@ -49,9 +49,9 @@ namespace Aki.Custom.Patches private static bool IsTargetMethod(MethodInfo mi) { var parameters = mi.GetParameters(); - return (parameters.Length == 6 - && parameters[0].Name == "bundleLock" - && parameters[1].Name == "defaultKey" + return (parameters.Length == 6 + && parameters[0].Name == "bundleLock" + && parameters[1].Name == "defaultKey" && parameters[4].Name == "shouldExclude"); } diff --git a/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs b/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs index 12bd3e0..7f82558 100644 --- a/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs +++ b/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs @@ -33,7 +33,7 @@ namespace Aki.Custom.Patches var player = Singleton.Instance.MainPlayer; if (profileId == player?.Profile.Id) { - GClass2731.Instance.CloseAllScreensForced(); + GClass2974.Instance.CloseAllScreensForced(); } return true; diff --git a/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs b/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs index 80b5cd0..047a692 100644 --- a/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs +++ b/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs @@ -29,7 +29,6 @@ namespace Aki.Custom.Patches var raidSettings = Traverse.Create(controller).Field("RaidSettings").Value; raidSettings.RaidMode = ERaidMode.Local; - raidSettings.BotSettings.IsEnabled = true; // Default checkbox to be ticked ____offlineModeToggle.isOn = true; diff --git a/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs b/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs index a7c8976..be71c06 100644 --- a/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs +++ b/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs @@ -17,7 +17,7 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { var desiredType = typeof(RaidSettingsWindow); - var desiredMethod = desiredType.GetMethod("method_9", BindingFlags.NonPublic | BindingFlags.Instance); + var desiredMethod = desiredType.GetMethod("method_8", BindingFlags.NonPublic | BindingFlags.Instance); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); diff --git a/project/Aki.PrePatch/AkiBotsPrePatcher.cs b/project/Aki.PrePatch/AkiBotsPrePatcher.cs index 23bc1f9..42db019 100644 --- a/project/Aki.PrePatch/AkiBotsPrePatcher.cs +++ b/project/Aki.PrePatch/AkiBotsPrePatcher.cs @@ -7,8 +7,8 @@ namespace Aki.PrePatch { public static IEnumerable TargetDLLs { get; } = new[] { "Assembly-CSharp.dll" }; - public static int sptUsecValue = 32; - public static int sptBearValue = 33; + public static int sptUsecValue = 33; + public static int sptBearValue = 34; public static void Patch(ref AssemblyDefinition assembly) { diff --git a/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs b/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs index 58a791a..02b92d5 100644 --- a/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs +++ b/project/Aki.SinglePlayer/Models/Progression/LighthouseProgressionClass.cs @@ -14,7 +14,7 @@ namespace Aki.SinglePlayer.Models.Progression private bool _addedToEnemy; private List _mines; private RecodableItemClass _transmitter; - private List _bosses; + private List _bosses; private bool _aggressor; private bool _disabledDoor; private readonly string _transmitterId = "62e910aaf957f2915e0a5e36"; @@ -22,10 +22,13 @@ namespace Aki.SinglePlayer.Models.Progression public void Start() { _gameWorld = Singleton.Instance; - _bosses = new List(); + _bosses = new List(); _mines = GameObject.FindObjectsOfType().ToList(); - if (_gameWorld == null || _gameWorld.MainPlayer.Location.ToLower() != "lighthouse") return; + if (_gameWorld == null || !string.Equals(_gameWorld.MainPlayer.Location, "lighthouse", System.StringComparison.OrdinalIgnoreCase)) + { + return; + } // if player is a scav, there is no need to continue this method. if (_gameWorld.MainPlayer.Side == EPlayerSide.Savage) @@ -55,7 +58,10 @@ namespace Aki.SinglePlayer.Models.Progression _timer += Time.deltaTime; - if (_timer < 10f) return; + if (_timer < 10f) + { + return; + } if (_bosses.Count == 0) { @@ -100,22 +106,23 @@ namespace Aki.SinglePlayer.Models.Progression private void SetupBosses() { - foreach (var player in _gameWorld.AllPlayers) + foreach (var aiBot in _gameWorld.AllAlivePlayersList) { - if (!player.IsYourPlayer) + if (!aiBot.IsYourPlayer) { - if (player.AIData.BotOwner.IsRole(WildSpawnType.bossZryachiy) || player.AIData.BotOwner.IsRole(WildSpawnType.followerZryachiy)) + // Edge case of bossZryachiy not being hostile to player + if (aiBot.AIData.BotOwner.IsRole(WildSpawnType.bossZryachiy) || aiBot.AIData.BotOwner.IsRole(WildSpawnType.followerZryachiy)) { - // Sub to Bosses OnDeath event, Set mainplayer to aggressor on this script - player.OnPlayerDeadOrUnspawn += player1 => + // Subscribe to Bosses OnDeath event + aiBot.OnPlayerDeadOrUnspawn += player1 => { - if (player1.KillerId != null && player1.KillerId == _gameWorld.MainPlayer.ProfileId) + if (player1?.KillerId == _gameWorld.MainPlayer.ProfileId) { _aggressor = true; } }; - _bosses.Add(player); + _bosses.Add(aiBot); } } } diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs index 42aed36..e37c645 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs @@ -23,8 +23,12 @@ namespace Aki.SinglePlayer.Patches.RaidFix public GetNewBotTemplatesPatch() { - _getNewProfileMethod = typeof(BotsPresets) - .GetMethod(nameof(BotsPresets.GetNewProfile), PatchConstants.PrivateFlags); + var desiredType = typeof(BotsPresets); + _getNewProfileMethod = desiredType + .GetMethod(nameof(BotsPresets.GetNewProfile), BindingFlags.Instance | BindingFlags.NonPublic); // want the func with 2 params (protected) + + Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); + Logger.LogDebug($"{this.GetType().Name} Method: {_getNewProfileMethod?.Name}"); } protected override MethodBase GetTargetMethod() @@ -36,13 +40,14 @@ namespace Aki.SinglePlayer.Patches.RaidFix private bool IsTargetMethod(MethodInfo mi) { var parameters = mi.GetParameters(); - return (parameters.Length == 2 + return (parameters.Length == 3 && parameters[0].Name == "data" - && parameters[1].Name == "cancellationToken"); + && parameters[1].Name == "cancellationToken" + && parameters[2].Name == "withDelete"); } [PatchPrefix] - private static bool PatchPrefix(ref Task __result, BotsPresets __instance, IBotData data) + private static bool PatchPrefix(ref Task __result, BotsPresets __instance, GClass626 data, bool withDelete) { /* in short when client wants new bot and GetNewProfile() return null (if not more available templates or they don't satisfy by Role and Difficulty condition) @@ -57,7 +62,17 @@ namespace Aki.SinglePlayer.Patches.RaidFix var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); var taskAwaiter = (Task)null; - var profile = (Profile)_getNewProfileMethod.Invoke(__instance, new object[] { data }); + + try + { + _getNewProfileMethod.Invoke(__instance, new object[] { data, true }); + } + catch (Exception e) + { + Logger.LogDebug($"getnewbot failed: {e.Message} {e.InnerException}"); + throw; + } + // load from server var source = data.PrepareToLoadBackend(1).ToList(); diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs index 26eeaa6..1e377c0 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs @@ -2,7 +2,7 @@ using HarmonyLib; using System; using System.Reflection; -using TraderInfo = EFT.Profile.GClass1666; +using TraderInfo = EFT.Profile.GClass1726; namespace Aki.SinglePlayer.Patches.RaidFix { diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs index ba1938a..4740b12 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs @@ -2,7 +2,6 @@ using Aki.Reflection.Patching; using Aki.Reflection.Utils; using EFT; using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,10 +9,10 @@ namespace Aki.SinglePlayer.Patches.RaidFix { public class RemoveUsedBotProfilePatch : ModulePatch { - private static BindingFlags _flags; - private static Type _targetInterface; - private static Type _targetType; - private static FieldInfo _profilesField; + private static readonly BindingFlags _flags; + private static readonly Type _targetInterface; + private static readonly Type _targetType; + private static readonly FieldInfo _profilesField; static RemoveUsedBotProfilePatch() { @@ -21,7 +20,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix _flags = BindingFlags.Instance | BindingFlags.NonPublic; _targetInterface = PatchConstants.EftTypes.Single(IsTargetInterface); - _targetType = PatchConstants.EftTypes.Single(IsTargetType); + _targetType = typeof(BotsPresets); _profilesField = _targetType.GetField("list_0", _flags); } @@ -41,21 +40,11 @@ namespace Aki.SinglePlayer.Patches.RaidFix } [PatchPrefix] - private static bool PatchPrefix(ref Profile __result, object __instance, IBotData data) + private static bool PatchPrefix(ref Profile __result, object __instance, GClass626 data, ref bool withDelete) { - var profiles = (List)_profilesField.GetValue(__instance); + withDelete = true; - if (profiles.Count > 0) - { - // second parameter makes client remove used profiles - __result = data.ChooseProfile(profiles, true); - } - else - { - __result = null; - } - - return false; + return true; // Do original method } } } diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs index 79e83b8..96141a5 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs @@ -21,7 +21,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix .GetMethod("FindActiveEffect", BindingFlags.Instance | BindingFlags.Public) .MakeGenericMethod(typeof(ActiveHealthControllerClass) .GetNestedType("Stun", BindingFlags.Instance | BindingFlags.NonPublic)) - .Invoke(Singleton.Instance.AllPlayers[0].ActiveHealthController, new object[] { EBodyPart.Common }) != null; + .Invoke(Singleton.Instance.MainPlayer.ActiveHealthController, new object[] { EBodyPart.Common }) != null; return shouldInvoke; } diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs index 2ab15f1..4dc694c 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs @@ -32,8 +32,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode Logger.LogError("Unable to Find Gameworld or RegisterPlayers... Unable to Disable Extracts for Scav raid"); } - // One of the RegisteredPlayers will have the IsYourPlayer flag set, which will be our own Player instance. - Player player = gameWorld.RegisteredPlayers.Find(p => p.IsYourPlayer); + Player player = gameWorld.MainPlayer; var exfilController = gameWorld.ExfiltrationController; diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs index cc6178d..0c32402 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs @@ -111,13 +111,16 @@ namespace Aki.SinglePlayer.Patches.ScavMode { var profile = PatchConstants.BackEndSession.Profile; var menuController = (object)GetMenuController(); + + // Get fields from MainMenuController.cs var raidSettings = Traverse.Create(menuController).Field("raidSettings_0").GetValue(); - var matchmakerPlayersController = Traverse.Create(menuController).Field("gclass2784_0").GetValue(); - var gclass = new MatchmakerOfflineRaidScreen.GClass2773(profile?.Info, ref raidSettings, matchmakerPlayersController); + var matchmakerPlayersController = Traverse.Create(menuController).Field("gclass3027_0").GetValue(); + + var gclass = new MatchmakerOfflineRaidScreen.GClass3016(profile?.Info, ref raidSettings, matchmakerPlayersController); gclass.OnShowNextScreen += LoadOfflineRaidNextScreen; - // ready method + // Ready method gclass.OnShowReadyScreen += (OfflineRaidAction)Delegate.CreateDelegate(typeof(OfflineRaidAction), menuController, "method_67"); gclass.ShowScreen(EScreenState.Queued); } @@ -132,10 +135,10 @@ namespace Aki.SinglePlayer.Patches.ScavMode raidSettings.WavesSettings.IsBosses = true; } - // set offline raid values + // Set offline raid values _isLocalField.SetValue(menuController, raidSettings.Local); - // load ready screen method + // Load ready screen method _onReadyScreenMethod.Invoke(menuController, null); } diff --git a/project/Shared/Hollowed/hollowed.dll b/project/Shared/Hollowed/hollowed.dll index 71ef57a..8b9cc9f 100644 Binary files a/project/Shared/Hollowed/hollowed.dll and b/project/Shared/Hollowed/hollowed.dll differ