Changeset View
Standalone View
binaries/data/mods/public/simulation/helpers/Commands.js
Show All 34 Lines | function ProcessCommand(player, cmd) | ||||
// Note: checks of UnitAI targets are not robust enough here, as ownership | // Note: checks of UnitAI targets are not robust enough here, as ownership | ||||
// can change after the order is issued, they should be checked by UnitAI | // can change after the order is issued, they should be checked by UnitAI | ||||
// when the specific behavior (e.g. attack, garrison) is performed. | // when the specific behavior (e.g. attack, garrison) is performed. | ||||
// (Also it's not ideal if a command silently fails, it's nicer if UnitAI | // (Also it's not ideal if a command silently fails, it's nicer if UnitAI | ||||
// moves the entities closer to the target before giving up.) | // moves the entities closer to the target before giving up.) | ||||
// Now handle various commands | // Now handle various commands | ||||
if (g_Commands[cmd.type]) | if (g_Commands[cmd.type]) | ||||
Lint: ESLintBear (no-use-before-define): `'g_Commands' was used before it was defined.` | |||||
{ | { | ||||
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); | var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); | ||||
cmpTrigger.CallEvent("PlayerCommand", { "player": player, "cmd": cmd }); | cmpTrigger.CallEvent("PlayerCommand", { "player": player, "cmd": cmd }); | ||||
g_Commands[cmd.type](player, cmd, data); | g_Commands[cmd.type](player, cmd, data); | ||||
Lint: ESLintBear (no-use-before-define) 'g_Commands' was used before it was defined. Lint: ESLintBear (no-use-before-define): `'g_Commands' was used before it was defined.` | |||||
} | } | ||||
else | else | ||||
error("Invalid command: unknown command type: "+uneval(cmd)); | error("Invalid command: unknown command type: "+uneval(cmd)); | ||||
} | } | ||||
var g_Commands = { | var g_Commands = { | ||||
Lint: JSHintBear 'g_Commands' was used before it was defined. Lint: JSHintBear: `'g_Commands' was used before it was defined.` | |||||
"aichat": function(player, cmd, data) | "aichat": function(player, cmd, data) | ||||
{ | { | ||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | ||||
var notification = { "players": [player] }; | var notification = { "players": [player] }; | ||||
for (var key in cmd) | for (var key in cmd) | ||||
notification[key] = cmd[key]; | notification[key] = cmd[key]; | ||||
cmpGuiInterface.PushNotification(notification); | cmpGuiInterface.PushNotification(notification); | ||||
▲ Show 20 Lines • Show All 444 Lines • ▼ Show 20 Lines | "unload-template": function(player, cmd, data) | ||||
var entities = FilterEntityListWithAllies(cmd.garrisonHolders, player, data.controlAllUnits); | var entities = FilterEntityListWithAllies(cmd.garrisonHolders, player, data.controlAllUnits); | ||||
for (let garrisonHolder of entities) | for (let garrisonHolder of entities) | ||||
{ | { | ||||
var cmpGarrisonHolder = Engine.QueryInterface(garrisonHolder, IID_GarrisonHolder); | var cmpGarrisonHolder = Engine.QueryInterface(garrisonHolder, IID_GarrisonHolder); | ||||
if (cmpGarrisonHolder) | if (cmpGarrisonHolder) | ||||
{ | { | ||||
// Only the owner of the garrisonHolder may unload entities from any owners | // Only the owner of the garrisonHolder may unload entities from any owners | ||||
if (!IsOwnedByPlayer(player, garrisonHolder) && !data.controlAllUnits | if (!IsOwnedByPlayer(player, garrisonHolder) && !data.controlAllUnits | ||||
&& player != +cmd.owner) | && player != +cmd.owner) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
continue; | continue; | ||||
Lint: ESLintBear (indent) Expected indentation of 5 tabs but found 6. Lint: ESLintBear (indent): `Expected indentation of 5 tabs but found 6.` | |||||
if (!cmpGarrisonHolder.UnloadTemplate(cmd.template, cmd.owner, cmd.all)) | if (!cmpGarrisonHolder.UnloadTemplate(cmd.template, cmd.owner, cmd.all)) | ||||
notifyUnloadFailure(player, garrisonHolder); | notifyUnloadFailure(player, garrisonHolder); | ||||
} | } | ||||
} | } | ||||
}, | }, | ||||
"unload-all-by-owner": function(player, cmd, data) | "unload-all-by-owner": function(player, cmd, data) | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | for (let ent of data.entities) | ||||
"players": [player], | "players": [player], | ||||
"message": markForTranslation("Cannot upgrade as distance requirements are not verified or terrain is obstructed.") | "message": markForTranslation("Cannot upgrade as distance requirements are not verified or terrain is obstructed.") | ||||
}); | }); | ||||
continue; | continue; | ||||
} | } | ||||
if (!CanGarrisonedChangeTemplate(ent, cmd.template)) | if (!CanGarrisonedChangeTemplate(ent, cmd.template)) | ||||
{ | { | ||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | ||||
Lint: JSHintBear 'cmpGUIInterface' is already defined. Lint: JSHintBear: `'cmpGUIInterface' is already defined.` | |||||
cmpGUIInterface.PushNotification({ | cmpGUIInterface.PushNotification({ | ||||
"players": [player], | "players": [player], | ||||
"message": markForTranslation("Cannot upgrade a garrisoned entity.") | "message": markForTranslation("Cannot upgrade a garrisoned entity.") | ||||
}); | }); | ||||
continue; | continue; | ||||
} | } | ||||
// Check entity limits | // Check entity limits | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | "attack-request": function(player, cmd, data) | ||||
let cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); | let cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); | ||||
if (cmpAIInterface) | if (cmpAIInterface) | ||||
cmpAIInterface.PushEvent("AttackRequest", cmd); | cmpAIInterface.PushEvent("AttackRequest", cmd); | ||||
}, | }, | ||||
"spy-request": function(player, cmd, data) | "spy-request": function(player, cmd, data) | ||||
{ | { | ||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
let ent = pickRandom(cmpRangeManager.GetEntitiesByPlayer(cmd.player).filter(ent => { | let ent = pickRandom(cmpRangeManager.GetEntitiesByPlayer(cmd.player).filter(ent => { | ||||
Lint: ESLintBear (no-shadow) 'ent' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'ent' is already declared in the upper scope.` | |||||
let cmpVisionSharing = Engine.QueryInterface(ent, IID_VisionSharing); | let cmpVisionSharing = Engine.QueryInterface(ent, IID_VisionSharing); | ||||
return cmpVisionSharing && cmpVisionSharing.IsBribable() && !cmpVisionSharing.ShareVisionWith(player); | return cmpVisionSharing && cmpVisionSharing.IsBribable() && !cmpVisionSharing.ShareVisionWith(player); | ||||
})); | })); | ||||
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | ||||
cmpGUIInterface.PushNotification({ | cmpGUIInterface.PushNotification({ | ||||
"type": "spy-response", | "type": "spy-response", | ||||
"players": [player], | "players": [player], | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | var g_Commands = { | ||||
{ | { | ||||
for (let ent of data.entities) | for (let ent of data.entities) | ||||
{ | { | ||||
let cmpResourceDropsite = Engine.QueryInterface(ent, IID_ResourceDropsite); | let cmpResourceDropsite = Engine.QueryInterface(ent, IID_ResourceDropsite); | ||||
if (cmpResourceDropsite && cmpResourceDropsite.IsSharable()) | if (cmpResourceDropsite && cmpResourceDropsite.IsSharable()) | ||||
cmpResourceDropsite.SetSharing(cmd.shared); | cmpResourceDropsite.SetSharing(cmd.shared); | ||||
} | } | ||||
}, | }, | ||||
"map-flare": function(player, cmd, data) | |||||
{ | |||||
let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | |||||
cmpGuiInterface.PushNotification({ | |||||
"type": "map-flare", | |||||
"players": [player], | |||||
"target": cmd.target | |||||
}); | |||||
Done Inline Actions-1 tab, let or inline elexis: -1 tab, let or inline | |||||
}, | |||||
}; | }; | ||||
/** | /** | ||||
* Sends a GUI notification about unit(s) that failed to ungarrison. | * Sends a GUI notification about unit(s) that failed to ungarrison. | ||||
*/ | */ | ||||
function notifyUnloadFailure(player, garrisonHolder) | function notifyUnloadFailure(player, garrisonHolder) | ||||
{ | { | ||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | for (var i = 0; i < numPoints; ++i) | ||||
if (cmpTerrain.GetGroundLevel(nx, nz) < cmpWaterManager.GetWaterLevel(nx, nz)) | if (cmpTerrain.GetGroundLevel(nx, nz) < cmpWaterManager.GetWaterLevel(nx, nz)) | ||||
waterPoints.push(i); | waterPoints.push(i); | ||||
} | } | ||||
var consec = []; | var consec = []; | ||||
var length = waterPoints.length; | var length = waterPoints.length; | ||||
if (!length) | if (!length) | ||||
continue; | continue; | ||||
for (var i = 0; i < length; ++i) | for (var i = 0; i < length; ++i) | ||||
Lint: JSHintBear 'i' is already defined. Lint: JSHintBear: `'i' is already defined.` | |||||
{ | { | ||||
var count = 0; | var count = 0; | ||||
for (let j = 0; j < length - 1; ++j) | for (let j = 0; j < length - 1; ++j) | ||||
{ | { | ||||
if ((waterPoints[(i + j) % length] + 1) % numPoints == waterPoints[(i + j + 1) % length]) | if ((waterPoints[(i + j) % length] + 1) % numPoints == waterPoints[(i + j + 1) % length]) | ||||
++count; | ++count; | ||||
else | else | ||||
break; | break; | ||||
} | } | ||||
consec[i] = count; | consec[i] = count; | ||||
} | } | ||||
var start = 0; | var start = 0; | ||||
var count = 0; | var count = 0; | ||||
Lint: JSHintBear 'count' is already defined. Lint: JSHintBear: `'count' is already defined.` | |||||
for (var c in consec) | for (var c in consec) | ||||
{ | { | ||||
if (consec[c] > count) | if (consec[c] > count) | ||||
{ | { | ||||
start = c; | start = c; | ||||
count = consec[c]; | count = consec[c]; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | function TryConstructBuilding(player, cmpPlayer, controlAllUnits, cmd) | ||||
} | } | ||||
var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager); | var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager); | ||||
if (cmpTechnologyManager && !cmpTechnologyManager.CanProduce(cmd.template)) | if (cmpTechnologyManager && !cmpTechnologyManager.CanProduce(cmd.template)) | ||||
{ | { | ||||
if (g_DebugCommands) | if (g_DebugCommands) | ||||
warn("Invalid command: required technology check failed for player "+player+": "+uneval(cmd)); | warn("Invalid command: required technology check failed for player "+player+": "+uneval(cmd)); | ||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); | ||||
Lint: JSHintBear 'cmpGuiInterface' is already defined. Lint: JSHintBear: `'cmpGuiInterface' is already defined.` | |||||
cmpGuiInterface.PushNotification({ | cmpGuiInterface.PushNotification({ | ||||
"type": "text", | "type": "text", | ||||
"players": [player], | "players": [player], | ||||
"message": markForTranslation("The building's technology requirements are not met."), | "message": markForTranslation("The building's technology requirements are not met."), | ||||
"translateMessage": true | "translateMessage": true | ||||
}); | }); | ||||
// Remove the foundation because the construction was aborted | // Remove the foundation because the construction was aborted | ||||
Show All 21 Lines | if (cmpVisual && cmd.actorSeed !== undefined) | ||||
cmpVisual.SetActorSeed(cmd.actorSeed); | cmpVisual.SetActorSeed(cmd.actorSeed); | ||||
// Initialise the foundation | // Initialise the foundation | ||||
var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation); | var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation); | ||||
cmpFoundation.InitialiseConstruction(player, cmd.template); | cmpFoundation.InitialiseConstruction(player, cmd.template); | ||||
// send Metadata info if any | // send Metadata info if any | ||||
if (cmd.metadata) | if (cmd.metadata) | ||||
Engine.PostMessage(ent, MT_AIMetadata, { "id": ent, "metadata" : cmd.metadata, "owner" : player } ); | Engine.PostMessage(ent, MT_AIMetadata, { "id": ent, "metadata" : cmd.metadata, "owner" : player } ); | ||||
Lint: ESLintBear (space-in-parens) There should be no spaces inside this paren. Lint: ESLintBear (space-in-parens): `There should be no spaces inside this paren.` | |||||
Lint: ESLintBear (key-spacing) Extra space after key 'metadata'. Lint: ESLintBear (key-spacing): `Extra space after key 'metadata'.` | |||||
Lint: ESLintBear (key-spacing) Extra space after key 'owner'. Lint: ESLintBear (key-spacing): `Extra space after key 'owner'.` | |||||
// Tell the units to start building this new entity | // Tell the units to start building this new entity | ||||
if (cmd.autorepair) | if (cmd.autorepair) | ||||
{ | { | ||||
ProcessCommand(player, { | ProcessCommand(player, { | ||||
"type": "repair", | "type": "repair", | ||||
"entities": entities, | "entities": entities, | ||||
"target": ent, | "target": ent, | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | if (cmd.startSnappedEntity) | ||||
var cmpSnappedStartObstruction = Engine.QueryInterface(cmd.startSnappedEntity, IID_Obstruction); | var cmpSnappedStartObstruction = Engine.QueryInterface(cmd.startSnappedEntity, IID_Obstruction); | ||||
if (!cmpSnappedStartObstruction) | if (!cmpSnappedStartObstruction) | ||||
{ | { | ||||
error("[TryConstructWall] Snapped entity on starting side does not have an obstruction component"); | error("[TryConstructWall] Snapped entity on starting side does not have an obstruction component"); | ||||
return; | return; | ||||
} | } | ||||
lastTowerControlGroup = cmpSnappedStartObstruction.GetControlGroup(); | lastTowerControlGroup = cmpSnappedStartObstruction.GetControlGroup(); | ||||
//warn("setting lastTowerControlGroup to control group of start snapped entity " + cmd.startSnappedEntity + ": " + lastTowerControlGroup); | //warn("setting lastTowerControlGroup to control group of start snapped entity " + cmd.startSnappedEntity + ": " + lastTowerControlGroup); | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
} | } | ||||
var i = 0; | var i = 0; | ||||
var queued = cmd.queued; | var queued = cmd.queued; | ||||
var pieces = clone(cmd.pieces); | var pieces = clone(cmd.pieces); | ||||
for (; i < pieces.length; ++i) | for (; i < pieces.length; ++i) | ||||
{ | { | ||||
var piece = pieces[i]; | var piece = pieces[i]; | ||||
// All wall pieces after the first must be queued. | // All wall pieces after the first must be queued. | ||||
if (i > 0 && !queued) | if (i > 0 && !queued) | ||||
queued = true; | queued = true; | ||||
// 'lastTowerControlGroup' must always be defined and valid here, except if we're at the first piece and we didn't do | // 'lastTowerControlGroup' must always be defined and valid here, except if we're at the first piece and we didn't do | ||||
// start position snapping (implying that the first entity we build must be a tower) | // start position snapping (implying that the first entity we build must be a tower) | ||||
if (lastTowerControlGroup === null || lastTowerControlGroup == INVALID_ENTITY) | if (lastTowerControlGroup === null || lastTowerControlGroup == INVALID_ENTITY) | ||||
{ | { | ||||
if (!(i == 0 && piece.template == cmd.wallSet.templates.tower && !cmd.startSnappedEntity)) | if (!(i == 0 && piece.template == cmd.wallSet.templates.tower && !cmd.startSnappedEntity)) | ||||
{ | { | ||||
error("[TryConstructWall] Expected last tower control group to be available, none found (1st pass, iteration " + i + ")"); | error("[TryConstructWall] Expected last tower control group to be available, none found (1st pass, iteration " + i + ")"); | ||||
Lint: ESLintBear (no-mixed-spaces-and-tabs) Mixed spaces and tabs. Lint: ESLintBear (no-mixed-spaces-and-tabs): `Mixed spaces and tabs.` | |||||
break; | break; | ||||
Lint: ESLintBear (no-mixed-spaces-and-tabs) Mixed spaces and tabs. Lint: ESLintBear (no-mixed-spaces-and-tabs): `Mixed spaces and tabs.` | |||||
} | } | ||||
} | } | ||||
var constructPieceCmd = { | var constructPieceCmd = { | ||||
"type": "construct", | "type": "construct", | ||||
"entities": cmd.entities, | "entities": cmd.entities, | ||||
"template": piece.template, | "template": piece.template, | ||||
"x": piece.x, | "x": piece.x, | ||||
Show All 26 Lines | if (pieceEntityId) | ||||
// if we built a tower, do the control group dance (see outline above) and update lastTowerControlGroup and lastTowerIndex | // if we built a tower, do the control group dance (see outline above) and update lastTowerControlGroup and lastTowerIndex | ||||
if (piece.template == cmd.wallSet.templates.tower) | if (piece.template == cmd.wallSet.templates.tower) | ||||
{ | { | ||||
var cmpTowerObstruction = Engine.QueryInterface(pieceEntityId, IID_Obstruction); | var cmpTowerObstruction = Engine.QueryInterface(pieceEntityId, IID_Obstruction); | ||||
var newTowerControlGroup = pieceEntityId; | var newTowerControlGroup = pieceEntityId; | ||||
if (i > 0) | if (i > 0) | ||||
{ | { | ||||
//warn(" updating previous wall piece's secondary control group to " + newTowerControlGroup); | //warn(" updating previous wall piece's secondary control group to " + newTowerControlGroup); | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
var cmpPreviousObstruction = Engine.QueryInterface(pieces[i-1].ent, IID_Obstruction); | var cmpPreviousObstruction = Engine.QueryInterface(pieces[i-1].ent, IID_Obstruction); | ||||
// TODO: ensure that cmpPreviousObstruction exists | // TODO: ensure that cmpPreviousObstruction exists | ||||
// TODO: ensure that the previous obstruction does not yet have a secondary control group set | // TODO: ensure that the previous obstruction does not yet have a secondary control group set | ||||
cmpPreviousObstruction.SetControlGroup2(newTowerControlGroup); | cmpPreviousObstruction.SetControlGroup2(newTowerControlGroup); | ||||
} | } | ||||
// TODO: ensure that cmpTowerObstruction exists | // TODO: ensure that cmpTowerObstruction exists | ||||
cmpTowerObstruction.SetControlGroup(newTowerControlGroup); // give the tower its own unique control group | cmpTowerObstruction.SetControlGroup(newTowerControlGroup); // give the tower its own unique control group | ||||
Show All 25 Lines | if (!cmpSnappedEndObstruction) | ||||
return; | return; | ||||
} | } | ||||
lastTowerControlGroup = cmpSnappedEndObstruction.GetControlGroup(); | lastTowerControlGroup = cmpSnappedEndObstruction.GetControlGroup(); | ||||
} | } | ||||
for (var j = lastBuiltPieceIndex; j >= 0; --j) | for (var j = lastBuiltPieceIndex; j >= 0; --j) | ||||
{ | { | ||||
var piece = pieces[j]; | var piece = pieces[j]; | ||||
Lint: JSHintBear 'piece' is already defined. Lint: JSHintBear: `'piece' is already defined.` | |||||
if (!piece.ent) | if (!piece.ent) | ||||
{ | { | ||||
error("[TryConstructWall] No entity ID set for constructed entity of template '" + piece.template + "'"); | error("[TryConstructWall] No entity ID set for constructed entity of template '" + piece.template + "'"); | ||||
continue; | continue; | ||||
} | } | ||||
var cmpPieceObstruction = Engine.QueryInterface(piece.ent, IID_Obstruction); | var cmpPieceObstruction = Engine.QueryInterface(piece.ent, IID_Obstruction); | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | function GetFormationUnitAIs(ents, player, formationTemplate) | ||||
} | } | ||||
// Separate out the units that don't support the chosen formation | // Separate out the units that don't support the chosen formation | ||||
var formedEnts = []; | var formedEnts = []; | ||||
var nonformedUnitAIs = []; | var nonformedUnitAIs = []; | ||||
for (let ent of ents) | for (let ent of ents) | ||||
{ | { | ||||
// Skip units with no UnitAI or no position | // Skip units with no UnitAI or no position | ||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | ||||
Lint: JSHintBear 'cmpUnitAI' is already defined. Lint: JSHintBear: `'cmpUnitAI' is already defined.` | |||||
var cmpPosition = Engine.QueryInterface(ent, IID_Position); | var cmpPosition = Engine.QueryInterface(ent, IID_Position); | ||||
if (!cmpUnitAI || !cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpUnitAI || !cmpPosition || !cmpPosition.IsInWorld()) | ||||
continue; | continue; | ||||
var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | ||||
// TODO: We only check if the formation is usable by some units | // TODO: We only check if the formation is usable by some units | ||||
// if we move them to it. We should check if we can use formations | // if we move them to it. We should check if we can use formations | ||||
// for the other cases. | // for the other cases. | ||||
Show All 22 Lines | function GetFormationUnitAIs(ents, player, formationTemplate) | ||||
let formationIds = Object.keys(formation.members); | let formationIds = Object.keys(formation.members); | ||||
if (formationIds.length == 1) | if (formationIds.length == 1) | ||||
{ | { | ||||
// Selected units either belong to this formation or have no formation | // Selected units either belong to this formation or have no formation | ||||
// Check that all its members are selected | // Check that all its members are selected | ||||
var fid = formationIds[0]; | var fid = formationIds[0]; | ||||
var cmpFormation = Engine.QueryInterface(+fid, IID_Formation); | var cmpFormation = Engine.QueryInterface(+fid, IID_Formation); | ||||
if (cmpFormation && cmpFormation.GetMemberCount() == formation.members[fid].length | if (cmpFormation && cmpFormation.GetMemberCount() == formation.members[fid].length | ||||
&& cmpFormation.GetMemberCount() == formation.entities.length) | && cmpFormation.GetMemberCount() == formation.entities.length) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
{ | { | ||||
cmpFormation.DeleteTwinFormations(); | cmpFormation.DeleteTwinFormations(); | ||||
// The whole formation was selected, so reuse its controller for this command | // The whole formation was selected, so reuse its controller for this command | ||||
formationUnitAIs = [Engine.QueryInterface(+fid, IID_UnitAI)]; | formationUnitAIs = [Engine.QueryInterface(+fid, IID_UnitAI)]; | ||||
if (formationTemplate && CanMoveEntsIntoFormation(formation.entities, formationTemplate)) | if (formationTemplate && CanMoveEntsIntoFormation(formation.entities, formationTemplate)) | ||||
cmpFormation.LoadFormation(formationTemplate); | cmpFormation.LoadFormation(formationTemplate); | ||||
} | } | ||||
} | } | ||||
if (!formationUnitAIs.length) | if (!formationUnitAIs.length) | ||||
{ | { | ||||
// We need to give the selected units a new formation controller | // We need to give the selected units a new formation controller | ||||
// TODO replace the fixed 60 with something sensible, based on vision range f.e. | // TODO replace the fixed 60 with something sensible, based on vision range f.e. | ||||
var formationSeparation = 60; | var formationSeparation = 60; | ||||
var clusters = ClusterEntities(formation.entities, formationSeparation); | var clusters = ClusterEntities(formation.entities, formationSeparation); | ||||
var formationEnts = []; | var formationEnts = []; | ||||
for (let cluster of clusters) | for (let cluster of clusters) | ||||
{ | { | ||||
if (!formationTemplate || !CanMoveEntsIntoFormation(cluster, formationTemplate)) | if (!formationTemplate || !CanMoveEntsIntoFormation(cluster, formationTemplate)) | ||||
{ | { | ||||
// Use the last formation template if everyone was using it | // Use the last formation template if everyone was using it | ||||
var lastFormationTemplate = undefined; | var lastFormationTemplate = undefined; | ||||
Lint: ESLintBear (no-undef-init) It's not necessary to initialize 'lastFormationTemplate' to undefined. Lint: ESLintBear (no-undef-init): `It's not necessary to initialize 'lastFormationTemplate' to undefined.` | |||||
for (let ent of cluster) | for (let ent of cluster) | ||||
{ | { | ||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | ||||
Lint: JSHintBear 'cmpUnitAI' is already defined. Lint: JSHintBear: `'cmpUnitAI' is already defined.` | |||||
if (cmpUnitAI) | if (cmpUnitAI) | ||||
{ | { | ||||
var template = cmpUnitAI.GetFormationTemplate(); | var template = cmpUnitAI.GetFormationTemplate(); | ||||
if (lastFormationTemplate === undefined) | if (lastFormationTemplate === undefined) | ||||
{ | { | ||||
lastFormationTemplate = template; | lastFormationTemplate = template; | ||||
} | } | ||||
else if (lastFormationTemplate != template) | else if (lastFormationTemplate != template) | ||||
Show All 16 Lines | for (let cluster of clusters) | ||||
for (let ent of cluster) | for (let ent of cluster) | ||||
nonformedUnitAIs.push(Engine.QueryInterface(ent, IID_UnitAI)); | nonformedUnitAIs.push(Engine.QueryInterface(ent, IID_UnitAI)); | ||||
continue; | continue; | ||||
} | } | ||||
// Create the new controller | // Create the new controller | ||||
var formationEnt = Engine.AddEntity(formationTemplate); | var formationEnt = Engine.AddEntity(formationTemplate); | ||||
var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation); | var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation); | ||||
Lint: JSHintBear 'cmpFormation' is already defined. Lint: JSHintBear: `'cmpFormation' is already defined.` | |||||
formationUnitAIs.push(Engine.QueryInterface(formationEnt, IID_UnitAI)); | formationUnitAIs.push(Engine.QueryInterface(formationEnt, IID_UnitAI)); | ||||
cmpFormation.SetFormationSeparation(formationSeparation); | cmpFormation.SetFormationSeparation(formationSeparation); | ||||
cmpFormation.SetMembers(cluster); | cmpFormation.SetMembers(cluster); | ||||
for (let ent of formationEnts) | for (let ent of formationEnts) | ||||
cmpFormation.RegisterTwinFormation(ent); | cmpFormation.RegisterTwinFormation(ent); | ||||
formationEnts.push(formationEnt); | formationEnts.push(formationEnt); | ||||
Show All 26 Lines | for (let i = 0; i < ents.length; ++i) | ||||
let cmpPosition = Engine.QueryInterface(ents[i], IID_Position); | let cmpPosition = Engine.QueryInterface(ents[i], IID_Position); | ||||
positions.push(cmpPosition.GetPosition2D()); | positions.push(cmpPosition.GetPosition2D()); | ||||
for (let j = 0; j < i; ++j) | for (let j = 0; j < i; ++j) | ||||
matrix[i][j] = positions[i].distanceToSquared(positions[j]); | matrix[i][j] = positions[i].distanceToSquared(positions[j]); | ||||
} | } | ||||
while (clusters.length > 1) | while (clusters.length > 1) | ||||
{ | { | ||||
// search two clusters that are closer than the required distance | // search two clusters that are closer than the required distance | ||||
let closeClusters = undefined; | let closeClusters = undefined; | ||||
Lint: JSHintBear It's not necessary to initialize 'closeClusters' to 'undefined'. Lint: JSHintBear: `It's not necessary to initialize 'closeClusters' to 'undefined'.` | |||||
Lint: ESLintBear (no-undef-init) It's not necessary to initialize 'closeClusters' to undefined. Lint: ESLintBear (no-undef-init): `It's not necessary to initialize 'closeClusters' to undefined.` | |||||
for (let i = matrix.length - 1; i >= 0 && !closeClusters; --i) | for (let i = matrix.length - 1; i >= 0 && !closeClusters; --i) | ||||
for (let j = i - 1; j >= 0 && !closeClusters; --j) | for (let j = i - 1; j >= 0 && !closeClusters; --j) | ||||
if (matrix[i][j] < distSq) | if (matrix[i][j] < distSq) | ||||
closeClusters = [i,j]; | closeClusters = [i,j]; | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
// if no more close clusters found, just return all found clusters so far | // if no more close clusters found, just return all found clusters so far | ||||
if (!closeClusters) | if (!closeClusters) | ||||
return clusters; | return clusters; | ||||
// make a new cluster with the entities from the two found clusters | // make a new cluster with the entities from the two found clusters | ||||
let newCluster = clusters[closeClusters[0]].concat(clusters[closeClusters[1]]); | let newCluster = clusters[closeClusters[0]].concat(clusters[closeClusters[1]]); | ||||
// calculate the minimum distance between the new cluster and all other remaining | // calculate the minimum distance between the new cluster and all other remaining | ||||
// clusters by taking the minimum of the two distances. | // clusters by taking the minimum of the two distances. | ||||
let distances = []; | let distances = []; | ||||
for (let i = 0; i < clusters.length; ++i) | for (let i = 0; i < clusters.length; ++i) | ||||
{ | { | ||||
let a = closeClusters[1]; | let a = closeClusters[1]; | ||||
let b = closeClusters[0]; | let b = closeClusters[0]; | ||||
if (i == a || i == b) | if (i == a || i == b) | ||||
continue; | continue; | ||||
let dist1 = matrix[a][i] !== undefined ? matrix[a][i] : matrix[i][a]; | let dist1 = matrix[a][i] !== undefined ? matrix[a][i] : matrix[i][a]; | ||||
let dist2 = matrix[b][i] !== undefined ? matrix[b][i] : matrix[i][b]; | let dist2 = matrix[b][i] !== undefined ? matrix[b][i] : matrix[i][b]; | ||||
distances.push(Math.min(dist1, dist2)); | distances.push(Math.min(dist1, dist2)); | ||||
} | } | ||||
// remove the rows and columns in the matrix for the merged clusters, | // remove the rows and columns in the matrix for the merged clusters, | ||||
// and the clusters themselves from the cluster list | // and the clusters themselves from the cluster list | ||||
clusters.splice(closeClusters[0],1); | clusters.splice(closeClusters[0],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
clusters.splice(closeClusters[1],1); | clusters.splice(closeClusters[1],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
matrix.splice(closeClusters[0],1); | matrix.splice(closeClusters[0],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
matrix.splice(closeClusters[1],1); | matrix.splice(closeClusters[1],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
for (let i = 0; i < matrix.length; ++i) | for (let i = 0; i < matrix.length; ++i) | ||||
{ | { | ||||
if (matrix[i].length > closeClusters[0]) | if (matrix[i].length > closeClusters[0]) | ||||
matrix[i].splice(closeClusters[0],1); | matrix[i].splice(closeClusters[0],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
if (matrix[i].length > closeClusters[1]) | if (matrix[i].length > closeClusters[1]) | ||||
matrix[i].splice(closeClusters[1],1); | matrix[i].splice(closeClusters[1],1); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
} | } | ||||
// add a new row of distances to the matrix and the new cluster | // add a new row of distances to the matrix and the new cluster | ||||
clusters.push(newCluster); | clusters.push(newCluster); | ||||
matrix.push(distances); | matrix.push(distances); | ||||
} | } | ||||
return clusters; | return clusters; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 126 Lines • Show Last 20 Lines |
'g_Commands' was used before it was defined.