diff --git a/source/GameInterface/Services/CampaignService/Patches/CampaignEventsPatch.cs b/source/GameInterface/Services/CampaignService/Patches/CampaignEventsPatch.cs deleted file mode 100644 index f5db1427d..000000000 --- a/source/GameInterface/Services/CampaignService/Patches/CampaignEventsPatch.cs +++ /dev/null @@ -1,35 +0,0 @@ -using HarmonyLib; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using TaleWorlds.CampaignSystem; - -namespace GameInterface.Services.CampaignService.Patches -{ - [HarmonyPatch] - internal class CampaignEventsPatch - { - public static IEnumerable TargetMethods() - { - return typeof(CampaignEvents).GetMethods() - .Where(m => m.Name.ToLower().Contains("tick")) - .Where(m => m.Name.StartsWith("get_") == false); - } - - public static bool Prefix() => ModInformation.IsServer; - } - - [HarmonyPatch] - internal class CampaignEventDispatcherPatch - { - public static IEnumerable TargetMethods() - { - return typeof(CampaignEventDispatcher).GetMethods() - .Where(m => m.Name.ToLower().Contains("tick")) - .Where(m => m.Name.StartsWith("get_") == false) - .AddItem(AccessTools.Method(typeof(Campaign), nameof(Campaign.Tick))); - } - - public static bool Prefix() => ModInformation.IsServer; - } -} diff --git a/source/GameInterface/Services/CampaignService/Patches/CompanionsCampaignBehaviorPatches.cs b/source/GameInterface/Services/CampaignService/Patches/CompanionsCampaignBehaviorPatches.cs index d7ad63845..9272a9580 100644 --- a/source/GameInterface/Services/CampaignService/Patches/CompanionsCampaignBehaviorPatches.cs +++ b/source/GameInterface/Services/CampaignService/Patches/CompanionsCampaignBehaviorPatches.cs @@ -7,7 +7,7 @@ namespace GameInterface.Services.CampaignService.Patches; internal class CompanionsCampaignBehaviorPatches { [HarmonyPrefix] - [HarmonyPatch("CreateCompanionAndAddToSettlement")] + [HarmonyPatch(nameof(CompanionsCampaignBehavior.CreateCompanionAndAddToSettlement))] private static bool Prefix() { return false; diff --git a/source/GameInterface/Services/MobileParties/Handlers/SettlementExitEnterHandler.cs b/source/GameInterface/Services/MobileParties/Handlers/SettlementExitEnterHandler.cs index b57bdcd1a..984a85dbe 100644 --- a/source/GameInterface/Services/MobileParties/Handlers/SettlementExitEnterHandler.cs +++ b/source/GameInterface/Services/MobileParties/Handlers/SettlementExitEnterHandler.cs @@ -1,9 +1,17 @@ -using Common.Logging; +using Common; +using Common.Logging; using Common.Messaging; +using Common.Util; using GameInterface.Services.MobileParties.Interfaces; using GameInterface.Services.MobileParties.Messages.Behavior; using GameInterface.Services.MobileParties.Patches; +using GameInterface.Services.ObjectManager; using Serilog; +using TaleWorlds.CampaignSystem; +using TaleWorlds.CampaignSystem.Encounters; +using TaleWorlds.CampaignSystem.Party; +using TaleWorlds.CampaignSystem.Settlements; +using TaleWorlds.CampaignSystem.ViewModelCollection.Party; namespace GameInterface.Services.MobileParties.Handlers; @@ -15,14 +23,14 @@ internal class SettlementExitEnterHandler : IHandler private static readonly ILogger Logger = LogManager.GetLogger(); private readonly IMessageBroker messageBroker; - private readonly IMobilePartyInterface partyInterface; + private readonly IObjectManager objectManager; public SettlementExitEnterHandler( IMessageBroker messageBroker, - IMobilePartyInterface partyInterface) + IObjectManager objectManager) { this.messageBroker = messageBroker; - this.partyInterface = partyInterface; + this.objectManager = objectManager; messageBroker.Subscribe(Handle); messageBroker.Subscribe(Handle); @@ -42,25 +50,78 @@ private void Handle(MessagePayload obj) { var payload = obj.What; - partyInterface.EnterSettlement(payload.PartyId, payload.SettlementId); + if (objectManager.TryGetObject(payload.PartyId, out MobileParty mobileParty) == false) + { + Logger.Error("PartyId not found: {id}", payload.PartyId); + return; + } + + if (objectManager.TryGetObject(payload.SettlementId, out Settlement settlement) == false) + { + Logger.Error("SettlementId not found: {id}", payload.SettlementId); + return; + } + + EnterSettlementActionPatches.OverrideApplyForParty(mobileParty, settlement); } private void Handle(MessagePayload obj) { var payload = obj.What; - partyInterface.LeaveSettlement(payload.PartyId); + if (objectManager.TryGetObject(payload.PartyId, out MobileParty mobileParty) == false) + { + Logger.Error("PartyId not found: {id}", payload.PartyId); + return; + } + + LeaveSettlementActionPatches.OverrideApplyForParty(mobileParty); } private void Handle(MessagePayload obj) { var payload = obj.What; - partyInterface.StartPlayerSettlementEncounter(payload.PartyId, payload.SettlementId); + if (objectManager.TryGetObject(payload.PartyId, out MobileParty mobileParty) == false) + { + Logger.Error("PartyId not found: {id}", payload.PartyId); + return; + } + + if (objectManager.TryGetObject(payload.SettlementId, out Settlement settlement) == false) + { + Logger.Error("SettlementId not found: {id}", payload.SettlementId); + return; + } + + var settlementParty = settlement.Party; + if (settlementParty == null) + { + Logger.Error("Settlement {settlementName} did not have a party value", settlement.Name); + return; + } + + if (PlayerEncounter.Current != null) return; + + GameLoopRunner.RunOnMainThread(() => + { + using (new AllowedThread()) + { + PlayerEncounter.Start(); + PlayerEncounter.Current.Init(mobileParty.Party, settlementParty, settlement); + } + }, blocking: true); } private void Handle(MessagePayload obj) { - partyInterface.EndPlayerSettlementEncounter(); + GameLoopRunner.RunOnMainThread(() => + { + using (new AllowedThread()) + { + PlayerEncounter.Finish(true); + Campaign.Current.SaveHandler.SignalAutoSave(); + } + }, blocking: true); } } \ No newline at end of file diff --git a/source/GameInterface/Services/MobileParties/Interfaces/MobilePartyInterface.cs b/source/GameInterface/Services/MobileParties/Interfaces/MobilePartyInterface.cs index bc9c0d77b..4aeb03aac 100644 --- a/source/GameInterface/Services/MobileParties/Interfaces/MobilePartyInterface.cs +++ b/source/GameInterface/Services/MobileParties/Interfaces/MobilePartyInterface.cs @@ -18,21 +18,6 @@ namespace GameInterface.Services.MobileParties.Interfaces; /// internal interface IMobilePartyInterface : IGameAbstraction { - /// - /// Starts a settlement encounter for the player, bypasses patch skip rules - /// - /// Party to enter settlement as StringId - /// Settlement to enter as StringId - void StartPlayerSettlementEncounter(string partyId, string settlementId); - /// - /// Forces the player party to leave their current settlement encounter - /// - void EndPlayerSettlementEncounter(); - /// - /// Forces party to enter settlement, bypasses patch skip rules - /// - /// Party to leave current settlement - void LeaveSettlement(string partyId); /// /// Handles the initialization of a newly transfered party /// @@ -43,12 +28,6 @@ internal interface IMobilePartyInterface : IGameAbstraction /// /// Owner to assign all parties void RegisterAllPartiesAsControlled(string ownerId); - /// - /// Forces party to enter settlement, bypasses patch skip rules - /// - /// Party to enter settlement as StringId - /// Settlement to enter as StringId - void EnterSettlement(string partyId, string settlementId); } internal class MobilePartyInterface : IMobilePartyInterface @@ -84,77 +63,4 @@ public void RegisterAllPartiesAsControlled(string ownerId) controlledEntityRegistry.RegisterAsControlled(ownerId, party.Key); } } - - public void StartPlayerSettlementEncounter(string partyId, string settlementId) - { - if (objectManager.TryGetObject(partyId, out MobileParty mobileParty) == false) - { - Logger.Error("PartyId not found: {id}", partyId); - return; - } - - if (objectManager.TryGetObject(settlementId, out Settlement settlement) == false) - { - Logger.Error("SettlementId not found: {id}", settlementId); - return; - } - - var settlementParty = settlement.Party; - if (settlementParty is null) - { - Logger.Error("Settlement {settlementName} did not have a party value", settlement.Name); - return; - } - - if (PlayerEncounter.Current is not null) return; - - GameLoopRunner.RunOnMainThread(() => - { - using (new AllowedThread()) - { - PlayerEncounter.Start(); - PlayerEncounter.Current.Init(mobileParty.Party, settlementParty, settlement); - } - }); - } - - public void EndPlayerSettlementEncounter() - { - GameLoopRunner.RunOnMainThread(() => - { - using (new AllowedThread()) - { - PlayerEncounter.Finish(true); - Campaign.Current.SaveHandler.SignalAutoSave(); - } - }); - } - - public void EnterSettlement(string partyId, string settlementId) - { - if (objectManager.TryGetObject(partyId, out MobileParty mobileParty) == false) - { - Logger.Error("PartyId not found: {id}", partyId); - return; - } - - if (objectManager.TryGetObject(settlementId, out Settlement settlement) == false) - { - Logger.Error("SettlementId not found: {id}", settlementId); - return; - } - - EnterSettlementActionPatches.OverrideApplyForParty(mobileParty, settlement); - } - - public void LeaveSettlement(string partyId) - { - if (objectManager.TryGetObject(partyId, out MobileParty mobileParty) == false) - { - Logger.Error("PartyId not found: {id}", partyId); - return; - } - - LeaveSettlementActionPatches.OverrideApplyForParty(mobileParty); - } } diff --git a/source/GameInterface/Services/MobileParties/Patches/LeaveSettlementActionPatches.cs b/source/GameInterface/Services/MobileParties/Patches/LeaveSettlementActionPatches.cs index 048c0e482..c6c60b70e 100644 --- a/source/GameInterface/Services/MobileParties/Patches/LeaveSettlementActionPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/LeaveSettlementActionPatches.cs @@ -6,6 +6,7 @@ using HarmonyLib; using TaleWorlds.CampaignSystem.Actions; using TaleWorlds.CampaignSystem.Party; +using TaleWorlds.CampaignSystem.Settlements; namespace GameInterface.Services.MobileParties.Patches; @@ -36,9 +37,8 @@ public static void OverrideApplyForParty(MobileParty party) { using (new AllowedThread()) { - if (party.CurrentSettlement is null) return; LeaveSettlementAction.ApplyForParty(party); } - }); + }, blocking: true); } } diff --git a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs index 76303ef2b..4a40c8cae 100644 --- a/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/ParallelRobustnessPatches.cs @@ -18,7 +18,13 @@ internal class ParallelRobustnessPatches static bool ParallelCheckExitingSettlements(CampaignTickCacheDataStore __instance, int startInclusive, int endExclusive) { for (int index = startInclusive; index < endExclusive; ++index) - Campaign.Current.MobileParties[index].CheckExitingSettlementParallel(ref __instance._exitingSettlementCount, ref __instance._exitingSettlementMobilePartyList); + { + MobileParty mobileParty = Campaign.Current.MobileParties[index]; + + if (mobileParty.Party == null) continue; + + mobileParty.CheckExitingSettlementParallel(ref __instance._exitingSettlementCount, ref __instance._exitingSettlementMobilePartyList); + } return false; } diff --git a/source/GameInterface/Services/MobileParties/Patches/PlayerEncounterPatches.cs b/source/GameInterface/Services/MobileParties/Patches/PlayerEncounterPatches.cs index 724502125..12ff2520e 100644 --- a/source/GameInterface/Services/MobileParties/Patches/PlayerEncounterPatches.cs +++ b/source/GameInterface/Services/MobileParties/Patches/PlayerEncounterPatches.cs @@ -1,19 +1,17 @@ using Common.Logging; using Common.Messaging; -using GameInterface.Services.Entity; using GameInterface.Services.MobileParties.Extensions; using GameInterface.Services.MobileParties.Messages.Behavior; using HarmonyLib; using Serilog; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; +using System.Reflection.Emit; using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Encounters; using TaleWorlds.CampaignSystem.Party; using TaleWorlds.CampaignSystem.Settlements; -using static HarmonyLib.Code; namespace GameInterface.Services.MobileParties.Patches; @@ -26,65 +24,20 @@ internal class EncounterManagerPatches { private static ILogger Logger = LogManager.GetLogger(); - private static bool inSettlement = false; - private static MethodInfo Start => typeof(PlayerEncounter).GetMethod(nameof(PlayerEncounter.Start)); - private static MethodInfo Init => typeof(PlayerEncounter).GetMethod( - "Init", - BindingFlags.NonPublic | BindingFlags.Instance, - null, - new Type[] { typeof(PartyBase), typeof(PartyBase), typeof(Settlement) }, - null); - - /// - /// In the method - /// Replaces - /// PlayerEncounter.Start(); - /// PlayerEncounter.Current.Init(attackerParty.Party, settlement.Party, settlement); - /// With - /// EncounterManagerPatches.PlayerEncounterIntercept(attackerParty.Party, settlement.Party, settlement); - /// - /// previous instructions - /// patched instructions - [HarmonyTranspiler] + [HarmonyPrefix] [HarmonyPatch(nameof(EncounterManager.StartSettlementEncounter))] - private static IEnumerable Transpiler(IEnumerable instrs) + private static bool Prefix(MobileParty attackerParty, Settlement settlement) { - List instructions = instrs.ToList(); - - int startIdx = instructions.FindIndex(i => i.opcode == Call.opcode && i.operand as MethodInfo == Start); - - if (startIdx == -1) return instrs; - - instructions.RemoveRange(startIdx, 2); - - int initIdx = instructions.FindIndex(i => i.opcode == Callvirt.opcode && i.operand as MethodInfo == Init); - - if (initIdx == -1) return instrs; + if (ModInformation.IsServer) return true; - instructions[initIdx].opcode = Call.opcode; - instructions[initIdx].operand = typeof(EncounterManagerPatches) - .GetMethod(nameof(PlayerEncounterIntercept), BindingFlags.NonPublic | BindingFlags.Static); - - return instructions; - } - - private static void PlayerEncounterIntercept(PartyBase attackerParty, PartyBase defenderParty, Settlement settlement) - { - if (inSettlement) return; + if (attackerParty.IsPartyControlled() == false) return false; var message = new StartSettlementEncounterAttempted( - attackerParty.MobileParty.StringId, + attackerParty.StringId, settlement.StringId); MessageBroker.Instance.Publish(attackerParty, message); - inSettlement = true; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerEncounter), nameof(PlayerEncounter.Finish))] - private static void PlayerEncounterFinishPatch(bool forcePlayerOutFromSettlement) - { - inSettlement = false; + return false; } [HarmonyPrefix] diff --git a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIPatches.cs b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIPatches.cs index 4ba555609..34ef2c80e 100644 --- a/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIPatches.cs +++ b/source/GameInterface/Services/MobilePartyAIs/Patches/MobilePartyAIPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Party; namespace GameInterface.Services.MobilePartyAIs.Patches @@ -14,5 +15,16 @@ static bool GetTargetPositionAndFace_Fix(ref MobilePartyAi __instance) if (__instance._mobileParty == null) return false; return true; } + + [HarmonyPatch(nameof(MobilePartyAi.CheckPartyNeedsUpdate))] + [HarmonyPrefix] + static void Prefix(ref MobilePartyAi __instance) + { + if (ModInformation.IsServer) return; + + if (__instance._mobileParty != MobileParty.MainParty) return; + + EncounterManager.HandleEncounterForMobileParty(__instance._mobileParty, 0f); + } } }