2010-04-19 21:47:23 +02:00
var g _ProgressInterval = 1000 ;
function TrainingQueue ( ) { }
TrainingQueue . prototype . Schema =
2010-04-23 18:09:03 +02:00
"<a:help>Allows the building to train new units.</a:help>" +
"<a:example>" +
2010-07-03 03:23:23 +02:00
"<Entities datatype='tokens'>" +
2010-04-23 18:09:03 +02:00
"\n units/{civ}_support_female_citizen\n units/{civ}_support_trader\n units/celt_infantry_spearman_b\n " +
"</Entities>" +
"</a:example>" +
"<element name='Entities' a:help='Space-separated list of entity template names that this building can train. The special string \"{civ}\" will be automatically replaced by the building's four-character civ code'>" +
2010-07-03 03:23:23 +02:00
"<attribute name='datatype'>" +
"<value>tokens</value>" +
"</attribute>" +
2010-04-19 21:47:23 +02:00
"<text/>" +
"</element>" ;
TrainingQueue . prototype . Init = function ( )
{
this . nextID = 1 ;
this . queue = [ ] ;
// Queue items are:
// {
// "id": 1,
// "template": "units/example",
// "count": 10,
// "resources": { "wood": 100, ... },
// "timeTotal": 15000, // msecs
// "timeRemaining": 10000, // msecs
// }
this . timer = undefined ; // g_ProgressInterval msec timer, active while the queue is non-empty
} ;
TrainingQueue . prototype . GetEntitiesList = function ( )
{
2010-07-03 03:23:23 +02:00
var string = this . template . Entities . _string ;
2010-04-19 21:47:23 +02:00
// Replace the "{civ}" codes with this entity's civ ID
var cmpIdentity = Engine . QueryInterface ( this . entity , IID _Identity ) ;
if ( cmpIdentity )
string = string . replace ( /\{civ\}/g , cmpIdentity . GetCiv ( ) ) ;
return string . split ( /\s+/ ) ;
} ;
TrainingQueue . prototype . AddBatch = function ( player , templateName , count )
{
// TODO: there should probably be a limit on the number of queued batches
// TODO: there should be a way for the GUI to determine whether it's going
// to be possible to add a batch (based on resource costs and length limits)
// Find the template data so we can determine the build costs
var cmpTempMan = Engine . QueryInterface ( SYSTEM _ENTITY , IID _TemplateManager ) ;
var template = cmpTempMan . GetTemplate ( templateName ) ;
if ( ! template )
return ;
var timeMult = count ; // TODO: we want some kind of discount for larger batches
var costMult = count ;
var time = timeMult * ( template . Cost . BuildTime || 1 ) ;
var costs = { } ;
for each ( var r in [ "food" , "wood" , "stone" , "metal" ] )
{
if ( template . Cost . Resources [ r ] )
costs [ r ] = Math . floor ( costMult * template . Cost . Resources [ r ] ) ;
else
costs [ r ] = 0 ;
}
// Find the player
var cmpPlayerMan = Engine . QueryInterface ( SYSTEM _ENTITY , IID _PlayerManager ) ;
var playerEnt = cmpPlayerMan . GetPlayerByID ( player ) ;
var cmpPlayer = Engine . QueryInterface ( playerEnt , IID _Player ) ;
if ( ! cmpPlayer . TrySubtractResources ( costs ) )
{
// TODO: report error to player (they ran out of resources)
return ;
}
this . queue . push ( {
"id" : this . nextID ++ ,
"template" : templateName ,
"count" : count ,
"resources" : costs ,
"timeTotal" : time * 1000 ,
"timeRemaining" : time * 1000 ,
} ) ;
// If this is the first item in the queue, start the timer
if ( ! this . timer )
{
var cmpTimer = Engine . QueryInterface ( SYSTEM _ENTITY , IID _Timer ) ;
this . timer = cmpTimer . SetTimeout ( this . entity , IID _TrainingQueue , "ProgressTimeout" , g _ProgressInterval , { } ) ;
}
} ;
TrainingQueue . prototype . RemoveBatch = function ( player , id )
{
for ( var i = 0 ; i < this . queue . length ; ++ i )
{
var item = this . queue [ i ] ;
if ( item . id != id )
continue ;
// Now we've found the item to remove
// Find the player
var cmpPlayerMan = Engine . QueryInterface ( SYSTEM _ENTITY , IID _PlayerManager ) ;
var playerEnt = cmpPlayerMan . GetPlayerByID ( player ) ;
var cmpPlayer = Engine . QueryInterface ( playerEnt , IID _Player ) ;
// Refund the resource cost for this batch
cmpPlayer . AddResources ( item . resources ) ;
// Remove from the queue
// (We don't need to remove the timer - it'll expire if it discovers the queue is empty)
this . queue . splice ( i , 1 ) ;
return ;
}
}
TrainingQueue . prototype . GetQueue = function ( )
{
var out = [ ] ;
for each ( var item in this . queue )
{
out . push ( {
"id" : item . id ,
"template" : item . template ,
"count" : item . count ,
"progress" : 1 - ( item . timeRemaining / item . timeTotal ) ,
} ) ;
}
return out ;
} ;
TrainingQueue . prototype . OnDestroy = function ( )
{
// If the building is destroyed while it's got a large training queue,
// you lose all the resources invested in that queue. That'll teach you
// to be so reckless with your buildings.
if ( this . timer )
{
var cmpTimer = Engine . QueryInterface ( SYSTEM _ENTITY , IID _Timer ) ;
cmpTimer . CancelTimer ( this . timer ) ;
}
} ;
TrainingQueue . prototype . SpawnUnits = function ( templateName , count )
{
var cmpFootprint = Engine . QueryInterface ( this . entity , IID _Footprint ) ;
var cmpPosition = Engine . QueryInterface ( this . entity , IID _Position ) ;
var cmpOwnership = Engine . QueryInterface ( this . entity , IID _Ownership ) ;
2010-08-05 12:20:47 +02:00
var cmpRallyPoint = Engine . QueryInterface ( this . entity , IID _RallyPoint ) ;
2010-04-19 21:47:23 +02:00
for ( var i = 0 ; i < count ; ++ i )
{
var ent = Engine . AddEntity ( templateName ) ;
var pos = cmpFootprint . PickSpawnPoint ( ent ) ;
if ( pos . y < 0 )
{
// Whoops, something went wrong (maybe there wasn't any space to spawn the unit).
// What should we do here?
// For now, just move the unit into the middle of the building where it'll probably get stuck
pos = cmpPosition . GetPosition ( ) ;
2010-07-04 19:19:38 +02:00
warn ( "Can't find free space to spawn trained unit" ) ;
2010-04-19 21:47:23 +02:00
}
var cmpNewPosition = Engine . QueryInterface ( ent , IID _Position ) ;
cmpNewPosition . JumpTo ( pos . x , pos . z ) ;
// TODO: what direction should they face in?
var cmpNewOwnership = Engine . QueryInterface ( ent , IID _Ownership ) ;
cmpNewOwnership . SetOwner ( cmpOwnership . GetOwner ( ) ) ;
2010-08-05 12:20:47 +02:00
// If a rally point is set, walk towards it
var cmpUnitAI = Engine . QueryInterface ( ent , IID _UnitAI ) ;
if ( cmpUnitAI && cmpRallyPoint )
{
var rallyPos = cmpRallyPoint . GetPosition ( ) ;
if ( rallyPos )
cmpUnitAI . Walk ( rallyPos . x , rallyPos . z , false ) ;
}
2010-08-10 03:25:24 +02:00
// Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
if ( i == 0 )
PlaySound ( "trained" , ent ) ;
2010-04-19 21:47:23 +02:00
}
} ;
TrainingQueue . prototype . ProgressTimeout = function ( data )
{
// Allocate the 1000msecs to as many queue items as it takes
// until we've used up all the time (so that we work accurately
// with items that take fractions of a second)
var time = g _ProgressInterval ;
2010-04-30 01:36:05 +02:00
time *= 10 ; // XXX: this is a hack to make testing easier
2010-04-19 21:47:23 +02:00
while ( time > 0 && this . queue . length )
{
var item = this . queue [ 0 ] ;
if ( item . timeRemaining > time )
{
item . timeRemaining -= time ;
break ;
}
// This item is finished now
time -= item . timeRemaining ;
this . SpawnUnits ( item . template , item . count ) ;
this . queue . shift ( ) ;
}
// If the queue's empty, delete the timer, else repeat it
if ( this . queue . length == 0 )
{
this . timer = undefined ;
}
else
{
var cmpTimer = Engine . QueryInterface ( SYSTEM _ENTITY , IID _Timer ) ;
this . timer = cmpTimer . SetTimeout ( this . entity , IID _TrainingQueue , "ProgressTimeout" , g _ProgressInterval , data ) ;
}
}
Engine . RegisterComponentType ( IID _TrainingQueue , "TrainingQueue" , TrainingQueue ) ;