2010-02-12 23:46:53 +01:00
function ResourceGatherer ( ) { }
2010-04-09 21:02:39 +02:00
ResourceGatherer . prototype . Schema =
2010-04-23 18:09:03 +02:00
"<a:help>Lets the unit gather resources from entities that have the ResourceSupply component.</a:help>" +
"<a:example>" +
2010-07-24 22:26:25 +02:00
"<MaxDistance>2.0</MaxDistance>" +
2010-04-23 18:09:03 +02:00
"<BaseSpeed>1.0</BaseSpeed>" +
"<Rates>" +
"<food.fish>1</food.fish>" +
2010-07-21 20:51:27 +02:00
"<metal.ore>3</metal.ore>" +
"<stone.rock>3</stone.rock>" +
"<wood.tree>2</wood.tree>" +
2010-04-23 18:09:03 +02:00
"</Rates>" +
2010-11-13 20:15:29 +01:00
"<Capacities>" +
"<food>10</food>" +
"<metal>10</metal>" +
"<stone>10</stone>" +
"<wood>10</wood>" +
"</Capacities>" +
2010-04-23 18:09:03 +02:00
"</a:example>" +
2010-07-24 22:26:25 +02:00
"<element name='MaxDistance' a:help='Max resource-gathering distance'>" +
"<ref name='positiveDecimal'/>" +
"</element>" +
2010-04-23 18:09:03 +02:00
"<element name='BaseSpeed' a:help='Base resource-gathering rate (in resource units per second)'>" +
"<ref name='positiveDecimal'/>" +
"</element>" +
"<element name='Rates' a:help='Per-resource-type gather rate multipliers. If a resource type is not specified then it cannot be gathered by this unit'>" +
2010-04-09 21:02:39 +02:00
"<interleave>" +
2010-04-23 18:09:03 +02:00
"<optional><element name='food' a:help='Food gather rate (may be overridden by \"food.*\" subtypes)'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='wood' a:help='Wood gather rate'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='stone' a:help='Stone gather rate'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='metal' a:help='Metal gather rate'><ref name='positiveDecimal'/></element></optional>" +
2012-04-28 21:25:44 +02:00
"<optional><element name='treasure' a:help='Treasure gather rate (only presense on value makes sense, size is only used to determine the delay before gathering, so it should be set to 1)'><ref name='positiveDecimal'/></element></optional>" +
2010-04-23 18:09:03 +02:00
"<optional><element name='food.fish' a:help='Fish gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='food.fruit' a:help='Fruit gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='food.grain' a:help='Grain gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='food.meat' a:help='Meat gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='food.milk' a:help='Milk gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" +
2010-07-21 20:51:27 +02:00
"<optional><element name='wood.tree' a:help='Tree gather rate (overrides \"wood\")'><ref name='positiveDecimal'/></element></optional>" +
2010-11-12 19:58:33 +01:00
"<optional><element name='wood.ruins' a:help='Tree gather rate (overrides \"wood\")'><ref name='positiveDecimal'/></element></optional>" +
2010-07-21 20:51:27 +02:00
"<optional><element name='stone.rock' a:help='Rock gather rate (overrides \"stone\")'><ref name='positiveDecimal'/></element></optional>" +
2010-11-12 19:58:33 +01:00
"<optional><element name='stone.ruins' a:help='Rock gather rate (overrides \"stone\")'><ref name='positiveDecimal'/></element></optional>" +
2010-11-20 22:37:31 +01:00
"<optional><element name='metal.ore' a:help='Ore gather rate (overrides \"metal\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='treasure.food' a:help='Food treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='treasure.wood' a:help='Wood treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='treasure.stone' a:help='Stone treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" +
"<optional><element name='treasure.metal' a:help='Metal treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" +
2010-04-09 21:02:39 +02:00
"</interleave>" +
2010-11-13 20:15:29 +01:00
"</element>" +
"<element name='Capacities' a:help='Per-resource-type maximum carrying capacity'>" +
"<interleave>" +
"<element name='food' a:help='Food capacity'><ref name='positiveDecimal'/></element>" +
"<element name='wood' a:help='Wood capacity'><ref name='positiveDecimal'/></element>" +
"<element name='stone' a:help='Stone capacity'><ref name='positiveDecimal'/></element>" +
"<element name='metal' a:help='Metal capacity'><ref name='positiveDecimal'/></element>" +
"</interleave>" +
2010-04-09 21:02:39 +02:00
"</element>" ;
2010-02-12 23:46:53 +01:00
ResourceGatherer . prototype . Init = function ( )
{
2010-12-08 17:12:04 +01:00
this . carrying = { } ; // { generic type: integer amount currently carried }
2010-11-13 20:15:29 +01:00
// (Note that this component supports carrying multiple types of resources,
// each with an independent capacity, but the rest of the game currently
// ensures and assumes we'll only be carrying one type at once)
2010-11-13 22:39:39 +01:00
// The last exact type gathered, so we can render appropriate props
this . lastCarriedType = undefined ; // { generic, specific }
2010-11-13 20:15:29 +01:00
} ;
/ * *
* Returns data about what resources the unit is currently carrying ,
* in the form [ { "type" : "wood" , "amount" : 7 , "max" : 10 } ]
* /
ResourceGatherer . prototype . GetCarryingStatus = function ( )
{
var ret = [ ] ;
for ( var type in this . carrying )
{
ret . push ( {
"type" : type ,
"amount" : this . carrying [ type ] ,
2012-04-20 22:37:12 +02:00
"max" : + this . GetCapacities ( ) [ type ]
2010-11-13 20:15:29 +01:00
} ) ;
}
return ret ;
} ;
2011-05-02 17:03:01 +02:00
/ * *
* Used to instantly give resources to unit
* @ param resources The same structure as returned form GetCarryingStatus
* /
ResourceGatherer . prototype . GiveResources = function ( resources )
{
for each ( var resource in resources )
{
this . carrying [ resource . type ] = + ( resource . amount ) ;
}
2013-08-14 07:14:20 +02:00
2012-04-18 13:30:28 +02:00
Engine . PostMessage ( this . entity , MT _ResourceCarryingChanged , { "to" : this . GetCarryingStatus ( ) } ) ;
2011-05-02 17:03:01 +02:00
} ;
2010-11-13 20:15:29 +01:00
/ * *
2010-12-08 17:12:04 +01:00
* Returns the generic type of one particular resource this unit is
2010-11-13 20:15:29 +01:00
* currently carrying , or undefined if none .
* /
ResourceGatherer . prototype . GetMainCarryingType = function ( )
{
// Return the first key, if any
for ( var type in this . carrying )
return type ;
return undefined ;
2010-02-12 23:46:53 +01:00
} ;
2010-11-13 22:39:39 +01:00
/ * *
2010-12-08 17:12:04 +01:00
* Returns the exact resource type we last picked up , as long as
* we ' re still carrying something similar enough , in the form
2010-11-13 22:39:39 +01:00
* { generic , specific }
* /
ResourceGatherer . prototype . GetLastCarriedType = function ( )
{
2011-02-11 00:09:28 +01:00
if ( this . lastCarriedType && this . lastCarriedType . generic in this . carrying )
2010-12-08 17:12:04 +01:00
return this . lastCarriedType ;
else
return undefined ;
2010-11-13 22:39:39 +01:00
} ;
2012-04-21 18:37:35 +02:00
ResourceGatherer . prototype . GetGatherRates = function ( )
2012-04-20 19:21:04 +02:00
{
2012-04-21 18:37:35 +02:00
var ret = { } ;
2013-03-13 21:44:48 +01:00
var cmpPlayer = QueryOwnerInterface ( this . entity , IID _Player ) ;
2013-08-14 07:14:20 +02:00
2013-03-13 21:44:48 +01:00
var baseSpeed = ApplyTechModificationsToEntity ( "ResourceGatherer/BaseSpeed" , + this . template . BaseSpeed , this . entity ) * cmpPlayer . GetGatherRateMultiplier ( ) ;
2012-05-05 00:51:14 +02:00
2012-04-21 18:37:35 +02:00
for ( var r in this . template . Rates )
2012-04-20 19:21:04 +02:00
{
2013-01-08 01:00:21 +01:00
var rate = ApplyTechModificationsToEntity ( "ResourceGatherer/Rates/" + r , + this . template . Rates [ r ] , this . entity ) ;
2012-04-21 18:37:35 +02:00
ret [ r ] = rate * baseSpeed ;
2012-04-20 19:21:04 +02:00
}
2012-05-05 00:51:14 +02:00
2012-04-21 18:37:35 +02:00
return ret ;
2010-02-12 23:46:53 +01:00
} ;
2012-04-20 22:37:12 +02:00
ResourceGatherer . prototype . GetCapacities = function ( )
{
2012-04-21 18:37:35 +02:00
var ret = { } ;
2012-05-05 00:51:14 +02:00
2012-04-21 18:37:35 +02:00
for ( var r in this . template . Capacities )
2012-04-20 22:37:12 +02:00
{
2013-01-08 01:00:21 +01:00
ret [ r ] = ApplyTechModificationsToEntity ( "ResourceGatherer/Capacities/" + r , + this . template . Capacities [ r ] , this . entity ) ;
2012-04-20 22:37:12 +02:00
}
2012-05-05 00:51:14 +02:00
2012-04-21 18:37:35 +02:00
return ret ;
2012-04-20 22:37:12 +02:00
} ;
2010-02-12 23:46:53 +01:00
ResourceGatherer . prototype . GetRange = function ( )
{
2010-07-25 00:09:59 +02:00
return { "max" : + this . template . MaxDistance , "min" : 0 } ;
2010-02-12 23:46:53 +01:00
// maybe this should depend on the unit or target or something?
2012-04-20 19:21:04 +02:00
} ;
2010-02-12 23:46:53 +01:00
2010-11-20 22:37:31 +01:00
/ * *
* Try to gather treasure
* @ return 'true' if treasure is successfully gathered and 'false' in the other case
* /
ResourceGatherer . prototype . TryInstantGather = function ( target )
{
var cmpResourceSupply = Engine . QueryInterface ( target , IID _ResourceSupply ) ;
var type = cmpResourceSupply . GetType ( ) ;
2013-08-14 07:14:20 +02:00
2010-11-20 22:37:31 +01:00
if ( type . generic != "treasure" ) return false ;
2013-08-14 07:14:20 +02:00
2010-11-20 22:37:31 +01:00
var status = cmpResourceSupply . TakeResources ( cmpResourceSupply . GetCurrentAmount ( ) ) ;
var cmpPlayer = QueryOwnerInterface ( this . entity , IID _Player ) ;
cmpPlayer . AddResource ( type . specific , status . amount ) ;
2013-08-14 07:14:20 +02:00
2010-11-20 22:37:31 +01:00
var cmpStatisticsTracker = QueryOwnerInterface ( this . entity , IID _StatisticsTracker ) ;
if ( cmpStatisticsTracker )
cmpStatisticsTracker . IncreaseTreasuresCollectedCounter ( ) ;
return true ;
} ;
2010-02-12 23:46:53 +01:00
/ * *
* Gather from the target entity . This should only be called after a successful range check ,
* and if the target has a compatible ResourceSupply .
2012-04-28 21:25:44 +02:00
* Call interval will be determined by gather rate , so always gather 1 amount when called .
2010-02-12 23:46:53 +01:00
* /
ResourceGatherer . prototype . PerformGather = function ( target )
{
2012-04-28 21:25:44 +02:00
if ( ! this . GetTargetGatherRate ( target ) )
2010-07-08 22:08:08 +02:00
return { "exhausted" : true } ;
2012-04-28 21:25:44 +02:00
var gatherAmount = 1 ;
2010-02-12 23:46:53 +01:00
var cmpResourceSupply = Engine . QueryInterface ( target , IID _ResourceSupply ) ;
var type = cmpResourceSupply . GetType ( ) ;
2010-11-13 20:15:29 +01:00
// Initialise the carried count if necessary
if ( ! this . carrying [ type . generic ] )
this . carrying [ type . generic ] = 0 ;
2010-02-12 23:46:53 +01:00
2010-11-13 20:15:29 +01:00
// Find the maximum so we won't exceed our capacity
2012-04-20 22:37:12 +02:00
var maxGathered = this . GetCapacities ( ) [ type . generic ] - this . carrying [ type . generic ] ;
2010-11-13 20:15:29 +01:00
2012-04-28 21:25:44 +02:00
var status = cmpResourceSupply . TakeResources ( Math . min ( gatherAmount , maxGathered ) ) ;
2010-11-13 20:15:29 +01:00
this . carrying [ type . generic ] += status . amount ;
2010-11-13 22:39:39 +01:00
this . lastCarriedType = type ;
2010-11-13 20:15:29 +01:00
// Update stats of how much the player collected.
// (We have to do it here rather than at the dropsite, because we
// need to know what subtype it was)
var cmpStatisticsTracker = QueryOwnerInterface ( this . entity , IID _StatisticsTracker ) ;
if ( cmpStatisticsTracker )
cmpStatisticsTracker . IncreaseResourceGatheredCounter ( type . generic , status . amount , type . specific ) ;
2013-08-14 07:14:20 +02:00
2012-04-18 13:30:28 +02:00
Engine . PostMessage ( this . entity , MT _ResourceCarryingChanged , { "to" : this . GetCarryingStatus ( ) } ) ;
2010-07-21 18:09:58 +02:00
// Tell the target we're gathering from it
Engine . PostMessage ( target , MT _ResourceGather ,
{ "entity" : target , "gatherer" : this . entity } ) ;
2010-11-13 20:15:29 +01:00
return {
"amount" : status . amount ,
"exhausted" : status . exhausted ,
2012-04-20 22:37:12 +02:00
"filled" : ( this . carrying [ type . generic ] >= this . GetCapacities ( ) [ type . generic ] )
2010-11-13 20:15:29 +01:00
} ;
2010-02-12 23:46:53 +01:00
} ;
2010-07-08 22:08:08 +02:00
/ * *
* Compute the amount of resources collected per second from the target .
* Returns 0 if resources cannot be collected ( e . g . the target doesn ' t
* exist , or is the wrong type ) .
* /
ResourceGatherer . prototype . GetTargetGatherRate = function ( target )
{
2012-08-05 16:01:05 +02:00
var cmpPlayer = QueryOwnerInterface ( this . entity , IID _Player ) ;
2010-07-08 22:08:08 +02:00
var cmpResourceSupply = Engine . QueryInterface ( target , IID _ResourceSupply ) ;
if ( ! cmpResourceSupply )
return 0 ;
var type = cmpResourceSupply . GetType ( ) ;
2012-04-28 21:25:44 +02:00
var rates = this . GetGatherRates ( ) ;
2010-07-08 22:08:08 +02:00
var rate ;
2012-04-28 21:25:44 +02:00
if ( type . specific && rates [ type . generic + "." + type . specific ] )
2012-08-05 17:04:35 +02:00
{
2013-07-02 01:46:03 +02:00
rate = rates [ type . generic + "." + type . specific ] / cmpPlayer . GetCheatTimeMultiplier ( ) ;
2012-08-05 17:04:35 +02:00
}
else if ( type . generic && rates [ type . generic ] )
{
2013-07-02 01:46:03 +02:00
rate = rates [ type . generic ] / cmpPlayer . GetCheatTimeMultiplier ( ) ;
2013-07-30 08:04:53 +02:00
}
2013-08-14 07:14:20 +02:00
// Apply diminishing returns with more gatherers, for e.g. infinite farms. For most resources this has no effect. (GetDiminishingReturns will return null.)
// Note to people looking to change <DiminishingReturns> in a template: This is a bit complicated. Basically, the lower that number is
// the steeper diminishing returns will be. I suggest playing around with Wolfram Alpha or a graphing calculator a bit.
// In each of the following links, replace 0.65 with the gather rate of your worker for the resource with diminishing returns and
// 14 with the constant you wish to use to control the diminishing returns.
// (In this case 0.65 is the women farming rate, in resources/second, and 14 is a good constant for farming.)
// This is the gather rate in resources/second of each individual worker as the total number of workers goes up:
// http://www.wolframalpha.com/input/?i=plot+%281%2F2+cos%28%28x-1%29*pi%2F14%29+%2B+1%2F2%29+*+0.65+from+1+to+5
// This is the total output of the resource in resources/second:
// http://www.wolframalpha.com/input/?i=plot+x%281%2F2+cos%28%28x-1%29*pi%2F14%29+%2B+1%2F2%29+*+0.65+from+1+to+5
// This is the fraction of a worker each new worker is worth (the 5th worker in this example is only producing about half as much as the first one):
// http://www.wolframalpha.com/input/?i=plot+x%281%2F2+cos%28%28x-1%29*pi%2F14%29+%2B+1%2F2%29+-++%28x-1%29%281%2F2+cos%28%28x-2%29*pi%2F14%29+%2B+1%2F2%29+from+x%3D1+to+5+and+y%3D0+to+1
// Here's how this technically works:
// The cosine function is an oscillating curve, normally between -1 and 1. Multiplying by 0.5 squishes that down to
// between -0.5 and 0.5. Adding 0.5 to that changes the range to 0 to 1. The diminishingReturns constant
// adjusts the period of the curve.
// Alternatively, just find scythetwirler (who came up with the math here) or alpha123 (who wrote the code) on IRC.
2013-07-30 08:04:53 +02:00
var diminishingReturns = cmpResourceSupply . GetDiminishingReturns ( ) ;
if ( diminishingReturns )
2013-08-14 07:14:20 +02:00
rate = ( 0.5 * Math . cos ( ( cmpResourceSupply . GetGatherers ( ) . length - 1 ) * Math . PI / diminishingReturns ) + 0.5 ) * rate ;
2013-07-30 08:04:53 +02:00
2013-08-14 07:14:20 +02:00
return rate || 0 ;
2012-04-20 19:21:04 +02:00
} ;
2010-02-12 23:46:53 +01:00
2010-11-13 20:15:29 +01:00
/ * *
* Returns whether this unit can carry more of the given type of resource .
* ( This ignores whether the unit is actually able to gather that
* resource type or not . )
* /
ResourceGatherer . prototype . CanCarryMore = function ( type )
{
var amount = ( this . carrying [ type ] || 0 ) ;
2012-04-20 22:37:12 +02:00
return ( amount < this . GetCapacities ( ) [ type ] ) ;
2010-11-13 20:15:29 +01:00
} ;
/ * *
* Returns whether this unit is carrying any resources of a type that is
* not the requested type . ( This is to support cases where the unit is
* only meant to be able to carry one type at once . )
* /
ResourceGatherer . prototype . IsCarryingAnythingExcept = function ( exceptedType )
{
for ( var type in this . carrying )
if ( type != exceptedType )
return true ;
return false ;
} ;
/ * *
* Transfer our carried resources to our owner immediately .
* Only resources of the given types will be transferred .
* ( This should typically be called after reaching a dropsite ) .
* /
ResourceGatherer . prototype . CommitResources = function ( types )
{
var cmpPlayer = QueryOwnerInterface ( this . entity , IID _Player ) ;
for each ( var type in types )
{
if ( type in this . carrying )
{
cmpPlayer . AddResource ( type , this . carrying [ type ] ) ;
delete this . carrying [ type ] ;
}
}
2013-08-14 07:14:20 +02:00
2012-04-18 13:30:28 +02:00
Engine . PostMessage ( this . entity , MT _ResourceCarryingChanged , { "to" : this . GetCarryingStatus ( ) } ) ;
2010-11-13 20:15:29 +01:00
} ;
/ * *
* Drop all currently - carried resources .
* ( Currently they just vanish after being dropped - we don ' t bother depositing
* them onto the ground . )
* /
ResourceGatherer . prototype . DropResources = function ( )
{
this . carrying = { } ;
2013-08-14 07:14:20 +02:00
2012-04-18 13:30:28 +02:00
Engine . PostMessage ( this . entity , MT _ResourceCarryingChanged , { "to" : this . GetCarryingStatus ( ) } ) ;
2010-11-13 20:15:29 +01:00
} ;
2010-02-12 23:46:53 +01:00
Engine . RegisterComponentType ( IID _ResourceGatherer , "ResourceGatherer" , ResourceGatherer ) ;