diff --git a/src/main.ts b/src/main.ts
index 2f747aa..990aa85 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -26,12 +26,12 @@ function gameLoop (state: GameState, renderer: IRenderer): void {
const state = config.generateState();
// re-run main loop immediately on user clicks
- state.onResourceClick = () => {
+ state.onResourceClick.push(() => {
if (globalTimeout !== null) {
clearTimeout(globalTimeout);
gameLoop(state, renderer);
}
- }
+ });
if (document.readyState !== 'loading') gameLoop(state, renderer);
else document.addEventListener('DOMContentLoaded', () => gameLoop(state, renderer));
diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts
index 75e20ef..25791c9 100644
--- a/src/model/GameConfig.ts
+++ b/src/model/GameConfig.ts
@@ -1,7 +1,7 @@
///
///
-///
///
+///
class GameConfig {
public worldPopulation: number = 790000000;
@@ -20,7 +20,8 @@ class GameConfig {
const state = new GameState();
// create player organization
- state.addResource('plorg', new PlayerOrganization());
+ state.addResource('plorg', new Religion(
+ 'Player', 'In you they trust.', 0));
// create world religions
state.addResource('xtian', new Religion(
@@ -55,8 +56,9 @@ class GameConfig {
'Non-Religious', 'Atheists and agnostics.',
this.relNoneShare * this.worldPopulation));
- // add crafting resources
+ // add purchasable resources
state.addResource('money', new Money(0, 1000));
+ state.addResource('bonds', new SavingsBonds(0));
return state;
}
diff --git a/src/model/GameState.ts b/src/model/GameState.ts
index af3ec18..daccdb6 100644
--- a/src/model/GameState.ts
+++ b/src/model/GameState.ts
@@ -4,7 +4,7 @@ class GameState {
private _resources: Record = { };
private _resourceKeys: string[] = [];
- public onResourceClick: () => void = null;
+ public onResourceClick: (() => void)[] = [];
public addResource (key: string, resource: IResource): void {
this._resourceKeys.push(key);
@@ -30,22 +30,28 @@ class GameState {
public performClick (resourceKey: string): void {
if (this._resources[resourceKey].clickAction !== null) {
this._resources[resourceKey].clickAction(this);
- if (this.onResourceClick !== null) {
- this.onResourceClick();
+ for (const callback of this.onResourceClick) {
+ callback();
}
}
}
public deductCost (cost: { [rkey: string]: number }): boolean {
if (cost === null || Object.keys(cost) === null) return true;
- for (const rkey of Object.keys(cost)) {
- if (this._resources[rkey].value < cost[rkey]) {
- return false;
- }
- }
+ if (!this.isPurchasable(cost)) return false;
for (const rkey of Object.keys(cost)) {
this._resources[rkey].value -= cost[rkey];
}
return true;
}
+
+ public isPurchasable (cost: { [rkey: string]: number }): boolean {
+ if (cost === null || Object.keys(cost) === null) return true;
+ for (const rkey of Object.keys(cost)) {
+ if (this._resources[rkey].value < cost[rkey]) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/model/resource/IResource.ts b/src/model/resource/IResource.ts
index 2eb8937..7ded881 100644
--- a/src/model/resource/IResource.ts
+++ b/src/model/resource/IResource.ts
@@ -11,7 +11,9 @@ interface IResource {
resourceType: ResourceType;
value: number;
max?: number;
- unlocked: boolean;
+ cost: { [key: string]: number };
+
+ isUnlocked: (state: GameState) => boolean;
clickText: string;
clickDescription: string;
diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts
index 9545106..043c244 100644
--- a/src/model/resource/Money.ts
+++ b/src/model/resource/Money.ts
@@ -5,9 +5,12 @@ class Money extends Purchasable {
public value: number,
public max: number
) {
- super('Money', 'Used to purchase goods and services.', null);
+ super('Money', 'Used to purchase goods and services.');
this.clickText = 'Beg';
this.clickDescription = 'Alms for the poor.';
- this.unlocked = true;
+ }
+
+ public isUnlocked (state: GameState): boolean {
+ return true;
}
}
diff --git a/src/model/resource/PlayerOrganization.ts b/src/model/resource/PlayerOrganization.ts
deleted file mode 100644
index 8f6c628..0000000
--- a/src/model/resource/PlayerOrganization.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-///
-
-class PlayerOrganization implements IResource {
- public readonly name = 'Player';
- public readonly description = 'In you they trust.';
- public readonly resourceType = ResourceType.Religion;
- public readonly max?: number = null;
- public readonly unlocked = true;
-
- public readonly clickText: string = null;
- public readonly clickDescription: string = null;
- public readonly clickAction: () => void = null;
-
- public readonly advanceAction: (time: number) => void = null;
-
- public value = 0;
-
-}
diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts
index 8e1d1b8..e4bd662 100644
--- a/src/model/resource/Purchasable.ts
+++ b/src/model/resource/Purchasable.ts
@@ -4,25 +4,36 @@ abstract class Purchasable implements IResource {
public readonly resourceType = ResourceType.Infrastructure;
public readonly max?: number = null;
public value: number = 0;
- public unlocked: boolean = false;
- public clickText: string = "Purchase";
- public clickDescription: string = null;
+ public clickText: string = 'Purchase';
+ public clickDescription: string = 'Purchase';
+
+ public cost: { [key: string]: number } = null;
+ protected _costMultiplier: { [key: string]: number } = null;
constructor (
public readonly name: string,
- public readonly description: string,
- private _cost: { [key: string]: number }
+ public readonly description: string
) { }
public clickAction (state: GameState) {
if (this.max !== null && this.value >= this.max) return;
- if (state.deductCost(this._cost)) {
+ if (state.deductCost(this.cost)) {
this.value += 1;
+ if (this._costMultiplier !== null
+ && Object.keys(this._costMultiplier !== null)) {
+ for (const rkey of Object.keys(this._costMultiplier)) {
+ this.cost[rkey] *= this._costMultiplier[rkey];
+ }
+ }
}
}
- public advanceAction (time: number, state: GameState) {
- // do nothing
+ public advanceAction (time: number, state: GameState): void {
+ return;
+ }
+
+ public isUnlocked (state: GameState): boolean {
+ return false;
}
}
diff --git a/src/model/resource/Religion.ts b/src/model/resource/Religion.ts
index 6635cc0..8e4af0e 100644
--- a/src/model/resource/Religion.ts
+++ b/src/model/resource/Religion.ts
@@ -3,16 +3,19 @@
class Religion implements IResource {
public readonly resourceType = ResourceType.Religion;
public readonly max?: number = null;
- public readonly unlocked = true;
public readonly clickText: string = null;
public readonly clickDescription: string = null;
public readonly clickAction: () => void = null;
public readonly advanceAction: (time: number) => void = null;
+ public readonly cost: { [key: string]: number } = null;
constructor (
public readonly name: string,
public readonly description: string,
public value: number,
- ) {
+ ) { }
+
+ public isUnlocked (state: GameState): boolean {
+ return true;
}
}
diff --git a/src/model/resource/SavingsBonds.ts b/src/model/resource/SavingsBonds.ts
new file mode 100644
index 0000000..bff43fd
--- /dev/null
+++ b/src/model/resource/SavingsBonds.ts
@@ -0,0 +1,23 @@
+///
+
+class SavingsBonds extends Purchasable {
+ public max?: number = null;
+ private _isUnlocked = false;
+
+ constructor (
+ public value: number,
+ ) {
+ super('Savings Bonds', 'Grows money by a small amount over time.');
+ this.cost = { 'money': 25 };
+ this._costMultiplier = { 'money': 1.1 };
+ }
+
+ public isUnlocked (state: GameState): boolean {
+ if (this._isUnlocked) return true;
+ if (state.getResource('money').value >= 25) {
+ this._isUnlocked = true;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/render/DebugRenderer.ts b/src/render/DebugRenderer.ts
index 6d0de8a..f74fec8 100644
--- a/src/render/DebugRenderer.ts
+++ b/src/render/DebugRenderer.ts
@@ -3,6 +3,7 @@
class DebugRenderer implements IRenderer {
private _initialized = false;
+ private _handleClick = true;
public render (state: GameState) {
const container = document.getElementById('irreligious-game');
@@ -11,15 +12,17 @@ class DebugRenderer implements IRenderer {
} else {
if (!this._initialized) {
this._initialized = true;
+ state.onResourceClick.push(() => this._handleClick = true);
const style = document.createElement('link');
style.setAttribute('rel', 'stylesheet');
style.setAttribute('href', 'css/debugger.css');
const head = document.getElementsByTagName('head')[0];
head.appendChild(style);
}
- for (const rkey of state.getResources()) {
+ const rkeys = state.getResources();
+ for (const rkey of rkeys) {
const resource = state.getResource(rkey);
- if (resource.unlocked) {
+ if (resource.isUnlocked(state)) {
let el = document.getElementById(`r_${rkey}`);
if (el === null) {
el = document.createElement('div');
@@ -27,11 +30,14 @@ class DebugRenderer implements IRenderer {
el.id = `r_${rkey}`;
let content = `
${resource.name}
-
+
`;
if (resource.clickText !== null) {
content += `
`;
}
+ if (resource.cost !== null && Object.keys(resource.cost) !== null) {
+ content += `
Cost: `;
+ }
el.innerHTML = content;
container.appendChild(el);
if (resource.clickAction !== null) {
@@ -43,9 +49,31 @@ class DebugRenderer implements IRenderer {
const elT = el.getElementsByClassName('max')[0];
elV.innerHTML = this.formatNumber(resource.value, 1);
elT.innerHTML = resource.max !== null ? ` / ${this.formatNumber(resource.max, 2)}` : '';
+ if (this._handleClick) {
+ const elC = el.getElementsByClassName('cost');
+ if (elC.length > 0) {
+ elC[0].innerHTML = this.getCostStr(resource, state);
+ }
+ }
+ }
+ }
+ this._handleClick = false;
+ }
+ }
+
+ private getCostStr (resource: IResource, state: GameState) {
+ let cost = '';
+ for (const rkey of state.getResources()) {
+ if (resource.cost[rkey] !== undefined) {
+ if (cost !== '') cost += ', ';
+ if (rkey === 'money') {
+ cost += `$${this.formatNumber(resource.cost[rkey], 1)}`;
+ } else {
+ cost += `${this.formatNumber(resource.cost[rkey], 1)} ${state.getResource(rkey).name}`;
}
}
}
+ return cost;
}
private formatNumber (num: number, digits: number): string {