changed cost into function; changed inc into ResourcNumber

This commit is contained in:
Rudis Muiznieks 2021-09-12 22:27:07 -05:00
parent a0daffbddf
commit 0b6e263cc7
19 changed files with 162 additions and 131 deletions

14
TODO.md
View File

@ -1,7 +1,5 @@
## In Progress ## 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 ## Initial Game Progression Plan
@ -13,17 +11,16 @@
- [x] houses (+follower cap) - [x] houses (+follower cap)
- [x] churches (+pastor cap) - [x] churches (+pastor cap)
- [x] hire pastors to recruit and gather tithes (+money, +followers) - [x] hire pastors to recruit and gather tithes (+money, +followers)
- [ ] hire compound managers to auto-build (+tents, +houses, +churches) - [x] hire compound managers to auto-build (+tents, +houses, +churches)
- [ ] fluctuating crypto market value passive determines crypto value - [x] fluctuating crypto market value passive determines crypto value
- [ ] prosperity gospel policy increases tithes, decreases recruitment rate - [ ] prosperity gospel policy increases tithes, decreases recruitment rate
- main resource problems: - main resource problems:
- [x] lose money on purchases - [x] lose money on purchases
- [x] lose money for salaries - [x] lose money for salaries
- [ ] lose money for compound maintenance
- [x] gain money from tithes - [x] gain money from tithes
- [x] low credibility loses followers - [x] low credibility loses followers
- [ ] low and decreasing money loses jobs - [x] low and decreasing money loses jobs
- [ ] gain or lose money on crypto based on market passive - [x] gain or lose money on crypto based on market passive
### Phase 2: 1K-100K Followers ### Phase 2: 1K-100K Followers
@ -84,10 +81,7 @@
## Short-Term Todos ## Short-Term Todos
- [ ] add `disabledHint` to `userActions` to generate hint about how to enable an action - [ ] 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 - [ ] 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 ## Long-Term Ideas

View File

