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,58 @@ "" + "" + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "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 key in this.template.Change) + this.AddRegenTimer(key); // 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 +107,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 +140,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 +161,12 @@ cmpFogging.Activate(); if (this.infinite) - return { "amount": rate, "exhausted": false }; + return { "amount": amount, "exhausted": false }; - // 'rate' should be a non-negative integer + let oldAmount = this.amount; + this.SetAmount(oldAmount - amount); - 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); - - 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 +198,89 @@ // 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) - { - for (var i = 0; i < this.gatherers.length; ++i) - this.RemoveGatherer(gathererID, i); - } - else + if (player != undefined && player != INVALID_PLAYER) { - 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); +}; + +ResourceSupply.prototype.AddRegenTimer = function(key) +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + if (!this.regenTimers) + this.regenTimers = {}; + + if (!this.regenTimers[key]) + { + let change = this.template.Change[key]; + let changeWithName = { "Name" : key} + for (let attribute in change) + changeWithName[attribute] = change[attribute]; + + let interval = ApplyValueModificationsToEntity("ResourceSupply/Change/" + key + "/Interval", +change.Interval, this.entity); + this.regenTimers[key] = cmpTimer.SetInterval(this.entity, IID_ResourceSupply, "RegenSupply", change.Delay ? +change.Delay : 0, interval, changeWithName); + } +}; + +ResourceSupply.prototype.RegenSupply = function(change) +{ + let cmpHealth = Engine.QueryInterface(this.entity, IID_Health); + if (change.Constraint && + (change.Constraint == "Alive" && !cmpHealth || + change.Constraint == "Dead" && cmpHealth)) + return; + + this.SetAmount(this.amount + ApplyValueModificationsToEntity("ResourceSupply/Change/" + change.Name + "/Value", +change.Value, this.entity)); +}; + +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.regenTimers) + return; + + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + for (let key in this.regenTimers) + cmpTimer.CancelTimer(this.regenTimers[key]); +}; + +ResourceSupply.prototype.OnValueModification = function(msg) +{ + // Only cancel timers if the interval was changed. + if (msg.component != "ResourceSupply" || !this.template.Change || !msg.valueNames.some(a => a.contains("Interval"))) + return; + + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + for (let value of msg.valueNames) + { + // Get the third element of the message as it contains the change key. + let key = value.split("/")[2]; + cmpTimer.CancelTimer(this.regenTimers[key]); + this.AddRegenTimer(key); + } }; 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 @@ +