public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self) { var mi = self.Info.TraitInfo<MobileInfo>(); var li = mi.LocomotorInfo; var targetCell = world.Map.CellContaining(target); // Correct for SubCell offset target -= world.Map.Grid.OffsetOfSubCell(srcSub); // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal var tilesInRange = world.Map.FindTilesInCircle(targetCell, range.Length / 1024 + 1) .Where(t => (world.Map.CenterOfCell(t) - target).LengthSquared <= range.LengthSquared && mi.CanEnterCell(self.World, self, t)); // See if there is any cell within range that does not involve a cross-domain request // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win var domainIndex = world.WorldActor.TraitOrDefault<DomainIndex>(); if (domainIndex != null) { tilesInRange = new List<CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, li))); if (!tilesInRange.Any()) return EmptyPath; } using (var fromSrc = PathSearch.FromPoints(world, li, self, tilesInRange, source, true)) using (var fromDest = PathSearch.FromPoint(world, li, self, source, targetCell, true).Reverse()) return FindBidiPath(fromSrc, fromDest); }
List <CPos> CalculatePathToTarget(Actor self, BlockedByActor check) { var loc = self.Location; // PERF: Assume that CandidateMovementCells doesn't change within a tick to avoid repeated queries // when Move enumerates different BlockedByActor values if (searchCellsTick != self.World.WorldTick) { searchCells.Clear(); searchCellsTick = self.World.WorldTick; foreach (var cell in CandidateMovementCells(self)) { if (domainIndex.IsPassable(loc, cell, Mobile.Info.LocomotorInfo) && Mobile.CanEnterCell(cell)) { searchCells.Add(cell); } } } if (!searchCells.Any()) { return(NoPath); } using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Locomotor, self, searchCells, loc, check)) using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Locomotor, self, loc, lastVisibleTargetLocation, check).Reverse()) return(pathFinder.FindBidiPath(fromSrc, fromDest)); }
public List <CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WRange range, Actor self) { var mi = self.Info.Traits.Get <MobileInfo>(); var targetCell = world.Map.CellContaining(target); var rangeSquared = range.Range * range.Range; // Correct for SubCell offset target -= world.Map.OffsetOfSubCell(srcSub); // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal var tilesInRange = world.Map.FindTilesInCircle(targetCell, range.Range / 1024 + 1) .Where(t => (world.Map.CenterOfCell(t) - target).LengthSquared <= rangeSquared && mi.CanEnterCell(self.World as World, self as Actor, t)); // See if there is any cell within range that does not involve a cross-domain request // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win var domainIndex = world.WorldActor.TraitOrDefault <DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.TileSet); tilesInRange = new List <CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, (uint)passable))); if (!tilesInRange.Any()) { return(EmptyPath); } } var path = FindBidiPath( PathSearch.FromPoints(world, mi, self, tilesInRange, source, true), PathSearch.FromPoint(world, mi, self, source, targetCell, true).Reverse()); return(path); }
public List <CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self) { var mi = self.Info.TraitInfo <MobileInfo>(); var targetCell = world.Map.CellContaining(target); //Correct for SubCell offset. //纠正SubCell偏移 target -= world.Map.Grid.OffsetOfSubCell(srcSub); var tilesInRange = world.Map.FindTilesInCircle(targetCell, range.Length / 1024 + 1). Where(t => (world.Map.CenterOfCell(t) - target).LengthSquared <= range.LengthSquared && mi.CanEnterCell(self.World, self, t)); var domainIndex = world.WorldActor.TraitOrDefault <DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.Map.Rules.TileSet); tilesInRange = new List <CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, mi, (uint)passable))); if (!tilesInRange.Any()) { return(EmptyPath); } } using (var fromSrc = PathSearch.FromPoints(world, mi, self, tilesInRange, source, true)) using (var fromDest = PathSearch.FromPoint(world, mi, self, source, targetCell, true).Reverse()) return(FindBidiPath(fromSrc, fromDest)); }
public Actor ClosestProc(Actor self, Actor ignore) { // Find all refineries and their occupancy count: var refs = 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() }) .ToDictionary(r => r.Location); // Start a search from each refinery's delivery location: List <CPos> path; var mi = self.Info.TraitInfo <MobileInfo>(); using (var search = PathSearch.FromPoints(self.World, mi, self, refs.Values.Select(r => r.Location), self.Location, false) .WithCustomCost(loc => { if (!refs.ContainsKey(loc)) { return(0); } var occupancy = refs[loc].Occupancy; // Too many harvesters clogs up the refinery's delivery location: if (occupancy >= Info.MaxUnloadQueue) { return(Constants.InvalidNode); } // Prefer refineries with less occupancy (multiplier is to offset distance cost): return(occupancy * Info.UnloadQueueCostModifier); })) path = self.World.WorldActor.Trait <IPathFinder>().FindPath(search); if (path.Count != 0) { return(refs[path.Last()].Actor); } return(null); }
Actor ClosestProc(Actor self, Actor ignore) { // Find all refineries and their occupancy count: var refs = ( from r in self.World.ActorsWithTrait <IAcceptOre>() where r.Actor != ignore && r.Actor.Owner == self.Owner && IsAcceptableProcType(r.Actor) let linkedHarvs = self.World.ActorsWithTrait <Harvester>().Where(a => a.Trait.LinkedProc == r.Actor).Count() select new { Location = r.Actor.Location + r.Trait.DeliverOffset, Actor = r.Actor, Occupancy = linkedHarvs } ).ToDictionary(r => r.Location); // Start a search from each refinery's delivery location: var mi = self.Info.Traits.Get <MobileInfo>(); var path = self.World.WorldActor.Trait <PathFinder>().FindPath( PathSearch.FromPoints(self.World, mi, self, refs.Values.Select(r => r.Location), self.Location, false) .WithCustomCost((loc) => { if (!refs.ContainsKey(loc)) { return(0); } var occupancy = refs[loc].Occupancy; // 4 harvesters clogs up the refinery's delivery location: if (occupancy >= 3) { return(int.MaxValue); } // Prefer refineries with less occupancy (multiplier is to offset distance cost): return(occupancy * 12); }) ); // Reverse the found-path to find the refinery location instead of our location: path.Reverse(); if (path.Count != 0) { return(refs[path[0]].Actor); } return(null); }
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.FromPoints(self.World, mobile.Locomotor, self, refineries.Select(r => r.Key), self.Location, BlockedByActor.None) .WithCustomCost(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); }
public List <CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self, BlockedByActor check) { if (!cached) { domainIndex = world.WorldActor.TraitOrDefault <DomainIndex>(); cached = true; } // PERF: Because we can be sure that OccupiesSpace is Mobile here, we can save some performance by avoiding querying for the trait. var mobile = (Mobile)self.OccupiesSpace; var locomotor = mobile.Locomotor; var targetCell = world.Map.CellContaining(target); // Correct for SubCell offset target -= world.Map.Grid.OffsetOfSubCell(srcSub); var rangeLengthSquared = range.LengthSquared; var map = world.Map; // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal var tilesInRange = map.FindTilesInCircle(targetCell, range.Length / 1024 + 1) .Where(t => (map.CenterOfCell(t) - target).LengthSquared <= rangeLengthSquared && mobile.Info.CanEnterCell(world, self, t)); // See if there is any cell within range that does not involve a cross-domain request // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win if (domainIndex != null) { tilesInRange = new List <CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, locomotor))); if (!tilesInRange.Any()) { return(EmptyPath); } } using (var fromSrc = PathSearch.FromPoints(world, locomotor, self, tilesInRange, source, check)) using (var fromDest = PathSearch.FromPoint(world, locomotor, self, source, targetCell, check).Reverse()) return(FindBidiPath(fromSrc, fromDest)); }
public Actor ClosestDestination(Actor self) { var refineries = self.World.ActorsWithTrait <AcceptsDeliveredResources>() .Where(r => r.Actor.Owner == self.Owner); List <CPos> path; using (var search = PathSearch.FromPoints(self.World, mobile.Locomotor, self, refineries.Select(r => r.Actor.Location), self.Location, BlockedByActor.None) .WithCustomCost(loc => self.World.FindActorsInCircle(self.World.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && self.Owner.Stances[u.Owner] == Stance.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (self.World.Map.CenterOfCell(loc) - u.CenterPosition).Length)))) path = self.World.WorldActor.Trait <IPathFinder>().FindPath(search); if (path.Count != 0) { return(refineries.First(r => r.Actor.Location == path.Last()).Actor); } return(null); }
public Actor ClosestDestination(Actor self) { var actors = string.IsNullOrEmpty(ResourceType) ? self.World.ActorsHavingTrait <ResourceCollector>().Where(r => r.Owner == self.Owner) : self.World.ActorsHavingTrait <AcceptsDeliveredResources>().Where(r => r.Owner == self.Owner); List <CPos> path; using (var search = PathSearch.FromPoints(self.World, mobile.Locomotor, self, actors.Select(a => a.Location), self.Location, BlockedByActor.None) .WithCustomCost(loc => self.World.FindActorsInCircle(self.World.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && self.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (self.World.Map.CenterOfCell(loc) - u.CenterPosition).Length)))) path = self.World.WorldActor.Trait <IPathFinder>().FindPath(search); if (path.Count != 0) { return(actors.First(r => r.Location == path.Last())); } return(null); }
List <CPos> CalculatePathToTarget(Actor self) { var targetCells = CandidateMovementCells(self); var searchCells = new List <CPos>(); var loc = self.Location; foreach (var cell in targetCells) { if (domainIndex.IsPassable(loc, cell, Mobile.Info, movementClass) && Mobile.CanEnterCell(cell)) { searchCells.Add(cell); } } if (!searchCells.Any()) { return(NoPath); } using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Info, self, searchCells, loc, true)) using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Info, self, loc, targetPosition, true).Reverse()) return(pathFinder.FindBidiPath(fromSrc, fromDest)); }
Actor ClosestProc(Actor self, Actor ignore) { var refs = self.World.ActorsWithTrait <IAcceptOre>() .Where(x => x.Actor != ignore && x.Actor.Owner == self.Owner) .ToList(); var mi = self.Info.Traits.Get <MobileInfo>(); var path = self.World.WorldActor.Trait <PathFinder>().FindPath( PathSearch.FromPoints(self.World, mi, self.Owner, refs.Select(r => r.Actor.Location + r.Trait.DeliverOffset), self.Location, false)); path.Reverse(); if (path.Count != 0) { return(refs.Where(x => x.Actor.Location + x.Trait.DeliverOffset == path[0]) .Select(a => a.Actor).FirstOrDefault()); } else { return(null); } }