2010-02-05 23:00:39 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
This is currently just a very simplistic state machine that lets units be commanded around
|
2010-02-12 23:46:53 +01:00
|
|
|
and then autonomously carry out the orders. It might need to be entirely redesigned.
|
2010-02-05 23:00:39 +01:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const STATE_IDLE = 0;
|
|
|
|
const STATE_WALKING = 1;
|
|
|
|
const STATE_ATTACKING = 2;
|
2010-03-12 22:41:40 +01:00
|
|
|
const STATE_REPAIRING = 3;
|
|
|
|
const STATE_GATHERING = 4;
|
2010-02-05 23:00:39 +01:00
|
|
|
|
|
|
|
/* Attack process:
|
|
|
|
* When starting attack:
|
|
|
|
* Activate attack animation (with appropriate repeat speed and offset)
|
|
|
|
* Set this.attackTimer to run at maximum of:
|
|
|
|
* GetTimers().prepare msec from now
|
|
|
|
* this.attackRechargeTime
|
|
|
|
* Loop:
|
|
|
|
* Wait for the timer
|
|
|
|
* Perform the attack
|
|
|
|
* Set this.attackRechargeTime to now plus GetTimers().recharge
|
|
|
|
* Set this.attackTimer to run after GetTimers().repeat
|
|
|
|
* At any point it's safe to cancel the attack and switch to a different action
|
|
|
|
* (The rechargeTime is to prevent people spamming the attack command and getting
|
|
|
|
* faster-than-normal attacks)
|
|
|
|
*/
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
/* Repeat/Gather process is about the same, except with less synchronisation - the action
|
2010-02-12 23:46:53 +01:00
|
|
|
* is just performed 1sec after initiated, and then repeated every 1sec.
|
2010-03-12 22:41:40 +01:00
|
|
|
* (TODO: it'd be nice to avoid most of the duplication between Attack and Repeat and Gather code)
|
2010-02-12 23:46:53 +01:00
|
|
|
*/
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
function UnitAI() {}
|
|
|
|
|
|
|
|
UnitAI.prototype.Init = function()
|
|
|
|
{
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
|
|
|
|
// The earliest time at which we'll have 'recovered' from the previous attack, and
|
|
|
|
// can start preparing a new attack
|
|
|
|
this.attackRechargeTime = 0;
|
|
|
|
// Timer for AttackTimeout
|
|
|
|
this.attackTimer = undefined;
|
2010-02-10 20:28:46 +01:00
|
|
|
// Current target entity ID
|
|
|
|
this.attackTarget = undefined;
|
2010-02-12 23:46:53 +01:00
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
// Timer for RepairTimeout
|
|
|
|
this.repairTimer = undefined;
|
|
|
|
// Current target entity ID
|
|
|
|
this.repairTarget = undefined;
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
// Timer for GatherTimeout
|
|
|
|
this.gatherTimer = undefined;
|
|
|
|
// Current target entity ID
|
|
|
|
this.gatherTarget = undefined;
|
2010-02-07 21:06:16 +01:00
|
|
|
};
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
//// Interface functions ////
|
|
|
|
|
|
|
|
UnitAI.prototype.Walk = function(x, z)
|
|
|
|
{
|
2010-02-07 21:06:16 +01:00
|
|
|
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
|
|
|
if (!cmpMotion)
|
2010-02-05 23:00:39 +01:00
|
|
|
return;
|
|
|
|
|
2010-02-07 21:06:16 +01:00
|
|
|
this.SelectAnimation("walk", false, cmpMotion.GetSpeed());
|
2010-04-04 23:24:39 +02:00
|
|
|
PlaySound("walk", this.entity);
|
2010-02-07 21:06:16 +01:00
|
|
|
|
|
|
|
cmpMotion.MoveToPoint(x, z, 0, 0);
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
this.state = STATE_WALKING;
|
|
|
|
};
|
|
|
|
|
|
|
|
UnitAI.prototype.Attack = function(target)
|
|
|
|
{
|
2010-02-12 23:46:53 +01:00
|
|
|
// Verify that we're able to respond to Attack commands
|
2010-02-05 23:00:39 +01:00
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
|
|
|
if (!cmpAttack)
|
|
|
|
return;
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
// TODO: verify that this is a valid target
|
|
|
|
|
|
|
|
// Stop any previous action timers
|
|
|
|
this.CancelTimers();
|
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
// Remember the target, and start moving towards it
|
|
|
|
this.attackTarget = target;
|
2010-02-12 23:46:53 +01:00
|
|
|
this.MoveToTarget(target, cmpAttack.GetRange());
|
2010-02-10 20:28:46 +01:00
|
|
|
this.state = STATE_ATTACKING;
|
2010-02-12 23:46:53 +01:00
|
|
|
};
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
UnitAI.prototype.Repair = function(target)
|
|
|
|
{
|
|
|
|
// Verify that we're able to respond to Repair commands
|
|
|
|
var cmpBuilder = Engine.QueryInterface(this.entity, IID_Builder);
|
|
|
|
if (!cmpBuilder)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: verify that this is a valid target
|
|
|
|
|
|
|
|
// Stop any previous action timers
|
|
|
|
this.CancelTimers();
|
|
|
|
|
|
|
|
// Remember the target, and start moving towards it
|
|
|
|
this.repairTarget = target;
|
|
|
|
this.MoveToTarget(target, cmpBuilder.GetRange());
|
|
|
|
this.state = STATE_REPAIRING;
|
|
|
|
};
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
UnitAI.prototype.Gather = function(target)
|
|
|
|
{
|
|
|
|
// Verify that we're able to respond to Gather commands
|
|
|
|
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
|
|
|
if (!cmpResourceGatherer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: verify that this is a valid target
|
|
|
|
|
|
|
|
// Stop any previous action timers
|
|
|
|
this.CancelTimers();
|
|
|
|
|
|
|
|
// Remember the target, and start moving towards it
|
|
|
|
this.gatherTarget = target;
|
|
|
|
this.MoveToTarget(target, cmpResourceGatherer.GetRange());
|
|
|
|
this.state = STATE_GATHERING;
|
2010-02-10 20:28:46 +01:00
|
|
|
};
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
//// Message handlers ////
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
UnitAI.prototype.OnDestroy = function()
|
|
|
|
{
|
2010-02-12 23:46:53 +01:00
|
|
|
// Clean up any timers that are now obsolete
|
|
|
|
this.CancelTimers();
|
2010-02-10 20:28:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
UnitAI.prototype.OnMotionChanged = function(msg)
|
|
|
|
{
|
|
|
|
if (msg.speed)
|
|
|
|
{
|
|
|
|
// Started moving
|
|
|
|
// => play the appropriate animation
|
|
|
|
this.SelectAnimation("walk", false, msg.speed);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (this.state == STATE_WALKING)
|
|
|
|
{
|
|
|
|
// Stopped walking
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
}
|
|
|
|
else if (this.state == STATE_ATTACKING)
|
|
|
|
{
|
|
|
|
// We were attacking, and have stopped moving
|
|
|
|
// => check if we can still reach the target now
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
if (!this.MoveIntoAttackRange())
|
|
|
|
return;
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
// In range, so perform the attack,
|
|
|
|
// after the prepare time but not before the previous attack's recharge
|
2010-02-05 23:00:39 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
2010-02-07 21:06:16 +01:00
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
var timers = cmpAttack.GetTimers();
|
|
|
|
var time = Math.max(timers.prepare, this.attackRechargeTime - cmpTimer.GetTime());
|
|
|
|
this.attackTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "AttackTimeout", time, {});
|
|
|
|
|
|
|
|
// Start the idle animation before we switch to the attack
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
}
|
2010-03-12 22:41:40 +01:00
|
|
|
else if (this.state == STATE_REPAIRING)
|
|
|
|
{
|
|
|
|
// We were repairing, and have stopped moving
|
|
|
|
// => check if we can still reach the target now
|
|
|
|
|
|
|
|
if (!this.MoveIntoRepairRange())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// In range, so perform the repairing
|
|
|
|
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
this.repairTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "RepairTimeout", 1000, {});
|
|
|
|
|
|
|
|
// Start the repair/build animation
|
|
|
|
this.SelectAnimation("build");
|
|
|
|
}
|
2010-02-12 23:46:53 +01:00
|
|
|
else if (this.state == STATE_GATHERING)
|
|
|
|
{
|
|
|
|
// We were gathering, and have stopped moving
|
|
|
|
// => check if we can still reach the target now
|
|
|
|
|
|
|
|
if (!this.MoveIntoGatherRange())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// In range, so perform the gathering
|
|
|
|
|
|
|
|
var cmpResourceSupply = Engine.QueryInterface(this.gatherTarget, IID_ResourceSupply);
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
|
2010-04-04 23:24:39 +02:00
|
|
|
// Get the animation/sound type name
|
|
|
|
var type = cmpResourceSupply.GetType();
|
|
|
|
var typename = "gather_" + (type.specific || type.generic);
|
|
|
|
|
|
|
|
this.gatherTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "GatherTimeout", 1000, {"typename": typename});
|
2010-02-12 23:46:53 +01:00
|
|
|
|
|
|
|
// Start the gather animation
|
2010-04-04 23:24:39 +02:00
|
|
|
this.SelectAnimation(typename);
|
2010-02-12 23:46:53 +01:00
|
|
|
}
|
2010-02-10 20:28:46 +01:00
|
|
|
}
|
2010-02-07 21:06:16 +01:00
|
|
|
};
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
//// Private functions ////
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
function hypot2(x, y)
|
|
|
|
{
|
|
|
|
return x*x + y*y;
|
|
|
|
}
|
|
|
|
|
|
|
|
UnitAI.prototype.CheckRange = function(target, range)
|
|
|
|
{
|
|
|
|
// Target must be in the world
|
|
|
|
var cmpPositionTarget = Engine.QueryInterface(target, IID_Position);
|
|
|
|
if (!cmpPositionTarget || !cmpPositionTarget.IsInWorld())
|
|
|
|
return { "error": "not-in-world" };
|
|
|
|
|
|
|
|
// We must be in the world
|
|
|
|
var cmpPositionSelf = Engine.QueryInterface(this.entity, IID_Position);
|
|
|
|
if (!cmpPositionSelf || !cmpPositionSelf.IsInWorld())
|
|
|
|
return { "error": "not-in-world" };
|
|
|
|
|
|
|
|
// Target must be within range
|
|
|
|
var posTarget = cmpPositionTarget.GetPosition();
|
|
|
|
var posSelf = cmpPositionSelf.GetPosition();
|
|
|
|
var dist2 = hypot2(posTarget.x - posSelf.x, posTarget.z - posSelf.z);
|
|
|
|
// TODO: ought to be distance to closest point in footprint, not to center
|
|
|
|
// The +4 is a hack to give a ~1 tile tolerance, because the pathfinder doesn't
|
|
|
|
// always get quite close enough to the target
|
|
|
|
if (dist2 > (range.max+4)*(range.max+4))
|
|
|
|
return { "error": "out-of-range" };
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
UnitAI.prototype.CancelTimers = function()
|
|
|
|
{
|
|
|
|
if (this.attackTimer)
|
|
|
|
{
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
cmpTimer.CancelTimer(this.attackTimer);
|
|
|
|
this.attackTimer = undefined;
|
|
|
|
}
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
if (this.repairTimer)
|
|
|
|
{
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
cmpTimer.CancelTimer(this.repairTimer);
|
|
|
|
this.repairTimer = undefined;
|
|
|
|
}
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
if (this.gatherTimer)
|
|
|
|
{
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
cmpTimer.CancelTimer(this.gatherTimer);
|
|
|
|
this.gatherTimer = undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-02-10 20:28:46 +01:00
|
|
|
/**
|
|
|
|
* Tries to move into range of the attack target.
|
|
|
|
* Returns true if it's already in range.
|
|
|
|
*/
|
|
|
|
UnitAI.prototype.MoveIntoAttackRange = function()
|
|
|
|
{
|
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
2010-02-12 23:46:53 +01:00
|
|
|
var range = cmpAttack.GetRange();
|
2010-02-10 20:28:46 +01:00
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
var rangeStatus = this.CheckRange(this.attackTarget, range);
|
2010-02-10 20:28:46 +01:00
|
|
|
if (rangeStatus.error)
|
|
|
|
{
|
|
|
|
if (rangeStatus.error == "out-of-range")
|
|
|
|
{
|
|
|
|
// Out of range => need to move closer
|
|
|
|
// (The target has probably moved while we were chasing it)
|
2010-02-12 23:46:53 +01:00
|
|
|
this.MoveToTarget(this.attackTarget, range);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise it's impossible to reach the target, so give up
|
|
|
|
// and switch back to idle
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
UnitAI.prototype.MoveIntoRepairRange = function()
|
|
|
|
{
|
|
|
|
var cmpBuilder = Engine.QueryInterface(this.entity, IID_Builder);
|
|
|
|
var range = cmpBuilder.GetRange();
|
|
|
|
|
|
|
|
var rangeStatus = this.CheckRange(this.repairTarget, range);
|
|
|
|
if (rangeStatus.error)
|
|
|
|
{
|
|
|
|
if (rangeStatus.error == "out-of-range")
|
|
|
|
{
|
|
|
|
// Out of range => need to move closer
|
|
|
|
// (The target has probably moved while we were chasing it)
|
|
|
|
this.MoveToTarget(this.repairTarget, range);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise it's impossible to reach the target, so give up
|
|
|
|
// and switch back to idle
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
UnitAI.prototype.MoveIntoGatherRange = function()
|
|
|
|
{
|
|
|
|
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
|
|
|
var range = cmpResourceGatherer.GetRange();
|
|
|
|
|
|
|
|
var rangeStatus = this.CheckRange(this.gatherTarget, range);
|
|
|
|
if (rangeStatus.error)
|
|
|
|
{
|
|
|
|
if (rangeStatus.error == "out-of-range")
|
|
|
|
{
|
|
|
|
// Out of range => need to move closer
|
|
|
|
// (The target has probably moved while we were chasing it)
|
|
|
|
this.MoveToTarget(this.gatherTarget, range);
|
2010-02-10 20:28:46 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise it's impossible to reach the target, so give up
|
|
|
|
// and switch back to idle
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
// TODO: refactor all this repetitive code
|
|
|
|
|
2010-02-07 21:06:16 +01:00
|
|
|
UnitAI.prototype.SelectAnimation = function(name, once, speed)
|
|
|
|
{
|
|
|
|
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
|
|
|
|
if (!cmpVisual)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cmpVisual.SelectAnimation(name, once, speed);
|
|
|
|
};
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
UnitAI.prototype.MoveToTarget = function(target, range)
|
2010-02-05 23:00:39 +01:00
|
|
|
{
|
|
|
|
var cmpPositionTarget = Engine.QueryInterface(target, IID_Position);
|
|
|
|
if (!cmpPositionTarget || !cmpPositionTarget.IsInWorld())
|
|
|
|
return;
|
|
|
|
|
|
|
|
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
|
|
|
|
|
|
|
var pos = cmpPositionTarget.GetPosition();
|
2010-02-12 23:46:53 +01:00
|
|
|
cmpMotion.MoveToPoint(pos.x, pos.z, range.min, range.max);
|
2010-02-05 23:00:39 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
UnitAI.prototype.AttackTimeout = function(data)
|
|
|
|
{
|
|
|
|
// If we stopped attacking before this timeout, then don't do any processing here
|
|
|
|
if (this.state != STATE_ATTACKING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check if we can still reach the target
|
2010-02-10 20:28:46 +01:00
|
|
|
if (!this.MoveIntoAttackRange())
|
2010-02-05 23:00:39 +01:00
|
|
|
return;
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
|
|
|
|
2010-02-07 21:06:16 +01:00
|
|
|
// Play the attack animation
|
2010-02-10 20:28:46 +01:00
|
|
|
this.SelectAnimation("melee", false, 1);
|
2010-04-04 23:24:39 +02:00
|
|
|
// Play the sound
|
|
|
|
// TODO: these sounds should be triggered by the animation instead
|
|
|
|
PlaySound("attack", this.entity);
|
2010-02-07 21:06:16 +01:00
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
// Hit the target
|
2010-02-10 20:28:46 +01:00
|
|
|
cmpAttack.PerformAttack(this.attackTarget);
|
2010-02-05 23:00:39 +01:00
|
|
|
|
|
|
|
// Set a timer to hit the target again
|
2010-02-10 20:28:46 +01:00
|
|
|
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
|
|
|
|
var timers = cmpAttack.GetTimers();
|
|
|
|
this.attackRechargeTime = cmpTimer.GetTime() + timers.recharge;
|
|
|
|
this.attackTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "AttackTimeout", timers.repeat, data);
|
2010-02-05 23:00:39 +01:00
|
|
|
};
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
UnitAI.prototype.RepairTimeout = function(data)
|
|
|
|
{
|
|
|
|
// If we stopped repairing before this timeout, then don't do any processing here
|
|
|
|
if (this.state != STATE_REPAIRING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check if we can still reach the target
|
|
|
|
if (!this.MoveIntoRepairRange())
|
|
|
|
return;
|
|
|
|
|
|
|
|
var cmpBuilder = Engine.QueryInterface(this.entity, IID_Builder);
|
|
|
|
|
2010-04-04 23:24:39 +02:00
|
|
|
// Play the sound
|
|
|
|
PlaySound("build", this.entity);
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
// Repair/build the target
|
2010-04-04 23:24:39 +02:00
|
|
|
// TODO: these sounds should be triggered by the animation instead
|
2010-03-12 22:41:40 +01:00
|
|
|
var status = cmpBuilder.PerformBuilding(this.repairTarget);
|
|
|
|
|
|
|
|
// If the target is fully built and repaired, then stop and go back to idle
|
|
|
|
if (status.finished)
|
|
|
|
{
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set a timer to gather again
|
|
|
|
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
this.repairTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "RepairTimeout", 1000, data);
|
|
|
|
};
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
UnitAI.prototype.GatherTimeout = function(data)
|
|
|
|
{
|
|
|
|
// If we stopped gathering before this timeout, then don't do any processing here
|
|
|
|
if (this.state != STATE_GATHERING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check if we can still reach the target
|
|
|
|
if (!this.MoveIntoGatherRange())
|
|
|
|
return;
|
|
|
|
|
2010-03-12 22:41:40 +01:00
|
|
|
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
|
|
|
|
|
2010-04-04 23:24:39 +02:00
|
|
|
// Play the gather_* sound
|
|
|
|
// TODO: these sounds should be triggered by the animation instead
|
|
|
|
PlaySound(data.typename, this.entity);
|
|
|
|
|
2010-02-12 23:46:53 +01:00
|
|
|
// Gather from the target
|
|
|
|
var status = cmpResourceGatherer.PerformGather(this.gatherTarget);
|
|
|
|
|
|
|
|
// If the resource is exhausted, then stop and go back to idle
|
|
|
|
if (status.exhausted)
|
|
|
|
{
|
|
|
|
this.state = STATE_IDLE;
|
|
|
|
this.SelectAnimation("idle");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set a timer to gather again
|
|
|
|
|
|
|
|
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
|
|
|
this.gatherTimer = cmpTimer.SetTimeout(this.entity, IID_UnitAI, "GatherTimeout", 1000, data);
|
|
|
|
};
|
|
|
|
|
2010-02-05 23:00:39 +01:00
|
|
|
Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
|