Index: binaries/data/mods/public/simulation/components/Health.js =================================================================== --- binaries/data/mods/public/simulation/components/Health.js +++ binaries/data/mods/public/simulation/components/Health.js @@ -285,15 +285,23 @@ // persistent corpse retaining the ResourceSupply element of the parent. let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); let templateName = cmpTemplateManager.GetCurrentTemplateName(this.entity); - let corpse; + let entCorpse; if (leaveResources) - corpse = Engine.AddEntity("resource|" + templateName); + { + let cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply); + if (cmpResourceSupply) + { + entCorpse = Engine.AddEntity("resource|" + templateName); + let cmpResourceSupplyCorpse = Engine.QueryInterface(entCorpse, IID_ResourceSupply); + cmpResourceSupplyCorpse.SetAmount(cmpResourceSupply.GetCurrentAmount()); + } + } else - corpse = Engine.AddLocalEntity("corpse|" + templateName); + entCorpse = Engine.AddLocalEntity("corpse|" + templateName); // Copy various parameters so it looks just like us - let cmpCorpsePosition = Engine.QueryInterface(corpse, IID_Position); + let cmpCorpsePosition = Engine.QueryInterface(entCorpse, IID_Position); let pos = cmpPosition.GetPosition(); cmpCorpsePosition.JumpTo(pos.x, pos.z); let rot = cmpPosition.GetRotation(); @@ -301,17 +309,17 @@ cmpCorpsePosition.SetXZRotation(rot.x, rot.z); let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); - let cmpCorpseOwnership = Engine.QueryInterface(corpse, IID_Ownership); + let cmpCorpseOwnership = Engine.QueryInterface(entCorpse, IID_Ownership); cmpCorpseOwnership.SetOwner(cmpOwnership.GetOwner()); let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); - let cmpCorpseVisual = Engine.QueryInterface(corpse, IID_Visual); + let cmpCorpseVisual = Engine.QueryInterface(entCorpse, IID_Visual); cmpCorpseVisual.SetActorSeed(cmpVisual.GetActorSeed()); // Make it fall over cmpCorpseVisual.SelectAnimation("death", true, 1.0); - return corpse; + return entCorpse; }; Health.prototype.CreateDeathSpawnedEntity = function() Index: binaries/data/mods/public/simulation/components/ResourceSupply.js =================================================================== --- binaries/data/mods/public/simulation/components/ResourceSupply.js +++ binaries/data/mods/public/simulation/components/ResourceSupply.js @@ -4,10 +4,24 @@ "Provides a supply of one particular type of resource." + "" + "1000" + + "1500" + "food.meat" + "false" + "25" + "0.8" + + "" + + "" + + "Alive" + + "2" + + "1000" + + "" + + "" + + "Dead" + + "-1" + + "1000" + + "2000" + + "" + + "" + "" + "" + "" + @@ -15,6 +29,11 @@ "" + "Infinity" + "" + + "" + + "" + + "" + + "" + + "" + "" + Resources.BuildChoicesSchema(true, true) + "" + @@ -25,21 +44,61 @@ "" + "" + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "Alive" + + "Dead" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + ""; ResourceSupply.prototype.Init = function() { // Current resource amount (non-negative) - this.amount = this.GetMaxAmount(); + this.amount = +this.template.Amount; + this.maxAmount = this.amount; + if (this.template.MaxAmount && +this.template.MaxAmount > this.amount) + this.maxAmount = +this.template.MaxAmount; + + this.infinite = !isFinite(+this.template.Amount); + + if (this.template.Change && !this.infinite) + for (let changeKey in this.template.Change) + this.AddTimer(changeKey); // 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 }; }; @@ -51,12 +110,12 @@ ResourceSupply.prototype.GetKillBeforeGather = function() { - return (this.template.KillBeforeGather == "true"); + return this.template.KillBeforeGather == "true"; }; ResourceSupply.prototype.GetMaxAmount = function() { - return +this.template.Amount; + return this.maxAmount; }; ResourceSupply.prototype.GetCurrentAmount = function() @@ -84,13 +143,20 @@ { let numGatherers = this.GetNumGatherers(); if (numGatherers > 1) - return diminishingReturns == 1 ? 1 : (1. - Math.pow(diminishingReturns, numGatherers)) / (1. - diminishingReturns) / numGatherers; + return diminishingReturns == 1 ? 1 : (1 - Math.pow(diminishingReturns, numGatherers)) / (1 - diminishingReturns) / numGatherers; } } return null; }; -ResourceSupply.prototype.TakeResources = function(rate) +ResourceSupply.prototype.SetAmount = function(newAmount) +{ + let oldAmount = this.amount; + this.amount = Math.min(Math.max(newAmount, 0), this.GetMaxAmount()); + this.UpdateSupplyStatus(oldAmount); +}; + +ResourceSupply.prototype.TakeResources = function(amount) { // Before changing the amount, activate Fogging if necessary to hide changes let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging); @@ -98,21 +164,12 @@ cmpFogging.Activate(); if (this.infinite) - return { "amount": rate, "exhausted": false }; + 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; - - // Remove entities that have been exhausted - if (this.amount === 0) - Engine.DestroyEntity(this.entity); + let oldAmount = this.amount; + this.SetAmount(oldAmount - amount); - Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": old, "to": this.amount }); - - return { "amount": change, "exhausted": (this.amount === 0) }; + return { "amount": oldAmount - this.amount, "exhausted": this.amount == 0 }; }; ResourceSupply.prototype.GetType = function() @@ -144,23 +201,109 @@ // 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 - if (player == undefined || player == INVALID_PLAYER) + if (player != undefined && player != INVALID_PLAYER) { - for (var i = 0; i < this.gatherers.length; ++i) - this.RemoveGatherer(gathererID, i); - } - else - { - var index = this.gatherers[player].indexOf(gathererID); + let index = this.gatherers[player].indexOf(gathererID); if (index !== -1) { - this.gatherers[player].splice(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; } } + // This can happen if the unit is dead + else + for (let i = 0; i < this.gatherers.length; ++i) + this.RemoveGatherer(gathererID, i); +}; + +/** + * @param {string} changeKey the name of the change to apply to the entity + */ +ResourceSupply.prototype.AddTimer = function(changeKey) +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + if (!this.timers) + this.timers = {}; + + if (!this.timers[changeKey]) + { + let change = this.template.Change[changeKey]; + let interval = ApplyValueModificationsToEntity("ResourceSupply/Change/" + changeKey + "/Interval", +change.Interval, this.entity); + this.timers[changeKey] = cmpTimer.SetInterval(this.entity, IID_ResourceSupply, "RegenSupply", +change.Delay, interval, changeKey); + } +}; + +/** + * @param {string} changeKey the name of the change to apply to the entity + */ +ResourceSupply.prototype.RegenSupply = function(changeKey) +{ + let change = this.template.Change[changeKey]; + let cmpHealth = Engine.QueryInterface(this.entity, IID_Health); + if (change.Constraint && + (change.Constraint == "Alive" && !cmpHealth || + change.Constraint == "Dead" && cmpHealth)) + return; + + let newAmount = ApplyValueModificationsToEntity("ResourceSupply/Change/" + changeKey + "/Value", +change.Value, this.entity); + + if (this.change.Limit) + { + if (newAmount > 0 && this.amount + newAmount > limit) + return; + + if (newAmount < 0 && this.amount - newAmount < limit) + return; + } + + this.SetAmount(this.amount + newAmount); +}; + +ResourceSupply.prototype.UpdateSupplyStatus = function(oldAmount) +{ + // Remove entities that have been exhausted. + if (this.amount == 0) + Engine.DestroyEntity(this.entity); + + // Do not send messages if the resource count didn't change. + if (oldAmount != this.amount) + Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { + "from": oldAmount, + "to": this.amount + }); +}; + +ResourceSupply.prototype.OnDestroy = function() +{ + if (!this.timers) + return; + + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + for (let changeKey in this.timers) + cmpTimer.CancelTimer(this.timers[changeKey]); +}; + +ResourceSupply.prototype.OnValueModification = function(msg) +{ + if (msg.component != "ResourceSupply" || !this.template.Change && this.infinite) + return; + + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + for (let value of msg.valueNames) + { + if (!value) + continue; + + let elements = value.split("/"); + if (elements.length != 4 || elements[3] != "Interval") + continue; + + // Get the third element of the message as it contains the change key. + let changeKey = elements[2]; + cmpTimer.CancelTimer(this.timers[changeKey]); + this.AddTimer(changeKey); + } }; Engine.RegisterComponentType(IID_ResourceSupply, "ResourceSupply", ResourceSupply); Index: binaries/data/mods/public/simulation/templates/special/filter/resource.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/filter/resource.xml +++ binaries/data/mods/public/simulation/templates/special/filter/resource.xml @@ -2,6 +2,7 @@ +