diff --git a/src/model/GameState.ts b/src/model/GameState.ts index 8d3fa15..8b946e6 100644 --- a/src/model/GameState.ts +++ b/src/model/GameState.ts @@ -45,7 +45,7 @@ class GameState { for (const rkey of this._resourceKeys) { const resource = this._resources[rkey]; if (resource?.isUnlocked(this) === true) { - if (resource.advanceAction !== null) + if (resource.advanceAction !== undefined) resource.advanceAction(time, this); } } @@ -55,12 +55,12 @@ class GameState { const resource = this._resources[rkey]; if (resource === undefined || !resource.isUnlocked(this)) continue; - if (resource.inc !== null && (resource.max === null + if (resource.inc !== undefined && (resource.max === undefined || resource.value < resource.max(this))) { resource.addValue(resource.inc(this) * time / 1000, this); } - if (resource.max !== null && resource.value > resource.max(this)) { + if (resource.max !== undefined && resource.value > resource.max(this)) { resource.addValue((resource.value - resource.max(this)) * -1, this); } if (resource.value < 0) { @@ -73,7 +73,7 @@ class GameState { const resource = this._resources[resourceKey]; if (resource === undefined || !resource.isUnlocked(this)) return; - if (resource.clickAction !== null) { + if (resource.clickAction !== undefined) { resource.clickAction(this); for (const callback of this.onResourceClick) { callback(); @@ -95,8 +95,8 @@ class GameState { } public isPurchasable ( - cost: { [key in ResourceKey]?: number } | null): boolean { - if (cost === null) return true; + cost?: { [key in ResourceKey]?: number }): boolean { + if (cost === undefined) return true; for (const key in cost) { const rkey = key; if ((this._resources[rkey]?.value ?? 0) < (cost[rkey] ?? 0)) { @@ -120,9 +120,11 @@ class GameState { }; for (const key in this._resources) { const rkey = key; + const resource = this._resources[rkey]; + if (resource === undefined) continue; saveObj[rkey] = { - value: this._resources[rkey]?.value ?? 0, - cost: this._resources[rkey]?.cost ?? null, + value: resource.value, + cost: resource.cost, }; } const saveStr: string = btoa(JSON.stringify(saveObj)); @@ -139,13 +141,13 @@ class GameState { const rkey = key; const saveRes = <{ value: number; - cost: { [key: string]: number } | null; + cost?: { [key: string]: number }; } | undefined> saveObj[key]; if (saveRes !== undefined) { // @ts-expect-error writing read-only value from save data this._resources[rkey].value = saveRes.value; // @ts-expect-error writing read-only cost from save data - this._resources[rkey].cost = saveRes.cost ?? null; + this._resources[rkey].cost = saveRes.cost; } } } else { @@ -172,7 +174,7 @@ class GameState { type SaveData = { [key: string]: { value: number; - cost: { [key: string]: number } | null; + cost?: { [key: string]: number }; } | { maj: number, min: number } | undefined; version?: { maj: number, min: number }; }; diff --git a/src/model/resource/Church.ts b/src/model/resource/Church.ts index 5e72ebd..7fe3017 100644 --- a/src/model/resource/Church.ts +++ b/src/model/resource/Church.ts @@ -9,7 +9,8 @@ class Church extends Infrastructure { } public max: (state: GameState) => number = (state) => - state.resource.compounds?.value ?? 0; + (state.resource.compounds?.value ?? 0) + * state.config.cfgCompoundChurchCapacity; 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 40343ef..c7a2f99 100644 --- a/src/model/resource/IResource.ts +++ b/src/model/resource/IResource.ts @@ -34,18 +34,21 @@ interface IResource { readonly name: string; readonly description: string; readonly valueInWholeNumbers: boolean; - readonly clickText: string | null; - readonly clickDescription: string | null; + + readonly value: number; + readonly cost?: { [key in ResourceKey]?: number }; + + readonly clickText?: string; + readonly clickDescription?: string; // readonly altClickText?: string; // readonly altClickDescription?: string; - readonly value: number; - readonly cost: { [key in ResourceKey]?: number } | null; - max: ((state: GameState) => number) | null; - inc: ((state: GameState) => number) | null; - clickAction: ((state: GameState) => void) | null; + max?: (state: GameState) => number; + inc?: (state: GameState) => number; + clickAction?: (state: GameState) => void; // altClickAction (state: GameState): void; + advanceAction?: (time: number, state: GameState) => void; + addValue: (amount: number, state: GameState) => void; isUnlocked: (state: GameState) => boolean; - advanceAction: ((time: number, state: GameState) => void) | null; } diff --git a/src/model/resource/Job.ts b/src/model/resource/Job.ts index f8f3c23..cd51f04 100644 --- a/src/model/resource/Job.ts +++ b/src/model/resource/Job.ts @@ -8,8 +8,8 @@ abstract class Job implements IResource { public value = 0; public readonly cost: { [key in ResourceKey]?: number } = { }; - public max: ((state: GameState) => number) | null = null; - public inc: ((state: GameState) => number) | null = null; + public max?: (state: GameState) => number = undefined; + public inc?: (state: GameState) => number = undefined; protected _costMultiplier: { [key in ResourceKey]?: number } = { }; protected _isUnlocked = false; @@ -19,13 +19,12 @@ abstract class Job implements IResource { public readonly description: string ) { } - public clickAction (state: GameState): void { if (this._availableJobs(state) <= 0) { state.log('You have no unemployed followers to promote.'); return; } - if (this.max !== null && this.value < this.max(state) + if (this.max !== undefined && this.value < this.max(state) && state.deductCost(this.cost)) { this.addValue(1); state.log(this._hireLog(1, state)); diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts index 97ceea4..13c9df9 100644 --- a/src/model/resource/Money.ts +++ b/src/model/resource/Money.ts @@ -29,6 +29,10 @@ class Money extends Purchasable { inc += (state.resource.cryptoCurrency?.value ?? 0) * state.config.cfgCryptoReturnAmount; + // salaries + inc -= (state.resource.pastors?.value ?? 0) + * state.config.cfgPastorSalary; + return inc; }; diff --git a/src/model/resource/Passive.ts b/src/model/resource/Passive.ts index 5d1809d..ef3f054 100644 --- a/src/model/resource/Passive.ts +++ b/src/model/resource/Passive.ts @@ -3,17 +3,9 @@ abstract class Passive implements IResource { public readonly resourceType = ResourceType.passive; public readonly valueInWholeNumbers = false; - public readonly clickText = null; - public readonly clickDescription = null; public value = 0; - public readonly cost = null; - public readonly clickAction = null; - - public max: ((state: GameState) => number) | null = null; - public inc: ((state: GameState) => number) | null = null; - public advanceAction: ( - (time: number, state: GameState) => void) | null = null; + public advanceAction?: (time: number, state: GameState) => void = undefined; constructor ( public readonly name: string, diff --git a/src/model/resource/Pastor.ts b/src/model/resource/Pastor.ts index 46a6a5a..adbe587 100644 --- a/src/model/resource/Pastor.ts +++ b/src/model/resource/Pastor.ts @@ -8,6 +8,7 @@ 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; @@ -32,9 +33,9 @@ class Pastor extends Job { if (Math.floor(plorg?.value ?? 0) < tithed) tithed = Math.floor(plorg?.value ?? 0); let collected = tithed * state.config.cfgTitheAmount; - if (money?.max !== null - && collected > (money?.max(state) ?? 0) - (money?.value ?? 0)) - collected = (money?.max(state) ?? 0) - (money?.value ?? 0); + if (money?.max !== undefined + && collected > money.max(state) - money.value) + collected = money.max(state) - money.value; if (collected > 0) { money?.addValue(collected, state); state.log(`Your pastors collected $${state.config.formatNumber(collected)} in tithings from ${state.config.formatNumber(tithed)} followers.`); diff --git a/src/model/resource/PlayerOrg.ts b/src/model/resource/PlayerOrg.ts index a4bb00a..7d067b7 100644 --- a/src/model/resource/PlayerOrg.ts +++ b/src/model/resource/PlayerOrg.ts @@ -8,7 +8,6 @@ class PlayerOrg implements IResource { public readonly clickText = 'Recruit'; public readonly clickDescription = 'Gather new followers.'; public value = 0; - public readonly cost = null; private _timeSinceLastLost = 0; private _lastRecruitmentLog = 0; @@ -33,8 +32,7 @@ class PlayerOrg implements IResource { // credibility adjustment const creds = state.resource.credibility; - if (creds?.max !== null) inc *= - (creds?.value ?? 0) / (creds?.max(state) ?? state.config.cfgPassiveMax); + if (creds?.max !== undefined) inc *= creds.value / creds.max(state); return inc; } @@ -48,9 +46,8 @@ class PlayerOrg implements IResource { // chance to fail increases as credibility decreases const creds = state.resource.credibility; - if (creds?.max !== null) { - const ratio = Math.ceil(creds?.value ?? 0) / (creds?.max(state) - ?? state.config.cfgPassiveMax); + if (creds?.max !== undefined) { + const ratio = Math.ceil(creds.value) / creds.max(state); if (Math.random() > ratio) { state.log('Your recruitment efforts failed.'); return; @@ -99,10 +96,9 @@ class PlayerOrg implements IResource { if (this._timeSinceLastLost > state.config.cfgCredibilityFollowerLossTime) { if (this.value > 0) { const creds = state.resource.credibility; - if (creds?.max !== null) { + if (creds?.max !== undefined) { const ratio = - Math.ceil(creds?.value ?? 0) / (creds?.max(state) - ?? state.config.cfgPassiveMax); + Math.ceil(creds.value) / creds.max(state); if (Math.random() > ratio) { const lost = Math.ceil(this.value * state.config.cfgCredibilityFollowerLossRatio diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts index 710edee..49f4688 100644 --- a/src/model/resource/Purchasable.ts +++ b/src/model/resource/Purchasable.ts @@ -8,8 +8,8 @@ abstract class Purchasable implements IResource { public value = 0; public readonly cost: { [key in ResourceKey]?: number } = { }; - public inc: ((state: GameState) => number) | null = null; - public max: ((_state: GameState) => number) | null = null; + public inc?: (state: GameState) => number = undefined; + public max?: (_state: GameState) => number = undefined; protected _costMultiplier: { [key in ResourceKey]?: number } = { }; protected _isUnlocked = false; @@ -21,7 +21,7 @@ abstract class Purchasable implements IResource { public clickAction (state: GameState): void { - if (this.max !== null && this.value >= this.max(state)) return; + if (this.max !== undefined && this.value >= this.max(state)) return; if (state.deductCost(this.cost)) { const amount = this._purchaseAmount(state); if (amount > 0) { diff --git a/src/model/resource/Religion.ts b/src/model/resource/Religion.ts index 5e10791..bc32b6c 100644 --- a/src/model/resource/Religion.ts +++ b/src/model/resource/Religion.ts @@ -3,14 +3,6 @@ class Religion implements IResource { public readonly resourceType = ResourceType.religion; public readonly valueInWholeNumbers = true; - public readonly clickText = null; - public readonly clickDescription = null; - public readonly cost = null; - - public readonly max = null; - public readonly inc = null; - public readonly clickAction = null; - public readonly advanceAction = null; constructor ( public readonly name: string, diff --git a/src/model/resource/Research.ts b/src/model/resource/Research.ts index 8d3567f..4f4567a 100644 --- a/src/model/resource/Research.ts +++ b/src/model/resource/Research.ts @@ -2,6 +2,7 @@ abstract class Research extends Purchasable { public readonly resourceType = ResourceType.research; + public inc = undefined; constructor ( public readonly name: string, diff --git a/src/render/DebugRenderer.ts b/src/render/DebugRenderer.ts index 06f60ec..a16adaa 100644 --- a/src/render/DebugRenderer.ts +++ b/src/render/DebugRenderer.ts @@ -58,19 +58,20 @@ class DebugRenderer implements IRenderer { `; - if (resource.clickText !== null && resource.clickDescription !== null) { + if (resource.clickText !== undefined + && resource.clickDescription !== undefined) { content += `
`; } - if (resource.cost !== null + if (resource.cost !== undefined && Object.keys(resource.cost).length !== 0) { content += "
Cost: "; } el.innerHTML = content; resContainer.appendChild(el); - if (resource.clickAction !== null) { + if (resource.clickAction !== undefined) { const btn = el.getElementsByClassName('resource-btn')[0]; btn.addEventListener('click', (): void => { state.performClick(rkey); @@ -103,17 +104,18 @@ class DebugRenderer implements IRenderer { ? Math.floor(resource.value) : resource.value; elV.innerHTML = state.config.formatNumber(value); - elT.innerHTML = resource.max !== null + elT.innerHTML = resource.max !== undefined ? ` / ${state.config.formatNumber(resource.max(state))}` : ''; const elB = el.getElementsByClassName('resource-btn'); if (elB.length > 0) { const enabled = state.isPurchasable(resource.cost) - && (resource.max === null || resource.value < resource.max(state)); + && (resource.max === undefined + || resource.value < resource.max(state)); if (enabled) elB[0].removeAttribute('disabled'); else elB[0].setAttribute('disabled', 'disabled'); } - if (resource.inc !== null && resource.inc(state) > 0) { + if (resource.inc !== undefined && resource.inc(state) > 0) { const elI = el.getElementsByClassName('resource-inc')[0]; elI.innerHTML = ` +${state.config.formatNumber(resource.inc(state))}/s`;