forked from 0ad/0ad
Ykkrosh
5d764f1435
Make some parts of logger and console thread-safe. Simplify console: remove special commands; remove prefix for script commands. Remove LOG_ONCE, to simplify logger. Change some code to use the new logger interface. Fix OOS when revealing map. Clean up some formatting. This was SVN commit r8511.
308 lines
8.6 KiB
JavaScript
308 lines
8.6 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 "reveal-map":
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
cmpRangeManager.SetLosRevealAll(cmd.enable);
|
|
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);
|
|
|
|
// Check whether it's obstructed by other entities
|
|
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;
|
|
}
|
|
|
|
// Check whether it's in a visible region
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
var visible = (cmpRangeManager.GetLosVisibility(ent, player) == "visible");
|
|
if (!visible)
|
|
{
|
|
// TODO: report error to player (the building site was not visible)
|
|
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)
|
|
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;
|
|
|
|
case "defeat-player":
|
|
// Get player entity by playerId
|
|
var cmpPlayerMananager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
|
var playerEnt = cmpPlayerManager.GetPlayerByID(cmd.playerId);
|
|
// Send "OnPlayerDefeated" message to player
|
|
Engine.PostMessage(playerEnt, MT_PlayerDefeated, null);
|
|
break;
|
|
|
|
case "garrison":
|
|
var targetCmpOwnership = Engine.QueryInterface(cmd.target, IID_Ownership);
|
|
if (!targetCmpOwnership || targetCmpOwnership.GetOwner() != player)
|
|
break;
|
|
for each (var ent in cmd.entities)
|
|
{
|
|
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
|
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
|
|
break;
|
|
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
|
if (cmpUnitAI)
|
|
cmpUnitAI.Garrison(cmd.target);
|
|
}
|
|
break;
|
|
|
|
case "unload":
|
|
var cmpOwnership = Engine.QueryInterface(cmd.garrisonHolder, IID_Ownership);
|
|
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
|
|
break;
|
|
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
|
|
if (cmpGarrisonHolder)
|
|
cmpGarrisonHolder.Unload(cmd.entity);
|
|
break;
|
|
|
|
case "unload-all":
|
|
var cmpOwnership = Engine.QueryInterface(cmd.garrisonHolder, IID_Ownership);
|
|
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
|
|
break;
|
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
|
|
cmpGarrisonHolder.UnloadAll();
|
|
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);
|