예제 #1
0
        // 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 IBotTick.BotTick(IBot bot)
        {
            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(world);

                        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(world);

                        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;
                    bot.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false)
                    {
                        SuppressVisualFeedback = true
                    });
                }
            }
        }
예제 #3
0
        /// <summary>Detail scans an area, evaluating positions.</summary>
        CPos?FindAttackLocationToSupportPower(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 availableTargets = world.ActorsHavingTrait <IOccupySpace>().Where(x => x.IsInWorld && !x.IsDead &&
                                                                                  (powerDecision.IgnoreVisibility || x.CanBeViewedByPlayer(player)) &&
                                                                                  powerDecision.Against.HasRelationship(player.RelationshipWith(x.Owner)) &&
                                                                                  powerDecision.Types.Overlaps(x.GetEnabledTargetTypes()));

            foreach (var a in availableTargets)
            {
                var pos = a.CenterPosition;
                var consideredAttractiveness = 0;
                consideredAttractiveness += powerDecision.GetAttractiveness(pos, player);

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

                bestAttractiveness = consideredAttractiveness;
                bestLocation       = world.Map.CellContaining(pos);
            }

            return(bestLocation);
        }
        /// <summary>Scans the map in chunks, evaluating all actors in each.</summary>
        CPos?FindCoarseAttackLocationToSupportPower(SupportPowerInstance readyPower)
        {
            CPos?bestLocation  = null;
            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;
            var suitableLocations   = new List <int[]>();
            var totalAttractiveness = 0;

            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 = player.FrozenActorLayer != null?player.FrozenActorLayer.FrozenActorsInRegion(region) : Enumerable.Empty <FrozenActor>();

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

                    suitableLocations.Add(new int[] { i, j, consideredAttractiveness });
                    totalAttractiveness += consideredAttractiveness;
                }
            }

            if (suitableLocations.Any())
            {
                // Only consider locations of above average attractiveness.
                var avgAttractiveness = totalAttractiveness / suitableLocations.Count();

                foreach (var location in Util.Shuffle(suitableLocations, world.LocalRandom))
                {
                    if (location[2] >= avgAttractiveness)
                    {
                        bestLocation = new MPos(location[0], location[1]).ToCPos(map);
                        break;
                    }
                }
            }

            return(bestLocation);
        }
예제 #5
0
        void ITick.Tick(Actor self)
        {
            if (IsTraitDisabled)
            {
                return;
            }

            if (resLayer == null || resLayer.IsResourceLayerEmpty)
            {
                return;
            }

            if (--scanForIdleHarvestersTicks > 0)
            {
                return;
            }

            harvesters.RemoveAll(unitCannotBeOrdered);
            scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval;

            // Find new harvesters
            // TODO: Look for a more performance-friendly way to update this list
            var newHarvesters = world.ActorsHavingTrait <Harvester>().Where(a => a.Owner == player && !harvesters.Contains(a));

            foreach (var a in newHarvesters)
            {
                harvesters.Add(a);
            }

            // Find idle harvesters and give them orders:
            foreach (var harvester in harvesters)
            {
                var harv = harvester.Trait <Harvester>();
                if (!harv.IsEmpty)
                {
                    continue;
                }

                if (!harvester.IsIdle)
                {
                    var act = harvester.CurrentActivity;
                    if (!harv.LastSearchFailed || act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources))
                    {
                        continue;
                    }
                }

                var para = harvester.TraitOrDefault <Parachutable>();
                if (para != null && para.IsInAir)
                {
                    continue;
                }

                // Tell the idle harvester to quit slacking:
                var newSafeResourcePatch = FindNextResource(harvester, harv);
                AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch));
                botOrderManager.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false));
            }
        }
