Improve findIdleUnit and hasIdleUnit code. Patch by Clockwork-Muse. Fixes #3826
This was SVN commit r18139.
This commit is contained in:
parent
09fd6da38e
commit
cc3d7d58ea
@ -39,6 +39,7 @@
|
||||
{"nick": "Calvinh", "name": "Carl-Johan Höiby"},
|
||||
{"name": "Cédric Houbart"},
|
||||
{"nick": "Chakakhan", "name": "Kenny Long"},
|
||||
{"nick": "Clockwork-Muse", "name": "Stephen A. Imhoff"},
|
||||
{"nick": "Cracker78", "name": "Chad Heim"},
|
||||
{"nick": "Crynux", "name": "Stephen J. Fewer"},
|
||||
{"nick": "cwprogger"},
|
||||
|
@ -1683,14 +1683,14 @@ function setCameraFollow(entity)
|
||||
}
|
||||
|
||||
var lastIdleUnit = 0;
|
||||
var currIdleClass = 0;
|
||||
var lastIdleType = undefined;
|
||||
var currIdleClassIndex = 0;
|
||||
var lastIdleClasses = [];
|
||||
|
||||
function resetIdleUnit()
|
||||
{
|
||||
lastIdleUnit = 0;
|
||||
currIdleClass = 0;
|
||||
lastIdleType = undefined;
|
||||
currIdleClassIndex = 0;
|
||||
lastIdleClasses = [];
|
||||
}
|
||||
|
||||
function findIdleUnit(classes)
|
||||
@ -1699,61 +1699,46 @@ function findIdleUnit(classes)
|
||||
var selectall = Engine.HotkeyIsPressed("selection.offscreen");
|
||||
|
||||
// Reset the last idle unit, etc., if the selection type has changed.
|
||||
var type = classes.join();
|
||||
if (selectall || type != lastIdleType)
|
||||
if (selectall || classes.length != lastIdleClasses.length || !classes.every((v,i) => v === lastIdleClasses[i]))
|
||||
resetIdleUnit();
|
||||
lastIdleType = type;
|
||||
lastIdleClasses = classes;
|
||||
|
||||
// If selectall is true, there is no limit and it's necessary to iterate
|
||||
// over all of the classes, resetting only when the first match is found.
|
||||
var matched = false;
|
||||
|
||||
for (var i = 0; i < classes.length; ++i)
|
||||
var data = {
|
||||
"viewedPlayer": g_ViewedPlayer,
|
||||
"excludeUnits": append ? g_Selection.toList() : [],
|
||||
// If the current idle class index is not 0, put the class at that index first.
|
||||
"idleClasses": classes.slice(currIdleClassIndex, classes.length).concat(classes.slice(0, currIdleClassIndex))
|
||||
};
|
||||
if (!selectall)
|
||||
{
|
||||
var data = {
|
||||
"idleClass": classes[currIdleClass],
|
||||
"prevUnit": lastIdleUnit,
|
||||
"limit": 1,
|
||||
"excludeUnits": []
|
||||
};
|
||||
|
||||
if (append)
|
||||
data.excludeUnits = g_Selection.toList();
|
||||
|
||||
if (selectall)
|
||||
data = { "idleClass": classes[currIdleClass] };
|
||||
|
||||
data.viewedPlayer = g_ViewedPlayer;
|
||||
|
||||
// Check if we have new valid entity
|
||||
var idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", data);
|
||||
if (idleUnits.length && idleUnits[0] != lastIdleUnit)
|
||||
{
|
||||
lastIdleUnit = idleUnits[0];
|
||||
if (!append && (!selectall || selectall && !matched))
|
||||
g_Selection.reset();
|
||||
|
||||
if (selectall)
|
||||
g_Selection.addList(idleUnits);
|
||||
else
|
||||
{
|
||||
g_Selection.addList([lastIdleUnit]);
|
||||
var position = GetEntityState(lastIdleUnit).position;
|
||||
if (position)
|
||||
Engine.CameraMoveTo(position.x, position.z);
|
||||
return;
|
||||
}
|
||||
|
||||
matched = true;
|
||||
}
|
||||
|
||||
lastIdleUnit = 0;
|
||||
currIdleClass = (currIdleClass + 1) % classes.length;
|
||||
data.limit = 1;
|
||||
data.prevUnit = lastIdleUnit;
|
||||
}
|
||||
|
||||
// TODO: display a message or play a sound to indicate no more idle units, or something
|
||||
// Reset for next cycle
|
||||
resetIdleUnit();
|
||||
var idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", data);
|
||||
if (!idleUnits.length)
|
||||
{
|
||||
// TODO: display a message or play a sound to indicate no more idle units, or something
|
||||
// Reset for next cycle
|
||||
resetIdleUnit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!append)
|
||||
g_Selection.reset();
|
||||
g_Selection.addList(idleUnits);
|
||||
|
||||
if (selectall)
|
||||
return;
|
||||
|
||||
lastIdleUnit = idleUnits[0];
|
||||
var entityState = GetEntityState(lastIdleUnit);
|
||||
var position = entityState.position;
|
||||
if (position)
|
||||
Engine.CameraMoveTo(position.x, position.z);
|
||||
// Move the idle class index to the first class an idle unit was found for.
|
||||
var indexChange = data.idleClasses.findIndex(elem => hasClass(entityState, elem));
|
||||
currIdleClassIndex = (currIdleClassIndex + indexChange) % classes.length;
|
||||
}
|
||||
|
||||
function stopUnits(entities)
|
||||
|
@ -650,20 +650,11 @@ function changeGameSpeed(speed)
|
||||
|
||||
function hasIdleWorker()
|
||||
{
|
||||
for (let workerType of g_WorkerTypes)
|
||||
{
|
||||
let idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", {
|
||||
return Engine.GuiInterfaceCall("HasIdleUnits", {
|
||||
"viewedPlayer": g_ViewedPlayer,
|
||||
"idleClass": workerType,
|
||||
"prevUnit": undefined,
|
||||
"limit": 1,
|
||||
"idleClasses": g_WorkerTypes,
|
||||
"excludeUnits": []
|
||||
});
|
||||
|
||||
if (idleUnits.length > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function updateIdleWorkerButton()
|
||||
|
@ -1645,34 +1645,93 @@ GuiInterface.prototype.PlaySound = function(player, data)
|
||||
PlaySound(data.name, data.entity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find any idle units.
|
||||
*
|
||||
* @param data.viewedPlayer The player for which to find idle units.
|
||||
* @param data.idleClasses Array of class names to include.
|
||||
* @param data.prevUnit The previous idle unit, if calling a second time to iterate through units. May be left undefined.
|
||||
* @param data.limit The number of idle units to return. May be left undefined (will return all idle units).
|
||||
* @param data.excludeUnits Array of units to exclude.
|
||||
*
|
||||
* Returns an array of idle units.
|
||||
* If multiple classes were supplied, and multiple items will be returned, the items will be sorted by class.
|
||||
*/
|
||||
GuiInterface.prototype.FindIdleUnits = function(player, data)
|
||||
{
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
let playerEntities = cmpRangeManager.GetEntitiesByPlayer(data.viewedPlayer).filter(entity => {
|
||||
|
||||
let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
if (!cmpUnitAI || !cmpUnitAI.IsIdle() || cmpUnitAI.IsGarrisoned())
|
||||
return false;
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
|
||||
if (!cmpIdentity || !cmpIdentity.HasClass(data.idleClass))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
let idleUnits = [];
|
||||
|
||||
for (let ent of playerEntities)
|
||||
// The general case is that only the 'first' idle unit is required; filtering would examine every unit.
|
||||
// This loop imitates a grouping/aggregation on the first matching idle class.
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
for (let entity of cmpRangeManager.GetEntitiesByPlayer(data.viewedPlayer))
|
||||
{
|
||||
if (ent <= data.prevUnit|0 || data.excludeUnits.indexOf(ent) > -1)
|
||||
let filtered = this.IdleUnitFilter(entity, data.idleClasses, data.excludeUnits);
|
||||
if (!filtered.idle)
|
||||
continue;
|
||||
idleUnits.push(ent);
|
||||
if (data.limit && idleUnits.length >= data.limit)
|
||||
break;
|
||||
|
||||
// If the entity is in the 'current' (first, 0) bucket on a resumed search, it must be after the "previous" unit, if any.
|
||||
// By adding to the 'end', there is no pause if the series of units loops.
|
||||
var bucket = filtered.bucket;
|
||||
if(bucket == 0 && data.prevUnit && entity <= data.prevUnit)
|
||||
bucket = data.idleClasses.length;
|
||||
|
||||
if (!idleUnits[bucket])
|
||||
idleUnits[bucket] = [];
|
||||
idleUnits[bucket].push(entity);
|
||||
|
||||
// If enough units have been collected in the first bucket, go ahead and return them.
|
||||
if (data.limit && bucket == 0 && idleUnits[0].length == data.limit)
|
||||
return idleUnits[0];
|
||||
}
|
||||
|
||||
return idleUnits;
|
||||
let reduced = idleUnits.reduce((prev, curr) => prev.concat(curr), []);
|
||||
if (data.limit && reduced.length > data.limit)
|
||||
return reduced.slice(0, data.limit);
|
||||
|
||||
return reduced;
|
||||
};
|
||||
|
||||
/**
|
||||
* Discover if the player has idle units.
|
||||
*
|
||||
* @param data.viewedPlayer The player for which to find idle units.
|
||||
* @param data.idleClasses Array of class names to include.
|
||||
* @param data.excludeUnits Array of units to exclude.
|
||||
*
|
||||
* Returns a boolean of whether the player has any idle units
|
||||
*/
|
||||
GuiInterface.prototype.HasIdleUnits = function(player, data)
|
||||
{
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
return cmpRangeManager.GetEntitiesByPlayer(data.viewedPlayer).some(unit => this.IdleUnitFilter(unit, data.idleClasses, data.excludeUnits).idle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether to filter an idle unit
|
||||
*
|
||||
* @param unit The unit to filter.
|
||||
* @param idleclasses Array of class names to include.
|
||||
* @param excludeUnits Array of units to exclude.
|
||||
*
|
||||
* Returns an object with the following fields:
|
||||
* - idle - true if the unit is considered idle by the filter, false otherwise.
|
||||
* - bucket - if idle, set to the index of the first matching idle class, undefined otherwise.
|
||||
*/
|
||||
GuiInterface.prototype.IdleUnitFilter = function(unit, idleClasses, excludeUnits)
|
||||
{
|
||||
let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI);
|
||||
if (!cmpUnitAI || !cmpUnitAI.IsIdle() || cmpUnitAI.IsGarrisoned())
|
||||
return { "idle": false };
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(unit, IID_Identity);
|
||||
if(!cmpIdentity)
|
||||
return { "idle": false };
|
||||
|
||||
let bucket = idleClasses.findIndex(elem => cmpIdentity.HasClass(elem));
|
||||
if (bucket == -1 || excludeUnits.indexOf(unit) > -1)
|
||||
return { "idle": false };
|
||||
|
||||
return { "idle": true, "bucket": bucket };
|
||||
};
|
||||
|
||||
GuiInterface.prototype.GetTradingRouteGain = function(player, data)
|
||||
@ -1899,6 +1958,7 @@ let exposedFunctions = {
|
||||
"GetFoundationSnapData": 1,
|
||||
"PlaySound": 1,
|
||||
"FindIdleUnits": 1,
|
||||
"HasIdleUnits": 1,
|
||||
"GetTradingRouteGain": 1,
|
||||
"GetTradingDetails": 1,
|
||||
"CanCapture": 1,
|
||||
|
Loading…
Reference in New Issue
Block a user