Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js
+++ binaries/data/mods/public/globalscripts/Templates.js
@@ -466,6 +466,16 @@
"GainMultiplier": getEntityValue("Trader/GainMultiplier")
};
+ if (template.Upkeep)
+ {
+ ret.upkeep = {
+ "interval": +template.Upkeep.Interval,
+ "rates": {}
+ };
+ for (let type in template.Upkeep.Rates)
+ ret.upkeep.rates[type] = getEntityValue("Upkeep/Rates/" + type);
+ }
+
if (template.WallSet)
{
ret.wallSet = {
Index: binaries/data/mods/public/gui/common/tooltips.js
===================================================================
--- binaries/data/mods/public/gui/common/tooltips.js
+++ binaries/data/mods/public/gui/common/tooltips.js
@@ -702,6 +702,30 @@
});
}
+function getUpkeepTooltip(template)
+{
+ if (!template.upkeep)
+ return "";
+
+ let resCodes = g_ResourceData.GetCodes().filter(res => !!template.upkeep.rates[res]);
+ if (!resCodes.length)
+ return "";
+
+ return sprintf(translate("%(label)s %(details)s"), {
+ "label": headerFont(translate("Upkeep:")),
+ "details": sprintf(translate("%(resources)s / %(time)s"), {
+ "resources":
+ resCodes.map(
+ res => sprintf(translate("%(resourceIcon)s %(rate)s"), {
+ "resourceIcon": resourceIcon(res),
+ "rate": template.upkeep.rates[res]
+ })
+ ).join(" "),
+ "time": getSecondsString(template.upkeep.interval / 1000)
+ })
+ });
+}
+
/**
* Returns an array of strings for a set of wall pieces. If the pieces share
* resource type requirements, output will be of the form '10 to 30 Stone',
Index: binaries/data/mods/public/gui/reference/common/ReferencePage.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/ReferencePage.js
+++ binaries/data/mods/public/gui/reference/common/ReferencePage.js
@@ -63,5 +63,6 @@
getResourceSupplyTooltip,
getPopulationBonusTooltip,
getResourceTrickleTooltip,
+ getUpkeepTooltip,
getLootTooltip
];
Index: binaries/data/mods/public/gui/session/selection_details.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_details.js
+++ binaries/data/mods/public/gui/session/selection_details.js
@@ -320,6 +320,7 @@
getGarrisonTooltip,
getProjectilesTooltip,
getResourceTrickleTooltip,
+ getUpkeepTooltip,
getLootTooltip
].map(func => func(entState)).filter(tip => tip).join("\n");
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -531,6 +531,13 @@
"run": cmpUnitMotion.GetWalkSpeed() * cmpUnitMotion.GetRunMultiplier()
};
+ let cmpUpkeep = Engine.QueryInterface(ent, IID_Upkeep);
+ if (cmpUpkeep)
+ ret.upkeep = {
+ "interval": cmpUpkeep.GetInterval(),
+ "rates": cmpUpkeep.GetRates()
+ };
+
return ret;
};
Index: binaries/data/mods/public/simulation/components/Upkeep.js
===================================================================
--- binaries/data/mods/public/simulation/components/Upkeep.js
+++ binaries/data/mods/public/simulation/components/Upkeep.js
@@ -1,26 +1,32 @@
-function ResourceTrickle() {}
+function Upkeep() {}
-ResourceTrickle.prototype.Schema =
- "Controls the resource trickle ability of the unit." +
- "" +
+Upkeep.prototype.Schema =
+ "Controls the resource upkeep of an entity." +
+ "" +
Resources.BuildSchema("nonNegativeDecimal") +
"" +
- "" +
+ "" +
"" +
"";
-ResourceTrickle.prototype.Init = function()
+Upkeep.prototype.Init = function()
{
- this.trickleInterval = +this.template.Interval;
+ this.upkeepInterval = +this.template.Interval;
this.CheckTimer();
};
-ResourceTrickle.prototype.GetInterval = function()
+/**
+ * @return {number} - The interval between resource subtractions, in ms.
+ */
+Upkeep.prototype.GetInterval = function()
{
- return this.trickleInterval;
+ return this.upkeepInterval;
};
-ResourceTrickle.prototype.GetRates = function()
+/**
+ * @return {Object} - The upkeep rates in the form of { "resourceName": {number} }.
+ */
+Upkeep.prototype.GetRates = function()
{
return this.rates;
};
@@ -28,42 +34,56 @@
/**
* @return {boolean} - Whether this entity has at least one non-zero trickle rate.
*/
-ResourceTrickle.prototype.ComputeRates = function()
+Upkeep.prototype.ComputeRates = function()
{
this.rates = {};
- let hasTrickle = false;
+ let hasUpkeep = false;
for (let resource in this.template.Rates)
{
- let rate = ApplyValueModificationsToEntity("ResourceTrickle/Rates/" + resource, +this.template.Rates[resource], this.entity);
+ let rate = ApplyValueModificationsToEntity("Upkeep/Rates/" + resource, +this.template.Rates[resource], this.entity);
if (rate)
{
this.rates[resource] = rate;
- hasTrickle = true;
+ hasUpkeep = true;
}
}
- return hasTrickle;
+ return hasUpkeep;
};
-ResourceTrickle.prototype.Trickle = function(data, lateness)
+/**
+ * Try to subtract the needed resources.
+ * Data and lateness are unused.
+ */
+Upkeep.prototype.Trickle = function(data, lateness)
{
- // The player entity may also have a ResourceTrickle component
- let cmpPlayer = QueryOwnerInterface(this.entity) || Engine.QueryInterface(this.entity, IID_Player);
+ let cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer)
return;
- cmpPlayer.AddResources(this.rates);
+ if (!cmpPlayer.TrySubtractResources(this.rates))
+ this.HandleInsufficientUpkeep();
};
-ResourceTrickle.prototype.OnValueModification = function(msg)
+/**
+ * E.g. take a hitpoint, reduce CP.
+ */
+Upkeep.prototype.HandleInsufficientUpkeep = function()
+{
+};
+
+Upkeep.prototype.OnValueModification = function(msg)
{
- if (msg.component != "ResourceTrickle")
+ if (msg.component != "Upkeep")
return;
this.CheckTimer();
};
-ResourceTrickle.prototype.CheckTimer = function()
+/**
+ * Recalculate the interval and update the timer accordingly.
+ */
+Upkeep.prototype.CheckTimer = function()
{
if (!this.ComputeRates())
{
@@ -76,9 +96,9 @@
return;
}
- let oldTrickleInterval = this.trickleInterval;
- this.trickleInterval = ApplyValueModificationsToEntity("ResourceTrickle/Interval", +this.template.Interval, this.entity);
- if (this.trickleInterval < 0)
+ let oldUpkeepInterval = this.upkeepInterval;
+ this.upkeepInterval = ApplyValueModificationsToEntity("Upkeep/Interval", +this.template.Interval, this.entity);
+ if (this.upkeepInterval < 0)
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
@@ -88,20 +108,20 @@
if (this.timer)
{
- if (this.trickleInterval == oldTrickleInterval)
+ if (this.upkeepInterval == oldUpkeepInterval)
return;
// If the timer wasn't invalidated before (interval <= 0), just update it.
- if (oldTrickleInterval > 0)
+ if (oldUpkeepInterval > 0)
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- cmpTimer.UpdateRepeatTime(this.timer, this.trickleInterval);
+ cmpTimer.UpdateRepeatTime(this.timer, this.upkeepInterval);
return;
}
}
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- this.timer = cmpTimer.SetInterval(this.entity, IID_ResourceTrickle, "Trickle", this.trickleInterval, this.trickleInterval, undefined);
+ this.timer = cmpTimer.SetInterval(this.entity, IID_Upkeep, "Trickle", this.upkeepInterval, this.upkeepInterval, undefined);
};
-Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle);
+Engine.RegisterComponentType(IID_Upkeep, "Upkeep", Upkeep);
Index: binaries/data/mods/public/simulation/components/interfaces/Upkeep.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Upkeep.js
+++ binaries/data/mods/public/simulation/components/interfaces/Upkeep.js
@@ -1 +1 @@
-Engine.RegisterInterface("ResourceTrickle");
+Engine.RegisterInterface("Upkeep");
Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -35,6 +35,7 @@
Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("interfaces/Upgrade.js");
+Engine.LoadComponentScript("interfaces/Upkeep.js");
Engine.LoadComponentScript("interfaces/BuildingAI.js");
Engine.LoadComponentScript("GuiInterface.js");
Index: binaries/data/mods/public/simulation/components/tests/test_Upkeep.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Upkeep.js
+++ binaries/data/mods/public/simulation/components/tests/test_Upkeep.js
@@ -16,22 +16,23 @@
}
};
-Engine.LoadComponentScript("interfaces/ResourceTrickle.js");
-Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("interfaces/Player.js");
+Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("interfaces/Upkeep.js");
Engine.LoadComponentScript("Player.js");
-Engine.LoadComponentScript("ResourceTrickle.js");
Engine.LoadComponentScript("Timer.js");
+Engine.LoadComponentScript("Upkeep.js");
-// Resource Trickle requires this function to be defined before the component is built.
+// Upkeep 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 testedEnt = 10;
let turnLength = 0.2;
-let playerEnt = 10;
+let playerEnt = 1;
let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer", {});
-let cmpResourceTrickle = ConstructComponent(wonderEnt, "ResourceTrickle", {
+let cmpUpkeep = ConstructComponent(testedEnt, "Upkeep", {
"Interval": "200",
"Rates": {
"food": "0",
@@ -55,123 +56,166 @@
let QueryOwnerInterface = () => cmpPlayer;
Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
+Engine.RegisterGlobal("QueryPlayerIDInterface", () => null);
TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 200);
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
// Since there is no rate > 0, nothing should change.
-TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {});
-TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false);
+TS_ASSERT_UNEVAL_EQUALS(cmpUpkeep.GetRates(), {});
+TS_ASSERT_EQUALS(cmpUpkeep.ComputeRates(), false);
cmpTimer.OnUpdate({ "turnLength": turnLength });
TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
-// Test that only trickling food works.
+// Test that only requiring food works.
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/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 });
+// Calling OnValueModification will reset the timer, which can then be called, thus decreasing the resources of the player.
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_UNEVAL_EQUALS(cmpUpkeep.GetRates(), { "food": 1 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
-TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), true);
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 299, "metal": 300 });
+TS_ASSERT_EQUALS(cmpUpkeep.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);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_UNEVAL_EQUALS(cmpUpkeep.GetRates(), {});
+TS_ASSERT_EQUALS(cmpUpkeep.ComputeRates(), false);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 299, "metal": 300 });
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Interval")
+ if (valueName == "Upkeep/Interval")
return currentValue + 200;
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/Rates/food")
return currentValue + 1;
return currentValue;
};
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
-cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 400);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 400);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 299, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 302, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 298, "metal": 300 });
// Interval becomes a normal timer, thus cancelled after the first execution.
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Interval")
+ if (valueName == "Upkeep/Interval")
return currentValue - 200;
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/Rates/food")
return currentValue + 1;
return currentValue;
};
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
-cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 0);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 0);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 302, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 298, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 303, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 297, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 303, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 297, "metal": 300 });
// Timer became invalidated, check whether it's recreated properly after that.
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Interval")
+ if (valueName == "Upkeep/Interval")
return currentValue - 100;
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/Rates/food")
return currentValue + 1;
return currentValue;
};
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
-cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 100);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 100);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 305, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 295, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 307, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 293, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 291, "metal": 300 });
// Value is now invalid, timer should be cancelled.
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Interval")
+ if (valueName == "Upkeep/Interval")
return currentValue - 201;
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/Rates/food")
return currentValue + 1;
return currentValue;
};
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
-cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), -1);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), -1);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 291, "metal": 300 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 291, "metal": 300 });
// Timer became invalidated, check whether it's recreated properly after that.
ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
- if (valueName == "ResourceTrickle/Rates/food")
+ if (valueName == "Upkeep/Rates/food")
+ return currentValue + 1;
+
+ return currentValue;
+};
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 290, "metal": 300 });
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 289, "metal": 300 });
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 288, "metal": 300 });
+
+// Test multiple upkeep resources.
+ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
+ if (valueName == "Upkeep/Rates/food")
return currentValue + 1;
+ if (valueName == "Upkeep/Rates/metal")
+ return currentValue + 2;
return currentValue;
};
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
-cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
-TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 200);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 287, "metal": 298 });
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 286, "metal": 296 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 310, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 285, "metal": 294 });
+
+// Test we don't go into negative resources.
+let cmpGUI = AddMock(SYSTEM_ENTITY, IID_GuiInterface, {
+ "PushNotification": () => {}
+});
+let notificationSpy = new Spy(cmpGUI, "PushNotification");
+ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
+ if (valueName == "Upkeep/Rates/food")
+ return currentValue + 1;
+
+ return currentValue;
+};
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+cmpUpkeep.OnValueModification({ "component": "Upkeep" });
+TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
+cmpTimer.OnUpdate({ "turnLength": turnLength * 285 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 311, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
+TS_ASSERT_EQUALS(notificationSpy._called, 1);
cmpTimer.OnUpdate({ "turnLength": turnLength });
-TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 312, "metal": 300 });
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
+TS_ASSERT_EQUALS(notificationSpy._called, 2);
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
@@ -135,6 +135,12 @@
140
10000
+
+
+ 10.0
+
+ 1000
+
90