예제 #6
0
        /// <summary>Scans the map in chunks, evaluating all actors in each.</summary>
        CPos?FindCoarseAttackLocationToSupportPower(SupportPowerInstance readyPower)
        {
            var powerDecision = powerDecisions[readyPower.Info.OrderName];

            if (powerDecision == null)
            {
                AIUtils.BotDebug("{0} couldn't find powerDecision for {1}", player.PlayerName, readyPower.Info.OrderName);
                return(null);
            }

            var map                 = world.Map;
            var checkRadius         = powerDecision.CoarseScanRadius;
            var suitableLocations   = new List <(MPos UV, int Attractiveness)>();
            var totalAttractiveness = 0;

            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 = player.FrozenActorLayer != null?player.FrozenActorLayer.FrozenActorsInRegion(region) : Enumerable.Empty <FrozenActor>();

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

                    suitableLocations.Add((tl, consideredAttractiveness));
                    totalAttractiveness += consideredAttractiveness;
                }
            }

            if (suitableLocations.Count == 0)
            {
                return(null);
            }

            // Pick a random location with above average attractiveness.
            var averageAttractiveness = totalAttractiveness / suitableLocations.Count;

            return(suitableLocations.Shuffle(world.LocalRandom)
                   .First(x => x.Attractiveness >= averageAttractiveness)
                   .UV.ToCPos(map));
        }
예제 #7
0
        // Won't work for shipyards...
        CPos ChooseRallyLocationNear(Actor producer)
        {
            var possibleRallyPoints = world.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(world.LocalRandom));
        }
예제 #8
0
        void IBotRespondToAttack.RespondToAttack(IBot bot, Actor self, AttackInfo e)
        {
            var rb = self.TraitOrDefault <RepairableBuilding>();

            if (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);
                    bot.QueueOrder(new Order("RepairBuilding", self.Owner.PlayerActor, Target.FromActor(self), false));
                }
            }
        }
예제 #9
0
        /// <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 = player.FrozenActorLayer != null?player.FrozenActorLayer.FrozenActorsInRegion(region) : Enumerable.Empty <FrozenActor>();

                    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);
        }
예제 #10
0
        void IBotRespondToAttack.RespondToAttack(IBot bot, Actor self, AttackInfo e)
        {
            // HACK: We don't want D2k bots to repair all their buildings on placement
            // where half their HP is removed via neutral terrain damage.
            // TODO: Implement concrete placement for D2k bots and remove this hack.
            if (e.Attacker.Owner.Stances[self.Owner] == Stance.Neutral)
            {
                return;
            }

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

            if (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);
                    bot.QueueOrder(new Order("RepairBuilding", self.Owner.PlayerActor, Target.FromActor(self), false));
                }
            }
        }
예제 #11
0
        protected void OrderHarvesters(IBot bot)
        {
            var toRemove = harvesters.Keys.Where(unitCannotBeOrdered).ToList();

            foreach (var a in toRemove)
            {
                harvesters.Remove(a);
            }

            // Find new harvesters
            // TODO: Look for a more performance-friendly way to update this list
            var newHarvesters = world.ActorsHavingTrait <Harvester>().Where(a => a.Owner == player && !harvesters.ContainsKey(a));

            foreach (var a in newHarvesters)
            {
                harvesters[a] = new HarvesterTraitWrapper(a);
            }

            // Find idle harvesters and give them orders:
            foreach (var h in harvesters)
            {
                if (!h.Key.IsIdle)
                {
                    var act = h.Key.CurrentActivity as FindAndDeliverResources;

                    // Ignore this actor if FindAndDeliverResources is working fine or it is performing a different activity
                    if (act == null || !act.LastSearchFailed)
                    {
                        continue;
                    }
                }

                // Tell the idle harvester to quit slacking:
                var newSafeResourcePatch = FindNextResource(h.Key, h.Value);
                AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(h.Key, newSafeResourcePatch));
                bot.QueueOrder(new Order("Harvest", h.Key, newSafeResourcePatch, false));
            }
        }
예제 #12
0
        /// <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("{0} couldn't find powerDecision for {1}", player.PlayerName, 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);

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

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

            return(bestLocation);
        }
예제 #13
0
        void IBotTick.BotTick(IBot bot)
        {
            var colonies = AIUtils.GetActorsWithTrait <Colony>(world).Where(c => c.Owner == player).ToArray();

            foreach (var colony in colonies)
            {
                var health = colony.Trait <IHealth>();
                if (health.DamageState == Info.DamageState)
                {
                    var queue = colony.TraitOrDefault <ProductionQueue>();                    // Turrets don't produce
                    if (queue == null)
                    {
                        continue;
                    }

                    foreach (var current in queue.AllQueued())
                    {
                        bot.QueueOrder(Order.CancelProduction(queue.Actor, current.Item, 1));
                        AIUtils.BotDebug("{0}: Stopping production of {1} at {2} to heal.".F(player.PlayerName, current.Item, colony));
                    }
                }
            }
        }
