/// <summary> /// Calculates a path for the actor from multiple possible sources to target. /// Returned path is *reversed* and given target to source. /// The shortest path between a source and the target is returned. /// </summary> /// <remarks> /// Searches that provide a multiple source cells are slower than those than provide only a single source cell, /// as optimizations are possible for the single source case. Use searches from multiple source cells /// sparingly. /// </remarks> public List <CPos> FindUnitPathToTargetCell( Actor self, IEnumerable <CPos> sources, CPos target, BlockedByActor check, Func <CPos, int> customCost = null, Actor ignoreActor = null, bool laneBias = true) { var sourcesList = sources.ToList(); if (sourcesList.Count == 0) { return(NoPath); } var locomotor = GetLocomotor(self); // If the target cell is inaccessible, bail early. var inaccessible = !locomotor.CanMoveFreelyInto(self, target, check, ignoreActor) || (!(customCost is null) && customCost(target) == PathGraph.PathCostForInvalidPath); if (inaccessible) { return(NoPath); } // When searching from only one source cell, some optimizations are possible. if (sourcesList.Count == 1) { var source = sourcesList[0]; // For adjacent cells on the same layer, we can return the path without invoking a full search. if (source.Layer == target.Layer && (source - target).LengthSquared < 3) { return new List <CPos>(2) { target, source } } ; // With one starting point, we can use a bidirectional search. using (var fromTarget = PathSearch.ToTargetCell( world, locomotor, self, new[] { target }, source, check, ignoreActor: ignoreActor)) using (var fromSource = PathSearch.ToTargetCell( world, locomotor, self, new[] { source }, target, check, ignoreActor: ignoreActor, inReverse: true)) return(PathSearch.FindBidiPath(fromTarget, fromSource)); } // With multiple starting points, we can only use a unidirectional search. using (var search = PathSearch.ToTargetCell( world, locomotor, self, sourcesList, target, check, customCost, ignoreActor, laneBias)) return(search.FindPath()); }
// Scriptable move order // Ignores lane bias public Move(Actor self, CPos destination, Color?targetLineColor = null) { // PERF: Because we can be sure that OccupiesSpace is Mobile here, we can save some performance by avoiding querying for the trait. mobile = (Mobile)self.OccupiesSpace; getPath = check => { using (var search = PathSearch.ToTargetCell( self.World, mobile.Locomotor, self, mobile.ToCell, destination, check, laneBias: false)) return(mobile.Pathfinder.FindPath(search)); }; this.destination = destination; this.targetLineColor = targetLineColor; nearEnough = WDist.Zero; }
public Actor ClosestProc(Actor self, Actor ignore) { // Find all refineries and their occupancy count: var refineries = self.World.ActorsWithTrait <IAcceptResources>() .Where(r => r.Actor != ignore && r.Actor.Owner == self.Owner && IsAcceptableProcType(r.Actor)) .Select(r => new { Location = r.Actor.Location + r.Trait.DeliveryOffset, Actor = r.Actor, Occupancy = self.World.ActorsHavingTrait <Harvester>(h => h.LinkedProc == r.Actor).Count() }).ToLookup(r => r.Location); // Start a search from each refinery's delivery location: List <CPos> path; using (var search = PathSearch.ToTargetCell( self.World, mobile.Locomotor, self, refineries.Select(r => r.Key), self.Location, BlockedByActor.None, location => { if (!refineries.Contains(location)) { return(0); } var occupancy = refineries[location].First().Occupancy; // Too many harvesters clogs up the refinery's delivery location: if (occupancy >= Info.MaxUnloadQueue) { return(PathGraph.PathCostForInvalidPath); } // Prefer refineries with less occupancy (multiplier is to offset distance cost): return(occupancy * Info.UnloadQueueCostModifier); })) path = mobile.Pathfinder.FindPath(search); if (path.Count > 0) { return(refineries[path.Last()].First().Actor); } return(null); }
/// <summary> /// Calculates a path for the actor from source to target. /// Returned path is *reversed* and given target to source. /// </summary> public List <CPos> FindUnitPath(CPos source, CPos target, Actor self, Actor ignoreActor, BlockedByActor check) { // PERF: Because we can be sure that OccupiesSpace is Mobile here, we can save some performance by avoiding querying for the trait. var locomotor = ((Mobile)self.OccupiesSpace).Locomotor; if (!cached) { domainIndex = world.WorldActor.TraitOrDefault <DomainIndex>(); cached = true; } // If a water-land transition is required, bail early if (domainIndex != null && !domainIndex.IsPassable(source, target, locomotor)) { return(NoPath); } var distance = source - target; var canMoveFreely = locomotor.CanMoveFreelyInto(self, target, check, null); if (distance.LengthSquared < 3 && !canMoveFreely) { return(NoPath); } if (source.Layer == target.Layer && distance.LengthSquared < 3 && canMoveFreely) { return new List <CPos> { target } } ; List <CPos> pb; using (var fromSrc = PathSearch.ToTargetCell(world, locomotor, self, target, source, check, ignoreActor: ignoreActor)) using (var fromDest = PathSearch.ToTargetCell(world, locomotor, self, source, target, check, ignoreActor: ignoreActor, inReverse: true)) pb = FindBidiPath(fromSrc, fromDest); return(pb); }