moved all math.floor(value) logic to baseclass

This commit is contained in:
Rudis Muiznieks 2021-09-12 13:58:57 -05:00
parent 9ba7e70de4
commit 14e1b646a9
25 changed files with 209 additions and 184 deletions

View File

@ -52,7 +52,7 @@ body, html {
#resource-container-job .resource { #resource-container-job .resource {
background-color: #fcf; background-color: #fcf;
} }
#resource-container-consumable .resource { #resource-container-purchasable .resource {
background-color: #cfc; background-color: #cfc;
} }
#resource-container-infrastructure .resource { #resource-container-infrastructure .resource {

View File

@ -105,12 +105,12 @@ class GameConfig {
const state = new GameState(this); const state = new GameState(this);
// create player organization // create player organization
state.addResource(ResourceKey.followers, new Follower()); state.addResource(new Follower());
// create world religions // create world religions
state.addResource( state.addResource(
ResourceKey.christianity,
new Religion( new Religion(
ResourceKey.christianity,
'Christianity', 'Christianity',
'christian', 'christian',
'christians', 'christians',
@ -120,8 +120,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.islam,
new Religion( new Religion(
ResourceKey.islam,
'Islam', 'Islam',
'muslim', 'muslim',
'muslims', 'muslims',
@ -131,8 +131,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.hinduism,
new Religion( new Religion(
ResourceKey.hinduism,
'Hinduism', 'Hinduism',
'hindu', 'hindu',
'hindus', 'hindus',
@ -142,8 +142,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.buddhism,
new Religion( new Religion(
ResourceKey.buddhism,
'Buddhism', 'Buddhism',
'buddhist', 'buddhist',
'buddhists', 'buddhists',
@ -153,8 +153,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.sikhism,
new Religion( new Religion(
ResourceKey.sikhism,
'Sikhism', 'Sikhism',
'sikh', 'sikh',
'sikhs', 'sikhs',
@ -164,8 +164,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.judaism,
new Religion( new Religion(
ResourceKey.judaism,
'Judaism', 'Judaism',
'jew', 'jew',
'jews', 'jews',
@ -175,8 +175,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.other,
new Religion( new Religion(
ResourceKey.other,
'Other', 'Other',
'person from other faiths', 'person from other faiths',
'people from other faiths', 'people from other faiths',
@ -186,8 +186,8 @@ class GameConfig {
); );
state.addResource( state.addResource(
ResourceKey.atheism,
new Religion( new Religion(
ResourceKey.atheism,
'Non-Religious', 'Non-Religious',
'atheist', 'atheist',
'atheists', 'atheists',
@ -197,24 +197,24 @@ class GameConfig {
); );
// add jobs // add jobs
state.addResource(ResourceKey.pastors, new Pastor()); state.addResource(new Pastor());
state.addResource(ResourceKey.compoundManagers, new CompoundManager()); state.addResource(new CompoundManager());
// add resources // add resources
state.addResource(ResourceKey.money, new Money(3.5)); state.addResource(new Money(3.5));
state.addResource(ResourceKey.cryptoCurrency, new CryptoCurrency(this)); state.addResource(new CryptoCurrency(this));
state.addResource(ResourceKey.tents, new Tent(this)); state.addResource(new Tent(this));
state.addResource(ResourceKey.houses, new House(this)); state.addResource(new House(this));
state.addResource(ResourceKey.churches, new Church(this)); state.addResource(new Church(this));
state.addResource(ResourceKey.compounds, new Compound(this)); state.addResource(new Compound(this));
state.addResource(ResourceKey.megaChurches, new Megachurch(this)); state.addResource(new Megachurch(this));
// add research // add research
state.addResource(ResourceKey.buildingPermit, new BuildingPermit(this)); state.addResource(new BuildingPermit(this));
// add passive resources // add passive resources
state.addResource(ResourceKey.credibility, new Credibility(this)); state.addResource(new Credibility(this));
state.addResource(ResourceKey.cryptoMarket, new CryptoMarket(this)); state.addResource(new CryptoMarket(this));
return state; return state;
} }

View File

@ -26,9 +26,9 @@ class GameState {
return this._resourceKeys; return this._resourceKeys;
} }
public addResource(key: ResourceKey, resource: IResource): void { public addResource(resource: IResource): void {
this._resourceKeys.push(key); this._resourceKeys.push(resource.resourceKey);
this._resources[key] = resource; this._resources[resource.resourceKey] = resource;
} }
public advance(time: number): void { public advance(time: number): void {
@ -161,16 +161,7 @@ class GameState {
if (resource === undefined) continue; if (resource === undefined) continue;
const saveRes = saveObj.resources[rkey]; const saveRes = saveObj.resources[rkey];
if (saveRes !== undefined) { if (saveRes !== undefined) {
// @ts-expect-error writing read-only value from save data resource.restoreConfig(saveRes);
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);
}
} }
} }
} else { } else {

View File

@ -1,6 +1,8 @@
/// <reference path="./Research.ts" /> /// <reference path="./Research.ts" />
class BuildingPermit extends Research { class BuildingPermit extends Research {
public readonly resourceKey = ResourceKey.buildingPermit;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Building Permit', 'Building Permit',
@ -11,12 +13,12 @@ class BuildingPermit extends Research {
this.cost.money = config.cfgInitialCost.buildingPermit; this.cost.money = config.cfgInitialCost.buildingPermit;
} }
public isUnlocked(state: GameState): boolean { public isUnlocked = (state: GameState): boolean => {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;
const compounds = state.resource.compounds; const compounds = state.resource.compounds;
if (compounds !== undefined && compounds.value > 0) { if (compounds !== undefined && compounds.value > 0) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Infrastructure.ts" /> /// <reference path="./Infrastructure.ts" />
class Church extends Infrastructure { class Church extends Infrastructure {
public readonly resourceKey = ResourceKey.churches;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Churches', 'Churches',
@ -17,13 +19,11 @@ class Church extends Infrastructure {
this._costMultiplier.money = config.cfgCostMultiplier.churches; this._costMultiplier.money = config.cfgCostMultiplier.churches;
} }
public max: (state: GameState) => number = (state) => public max = (state: GameState): number =>
Math.floor(
(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 = (state) => { public inc = (state: GameState): number => {
// compound managers // compound managers
return ( return (
(state.resource.compoundManagers?.value ?? 0) * (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; if (this._isUnlocked) return true;
const compounds = state.resource.compounds; const compounds = state.resource.compounds;
if (compounds !== undefined && compounds.value > 0) { if (compounds !== undefined && compounds.value > 0) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Infrastructure.ts" /> /// <reference path="./Infrastructure.ts" />
class Compound extends Infrastructure { class Compound extends Infrastructure {
public readonly resourceKey = ResourceKey.compounds;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Compounds', 'Compounds',
@ -13,7 +15,7 @@ class Compound extends Infrastructure {
this._costMultiplier.money = config.cfgCostMultiplier.compounds; this._costMultiplier.money = config.cfgCostMultiplier.compounds;
} }
public isUnlocked(state: GameState): boolean { public isUnlocked = (state: GameState): boolean => {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;
const tents = state.resource.tents; const tents = state.resource.tents;
if ( if (
@ -23,5 +25,5 @@ class Compound extends Infrastructure {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Job.ts" /> /// <reference path="./Job.ts" />
class CompoundManager extends Job { class CompoundManager extends Job {
public readonly resourceKey = ResourceKey.compoundManagers;
constructor() { constructor() {
super( super(
'Compound Managers', 'Compound Managers',
@ -10,16 +12,16 @@ class CompoundManager extends Job {
); );
} }
public max: (state: GameState) => number = (state) => { public max = (state: GameState): number => {
return ( return (
Math.floor(state.resource.compounds?.value ?? 0) * (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.compoundManagers ?? 0) (state.config.cfgCapacity.compounds?.compoundManagers ?? 0)
); );
}; };
public isUnlocked(state: GameState): boolean { public isUnlocked = (state: GameState): boolean => {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;
this._isUnlocked = state.resource.compounds?.isUnlocked(state) === true; this._isUnlocked = state.resource.compounds?.isUnlocked(state) === true;
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Passive.ts" /> /// <reference path="./Passive.ts" />
class Credibility extends Passive { class Credibility extends Passive {
public readonly resourceKey = ResourceKey.credibility;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Credibility', 'Credibility',
@ -8,7 +10,7 @@ class Credibility extends Passive {
'credibilities', 'credibilities',
'Affects your ability to retain followers and collect tithes.' 'Affects your ability to retain followers and collect tithes.'
); );
this.value = config.cfgPassiveMax; this.rawValue = config.cfgPassiveMax;
} }
public static ratio(state: GameState): number { public static ratio(state: GameState): number {
@ -22,9 +24,8 @@ class Credibility extends Passive {
: cred.value / cred.max(state); : cred.value / cred.max(state);
} }
public max: (state: GameState) => number = (state) => public max = (state: GameState): number => state.config.cfgPassiveMax;
state.config.cfgPassiveMax;
public inc: (state: GameState) => number = (state) => public inc = (state: GameState): number =>
state.config.cfgCredibilityRestoreRate; state.config.cfgCredibilityRestoreRate;
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Purchasable.ts" /> /// <reference path="./Purchasable.ts" />
class CryptoCurrency extends Purchasable { class CryptoCurrency extends Purchasable {
public readonly resourceKey = ResourceKey.cryptoCurrency;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'FaithCoin', 'FaithCoin',

View File

@ -1,6 +1,8 @@
/// <reference path="./Hidden.ts" /> /// <reference path="./Hidden.ts" />
class CryptoMarket extends Hidden { class CryptoMarket extends Hidden {
public readonly resourceKey = ResourceKey.cryptoMarket;
private _adjustmentTime = 0; private _adjustmentTime = 0;
constructor(config: GameConfig) { constructor(config: GameConfig) {
@ -9,7 +11,7 @@ class CryptoMarket extends Hidden {
'crypto markets', 'crypto markets',
'How much money a single FaithCoin is worth' '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 => public max = (state: GameState): number =>

View File

@ -1,14 +1,15 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
/// <reference path="./Job.ts" /> /// <reference path="./Job.ts" />
class Follower implements IResource { class Follower extends Resource {
public readonly resourceType = ResourceType.religion; public readonly resourceType = ResourceType.religion;
public readonly resourceKey = ResourceKey.followers;
public readonly label = 'Your Followers'; public readonly label = 'Your Followers';
public readonly singularName = 'follower'; public readonly singularName = 'follower';
public readonly pluralName = 'followers'; public readonly pluralName = 'followers';
public readonly description = 'In you they trust.'; public readonly description = 'In you they trust.';
public readonly valueInWholeNumbers = true; public readonly valueInWholeNumbers = true;
public value = 0;
public userActions: ResourceAction[] = [ public userActions: ResourceAction[] = [
{ {
@ -21,25 +22,24 @@ class Follower implements IResource {
}, },
]; ];
private _timeSinceLastLost = 0;
private _lastRecruitmentLog = 0; private _lastRecruitmentLog = 0;
private _followerSources: ResourceNumber = {}; private _followerSources: ResourceNumber = {};
private _followerDests: ResourceNumber = {}; private _followerDests: ResourceNumber = {};
private _timeSinceLastQuit = 0; private _timeSinceLastQuit = 0;
private _quitTracker: ResourceNumber = {}; private _quitTracker: ResourceNumber = {};
public max(state: GameState): number { public max = (state: GameState): number => {
let max = state.config.cfgInitialMax.followers ?? 0; let max = state.config.cfgInitialMax.followers ?? 0;
max += max +=
Math.floor(state.resource.tents?.value ?? 0) * (state.resource.tents?.value ?? 0) *
(state.config.cfgCapacity.tents?.followers ?? 0); (state.config.cfgCapacity.tents?.followers ?? 0);
max += max +=
Math.floor(state.resource.houses?.value ?? 0) * (state.resource.houses?.value ?? 0) *
(state.config.cfgCapacity.houses?.followers ?? 0); (state.config.cfgCapacity.houses?.followers ?? 0);
return max; return max;
} };
public inc(state: GameState): number { public inc = (state: GameState): number => {
let inc = 0; let inc = 0;
// pastor recruiting // pastor recruiting
@ -52,12 +52,12 @@ class Follower implements IResource {
if (creds?.max !== undefined) inc *= creds.value / creds.max(state);*/ if (creds?.max !== undefined) inc *= creds.value / creds.max(state);*/
return inc; return inc;
} };
public addValue(amount: number, state: GameState): void { public addValue = (amount: number, state: GameState): void => {
const oldValue = this.value; const oldValue = this.value;
this.value += amount; this.rawValue += amount;
const diff = Math.floor(this.value) - Math.floor(oldValue); const diff = this.value - oldValue;
if (diff > 0) { if (diff > 0) {
// gained followers must come from other faiths // gained followers must come from other faiths
@ -80,13 +80,11 @@ class Follower implements IResource {
} }
} }
} }
} };
public isUnlocked(_state: GameState): boolean { public isUnlocked = (): boolean => true;
return 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 // chance for some followers to quit their jobs if money === 0
const money = state.resource.money; const money = state.resource.money;
const totalJobs = Job.totalJobs(state); const totalJobs = Job.totalJobs(state);
@ -190,7 +188,7 @@ class Follower implements IResource {
} }
this._lastRecruitmentLog = state.now; this._lastRecruitmentLog = state.now;
} }
} };
private _recruitFollower(state: GameState): void { private _recruitFollower(state: GameState): void {
// don't exceed max // don't exceed max

View File

@ -1,21 +1,18 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
abstract class Hidden implements IResource { abstract class Hidden extends Resource {
public readonly resourceType = ResourceType.passive; public readonly resourceType = ResourceType.passive;
public readonly label = undefined;
public readonly valueInWholeNumbers = false; public readonly valueInWholeNumbers = false;
public value = 0;
constructor( constructor(
public readonly singularName: string, public readonly singularName: string,
public readonly pluralName: string, public readonly pluralName: string,
public readonly description: string public readonly description: string
) {} ) {
super();
public addValue(amount: number, _state: GameState): void {
this.value += amount;
} }
public isUnlocked(_state: GameState): boolean { public isUnlocked = (): boolean => true;
return true;
}
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Infrastructure.ts" /> /// <reference path="./Infrastructure.ts" />
class House extends Infrastructure { class House extends Infrastructure {
public readonly resourceKey = ResourceKey.houses;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Houses', 'Houses',
@ -15,13 +17,11 @@ class House extends Infrastructure {
this._costMultiplier.money = config.cfgCostMultiplier.houses; this._costMultiplier.money = config.cfgCostMultiplier.houses;
} }
public max: (state: GameState) => number = (state) => public max = (state: GameState): number =>
Math.floor(
(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 = (state) => { public inc = (state: GameState): number => {
// compound managers // compound managers
return ( return (
(state.resource.compoundManagers?.value ?? 0) * (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; if (this._isUnlocked) return true;
const compounds = state.resource.compounds; const compounds = state.resource.compounds;
if (compounds !== undefined && compounds.value > 0) { if (compounds !== undefined && compounds.value > 0) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -2,13 +2,14 @@
interface IResource { interface IResource {
readonly resourceType: ResourceType; readonly resourceType: ResourceType;
readonly resourceKey: ResourceKey;
readonly label?: string; readonly label?: string;
readonly singularName: string; readonly singularName: string;
readonly pluralName: string; readonly pluralName: string;
readonly description: string; readonly description: string;
readonly valueInWholeNumbers: boolean; readonly valueInWholeNumbers: boolean;
readonly value: number;
readonly cost?: ResourceNumber; readonly cost?: ResourceNumber;
max?: (state: GameState) => number; max?: (state: GameState) => number;
@ -19,6 +20,8 @@ interface IResource {
addValue: (amount: number, state: GameState) => void; addValue: (amount: number, state: GameState) => void;
isUnlocked: (state: GameState) => boolean; isUnlocked: (state: GameState) => boolean;
restoreConfig: (config: ResourceConfig) => void;
emitConfig?: () => ResourceConfigValues; emitConfig?: () => ResourceConfigValues;
restoreConfig?: (config: ResourceConfigValues) => void;
get value(): number;
} }

View File

@ -1,9 +1,9 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
abstract class Job implements IResource { abstract class Job extends Resource {
public readonly resourceType = ResourceType.job; public readonly resourceType = ResourceType.job;
public readonly valueInWholeNumbers = true; public readonly valueInWholeNumbers = true;
public value = 0;
public readonly cost: ResourceNumber = {}; public readonly cost: ResourceNumber = {};
public max?: (state: GameState) => number = undefined; public max?: (state: GameState) => number = undefined;
@ -39,7 +39,9 @@ abstract class Job implements IResource {
public readonly singularName: string, public readonly singularName: string,
public readonly pluralName: string, public readonly pluralName: string,
public readonly description: string public readonly description: string
) {} ) {
super();
}
public static jobResources(state: GameState): ResourceKey[] { public static jobResources(state: GameState): ResourceKey[] {
return state.resources.filter((rkey) => { return state.resources.filter((rkey) => {
@ -74,20 +76,15 @@ abstract class Job implements IResource {
return followers - hired; return followers - hired;
} }
public addValue(amount: number): void { public isUnlocked: (_state: GameState) => boolean = () => {
this.value += amount;
if (this.value < 0) this.value = 0;
}
public isUnlocked(_state: GameState): boolean {
return this._isUnlocked; 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 // if we're out of followers then the jobs also vacate
const avail = Job.availableJobs(state); const avail = Job.availableJobs(state);
if (avail < 0 && this.value > 0) { if (avail < 0 && this.value > 0) {
this.addValue(avail); this.addValue(avail, state);
} }
return; return;
@ -110,7 +107,7 @@ abstract class Job implements IResource {
this.value < this.max(state) && this.value < this.max(state) &&
state.deductCost(this.cost) state.deductCost(this.cost)
) { ) {
this.addValue(1); this.addValue(1, state);
state.log(this._hireLog(1, state)); state.log(this._hireLog(1, state));
for (const key in this._costMultiplier) { for (const key in this._costMultiplier) {
const rkey = <ResourceKey>key; const rkey = <ResourceKey>key;
@ -122,7 +119,7 @@ abstract class Job implements IResource {
private _demoteFollower(state: GameState): void { private _demoteFollower(state: GameState): void {
if (this.value <= 0) return; if (this.value <= 0) return;
this.addValue(-1); this.addValue(-1, state);
state.log(this._hireLog(-1, state)); state.log(this._hireLog(-1, state));
for (const key in this._costMultiplier) { for (const key in this._costMultiplier) {
const rkey = <ResourceKey>key; const rkey = <ResourceKey>key;

View File

@ -1,6 +1,8 @@
/// <reference path="./Infrastructure.ts" /> /// <reference path="./Infrastructure.ts" />
class Megachurch extends Infrastructure { class Megachurch extends Infrastructure {
public readonly resourceKey = ResourceKey.megaChurches;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Megachurches', 'Megachurches',
@ -15,15 +17,15 @@ class Megachurch extends Infrastructure {
this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; this._costMultiplier.money = config.cfgCostMultiplier.megaChurches;
} }
public max: (state: GameState) => number = (state) => public max = (state: GameState): number =>
state.config.cfgInitialMax.megaChurches ?? 0; state.config.cfgInitialMax.megaChurches ?? 0;
public isUnlocked(state: GameState): boolean { public isUnlocked = (state: GameState): boolean => {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;
const permit = state.resource.buildingPermit; const permit = state.resource.buildingPermit;
if (permit !== undefined && permit.value > 0) { if (permit !== undefined && permit.value > 0) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,7 +1,9 @@
/// <reference path="./Purchasable.ts" /> /// <reference path="./Resource.ts" />
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 label = 'Money';
public readonly singularName = '${}'; public readonly singularName = '${}';
public readonly pluralName = '${}'; public readonly pluralName = '${}';
@ -23,15 +25,14 @@ class Money implements IResource {
private _lastCollectionTime = 0; private _lastCollectionTime = 0;
constructor(public value: number) {} public constructor(initialValue: number) {
super();
public isUnlocked = (_state: GameState): boolean => true; this.rawValue = initialValue;
public addValue(amount: number, _state: GameState): void {
this.value += amount;
} }
public max: (state: GameState) => number = (state: GameState) => { public isUnlocked = (_: GameState): boolean => true;
public max = (state: GameState): number => {
let max = state.config.cfgInitialMax.money ?? 0; let max = state.config.cfgInitialMax.money ?? 0;
max += max +=
(state.resource.compounds?.value ?? 0) * (state.resource.compounds?.value ?? 0) *
@ -39,20 +40,20 @@ class Money implements IResource {
return max; return max;
}; };
public inc: (state: GameState) => number = (state) => { public inc = (state: GameState): number => {
let inc = 0; let inc = 0;
// tithings // tithings
inc += inc +=
(Math.floor(state.resource.pastors?.value ?? 0) * ((state.resource.pastors?.value ?? 0) *
Math.floor(state.resource.followers?.value ?? 0) * (state.resource.followers?.value ?? 0) *
(state.config.cfgTitheAmount ?? 0) * (state.config.cfgTitheAmount ?? 0) *
Credibility.ratio(state)) / Credibility.ratio(state)) /
state.config.cfgTimeBetweenTithes; state.config.cfgTimeBetweenTithes;
// salaries // salaries
inc -= inc -=
Math.floor(state.resource.compoundManagers?.value ?? 0) * (state.resource.compoundManagers?.value ?? 0) *
(state.config.cfgSalary.compoundManagers ?? 0); (state.config.cfgSalary.compoundManagers ?? 0);
return inc; return inc;
@ -92,11 +93,9 @@ class Money implements IResource {
if (followers !== undefined) { if (followers !== undefined) {
state.log( state.log(
`You collected $${formatNumber(amount)} from ${formatNumber( `You collected $${formatNumber(amount)} from ${formatNumber(
Math.floor(followers.value) followers.value
)} ${ )} ${
Math.floor(followers.value) > 1 followers.value > 1 ? followers.pluralName : followers.singularName
? followers.pluralName
: followers.singularName
}.` }.`
); );
} else { } else {

View File

@ -1,22 +1,18 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
abstract class Passive implements IResource { abstract class Passive extends Resource {
public readonly resourceType = ResourceType.passive; public readonly resourceType = ResourceType.passive;
public readonly valueInWholeNumbers = false; public readonly valueInWholeNumbers = false;
public value = 0;
constructor( constructor(
public readonly label: string, public readonly label: string,
public readonly singularName: string, public readonly singularName: string,
public readonly pluralName: string, public readonly pluralName: string,
public readonly description: string public readonly description: string
) {} ) {
super();
public addValue(amount: number, _state: GameState): void {
this.value += amount;
} }
public isUnlocked(_state: GameState): boolean { public isUnlocked = (): boolean => true;
return true;
}
} }

View File

@ -1,6 +1,8 @@
/// <reference path="./Job.ts" /> /// <reference path="./Job.ts" />
class Pastor extends Job { class Pastor extends Job {
public readonly resourceKey = ResourceKey.pastors;
constructor() { constructor() {
super( super(
'Pastors', 'Pastors',
@ -10,19 +12,19 @@ class Pastor extends Job {
); );
} }
public max: (state: GameState) => number = (state) => { public max = (state: GameState): number => {
let max = let max =
Math.floor(state.resource.churches?.value ?? 0) * (state.resource.churches?.value ?? 0) *
(state.config.cfgCapacity.churches?.pastors ?? 0); (state.config.cfgCapacity.churches?.pastors ?? 0);
max += max +=
Math.floor(state.resource.megaChurches?.value ?? 0) * (state.resource.megaChurches?.value ?? 0) *
(state.config.cfgCapacity.megaChurches?.pastors ?? 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; if (this._isUnlocked) return true;
this._isUnlocked = state.resource.churches?.isUnlocked(state) === true; this._isUnlocked = state.resource.churches?.isUnlocked(state) === true;
return this._isUnlocked; return this._isUnlocked;
} };
} }

View File

@ -1,13 +1,13 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
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 valueInWholeNumbers = true;
public value = 0; public cost: ResourceNumber = {};
public readonly cost: ResourceNumber = {};
public inc?: (state: GameState) => number = undefined; public inc?: (state: GameState) => number = undefined;
public max?: (_state: GameState) => number = undefined; public max?: (state: GameState) => number = undefined;
public userActions: ResourceAction[] = [ public userActions: ResourceAction[] = [
{ {
@ -37,6 +37,7 @@ abstract class Purchasable implements IResource {
private readonly _sellButtonText = 'Sell', private readonly _sellButtonText = 'Sell',
private readonly _sellDescription = `Sell a ${singularName}.` private readonly _sellDescription = `Sell a ${singularName}.`
) { ) {
super();
if (canSell) { if (canSell) {
this.userActions.push({ this.userActions.push({
name: this._sellButtonText, name: this._sellButtonText,
@ -49,28 +50,25 @@ abstract class Purchasable implements IResource {
} }
} }
public addValue(amount: number, _state: GameState): void { public isUnlocked = (state: GameState): boolean => {
this.value += amount;
}
public isUnlocked(state: GameState): boolean {
if (!this._isUnlocked && state.isPurchasable(this.cost)) { if (!this._isUnlocked && state.isPurchasable(this.cost)) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;
} };
public advanceAction(_time: number, _state: GameState): void { public emitConfig = (): ResourceConfigValues => {
return;
}
public emitConfig: () => ResourceConfigValues = () => {
return { isUnlocked: this._isUnlocked }; return { isUnlocked: this._isUnlocked };
}; };
public restoreConfig: (config: ResourceConfigValues) => void = (config) => { public restoreConfig = (config: ResourceConfig): void => {
if (typeof config.isUnlocked === 'boolean') { this.rawValue = config.value;
this._isUnlocked = config.isUnlocked; 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 { 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)) {
this.value += 1; this.addValue(1, state);
state.log(this._purchaseLog(1, state)); state.log(this._purchaseLog(1, state));
for (const key in this._costMultiplier) { for (const key in this._costMultiplier) {
const rkey = <ResourceKey>key; const rkey = <ResourceKey>key;
@ -118,7 +116,7 @@ abstract class Purchasable implements IResource {
costBack[rkey] = cost * -1 * multiplier; costBack[rkey] = cost * -1 * multiplier;
state.deductCost(costBack); state.deductCost(costBack);
} }
this.value -= 1; this.addValue(1, state);
state.log(this._purchaseLog(-1, state)); state.log(this._purchaseLog(-1, state));
} }
} }

View File

@ -1,22 +1,20 @@
/// <reference path="./IResource.ts" /> /// <reference path="./Resource.ts" />
class Religion implements IResource { class Religion extends Resource {
public readonly resourceType = ResourceType.religion; public readonly resourceType = ResourceType.religion;
public readonly valueInWholeNumbers = true; public readonly valueInWholeNumbers = true;
constructor( constructor(
public readonly resourceKey: ResourceKey,
public readonly label: string, public readonly label: string,
public readonly singularName: string, public readonly singularName: string,
public readonly pluralName: string, public readonly pluralName: string,
public readonly description: string, public readonly description: string,
public value: number initialValue: number
) {} ) {
super();
public addValue(amount: number, _state: GameState): void { this.rawValue = initialValue;
this.value += amount;
} }
public isUnlocked(_state: GameState): boolean { public isUnlocked = (): boolean => true;
return true;
}
} }

View File

@ -2,6 +2,7 @@
abstract class Research extends Purchasable { abstract class Research extends Purchasable {
public readonly resourceType = ResourceType.research; public readonly resourceType = ResourceType.research;
public inc = undefined; public inc = undefined;
constructor( constructor(
@ -19,8 +20,7 @@ abstract class Research extends Purchasable {
'Learn', 'Learn',
'Complete this research.' 'Complete this research.'
); );
this.value = 0;
} }
public max: (state: GameState) => number = (_state) => 1; public max = (_: GameState): number => 1;
} }

View File

@ -0,0 +1,31 @@
/// <reference path="./IResource.ts" />
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;
};
}

View File

@ -3,7 +3,7 @@
enum ResourceType { enum ResourceType {
religion = 'religion', religion = 'religion',
job = 'job', job = 'job',
consumable = 'consumable', purchasable = 'purchasable',
infrastructure = 'infrastructure', infrastructure = 'infrastructure',
research = 'research', research = 'research',
passive = 'passive', passive = 'passive',

View File

@ -1,6 +1,8 @@
/// <reference path="./Infrastructure.ts" /> /// <reference path="./Infrastructure.ts" />
class Tent extends Infrastructure { class Tent extends Infrastructure {
public readonly resourceKey = ResourceKey.tents;
constructor(config: GameConfig) { constructor(config: GameConfig) {
super( super(
'Tents', 'Tents',
@ -15,16 +17,16 @@ class Tent extends Infrastructure {
this._costMultiplier.money = config.cfgCostMultiplier.tents; this._costMultiplier.money = config.cfgCostMultiplier.tents;
} }
public max: (state: GameState) => number = (state) => { public max = (state: GameState): number => {
// ten extra tents per compound // ten extra tents per compound
let max = state.config.cfgInitialMax.tents ?? 0; let max = state.config.cfgInitialMax.tents ?? 0;
max += max +=
(state.resource.compounds?.value ?? 0) * (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.tents ?? 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 // compound managers
(state.resource.compoundManagers?.value ?? 0) * (state.resource.compoundManagers?.value ?? 0) *
(state.config.cfgBuySpeed.compoundManagers?.tents ?? 0); (state.config.cfgBuySpeed.compoundManagers?.tents ?? 0);