1
0
forked from 0ad/0ad
0ad/binaries/data/mods/public/simulation/helpers/Commands.js
Ykkrosh 2b57f4f998 # Initial support for formation movement.
Support asynchronous path queries.
Allow escaping when stuck in impassable terrain tiles.
Split Update message in multiple phases, to cope with ordering
requirements.
Support automatic walk/run animation switching.

This was SVN commit r8058.
2010-09-03 09:55:14 +00:00

254 lines
6.7 KiB
JavaScript

function ProcessCommand(player, cmd)
{
// print("command: " + player + " " + uneval(cmd) + "\n");
// TODO: all of this stuff needs to do checks for valid arguments
// (e.g. make sure players own the units they're trying to use)
switch (cmd.type)
{
case "debug-print":
print(cmd.message);
break;
case "walk":
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
if (cmpUnitAI)
cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
break;
case "attack":
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
if (cmpUnitAI)
cmpUnitAI.Attack(cmd.target, cmd.queued);
break;
case "repair":
// This covers both repairing damaged buildings, and constructing unfinished foundations
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
if (cmpUnitAI)
cmpUnitAI.Repair(cmd.target, cmd.queued);
break;
case "gather":
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
if (cmpUnitAI)
cmpUnitAI.Gather(cmd.target, cmd.queued);
break;
case "train":
var queue = Engine.QueryInterface(cmd.entity, IID_TrainingQueue);
if (queue)
queue.AddBatch(cmd.template, +cmd.count);
break;
case "stop-train":
var queue = Engine.QueryInterface(cmd.entity, IID_TrainingQueue);
if (queue)
queue.RemoveBatch(cmd.id);
break;
case "construct":
/*
* 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.
*/
// Find the player
var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var playerEnt = cmpPlayerMan.GetPlayerByID(player);
var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
// Tentatively create the foundation (we might find later that it's a invalid build command)
var ent = Engine.AddEntity("foundation|" + cmd.template);
// TODO: report errors (e.g. invalid template names)
// Move the foundation to the right place
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
cmpPosition.JumpTo(cmd.x, cmd.z);
cmpPosition.SetYRotation(cmd.angle);
var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
if (cmpObstruction && cmpObstruction.CheckCollisions())
{
// TODO: report error to player (the building site was obstructed)
// Remove the foundation because the construction was aborted
Engine.DestroyEntity(ent);
break;
}
var cmpCost = Engine.QueryInterface(ent, IID_Cost);
if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
{
// TODO: report error to player (they ran out of resources)
// Remove the foundation because the construction was aborted
Engine.DestroyEntity(ent);
break;
}
// 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
ProcessCommand(player, {
"type": "repair",
"entities": cmd.entities,
"target": ent,
"queued": cmd.queued
});
break;
case "delete-entity":
// Verify the player owns the unit
var cmpOwnership = Engine.QueryInterface(cmd.entity, IID_Ownership);
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
break;
var cmpHealth = Engine.QueryInterface(cmd.entity, IID_Health);
if (cmpHealth)
cmpHealth.Kill();
else
Engine.DestroyEntity(cmd.entity);
break;
case "set-rallypoint":
for each (var ent in cmd.entities)
{
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
if (cmpRallyPoint)
cmpRallyPoint.SetPosition(cmd.x, cmd.z);
}
break;
case "unset-rallypoint":
for each (var ent in cmd.entities)
{
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
if (cmpRallyPoint)
cmpRallyPoint.Unset();
}
break;
default:
error("Ignoring unrecognised command type '" + cmd.type + "'");
}
}
/**
* Get some information about the formations used by entities.
*/
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);
if (cmpUnitAI)
{
var fid = cmpUnitAI.GetFormationController();
if (fid != INVALID_ENTITY)
{
if (!members[fid])
members[fid] = [];
members[fid].push(ent);
}
entities.push(ent);
}
}
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]);
}
}
/**
* Return null or a UnitAI belonging either to the selected unit
* or to a formation entity for the selected group of units.
*/
function GetFormationUnitAI(ents)
{
// If an individual was selected, remove it from any formation
// and command it individually
if (ents.length == 1)
{
RemoveFromFormation(ents);
return Engine.QueryInterface(ents[0], IID_UnitAI);
}
// Find what formations the selected entities are currently in
var formation = ExtractFormations(ents);
if (formation.entities.length == 0)
{
// No units with AI - nothing to do here
return null;
}
var formationEnt = undefined;
if (formation.ids.length == 1)
{
// Selected units all belong to the same formation.
// Check that it doesn't have any other members
var fid = formation.ids[0];
var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
if (cmpFormation && cmpFormation.GetMemberCount() == formation.entities.length)
{
// 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);
}
return Engine.QueryInterface(formationEnt, IID_UnitAI);
}
Engine.RegisterGlobal("ProcessCommand", ProcessCommand);