Skip to content

Commit

Permalink
AI Changes and Fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
MustaphaTR committed Mar 26, 2020
1 parent aafc627 commit 920e348
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 6 deletions.
5 changes: 5 additions & 0 deletions OpenRA.Mods.Common/AIUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public static bool IsAreaAvailable<T>(World world, Player player, Map map, int r
.Any(availableCells => availableCells > 0);
}

public static bool IsOwnedByEnemy(Actor a, Player player)
{
return player.Stances[a.Owner] == Stance.Enemy && a.Owner.InternalName.ToLowerInvariant().StartsWith("multi");
}

public static IEnumerable<ProductionQueue> FindQueues(Player player, string category)
{
return player.World.ActorsWithTrait<ProductionQueue>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,10 @@ ActorInfo GetProducibleBuilding(HashSet<string> actors, IEnumerable<ActorInfo> b
if (!baseBuilder.Info.BuildingLimits.ContainsKey(actor.Name))
return true;
var producers = world.Actors.Where(a => a.Owner == player && a.TraitsImplementing<ProductionQueue>() != null);
var producers = world.Actors.Where(a => a.Owner == player && a.TraitsImplementing<ProductionQueue>().Any());
var productionQueues = producers.SelectMany(a => a.TraitsImplementing<ProductionQueue>());
var activeProductionQueues = productionQueues.Where(pq => pq.AllQueued().Any());
var queues = activeProductionQueues.Select(pq => pq.AllQueued().Where(q => q.Item == actor.Name));
var queues = activeProductionQueues.Where(pq => pq.AllQueued().Where(q => q.Item == actor.Name).Any());
return playerBuildings.Count(a => a.Info.Name == actor.Name) + queues.Count() < baseBuilder.Info.BuildingLimits[actor.Name];
});
Expand Down Expand Up @@ -328,6 +328,10 @@ ActorInfo HackyChooseBuildingToBuild(ProductionQueue queue, IEnumerable<ActorInf
baseBuilder.Info.BuildingDelays[name] > world.WorldTick)
continue;

// Can we build this structure?
if (!buildableThings.Any(b => b.Name == name))
continue;

if (playerResources != null && playerResources.Cash <= baseBuilder.Info.ConstructionMinimumCash && !baseBuilder.Info.CashGeneratorTypes.Contains(name))
continue;

Expand All @@ -345,10 +349,10 @@ ActorInfo HackyChooseBuildingToBuild(ProductionQueue queue, IEnumerable<ActorInf
//}

// Do we want to build this structure?
var producers = world.Actors.Where(a => a.Owner == queue.Actor.Owner && a.TraitsImplementing<ProductionQueue>() != null);
var producers = world.Actors.Where(a => a.Owner == queue.Actor.Owner && a.TraitsImplementing<ProductionQueue>().Any());
var productionQueues = producers.SelectMany(a => a.TraitsImplementing<ProductionQueue>());
var activeProductionQueues = productionQueues.Where(pq => pq.AllQueued().Any());
var queues = activeProductionQueues.Select(pq => pq.AllQueued().Where(q => q.Item == name));
var queues = activeProductionQueues.Where(pq => pq.AllQueued().Where(q => q.Item == name).Any());

var count = playerBuildings.Count(a => a.Info.Name == name) + (queues == null ? 0 : queues.Count());
if (count * 100 > frac.Value * playerBuildings.Length)
Expand All @@ -365,14 +369,30 @@ ActorInfo HackyChooseBuildingToBuild(ProductionQueue queue, IEnumerable<ActorInf
|| !AIUtils.IsAreaAvailable<GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes)))
continue;

// Will this put us into low power?
if (!world.Map.Rules.Actors.ContainsKey(name))
{
AIUtils.BotDebug("{0} tryed to build an actor named {1}, no such actor exists.", queue.Actor.Owner, name);
continue;
}

// Maybe we can't queue this because of InstantCashDrain logic?
var actor = world.Map.Rules.Actors[name];
if (playerResources != null)
{
var nonICDQueues = productionQueues.Where(pq => !pq.Info.InstantCashDrain);
if (!nonICDQueues.Any())
{
var ICDQueues = productionQueues.Where(pq => pq.Info.InstantCashDrain);
if (ICDQueues.Any())
{
var cost = ICDQueues.Min(q => q.GetProductionCost(actor));
if (playerResources.Cash < cost)
continue;
}
}
}

// Will this put us into low power?
if (playerPower != null && (playerPower.ExcessPower < minimumExcessPower || !HasSufficientPowerForActor(actor)))
{
// Try building a power plant instead
Expand Down
93 changes: 92 additions & 1 deletion OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class SquadManagerBotModuleInfo : ConditionalTraitInfo
"Those units are not used by defensive squads.")]
public readonly HashSet<string> SiegeUnitTypes = new HashSet<string>();

[Desc("These units are built and deployed in a safe position in the base. Needs GrantConditionOnDeploy on the unit.")]
public readonly HashSet<string> FragileDeployerTypes = new HashSet<string>();

