From ce3ae26b4bd3bc00e6d8bc770233fd930dd4b49e Mon Sep 17 00:00:00 2001 From: Rudis Muiznieks Date: Sun, 5 Sep 2021 20:43:11 -0500 Subject: [PATCH] config organization --- src/model/GameConfig.ts | 76 +++++++++++++++------------- src/model/GameState.ts | 5 +- src/model/resource/BuildingPermit.ts | 2 +- src/model/resource/Church.ts | 8 +-- src/model/resource/Compound.ts | 7 +-- src/model/resource/CryptoCurrency.ts | 6 +-- src/model/resource/House.ts | 8 +-- src/model/resource/IResource.ts | 4 +- src/model/resource/Job.ts | 27 ++++++++-- src/model/resource/MegaChurch.ts | 8 +-- src/model/resource/Money.ts | 6 +-- src/model/resource/Pastor.ts | 10 ++-- src/model/resource/PlayerOrg.ts | 10 ++-- src/model/resource/Purchasable.ts | 4 +- src/model/resource/Tent.ts | 10 ++-- 15 files changed, 108 insertions(+), 83 deletions(-) diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts index 692e606..84211b0 100644 --- a/src/model/GameConfig.ts +++ b/src/model/GameConfig.ts @@ -27,49 +27,55 @@ class GameConfig { public relNoneShare = 0.16; // general configs - public cfgPassiveMax = 100; + public cfgInitialMax: ResourceNumber = { + cryptoCurrency: 1000, + megaChurches: 2, + money: 500000, + playerOrg: 5, + tents: 5, + }; + + public cfgInitialCost: ResourceNumber = { + buildingPermit: 250000, + churches: 150000, + compounds: 15000, + cryptoCurrency: 100, + houses: 75000, + megaChurches: 750000, + tents: 250, + }; + + public cfgCostMultiplier: ResourceNumber = { + churches: 1.01, + compounds: 1.5, + cryptoCurrency: 1.1, + houses: 1.01, + megaChurches: 1.01, + tents: 1.05, + }; + + public cfgSalary: ResourceNumber = { + pastors: 7.5, + }; + + public cfgCapacity: { [key in ResourceKey]?: ResourceNumber } = { + churches: { pastors: 2 }, + compounds: { churches: 1, houses: 2, money: 500000, tents: 10 }, + houses: { playerOrg: 10 }, + megaChurches: { pastors: 5 }, + tents: { playerOrg: 2 }, + }; public cfgCredibilityFollowerLossRatio = 0.04; public cfgCredibilityFollowerLossTime = 10000; public cfgCredibilityRestoreRate = 0.25; - + public cfgCryptoReturnAmount = 1; + public cfgFollowerGainLossLogTimer = 10000; + public cfgPassiveMax = 100; public cfgPastorRecruitRate = 0.01; public cfgPastorTitheCollectionFollowerMax = 100; - public cfgPastorSalary = 7.5; - - public cfgFollowerGainLossLogTimer = 10000; - public cfgFollowerStartingMax = 5; - public cfgTimeBetweenTithes = 30000; public cfgTitheAmount = 10; - public cfgCryptoReturnAmount = 1; - public cfgMoneyStartingMax = 500000; - - public cfgBuildingPermitCost = 250000; - - public cfgChurchCostMultiplier = 1.01; - public cfgChurchPastorCapacity = 2; - public cfgChurchStartingCost = 150000; - public cfgCompoundChurchCapacity = 1; - public cfgCompoundCostMultiplier = 1.5; - public cfgCompoundHouseCapacity = 2; - public cfgCompoundMoneyCapacity = 500000; - public cfgCompoundStartingCost = 15000; - public cfgCompoundTentCapacity = 10; - public cfgCryptoCostMultiplier = 1.1; - public cfgCryptoStartingCost = 100; - public cfgCryptoStartingMax = 1000; - public cfgHouseCostMultiplier = 1.01; - public cfgHouseFollowerCapacity = 10; - public cfgHouseStartingCost = 75000; - public cfgMegaChurchCostMultiplier = 1.01; - public cfgMegaChurchPastorCapacity = 5; - public cfgMegaChurchStartingCost = 7500000; - public cfgMegaChurchStartingMax = 2; - public cfgTentCostMultiplier = 1.05; - public cfgTentFollowerCapacity = 2; - public cfgTentStartingCost = 250; - public cfgTentStartingMax = 5; public generateState (): GameState { const state = new GameState(this); diff --git a/src/model/GameState.ts b/src/model/GameState.ts index 8b946e6..fe90da2 100644 --- a/src/model/GameState.ts +++ b/src/model/GameState.ts @@ -81,7 +81,7 @@ class GameState { } } - public deductCost (cost: { [key in ResourceKey]?: number } | null): boolean { + public deductCost (cost: ResourceNumber | null): boolean { if (cost === null) return true; if (!this.isPurchasable(cost)) return false; for (const key in cost) { @@ -94,8 +94,7 @@ class GameState { return true; } - public isPurchasable ( - cost?: { [key in ResourceKey]?: number }): boolean { + public isPurchasable (cost?: ResourceNumber): boolean { if (cost === undefined) return true; for (const key in cost) { const rkey = key; diff --git a/src/model/resource/BuildingPermit.ts b/src/model/resource/BuildingPermit.ts index fffa99a..97f76f5 100644 --- a/src/model/resource/BuildingPermit.ts +++ b/src/model/resource/BuildingPermit.ts @@ -4,7 +4,7 @@ class BuildingPermit extends Research { constructor (config: GameConfig) { super('Building Permit', 'Unlocks several new buildings you can build outside of your compounds.'); - this.cost.money = config.cfgBuildingPermitCost; + this.cost.money = config.cfgInitialMax.buildingPermit; } public isUnlocked (state: GameState): boolean { diff --git a/src/model/resource/Church.ts b/src/model/resource/Church.ts index 7fe3017..6e4e36b 100644 --- a/src/model/resource/Church.ts +++ b/src/model/resource/Church.ts @@ -3,14 +3,14 @@ class Church extends Infrastructure { constructor (config: GameConfig) { super('Churches', - `Preaching grounds for ${config.formatNumber(config.cfgChurchPastorCapacity)} pastors.`); - this.cost.money = config.cfgChurchStartingCost; - this._costMultiplier.money = config.cfgChurchCostMultiplier; + `Preaching grounds for ${config.formatNumber(config.cfgCapacity.churches?.pastors ?? 0)} pastors.`); + this.cost.money = config.cfgInitialCost.churches; + this._costMultiplier.money = config.cfgCostMultiplier.churches; } public max: (state: GameState) => number = (state) => (state.resource.compounds?.value ?? 0) - * state.config.cfgCompoundChurchCapacity; + * (state.config.cfgCapacity.compounds?.churches ?? 0); public isUnlocked (state: GameState): boolean { if (this._isUnlocked) return true; diff --git a/src/model/resource/Compound.ts b/src/model/resource/Compound.ts index 0a0333b..f1354c0 100644 --- a/src/model/resource/Compound.ts +++ b/src/model/resource/Compound.ts @@ -4,14 +4,15 @@ class Compound extends Infrastructure { constructor (config: GameConfig) { super('Compounds', 'Provides space for tents, houses, and churches and a place to hide more money.'); - this.cost.money = config.cfgCompoundStartingCost; - this._costMultiplier.money = config.cfgCompoundCostMultiplier; + this.cost.money = config.cfgInitialCost.compounds; + this._costMultiplier.money = config.cfgCostMultiplier.compounds; } public isUnlocked (state: GameState): boolean { if (this._isUnlocked) return true; const tents = state.resource.tents; - if (tents !== undefined && tents.value >= state.config.cfgTentStartingMax) { + if (tents !== undefined + && tents.value >= (state.config.cfgInitialMax.tents ?? 0)) { this._isUnlocked = true; } return this._isUnlocked; diff --git a/src/model/resource/CryptoCurrency.ts b/src/model/resource/CryptoCurrency.ts index f96fadd..190f7b3 100644 --- a/src/model/resource/CryptoCurrency.ts +++ b/src/model/resource/CryptoCurrency.ts @@ -4,11 +4,11 @@ class CryptoCurrency extends Purchasable { constructor (config: GameConfig) { super('Faithcoin', "A crypto coin that can't be spent directly, but provides a steady stream of passive income."); - this.cost.money = config.cfgCryptoStartingCost; - this._costMultiplier.money = config.cfgCryptoCostMultiplier; + this.cost.money = config.cfgInitialCost.cryptoCurrency; + this._costMultiplier.money = config.cfgCostMultiplier.cryptoCurrency; this.valueInWholeNumbers = false; } public max: (state: GameState) => number = (state) => - state.config.cfgCryptoStartingMax; + state.config.cfgInitialMax.cryptoCurrency ?? 0; } diff --git a/src/model/resource/House.ts b/src/model/resource/House.ts index 3562f35..835bdca 100644 --- a/src/model/resource/House.ts +++ b/src/model/resource/House.ts @@ -3,14 +3,14 @@ class House extends Infrastructure { constructor (config: GameConfig) { super('Houses', - `Provides room to house ${config.formatNumber(config.cfgHouseFollowerCapacity)} followers.`); - this.cost.money = config.cfgHouseStartingCost; - this._costMultiplier.money = config.cfgHouseCostMultiplier; + `Provides room to house ${config.formatNumber(config.cfgCapacity.houses?.playerOrg ?? 0)} followers.`); + this.cost.money = config.cfgInitialCost.houses; + this._costMultiplier.money = config.cfgCostMultiplier.houses; } public max: (state: GameState) => number = (state) => (state.resource.compounds?.value ?? 0) - * state.config.cfgCompoundHouseCapacity; + * (state.config.cfgCapacity.compounds?.houses ?? 0); public isUnlocked (state: GameState): boolean { if (this._isUnlocked) return true; diff --git a/src/model/resource/IResource.ts b/src/model/resource/IResource.ts index c7a2f99..36a0186 100644 --- a/src/model/resource/IResource.ts +++ b/src/model/resource/IResource.ts @@ -29,6 +29,8 @@ enum ResourceKey { credibility = 'credibility', } +type ResourceNumber = { [key in ResourceKey]?: number }; + interface IResource { readonly resourceType: ResourceType; readonly name: string; @@ -36,7 +38,7 @@ interface IResource { readonly valueInWholeNumbers: boolean; readonly value: number; - readonly cost?: { [key in ResourceKey]?: number }; + readonly cost?: ResourceNumber; readonly clickText?: string; readonly clickDescription?: string; diff --git a/src/model/resource/Job.ts b/src/model/resource/Job.ts index cd51f04..77b965a 100644 --- a/src/model/resource/Job.ts +++ b/src/model/resource/Job.ts @@ -6,7 +6,7 @@ abstract class Job implements IResource { public readonly clickText = 'Hire'; public readonly clickDescription = 'Promote one of your followers.'; public value = 0; - public readonly cost: { [key in ResourceKey]?: number } = { }; + public readonly cost: ResourceNumber = { }; public max?: (state: GameState) => number = undefined; public inc?: (state: GameState) => number = undefined; @@ -38,13 +38,19 @@ abstract class Job implements IResource { public addValue (amount: number): void { this.value += amount; + if (this.value < 0) this.value = 0; } public isUnlocked (_state: GameState): boolean { return this._isUnlocked; } - public advanceAction (_time: number, _state: GameState): void { + public advanceAction (_time: number, state: GameState): void { + // if we're out of followers then the jobs also vacate + const avail = this._availableJobs(state); + if (avail < 0 && this.value > 0) { + this.addValue(avail); + } return; } @@ -58,9 +64,20 @@ abstract class Job implements IResource { ? tot + res.value : tot; }, 0); - let max = followers - hired; - if (max < 0) max = 0; - return max; + return followers - hired; + } + + protected _totalPayroll (state: GameState): number { + // number of followers minus the number of filled jobs + const followers = state.resource.playerOrg?.value ?? 0; + const hired = state.resources.reduce( + (tot: number, rkey: ResourceKey): number => { + const res = state.resource[rkey]; + return res?.resourceType === ResourceType.job + ? tot + res.value + : tot; + }, 0); + return followers - hired; } protected _hireLog (amount: number, _state: GameState): string { diff --git a/src/model/resource/MegaChurch.ts b/src/model/resource/MegaChurch.ts index eb8dbde..26f29e7 100644 --- a/src/model/resource/MegaChurch.ts +++ b/src/model/resource/MegaChurch.ts @@ -3,13 +3,13 @@ class MegaChurch extends Infrastructure { constructor (config: GameConfig) { super('MegaChurches', - `Room for ${config.formatNumber(config.cfgMegaChurchPastorCapacity)} pastors`); - this.cost.money = config.cfgMegaChurchStartingCost; - this._costMultiplier.money = config.cfgMegaChurchCostMultiplier; + `Room for ${config.formatNumber(config.cfgCapacity.megaChurches?.pastors ?? 0)} pastors`); + this.cost.money = config.cfgInitialCost.megaChurches; + this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; } public max: (state: GameState) => number = (state) => - state.config.cfgMegaChurchStartingMax; + state.config.cfgInitialMax.megaChurches ?? 0; public isUnlocked (state: GameState): boolean { if (this._isUnlocked) return true; diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts index 13c9df9..8654cf0 100644 --- a/src/model/resource/Money.ts +++ b/src/model/resource/Money.ts @@ -16,9 +16,9 @@ class Money extends Purchasable { } public max: (state: GameState) => number = (state: GameState) => { - let max = state.config.cfgMoneyStartingMax; + let max = state.config.cfgInitialMax.money ?? 0; max += (state.resource.compounds?.value ?? 0) - * state.config.cfgCompoundMoneyCapacity; + * (state.config.cfgCapacity.compounds?.money ?? 0); return max; }; @@ -31,7 +31,7 @@ class Money extends Purchasable { // salaries inc -= (state.resource.pastors?.value ?? 0) - * state.config.cfgPastorSalary; + * (state.config.cfgSalary.pastors ?? 0); return inc; }; diff --git a/src/model/resource/Pastor.ts b/src/model/resource/Pastor.ts index adbe587..cdc9fc9 100644 --- a/src/model/resource/Pastor.ts +++ b/src/model/resource/Pastor.ts @@ -8,12 +8,11 @@ class Pastor extends Job { 'Collect tithings for you and recruit new members from other faiths automatically.'); } - public max: (state: GameState) => number = (state) => { let max = (state.resource.churches?.value ?? 0) - * state.config.cfgChurchPastorCapacity; + * (state.config.cfgCapacity.churches?.pastors ?? 0); max += (state.resource.megaChurches?.value ?? 0) - * state.config.cfgMegaChurchPastorCapacity; + * (state.config.cfgCapacity.megaChurches?.pastors ?? 0); return max; }; @@ -24,12 +23,13 @@ class Pastor extends Job { } public advanceAction (time: number, state: GameState): void { + super.advanceAction(time, state); this._timeSinceLastTithe += time; if (this._timeSinceLastTithe >= state.config.cfgTimeBetweenTithes) { const money = state.resource.money; const plorg = state.resource.playerOrg; - let tithed = this.value - * state.config.cfgPastorTitheCollectionFollowerMax; + let tithed = Math.floor(this.value + * state.config.cfgPastorTitheCollectionFollowerMax); if (Math.floor(plorg?.value ?? 0) < tithed) tithed = Math.floor(plorg?.value ?? 0); let collected = tithed * state.config.cfgTitheAmount; diff --git a/src/model/resource/PlayerOrg.ts b/src/model/resource/PlayerOrg.ts index 7d067b7..44e41c0 100644 --- a/src/model/resource/PlayerOrg.ts +++ b/src/model/resource/PlayerOrg.ts @@ -11,15 +11,15 @@ class PlayerOrg implements IResource { private _timeSinceLastLost = 0; private _lastRecruitmentLog = 0; - private _followerSources: { [key in ResourceKey]?: number } = { }; - private _followerDests: { [key in ResourceKey]?: number } = { }; + private _followerSources: ResourceNumber = { }; + private _followerDests: ResourceNumber = { }; public max (state: GameState): number { - let max = state.config.cfgFollowerStartingMax; + let max = state.config.cfgInitialMax.playerOrg ?? 0; max += (state.resource.tents?.value ?? 0) - * state.config.cfgTentFollowerCapacity; + * (state.config.cfgCapacity.tents?.playerOrg ?? 0); max += (state.resource.houses?.value ?? 0) - * state.config.cfgHouseFollowerCapacity; + * (state.config.cfgCapacity.houses?.playerOrg ?? 0); return max; } diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts index 49f4688..3195b5b 100644 --- a/src/model/resource/Purchasable.ts +++ b/src/model/resource/Purchasable.ts @@ -6,12 +6,12 @@ abstract class Purchasable implements IResource { public clickText = 'Purchase'; public clickDescription = 'Purchase'; public value = 0; - public readonly cost: { [key in ResourceKey]?: number } = { }; + public readonly cost: ResourceNumber = { }; public inc?: (state: GameState) => number = undefined; public max?: (_state: GameState) => number = undefined; - protected _costMultiplier: { [key in ResourceKey]?: number } = { }; + protected _costMultiplier: ResourceNumber = { }; protected _isUnlocked = false; constructor ( diff --git a/src/model/resource/Tent.ts b/src/model/resource/Tent.ts index 05a9f50..86ea24b 100644 --- a/src/model/resource/Tent.ts +++ b/src/model/resource/Tent.ts @@ -3,16 +3,16 @@ class Tent extends Infrastructure { constructor (config: GameConfig) { super('Tents', - `Provides room to house ${config.formatNumber(config.cfgTentFollowerCapacity)} followers.`); - this.cost.money = config.cfgTentStartingCost; - this._costMultiplier.money = config.cfgTentCostMultiplier; + `Provides room to house ${config.formatNumber(config.cfgCapacity.tents?.playerOrg ?? 0)} followers.`); + this.cost.money = config.cfgInitialCost.tents; + this._costMultiplier.money = config.cfgCostMultiplier.tents; } public max: (state: GameState) => number = (state) => { // ten extra tents per compound - let max = state.config.cfgTentStartingMax; + let max = state.config.cfgInitialMax.tents ?? 0; max += (state.resource.compounds?.value ?? 0) - * state.config.cfgCompoundTentCapacity; + * (state.config.cfgCapacity.compounds?.tents ?? 0); return max; }; }