diff --git a/.eslintrc.json b/.eslintrc.json
index 3c548b9..201db55 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -9,6 +9,10 @@
"parserOptions": {"project": "tsconfig.json"},
"plugins": ["@typescript-eslint"],
"rules": {
+ "max-len": ["warn", {
+ "ignoreStrings": true,
+ "ignoreTemplateLiterals": true,
+ "ignoreRegExpLiterals": true}],
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/array-type": ["warn", {"default": "array-simple"}],
@@ -29,7 +33,6 @@
"@typescript-eslint/no-base-to-string": "error",
"@typescript-eslint/no-confusing-non-null-assertion": "error",
"@typescript-eslint/no-confusing-void-expression": "error",
- "@typescript-eslint/no-dynamic-delete": "warn",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-implicit-any-catch": "error",
"@typescript-eslint/no-invalid-void-type": "error",
@@ -61,12 +64,6 @@
"allowNullableObject": false}],
"@typescript-eslint/switch-exhaustiveness-check": "warn",
"@typescript-eslint/type-annotation-spacing": "warn",
- "@typescript-eslint/typedef": ["error", {
- "arrowParameter": true,
- "memberVariableDeclaration": true,
- "parameter": true,
- "propertyDeclaration": true,
- "variableDeclaration": true}],
"@typescript-eslint/unified-signatures": "warn",
"@typescript-eslint/brace-style": "warn",
"@typescript-eslint/comma-dangle": ["warn", "always-multiline"],
@@ -74,7 +71,17 @@
"@typescript-eslint/default-param-last": "error",
"@typescript-eslint/dot-notation": "error",
"@typescript-eslint/func-call-spacing": ["warn", "never"],
- "@typescript-eslint/indent": ["warn", 2],
+ "@typescript-eslint/indent": ["warn", 2, {
+ "SwitchCase": 1,
+ "VariableDeclarator": 1,
+ "outerIIFEBody": 1,
+ "FunctionDeclaration": {"parameters": 1, "body": 1},
+ "CallExpression": {"arguments": 1},
+ "ArrayExpression": 1,
+ "ObjectExpression": 1,
+ "MemberExpression": 2,
+ "flatTernaryExpressions": false,
+ "ignoreComments": false}],
"@typescript-eslint/keyword-spacing": "error",
"@typescript-eslint/lines-between-class-members": ["warn", "always", {
"exceptAfterSingleLine": true}],
diff --git a/src/main.ts b/src/main.ts
index fe8055c..757b685 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -2,7 +2,7 @@
///
let globalStartTime = 0;
-let globalTimeout: number = null;
+let globalTimeout: number | null = null;
const cycleLength = 250;
function gameLoop (state: GameState, renderer: IRenderer): void {
@@ -15,8 +15,9 @@ function gameLoop (state: GameState, renderer: IRenderer): void {
// run again in 1sec
globalStartTime = new Date().getTime();
- globalTimeout = setTimeout((): void =>
- gameLoop(state, renderer), cycleLength);
+ globalTimeout = setTimeout((): void => {
+ gameLoop(state, renderer);
+ }, cycleLength);
}
function startGame (state: GameState, renderer: IRenderer): void {
@@ -47,6 +48,7 @@ function startGame (state: GameState, renderer: IRenderer): void {
});
if (document.readyState !== 'loading') startGame(state, renderer);
- else document.addEventListener('DOMContentLoaded', (): void =>
- startGame(state, renderer));
+ else document.addEventListener('DOMContentLoaded', (): void => {
+ startGame(state, renderer);
+ });
})();
diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts
index e5ee99d..f404e82 100644
--- a/src/model/GameConfig.ts
+++ b/src/model/GameConfig.ts
@@ -25,15 +25,20 @@ class GameConfig {
public relOtherShare = 0.02;
public relNoneShare = 0.16;
+ public cfgStartingPlayerMax = 5;
+ public cfgStartingMoneyMax = 500000;
+ public cfgStartingTentMax = 5;
+ public cfgStartingCryptoMax = 1000;
+ public cfgStartingMegaChurchMax = 2;
+
public cfgTitheAmount = 10;
public cfgTimeBetweenTithes = 30000;
public cfgCryptoReturnAmount = 1;
public cfgCredibilityRestoreRate = 0.25;
- public cfgPastorRecruitRate= 0.01;
+ public cfgPastorRecruitRate = 0.01;
public generateState (): GameState {
- const state: GameState = new GameState();
- state.config = this;
+ const state: GameState = new GameState(this);
// create player organization
state.addResource('plorg', new PlayerOrg());
diff --git a/src/model/GameState.ts b/src/model/GameState.ts
index ea76436..40f4e65 100644
--- a/src/model/GameState.ts
+++ b/src/model/GameState.ts
@@ -1,20 +1,25 @@
class GameState {
+ public readonly config: GameConfig;
+
+ public onResourceClick: Array<() => void> = [];
+ public logger: ILogger | null = null;
+ public numberFormatDigits = 1;
+
+ public now = 0;
+
private readonly _versionMaj: number = 0;
private readonly _versionMin: number = 1;
- public config: GameConfig;
-
private _timeSinceSave = 0;
private readonly _timeBetweenSaves: number = 10000;
- private _resources: {[key: string]: IResource} = { };
- private _resourceKeys: string[] = [];
+ private _resources: { [key: string]: IResource } = { };
+ private readonly _resourceKeys: string[] = [];
- public onResourceClick: Array<() => void> = [];
- public logger: ILogger = null;
- public numberFormatDigits = 1;
- public now = 0;
+ constructor (config: GameConfig) {
+ this.config = config;
+ }
public addResource (key: string, resource: IResource): void {
this._resourceKeys.push(key);
@@ -32,32 +37,29 @@ class GameState {
// advance each resource
for (const rkey of this._resourceKeys) {
- if (this._resources[rkey].isUnlocked(this)
- && this._resources[rkey].advanceAction !== null) {
- this._resources[rkey].advanceAction(time, this);
+ const resource = this._resources[rkey];
+ if (this._resources[rkey].isUnlocked(this)) {
+ if (resource.advanceAction !== null)
+ resource.advanceAction(time, this);
}
}
// perform auto increments
for (const rkey of this._resourceKeys) {
- if (!this._resources[rkey].isUnlocked(this)) continue;
+ const resource = this._resources[rkey];
+ if (!resource.isUnlocked(this)) continue;
- const max: number = this._resources[rkey].max
- ? this._resources[rkey].max(this)
- : null;
- const inc: number = this._resources[rkey].inc
- ? this._resources[rkey].inc(this)
- : 0;
- if (inc > 0 && (max === null
- || this._resources[rkey].value < max)) {
- this._resources[rkey].addValue(inc * time / 1000, this);
+ if (resource.inc !== null && (resource.max === null
+ || this._resources[rkey].value < resource.max(this))) {
+ this._resources[rkey].addValue(resource.inc(this) * time / 1000, this);
}
- const val: number = this._resources[rkey].value;
- if (max !== null && val > max) {
- this._resources[rkey].addValue((val - max) * -1, this);
+
+ if (resource.max !== null && resource.value > resource.max(this)) {
+ this._resources[rkey].addValue(
+ (resource.value - resource.max(this)) * -1, this);
}
- if (val < 0) {
- this._resources[rkey].addValue(val * -1, this);
+ if (resource.value < 0) {
+ this._resources[rkey].addValue(resource.value * -1, this);
}
}
}
@@ -71,18 +73,19 @@ class GameState {
}
public performClick (resourceKey: string): void {
- if (!this._resources[resourceKey].isUnlocked(this)) return;
+ const resource = this._resources[resourceKey];
+ if (!resource.isUnlocked(this)) return;
- if (this._resources[resourceKey].clickAction !== null) {
- this._resources[resourceKey].clickAction(this);
+ if (resource.clickAction !== null) {
+ resource.clickAction(this);
for (const callback of this.onResourceClick) {
callback();
}
}
}
- public deductCost (cost: { [rkey: string]: number }): boolean {
- if (cost === null || Object.keys(cost) === null) return true;
+ public deductCost (cost: { [rkey: string]: number } | null): boolean {
+ if (cost === null) return true;
if (!this.isPurchasable(cost)) return false;
for (const rkey of Object.keys(cost)) {
this._resources[rkey].addValue(cost[rkey] * -1, this);
@@ -90,8 +93,8 @@ class GameState {
return true;
}
- public isPurchasable (cost: { [rkey: string]: number }): boolean {
- if (cost === null || Object.keys(cost) === null) return true;
+ public isPurchasable (cost: { [rkey: string]: number } | null): boolean {
+ if (cost === null) return true;
for (const rkey of Object.keys(cost)) {
if (this._resources[rkey].value < cost[rkey]) {
return false;
@@ -101,24 +104,24 @@ class GameState {
}
public formatNumber (num: number): string {
- type vlookup = { value: number, symbol: string };
- const lookup: vlookup[] = [
+ type UnitLookup = { value: number, symbol: string };
+ const lookup: UnitLookup[] = [
{ value: 1, symbol: '' },
{ value: 1e3, symbol: 'K' },
{ value: 1e6, symbol: 'M' },
{ value: 1e9, symbol: 'G' },
{ value: 1e12, symbol: 'T' },
{ value: 1e15, symbol: 'P' },
- { value: 1e18, symbol: 'E' }
+ { value: 1e18, symbol: 'E' },
];
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
- let item: vlookup;
+ let item: UnitLookup | undefined;
for (item of lookup.slice().reverse()) {
if (num >= item.value) break;
}
- return item
- ? (num / item.value).toFixed(this.numberFormatDigits)
- .replace(rx, '$1') + item.symbol
+ return item !== undefined
+ ? (num / item.value).toFixed(
+ this.numberFormatDigits).replace(rx, '$1') + item.symbol
: num.toFixed(this.numberFormatDigits).replace(rx, '$1');
}
@@ -129,15 +132,15 @@ class GameState {
}
public save (): void {
- const saveObj: any = { };
+ const saveObj: SaveData = {};
saveObj.version = {
maj: this._versionMaj,
- min: this._versionMin
+ min: this._versionMin,
};
for (const rkey of this._resourceKeys) {
saveObj[rkey] = {
value: this._resources[rkey].value,
- cost: this._resources[rkey].cost
+ cost: this._resources[rkey].cost,
};
}
const saveStr: string = btoa(JSON.stringify(saveObj));
@@ -145,33 +148,32 @@ class GameState {
}
public load (): void {
- const saveStr: string = localStorage.getItem('savegame');
+ const saveStr: string | null = localStorage.getItem('savegame');
if (saveStr !== null) {
try {
- const saveObj: { [key: string]: any } =
- JSON.parse(atob(saveStr));
- if (this._versionMaj === saveObj.version.maj) {
+ const saveObj: SaveData = JSON.parse(atob(saveStr));
+ if (this._versionMaj === saveObj.version?.maj) {
for (const rkey of this._resourceKeys) {
- if (saveObj[rkey] !== undefined
- && saveObj[rkey].value !== undefined
- && saveObj[rkey].cost !== undefined) {
- // @ts-ignore
- this._resources[rkey].value = saveObj[rkey].value;
- // @ts-ignore
- this._resources[rkey].cost = saveObj[rkey].cost;
+ const saveRes = <{
+ value: number;
+ cost: { [key: string]: number } | null;
+ } | undefined> saveObj[rkey];
+ if (saveRes !== undefined) {
+ // @ts-expect-error writing read-only value from save data
+ this._resources[rkey].value = saveRes.value;
+ // @ts-expect-error writing read-only cost from save data
+ this._resources[rkey].cost = saveRes.cost ?? null;
}
}
} else {
// tslint:disable-next-line
console.log('The saved game is too old to load.');
}
- } catch (e) {
- // tslint:disable-next-line
+ } catch (e: unknown) {
console.log('There was an error loading the saved game.');
- console.log(e); // tslint:disable-line
+ console.log(e);
}
} else {
- // tslint:disable-next-line
console.log('No save game was found.');
}
}
@@ -183,3 +185,11 @@ class GameState {
this.log('Reset all game resources.');
}
}
+
+type SaveData = {
+ [key: string]: {
+ value: number;
+ cost: { [key: string]: number } | null;
+ } | { maj: number, min: number } | undefined;
+ version?: { maj: number, min: number };
+};
diff --git a/src/model/logging/DebugLogger.ts b/src/model/logging/DebugLogger.ts
index 9eab0e5..d71f52b 100644
--- a/src/model/logging/DebugLogger.ts
+++ b/src/model/logging/DebugLogger.ts
@@ -1,5 +1,5 @@
class DebugLogger implements ILogger {
- private _container: HTMLElement;
+ private readonly _container: HTMLElement;
constructor (container: HTMLElement) {
this._container = container;
@@ -9,7 +9,9 @@ class DebugLogger implements ILogger {
const p: HTMLElement = document.createElement('p');
p.innerText = text;
this._container.appendChild(p);
- this._container.parentElement.scrollTop =
- this._container.parentElement.scrollHeight;
+ if (this._container.parentElement !== null) {
+ this._container.parentElement.scrollTop =
+ this._container.parentElement.scrollHeight;
+ }
}
}
diff --git a/src/model/logging/ILogger.ts b/src/model/logging/ILogger.ts
index 2ea8fd6..2dc7c31 100644
--- a/src/model/logging/ILogger.ts
+++ b/src/model/logging/ILogger.ts
@@ -1,3 +1,3 @@
interface ILogger {
- msg (text: string): void;
+ msg: (text: string) => void;
}
diff --git a/src/model/resource/Church.ts b/src/model/resource/Church.ts
index 9ebcfd9..48dcddc 100644
--- a/src/model/resource/Church.ts
+++ b/src/model/resource/Church.ts
@@ -8,10 +8,8 @@ class Church extends Infrastructure {
this._costMultiplier.money = 1.01;
}
- public max (state: GameState): number {
- // one church per compound
- return state.getResource('cmpnd').value;
- }
+ public max: (state: GameState) => number = (state) =>
+ state.getResource('cmpnd').value;
public isUnlocked (state: GameState): boolean {
if (this._isUnlocked) return true;
diff --git a/src/model/resource/Credibility.ts b/src/model/resource/Credibility.ts
index 49f2b84..d928740 100644
--- a/src/model/resource/Credibility.ts
+++ b/src/model/resource/Credibility.ts
@@ -5,15 +5,10 @@ class Credibility extends Passive {
super(
'Credibility',
'Affects your ability to recruit and retain followers.');
- this._baseMax = 100;
this.value = 100;
}
- public max (): number {
- return 100;
- }
-
- public inc (state: GameState): number {
- return state.config.cfgCredibilityRestoreRate;
- }
+ public max: (state: GameState) => number = (_state) => 100;
+ public inc: (state: GameState) => number = (state) =>
+ state.config.cfgCredibilityRestoreRate;
}
diff --git a/src/model/resource/CryptoCurrency.ts b/src/model/resource/CryptoCurrency.ts
index ab475b0..10efdba 100644
--- a/src/model/resource/CryptoCurrency.ts
+++ b/src/model/resource/CryptoCurrency.ts
@@ -6,7 +6,9 @@ class CryptoCurrency extends Purchasable {
"A crypto coin that can't be spent directly, but provides a steady stream of passive income.");
this.cost.money = 100;
this._costMultiplier.money = 1.1;
- this._baseMax = 1000;
this.valueInWholeNumbers = false;
}
+
+ public max: (state: GameState) => number = (state) =>
+ state.config.cfgStartingCryptoMax;
}
diff --git a/src/model/resource/House.ts b/src/model/resource/House.ts
index eb65f09..8e92233 100644
--- a/src/model/resource/House.ts
+++ b/src/model/resource/House.ts
@@ -8,10 +8,9 @@ class House extends Infrastructure {
this._costMultiplier.money = 1.01;
}
- public max (state: GameState): number {
- // two houses per compound
- return state.getResource('cmpnd').value * 2;
- }
+ // two houses per compound
+ public max: (state: GameState) => number = (state) =>
+ state.getResource('cmpnd').value * 2;
public isUnlocked (state: GameState): boolean {
if (this._isUnlocked) return true;
diff --git a/src/model/resource/IResource.ts b/src/model/resource/IResource.ts
index 75c4af6..4ee0815 100644
--- a/src/model/resource/IResource.ts
+++ b/src/model/resource/IResource.ts
@@ -1,29 +1,29 @@
enum ResourceType {
- Religion = 'religion',
- Job = 'job',
- Consumable = 'consumable',
- Infrastructure = 'infrastructure',
- Research = 'research',
- Passive = 'passive'
+ religion = 'religion',
+ job = 'job',
+ consumable = 'consumable',
+ infrastructure = 'infrastructure',
+ research = 'research',
+ passive = 'passive',
}
interface IResource {
readonly resourceType: ResourceType;
- readonly name: string | null;
- readonly description: string | null;
+ readonly name: string;
+ readonly description: string;
readonly valueInWholeNumbers: boolean;
- readonly clickText: string;
- readonly clickDescription: string;
+ readonly clickText: string | null;
+ readonly clickDescription: string | null;
// readonly altClickText?: string;
// readonly altClickDescription?: string;
readonly value: number;
- readonly cost: { [key: string]: number };
+ readonly cost: { [key: string]: number } | null;
- max (state: GameState): number | null;
- inc (state: GameState): number | null;
- clickAction(state: GameState): void;
+ max: ((state: GameState) => number) | null;
+ inc: ((state: GameState) => number) | null;
+ clickAction: ((state: GameState) => void) | null;
// altClickAction (state: GameState): void;
- addValue (amount: number, state: GameState): void;
- isUnlocked (state: GameState): boolean;
- advanceAction (time: number, state: GameState): void;
+ addValue: (amount: number, state: GameState) => void;
+ isUnlocked: (state: GameState) => boolean;
+ advanceAction: ((time: number, state: GameState) => void) | null;
}
diff --git a/src/model/resource/Infrastructure.ts b/src/model/resource/Infrastructure.ts
index 9daec6a..7fb19ff 100644
--- a/src/model/resource/Infrastructure.ts
+++ b/src/model/resource/Infrastructure.ts
@@ -1,5 +1,5 @@
///
abstract class Infrastructure extends Purchasable {
- public readonly resourceType: ResourceType = ResourceType.Infrastructure;
+ public readonly resourceType: ResourceType = ResourceType.infrastructure;
}
diff --git a/src/model/resource/Job.ts b/src/model/resource/Job.ts
index c71d948..2144060 100644
--- a/src/model/resource/Job.ts
+++ b/src/model/resource/Job.ts
@@ -1,14 +1,16 @@
///
abstract class Job implements IResource {
- public readonly resourceType: ResourceType = ResourceType.Job;
+ public readonly resourceType: ResourceType = ResourceType.job;
public readonly valueInWholeNumbers: boolean = true;
public readonly clickText: string = 'Hire';
- public readonly clickDescription: string =
- 'Promote one of your followers.';
+ public readonly clickDescription: string = 'Promote one of your followers.';
public value = 0;
public readonly cost: { [key: string]: number } = { };
+ public max: ((state: GameState) => number) | null = null;
+ public inc: ((state: GameState) => number) | null = null;
+
protected _costMultiplier: { [key: string]: number } = { };
protected _isUnlocked = false;
@@ -17,20 +19,14 @@ abstract class Job implements IResource {
public readonly description: string
) { }
- public max (state: GameState): number | null {
- return null;
- }
-
- public inc (): number | null {
- return null;
- }
public clickAction (state: GameState): void {
if (this._availableJobs(state) <= 0) {
state.log('You have no unemployed followers to promote.');
return;
}
- if (this.value < this.max(state) && state.deductCost(this.cost)) {
+ if (this.max !== null && this.value < this.max(state)
+ && state.deductCost(this.cost)) {
this.addValue(1);
state.log(this._hireLog(1, state));
for (const rkey of Object.keys(this._costMultiplier)) {
@@ -43,21 +39,21 @@ abstract class Job implements IResource {
this.value += amount;
}
- public isUnlocked (state: GameState): boolean {
+ public isUnlocked (_state: GameState): boolean {
return this._isUnlocked;
}
- public advanceAction (time: number, state: GameState): void {
+ public advanceAction (_time: number, _state: GameState): void {
return;
}
protected _availableJobs (state: GameState): number {
// number of followers minus the number of filled jobs
const followers: number = state.getResource('plorg').value;
- const hired: number = state.getResources()
- .reduce((tot: number, rkey: string): number => {
+ const hired: number = state.getResources().reduce(
+ (tot: number, rkey: string): number => {
const res: IResource = state.getResource(rkey);
- return res.resourceType === ResourceType.Job
+ return res.resourceType === ResourceType.job
? tot + res.value
: tot;
}, 0);
@@ -66,7 +62,7 @@ abstract class Job implements IResource {
return max;
}
- protected _hireLog (amount: number, state: GameState): string {
+ protected _hireLog (amount: number, _state: GameState): string {
return `You hired ${amount} x ${this.name}.`;
}
}
diff --git a/src/model/resource/MegaChurch.ts b/src/model/resource/MegaChurch.ts
index 0a18437..498e819 100644
--- a/src/model/resource/MegaChurch.ts
+++ b/src/model/resource/MegaChurch.ts
@@ -6,9 +6,11 @@ class MegaChurch extends Infrastructure {
'Room for 5 pastors');
this.cost.money = 7500000;
this._costMultiplier.money = 1.01;
- this._baseMax = 2;
}
+ public max: (state: GameState) => number = (state) =>
+ state.config.cfgStartingMegaChurchMax;
+
public isUnlocked (state: GameState): boolean {
if (this._isUnlocked) return true;
const permit: IResource = state.getResource('blpmt');
diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts
index 0a29a95..71b4840 100644
--- a/src/model/resource/Money.ts
+++ b/src/model/resource/Money.ts
@@ -1,7 +1,7 @@
///
class Money extends Purchasable {
- public readonly resourceType: ResourceType = ResourceType.Consumable;
+ public readonly resourceType: ResourceType = ResourceType.consumable;
private _lastCollectionTime = 0;
@@ -11,18 +11,17 @@ class Money extends Purchasable {
super('Money', 'Used to purchase goods and services.');
this.clickText = 'Collect Tithes';
this.clickDescription = 'Voluntary contributions from followers.';
- this._baseMax = 500000;
this.valueInWholeNumbers = false;
this._isUnlocked = true;
}
- public max (state: GameState): number | null {
- let max: number = this._baseMax;
+ public max: (state: GameState) => number = (state: GameState) => {
+ let max: number = state.config.cfgStartingMoneyMax;
max += state.getResource('cmpnd').value * 500000;
return max;
- }
+ };
- public inc (state: GameState): number {
+ public inc: (state: GameState) => number = (state) => {
let inc = 0;
// crypto currency
@@ -30,7 +29,7 @@ class Money extends Purchasable {
* state.config.cfgCryptoReturnAmount;
return inc;
- }
+ };
protected _purchaseAmount (state: GameState): number {
const plorg: IResource = state.getResource('plorg');
diff --git a/src/model/resource/Passive.ts b/src/model/resource/Passive.ts
index fbecc79..0f6c376 100644
--- a/src/model/resource/Passive.ts
+++ b/src/model/resource/Passive.ts
@@ -1,7 +1,7 @@
///
abstract class Passive implements IResource {
- public readonly resourceType: ResourceType = ResourceType.Passive;
+ public readonly resourceType: ResourceType = ResourceType.passive;
public readonly valueInWholeNumbers: boolean = false;
public readonly clickText: null = null;
public readonly clickDescription: null = null;
@@ -10,31 +10,22 @@ abstract class Passive implements IResource {
public readonly clickAction: null = null;
- protected _baseMax: number | null;
- protected _baseInc: number | null;
+ public max: ((state: GameState) => number) | null = null;
+ public inc: ((state: GameState) => number) | null = null;
+ public advanceAction: ((time: number, state: GameState) => void) | null = null;
constructor (
public readonly name: string,
public readonly description: string
) { }
- public max (state: GameState): number | null {
- return this._baseMax;
- }
- public inc (state: GameState): number | null {
- return this._baseInc;
- }
-
- public addValue (amount: number, state: GameState): void {
+ public addValue (amount: number, _state: GameState): void {
this.value += amount;
}
- public isUnlocked (state: GameState): boolean {
+ public isUnlocked (_state: GameState): boolean {
return true;
}
- public advanceAction (time: number, state: GameState): void {
- return;
- }
}
diff --git a/src/model/resource/Pastor.ts b/src/model/resource/Pastor.ts
index 88bd2ec..8a56e80 100644
--- a/src/model/resource/Pastor.ts
+++ b/src/model/resource/Pastor.ts
@@ -8,11 +8,11 @@ class Pastor extends Job {
'Collect tithings for you and recruit new members from other faiths automatically.');
}
- public max (state: GameState): number {
+ public max: (state: GameState) => number = (state) => {
let max: number = state.getResource('chrch').value * 2;
max += state.getResource('mchch').value * 5;
return max;
- }
+ };
public isUnlocked (state: GameState): boolean {
if (this._isUnlocked) return true;
@@ -30,7 +30,7 @@ class Pastor extends Job {
if (Math.floor(plorg.value) < tithed)
tithed = Math.floor(plorg.value);
let collected: number = tithed * state.config.cfgTitheAmount;
- if (collected > money.max(state) - money.value)
+ if (money.max !== null && collected > money.max(state) - money.value)
collected = money.max(state) - money.value;
if (collected > 0) {
money.addValue(collected, state);
diff --git a/src/model/resource/PlayerOrg.ts b/src/model/resource/PlayerOrg.ts
index 5d7e214..ed4f3a3 100644
--- a/src/model/resource/PlayerOrg.ts
+++ b/src/model/resource/PlayerOrg.ts
@@ -1,7 +1,7 @@
///
class PlayerOrg implements IResource {
- public readonly resourceType: ResourceType = ResourceType.Religion;
+ public readonly resourceType: ResourceType = ResourceType.religion;
public readonly name: string = 'Player';
public readonly description: string = 'In you they trust.';
public readonly valueInWholeNumbers: boolean = true;
@@ -11,13 +11,12 @@ class PlayerOrg implements IResource {
public readonly cost: null = null;
private _timeSinceLastLost = 0;
- private _baseMax = 5;
private _lastRecruitmentLog = 0;
private _followerSources: { [key: string]: number } = { };
private _followerDests: { [key: string]: number } = { };
public max (state: GameState): number {
- let max: number = this._baseMax;
+ let max: number = state.config.cfgStartingPlayerMax;
max += state.getResource('tents').value * 2;
max += state.getResource('house').value * 10;
return max;
@@ -27,12 +26,12 @@ class PlayerOrg implements IResource {
let inc = 0;
// pastor recruiting
- const pastors: number = state.getResource('pstor').value;
+ const pastors = state.getResource('pstor').value;
inc += pastors * state.config.cfgPastorRecruitRate;
// credibility adjustment
- const creds: IResource = state.getResource('creds');
- inc *= creds.value / creds.max(state);
+ const creds = state.getResource('creds');
+ if (creds.max !== null) inc *= creds.value / creds.max(state);
return inc;
}
@@ -46,10 +45,12 @@ class PlayerOrg implements IResource {
// chance to fail increases as credibility decreases
const creds: IResource = state.getResource('creds');
- const ratio: number = Math.ceil(creds.value) / creds.max(state);
- if (Math.random() > ratio) {
- state.log('Your recruitment efforts failed.');
- return;
+ if (creds.max !== null) {
+ const ratio: number = Math.ceil(creds.value) / creds.max(state);
+ if (Math.random() > ratio) {
+ state.log('Your recruitment efforts failed.');
+ return;
+ }
}
this._lastRecruitmentLog = 0; // always log on click
@@ -57,17 +58,17 @@ class PlayerOrg implements IResource {
}
public addValue (amount: number, state: GameState): void {
- const oldValue: number = this.value;
+ const oldValue = this.value;
this.value += amount;
- const diff: number = Math.floor(this.value) - Math.floor(oldValue);
+ const diff = Math.floor(this.value) - Math.floor(oldValue);
if (diff > 0) {
// gained followers must come from other faiths
for (let i = 0; i < diff; i++) {
- const source: [string, IResource] = this._getRandomReligion(state);
+ const source = this._getRandomReligion(state);
source[1].addValue(-1, state);
- const curFollowers: number = this._followerSources[source[0]];
- this._followerSources[source[0]] = curFollowers
+ const curFollowers = this._followerSources[source[0]];
+ this._followerSources[source[0]] = !isNaN(curFollowers)
? curFollowers + 1
: 1;
}
@@ -77,14 +78,14 @@ class PlayerOrg implements IResource {
const dest: [string, IResource] = this._getRandomReligion(state);
dest[1].addValue(1, state);
const curFollowers: number = this._followerDests[dest[0]];
- this._followerDests[dest[0]] = curFollowers
+ this._followerDests[dest[0]] = !isNaN(curFollowers)
? curFollowers + 1
: 1;
}
}
}
- public isUnlocked (state: GameState): boolean {
+ public isUnlocked (_state: GameState): boolean {
return true;
}
@@ -93,11 +94,13 @@ class PlayerOrg implements IResource {
this._timeSinceLastLost += time;
if (this._timeSinceLastLost > 10000) {
if (this.value > 0) {
- const creds: IResource = state.getResource('creds');
- const ratio: number = Math.ceil(creds.value) / creds.max(state);
- if (Math.random() > ratio) {
- const lost: number = Math.ceil(this.value / 25 * (1 - ratio));
- this.addValue(lost * -1, state);
+ const creds = state.getResource('creds');
+ if (creds.max !== null) {
+ const ratio: number = Math.ceil(creds.value) / creds.max(state);
+ if (Math.random() > ratio) {
+ const lost: number = Math.ceil(this.value / 25 * (1 - ratio));
+ this.addValue(lost * -1, state);
+ }
}
}
this._timeSinceLastLost = 0;
@@ -113,8 +116,7 @@ class PlayerOrg implements IResource {
for (const rkey of Object.keys(this._followerDests)) {
if (msg !== '') msg += ', ';
const religion: IResource = state.getResource(rkey);
- msg +=
- `${state.formatNumber(this._followerDests[rkey])} to ${religion.name}`;
+ msg += `${state.formatNumber(this._followerDests[rkey])} to ${religion.name}`;
total += this._followerDests[rkey];
delete this._followerDests[rkey];
}
diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts
index 7ea0f33..e943638 100644
--- a/src/model/resource/Purchasable.ts
+++ b/src/model/resource/Purchasable.ts
@@ -1,15 +1,17 @@
///
abstract class Purchasable implements IResource {
- public readonly resourceType: ResourceType = ResourceType.Consumable;
+ public readonly resourceType: ResourceType = ResourceType.consumable;
public valueInWholeNumbers = true;
public clickText = 'Purchase';
public clickDescription = 'Purchase';
public value = 0;
public readonly cost: { [key: string]: number } = { };
+ public inc: ((state: GameState) => number) | null = null;
+ public max: ((_state: GameState) => number) | null = null;
+
protected _costMultiplier: { [key: string]: number } = { };
- protected _baseMax: number | null = null;
protected _isUnlocked = false;
constructor (
@@ -17,16 +19,9 @@ abstract class Purchasable implements IResource {
public readonly description: string
) { }
- public max (state: GameState): number | null {
- return this._baseMax;
- }
-
- public inc (state: GameState): number | null {
- return null;
- }
public clickAction (state: GameState): void {
- if (this.max(state) !== null && this.value >= this.max(state)) return;
+ if (this.max !== null && this.value >= this.max(state)) return;
if (state.deductCost(this.cost)) {
const amount: number = this._purchaseAmount(state);
if (amount > 0) {
@@ -39,7 +34,7 @@ abstract class Purchasable implements IResource {
}
}
- public addValue (amount: number, state: GameState): void {
+ public addValue (amount: number, _state: GameState): void {
this.value += amount;
}
diff --git a/src/model/resource/Religion.ts b/src/model/resource/Religion.ts
index b958177..c513955 100644
--- a/src/model/resource/Religion.ts
+++ b/src/model/resource/Religion.ts
@@ -1,7 +1,7 @@
///
class Religion implements IResource {
- public readonly resourceType: ResourceType = ResourceType.Religion;
+ public readonly resourceType: ResourceType = ResourceType.religion;
public readonly valueInWholeNumbers: boolean = true;
public readonly clickText: null = null;
public readonly clickDescription: null = null;
@@ -18,11 +18,11 @@ class Religion implements IResource {
public value: number,
) { }
- public addValue (amount: number, state: GameState): void {
+ public addValue (amount: number, _state: GameState): void {
this.value += amount;
}
- public isUnlocked (state: GameState): boolean {
+ public isUnlocked (_state: GameState): boolean {
return true;
}
}
diff --git a/src/model/resource/Research.ts b/src/model/resource/Research.ts
index a822a59..84c86a4 100644
--- a/src/model/resource/Research.ts
+++ b/src/model/resource/Research.ts
@@ -1,7 +1,7 @@
///
abstract class Research extends Purchasable {
- public readonly resourceType: ResourceType = ResourceType.Research;
+ public readonly resourceType: ResourceType = ResourceType.research;
constructor (
public readonly name: string,
@@ -9,8 +9,9 @@ abstract class Research extends Purchasable {
) {
super(name, description);
this.value = 0;
- this._baseMax = 1;
this.clickText = 'Learn';
- this.clickDescription = 'Complete this research.'
+ this.clickDescription = 'Complete this research.';
}
+
+ public max: (_state: GameState) => number = (_state) => 1;
}
diff --git a/src/model/resource/Tent.ts b/src/model/resource/Tent.ts
index 1c970fd..5602c69 100644
--- a/src/model/resource/Tent.ts
+++ b/src/model/resource/Tent.ts
@@ -6,13 +6,12 @@ class Tent extends Infrastructure {
'Provides room to house 2 followers.');
this.cost.money = 250;
this._costMultiplier.money = 1.05;
- this._baseMax = 5;
}
- public max (state: GameState): number {
+ public max: (state: GameState) => number = (state) => {
// ten extra tents per compound
- let max: number = this._baseMax;
+ let max: number = state.config.cfgStartingTentMax;
max += state.getResource('cmpnd').value * 10;
return max;
- }
+ };
}
diff --git a/src/render/DebugRenderer.ts b/src/render/DebugRenderer.ts
index aa71b26..51ed772 100644
--- a/src/render/DebugRenderer.ts
+++ b/src/render/DebugRenderer.ts
@@ -1,38 +1,41 @@
///
-class DebugRenderer implements IRenderer { // eslint-disable-line @typescript-eslint/no-unused-vars
+class DebugRenderer implements IRenderer {
private _initialized = false;
private _handleClick = true;
public render (state: GameState): void {
const rkeys: string[] = state.getResources();
+ const container = document.getElementById('irreligious-game');
if (!this._initialized) {
- const container: HTMLElement =
- document.getElementById('irreligious-game');
+ if (container === null) {
+ console.error('could not find game container');
+ return;
+ }
this._initialized = true;
state.onResourceClick.push((): void => {
this._handleClick = true;
});
- const style: HTMLElement = document.createElement('link');
+ const style = document.createElement('link');
style.setAttribute('rel', 'stylesheet');
style.setAttribute('href', 'css/debugger.css');
- const head: HTMLElement = document.getElementsByTagName('head')[0];
+ const head = document.getElementsByTagName('head')[0];
head.appendChild(style);
// create resource area and logging area
- const resDiv: HTMLElement = document.createElement('div');
+ const resDiv = document.createElement('div');
resDiv.id = 'resource-section';
container.appendChild(resDiv);
- const logDiv: HTMLElement = document.createElement('div');
+ const logDiv = document.createElement('div');
logDiv.id = 'logging-section';
container.appendChild(logDiv);
- const logContent: HTMLElement = document.createElement('div');
+ const logContent = document.createElement('div');
logDiv.appendChild(logContent);
state.logger = new DebugLogger(logContent);
// create containers for each resource type
for (const item in ResourceType) {
if (isNaN(Number(item))) {
- const el: HTMLElement = document.createElement('div');
- el.id = `resource-container-${ResourceType[item]}`;
+ const el = document.createElement('div');
+ el.id = `resource-container-${item.toString()}`;
el.className = 'resource-type-container';
resDiv.appendChild(el);
}
@@ -40,23 +43,21 @@ class DebugRenderer implements IRenderer { // eslint-disable-line @typescript-es
// create containers for each resource
for (const rkey of rkeys) {
const resource: IResource = state.getResource(rkey);
- const resContainer: HTMLElement =
- document.getElementById(
- `resource-container-${resource.resourceType}`);
- const el: HTMLElement = document.createElement('div');
+ const resContainer = document.getElementById(
+ `resource-container-${resource.resourceType}`);
+ if (resContainer === null) continue;
+ const el = document.createElement('div');
el.className = 'resource locked';
el.id = `resource-details-${rkey}`;
let content = `
- ${this._escape(resource.name
- ? resource.name
- : rkey)}
+ ${this._escape(resource.name)}
`;
- if (resource.clickText !== null) {
+ if (resource.clickText !== null && resource.clickDescription !== null) {
content += `