private bool TrySetNewPath() { PawnPath pawnPath = this.GenerateNewPath(); if (!pawnPath.Found) { this.PatherFailed(); return(false); } if (this.curPath != null) { this.curPath.ReleaseToPool(); } this.curPath = pawnPath; int num = 0; while (num < 20 && num < this.curPath.NodesLeftCount) { IntVec3 c = this.curPath.Peek(num); if (PawnUtility.ShouldCollideWithPawns(this.pawn) && PawnUtility.AnyPawnBlockingPathAt(c, this.pawn, false, false, false)) { this.foundPathWhichCollidesWithPawns = Find.TickManager.TicksGame; } if (PawnUtility.KnownDangerAt(c, this.pawn.Map, this.pawn)) { this.foundPathWithDanger = Find.TickManager.TicksGame; } if (this.foundPathWhichCollidesWithPawns == Find.TickManager.TicksGame && this.foundPathWithDanger == Find.TickManager.TicksGame) { break; } num++; } return(true); }
private bool WillCollideWithPawnAt(IntVec3 c) { if (!PawnUtility.ShouldCollideWithPawns(this.pawn)) { return(false); } return(PawnUtility.AnyPawnBlockingPathAt(c, this.pawn, false, false)); }
public PawnPath findPath(Map map, IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode, ByteGrid avoidGrid, Area allowedArea, int costsCardinal, int costsDiagonal) { this.map = map; this.cellIndices = this.map.cellIndices; this.edificeGrid = map.edificeGrid; this.traverseParms = traverseParms; this.dest = dest; this.peMode = peMode; this.costPerMoveCardinal = costsCardinal; this.costPerMoveDiagonal = costsDiagonal; this.avoidGrid = avoidGrid; this.allowedArea = allowedArea; this.pathGridArray = map.pathGrid.pathGrid; this.pathGrid = map.pathGrid; this.topGrid = map.terrainGrid.topGrid; this.pawn = traverseParms.pawn; this.drafted = pawn != null && pawn.Drafted; this.blueprintGrid = map.blueprintGrid.InnerArray; //Only colonists and tamed animals should respect restrictions. //Drafted pawns move unrestricted. //Some job types like firefighting should exclude the restrictions. List <string> exceptions = QOLMod.getSettings().pfRestrictionExcemptions; if (this.pawn.Faction != null && this.pawn.Faction.IsPlayer && !this.pawn.Drafted && (exceptions == null || pawn.jobs.curJob == null || !exceptions.Contains(pawn.jobs.curJob.def.defName))) { this.pathfinderDirections = map.GetComponent <MapComponent_PathfinderDirections>(); } else { this.pathfinderDirections = null; } this.drawPath = DebugViewSettings.drawPaths; this.mapSizeX = map.Size.x; this.mapSizeZ = map.Size.z; this.dontPassWater = traverseParms.mode == TraverseMode.NoPassClosedDoorsOrWater || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; this.collidesWithPawns = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); Step step = astar(start); if (step == null) { return(PawnPath.NotFound); } PawnPath emptyPawnPath = map.pawnPathPool.GetEmptyPawnPath(); int costs = step.costToReachThis; while (step != null) { emptyPawnPath.AddNode(step.current); step = step.predecessor; } emptyPawnPath.SetupFound((float)costs, false); return(emptyPawnPath); }
public static bool TrySetNewPath(ref Pawn_PathFollower instance, ref IntVec3 lastPathedTargetPosition, LocalTargetInfo destination, Pawn pawn, Map map, ref PathEndMode peMode) { PawnPath pawnPath = GenerateNewPath(ref lastPathedTargetPosition, destination, ref pawn, map, peMode); if (!pawnPath.Found) { PatherFailedHelper(ref instance, pawn); return(false); } if (!(instance.curPath is null)) { instance.curPath.ReleaseToPool(); } instance.curPath = pawnPath; int num = 0; int foundPathWhichCollidesWithPawns = Traverse.Create(instance).Field("foundPathWhichCollidesWithPawns").GetValue <int>(); int foundPathWithDanger = Traverse.Create(instance).Field("foundPathWithDanger").GetValue <int>(); while (num < 20 && num < instance.curPath.NodesLeftCount) { IntVec3 c = instance.curPath.Peek(num); if (pawn.GetComp <CompShips>().beached) { break; } if (PawnUtility.ShouldCollideWithPawns(pawn) && PawnUtility.AnyPawnBlockingPathAt(c, pawn, false, false, false)) { foundPathWhichCollidesWithPawns = Find.TickManager.TicksGame; } if (PawnUtility.KnownDangerAt(c, pawn.Map, pawn)) { foundPathWithDanger = Find.TickManager.TicksGame; } if (foundPathWhichCollidesWithPawns == Find.TickManager.TicksGame && foundPathWithDanger == Find.TickManager.TicksGame) { break; } num++; } return(true); }
public (PawnPath path, bool found) FindVehiclePath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, CancellationToken token, PathEndMode peMode = PathEndMode.OnCell, bool waterPathing = false) { if (report) { Debug.Message($"{VehicleHarmony.LogLabel} MainPath for {traverseParms.pawn.LabelShort} - ThreadId: [{Thread.CurrentThread.ManagedThreadId}] TaskId: [{Task.CurrentId}]"); } postCalculatedCells.Clear(); VehicleMapping VehicleMapping = map.GetCachedMapComponent <VehicleMapping>(); if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } VehiclePawn pawn = traverseParms.pawn as VehiclePawn; if (!pawn.IsBoat() && waterPathing) { Log.Error($"Set to waterPathing but {pawn.LabelShort} is not registered as a Boat. Self Correcting..."); waterPathing = false; } if (!(pawn is null) && pawn.Map != map) { Log.Error(string.Concat(new object[] { "Tried to FindVehiclePath for pawn which is spawned in another map. Their map PathFinder should have been used, not this one. " + "pawn=", pawn, " pawn.Map=", pawn.Map, " map=", map })); return(PawnPath.NotFound, false); } if (!start.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindShipPath with invalid start ", start, ", pawn=", pawn })); return(PawnPath.NotFound, false); } if (!dest.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid dest ", dest, ", pawn= ", pawn })); return(PawnPath.NotFound, false); } if (traverseParms.mode == TraverseMode.ByPawn) { if (waterPathing) { if (!ShipReachabilityUtility.CanReachShip(pawn, dest, peMode, Danger.Deadly, false, traverseParms.mode)) { return(PawnPath.NotFound, false); } } else { if (!ReachabilityUtility.CanReach(pawn, dest, peMode, Danger.Deadly, false, traverseParms.mode)) { return(PawnPath.NotFound, false); } } } else { if (waterPathing) { if (!VehicleMapping.VehicleReachability.CanReachShip(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound, false); } } else { if (!map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound, false); } } } cellIndices = map.cellIndices; VehiclePathGrid = VehicleMapping.VehiclePathGrid; pathGrid = map.pathGrid; this.edificeGrid = map.edificeGrid.InnerArray; blueprintGrid = map.blueprintGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int num = cellIndices.CellToIndex(start); int num2 = cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = (pawn is null) ? null : pawn.GetAvoidGrid(true); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] boatsArray = VehiclePathGrid.pathGrid; int[] vehicleArray = pathGrid.pathGrid; TerrainDef[] topGrid = map.terrainGrid.topGrid; EdificeGrid edificeGrid = map.edificeGrid; int num3 = 0; int num4 = 0; Area allowedArea = GetAllowedArea(pawn); bool flag5 = !(pawn is null) && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && !(VehicleGridsUtility.GetRegion(start, map, RegionType.Set_Passable) is null) && flag2; bool flag8 = !flag || !flag3; bool flag9 = false; bool flag10 = !(pawn is null) && pawn.Drafted; bool flag11 = !(pawn is null) && !(pawn is null); int num5 = (!flag11) ? NodesToOpenBeforeRegionbasedPathing_NonShip : NodesToOpenBeforeRegionBasedPathing_Ship; int num6 = 0; int num7 = 0; float num8 = DetermineHeuristicStrength(pawn, start, dest); int num9 = !(pawn is null) ? pawn.TicksPerMoveCardinal : DefaultMoveTicksCardinal; int num10 = !(pawn is null) ? pawn.TicksPerMoveDiagonal : DefaultMoveTicksDiagonal; CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); InitStatusesAndPushStartNode(ref num, start); Rot8 rot = pawn.FullRotation; int iterations = 0; for (;;) { if (token.IsCancellationRequested) { return(PawnPath.NotFound, false); } iterations++; if (openList.Count <= 0) { break; } num6 += openList.Count; num7++; CostNode costNode = openList.Pop(); num = costNode.index; if (costNode.cost == calcGrid[num].costNodeCost && calcGrid[num].status != statusClosedValue) { IntVec3 c = cellIndices.IndexToCell(num); IntVec3 prevCell = c; int x2 = c.x; int z2 = c.z; if (flag6) { DebugFlash(c, calcGrid[num].knownCost / 1500f, calcGrid[num].knownCost.ToString()); } if (flag4) { if (num == num2) { goto Block_32; } } else if (cellRect.Contains(c) && !disallowedCornerIndices.Contains(num)) { goto Block_32; } if (num3 > SearchLimit) { goto Block_33; } List <IntVec3> fullRectCells = CellRect.CenteredOn(c, pawn.def.size.x, pawn.def.size.z).Where(cl2 => cl2 != c).ToList(); for (int i = 0; i < 8; i++) { uint num11 = (uint)(x2 + Directions[i]); //x uint num12 = (uint)(z2 + Directions[i + 8]); //y if (num11 < ((ulong)mapSizeX) && num12 < (ulong)(mapSizeZ)) { int num13 = (int)num11; int num14 = (int)num12; int num15 = cellIndices.CellToIndex(num13, num14); IntVec3 cellToCheck = cellIndices.IndexToCell(num15); if (VehicleMod.settings.main.fullVehiclePathing && pawn.LocationRestrictedBySize(cellToCheck)) { goto EndPathing; } if (calcGrid[num15].status != statusClosedValue || flag9) { int num16 = 0; bool flag12 = false; //Extra cost for traversing water if (flag2 || !new IntVec3(num13, 0, num14).GetTerrain(map).HasTag("Water")) { if (waterPathing) { if (!pawn.DrivableFast(num15)) { if (!flag) { if (flag6) { DebugFlash(new IntVec3(num13, 0, num14), 0.22f, "walk"); } goto EndPathing; } num16 += 70; Building building = edificeGrid[num15]; if (building is null) { goto EndPathing; } if (!IsDestroyable(building)) { goto EndPathing; } num16 += (int)(building.HitPoints * 0.2f); } } else { if (!pawn.DrivableFast(num15)) { if (!flag) { if (flag6) { DebugFlash(new IntVec3(num13, 0, num14), 0.22f, "walk"); } goto EndPathing; } flag12 = true; num16 += 70; Building building = edificeGrid[num15]; if (building is null) { goto EndPathing; } if (!IsDestroyable(building)) { goto EndPathing; } num16 += (int)(building.HitPoints * 0.2f); } } if (i > 3) { switch (i) { case 4: if (BlocksDiagonalMovement(pawn, num - mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } if (BlocksDiagonalMovement(pawn, num + 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } break; case 5: if (BlocksDiagonalMovement(pawn, num + mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } if (BlocksDiagonalMovement(pawn, num + 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } break; case 6: if (BlocksDiagonalMovement(pawn, num + mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } if (BlocksDiagonalMovement(pawn, num - 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } break; case 7: if (BlocksDiagonalMovement(pawn, num - mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } if (BlocksDiagonalMovement(pawn, num - 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "ships"); } goto EndPathing; } num16 += 70; } break; } } int num17 = (i <= 3) ? num9 : num10; num17 += num16; //if (Rot8.DirectionFromCells(prevCell, cellToCheck) != rot) //{ // Log.Message("Additional Cost"); // num17 += ChangeDirectionAdditionalCost; //} if (!flag12 && !waterPathing) { //Extra Terrain costs if (pawn.VehicleDef.properties.customTerrainCosts?.NotNullAndAny() ?? false) { TerrainDef currentTerrain = map.terrainGrid.TerrainAt(num15); if (pawn.VehicleDef.properties.customTerrainCosts.ContainsKey(currentTerrain)) { int customCost = pawn.VehicleDef.properties.customTerrainCosts[currentTerrain]; if (customCost < 0) { goto EndPathing; } num17 += customCost; } else { num17 += vehicleArray[num15]; } } else { num17 += vehicleArray[num15]; } num17 += flag10 ? topGrid[num15].extraDraftedPerceivedPathCost : topGrid[num15].extraNonDraftedPerceivedPathCost; } if (byteGrid != null) { num17 += (byteGrid[num15] * 8); } //Allowed area cost? if (flag5 && MultithreadHelper.AnyVehicleBlockingPathAt(new IntVec3(num13, 0, num14), pawn, false, false, true) != null) { num17 += Cost_PawnCollision; } Building building2 = edificeGrid[num15]; if (!(building2 is null)) { //Building Costs Here } if (blueprintGrid[num15] != null) { List <Blueprint> list = new List <Blueprint>(blueprintGrid[num15]); if (!list.NullOrEmpty()) { int num18 = 0; foreach (Blueprint bp in list) { num18 = Mathf.Max(num18, GetBlueprintCost(bp, pawn)); } if (num18 == int.MaxValue) { goto EndPathing; } num17 += num18; } } int num19 = num17 + calcGrid[num].knownCost; ushort status = calcGrid[num15].status; //if(pawn.Props.useFullHitboxPathing) //{ // foreach(IntVec3 fullRect in fullRectCells) // { // if(fullRect != cellToCheck) // { // num19 += calcGrid[cellIndices.CellToIndex(fullRect)].knownCost; // Log.Message($"Cell: {fullRect} Cost: {num19}"); // if(postCalculatedCells.ContainsKey(fullRect)) // { // postCalculatedCells[fullRect] = num19; // } // else // { // postCalculatedCells.Add(fullRect, num19); // } // } // } //} //Only generate path costs for linear non-reverse pathing check if (report) { if (postCalculatedCells.ContainsKey(cellToCheck)) { postCalculatedCells[cellToCheck] = num19; } else { postCalculatedCells.Add(cellToCheck, num19); } } if (waterPathing && !map.terrainGrid.TerrainAt(num15).IsWater) { num19 += 10000; } if (status == statusClosedValue || status == statusOpenValue) { int num20 = 0; if (status == statusClosedValue) { num20 = num9; } if (calcGrid[num15].knownCost <= num19 + num20) { goto EndPathing; } } if (flag9) { calcGrid[num15].heuristicCost = waterPathing ? Mathf.RoundToInt(regionCostCalculatorSea.GetPathCostFromDestToRegion(num15) * RegionheuristicWeighByNodesOpened.Evaluate(num4)) : Mathf.RoundToInt(regionCostCalculatorLand.GetPathCostFromDestToRegion(num15) * RegionheuristicWeighByNodesOpened.Evaluate(num4)); if (calcGrid[num15].heuristicCost < 0) { Log.ErrorOnce(string.Concat(new object[] { "Heuristic cost overflow for vehicle ", pawn.ToStringSafe <Pawn>(), " pathing from ", start, " to ", dest, "." }), pawn.GetHashCode() ^ 193840009); calcGrid[num15].heuristicCost = 0; } } else if (status != statusClosedValue && status != statusOpenValue) { int dx = Math.Abs(num13 - x); int dz = Math.Abs(num14 - z); int num21 = GenMath.OctileDistance(dx, dz, num9, num10); calcGrid[num15].heuristicCost = Mathf.RoundToInt((float)num21 * num8); } int num22 = num19 + calcGrid[num15].heuristicCost; if (num22 < 0) { Log.ErrorOnce(string.Concat(new object[] { "Node cost overflow for ship ", pawn.ToStringSafe <Pawn>(), " pathing from ", start, " to ", dest, "." }), pawn.GetHashCode() ^ 87865822); num22 = 0; } calcGrid[num15].parentIndex = num; calcGrid[num15].knownCost = num19; calcGrid[num15].status = statusOpenValue; calcGrid[num15].costNodeCost = num22; num4++; rot = Rot8.DirectionFromCells(prevCell, cellToCheck); openList.Push(new CostNode(num15, num22)); } } } EndPathing :; } num3++; calcGrid[num].status = statusClosedValue; if (num4 >= num5 && flag7 && !flag9) { flag9 = true; if (waterPathing) { regionCostCalculatorSea.Init(cellRect, traverseParms, num9, num10, byteGrid, allowedArea, flag10, disallowedCornerIndices); } else { regionCostCalculatorLand.Init(cellRect, traverseParms, num9, num10, byteGrid, allowedArea, flag10, disallowedCornerIndices); } InitStatusesAndPushStartNode(ref num, start); num4 = 0; num3 = 0; } } } string text = ((pawn is null) || pawn.CurJob is null) ? "null" : pawn.CurJob.ToString(); string text2 = ((pawn is null) || pawn.Faction is null) ? "null" : pawn.Faction.ToString(); if (report) { Log.Warning(string.Concat(new object[] { "ship pawn: ", pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2, "\niterations: ", iterations })); } DebugDrawRichData(); return(PawnPath.NotFound, false); Block_32: PawnPath result = PawnPath.NotFound; if (report) { result = FinalizedPath(num, flag9); } DebugDrawPathCost(); return(result, true); Block_33: Log.Warning(string.Concat(new object[] { "Ship ", pawn, " pathing from ", start, " to ", dest, " hit search limit of ", SearchLimit, " cells." })); DebugDrawRichData(); return(PawnPath.NotFound, false); }
public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != this.map) { Log.Error("Tried to FindPath for pawn which is spawned in another map. His map PathFinder should have been used, not this one. pawn=" + pawn + " pawn.Map=" + pawn.Map + " map=" + this.map); return(PawnPath.NotFound); } if (!start.IsValid) { Log.Error("Tried to FindPath with invalid start " + start + ", pawn= " + pawn); return(PawnPath.NotFound); } if (!dest.IsValid) { Log.Error("Tried to FindPath with invalid dest " + dest + ", pawn= " + pawn); return(PawnPath.NotFound); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!this.map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } this.PfProfilerBeginSample("FindPath for " + pawn + " from " + start + " to " + dest + ((!dest.HasThing) ? string.Empty : (" at " + dest.Cell))); this.cellIndices = this.map.cellIndices; this.pathGrid = this.map.pathGrid; this.edificeGrid = this.map.edificeGrid.InnerArray; IntVec3 cell = dest.Cell; int x = cell.x; IntVec3 cell2 = dest.Cell; int z = cell2.z; int num = this.cellIndices.CellToIndex(start); int num2 = this.cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = (pawn == null) ? null : pawn.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = this.CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = this.map.pathGrid.pathGrid; EdificeGrid edificeGrid = this.map.edificeGrid; int num3 = 0; int num4 = 0; Area allowedArea = this.GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && start.GetRegion(this.map, RegionType.Set_Passable) != null; bool flag8 = !flag || !flag3; bool flag9 = false; int num5 = 0; int num6 = 0; float num7 = this.DetermineHeuristicStrength(pawn, start, dest); int num8; int num9; if (pawn != null) { num8 = pawn.TicksPerMoveCardinal; num9 = pawn.TicksPerMoveDiagonal; } else { num8 = 13; num9 = 18; } this.CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); this.InitStatusesAndPushStartNode(ref num, start); while (true) { this.PfProfilerBeginSample("Open cell"); if (this.openList.Count <= 0) { string text = (pawn == null || pawn.CurJob == null) ? "null" : pawn.CurJob.ToString(); string text2 = (pawn == null || pawn.Faction == null) ? "null" : pawn.Faction.ToString(); Log.Warning(pawn + " pathing from " + start + " to " + dest + " ran out of cells to process.\nJob:" + text + "\nFaction: " + text2); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); } num5 += this.openList.Count; num6++; CostNode costNode = this.openList.Pop(); num = costNode.index; if (costNode.cost != this.calcGrid[num].costNodeCost) { this.PfProfilerEndSample(); continue; } if (this.calcGrid[num].status == this.statusClosedValue) { this.PfProfilerEndSample(); continue; } IntVec3 c = this.cellIndices.IndexToCell(num); int x2 = c.x; int z2 = c.z; if (flag6) { this.DebugFlash(c, (float)((float)this.calcGrid[num].knownCost / 1500.0), this.calcGrid[num].knownCost.ToString()); } if (flag4) { if (num == num2) { this.PfProfilerEndSample(); PawnPath result = this.FinalizedPath(num); this.PfProfilerEndSample(); return(result); } } else if (cellRect.Contains(c) && !this.disallowedCornerIndices.Contains(num)) { this.PfProfilerEndSample(); PawnPath result2 = this.FinalizedPath(num); this.PfProfilerEndSample(); return(result2); } if (num3 <= 160000) { this.PfProfilerEndSample(); this.PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num10 = (uint)(x2 + PathFinder.Directions[i]); uint num11 = (uint)(z2 + PathFinder.Directions[i + 8]); int num12; int num13; int num14; bool flag10; int num15; if (num10 < this.mapSizeX && num11 < this.mapSizeZ) { num12 = (int)num10; num13 = (int)num11; num14 = this.cellIndices.CellToIndex(num12, num13); if (this.calcGrid[num14].status == this.statusClosedValue && !flag9) { continue; } num15 = 0; flag10 = false; if (!flag2 && new IntVec3(num12, 0, num13).GetTerrain(this.map).HasTag("Water")) { continue; } if (!this.pathGrid.WalkableFast(num14)) { if (!flag) { if (flag6) { this.DebugFlash(new IntVec3(num12, 0, num13), 0.22f, "walk"); } } else { flag10 = true; num15 += 70; Building building = edificeGrid[num14]; if (building != null && PathFinder.IsDestroyable(building)) { num15 += (int)((float)building.HitPoints * 0.10999999940395355); goto IL_0749; } } continue; } goto IL_0749; } continue; IL_092b: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } continue; } num15 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } continue; } num15 += 70; } goto IL_09bf; IL_0b0e: ushort status; if (status != this.statusClosedValue && status != this.statusOpenValue) { if (flag9) { this.calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)this.regionCostCalculator.GetPathCostFromDestToRegion(num14) * PathFinder.RegionHeuristicWeightByNodesOpened.Evaluate((float)num4)); } else { int dx = Math.Abs(num12 - x); int dz = Math.Abs(num13 - z); int num16 = GenMath.OctileDistance(dx, dz, num8, num9); this.calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)num16 * num7); } } int num17; int num18 = num17 + this.calcGrid[num14].heuristicCost; this.calcGrid[num14].parentIndex = num; this.calcGrid[num14].knownCost = num17; this.calcGrid[num14].status = this.statusOpenValue; this.calcGrid[num14].costNodeCost = num18; num4++; this.openList.Push(new CostNode(num14, num18)); continue; IL_0803: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } continue; } num15 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } continue; } num15 += 70; } goto IL_09bf; IL_09bf: int num19 = (i <= 3) ? num8 : num9; num19 += num15; if (!flag10) { num19 += array[num14]; } if (byteGrid != null) { num19 += byteGrid[num14] * 8; } if (allowedArea != null && !allowedArea[num14]) { num19 += 600; } if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num12, 0, num13), pawn, false, false)) { num19 += 175; } Building building2 = this.edificeGrid[num14]; if (building2 != null) { this.PfProfilerBeginSample("Edifices"); int buildingCost = PathFinder.GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == 2147483647) { this.PfProfilerEndSample(); continue; } num19 += buildingCost; this.PfProfilerEndSample(); } num17 = num19 + this.calcGrid[num].knownCost; status = this.calcGrid[num14].status; if (status != this.statusClosedValue && status != this.statusOpenValue) { goto IL_0b0e; } int num20 = 0; if (status == this.statusClosedValue) { num20 = num8; } if (this.calcGrid[num14].knownCost > num17 + num20) { goto IL_0b0e; } continue; IL_0897: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } continue; } num15 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } continue; } num15 += 70; } goto IL_09bf; IL_0749: switch (i) { case 4: break; case 5: goto IL_0803; case 6: goto IL_0897; case 7: goto IL_092b; default: goto IL_09bf; } if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } continue; } num15 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } continue; } num15 += 70; } goto IL_09bf; } this.PfProfilerEndSample(); num3++; this.calcGrid[num].status = this.statusClosedValue; if (num4 >= 2000 && flag7 && !flag9) { flag9 = true; this.regionCostCalculator.Init(cellRect, traverseParms, num8, num9, byteGrid, allowedArea, this.disallowedCornerIndices); this.InitStatusesAndPushStartNode(ref num, start); num4 = 0; num3 = 0; } continue; } break; } Log.Warning(pawn + " pathing from " + start + " to " + dest + " hit search limit of " + 160000 + " cells."); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); }
//public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, Pawn pawn, PathEndMode peMode = PathEndMode.OnCell) //{ // bool flag = false; // if (pawn != null && pawn.CurJob != null && pawn.CurJob.canBash) // { // flag = true; // } // Danger maxDanger = Danger.Deadly; // bool canBash = flag; // return this.FindPath(start, dest, TraverseParms.For(pawn, maxDanger, TraverseMode.ByPawn, canBash), peMode); //} public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { //Walk through Walls Mode if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } //Check if there's a pawn to path for Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != this.map) { Log.Error(string.Concat(new object[] { "Tried to FindPath for pawn which is spawned in another map. His map CVPathFinder should have been used, not this one. pawn=", pawn, " pawn.Map=", pawn.Map, " map=", this.map })); return(PawnPath.NotFound); } //Make sure the start is valid if (!start.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid start ", start, ", pawn= ", pawn })); return(PawnPath.NotFound); } //Make sure the end is valid if (!dest.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid dest ", dest, ", pawn= ", pawn })); return(PawnPath.NotFound); } //Check if it's possible to get to the destination Log.Error(traverseParms.mode.ToString()); if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!this.map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } Log.Error("a"); this.PfProfilerBeginSample(string.Concat(new object[] { "FindPath for ", pawn, " from ", start, " to ", dest, (!dest.HasThing) ? string.Empty : (" at " + dest.Cell) })); this.cellIndices = this.map.cellIndices; this.pathGrid = this.map.pathGrid; this.edificeGrid = this.map.edificeGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int num = this.cellIndices.CellToIndex(start); int num2 = this.cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = (pawn == null) ? null : pawn.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = this.CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = this.map.pathGrid.pathGrid; EdificeGrid edificeGrid = this.map.edificeGrid; int num3 = 0; int num4 = 0; Area allowedArea = this.GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && start.GetRegion(this.map, RegionType.Set_Passable) != null; bool flag8 = !flag || !flag3; bool flag9 = false; int num5 = 0; int num6 = 0; float num7 = this.DetermineHeuristicStrength(pawn, start, dest); int num8; int num9; Log.Error("b"); if (pawn != null) { num8 = pawn.TicksPerMoveCardinal; num9 = pawn.TicksPerMoveDiagonal; } else { num8 = 13; num9 = 18; } this.CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); this.InitStatusesAndPushStartNode(ref num, start); while (true) { Log.Error("c"); this.PfProfilerBeginSample("Open cell"); if (this.openList.Count <= 0) { break; } num5 += this.openList.Count; num6++; CVPathFinder.CostNode costNode = this.openList.Pop(); num = costNode.index; if (costNode.cost != this.calcGrid[num].costNodeCost) { Log.Error("d"); this.PfProfilerEndSample(); } else if (this.calcGrid[num].status == this.statusClosedValue) { Log.Error("e"); this.PfProfilerEndSample(); } else { Log.Error("f"); IntVec3 c = this.cellIndices.IndexToCell(num); int x2 = c.x; int z2 = c.z; if (flag6) { this.DebugFlash(c, (float)this.calcGrid[num].knownCost / 1500f, this.calcGrid[num].knownCost.ToString()); } if (flag4) { if (num == num2) { goto Block_27; } } else if (cellRect.Contains(c) && !this.disallowedCornerIndices.Contains(num)) { goto Block_29; } if (num3 > 160000) { goto Block_30; } this.PfProfilerEndSample(); this.PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num10 = (uint)(x2 + CVPathFinder.Directions[i]); uint num11 = (uint)(z2 + CVPathFinder.Directions[i + 8]); if ((ulong)num10 < (ulong)((long)this.mapSizeX) && (ulong)num11 < (ulong)((long)this.mapSizeZ)) { int num12 = (int)num10; int num13 = (int)num11; int num14 = this.cellIndices.CellToIndex(num12, num13); if (this.calcGrid[num14].status != this.statusClosedValue || flag9) { int num15 = 0; bool flag10 = false; if (flag2 || !new IntVec3(num12, 0, num13).GetTerrain(this.map).HasTag("Water") || (pawn.GetComp <CompVehicle>() != null && pawn.GetComp <CompVehicle>().Props.vehicleType == VehicleType.Amphibious)) { if (!this.pathGrid.WalkableFast(num14)) { if (!flag) { if (flag6) { this.DebugFlash(new IntVec3(num12, 0, num13), 0.22f, "walk"); } goto IL_C2E; } flag10 = true; num15 += 70; Building building = edificeGrid[num14]; if (building == null) { goto IL_C2E; } if (!CVPathFinder.IsDestroyable(building)) { goto IL_C2E; } num15 += (int)((float)building.HitPoints * 0.11f); } if (i > 3) { switch (i) { case 4: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } break; case 5: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } break; case 6: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } break; case 7: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } goto IL_C2E; } num15 += 70; } break; } } Log.Error("g"); int num16 = (i <= 3) ? num8 : num9; num16 += num15; if (!flag10) { num16 += array[num14]; } if (byteGrid != null) { num16 += (int)(byteGrid[num14] * 8); } if (allowedArea != null && !allowedArea[num14]) { num16 += 600; } if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num12, 0, num13), pawn, false, false)) { num16 += 175; } Log.Error("h"); Building building2 = this.edificeGrid[num14]; if (building2 != null) { this.PfProfilerBeginSample("Edifices"); int buildingCost = CVPathFinder.GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == 2147483647) { this.PfProfilerEndSample(); goto IL_C2E; } num16 += buildingCost; this.PfProfilerEndSample(); } int num17 = num16 + this.calcGrid[num].knownCost; ushort status = this.calcGrid[num14].status; if (status == this.statusClosedValue || status == this.statusOpenValue) { int num18 = 0; if (status == this.statusClosedValue) { num18 = num8; } if (this.calcGrid[num14].knownCost <= num17 + num18) { goto IL_C2E; } } if (status != this.statusClosedValue && status != this.statusOpenValue) { if (flag9) { this.calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)this.regionCostCalculator.GetPathCostFromDestToRegion(num14) * CVPathFinder.RegionHeuristicWeightByNodesOpened.Evaluate((float)num4)); } else { int dx = Math.Abs(num12 - x); int dz = Math.Abs(num13 - z); int num19 = GenMath.OctileDistance(dx, dz, num8, num9); this.calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)num19 * num7); } } int num20 = num17 + this.calcGrid[num14].heuristicCost; this.calcGrid[num14].parentIndex = num; this.calcGrid[num14].knownCost = num17; this.calcGrid[num14].status = this.statusOpenValue; this.calcGrid[num14].costNodeCost = num20; num4++; this.openList.Push(new CVPathFinder.CostNode(num14, num20)); } } } IL_C2E :; } this.PfProfilerEndSample(); num3++; this.calcGrid[num].status = this.statusClosedValue; if (num4 >= 2000 && flag7 && !flag9) { flag9 = true; this.regionCostCalculator.Init(cellRect, traverseParms, num8, num9, byteGrid, allowedArea, this.disallowedCornerIndices); this.InitStatusesAndPushStartNode(ref num, start); num4 = 0; num3 = 0; } } } string text = (pawn == null || pawn.CurJob == null) ? "null" : pawn.CurJob.ToString(); string text2 = (pawn == null || pawn.Faction == null) ? "null" : pawn.Faction.ToString(); Log.Warning(string.Concat(new object[] { pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2 })); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); Block_27: this.PfProfilerEndSample(); PawnPath result = this.FinalizedPath(num); this.PfProfilerEndSample(); return(result); Block_29: this.PfProfilerEndSample(); PawnPath result2 = this.FinalizedPath(num); this.PfProfilerEndSample(); return(result2); Block_30: Log.Warning(string.Concat(new object[] { pawn, " pathing from ", start, " to ", dest, " hit search limit of ", 160000, " cells." })); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); }
private static bool setup(IntVec3 s, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode, Map map) { sMap = map; sMapW = map.Size.x; sMapH = map.Size.z; if (s.x < 0 || s.y < 0 || s.z < 0 || s.x >= sMapW || s.z >= sMapH) { #if DEBUG_LOG_ERROR Log.Error("Start point is out side of map:" + s); #endif return(true); } IntVec3 e = dest.Cell; if (e.x < 0 || e.y < 0 || e.z < 0 || e.x >= sMapW || e.z >= sMapH) { #if DEBUG_LOG_ERROR Log.Error("End point is out side of map:" + e); #endif return(true); } if (dest.HasThing && dest.Thing.Map != map) { #if DEBUG_LOG_ERROR Log.Error("Tartget thing is not this map:" + dest.Thing.ThingID); #endif return(true); } int w = (sMapW + NodeSetBitMask) >> NodeSetBitShift; sMapW4 = w; int h = (sMapH + NodeSetBitMask) >> NodeSetBitShift; int wh_size = w * h; if (sNodeSet == null || sNodeSet.Length < wh_size) { sNodeSet = new NodeSet[wh_size]; } sEndX = dest.Cell.x; sEndY = dest.Cell.z; sPathGrid = sMap.pathGrid; sPathGridArray = sMap.pathGrid.pathGrid; sEdifice = sMap.edificeGrid.InnerArray; sTopGrid = sMap.terrainGrid.topGrid; sBlueprint = sMap.blueprintGrid.InnerArray; sTraverseParms = traverseParms; Pawn pawn = traverseParms.pawn; if (pawn != null) { if (!pawn.Spawned || pawn.Map != map) { #if DEBUG_LOG_ERROR Log.Error("Pawn is not spawned or diff map"); #endif return(true); } sCardinal = pawn.TicksPerMoveCardinal; sDiagonal = pawn.TicksPerMoveDiagonal; sAvoidGrid = pawn.GetAvoidGrid(true); sAllowedArea = allowedArea(pawn); sDrafted = pawn.Drafted; sShouldCollideWithPawns = PawnUtility.ShouldCollideWithPawns(pawn); } else { sCardinal = DefaultMoveTicksCardinal; sDiagonal = DefaultMoveTicksDiagonal; sAvoidGrid = null; sAllowedArea = null; sDrafted = false; sShouldCollideWithPawns = false; } sD2C = sDiagonal - (2 * sCardinal); sAllowWater = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; sPassAllDestroyableThings = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; setupDestRect(dest, traverseParms, peMode, sMap); return(false); }
public static IAttackTarget BestAttackTarget(IAttackTargetSearcher searcher, TargetScanFlags flags, Predicate <Thing> validator = null, float minDist = 0f, float maxDist = 9999f, IntVec3 locus = default(IntVec3), float maxTravelRadiusFromLocus = float.MaxValue, bool canBash = false, bool canTakeTargetsCloserThanEffectiveMinRange = true) { Thing searcherThing = searcher.Thing; Pawn searcherPawn = searcher as Pawn; Verb verb = searcher.CurrentEffectiveVerb; if (verb == null) { Log.Error("BestAttackTarget with " + searcher.ToStringSafe() + " who has no attack verb."); return(null); } bool onlyTargetMachines = verb.IsEMP(); float minDistSquared = minDist * minDist; float num = maxTravelRadiusFromLocus + verb.verbProps.range; float maxLocusDistSquared = num * num; Func <IntVec3, bool> losValidator = null; if ((flags & TargetScanFlags.LOSBlockableByGas) != 0) { losValidator = delegate(IntVec3 vec3) { Gas gas = vec3.GetGas(searcherThing.Map); return(gas == null || !gas.def.gas.blockTurretTracking); }; } Predicate <IAttackTarget> innerValidator = delegate(IAttackTarget t) { Thing thing = t.Thing; if (t == searcher) { return(false); } if (minDistSquared > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < minDistSquared) { return(false); } if (!canTakeTargetsCloserThanEffectiveMinRange) { float num2 = verb.verbProps.EffectiveMinRange(thing, searcherThing); if (num2 > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < num2 * num2) { return(false); } } if (maxTravelRadiusFromLocus < 9999f && (float)(thing.Position - locus).LengthHorizontalSquared > maxLocusDistSquared) { return(false); } if (!searcherThing.HostileTo(thing)) { return(false); } if (validator != null && !validator(thing)) { return(false); } if (searcherPawn != null) { Lord lord = searcherPawn.GetLord(); if (lord != null && !lord.LordJob.ValidateAttackTarget(searcherPawn, thing)) { return(false); } } if ((flags & TargetScanFlags.NeedLOSToAll) != 0) { if (losValidator != null && (!losValidator(searcherThing.Position) || !losValidator(thing.Position))) { return(false); } if (!searcherThing.CanSee(thing, losValidator)) { if (t is Pawn) { if ((flags & TargetScanFlags.NeedLOSToPawns) != 0) { return(false); } } else if ((flags & TargetScanFlags.NeedLOSToNonPawns) != 0) { return(false); } } } if (((flags & TargetScanFlags.NeedThreat) != 0 || (flags & TargetScanFlags.NeedAutoTargetable) != 0) && t.ThreatDisabled(searcher)) { return(false); } if ((flags & TargetScanFlags.NeedAutoTargetable) != 0 && !IsAutoTargetable(t)) { return(false); } if ((flags & TargetScanFlags.NeedActiveThreat) != 0 && !GenHostility.IsActiveThreatTo(t, searcher.Thing.Faction)) { return(false); } Pawn pawn = t as Pawn; if (onlyTargetMachines && pawn != null && pawn.RaceProps.IsFlesh) { return(false); } if ((flags & TargetScanFlags.NeedNonBurning) != 0 && thing.IsBurning()) { return(false); } if (searcherThing.def.race != null && (int)searcherThing.def.race.intelligence >= 2) { CompExplosive compExplosive = thing.TryGetComp <CompExplosive>(); if (compExplosive != null && compExplosive.wickStarted) { return(false); } } if (thing.def.size.x == 1 && thing.def.size.z == 1) { if (thing.Position.Fogged(thing.Map)) { return(false); } } else { bool flag2 = false; foreach (IntVec3 item in thing.OccupiedRect()) { if (!item.Fogged(thing.Map)) { flag2 = true; break; } } if (!flag2) { return(false); } } return(true); }; if (HasRangedAttack(searcher) && (searcherPawn == null || !searcherPawn.InAggroMentalState)) { tmpTargets.Clear(); tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher)); if ((flags & TargetScanFlags.NeedReachable) != 0) { Predicate <IAttackTarget> oldValidator2 = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator2(t) && CanReach(searcherThing, t.Thing, canBash)); } bool flag = false; for (int i = 0; i < tmpTargets.Count; i++) { IAttackTarget attackTarget = tmpTargets[i]; if (attackTarget.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) && innerValidator(attackTarget) && CanShootAtFromCurrentPosition(attackTarget, searcher, verb)) { flag = true; break; } } IAttackTarget result; if (flag) { tmpTargets.RemoveAll((IAttackTarget x) => !x.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) || !innerValidator(x)); result = GetRandomShootingTargetByScore(tmpTargets, searcher, verb); } else { result = (IAttackTarget)GenClosest.ClosestThing_Global(validator: ((flags & TargetScanFlags.NeedReachableIfCantHitFromMyPos) == 0 || (flags & TargetScanFlags.NeedReachable) != 0) ? ((Predicate <Thing>)((Thing t) => innerValidator((IAttackTarget)t))) : ((Predicate <Thing>)((Thing t) => innerValidator((IAttackTarget)t) && (CanReach(searcherThing, t, canBash) || CanShootAtFromCurrentPosition((IAttackTarget)t, searcher, verb)))), center: searcherThing.Position, searchSet: tmpTargets, maxDistance: maxDist); } tmpTargets.Clear(); return(result); } if (searcherPawn != null && searcherPawn.mindState.duty != null && searcherPawn.mindState.duty.radius > 0f && !searcherPawn.InMentalState) { Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = delegate(IAttackTarget t) { if (!oldValidator(t)) { return(false); } return(t.Thing.Position.InHorDistOf(searcherPawn.mindState.duty.focus.Cell, searcherPawn.mindState.duty.radius) ? true : false); }; } IAttackTarget attackTarget2 = (IAttackTarget)GenClosest.ClosestThingReachable(searcherThing.Position, searcherThing.Map, ThingRequest.ForGroup(ThingRequestGroup.AttackTarget), PathEndMode.Touch, TraverseParms.For(searcherPawn, Danger.Deadly, TraverseMode.ByPawn, canBash), maxDist, (Thing x) => innerValidator((IAttackTarget)x), null, 0, (maxDist > 800f) ? (-1) : 40); if (attackTarget2 != null && PawnUtility.ShouldCollideWithPawns(searcherPawn)) { IAttackTarget attackTarget3 = FindBestReachableMeleeTarget(innerValidator, searcherPawn, maxDist, canBash); if (attackTarget3 != null) { float lengthHorizontal = (searcherPawn.Position - attackTarget2.Thing.Position).LengthHorizontal; float lengthHorizontal2 = (searcherPawn.Position - attackTarget3.Thing.Position).LengthHorizontal; if (Mathf.Abs(lengthHorizontal - lengthHorizontal2) < 50f) { attackTarget2 = attackTarget3; } } } return(attackTarget2); }
public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != this.map) { Log.Error(string.Concat(new object[] { "Tried to FindPath for pawn which is spawned in another map. His map PathFinder should have been used, not this one. pawn=", pawn, " pawn.Map=", pawn.Map, " map=", this.map }), false); return(PawnPath.NotFound); } if (!start.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid start ", start, ", pawn= ", pawn }), false); return(PawnPath.NotFound); } if (!dest.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid dest ", dest, ", pawn= ", pawn }), false); return(PawnPath.NotFound); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!this.map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } this.PfProfilerBeginSample(string.Concat(new object[] { "FindPath for ", pawn, " from ", start, " to ", dest, (!dest.HasThing) ? string.Empty : (" at " + dest.Cell) })); this.cellIndices = this.map.cellIndices; this.pathGrid = this.map.pathGrid; this.edificeGrid = this.map.edificeGrid.InnerArray; this.blueprintGrid = this.map.blueprintGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int num = this.cellIndices.CellToIndex(start); int num2 = this.cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = (pawn == null) ? null : pawn.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = this.CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = this.map.pathGrid.pathGrid; TerrainDef[] topGrid = this.map.terrainGrid.topGrid; EdificeGrid edificeGrid = this.map.edificeGrid; int num3 = 0; int num4 = 0; Area allowedArea = this.GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && start.GetRegion(this.map, RegionType.Set_Passable) != null && flag2; bool flag8 = !flag || !flag3; bool flag9 = false; bool flag10 = pawn != null && pawn.Drafted; bool flag11 = pawn != null && pawn.IsColonist; int num5 = (!flag11) ? 2000 : 100000; int num6 = 0; int num7 = 0; float num8 = this.DetermineHeuristicStrength(pawn, start, dest); int num9; int num10; if (pawn != null) { num9 = pawn.TicksPerMoveCardinal; num10 = pawn.TicksPerMoveDiagonal; } else { num9 = 13; num10 = 18; } this.CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); this.InitStatusesAndPushStartNode(ref num, start); for (;;) { this.PfProfilerBeginSample("Open cell"); if (this.openList.Count <= 0) { break; } num6 += this.openList.Count; num7++; PathFinder.CostNode costNode = this.openList.Pop(); num = costNode.index; if (costNode.cost != this.calcGrid[num].costNodeCost) { this.PfProfilerEndSample(); } else if (this.calcGrid[num].status == this.statusClosedValue) { this.PfProfilerEndSample(); } else { IntVec3 c = this.cellIndices.IndexToCell(num); int x2 = c.x; int z2 = c.z; if (flag6) { this.DebugFlash(c, (float)this.calcGrid[num].knownCost / 1500f, this.calcGrid[num].knownCost.ToString()); } if (flag4) { if (num == num2) { goto Block_32; } } else if (cellRect.Contains(c) && !this.disallowedCornerIndices.Contains(num)) { goto Block_34; } if (num3 > 160000) { goto Block_35; } this.PfProfilerEndSample(); this.PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num11 = (uint)(x2 + PathFinder.Directions[i]); uint num12 = (uint)(z2 + PathFinder.Directions[i + 8]); if ((ulong)num11 < (ulong)((long)this.mapSizeX) && (ulong)num12 < (ulong)((long)this.mapSizeZ)) { int num13 = (int)num11; int num14 = (int)num12; int num15 = this.cellIndices.CellToIndex(num13, num14); if (this.calcGrid[num15].status != this.statusClosedValue || flag9) { int num16 = 0; bool flag12 = false; if (flag2 || !new IntVec3(num13, 0, num14).GetTerrain(this.map).HasTag("Water")) { if (!this.pathGrid.WalkableFast(num15)) { if (!flag) { if (flag6) { this.DebugFlash(new IntVec3(num13, 0, num14), 0.22f, "walk"); } goto IL_D53; } flag12 = true; num16 += 70; Building building = edificeGrid[num15]; if (building == null) { goto IL_D53; } if (!PathFinder.IsDestroyable(building)) { goto IL_D53; } num16 += (int)((float)building.HitPoints * 0.2f); } if (i > 3) { switch (i) { case 4: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } break; case 5: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } break; case 6: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } break; case 7: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } goto IL_D53; } num16 += 70; } break; } } int num17 = (i <= 3) ? num9 : num10; num17 += num16; if (!flag12) { num17 += array[num15]; if (flag10) { num17 += topGrid[num15].extraDraftedPerceivedPathCost; } else { num17 += topGrid[num15].extraNonDraftedPerceivedPathCost; } } if (byteGrid != null) { num17 += (int)(byteGrid[num15] * 8); } if (allowedArea != null && !allowedArea[num15]) { num17 += 600; } if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num13, 0, num14), pawn, false, false, true)) { num17 += 175; } Building building2 = this.edificeGrid[num15]; if (building2 != null) { this.PfProfilerBeginSample("Edifices"); int buildingCost = PathFinder.GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == 2147483647) { this.PfProfilerEndSample(); goto IL_D53; } num17 += buildingCost; this.PfProfilerEndSample(); } List <Blueprint> list = this.blueprintGrid[num15]; if (list != null) { this.PfProfilerBeginSample("Blueprints"); int num18 = 0; for (int j = 0; j < list.Count; j++) { num18 = Mathf.Max(num18, PathFinder.GetBlueprintCost(list[j], pawn)); } if (num18 == 2147483647) { this.PfProfilerEndSample(); goto IL_D53; } num17 += num18; this.PfProfilerEndSample(); } int num19 = num17 + this.calcGrid[num].knownCost; ushort status = this.calcGrid[num15].status; if (status == this.statusClosedValue || status == this.statusOpenValue) { int num20 = 0; if (status == this.statusClosedValue) { num20 = num9; } if (this.calcGrid[num15].knownCost <= num19 + num20) { goto IL_D53; } } if (status != this.statusClosedValue && status != this.statusOpenValue) { if (flag9) { this.calcGrid[num15].heuristicCost = Mathf.RoundToInt((float)this.regionCostCalculator.GetPathCostFromDestToRegion(num15) * PathFinder.RegionHeuristicWeightByNodesOpened.Evaluate((float)num4)); } else { int dx = Math.Abs(num13 - x); int dz = Math.Abs(num14 - z); int num21 = GenMath.OctileDistance(dx, dz, num9, num10); this.calcGrid[num15].heuristicCost = Mathf.RoundToInt((float)num21 * num8); } } int num22 = num19 + this.calcGrid[num15].heuristicCost; this.calcGrid[num15].parentIndex = num; this.calcGrid[num15].knownCost = num19; this.calcGrid[num15].status = this.statusOpenValue; this.calcGrid[num15].costNodeCost = num22; num4++; this.openList.Push(new PathFinder.CostNode(num15, num22)); } } } IL_D53 :; } this.PfProfilerEndSample(); num3++; this.calcGrid[num].status = this.statusClosedValue; if (num4 >= num5 && flag7 && !flag9) { flag9 = true; this.regionCostCalculator.Init(cellRect, traverseParms, num9, num10, byteGrid, allowedArea, flag10, this.disallowedCornerIndices); this.InitStatusesAndPushStartNode(ref num, start); num4 = 0; num3 = 0; } } } string text = (pawn == null || pawn.CurJob == null) ? "null" : pawn.CurJob.ToString(); string text2 = (pawn == null || pawn.Faction == null) ? "null" : pawn.Faction.ToString(); Log.Warning(string.Concat(new object[] { pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2 }), false); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); Block_32: this.PfProfilerEndSample(); PawnPath result = this.FinalizedPath(num, flag9); this.PfProfilerEndSample(); return(result); Block_34: this.PfProfilerEndSample(); PawnPath result2 = this.FinalizedPath(num, flag9); this.PfProfilerEndSample(); return(result2); Block_35: Log.Warning(string.Concat(new object[] { pawn, " pathing from ", start, " to ", dest, " hit search limit of ", 160000, " cells." }), false); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); }
public static IAttackTarget BestAttackTarget(IAttackTargetSearcher searcher, TargetScanFlags flags, Predicate <Thing> validator = null, float minDist = 0f, float maxDist = 9999f, IntVec3 locus = default(IntVec3), float maxTravelRadiusFromLocus = 3.40282347E+38f, bool canBash = false, bool canTakeTargetsCloserThanEffectiveMinRange = true) { Log.Message("Using VEF_AttackTargetFinder"); Thing searcherThing = searcher.Thing; Pawn searcherPawn = searcher as Pawn; Verb verb = searcher.CurrentEffectiveVerb; Log.Message("searcher is " + searcherPawn.Label); Log.Message("Attack Verb is " + verb.ToString()); if (verb == null) { Log.Error("BestAttackTarget with " + searcher.ToStringSafe <IAttackTargetSearcher>() + " who has no attack verb.", false); return(null); } bool onlyTargetMachines = verb.IsEMP(); float minDistSquared = minDist * minDist; float num = maxTravelRadiusFromLocus + verb.verbProps.range; float maxLocusDistSquared = num * num; Func <IntVec3, bool> losValidator = null; if ((byte)(flags & TargetScanFlags.LOSBlockableByGas) != 0) { losValidator = delegate(IntVec3 vec3) { Gas gas = vec3.GetGas(searcherThing.Map); return(gas == null || !gas.def.gas.blockTurretTracking); }; } Predicate <IAttackTarget> innerValidator = delegate(IAttackTarget t) { Log.Message("Beginning of innerValidator - Validating for " + t.Thing.Label); Thing thing = t.Thing; if (t == searcher) { Log.Message("IAttackTarget is Searcher - Invalid target"); return(false); } if (minDistSquared > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < minDistSquared) { Log.Message("Verb has minDistance and IAttackTarget is within minimum distance - Invalid target"); return(false); } if (!canTakeTargetsCloserThanEffectiveMinRange) { Log.Message("Searcher cannot attack targets closer that verb.verbProps.EffectiveMinRange"); float num2 = verb.verbProps.EffectiveMinRange(thing, searcherThing); if (num2 > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < num2 * num2) { Log.Message("Distance between Searcher and IAttackTarget is greater than 0, but less than verb.verbProps.EffectiveMinimumRange - Invalid target"); return(false); } } if (maxTravelRadiusFromLocus < 9999f && (float)(thing.Position - locus).LengthHorizontalSquared > maxLocusDistSquared) { Log.Message("Seacrher has limited permissable movement and IAttackTarget is outside of that distance - Invalid target"); return(false); } if (!searcherThing.HostileTo(thing)) { Log.Message("Searcher is not hostile towards IAttackTarget - Invalid target"); return(false); } if (validator != null && !validator(thing)) { Log.Message("Searcher has validator and validator failed. Validator checked for " + validator.GetType().ToString()); return(false); } if (searcherPawn != null) { Log.Message("Searcher is Pawn - Get Lord"); Lord lord = searcherPawn.GetLord(); if (lord != null && !lord.LordJob.ValidateAttackTarget(searcherPawn, thing)) { Log.Message("Lord is in incorrect state - Return false"); return(false); } } if ((byte)(flags & TargetScanFlags.NeedLOSToAll) != 0 && !searcherThing.CanSee(thing, losValidator)) { Log.Message("Searher needs LOS to some or all targets, but cannot see target."); if (t is Pawn) { Log.Message("IAttackTarget is Pawn"); if ((byte)(flags & TargetScanFlags.NeedLOSToPawns) != 0) { Log.Message("Searcher needs LOS to Pawn - Invalid target"); return(false); } } else if ((byte)(flags & TargetScanFlags.NeedLOSToNonPawns) != 0) { Log.Message("IAttackTarget is nonPawn"); Log.Message("Searcher needs LOS to NonPawns - Invalid target"); return(false); } } if ((byte)(flags & TargetScanFlags.NeedThreat) != 0 && t.ThreatDisabled(searcher)) { Log.Message("Searcher needs Threat, IAttackTarget is not threat - Invalid target"); return(false); } Pawn pawn = t as Pawn; if (onlyTargetMachines && pawn != null && pawn.RaceProps.IsFlesh) { Log.Message("Verb is EMP and target is flesh - Invalid target"); return(false); } if ((byte)(flags & TargetScanFlags.NeedNonBurning) != 0 && thing.IsBurning()) { Log.Message("Verb is incendiary and target is already burning - Invalid target"); return(false); } if (searcherThing.def.race != null && searcherThing.def.race.intelligence >= Intelligence.Humanlike) { Log.Message("Searcher is ToolUser - Validating use of Explosives"); CompExplosive compExplosive = thing.TryGetComp <CompExplosive>(); if (compExplosive != null && compExplosive.wickStarted) { Log.Message("Explosive is already lit - Invalid verb"); return(false); } } if (thing.def.size.x == 1 && thing.def.size.z == 1) { if (thing.Position.Fogged(thing.Map)) { Log.Message("IAttackTarget is in Fog - Invalid target"); return(false); } } else { bool flag2 = false; CellRect.CellRectIterator iterator = thing.OccupiedRect().GetIterator(); while (!iterator.Done()) { if (!iterator.Current.Fogged(thing.Map)) { Log.Message("IAttackTarget is not in Fog - return true"); flag2 = true; break; } iterator.MoveNext(); } if (!flag2) { return(false); } } Log.Message("IAttackTarget is valid target"); return(true); }; if (HasRangedAttack(searcher)) { Log.Message(searcher.Thing.Label + " has ranged verb."); tmpTargets.Clear(); tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher)); Log.Message("initializing tmpTargets"); if ((byte)(flags & TargetScanFlags.NeedReachable) != 0) { Log.Message("TargetScanFlags.NeedReachable"); Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator(t) && CanReach(searcherThing, t.Thing, canBash)); } bool flag = false; if (tmpTargets.Count == 0) { Log.Message("There are no tmpTargets - return null"); return(null); } else { Log.Message("There are " + tmpTargets.Count + " tmpTargets"); } for (int i = 0; i < tmpTargets.Count; i++) { Log.Message("Iterating tmpTargets to check for targets that can be hit from current location. Iteration " + i); IAttackTarget attackTarget = tmpTargets[i]; if (attackTarget.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) && innerValidator(attackTarget) && CanShootAtFromCurrentPosition(attackTarget, searcher, verb)) { Log.Message("tempTarget " + i + " can be hit from current position."); flag = true; break; } Log.Message("tempTarget " + i + " cannot be hit from current position."); } IAttackTarget result = null; if (flag) { Log.Message("A target can be shot from current position. Clearing tempTargets outside of range and picking one."); tmpTargets.RemoveAll((IAttackTarget x) => !x.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) || !innerValidator(x)); result = GetRandomShootingTargetByScore(tmpTargets, searcher, verb); Log.Message("Target that can be shot from present location is: " + result.Thing.ToString()); } else { Log.Message("No target can be hit from current position. Refining validator..."); Predicate <Thing> validator2; if ((byte)(flags & TargetScanFlags.NeedReachableIfCantHitFromMyPos) != 0 && (byte)(flags & TargetScanFlags.NeedReachable) == 0) { Log.Message("Using validator2 where (byte)TargetScanFalgs flags are not 0"); validator2 = ((Thing t) => innerValidator((IAttackTarget)t) && (CanReach(searcherThing, t, canBash) || CanShootAtFromCurrentPosition((IAttackTarget)t, searcher, verb))); } else { Log.Message("Using validator2 where (byte)TargetScanFlags flags are 0"); validator2 = ((Thing t) => innerValidator((IAttackTarget)t)); } Log.Message("--- MARKER ---"); result = (IAttackTarget)GenClosest.ClosestThing_Global(searcherThing.Position, tmpTargets, maxDist, validator2, null); if (result == null) { Log.Message("Taregt selection complete. Clearing tmpTargets. There is no valid target"); } else { Log.Message("Taregt selection complete. Clearing tmpTargets. Final target is: " + result.Thing.ToString()); } } tmpTargets.Clear(); return(result); } if (searcherPawn != null && searcherPawn.mindState.duty != null && searcherPawn.mindState.duty.radius > 0f && !searcherPawn.InMentalState) { Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator(t) && t.Thing.Position.InHorDistOf(searcherPawn.mindState.duty.focus.Cell, searcherPawn.mindState.duty.radius)); } IntVec3 position = searcherThing.Position; Map map = searcherThing.Map; ThingRequest thingReq = ThingRequest.ForGroup(ThingRequestGroup.AttackTarget); PathEndMode peMode = PathEndMode.Touch; Pawn searcherPawn2 = searcherPawn; Danger maxDanger = Danger.Deadly; bool canBash2 = canBash; TraverseParms traverseParams = TraverseParms.For(searcherPawn2, maxDanger, TraverseMode.ByPawn, canBash2); float maxDist2 = maxDist; Predicate <Thing> validator3 = (Thing x) => innerValidator((IAttackTarget)x); int searchRegionsMax = (maxDist <= 800f) ? 40 : -1; IAttackTarget attackTarget2 = (IAttackTarget)GenClosest.ClosestThingReachable(position, map, thingReq, peMode, traverseParams, maxDist2, validator3, null, 0, searchRegionsMax, false, RegionType.Set_Passable, false); if (attackTarget2 != null && PawnUtility.ShouldCollideWithPawns(searcherPawn)) { IAttackTarget attackTarget3 = FindBestReachableMeleeTarget(innerValidator, searcherPawn, maxDist, canBash); if (attackTarget3 != null) { float lengthHorizontal = (searcherPawn.Position - attackTarget2.Thing.Position).LengthHorizontal; float lengthHorizontal2 = (searcherPawn.Position - attackTarget3.Thing.Position).LengthHorizontal; if (Mathf.Abs(lengthHorizontal - lengthHorizontal2) < 50f) { attackTarget2 = attackTarget3; } } } return(attackTarget2); }
public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != map) { Log.Warning(String.Format("Map object was rebuilt, but new pather was not assigned to new map (new {0}) (old {1})", pawn.Map.uniqueID, map.uniqueID)); Log.Error("Tried to FindPath for pawn which is spawned in another map. His map PathFinder should have been used, not this one. pawn=" + pawn + " pawn.Map=" + pawn.Map + " map=" + map); return(PawnPath.NotFound); } if (!start.IsValid) { Log.Error("Tried to FindPath with invalid start " + start + ", pawn= " + pawn); return(PawnPath.NotFound); } if (!dest.IsValid) { Log.Error("Tried to FindPath with invalid dest " + dest + ", pawn= " + pawn); return(PawnPath.NotFound); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } PfProfilerBeginSample("FindPath for " + pawn + " from " + start + " to " + dest + (dest.HasThing ? (" at " + dest.Cell) : "")); cellIndices = map.cellIndices; pathGrid = map.pathGrid; this.edificeGrid = map.edificeGrid.InnerArray; blueprintGrid = map.blueprintGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int curIndex = cellIndices.CellToIndex(start); int num = cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = pawn?.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = map.pathGrid.pathGrid; TerrainDef[] topGrid = map.terrainGrid.topGrid; EdificeGrid edificeGrid = map.edificeGrid; int num2 = 0; int num3 = 0; Area allowedArea = GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = (!flag && start.GetRegion(map) != null) & flag2; bool flag7 = !flag || !flag3; bool flag8 = false; bool flag9 = pawn?.Drafted ?? false; int num4 = (pawn?.IsColonist ?? false) ? 100000 : 2000; int num5 = 0; int num6 = 0; float num7 = DetermineHeuristicStrength(pawn, start, dest); // BEGIN CHANGED SECTION // In case pawn is null int num8 = 13; int num9 = 18; Dictionary <TerrainDef, int> pawnTerrainCacheCardinal = new Dictionary <TerrainDef, int>(); Dictionary <TerrainDef, int> pawnTerrainCacheDiagonal = new Dictionary <TerrainDef, int>(); Dictionary <TerrainDef, bool> pawnSpecialMovementCache = new Dictionary <TerrainDef, bool>(); Dictionary <TerrainDef, bool> pawnImpassibleMovementCache = new Dictionary <TerrainDef, bool>(); Dictionary <TerrainDef, int> pawnTerrainMovementCache = new Dictionary <TerrainDef, int>(); // END CHANGED SECTION CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); InitStatusesAndPushStartNode(ref curIndex, start); while (true) { PfProfilerBeginSample("Open cell"); if (openList.Count <= 0) { string text = (pawn != null && pawn.CurJob != null) ? pawn.CurJob.ToString() : "null"; string text2 = (pawn != null && pawn.Faction != null) ? pawn.Faction.ToString() : "null"; Log.Warning(pawn + " pathing from " + start + " to " + dest + " ran out of cells to process.\nJob:" + text + "\nFaction: " + text2); DebugDrawRichData(); PfProfilerEndSample(); PfProfilerEndSample(); return(PawnPath.NotFound); } num5 += openList.Count; num6++; CostNode costNode = openList.Pop(); curIndex = costNode.index; if (costNode.cost != calcGrid[curIndex].costNodeCost) { PfProfilerEndSample(); continue; } if (calcGrid[curIndex].status == statusClosedValue) { PfProfilerEndSample(); continue; } IntVec3 c = cellIndices.IndexToCell(curIndex); int x2 = c.x; int z2 = c.z; if (flag4) { if (curIndex == num) { PfProfilerEndSample(); PawnPath result = FinalizedPath(curIndex, flag8); PfProfilerEndSample(); return(result); } } else if (cellRect.Contains(c) && !disallowedCornerIndices.Contains(curIndex)) { PfProfilerEndSample(); PawnPath result2 = FinalizedPath(curIndex, flag8); PfProfilerEndSample(); return(result2); } if (num2 > 160000) { break; } PfProfilerEndSample(); PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num10 = (uint)(x2 + Directions[i]); uint num11 = (uint)(z2 + Directions[i + 8]); if (num10 >= mapSizeX || num11 >= mapSizeZ) { continue; } int num12 = (int)num10; int num13 = (int)num11; int num14 = cellIndices.CellToIndex(num12, num13); if (calcGrid[num14].status == statusClosedValue && !flag8) { continue; } // BEGIN CHANGED SECTION IntVec3 targetCell = new IntVec3(num12, 0, num13); TerrainDef targetTerrain = topGrid[num14]; if (pawn != null) { // Use cache of terrain movement indicators to avoid a lot of repeated computation if (!pawnImpassibleMovementCache.TryGetValue(targetTerrain, out bool impassible)) { impassible = pawn.kindDef.UnreachableTerrainCheck(targetTerrain); pawnImpassibleMovementCache[targetTerrain] = impassible; } if (impassible) { // Skip this cell for pathing calculations calcGrid[num14].status = statusClosedValue; continue; } // Overwrite directional move costs if (!pawnTerrainCacheCardinal.TryGetValue(targetTerrain, out num8)) { num8 = pawn.TerrainAwareTicksPerMoveCardinal(targetTerrain); pawnTerrainCacheCardinal[targetTerrain] = num8; } if (!pawnTerrainCacheDiagonal.TryGetValue(targetTerrain, out num9)) { num9 = pawn.TerrainAwareTicksPerMoveDiagonal(targetTerrain); pawnTerrainCacheDiagonal[targetTerrain] = num9; } } // END CHANGED SECTION int num15 = 0; bool flag10 = false; //if (!flag2 && new IntVec3(num12, 0, num13).GetTerrain(map).HasTag("Water")) if (!flag2 && targetTerrain.HasTag("Water")) { continue; } if (!pathGrid.WalkableFast(num14)) { if (!flag) { continue; } flag10 = true; num15 += 70; Building building = edificeGrid[num14]; if (building == null || !IsDestroyable(building)) { continue; } num15 += (int)((float)building.HitPoints * 0.2f); } switch (i) { case 4: if (BlocksDiagonalMovement(curIndex - mapSizeX)) { if (flag7) { continue; } num15 += 70; } if (BlocksDiagonalMovement(curIndex + 1)) { if (flag7) { continue; } num15 += 70; } break; case 5: if (BlocksDiagonalMovement(curIndex + mapSizeX)) { if (flag7) { continue; } num15 += 70; } if (BlocksDiagonalMovement(curIndex + 1)) { if (flag7) { continue; } num15 += 70; } break; case 6: if (BlocksDiagonalMovement(curIndex + mapSizeX)) { if (flag7) { continue; } num15 += 70; } if (BlocksDiagonalMovement(curIndex - 1)) { if (flag7) { continue; } num15 += 70; } break; case 7: if (BlocksDiagonalMovement(curIndex - mapSizeX)) { if (flag7) { continue; } num15 += 70; } if (BlocksDiagonalMovement(curIndex - 1)) { if (flag7) { continue; } num15 += 70; } break; } int num16 = (i > 3) ? num9 : num8; num16 += num15; if (!flag10) { // BEGIN CHANGED SECTION //num16 += array[num14]; //num16 = ((!flag9) ? (num16 + topGrid[num14].extraNonDraftedPerceivedPathCost) : (num16 + topGrid[num14].extraDraftedPerceivedPathCost)); if (pawn == null) { num16 += array[num14]; if (flag9) { num16 += targetTerrain.extraDraftedPerceivedPathCost; } else { num16 += targetTerrain.extraNonDraftedPerceivedPathCost; } } else { // Use cache of terrain perceived cost instead of fixed pathCost grid to avoid a lot of repeated computation while maintaining accuracy if (!pawnTerrainMovementCache.TryGetValue(targetTerrain, out int terrainMoveCost)) { terrainMoveCost = pawn.TerrainMoveCost(targetTerrain); pawnTerrainMovementCache[targetTerrain] = terrainMoveCost; } // This was really really expensive, so we opted to mod this pre-calced value //num16 += pathGrid.TerrainCalculatedCostAt(map, pawn, targetCell, true, IntVec3.Invalid, terrainMoveCost); num16 += pathGrid.ApplyTerrainModToCalculatedCost(targetTerrain, array[num14], terrainMoveCost); // Use cache of terrain movement indicators to avoid a lot of repeated computation if (!pawnSpecialMovementCache.TryGetValue(targetTerrain, out bool specialMovement)) { specialMovement = pawn.TerrainMoveStat(targetTerrain) != StatDefOf.MoveSpeed; pawnSpecialMovementCache[targetTerrain] = specialMovement; } // Skip applying the PerceivedPathCost hack if we've got a specialized speed stat for this terrain if (!specialMovement) { if (flag9) { num16 += targetTerrain.extraDraftedPerceivedPathCost; } else { num16 += targetTerrain.extraNonDraftedPerceivedPathCost; } } } // END CHANGED SECTION } if (byteGrid != null) { num16 += byteGrid[num14] * 8; } if (allowedArea != null && !allowedArea[num14]) { num16 += 600; } //new IntVec3(num12, 0, num13) -> targetCell if (flag5 && PawnUtility.AnyPawnBlockingPathAt(targetCell, pawn, actAsIfHadCollideWithPawnsJob: false, collideOnlyWithStandingPawns: false, forPathFinder: true)) { num16 += 175; } Building building2 = this.edificeGrid[num14]; if (building2 != null) { PfProfilerBeginSample("Edifices"); int buildingCost = GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == int.MaxValue) { PfProfilerEndSample(); continue; } num16 += buildingCost; PfProfilerEndSample(); } List <Blueprint> list = blueprintGrid[num14]; if (list != null) { PfProfilerBeginSample("Blueprints"); int num17 = 0; for (int j = 0; j < list.Count; j++) { num17 = Mathf.Max(num17, GetBlueprintCost(list[j], pawn)); } if (num17 == int.MaxValue) { PfProfilerEndSample(); continue; } num16 += num17; PfProfilerEndSample(); } int num18 = num16 + calcGrid[curIndex].knownCost; ushort status = calcGrid[num14].status; if (status == statusClosedValue || status == statusOpenValue) { int num19 = 0; if (status == statusClosedValue) { num19 = num8; } if (calcGrid[num14].knownCost <= num18 + num19) { continue; } } if (flag8) { calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)regionCostCalculator.GetPathCostFromDestToRegion(num14) * RegionHeuristicWeightByNodesOpened.Evaluate(num3)); if (calcGrid[num14].heuristicCost < 0) { Log.ErrorOnce("Heuristic cost overflow for " + pawn.ToStringSafe() + " pathing from " + start + " to " + dest + ".", pawn.GetHashCode() ^ 0xB8DC389); calcGrid[num14].heuristicCost = 0; } } else if (status != statusClosedValue && status != statusOpenValue) { int dx = Math.Abs(num12 - x); int dz = Math.Abs(num13 - z); int num20 = GenMath.OctileDistance(dx, dz, num8, num9); calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)num20 * num7); } int num21 = num18 + calcGrid[num14].heuristicCost; if (num21 < 0) { Log.ErrorOnce("Node cost overflow for " + pawn.ToStringSafe() + " pathing from " + start + " to " + dest + ".", pawn.GetHashCode() ^ 0x53CB9DE); num21 = 0; } calcGrid[num14].parentIndex = curIndex; calcGrid[num14].knownCost = num18; calcGrid[num14].status = statusOpenValue; calcGrid[num14].costNodeCost = num21; num3++; openList.Push(new CostNode(num14, num21)); } PfProfilerEndSample(); num2++; calcGrid[curIndex].status = statusClosedValue; if (num3 >= num4 && flag6 && !flag8) { flag8 = true; regionCostCalculator.Init(cellRect, traverseParms, num8, num9, byteGrid, allowedArea, flag9, disallowedCornerIndices); InitStatusesAndPushStartNode(ref curIndex, start); num3 = 0; num2 = 0; } } Log.Warning(pawn + " pathing from " + start + " to " + dest + " hit search limit of " + 160000 + " cells."); DebugDrawRichData(); PfProfilerEndSample(); PfProfilerEndSample(); return(PawnPath.NotFound); }
public override bool Applies() { return(pawn != null && PawnUtility.ShouldCollideWithPawns(pawn)); }
public PawnPath FindPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != map) { Log.Error("Tried to FindPath for pawn which is spawned in another map. His map PathFinder should have been used, not this one. pawn=" + pawn + " pawn.Map=" + pawn.Map + " map=" + map); return(PawnPath.NotFound); } if (!start.IsValid) { Log.Error("Tried to FindPath with invalid start " + start + ", pawn= " + pawn); return(PawnPath.NotFound); } if (!dest.IsValid) { Log.Error("Tried to FindPath with invalid dest " + dest + ", pawn= " + pawn); return(PawnPath.NotFound); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!map.reachability.CanReach(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } PfProfilerBeginSample("FindPath for " + pawn + " from " + start + " to " + dest + ((!dest.HasThing) ? string.Empty : (" at " + dest.Cell))); cellIndices = map.cellIndices; pathGrid = map.pathGrid; this.edificeGrid = map.edificeGrid.InnerArray; blueprintGrid = map.blueprintGrid.InnerArray; IntVec3 cell = dest.Cell; int x = cell.x; IntVec3 cell2 = dest.Cell; int z = cell2.z; int curIndex = cellIndices.CellToIndex(start); int num = cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = pawn?.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = map.pathGrid.pathGrid; TerrainDef[] topGrid = map.terrainGrid.topGrid; EdificeGrid edificeGrid = map.edificeGrid; int num2 = 0; int num3 = 0; Area allowedArea = GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && start.GetRegion(map) != null && flag2; bool flag8 = !flag || !flag3; bool flag9 = false; bool flag10 = pawn?.Drafted ?? false; int num4 = (!(pawn?.IsColonist ?? false)) ? 2000 : 100000; int num5 = 0; int num6 = 0; float num7 = DetermineHeuristicStrength(pawn, start, dest); int num8; int num9; if (pawn != null) { num8 = pawn.TicksPerMoveCardinal; num9 = pawn.TicksPerMoveDiagonal; } else { num8 = 13; num9 = 18; } CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); InitStatusesAndPushStartNode(ref curIndex, start); while (true) { PfProfilerBeginSample("Open cell"); if (openList.Count <= 0) { string text = (pawn == null || pawn.CurJob == null) ? "null" : pawn.CurJob.ToString(); string text2 = (pawn == null || pawn.Faction == null) ? "null" : pawn.Faction.ToString(); Log.Warning(pawn + " pathing from " + start + " to " + dest + " ran out of cells to process.\nJob:" + text + "\nFaction: " + text2); DebugDrawRichData(); PfProfilerEndSample(); return(PawnPath.NotFound); } num5 += openList.Count; num6++; CostNode costNode = openList.Pop(); curIndex = costNode.index; if (costNode.cost != calcGrid[curIndex].costNodeCost) { PfProfilerEndSample(); } else if (calcGrid[curIndex].status == statusClosedValue) { PfProfilerEndSample(); } else { IntVec3 c = cellIndices.IndexToCell(curIndex); int x2 = c.x; int z2 = c.z; if (flag6) { DebugFlash(c, (float)calcGrid[curIndex].knownCost / 1500f, calcGrid[curIndex].knownCost.ToString()); } if (flag4) { if (curIndex == num) { PfProfilerEndSample(); PawnPath result = FinalizedPath(curIndex, flag9); PfProfilerEndSample(); return(result); } } else if (cellRect.Contains(c) && !disallowedCornerIndices.Contains(curIndex)) { PfProfilerEndSample(); PawnPath result2 = FinalizedPath(curIndex, flag9); PfProfilerEndSample(); return(result2); } if (num2 > 160000) { break; } PfProfilerEndSample(); PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num10 = (uint)(x2 + Directions[i]); uint num11 = (uint)(z2 + Directions[i + 8]); if (num10 < mapSizeX && num11 < mapSizeZ) { int num12 = (int)num10; int num13 = (int)num11; int num14 = cellIndices.CellToIndex(num12, num13); if (calcGrid[num14].status != statusClosedValue || flag9) { int num15 = 0; bool flag11 = false; if (flag2 || !new IntVec3(num12, 0, num13).GetTerrain(map).HasTag("Water")) { if (!pathGrid.WalkableFast(num14)) { if (!flag) { if (flag6) { DebugFlash(new IntVec3(num12, 0, num13), 0.22f, "walk"); } continue; } flag11 = true; num15 += 70; Building building = edificeGrid[num14]; if (building == null || !IsDestroyable(building)) { continue; } num15 += (int)((float)building.HitPoints * 0.2f); } switch (i) { case 4: if (BlocksDiagonalMovement(curIndex - mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } break; } num15 += 70; } if (BlocksDiagonalMovement(curIndex + 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } break; } num15 += 70; } goto default; case 5: if (BlocksDiagonalMovement(curIndex + mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } break; } num15 += 70; } if (BlocksDiagonalMovement(curIndex + 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "corn"); } break; } num15 += 70; } goto default; case 6: if (BlocksDiagonalMovement(curIndex + mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "corn"); } break; } num15 += 70; } if (BlocksDiagonalMovement(curIndex - 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } break; } num15 += 70; } goto default; case 7: if (BlocksDiagonalMovement(curIndex - mapSizeX)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "corn"); } break; } num15 += 70; } if (BlocksDiagonalMovement(curIndex - 1)) { if (flag8) { if (flag6) { DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "corn"); } break; } num15 += 70; } goto default; default: { int num16 = (i <= 3) ? num8 : num9; num16 += num15; if (!flag11) { num16 += array[num14]; num16 = ((!flag10) ? (num16 + topGrid[num14].extraNonDraftedPerceivedPathCost) : (num16 + topGrid[num14].extraDraftedPerceivedPathCost)); } if (byteGrid != null) { num16 += byteGrid[num14] * 8; } if (allowedArea != null && !allowedArea[num14]) { num16 += 600; } if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num12, 0, num13), pawn, actAsIfHadCollideWithPawnsJob: false, collideOnlyWithStandingPawns: false, forPathFinder: true)) { num16 += 175; } Building building2 = this.edificeGrid[num14]; if (building2 != null) { PfProfilerBeginSample("Edifices"); int buildingCost = GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == 2147483647) { PfProfilerEndSample(); break; } num16 += buildingCost; PfProfilerEndSample(); } List <Blueprint> list = blueprintGrid[num14]; if (list != null) { PfProfilerBeginSample("Blueprints"); int num17 = 0; for (int j = 0; j < list.Count; j++) { num17 = Mathf.Max(num17, GetBlueprintCost(list[j], pawn)); } if (num17 == 2147483647) { PfProfilerEndSample(); break; } num16 += num17; PfProfilerEndSample(); } int num18 = num16 + calcGrid[curIndex].knownCost; ushort status = calcGrid[num14].status; if (status == statusClosedValue || status == statusOpenValue) { int num19 = 0; if (status == statusClosedValue) { num19 = num8; } if (calcGrid[num14].knownCost <= num18 + num19) { break; } } if (flag9) { calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)regionCostCalculator.GetPathCostFromDestToRegion(num14) * RegionHeuristicWeightByNodesOpened.Evaluate((float)num3)); if (calcGrid[num14].heuristicCost < 0) { Log.ErrorOnce("Heuristic cost overflow for " + pawn.ToStringSafe() + " pathing from " + start + " to " + dest + ".", pawn.GetHashCode() ^ 0xB8DC389); calcGrid[num14].heuristicCost = 0; } } else if (status != statusClosedValue && status != statusOpenValue) { int dx = Math.Abs(num12 - x); int dz = Math.Abs(num13 - z); int num20 = GenMath.OctileDistance(dx, dz, num8, num9); calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)num20 * num7); } int num21 = num18 + calcGrid[num14].heuristicCost; if (num21 < 0) { Log.ErrorOnce("Node cost overflow for " + pawn.ToStringSafe() + " pathing from " + start + " to " + dest + ".", pawn.GetHashCode() ^ 0x53CB9DE); num21 = 0; } calcGrid[num14].parentIndex = curIndex; calcGrid[num14].knownCost = num18; calcGrid[num14].status = statusOpenValue; calcGrid[num14].costNodeCost = num21; num3++; openList.Push(new CostNode(num14, num21)); break; } } } } } } PfProfilerEndSample(); num2++; calcGrid[curIndex].status = statusClosedValue; if (num3 >= num4 && flag7 && !flag9) { flag9 = true; regionCostCalculator.Init(cellRect, traverseParms, num8, num9, byteGrid, allowedArea, flag10, disallowedCornerIndices); InitStatusesAndPushStartNode(ref curIndex, start); num3 = 0; num2 = 0; } } } Log.Warning(pawn + " pathing from " + start + " to " + dest + " hit search limit of " + 160000 + " cells."); DebugDrawRichData(); PfProfilerEndSample(); return(PawnPath.NotFound); }
public static IAttackTarget BestAttackTarget(IAttackTargetSearcher searcher, TargetScanFlags flags, Predicate <Thing> validator = null, float minDist = 0f, float maxDist = 9999f, IntVec3 locus = default(IntVec3), float maxTravelRadiusFromLocus = 3.40282347E+38f, bool canBash = false) { Thing searcherThing = searcher.Thing; Pawn searcherPawn = searcher as Pawn; Verb verb = searcher.CurrentEffectiveVerb; if (verb == null) { Log.Error("BestAttackTarget with " + searcher + " who has no attack verb."); return(null); } bool onlyTargetMachines = verb != null && verb.IsEMP(); float minDistanceSquared = minDist * minDist; float num = maxTravelRadiusFromLocus + verb.verbProps.range; float maxLocusDistSquared = num * num; Func <IntVec3, bool> losValidator = null; if ((flags & TargetScanFlags.LOSBlockableByGas) != 0) { losValidator = delegate(IntVec3 vec3) { Gas gas = vec3.GetGas(searcherThing.Map); return(gas == null || !gas.def.gas.blockTurretTracking); }; } Predicate <IAttackTarget> innerValidator = delegate(IAttackTarget t) { Thing thing = t.Thing; if (t == searcher) { return(false); } if (minDistanceSquared > 0.0 && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < minDistanceSquared) { return(false); } if (maxTravelRadiusFromLocus < 9999.0 && (float)(thing.Position - locus).LengthHorizontalSquared > maxLocusDistSquared) { return(false); } if (!searcherThing.HostileTo(thing)) { return(false); } if (validator != null && !validator(thing)) { return(false); } if ((flags & TargetScanFlags.NeedLOSToAll) != 0 && !searcherThing.CanSee(thing, losValidator)) { if (t is Pawn) { if ((flags & TargetScanFlags.NeedLOSToPawns) != 0) { return(false); } } else if ((flags & TargetScanFlags.NeedLOSToNonPawns) != 0) { return(false); } } if ((flags & TargetScanFlags.NeedThreat) != 0 && t.ThreatDisabled()) { return(false); } Pawn pawn2 = t as Pawn; if (onlyTargetMachines && pawn2 != null && pawn2.RaceProps.IsFlesh) { return(false); } if ((flags & TargetScanFlags.NeedNonBurning) != 0 && thing.IsBurning()) { return(false); } if (searcherThing.def.race != null && (int)searcherThing.def.race.intelligence >= 2) { CompExplosive compExplosive = thing.TryGetComp <CompExplosive>(); if (compExplosive != null && compExplosive.wickStarted) { return(false); } } if (thing.def.size.x == 1 && thing.def.size.z == 1) { if (thing.Position.Fogged(thing.Map)) { return(false); } } else { bool flag2 = false; CellRect.CellRectIterator iterator = thing.OccupiedRect().GetIterator(); while (!iterator.Done()) { if (iterator.Current.Fogged(thing.Map)) { iterator.MoveNext(); continue; } flag2 = true; break; } if (!flag2) { return(false); } } return(true); }; if (AttackTargetFinder.HasRangedAttack(searcher)) { AttackTargetFinder.tmpTargets.Clear(); AttackTargetFinder.tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher)); if ((flags & TargetScanFlags.NeedReachable) != 0) { Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator(t) && AttackTargetFinder.CanReach(searcherThing, t.Thing, canBash)); } bool flag = false; if (searcherThing.Faction != Faction.OfPlayer) { for (int i = 0; i < AttackTargetFinder.tmpTargets.Count; i++) { IAttackTarget attackTarget = AttackTargetFinder.tmpTargets[i]; if (attackTarget.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) && innerValidator(attackTarget) && AttackTargetFinder.CanShootAtFromCurrentPosition(attackTarget, searcher, verb)) { flag = true; break; } } } IAttackTarget result; if (flag) { AttackTargetFinder.tmpTargets.RemoveAll((IAttackTarget x) => !x.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) || !innerValidator(x)); result = AttackTargetFinder.GetRandomShootingTargetByScore(AttackTargetFinder.tmpTargets, searcher, verb); } else { Predicate <Thing> validator2 = ((flags & TargetScanFlags.NeedReachableIfCantHitFromMyPos) == TargetScanFlags.None || (flags & TargetScanFlags.NeedReachable) != 0) ? ((Predicate <Thing>)((Thing t) => innerValidator((IAttackTarget)t))) : ((Predicate <Thing>)((Thing t) => innerValidator((IAttackTarget)t) && (AttackTargetFinder.CanReach(searcherThing, t, canBash) || AttackTargetFinder.CanShootAtFromCurrentPosition((IAttackTarget)t, searcher, verb)))); result = (IAttackTarget)GenClosest.ClosestThing_Global(searcherThing.Position, AttackTargetFinder.tmpTargets, maxDist, validator2, null); } AttackTargetFinder.tmpTargets.Clear(); return(result); } if (searcherPawn != null && searcherPawn.mindState.duty != null && searcherPawn.mindState.duty.radius > 0.0 && !searcherPawn.InMentalState) { Predicate <IAttackTarget> oldValidator2 = innerValidator; innerValidator = delegate(IAttackTarget t) { if (!oldValidator2(t)) { return(false); } if (!t.Thing.Position.InHorDistOf(searcherPawn.mindState.duty.focus.Cell, searcherPawn.mindState.duty.radius)) { return(false); } return(true); }; } IntVec3 root = searcherThing.Position; Map map = searcherThing.Map; ThingRequest thingReq = ThingRequest.ForGroup(ThingRequestGroup.AttackTarget); PathEndMode peMode = PathEndMode.Touch; Pawn pawn = searcherPawn; Danger maxDanger = Danger.Deadly; bool canBash2 = canBash; TraverseParms traverseParams = TraverseParms.For(pawn, maxDanger, TraverseMode.ByPawn, canBash2); float maxDistance = maxDist; Predicate <Thing> validator3 = (Thing x) => innerValidator((IAttackTarget)x); int searchRegionsMax = (!(maxDist > 800.0)) ? 40 : (-1); IAttackTarget attackTarget2 = (IAttackTarget)GenClosest.ClosestThingReachable(root, map, thingReq, peMode, traverseParams, maxDistance, validator3, null, 0, searchRegionsMax, false, RegionType.Set_Passable, false); if (attackTarget2 != null && PawnUtility.ShouldCollideWithPawns(searcherPawn)) { IAttackTarget attackTarget3 = AttackTargetFinder.FindBestReachableMeleeTarget(innerValidator, searcherPawn, maxDist, canBash); if (attackTarget3 != null) { root = searcherPawn.Position - attackTarget2.Thing.Position; float lengthHorizontal = root.LengthHorizontal; float lengthHorizontal2 = (searcherPawn.Position - attackTarget3.Thing.Position).LengthHorizontal; if (Mathf.Abs(lengthHorizontal - lengthHorizontal2) < 50.0) { attackTarget2 = attackTarget3; } } } return(attackTarget2); }
private bool NeedNewPath() { if (!this.destination.IsValid || this.curPath == null || !this.curPath.Found || this.curPath.NodesLeftCount == 0) { return(true); } if (this.destination.HasThing && this.destination.Thing.Map != this.pawn.Map) { return(true); } if ((this.pawn.Position.InHorDistOf(this.curPath.LastNode, 15f) || this.pawn.Position.InHorDistOf(this.destination.Cell, 15f)) && !ReachabilityImmediate.CanReachImmediate(this.curPath.LastNode, this.destination, this.pawn.Map, this.peMode, this.pawn)) { return(true); } if (this.curPath.UsedRegionHeuristics && this.curPath.NodesConsumedCount >= 75) { return(true); } if (this.lastPathedTargetPosition != this.destination.Cell) { float num = (float)(this.pawn.Position - this.destination.Cell).LengthHorizontalSquared; float num2; if (num > 900f) { num2 = 10f; } else if (num > 289f) { num2 = 5f; } else if (num > 100f) { num2 = 3f; } else if (num > 49f) { num2 = 2f; } else { num2 = 0.5f; } if ((float)(this.lastPathedTargetPosition - this.destination.Cell).LengthHorizontalSquared > num2 * num2) { return(true); } } bool flag = PawnUtility.ShouldCollideWithPawns(this.pawn); bool flag2 = this.curPath.NodesLeftCount < 30; IntVec3 other = IntVec3.Invalid; int num3 = 0; while (num3 < 20 && num3 < this.curPath.NodesLeftCount) { IntVec3 intVec = this.curPath.Peek(num3); if (!intVec.Walkable(this.pawn.Map)) { return(true); } if (flag && !this.BestPathHadPawnsInTheWayRecently() && (PawnUtility.AnyPawnBlockingPathAt(intVec, this.pawn, false, true, false) || (flag2 && PawnUtility.AnyPawnBlockingPathAt(intVec, this.pawn, false, false, false)))) { return(true); } if (!this.BestPathHadDangerRecently() && PawnUtility.KnownDangerAt(intVec, this.pawn.Map, this.pawn)) { return(true); } Building_Door building_Door = intVec.GetEdifice(this.pawn.Map) as Building_Door; if (building_Door != null) { if (!building_Door.CanPhysicallyPass(this.pawn) && !this.pawn.HostileTo(building_Door)) { return(true); } if (building_Door.IsForbiddenToPass(this.pawn)) { return(true); } } if (num3 != 0 && intVec.AdjacentToDiagonal(other) && (PathFinder.BlocksDiagonalMovement(intVec.x, other.z, this.pawn.Map) || PathFinder.BlocksDiagonalMovement(other.x, intVec.z, this.pawn.Map))) { return(true); } other = intVec; num3++; } return(false); }
//public static FastPriorityQueue<CostNode2> openList = new FastPriorityQueue<CostNode2>(new CostNodeComparer2()); //public static PathFinderNodeFast[] calcGrid = new PathFinderNodeFast[40000]; //public static ushort statusOpenValue = 1; //public static ushort statusClosedValue = 2; public static bool FindPath(PathFinder __instance, ref PawnPath __result, IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { FastPriorityQueue <CostNode2> openList = getOpenList(); ushort statusOpenValue = getOpenValue(); ushort statusClosedValue = getClosedValue(); PathFinderNodeFast[] calcGrid = getCalcGrid(__instance); RegionCostCalculatorWrapper regionCostCalculator = getRegionCostCalculatorWrapper(__instance); if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (pawn != null && pawn.Map != mapField(__instance)) { Log.Error(string.Concat("Tried to FindPath for pawn which is spawned in another map. His map PathFinder should have been used, not this one. pawn=", pawn, " pawn.Map=", pawn.Map, " map=", mapField(__instance))); __result = PawnPath.NotFound; return(false); } if (!start.IsValid) { Log.Error(string.Concat("Tried to FindPath with invalid start ", start, ", pawn= ", pawn)); __result = PawnPath.NotFound; return(false); } if (!dest.IsValid) { Log.Error(string.Concat("Tried to FindPath with invalid dest ", dest, ", pawn= ", pawn)); __result = PawnPath.NotFound; return(false); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!pawn.CanReach(dest, peMode, Danger.Deadly, traverseParms.canBash, traverseParms.mode)) { __result = PawnPath.NotFound; return(false); } } else if (!mapField(__instance).reachability.CanReach(start, dest, peMode, traverseParms)) { __result = PawnPath.NotFound; return(false); } PfProfilerBeginSample(string.Concat("FindPath for ", pawn, " from ", start, " to ", dest, dest.HasThing ? (" at " + dest.Cell) : "")); cellIndicesField(__instance) = mapField(__instance).cellIndices; pathGridField(__instance) = mapField(__instance).pathGrid; edificeGridField(__instance) = mapField(__instance).edificeGrid.InnerArray; blueprintGridField(__instance) = mapField(__instance).blueprintGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int curIndex = cellIndicesField(__instance).CellToIndex(start); int num = cellIndicesField(__instance).CellToIndex(dest.Cell); ByteGrid byteGrid = pawn?.GetAvoidGrid(); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect destinationRect = CalculateDestinationRect(dest, peMode); bool flag4 = destinationRect.Width == 1 && destinationRect.Height == 1; int[] array = mapField(__instance).pathGrid.pathGrid; TerrainDef[] topGrid = mapField(__instance).terrainGrid.topGrid; EdificeGrid edificeGrid = mapField(__instance).edificeGrid; int num2 = 0; int num3 = 0; Area allowedArea = GetAllowedArea(pawn); bool flag5 = pawn != null && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = !flag && start.GetRegion(mapField(__instance)) != null && flag2; bool flag7 = !flag || !flag3; bool flag8 = false; bool flag9 = pawn?.Drafted ?? false; int num4 = (pawn?.IsColonist ?? false) ? 100000 : 2000; int num5 = 0; int num6 = 0; float num7 = DetermineHeuristicStrength(pawn, start, dest); int num8; int num9; if (pawn != null) { num8 = pawn.TicksPerMoveCardinal; num9 = pawn.TicksPerMoveDiagonal; } else { num8 = 13; num9 = 18; } CalculateAndAddDisallowedCorners2(disallowedCornerIndicesField(__instance), mapField(__instance), peMode, destinationRect); InitStatusesAndPushStartNode3(ref curIndex, start, cellIndicesField(__instance), calcGrid, ref statusOpenValue, ref statusClosedValue); openList.Clear(); openList.Push(new CostNode2(curIndex, 0)); while (true) { PfProfilerBeginSample("Open cell"); if (openList.Count <= 0) { string text = (pawn != null && pawn.CurJob != null) ? pawn.CurJob.ToString() : "null"; string text2 = (pawn != null && pawn.Faction != null) ? pawn.Faction.ToString() : "null"; Log.Warning(string.Concat(pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2)); DebugDrawRichData(); PfProfilerEndSample(); PfProfilerEndSample(); __result = PawnPath.NotFound; return(false); } num5 += openList.Count; num6++; CostNode2 costNode = openList.Pop(); curIndex = costNode.index; if (costNode.cost != calcGrid[curIndex].costNodeCost) { PfProfilerEndSample(); continue; } if (calcGrid[curIndex].status == statusClosedValue) { PfProfilerEndSample(); continue; } IntVec3 c = cellIndicesField(__instance).IndexToCell(curIndex); int x2 = c.x; int z2 = c.z; if (flag4) { if (curIndex == num) { PfProfilerEndSample(); PawnPath result = FinalizedPath2(curIndex, flag8, cellIndicesField(__instance), calcGrid); PfProfilerEndSample(); __result = result; return(false); } } else if (destinationRect.Contains(c) && !disallowedCornerIndicesField(__instance).Contains(curIndex)) { PfProfilerEndSample(); PawnPath result2 = FinalizedPath2(curIndex, flag8, cellIndicesField(__instance), calcGrid); PfProfilerEndSample(); __result = result2; return(false); } if (num2 > 160000) { break; } PfProfilerEndSample(); PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num10 = (uint)(x2 + Directions[i]); uint num11 = (uint)(z2 + Directions[i + 8]); if (num10 >= mapSizeXField(__instance) || num11 >= mapSizeZField(__instance)) { continue; } int num12 = (int)num10; int num13 = (int)num11; int num14 = cellIndicesField(__instance).CellToIndex(num12, num13); if (calcGrid[num14].status == statusClosedValue && !flag8) { continue; } int num15 = 0; bool flag10 = false; if (!flag2 && new IntVec3(num12, 0, num13).GetTerrain(mapField(__instance)).HasTag("Water")) { continue; } if (!pathGridField(__instance).WalkableFast(num14)) { if (!flag) { continue; } flag10 = true; num15 += 70; Building building = edificeGrid[num14]; if (building == null || !PathFinder.IsDestroyable(building)) { continue; } num15 += (int)(building.HitPoints * 0.2f); } switch (i) { case 4: if (PathFinder.BlocksDiagonalMovement(curIndex - mapSizeXField(__instance), mapField(__instance))) { if (flag7) { continue; } num15 += 70; } if (PathFinder.BlocksDiagonalMovement(curIndex + 1, mapField(__instance))) { if (flag7) { continue; } num15 += 70; } break; case 5: if (PathFinder.BlocksDiagonalMovement(curIndex + mapSizeXField(__instance), mapField(__instance))) { if (flag7) { continue; } num15 += 70; } if (PathFinder.BlocksDiagonalMovement(curIndex + 1, mapField(__instance))) { if (flag7) { continue; } num15 += 70; } break; case 6: if (PathFinder.BlocksDiagonalMovement(curIndex + mapSizeXField(__instance), mapField(__instance))) { if (flag7) { continue; } num15 += 70; } if (PathFinder.BlocksDiagonalMovement(curIndex - 1, mapField(__instance))) { if (flag7) { continue; } num15 += 70; } break; case 7: if (PathFinder.BlocksDiagonalMovement(curIndex - mapSizeXField(__instance), mapField(__instance))) { if (flag7) { continue; } num15 += 70; } if (PathFinder.BlocksDiagonalMovement(curIndex - 1, mapField(__instance))) { if (flag7) { continue; } num15 += 70; } break; } int num16 = (i > 3) ? num9 : num8; num16 += num15; if (!flag10) { num16 += array[num14]; num16 = ((!flag9) ? (num16 + topGrid[num14].extraNonDraftedPerceivedPathCost) : (num16 + topGrid[num14].extraDraftedPerceivedPathCost)); } if (byteGrid != null) { num16 += byteGrid[num14] * 8; } if (allowedArea != null && !allowedArea[num14]) { num16 += 600; } if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num12, 0, num13), pawn, actAsIfHadCollideWithPawnsJob: false, collideOnlyWithStandingPawns: false, forPathFinder: true)) { num16 += 175; } Building building2 = edificeGridField(__instance)[num14]; if (building2 != null) { PfProfilerBeginSample("Edifices"); int buildingCost = PathFinder.GetBuildingCost(building2, traverseParms, pawn); if (buildingCost == int.MaxValue) { PfProfilerEndSample(); continue; } num16 += buildingCost; PfProfilerEndSample(); } List <Blueprint> list = blueprintGridField(__instance)[num14]; if (list != null) { PfProfilerBeginSample("Blueprints"); int num17 = 0; for (int j = 0; j < list.Count; j++) { num17 = Mathf.Max(num17, PathFinder.GetBlueprintCost(list[j], pawn)); } if (num17 == int.MaxValue) { PfProfilerEndSample(); continue; } num16 += num17; PfProfilerEndSample(); } int num18 = num16 + calcGrid[curIndex].knownCost; ushort status = calcGrid[num14].status; if (status == statusClosedValue || status == statusOpenValue) { int num19 = 0; if (status == statusClosedValue) { num19 = num8; } if (calcGrid[num14].knownCost <= num18 + num19) { continue; } } if (flag8) { calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)regionCostCalculator.GetPathCostFromDestToRegion(num14) * RegionHeuristicWeightByNodesOpened.Evaluate(num3)); if (calcGrid[num14].heuristicCost < 0) { Log.ErrorOnce(string.Concat("Heuristic cost overflow for ", pawn.ToStringSafe(), " pathing from ", start, " to ", dest, "."), pawn.GetHashCode() ^ 0xB8DC389); calcGrid[num14].heuristicCost = 0; } } else if (status != statusClosedValue && status != statusOpenValue) { int dx = Math.Abs(num12 - x); int dz = Math.Abs(num13 - z); int num20 = GenMath.OctileDistance(dx, dz, num8, num9); calcGrid[num14].heuristicCost = Mathf.RoundToInt((float)num20 * num7); } int num21 = num18 + calcGrid[num14].heuristicCost; if (num21 < 0) { Log.ErrorOnce(string.Concat("Node cost overflow for ", pawn.ToStringSafe(), " pathing from ", start, " to ", dest, "."), pawn.GetHashCode() ^ 0x53CB9DE); num21 = 0; } calcGrid[num14].parentIndex = curIndex; calcGrid[num14].knownCost = num18; calcGrid[num14].status = statusOpenValue; calcGrid[num14].costNodeCost = num21; num3++; openList.Push(new CostNode2(num14, num21)); } PfProfilerEndSample(); num2++; calcGrid[curIndex].status = statusClosedValue; if (num3 >= num4 && flag6 && !flag8) { flag8 = true; regionCostCalculator.Init(destinationRect, traverseParms, num8, num9, byteGrid, allowedArea, flag9, disallowedCornerIndicesField(__instance)); InitStatusesAndPushStartNode3(ref curIndex, start, cellIndicesField(__instance), calcGrid, ref statusOpenValue, ref statusClosedValue); openList.Clear(); openList.Push(new CostNode2(curIndex, 0)); num3 = 0; num2 = 0; } } Log.Warning(string.Concat(pawn, " pathing from ", start, " to ", dest, " hit search limit of ", 160000, " cells.")); DebugDrawRichData(); PfProfilerEndSample(); PfProfilerEndSample(); __result = PawnPath.NotFound; return(false); }
public PawnPath FindShipPath(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode = PathEndMode.OnCell) { if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAllDestroyableThings; } Pawn pawn = traverseParms.pawn; if (!(pawn is null) && pawn.Map != this.map) { Log.Error(string.Concat(new object[] { "Tried to FindShipPath for pawn which is spawned in another map. his map ShipPathFinder should have been used, not this one. " + "pawn=", pawn, " pawn.Map=", pawn.Map, " map=", this.map }), false); return(PawnPath.NotFound); } if (!start.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindShipPath with invalid start ", start, ", pawn=", pawn }), false); return(PawnPath.NotFound); } if (!dest.IsValid) { Log.Error(string.Concat(new object[] { "Tried to FindPath with invalid dest ", dest, ", pawn= ", pawn }), false); return(PawnPath.NotFound); } if (traverseParms.mode == TraverseMode.ByPawn) { if (!ShipReachabilityUtility.CanReachShip(pawn, dest, peMode, Danger.Deadly, false, traverseParms.mode)) { return(PawnPath.NotFound); } } else if (!mapE.getShipReachability.CanReachShip(start, dest, peMode, traverseParms)) { return(PawnPath.NotFound); } this.PfProfilerBeginSample(string.Concat(new object[] { "FindPath for ", pawn, " from ", start, " to ", dest, (!dest.HasThing) ? string.Empty : (" at " + dest.Cell) })); this.cellIndices = this.map.cellIndices; this.shipPathGrid = mapE.getShipPathGrid; this.edificeGrid = this.map.edificeGrid.InnerArray; this.blueprintGrid = this.map.blueprintGrid.InnerArray; int x = dest.Cell.x; int z = dest.Cell.z; int num = this.cellIndices.CellToIndex(start); int num2 = this.cellIndices.CellToIndex(dest.Cell); ByteGrid byteGrid = (pawn is null) ? null : pawn.GetAvoidGrid(true); bool flag = traverseParms.mode == TraverseMode.PassAllDestroyableThings || traverseParms.mode == TraverseMode.PassAllDestroyableThingsNotWater; bool flag2 = traverseParms.mode != TraverseMode.NoPassClosedDoorsOrWater && traverseParms.mode != TraverseMode.PassAllDestroyableThingsNotWater; bool flag3 = !flag; CellRect cellRect = this.CalculateDestinationRect(dest, peMode); bool flag4 = cellRect.Width == 1 && cellRect.Height == 1; int[] array = mapE.getShipPathGrid.pathGrid; TerrainDef[] topGrid = this.map.terrainGrid.topGrid; EdificeGrid edificeGrid = this.map.edificeGrid; int num3 = 0; int num4 = 0; Area allowedArea = this.GetAllowedArea(pawn); bool flag5 = !(pawn is null) && PawnUtility.ShouldCollideWithPawns(pawn); bool flag6 = true && DebugViewSettings.drawPaths; bool flag7 = !flag && !(WaterGridsUtility.GetRegion(start, this.map, RegionType.Set_Passable) is null) && flag2; bool flag8 = !flag || !flag3; bool flag9 = false; bool flag10 = !(pawn is null) && pawn.Drafted; bool flag11 = !(pawn is null) && !(pawn.GetComp <CompShips>() is null); int num5 = (!flag11) ? NodesToOpenBeforeRegionbasedPathing_NonShip : NodesToOpenBeforeRegionBasedPathing_Ship; int num6 = 0; int num7 = 0; float num8 = this.DetermineHeuristicStrength(pawn, start, dest); int num9 = !(pawn is null) ? pawn.TicksPerMoveCardinal : DefaultMoveTicksCardinal; int num10 = !(pawn is null) ? pawn.TicksPerMoveDiagonal : DefaultMoveTicksDiagonal; this.CalculateAndAddDisallowedCorners(traverseParms, peMode, cellRect); this.InitStatusesAndPushStartNode(ref num, start); for (;;) { this.PfProfilerBeginSample("Open cell"); if (this.openList.Count <= 0) { break; } num6 += this.openList.Count; num7++; ShipPathFinder.CostNode costNode = this.openList.Pop(); num = costNode.index; if (costNode.cost != this.calcGrid[num].costNodeCost) { this.PfProfilerEndSample(); } else if (this.calcGrid[num].status == this.statusClosedValue) { this.PfProfilerEndSample(); } else { IntVec3 c = this.cellIndices.IndexToCell(num); int x2 = c.x; int z2 = c.z; if (flag6) { this.DebugFlash(c, (float)this.calcGrid[num].knownCost / 1500f, this.calcGrid[num].knownCost.ToString()); } if (flag4) { if (num == num2) { goto Block_32; } } else if (cellRect.Contains(c) && !this.disallowedCornerIndices.Contains(num)) { goto Block_34; } if (num3 > SearchLimit) { goto Block_35; } this.PfProfilerEndSample(); this.PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { uint num11 = (uint)(x2 + ShipPathFinder.Directions[i]); uint num12 = (uint)(z2 + ShipPathFinder.Directions[i + 8]); if ((ulong)num11 < ((ulong)this.mapSizeX) && (ulong)num12 < (ulong)((long)this.mapSizeZ)) { int num13 = (int)num11; int num14 = (int)num12; int num15 = this.cellIndices.CellToIndex(num13, num14); if (this.calcGrid[num15].status != this.statusClosedValue || flag9) { int num16 = 0; //bool flag12 = false; Extra cost for traversing water if (flag2 || !new IntVec3(num13, 0, num14).GetTerrain(this.map).HasTag("Water")) { if (!this.shipPathGrid.WalkableFast(num15)) { if (!flag) { if (flag6) { this.DebugFlash(new IntVec3(num13, 0, num14), 0.22f, "walk"); } goto IL_E3A; } //flag12 = true; num16 += 70; Building building = edificeGrid[num15]; if (building is null) { goto IL_E3A; } if (!IsDestroyable(building)) { goto IL_E3A; } num16 += (int)((float)building.HitPoints * 0.2f); } if (i > 3) { switch (i) { case 4: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } break; case 5: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } if (this.BlocksDiagonalMovement(num + 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 + 1, 0, z2), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } break; case 6: if (this.BlocksDiagonalMovement(num + this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 + 1), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } break; case 7: if (this.BlocksDiagonalMovement(num - this.mapSizeX)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2, 0, z2 - 1), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } if (this.BlocksDiagonalMovement(num - 1)) { if (flag8) { if (flag6) { this.DebugFlash(new IntVec3(x2 - 1, 0, z2), 0.9f, "ships"); } goto IL_E3A; } num16 += 70; } break; } } int num17 = (i <= 3) ? num9 : num10; num17 += num16; /*if(!flag12) * { * //Extra Costs for traversing water * num17 += array[num15]; * num17 += flag10 ? topGrid[num15].extraDraftedPerceivedPathCost : topGrid[num15].extraNonDraftedPerceivedPathCost; * }*/ if (!(byteGrid is null)) { num17 += (int)(byteGrid[num15] * 8); } //Allowed area cost? if (flag5 && PawnUtility.AnyPawnBlockingPathAt(new IntVec3(num13, 0, num14), pawn, false, false, true)) { num17 += Cost_PawnCollision; } Building building2 = this.edificeGrid[num15]; if (!(building2 is null)) { //Building Costs Here } List <Blueprint> list = this.blueprintGrid[num15]; if (!(list is null)) { this.PfProfilerBeginSample("Blueprints"); int num18 = 0; foreach (Blueprint bp in list) { num18 = Mathf.Max(num18, GetBlueprintCost(bp, pawn)); } if (num18 == int.MaxValue) { this.PfProfilerEndSample(); goto IL_E3A; } num17 += num18; this.PfProfilerEndSample(); } int num19 = num17 + this.calcGrid[num].knownCost; ushort status = this.calcGrid[num15].status; if (!(pawn.GetComp <CompShips>() is null) && !this.map.terrainGrid.TerrainAt(num15).IsWater) { num19 += 10000; } if (status == this.statusClosedValue || status == this.statusOpenValue) { int num20 = 0; if (status == this.statusClosedValue) { num20 = num9; } if (this.calcGrid[num15].knownCost <= num19 + num20) { goto IL_E3A; } } if (flag9) { this.calcGrid[num15].heuristicCost = Mathf.RoundToInt((float)this.regionCostCalculator.GetPathCostFromDestToRegion(num15) * ShipPathFinder.RegionheuristicWeighByNodesOpened.Evaluate((float)num4)); if (this.calcGrid[num15].heuristicCost < 0) { Log.ErrorOnce(string.Concat(new object[] { "Heuristic cost overflow for ship ", pawn.ToStringSafe <Pawn>(), " pathing from ", start, " to ", dest, "." }), pawn.GetHashCode() ^ 193840009, false); this.calcGrid[num15].heuristicCost = 0; } } else if (status != this.statusClosedValue && status != this.statusOpenValue) { int dx = Math.Abs(num13 - x); int dz = Math.Abs(num14 - z); int num21 = GenMath.OctileDistance(dx, dz, num9, num10); this.calcGrid[num15].heuristicCost = Mathf.RoundToInt((float)num21 * num8); } int num22 = num19 + this.calcGrid[num15].heuristicCost; //Log.Message("Num19: " + num19 + " || num22: " + num22); if (num22 < 0) { Log.ErrorOnce(string.Concat(new object[] { "Node cost overflow for ship ", pawn.ToStringSafe <Pawn>(), " pathing from ", start, " to ", dest, "." }), pawn.GetHashCode() ^ 87865822, false); num22 = 0; } this.calcGrid[num15].parentIndex = num; this.calcGrid[num15].knownCost = num19; this.calcGrid[num15].status = this.statusOpenValue; this.calcGrid[num15].costNodeCost = num22; num4++; this.openList.Push(new ShipPathFinder.CostNode(num15, num22)); } } } IL_E3A :; } this.PfProfilerEndSample(); num3++; this.calcGrid[num].status = this.statusClosedValue; if (num4 >= num5 && flag7 && !flag9) { flag9 = true; this.regionCostCalculator.Init(cellRect, traverseParms, num9, num10, byteGrid, allowedArea, flag10, this.disallowedCornerIndices); this.InitStatusesAndPushStartNode(ref num, start); num4 = 0; num3 = 0; } } } string text = ((pawn is null) || pawn.CurJob is null) ? "null" : pawn.CurJob.ToString(); string text2 = ((pawn is null) || pawn.Faction is null) ? "null" : pawn.Faction.ToString(); Log.Warning(string.Concat(new object[] { "ship pawn: ", pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2 }), false); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); Block_32: this.PfProfilerEndSample(); PawnPath result = this.FinalizedPath(num, flag9); this.PfProfilerEndSample(); return(result); Block_34: this.PfProfilerEndSample(); PawnPath result2 = this.FinalizedPath(num, flag9); this.PfProfilerEndSample(); return(result2); Block_35: Log.Warning(string.Concat(new object[] { "Ship ", pawn, " pathing from ", start, " to ", dest, " hit search limit of ", SearchLimit, " cells." }), false); this.DebugDrawRichData(); this.PfProfilerEndSample(); return(PawnPath.NotFound); }
public static IAttackTarget BestAttackTarget(IAttackTargetSearcher searcher, Verb verb, TargetScanFlags flags, Predicate <Thing> validator = null, float minDist = 0f, float maxDist = 9999f, IntVec3 locus = default(IntVec3), float maxTravelRadiusFromLocus = 3.40282347E+38f, bool canBash = false, bool canTakeTargetsCloserThanEffectiveMinRange = true) { Thing searcherThing = searcher.Thing; Pawn searcherPawn = searcher as Pawn; if (verb == null) { Log.Error("BestAttackTarget with " + searcher.ToStringSafe <IAttackTargetSearcher>() + " who has no attack verb.", false); return(null); } bool onlyTargetMachines = verb.IsEMP(); float minDistSquared = minDist * minDist; float num = maxTravelRadiusFromLocus + verb.verbProps.range; float maxLocusDistSquared = num * num; Func <IntVec3, bool> losValidator = null; if ((byte)(flags & TargetScanFlags.LOSBlockableByGas) != 0) { losValidator = delegate(IntVec3 vec3) { Gas gas = vec3.GetGas(searcherThing.Map); return(gas == null || !gas.def.gas.blockTurretTracking); }; } Predicate <IAttackTarget> innerValidator = delegate(IAttackTarget t) { Thing thing = t.Thing; if (t == searcher) { return(false); } if (minDistSquared > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < minDistSquared) { return(false); } if (!canTakeTargetsCloserThanEffectiveMinRange) { float num2 = verb.verbProps.EffectiveMinRange(thing, searcherThing); if (num2 > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < num2 * num2) { return(false); } } if (maxTravelRadiusFromLocus < 9999f && (float)(thing.Position - locus).LengthHorizontalSquared > maxLocusDistSquared) { return(false); } if (!searcherThing.HostileTo(thing)) { return(false); } if (validator != null && !validator(thing)) { return(false); } if (searcherPawn != null) { Lord lord = searcherPawn.GetLord(); if (lord != null && !lord.LordJob.ValidateAttackTarget(searcherPawn, thing)) { return(false); } } if ((byte)(flags & TargetScanFlags.NeedLOSToAll) != 0 && !searcherThing.CanSee(thing, losValidator)) { if (t is Pawn) { if ((byte)(flags & TargetScanFlags.NeedLOSToPawns) != 0) { return(false); } } else if ((byte)(flags & TargetScanFlags.NeedLOSToNonPawns) != 0) { return(false); } } if ((byte)(flags & TargetScanFlags.NeedThreat) != 0 && t.ThreatDisabled(searcher)) { return(false); } if (onlyTargetMachines && t is Pawn pawn && pawn.RaceProps.IsFlesh) { return(false); } if ((byte)(flags & TargetScanFlags.NeedNonBurning) != 0 && thing.IsBurning()) { return(false); } if (searcherThing.def.race != null && searcherThing.def.race.intelligence >= Intelligence.Humanlike) { CompExplosive compExplosive = thing.TryGetComp <CompExplosive>(); if (compExplosive != null && compExplosive.wickStarted) { return(false); } } if (thing.def.size.x == 1 && thing.def.size.z == 1) { if (thing.Position.Fogged(thing.Map)) { return(false); } } else { bool flag2 = false; foreach (IntVec3 cellRect in thing.OccupiedRect()) { if (cellRect.Fogged(thing.Map)) { flag2 = true; break; } } if (!flag2) { return(false); } } return(true); }; if (PCF_AttackTargetFinder.HasRangedAttack(searcher, verb)) { PCF_AttackTargetFinder.tmpTargets.Clear(); PCF_AttackTargetFinder.tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher)); if ((byte)(flags & TargetScanFlags.NeedReachable) != 0) { Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator(t) && PCF_AttackTargetFinder.CanReach(searcherThing, t.Thing, canBash)); } bool flag = false; for (int i = 0; i < PCF_AttackTargetFinder.tmpTargets.Count; i++) { IAttackTarget attackTarget = PCF_AttackTargetFinder.tmpTargets[i]; if (attackTarget.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) && innerValidator(attackTarget) && PCF_AttackTargetFinder.CanShootAtFromCurrentPosition(attackTarget, searcher, verb)) { flag = true; break; } } IAttackTarget result; if (flag) { PCF_AttackTargetFinder.tmpTargets.RemoveAll((IAttackTarget x) => !x.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) || !innerValidator(x)); result = PCF_AttackTargetFinder.GetRandomShootingTargetByScore(PCF_AttackTargetFinder.tmpTargets, searcher, verb); } else { Predicate <Thing> validator2; if ((byte)(flags & TargetScanFlags.NeedReachableIfCantHitFromMyPos) != 0 && (byte)(flags & TargetScanFlags.NeedReachable) == 0) { validator2 = ((Thing t) => innerValidator((IAttackTarget)t) && (PCF_AttackTargetFinder.CanReach(searcherThing, t, canBash) || PCF_AttackTargetFinder.CanShootAtFromCurrentPosition((IAttackTarget)t, searcher, verb))); } else { validator2 = ((Thing t) => innerValidator((IAttackTarget)t)); } result = (IAttackTarget)GenClosest.ClosestThing_Global(searcherThing.Position, PCF_AttackTargetFinder.tmpTargets, maxDist, validator2, null); } PCF_AttackTargetFinder.tmpTargets.Clear(); return(result); } if (searcherPawn != null && searcherPawn.mindState.duty != null && searcherPawn.mindState.duty.radius > 0f && !searcherPawn.InMentalState) { Predicate <IAttackTarget> oldValidator = innerValidator; innerValidator = ((IAttackTarget t) => oldValidator(t) && t.Thing.Position.InHorDistOf(searcherPawn.mindState.duty.focus.Cell, searcherPawn.mindState.duty.radius)); } IntVec3 position = searcherThing.Position; Map map = searcherThing.Map; ThingRequest thingReq = ThingRequest.ForGroup(ThingRequestGroup.AttackTarget); PathEndMode peMode = PathEndMode.Touch; Pawn searcherPawn2 = searcherPawn; Danger maxDanger = Danger.Deadly; bool canBash2 = canBash; TraverseParms traverseParams = TraverseParms.For(searcherPawn2, maxDanger, TraverseMode.ByPawn, canBash2); float maxDist2 = maxDist; bool validator3(Thing x) => innerValidator((IAttackTarget)x); int searchRegionsMax = (maxDist <= 800f) ? 40 : -1; IAttackTarget attackTarget2 = (IAttackTarget)GenClosest.ClosestThingReachable(position, map, thingReq, peMode, traverseParams, maxDist2, validator3, null, 0, searchRegionsMax, false, RegionType.Set_Passable, false); if (attackTarget2 != null && PawnUtility.ShouldCollideWithPawns(searcherPawn)) { IAttackTarget attackTarget3 = PCF_AttackTargetFinder.FindBestReachableMeleeTarget(innerValidator, searcherPawn, maxDist, canBash); if (attackTarget3 != null) { float lengthHorizontal = (searcherPawn.Position - attackTarget2.Thing.Position).LengthHorizontal; float lengthHorizontal2 = (searcherPawn.Position - attackTarget3.Thing.Position).LengthHorizontal; if (Mathf.Abs(lengthHorizontal - lengthHorizontal2) < 50f) { attackTarget2 = attackTarget3; } } } return(attackTarget2); }
//The standard A* search algorithm has been modified to implement the bidirectional pathmax algorithm //("Inconsistent heuristics in theory and practice" Felner et al.) http://web.cs.du.edu/~sturtevant/papers/incnew.pdf internal PawnPath FindPathInner(IntVec3 start, LocalTargetInfo dest, TraverseParms traverseParms, PathEndMode peMode, HeuristicMode mode = HeuristicMode.Better) { //The initialization is largely unchanged from Core, aside from coding style in some spots #region initialization if (DebugSettings.pathThroughWalls) { traverseParms.mode = TraverseMode.PassAnything; } Pawn pawn = traverseParms.pawn; bool canPassAnything = traverseParms.mode == TraverseMode.PassAnything; if (!ValidateFindPathParameters(pawn, start, dest, traverseParms, peMode, canPassAnything)) { return(PawnPath.NotFound); } PfProfilerBeginSample(string.Concat("FindPath for ", pawn, " from ", start, " to ", dest, (!dest.HasThing) ? string.Empty : (" at " + dest.Cell))); destinationX = dest.Cell.x; destinationZ = dest.Cell.z; var cellIndices = this.map.cellIndices; curIndex = cellIndices.CellToIndex(start); destinationIndex = cellIndices.CellToIndex(dest.Cell); if (!dest.HasThing || peMode == PathEndMode.OnCell) { destinationRect = CellRect.SingleCell(dest.Cell); } else { destinationRect = dest.Thing.OccupiedRect(); } if (peMode == PathEndMode.Touch) { destinationRect = destinationRect.ExpandedBy(1); } destinationRect = destinationRect.ClipInsideMap(map); var regions = destinationRect.Cells.Select(c => this.map.regionGrid.GetValidRegionAt_NoRebuild(c)).Where(r => r != null); //Pretty sure this shouldn't be able to happen... if (mode == HeuristicMode.Better && !canPassAnything && !regions.Any()) { mode = HeuristicMode.Vanilla; Log.Warning("Pathfinding destination not in region, must fall back to vanilla!"); } destinationIsOneCell = (destinationRect.Width == 1 && destinationRect.Height == 1); this.pathGridDirect = this.map.pathGrid.pathGrid; this.edificeGrid = this.map.edificeGrid.InnerArray; statusOpenValue += 2; statusClosedValue += 2; if (statusClosedValue >= 65435) { ResetStatuses(); } if (pawn?.RaceProps.Animal == true) { heuristicStrength = 30; } else { float lengthHorizontal = (start - dest.Cell).LengthHorizontal; heuristicStrength = (int)Math.Round(HeuristicStrengthHuman_DistanceCurve.Evaluate(lengthHorizontal)); } closedCellCount = 0; openList.Clear(); debug_pathFailMessaged = false; debug_totalOpenListCount = 0; debug_openCellsPopped = 0; PawnPathCostSettings pawnPathCosts = GetPawnPathCostSettings(traverseParms.pawn); moveTicksCardinal = pawnPathCosts.moveTicksCardinal; moveTicksDiagonal = pawnPathCosts.moveTicksDiagonal; //Where the magic happens RegionPathCostHeuristic regionCost = new RegionPathCostHeuristic(map, start, destinationRect, regions, traverseParms, pawnPathCosts); if (mode == HeuristicMode.Better) { if (canPassAnything) { //Roughly preserves the Vanilla behavior of increasing path accuracy for shorter paths and slower pawns, though not as smoothly. Only applies to sappers. heuristicStrength = Math.Max(1, (int)Math.Round(heuristicStrength / (float)moveTicksCardinal)); } else { var totalCostEst = (debug_totalHeuristicCostEstimate = regionCost.GetPathCostToRegion(curIndex)) + (moveTicksCardinal * 50); //Add constant cost so it tries harder on short paths regionHeuristicWeightReal[1].x = totalCostEst / 2; regionHeuristicWeightReal[2].x = totalCostEst; } regionHeuristicWeight = weightEnabled ? regionHeuristicWeightReal : regionHeuristicWeightNone; } else { regionHeuristicWeight = regionHeuristicWeightNone; } calcGrid[curIndex].knownCost = 0; calcGrid[curIndex].heuristicCost = 0; calcGrid[curIndex].parentIndex = curIndex; calcGrid[curIndex].status = statusOpenValue; openList.Push(new CostNode(curIndex, 0)); bool shouldCollideWithPawns = false; if (pawn != null) { shouldCollideWithPawns = PawnUtility.ShouldCollideWithPawns(pawn); } #endregion while (true) { PfProfilerBeginSample("Open cell pop"); if (openList.Count <= 0) { break; } debug_openCellsPopped++; var thisNode = openList.Pop(); curIndex = thisNode.gridIndex; PfProfilerEndSample(); PfProfilerBeginSample("Open cell"); if (calcGrid[curIndex].status == statusClosedValue) { PfProfilerEndSample(); } else { #if DEBUG calcGrid[curIndex].timesPopped++; #endif curIntVec3 = cellIndices.IndexToCell(curIndex); if (DebugViewSettings.drawPaths && !disableDebugFlash && debug_openCellsPopped < 20000) { //draw backpointer var arrow = GetBackPointerArrow(cellIndices.IndexToCell(calcGrid[curIndex].parentIndex), curIntVec3); string leading = ""; string trailing = ""; #if DEBUG switch (calcGrid[curIndex].timesPopped) { case 1: trailing = "\n\n"; // $"\n\n\n{thisNode.totalCostEstimate}({calcGrid[curIndex].knownCost + calcGrid[curIndex].originalHeuristicCost})"; break; case 2: trailing = "\n"; break; case 3: break; case 4: leading = "\n"; break; default: leading = "\n\n"; break; } #endif DebugFlash(curIntVec3, calcGrid[curIndex].knownCost / 1500f, leading + calcGrid[curIndex].knownCost + " " + arrow + " " + debug_openCellsPopped + trailing); } if (curIndex == destinationIndex || (!destinationIsOneCell && destinationRect.Contains(curIntVec3))) { PfProfilerEndSample(); PfProfilerBeginSample("Finalize Path"); var ret = FinalizedPath(curIndex); PfProfilerEndSample(); return(ret); } //With reopening closed nodes, this limit can be reached a lot more easily. I've left it as is because it gets users to report bad paths. if (closedCellCount > 160000) { Log.Warning(string.Concat(pawn, " pathing from ", start, " to ", dest, " hit search limit of ", 160000, " cells.")); PfProfilerEndSample(); return(PawnPath.NotFound); } PfProfilerEndSample(); PfProfilerBeginSample("Neighbor consideration"); for (int i = 0; i < 8; i++) { neighIndexes[i] = -1; neighX = (ushort)(curIntVec3.x + Directions[i]); neighZ = (ushort)(curIntVec3.z + Directions[i + 8]); if (neighX >= mapSizeX || neighZ >= mapSizeZ) { continue; } switch (i) { case 4: //Northeast if (!pathGridDirect.WalkableExtraFast(curIndex - mapSizeX) || !pathGridDirect.WalkableExtraFast(curIndex + 1)) { continue; } break; case 5: //Southeast if (!pathGridDirect.WalkableExtraFast(curIndex + mapSizeX) || !pathGridDirect.WalkableExtraFast(curIndex + 1)) { continue; } break; case 6: //Southwest if (!pathGridDirect.WalkableExtraFast(curIndex + mapSizeX) || !pathGridDirect.WalkableExtraFast(curIndex - 1)) { continue; } break; case 7: //Northwest if (!pathGridDirect.WalkableExtraFast(curIndex - mapSizeX) || !pathGridDirect.WalkableExtraFast(curIndex - 1)) { continue; } break; } neighIndex = cellIndices.CellToIndex(neighX, neighZ); if ((calcGrid[neighIndex].status != statusClosedValue) && (calcGrid[neighIndex].status != statusOpenValue)) { if (10000 <= (calcGrid[neighIndex].perceivedPathCost = GetTotalPerceivedPathCost(traverseParms, canPassAnything, shouldCollideWithPawns, pawn, pawnPathCosts))) { continue; } #if DEBUG calcGrid[neighIndex].timesPopped = 0; #endif #region heuristic PfProfilerBeginSample("Heuristic"); switch (mode) { case HeuristicMode.Vanilla: h = heuristicStrength * (Math.Abs(neighX - destinationX) + Math.Abs(neighZ - destinationZ)); break; case HeuristicMode.AdmissableOctile: { var dx = Math.Abs(neighX - destinationX); var dy = Math.Abs(neighZ - destinationZ); h = moveTicksCardinal * (dx + dy) + (moveTicksDiagonal - 2 * moveTicksCardinal) * Math.Min(dx, dy); } break; case HeuristicMode.Better: if (canPassAnything) { var dx = Math.Abs(neighX - destinationX); var dy = Math.Abs(neighZ - destinationZ); h = heuristicStrength * (moveTicksCardinal * (dx + dy) + (moveTicksDiagonal - 2 * moveTicksCardinal) * Math.Min(dx, dy)); } else { h = regionCost.GetPathCostToRegion(neighIndex); } break; } calcGrid[neighIndex].heuristicCost = h; #if PATHMAX calcGrid[neighIndex].originalHeuristicCost = h; #endif PfProfilerEndSample(); #endregion } if (calcGrid[neighIndex].perceivedPathCost < 10000) { neighIndexes[i] = neighIndex; } if (mode == HeuristicMode.Better && (calcGrid[neighIndex].status == statusOpenValue && Math.Max(i > 3 ? (int)(calcGrid[curIndex].perceivedPathCost * diagonalPerceivedCostWeight) + moveTicksDiagonal : calcGrid[curIndex].perceivedPathCost + moveTicksCardinal, 1) + calcGrid[neighIndex].knownCost < calcGrid[curIndex].knownCost)) { calcGrid[curIndex].parentIndex = neighIndex; calcGrid[curIndex].knownCost = Math.Max(i > 3 ? (int)(calcGrid[curIndex].perceivedPathCost * diagonalPerceivedCostWeight) + moveTicksDiagonal : calcGrid[curIndex].perceivedPathCost + moveTicksCardinal, 1) + calcGrid[neighIndex].knownCost; } } #region BPMX Best H #if PATHMAX PfProfilerBeginSample("BPMX Best H"); int bestH = calcGrid[curIndex].heuristicCost; if (mode == HeuristicMode.Better && pathmaxEnabled) { for (int i = 0; i < 8; i++) { neighIndex = neighIndexes[i]; if (neighIndex < 0) { continue; } bestH = Math.Max(bestH, calcGrid[neighIndex].heuristicCost - (calcGrid[curIndex].perceivedPathCost + (i > 3 ? moveTicksDiagonal : moveTicksCardinal))); } } //Pathmax Rule 3: set the current node heuristic to the best value of all connected nodes calcGrid[curIndex].heuristicCost = bestH; PfProfilerEndSample(); #endif #endregion #region Updating open list for (int i = 0; i < 8; i++) { neighIndex = neighIndexes[i]; if (neighIndex < 0) { continue; } if (calcGrid[neighIndex].status == statusClosedValue && (canPassAnything || mode != HeuristicMode.Better)) { continue; } //When path costs are significantly higher than move costs (e.g. snowy ice, or outside of allowed areas), //small differences in the weighted heuristic overwhelm the added cost of diagonal movement, so nodes //can often be visited in unnecessary zig-zags, causing lots of nodes to be reopened later, and weird looking //paths if they are not revisited. Weighting the diagonal path cost slightly counteracts this behavior, and //should result in natural looking paths when it does cause suboptimal behavior var thisDirEdgeCost = (i > 3 ? (int)(calcGrid[neighIndex].perceivedPathCost * diagonalPerceivedCostWeight) + moveTicksDiagonal : calcGrid[neighIndex].perceivedPathCost + moveTicksCardinal); //var thisDirEdgeCost = calcGrid[neighIndex].perceivedPathCost + (i > 3 ? moveTicksDiagonal : moveTicksCardinal); //Some mods can result in negative path costs. That works well enough with Vanilla, since it won't revisit closed nodes, but when we do, it's an infinite loop. thisDirEdgeCost = (ushort)Math.Max(thisDirEdgeCost, 1); neighCostThroughCur = thisDirEdgeCost + calcGrid[curIndex].knownCost; #if PATHMAX //Pathmax Rule 1 int nodeH = (mode == HeuristicMode.Better && pathmaxEnabled) ? Math.Max(calcGrid[neighIndex].heuristicCost, bestH - thisDirEdgeCost) : calcGrid[neighIndex].heuristicCost; #endif if (calcGrid[neighIndex].status == statusClosedValue || calcGrid[neighIndex].status == statusOpenValue) { #if PATHMAX bool needsUpdate = false; #endif int minReopenGain = 0; if (calcGrid[neighIndex].status == statusOpenValue) { #if PATHMAX needsUpdate = nodeH > calcGrid[neighIndex].heuristicCost; #endif } else { //Don't reopen closed nodes if the path cost difference isn't large enough to justify it; otherwise there can be cascades of revisiting the same nodes over and over for tiny path improvements each time //Increasing the threshold as more cells get reopened further helps prevent cascades minReopenGain = moveTicksCardinal + closedCellsReopened / 5; if (pawnPathCosts.area?[neighIndex] == false) { minReopenGain *= 10; } } #if PATHMAX calcGrid[neighIndex].heuristicCost = nodeH; #endif if (!(neighCostThroughCur + minReopenGain < calcGrid[neighIndex].knownCost)) { #if PATHMAX if (needsUpdate) //if the heuristic cost was increased for an open node, we need to adjust its spot in the queue { var neighCell = cellIndices.IndexToCell(neighIndex); var edgeCost = Math.Max(calcGrid[neighIndex].parentX != neighCell.x && calcGrid[neighIndex].parentZ != neighCell.z ? (int)(calcGrid[neighIndex].perceivedPathCost * diagonalPercievedCostWeight) + moveTicksDiagonal : calcGrid[neighIndex].perceivedPathCost + moveTicksCardinal, 1); openList.PushOrUpdate(new CostNode(neighIndex, calcGrid[neighIndex].knownCost - edgeCost + (int)Math.Ceiling((edgeCost + nodeH) * regionHeuristicWeight.Evaluate(calcGrid[neighIndex].knownCost)))); } #endif continue; } if (calcGrid[neighIndex].status == statusClosedValue) { closedCellsReopened++; } } //else //{ // DebugFlash(cellIndices.IndexToCell(neighIndex), 0.2f, $"\n\n{neighCostThroughCur} | {nodeH}\n{calcGrid[curIndex].knownCost + (int)Math.Ceiling((nodeH + thisDirEdgeCost) * regionHeuristicWeight.Evaluate(calcGrid[curIndex].knownCost))}"); //} calcGrid[neighIndex].parentIndex = curIndex; calcGrid[neighIndex].knownCost = neighCostThroughCur; calcGrid[neighIndex].status = statusOpenValue; #if PATHMAX calcGrid[neighIndex].heuristicCost = nodeH; #endif PfProfilerBeginSample("Push Open"); openList.PushOrUpdate(new CostNode(neighIndex, calcGrid[curIndex].knownCost + (int)Math.Ceiling((calcGrid[neighIndex].heuristicCost + thisDirEdgeCost) * regionHeuristicWeight.Evaluate(calcGrid[curIndex].knownCost)))); debug_totalOpenListCount++; PfProfilerEndSample(); } #endregion PfProfilerEndSample(); closedCellCount++; calcGrid[curIndex].status = statusClosedValue; } } if (!debug_pathFailMessaged) { string text = pawn?.CurJob?.ToString() ?? "null"; string text2 = pawn?.Faction?.ToString() ?? "null"; Log.Warning(string.Concat(pawn, " pathing from ", start, " to ", dest, " ran out of cells to process.\nJob:", text, "\nFaction: ", text2)); debug_pathFailMessaged = true; } PfProfilerEndSample(); return(PawnPath.NotFound); }