replaced eslint formatting rules with prettier

This commit is contained in:
Rudis Muiznieks 2021-09-06 12:02:38 -05:00
parent afe0592498
commit e939dec388
29 changed files with 485 additions and 234 deletions

View File

@ -2,13 +2,18 @@
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking" "plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": {"project": "tsconfig.json"}, "parserOptions": {"project": "tsconfig.json"},
"plugins": ["@typescript-eslint"], "plugins": [
"@typescript-eslint",
"prettier"
],
"rules": { "rules": {
"@typescript-eslint/triple-slash-reference": "off", "@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-unused-vars": "off" "@typescript-eslint/no-unused-vars": "off",
"prettier/prettier": "warn"
} }
} }

3
.prettierrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

103
package-lock.json generated
View File

@ -9,6 +9,9 @@
"@typescript-eslint/parser": "^4.30.0", "@typescript-eslint/parser": "^4.30.0",
"@typescript-eslint/typescript-estree": "^4.30.0", "@typescript-eslint/typescript-estree": "^4.30.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.3.2",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"typescript-eslint-language-service": "^4.1.5", "typescript-eslint-language-service": "^4.1.5",
"typescript-language-server": "^0.6.2" "typescript-language-server": "^0.6.2"
@ -762,6 +765,39 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint-config-prettier": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
"integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
"integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0"
},
"engines": {
"node": ">=6.0.0"
},
"peerDependencies": {
"eslint": ">=7.28.0",
"prettier": ">=2.0.0"
},
"peerDependenciesMeta": {
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -910,6 +946,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"node_modules/fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
@ -1483,6 +1525,30 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/progress": { "node_modules/progress": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@ -2561,6 +2627,22 @@
"v8-compile-cache": "^2.0.3" "v8-compile-cache": "^2.0.3"
} }
}, },
"eslint-config-prettier": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
"integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
"dev": true,
"requires": {}
},
"eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
"integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -2671,6 +2753,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"fast-glob": { "fast-glob": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
@ -3112,6 +3200,21 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true "dev": true
}, },
"prettier": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"progress": { "progress": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",

View File

@ -9,6 +9,9 @@
"@typescript-eslint/parser": "^4.30.0", "@typescript-eslint/parser": "^4.30.0",
"@typescript-eslint/typescript-estree": "^4.30.0", "@typescript-eslint/typescript-estree": "^4.30.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.3.2",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"typescript-eslint-language-service": "^4.1.5", "typescript-eslint-language-service": "^4.1.5",
"typescript-language-server": "^0.6.2" "typescript-language-server": "^0.6.2"

View File

@ -10,8 +10,8 @@ const cycleLength = 250;
function gameLoop(state: GameState, renderer: IRenderer): void { function gameLoop(state: GameState, renderer: IRenderer): void {
// figure out how much actual time has passed // figure out how much actual time has passed
const elapsedTime: number = globalStartTime > 0 const elapsedTime: number =
? new Date().getTime() - globalStartTime : 0; globalStartTime > 0 ? new Date().getTime() - globalStartTime : 0;
state.advance(elapsedTime); state.advance(elapsedTime);
renderer.render(state); renderer.render(state);
@ -30,7 +30,8 @@ function startGame (state: GameState, renderer: IRenderer): void {
function initialRender(state: GameState): void { function initialRender(state: GameState): void {
if (state.logger === null) return; if (state.logger === null) return;
state.logger.unsafeMsg(`<strong>Welcome to irreligio.us!</strong> <em>alpha v${versionMajor}.${versionMinor}</em> state.logger
.unsafeMsg(`<strong>Welcome to irreligio.us!</strong> <em>alpha v${versionMajor}.${versionMinor}</em>
<br><br> <br><br>
The game is still in an active state of development and nowhere near its final form. This is a debugging interface that can show all resources even before they're unlocked, and many factors may be sped up significantly to aid in development. There is a chance that playing it now may spoil aspects of the game for you later when it's closer to being finished. The game is still in an active state of development and nowhere near its final form. This is a debugging interface that can show all resources even before they're unlocked, and many factors may be sped up significantly to aid in development. There is a chance that playing it now may spoil aspects of the game for you later when it's closer to being finished.
<br><br> <br><br>
@ -63,7 +64,8 @@ The game's source code on <a href='https://github.com/rudism/irreligious'>Github
}); });
if (document.readyState !== 'loading') startGame(state, renderer); if (document.readyState !== 'loading') startGame(state, renderer);
else document.addEventListener('DOMContentLoaded', (): void => { else
document.addEventListener('DOMContentLoaded', (): void => {
startGame(state, renderer); startGame(state, renderer);
}); });
})(); })();

View File

