2010-11-21 19:41:13 +01:00
|
|
|
//Number of rounds of firing per 2 seconds
|
|
|
|
const roundCount = 10;
|
|
|
|
const timerInterval = 2000 / roundCount;
|
|
|
|
|
2010-11-17 08:30:25 +01:00
|
|
|
function BuildingAI() {}
|
|
|
|
|
|
|
|
BuildingAI.prototype.Schema =
|
|
|
|
"<element name='DefaultArrowCount'>" +
|
|
|
|
"<data type='nonNegativeInteger'/>" +
|
|
|
|
"</element>" +
|
|
|
|
"<element name='GarrisonArrowMultiplier'>" +
|
|
|
|
"<ref name='nonNegativeDecimal'/>" +
|
|
|
|
"</element>";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize BuildingAI Component
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.Init = function()
|
|
|
|
{
|
2012-04-26 23:58:05 +02:00
|
|
|
if (this.GetDefaultArrowCount() > 0 || this.GetGarrisonArrowMultiplier() > 0)
|
2010-11-17 08:30:25 +01:00
|
|
|
{
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
2010-11-21 19:41:13 +01:00
|
|
|
this.currentRound = 0;
|
|
|
|
//Arrows left to fire
|
|
|
|
this.arrowsLeft = 0;
|
|
|
|
this.timer = cmpTimer.SetTimeout(this.entity, IID_BuildingAI, "FireArrows", timerInterval, {});
|
|
|
|
this.targetUnits = [];
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BuildingAI.prototype.OnOwnershipChanged = function(msg)
|
|
|
|
{
|
|
|
|
if (msg.to != -1)
|
|
|
|
this.SetupRangeQuery(msg.to);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cleanup on destroy
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.OnDestroy = function()
|
|
|
|
{
|
|
|
|
if (this.timer)
|
|
|
|
{
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
cmpTimer.CancelTimer(this.timer);
|
|
|
|
this.timer = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up range queries
|
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
|
|
if (this.enemyUnitsQuery)
|
|
|
|
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup the Range Query to detect units coming in & out of range
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.SetupRangeQuery = function(owner)
|
|
|
|
{
|
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
|
|
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
2010-11-17 19:25:00 +01:00
|
|
|
if (this.enemyUnitsQuery)
|
|
|
|
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
2010-11-17 08:30:25 +01:00
|
|
|
var players = [];
|
|
|
|
|
2011-07-02 02:06:39 +02:00
|
|
|
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);
|
2010-11-17 08:30:25 +01:00
|
|
|
var numPlayers = cmpPlayerManager.GetNumPlayers();
|
|
|
|
|
|
|
|
for (var i = 1; i < numPlayers; ++i)
|
|
|
|
{ // Exclude gaia, allies, and self
|
|
|
|
// TODO: How to handle neutral players - Special query to attack military only?
|
2011-07-02 02:06:39 +02:00
|
|
|
if (cmpPlayer.IsEnemy(i))
|
2010-11-17 08:30:25 +01:00
|
|
|
players.push(i);
|
|
|
|
}
|
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
|
|
|
if (cmpAttack)
|
|
|
|
{
|
|
|
|
var range = cmpAttack.GetRange("Ranged");
|
2012-04-17 23:05:01 +02:00
|
|
|
this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, 0, cmpRangeManager.GetEntityFlagMask("normal"));
|
2010-11-17 08:30:25 +01:00
|
|
|
cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when units enter or leave range
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.OnRangeUpdate = function(msg)
|
|
|
|
{
|
2011-07-08 01:07:36 +02:00
|
|
|
if (msg.tag != this.enemyUnitsQuery)
|
|
|
|
return;
|
|
|
|
|
2010-11-21 19:41:13 +01:00
|
|
|
if (msg.added.length > 0)
|
2010-11-17 19:25:00 +01:00
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
for each (var entity in msg.added)
|
|
|
|
{
|
|
|
|
this.targetUnits.push(entity);
|
|
|
|
}
|
2010-11-17 19:25:00 +01:00
|
|
|
}
|
2010-11-21 19:41:13 +01:00
|
|
|
if (msg.removed.length > 0)
|
2010-11-17 19:25:00 +01:00
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
for each (var entity in msg.removed)
|
|
|
|
{
|
|
|
|
this.targetUnits.splice(this.targetUnits.indexOf(entity), 1);
|
|
|
|
}
|
2010-11-17 19:25:00 +01:00
|
|
|
}
|
2010-11-17 08:30:25 +01:00
|
|
|
};
|
|
|
|
|
2012-04-26 23:58:05 +02:00
|
|
|
BuildingAI.prototype.GetDefaultArrowCount = function()
|
|
|
|
{
|
2012-05-05 00:51:14 +02:00
|
|
|
var arrowCount = +this.template.DefaultArrowCount;
|
2012-04-26 23:58:05 +02:00
|
|
|
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
|
|
|
if (cmpTechMan)
|
2012-05-05 00:51:14 +02:00
|
|
|
arrowCount = cmpTechMan.ApplyModifications("BuildingAI/DefaultArrowCount", arrowCount, this.entity);
|
|
|
|
return arrowCount;
|
2012-04-26 23:58:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
BuildingAI.prototype.GetGarrisonArrowMultiplier = function()
|
|
|
|
{
|
2012-05-05 00:51:14 +02:00
|
|
|
var arrowMult = +this.template.GarrisonArrowMultiplier;
|
2012-04-26 23:58:05 +02:00
|
|
|
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
|
|
|
if (cmpTechMan)
|
2012-05-05 00:51:14 +02:00
|
|
|
arrowMult = cmpTechMan.ApplyModifications("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
|
|
|
|
return arrowMult;
|
2012-04-26 23:58:05 +02:00
|
|
|
};
|
|
|
|
|
2010-11-17 08:30:25 +01:00
|
|
|
/**
|
|
|
|
* Returns the number of arrows which needs to be fired.
|
|
|
|
* DefaultArrowCount + Garrisoned Archers(ie., any unit capable
|
|
|
|
* of shooting arrows from inside buildings)
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.GetArrowCount = function()
|
|
|
|
{
|
2012-04-26 23:58:05 +02:00
|
|
|
var count = this.GetDefaultArrowCount();
|
2010-11-17 08:30:25 +01:00
|
|
|
var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
|
|
|
if (cmpGarrisonHolder)
|
|
|
|
{
|
2012-04-26 23:58:05 +02:00
|
|
|
count += Math.round(cmpGarrisonHolder.GetGarrisonedArcherCount() * this.GetGarrisonArrowMultiplier());
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
|
|
|
return count;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fires arrows. Called every N times every 2 seconds
|
|
|
|
* where N is the number of Arrows
|
|
|
|
*/
|
|
|
|
BuildingAI.prototype.FireArrows = function()
|
|
|
|
{
|
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
|
|
|
if (cmpAttack)
|
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
this.timer = cmpTimer.SetTimeout(this.entity, IID_BuildingAI, "FireArrows", timerInterval, {});
|
|
|
|
var arrowsToFire = 0;
|
|
|
|
if (this.currentRound > (roundCount - 1))
|
2010-11-17 08:30:25 +01:00
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
//Reached end of rounds. Reset count
|
|
|
|
this.currentRound = 0;
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
2010-11-21 19:41:13 +01:00
|
|
|
|
|
|
|
if (this.currentRound == 0)
|
2010-11-17 08:30:25 +01:00
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
//First round. Calculate arrows to fire
|
|
|
|
this.arrowsLeft = this.GetArrowCount();
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
|
|
|
|
2010-11-21 19:41:13 +01:00
|
|
|
if (this.currentRound == (roundCount - 1))
|
2010-11-17 08:30:25 +01:00
|
|
|
{
|
2010-11-21 19:41:13 +01:00
|
|
|
//Last round. Need to fire all left-over arrows
|
|
|
|
arrowsToFire = this.arrowsLeft;
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
2010-11-21 19:41:13 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
//Fire N arrows, 0 <= N <= Number of arrows left
|
|
|
|
arrowsToFire = Math.floor(Math.random() * this.arrowsLeft);
|
|
|
|
}
|
|
|
|
if (this.targetUnits.length > 0)
|
|
|
|
{
|
|
|
|
for (var i = 0;i < arrowsToFire;i++)
|
|
|
|
{
|
|
|
|
cmpAttack.PerformAttack("Ranged", this.targetUnits[Math.floor(Math.random() * this.targetUnits.length)]);
|
|
|
|
PlaySound("arrowfly", this.entity);
|
|
|
|
}
|
|
|
|
this.arrowsLeft -= arrowsToFire;
|
|
|
|
}
|
|
|
|
this.currentRound++;
|
2010-11-17 08:30:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-07-08 01:07:36 +02:00
|
|
|
Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI);
|