예제 #14
0
        void IBotTick.BotTick(IBot bot)
        {
            if (resourceLayer == null || resourceLayer.IsEmpty)
            {
                return;
            }

            if (--scanForIdleHarvestersTicks > 0)
            {
                return;
            }

            var toRemove = harvesters.Keys.Where(unitCannotBeOrdered).ToList();

            foreach (var a in toRemove)
            {
                harvesters.Remove(a);
            }

            scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval;

            // Find new harvesters
            // TODO: Look for a more performance-friendly way to update this list
            var newHarvesters = world.ActorsHavingTrait <Harvester>().Where(a => a.Owner == player && !harvesters.ContainsKey(a));

            foreach (var a in newHarvesters)
            {
                harvesters[a] = new HarvesterTraitWrapper(a);
            }

            // Find idle harvesters and give them orders:
            foreach (var h in harvesters)
            {
                if (!h.Key.IsIdle)
                {
                    // Ignore this actor if FindAndDeliverResources is working fine or it is performing a different activity
                    if (!(h.Key.CurrentActivity is FindAndDeliverResources act) || !act.LastSearchFailed)
                    {
                        continue;
                    }
                }

                if (h.Value.Parachutable != null && h.Value.Parachutable.IsInAir)
                {
                    continue;
                }

                // Tell the idle harvester to quit slacking:
                var newSafeResourcePatch = FindNextResource(h.Key, h.Value);
                AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(h.Key, newSafeResourcePatch));
                bot.QueueOrder(new Order("Harvest", h.Key, newSafeResourcePatch, false));
            }

            // Less harvesters than refineries - build a new harvester
            var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled);

            if (unitBuilder != null && Info.HarvesterTypes.Any())
            {
                var harvInfo        = AIUtils.GetInfoByCommonName(Info.HarvesterTypes, player);
                var harvCountTooLow = AIUtils.CountActorByCommonName(Info.HarvesterTypes, player) < AIUtils.CountBuildingByCommonName(Info.RefineryTypes, player);
                if (harvCountTooLow && unitBuilder.RequestedProductionCount(bot, harvInfo.Name) == 0)
                {
                    unitBuilder.RequestUnitProduction(bot, harvInfo.Name);
                }
            }
        }
예제 #15
0
        void IBotTick.BotTick(IBot bot)
        {
            if (cubeSpawner == null || !cubeSpawner.IsTraitEnabled() || !cubeSpawner.Enabled)
            {
                return;
            }

            if (--scanForcubesTicks > 0)
            {
                return;
            }

            scanForcubesTicks = Info.ScanForCubesInterval;

            var cubes = world.ActorsHavingTrait <Crate>().ToList();

            if (!cubes.Any())
            {
                return;
            }

            if (Info.CheckTargetsForVisibility)
            {
                cubes.RemoveAll(c => !c.CanBeViewedByPlayer(player));
            }

            var idleUnits = world.ActorsHavingTrait <Mobile>().Where(a => a.Owner == player && a.IsIdle &&
                                                                     (Info.IncludedUnitTypes.Contains(a.Info.Name) || (!Info.IncludedUnitTypes.Any() && !Info.ExcludedUnitTypes.Contains(a.Info.Name)))).ToList();

            if (!idleUnits.Any())
            {
                return;
            }

            foreach (var cube in cubes)
            {
                if (alreadyPursuitcubes.Contains(cube))
                {
                    continue;
                }

                if (!cube.IsAtGroundLevel())
                {
                    continue;
                }

                var cubeCollector = idleUnits.ClosestTo(cube);
                if (cubeCollector == null)
                {
                    continue;
                }

                if ((cube.Location - cubeCollector.Location).Length > maxProximity)
                {
                    continue;
                }

                idleUnits.Remove(cubeCollector);

                var target = PathToNextcube(cubeCollector, cube);
                if (target.Type == TargetType.Invalid)
                {
                    continue;
                }

                var cell = world.Map.CellContaining(target.CenterPosition);
                AIUtils.BotDebug("AI: Ordering {0} to {1} for cube pick up.".F(cubeCollector, cell));
                bot.QueueOrder(new Order("Move", cubeCollector, target, true));
                alreadyPursuitcubes.Add(cube);
            }
        }
        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);
                }
            }
        }
