diff --git a/project/Aki.SinglePlayer/AkiSingleplayerPlugin.cs b/project/Aki.SinglePlayer/AkiSingleplayerPlugin.cs index 2e18a34..1891988 100644 --- a/project/Aki.SinglePlayer/AkiSingleplayerPlugin.cs +++ b/project/Aki.SinglePlayer/AkiSingleplayerPlugin.cs @@ -42,6 +42,7 @@ namespace Aki.SinglePlayer new SpawnPmcPatch().Enable(); new PostRaidHealingPricePatch().Enable(); new EndByTimerPatch().Enable(); + new InRaidQuestAvailablePatch().Enable(); new PostRaidHealScreenPatch().Enable(); new VoIPTogglerPatch().Enable(); new MidRaidQuestChangePatch().Enable(); diff --git a/project/Aki.SinglePlayer/Patches/Quests/InRaidQuestAvailablePatch.cs b/project/Aki.SinglePlayer/Patches/Quests/InRaidQuestAvailablePatch.cs new file mode 100644 index 0000000..1a7c67e --- /dev/null +++ b/project/Aki.SinglePlayer/Patches/Quests/InRaidQuestAvailablePatch.cs @@ -0,0 +1,58 @@ +using Aki.Reflection.Patching; +using Aki.Reflection.Utils; +using EFT.Quests; +using HarmonyLib; +using System; +using System.Linq; +using System.Reflection; + +namespace Aki.SinglePlayer.Patches.Quests +{ + /** + * Lightkeeper quests change their state in-raid, and will change to the `AppearStatus` of the quest once + * the AvailableAfter time has been hit. This defaults to `Locked`, but should actually be `AvailableForStart` + * + * So if we get a quest state change from `AvailableAfter` to `Locked`, we should actually change to `AvailableForStart` + */ + public class InRaidQuestAvailablePatch : ModulePatch + { + private static PropertyInfo _questStatusProperty; + + protected override MethodBase GetTargetMethod() + { + var targetType = PatchConstants.EftTypes.FirstOrDefault(IsTargetType); + var targetMethod = AccessTools.Method(targetType, "SetStatus"); + + _questStatusProperty = AccessTools.Property(targetType, "QuestStatus"); + + Logger.LogDebug($"{this.GetType().Name} Type: {targetType?.Name}"); + Logger.LogDebug($"{this.GetType().Name} Method: {targetMethod?.Name}"); + Logger.LogDebug($"{this.GetType().Name} QuestStatus: {_questStatusProperty?.Name}"); + + return targetMethod; + } + + private bool IsTargetType(Type type) + { + if (type.GetProperty("StatusTransition") != null && + type.GetProperty("IsChangeAllowed") != null && + type.GetProperty("NeedCountdown") == null) + { + return true; + } + + return false; + } + + [PatchPrefix] + private static void PatchPrefix(object __instance, ref EQuestStatus status) + { + var currentStatus = (EQuestStatus)_questStatusProperty.GetValue(__instance); + + if (currentStatus == EQuestStatus.AvailableAfter && status == EQuestStatus.Locked) + { + status = EQuestStatus.AvailableForStart; + } + } + } +}