From 3d9d2344d37cd06491c6b776b1da25f6ae9d083d Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Fri, 17 Nov 2023 17:45:31 +0100 Subject: [PATCH 1/4] fix: Execution continues after callback This causes both the success and then the failure cases to be called. --- js/religion.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/religion.js b/js/religion.js index 68c87f2be4..e9c67f6458 100644 --- a/js/religion.js +++ b/js/religion.js @@ -1375,6 +1375,7 @@ dojo.declare("classes.ui.religion.TransformBtnController", com.nuclearunicorn.ga var batchSize = event.shiftKey ? 10000 : event.ctrlKey || event.metaKey ? this.game.opts.batchSize : 1; callback(this._transform(model, batchSize)); + return; } callback(false); }, @@ -1387,6 +1388,7 @@ dojo.declare("classes.ui.religion.TransformBtnController", com.nuclearunicorn.ga var amt = Math.floor(this._canAfford(model) / divider); if (model.enabled && amt >= 1) { callback(this._transform(model, amt)); + return; } callback(false); }, @@ -1487,6 +1489,7 @@ dojo.declare("classes.ui.religion.RefineTearsBtnController", com.nuclearunicorn. } callback(true); + return; } callback(false); }, From 4d6701c72bf16b6d19e6f03d803ce624fcfd11a2 Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Sat, 18 Nov 2023 12:22:02 +0100 Subject: [PATCH 2/4] ci: Only deploy branches, not PRs --- .github/workflows/main.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 769aa0e77a..a1a0441e56 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ on: - dev/alpha - dev/beta pull_request: - branches: + branches: - master - dev/alpha - dev/beta @@ -21,10 +21,10 @@ jobs: uses: DerYeger/yarn-setup-action@master with: node-version: 16 - + - name: Install dependencies run: yarn - + - name: Lint run: yarn lint @@ -33,6 +33,7 @@ jobs: deploy: runs-on: ubuntu-latest + if: github.event_name == 'push' needs: build steps: - name: executing remote ssh commands using password @@ -42,5 +43,3 @@ jobs: host: ${{ secrets.KG_SSH_HOST }} key: ${{ secrets.KG_SSH_KEY }} script: /var/www/kittensgame.com/html/deploy.sh - - From d332e778bef04ed7ac04b4c712e7fecf01970337 Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Sat, 18 Nov 2023 12:25:50 +0100 Subject: [PATCH 3/4] ci: Use regular `actions/setup-node` with latest LTS+Corepack --- .github/workflows/main.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1a0441e56..63ab578b20 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: KG Deploy code +name: KG Test/Deploy Code on: push: branches: @@ -17,13 +17,16 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Yarn setup - uses: DerYeger/yarn-setup-action@master + - name: NodeJS setup + uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 20 + + - name: Enable Corepack + run: corepack enable - name: Install dependencies - run: yarn + run: yarn install --frozen-lockfile - name: Lint run: yarn lint @@ -36,7 +39,7 @@ jobs: if: github.event_name == 'push' needs: build steps: - - name: executing remote ssh commands using password + - name: Executing remote SSH commands using password uses: appleboy/ssh-action@v1.0.0 with: username: ${{ secrets.KG_SSH_USER }} From 53ef409c7560f8a0403a8860124c24e6e42b23f8 Mon Sep 17 00:00:00 2001 From: Brent-Call Date: Sat, 18 Nov 2023 15:10:05 -0500 Subject: [PATCH 4/4] Change signature of callback function to be backwards-compatible --- core.js | 42 ++++++++++-------- js/buildings.js | 2 +- js/challenges.js | 2 +- js/prestige.js | 2 +- js/religion.js | 28 ++++++------ js/science.js | 27 ++++++------ js/space.js | 2 +- js/time.js | 29 +++++++------ js/workshop.js | 4 +- test/game.test.js | 108 +++++++++++++++++++++++++++++----------------- 10 files changed, 141 insertions(+), 105 deletions(-) diff --git a/core.js b/core.js index 8d8a9fdcf2..7b51eb0463 100644 --- a/core.js +++ b/core.js @@ -719,8 +719,8 @@ dojo.declare("com.nuclearunicorn.game.ui.ButtonController", null, { * @param event (OPTIONAL) Object. If given, contains information such as whether the shift key was pressed. This is * used by buildings to determine if we're buying 1, buying 10, buying all, etc. If null, uses default behavior. * @param callback Function. This is how we communicate the results of trying to buy the item. The callback function is called - * with a single parameter, an object containing two fields: - * itemBought Boolean. True if one or more items were bought, false if nothing happened. + * with two parameters. The first is itemBought, a Boolean encoding whether one or more items were bought. + * The second is an object containing the following field: * reason String. Used as an enumeration encoding *why* the result happened the way it did. * If itemBought is true, can be one of the following values: * "paid-for" We met the requirements & bought the item legitimately. @@ -741,11 +741,11 @@ dojo.declare("com.nuclearunicorn.game.ui.ButtonController", null, { */ buyItem: function(model, event, callback){ if (!this.hasResources(model)) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, {reason: "cannot-afford" }); return; } if (!model.enabled) { - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, {reason: "not-enabled" }); return; } //Else, we meet all requirements to buy this item: @@ -758,7 +758,7 @@ dojo.declare("com.nuclearunicorn.game.ui.ButtonController", null, { } //A lot of normal UI buttons that don't involve building things use this method, so check if things are free: - callback({ itemBought: true, reason: ((model.prices && model.prices.length) ? "paid-for" : "item-is-free") }); + callback(true /*itemBought*/, {reason: ((model.prices && model.prices.length) ? "paid-for" : "item-is-free") }); }, refund: function(model){ @@ -939,8 +939,12 @@ dojo.declare("com.nuclearunicorn.game.ui.Button", com.nuclearunicorn.core.Contro onClick: function(event){ this.animate(); var self = this; - this.controller.buyItem(this.model, event, function(result) { - if (typeof(result) == "object" && result.itemBought) { + this.controller.buyItem(this.model, event, function(itemBought, extendedInfo) { + if (typeof(itemBought) !== "boolean" || typeof(extendedInfo) !== "object") { + console.error("Invalid arguments passed to callback function."); + return; + } + if (itemBought) { self.update(); } }); @@ -2000,7 +2004,7 @@ dojo.declare("com.nuclearunicorn.game.ui.BuildingStackableBtnController", com.nu buyItem: function(model, event, callback) { var isInDevMode = this.game.devMode; if (!this.hasResources(model) && !isInDevMode) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, {reason: "cannot-afford" }); return; } if (!model.enabled && !isInDevMode) { @@ -2013,7 +2017,7 @@ dojo.declare("com.nuclearunicorn.game.ui.BuildingStackableBtnController", com.nu whyArentWeEnabled = "already-bought"; } - callback({ itemBought: false, reason: whyArentWeEnabled }); + callback(false /*itemBought*/, {reason: whyArentWeEnabled }); return; } //Else, we meet all the requirements for being able to buy this item: @@ -2024,7 +2028,7 @@ dojo.declare("com.nuclearunicorn.game.ui.BuildingStackableBtnController", com.nu this.game.ui.confirm("", $I("iron.will.break.confirmation.msg"), function() { self._buyItem_step2(model, event, callback); }, function() { - callback({ itemBought: false, reason: "player-denied" /*The player decided not to buy this after all.*/ }); + callback(false /*itemBought*/, {reason: "player-denied" /*The player decided not to buy this after all.*/ }); }); } else { this._buyItem_step2(model, event, callback); @@ -2034,29 +2038,29 @@ dojo.declare("com.nuclearunicorn.game.ui.BuildingStackableBtnController", com.nu _buyItem_step2: function(model, event, callback) { if (!event) { event = {}; /*event is an optional parameter*/ } //This is what we pass to the callback function if we succeed: - var resultIfBuySuccessful = { itemBought: true, reason: (this.game.devMode ? "dev-mode" : "paid-for") }; + var resultIfBuySuccessful = { reason: (this.game.devMode ? "dev-mode" : "paid-for") }; var meta = model.metadata; if (!meta.noStackable && event.shiftKey) { var maxBld = 10000; if (this.game.opts.noConfirm) { this.build(model, maxBld); - callback(resultIfBuySuccessful); + callback(true /*itemBought*/, resultIfBuySuccessful); } else { var self = this; this.game.ui.confirm($I("construct.all.confirmation.title"), $I("construct.all.confirmation.msg"), function() { self.build(model, maxBld); - callback(resultIfBuySuccessful); + callback(true /*itemBought*/, resultIfBuySuccessful); }, function() { - callback({ itemBought: false, reason: "player-denied" /*The player decided not to buy this after all.*/}); + callback(false /*itemBought*/, {reason: "player-denied" /*The player decided not to buy this after all.*/}); }); } } else if (!meta.noStackable && (event.ctrlKey || event.metaKey /*osx tears*/)) { this.build(model, this.game.opts.batchSize || 10); - callback(resultIfBuySuccessful); + callback(true /*itemBought*/, resultIfBuySuccessful); } else { this.build(model, 1); - callback(resultIfBuySuccessful); + callback(true /*itemBought*/, resultIfBuySuccessful); } }, @@ -2185,18 +2189,18 @@ dojo.declare("com.nuclearunicorn.game.ui.BuildingNotStackableBtnController", com buyItem: function(model, event, callback) { var isInDevMode = this.game.devMode; if (model.metadata.researched && !isInDevMode) { - callback({ itemBought: false, reason: "already-bought" }); + callback(false /*itemBought*/, {reason: "already-bought" }); return; } if (!this.hasResources(model) && !isInDevMode) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, {reason: "cannot-afford" }); return; } //Else, we can buy it: this.payPrice(model); this.onPurchase(model); - callback({ itemBought: true, reason: (isInDevMode ? "dev-mode" : "paid-for") }); + callback(true /*itemBought*/, {reason: (isInDevMode ? "dev-mode" : "paid-for") }); this.game.render(); return; }, diff --git a/js/buildings.js b/js/buildings.js index 5dd15abaa2..5be965f700 100644 --- a/js/buildings.js +++ b/js/buildings.js @@ -2764,7 +2764,7 @@ dojo.declare("classes.game.ui.GatherCatnipButtonController", com.nuclearunicorn. } this.game.bld.gatherCatnip(); - callback({ itemBought: true, reason: "item-is-free" /*It costs no resources to gather catnip, so we can't fail to buy it*/}); + callback(true /*itemBought*/, {reason: "item-is-free" /*It costs no resources to gather catnip, so we can't fail to buy it*/}); } }); diff --git a/js/challenges.js b/js/challenges.js index a1ffc502bb..892ac33972 100644 --- a/js/challenges.js +++ b/js/challenges.js @@ -714,7 +714,7 @@ dojo.declare("classes.ui.ChallengeBtnController", com.nuclearunicorn.game.ui.Bui buyItem: function(model, event, callback) { this.togglePending(model); - callback({ itemBought: true, reason: "item-is-free" /*We just toggled the pending state; simple, really*/}); + callback(true /*itemBought*/, {reason: "item-is-free" /*We just toggled the pending state; simple, really*/}); }, togglePending: function(model){ diff --git a/js/prestige.js b/js/prestige.js index 480e737b00..c28d6fe599 100644 --- a/js/prestige.js +++ b/js/prestige.js @@ -543,7 +543,7 @@ dojo.declare("classes.ui.PrestigeBtnController", com.nuclearunicorn.game.ui.Buil if (this.game.science.get("metaphysics").researched) { this.inherited(arguments); } else { - callback({ itemBought: false, reason: "not-unlocked" }); + callback(false /*itemBought*/, { reason: "not-unlocked" }); } }, diff --git a/js/religion.js b/js/religion.js index ff61f75be9..d6432093ca 100644 --- a/js/religion.js +++ b/js/religion.js @@ -1372,13 +1372,13 @@ dojo.declare("classes.ui.religion.TransformBtnController", com.nuclearunicorn.ga buyItem: function(model, event, callback) { if (!this.hasResources(model)) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } if (!model.enabled) { //As far as I can tell, this shouldn't ever happen because being //unable to afford it is the only reason for it to be disabled. - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, { reason: "not-enabled" }); return; } if (!event) { event = {}; /*event is an optional parameter*/ } @@ -1386,10 +1386,10 @@ dojo.declare("classes.ui.religion.TransformBtnController", com.nuclearunicorn.ga event.ctrlKey || event.metaKey ? this.game.opts.batchSize : 1; var didWeSucceed = this._transform(model, batchSize); if (didWeSucceed) { - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); } else { //_transform(model, amt) returns false if we can't afford it - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); } }, @@ -1400,21 +1400,21 @@ dojo.declare("classes.ui.religion.TransformBtnController", com.nuclearunicorn.ga transform: function(model, divider, event, callback) { var amt = Math.floor(this._canAfford(model) / divider); if (amt < 1) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } if (!model.enabled) { //As far as I can tell, this shouldn't ever happen because being //unable to afford it is the only reason for it to be disabled. - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, { reason: "not-enabled" }); return; } var didWeSucceed = this._transform(model, amt); if (didWeSucceed) { - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); } else { //_transform(model, amt) returns false if we can't afford it - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); } }, @@ -1498,19 +1498,19 @@ dojo.declare("classes.ui.religion.RefineTearsBtnController", com.nuclearunicorn. buyItem: function(model, event, callback, count){ if (!this.hasResources(model)) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } if (!model.enabled) { //As far as I can tell, this shouldn't ever happen because being //unable to afford it is the only reason for it to be disabled. - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, { reason: "not-enabled" }); return; } if (this.game.resPool.get("sorrow").value >= this.game.resPool.get("sorrow").maxValue){ //We can't refine because we're at the limit. this.game.msg($I("religion.refineTearsBtn.refine.msg.failure")); - callback({ itemBought: false, reason: "already-bought" }); + callback(false /*itemBought*/, { reason: "already-bought" }); return; } if (!event) { event = {}; /*event is an optional parameter*/ } @@ -1522,7 +1522,7 @@ dojo.declare("classes.ui.religion.RefineTearsBtnController", com.nuclearunicorn. this.payPrice(model); this.refine(); } - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); }, refine: function(){ @@ -1621,11 +1621,11 @@ dojo.declare("classes.ui.PactsPanel", com.nuclearunicorn.game.ui.Panel, { this.updateEnabled(model); if (!this.hasResources(model) && !this.game.devMode) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } if(!this.shouldBeBough(model, this.game)){ - callback({ itemBought: false, reason: "already-bought" /*No more pacts available*/ }); + callback(false /*itemBought*/, { reason: "already-bought" /*No more pacts available*/ }); return; } this._buyItem_step2(model, event, callback); diff --git a/js/science.js b/js/science.js index 01499e0206..4a23815a46 100644 --- a/js/science.js +++ b/js/science.js @@ -1874,55 +1874,54 @@ dojo.declare("classes.ui.PolicyBtnController", com.nuclearunicorn.game.ui.Buildi } }, - //Returns an object with two fields: - // itemBought: Boolean. Are we able to buy this policy at this time? + //Returns an object with one field: // reason: String. If we can't buy it, why not? If we can buy it, returns // "paid-for" regardless of whether we can actually afford it or not. shouldBeBought: function(model, game){ //fail checks: if(this.game.village.leader && model.metadata.requiredLeaderJob && this.game.village.leader.job != model.metadata.requiredLeaderJob){ var jobTitle = this.game.village.getJob(model.metadata.requiredLeaderJob).title; this.game.msg($I("msg.policy.wrongLeaderJobForResearch", [model.metadata.label, jobTitle]), "important"); - return { itemBought: false, reason: "blocked" }; + return { reason: "blocked" }; }else if(model.metadata.name == "transkittenism" && this.game.bld.getBuildingExt("aiCore").meta.effects["aiLevel"] >= 15){ this.game.msg($I("msg.policy.aiNotMerges"),"alert", "ai"); - return { itemBought: false, reason: "blocked" }; + return { reason: "blocked" }; }else if(model.metadata.blocked != true) { for(var i = 0; i < model.metadata.blocks.length; i++){ if(this.game.science.getPolicy(model.metadata.blocks[i]).researched){ model.metadata.blocked = true; - return { itemBought: false, reason: "blocked" }; + return { reason: "blocked" }; } } var confirmed = false; //confirmation: if(game.opts.noConfirm){ - return { itemBought: true, reason: "paid-for" }; + return { reason: "paid-for" }; } game.ui.confirm($I("policy.confirmation.title"), $I("policy.confirmation.title"), function() { confirmed = true; }); - return confirmed ? { itemBought: true, reason: "paid-for" } : { itemBought: false, reason: "player-denied" }; + return confirmed ? { reason: "paid-for" } : { reason: "player-denied" }; } //Else, the policy was blocked: - return { itemBought: false, reason: "blocked" }; + return { reason: "blocked" }; }, buyItem: function(model, event, callback) { var isInDevMode = this.game.devMode; if (model.metadata.researched && !isInDevMode) { - callback({ itemBought: false, reason: "already-bought" }); + callback(false /*itemBought*/, { reason: "already-bought" }); return; } if (!this.hasResources(model) && !isInDevMode) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } - var result = this.shouldBeBought(model, this.game); - if(!result.itemBought){ - callback(result); //Tell them *why* we failed to buy the item. + var extendedInfo = this.shouldBeBought(model, this.game); + if(extendedInfo.reason !== "paid-for"){ + callback(false /*itemBought*/, extendedInfo); //Tell them *why* we failed to buy the item. return; } this.payPrice(model); this.onPurchase(model); - callback({ itemBought: true, reason: (this.game.devMode ? "dev-mode" : "paid-for") }); + callback(true /*itemBought*/, { reason: (this.game.devMode ? "dev-mode" : "paid-for") }); this.game.render(); }, onPurchase: function(model){ diff --git a/js/space.js b/js/space.js index c280b820bb..c17d2a1ca7 100644 --- a/js/space.js +++ b/js/space.js @@ -1304,7 +1304,7 @@ dojo.declare("com.nuclearunicorn.game.ui.SpaceProgramBtnController", com.nuclear if (model.metadata.val == 0) { this.inherited(arguments); } else { - callback({ itemBought: false, reason: "already-bought" }); + callback(false /*itemBought*/, { reason: "already-bought" }); } }, diff --git a/js/time.js b/js/time.js index 840cd962c6..0178d1b0e1 100644 --- a/js/time.js +++ b/js/time.js @@ -999,7 +999,7 @@ dojo.declare("classes.ui.time.AccelerateTimeBtnController", com.nuclearunicorn.g } else { self.game.time.isAccelerated = !self.game.time.isAccelerated; } - callback({ itemBought: true, reason: "item-is-free" /*It costs flux, but you can still toggle it freely*/ }); + callback(true /*itemBought*/, { reason: "item-is-free" /*It costs flux, but you can still toggle it freely*/ }); } }); @@ -1203,13 +1203,13 @@ dojo.declare("classes.ui.time.ShatterTCBtnController", com.nuclearunicorn.game.u buyItem: function(model, event, callback){ if (!this.hasResources(model)) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return true; } if (!model.enabled) { //As far as I can tell, this shouldn't ever happen because being //unable to afford it is the only reason for it to be disabled. - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, { reason: "not-enabled" }); return true; } var price = this.getPrices(model); @@ -1217,7 +1217,7 @@ dojo.declare("classes.ui.time.ShatterTCBtnController", com.nuclearunicorn.game.u this.game.resPool.addResEvent(price[i].name, -price[i].val); } this.doShatter(model, 1); - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); return true; }, @@ -1386,15 +1386,15 @@ dojo.declare("classes.ui.time.FixCryochamberBtnController", com.nuclearunicorn.g buyItem: function(model, event, callback) { if (this.game.time.getVSU("usedCryochambers").val == 0) { - callback({ itemBought: false, reason: "already-bought" }); + callback(false /*itemBought*/, { reason: "already-bought" }); return; } if (!model.visible) { - callback({ itemBought: false, reason: "not-unlocked" }); + callback(false /*itemBought*/, { reason: "not-unlocked" }); return; } if (!this.hasResources(model)) { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); return; } @@ -1414,9 +1414,9 @@ dojo.declare("classes.ui.time.FixCryochamberBtnController", com.nuclearunicorn.g if(fixHappened){ var cry = this.game.time.getVSU("cryochambers"); cry.calculateEffects(cry, this.game); - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); } else { - callback({ itemBought: false, reason: "not-enabled" }); + callback(false /*itemBought*/, { reason: "not-enabled" }); } }, @@ -2251,10 +2251,15 @@ dojo.declare("classes.queue.manager", null,{ return; } + var wasItemBought = false; var resultOfBuyingItem = null; - controllerAndModel.controller.buyItem(controllerAndModel.model, null, function(args) { resultOfBuyingItem = args; }); + controllerAndModel.controller.buyItem(controllerAndModel.model, null, + function(success, extendedInfo) { + wasItemBought = success; + resultOfBuyingItem = extendedInfo; + }); - if (typeof(resultOfBuyingItem) !== "object" || !resultOfBuyingItem) { + if (typeof(wasItemBought) !== "boolean" || typeof(resultOfBuyingItem) !== "object") { console.error("Invalid result after attempting to buy item via queue", resultOfBuyingItem); return; } @@ -2262,7 +2267,7 @@ dojo.declare("classes.queue.manager", null,{ var reason = resultOfBuyingItem.reason; //String explaining *why* we failed to buy the item //Depending on the result, do something different: - if (resultOfBuyingItem.itemBought){ + if (wasItemBought){ //Item successfully purchased! Remove it from the queue because we did it :D this.dropLastItem(); this.game._publish("ui/update", this.game); diff --git a/js/workshop.js b/js/workshop.js index 9f8d04539e..9dbe9a79b8 100644 --- a/js/workshop.js +++ b/js/workshop.js @@ -2910,9 +2910,9 @@ dojo.declare("com.nuclearunicorn.game.ui.CraftButtonController", com.nuclearunic buyItem: function(model, event, callback) { var wasCraftSuccessful = this.game.workshop.craft(model.craft.name, 1); if (wasCraftSuccessful) { - callback({ itemBought: true, reason: "paid-for" }); + callback(true /*itemBought*/, { reason: "paid-for" }); } else { - callback({ itemBought: false, reason: "cannot-afford" }); + callback(false /*itemBought*/, { reason: "cannot-afford" }); } } }); diff --git a/test/game.test.js b/test/game.test.js index 3747612e36..c2f60c6e8c 100644 --- a/test/game.test.js +++ b/test/game.test.js @@ -481,8 +481,12 @@ test("Spaceports should be unlocked correctly and have a custom price logic appl test("buyItem internals should work properly for Resource Retrieval", () => { const controller = new classes.ui.time.ChronoforgeBtnController(game); const model = controller.fetchModel({ id: "ressourceRetrieval" }); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; //Before we get started, this test assumes certain things about Resource Retrievals: //We assume there is a limit of 100. @@ -495,7 +499,7 @@ test("buyItem internals should work properly for Resource Retrieval", () => { controller.buyItem(model, null, callbackFunction); expect(model.metadata.on).toBe(0); expect(model.metadata.val).toBe(0); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); //Enter dev mode: @@ -504,7 +508,7 @@ test("buyItem internals should work properly for Resource Retrieval", () => { controller.buyItem(model, null, callbackFunction); expect(model.metadata.on).toBe(1); expect(model.metadata.val).toBe(1); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "dev-mode"); //Now exit dev mode & try to buy the next building legitimately. @@ -515,7 +519,7 @@ test("buyItem internals should work properly for Resource Retrieval", () => { expect(game.resPool.get("timeCrystal").value).toBe(50); expect(model.metadata.on).toBe(2); expect(model.metadata.val).toBe(2); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Test that holding the CTRL key builds batchSize of the item. @@ -527,7 +531,7 @@ test("buyItem internals should work properly for Resource Retrieval", () => { expect(game.resPool.get("timeCrystal").value).toBeCloseTo(78442.3093, 4); expect(model.metadata.on).toBe(8); expect(model.metadata.val).toBe(8); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Test that holding the SHIFT key builds as many of the item as you can afford & overrides CTRL key. @@ -538,26 +542,26 @@ test("buyItem internals should work properly for Resource Retrieval", () => { expect(model.metadata.on).toBe(21); expect(model.metadata.val).toBe(21); expect(game.resPool.get("timeCrystal").value).toBeCloseTo(203642.5938, 4); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Test that we get the same result no matter the event parameters if we can't afford any. //We'll test each combination of 2 Boolean parameters for a total of 2^2 = 4 combinations controller.updateEnabled(model); controller.buyItem(model, { ctrlKey: true, shiftKey: true }, callbackFunction); //11 - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); controller.updateEnabled(model); controller.buyItem(model, { ctrlKey: false, shiftKey: true }, callbackFunction); //01 - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); controller.updateEnabled(model); controller.buyItem(model, { ctrlKey: true, shiftKey: false }, callbackFunction); //10 - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); controller.updateEnabled(model); controller.buyItem(model, { ctrlKey: false, shiftKey: false }, callbackFunction); //00 - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); //Double-check that nothing was built in any of those 4 tests: @@ -570,7 +574,7 @@ test("buyItem internals should work properly for Resource Retrieval", () => { controller.buyItem(model, { ctrlKey: false, shiftKey: true }, callbackFunction); expect(model.metadata.on).toBe(100); expect(model.metadata.val).toBe(100); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Test that we can't buy any more even if we have unlimited resources. @@ -578,15 +582,19 @@ test("buyItem internals should work properly for Resource Retrieval", () => { controller.buyItem(model, null, callbackFunction); expect(model.metadata.on).toBe(100); expect(model.metadata.val).toBe(100); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "already-bought"); }); test("buyItem internals should work properly for Calendar", () => { const controller = new com.nuclearunicorn.game.ui.TechButtonController(game); const model = controller.fetchModel({ id: "calendar" }); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; //Before we get started, this test assumes certain things about Calendar: //We assume it costs 30 science. @@ -596,7 +604,7 @@ test("buyItem internals should work properly for Calendar", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(false); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); //Give ourselves plenty of science & try again; this time it should succeed: @@ -604,7 +612,7 @@ test("buyItem internals should work properly for Calendar", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(true); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); expect(game.resPool.get("science").value).toBe(70); @@ -612,7 +620,7 @@ test("buyItem internals should work properly for Calendar", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(true); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "already-bought"); expect(game.resPool.get("science").value).toBe(70); }); @@ -620,8 +628,12 @@ test("buyItem internals should work properly for Calendar", () => { test("buyItem internals should work properly for Liberty & Tradition", () => { const controller = new classes.ui.PolicyBtnController(game); let model = controller.fetchModel({ id: "liberty" }); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; //Before we get started, this test assumes certain things about Liberty: //We assume it costs 150 culture. @@ -634,7 +646,7 @@ test("buyItem internals should work properly for Liberty & Tradition", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(false); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); //Give ourselves plenty of culture & try again; this time it should succeed: @@ -642,7 +654,7 @@ test("buyItem internals should work properly for Liberty & Tradition", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(true); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); expect(game.resPool.get("culture").value).toBe(350); @@ -650,7 +662,7 @@ test("buyItem internals should work properly for Liberty & Tradition", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(true); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "already-bought"); expect(game.resPool.get("culture").value).toBe(350); @@ -660,7 +672,7 @@ test("buyItem internals should work properly for Liberty & Tradition", () => { controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(model.metadata.researched).toBe(false); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "blocked"); expect(game.resPool.get("culture").value).toBe(350); }); @@ -668,8 +680,12 @@ test("buyItem internals should work properly for Liberty & Tradition", () => { test("buyItem internals should work properly for Fix Cryochamber", () => { const controller = new classes.ui.time.FixCryochamberBtnController(game); let model = controller.fetchModel({}); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; const cryochambers = game.time.getVSU("cryochambers"); const usedCryochambers = game.time.getVSU("usedCryochambers"); @@ -677,7 +693,7 @@ test("buyItem internals should work properly for Fix Cryochamber", () => { controller.updateEnabled(model); controller.updateVisible(model); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "already-bought"); expect(cryochambers.val).toBe(0); expect(usedCryochambers.val).toBe(0); @@ -689,7 +705,7 @@ test("buyItem internals should work properly for Fix Cryochamber", () => { controller.updateEnabled(model); controller.updateVisible(model); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "not-unlocked"); expect(cryochambers.val).toBe(0); expect(usedCryochambers.val).toBe(5); @@ -700,7 +716,7 @@ test("buyItem internals should work properly for Fix Cryochamber", () => { controller.updateEnabled(model); controller.updateVisible(model); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); expect(cryochambers.val).toBe(0); expect(usedCryochambers.val).toBe(5); @@ -713,7 +729,7 @@ test("buyItem internals should work properly for Fix Cryochamber", () => { controller.updateEnabled(model); controller.updateVisible(model); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); expect(cryochambers.val).toBe(1); expect(usedCryochambers.val).toBe(4); @@ -727,8 +743,12 @@ test("buyItem internals should work properly for Fix Cryochamber", () => { test("buyItem internals should work properly for crafting steel", () => { const controller = new com.nuclearunicorn.game.ui.CraftButtonController(game); const model = controller.fetchModel({ craft: "steel" }); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; //Before we get started, this test assumes certain things about crafting steel: //We assume it costs 100 iron, 100 coal per craft. @@ -740,9 +760,9 @@ test("buyItem internals should work properly for crafting steel", () => { //Crafting should fail when we can't afford it: controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(game.resPool.get("steel").value).toBe(0); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); //Crafting should succeed when we can afford it: @@ -750,7 +770,7 @@ test("buyItem internals should work properly for crafting steel", () => { game.resPool.addResEvent("coal", 199); controller.buyItem(model, null, callbackFunction); expect(game.resPool.get("steel").value).toBe(1); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Crafting should fail again because we consumed resources & don't have enough anymore: @@ -758,15 +778,19 @@ test("buyItem internals should work properly for crafting steel", () => { expect(game.resPool.get("steel").value).toBe(1); expect(game.resPool.get("iron").value).toBe(99); expect(game.resPool.get("coal").value).toBe(99); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); }); test("buyItem internals should work properly for shattering time crystals", () => { const controller = new classes.ui.time.ShatterTCBtnController(game); const model = controller.fetchModel({ prices: [{name: "timeCrystal", val: 1}] }); + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; //For shattering TCs, we'll watch what happens with the calendar year: expect(game.calendar.year).toBe(0); @@ -775,7 +799,7 @@ test("buyItem internals should work properly for shattering time crystals", () = controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(game.calendar.year).toBe(0); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "cannot-afford"); game.resPool.addResEvent("timeCrystal", 3); @@ -784,7 +808,7 @@ test("buyItem internals should work properly for shattering time crystals", () = controller.updateEnabled(model); controller.buyItem(model, null, callbackFunction); expect(game.calendar.year).toBe(1); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "paid-for"); //Shattering should have cost us the correct amount of time crystals. @@ -792,8 +816,12 @@ test("buyItem internals should work properly for shattering time crystals", () = }); test("buyItem internals should work properly for items with no cost", () => { + let wasItemBought = null; let callbackResult = null; - const callbackFunction = function(result) { callbackResult = result; }; + const callbackFunction = function(success, extendedInfo) { + wasItemBought = success; + callbackResult = extendedInfo; + }; let handlerResult = null; const handlerFunction = function(model) { handlerResult = model.name; }; let controller = new com.nuclearunicorn.game.ui.ButtonModernController(game); @@ -802,14 +830,14 @@ test("buyItem internals should work properly for items with no cost", () => { //If we force-disable the model, we shouldn't be able to buy it: model.enabled = false; controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", false); + expect(wasItemBought).toBe(false); expect(callbackResult).toHaveProperty("reason", "not-enabled"); expect(handlerResult).toBe(null); //Proof that the handler wasn't called //Buying a free item should be free & call the handler: model.enabled = true; controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "item-is-free"); expect(handlerResult).toBe("A"); @@ -819,7 +847,7 @@ test("buyItem internals should work properly for items with no cost", () => { //Gathering catnip should be free & always succeed: expect(game.resPool.get("catnip").value).toBe(0); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "item-is-free"); expect(game.resPool.get("catnip").value).toBe(1); @@ -829,11 +857,11 @@ test("buyItem internals should work properly for items with no cost", () => { //Toggle pending challenge should be free & always succeed: expect(model.metadata.pending).toBe(false); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "item-is-free"); expect(model.metadata.pending).toBe(true); controller.buyItem(model, null, callbackFunction); - expect(callbackResult).toHaveProperty("itemBought", true); + expect(wasItemBought).toBe(true); expect(callbackResult).toHaveProperty("reason", "item-is-free"); expect(model.metadata.pending).toBe(false); }); \ No newline at end of file