예제 #17
0
        void QueueCaptureOrders(IBot bot)
        {
            if (!Info.CapturingActorTypes.Any() || player.WinState != WinState.Undefined)
            {
                return;
            }

            activeCapturers.RemoveAll(unitCannotBeOrderedOrIsIdle);

            var newUnits = world.ActorsHavingTrait <IPositionable>()
                           .Where(a => a.Owner == player && !activeCapturers.Contains(a));

            var capturers = newUnits
                            .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo <CapturesInfo>())
                            .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>()))
                            .Where(tp => tp.Trait != null)
                            .ToArray();

            if (capturers.Length == 0)
            {
                return;
            }

            var randPlayer = world.Players.Where(p => !p.Spectating &&
                                                 Info.CapturableStances.HasStance(player.Stances[p])).Random(world.LocalRandom);

            var targetOptions = Info.CheckCaptureTargetsForVisibility
                                ? GetVisibleActorsBelongingToPlayer(randPlayer)
                                : GetActorsThatCanBeOrderedByPlayer(randPlayer);

            var capturableTargetOptions = targetOptions
                                          .Where(target =>
            {
                var captureManager = target.TraitOrDefault <CaptureManager>();
                if (captureManager == null)
                {
                    return(false);
                }

                return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait)));
            })
                                          .OrderByDescending(target => target.GetSellValue())
                                          .Take(maximumCaptureTargetOptions);

            if (Info.CapturableActorTypes.Any())
            {
                capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant()));
            }

            if (!capturableTargetOptions.Any())
            {
                return;
            }

            var failedAttempts = captureHistory.Where(a => a.Key.IsDead).Select(a => a.Value);

            foreach (var capturer in capturers)
            {
                var targetActor = capturableTargetOptions.MinByOrDefault(t => (t.CenterPosition - capturer.Actor.CenterPosition).LengthSquared);
                if (targetActor == null)
                {
                    continue;
                }

                if (failedAttempts.Any(f => f.Actor == targetActor))
                {
                    AIUtils.BotDebug("AI ({0}): skipping capture of {1} as there was a previously failed attempt.", player.ClientIndex, targetActor);
                    continue;
                }

                var target = Target.FromActor(targetActor);
                bot.QueueOrder(new Order("CaptureActor", capturer.Actor, target, true));
                AIUtils.BotDebug("AI ({0}): Ordered {1} to capture {2}", player.ClientIndex, capturer.Actor, targetActor);
                activeCapturers.Add(capturer.Actor);

                if (!captureHistory.ContainsKey(capturer.Actor))
                {
                    captureHistory.Add(capturer.Actor, target);
                }
            }
        }
