Inheritance: IResolveOrder, ITick, ITechTreeElement, INotifyOwnerChanged, INotifyKilled, INotifySold, ISync, INotifyTransform
        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>();
        }
Exemple #4
0
        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;
        }
Exemple #5
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;
 }
Exemple #6
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;
     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);
        }
Exemple #8
0
        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.");
            }
        }
Exemple #9
0
        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);
        }
Exemple #13
0
        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;
		}