Exemple #1
0
        // Won't work for shipyards...
        CPos ChooseRallyLocationNear(Actor producer)
        {
            var possibleRallyPoints = Map.FindTilesInCircle(producer.Location, Info.RallyPointScanRadius)
                                      .Where(c => IsRallyPointValid(c, producer.Info.TraitInfoOrDefault <BuildingInfo>()));

            if (!possibleRallyPoints.Any())
            {
                AIUtils.BotDebug("Bot Bug: No possible rallypoint near {0}", producer.Location);
                return(producer.Location);
            }

            return(possibleRallyPoints.Random(Random));
        }
Exemple #2
0
        void INotifyDamage.Damaged(Actor self, AttackInfo e)
        {
            if (!IsEnabled || e.Attacker == null)
            {
                return;
            }

            if (e.Attacker.Owner.Stances[self.Owner] == Stance.Neutral)
            {
                return;
            }

            var rb = self.TraitOrDefault <RepairableBuilding>();

            if (Info.ShouldRepairBuildings && rb != null)
            {
                if (e.DamageState > DamageState.Light && e.PreviousDamageState <= DamageState.Light && !rb.RepairActive)
                {
                    AIUtils.BotDebug("Bot noticed damage {0} {1}->{2}, repairing.",
                                     self, e.PreviousDamageState, e.DamageState);
                    QueueOrder(new Order("RepairBuilding", self.Owner.PlayerActor, Target.FromActor(self), false));
                }
            }

            if (e.Attacker.Disposed)
            {
                return;
            }

            if (!e.Attacker.Info.HasTraitInfo <ITargetableInfo>())
            {
                return;
            }

            // Protected priority assets, MCVs, harvesters and buildings
            if ((self.Info.HasTraitInfo <HarvesterInfo>() || self.Info.HasTraitInfo <BuildingInfo>() || self.Info.HasTraitInfo <BaseBuildingInfo>()) &&
                Player.Stances[e.Attacker.Owner] == Stance.Enemy)
            {
                defenseCenter = e.Attacker.Location;
                ProtectOwn(e.Attacker);
            }
        }
        /// <summary>Scans the map in chunks, evaluating all actors in each.</summary>
        CPos?FindCoarseAttackLocationToSupportPower(SupportPowerInstance readyPower)
        {
            CPos?bestLocation       = null;
            var  bestAttractiveness = 0;
            var  powerDecision      = powerDecisions[readyPower.Info.OrderName];

            if (powerDecision == null)
            {
                AIUtils.BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", readyPower.Info.OrderName);
                return(null);
            }

            var map         = world.Map;
            var checkRadius = powerDecision.CoarseScanRadius;

            for (var i = 0; i < map.MapSize.X; i += checkRadius)
            {
                for (var j = 0; j < map.MapSize.Y; j += checkRadius)
                {
                    var tl     = new MPos(i, j);
                    var br     = new MPos(i + checkRadius, j + checkRadius);
                    var region = new CellRegion(map.Grid.Type, tl, br);

                    // HACK: The AI code should not be messing with raw coordinate transformations
                    var wtl     = world.Map.CenterOfCell(tl.ToCPos(map));
                    var wbr     = world.Map.CenterOfCell(br.ToCPos(map));
                    var targets = world.ActorMap.ActorsInBox(wtl, wbr);

                    var frozenTargets            = frozenLayer.FrozenActorsInRegion(region);
                    var consideredAttractiveness = powerDecision.GetAttractiveness(targets, player) + powerDecision.GetAttractiveness(frozenTargets, player);
                    if (consideredAttractiveness <= bestAttractiveness || consideredAttractiveness < powerDecision.MinimumAttractiveness)
                    {
                        continue;
                    }

                    bestAttractiveness = consideredAttractiveness;
                    bestLocation       = new MPos(i, j).ToCPos(map);
                }
            }

            return(bestLocation);
        }
Exemple #4
0
        void QueueCaptureOrderFor <TTargetType>(Actor capturer, CaptureTarget <TTargetType> target) where TTargetType : class, ITraitInfoInterface
        {
            if (capturer == null)
            {
                return;
            }

            if (target == null)
            {
                return;
            }

            if (target.Actor == null)
            {
                return;
            }

            QueueOrder(new Order(target.OrderString, capturer, Target.FromActor(target.Actor), true));
            AIUtils.BotDebug("AI ({0}): Ordered {1} to capture {2}", Player.ClientIndex, capturer, target.Actor);
            activeUnits.Remove(capturer);
        }
        /// <summary>Detail scans an area, evaluating positions.</summary>
        CPos?FindFineAttackLocationToSupportPower(SupportPowerInstance readyPower, CPos checkPos, int extendedRange = 1)
        {
            CPos?bestLocation       = null;
            var  bestAttractiveness = 0;
            var  powerDecision      = powerDecisions[readyPower.Info.OrderName];

            if (powerDecision == null)
            {
                AIUtils.BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", readyPower.Info.OrderName);
                return(null);
            }

            var checkRadius = powerDecision.CoarseScanRadius;
            var fineCheck   = powerDecision.FineScanRadius;

            for (var i = 0 - extendedRange; i <= (checkRadius + extendedRange); i += fineCheck)
            {
                var x = checkPos.X + i;

                for (var j = 0 - extendedRange; j <= (checkRadius + extendedRange); j += fineCheck)
                {
                    var y   = checkPos.Y + j;
                    var pos = world.Map.CenterOfCell(new CPos(x, y));
                    var consideredAttractiveness = 0;
                    consideredAttractiveness += powerDecision.GetAttractiveness(pos, player, frozenLayer);

                    if (consideredAttractiveness <= bestAttractiveness || consideredAttractiveness < powerDecision.MinimumAttractiveness)
                    {
                        continue;
                    }

                    bestAttractiveness = consideredAttractiveness;
                    bestLocation       = new CPos(x, y);
                }
            }

            return(bestLocation);
        }