예제 #18
0
        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 (powerManager != null && powerManager.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 (powerManager != null && (powerManager.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 (powerManager.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);
        }
예제 #19
0
        void QueueCaptureOrders(IBot bot)
        {
            if (player.WinState != WinState.Undefined)
            {
                return;
            }

            var newUnits = world.ActorsHavingTrait <Captures>()
                           .Where(a => a.Owner == player && !a.IsDead && a.IsInWorld);

            if (!newUnits.Any())
            {
                return;
            }

            var capturers = newUnits
                            .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name))
                            .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>()))
                            .Where(tp => tp.Trait != null);

            if (!capturers.Any())
            {
                return;
            }

            var baseCenter = world.Map.CenterOfCell(initialBaseCenter);

            if (world.LocalRandom.Next(100) < Info.PriorityCaptureChance)
            {
                var priorityTargets = world.Actors.Where(a =>
                                                         !a.IsDead && a.IsInWorld && Info.CapturableStances.HasRelationship(player.RelationshipWith(a.Owner)) &&
                                                         Info.PriorityCapturableActorTypes.Contains(a.Info.Name.ToLowerInvariant()));

                if (Info.CheckCaptureTargetsForVisibility)
                {
                    priorityTargets = priorityTargets.Where(a => a.CanBeViewedByPlayer(player));
                }

                if (priorityTargets.Any())
                {
                    priorityTargets = priorityTargets.OrderBy(a => (a.CenterPosition - baseCenter).LengthSquared);

                    var priorityCaptures = Math.Min(capturers.Count(), priorityTargets.Count());

                    for (int i = 0; i < priorityCaptures; i++)
                    {
                        var capturer = capturers.First();

                        var priorityTarget = priorityTargets.First();

                        var captureManager = priorityTarget.TraitOrDefault <CaptureManager>();
                        if (captureManager != null && captureManager.CanBeTargetedBy(priorityTarget, capturer.Actor, capturer.Trait))
                        {
                            var safeTarget = SafePath(capturer.Actor, priorityTarget);
                            if (safeTarget.Type == TargetType.Invalid)
                            {
                                priorityTargets = priorityTargets.Skip(1);
                                capturers       = capturers.Skip(1);
                                continue;
                            }

                            bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true));
                            AIUtils.BotDebug("AI ({0}): Ordered {1} {2} to capture {3} {4} in priority mode.",
                                             player.ClientIndex, capturer.Actor, capturer.Actor.ActorID, priorityTarget, priorityTarget.ActorID);
                        }

                        priorityTargets = priorityTargets.Skip(1);
                        capturers       = capturers.Skip(1);
                    }

                    if (!capturers.Any())
                    {
                        return;
                    }
                }
            }

            var randomPlayer = world.Players.Where(p => !p.Spectating &&
                                                   Info.CapturableStances.HasRelationship(player.RelationshipWith(p))).Random(world.LocalRandom);

            var targetOptions = Info.CheckCaptureTargetsForVisibility
                                ? GetVisibleActorsBelongingToPlayer(randomPlayer)
                                : GetActorsThatCanBeOrderedByPlayer(randomPlayer);

            var capturableTargetOptions = targetOptions
                                          .Where(target =>
            {
                var captureManager = target.TraitOrDefault <CaptureManager>();
                if (captureManager == null)
                {
                    return(false);
                }

                return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait)));
            })
                                          .OrderBy(target => (target.CenterPosition - baseCenter).LengthSquared)
                                          .Take(maximumCaptureTargetOptions);

            if (Info.CapturableActorTypes.Any())
            {
                capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant()));
            }

            if (!capturableTargetOptions.Any())
            {
                return;
            }

            foreach (var capturer in capturers)
            {
                var nearestTargetActors = capturableTargetOptions.OrderBy(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared);
                foreach (var nearestTargetActor in nearestTargetActors)
                {
                    var safeTarget = SafePath(capturer.Actor, nearestTargetActor);
                    if (safeTarget.Type == TargetType.Invalid)
                    {
                        continue;
                    }

                    bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true));
                    AIUtils.BotDebug("AI: Ordered {0} to capture {1}", capturer.Actor, nearestTargetActor);
                    break;
                }
            }
        }
예제 #20
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;
                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);
        }