@ -79,10 +79,7 @@ class GameConfig {
public cfgTitheAmount = 10; public cfgTitheAmount = 10;
public cfgTitheCredibilityHitFactor = 3; public cfgTitheCredibilityHitFactor = 3;
constructor ( constructor(public versionMajor: number, public versionMinor: number) {}
public versionMajor: number,
public versionMinor: number,
) {}
public generateState(): GameState { public generateState(): GameState {
const state = new GameState(this); const state = new GameState(this);
@ -91,43 +88,99 @@ class GameConfig {
state.addResource(ResourceKey.followers, new Follower()); state.addResource(ResourceKey.followers, new Follower());
// create world religions // create world religions
state.addResource(ResourceKey.christianity, new Religion( state.addResource(
'Christianity', 'christian', 'christians', 'God, Jesus, Bible, churches.', ResourceKey.christianity,
(this.cfgReligion.christianity ?? 0) * this.worldPopulation)); new Religion(
'Christianity',
'christian',
'christians',
'God, Jesus, Bible, churches.',
(this.cfgReligion.christianity ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.islam, new Religion( state.addResource(
'Islam', 'muslim', 'muslims', 'God, Muhammad, Quran, mosques.', ResourceKey.islam,
(this.cfgReligion.islam ?? 0) * this.worldPopulation)); new Religion(
'Islam',
'muslim',
'muslims',
'God, Muhammad, Quran, mosques.',
(this.cfgReligion.islam ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.hinduism, new Religion( state.addResource(
'Hinduism', 'hindu', 'hindus', 'Dogma-free spiritualism.', ResourceKey.hinduism,
(this.cfgReligion.hinduism ?? 0) * this.worldPopulation)); new Religion(
'Hinduism',
'hindu',
'hindus',
'Dogma-free spiritualism.',
(this.cfgReligion.hinduism ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.buddhism, new Religion( state.addResource(
'Buddhism', 'buddhist', 'buddhists', 'The minimization of suffering.', ResourceKey.buddhism,
(this.cfgReligion.buddhism ?? 0) * this.worldPopulation)); new Religion(
'Buddhism',
'buddhist',
'buddhists',
'The minimization of suffering.',
(this.cfgReligion.buddhism ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.sikhism, new Religion( state.addResource(
'Sikhism', 'sikh', 'sikhs', 'Meditation and ten Gurus', ResourceKey.sikhism,
(this.cfgReligion.sikhism ?? 0) * this.worldPopulation)); new Religion(
'Sikhism',
'sikh',
'sikhs',
'Meditation and ten Gurus',
(this.cfgReligion.sikhism ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.judaism, new Religion( state.addResource(
'Judaism', 'jew', 'jews', 'God, Abraham, Torah, synagogues.', ResourceKey.judaism,
(this.cfgReligion.judaism ?? 0) * this.worldPopulation)); new Religion(
'Judaism',
'jew',
'jews',
'God, Abraham, Torah, synagogues.',
(this.cfgReligion.judaism ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.other, new Religion( state.addResource(
'Other', 'person from other faiths', 'people from other faiths', 'A variety of belief systems.', ResourceKey.other,
(this.cfgReligion.other ?? 0) * this.worldPopulation)); new Religion(
'Other',
'person from other faiths',
'people from other faiths',
'A variety of belief systems.',
(this.cfgReligion.other ?? 0) * this.worldPopulation
)
);
state.addResource(ResourceKey.atheism, new Religion( state.addResource(
'Non-Religious', 'atheist', 'atheists', 'Atheists and agnostics.', ResourceKey.atheism,
(this.cfgReligion.atheism ?? 0) * this.worldPopulation)); new Religion(
'Non-Religious',
'atheist',
'atheists',
'Atheists and agnostics.',
(this.cfgReligion.atheism ?? 0) * this.worldPopulation
)
);
// add jobs // add jobs
state.addResource(ResourceKey.pastors, new Pastor()); state.addResource(ResourceKey.pastors, new Pastor());
// add resources // add resources
state.addResource(ResourceKey.money, new Money(3.50)); state.addResource(ResourceKey.money, new Money(3.5));
state.addResource(ResourceKey.cryptoCurrency, new CryptoCurrency(this)); state.addResource(ResourceKey.cryptoCurrency, new CryptoCurrency(this));
state.addResource(ResourceKey.tents, new Tent(this)); state.addResource(ResourceKey.tents, new Tent(this));
state.addResource(ResourceKey.houses, new House(this)); state.addResource(ResourceKey.houses, new House(this));

View File

@ -54,9 +54,11 @@ class GameState {
const resource = this._resources[rkey]; const resource = this._resources[rkey];
if (resource === undefined || !resource.isUnlocked(this)) continue; if (resource === undefined || !resource.isUnlocked(this)) continue;
if (resource.inc !== undefined && (resource.max === undefined if (
|| resource.value < resource.max(this))) { resource.inc !== undefined &&
resource.addValue(resource.inc(this) * time / 1000, this); (resource.max === undefined || resource.value < resource.max(this))
) {
resource.addValue((resource.inc(this) * time) / 1000, this);
} }
if (resource.max !== undefined && resource.value > resource.max(this)) { if (resource.max !== undefined && resource.value > resource.max(this)) {
@ -70,9 +72,13 @@ class GameState {
public performAction(resourceKey: ResourceKey, actionIndex: number): void { public performAction(resourceKey: ResourceKey, actionIndex: number): void {
const resource = this._resources[resourceKey]; const resource = this._resources[resourceKey];
if (resource === undefined || resource.userActions === undefined if (
|| actionIndex > resource.userActions.length resource === undefined ||
|| !resource.isUnlocked(this)) return; resource.userActions === undefined ||
actionIndex > resource.userActions.length ||
!resource.isUnlocked(this)
)
return;
const action = resource.userActions[actionIndex]; const action = resource.userActions[actionIndex];
@ -153,8 +159,10 @@ class GameState {
resource.value = saveRes.value; resource.value = saveRes.value;
// @ts-expect-error writing read-only cost from save data // @ts-expect-error writing read-only cost from save data
resource.cost = saveRes.cost; resource.cost = saveRes.cost;
if (saveRes.config !== undefined if (
&& resource.restoreConfig !== undefined) { saveRes.config !== undefined &&
resource.restoreConfig !== undefined
) {
resource.restoreConfig(saveRes.config); resource.restoreConfig(saveRes.config);
} }
} }

View File

@ -1,7 +1,7 @@
const numberFormatDigits = 1; const numberFormatDigits = 1;
function formatNumber(num: number): string { function formatNumber(num: number): string {
type UnitLookup = { value: number, symbol: string }; type UnitLookup = { value: number; symbol: string };
const lookup: UnitLookup[] = [ const lookup: UnitLookup[] = [
{ value: 1, symbol: '' }, { value: 1, symbol: '' },
{ value: 1e3, symbol: 'K' }, { value: 1e3, symbol: 'K' },
@ -17,7 +17,7 @@ function formatNumber (num: number): string {
if (num >= item.value) break; if (num >= item.value) break;
} }
return item !== undefined return item !== undefined
? (num / item.value).toFixed( ? (num / item.value).toFixed(numberFormatDigits).replace(rx, '$1') +
numberFormatDigits).replace(rx, '$1') + item.symbol item.symbol
: num.toFixed(numberFormatDigits).replace(rx, '$1'); : num.toFixed(numberFormatDigits).replace(rx, '$1');
} }

View File

@ -6,8 +6,9 @@ class BuildingPermit extends Research {
'Building Permit', 'Building Permit',
'building permit', 'building permit',
'building permits', 'building permits',
'Unlocks several new buildings you can build outside of your compounds.'); 'Unlocks several new buildings you can build outside of your compounds.'
this.cost.money = config.cfgInitialMax.buildingPermit; );
this.cost.money = config.cfgInitialCost.buildingPermit;
} }
public isUnlocked(state: GameState): boolean { public isUnlocked(state: GameState): boolean {

View File

@ -6,14 +6,17 @@ class Church extends Infrastructure {
'Churches', 'Churches',
'church', 'church',
'churches', 'churches',
`Preaching grounds for ${formatNumber(config.cfgCapacity.churches?.pastors ?? 0)} pastors.`); `Preaching grounds for ${formatNumber(
config.cfgCapacity.churches?.pastors ?? 0
)} pastors.`
);
this.cost.money = config.cfgInitialCost.churches; this.cost.money = config.cfgInitialCost.churches;
this._costMultiplier.money = config.cfgCostMultiplier.churches; this._costMultiplier.money = config.cfgCostMultiplier.churches;
} }
public max: (state: GameState) => number = (state) => public max: (state: GameState) => number = (state) =>
(state.resource.compounds?.value ?? 0) (state.resource.compounds?.value ?? 0) *
* (state.config.cfgCapacity.compounds?.churches ?? 0); (state.config.cfgCapacity.compounds?.churches ?? 0);
public isUnlocked(state: GameState): boolean { public isUnlocked(state: GameState): boolean {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;

View File

@ -6,7 +6,8 @@ class Compound extends Infrastructure {
'Compounds', 'Compounds',
'compound', 'compound',
'compounds', 'compounds',
'Provides space for tents, houses, and churches and a place to hide more money.'); 'Provides space for tents, houses, and churches and a place to hide more money.'
);
this.cost.money = config.cfgInitialCost.compounds; this.cost.money = config.cfgInitialCost.compounds;
this._costMultiplier.money = config.cfgCostMultiplier.compounds; this._costMultiplier.money = config.cfgCostMultiplier.compounds;
} }
@ -14,8 +15,10 @@ class Compound extends Infrastructure {
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 (tents !== undefined if (
&& tents.value >= (state.config.cfgInitialMax.tents ?? 0)) { tents !== undefined &&
tents.value >= (state.config.cfgInitialMax.tents ?? 0)
) {
this._isUnlocked = true; this._isUnlocked = true;
} }
return this._isUnlocked; return this._isUnlocked;

View File

@ -6,7 +6,8 @@ class Credibility extends Passive {
'Credibility', 'Credibility',
'credibility', 'credibility',
'credibilities', 'credibilities',
'Affects your ability to recruit and retain followers.'); 'Affects your ability to recruit and retain followers.'
);
this.value = config.cfgPassiveMax; this.value = config.cfgPassiveMax;
} }

View File

@ -6,7 +6,8 @@ class CryptoCurrency extends Purchasable {
'FaithCoin', 'FaithCoin',
'faithcoin', 'faithcoin',
'faithcoins', 'faithcoins',
"A crypto coin that can't be spent directly, but provides a steady stream of passive income."); "A crypto coin that can't be spent directly, but provides a steady stream of passive income."
);
this.cost.money = config.cfgInitialCost.cryptoCurrency; this.cost.money = config.cfgInitialCost.cryptoCurrency;
this._costMultiplier.money = config.cfgCostMultiplier.cryptoCurrency; this._costMultiplier.money = config.cfgCostMultiplier.cryptoCurrency;
this.valueInWholeNumbers = false; this.valueInWholeNumbers = false;

View File

@ -27,10 +27,12 @@ class Follower implements IResource {
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 += (state.resource.tents?.value ?? 0) max +=
* (state.config.cfgCapacity.tents?.followers ?? 0); (state.resource.tents?.value ?? 0) *
max += (state.resource.houses?.value ?? 0) (state.config.cfgCapacity.tents?.followers ?? 0);
* (state.config.cfgCapacity.houses?.followers ?? 0); max +=
(state.resource.houses?.value ?? 0) *
(state.config.cfgCapacity.houses?.followers ?? 0);
return max; return max;
} }
@ -87,12 +89,13 @@ class Follower implements IResource {
if (this.value > 0) { if (this.value > 0) {
const creds = state.resource.credibility; const creds = state.resource.credibility;
if (creds?.max !== undefined) { if (creds?.max !== undefined) {
const ratio = const ratio = Math.ceil(creds.value) / creds.max(state);
Math.ceil(creds.value) / creds.max(state);
if (Math.random() > ratio) { if (Math.random() > ratio) {
const lost = Math.ceil(this.value const lost = Math.ceil(
* state.config.cfgCredibilityFollowerLossRatio this.value *
* (1 - ratio)); state.config.cfgCredibilityFollowerLossRatio *
(1 - ratio)
);
this.addValue(lost * -1, state); this.addValue(lost * -1, state);
} }
} }
@ -101,10 +104,12 @@ class Follower implements IResource {
} }
// log lost and gained followers every 10s // log lost and gained followers every 10s
if (state.now if (
- this._lastRecruitmentLog > state.config.cfgFollowerGainLossLogTimer state.now - this._lastRecruitmentLog >
&& (Object.keys(this._followerSources).length > 0 state.config.cfgFollowerGainLossLogTimer &&
|| Object.keys(this._followerDests).length > 0)) { (Object.keys(this._followerSources).length > 0 ||
Object.keys(this._followerDests).length > 0)
) {
if (Object.keys(this._followerDests).length > 0) { if (Object.keys(this._followerDests).length > 0) {
let msg = ''; let msg = '';
let total = 0; let total = 0;
@ -114,12 +119,18 @@ class Follower implements IResource {
const followers = this._followerDests[rkey]; const followers = this._followerDests[rkey];
if (religion !== undefined && followers !== undefined) { if (religion !== undefined && followers !== undefined) {
if (msg !== '') msg += ', '; if (msg !== '') msg += ', ';
msg += `${formatNumber(followers)} ${followers > 1 ? religion.pluralName : religion.singularName}`; msg += `${formatNumber(followers)} ${
followers > 1 ? religion.pluralName : religion.singularName
}`;
total += followers; total += followers;
delete this._followerDests[rkey]; delete this._followerDests[rkey];
} }
} }
state.log(`You lost ${formatNumber(total)} ${total > 1 ? this.pluralName : this.singularName}: ${msg}`); state.log(
`You lost ${formatNumber(total)} ${
total > 1 ? this.pluralName : this.singularName
}: ${msg}`
);
} }
if (Object.keys(this._followerSources).length > 0) { if (Object.keys(this._followerSources).length > 0) {
let msg = ''; let msg = '';
@ -130,13 +141,18 @@ class Follower implements IResource {
const followers = this._followerSources[rkey]; const followers = this._followerSources[rkey];
if (religion !== undefined && followers !== undefined) { if (religion !== undefined && followers !== undefined) {
if (msg !== '') msg += ', '; if (msg !== '') msg += ', ';
msg += msg += `${formatNumber(followers)} ${
`${formatNumber(followers)} ${followers > 1 ? religion.pluralName : religion.singularName}`; followers > 1 ? religion.pluralName : religion.singularName
}`;
total += followers; total += followers;
delete this._followerSources[rkey]; delete this._followerSources[rkey];
} }
} }
state.log(`You gained ${formatNumber(total)} ${total > 1 ? this.pluralName : this.singularName}: ${msg}`); state.log(
`You gained ${formatNumber(total)} ${
total > 1 ? this.pluralName : this.singularName
}: ${msg}`
);
} }
this._lastRecruitmentLog = state.now; this._lastRecruitmentLog = state.now;
} }
@ -164,10 +180,18 @@ class Follower implements IResource {
} }
private _getRandomReligion( private _getRandomReligion(
state: GameState): [ResourceKey, IResource] | null { state: GameState
const religs = [ResourceKey.christianity, ResourceKey.islam, ): [ResourceKey, IResource] | null {
ResourceKey.hinduism, ResourceKey.buddhism, ResourceKey.sikhism, const religs = [
ResourceKey.judaism, ResourceKey.other, ResourceKey.atheism]; ResourceKey.christianity,
ResourceKey.islam,
ResourceKey.hinduism,
ResourceKey.buddhism,
ResourceKey.sikhism,
ResourceKey.judaism,
ResourceKey.other,
ResourceKey.atheism,
];
const source = religs[Math.floor(Math.random() * 8)]; const source = religs[Math.floor(Math.random() * 8)];
const resource = state.resource[source]; const resource = state.resource[source];
return resource !== undefined ? [source, resource] : null; return resource !== undefined ? [source, resource] : null;

View File

@ -6,14 +6,17 @@ class House extends Infrastructure {
'Houses', 'Houses',
'house', 'house',
'houses', 'houses',
`Provides room to house ${formatNumber(config.cfgCapacity.houses?.followers ?? 0)} followers.`); `Provides room to house ${formatNumber(
config.cfgCapacity.houses?.followers ?? 0
)} followers.`
);
this.cost.money = config.cfgInitialCost.houses; this.cost.money = config.cfgInitialCost.houses;
this._costMultiplier.money = config.cfgCostMultiplier.houses; this._costMultiplier.money = config.cfgCostMultiplier.houses;
} }
public max: (state: GameState) => number = (state) => public max: (state: GameState) => number = (state) =>
(state.resource.compounds?.value ?? 0) (state.resource.compounds?.value ?? 0) *
* (state.config.cfgCapacity.compounds?.houses ?? 0); (state.config.cfgCapacity.compounds?.houses ?? 0);
public isUnlocked(state: GameState): boolean { public isUnlocked(state: GameState): boolean {
if (this._isUnlocked) return true; if (this._isUnlocked) return true;

View File

@ -20,6 +20,7 @@ interface IResource {
isUnlocked: (state: GameState) => boolean; isUnlocked: (state: GameState) => boolean;
emitConfig?: () => { [key: string]: string | number | boolean }; emitConfig?: () => { [key: string]: string | number | boolean };
restoreConfig?: ( restoreConfig?: (config: {
config: { [key: string]: string | number | boolean }) => void; [key: string]: string | number | boolean;
}) => void;
} }

View File

@ -14,8 +14,8 @@ abstract class Job implements IResource {
name: 'Hire', name: 'Hire',
description: 'Promote one of your followers.', description: 'Promote one of your followers.',
isEnabled: (state: GameState): boolean => isEnabled: (state: GameState): boolean =>
(this.max === undefined || this.value < this.max(state)) (this.max === undefined || this.value < this.max(state)) &&
&& this._availableJobs(state) > 0, this._availableJobs(state) > 0,
performAction: (state: GameState): void => { performAction: (state: GameState): void => {
this._promoteFollower(state); this._promoteFollower(state);
}, },
@ -64,10 +64,10 @@ abstract class Job implements IResource {
const hired = state.resources.reduce( const hired = state.resources.reduce(
(tot: number, rkey: ResourceKey): number => { (tot: number, rkey: ResourceKey): number => {
const res = state.resource[rkey]; const res = state.resource[rkey];
return res?.resourceType === ResourceType.job return res?.resourceType === ResourceType.job ? tot + res.value : tot;
? tot + res.value },
: tot; 0
}, 0); );
return followers - hired; return followers - hired;
} }
@ -77,23 +77,30 @@ abstract class Job implements IResource {
const hired = state.resources.reduce( const hired = state.resources.reduce(
(tot: number, rkey: ResourceKey): number => { (tot: number, rkey: ResourceKey): number => {
const res = state.resource[rkey]; const res = state.resource[rkey];
return res?.resourceType === ResourceType.job return res?.resourceType === ResourceType.job ? tot + res.value : tot;
? tot + res.value },
: tot; 0
}, 0); );
return followers - hired; return followers - hired;
} }
protected _hireLog(amount: number, _state: GameState): string { protected _hireLog(amount: number, _state: GameState): string {
return amount > 0 return amount > 0
? `You hired ${amount} ${amount > 1 ? this.pluralName : this.singularName}.` ? `You hired ${amount} ${
: `You fired ${amount * -1} ${amount * -1 > 1 ? this.pluralName : this.singularName}.`; amount > 1 ? this.pluralName : this.singularName
}.`
: `You fired ${amount * -1} ${
amount * -1 > 1 ? this.pluralName : this.singularName
}.`;
} }
private _promoteFollower(state: GameState): void { private _promoteFollower(state: GameState): void {
if (this._availableJobs(state) <= 0) return; if (this._availableJobs(state) <= 0) return;
if (this.max !== undefined && this.value < this.max(state) if (
&& state.deductCost(this.cost)) { this.max !== undefined &&
this.value < this.max(state) &&
state.deductCost(this.cost)
) {
this.addValue(1); this.addValue(1);
state.log(this._hireLog(1, state)); state.log(this._hireLog(1, state));
for (const key in this._costMultiplier) { for (const key in this._costMultiplier) {

View File

@ -6,7 +6,10 @@ class Megachurch extends Infrastructure {
'Megachurches', 'Megachurches',
'megachurch', 'megachurch',
'megachurches', 'megachurches',
`Room for ${formatNumber(config.cfgCapacity.megaChurches?.pastors ?? 0)} pastors`); `Room for ${formatNumber(
config.cfgCapacity.megaChurches?.pastors ?? 0
)} pastors`
);
this.cost.money = config.cfgInitialCost.megaChurches; this.cost.money = config.cfgInitialCost.megaChurches;
this._costMultiplier.money = config.cfgCostMultiplier.megaChurches; this._costMultiplier.money = config.cfgCostMultiplier.megaChurches;
} }

View File

@ -13,8 +13,8 @@ class Money implements IResource {
name: 'Collect Tithes', name: 'Collect Tithes',
description: 'Voluntary contributions from followers.', description: 'Voluntary contributions from followers.',
isEnabled: (state: GameState): boolean => isEnabled: (state: GameState): boolean =>
this.value < this.max(state) this.value < this.max(state) &&
&& (state.resource.followers?.value ?? 0) >= 1, (state.resource.followers?.value ?? 0) >= 1,
performAction: (state: GameState): void => { performAction: (state: GameState): void => {
this._collectTithes(state); this._collectTithes(state);
}, },
@ -23,9 +23,7 @@ class Money implements IResource {
private _lastCollectionTime = 0; private _lastCollectionTime = 0;
constructor ( constructor(public value: number) {}
public value: number
) {}
public isUnlocked = (_state: GameState): boolean => true; public isUnlocked = (_state: GameState): boolean => true;
@ -35,8 +33,9 @@ class Money implements IResource {
public max: (state: GameState) => number = (state: GameState) => { public max: (state: GameState) => number = (state: GameState) => {
let max = state.config.cfgInitialMax.money ?? 0; let max = state.config.cfgInitialMax.money ?? 0;
max += (state.resource.compounds?.value ?? 0) max +=
* (state.config.cfgCapacity.compounds?.money ?? 0); (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.money ?? 0);
return max; return max;
}; };
@ -44,12 +43,14 @@ class Money implements IResource {
let inc = 0; let inc = 0;
// crypto currency // crypto currency
inc += (state.resource.cryptoCurrency?.value ?? 0) inc +=
* state.config.cfgCryptoReturnAmount; (state.resource.cryptoCurrency?.value ?? 0) *
state.config.cfgCryptoReturnAmount;
// salaries // salaries
inc -= (state.resource.pastors?.value ?? 0) inc -=
* (state.config.cfgSalary.pastors ?? 0); (state.resource.pastors?.value ?? 0) *
(state.config.cfgSalary.pastors ?? 0);
return inc; return inc;
}; };
@ -63,8 +64,10 @@ class Money implements IResource {
// collecting too frequently hurts credibility // collecting too frequently hurts credibility
const diff = state.now - this._lastCollectionTime; const diff = state.now - this._lastCollectionTime;
if (diff < state.config.cfgTimeBetweenTithes) { if (diff < state.config.cfgTimeBetweenTithes) {
const lost = state.config.cfgTimeBetweenTithes const lost =
/ diff / state.config.cfgTitheCredibilityHitFactor; state.config.cfgTimeBetweenTithes /
diff /
state.config.cfgTitheCredibilityHitFactor;
state.resource.credibility?.addValue(lost * -1, state); state.resource.credibility?.addValue(lost * -1, state);
} }
@ -80,7 +83,11 @@ class Money implements IResource {
protected _purchaseLog(amount: number, state: GameState): string { protected _purchaseLog(amount: number, state: GameState): string {
const followers = state.resource.followers; const followers = state.resource.followers;
if (followers !== undefined) { if (followers !== undefined) {
return `You collected $${formatNumber(amount)} from ${formatNumber(followers.value)} ${followers.value > 1 ? followers.pluralName : followers.singularName}.`; return `You collected $${formatNumber(amount)} from ${formatNumber(
followers.value
)} ${
followers.value > 1 ? followers.pluralName : followers.singularName
}.`;
} }
return `You collected $${formatNumber(amount)} in tithings.`; return `You collected $${formatNumber(amount)} in tithings.`;
} }

View File

@ -14,7 +14,6 @@ abstract class Passive implements IResource {
public readonly description: string public readonly description: string
) {} ) {}
public addValue(amount: number, _state: GameState): void { public addValue(amount: number, _state: GameState): void {
this.value += amount; this.value += amount;
} }
@ -22,5 +21,4 @@ abstract class Passive implements IResource {
public isUnlocked(_state: GameState): boolean { public isUnlocked(_state: GameState): boolean {
return true; return true;
} }
} }

View File

@ -8,14 +8,17 @@ class Pastor extends Job {
'Pastors', 'Pastors',
'pastor', 'pastor',
'pastors', 'pastors',
'Collect tithings for you and recruit new members from other faiths automatically.'); 'Collect tithings for you and recruit new members from other faiths automatically.'
);
} }
public max: (state: GameState) => number = (state) => { public max: (state: GameState) => number = (state) => {
let max = (state.resource.churches?.value ?? 0) let max =
* (state.config.cfgCapacity.churches?.pastors ?? 0); (state.resource.churches?.value ?? 0) *
max += (state.resource.megaChurches?.value ?? 0) (state.config.cfgCapacity.churches?.pastors ?? 0);
* (state.config.cfgCapacity.megaChurches?.pastors ?? 0); max +=
(state.resource.megaChurches?.value ?? 0) *
(state.config.cfgCapacity.megaChurches?.pastors ?? 0);
return max; return max;
}; };
@ -31,18 +34,27 @@ class Pastor extends Job {
if (this._timeSinceLastTithe >= state.config.cfgTimeBetweenTithes) { if (this._timeSinceLastTithe >= state.config.cfgTimeBetweenTithes) {
const money = state.resource.money; const money = state.resource.money;
const followers = state.resource.followers; const followers = state.resource.followers;
let tithed = Math.floor(this.value let tithed = Math.floor(
* state.config.cfgPastorTitheCollectionFollowerMax); this.value * state.config.cfgPastorTitheCollectionFollowerMax
);
if (Math.floor(followers?.value ?? 0) < tithed) if (Math.floor(followers?.value ?? 0) < tithed)
tithed = Math.floor(followers?.value ?? 0); tithed = Math.floor(followers?.value ?? 0);
let collected = tithed * state.config.cfgTitheAmount; let collected = tithed * state.config.cfgTitheAmount;
if (money?.max !== undefined if (
&& collected > money.max(state) - money.value) money?.max !== undefined &&
collected > money.max(state) - money.value
)
collected = money.max(state) - money.value; collected = money.max(state) - money.value;
if (collected > 0) { if (collected > 0) {
money?.addValue(collected, state); money?.addValue(collected, state);
if (followers !== undefined) { if (followers !== undefined) {
state.log(`Your pastors collected $${formatNumber(collected)} in tithings from ${formatNumber(tithed)} ${tithed > 1 ? followers.pluralName : followers.singularName}.`); state.log(
`Your pastors collected $${formatNumber(
collected
)} in tithings from ${formatNumber(tithed)} ${
tithed > 1 ? followers.pluralName : followers.singularName
}.`
);
} }
} }
this._timeSinceLastTithe = 0; this._timeSinceLastTithe = 0;

View File

@ -14,8 +14,8 @@ abstract class Purchasable implements IResource {
name: this._purchaseButtonText, name: this._purchaseButtonText,
description: this._purchaseDescription, description: this._purchaseDescription,
isEnabled: (state: GameState): boolean => isEnabled: (state: GameState): boolean =>
(this.max === undefined || this.value < this.max(state)) (this.max === undefined || this.value < this.max(state)) &&
&& state.isPurchasable(this.cost), state.isPurchasable(this.cost),
performAction: (state: GameState): void => { performAction: (state: GameState): void => {
this._purchase(state); this._purchase(state);
}, },
@ -31,7 +31,7 @@ abstract class Purchasable implements IResource {
public readonly pluralName: string, public readonly pluralName: string,
public readonly description: string, public readonly description: string,
private readonly _purchaseButtonText: string = 'Purchase', private readonly _purchaseButtonText: string = 'Purchase',
private readonly _purchaseDescription: string = `Buy a ${singularName}.`, private readonly _purchaseDescription: string = `Buy a ${singularName}.`
) {} ) {}
public addValue(amount: number, _state: GameState): void { public addValue(amount: number, _state: GameState): void {
@ -50,7 +50,9 @@ abstract class Purchasable implements IResource {
} }
protected _purchaseLog(amount: number, _state: GameState): string { protected _purchaseLog(amount: number, _state: GameState): string {
return `You purchased ${amount} ${amount > 1 ? this.pluralName : this.singularName}.`; return `You purchased ${amount} ${
amount > 1 ? this.pluralName : this.singularName
}.`;
} }
private _purchase(state: GameState): void { private _purchase(state: GameState): void {

View File

@ -9,7 +9,7 @@ class Religion 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,
public value: number, public value: number
) {} ) {}
public addValue(amount: number, _state: GameState): void { public addValue(amount: number, _state: GameState): void {

View File

@ -16,7 +16,8 @@ abstract class Research extends Purchasable {
pluralName, pluralName,
description, description,
'Learn', 'Learn',
'Complete this research.'); 'Complete this research.'
);
this.value = 0; this.value = 0;
} }

View File

@ -47,6 +47,6 @@ type ResourceConfig = {
}; };
type SaveData = { type SaveData = {
version: { maj: number, min: number }; version: { maj: number; min: number };
resources: { [key in ResourceKey]?: ResourceConfig }; resources: { [key in ResourceKey]?: ResourceConfig };
}; };

View File

@ -6,7 +6,10 @@ class Tent extends Infrastructure {
'Tents', 'Tents',
'tent', 'tent',
'tents', 'tents',
`Provides room to house ${formatNumber(config.cfgCapacity.tents?.followers ?? 0)} followers.`); `Provides room to house ${formatNumber(
config.cfgCapacity.tents?.followers ?? 0
)} followers.`
);
this.cost.money = config.cfgInitialCost.tents; this.cost.money = config.cfgInitialCost.tents;
this._costMultiplier.money = config.cfgCostMultiplier.tents; this._costMultiplier.money = config.cfgCostMultiplier.tents;
} }
@ -14,8 +17,9 @@ class Tent extends Infrastructure {
public max: (state: GameState) => number = (state) => { public max: (state: GameState) => number = (state) => {
// 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 += (state.resource.compounds?.value ?? 0) max +=
* (state.config.cfgCapacity.compounds?.tents ?? 0); (state.resource.compounds?.value ?? 0) *
(state.config.cfgCapacity.compounds?.tents ?? 0);
return max; return max;
}; };
} }

View File

@ -46,7 +46,8 @@ class DebugRenderer implements IRenderer {
const resource = state.resource[rkey]; const resource = state.resource[rkey];
if (resource === undefined || resource.label === undefined) continue; if (resource === undefined || resource.label === undefined) continue;
const resContainer = document.getElementById( const resContainer = document.getElementById(
`resource-container-${resource.resourceType}`); `resource-container-${resource.resourceType}`
);
if (resContainer === null) continue; if (resContainer === null) continue;
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'resource locked'; el.className = 'resource locked';
@ -70,8 +71,10 @@ class DebugRenderer implements IRenderer {
${this._escape(action.name)}</button>`; ${this._escape(action.name)}</button>`;
} }
} }
if (resource.cost !== undefined if (
&& Object.keys(resource.cost).length !== 0) { resource.cost !== undefined &&
Object.keys(resource.cost).length !== 0
) {
content += "<br>Cost: <span class='resource-cost'></span>"; content += "<br>Cost: <span class='resource-cost'></span>";
} }
el.innerHTML = content; el.innerHTML = content;
@ -93,8 +96,9 @@ class DebugRenderer implements IRenderer {
<button id='dbg-btn-reset'>Reset Game</button> <button id='dbg-btn-reset'>Reset Game</button>
`; `;
resDiv.appendChild(footer); resDiv.appendChild(footer);
document.getElementById('dbg-btn-reset')?.addEventListener('click', document
(): void => { .getElementById('dbg-btn-reset')
?.addEventListener('click', (): void => {
state.reset(); state.reset();
container.innerHTML = ''; container.innerHTML = '';
this._initialized = false; this._initialized = false;
@ -112,7 +116,8 @@ class DebugRenderer implements IRenderer {
? Math.floor(resource.value) ? Math.floor(resource.value)
: resource.value; : resource.value;
elV.innerHTML = formatNumber(value); elV.innerHTML = formatNumber(value);
elT.innerHTML = resource.max !== undefined elT.innerHTML =
resource.max !== undefined
? ` / ${formatNumber(resource.max(state))}` ? ` / ${formatNumber(resource.max(state))}`
: ''; : '';
if (resource.userActions !== undefined) { if (resource.userActions !== undefined) {
@ -128,8 +133,7 @@ class DebugRenderer implements IRenderer {
} }
if (resource.inc !== undefined && resource.inc(state) > 0) { if (resource.inc !== undefined && resource.inc(state) > 0) {
const elI = el.getElementsByClassName('resource-inc')[0]; const elI = el.getElementsByClassName('resource-inc')[0];
elI.innerHTML = elI.innerHTML = ` +${formatNumber(resource.inc(state))}/s`;
` +${formatNumber(resource.inc(state))}/s`;
} }
if (this._handleClick) { if (this._handleClick) {
const elC = el.getElementsByClassName('resource-cost'); const elC = el.getElementsByClassName('resource-cost');
@ -160,8 +164,7 @@ class DebugRenderer implements IRenderer {
'/': '&#x2F;', '/': '&#x2F;',
}; };
const escaper = /[&<>"'/]/g; const escaper = /[&<>"'/]/g;
return text.replace(escaper, (match: string): string => return text.replace(escaper, (match: string): string => escapes[match]);
escapes[match]);
} }
private _getCostStr(resource: IResource, state: GameState): string { private _getCostStr(resource: IResource, state: GameState): string {