2011-11-22 01:16:35 +01:00
|
|
|
// Setting this to true will display some warnings when commands
|
|
|
|
// are likely to fail, which may be useful for debugging AIs
|
|
|
|
var g_DebugCommands = false;
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
function ProcessCommand(player, cmd)
|
|
|
|
{
|
2011-07-02 02:06:39 +02:00
|
|
|
// Do some basic checks here that commanding player is valid
|
|
|
|
var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
|
|
|
if (!cmpPlayerMan || player < 0)
|
|
|
|
return;
|
|
|
|
var playerEnt = cmpPlayerMan.GetPlayerByID(player);
|
|
|
|
if (playerEnt == INVALID_ENTITY)
|
|
|
|
return;
|
|
|
|
var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
|
|
|
|
if (!cmpPlayer)
|
|
|
|
return;
|
|
|
|
var controlAllUnits = cmpPlayer.CanControlAllUnits();
|
|
|
|
|
2011-11-22 01:16:35 +01:00
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// moves the entities closer to the target before giving up.)
|
|
|
|
|
2011-07-02 02:06:39 +02:00
|
|
|
// Now handle various commands
|
2010-01-09 20:20:14 +01:00
|
|
|
switch (cmd.type)
|
|
|
|
{
|
2010-06-30 23:41:04 +02:00
|
|
|
case "debug-print":
|
|
|
|
print(cmd.message);
|
|
|
|
break;
|
|
|
|
|
2011-01-12 13:21:41 +01:00
|
|
|
case "chat":
|
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({"type": "chat", "player": player, "message": cmd.message});
|
|
|
|
break;
|
2012-04-25 22:56:35 +02:00
|
|
|
|
|
|
|
case "quit":
|
|
|
|
// Let the AI exit the game for testing purposes
|
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({"type": "quit"});
|
|
|
|
break;
|
2011-01-12 13:21:41 +01:00
|
|
|
|
2011-07-02 02:06:39 +02:00
|
|
|
case "control-all":
|
|
|
|
cmpPlayer.SetControlAllUnits(cmd.flag);
|
|
|
|
break;
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-10-31 23:00:28 +01:00
|
|
|
case "reveal-map":
|
2011-06-29 01:24:42 +02:00
|
|
|
// Reveal the map for all players, not just the current player,
|
|
|
|
// primarily to make it obvious to everyone that the player is cheating
|
2010-10-31 23:00:28 +01:00
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
2011-06-29 01:24:42 +02:00
|
|
|
cmpRangeManager.SetLosRevealAll(-1, cmd.enable);
|
2010-10-31 23:00:28 +01:00
|
|
|
break;
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
case "walk":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
2011-07-07 19:05:22 +02:00
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
2010-09-03 11:55:14 +02:00
|
|
|
cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
|
2011-07-07 19:05:22 +02:00
|
|
|
});
|
2010-01-09 20:20:14 +01:00
|
|
|
break;
|
2010-01-24 18:24:35 +01:00
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
case "attack":
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands && !IsOwnedByEnemyOfPlayer(player, cmd.target))
|
2011-07-02 02:06:39 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
// This check is for debugging only!
|
|
|
|
warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd));
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
// See UnitAI.CanAttack for target checks
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
|
|
|
cmpUnitAI.Attack(cmd.target, cmd.queued);
|
|
|
|
});
|
2010-01-09 20:20:14 +01:00
|
|
|
break;
|
2010-01-24 18:24:35 +01:00
|
|
|
|
2012-04-17 22:22:13 +02:00
|
|
|
case "heal":
|
|
|
|
if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByAllyOfPlayer(player, cmd.target)))
|
|
|
|
{
|
|
|
|
// This check is for debugging only!
|
|
|
|
warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
// See UnitAI.CanHeal for target checks
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
|
|
|
cmpUnitAI.Heal(cmd.target, cmd.queued);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
case "repair":
|
|
|
|
// This covers both repairing damaged buildings, and constructing unfinished foundations
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands && !IsOwnedByAllyOfPlayer(player, cmd.target))
|
2011-07-02 02:06:39 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
// This check is for debugging only!
|
|
|
|
warn("Invalid command: repair target is not owned by ally of player "+player+": "+uneval(cmd));
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
// See UnitAI.CanRepair for target checks
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
|
|
|
cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued);
|
|
|
|
});
|
2010-03-12 22:41:40 +01:00
|
|
|
break;
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
case "gather":
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByGaia(cmd.target)))
|
2011-09-30 21:54:04 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
// This check is for debugging only!
|
|
|
|
warn("Invalid command: resource is not owned by gaia or player "+player+": "+uneval(cmd));
|
2011-09-30 21:54:04 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
// See UnitAI.CanGather for target checks
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
|
|
|
cmpUnitAI.Gather(cmd.target, cmd.queued);
|
|
|
|
});
|
2010-02-12 23:46:53 +01:00
|
|
|
break;
|
2011-12-16 17:08:26 +01:00
|
|
|
|
2012-03-02 00:16:01 +01:00
|
|
|
case "gather-near-position":
|
2011-12-16 17:08:26 +01:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
2012-03-02 00:16:01 +01:00
|
|
|
cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.queued);
|
2011-12-16 17:08:26 +01:00
|
|
|
});
|
|
|
|
break;
|
2010-02-12 23:46:53 +01:00
|
|
|
|
2010-11-13 20:15:29 +01:00
|
|
|
case "returnresource":
|
2011-07-02 02:06:39 +02:00
|
|
|
// Check dropsite is owned by player
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands && IsOwnedByPlayer(player, cmd.target))
|
2011-07-02 02:06:39 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
// This check is for debugging only!
|
|
|
|
warn("Invalid command: dropsite is not owned by player "+player+": "+uneval(cmd));
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
// See UnitAI.CanReturnResource for target checks
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
|
|
|
cmpUnitAI.ReturnResource(cmd.target, cmd.queued);
|
|
|
|
});
|
2010-11-13 20:15:29 +01:00
|
|
|
break;
|
|
|
|
|
2010-04-19 21:47:23 +02:00
|
|
|
case "train":
|
2012-04-21 02:21:01 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
// Verify that the building(s) can be controlled by the player
|
|
|
|
if (entities.length > 0)
|
2011-07-02 02:06:39 +02:00
|
|
|
{
|
2012-04-21 02:21:01 +02:00
|
|
|
for each (var ent in entities)
|
2012-04-20 19:21:04 +02:00
|
|
|
{
|
2012-04-21 02:21:01 +02:00
|
|
|
var cmpTechMan = QueryOwnerInterface(ent, IID_TechnologyManager);
|
|
|
|
// TODO: Enable this check once the AI gets technology support
|
|
|
|
if (cmpTechMan.CanProduce(cmd.template) || true)
|
|
|
|
{
|
|
|
|
var queue = Engine.QueryInterface(ent, IID_ProductionQueue);
|
|
|
|
// Check if the building can train the unit
|
|
|
|
if (queue && queue.GetEntitiesList().indexOf(cmd.template) != -1)
|
|
|
|
queue.AddBatch(cmd.template, "unit", +cmd.count, cmd.metadata);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
warn("Invalid command: training requires unresearched technology: " + uneval(cmd));
|
|
|
|
}
|
2012-04-20 19:21:04 +02:00
|
|
|
}
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
2012-04-21 02:21:01 +02:00
|
|
|
warn("Invalid command: training building(s) cannot be controlled by player "+player+": "+uneval(cmd));
|
2011-11-22 01:16:35 +01:00
|
|
|
}
|
2010-04-19 21:47:23 +02:00
|
|
|
break;
|
|
|
|
|
2012-04-20 19:21:04 +02:00
|
|
|
case "research":
|
|
|
|
// Verify that the building can be controlled by the player
|
|
|
|
if (CanControlUnit(cmd.entity, player, controlAllUnits))
|
|
|
|
{
|
|
|
|
var cmpTechMan = QueryOwnerInterface(cmd.entity, IID_TechnologyManager);
|
|
|
|
// TODO: Enable this check once the AI gets technology support
|
|
|
|
if (cmpTechMan.CanResearch(cmd.template) || true)
|
|
|
|
{
|
|
|
|
var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue);
|
|
|
|
if (queue)
|
|
|
|
queue.AddBatch(cmd.template, "technology");
|
|
|
|
}
|
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: Requirements to research technology are not met: " + uneval(cmd));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: research building cannot be controlled by player "+player+": "+uneval(cmd));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "stop-production":
|
2011-11-22 01:16:35 +01:00
|
|
|
// Verify that the building can be controlled by the player
|
2011-07-02 02:06:39 +02:00
|
|
|
if (CanControlUnit(cmd.entity, player, controlAllUnits))
|
|
|
|
{
|
2012-04-20 19:21:04 +02:00
|
|
|
var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue);
|
2011-07-02 02:06:39 +02:00
|
|
|
if (queue)
|
|
|
|
queue.RemoveBatch(cmd.id);
|
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
2012-04-20 19:21:04 +02:00
|
|
|
warn("Invalid command: production building cannot be controlled by player "+player+": "+uneval(cmd));
|
2011-11-22 01:16:35 +01:00
|
|
|
}
|
2010-04-19 21:47:23 +02:00
|
|
|
break;
|
|
|
|
|
2010-01-24 18:24:35 +01:00
|
|
|
case "construct":
|
2011-02-10 17:06:28 +01:00
|
|
|
// Message structure:
|
|
|
|
// {
|
|
|
|
// "type": "construct",
|
|
|
|
// "entities": [...],
|
|
|
|
// "template": "...",
|
|
|
|
// "x": ...,
|
|
|
|
// "z": ...,
|
|
|
|
// "angle": ...,
|
|
|
|
// "autorepair": true, // whether to automatically start constructing/repairing the new foundation
|
|
|
|
// "autocontinue": true, // whether to automatically gather/build/etc after finishing this
|
|
|
|
// "queued": true,
|
|
|
|
// }
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
/*
|
|
|
|
* Construction process:
|
|
|
|
* . Take resources away immediately.
|
|
|
|
* . Create a foundation entity with 1hp, 0% build progress.
|
|
|
|
* . Increase hp and build progress up to 100% when people work on it.
|
|
|
|
* . If it's destroyed, an appropriate fraction of the resource cost is refunded.
|
|
|
|
* . If it's completed, it gets replaced with the real building.
|
|
|
|
*/
|
2011-07-02 02:06:39 +02:00
|
|
|
|
|
|
|
// Check that we can control these units
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
if (!entities.length)
|
|
|
|
break;
|
2010-03-12 22:41:40 +01:00
|
|
|
|
2010-03-18 00:16:04 +01:00
|
|
|
// Tentatively create the foundation (we might find later that it's a invalid build command)
|
2010-03-12 22:41:40 +01:00
|
|
|
var ent = Engine.AddEntity("foundation|" + cmd.template);
|
2011-08-06 10:11:05 +02:00
|
|
|
if (ent == INVALID_ENTITY)
|
|
|
|
{
|
|
|
|
// Error (e.g. invalid template names)
|
2011-11-22 01:16:35 +01:00
|
|
|
error("Error creating foundation entity for '" + cmd.template + "'");
|
2011-08-06 10:11:05 +02:00
|
|
|
break;
|
|
|
|
}
|
2010-03-12 22:41:40 +01:00
|
|
|
|
2010-03-18 00:16:04 +01:00
|
|
|
// Move the foundation to the right place
|
|
|
|
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
|
|
|
|
cmpPosition.JumpTo(cmd.x, cmd.z);
|
|
|
|
cmpPosition.SetYRotation(cmd.angle);
|
|
|
|
|
2011-11-22 01:16:35 +01:00
|
|
|
// Check whether it's obstructed by other entities or invalid terrain
|
|
|
|
var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions);
|
|
|
|
if (!cmpBuildRestrictions || !cmpBuildRestrictions.CheckPlacement(player))
|
2010-03-18 00:16:04 +01:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands)
|
2011-08-15 02:25:22 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
warn("Invalid command: build restrictions check failed for player "+player+": "+uneval(cmd));
|
2011-08-15 02:25:22 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({ "player": player, "message": "Building site was obstructed" });
|
|
|
|
|
|
|
|
// Remove the foundation because the construction was aborted
|
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check build limits
|
|
|
|
var cmpBuildLimits = QueryPlayerIDInterface(player, IID_BuildLimits);
|
|
|
|
if (!cmpBuildLimits || !cmpBuildLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
|
|
|
|
{
|
|
|
|
if (g_DebugCommands)
|
2011-08-15 02:25:22 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
warn("Invalid command: build limits check failed for player "+player+": "+uneval(cmd));
|
2011-08-15 02:25:22 +02:00
|
|
|
}
|
|
|
|
|
2011-11-22 01:16:35 +01:00
|
|
|
// TODO: The UI should tell the user they can't build this (but we still need this check)
|
|
|
|
|
|
|
|
// Remove the foundation because the construction was aborted
|
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
break;
|
|
|
|
}
|
2012-04-20 19:21:04 +02:00
|
|
|
|
|
|
|
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
|
|
|
// TODO: Enable this check once the AI gets technology support
|
|
|
|
if (!cmpTechMan.CanProduce(cmd.template) && false)
|
|
|
|
{
|
|
|
|
if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: required technology check failed for player "+player+": "+uneval(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({ "player": player, "message": "Building's technology requirements are not met." });
|
|
|
|
|
|
|
|
// Remove the foundation because the construction was aborted
|
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
|
|
|
// TODO: AI has no visibility info
|
|
|
|
if (!cmpPlayer.IsAI())
|
|
|
|
{
|
2011-11-24 01:58:34 +01:00
|
|
|
// Check whether it's in a visible or fogged region
|
|
|
|
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
|
|
|
|
// which would show them as hidden instead of fogged
|
2011-08-15 02:25:22 +02:00
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
2011-11-24 02:16:30 +01:00
|
|
|
var visible = (cmpRangeManager && cmpRangeManager.GetLosVisibility(ent, player, true) != "hidden");
|
|
|
|
if (!visible)
|
2011-08-15 02:25:22 +02:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: foundation visibility check failed for player "+player+": "+uneval(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({ "player": player, "message": "Building site was not visible" });
|
2011-08-15 02:25:22 +02:00
|
|
|
|
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
break;
|
|
|
|
}
|
2010-10-04 19:34:33 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
var cmpCost = Engine.QueryInterface(ent, IID_Cost);
|
|
|
|
if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
|
2010-01-24 18:24:35 +01:00
|
|
|
{
|
2011-11-22 01:16:35 +01:00
|
|
|
if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: building cost check failed for player "+player+": "+uneval(cmd));
|
|
|
|
}
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
break;
|
2010-01-24 18:24:35 +01:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
// Make it owned by the current player
|
|
|
|
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
|
|
|
cmpOwnership.SetOwner(player);
|
|
|
|
|
|
|
|
// Initialise the foundation
|
|
|
|
var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
|
|
|
|
cmpFoundation.InitialiseConstruction(player, cmd.template);
|
|
|
|
|
|
|
|
// Tell the units to start building this new entity
|
2011-02-10 17:06:28 +01:00
|
|
|
if (cmd.autorepair)
|
|
|
|
{
|
|
|
|
ProcessCommand(player, {
|
|
|
|
"type": "repair",
|
2011-07-02 02:06:39 +02:00
|
|
|
"entities": entities,
|
2011-02-10 17:06:28 +01:00
|
|
|
"target": ent,
|
2011-02-10 20:50:08 +01:00
|
|
|
"autocontinue": cmd.autocontinue,
|
2011-02-10 17:06:28 +01:00
|
|
|
"queued": cmd.queued
|
|
|
|
});
|
|
|
|
}
|
2010-03-12 22:41:40 +01:00
|
|
|
|
2010-01-24 18:24:35 +01:00
|
|
|
break;
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2011-01-12 13:21:41 +01:00
|
|
|
case "delete-entities":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
for each (var ent in entities)
|
2011-01-12 13:21:41 +01:00
|
|
|
{
|
|
|
|
var cmpHealth = Engine.QueryInterface(ent, IID_Health);
|
|
|
|
if (cmpHealth)
|
|
|
|
cmpHealth.Kill();
|
|
|
|
else
|
|
|
|
Engine.DestroyEntity(ent);
|
|
|
|
}
|
2010-08-01 07:09:30 +02:00
|
|
|
break;
|
2010-01-24 18:24:35 +01:00
|
|
|
|
2010-08-05 12:20:47 +02:00
|
|
|
case "set-rallypoint":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
for each (var ent in entities)
|
2010-08-05 12:20:47 +02:00
|
|
|
{
|
|
|
|
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
|
|
|
|
if (cmpRallyPoint)
|
2011-12-16 17:08:26 +01:00
|
|
|
{
|
2010-08-05 12:20:47 +02:00
|
|
|
cmpRallyPoint.SetPosition(cmd.x, cmd.z);
|
2011-12-16 17:08:26 +01:00
|
|
|
cmpRallyPoint.SetData(cmd.data);
|
|
|
|
}
|
2010-08-05 12:20:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "unset-rallypoint":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
for each (var ent in entities)
|
2010-08-05 12:20:47 +02:00
|
|
|
{
|
|
|
|
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
|
|
|
|
if (cmpRallyPoint)
|
|
|
|
cmpRallyPoint.Unset();
|
|
|
|
}
|
|
|
|
break;
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-10-01 22:51:21 +02:00
|
|
|
case "defeat-player":
|
|
|
|
// Send "OnPlayerDefeated" message to player
|
2011-11-22 01:16:35 +01:00
|
|
|
Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": player } );
|
2010-10-01 22:51:21 +02:00
|
|
|
break;
|
2010-08-05 12:20:47 +02:00
|
|
|
|
2010-10-24 00:43:15 +02:00
|
|
|
case "garrison":
|
2011-11-22 01:16:35 +01:00
|
|
|
// Verify that the building can be controlled by the player
|
2011-07-02 02:06:39 +02:00
|
|
|
if (CanControlUnit(cmd.target, player, controlAllUnits))
|
|
|
|
{
|
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
2011-07-07 19:05:22 +02:00
|
|
|
GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
|
2011-07-02 02:06:39 +02:00
|
|
|
cmpUnitAI.Garrison(cmd.target);
|
2011-07-07 19:05:22 +02:00
|
|
|
});
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: garrison target cannot be controlled by player "+player+": "+uneval(cmd));
|
|
|
|
}
|
2010-10-24 00:43:15 +02:00
|
|
|
break;
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-10-24 00:43:15 +02:00
|
|
|
case "unload":
|
2011-11-22 01:16:35 +01:00
|
|
|
// Verify that the building can be controlled by the player
|
2011-07-02 02:06:39 +02:00
|
|
|
if (CanControlUnit(cmd.garrisonHolder, player, controlAllUnits))
|
|
|
|
{
|
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
|
2012-03-21 16:18:31 +01:00
|
|
|
var notUngarrisoned = 0;
|
|
|
|
for each (ent in cmd.entities)
|
|
|
|
{
|
|
|
|
if (!cmpGarrisonHolder || !cmpGarrisonHolder.Unload(ent))
|
|
|
|
{
|
|
|
|
notUngarrisoned++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (notUngarrisoned != 0)
|
2011-08-17 03:43:23 +02:00
|
|
|
{
|
|
|
|
var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
|
2012-03-21 16:18:31 +01:00
|
|
|
var notification = {"player": cmpPlayer.GetPlayerID(), "message": (notUngarrisoned == 1 ? "Unable to ungarrison unit" : "Unable to ungarrison units")};
|
2011-08-17 03:43:23 +02:00
|
|
|
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGUIInterface.PushNotification(notification);
|
2012-03-21 16:18:31 +01:00
|
|
|
}
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: unload target cannot be controlled by player "+player+": "+uneval(cmd));
|
|
|
|
}
|
2010-10-24 00:43:15 +02:00
|
|
|
break;
|
2011-11-22 01:16:35 +01:00
|
|
|
|
2010-10-24 00:43:15 +02:00
|
|
|
case "unload-all":
|
2011-11-22 01:16:35 +01:00
|
|
|
// Verify that the building can be controlled by the player
|
2011-07-02 02:06:39 +02:00
|
|
|
if (CanControlUnit(cmd.garrisonHolder, player, controlAllUnits))
|
|
|
|
{
|
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
|
2011-08-17 03:43:23 +02:00
|
|
|
if (!cmpGarrisonHolder || !cmpGarrisonHolder.UnloadAll())
|
|
|
|
{
|
|
|
|
var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
|
|
|
|
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Unable to ungarrison all units"};
|
|
|
|
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGUIInterface.PushNotification(notification);
|
|
|
|
}
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
2011-11-22 01:16:35 +01:00
|
|
|
else if (g_DebugCommands)
|
|
|
|
{
|
|
|
|
warn("Invalid command: unload-all target cannot be controlled by player "+player+": "+uneval(cmd));
|
|
|
|
}
|
2010-10-24 00:43:15 +02:00
|
|
|
break;
|
2011-05-01 22:40:53 +02:00
|
|
|
|
|
|
|
case "formation":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
2012-05-04 01:32:10 +02:00
|
|
|
GetFormationUnitAIs(entities, cmd.name).forEach(function(cmpUnitAI) {
|
2011-07-07 19:05:22 +02:00
|
|
|
var cmpFormation = Engine.QueryInterface(cmpUnitAI.entity, IID_Formation);
|
|
|
|
if (!cmpFormation)
|
|
|
|
return;
|
|
|
|
cmpFormation.LoadFormation(cmd.name);
|
|
|
|
cmpFormation.MoveMembersIntoFormation(true);
|
|
|
|
});
|
2011-05-01 22:40:53 +02:00
|
|
|
break;
|
2011-05-15 23:53:24 +02:00
|
|
|
|
|
|
|
case "promote":
|
2011-11-22 01:16:35 +01:00
|
|
|
// No need to do checks here since this is a cheat anyway
|
2011-05-15 23:53:24 +02:00
|
|
|
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
|
|
|
cmpGuiInterface.PushNotification({"type": "chat", "player": player, "message": "(Cheat - promoted units)"});
|
|
|
|
|
|
|
|
for each (var ent in cmd.entities)
|
|
|
|
{
|
|
|
|
var cmpPromotion = Engine.QueryInterface(ent, IID_Promotion);
|
|
|
|
if (cmpPromotion)
|
|
|
|
cmpPromotion.IncreaseXp(cmpPromotion.GetRequiredXp() - cmpPromotion.GetCurrentXp());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-06-18 00:13:39 +02:00
|
|
|
case "stance":
|
2011-07-02 02:06:39 +02:00
|
|
|
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
|
|
|
for each (var ent in entities)
|
2011-06-18 00:13:39 +02:00
|
|
|
{
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
|
|
|
if (cmpUnitAI)
|
|
|
|
cmpUnitAI.SwitchToStance(cmd.name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-03-08 21:42:28 +01:00
|
|
|
case "setup-trade-route":
|
|
|
|
for each (var ent in cmd.entities)
|
|
|
|
{
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
|
|
|
if (cmpUnitAI)
|
|
|
|
cmpUnitAI.SetupTradeRoute(cmd.target);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "select-trading-goods":
|
2012-04-06 17:07:04 +02:00
|
|
|
for each (var ent in cmd.entities)
|
|
|
|
{
|
|
|
|
var cmpTrader = Engine.QueryInterface(ent, IID_Trader);
|
|
|
|
if (cmpTrader)
|
|
|
|
cmpTrader.SetPreferredGoods(cmd.preferredGoods);
|
|
|
|
}
|
2012-03-08 21:42:28 +01:00
|
|
|
break;
|
|
|
|
|
2011-11-24 16:43:32 +01:00
|
|
|
case "barter":
|
|
|
|
var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
|
|
|
|
cmpBarter.ExchangeResources(playerEnt, cmd.sell, cmd.buy, cmd.amount);
|
|
|
|
break;
|
2012-04-25 22:56:35 +02:00
|
|
|
|
|
|
|
case "set-shading-color":
|
|
|
|
// Debug command to make an entity brightly colored
|
|
|
|
for each (var ent in cmd.entities)
|
|
|
|
{
|
|
|
|
var cmpVisual = Engine.QueryInterface(ent, IID_Visual)
|
|
|
|
if (cmpVisual)
|
|
|
|
cmpVisual.SetShadingColour(cmd.rgb[0], cmd.rgb[1], cmd.rgb[2], 0) // alpha isn't used so just send 0
|
|
|
|
}
|
|
|
|
break;
|
2011-11-24 16:43:32 +01:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
default:
|
2011-11-22 01:16:35 +01:00
|
|
|
error("Invalid command: unknown command type: "+uneval(cmd));
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
/**
|
|
|
|
* Get some information about the formations used by entities.
|
2011-07-07 19:05:22 +02:00
|
|
|
* The entities must have a UnitAI component.
|
2010-09-03 11:55:14 +02:00
|
|
|
*/
|
|
|
|
function ExtractFormations(ents)
|
|
|
|
{
|
|
|
|
var entities = []; // subset of ents that have UnitAI
|
|
|
|
var members = {}; // { formationentity: [ent, ent, ...], ... }
|
|
|
|
for each (var ent in ents)
|
|
|
|
{
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
2011-07-07 19:05:22 +02:00
|
|
|
var fid = cmpUnitAI.GetFormationController();
|
|
|
|
if (fid != INVALID_ENTITY)
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
2011-07-07 19:05:22 +02:00
|
|
|
if (!members[fid])
|
|
|
|
members[fid] = [];
|
|
|
|
members[fid].push(ent);
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
2011-07-07 19:05:22 +02:00
|
|
|
entities.push(ent);
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var ids = [ id for (id in members) ];
|
|
|
|
|
|
|
|
return { "entities": entities, "members": members, "ids": ids };
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove the given list of entities from their current formations.
|
|
|
|
*/
|
|
|
|
function RemoveFromFormation(ents)
|
|
|
|
{
|
|
|
|
var formation = ExtractFormations(ents);
|
|
|
|
for (var fid in formation.members)
|
|
|
|
{
|
|
|
|
var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
|
|
|
|
if (cmpFormation)
|
|
|
|
cmpFormation.RemoveMembers(formation.members[fid]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-07 19:05:22 +02:00
|
|
|
* Returns a list of UnitAI components, each belonging either to a
|
|
|
|
* selected unit or to a formation entity for groups of the selected units.
|
2010-09-03 11:55:14 +02:00
|
|
|
*/
|
2012-05-04 01:32:10 +02:00
|
|
|
function GetFormationUnitAIs(ents, formName)
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
|
|
|
// If an individual was selected, remove it from any formation
|
|
|
|
// and command it individually
|
|
|
|
if (ents.length == 1)
|
|
|
|
{
|
2011-07-07 19:05:22 +02:00
|
|
|
// Skip unit if it has no UnitAI
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ents[0], IID_UnitAI);
|
|
|
|
if (!cmpUnitAI)
|
|
|
|
return [];
|
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
RemoveFromFormation(ents);
|
|
|
|
|
2011-07-07 19:05:22 +02:00
|
|
|
return [ cmpUnitAI ];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Separate out the units that don't support the chosen formation
|
|
|
|
var formedEnts = [];
|
|
|
|
var nonformedUnitAIs = [];
|
|
|
|
for each (var ent in ents)
|
|
|
|
{
|
|
|
|
// Skip units with no UnitAI
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
|
|
|
if (!cmpUnitAI)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
2012-05-04 01:32:10 +02:00
|
|
|
// 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
|
|
|
|
// for the other cases.
|
|
|
|
// We only use "LineClosed" instead of "Line Closed" to access the templates.
|
|
|
|
if (cmpIdentity && cmpIdentity.CanUseFormation(formName === undefined ? "LineClosed" : formName.replace(/\s+/,'')))
|
2011-07-07 19:05:22 +02:00
|
|
|
formedEnts.push(ent);
|
|
|
|
else
|
|
|
|
nonformedUnitAIs.push(cmpUnitAI);
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
2011-07-07 19:05:22 +02:00
|
|
|
if (formedEnts.length == 0)
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
2011-07-07 19:05:22 +02:00
|
|
|
// No units support the foundation - return all the others
|
|
|
|
return nonformedUnitAIs;
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
2011-07-07 19:05:22 +02:00
|
|
|
// Find what formations the formationable selected entities are currently in
|
|
|
|
var formation = ExtractFormations(formedEnts);
|
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
var formationEnt = undefined;
|
|
|
|
if (formation.ids.length == 1)
|
|
|
|
{
|
2012-03-30 22:09:50 +02:00
|
|
|
// Selected units either belong to this formation or have no formation
|
|
|
|
// Check that all its members are selected
|
2010-09-03 11:55:14 +02:00
|
|
|
var fid = formation.ids[0];
|
|
|
|
var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
|
2012-03-30 22:09:50 +02:00
|
|
|
if (cmpFormation && cmpFormation.GetMemberCount() == formation.members[fid].length
|
|
|
|
&& cmpFormation.GetMemberCount() == formation.entities.length)
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
|
|
|
// The whole formation was selected, so reuse its controller for this command
|
|
|
|
formationEnt = +fid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!formationEnt)
|
|
|
|
{
|
|
|
|
// We need to give the selected units a new formation controller
|
|
|
|
|
|
|
|
// Remove selected units from their current formation
|
|
|
|
for (var fid in formation.members)
|
|
|
|
{
|
|
|
|
var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
|
|
|
|
if (cmpFormation)
|
|
|
|
cmpFormation.RemoveMembers(formation.members[fid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the new controller
|
|
|
|
formationEnt = Engine.AddEntity("special/formation");
|
|
|
|
var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation);
|
|
|
|
cmpFormation.SetMembers(formation.entities);
|
2011-05-01 22:40:53 +02:00
|
|
|
|
|
|
|
// If all the selected units were previously in formations of the same shape,
|
|
|
|
// then set this new formation to that shape too; otherwise use the default shape
|
|
|
|
var lastFormationName = undefined;
|
|
|
|
for each (var ent in formation.entities)
|
|
|
|
{
|
|
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
|
|
|
if (cmpUnitAI)
|
|
|
|
{
|
|
|
|
var name = cmpUnitAI.GetLastFormationName();
|
|
|
|
if (lastFormationName === undefined)
|
|
|
|
{
|
|
|
|
lastFormationName = name;
|
|
|
|
}
|
|
|
|
else if (lastFormationName != name)
|
|
|
|
{
|
|
|
|
lastFormationName = undefined;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var formationName;
|
|
|
|
if (lastFormationName)
|
|
|
|
formationName = lastFormationName;
|
|
|
|
else
|
|
|
|
formationName = "Line Closed";
|
|
|
|
|
|
|
|
if (CanMoveEntsIntoFormation(formation.entities, formationName))
|
|
|
|
{
|
|
|
|
cmpFormation.LoadFormation(formationName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-21 17:45:02 +01:00
|
|
|
cmpFormation.LoadFormation("Scatter");
|
2011-05-01 22:40:53 +02:00
|
|
|
}
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
2011-07-07 19:05:22 +02:00
|
|
|
return nonformedUnitAIs.concat(Engine.QueryInterface(formationEnt, IID_UnitAI));
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
2012-03-21 17:45:02 +01:00
|
|
|
function GetFormationRequirements(formationName)
|
|
|
|
{
|
|
|
|
var countRequired = 1;
|
|
|
|
var classesRequired;
|
|
|
|
switch(formationName)
|
|
|
|
{
|
|
|
|
case "Scatter":
|
|
|
|
case "Column Closed":
|
|
|
|
case "Line Closed":
|
|
|
|
case "Column Open":
|
|
|
|
case "Line Open":
|
|
|
|
case "Battle Line":
|
|
|
|
break;
|
|
|
|
case "Box":
|
|
|
|
countRequired = 4;
|
|
|
|
break;
|
|
|
|
case "Flank":
|
|
|
|
countRequired = 8;
|
|
|
|
break;
|
|
|
|
case "Skirmish":
|
|
|
|
classesRequired = ["Ranged"];
|
|
|
|
break;
|
|
|
|
case "Wedge":
|
|
|
|
countRequired = 3;
|
|
|
|
classesRequired = ["Cavalry"];
|
|
|
|
break;
|
|
|
|
case "Phalanx":
|
|
|
|
countRequired = 10;
|
|
|
|
classesRequired = ["Melee", "Infantry"];
|
|
|
|
break;
|
|
|
|
case "Syntagma":
|
|
|
|
countRequired = 9;
|
|
|
|
classesRequired = ["Melee", "Infantry"]; // TODO: pike only
|
|
|
|
break;
|
|
|
|
case "Testudo":
|
|
|
|
countRequired = 9;
|
|
|
|
classesRequired = ["Melee", "Infantry"];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// We encountered a unknown formation -> warn the user
|
|
|
|
warn("Commands.js: GetFormationRequirements: unknown formation: " + formationName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return { "count": countRequired, "classesRequired": classesRequired };
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-01 22:40:53 +02:00
|
|
|
function CanMoveEntsIntoFormation(ents, formationName)
|
|
|
|
{
|
|
|
|
var count = ents.length;
|
|
|
|
|
|
|
|
// TODO: should check the player's civ is allowed to use this formation
|
2012-05-04 01:32:10 +02:00
|
|
|
// See simulation/components/Player.js GetFormations() for a list of all allowed formations
|
2011-05-01 22:40:53 +02:00
|
|
|
|
2012-03-21 17:45:02 +01:00
|
|
|
var requirements = GetFormationRequirements(formationName);
|
|
|
|
if (!requirements)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (count < requirements.count)
|
2011-05-01 22:40:53 +02:00
|
|
|
return false;
|
|
|
|
|
2012-03-21 17:45:02 +01:00
|
|
|
var scatterOnlyUnits = true;
|
2011-05-01 22:40:53 +02:00
|
|
|
for each (var ent in ents)
|
|
|
|
{
|
|
|
|
var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
|
|
|
if (cmpIdentity)
|
|
|
|
{
|
|
|
|
var classes = cmpIdentity.GetClassesList();
|
2012-03-21 17:45:02 +01:00
|
|
|
if (scatterOnlyUnits && (classes.indexOf("Worker") == -1 || classes.indexOf("Support") == -1))
|
|
|
|
scatterOnlyUnits = false;
|
|
|
|
for each (var classRequired in requirements.classesRequired)
|
2011-05-01 22:40:53 +02:00
|
|
|
{
|
|
|
|
if (classes.indexOf(classRequired) == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-21 17:45:02 +01:00
|
|
|
if (scatterOnlyUnits)
|
2011-05-01 22:40:53 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-07-02 02:06:39 +02:00
|
|
|
/**
|
|
|
|
* Check if player can control this entity
|
|
|
|
* returns: true if the entity is valid and owned by the player if
|
|
|
|
* or control all units is activated for the player, else false
|
|
|
|
*/
|
|
|
|
function CanControlUnit(entity, player, controlAll)
|
|
|
|
{
|
2011-08-06 10:11:05 +02:00
|
|
|
return (IsOwnedByPlayer(player, entity) || controlAll);
|
2011-07-02 02:06:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter entities which the player can control
|
|
|
|
*/
|
|
|
|
function FilterEntityList(entities, player, controlAll)
|
|
|
|
{
|
|
|
|
return entities.filter(function(ent) { return CanControlUnit(ent, player, controlAll);} );
|
|
|
|
}
|
|
|
|
|
2012-03-21 17:45:02 +01:00
|
|
|
Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements);
|
2011-05-01 22:40:53 +02:00
|
|
|
Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
|
2010-01-09 20:20:14 +01:00
|
|
|
Engine.RegisterGlobal("ProcessCommand", ProcessCommand);
|