예제 #21
0
        void IBotTick.BotTick(IBot bot)
        {
            if (--scanForBitsTicks > 0)
            {
                return;
            }

            scanForBitsTicks = Info.ScanInterval;

            var bits = world.ActorsHavingTrait <ColonyBit>().ToList();

            if (!bits.Any())
            {
                return;
            }

            if (Info.CheckTargetsForVisibility)
            {
                bits.RemoveAll(c => !c.CanBeViewedByPlayer(player));
            }

            var units = world.ActorsHavingTrait <Mobile>().Where(a => a.Owner == player && a.IsIdle &&
                                                                 (Info.IncludedUnitTypes.Contains(a.Info.Name) || (!Info.IncludedUnitTypes.Any() && !Info.ExcludedUnitTypes.Contains(a.Info.Name)))).ToList();

            if (!units.Any())
            {
                return;
            }

            foreach (var bit in bits)
            {
                var bitCollector = units.ClosestTo(bit);
                if (bitCollector == null)
                {
                    continue;
                }

                if ((bit.Location - bitCollector.Location).Length > maxProximity)
                {
                    continue;
                }

                units.Remove(bitCollector);

                if (squadManagerBotModule == null)
                {
                    squadManagerBotModule = bot.Player.PlayerActor.TraitsImplementing <SquadManagerBotModule>().FirstEnabledTraitOrDefault();
                }

                if (squadManagerBotModule != null)
                {
                    // You got ONE job!
                    var squad = squadManagerBotModule.Squads.FirstOrDefault(s => s.Units.Contains(bitCollector));
                    if (squad != null)
                    {
                        squad.Units.Remove(bitCollector);
                    }
                }

                var target = Target.FromCell(world, bit.Location);
                AIUtils.BotDebug("AI: Ordering unit {0} to {1} for colony bit pick up.".F(bitCollector, target));
                bot.QueueOrder(new Order("Stop", bitCollector, false));
                bot.QueueOrder(new Order("Move", bitCollector, target, false));
            }
        }
        ActorInfo ChooseBuildingToBuild(BuilderUnit 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));
            var hq       = GetProducibleBuilding(baseBuilder.Info.HQTypes, buildableThings);
            var water    = GetProducibleBuilding(baseBuilder.Info.RefineryTypes, buildableThings);
            var barracks = GetProducibleBuilding(baseBuilder.Info.BarracksTypes, buildableThings);
            var vehicles = GetProducibleBuilding(baseBuilder.Info.VehiclesFactoryTypes, buildableThings);

            // First priority is to get an HQ
            if (hq != null && NumBuildingsBuiltBuildingOrOrdered(hq) == 0)
            {
                AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (no HQ)", queue.Actor.Owner, hq.Name);
                return(hq);
            }

            // Second is to get out of a low power situation
            if (power != null)
            {
                if (NumBuildingsBuiltBuildingOrOrdered(power) == 0)
                {
                    AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name);
                    return(power);
                }

                if (playerPower.ExcessPower < minimumExcessPower && NumBuildingsBuildingOrOrdered(power) == 0)
                {
                    AIUtils.BotDebug("AI: {0} decided to build {1}: Increase power overhead", queue.Actor.Owner, power.Name);
                    return(power);
                }
            }

            // Next is to build up a strong economy
            if (water != null)
            {
                if (NumBuildingsBuiltBuildingOrOrdered(water) < 2)
                {
                    AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (not enough water launch pads)", queue.Actor.Owner, water.Name);
                    return(water);
                }
            }

            if (barracks != null)
            {
                if (NumBuildingsBuiltBuildingOrOrdered(barracks) < 1)
                {
                    AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (not enough training facilities)", queue.Actor.Owner, barracks.Name);
                    return(barracks);
                }

                if (vehicles != null)
                {
                    if (NumBuildingsBuiltBuildingOrOrdered(vehicles) < 1)
                    {
                        AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (not enough assembly plants)", queue.Actor.Owner, vehicles.Name);
                        return(vehicles);
                    }
                }

                if (NumBuildingsBuiltBuildingOrOrdered(barracks) < 2)
                {
                    AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (not enough training facilities)", queue.Actor.Owner, barracks.Name);
                    return(barracks);
                }

                if (vehicles != null)
                {
                    if (NumBuildingsBuiltBuildingOrOrdered(vehicles) < 2)
                    {
                        AIUtils.BotDebug("AI: {0} decided to build {1}: Priority override (not enough assembly plants)", queue.Actor.Owner, vehicles.Name);
                        return(vehicles);
                    }
                }
            }

            // 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;
                }

                // TODO: This is a DR hack
                if (baseBuilder.Info.BuildingAliases.ContainsKey(name))
                {
                    var aliases = baseBuilder.Info.BuildingAliases[name];
                    if (playerBuildings.Count(a => aliases.Contains(a.Info.Name)) >= baseBuilder.Info.BuildingLimits[name])
                    {
                        continue;
                    }
                }

                if (baseBuilder.Info.BuildingLimits.ContainsKey(name) && baseBuilder.Info.BuildingLimits[name] <= count)
                {
                    continue;
                }

                var actor = world.Map.Rules.Actors[name];

                // Do we already have one under construction or ordered?
                if (NumBuildingsBuildingOrOrdered(actor) > 0)
                {
                    continue;
                }

                // Will this put us into low power?
                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);
        }
