diff --git a/public/css/debugger.css b/public/css/debugger.css index 8713844..cb6f982 100644 --- a/public/css/debugger.css +++ b/public/css/debugger.css @@ -52,7 +52,7 @@ body, html { #resource-container-job .resource { background-color: #fcf; } -#resource-container-consumable .resource { +#resource-container-purchasable .resource { background-color: #cfc; } #resource-container-infrastructure .resource { diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts index 15d218b..b680fbe 100644 --- a/src/model/GameConfig.ts +++ b/src/model/GameConfig.ts @@ -105,12 +105,12 @@ class GameConfig { const state = new GameState(this); // create player organization - state.addResource(ResourceKey.followers, new Follower()); + state.addResource(new Follower()); // create world religions state.addResource( - ResourceKey.christianity, new Religion( + ResourceKey.christianity, 'Christianity', 'christian', 'christians', @@ -120,8 +120,8 @@ class GameConfig { ); state.addResource( - ResourceKey.islam, new Religion( + ResourceKey.islam, 'Islam', 'muslim', 'muslims', @@ -131,8 +131,8 @@ class GameConfig { ); state.addResource( - ResourceKey.hinduism, new Religion( + ResourceKey.hinduism, 'Hinduism', 'hindu', 'hindus', @@ -142,8 +142,8 @@ class GameConfig { ); state.addResource( - ResourceKey.buddhism, new Religion( + ResourceKey.buddhism, 'Buddhism', 'buddhist', 'buddhists', @@ -153,8 +153,8 @@ class GameConfig { ); state.addResource( - ResourceKey.sikhism, new Religion( + ResourceKey.sikhism, 'Sikhism', 'sikh', 'sikhs', @@ -164,8 +164,8 @@ class GameConfig { ); state.addResource( - ResourceKey.judaism, new Religion( + ResourceKey.judaism, 'Judaism', 'jew', 'jews', @@ -175,8 +175,8 @@ class GameConfig { ); state.addResource( - ResourceKey.other, new Religion( + ResourceKey.other, 'Other', 'person from other faiths', 'people from other faiths', @@ -186,8 +186,8 @@ class GameConfig { ); state.addResource( - ResourceKey.atheism, new Religion( + ResourceKey.atheism, 'Non-Religious', 'atheist', 'atheists', @@ -197,24 +197,24 @@ class GameConfig { ); // add jobs - state.addResource(ResourceKey.pastors, new Pastor()); - state.addResource(ResourceKey.compoundManagers, new CompoundManager()); + state.addResource(new Pastor()); + state.addResource(new CompoundManager()); // add resources - state.addResource(ResourceKey.money, new Money(3.5)); - state.addResource(ResourceKey.cryptoCurrency, new CryptoCurrency(this)); - state.addResource(ResourceKey.tents, new Tent(this)); - state.addResource(ResourceKey.houses, new House(this)); - state.addResource(ResourceKey.churches, new Church(this)); - state.addResource(ResourceKey.compounds, new Compound(this)); - state.addResource(ResourceKey.megaChurches, new Megachurch(this)); + state.addResource(new Money(3.5)); + state.addResource(new CryptoCurrency(this)); + state.addResource(new Tent(this)); + state.addResource(new House(this)); + state.addResource(new Church(this)); + state.addResource(new Compound(this)); + state.addResource(new Megachurch(this)); // add research - state.addResource(ResourceKey.buildingPermit, new BuildingPermit(this)); + state.addResource(new BuildingPermit(this)); // add passive resources - state.addResource(ResourceKey.credibility, new Credibility(this)); - state.addResource(ResourceKey.cryptoMarket, new CryptoMarket(this)); + state.addResource(new Credibility(this)); + state.addResource(new CryptoMarket(this)); return state; } diff --git a/src/model/GameState.ts b/src/model/GameState.ts index 2f063f5..e32c23e 100644 --- a/src/model/GameState.ts +++ b/src/model/GameState.ts @@ -26,9 +26,9 @@ class GameState { return this._resourceKeys; } - public addResource(key: ResourceKey, resource: IResource): void { - this._resourceKeys.push(key); - this._resources[key] = resource; + public addResource(resource: IResource): void { + this._resourceKeys.push(resource.resourceKey); + this._resources[resource.resourceKey] = resource; } public advance(time: number): void { @@ -161,16 +161,7 @@ class GameState { if (resource === undefined) continue; const saveRes = saveObj.resources[rkey]; if (saveRes !== undefined) { - // @ts-expect-error writing read-only value from save data - resource.value = saveRes.value; - // @ts-expect-error writing read-only cost from save data - resource.cost = saveRes.cost; - if ( - saveRes.config !== undefined && - resource.restoreConfig !== undefined - ) { - resource.restoreConfig(saveRes.config); - } + resource.restoreConfig(saveRes); } } } else { diff --git a/src/model/resource/BuildingPermit.ts b/src/model/resource/BuildingPermit.ts index 620cf42..7061471 100644 --- a/src/model/resource/BuildingPermit.ts +++ b/src/model/resource/BuildingPermit.ts @@ -1,6 +1,8 @@ /// class BuildingPermit extends Research { + public readonly resourceKey = ResourceKey.buildingPermit; + constructor(config: GameConfig) { super( 'Building Permit', @@ -11,12 +13,12 @@ class BuildingPermit extends Research { this.cost.money = config.cfgInitialCost.buildingPermit; } - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; const compounds = state.resource.compounds; if (compounds !== undefined && compounds.value > 0) { this._isUnlocked = true; } return this._isUnlocked; - } + }; } diff --git a/src/model/resource/Church.ts b/src/model/resource/Church.ts index a7c0cc6..0fa2470 100644 --- a/src/model/resource/Church.ts +++ b/src/model/resource/Church.ts @@ -1,6 +1,8 @@ /// class Church extends Infrastructure { + public readonly resourceKey = ResourceKey.churches; + constructor(config: GameConfig) { super( 'Churches', @@ -17,13 +19,11 @@ class Church extends Infrastructure { this._costMultiplier.money = config.cfgCostMultiplier.churches; } - public max: (state: GameState) => number = (state) => - Math.floor( - (state.resource.compounds?.value ?? 0) * - (state.config.cfgCapacity.compounds?.churches ?? 0) - ); + public max = (state: GameState): number => + (state.resource.compounds?.value ?? 0) * + (state.config.cfgCapacity.compounds?.churches ?? 0); - public inc: (state: GameState) => number = (state) => { + public inc = (state: GameState): number => { // compound managers return ( (state.resource.compoundManagers?.value ?? 0) * @@ -31,12 +31,12 @@ class Church extends Infrastructure { ); }; - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; const compounds = state.resource.compounds; if (compounds !== undefined && compounds.value > 0) { this._isUnlocked = true; } return this._isUnlocked; - } + }; } diff --git a/src/model/resource/Compound.ts b/src/model/resource/Compound.ts index 27382ff..e3bd02d 100644 --- a/src/model/resource/Compound.ts +++ b/src/model/resource/Compound.ts @@ -1,6 +1,8 @@ /// class Compound extends Infrastructure { + public readonly resourceKey = ResourceKey.compounds; + constructor(config: GameConfig) { super( 'Compounds', @@ -13,7 +15,7 @@ class Compound extends Infrastructure { this._costMultiplier.money = config.cfgCostMultiplier.compounds; } - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; const tents = state.resource.tents; if ( @@ -23,5 +25,5 @@ class Compound extends Infrastructure { this._isUnlocked = true; } return this._isUnlocked; - } + }; } diff --git a/src/model/resource/CompoundManager.ts b/src/model/resource/CompoundManager.ts index f877d25..ac1b875 100644 --- a/src/model/resource/CompoundManager.ts +++ b/src/model/resource/CompoundManager.ts @@ -1,6 +1,8 @@ /// class CompoundManager extends Job { + public readonly resourceKey = ResourceKey.compoundManagers; + constructor() { super( 'Compound Managers', @@ -10,16 +12,16 @@ class CompoundManager extends Job { ); } - public max: (state: GameState) => number = (state) => { + public max = (state: GameState): number => { return ( - Math.floor(state.resource.compounds?.value ?? 0) * + (state.resource.compounds?.value ?? 0) * (state.config.cfgCapacity.compounds?.compoundManagers ?? 0) ); }; - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; this._isUnlocked = state.resource.compounds?.isUnlocked(state) === true; return this._isUnlocked; - } + }; } diff --git a/src/model/resource/Credibility.ts b/src/model/resource/Credibility.ts index 9d37853..c6701c5 100644 --- a/src/model/resource/Credibility.ts +++ b/src/model/resource/Credibility.ts @@ -1,6 +1,8 @@ /// class Credibility extends Passive { + public readonly resourceKey = ResourceKey.credibility; + constructor(config: GameConfig) { super( 'Credibility', @@ -8,7 +10,7 @@ class Credibility extends Passive { 'credibilities', 'Affects your ability to retain followers and collect tithes.' ); - this.value = config.cfgPassiveMax; + this.rawValue = config.cfgPassiveMax; } public static ratio(state: GameState): number { @@ -22,9 +24,8 @@ class Credibility extends Passive { : cred.value / cred.max(state); } - public max: (state: GameState) => number = (state) => - state.config.cfgPassiveMax; + public max = (state: GameState): number => state.config.cfgPassiveMax; - public inc: (state: GameState) => number = (state) => + public inc = (state: GameState): number => state.config.cfgCredibilityRestoreRate; } diff --git a/src/model/resource/CryptoCurrency.ts b/src/model/resource/CryptoCurrency.ts index cb9be51..d3be335 100644 --- a/src/model/resource/CryptoCurrency.ts +++ b/src/model/resource/CryptoCurrency.ts @@ -1,6 +1,8 @@ /// class CryptoCurrency extends Purchasable { + public readonly resourceKey = ResourceKey.cryptoCurrency; + constructor(config: GameConfig) { super( 'FaithCoin', diff --git a/src/model/resource/CryptoMarket.ts b/src/model/resource/CryptoMarket.ts index cfa8c7b..165bb4c 100644 --- a/src/model/resource/CryptoMarket.ts +++ b/src/model/resource/CryptoMarket.ts @@ -1,6 +1,8 @@ /// class CryptoMarket extends Hidden { + public readonly resourceKey = ResourceKey.cryptoMarket; + private _adjustmentTime = 0; constructor(config: GameConfig) { @@ -9,7 +11,7 @@ class CryptoMarket extends Hidden { 'crypto markets', 'How much money a single FaithCoin is worth' ); - this.value = config.cfgInitialCost.cryptoCurrency ?? 0; + this.rawValue = config.cfgInitialCost.cryptoCurrency ?? 0; } public max = (state: GameState): number => diff --git a/src/model/resource/Follower.ts b/src/model/resource/Follower.ts index 3538c0c..2c0d246 100644 --- a/src/model/resource/Follower.ts +++ b/src/model/resource/Follower.ts @@ -1,14 +1,15 @@ -/// +/// /// -class Follower implements IResource { +class Follower extends Resource { public readonly resourceType = ResourceType.religion; + public readonly resourceKey = ResourceKey.followers; + public readonly label = 'Your Followers'; public readonly singularName = 'follower'; public readonly pluralName = 'followers'; public readonly description = 'In you they trust.'; public readonly valueInWholeNumbers = true; - public value = 0; public userActions: ResourceAction[] = [ { @@ -21,25 +22,24 @@ class Follower implements IResource { }, ]; - private _timeSinceLastLost = 0; private _lastRecruitmentLog = 0; private _followerSources: ResourceNumber = {}; private _followerDests: ResourceNumber = {}; private _timeSinceLastQuit = 0; private _quitTracker: ResourceNumber = {}; - public max(state: GameState): number { + public max = (state: GameState): number => { let max = state.config.cfgInitialMax.followers ?? 0; max += - Math.floor(state.resource.tents?.value ?? 0) * + (state.resource.tents?.value ?? 0) * (state.config.cfgCapacity.tents?.followers ?? 0); max += - Math.floor(state.resource.houses?.value ?? 0) * + (state.resource.houses?.value ?? 0) * (state.config.cfgCapacity.houses?.followers ?? 0); return max; - } + }; - public inc(state: GameState): number { + public inc = (state: GameState): number => { let inc = 0; // pastor recruiting @@ -52,12 +52,12 @@ class Follower implements IResource { if (creds?.max !== undefined) inc *= creds.value / creds.max(state);*/ return inc; - } + }; - public addValue(amount: number, state: GameState): void { + public addValue = (amount: number, state: GameState): void => { const oldValue = this.value; - this.value += amount; - const diff = Math.floor(this.value) - Math.floor(oldValue); + this.rawValue += amount; + const diff = this.value - oldValue; if (diff > 0) { // gained followers must come from other faiths @@ -80,13 +80,11 @@ class Follower implements IResource { } } } - } + }; - public isUnlocked(_state: GameState): boolean { - return true; - } + public isUnlocked = (): boolean => true; - public advanceAction(time: number, state: GameState): void { + public advanceAction = (time: number, state: GameState): void => { // chance for some followers to quit their jobs if money === 0 const money = state.resource.money; const totalJobs = Job.totalJobs(state); @@ -190,7 +188,7 @@ class Follower implements IResource { } this._lastRecruitmentLog = state.now; } - } + }; private _recruitFollower(state: GameState): void { // don't exceed max diff --git a/src/model/resource/Hidden.ts b/src/model/resource/Hidden.ts index 1c7249d..484b32a 100644 --- a/src/model/resource/Hidden.ts +++ b/src/model/resource/Hidden.ts @@ -1,21 +1,18 @@ -/// +/// -abstract class Hidden implements IResource { +abstract class Hidden extends Resource { public readonly resourceType = ResourceType.passive; + public readonly label = undefined; + public readonly valueInWholeNumbers = false; - public value = 0; constructor( public readonly singularName: string, public readonly pluralName: string, public readonly description: string - ) {} - - public addValue(amount: number, _state: GameState): void { - this.value += amount; + ) { + super(); } - public isUnlocked(_state: GameState): boolean { - return true; - } + public isUnlocked = (): boolean => true; } diff --git a/src/model/resource/House.ts b/src/model/resource/House.ts index 2a98362..c4da4c2 100644 --- a/src/model/resource/House.ts +++ b/src/model/resource/House.ts @@ -1,6 +1,8 @@ /// class House extends Infrastructure { + public readonly resourceKey = ResourceKey.houses; + constructor(config: GameConfig) { super( 'Houses', @@ -15,13 +17,11 @@ class House extends Infrastructure { this._costMultiplier.money = config.cfgCostMultiplier.houses; } - public max: (state: GameState) => number = (state) => - Math.floor( - (state.resource.compounds?.value ?? 0) * - (state.config.cfgCapacity.compounds?.houses ?? 0) - ); + public max = (state: GameState): number => + (state.resource.compounds?.value ?? 0) * + (state.config.cfgCapacity.compounds?.houses ?? 0); - public inc: (state: GameState) => number = (state) => { + public inc = (state: GameState): number => { // compound managers return ( (state.resource.compoundManagers?.value ?? 0) * @@ -29,12 +29,12 @@ class House extends Infrastructure { ); }; - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; const compounds = state.resource.compounds; if (compounds !== undefined && compounds.value > 0) { this._isUnlocked = true; } return this._isUnlocked; - } + }; } diff --git a/src/model/resource/IResource.ts b/src/model/resource/IResource.ts index 289f04c..1ac8511 100644 --- a/src/model/resource/IResource.ts +++ b/src/model/resource/IResource.ts @@ -2,13 +2,14 @@ interface IResource { readonly resourceType: ResourceType; + readonly resourceKey: ResourceKey; + readonly label?: string; readonly singularName: string; readonly pluralName: string; readonly description: string; readonly valueInWholeNumbers: boolean; - readonly value: number; readonly cost?: ResourceNumber; max?: (state: GameState) => number; @@ -19,6 +20,8 @@ interface IResource { addValue: (amount: number, state: GameState) => void; isUnlocked: (state: GameState) => boolean; + restoreConfig: (config: ResourceConfig) => void; emitConfig?: () => ResourceConfigValues; - restoreConfig?: (config: ResourceConfigValues) => void; + + get value(): number; } diff --git a/src/model/resource/Job.ts b/src/model/resource/Job.ts index c55f0e2..b551c07 100644 --- a/src/model/resource/Job.ts +++ b/src/model/resource/Job.ts @@ -1,9 +1,9 @@ -/// +/// -abstract class Job implements IResource { +abstract class Job extends Resource { public readonly resourceType = ResourceType.job; + public readonly valueInWholeNumbers = true; - public value = 0; public readonly cost: ResourceNumber = {}; public max?: (state: GameState) => number = undefined; @@ -39,7 +39,9 @@ abstract class Job implements IResource { public readonly singularName: string, public readonly pluralName: string, public readonly description: string - ) {} + ) { + super(); + } public static jobResources(state: GameState): ResourceKey[] { return state.resources.filter((rkey) => { @@ -74,20 +76,15 @@ abstract class Job implements IResource { return followers - hired; } - public addValue(amount: number): void { - this.value += amount; - if (this.value < 0) this.value = 0; - } - - public isUnlocked(_state: GameState): boolean { + public isUnlocked: (_state: GameState) => boolean = () => { return this._isUnlocked; - } + }; public advanceAction(_time: number, state: GameState): void { // if we're out of followers then the jobs also vacate const avail = Job.availableJobs(state); if (avail < 0 && this.value > 0) { - this.addValue(avail); + this.addValue(avail, state); } return; @@ -110,7 +107,7 @@ abstract class Job implements IResource { this.value < this.max(state) && state.deductCost(this.cost) ) { - this.addValue(1); + this.addValue(1, state); state.log(this._hireLog(1, state)); for (const key in this._costMultiplier) { const rkey = key; @@ -122,7 +119,7 @@ abstract class Job implements IResource { private _demoteFollower(state: GameState): void { if (this.value <= 0) return; - this.addValue(-1); + this.addValue(-1, state); state.log(this._hireLog(-1, state)); for (const key in this._costMultiplier) { const rkey = key; diff --git a/src/model/resource/Megachurch.ts b/src/model/resource/Megachurch.ts index c8d39ca..bfba87c 100644 --- a/src/model/resource/Megachurch.ts +++ b/src/model/resource/Megachurch.ts @@ -1,6 +1,8 @@ /// class Megachurch extends Infrastructure { + public readonly resourceKey = ResourceKey.megaChurches; + constructor(config: GameConfig) { super( 'Megachurches', @@ -15,15 +17,15 @@ class Megachurch extends Infrastructure { this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; } - public max: (state: GameState) => number = (state) => + public max = (state: GameState): number => state.config.cfgInitialMax.megaChurches ?? 0; - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; const permit = state.resource.buildingPermit; if (permit !== undefined && permit.value > 0) { this._isUnlocked = true; } return this._isUnlocked; - } + }; } diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts index 0dcbe3c..164f84d 100644 --- a/src/model/resource/Money.ts +++ b/src/model/resource/Money.ts @@ -1,7 +1,9 @@ -/// +/// + +class Money extends Resource { + public readonly resourceType = ResourceType.purchasable; + public readonly resourceKey = ResourceKey.money; -class Money implements IResource { - public readonly resourceType = ResourceType.consumable; public readonly label = 'Money'; public readonly singularName = '${}'; public readonly pluralName = '${}'; @@ -23,15 +25,14 @@ class Money implements IResource { private _lastCollectionTime = 0; - constructor(public value: number) {} - - public isUnlocked = (_state: GameState): boolean => true; - - public addValue(amount: number, _state: GameState): void { - this.value += amount; + public constructor(initialValue: number) { + super(); + this.rawValue = initialValue; } - public max: (state: GameState) => number = (state: GameState) => { + public isUnlocked = (_: GameState): boolean => true; + + public max = (state: GameState): number => { let max = state.config.cfgInitialMax.money ?? 0; max += (state.resource.compounds?.value ?? 0) * @@ -39,20 +40,20 @@ class Money implements IResource { return max; }; - public inc: (state: GameState) => number = (state) => { + public inc = (state: GameState): number => { let inc = 0; // tithings inc += - (Math.floor(state.resource.pastors?.value ?? 0) * - Math.floor(state.resource.followers?.value ?? 0) * + ((state.resource.pastors?.value ?? 0) * + (state.resource.followers?.value ?? 0) * (state.config.cfgTitheAmount ?? 0) * Credibility.ratio(state)) / state.config.cfgTimeBetweenTithes; // salaries inc -= - Math.floor(state.resource.compoundManagers?.value ?? 0) * + (state.resource.compoundManagers?.value ?? 0) * (state.config.cfgSalary.compoundManagers ?? 0); return inc; @@ -92,11 +93,9 @@ class Money implements IResource { if (followers !== undefined) { state.log( `You collected $${formatNumber(amount)} from ${formatNumber( - Math.floor(followers.value) + followers.value )} ${ - Math.floor(followers.value) > 1 - ? followers.pluralName - : followers.singularName + followers.value > 1 ? followers.pluralName : followers.singularName }.` ); } else { diff --git a/src/model/resource/Passive.ts b/src/model/resource/Passive.ts index c537b6a..764f20b 100644 --- a/src/model/resource/Passive.ts +++ b/src/model/resource/Passive.ts @@ -1,22 +1,18 @@ -/// +/// -abstract class Passive implements IResource { +abstract class Passive extends Resource { public readonly resourceType = ResourceType.passive; + public readonly valueInWholeNumbers = false; - public value = 0; constructor( public readonly label: string, public readonly singularName: string, public readonly pluralName: string, public readonly description: string - ) {} - - public addValue(amount: number, _state: GameState): void { - this.value += amount; + ) { + super(); } - public isUnlocked(_state: GameState): boolean { - return true; - } + public isUnlocked = (): boolean => true; } diff --git a/src/model/resource/Pastor.ts b/src/model/resource/Pastor.ts index 9e3b356..4cd36ab 100644 --- a/src/model/resource/Pastor.ts +++ b/src/model/resource/Pastor.ts @@ -1,6 +1,8 @@ /// class Pastor extends Job { + public readonly resourceKey = ResourceKey.pastors; + constructor() { super( 'Pastors', @@ -10,19 +12,19 @@ class Pastor extends Job { ); } - public max: (state: GameState) => number = (state) => { + public max = (state: GameState): number => { let max = - Math.floor(state.resource.churches?.value ?? 0) * + (state.resource.churches?.value ?? 0) * (state.config.cfgCapacity.churches?.pastors ?? 0); max += - Math.floor(state.resource.megaChurches?.value ?? 0) * + (state.resource.megaChurches?.value ?? 0) * (state.config.cfgCapacity.megaChurches?.pastors ?? 0); - return Math.floor(max); + return max; }; - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (this._isUnlocked) return true; this._isUnlocked = state.resource.churches?.isUnlocked(state) === true; return this._isUnlocked; - } + }; } diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts index b403294..419df5e 100644 --- a/src/model/resource/Purchasable.ts +++ b/src/model/resource/Purchasable.ts @@ -1,13 +1,13 @@ -/// +/// + +abstract class Purchasable extends Resource { + public readonly resourceType: ResourceType = ResourceType.purchasable; -abstract class Purchasable implements IResource { - public readonly resourceType: ResourceType = ResourceType.consumable; public valueInWholeNumbers = true; - public value = 0; - public readonly cost: ResourceNumber = {}; + public cost: ResourceNumber = {}; public inc?: (state: GameState) => number = undefined; - public max?: (_state: GameState) => number = undefined; + public max?: (state: GameState) => number = undefined; public userActions: ResourceAction[] = [ { @@ -37,6 +37,7 @@ abstract class Purchasable implements IResource { private readonly _sellButtonText = 'Sell', private readonly _sellDescription = `Sell a ${singularName}.` ) { + super(); if (canSell) { this.userActions.push({ name: this._sellButtonText, @@ -49,28 +50,25 @@ abstract class Purchasable implements IResource { } } - public addValue(amount: number, _state: GameState): void { - this.value += amount; - } - - public isUnlocked(state: GameState): boolean { + public isUnlocked = (state: GameState): boolean => { if (!this._isUnlocked && state.isPurchasable(this.cost)) { this._isUnlocked = true; } return this._isUnlocked; - } + }; - public advanceAction(_time: number, _state: GameState): void { - return; - } - - public emitConfig: () => ResourceConfigValues = () => { + public emitConfig = (): ResourceConfigValues => { return { isUnlocked: this._isUnlocked }; }; - public restoreConfig: (config: ResourceConfigValues) => void = (config) => { - if (typeof config.isUnlocked === 'boolean') { - this._isUnlocked = config.isUnlocked; + public restoreConfig = (config: ResourceConfig): void => { + this.rawValue = config.value; + if (config.cost !== undefined) this.cost = config.cost; + if ( + config.config !== undefined && + typeof config.config.isUnlocked === 'boolean' + ) { + this._isUnlocked = config.config.isUnlocked; } }; @@ -88,7 +86,7 @@ abstract class Purchasable implements IResource { private _purchase(state: GameState): void { if (this.max !== undefined && this.value >= this.max(state)) return; if (state.deductCost(this.cost)) { - this.value += 1; + this.addValue(1, state); state.log(this._purchaseLog(1, state)); for (const key in this._costMultiplier) { const rkey = key; @@ -118,7 +116,7 @@ abstract class Purchasable implements IResource { costBack[rkey] = cost * -1 * multiplier; state.deductCost(costBack); } - this.value -= 1; + this.addValue(1, state); state.log(this._purchaseLog(-1, state)); } } diff --git a/src/model/resource/Religion.ts b/src/model/resource/Religion.ts index d42bc9e..fd5cae8 100644 --- a/src/model/resource/Religion.ts +++ b/src/model/resource/Religion.ts @@ -1,22 +1,20 @@ -/// +/// -class Religion implements IResource { +class Religion extends Resource { public readonly resourceType = ResourceType.religion; public readonly valueInWholeNumbers = true; constructor( + public readonly resourceKey: ResourceKey, public readonly label: string, public readonly singularName: string, public readonly pluralName: string, public readonly description: string, - public value: number - ) {} - - public addValue(amount: number, _state: GameState): void { - this.value += amount; + initialValue: number + ) { + super(); + this.rawValue = initialValue; } - public isUnlocked(_state: GameState): boolean { - return true; - } + public isUnlocked = (): boolean => true; } diff --git a/src/model/resource/Research.ts b/src/model/resource/Research.ts index 4d5ef0f..913af74 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( @@ -19,8 +20,7 @@ abstract class Research extends Purchasable { 'Learn', 'Complete this research.' ); - this.value = 0; } - public max: (state: GameState) => number = (_state) => 1; + public max = (_: GameState): number => 1; } diff --git a/src/model/resource/Resource.ts b/src/model/resource/Resource.ts new file mode 100644 index 0000000..e74c718 --- /dev/null +++ b/src/model/resource/Resource.ts @@ -0,0 +1,31 @@ +/// + +abstract class Resource implements IResource { + public cost?: ResourceNumber = undefined; + + protected rawValue = 0; + + public abstract readonly resourceType: ResourceType; + public abstract readonly resourceKey: ResourceKey; + + public abstract readonly label?: string; + public abstract readonly singularName: string; + public abstract readonly pluralName: string; + public abstract readonly description: string; + public abstract valueInWholeNumbers: boolean; + + public abstract isUnlocked: (state: GameState) => boolean; + + public get value(): number { + return this.valueInWholeNumbers ? Math.floor(this.rawValue) : this.rawValue; + } + + public addValue = (amount: number, _: GameState): void => { + this.rawValue += amount; + }; + + public restoreConfig = (config: ResourceConfig): void => { + this.rawValue = config.value; + this.cost = config.cost; + }; +} diff --git a/src/model/resource/SharedTypes.ts b/src/model/resource/SharedTypes.ts index 1ba92f4..946a466 100644 --- a/src/model/resource/SharedTypes.ts +++ b/src/model/resource/SharedTypes.ts @@ -3,7 +3,7 @@ enum ResourceType { religion = 'religion', job = 'job', - consumable = 'consumable', + purchasable = 'purchasable', infrastructure = 'infrastructure', research = 'research', passive = 'passive', diff --git a/src/model/resource/Tent.ts b/src/model/resource/Tent.ts index e806292..0a6661b 100644 --- a/src/model/resource/Tent.ts +++ b/src/model/resource/Tent.ts @@ -1,6 +1,8 @@ /// class Tent extends Infrastructure { + public readonly resourceKey = ResourceKey.tents; + constructor(config: GameConfig) { super( 'Tents', @@ -15,16 +17,16 @@ class Tent extends Infrastructure { this._costMultiplier.money = config.cfgCostMultiplier.tents; } - public max: (state: GameState) => number = (state) => { + public max = (state: GameState): number => { // ten extra tents per compound let max = state.config.cfgInitialMax.tents ?? 0; max += (state.resource.compounds?.value ?? 0) * (state.config.cfgCapacity.compounds?.tents ?? 0); - return Math.floor(max); + return max; }; - public inc: (state: GameState) => number = (state) => + public inc = (state: GameState): number => // compound managers (state.resource.compoundManagers?.value ?? 0) * (state.config.cfgBuySpeed.compoundManagers?.tents ?? 0);