@ -89,12 +89,12 @@ class GameConfig {
public cfgCryptoMarketAdjustAmount = 0.1; public cfgCryptoMarketAdjustAmount = 0.1;
public cfgCryptoMarketAdjustPeriod = 30000; public cfgCryptoMarketAdjustPeriod = 30000;
public cfgCryptoMarketGrowthBias = 0.1; public cfgCryptoMarketGrowthBias = 0.1;
public cfgDefaultSellMultiplier = 0.5;
public cfgFollowerGainLossLogTimer = 10000; public cfgFollowerGainLossLogTimer = 10000;
public cfgNoMoneyQuitRate = 0.2; public cfgNoMoneyQuitRate = 0.2;
public cfgNoMoneyQuitTime = 10000; public cfgNoMoneyQuitTime = 10000;
public cfgPassiveMax = 100; public cfgPassiveMax = 100;
public cfgPastorRecruitRate = 0.01; public cfgPastorRecruitRate = 0.01;
public cfgSellCostBackMultiplier = 0.5;
public cfgTimeBetweenTithes = 10000; public cfgTimeBetweenTithes = 10000;
public cfgTitheAmount = 10000; public cfgTitheAmount = 10000;
public cfgTitheCredibilityHitFactor = 3; public cfgTitheCredibilityHitFactor = 3;
@ -202,7 +202,7 @@ class GameConfig {
// add resources // add resources
state.addResource(new Money(3.5)); state.addResource(new Money(3.5));
state.addResource(new CryptoCurrency(this)); state.addResource(new CryptoCurrency());
state.addResource(new Tent(this)); state.addResource(new Tent(this));
state.addResource(new House(this)); state.addResource(new House(this));
state.addResource(new Church(this)); state.addResource(new Church(this));

View File

@ -58,7 +58,10 @@ class GameState {
resource.inc !== undefined && resource.inc !== undefined &&
(resource.max === undefined || resource.value < resource.max(this)) (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)) { if (resource.max !== undefined && resource.value > resource.max(this)) {
@ -138,7 +141,6 @@ class GameState {
if (resource === undefined) continue; if (resource === undefined) continue;
const resSav: ResourceConfig = { const resSav: ResourceConfig = {
value: resource.value, value: resource.value,
cost: resource.cost,
}; };
if (resource.emitConfig !== undefined) { if (resource.emitConfig !== undefined) {
resSav.config = resource.emitConfig(); resSav.config = resource.emitConfig();

View File

@ -1,3 +1,5 @@
/// <reference path="./resource/SharedTypes.ts" />
const numberFormatDigits = 1; const numberFormatDigits = 1;
function formatNumber(num: number): string { function formatNumber(num: number): string {
@ -26,3 +28,12 @@ function formatNumber(num: number): string {
return `${sign}${number}`; return `${sign}${number}`;
} }
function resourceNumberSum(res: ResourceNumber): number {
let sum = 0;
for (const key in res) {
const rkey = <ResourceKey>key;
sum += res[rkey] ?? 0;
}
return sum;
}

View File

@ -10,7 +10,7 @@ class BuildingPermit extends Research {
'building permits', 'building permits',
'Unlocks several new buildings you can build outside of your compounds.' '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 => { public isUnlocked = (state: GameState): boolean => {

View File

@ -15,7 +15,7 @@ class Church extends Infrastructure {
undefined, undefined,
undefined undefined
); );
this.cost.money = config.cfgInitialCost.churches; this._baseCost.money = config.cfgInitialCost.churches;
this._costMultiplier.money = config.cfgCostMultiplier.churches; this._costMultiplier.money = config.cfgCostMultiplier.churches;
} }
@ -23,12 +23,15 @@ class Church extends Infrastructure {
(state.resource.compounds?.value ?? 0) * (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.churches ?? 0); (state.config.cfgCapacity.compounds?.churches ?? 0);
public inc = (state: GameState): number => { public inc = (state: GameState): ResourceNumber => {
// compound managers const inc: ResourceNumber = {};
return ( const compoundManagers =
(state.resource.compoundManagers?.value ?? 0) * (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 => { public isUnlocked = (state: GameState): boolean => {

View File

@ -11,7 +11,7 @@ class Compound extends Infrastructure {
'Provides space for tents, houses, and churches and a place to hide more money.', 'Provides space for tents, houses, and churches and a place to hide more money.',
true true
); );
this.cost.money = config.cfgInitialCost.compounds; this._baseCost.money = config.cfgInitialCost.compounds;
this._costMultiplier.money = config.cfgCostMultiplier.compounds; this._costMultiplier.money = config.cfgCostMultiplier.compounds;
} }

View File

@ -26,6 +26,9 @@ class Credibility extends Passive {
public max = (state: GameState): number => state.config.cfgPassiveMax; public max = (state: GameState): number => state.config.cfgPassiveMax;
public inc = (state: GameState): number => public inc = (state: GameState): ResourceNumber => {
state.config.cfgCredibilityRestoreRate; const inc: ResourceNumber = {};
inc.credibility = state.config.cfgCredibilityRestoreRate;
return inc;
};
} }

View File

@ -3,7 +3,7 @@
class CryptoCurrency extends Purchasable { class CryptoCurrency extends Purchasable {
public readonly resourceKey = ResourceKey.cryptoCurrency; public readonly resourceKey = ResourceKey.cryptoCurrency;
constructor(config: GameConfig) { constructor() {
super( super(
'FaithCoin', 'FaithCoin',
'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.", "A crypto coin that can't be spent directly, but provides a steady stream of passive income.",
true true
); );
this.cost.money = config.cfgInitialCost.cryptoCurrency;
this.valueInWholeNumbers = false; 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 => public max = (state: GameState): number =>
state.config.cfgInitialMax.cryptoCurrency ?? 0; state.config.cfgInitialMax.cryptoCurrency ?? 0;

View File

@ -40,17 +40,13 @@ class CryptoMarket extends Hidden {
) { ) {
adjustment = state.config.cfgCryptoCurrencyMinimumValue - this.value; adjustment = state.config.cfgCryptoCurrencyMinimumValue - this.value;
} }
//if (Math.abs(adjustment) > 0) { if (Math.abs(adjustment) > 0) {
this.addValue(adjustment, state); this.addValue(adjustment, state);
state.log( state.log(
`FaithCoin just ${ `FaithCoin just ${
adjustment > 0 ? 'increased' : 'decreased' adjustment > 0 ? 'increased' : 'decreased'
} in value by $${formatNumber(Math.abs(adjustment))}.` } in value by $${formatNumber(Math.abs(adjustment))}.`
); );
//}
if (crypto?.cost !== undefined) {
crypto.cost.money = this.value;
state.autoAction(); // cause redraw
} }
} }
}; };

View File

@ -39,12 +39,14 @@ class Follower extends Resource {
return max; return max;
}; };
public inc = (state: GameState): number => { public inc = (state: GameState): ResourceNumber => {
let inc = 0; const inc: ResourceNumber = {};
// pastor recruiting // pastor recruiting
const pastors = state.resource.pastors?.value ?? 0; const pastors =
inc += pastors * state.config.cfgPastorRecruitRate; (state.resource.pastors?.value ?? 0) * state.config.cfgPastorRecruitRate;
if (pastors > 0) inc.pastors = pastors;
// credibility adjustment // credibility adjustment
// this should be based on notoriety instead // this should be based on notoriety instead

View File

@ -13,7 +13,7 @@ class House extends Infrastructure {
)} followers.`, )} followers.`,
true true
); );
this.cost.money = config.cfgInitialCost.houses; this._baseCost.money = config.cfgInitialCost.houses;
this._costMultiplier.money = config.cfgCostMultiplier.houses; this._costMultiplier.money = config.cfgCostMultiplier.houses;
} }
@ -21,12 +21,15 @@ class House extends Infrastructure {
(state.resource.compounds?.value ?? 0) * (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.houses ?? 0); (state.config.cfgCapacity.compounds?.houses ?? 0);
public inc = (state: GameState): number => { public inc = (state: GameState): ResourceNumber => {
// compound managers const inc: ResourceNumber = {};
return ( const compoundManagers =
(state.resource.compoundManagers?.value ?? 0) * (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 => { public isUnlocked = (state: GameState): boolean => {

View File

@ -4,10 +4,9 @@ abstract class Job extends Resource {
public readonly resourceType = ResourceType.job; public readonly resourceType = ResourceType.job;
public readonly valueInWholeNumbers = true; public readonly valueInWholeNumbers = true;
public readonly cost: ResourceNumber = {};
public max?: (state: GameState) => number = undefined; public max?: (state: GameState) => number = undefined;
public inc?: (state: GameState) => number = undefined; public inc?: (state: GameState) => ResourceNumber = undefined;
public userActions: ResourceAction[] = [ public userActions: ResourceAction[] = [
{ {
@ -31,7 +30,6 @@ abstract class Job extends Resource {
}, },
]; ];
protected _costMultiplier: { [key in ResourceKey]?: number } = {};
protected _isUnlocked = false; protected _isUnlocked = false;
constructor( constructor(
@ -102,18 +100,9 @@ abstract class Job extends Resource {
private _promoteFollower(state: GameState): void { private _promoteFollower(state: GameState): void {
if (Job.availableJobs(state) <= 0) return; if (Job.availableJobs(state) <= 0) return;
if ( if (this.max !== undefined && this.value < this.max(state)) {
this.max !== undefined &&
this.value < this.max(state) &&
state.deductCost(this.cost)
) {
this.addValue(1, state); this.addValue(1, state);
state.log(this._hireLog(1, state)); state.log(this._hireLog(1, state));
for (const key in this._costMultiplier) {
const rkey = <ResourceKey>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; if (this.value <= 0) return;
this.addValue(-1, state); this.addValue(-1, state);
state.log(this._hireLog(-1, state)); state.log(this._hireLog(-1, state));
for (const key in this._costMultiplier) {
const rkey = <ResourceKey>key;
this.cost[rkey] =
(this.cost[rkey] ?? 0) / (this._costMultiplier[rkey] ?? 1);
}
} }
} }

View File

@ -13,7 +13,7 @@ class Megachurch extends Infrastructure {
)} pastors`, )} pastors`,
true true
); );
this.cost.money = config.cfgInitialCost.megaChurches; this._baseCost.money = config.cfgInitialCost.megaChurches;
this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; this._costMultiplier.money = config.cfgCostMultiplier.megaChurches;
} }

View File

@ -40,11 +40,11 @@ class Money extends Resource {
return max; return max;
}; };
public inc = (state: GameState): number => { public inc = (state: GameState): ResourceNumber => {
let inc = 0; const inc: ResourceNumber = {};
// tithings // tithings
inc += const tithings =
((state.resource.pastors?.value ?? 0) * ((state.resource.pastors?.value ?? 0) *
(state.resource.followers?.value ?? 0) * (state.resource.followers?.value ?? 0) *
(state.config.cfgTitheAmount ?? 0) * (state.config.cfgTitheAmount ?? 0) *
@ -52,10 +52,13 @@ class Money extends Resource {
state.config.cfgTimeBetweenTithes; state.config.cfgTimeBetweenTithes;
// salaries // salaries
inc -= const compoundManagers =
(state.resource.compoundManagers?.value ?? 0) * (state.resource.compoundManagers?.value ?? 0) *
(state.config.cfgSalary.compoundManagers ?? 0); (state.config.cfgSalary.compoundManagers ?? 0);
if (tithings > 0) inc.pastors = tithings;
if (compoundManagers > 0) inc.compoundManagers = compoundManagers * -1;
return inc; return inc;
}; };

View File

@ -4,10 +4,9 @@ abstract class Purchasable extends Resource {
public readonly resourceType: ResourceType = ResourceType.purchasable; public readonly resourceType: ResourceType = ResourceType.purchasable;
public valueInWholeNumbers = true; public valueInWholeNumbers = true;
public cost: ResourceNumber = {};
public inc?: (state: GameState) => number = undefined;
public max?: (state: GameState) => number = undefined; public max?: (state: GameState) => number = undefined;
public inc?: (state: GameState) => ResourceNumber = undefined;
public userActions: ResourceAction[] = [ public userActions: ResourceAction[] = [
{ {
@ -15,19 +14,17 @@ abstract class Purchasable extends Resource {
description: this._purchaseDescription, description: this._purchaseDescription,
isEnabled: (state: GameState): boolean => isEnabled: (state: GameState): boolean =>
(this.max === undefined || this.value < this.max(state)) && (this.max === undefined || this.value < this.max(state)) &&
state.isPurchasable(this.cost), state.isPurchasable(this.cost(state)),
performAction: (state: GameState): void => { performAction: (state: GameState): void => {
this._purchase(state); this._purchase(state);
}, },
}, },
]; ];
protected _costMultiplier: ResourceNumber = {}; protected readonly _baseCost: ResourceNumber = {};
protected _sellMultiplier?: number | ResourceNumber; protected readonly _costMultiplier: ResourceNumber = {};
protected _isUnlocked = false; protected _isUnlocked = false;
private _lastWholeNumberValue = 0;
constructor( constructor(
public readonly label: string, public readonly label: string,
public readonly singularName: 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 = <ResourceKey>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 => { public isUnlocked = (state: GameState): boolean => {
if (!this._isUnlocked && state.isPurchasable(this.cost)) { if (!this._isUnlocked && state.isPurchasable(this.cost(state))) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; 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 = <ResourceKey>key;
this.cost[rkey] =
(this.cost[rkey] ?? 0) *
(this._costMultiplier[rkey] ?? 1) *
wholeNumberChange;
}
this._lastWholeNumberValue = this.value;
}
};
protected _purchaseLog(amount: number, _state: GameState): string { protected _purchaseLog(amount: number, _state: GameState): string {
let verb = 'purchased'; let verb = 'purchased';
if (amount < 0) { if (amount < 0) {
@ -101,7 +96,7 @@ abstract class Purchasable extends Resource {
private _purchase(state: GameState): void { private _purchase(state: GameState): void {
if (this.max !== undefined && this.value >= this.max(state)) return; 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); this.addValue(1, state);
state.log(this._purchaseLog(1, state)); state.log(this._purchaseLog(1, state));
} }
@ -109,25 +104,17 @@ abstract class Purchasable extends Resource {
private _sell(state: GameState): void { private _sell(state: GameState): void {
if (this.value <= 0) return; if (this.value <= 0) return;
const costBack: ResourceNumber = {};
for (const key in this.cost) {
const rkey = <ResourceKey>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); this.addValue(-1, state);
state.log(this._purchaseLog(-1, state)); state.log(this._purchaseLog(-1, state));
const costBack: ResourceNumber = {};
for (const key in this.cost(state)) {
const rkey = <ResourceKey>key;
const cost = this.cost(state)[rkey];
if (cost === undefined) continue;
costBack[rkey] = cost * state.config.cfgSellCostBackMultiplier * -1;
}
state.deductCost(costBack);
} }
} }

View File

@ -1,6 +1,10 @@
/// <reference path="./IResource.ts" /> /// <reference path="./IResource.ts" />
abstract class Resource implements IResource { 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; protected rawValue = 0;
public abstract readonly resourceType: ResourceType; public abstract readonly resourceType: ResourceType;

View File

@ -13,7 +13,7 @@ class Tent extends Infrastructure {
)} followers.`, )} followers.`,
true true
); );
this.cost.money = config.cfgInitialCost.tents; this._baseCost.money = config.cfgInitialCost.tents;
this._costMultiplier.money = config.cfgCostMultiplier.tents; this._costMultiplier.money = config.cfgCostMultiplier.tents;
} }
@ -26,8 +26,14 @@ class Tent extends Infrastructure {
return max; return max;
}; };
public inc = (state: GameState): number => public inc = (state: GameState): ResourceNumber => {
// compound managers const inc: ResourceNumber = {};
(state.resource.compoundManagers?.value ?? 0) * const compoundManagers =
(state.config.cfgBuySpeed.compoundManagers?.tents ?? 0); (state.resource.compoundManagers?.value ?? 0) *
(state.config.cfgBuySpeed.compoundManagers?.tents ?? 0);
if (compoundManagers > 0) {
inc.compoundManagers = compoundManagers;
}
return inc;
};
} }

View File

@ -73,7 +73,7 @@ class DebugRenderer implements IRenderer {
} }
if ( if (
resource.cost !== undefined && resource.cost !== undefined &&
Object.keys(resource.cost).length !== 0 Object.keys(resource.cost(state)).length !== 0
) { ) {
content += "<br>Cost: <span class='resource-cost'></span>"; content += "<br>Cost: <span class='resource-cost'></span>";
} }
@ -81,7 +81,6 @@ class DebugRenderer implements IRenderer {
resContainer.appendChild(el); resContainer.appendChild(el);
if (resource.userActions !== undefined) { if (resource.userActions !== undefined) {
for (let i = 0; i < resource.userActions.length; i++) { for (let i = 0; i < resource.userActions.length; i++) {
const action = resource.userActions[i];
const btn = document.getElementById(`resource-btn-${rkey}-${i}`); const btn = document.getElementById(`resource-btn-${rkey}-${i}`);
btn?.addEventListener('click', (): void => { btn?.addEventListener('click', (): void => {
state.performAction(rkey, i); state.performAction(rkey, i);
@ -135,13 +134,17 @@ class DebugRenderer implements IRenderer {
} }
} }
} }
const inc = if (resource.inc !== undefined) {
resource.inc !== undefined ? resource.inc(state) : undefined; const resInc = resource.inc(state);
const elI = el.getElementsByClassName('resource-inc')[0]; const inc = resourceNumberSum(resInc);
if (inc !== undefined && inc !== 0) { const elI = el.getElementsByClassName('resource-inc')[0];
elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`; if (inc !== 0) {
} else if (elI.innerHTML !== '') { elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`;
elI.innerHTML = ''; elI.setAttribute('title', this._getIncDetails(resource, state));
} else if (elI.innerHTML !== '') {
elI.innerHTML = '';
elI.removeAttribute('title');
}
} }
if (this._handleClick) { if (this._handleClick) {
const elC = el.getElementsByClassName('resource-cost'); const elC = el.getElementsByClassName('resource-cost');
@ -177,17 +180,37 @@ class DebugRenderer implements IRenderer {
private _getCostStr(resource: IResource, state: GameState): string { private _getCostStr(resource: IResource, state: GameState): string {
let cost = ''; let cost = '';
if (resource.cost === undefined) return cost;
for (const rkey of state.resources) { 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 (cost !== '') cost += ', ';
if (rkey === ResourceKey.money) { if (rkey === ResourceKey.money) {
cost += `$${formatNumber(resource.cost[rkey] ?? 0)}`; cost += `$${formatNumber(rcost)}`;
} else { } else {
cost += `${formatNumber(resource.cost[rkey] ?? 0)} cost += `${formatNumber(rcost)} ${
${state.resource[rkey]?.pluralName ?? rkey}`; (rcost > 1
? state.resource[rkey]?.pluralName
: state.resource[rkey]?.singularName) ?? rkey
}`;
} }
} }
} }
return cost; 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;
}
} }