// In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility) void BuildUnit(IBot bot, string name) { var actorInfo = world.Map.Rules.Actors[name]; if (actorInfo == null) { return; } var buildableInfo = actorInfo.TraitInfoOrDefault <BuildableInfo>(); if (buildableInfo == null) { return; } ProductionQueue queue = null; foreach (var pq in buildableInfo.Queue) { queue = AIUtils.FindQueues(player, pq).FirstOrDefault(q => !q.AllQueued().Any()); if (queue != null) { break; } } if (queue != null) { bot.QueueOrder(Order.StartProduction(queue.Actor, name, 1)); AIUtils.BotDebug("{0} decided to build {1} (external request)", queue.Actor.Owner, name); } }
void BuildUnit(IBot bot, string category, bool buildRandom) { // Pick a free queue var queue = AIUtils.FindQueues(player, category).FirstOrDefault(q => !q.AllQueued().Any()); if (queue == null) { return; } var unit = buildRandom ? ChooseRandomUnitToBuild(queue) : ChooseUnitToBuild(queue); if (unit == null) { return; } var name = unit.Name; if (Info.UnitsToBuild != null && !Info.UnitsToBuild.ContainsKey(name)) { return; } if (Info.UnitDelays != null && Info.UnitDelays.ContainsKey(name) && Info.UnitDelays[name] > world.WorldTick) { return; } if (Info.UnitLimits != null && Info.UnitLimits.ContainsKey(name) && world.Actors.Count(a => a.Owner == player && a.Info.Name == name) >= Info.UnitLimits[name]) { return; } bot.QueueOrder(Order.StartProduction(queue.Actor, name, 1)); }
public void Tick(IBot bot) { // If failed to place something N consecutive times, wait M ticks until resuming building production if (failCount >= baseBuilder.Info.MaximumFailedPlacementAttempts && --failRetryTicks <= 0) { var currentBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); var baseProviders = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); // Only bother resetting failCount if either a) the number of buildings has decreased since last failure M ticks ago, // or b) number of BaseProviders (construction yard or similar) has increased since then. // Otherwise reset failRetryTicks instead to wait again. if (currentBuildings < cachedBuildings || baseProviders > cachedBases) { failCount = 0; } else { failRetryTicks = baseBuilder.Info.StructureProductionResumeDelay; } } if (waterState == WaterCheck.NotChecked) { if (AIUtils.IsAreaAvailable <BaseProvider>(world, player, world.Map, baseBuilder.Info.MaxBaseRadius, baseBuilder.Info.WaterTerrainTypes)) { waterState = WaterCheck.EnoughWater; } else { waterState = WaterCheck.NotEnoughWater; checkForBasesTicks = baseBuilder.Info.CheckForNewBasesDelay; } } if (waterState == WaterCheck.NotEnoughWater && --checkForBasesTicks <= 0) { var currentBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); if (currentBases > cachedBases) { cachedBases = currentBases; waterState = WaterCheck.NotChecked; } } // Only update once per second or so if (--waitTicks > 0) { return; } playerBuildings = world.ActorsHavingTrait <Building>().Where(a => a.Owner == player).ToArray(); var excessPowerBonus = baseBuilder.Info.ExcessPowerIncrement * (playerBuildings.Count() / baseBuilder.Info.ExcessPowerIncreaseThreshold.Clamp(1, int.MaxValue)); minimumExcessPower = (baseBuilder.Info.MinimumExcessPower + excessPowerBonus).Clamp(baseBuilder.Info.MinimumExcessPower, baseBuilder.Info.MaximumExcessPower); var active = false; foreach (var queue in AIUtils.FindQueues(player, category)) { if (TickQueue(bot, queue)) { active = true; } } // Add a random factor so not every AI produces at the same tick early in the game. // Minimum should not be negative as delays in HackyAI could be zero. var randomFactor = world.LocalRandom.Next(0, baseBuilder.Info.StructureProductionRandomBonusDelay); // Needs to be at least 4 * OrderLatency because otherwise the AI frequently duplicates build orders (i.e. makes the same build decision twice) waitTicks = active ? 4 * world.OrderLatency + baseBuilder.Info.StructureProductionActiveDelay + randomFactor : baseBuilder.Info.StructureProductionInactiveDelay + randomFactor; }