/// <summary> /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// </summary> CPos?ClosestHarvestablePos(Actor self) { var mobile = self.Trait <Mobile>(); // Determine where to search from and how far to search: Func <CPos, bool> canHarvest = pos => { var resType = resourceLayer.GetResourceType(pos); if (resType != null && string.Compare(resType.Info.Name, Info.TargetResourceType, StringComparison.OrdinalIgnoreCase) == 0) { return(true); } return(false); }; // Find any harvestable resources: List <CPos> path; using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, BlockedByActor.Stationary, loc => domainIndex.IsPassable(self.Location, loc, mobile.Locomotor) && canHarvest(loc) && claimLayer.CanClaimCell(self, loc)) .FromPoint(self.Location)) path = pathFinder.FindPath(search); if (path.Count > 0) { return(path[0]); } return(null); }
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); } }
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)); }
/// <summary> /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// </summary> CPos?ClosestHarvestablePos(Actor self) { // Harvesters should respect an explicit harvest order instead of harvesting the current cell. if (orderLocation == null) { if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location)) { return(self.Location); } } else { if (harv.CanHarvestCell(self, orderLocation.Value) && claimLayer.CanClaimCell(self, orderLocation.Value)) { return(orderLocation); } orderLocation = null; } // Determine where to search from and how far to search: var searchFromLoc = lastHarvestedCell ?? GetSearchFromLocation(self); var searchRadius = lastHarvestedCell.HasValue ? harvInfo.SearchFromOrderRadius : harvInfo.SearchFromProcRadius; var searchRadiusSquared = searchRadius * searchRadius; // Find any harvestable resources: List <CPos> path; using (var search = PathSearch.Search(self.World, locomotorInfo, self, true, loc => domainIndex.IsPassable(self.Location, loc, locomotorInfo) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((loc - searchFromLoc).LengthSquared > searchRadiusSquared) { return(int.MaxValue); } return(0); }) .FromPoint(searchFromLoc) .FromPoint(self.Location)) path = pathFinder.FindPath(search); if (path.Count > 0) { return(path[0]); } return(null); }
/// <summary> /// Using LastOrderLocation and self.Location as starting points, /// perform A* search to find the nearest accessible and harvestable cell. /// </summary> CPos?ClosestHarvestablePos(Actor self, int searchRadius) { if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location)) { return(self.Location); } // Determine where to search from and how far to search: var searchFromLoc = harv.LastOrderLocation ?? self.Location; var searchRadiusSquared = searchRadius * searchRadius; BaseSpawnerSlaveEntry choosenSlave = null; var slaves = harv.GetSlaves(); if (slaves.Length > 0) { choosenSlave = slaves[0]; var mobile = choosenSlave.Actor.Trait <Mobile>(); var mobileInfo = choosenSlave.Actor.Info.TraitInfo <MobileInfo>(); // Find any harvestable resources: // var passable = (uint)mobileInfo.GetMovementClass(self.World.Map.Rules.TileSet); List <CPos> path; using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, true, loc => domainIndex.IsPassable(self.Location, loc, mobileInfo.LocomotorInfo) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((avoidCell.HasValue && loc == avoidCell.Value) || (loc - self.Location).LengthSquared > searchRadiusSquared) { return(int.MaxValue); } return(0); }) .FromPoint(self.Location) .FromPoint(searchFromLoc)) path = pathFinder.FindPath(search); if (path.Count > 0) { return(path[0]); } } return(null); }
Target FindNextResource(Actor actor, HarvesterTraitWrapper harv) { bool IsValidResource(CPos cell) => domainIndex.IsPassable(actor.Location, cell, harv.Mobile.Locomotor) && harv.Harvester.CanHarvestCell(cell) && claimLayer.CanClaimCell(actor, cell); var path = harv.Mobile.PathFinder.FindUnitPathToTargetCellByPredicate( actor, new[] { actor.Location }, IsValidResource, BlockedByActor.Stationary, loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) .Where(u => !u.IsDead && actor.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))); if (path.Count == 0) { return(Target.Invalid); } return(Target.FromCell(world, path[0])); }
CPos FindNextResource(Actor harvester) { var harvInfo = harvester.Info.TraitInfo <HarvesterInfo>(); var mobileInfo = harvester.Info.TraitInfo <MobileInfo>(); var passable = (uint)mobileInfo.GetMovementClass(World.Map.Rules.TileSet); var path = pathfinder.FindPath( PathSearch.Search(World, mobileInfo, harvester, true, loc => domainIndex.IsPassable(harvester.Location, loc, passable) && harvester.CanHarvestAt(loc, resLayer, harvInfo, territory)) .WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) .Where(u => !u.IsDead && harvester.Owner.Stances[u.Owner] == Stance.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(harvester.Location)); if (path.Count == 0) { return(CPos.Zero); } return(path[0]); }
Target FindNextResource(Actor actor, HarvesterTraitWrapper harv) { Func <CPos, bool> isValidResource = cell => domainIndex.IsPassable(actor.Location, cell, harv.Locomotor) && harv.Harvester.CanHarvestCell(actor, cell) && claimLayer.CanClaimCell(actor, cell); var path = pathfinder.FindPath( PathSearch.Search(world, harv.Locomotor, actor, BlockedByActor.Stationary, isValidResource) .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) .Where(u => !u.IsDead && actor.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(actor.Location)); if (path.Count == 0) { return(Target.Invalid); } return(Target.FromCell(world, path[0])); }
CPos FindNextResource(Actor actor, Harvester harv) { var mobileInfo = actor.Info.TraitInfo <MobileInfo>(); var passable = (uint)mobileInfo.GetMovementClass(World.Map.Rules.TileSet); Func <CPos, bool> isValidResource = cell => domainIndex.IsPassable(actor.Location, cell, mobileInfo, passable) && harv.CanHarvestCell(actor, cell) && claimLayer.CanClaimCell(actor, cell); var path = pathfinder.FindPath( PathSearch.Search(World, mobileInfo, actor, true, isValidResource) .WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length))).FromPoint(actor.Location)); if (path.Count == 0) { return(CPos.Zero); } return(path[0]); }
/// <summary> /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// </summary> CPos?ClosestHarvestablePos(Actor self) { if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location)) { return(self.Location); } // Determine where to search from and how far to search: var searchFromLoc = GetSearchFromLocation(self); var searchRadius = harv.LastOrderLocation.HasValue ? harvInfo.SearchFromOrderRadius : harvInfo.SearchFromProcRadius; var searchRadiusSquared = searchRadius * searchRadius; // Find any harvestable resources: var passable = (uint)mobileInfo.GetMovementClass(self.World.Map.Rules.TileSet); List <CPos> path; using (var search = PathSearch.Search(self.World, mobileInfo, self, true, loc => domainIndex.IsPassable(self.Location, loc, mobileInfo, passable) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((avoidCell.HasValue && loc == avoidCell.Value) || (loc - self.Location).LengthSquared > searchRadiusSquared) { return(int.MaxValue); } return(0); }) .FromPoint(self.Location) .FromPoint(searchFromLoc)) path = pathFinder.FindPath(search); if (path.Count > 0) { return(path[0]); } 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)); }
Target SafePath(Actor capturer, Actor target) { var locomotor = capturer.Trait <Mobile>().Locomotor; if (!domainIndex.IsPassable(capturer.Location, target.Location, locomotor)) { return(Target.Invalid); } var path = pathfinder.FindPath( PathSearch.FromPoint(world, locomotor, capturer, capturer.Location, target.Location, BlockedByActor.None) .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && capturer.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy && capturer.IsTargetableBy(u)) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(capturer.Location)); if (path.Count == 0) { return(Target.Invalid); } return(Target.FromActor(target)); }
Target FindNextResource(Actor actor, MinerTraitWrapper miner) { var towerInfo = AIUtils.GetInfoByCommonName(Info.DeployedActorTypes, player); var buildingInfo = towerInfo.TraitInfo <BuildingInfo>(); Func <CPos, bool> isValidResource = cell => domainIndex.IsPassable(actor.Location, cell, miner.Locomotor.Info) && Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type) && miner.Locomotor.CanStayInCell(cell) && world.CanPlaceBuilding(cell + miner.Transforms.Info.Offset, towerInfo, buildingInfo, actor); var path = pathfinder.FindPath( PathSearch.Search(world, miner.Locomotor, actor, BlockedByActor.Stationary, isValidResource) .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(actor.Location)); if (path.Count == 0) { return(Target.Invalid); } return(Target.FromCell(world, path[0])); }
Target PathToNextcube(Actor collector, Actor cube) { var locomotor = collector.Trait <Mobile>().Locomotor; if (!domainIndex.IsPassable(collector.Location, cube.Location, locomotor)) { return(Target.Invalid); } var path = pathfinder.FindPath( PathSearch.FromPoint(world, locomotor, collector, collector.Location, cube.Location, BlockedByActor.Stationary) .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && collector.Owner.RelationshipWith(u.Owner) == PlayerRelationship.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(collector.Location)); if (path.Count == 0) { return(Target.Invalid); } // Don't use the actor to avoid invalid targets when the cube disappears midway. return(Target.FromCell(world, cube.Location)); }
/// <summary> /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// </summary> CPos?ClosestHarvestablePos(Actor self) { // Harvesters should respect an explicit harvest order instead of harvesting the current cell. if (orderLocation == null) { if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location)) { return(self.Location); } } else { if (harv.CanHarvestCell(self, orderLocation.Value) && claimLayer.CanClaimCell(self, orderLocation.Value)) { return(orderLocation); } orderLocation = null; } // Determine where to search from and how far to search: var procLoc = GetSearchFromProcLocation(self); var searchFromLoc = lastHarvestedCell ?? procLoc ?? self.Location; var searchRadius = lastHarvestedCell.HasValue ? harvInfo.SearchFromHarvesterRadius : harvInfo.SearchFromProcRadius; var searchRadiusSquared = searchRadius * searchRadius; var map = self.World.Map; var procPos = procLoc.HasValue ? (WPos?)map.CenterOfCell(procLoc.Value) : null; var harvPos = self.CenterPosition; // Find any harvestable resources: List <CPos> path; using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, BlockedByActor.Stationary, loc => domainIndex.IsPassable(self.Location, loc, mobile.Locomotor) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((loc - searchFromLoc).LengthSquared > searchRadiusSquared) { return(PathGraph.PathCostForInvalidPath); } // Add a cost modifier to harvestable cells to prefer resources that are closer to the refinery. // This reduces the tendency for harvesters to move in straight lines if (procPos.HasValue && harvInfo.ResourceRefineryDirectionPenalty > 0 && harv.CanHarvestCell(self, loc)) { var pos = map.CenterOfCell(loc); // Calculate harv-cell-refinery angle (cosine rule) var b = pos - procPos.Value; if (b != WVec.Zero) { var c = pos - harvPos; if (c != WVec.Zero) { var a = harvPos - procPos.Value; var cosA = (int)(512 * (b.LengthSquared + c.LengthSquared - a.LengthSquared) / b.Length / c.Length); // Cost modifier varies between 0 and ResourceRefineryDirectionPenalty return(Math.Abs(harvInfo.ResourceRefineryDirectionPenalty / 2) + harvInfo.ResourceRefineryDirectionPenalty * cosA / 2048); } } } return(0); }) .FromPoint(searchFromLoc) .FromPoint(self.Location)) path = mobile.Pathfinder.FindPath(search); if (path.Count > 0) { return(path[0]); } return(null); }
public LuaTable ReinforceWithTransport(Player owner, string actorType, string[] cargoTypes, CPos[] entryPath, CPos[] exitPath = null, LuaFunction actionFunc = null, LuaFunction exitFunc = null, int dropRange = 3) { var transport = CreateActor(owner, actorType, true, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null); var cargo = transport.TraitOrDefault <Cargo>(); var passengers = new List <Actor>(); if (cargo != null && cargoTypes != null) { foreach (var cargoType in cargoTypes) { var passenger = CreateActor(owner, cargoType, false, entryPath[0]); passengers.Add(passenger); cargo.Load(transport, passenger); } } for (var i = 1; i < entryPath.Length; i++) { Move(transport, entryPath[i]); } if (actionFunc != null) { var af = (LuaFunction)actionFunc.CopyReference(); transport.QueueActivity(new CallFunc(() => { using (af) using (LuaValue t = transport.ToLuaValue(Context), p = passengers.ToArray().ToLuaValue(Context)) af.Call(t, p); })); } else { var aircraft = transport.TraitOrDefault <Aircraft>(); if (aircraft != null) { var destination = entryPath.Last(); // Try to find an alternative landing spot if we can't land at the current destination if (!aircraft.CanLand(destination) && dropRange > 0) { var locomotors = cargo.Passengers .Select(a => a.Info.TraitInfoOrDefault <MobileInfo>()) .Where(m => m != null) .Distinct() .Select(m => m.LocomotorInfo) .ToList(); foreach (var c in transport.World.Map.FindTilesInCircle(destination, dropRange)) { if (!aircraft.CanLand(c)) { continue; } if (!locomotors.All(m => domainIndex.IsPassable(destination, c, m))) { continue; } destination = c; break; } } if (aircraft.Info.VTOL) { if (destination != entryPath.Last()) { Move(transport, destination); } transport.QueueActivity(new Turn(transport, aircraft.Info.InitialFacing)); transport.QueueActivity(new Land(transport)); } else { transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, destination))); } transport.QueueActivity(new Wait(15)); } if (cargo != null) { transport.QueueActivity(new UnloadCargo(transport, true)); transport.QueueActivity(new WaitFor(() => cargo.IsEmpty(transport))); } transport.QueueActivity(new Wait(aircraft != null ? 50 : 25)); } if (exitFunc != null) { var ef = (LuaFunction)exitFunc.CopyReference(); transport.QueueActivity(new CallFunc(() => { using (ef) using (var t = transport.ToLuaValue(Context)) ef.Call(t); })); } else if (exitPath != null) { foreach (var wpt in exitPath) { Move(transport, wpt); } transport.QueueActivity(new RemoveSelf()); } var ret = Context.CreateTable(); using (LuaValue tKey = 1, tValue = transport.ToLuaValue(Context), pKey = 2, pValue = passengers.ToArray().ToLuaValue(Context)) { ret.Add(tKey, tValue); ret.Add(pKey, pValue); } return(ret); }