Exemple #6
0
        ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
        {
            var buildableThings = queue.BuildableItems();

            // This gets used quite a bit, so let's cache it here
            var power = GetProducibleBuilding(ai.Info.BuildingCommonNames.Power, 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("AI: {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 (!ai.HasAdequateProc() || !ai.HasMinimumProc())
            {
                var refinery = GetProducibleBuilding(ai.Info.BuildingCommonNames.Refinery, buildableThings);
                if (refinery != null && HasSufficientPowerForActor(refinery))
                {
                    AIUtils.BotDebug("AI: {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 (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold)
            {
                var production = GetProducibleBuilding(ai.Info.BuildingCommonNames.Production, buildableThings);
                if (production != null && HasSufficientPowerForActor(production))
                {
                    AIUtils.BotDebug("AI: {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 == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0 &&
                playerResources.Resources > ai.Info.NewProductionCashThreshold &&
                AIUtils.IsAreaAvailable <GivesBuildableArea>(ai.World, ai.Player, ai.Map, ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes))
            {
                var navalproduction = GetProducibleBuilding(ai.Info.BuildingCommonNames.NavalProduction, buildableThings);
                if (navalproduction != null && HasSufficientPowerForActor(navalproduction))
                {
                    AIUtils.BotDebug("AI: {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(ai.Info.BuildingCommonNames.Silo, buildableThings);
                if (silo != null && HasSufficientPowerForActor(silo))
                {
                    AIUtils.BotDebug("AI: {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 ai.Info.BuildingFractions.Shuffle(ai.Random))
            {
                var name = frac.Key;

                // 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 > frac.Value * playerBuildings.Length)
                {
                    continue;
                }

                if (ai.Info.BuildingLimits.ContainsKey(name) && ai.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 (ai.Info.BuildingCommonNames.NavalProduction.Contains(name) &&
                    (waterState == Water.NotEnoughWater ||
                     !AIUtils.IsAreaAvailable <GivesBuildableArea>(ai.World, ai.Player, ai.Map, ai.Info.CheckForWaterRadius, ai.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);
        }
Exemple #7
0
        bool TickQueue(ProductionQueue queue)
        {
            var currentBuilding = queue.AllQueued().FirstOrDefault();

            // Waiting to build something
            if (currentBuilding == null && failCount < ai.Info.MaximumFailedPlacementAttempts)
            {
                var item = ChooseBuildingToBuild(queue);
                if (item == null)
                {
                    return(false);
                }

                ai.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 (world.Map.Rules.Actors[currentBuilding.Item].HasTraitInfo <RefineryInfo>())
                {
                    type = BuildingType.Refinery;
                }

                var location = ai.ChooseBuildLocation(currentBuilding.Item, true, type);
                if (location == null)
                {
                    AIUtils.BotDebug("AI: {0} has nowhere to place {1}".F(player, currentBuilding.Item));
                    ai.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 == ai.Info.MaximumFailedPlacementAttempts)
                    {
                        cachedBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player);
                        cachedBases     = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player);
                    }
                }
                else
                {
                    failCount = 0;
                    ai.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 void TryToUseSupportPower(Actor self)
        {
            if (supportPowerManager == null)
            {
                return;
            }

            foreach (var sp in supportPowerManager.Powers.Values)
            {
                if (sp.Disabled)
                {
                    continue;
                }

                // Add power to dictionary if not in delay dictionary yet
                if (!waitingPowers.ContainsKey(sp))
                {
                    waitingPowers.Add(sp, 0);
                }

                if (waitingPowers[sp] > 0)
                {
                    waitingPowers[sp]--;
                }

                // If we have recently tried and failed to find a use location for a power, then do not try again until later
                var isDelayed = waitingPowers[sp] > 0;
                if (sp.Ready && !isDelayed && powerDecisions.ContainsKey(sp.Info.OrderName))
                {
                    var powerDecision = powerDecisions[sp.Info.OrderName];
                    if (powerDecision == null)
                    {
                        AIUtils.BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", sp.Info.OrderName);
                        continue;
                    }

                    var attackLocation = FindCoarseAttackLocationToSupportPower(sp);
                    if (attackLocation == null)
                    {
                        AIUtils.BotDebug("AI: {1} can't find suitable coarse attack location for support power {0}. Delaying rescan.", sp.Info.OrderName, player.PlayerName);
                        waitingPowers[sp] += powerDecision.GetNextScanTime(ai);

                        continue;
                    }

                    // Found a target location, check for precise target
                    attackLocation = FindFineAttackLocationToSupportPower(sp, (CPos)attackLocation);
                    if (attackLocation == null)
                    {
                        AIUtils.BotDebug("AI: {1} can't find suitable final attack location for support power {0}. Delaying rescan.", sp.Info.OrderName, player.PlayerName);
                        waitingPowers[sp] += powerDecision.GetNextScanTime(ai);

                        continue;
                    }

                    // Valid target found, delay by a few ticks to avoid rescanning before power fires via order
                    AIUtils.BotDebug("AI: {2} found new target location {0} for support power {1}.", attackLocation, sp.Info.OrderName, player.PlayerName);
                    waitingPowers[sp] += 10;
                    ai.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false)
                    {
                        SuppressVisualFeedback = true
                    });
                }
            }
        }