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;
+ }
}