2011-01-12 13:29:00 +01:00
|
|
|
function AIProxy() {}
|
|
|
|
|
|
|
|
AIProxy.prototype.Schema =
|
|
|
|
"<empty/>";
|
|
|
|
|
2011-01-16 00:35:20 +01:00
|
|
|
/**
|
|
|
|
* AIProxy passes its entity's state data to AI scripts.
|
|
|
|
*
|
|
|
|
* Efficiency is critical: there can be many thousands of entities,
|
|
|
|
* and the data returned by this component is serialized and copied to
|
|
|
|
* the AI thread every turn, so it can be quite expensive.
|
|
|
|
*
|
|
|
|
* We omit all data that can be derived statically from the template XML
|
|
|
|
* files - the AI scripts can parse the templates themselves.
|
|
|
|
* This violates the component interface abstraction and is potentially
|
|
|
|
* fragile if the template formats change (since both the component code
|
|
|
|
* and the AI will have to be updated in sync), but it's not *that* bad
|
|
|
|
* really and it helps performance significantly.
|
|
|
|
*
|
|
|
|
* We also add an optimisation to avoid copying non-changing values.
|
|
|
|
* The first call to GetRepresentation calls GetFullRepresentation,
|
|
|
|
* which constructs the complete entity state representation.
|
|
|
|
* After that, we simply listen to events from the rest of the gameplay code,
|
|
|
|
* and store the changed data in this.changes.
|
|
|
|
* Properties in this.changes will override those previously returned
|
|
|
|
* from GetRepresentation; if a property isn't overridden then the AI scripts
|
|
|
|
* will keep its old value.
|
|
|
|
*
|
|
|
|
* The event handlers should set this.changes.whatever to exactly the
|
|
|
|
* same as GetFullRepresentation would set.
|
|
|
|
*/
|
|
|
|
|
2011-01-12 13:29:00 +01:00
|
|
|
AIProxy.prototype.Init = function()
|
|
|
|
{
|
2011-01-16 00:35:20 +01:00
|
|
|
this.changes = null;
|
|
|
|
this.needsFullGet = true;
|
2011-03-03 01:16:14 +01:00
|
|
|
|
|
|
|
// Let the AIInterface know that we exist and that it should query us
|
|
|
|
this.NotifyChange();
|
2011-01-12 13:29:00 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.GetRepresentation = function()
|
|
|
|
{
|
2011-01-16 00:35:20 +01:00
|
|
|
// Return the full representation the first time we're called
|
|
|
|
var ret;
|
|
|
|
if (this.needsFullGet)
|
|
|
|
{
|
|
|
|
ret = this.GetFullRepresentation();
|
|
|
|
this.needsFullGet = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = this.changes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialise changes to null instead of {}, to avoid memory allocations in the
|
|
|
|
// common case where there will be no changes; event handlers should each reset
|
|
|
|
// it to {} if needed
|
|
|
|
this.changes = null;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2011-03-03 01:16:14 +01:00
|
|
|
AIProxy.prototype.NotifyChange = function()
|
|
|
|
{
|
|
|
|
if (!this.changes)
|
|
|
|
{
|
|
|
|
this.changes = {};
|
|
|
|
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.ChangedEntity(this.entity);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-02-05 21:35:34 +01:00
|
|
|
// AI representation-updating event handlers:
|
|
|
|
|
2011-01-16 00:35:20 +01:00
|
|
|
AIProxy.prototype.OnPositionChanged = function(msg)
|
|
|
|
{
|
2011-03-03 01:16:14 +01:00
|
|
|
this.NotifyChange();
|
2011-01-16 00:35:20 +01:00
|
|
|
|
|
|
|
if (msg.inWorld)
|
|
|
|
this.changes.position = [msg.x, msg.z];
|
|
|
|
else
|
|
|
|
this.changes.position = undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnHealthChanged = function(msg)
|
|
|
|
{
|
2011-03-03 01:16:14 +01:00
|
|
|
this.NotifyChange();
|
2011-01-16 00:35:20 +01:00
|
|
|
|
|
|
|
this.changes.hitpoints = msg.to;
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnOwnershipChanged = function(msg)
|
|
|
|
{
|
2011-03-03 01:16:14 +01:00
|
|
|
this.NotifyChange();
|
2011-01-16 00:35:20 +01:00
|
|
|
|
|
|
|
this.changes.owner = msg.to;
|
|
|
|
};
|
|
|
|
|
2011-02-05 21:35:34 +01:00
|
|
|
AIProxy.prototype.OnUnitIdleChanged = function(msg)
|
|
|
|
{
|
2011-03-03 01:16:14 +01:00
|
|
|
this.NotifyChange();
|
2011-02-05 21:35:34 +01:00
|
|
|
|
|
|
|
this.changes.idle = msg.idle;
|
|
|
|
};
|
|
|
|
|
2012-04-04 22:23:41 +02:00
|
|
|
AIProxy.prototype.OnUnitAIStateChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
|
|
|
|
this.changes.unitAIState = msg.to;
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnUnitAIOrderDataChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
|
|
|
|
this.changes.unitAIOrderData = msg.to;
|
|
|
|
};
|
|
|
|
|
2012-04-20 19:21:04 +02:00
|
|
|
AIProxy.prototype.OnProductionQueueChanged = function(msg)
|
2011-02-05 21:35:34 +01:00
|
|
|
{
|
2011-03-03 01:16:14 +01:00
|
|
|
this.NotifyChange();
|
2011-02-05 21:35:34 +01:00
|
|
|
|
2012-04-20 19:21:04 +02:00
|
|
|
var cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
|
|
|
|
this.changes.trainingQueue = cmpProductionQueue.GetQueue();
|
2012-04-18 13:30:28 +02:00
|
|
|
};
|
2011-02-05 21:35:34 +01:00
|
|
|
|
2011-11-14 00:23:58 +01:00
|
|
|
AIProxy.prototype.OnGarrisonedUnitsChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
|
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
|
|
|
this.changes.garrisoned = cmpGarrisonHolder.GetEntities();
|
2012-04-18 13:30:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnResourceSupplyChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
this.changes.resourceSupplyAmount = msg.to;
|
|
|
|
};
|
|
|
|
|
2013-03-13 21:10:46 +01:00
|
|
|
AIProxy.prototype.OnResourceSupplyGatherersChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
this.changes.resourceSupplyGatherers = msg.to;
|
|
|
|
};
|
|
|
|
|
2012-04-18 13:30:28 +02:00
|
|
|
AIProxy.prototype.OnResourceCarryingChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
this.changes.resourceCarrying = msg.to;
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnFoundationProgressChanged = function(msg)
|
|
|
|
{
|
|
|
|
this.NotifyChange();
|
|
|
|
this.changes.foundationProgress = msg.to;
|
|
|
|
};
|
2011-11-14 00:23:58 +01:00
|
|
|
|
2011-01-16 00:35:20 +01:00
|
|
|
// TODO: event handlers for all the other things
|
|
|
|
|
|
|
|
AIProxy.prototype.GetFullRepresentation = function()
|
|
|
|
{
|
|
|
|
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
|
|
|
|
|
|
|
var ret = {
|
|
|
|
// These properties are constant and won't need to be updated
|
|
|
|
"id": this.entity,
|
|
|
|
"template": cmpTemplateManager.GetCurrentTemplateName(this.entity)
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
|
|
|
if (cmpPosition)
|
|
|
|
{
|
|
|
|
// Updated by OnPositionChanged
|
|
|
|
|
|
|
|
if (cmpPosition.IsInWorld())
|
|
|
|
{
|
|
|
|
var pos = cmpPosition.GetPosition2D();
|
|
|
|
ret.position = [pos.x, pos.y];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret.position = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
|
|
|
|
if (cmpHealth)
|
|
|
|
{
|
|
|
|
// Updated by OnHealthChanged
|
|
|
|
ret.hitpoints = cmpHealth.GetHitpoints();
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
|
|
|
if (cmpOwnership)
|
|
|
|
{
|
|
|
|
// Updated by OnOwnershipChanged
|
|
|
|
ret.owner = cmpOwnership.GetOwner();
|
|
|
|
}
|
|
|
|
|
2011-02-05 21:35:34 +01:00
|
|
|
var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
|
|
|
|
if (cmpUnitAI)
|
|
|
|
{
|
|
|
|
// Updated by OnUnitIdleChanged
|
|
|
|
ret.idle = cmpUnitAI.IsIdle();
|
2012-04-04 22:23:41 +02:00
|
|
|
// Updated by OnUnitAIStateChanged
|
|
|
|
ret.unitAIState = cmpUnitAI.GetCurrentState();
|
|
|
|
// Updated by OnUnitAIOrderDataChanged
|
|
|
|
ret.unitAIOrderData = cmpUnitAI.GetOrderData();
|
2011-02-05 21:35:34 +01:00
|
|
|
}
|
|
|
|
|
2012-04-20 19:21:04 +02:00
|
|
|
var cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
|
|
|
|
if (cmpProductionQueue)
|
2011-01-16 00:35:20 +01:00
|
|
|
{
|
2012-04-20 19:21:04 +02:00
|
|
|
// Updated by OnProductionQueueChanged
|
|
|
|
ret.trainingQueue = cmpProductionQueue.GetQueue();
|
2011-01-16 00:35:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var cmpFoundation = Engine.QueryInterface(this.entity, IID_Foundation);
|
|
|
|
if (cmpFoundation)
|
|
|
|
{
|
2012-04-18 13:30:28 +02:00
|
|
|
// Updated by OnFoundationProgressChanged
|
2011-01-16 00:35:20 +01:00
|
|
|
ret.foundationProgress = cmpFoundation.GetBuildPercentage();
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
|
|
|
|
if (cmpResourceSupply)
|
|
|
|
{
|
2012-04-18 13:30:28 +02:00
|
|
|
// Updated by OnResourceSupplyChanged
|
2011-01-16 00:35:20 +01:00
|
|
|
ret.resourceSupplyAmount = cmpResourceSupply.GetCurrentAmount();
|
2013-03-13 21:10:46 +01:00
|
|
|
ret.resourceSupplyGatherers = cmpResourceSupply.GetGatherers();
|
2011-01-16 00:35:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
|
|
|
if (cmpResourceGatherer)
|
|
|
|
{
|
2012-04-18 13:30:28 +02:00
|
|
|
// Updated by OnResourceCarryingChanged
|
2011-01-16 00:35:20 +01:00
|
|
|
ret.resourceCarrying = cmpResourceGatherer.GetCarryingStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
|
|
|
if (cmpGarrisonHolder)
|
|
|
|
{
|
2012-04-18 13:30:28 +02:00
|
|
|
// Updated by OnGarrisonedUnitsChanged
|
2011-01-16 00:35:20 +01:00
|
|
|
ret.garrisoned = cmpGarrisonHolder.GetEntities();
|
|
|
|
}
|
2011-01-12 13:29:00 +01:00
|
|
|
|
2011-01-16 00:35:20 +01:00
|
|
|
return ret;
|
2011-01-12 13:29:00 +01:00
|
|
|
};
|
|
|
|
|
2011-02-05 21:35:34 +01:00
|
|
|
// AI event handlers:
|
|
|
|
// (These are passed directly as events to the AI scripts, rather than updating
|
|
|
|
// our proxy representation.)
|
|
|
|
// (This shouldn't include extremely high-frequency events, like PositionChanged,
|
|
|
|
// because that would be very expensive and AI will rarely care about all those
|
|
|
|
// events.)
|
|
|
|
|
|
|
|
AIProxy.prototype.OnCreate = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("Create", msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnDestroy = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("Destroy", msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnAttacked = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("Attacked", msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnConstructionFinished = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("ConstructionFinished", msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
AIProxy.prototype.OnTrainingFinished = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("TrainingFinished", msg);
|
|
|
|
};
|
|
|
|
|
2013-07-25 10:57:07 +02:00
|
|
|
AIProxy.prototype.OnAIMetadata = function(msg)
|
|
|
|
{
|
|
|
|
var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
|
|
|
|
cmpAIInterface.PushEvent("AIMetadata", msg);
|
|
|
|
};
|
|
|
|
|
2011-01-12 13:29:00 +01:00
|
|
|
Engine.RegisterComponentType(IID_AIProxy, "AIProxy", AIProxy);
|