Esempio n. 1
0
        /// <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());
        }
Esempio n. 2
0
        // 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;
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        /// <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);
        }