From b1df2b9567e29866454b36d9e47d56896eaef5a5 Mon Sep 17 00:00:00 2001 From: Heorhi Sporau Date: Tue, 18 Aug 2026 21:29:55 -0700 Subject: [PATCH] Expose vehicle caravan action boundaries Route vehicle selection through the vanilla completion transaction while substituting the vehicle-specific exit tile, and expose the final send action so Multiplayer compatibility can register the intended boundary directly. --- .../Patches/Patch_FormCaravanDialog.cs | 62 +++++++++------- .../Utility/Helpers/World/CaravanFormation.cs | 72 +++++++++---------- 2 files changed, 71 insertions(+), 63 deletions(-) diff --git a/Source/Vehicles/Harmony/Patches/Patch_FormCaravanDialog.cs b/Source/Vehicles/Harmony/Patches/Patch_FormCaravanDialog.cs index bb2a84ef..3a54dec8 100644 --- a/Source/Vehicles/Harmony/Patches/Patch_FormCaravanDialog.cs +++ b/Source/Vehicles/Harmony/Patches/Patch_FormCaravanDialog.cs @@ -144,6 +144,11 @@ void IPatchCategory.PatchMethods() original: AccessTools.Method(typeof(Dialog_FormCaravan), "DoBottomButtons"), transpiler: new HarmonyMethod(typeof(Patch_FormCaravanDialog), nameof(StartRoutePlanningForVehiclesTranspiler))); + HarmonyPatcher.Patch( + original: AccessTools.Method(typeof(Dialog_FormCaravan), + nameof(Dialog_FormCaravan.Notify_ChoseRoute)), + transpiler: new HarmonyMethod(typeof(Patch_FormCaravanDialog), + nameof(ChooseVehicleStartingTileTranspiler))); HarmonyPatcher.Patch(original: AccessTools.Method(typeof(Dialog_FormCaravan), "TrySend"), prefix: new HarmonyMethod(typeof(Patch_FormCaravanDialog), nameof(TryAndSendWithVehicles))); @@ -622,9 +627,6 @@ private static IEnumerable StartRoutePlanningForVehiclesTranspi MethodInfo startPlanningMethod = AccessTools.Method(typeof(WorldRoutePlanner), nameof(WorldRoutePlanner.Start), parameters: [typeof(Dialog_FormCaravan)]); - FieldInfo mapField = AccessTools.Field(typeof(Dialog_FormCaravan), "map"); - FieldInfo autoSelectTravelSuppliesField = - AccessTools.Field(typeof(Dialog_FormCaravan), "autoSelectTravelSupplies"); for (int i = 0; i < instructionList.Count; i++) { CodeInstruction instruction = instructionList[i]; @@ -634,13 +636,6 @@ private static IEnumerable StartRoutePlanningForVehiclesTranspi // Callvirt WorldRoutePlanner::Start(Dialog_FormCaravan) instruction = instructionList[++i]; // ON STACK - WorldRoutePlanner instance, Dialog_FormCaravan instance - // this.map - yield return new CodeInstruction(opcode: OpCodes.Ldarg_0); - yield return new CodeInstruction(opcode: OpCodes.Ldfld, operand: mapField); - // this.autoSelectTravelSupplies - yield return new CodeInstruction(opcode: OpCodes.Ldarg_0); - yield return new CodeInstruction(opcode: OpCodes.Ldfld, - operand: autoSelectTravelSuppliesField); yield return new CodeInstruction(opcode: OpCodes.Call, operand: AccessTools.Method(typeof(Patch_FormCaravanDialog), nameof(WorldRoutePannerReroute))); @@ -651,7 +646,7 @@ private static IEnumerable StartRoutePlanningForVehiclesTranspi // NOTE - It's easier to pass in the world route planner since it's already on the stack private static void WorldRoutePannerReroute(WorldRoutePlanner routePlanner, - Dialog_FormCaravan formCaravan, Map map, bool autoSelectTravelSupplies) + Dialog_FormCaravan formCaravan) { if (VehiclesSelected(formCaravan.transferables)) { @@ -666,33 +661,46 @@ private static void WorldRoutePannerReroute(WorldRoutePlanner routePlanner, { Find.WindowStack.Add(formCaravan); formCaravan.Notify_NoLongerChoosingRoute(); - }, ChoseVehicleRoute); + }, formCaravan.Notify_ChoseRoute); } else { routePlanner.Start(formCaravan); } - return; + } - void ChoseVehicleRoute(PlanetTile tile) - { - CaravanFormation.formation.DestinationTile = tile; - List vehicleDefs = TransferableUtility - .GetPawnsFromTransferables(formCaravan.transferables) - .UniqueVehicleDefsInList(); - CaravanFormation.formation.StartingTile = - CaravanHelper.BestExitTileToGoTo(vehicleDefs, tile, map); - CaravanFormation.formation.TicksToArriveDirty = true; - CaravanFormation.formation.DaysWorthOfFoodDirty = true; + private static IEnumerable ChooseVehicleStartingTileTranspiler( + IEnumerable instructions) + { + MethodInfo bestExitTileMethod = AccessTools.Method(typeof(CaravanExitMapUtility), + nameof(CaravanExitMapUtility.BestExitTileToGoTo), + parameters: [typeof(PlanetTile), typeof(Map)]); + MethodInfo replacement = AccessTools.Method(typeof(Patch_FormCaravanDialog), + nameof(BestExitTileToGoTo)); - formCaravan.soundAppear.PlayOneShotOnCamera(); - if (autoSelectTravelSupplies) + foreach (CodeInstruction instruction in instructions) + { + if (instruction.Calls(bestExitTileMethod)) { - CaravanFormation.formation.SelectApproximateBestTravelSupplies(); + yield return new CodeInstruction(OpCodes.Ldarg_0); + instruction.operand = replacement; } + yield return instruction; } } + private static PlanetTile BestExitTileToGoTo(PlanetTile destinationTile, Map map, + Dialog_FormCaravan formCaravan) + { + if (!VehiclesSelected(formCaravan.transferables)) + return CaravanExitMapUtility.BestExitTileToGoTo(destinationTile, map); + + List vehicleDefs = TransferableUtility + .GetPawnsFromTransferables(formCaravan.transferables) + .UniqueVehicleDefsInList(); + return CaravanHelper.BestExitTileToGoTo(vehicleDefs, destinationTile, map); + } + /// /// Reroute caravan send off to create VehicleCaravan or initialize vehicle caravan lord job. /// @@ -1018,4 +1026,4 @@ private static int AppendMapPawnsInVehicles(int count, MapParent mapParent) } return count; } -} \ No newline at end of file +} diff --git a/Source/Vehicles/Utility/Helpers/World/CaravanFormation.cs b/Source/Vehicles/Utility/Helpers/World/CaravanFormation.cs index 41fa9d67..43c58b89 100644 --- a/Source/Vehicles/Utility/Helpers/World/CaravanFormation.cs +++ b/Source/Vehicles/Utility/Helpers/World/CaravanFormation.cs @@ -138,50 +138,50 @@ public static void TrySendVehicleCaravan(Dialog_FormCaravan formCaravan) SoundDefOf.Tick_High.PlayOneShotOnCamera(); formCaravan.Close(false); } - return; + } - bool TryFormAndSendCaravan() + public static bool TryFormAndSendCaravan() + { + Assert.IsNotNull(formation); + foreach (Pawn pawn in formation.pawns) { - foreach (Pawn pawn in formation.pawns) - { - if (pawn is VehiclePawn vehicle) - { - vehicle.DisembarkAll(); - } - } - if (!CheckForErrors()) + if (pawn is VehiclePawn vehicle) { - return false; - } - Direction8Way direction8WayFromTo = - Find.WorldGrid.GetDirection8WayFromTo(formation.Dialog.CurrentTile, formation.StartingTile); - if (!TryFindExitSpot(formation.pawns, reachableForEveryColonist: true, out IntVec3 intVec)) - { - if (!TryFindExitSpot(formation.pawns, reachableForEveryColonist: false, out intVec)) - { - Messages.Message( - "CaravanCouldNotFindExitSpot".Translate(direction8WayFromTo.LabelShort()), - MessageTypeDefOf.RejectInput, false); - return false; - } - Messages.Message( - "CaravanCouldNotFindReachableExitSpot".Translate(direction8WayFromTo.LabelShort()), - new GlobalTargetInfo(intVec, formation.Map), MessageTypeDefOf.CautionInput, false); + vehicle.DisembarkAll(); } - if (!TryFindRandomPackingSpot(intVec, out IntVec3 meetingPoint)) + } + if (!CheckForErrors()) + { + return false; + } + Direction8Way direction8WayFromTo = + Find.WorldGrid.GetDirection8WayFromTo(formation.Dialog.CurrentTile, formation.StartingTile); + if (!TryFindExitSpot(formation.pawns, reachableForEveryColonist: true, out IntVec3 intVec)) + { + if (!TryFindExitSpot(formation.pawns, reachableForEveryColonist: false, out intVec)) { Messages.Message( - "CaravanCouldNotFindPackingSpot".Translate(direction8WayFromTo.LabelShort()), - new GlobalTargetInfo(intVec, formation.Map), MessageTypeDefOf.RejectInput, false); + "CaravanCouldNotFindExitSpot".Translate(direction8WayFromTo.LabelShort()), + MessageTypeDefOf.RejectInput, false); return false; } - formation.RecacheTransferables(); - VehicleCaravanFormingUtility.StartFormingCaravan(formation.Dialog.transferables, - meetingPoint, intVec, formation.StartingTile, formation.DestinationTile); - Messages.Message("CaravanFormationProcessStarted".Translate(), formation.vehicles[0], - MessageTypeDefOf.PositiveEvent, false); - return true; + Messages.Message( + "CaravanCouldNotFindReachableExitSpot".Translate(direction8WayFromTo.LabelShort()), + new GlobalTargetInfo(intVec, formation.Map), MessageTypeDefOf.CautionInput, false); } + if (!TryFindRandomPackingSpot(intVec, out IntVec3 meetingPoint)) + { + Messages.Message( + "CaravanCouldNotFindPackingSpot".Translate(direction8WayFromTo.LabelShort()), + new GlobalTargetInfo(intVec, formation.Map), MessageTypeDefOf.RejectInput, false); + return false; + } + formation.RecacheTransferables(); + VehicleCaravanFormingUtility.StartFormingCaravan(formation.Dialog.transferables, + meetingPoint, intVec, formation.StartingTile, formation.DestinationTile); + Messages.Message("CaravanFormationProcessStarted".Translate(), formation.vehicles[0], + MessageTypeDefOf.PositiveEvent, false); + return true; } private static void ReformInstantly() @@ -532,4 +532,4 @@ static bool ThingIsVehicle(Thing thing) } } } -} \ No newline at end of file +}