Index: ps/trunk/binaries/data/mods/public/simulation/components/ResourceSupply.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/ResourceSupply.js (revision 22102) +++ ps/trunk/binaries/data/mods/public/simulation/components/ResourceSupply.js (revision 22103) @@ -1,166 +1,185 @@ function ResourceSupply() {} ResourceSupply.prototype.Schema = "Provides a supply of one particular type of resource." + "" + "1000" + "food.meat" + "false" + "25" + "0.8" + "" + "" + "" + "" + "" + "Infinity" + "" + "" + Resources.BuildChoicesSchema(true, true) + "" + "" + "" + "" + "" + "" + "" + "" + ""; ResourceSupply.prototype.Init = function() { // Current resource amount (non-negative) this.amount = this.GetMaxAmount(); // List of IDs for each player this.gatherers = []; - let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers() + let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); for (let i = 0; i < numPlayers; ++i) this.gatherers.push([]); - this.infinite = !isFinite(+this.template.Amount); - let [type, subtype] = this.template.Type.split('.'); this.cachedType = { "generic": type, "specific": subtype }; }; ResourceSupply.prototype.IsInfinite = function() { - return this.infinite; + return !isFinite(+this.template.Amount); }; ResourceSupply.prototype.GetKillBeforeGather = function() { - return (this.template.KillBeforeGather == "true"); + return this.template.KillBeforeGather == "true"; }; ResourceSupply.prototype.GetMaxAmount = function() { return +this.template.Amount; }; ResourceSupply.prototype.GetCurrentAmount = function() { return this.amount; }; ResourceSupply.prototype.GetMaxGatherers = function() { return +this.template.MaxGatherers; }; ResourceSupply.prototype.GetNumGatherers = function() { return this.gatherers.reduce((a, b) => a + b.length, 0); }; -/* The rate of each additionnal gatherer rate follow a geometric sequence, with diminishingReturns as common ratio. */ +/** + * @return {{ "generic": string, "specific": string }} An object containing the subtype and the generic type. All resources must have both. + */ +ResourceSupply.prototype.GetType = function() +{ + return this.cachedType; +}; + +/** + * @param {number} gathererID The gatherer's entity id. + * @param {number} player The gatherer's id. + * @return {boolean} Whether the ResourceSupply can have additional gatherers. + */ +ResourceSupply.prototype.IsAvailable = function(player, gathererID) +{ + return this.GetCurrentAmount() > 0 && (this.GetNumGatherers() < this.GetMaxGatherers() || this.gatherers[player].indexOf(gathererID) != -1); +}; + +/** + * Each additional gatherer decreases the rate following a geometric sequence, with diminishingReturns as ratio. + * @return {number} The diminishing return if any, null otherwise. + */ ResourceSupply.prototype.GetDiminishingReturns = function() { - if ("DiminishingReturns" in this.template) - { - let diminishingReturns = ApplyValueModificationsToEntity("ResourceSupply/DiminishingReturns", +this.template.DiminishingReturns, this.entity); - if (diminishingReturns) - { - let numGatherers = this.GetNumGatherers(); - if (numGatherers > 1) - return diminishingReturns == 1 ? 1 : (1. - Math.pow(diminishingReturns, numGatherers)) / (1. - diminishingReturns) / numGatherers; - } - } + if (!this.template.DiminishingReturns) + return null; + + let diminishingReturns = ApplyValueModificationsToEntity("ResourceSupply/DiminishingReturns", +this.template.DiminishingReturns, this.entity); + if (!diminishingReturns) + return null; + + let numGatherers = this.GetNumGatherers(); + if (numGatherers > 1) + return diminishingReturns == 1 ? 1 : (1 - Math.pow(diminishingReturns, numGatherers)) / (1 - diminishingReturns) / numGatherers; + return null; }; -ResourceSupply.prototype.TakeResources = function(rate) +/** + * @param {number} amount The amount of resources that should be taken from the resource supply. The amount must be positive. + * @return {{ "amount": number, "exhausted": boolean }} The current resource amount in the entity and whether it's exhausted or not. + */ +ResourceSupply.prototype.TakeResources = function(amount) { // Before changing the amount, activate Fogging if necessary to hide changes let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging); if (cmpFogging) cmpFogging.Activate(); - if (this.infinite) - return { "amount": rate, "exhausted": false }; + if (this.IsInfinite()) + return { "amount": amount, "exhausted": false }; - // 'rate' should be a non-negative integer - - var old = this.amount; - this.amount = Math.max(0, old - rate); - var change = old - this.amount; + let oldAmount = this.GetCurrentAmount(); + this.amount = Math.max(0, oldAmount - amount); + let isExhausted = this.GetCurrentAmount() == 0; // Remove entities that have been exhausted - if (this.amount === 0) + if (isExhausted) Engine.DestroyEntity(this.entity); - Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": old, "to": this.amount }); - - return { "amount": change, "exhausted": (this.amount === 0) }; -}; - -ResourceSupply.prototype.GetType = function() -{ - // All resources must have both type and subtype - return this.cachedType; -}; + Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": oldAmount, "to": this.GetCurrentAmount() }); -ResourceSupply.prototype.IsAvailable = function(player, gathererID) -{ - return this.amount > 0 && (this.GetNumGatherers() < this.GetMaxGatherers() || this.gatherers[player].indexOf(gathererID) !== -1); + return { "amount": oldAmount - this.GetCurrentAmount(), "exhausted": isExhausted }; }; +/** + * @param {number} player The gatherer's id. + * @param {number} gathererID The gatherer's player id. + * @returns {boolean} Whether the gatherer was successfully added to the entity's gatherers list. + */ ResourceSupply.prototype.AddGatherer = function(player, gathererID) { if (!this.IsAvailable(player, gathererID)) return false; - if (this.gatherers[player].indexOf(gathererID) === -1) + if (this.gatherers[player].indexOf(gathererID) == -1) { this.gatherers[player].push(gathererID); // broadcast message, mainly useful for the AIs. Engine.PostMessage(this.entity, MT_ResourceSupplyNumGatherersChanged, { "to": this.GetNumGatherers() }); } return true; }; -// should this return false if the gatherer didn't gather from said resource? +/** + * @param {number} gathererID - The gatherer's entity id. + * @param {number} player - The gatherer's player id. + * @todo: Should this return false if the gatherer didn't gather from said resource? + */ ResourceSupply.prototype.RemoveGatherer = function(gathererID, player) { - // this can happen if the unit is dead + // This can happen if the unit is dead if (player == undefined || player == INVALID_PLAYER) { - for (var i = 0; i < this.gatherers.length; ++i) + for (let i = 0; i < this.gatherers.length; ++i) this.RemoveGatherer(gathererID, i); + + return; } - else - { - var index = this.gatherers[player].indexOf(gathererID); - if (index !== -1) - { - this.gatherers[player].splice(index,1); - // broadcast message, mainly useful for the AIs. - Engine.PostMessage(this.entity, MT_ResourceSupplyNumGatherersChanged, { "to": this.GetNumGatherers() }); - return; - } - } + + let index = this.gatherers[player].indexOf(gathererID); + if (index == -1) + return; + + this.gatherers[player].splice(index, 1); + // Broadcast message, mainly useful for the AIs. + Engine.PostMessage(this.entity, MT_ResourceSupplyNumGatherersChanged, { "to": this.GetNumGatherers() }); }; Engine.RegisterComponentType(IID_ResourceSupply, "ResourceSupply", ResourceSupply);