Index: binaries/data/mods/public/simulation/components/ResourceTrickle.js
===================================================================
--- binaries/data/mods/public/simulation/components/ResourceTrickle.js
+++ binaries/data/mods/public/simulation/components/ResourceTrickle.js
@@ -11,10 +11,7 @@
ResourceTrickle.prototype.Init = function()
{
- this.ComputeRates();
-
- let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- cmpTimer.SetInterval(this.entity, IID_ResourceTrickle, "Trickle", this.GetTimer(), this.GetTimer(), undefined);
+ this.CheckTimer();
};
ResourceTrickle.prototype.GetTimer = function()
@@ -27,11 +24,24 @@
return this.rates;
};
+/**
+ * @return {boolean} - Whether this entity at least one non-zero trickle rate.
+ */
ResourceTrickle.prototype.ComputeRates = function()
{
this.rates = {};
+ let hasTrickle = false;
for (let resource in this.template.Rates)
- this.rates[resource] = ApplyValueModificationsToEntity("ResourceTrickle/Rates/"+resource, +this.template.Rates[resource], this.entity);
+ {
+ let rate = ApplyValueModificationsToEntity("ResourceTrickle/Rates/" + resource, +this.template.Rates[resource], this.entity);
+ if (rate == 0)
+ continue;
+
+ this.rates[resource] = rate;
+ hasTrickle = true;
+ }
+
+ return hasTrickle;
};
ResourceTrickle.prototype.Trickle = function(data, lateness)
@@ -49,7 +59,28 @@
if (msg.component != "ResourceTrickle")
return;
- this.ComputeRates();
+ this.CheckTimer();
+};
+
+ResourceTrickle.prototype.CheckTimer = function()
+{
+ if (!this.ComputeRates())
+ {
+ if (this.timer)
+ {
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.timer);
+ delete this.timer;
+ }
+ return;
+ }
+
+ if (this.timer)
+ return;
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ let interval = +this.template.Interval;
+ this.timer = cmpTimer.SetInterval(this.entity, IID_ResourceTrickle, "Trickle", interval, interval, undefined);
};
Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle);
Index: binaries/data/mods/public/simulation/components/tests/test_ResourceTrickle.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/tests/test_ResourceTrickle.js
@@ -0,0 +1,90 @@
+Resources = {
+ "GetCodes": () => ["food", "metal"],
+ "GetTradableCodes": () => ["food", "metal"],
+ "GetBarterableCodes": () => ["food", "metal"],
+ "GetResource": () => ({}),
+ "BuildSchema": (type) => {
+ let schema = "";
+ for (let res of Resources.GetCodes())
+ schema +=
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+ return "" + schema + "";
+ }
+};
+
+Engine.LoadComponentScript("interfaces/ResourceTrickle.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("interfaces/Player.js");
+Engine.LoadComponentScript("Player.js");
+Engine.LoadComponentScript("ResourceTrickle.js");
+Engine.LoadComponentScript("Timer.js");
+
+// Resource Trickle requires this function to be defined before the component is built.
+let ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue;
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+let wonderEnt = 1;
+let turnLength = 0.2;
+let playerEnt = 10;
+let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer", {});
+
+let cmpResourceTrickle = ConstructComponent(wonderEnt, "ResourceTrickle", {
+ "Interval": "200",
+ "Rates": {
+ "food": "0.0",
+ "metal": "0.0"
+ }
+});
+
+let cmpPlayer = ConstructComponent(playerEnt, "Player", {
+ "SpyCostMultiplier": 1,
+ "BarterMultiplier": {
+ "Buy": {
+ "food": 1.0,
+ "metal": 1.0
+ },
+ "Sell": {
+ "food": 1.0,
+ "metal": 1.0
+ }
+ },
+});
+
+let QueryOwnerInterface = () => cmpPlayer;
+Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
+TS_ASSERT_EQUALS(cmpResourceTrickle.GetTimer(), 200);
+
+// Since there is no rate > 0, nothing should change.
+TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {});
+TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false);
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
+
+// Test that only trickling food works.
+ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
+ if (valueName == "ResourceTrickle/Rates/food")
+ return currentValue + 1;
+
+ return currentValue;
+};
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+// Calling OnValueModification will reset the timer, which can then be called, thus increasing the resources of the player.
+cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
+TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), { "food": 1 });
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
+TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), true);
+
+// Reset the trickle modification.
+ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue;
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
+TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {});
+TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false);
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
+