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])); }
void IBotTick.BotTick(IBot bot) { if (resourceLayer == null || resourceLayer.IsResourceLayerEmpty) { return; } if (--scanForIdleMinersTicks > 0) { return; } scanForIdleMinersTicks = Info.MinimumScanDelay; var toRemove = miners.Keys.Where(unitCannotBeOrdered).ToList(); foreach (var a in toRemove) { miners.Remove(a); } // TODO: Look for a more performance friendly way to update this list var newMiners = world.Actors.Where(a => Info.DeployableActorTypes.Contains(a.Info.Name) && a.Owner == player && !miners.ContainsKey(a)); foreach (var a in newMiners) { miners[a] = new MinerTraitWrapper(a); } foreach (var miner in miners) { if (!miner.Key.IsIdle) { continue; } if (Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(miner.Key.Location).Type)) { bot.QueueOrder(new Order("DeployTransform", miner.Key, true)); continue; } // Tell the idle miner to quit slacking: var newSafeResourcePatch = FindNextResource(miner.Key, miner.Value); if (newSafeResourcePatch.Type == TargetType.Invalid) { scanForIdleMinersTicks = Info.LastSearchFailedDelay; return; } AIUtils.BotDebug("AI: Miner {0} is idle. Ordering to {1} in search for new resources.".F(miner.Key, newSafeResourcePatch)); bot.QueueOrder(new Order("Move", miner.Key, newSafeResourcePatch, true)); } // Keep the economy running before starving out. var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled); if (unitBuilder != null) { var minerInfo = AIUtils.GetInfoByCommonName(Info.DeployableActorTypes, player); var miningTowers = AIUtils.CountBuildingByCommonName(Info.DeployedActorTypes, player); if (miningTowers < Info.MinimumDeployedActors && unitBuilder.RequestedProductionCount(bot, minerInfo.Name) == 0) { unitBuilder.RequestUnitProduction(bot, minerInfo.Name); } } }