From 0b6e263cc7c0878a1119fb8f4423e49ed845235b Mon Sep 17 00:00:00 2001 From: Rudis Muiznieks Date: Sun, 12 Sep 2021 22:27:07 -0500 Subject: [PATCH] changed cost into function; changed inc into ResourcNumber --- TODO.md | 14 ++---- src/model/GameConfig.ts | 4 +- src/model/GameState.ts | 6 ++- src/model/Utils.ts | 11 +++++ src/model/resource/BuildingPermit.ts | 2 +- src/model/resource/Church.ts | 15 +++--- src/model/resource/Compound.ts | 2 +- src/model/resource/Credibility.ts | 7 ++- src/model/resource/CryptoCurrency.ts | 16 +++++-- src/model/resource/CryptoMarket.ts | 18 +++---- src/model/resource/Follower.ts | 10 ++-- src/model/resource/House.ts | 15 +++--- src/model/resource/Job.ts | 20 +------- src/model/resource/Megachurch.ts | 2 +- src/model/resource/Money.ts | 11 +++-- src/model/resource/Purchasable.ts | 71 ++++++++++++---------------- src/model/resource/Resource.ts | 4 ++ src/model/resource/Tent.ts | 16 +++++-- src/render/DebugRenderer.ts | 49 ++++++++++++++----- 19 files changed, 162 insertions(+), 131 deletions(-) diff --git a/TODO.md b/TODO.md index a433cd2..538e4a7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,5 @@ ## In Progress -- [ ] change `inc` to a `ResourceNumber` instead of a `number` -- [ ] change `cost` into a function that computes based on `value` ## Initial Game Progression Plan @@ -13,17 +11,16 @@ - [x] houses (+follower cap) - [x] churches (+pastor cap) - [x] hire pastors to recruit and gather tithes (+money, +followers) - - [ ] hire compound managers to auto-build (+tents, +houses, +churches) - - [ ] fluctuating crypto market value passive determines crypto value + - [x] hire compound managers to auto-build (+tents, +houses, +churches) + - [x] fluctuating crypto market value passive determines crypto value - [ ] prosperity gospel policy increases tithes, decreases recruitment rate - main resource problems: - [x] lose money on purchases - [x] lose money for salaries - - [ ] lose money for compound maintenance - [x] gain money from tithes - [x] low credibility loses followers - - [ ] low and decreasing money loses jobs - - [ ] gain or lose money on crypto based on market passive + - [x] low and decreasing money loses jobs + - [x] gain or lose money on crypto based on market passive ### Phase 2: 1K-100K Followers @@ -84,10 +81,7 @@ ## Short-Term Todos - [ ] add `disabledHint` to `userActions` to generate hint about how to enable an action -- [x] add an action to sell purchasables and regain some portion of their cost - [ ] add a `policy` resource type that can be toggled on or off -- [x] remove recruitment effects of credibility (that will be for notoriety instead) -- [x] change `value` to a getter that does `Math.floor` if it's a whole number resource ## Long-Term Ideas diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts index b680fbe..d8ef21a 100644 --- a/src/model/GameConfig.ts +++ b/src/model/GameConfig.ts @@ -89,12 +89,12 @@ class GameConfig { public cfgCryptoMarketAdjustAmount = 0.1; public cfgCryptoMarketAdjustPeriod = 30000; public cfgCryptoMarketGrowthBias = 0.1; - public cfgDefaultSellMultiplier = 0.5; public cfgFollowerGainLossLogTimer = 10000; public cfgNoMoneyQuitRate = 0.2; public cfgNoMoneyQuitTime = 10000; public cfgPassiveMax = 100; public cfgPastorRecruitRate = 0.01; + public cfgSellCostBackMultiplier = 0.5; public cfgTimeBetweenTithes = 10000; public cfgTitheAmount = 10000; public cfgTitheCredibilityHitFactor = 3; @@ -202,7 +202,7 @@ class GameConfig { // add resources state.addResource(new Money(3.5)); - state.addResource(new CryptoCurrency(this)); + state.addResource(new CryptoCurrency()); state.addResource(new Tent(this)); state.addResource(new House(this)); state.addResource(new Church(this)); diff --git a/src/model/GameState.ts b/src/model/GameState.ts index e32c23e..ecfe44b 100644 --- a/src/model/GameState.ts +++ b/src/model/GameState.ts @@ -58,7 +58,10 @@ class GameState { resource.inc !== undefined && (resource.max === undefined || resource.value < resource.max(this)) ) { - resource.addValue((resource.inc(this) * time) / 1000, this); + resource.addValue( + (resourceNumberSum(resource.inc(this)) * time) / 1000, + this + ); } if (resource.max !== undefined && resource.value > resource.max(this)) { @@ -138,7 +141,6 @@ class GameState { if (resource === undefined) continue; const resSav: ResourceConfig = { value: resource.value, - cost: resource.cost, }; if (resource.emitConfig !== undefined) { resSav.config = resource.emitConfig(); diff --git a/src/model/Utils.ts b/src/model/Utils.ts index c7f096e..87551d8 100644 --- a/src/model/Utils.ts +++ b/src/model/Utils.ts @@ -1,3 +1,5 @@ +/// + const numberFormatDigits = 1; function formatNumber(num: number): string { @@ -26,3 +28,12 @@ function formatNumber(num: number): string { return `${sign}${number}`; } + +function resourceNumberSum(res: ResourceNumber): number { + let sum = 0; + for (const key in res) { + const rkey = key; + sum += res[rkey] ?? 0; + } + return sum; +} diff --git a/src/model/resource/BuildingPermit.ts b/src/model/resource/BuildingPermit.ts index 7061471..6d12cad 100644 --- a/src/model/resource/BuildingPermit.ts +++ b/src/model/resource/BuildingPermit.ts @@ -10,7 +10,7 @@ class BuildingPermit extends Research { 'building permits', 'Unlocks several new buildings you can build outside of your compounds.' ); - this.cost.money = config.cfgInitialCost.buildingPermit; + this._baseCost.money = config.cfgInitialCost.buildingPermit; } public isUnlocked = (state: GameState): boolean => { diff --git a/src/model/resource/Church.ts b/src/model/resource/Church.ts index 0fa2470..9f9902d 100644 --- a/src/model/resource/Church.ts +++ b/src/model/resource/Church.ts @@ -15,7 +15,7 @@ class Church extends Infrastructure { undefined, undefined ); - this.cost.money = config.cfgInitialCost.churches; + this._baseCost.money = config.cfgInitialCost.churches; this._costMultiplier.money = config.cfgCostMultiplier.churches; } @@ -23,12 +23,15 @@ class Church extends Infrastructure { (state.resource.compounds?.value ?? 0) * (state.config.cfgCapacity.compounds?.churches ?? 0); - public inc = (state: GameState): number => { - // compound managers - return ( + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; + const compoundManagers = (state.resource.compoundManagers?.value ?? 0) * - (state.config.cfgBuySpeed.compoundManagers?.churches ?? 0) - ); + (state.config.cfgBuySpeed.compoundManagers?.churches ?? 0); + if (compoundManagers > 0) { + inc.compoundManagers = compoundManagers; + } + return inc; }; public isUnlocked = (state: GameState): boolean => { diff --git a/src/model/resource/Compound.ts b/src/model/resource/Compound.ts index e3bd02d..e75a028 100644 --- a/src/model/resource/Compound.ts +++ b/src/model/resource/Compound.ts @@ -11,7 +11,7 @@ class Compound extends Infrastructure { 'Provides space for tents, houses, and churches and a place to hide more money.', true ); - this.cost.money = config.cfgInitialCost.compounds; + this._baseCost.money = config.cfgInitialCost.compounds; this._costMultiplier.money = config.cfgCostMultiplier.compounds; } diff --git a/src/model/resource/Credibility.ts b/src/model/resource/Credibility.ts index c6701c5..fa4ace9 100644 --- a/src/model/resource/Credibility.ts +++ b/src/model/resource/Credibility.ts @@ -26,6 +26,9 @@ class Credibility extends Passive { public max = (state: GameState): number => state.config.cfgPassiveMax; - public inc = (state: GameState): number => - state.config.cfgCredibilityRestoreRate; + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; + inc.credibility = state.config.cfgCredibilityRestoreRate; + return inc; + }; } diff --git a/src/model/resource/CryptoCurrency.ts b/src/model/resource/CryptoCurrency.ts index d3be335..8054026 100644 --- a/src/model/resource/CryptoCurrency.ts +++ b/src/model/resource/CryptoCurrency.ts @@ -3,7 +3,7 @@ class CryptoCurrency extends Purchasable { public readonly resourceKey = ResourceKey.cryptoCurrency; - constructor(config: GameConfig) { + constructor() { super( 'FaithCoin', 'faithcoin', @@ -11,11 +11,21 @@ class CryptoCurrency extends Purchasable { "A crypto coin that can't be spent directly, but provides a steady stream of passive income.", true ); - this.cost.money = config.cfgInitialCost.cryptoCurrency; this.valueInWholeNumbers = false; } - public isUnlocked = (_state: GameState): boolean => true; + public cost = (state: GameState): ResourceNumber => { + const cost: ResourceNumber = {}; + const market = state.resource.cryptoMarket; + if (market !== undefined) { + cost.money = market.value; + } else { + cost.money = state.config.cfgInitialCost.cryptoCurrency; + } + return cost; + }; + + public isUnlocked = (): boolean => true; public max = (state: GameState): number => state.config.cfgInitialMax.cryptoCurrency ?? 0; diff --git a/src/model/resource/CryptoMarket.ts b/src/model/resource/CryptoMarket.ts index 165bb4c..4a238a5 100644 --- a/src/model/resource/CryptoMarket.ts +++ b/src/model/resource/CryptoMarket.ts @@ -40,17 +40,13 @@ class CryptoMarket extends Hidden { ) { adjustment = state.config.cfgCryptoCurrencyMinimumValue - this.value; } - //if (Math.abs(adjustment) > 0) { - this.addValue(adjustment, state); - state.log( - `FaithCoin just ${ - adjustment > 0 ? 'increased' : 'decreased' - } in value by $${formatNumber(Math.abs(adjustment))}.` - ); - //} - if (crypto?.cost !== undefined) { - crypto.cost.money = this.value; - state.autoAction(); // cause redraw + if (Math.abs(adjustment) > 0) { + this.addValue(adjustment, state); + state.log( + `FaithCoin just ${ + adjustment > 0 ? 'increased' : 'decreased' + } in value by $${formatNumber(Math.abs(adjustment))}.` + ); } } }; diff --git a/src/model/resource/Follower.ts b/src/model/resource/Follower.ts index 2c0d246..5bb21f1 100644 --- a/src/model/resource/Follower.ts +++ b/src/model/resource/Follower.ts @@ -39,12 +39,14 @@ class Follower extends Resource { return max; }; - public inc = (state: GameState): number => { - let inc = 0; + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; // pastor recruiting - const pastors = state.resource.pastors?.value ?? 0; - inc += pastors * state.config.cfgPastorRecruitRate; + const pastors = + (state.resource.pastors?.value ?? 0) * state.config.cfgPastorRecruitRate; + + if (pastors > 0) inc.pastors = pastors; // credibility adjustment // this should be based on notoriety instead diff --git a/src/model/resource/House.ts b/src/model/resource/House.ts index c4da4c2..f6429f6 100644 --- a/src/model/resource/House.ts +++ b/src/model/resource/House.ts @@ -13,7 +13,7 @@ class House extends Infrastructure { )} followers.`, true ); - this.cost.money = config.cfgInitialCost.houses; + this._baseCost.money = config.cfgInitialCost.houses; this._costMultiplier.money = config.cfgCostMultiplier.houses; } @@ -21,12 +21,15 @@ class House extends Infrastructure { (state.resource.compounds?.value ?? 0) * (state.config.cfgCapacity.compounds?.houses ?? 0); - public inc = (state: GameState): number => { - // compound managers - return ( + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; + const compoundManagers = (state.resource.compoundManagers?.value ?? 0) * - (state.config.cfgBuySpeed.compoundManagers?.houses ?? 0) - ); + (state.config.cfgBuySpeed.compoundManagers?.houses ?? 0); + if (compoundManagers > 0) { + inc.compoundManagers = compoundManagers; + } + return inc; }; public isUnlocked = (state: GameState): boolean => { diff --git a/src/model/resource/Job.ts b/src/model/resource/Job.ts index b551c07..4ad04a1 100644 --- a/src/model/resource/Job.ts +++ b/src/model/resource/Job.ts @@ -4,10 +4,9 @@ abstract class Job extends Resource { public readonly resourceType = ResourceType.job; public readonly valueInWholeNumbers = true; - public readonly cost: ResourceNumber = {}; public max?: (state: GameState) => number = undefined; - public inc?: (state: GameState) => number = undefined; + public inc?: (state: GameState) => ResourceNumber = undefined; public userActions: ResourceAction[] = [ { @@ -31,7 +30,6 @@ abstract class Job extends Resource { }, ]; - protected _costMultiplier: { [key in ResourceKey]?: number } = {}; protected _isUnlocked = false; constructor( @@ -102,18 +100,9 @@ abstract class Job extends Resource { private _promoteFollower(state: GameState): void { if (Job.availableJobs(state) <= 0) return; - if ( - this.max !== undefined && - this.value < this.max(state) && - state.deductCost(this.cost) - ) { + if (this.max !== undefined && this.value < this.max(state)) { this.addValue(1, state); state.log(this._hireLog(1, state)); - for (const key in this._costMultiplier) { - const rkey = key; - this.cost[rkey] = - (this.cost[rkey] ?? 0) * (this._costMultiplier[rkey] ?? 1); - } } } @@ -121,10 +110,5 @@ abstract class Job extends Resource { if (this.value <= 0) return; this.addValue(-1, state); state.log(this._hireLog(-1, state)); - for (const key in this._costMultiplier) { - const rkey = key; - this.cost[rkey] = - (this.cost[rkey] ?? 0) / (this._costMultiplier[rkey] ?? 1); - } } } diff --git a/src/model/resource/Megachurch.ts b/src/model/resource/Megachurch.ts index bfba87c..26f0769 100644 --- a/src/model/resource/Megachurch.ts +++ b/src/model/resource/Megachurch.ts @@ -13,7 +13,7 @@ class Megachurch extends Infrastructure { )} pastors`, true ); - this.cost.money = config.cfgInitialCost.megaChurches; + this._baseCost.money = config.cfgInitialCost.megaChurches; this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; } diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts index 164f84d..eeeaa1d 100644 --- a/src/model/resource/Money.ts +++ b/src/model/resource/Money.ts @@ -40,11 +40,11 @@ class Money extends Resource { return max; }; - public inc = (state: GameState): number => { - let inc = 0; + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; // tithings - inc += + const tithings = ((state.resource.pastors?.value ?? 0) * (state.resource.followers?.value ?? 0) * (state.config.cfgTitheAmount ?? 0) * @@ -52,10 +52,13 @@ class Money extends Resource { state.config.cfgTimeBetweenTithes; // salaries - inc -= + const compoundManagers = (state.resource.compoundManagers?.value ?? 0) * (state.config.cfgSalary.compoundManagers ?? 0); + if (tithings > 0) inc.pastors = tithings; + if (compoundManagers > 0) inc.compoundManagers = compoundManagers * -1; + return inc; }; diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts index 906578c..8ee52dc 100644 --- a/src/model/resource/Purchasable.ts +++ b/src/model/resource/Purchasable.ts @@ -4,10 +4,9 @@ abstract class Purchasable extends Resource { public readonly resourceType: ResourceType = ResourceType.purchasable; public valueInWholeNumbers = true; - public cost: ResourceNumber = {}; - public inc?: (state: GameState) => number = undefined; public max?: (state: GameState) => number = undefined; + public inc?: (state: GameState) => ResourceNumber = undefined; public userActions: ResourceAction[] = [ { @@ -15,19 +14,17 @@ abstract class Purchasable extends Resource { description: this._purchaseDescription, isEnabled: (state: GameState): boolean => (this.max === undefined || this.value < this.max(state)) && - state.isPurchasable(this.cost), + state.isPurchasable(this.cost(state)), performAction: (state: GameState): void => { this._purchase(state); }, }, ]; - protected _costMultiplier: ResourceNumber = {}; - protected _sellMultiplier?: number | ResourceNumber; + protected readonly _baseCost: ResourceNumber = {}; + protected readonly _costMultiplier: ResourceNumber = {}; protected _isUnlocked = false; - private _lastWholeNumberValue = 0; - constructor( public readonly label: string, public readonly singularName: string, @@ -52,8 +49,21 @@ abstract class Purchasable extends Resource { } } + public cost = (_: GameState): ResourceNumber => { + if (this.value <= 0) return this._baseCost; + + const actualCost: ResourceNumber = {}; + for (const key in this._baseCost) { + const rkey = key; + const baseCost = this._baseCost[rkey] ?? 0; + const multiplier = this._costMultiplier[rkey] ?? 1; + actualCost[rkey] = baseCost * Math.pow(multiplier, this.value); + } + return actualCost; + }; + public isUnlocked = (state: GameState): boolean => { - if (!this._isUnlocked && state.isPurchasable(this.cost)) { + if (!this._isUnlocked && state.isPurchasable(this.cost(state))) { this._isUnlocked = true; } return this._isUnlocked; @@ -73,21 +83,6 @@ abstract class Purchasable extends Resource { } }; - public addValue = (amount: number, _: GameState): void => { - this.rawValue += amount; - const wholeNumberChange = this.value - this._lastWholeNumberValue; - if (wholeNumberChange > 0) { - for (const key in this._costMultiplier) { - const rkey = key; - this.cost[rkey] = - (this.cost[rkey] ?? 0) * - (this._costMultiplier[rkey] ?? 1) * - wholeNumberChange; - } - this._lastWholeNumberValue = this.value; - } - }; - protected _purchaseLog(amount: number, _state: GameState): string { let verb = 'purchased'; if (amount < 0) { @@ -101,7 +96,7 @@ abstract class Purchasable extends Resource { private _purchase(state: GameState): void { if (this.max !== undefined && this.value >= this.max(state)) return; - if (state.deductCost(this.cost)) { + if (state.deductCost(this.cost(state))) { this.addValue(1, state); state.log(this._purchaseLog(1, state)); } @@ -109,25 +104,17 @@ abstract class Purchasable extends Resource { private _sell(state: GameState): void { if (this.value <= 0) return; - const costBack: ResourceNumber = {}; - for (const key in this.cost) { - const rkey = key; - let cost = this.cost[rkey]; - if (cost === undefined) continue; - // revert cost multiplier - cost /= this._costMultiplier[rkey] ?? 1; - this.cost[rkey] = cost; - const multiplier = - this._sellMultiplier === undefined - ? state.config.cfgDefaultSellMultiplier - : typeof this._sellMultiplier === 'number' - ? this._sellMultiplier - : this._sellMultiplier[rkey] ?? state.config.cfgDefaultSellMultiplier; - // penalize return on used item - costBack[rkey] = cost * -1 * multiplier; - state.deductCost(costBack); - } + this.addValue(-1, state); state.log(this._purchaseLog(-1, state)); + + const costBack: ResourceNumber = {}; + for (const key in this.cost(state)) { + const rkey = key; + const cost = this.cost(state)[rkey]; + if (cost === undefined) continue; + costBack[rkey] = cost * state.config.cfgSellCostBackMultiplier * -1; + } + state.deductCost(costBack); } } diff --git a/src/model/resource/Resource.ts b/src/model/resource/Resource.ts index 418cab6..a49a61f 100644 --- a/src/model/resource/Resource.ts +++ b/src/model/resource/Resource.ts @@ -1,6 +1,10 @@ /// abstract class Resource implements IResource { + public inc?: (state: GameState) => ResourceNumber = undefined; + public cost?: (state: GameState) => ResourceNumber = undefined; + public max?: (state: GameState) => number = undefined; + protected rawValue = 0; public abstract readonly resourceType: ResourceType; diff --git a/src/model/resource/Tent.ts b/src/model/resource/Tent.ts index 0a6661b..35ff07d 100644 --- a/src/model/resource/Tent.ts +++ b/src/model/resource/Tent.ts @@ -13,7 +13,7 @@ class Tent extends Infrastructure { )} followers.`, true ); - this.cost.money = config.cfgInitialCost.tents; + this._baseCost.money = config.cfgInitialCost.tents; this._costMultiplier.money = config.cfgCostMultiplier.tents; } @@ -26,8 +26,14 @@ class Tent extends Infrastructure { return max; }; - public inc = (state: GameState): number => - // compound managers - (state.resource.compoundManagers?.value ?? 0) * - (state.config.cfgBuySpeed.compoundManagers?.tents ?? 0); + public inc = (state: GameState): ResourceNumber => { + const inc: ResourceNumber = {}; + const compoundManagers = + (state.resource.compoundManagers?.value ?? 0) * + (state.config.cfgBuySpeed.compoundManagers?.tents ?? 0); + if (compoundManagers > 0) { + inc.compoundManagers = compoundManagers; + } + return inc; + }; } diff --git a/src/render/DebugRenderer.ts b/src/render/DebugRenderer.ts index e40d32b..fec0718 100644 --- a/src/render/DebugRenderer.ts +++ b/src/render/DebugRenderer.ts @@ -73,7 +73,7 @@ class DebugRenderer implements IRenderer { } if ( resource.cost !== undefined && - Object.keys(resource.cost).length !== 0 + Object.keys(resource.cost(state)).length !== 0 ) { content += "
Cost: "; } @@ -81,7 +81,6 @@ class DebugRenderer implements IRenderer { resContainer.appendChild(el); if (resource.userActions !== undefined) { for (let i = 0; i < resource.userActions.length; i++) { - const action = resource.userActions[i]; const btn = document.getElementById(`resource-btn-${rkey}-${i}`); btn?.addEventListener('click', (): void => { state.performAction(rkey, i); @@ -135,13 +134,17 @@ class DebugRenderer implements IRenderer { } } } - const inc = - resource.inc !== undefined ? resource.inc(state) : undefined; - const elI = el.getElementsByClassName('resource-inc')[0]; - if (inc !== undefined && inc !== 0) { - elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`; - } else if (elI.innerHTML !== '') { - elI.innerHTML = ''; + if (resource.inc !== undefined) { + const resInc = resource.inc(state); + const inc = resourceNumberSum(resInc); + const elI = el.getElementsByClassName('resource-inc')[0]; + if (inc !== 0) { + elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`; + elI.setAttribute('title', this._getIncDetails(resource, state)); + } else if (elI.innerHTML !== '') { + elI.innerHTML = ''; + elI.removeAttribute('title'); + } } if (this._handleClick) { const elC = el.getElementsByClassName('resource-cost'); @@ -177,17 +180,37 @@ class DebugRenderer implements IRenderer { private _getCostStr(resource: IResource, state: GameState): string { let cost = ''; + if (resource.cost === undefined) return cost; for (const rkey of state.resources) { - if (resource.cost?.[rkey] !== undefined) { + const rcost = resource.cost(state)[rkey]; + if (rcost !== undefined && rcost > 0) { if (cost !== '') cost += ', '; if (rkey === ResourceKey.money) { - cost += `$${formatNumber(resource.cost[rkey] ?? 0)}`; + cost += `$${formatNumber(rcost)}`; } else { - cost += `${formatNumber(resource.cost[rkey] ?? 0)} - ${state.resource[rkey]?.pluralName ?? rkey}`; + cost += `${formatNumber(rcost)} ${ + (rcost > 1 + ? state.resource[rkey]?.pluralName + : state.resource[rkey]?.singularName) ?? rkey + }`; } } } return cost; } + + private _getIncDetails(resource: IResource, state: GameState): string { + let inc = ''; + if (resource.inc === undefined) return inc; + for (const rkey of state.resources) { + const incRes = state.resource[rkey]; + if (incRes === undefined || incRes.label === undefined) continue; + const rinc = resource.inc(state)[rkey]; + if (rinc !== undefined && rinc !== 0) { + if (inc !== '') inc += '\n'; + inc += `${incRes.label}: ${rinc > 0 ? '+' : ''}${formatNumber(rinc)}/s`; + } + } + return inc; + } }