Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/ResourceGatherer.js
Show All 24 Lines | "<element name='BaseSpeed' a:help='Base resource-gathering rate (in resource units per second)'>" + | ||||||||
"<ref name='positiveDecimal'/>" + | "<ref name='positiveDecimal'/>" + | ||||||||
"</element>" + | "</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'>" + | "<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'>" + | ||||||||
Resources.BuildSchema("positiveDecimal", [], true) + | Resources.BuildSchema("positiveDecimal", [], true) + | ||||||||
"</element>" + | "</element>" + | ||||||||
"<element name='Capacities' a:help='Per-resource-type maximum carrying capacity'>" + | "<element name='Capacities' a:help='Per-resource-type maximum carrying capacity'>" + | ||||||||
Resources.BuildSchema("positiveDecimal") + | Resources.BuildSchema("positiveDecimal") + | ||||||||
"</element>"; | "</element>"; | ||||||||
FreagarachAuthorUnsubmitted Done Inline Actions
Freagarach: | |||||||||
/* | |||||||||
* Call interval will be determined by gather rate, | |||||||||
* so always gather integer amount. | |||||||||
*/ | |||||||||
ResourceGatherer.prototype.GATHER_AMOUNT = 1; | |||||||||
ResourceGatherer.prototype.Init = function() | ResourceGatherer.prototype.Init = function() | ||||||||
{ | { | ||||||||
this.capacities = {}; | this.capacities = {}; | ||||||||
this.carrying = {}; // { generic type: integer amount currently carried } | this.carrying = {}; // { generic type: integer amount currently carried } | ||||||||
// (Note that this component supports carrying multiple types of resources, | // (Note that this component supports carrying multiple types of resources, | ||||||||
// each with an independent capacity, but the rest of the game currently | // each with an independent capacity, but the rest of the game currently | ||||||||
// ensures and assumes we'll only be carrying one type at once) | // ensures and assumes we'll only be carrying one type at once) | ||||||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | ResourceGatherer.prototype.GetCapacity = function(resourceType) | ||||||||
if (!this.template.Capacities[resourceType]) | if (!this.template.Capacities[resourceType]) | ||||||||
return 0; | return 0; | ||||||||
return this.capacities[resourceType]; | return this.capacities[resourceType]; | ||||||||
}; | }; | ||||||||
ResourceGatherer.prototype.GetRange = function() | ResourceGatherer.prototype.GetRange = function() | ||||||||
{ | { | ||||||||
return { "max": +this.template.MaxDistance, "min": 0 }; | return { "max": +this.template.MaxDistance, "min": 0 }; | ||||||||
// maybe this should depend on the unit or target or something? | |||||||||
}; | }; | ||||||||
/** | /** | ||||||||
* Gather from the target entity. This should only be called after a successful range check, | * @param {number} target - The target to gather from. | ||||||||
Done Inline ActionsYou don't need this I think, since it's already checked in the only place it's called from. Stan: You don't need this I think, since it's already checked in the only place it's called from. | |||||||||
Done Inline ActionsI added this here as a safeguard to modders. Freagarach: I added this here as a safeguard to modders. | |||||||||
* and if the target has a compatible ResourceSupply. | * @param {number} callerIID - The IID to notify on specific events. | ||||||||
* Call interval will be determined by gather rate, so always gather 1 amount when called. | * @return {boolean} - Whether we started gathering. | ||||||||
*/ | */ | ||||||||
ResourceGatherer.prototype.PerformGather = function(target) | ResourceGatherer.prototype.StartGathering = function(target, callerIID) | ||||||||
{ | { | ||||||||
if (!this.GetTargetGatherRate(target)) | if (this.target) | ||||||||
return { "exhausted": true }; | this.StopGathering(); | ||||||||
let gatherAmount = 1; | let rate = this.GetTargetGatherRate(target); | ||||||||
if (!rate) | |||||||||
return false; | |||||||||
let cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply); | let cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply); | ||||||||
if (!cmpResourceSupply || !cmpResourceSupply.AddActiveGatherer(this.entity)) | |||||||||
return false; | |||||||||
let genericResourceType = cmpResourceSupply.GetType().generic; | |||||||||
this.AddToPlayerCounter(genericResourceType); | |||||||||
// If we've already got some resources but they're the wrong type, | |||||||||
// drop them first to ensure we're only ever carrying one type. | |||||||||
if (this.IsCarryingAnythingExcept(genericResourceType)) | |||||||||
this.DropResources(); | |||||||||
// Calculate timing based on gather rates. | |||||||||
// This allows the gather rate to control how often we gather, instead of how much. | |||||||||
let timing = 1000 / rate; | |||||||||
this.target = target; | |||||||||
this.callerIID = callerIID; | |||||||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||||||
this.timer = cmpTimer.SetInterval(this.entity, IID_ResourceGatherer, "PerformGather", timing, timing, null); | |||||||||
return true; | |||||||||
}; | |||||||||
/** | |||||||||
* @param {string} reason - The reason why we stopped gathering used to notify the caller. | |||||||||
*/ | |||||||||
ResourceGatherer.prototype.StopGathering = function(reason) | |||||||||
{ | |||||||||
if (this.timer) | |||||||||
{ | |||||||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||||||
cmpTimer.CancelTimer(this.timer); | |||||||||
delete this.timer; | |||||||||
} | |||||||||
if (this.target) | |||||||||
{ | |||||||||
let cmpResourceSupply = Engine.QueryInterface(this.target, IID_ResourceSupply); | |||||||||
if (cmpResourceSupply) | |||||||||
cmpResourceSupply.RemoveGatherer(this.entity); | |||||||||
this.RemoveFromPlayerCounter(); | |||||||||
delete this.target; | |||||||||
} | |||||||||
// The callerIID component may start gathering again, | |||||||||
// replacing the callerIID, hence save that. | |||||||||
let callerIID = this.callerIID; | |||||||||
delete this.callerIID; | |||||||||
if (reason && callerIID) | |||||||||
{ | |||||||||
let component = Engine.QueryInterface(this.entity, callerIID); | |||||||||
if (component) | |||||||||
component.ProcessMessage(reason, null); | |||||||||
} | |||||||||
}; | |||||||||
/** | |||||||||
* Gather from our target entity. | |||||||||
* @params - data and lateness are unused. | |||||||||
*/ | |||||||||
ResourceGatherer.prototype.PerformGather = function(data, lateness) | |||||||||
{ | |||||||||
let cmpResourceSupply = Engine.QueryInterface(this.target, IID_ResourceSupply); | |||||||||
if (!cmpResourceSupply || cmpResourceSupply.GetCurrentAmount() <= 0) | |||||||||
{ | |||||||||
this.StopGathering("TargetInvalidated"); | |||||||||
return; | |||||||||
} | |||||||||
if (!this.IsTargetInRange(this.target)) | |||||||||
{ | |||||||||
this.StopGathering("OutOfRange"); | |||||||||
return; | |||||||||
} | |||||||||
let type = cmpResourceSupply.GetType(); | let type = cmpResourceSupply.GetType(); | ||||||||
// Initialise the carried count if necessary | |||||||||
if (!this.carrying[type.generic]) | if (!this.carrying[type.generic]) | ||||||||
this.carrying[type.generic] = 0; | this.carrying[type.generic] = 0; | ||||||||
// Find the maximum so we won't exceed our capacity | |||||||||
let maxGathered = this.GetCapacity(type.generic) - this.carrying[type.generic]; | let maxGathered = this.GetCapacity(type.generic) - this.carrying[type.generic]; | ||||||||
let status = cmpResourceSupply.TakeResources(Math.min(this.GATHER_AMOUNT, maxGathered)); | |||||||||
let status = cmpResourceSupply.TakeResources(Math.min(gatherAmount, maxGathered)); | |||||||||
this.carrying[type.generic] += status.amount; | this.carrying[type.generic] += status.amount; | ||||||||
this.lastCarriedType = type; | this.lastCarriedType = type; | ||||||||
// Update stats of how much the player collected. | // Update stats of how much the player collected. | ||||||||
// (We have to do it here rather than at the dropsite, because we | // (We have to do it here rather than at the dropsite, because we | ||||||||
Done Inline ActionsAdd the (not used) data and lateness parameters? Freagarach: Add the (not used) `data` and `lateness` parameters? | |||||||||
Done Inline ActionsOnly a subset of this function is needed. Freagarach: Only a subset of this function is needed. | |||||||||
// need to know what subtype it was) | // need to know what subtype it was.) | ||||||||
let cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); | let cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); | ||||||||
if (cmpStatisticsTracker) | if (cmpStatisticsTracker) | ||||||||
cmpStatisticsTracker.IncreaseResourceGatheredCounter(type.generic, status.amount, type.specific); | cmpStatisticsTracker.IncreaseResourceGatheredCounter(type.generic, status.amount, type.specific); | ||||||||
Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() }); | Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() }); | ||||||||
if (!this.CanCarryMore(type.generic)) | |||||||||
return { | this.StopGathering("InventoryFilled"); | ||||||||
Done Inline ActionsYou check it twice. Stan: You check it twice. | |||||||||
"amount": status.amount, | else if (status.exhausted) | ||||||||
"exhausted": status.exhausted, | this.StopGathering("TargetInvalidated"); | ||||||||
"filled": this.carrying[type.generic] >= this.GetCapacity(type.generic) | |||||||||
}; | |||||||||
}; | }; | ||||||||
/** | /** | ||||||||
* Compute the amount of resources collected per second from the target. | * Compute the amount of resources collected per second from the target. | ||||||||
* Returns 0 if resources cannot be collected (e.g. the target doesn't | * Returns 0 if resources cannot be collected (e.g. the target doesn't | ||||||||
* exist, or is the wrong type). | * exist, or is the wrong type). | ||||||||
*/ | */ | ||||||||
ResourceGatherer.prototype.GetTargetGatherRate = function(target) | ResourceGatherer.prototype.GetTargetGatherRate = function(target) | ||||||||
{ | { | ||||||||
let cmpResourceSupply = QueryMiragedInterface(target, IID_ResourceSupply); | let cmpResourceSupply = QueryMiragedInterface(target, IID_ResourceSupply); | ||||||||
if (!cmpResourceSupply) | if (!cmpResourceSupply || cmpResourceSupply.GetCurrentAmount() <= 0) | ||||||||
return 0; | return 0; | ||||||||
let type = cmpResourceSupply.GetType(); | let type = cmpResourceSupply.GetType(); | ||||||||
let rate = 0; | let rate = 0; | ||||||||
if (type.specific) | if (type.specific) | ||||||||
rate = this.GetGatherRate(type.generic+"."+type.specific); | rate = this.GetGatherRate(type.generic + "." + type.specific); | ||||||||
if (rate == 0 && type.generic) | if (rate == 0 && type.generic) | ||||||||
rate = this.GetGatherRate(type.generic); | rate = this.GetGatherRate(type.generic); | ||||||||
let diminishingReturns = cmpResourceSupply.GetDiminishingReturns(); | let diminishingReturns = cmpResourceSupply.GetDiminishingReturns(); | ||||||||
if (diminishingReturns) | if (diminishingReturns) | ||||||||
rate *= diminishingReturns; | rate *= diminishingReturns; | ||||||||
return rate; | return rate; | ||||||||
}; | }; | ||||||||
/** | /** | ||||||||
* @param {number} target - The entity ID of the target to check. | |||||||||
* @return {boolean} - Whether we can gather from the target. | |||||||||
*/ | |||||||||
ResourceGatherer.prototype.CanGather = function(target) | |||||||||
{ | |||||||||
return this.GetTargetGatherRate(target) > 0; | |||||||||
}; | |||||||||
/** | |||||||||
* Returns whether this unit can carry more of the given type of resource. | * Returns whether this unit can carry more of the given type of resource. | ||||||||
* (This ignores whether the unit is actually able to gather that | * (This ignores whether the unit is actually able to gather that | ||||||||
* resource type or not.) | * resource type or not.) | ||||||||
*/ | */ | ||||||||
ResourceGatherer.prototype.CanCarryMore = function(type) | ResourceGatherer.prototype.CanCarryMore = function(type) | ||||||||
{ | { | ||||||||
let amount = this.carrying[type] || 0; | let amount = this.carrying[type] || 0; | ||||||||
return amount < this.GetCapacity(type); | return amount < this.GetCapacity(type); | ||||||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | let cmpPlayer = playerid != undefined ? | ||||||||
QueryOwnerInterface(this.entity, IID_Player); | QueryOwnerInterface(this.entity, IID_Player); | ||||||||
if (cmpPlayer) | if (cmpPlayer) | ||||||||
cmpPlayer.RemoveResourceGatherer(this.lastGathered); | cmpPlayer.RemoveResourceGatherer(this.lastGathered); | ||||||||
delete this.lastGathered; | delete this.lastGathered; | ||||||||
}; | }; | ||||||||
/** | |||||||||
* @param {number} - The entity ID of the target to check. | |||||||||
* @return {boolean} - Whether this entity is in range of its target. | |||||||||
*/ | |||||||||
ResourceGatherer.prototype.IsTargetInRange = function(target) | |||||||||
{ | |||||||||
return Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager). | |||||||||
IsInTargetRange(this.entity, target, 0, +this.template.MaxDistance, false); | |||||||||
}; | |||||||||
// Since we cache gather rates, we need to make sure we update them when tech changes. | // Since we cache gather rates, we need to make sure we update them when tech changes. | ||||||||
// and when our owner change because owners can had different techs. | // and when our owner change because owners can had different techs. | ||||||||
ResourceGatherer.prototype.OnValueModification = function(msg) | ResourceGatherer.prototype.OnValueModification = function(msg) | ||||||||
{ | { | ||||||||
if (msg.component != "ResourceGatherer") | if (msg.component != "ResourceGatherer") | ||||||||
return; | return; | ||||||||
// NB: at the moment, 0 A.D. always uses the fast path, the other is mod support. | // NB: at the moment, 0 A.D. always uses the fast path, the other is mod support. | ||||||||
Show All 40 Lines |
Wildfire Games · Phabricator