Changeset 25191

Timestamp:
Apr 4, 2021, 8:52:20 AM (3 years ago)
Author:
Freagarach
Message:

Add Upkeep component.

Adds a new component effectively handling negative resource trickles.
Prevents resources from going negative and provides an effect for not being able to pay.
The effect chosen for 0 A.D. is having the entity not being controllable.

Not used in the public mod itself, but it is in quite some mods.

Based on an idea of @Nescio
Differential revision: D1323
Comments by: @Angen, (@smiley,) @Stan

Location:
ps/trunk/binaries/data/mods/public
Files:
6 edited
3 copied

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/public/globalscripts/Templates.js

    r25123 r25191  
    497497        };
    498498
     499
     500
     501
     502
     503
     504
     505
     506
     507
     508
    499509    if (template.WallSet)
    500510    {
  • ps/trunk/binaries/data/mods/public/gui/common/tooltips.js

    r25127 r25191  
    847847}
    848848
     849
     850
     851
     852
     853
     854
     855
     856
     857
     858
     859
     860
     861
     862
     863
     864
     865
     866
     867
     868
     869
     870
     871
     872
    849873/**
    850874 * Returns an array of strings for a set of wall pieces. If the pieces share
  • ps/trunk/binaries/data/mods/public/gui/reference/common/ReferencePage.js

    r25123 r25191  
    6666    getPopulationBonusTooltip,
    6767    getResourceTrickleTooltip,
     68
    6869    getLootTooltip
    6970];
  • ps/trunk/binaries/data/mods/public/gui/session/selection_details.js

    r25123 r25191  
    330330        getProjectilesTooltip,
    331331        getResourceTrickleTooltip,
     332
    332333        getLootTooltip
    333334    ].map(func => func(entState)).filter(tip => tip).join("\n");
  • ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js

    r25123 r25191  
    575575            "walk": cmpUnitMotion.GetWalkSpeed(),
    576576            "run": cmpUnitMotion.GetWalkSpeed() * cmpUnitMotion.GetRunMultiplier()
     577
     578
     579
     580
     581
     582
     583
    577584        };
    578585
  • ps/trunk/binaries/data/mods/public/simulation/components/Upkeep.js

    • Property svn:mime-type set to text/plain
    r25190 r25191  
    1 function ResourceTrickle() {}
     1function () {}
    22
    3 ResourceTrickle.prototype.Schema =
    4     "<a:help>Controls the resource trickle ability of the unit.</a:help>" +
    5     "<element name='Rates' a:help='Trickle Rates'>" +
     3.prototype.Schema =
     4    "<a:help>Controls the resource .</a:help>" +
     5    "<element name='Rates' a:help=' Rates'>" +
    66        Resources.BuildSchema("nonNegativeDecimal") +
    77    "</element>" +
    8     "<element name='Interval' a:help='Number of miliseconds must pass for the player to gain the next trickle.'>" +
     8    "<element name='Interval' a:help='Number of mil.'>" +
    99        "<ref name='nonNegativeDecimal'/>" +
    1010    "</element>";
    1111
    12 ResourceTrickle.prototype.Init = function()
     12.prototype.Init = function()
    1313{
    14     this.trickleInterval = +this.template.Interval;
     14    this.Interval = +this.template.Interval;
    1515    this.CheckTimer();
    1616};
    1717
    18 ResourceTrickle.prototype.GetInterval = function()
     18/**
     19 * @return {number} - The interval between resource subtractions, in ms.
     20 */
     21Upkeep.prototype.GetInterval = function()
    1922{
    20     return this.trickleInterval;
     23    return this.Interval;
    2124};
    2225
    23 ResourceTrickle.prototype.GetRates = function()
     26/**
     27 * @return {Object} - The upkeep rates in the form of { "resourceName": {number} }.
     28 */
     29Upkeep.prototype.GetRates = function()
    2430{
    2531    return this.rates;
     
    2733
    2834/**
    29  * @return {boolean} - Whether this entity has at least one non-zero trickle rate.
     35 * @return {boolean} - Whether this entity has at least one non-zero .
    3036 */
    31 ResourceTrickle.prototype.ComputeRates = function()
     37.prototype.ComputeRates = function()
    3238{
    3339    this.rates = {};
    34     let hasTrickle = false;
     40    let has = false;
    3541    for (let resource in this.template.Rates)
    3642    {
    37         let rate = ApplyValueModificationsToEntity("ResourceTrickle/Rates/" + resource, +this.template.Rates[resource], this.entity);
     43        let rate = ApplyValueModificationsToEntity("/Rates/" + resource, +this.template.Rates[resource], this.entity);
    3844        if (rate)
    3945        {
    4046            this.rates[resource] = rate;
    41             hasTrickle = true;
     47            has = true;
    4248        }
    4349    }
    4450
    45     return hasTrickle;
     51    return has;
    4652};
    4753
    48 ResourceTrickle.prototype.Trickle = function(data, lateness)
     54/**
     55 * Try to subtract the needed resources.
     56 * Data and lateness are unused.
     57 */
     58Upkeep.prototype.Pay = function(data, lateness)
    4959{
    50     // The player entity may also have a ResourceTrickle component
    51     let cmpPlayer = QueryOwnerInterface(this.entity) || Engine.QueryInterface(this.entity, IID_Player);
     60    let cmpPlayer = QueryOwnerInterface(this.entity);
    5261    if (!cmpPlayer)
    5362        return;
    5463
    55     cmpPlayer.AddResources(this.rates);
     64    if (!cmpPlayer.TrySubtractResources(this.rates))
     65        this.HandleInsufficientUpkeep();
     66    else
     67        this.HandleSufficientUpkeep();
    5668};
    5769
    58 ResourceTrickle.prototype.OnValueModification = function(msg)
     70/**
     71 * E.g. take a hitpoint, reduce CP.
     72 */
     73Upkeep.prototype.HandleInsufficientUpkeep = function()
    5974{
    60     if (msg.component != "ResourceTrickle")
     75    if (this.unpayed)
     76        return;
     77
     78    let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     79    if (cmpIdentity)
     80        cmpIdentity.SetControllable(false);
     81    this.unpayed = true;
     82};
     83
     84/**
     85 * Reset to the previous stage.
     86 */
     87Upkeep.prototype.HandleSufficientUpkeep = function()
     88{
     89    if (!this.unpayed)
     90        return;
     91
     92    let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     93    if (cmpIdentity)
     94        cmpIdentity.SetControllable(true);
     95    delete this.unpayed;
     96};
     97
     98Upkeep.prototype.OnValueModification = function(msg)
     99{
     100    if (msg.component != "Upkeep")
    61101        return;
    62102
     
    64104};
    65105
    66 ResourceTrickle.prototype.CheckTimer = function()
     106/**
     107 * Recalculate the interval and update the timer accordingly.
     108 */
     109Upkeep.prototype.CheckTimer = function()
    67110{
    68111    if (!this.ComputeRates())
     
    77120    }
    78121
    79     let oldTrickleInterval = this.trickleInterval;
    80     this.trickleInterval = ApplyValueModificationsToEntity("ResourceTrickle/Interval", +this.template.Interval, this.entity);
    81     if (this.trickleInterval < 0)
     122    let oldInterval;
     123    this./Interval", +this.template.Interval, this.entity);
     124    if (this.Interval < 0)
    82125    {
    83126        let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     
    89132    if (this.timer)
    90133    {
    91         if (this.trickleInterval == oldTrickleInterval)
     134        if (this.Interval)
    92135            return;
    93136
    94137        // If the timer wasn't invalidated before (interval <= 0), just update it.
    95         if (oldTrickleInterval > 0)
     138        if (oldInterval > 0)
    96139        {
    97140            let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    98             cmpTimer.UpdateRepeatTime(this.timer, this.trickleInterval);
     141            cmpTimer.UpdateRepeatTime(this.timer, this.Interval);
    99142            return;
    100143        }
     
    102145
    103146    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    104     this.timer = cmpTimer.SetInterval(this.entity, IID_ResourceTrickle, "Trickle", this.trickleInterval, this.trickleInterval, undefined);
     147    this.timer = cmpTimer.SetInterval(this.entity, IID_Interval, undefined);
    105148};
    106149
    107 Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle);
     150Engine.RegisterComponentType(IID_);
  • ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Upkeep.js

    • Property svn:mime-type set to text/plain
    r25190 r25191  
    1 Engine.RegisterInterface("ResourceTrickle");
     1Engine.RegisterInterface("");
  • ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js

    r25123 r25191  
    4040Engine.LoadComponentScript("interfaces/UnitAI.js");
    4141Engine.LoadComponentScript("interfaces/Upgrade.js");
     42
    4243Engine.LoadComponentScript("interfaces/BuildingAI.js");
    4344Engine.LoadComponentScript("GuiInterface.js");
  • ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Upkeep.js

    • Property svn:mime-type set to text/plain
    r25190 r25191  
    1717};
    1818
    19 Engine.LoadComponentScript("interfaces/ResourceTrickle.js");
     19Engine.LoadComponentScript("interfaces/Player.js");
     20Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
    2021Engine.LoadComponentScript("interfaces/Timer.js");
    21 Engine.LoadComponentScript("interfaces/Player.js");
     22Engine.LoadComponentScript("interfaces/.js");
    2223Engine.LoadComponentScript("Player.js");
    23 Engine.LoadComponentScript("ResourceTrickle.js");
    2424Engine.LoadComponentScript("Timer.js");
    25 
    26 // Resource Trickle requires this function to be defined before the component is built.
     25Engine.LoadComponentScript("Upkeep.js");
     26
     27// Upkeep requires this function to be defined before the component is built.
    2728let ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue;
    2829Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    29 let wonderEnt = 1;
     30let ;
    3031let turnLength = 0.2;
    31 let playerEnt = 10;
     32let playerEnt = 1;
    3233let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer", {});
    3334
    34 let cmpResourceTrickle = ConstructComponent(wonderEnt, "ResourceTrickle", {
     35let cmp", {
    3536    "Interval": "200",
    3637    "Rates": {
     
    5657let QueryOwnerInterface = () => cmpPlayer;
    5758Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
     59
    5860TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
    59 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 200);
     61TS_ASSERT_EQUALS(cmp.GetInterval(), 200);
    6062
    6163// Since there is no rate > 0, nothing should change.
    62 TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {});
    63 TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false);
     64TS_ASSERT_UNEVAL_EQUALS(cmp.GetRates(), {});
     65TS_ASSERT_EQUALS(cmp.ComputeRates(), false);
    6466cmpTimer.OnUpdate({ "turnLength": turnLength });
    6567TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 300, "metal": 300 });
    6668
    67 // Test that only trickling food works.
    68 ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    69     if (valueName == "ResourceTrickle/Rates/food")
    70         return currentValue + 1;
    71 
    72     return currentValue;
    73 };
    74 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    75 // Calling OnValueModification will reset the timer, which can then be called, thus increasing the resources of the player.
    76 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    77 TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), { "food": 1 });
    78 cmpTimer.OnUpdate({ "turnLength": turnLength });
    79 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
    80 TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), true);
    81 
    82 // Reset the trickle modification.
     69// Test that only ing food works.
     70ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
     71    if (valueName == "/Rates/food")
     72        return currentValue + 1;
     73
     74    return currentValue;
     75};
     76Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     77// Calling OnValueModification will reset the timer, which can then be called, thus creasing the resources of the player.
     78cmp" });
     79TS_ASSERT_UNEVAL_EQUALS(cmp.GetRates(), { "food": 1 });
     80cmpTimer.OnUpdate({ "turnLength": turnLength });
     81TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     82TS_ASSERT_EQUALS(cmp.ComputeRates(), true);
     83
     84// Reset the modification.
    8385ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue;
    8486Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    85 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    86 TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {});
    87 TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false);
    88 cmpTimer.OnUpdate({ "turnLength": turnLength });
    89 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
    90 
    91 ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    92     if (valueName == "ResourceTrickle/Interval")
     87cmp" });
     88TS_ASSERT_UNEVAL_EQUALS(cmp.GetRates(), {});
     89TS_ASSERT_EQUALS(cmp.ComputeRates(), false);
     90cmpTimer.OnUpdate({ "turnLength": turnLength });
     91TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     92
     93ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
     94    if (valueName == "/Interval")
    9395        return currentValue + 200;
    94     if (valueName == "ResourceTrickle/Rates/food")
    95         return currentValue + 1;
    96 
    97     return currentValue;
    98 };
    99 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    100 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    101 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 400);
    102 cmpTimer.OnUpdate({ "turnLength": turnLength });
    103 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 });
    104 cmpTimer.OnUpdate({ "turnLength": turnLength });
    105 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 302, "metal": 300 });
     96    if (valueName == "/Rates/food")
     97        return currentValue + 1;
     98
     99    return currentValue;
     100};
     101Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     102cmp" });
     103TS_ASSERT_EQUALS(cmp.GetInterval(), 400);
     104cmpTimer.OnUpdate({ "turnLength": turnLength });
     105TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     106cmpTimer.OnUpdate({ "turnLength": turnLength });
     107TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
    106108
    107109// Interval becomes a normal timer, thus cancelled after the first execution.
    108110ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    109     if (valueName == "ResourceTrickle/Interval")
     111    if (valueName == "/Interval")
    110112        return currentValue - 200;
    111     if (valueName == "ResourceTrickle/Rates/food")
    112         return currentValue + 1;
    113 
    114     return currentValue;
    115 };
    116 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    117 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    118 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 0);
    119 cmpTimer.OnUpdate({ "turnLength": turnLength });
    120 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 302, "metal": 300 });
    121 cmpTimer.OnUpdate({ "turnLength": turnLength });
    122 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 303, "metal": 300 });
    123 cmpTimer.OnUpdate({ "turnLength": turnLength });
    124 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 303, "metal": 300 });
     113    if (valueName == "/Rates/food")
     114        return currentValue + 1;
     115
     116    return currentValue;
     117};
     118Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     119cmp" });
     120TS_ASSERT_EQUALS(cmp.GetInterval(), 0);
     121cmpTimer.OnUpdate({ "turnLength": turnLength });
     122TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     123cmpTimer.OnUpdate({ "turnLength": turnLength });
     124TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     125cmpTimer.OnUpdate({ "turnLength": turnLength });
     126TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
    125127
    126128// Timer became invalidated, check whether it's recreated properly after that.
    127129ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    128     if (valueName == "ResourceTrickle/Interval")
     130    if (valueName == "/Interval")
    129131        return currentValue - 100;
    130     if (valueName == "ResourceTrickle/Rates/food")
    131         return currentValue + 1;
    132 
    133     return currentValue;
    134 };
    135 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    136 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    137 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 100);
    138 cmpTimer.OnUpdate({ "turnLength": turnLength });
    139 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 305, "metal": 300 });
    140 cmpTimer.OnUpdate({ "turnLength": turnLength });
    141 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 307, "metal": 300 });
    142 cmpTimer.OnUpdate({ "turnLength": turnLength });
    143 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
     132    if (valueName == "/Rates/food")
     133        return currentValue + 1;
     134
     135    return currentValue;
     136};
     137Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     138cmp" });
     139TS_ASSERT_EQUALS(cmp.GetInterval(), 100);
     140cmpTimer.OnUpdate({ "turnLength": turnLength });
     141TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 5, "metal": 300 });
     142cmpTimer.OnUpdate({ "turnLength": turnLength });
     143TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     144cmpTimer.OnUpdate({ "turnLength": turnLength });
     145TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
    144146
    145147// Value is now invalid, timer should be cancelled.
    146148ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    147     if (valueName == "ResourceTrickle/Interval")
     149    if (valueName == "/Interval")
    148150        return currentValue - 201;
    149     if (valueName == "ResourceTrickle/Rates/food")
    150         return currentValue + 1;
    151 
    152     return currentValue;
    153 };
    154 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    155 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    156 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), -1);
    157 cmpTimer.OnUpdate({ "turnLength": turnLength });
    158 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
    159 cmpTimer.OnUpdate({ "turnLength": turnLength });
    160 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 });
     151    if (valueName == "/Rates/food")
     152        return currentValue + 1;
     153
     154    return currentValue;
     155};
     156Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     157cmp" });
     158TS_ASSERT_EQUALS(cmp.GetInterval(), -1);
     159cmpTimer.OnUpdate({ "turnLength": turnLength });
     160TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
     161cmpTimer.OnUpdate({ "turnLength": turnLength });
     162TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": , "metal": 300 });
    161163
    162164// Timer became invalidated, check whether it's recreated properly after that.
    163165ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
    164     if (valueName == "ResourceTrickle/Rates/food")
    165         return currentValue + 1;
    166 
    167     return currentValue;
    168 };
    169 Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
    170 cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" });
    171 TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 200);
    172 cmpTimer.OnUpdate({ "turnLength": turnLength });
    173 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 310, "metal": 300 });
    174 cmpTimer.OnUpdate({ "turnLength": turnLength });
    175 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 311, "metal": 300 });
    176 cmpTimer.OnUpdate({ "turnLength": turnLength });
    177 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 312, "metal": 300 });
     166    if (valueName == "Upkeep/Rates/food")
     167        return currentValue + 1;
     168
     169    return currentValue;
     170};
     171Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     172cmpUpkeep.OnValueModification({ "component": "Upkeep" });
     173TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
     174cmpTimer.OnUpdate({ "turnLength": turnLength });
     175TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 290, "metal": 300 });
     176cmpTimer.OnUpdate({ "turnLength": turnLength });
     177TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 289, "metal": 300 });
     178cmpTimer.OnUpdate({ "turnLength": turnLength });
     179TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 288, "metal": 300 });
     180
     181// Test multiple upkeep resources.
     182ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
     183    if (valueName == "Upkeep/Rates/food")
     184        return currentValue + 1;
     185    if (valueName == "Upkeep/Rates/metal")
     186        return currentValue + 2;
     187
     188    return currentValue;
     189};
     190Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     191cmpUpkeep.OnValueModification({ "component": "Upkeep" });
     192TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
     193cmpTimer.OnUpdate({ "turnLength": turnLength });
     194TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 287, "metal": 298 });
     195cmpTimer.OnUpdate({ "turnLength": turnLength });
     196TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 286, "metal": 296 });
     197cmpTimer.OnUpdate({ "turnLength": turnLength });
     198TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 285, "metal": 294 });
     199
     200// Test we don't go into negative resources.
     201let cmpGUI = AddMock(SYSTEM_ENTITY, IID_GuiInterface, {
     202    "PushNotification": () => {}
     203});
     204let notificationSpy = new Spy(cmpGUI, "PushNotification");
     205ApplyValueModificationsToEntity = (valueName, currentValue, entity) => {
     206    if (valueName == "Upkeep/Rates/food")
     207        return currentValue + 1;
     208
     209    return currentValue;
     210};
     211Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
     212cmpUpkeep.OnValueModification({ "component": "Upkeep" });
     213TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200);
     214cmpTimer.OnUpdate({ "turnLength": turnLength * 285 });
     215TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
     216cmpTimer.OnUpdate({ "turnLength": turnLength });
     217TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
     218TS_ASSERT_EQUALS(notificationSpy._called, 1);
     219cmpTimer.OnUpdate({ "turnLength": turnLength });
     220TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 });
     221TS_ASSERT_EQUALS(notificationSpy._called, 2);
Note: See TracChangeset for help on using the changeset viewer.