예제 #23
0
        void IBotTick.BotTick(IBot bot)
        {
            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("{0} couldn't find powerDecision for {1}", player.PlayerName, sp.Info.OrderName);
                        continue;
                    }

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

                        continue;
                    }

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

                        continue;
                    }

                    // Valid target found, delay by a few ticks to avoid rescanning before power fires via order
                    AIUtils.BotDebug("{0} found new target location {1} for support power {2}.", player.PlayerName, attackLocation, sp.Info.OrderName);
                    waitingPowers[sp] += 10;

                    // Note: SelectDirectionalTarget uses uint.MaxValue in ExtraData to indicate that the player did not pick a direction.
                    bot.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false)
                    {
                        SuppressVisualFeedback = true, ExtraData = uint.MaxValue
                    });
                }
            }

            // Remove stale powers
            stalePowers.AddRange(waitingPowers.Keys.Where(wp => !supportPowerManager.Powers.ContainsKey(wp.Key)));
            foreach (var p in stalePowers)
            {
                waitingPowers.Remove(p);
            }

            stalePowers.Clear();
        }
예제 #24
0
        void QueueCaptureOrders(IBot bot)
        {
            if (!Info.CapturingActorTypes.Any() || player.WinState != WinState.Undefined)
            {
                return;
            }

            activeCapturers.RemoveAll(unitCannotBeOrderedOrIsIdle);

            var newUnits = world.ActorsHavingTrait <IPositionable>()
                           .Where(a => a.Owner == player && !activeCapturers.Contains(a));

            var capturers = newUnits
                            .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo <CapturesInfo>())
                            .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>()))
                            .Where(tp => tp.Trait != null)
                            .ToArray();

            if (capturers.Length == 0)
            {
                return;
            }

            var randomPlayer = world.Players.Where(p => !p.Spectating &&
                                                   Info.CapturableStances.HasStance(player.RelationshipWith(p))).Random(world.LocalRandom);

            var targetOptions = Info.CheckCaptureTargetsForVisibility
                                ? GetVisibleActorsBelongingToPlayer(randomPlayer)
                                : GetActorsThatCanBeOrderedByPlayer(randomPlayer);

            var capturableTargetOptions = targetOptions
                                          .Where(target =>
            {
                var captureManager = target.TraitOrDefault <CaptureManager>();
                if (captureManager == null)
                {
                    return(false);
                }

                return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait)));
            })
                                          .OrderByDescending(target => target.GetSellValue())
                                          .Take(maximumCaptureTargetOptions);

            if (Info.CapturableActorTypes.Any())
            {
                capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant()));
            }

            if (!capturableTargetOptions.Any())
            {
                return;
            }

            foreach (var capturer in capturers)
            {
                var nearestTargetActors = capturableTargetOptions.OrderBy(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared);
                foreach (var nearestTargetActor in nearestTargetActors)
                {
                    if (activeCapturers.Contains(capturer.Actor))
                    {
                        continue;
                    }

                    var safeTarget = SafePath(capturer.Actor, nearestTargetActor);
                    if (safeTarget.Type == TargetType.Invalid)
                    {
                        continue;
                    }

                    bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true));
                    AIUtils.BotDebug("AI ({0}): Ordered {1} to capture {2}", player.ClientIndex, capturer.Actor, nearestTargetActor);
                    activeCapturers.Add(capturer.Actor);
                }
            }
        }
예제 #25
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);
        }
예제 #26
0
        bool TickQueue(IBot bot, ProductionQueue queue)
        {
            if (queue.Actor.World.Actors.Any(actor =>
            {
                if (actor.Owner != queue.Actor.Owner)
                {
                    return(false);
                }

                var buildable = actor.Info.TraitInfoOrDefault <BuildableInfo>();
                if (buildable == null || !buildable.Queue.Contains(queue.Info.Type))
                {
                    return(false);
                }

                var withSpriteBody = actor.TraitOrDefault <WithSpriteBody>();
                if (withSpriteBody == null || !withSpriteBody.DefaultAnimation.CurrentSequence.Name.StartsWith("make"))
                {
                    return(false);
                }

                return(true);
            }))
            {
                return(true);
            }

            var choosenBuilding = ChooseBuildingToBuild(queue);

            if (choosenBuilding == null)
            {
                return(true);
            }

            var currentBuilding = queue.AllQueued().FirstOrDefault(item => item.Item == choosenBuilding.Name);

            if (currentBuilding == null)
            {
                return(true);
            }

            // 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;

                // Check if Building is a defense and if we should place it towards the enemy or not.
                if (world.Map.Rules.Actors[currentBuilding.Item].HasTraitInfo <AttackBaseInfo>() && world.LocalRandom.Next(100) < baseBuilder.Info.PlaceDefenseTowardsEnemyChance)
                {
                    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);
        }