ActorInfo ChooseUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) { return(null); } var myUnits = player.World .ActorsHavingTrait <IPositionable>() .Where(a => a.Owner == player) .Select(a => a.Info.Name).ToList(); foreach (var unit in Info.UnitsToBuild.Shuffle(world.LocalRandom)) { if (buildableThings.Any(b => b.Name == unit.Key)) { if (myUnits.Count(a => a == unit.Key) * 100 < unit.Value * myUnits.Count) { if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key])) { return(world.Map.Rules.Actors[unit.Key]); } } } } return(null); }
// 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); } }
public PlaceBuildingOrderGenerator(ProductionQueue queue, string name) { producer = queue.Actor; placeBuildingInfo = producer.Owner.PlayerActor.Info.Traits.Get<PlaceBuildingInfo>(); building = name; // Clear selection if using Left-Click Orders if (Game.Settings.Game.UseClassicMouseStyle) producer.World.Selection.Clear(); var map = producer.World.Map; var tileset = producer.World.TileSet.Id.ToLowerInvariant(); var info = map.Rules.Actors[building]; buildingInfo = info.Traits.Get<BuildingInfo>(); var buildableInfo = info.Traits.Get<BuildableInfo>(); var mostLikelyProducer = queue.MostLikelyProducer(); race = buildableInfo.ForceRace ?? (mostLikelyProducer.Trait != null ? mostLikelyProducer.Trait.Race : producer.Owner.Faction.InternalName); buildOk = map.SequenceProvider.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); buildBlocked = map.SequenceProvider.GetSequence("overlay", "build-invalid").GetSprite(0); buildingInfluence = producer.World.WorldActor.Trait<BuildingInfluence>(); }
public void Tick(Actor self) { if (queue == null) { var type = info.ProductionType ?? self.Trait <Production>().Info.Produces.First(); // Per-actor queue // Note: this includes disabled queues, as each bar must bind to exactly one queue. queue = self.TraitsImplementing <ProductionQueue>() .FirstOrDefault(q => type == null || type == q.Info.Type); if (queue == null) { // No queues available - check for classic production queues queue = self.Owner.PlayerActor.TraitsImplementing <ProductionQueue>() .FirstOrDefault(q => type == null || type == q.Info.Type); } if (queue == null) { throw new InvalidOperationException("No queues available for production type '{0}'".F(type)); } } var current = queue.CurrentItem(); value = current != null ? 1 - (float)current.RemainingCost / current.TotalCost : 0; }
public ProductionItem(ProductionQueue queue, string item, int cost, PowerManager pm, Action onComplete) { Item = item; RemainingTime = TotalTime = 1; RemainingCost = TotalCost = cost; OnComplete = onComplete; Queue = queue; this.pm = pm; }
public ProductionItem(ProductionQueue queue, string item, int cost, PowerManager pm, Action onComplete) { Item = item; RemainingTime = TotalTime = 1; RemainingCost = TotalCost = cost; OnComplete = onComplete; Queue = queue; this.pm = pm; ai = Queue.Actor.World.Map.Rules.Actors[Item]; bi = ai.TraitInfo <BuildableInfo>(); }
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) { return(null); } var unit = buildableThings.Random(world.LocalRandom); return(HasAdequateAirUnitReloadBuildings(unit) ? unit : null); }
void SelectQueue(Actor self) { var perBuildingQueues = self.TraitsImplementing <ProductionQueue>(); queue = perBuildingQueues.FirstOrDefault(q => q.Enabled && production.Produces.Contains(q.Info.Type)); if (queue == null) { var perPlayerQueues = self.Owner.PlayerActor.TraitsImplementing <ProductionQueue>(); queue = perPlayerQueues.FirstOrDefault(q => q.Enabled && production.Produces.Contains(q.Info.Type)); } if (queue == null) { throw new InvalidOperationException("Can't find production queues."); } }
void FindQueue() { var type = info.ProductionType ?? self.Info.TraitInfo <ProductionInfo>().Produces.First(); // Per-actor queue // Note: this includes disabled queues, as each bar must bind to exactly one queue. queue = self.TraitsImplementing <ProductionQueue>() .FirstOrDefault(q => type == null || type == q.Info.Type); if (queue == null) { // No queues available - check for classic production queues queue = self.Owner.PlayerActor.TraitsImplementing <ProductionQueue>() .FirstOrDefault(q => type == null || type == q.Info.Type); } if (queue == null) { throw new InvalidOperationException("No queues available for production type '{0}'".F(type)); } }
public void Tick(Actor self) { // search for the queue here once so we don't rely on order of trait initialization if (queue == null) { var production = self.TraitOrDefault <Production>(); var perBuildingQueues = self.TraitsImplementing <ProductionQueue>(); queue = perBuildingQueues.FirstOrDefault(q => q.Enabled && production.Info.Produces.Contains(q.Info.Type)); if (queue == null) { var perPlayerQueues = self.Owner.PlayerActor.TraitsImplementing <ProductionQueue>(); queue = perPlayerQueues.FirstOrDefault(q => q.Enabled && production.Info.Produces.Contains(q.Info.Type)); } if (queue == null) { throw new InvalidOperationException("Can't find production queues."); } } }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding(baseBuilder.Info.PowerTypes, buildableThings, a => a.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount)); // First priority is to get out of a low power situation if (playerPower != null && playerPower.ExcessPower < minimumExcessPower) { if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return(power); } } // Next is to build up a strong economy if (!baseBuilder.HasAdequateRefineryCount) { var refinery = GetProducibleBuilding(baseBuilder.Info.RefineryTypes, buildableThings); if (refinery != null && HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return(refinery); } if (power != null && refinery != null && !HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Make sure that we can spend as fast as we are earning if (baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding(baseBuilder.Info.ProductionTypes, buildableThings); if (production != null && HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return(production); } if (power != null && production != null && !HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings if (waterState == WaterCheck.EnoughWater && baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold && AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes)) { var navalproduction = GetProducibleBuilding(baseBuilder.Info.NavalProductionTypes, buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name); return(navalproduction); } if (power != null && navalproduction != null && !HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Create some head room for resource storage if we really need it if (playerResources.Resources > 0.8 * playerResources.ResourceCapacity) { var silo = GetProducibleBuilding(baseBuilder.Info.SiloTypes, buildableThings); if (silo != null && HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return(silo); } if (power != null && silo != null && !HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Build everything else foreach (var frac in baseBuilder.Info.BuildingFractions.Shuffle(world.LocalRandom)) { var name = frac.Key; // Does this building have initial delay, if so have we passed it? if (baseBuilder.Info.BuildingDelays != null && baseBuilder.Info.BuildingDelays.ContainsKey(name) && baseBuilder.Info.BuildingDelays[name] > world.WorldTick) { continue; } // Can we build this structure? if (!buildableThings.Any(b => b.Name == name)) { continue; } // Do we want to build this structure? var count = playerBuildings.Count(a => a.Info.Name == name); if (count * 100 > frac.Value * playerBuildings.Length) { continue; } if (baseBuilder.Info.BuildingLimits.ContainsKey(name) && baseBuilder.Info.BuildingLimits[name] <= count) { continue; } // If we're considering to build a naval structure, check whether there is enough water inside the base perimeter // and any structure providing buildable area close enough to that water. // TODO: Extend this check to cover any naval structure, not just production. if (baseBuilder.Info.NavalProductionTypes.Contains(name) && (waterState == WaterCheck.NotEnoughWater || !AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes))) { continue; } // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; if (playerPower != null && (playerPower.ExcessPower < minimumExcessPower || !HasSufficientPowerForActor(actor))) { // Try building a power plant instead if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0) { if (playerPower.PowerOutageRemainingTicks > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); } else { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); } return(power); } } // Lets build this AIUtils.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count); return(actor); } // Too spammy to keep enabled all the time, but very useful when debugging specific issues. // AIUtils.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group); return(null); }
bool TickQueue(IBot bot, ProductionQueue queue) { var currentBuilding = queue.AllQueued().FirstOrDefault(); // Waiting to build something if (currentBuilding == null && failCount < baseBuilder.Info.MaximumFailedPlacementAttempts) { var item = ChooseBuildingToBuild(queue); if (item == null) { return(false); } bot.QueueOrder(Order.StartProduction(queue.Actor, item.Name, 1)); } else if (currentBuilding != null && currentBuilding.Done) { // Production is complete // Choose the placement logic // HACK: HACK HACK HACK // TODO: Derive this from BuildingCommonNames instead var type = BuildingType.Building; CPos? location = null; string orderString = "PlaceBuilding"; // Check if Building is a plug for other Building var actorInfo = world.Map.Rules.Actors[currentBuilding.Item]; var plugInfo = actorInfo.TraitInfoOrDefault <PlugInfo>(); if (plugInfo != null) { var possibleBuilding = world.ActorsWithTrait <Pluggable>().FirstOrDefault(a => a.Actor.Owner == player && a.Trait.AcceptsPlug(a.Actor, plugInfo.Type)); if (possibleBuilding.Actor != null) { orderString = "PlacePlug"; location = possibleBuilding.Actor.Location + possibleBuilding.Trait.Info.Offset; } } else { // Check if Building is a defense and if we should place it towards the enemy or not. if (actorInfo.HasTraitInfo <AttackBaseInfo>() && world.LocalRandom.Next(100) < baseBuilder.Info.PlaceDefenseTowardsEnemyChance) { type = BuildingType.Defense; } else if (baseBuilder.Info.RefineryTypes.Contains(actorInfo.Name)) { type = BuildingType.Refinery; } location = ChooseBuildLocation(currentBuilding.Item, true, type); } if (location == null) { AIUtils.BotDebug("{0} has nowhere to place {1}".F(player, currentBuilding.Item)); bot.QueueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1)); failCount += failCount; // If we just reached the maximum fail count, cache the number of current structures if (failCount == baseBuilder.Info.MaximumFailedPlacementAttempts) { cachedBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); cachedBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); } } else { failCount = 0; bot.QueueOrder(new Order(orderString, player.PlayerActor, Target.FromCell(world, location.Value), false) { // Building to place TargetString = currentBuilding.Item, // Actor ID to associate the placement with ExtraData = queue.Actor.ActorID, SuppressVisualFeedback = true }); return(true); } } return(true); }
bool TickQueue(IBot bot, ProductionQueue queue) { var currentBuilding = queue.AllQueued().FirstOrDefault(); // Waiting to build something if (currentBuilding == null && failCount < baseBuilder.Info.MaximumFailedPlacementAttempts) { var item = ChooseBuildingToBuild(queue); if (item == null) { return(false); } bot.QueueOrder(Order.StartProduction(queue.Actor, item.Name, 1)); } else if (currentBuilding != null && currentBuilding.Done) { // Production is complete // Choose the placement logic // HACK: HACK HACK HACK // TODO: Derive this from BuildingCommonNames instead var type = BuildingType.Building; if (world.Map.Rules.Actors[currentBuilding.Item].HasTraitInfo <AttackBaseInfo>()) { type = BuildingType.Defense; } else if (baseBuilder.Info.RefineryTypes.Contains(world.Map.Rules.Actors[currentBuilding.Item].Name)) { type = BuildingType.Refinery; } var location = ChooseBuildLocation(currentBuilding.Item, true, type); if (location == null) { AIUtils.BotDebug("AI: {0} has nowhere to place {1}".F(player, currentBuilding.Item)); bot.QueueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1)); failCount += failCount; // If we just reached the maximum fail count, cache the number of current structures if (failCount == baseBuilder.Info.MaximumFailedPlacementAttempts) { cachedBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); cachedBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); } } else { failCount = 0; bot.QueueOrder(new Order("PlaceBuilding", player.PlayerActor, Target.FromCell(world, location.Value), false) { // Building to place TargetString = currentBuilding.Item, // Actor ID to associate the placement with ExtraData = queue.Actor.ActorID, SuppressVisualFeedback = true }); return(true); } } return(true); }
public override bool HandleMouseInput(MouseInput mi) { if (mi.Event == MouseInputEvent.Scroll) { Scroll(mi.ScrollDelta); return true; } if (mi.Button != MouseButton.Left) return true; if (mi.Event == MouseInputEvent.Down && !TakeMouseFocus(mi)) return true; if (!HasMouseFocus) return true; if (HasMouseFocus && mi.Event == MouseInputEvent.Up) return YieldMouseFocus(mi); leftPressed = leftButtonRect.Contains(mi.Location); rightPressed = rightButtonRect.Contains(mi.Location); var leftDisabled = listOffset >= 0; var rightDisabled = listOffset <= Bounds.Width - rightButtonRect.Width - leftButtonRect.Width - contentWidth; if (leftPressed || rightPressed) { if ((leftPressed && !leftDisabled) || (rightPressed && !rightDisabled)) Game.Sound.PlayNotification(world.Map.Rules, null, "Sounds", "ClickSound", null); else Game.Sound.PlayNotification(world.Map.Rules, null, "Sounds", "ClickDisabledSound", null); } // Check production tabs var offsetloc = mi.Location - new int2(leftButtonRect.Right - 1 + (int)listOffset, leftButtonRect.Y); if (offsetloc.X > 0 && offsetloc.X < contentWidth) { CurrentQueue = Groups[queueGroup].Tabs[offsetloc.X / (TabWidth - 1)].Queue; Game.Sound.PlayNotification(world.Map.Rules, null, "Sounds", "ClickSound", null); } return true; }