/// <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); }
public override bool Tick(Actor self) { if (harv.IsTraitDisabled) { Cancel(self, true); } if (IsCanceling || harv.IsFull) { return(true); } // Move towards the target cell if (self.Location != targetCell) { foreach (var n in notifyHarvesterActions) { n.MovingToResources(self, targetCell); } QueueChild(move.MoveTo(targetCell, 0)); return(false); } if (!harv.CanHarvestCell(self, self.Location)) { return(true); } // Turn to one of the harvestable facings if (harvInfo.HarvestFacings != 0) { var current = facing.Facing; var desired = body.QuantizeFacing(current, harvInfo.HarvestFacings); if (desired != current) { QueueChild(new Turn(self, desired)); return(false); } } var resource = resourceLayer.GetResource(self.Location); if (resource.Type == null || resourceLayer.RemoveResource(resource.Type, self.Location) != 1) { return(true); } harv.AcceptResource(self, resource.Type); foreach (var t in notifyHarvesterActions) { t.Harvested(self, resource.Type); } QueueChild(new Wait(harvInfo.BaleLoadDelay)); return(false); }
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); }
/// <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 override Activity Tick(Actor self) { if (ChildActivity != null) { ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity); if (ChildActivity != null) { return(this); } } if (IsCanceling || harv.IsFull) { return(NextActivity); } // Move towards the target cell if (self.Location != targetCell) { foreach (var n in self.TraitsImplementing <INotifyHarvesterAction>()) { n.MovingToResources(self, targetCell, new FindAndDeliverResources(self)); } self.SetTargetLine(Target.FromCell(self.World, targetCell), Color.Red, false); QueueChild(self, move.MoveTo(targetCell, 2), true); return(this); } if (!harv.CanHarvestCell(self, self.Location)) { return(NextActivity); } // Turn to one of the harvestable facings if (harvInfo.HarvestFacings != 0) { var current = facing.Facing; var desired = body.QuantizeFacing(current, harvInfo.HarvestFacings); if (desired != current) { QueueChild(self, new Turn(self, desired), true); return(this); } } var resource = resLayer.Harvest(self.Location); if (resource == null) { return(NextActivity); } harv.AcceptResource(self, resource); foreach (var t in self.TraitsImplementing <INotifyHarvesterAction>()) { t.Harvested(self, resource); } QueueChild(self, new Wait(harvInfo.BaleLoadDelay), true); return(this); }