public override IActivity Tick( Actor self ) { if( IsCanceled || target.Destroyed || !target.IsInWorld) return NextActivity; var mobile = self.Trait<Mobile>(); var ps1 = new PathSearch( self.World, mobile.Info ) { checkForBlocked = true, heuristic = location => 0, inReverse = true }; foreach( var cell in target.Trait<IOccupySpace>().OccupiedCells() ) { ps1.AddInitialCell( cell.First ); if( ( mobile.toCell - cell.First ).LengthSquared <= 2 ) return NextActivity; } ps1.heuristic = PathSearch.DefaultEstimator( mobile.toCell ); var ps2 = PathSearch.FromPoint( self.World, mobile.Info, mobile.toCell, target.Location, true ); var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 ); if( ret.Count > 0 ) ret.RemoveAt( 0 ); return Util.SequenceActivities( mobile.MoveTo( () => ret ), this ); }
public override Activity Tick( Actor self ) { if( IsCanceled || !target.IsValid) return NextActivity; var mobile = self.Trait<Mobile>(); var ps1 = new PathSearch( self.World, mobile.Info, self.Owner ) { checkForBlocked = true, heuristic = location => 0, inReverse = true }; foreach( var cell in Util.AdjacentCells(target) ) if (cell == self.Location) return NextActivity; else ps1.AddInitialCell( cell ); ps1.heuristic = PathSearch.DefaultEstimator( mobile.toCell ); var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self.Owner, mobile.toCell, Util.CellContaining(target.CenterLocation), true ); var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 ); return Util.SequenceActivities( mobile.MoveTo( () => ret ), this ); }
public static PathSearch FromPoint( World world, MobileInfo mi, int2 from, int2 target, bool checkForBlocked ) { var search = new PathSearch(world, mi) { heuristic = DefaultEstimator( target ), checkForBlocked = checkForBlocked }; search.AddInitialCell( from ); return search; }
const int MaxPathAge = 50; /* x 40ms ticks */ public List <CPos> FindUnitPath(CPos from, CPos target, Actor self) { using (new PerfSample("Pathfinder")) { var cached = CachedPaths.FirstOrDefault(p => p.from == from && p.to == target && p.actor == self); if (cached != null) { Log.Write("debug", "Actor {0} asked for a path from {1} tick(s) ago", self.ActorID, world.WorldTick - cached.tick); if (world.WorldTick - cached.tick > MaxPathAge) { CachedPaths.Remove(cached); } return(new List <CPos>(cached.result)); } var mi = self.Info.Traits.Get <MobileInfo>(); // If a water-land transition is required, bail early var domainIndex = self.World.WorldActor.TraitOrDefault <DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.TileSet); if (!domainIndex.IsPassable(from, target, (uint)passable)) { return(emptyPath); } } var pb = FindBidiPath( PathSearch.FromPoint(world, mi, self, target, from, true), PathSearch.FromPoint(world, mi, self, from, target, true).Reverse() ); CheckSanePath2(pb, from, target); CachedPaths.RemoveAll(p => world.WorldTick - p.tick > MaxPathAge); CachedPaths.Add(new CachedPath { from = from, to = target, actor = self, result = pb, tick = world.WorldTick }); return(new List <CPos>(pb)); } }
public List <CPos> FindUnitPathToRange(CPos src, SubCell srcSub, WPos target, WRange range, Actor self) { using (new PerfSample("Pathfinder")) { var mi = self.Info.Traits.Get <MobileInfo>(); var targetCell = self.World.Map.CellContaining(target); var rangeSquared = range.Range * range.Range; // Correct for SubCell offset target -= MobileInfo.SubCellOffsets[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, self, t, null, true, true)); // 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 = self.World.WorldActor.TraitOrDefault <DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.TileSet); tilesInRange = new List <CPos>(tilesInRange.Where(t => domainIndex.IsPassable(src, t, (uint)passable))); if (!tilesInRange.Any()) { return(emptyPath); } } var path = FindBidiPath( PathSearch.FromPoints(world, mi, self, tilesInRange, src, true), PathSearch.FromPoint(world, mi, self, src, targetCell, true).Reverse() ); return(path); } }
public static PathSearch FromPoints(World world, MobileInfo mi, IEnumerable<int2> froms, int2 target, bool checkForBlocked) { var search = new PathSearch(world, mi) { heuristic = DefaultEstimator(target), checkForBlocked = checkForBlocked }; foreach( var sl in froms ) search.AddInitialCell( sl ); return search; }
public static PathSearch Search( World world, MobileInfo mi, bool checkForBlocked ) { var search = new PathSearch(world, mi) { checkForBlocked = checkForBlocked }; return search; }
void UpdateInnerPath(Actor self) { var targetCells = Util.AdjacentCells(target); var searchCells = new List<CPos>(); var loc = self.Location; foreach (var cell in targetCells) if (mobile.CanEnterCell(cell) && (domainIndex == null || domainIndex.IsPassable(loc, cell, movementClass))) searchCells.Add(cell); if (searchCells.Any()) { var ps1 = new PathSearch(self.World, mobile.Info, self) { checkForBlocked = true, heuristic = location => 0, inReverse = true }; foreach (var cell in searchCells) ps1.AddInitialCell(cell); ps1.heuristic = PathSearch.DefaultEstimator(mobile.toCell); var ps2 = PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, targetPosition, true); var ret = pathFinder.FindBidiPath(ps1, ps2); inner = mobile.MoveTo(() => ret); } }