Prioritize least busy buildings and enforce max training queue limit

getTotalQueueTime(queue) calculates total remaining queue time of a production building.
getBuildingsSortedByQueueTime(entities), sorts building selection by their queued training time.
This commit is contained in:
guerringuerrin 2026-03-18 19:05:12 -03:00
parent 32e5520507
commit a6460189ed

View file

@ -1596,6 +1596,30 @@ function updateDefaultBatchSize()
g_BatchSize = getDefaultBatchTrainingSize();
}
/**
* Calculates the total remaining production queue time of a building.
*/
function getTotalQueueTime(queue)
{
return queue.reduce((sum, item) => sum + (item.timeRemaining ?? item.timeTotal ?? 0), 0);
}
/**
* Returns the given production building selection sorted by their current
* queued training time, from lowest to highest.
*/
function getBuildingsSortedByQueueTime(entities)
{
return entities.map(entity =>
{
const queue = GetEntityState(entity)?.production?.queue || [];
return {
"entity": entity,
"totalQueueTime": getTotalQueueTime(queue)
};
}).sort((a, b) => a.totalQueueTime - b.totalQueueTime).map(building => building.entity);
}
/**
* Add the unit shown at position to the training queue for all entities in the selection.
* @param {number} position - The position of the template to train.
@ -1628,6 +1652,7 @@ function addTrainingToQueue(selection, trainEntType, playerState)
if (!decrement)
template = GetTemplateData(trainEntType);
const sortedBuildings = getBuildingsSortedByQueueTime(appropriateBuildings);
// Batch training only possible if we can train at least 2 units.
if (Engine.HotkeyIsPressed("session.batchtrain") && (canBeAddedCount == undefined || canBeAddedCount > 1))
{
@ -1638,8 +1663,8 @@ function addTrainingToQueue(selection, trainEntType, playerState)
// If the order changed, we have a new selection and we should create a new batch.
// If we're already creating a batch of this unit (in the same structure(s)), then just extend it
// (if training limits allow).
if (g_BatchTrainingEntities.length == selection.length &&
g_BatchTrainingEntities.every((ent, i) => ent == selection[i]) &&
if (g_BatchTrainingEntities.length == sortedBuildings.length &&
g_BatchTrainingEntities.every((ent, i) => ent == sortedBuildings[i]) &&
g_BatchTrainingType == trainEntType)
{
if (decrement)
@ -1649,7 +1674,7 @@ function addTrainingToQueue(selection, trainEntType, playerState)
inputState = INPUT_NORMAL;
}
else if (canBeAddedCount == undefined ||
canBeAddedCount > g_NumberOfBatches * getBatchTrainingSize() * appropriateBuildings.length)
canBeAddedCount > g_NumberOfBatches * getBatchTrainingSize() * sortedBuildings.length)
{
if (Engine.GuiInterfaceCall("GetNeededResources", {
"cost": multiplyEntityCosts(template, (g_NumberOfBatches + 1) * getBatchTrainingSize())
@ -1671,14 +1696,14 @@ function addTrainingToQueue(selection, trainEntType, playerState)
return;
inputState = INPUT_BATCHTRAINING;
g_BatchTrainingEntities = selection;
g_BatchTrainingEntities = sortedBuildings;
g_BatchTrainingType = trainEntType;
g_BatchTrainingEntityAllowedCount = canBeAddedCount;
g_NumberOfBatches = 1;
}
else
{
let buildingsForTraining = appropriateBuildings;
let buildingsForTraining = sortedBuildings;
if (canBeAddedCount !== undefined)
buildingsForTraining = buildingsForTraining.slice(0, canBeAddedCount);
Engine.PostNetworkCommand({
@ -1734,6 +1759,7 @@ function flushTrainingBatch()
{
const batchedSize = g_NumberOfBatches * getBatchTrainingSize();
const appropriateBuildings = getBuildingsWhichCanTrainEntity(g_BatchTrainingEntities, g_BatchTrainingType);
const sortedBuildings = getBuildingsSortedByQueueTime(appropriateBuildings);
// If training limits don't allow us to train batchedSize in each appropriate structure.
if (g_BatchTrainingEntityAllowedCount !== undefined &&
g_BatchTrainingEntityAllowedCount < batchedSize * appropriateBuildings.length)
@ -1742,7 +1768,7 @@ function flushTrainingBatch()
const buildingsCountToTrainFullBatch = Math.floor(g_BatchTrainingEntityAllowedCount / batchedSize);
Engine.PostNetworkCommand({
"type": "train",
"entities": appropriateBuildings.slice(0, buildingsCountToTrainFullBatch),
"entities": sortedBuildings.slice(0, buildingsCountToTrainFullBatch),
"template": g_BatchTrainingType,
"count": batchedSize,
"pushFront": Engine.HotkeyIsPressed("session.pushorderfront")
@ -1753,7 +1779,7 @@ function flushTrainingBatch()
if (remainer)
Engine.PostNetworkCommand({
"type": "train",
"entities": [appropriateBuildings[buildingsCountToTrainFullBatch]],
"entities": [sortedBuildings[buildingsCountToTrainFullBatch]],
"template": g_BatchTrainingType,
"count": remainer,
"pushFront": Engine.HotkeyIsPressed("session.pushorderfront")
@ -1762,7 +1788,7 @@ function flushTrainingBatch()
else
Engine.PostNetworkCommand({
"type": "train",
"entities": appropriateBuildings,
"entities": sortedBuildings,
"template": g_BatchTrainingType,
"count": batchedSize,
"pushFront": Engine.HotkeyIsPressed("session.pushorderfront")