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"},
|
{"nick": "Calvinh", "name": "Carl-Johan Höiby"},
|
||||||
{"name": "Cédric Houbart"},
|
{"name": "Cédric Houbart"},
|
||||||
{"nick": "Chakakhan", "name": "Kenny Long"},
|
{"nick": "Chakakhan", "name": "Kenny Long"},
|
||||||
|
{"nick": "Clockwork-Muse", "name": "Stephen A. Imhoff"},
|
||||||
{"nick": "Cracker78", "name": "Chad Heim"},
|
{"nick": "Cracker78", "name": "Chad Heim"},
|
||||||
{"nick": "Crynux", "name": "Stephen J. Fewer"},
|
{"nick": "Crynux", "name": "Stephen J. Fewer"},
|
||||||
{"nick": "cwprogger"},
|
{"nick": "cwprogger"},
|
||||||
|
@ -1683,14 +1683,14 @@ function setCameraFollow(entity)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lastIdleUnit = 0;
|
var lastIdleUnit = 0;
|
||||||
var currIdleClass = 0;
|
var currIdleClassIndex = 0;
|
||||||
var lastIdleType = undefined;
|
var lastIdleClasses = [];
|
||||||
|
|
||||||
function resetIdleUnit()
|
function resetIdleUnit()
|
||||||
{
|
{
|
||||||
lastIdleUnit = 0;
|
lastIdleUnit = 0;
|
||||||
currIdleClass = 0;
|
currIdleClassIndex = 0;
|
||||||
lastIdleType = undefined;
|
lastIdleClasses = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function findIdleUnit(classes)
|
function findIdleUnit(classes)
|
||||||
@ -1699,61 +1699,46 @@ function findIdleUnit(classes)
|
|||||||
var selectall = Engine.HotkeyIsPressed("selection.offscreen");
|
var selectall = Engine.HotkeyIsPressed("selection.offscreen");
|
||||||
|
|
||||||
// Reset the last idle unit, etc., if the selection type has changed.
|
// Reset the last idle unit, etc., if the selection type has changed.
|
||||||
var type = classes.join();
|
if (selectall || classes.length != lastIdleClasses.length || !classes.every((v,i) => v === lastIdleClasses[i]))
|
||||||
if (selectall || type != lastIdleType)
|
|
||||||
resetIdleUnit();
|
resetIdleUnit();
|
||||||
lastIdleType = type;
|
lastIdleClasses = classes;
|
||||||
|
|
||||||
// If selectall is true, there is no limit and it's necessary to iterate
|
var data = {
|
||||||
// over all of the classes, resetting only when the first match is found.
|
"viewedPlayer": g_ViewedPlayer,
|
||||||
var matched = false;
|
"excludeUnits": append ? g_Selection.toList() : [],
|
||||||
|
// If the current idle class index is not 0, put the class at that index first.
|
||||||
for (var i = 0; i < classes.length; ++i)
|
"idleClasses": classes.slice(currIdleClassIndex, classes.length).concat(classes.slice(0, currIdleClassIndex))
|
||||||
|
};
|
||||||
|
if (!selectall)
|
||||||
{
|
{
|
||||||
var data = {
|
data.limit = 1;
|
||||||
"idleClass": classes[currIdleClass],
|
data.prevUnit = lastIdleUnit;
|
||||||
"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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: display a message or play a sound to indicate no more idle units, or something
|
var idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", data);
|
||||||
// Reset for next cycle
|
if (!idleUnits.length)
|
||||||
resetIdleUnit();
|
{
|
||||||
|
// 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)
|
function stopUnits(entities)
|
||||||
|
@ -650,20 +650,11 @@ function changeGameSpeed(speed)
|
|||||||
|
|
||||||
function hasIdleWorker()
|
function hasIdleWorker()
|
||||||
{
|
{
|
||||||
for (let workerType of g_WorkerTypes)
|
return Engine.GuiInterfaceCall("HasIdleUnits", {
|
||||||
{
|
|
||||||
let idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", {
|
|
||||||
"viewedPlayer": g_ViewedPlayer,
|
"viewedPlayer": g_ViewedPlayer,
|
||||||
"idleClass": workerType,
|
"idleClasses": g_WorkerTypes,
|
||||||
"prevUnit": undefined,
|
|
||||||
"limit": 1,
|
|
||||||
"excludeUnits": []
|
"excludeUnits": []
|
||||||
});
|
});
|
||||||
|
|
||||||
if (idleUnits.length > 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateIdleWorkerButton()
|
function updateIdleWorkerButton()
|
||||||
|
@ -1645,34 +1645,93 @@ GuiInterface.prototype.PlaySound = function(player, data)
|
|||||||
PlaySound(data.name, data.entity);
|
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)
|
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 = [];
|
let idleUnits = [];
|
||||||
|
// The general case is that only the 'first' idle unit is required; filtering would examine every unit.
|
||||||
for (let ent of playerEntities)
|
// 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;
|
continue;
|
||||||
idleUnits.push(ent);
|
|
||||||
if (data.limit && idleUnits.length >= data.limit)
|
// If the entity is in the 'current' (first, 0) bucket on a resumed search, it must be after the "previous" unit, if any.
|
||||||
break;
|
// 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)
|
GuiInterface.prototype.GetTradingRouteGain = function(player, data)
|
||||||
@ -1899,6 +1958,7 @@ let exposedFunctions = {
|
|||||||
"GetFoundationSnapData": 1,
|
"GetFoundationSnapData": 1,
|
||||||
"PlaySound": 1,
|
"PlaySound": 1,
|
||||||
"FindIdleUnits": 1,
|
"FindIdleUnits": 1,
|
||||||
|
"HasIdleUnits": 1,
|
||||||
"GetTradingRouteGain": 1,
|
"GetTradingRouteGain": 1,
|
||||||
"GetTradingDetails": 1,
|
"GetTradingDetails": 1,
|
||||||
"CanCapture": 1,
|
"CanCapture": 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user