Fix bug that AI producion pause when there is too many unit in UnitDelays
This commit is contained in:
@@ -22,8 +22,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// TODO: Investigate whether this might the (or at least one) reason why bots occasionally get into a state of doing nothing.
|
// TODO: Investigate whether this might the (or at least one) reason why bots occasionally get into a state of doing nothing.
|
||||||
// Reason: If this is less than SquadSize, the bot might get stuck between not producing more units due to this,
|
// Reason: If this is less than SquadSize, the bot might get stuck between not producing more units due to this,
|
||||||
// but also not creating squads since there aren't enough idle units.
|
// but also not creating squads since there aren't enough idle units.
|
||||||
[Desc("Only produce units as long as there are less than this amount of units idling inside the base.")]
|
[Desc("If > 0, only produce units as long as there are less than this amount of units idling inside the base.",
|
||||||
public readonly int IdleBaseUnitsMaximum = 12;
|
"Beware: if it is less than squad size, e.g. the `SquadSize` from `SquadManagerBotModule`, the bot might get stuck as there aren't enough idle units to create squad.")]
|
||||||
|
public readonly int IdleBaseUnitsMaximum = -1;
|
||||||
|
|
||||||
[Desc("Production queues AI uses for producing units.")]
|
[Desc("Production queues AI uses for producing units.")]
|
||||||
public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" };
|
public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" };
|
||||||
@@ -94,17 +95,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
queuedBuildRequests.Remove(buildRequest);
|
queuedBuildRequests.Remove(buildRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < Info.UnitQueues.Length; i++)
|
if (Info.IdleBaseUnitsMaximum <= 0 || Info.IdleBaseUnitsMaximum > idleUnitCount)
|
||||||
{
|
{
|
||||||
if (++currentQueueIndex >= Info.UnitQueues.Length)
|
for (var i = 0; i < Info.UnitQueues.Length; i++)
|
||||||
currentQueueIndex = 0;
|
|
||||||
|
|
||||||
if (AIUtils.FindQueues(player, Info.UnitQueues[currentQueueIndex]).Any())
|
|
||||||
{
|
{
|
||||||
// PERF: We tick only one type of valid queue at a time
|
if (++currentQueueIndex >= Info.UnitQueues.Length)
|
||||||
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
currentQueueIndex = 0;
|
||||||
BuildUnit(bot, Info.UnitQueues[currentQueueIndex], idleUnitCount < Info.IdleBaseUnitsMaximum);
|
|
||||||
break;
|
if (AIUtils.FindQueues(player, Info.UnitQueues[currentQueueIndex]).Any())
|
||||||
|
{
|
||||||
|
// PERF: We tick only one type of valid queue at a time
|
||||||
|
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
||||||
|
BuildRandomUnit(bot, Info.UnitQueues[currentQueueIndex]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,36 +124,22 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return queuedBuildRequests.Count(r => r == requestedActor);
|
return queuedBuildRequests.Count(r => r == requestedActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildUnit(IBot bot, string category, bool buildRandom)
|
void BuildRandomUnit(IBot bot, string category)
|
||||||
{
|
{
|
||||||
|
if (Info.UnitsToBuild.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// Pick a free queue
|
// Pick a free queue
|
||||||
var queue = AIUtils.FindQueues(player, category).FirstOrDefault(q => !q.AllQueued().Any());
|
var queue = AIUtils.FindQueues(player, category).FirstOrDefault(q => !q.AllQueued().Any());
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var unit = buildRandom ?
|
var unit = ChooseRandomUnitToBuild(queue);
|
||||||
ChooseRandomUnitToBuild(queue) :
|
|
||||||
ChooseUnitToBuild(queue);
|
|
||||||
|
|
||||||
if (unit == null)
|
if (unit == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var name = unit.Name;
|
bot.QueueOrder(Order.StartProduction(queue.Actor, unit.Name, 1));
|
||||||
|
|
||||||
if (Info.UnitsToBuild != null && !Info.UnitsToBuild.ContainsKey(name))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Info.UnitDelays != null &&
|
|
||||||
Info.UnitDelays.TryGetValue(name, out var delay) &&
|
|
||||||
delay > world.WorldTick)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Info.UnitLimits != null &&
|
|
||||||
Info.UnitLimits.TryGetValue(name, out var limit) &&
|
|
||||||
world.Actors.Count(a => a.Owner == player && a.Info.Name == name) >= limit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bot.QueueOrder(Order.StartProduction(queue.Actor, name, 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility)
|
// In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility)
|
||||||
@@ -180,30 +170,35 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue)
|
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue)
|
||||||
{
|
{
|
||||||
var buildableThings = queue.BuildableItems();
|
var buildableThings = queue.BuildableItems().Shuffle(world.LocalRandom).ToArray();
|
||||||
var unit = buildableThings.RandomOrDefault(world.LocalRandom);
|
if (buildableThings.Length == 0)
|
||||||
return unit != null && HasAdequateAirUnitReloadBuildings(unit) ? unit : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ActorInfo ChooseUnitToBuild(ProductionQueue queue)
|
|
||||||
{
|
|
||||||
var buildableThings = queue.BuildableItems().Select(b => b.Name).ToHashSet();
|
|
||||||
if (buildableThings.Count == 0)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var myUnits = player.World
|
var allUnits = world.Actors.Where(a => a.Owner == player && Info.UnitsToBuild.ContainsKey(a.Info.Name) && !a.IsDead).ToArray();
|
||||||
.ActorsHavingTrait<IPositionable>()
|
|
||||||
.Where(a => a.Owner == player)
|
|
||||||
.Select(a => a.Info.Name)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var unit in Info.UnitsToBuild.Shuffle(world.LocalRandom))
|
ActorInfo desiredUnit = null;
|
||||||
if (buildableThings.Contains(unit.Key))
|
var desiredError = int.MaxValue;
|
||||||
if (myUnits.Count(a => a == unit.Key) * 100 < unit.Value * myUnits.Count)
|
foreach (var unit in buildableThings)
|
||||||
if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key]))
|
{
|
||||||
return world.Map.Rules.Actors[unit.Key];
|
if (!Info.UnitsToBuild.ContainsKey(unit.Name) || (Info.UnitDelays != null && Info.UnitDelays.TryGetValue(unit.Name, out var delay) && delay > world.WorldTick))
|
||||||
|
continue;
|
||||||
|
|
||||||
return null;
|
var unitCount = allUnits.Count(a => a.Info.Name == unit.Name);
|
||||||
|
if (Info.UnitLimits != null && Info.UnitLimits.TryGetValue(unit.Name, out var count) && unitCount >= count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var error = allUnits.Length > 0 ? unitCount * 100 / allUnits.Length - Info.UnitsToBuild[unit.Name] : -1;
|
||||||
|
if (error < 0)
|
||||||
|
return HasAdequateAirUnitReloadBuildings(unit) ? unit : null;
|
||||||
|
|
||||||
|
if (error < desiredError)
|
||||||
|
{
|
||||||
|
desiredError = error;
|
||||||
|
desiredUnit = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return desiredUnit != null ? (HasAdequateAirUnitReloadBuildings(desiredUnit) ? desiredUnit : null) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For mods like RA (number of RearmActors must match the number of aircraft)
|
// For mods like RA (number of RearmActors must match the number of aircraft)
|
||||||
|
|||||||
Reference in New Issue
Block a user