[Desc("These units are randomly sent around after their production.")]
public readonly HashSet<string> DozerTypes = new HashSet<string>();

[Desc("Minimum number of units AI must have before attacking.")]
public readonly int SquadSize = 8;

Expand All @@ -66,6 +72,15 @@ public class SquadManagerBotModuleInfo : ConditionalTraitInfo
[Desc("Radius in cells around the base that should be scanned for units to be protected.")]
public readonly int ProtectUnitScanRadius = 15;

[Desc("Minimum radius in cells around base center to send dozer after building it.")]
public readonly int MinDozerSendingRadius = 4;

[Desc("Maximum radius in cells around base center to send dozer after building it.")]
public readonly int MaxDozerSendingRadius = 16;

[Desc("Same as MinBaseRadius but for fragile structures to push them further away.")]
public readonly int MinFragilePlacementRadius = 8;

[Desc("Maximum distance in cells from center of the base when checking for MCV deployment location.",
"Only applies if RestrictMCVDeploymentFallbackToBase is enabled and there's at least one construction yard.")]
public readonly int MaxBaseRadius = 20;
Expand Down Expand Up @@ -101,6 +116,7 @@ public CPos GetRandomBaseCenter()

readonly Predicate<Actor> unitCannotBeOrdered;


public List<Squad> Squads = new List<Squad>();

IBotPositionsUpdated[] notifyPositionsUpdated;
Expand Down Expand Up @@ -218,6 +234,57 @@ void AssignRolesToIdleUnits(IBot bot)
}
}

CPos FindPosFrontForUnit(CPos center, CVec front, int minRange, int maxRange, Actor actor)
{
var mobile = actor.TraitOrDefault<Mobile>();
if (mobile == null)
return center;

// zero vector case. we can't define front or rear.
if (front == CVec.Zero)
return FindPosForUnit(center, center, minRange, maxRange, actor);

var cells = World.Map.FindTilesInAnnulus(center, minRange, maxRange).Shuffle(World.LocalRandom);
foreach (var cell in cells)
{
var v = cell - center;
if (!mobile.CanEnterCell(cell))
continue;

if (CVec.Dot(front, v) > 0)
return cell;
}

// Front is so full of stuff that we can't move there.
return center;
}

// Find the moveable cell that is closest to pos and centered around center
CPos FindPosForUnit(CPos center, CPos target, int minRange, int maxRange, Actor actor)
{
var mobile = actor.TraitOrDefault<Mobile>();
if (mobile == null)
return center;

var cells = World.Map.FindTilesInAnnulus(center, minRange, maxRange);

// Sort by distance to target if we have one
if (center != target)
cells = cells.OrderBy(c => (c - target).LengthSquared);
else
cells = cells.Shuffle(World.LocalRandom);

foreach (var cell in cells)
{
if (!mobile.CanEnterCell(cell))
continue;

return cell;
}

return center;
}

void FindNewUnits(IBot bot)
{
var newUnits = World.ActorsHavingTrait<IPositionable>()
Expand All @@ -227,7 +294,31 @@ void FindNewUnits(IBot bot)

foreach (var a in newUnits)
{
unitsHangingAroundTheBase.Add(a);
var closestEnemy = World.ActorsHavingTrait<Building>().Where(actor => !actor.Disposed && AIUtils.IsOwnedByEnemy(actor, bot.Player))
.ClosestTo(World.Map.CenterOfCell(GetRandomBaseCenter()));
var baseCenter = GetRandomBaseCenter();
var mobile = a.TraitOrDefault<Mobile>();

CVec direction = CVec.Zero;
if (closestEnemy != null)
direction = baseCenter - closestEnemy.Location;

if (Info.FragileDeployerTypes.Contains(a.Info.Name) && mobile != null)
{
bot.QueueOrder(new Order("Move", a,
Target.FromCell(World, FindPosFrontForUnit(baseCenter, direction, Info.MinFragilePlacementRadius, Info.MaxBaseRadius, a)), true));
bot.QueueOrder(new Order("GrantConditionOnDeploy", a, true));
}
else if (Info.DozerTypes.Contains(a.Info.Name) && mobile != null)
{
var dozerTargetPos = World.Map.FindTilesInAnnulus(baseCenter, Info.MinDozerSendingRadius, Info.MaxDozerSendingRadius)
.Where(c => mobile.CanEnterCell(c)).Random(World.LocalRandom);

AIUtils.BotDebug("AI: {0} has chosen {1} to move its Dozer ({2})".F(a.Owner, dozerTargetPos, a));
bot.QueueOrder(new Order("Move", a, Target.FromCell(World, dozerTargetPos), true));
}
else
unitsHangingAroundTheBase.Add(a);

if (a.Info.HasTraitInfo<AircraftInfo>() && a.Info.HasTraitInfo<AttackBaseInfo>() && a.Info.HasTraitInfo<SelectableInfo>())
{
Expand Down

0 comments on commit 920e348

Please sign in to comment.