diff --git a/cmd b/cmd
index 5e01db2..878300b 100755
--- a/cmd
+++ b/cmd
@@ -2,11 +2,14 @@
cmd=$1
-if [ "$cmd" = "build" ]; then
+if [ "$cmd" = "lint" ]; then
+ tslint --project .
+elif [ "$cmd" = "build" ]; then
tslint --project . && tsc
elif [ "$cmd" = "run" ]; then
firefox public/index.html &
else
- echo "Usage: ./cmd build - lint and compile"
+ echo "Usage: ./cmd lint - lint"
+ echo " ./cmd build - lint and compile"
echo " ./cmd run - run in firefox"
fi
diff --git a/public/css/debugger.css b/public/css/debugger.css
index 873ec31..7cc7c98 100644
--- a/public/css/debugger.css
+++ b/public/css/debugger.css
@@ -1,11 +1,9 @@
-#irreligious-game, .resource-type-container {
- clear: all;
- overflow: auto;
+.resource-type-container {
+ display: flex;
}
-div.resource {
- float: left;
+.resource {
border: 2px solid black;
- margin-right: 5px;
- margin-bottom: 5px;
padding: 5px 10px;
+ margin: 0 5px 5px 0;
+ flex-shrink: 0;
}
diff --git a/src/main.ts b/src/main.ts
index 990aa85..d1d2a2b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -3,12 +3,13 @@
///
///
-let globalStartTime = 0;
+let globalStartTime: number = 0;
let globalTimeout: number = null;
+let cycleLength: number = 250;
function gameLoop (state: GameState, renderer: IRenderer): void {
// figure out how much actual time has passed
- const elapsedTime = globalStartTime > 0
+ const elapsedTime: number = globalStartTime > 0
? (new Date()).getTime() - globalStartTime : 0;
renderer.render(state);
@@ -16,17 +17,18 @@ function gameLoop (state: GameState, renderer: IRenderer): void {
// run again in 1sec
globalStartTime = (new Date()).getTime();
- globalTimeout = setTimeout(() => gameLoop(state, renderer), 1000);
+ globalTimeout = setTimeout((): void =>
+ gameLoop(state, renderer), cycleLength);
}
// run with default config at startup
-(() => {
- const config = new GameConfig();
- const renderer = new DebugRenderer();
- const state = config.generateState();
+((): void => {
+ const config: GameConfig = new GameConfig();
+ const renderer: IRenderer = new DebugRenderer();
+ const state: GameState = config.generateState();
// re-run main loop immediately on user clicks
- state.onResourceClick.push(() => {
+ state.onResourceClick.push((): void => {
if (globalTimeout !== null) {
clearTimeout(globalTimeout);
gameLoop(state, renderer);
@@ -34,5 +36,6 @@ function gameLoop (state: GameState, renderer: IRenderer): void {
});
if (document.readyState !== 'loading') gameLoop(state, renderer);
- else document.addEventListener('DOMContentLoaded', () => gameLoop(state, renderer));
+ else document.addEventListener('DOMContentLoaded', (): void =>
+ gameLoop(state, renderer));
})();
diff --git a/src/model/GameConfig.ts b/src/model/GameConfig.ts
index b964afb..903b7fe 100644
--- a/src/model/GameConfig.ts
+++ b/src/model/GameConfig.ts
@@ -17,7 +17,7 @@ class GameConfig {
public relNoneShare: number = 0.16;
public generateState (): GameState {
- const state = new GameState();
+ const state: GameState = new GameState();
// create player organization
state.addResource('plorg', new Religion(
@@ -57,7 +57,7 @@ class GameConfig {
this.relNoneShare * this.worldPopulation));
// add purchasable resources
- state.addResource('money', new Money(0, 1000));
+ state.addResource('money', new Money(0));
state.addResource('bonds', new Savings(0));
return state;
diff --git a/src/model/GameState.ts b/src/model/GameState.ts
index 79e4084..0169c34 100644
--- a/src/model/GameState.ts
+++ b/src/model/GameState.ts
@@ -16,8 +16,14 @@ class GameState {
if (this._resources[rkey].advanceAction !== null) {
this._resources[rkey].advanceAction(time, this);
}
- if (this._resources[rkey].inc > 0) {
- this._resources[rkey].value += this._resources[rkey].inc * time / 1000;
+ const max: number | null = this._resources[rkey].max(this);
+ if (this._resources[rkey].inc(this) > 0
+ && (max === null || this._resources[rkey].value < max)) {
+ this._resources[rkey].value +=
+ this._resources[rkey].inc(this) * time / 1000;
+ }
+ if (max !== null && this._resources[rkey].value > max) {
+ this._resources[rkey].value = max;
}
}
}
diff --git a/src/model/resource/IResource.ts b/src/model/resource/IResource.ts
index 1cf4dc9..9e9bb54 100644
--- a/src/model/resource/IResource.ts
+++ b/src/model/resource/IResource.ts
@@ -10,8 +10,8 @@ interface IResource {
resourceType: ResourceType;
value: number;
- inc: number;
- max?: number;
+ max: (state: GameState) => number | null;
+ inc: (state: GameState) => number | null;
cost: { [key: string]: number };
isUnlocked: (state: GameState) => boolean;
diff --git a/src/model/resource/Money.ts b/src/model/resource/Money.ts
index 043c244..6972d10 100644
--- a/src/model/resource/Money.ts
+++ b/src/model/resource/Money.ts
@@ -3,14 +3,21 @@
class Money extends Purchasable {
constructor (
public value: number,
- public max: number
) {
super('Money', 'Used to purchase goods and services.');
this.clickText = 'Beg';
this.clickDescription = 'Alms for the poor.';
+ this._baseMax = 1000;
}
public isUnlocked (state: GameState): boolean {
return true;
}
+
+ public inc (state: GameState): number {
+ let baseInc: number = 0;
+ // bonds give $1/s
+ baseInc += state.getResource('bonds').value;
+ return baseInc;
+ }
}
diff --git a/src/model/resource/Purchasable.ts b/src/model/resource/Purchasable.ts
index 79aeb0b..c2f7bc6 100644
--- a/src/model/resource/Purchasable.ts
+++ b/src/model/resource/Purchasable.ts
@@ -1,27 +1,26 @@
///
abstract class Purchasable implements IResource {
- public readonly resourceType = ResourceType.Infrastructure;
- public readonly max?: number = null;
+ public readonly resourceType: ResourceType = ResourceType.Infrastructure;
public value: number = 0;
- public inc: number = 0;
public clickText: string = 'Purchase';
public clickDescription: string = 'Purchase';
public cost: { [key: string]: number } = null;
+
protected _costMultiplier: { [key: string]: number } = null;
+ protected _baseMax: number | null = null;
constructor (
public readonly name: string,
public readonly description: string
) { }
- public clickAction (state: GameState) {
- if (this.max !== null && this.value >= this.max) return;
+ public clickAction (state: GameState): void {
+ if (this.max(state) !== null && this.value >= this.max(state)) return;
if (state.deductCost(this.cost)) {
this.value += 1;
- this.purchaseEffect(state);
if (this._costMultiplier !== null
&& Object.keys(this._costMultiplier !== null)) {
for (const rkey of Object.keys(this._costMultiplier)) {
@@ -31,6 +30,14 @@ abstract class Purchasable implements IResource {
}
}
+ public inc (state: GameState): number | null {
+ return null;
+ }
+
+ public max (state: GameState): number | null {
+ return this._baseMax;
+ }
+
public advanceAction (time: number, state: GameState): void {
return;
}
@@ -38,8 +45,4 @@ abstract class Purchasable implements IResource {
public isUnlocked (state: GameState): boolean {
return false;
}
-
- protected purchaseEffect (state: GameState) {
- return;
- }
}
diff --git a/src/model/resource/Religion.ts b/src/model/resource/Religion.ts
index 68f3462..1f97936 100644
--- a/src/model/resource/Religion.ts
+++ b/src/model/resource/Religion.ts
@@ -1,14 +1,15 @@
///
class Religion implements IResource {
- public readonly resourceType = ResourceType.Religion;
- public readonly max?: number = null;
+ public readonly resourceType: ResourceType = ResourceType.Religion;
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;
- public readonly inc: number = 0;
+
+ public readonly max: () => null = (): null => null;
+ public readonly inc: () => null = (): null => null;
+ public readonly clickAction: () => void = null;
constructor (
public readonly name: string,
diff --git a/src/model/resource/Savings.ts b/src/model/resource/Savings.ts
index 6bf65a7..a4f9ee3 100644
--- a/src/model/resource/Savings.ts
+++ b/src/model/resource/Savings.ts
@@ -1,8 +1,7 @@
///
class Savings extends Purchasable {
- public max?: number = null;
- private _isUnlocked = false;
+ private _isUnlocked: boolean = false;
constructor (
public value: number,
@@ -21,7 +20,7 @@ class Savings extends Purchasable {
return false;
}
- protected purchaseEffect (state: GameState) {
- state.getResource('money').inc += 1;
+ protected purchaseEffect (state: GameState): void {
+ return;
}
}
diff --git a/src/render/DebugRenderer.ts b/src/render/DebugRenderer.ts
index ce6fc8d..767ff35 100644
--- a/src/render/DebugRenderer.ts
+++ b/src/render/DebugRenderer.ts
@@ -2,67 +2,87 @@
///
class DebugRenderer implements IRenderer {
- private _initialized = false;
- private _handleClick = true;
+ private _initialized: boolean = false;
+ private _handleClick: boolean = true;
- public render (state: GameState) {
+ public render (state: GameState): void {
if (!this._initialized) {
- const container = document.getElementById('irreligious-game');
+ const container: HTMLElement =
+ document.getElementById('irreligious-game');
this._initialized = true;
- state.onResourceClick.push(() => this._handleClick = true);
- const style = document.createElement('link');
+ state.onResourceClick.push((): void => {
+ this._handleClick = true;
+ });
+ const style: HTMLElement = document.createElement('link');
style.setAttribute('rel', 'stylesheet');
style.setAttribute('href', 'css/debugger.css');
- const head = document.getElementsByTagName('head')[0];
+ const head: HTMLElement = document.getElementsByTagName('head')[0];
head.appendChild(style);
// create containers for each resource type
for (const item in ResourceType) {
if (isNaN(Number(item))) {
- const el = document.createElement('div');
+ const el: HTMLElement = document.createElement('div');
el.id = `resource-container-${ResourceType[item]}`;
el.className = 'resource-type-container';
container.appendChild(el);
}
}
}
- const rkeys = state.getResources();
+ const rkeys: string[] = state.getResources();
for (const rkey of rkeys) {
- const resource = state.getResource(rkey);
- console.log(`getting container resource-container-${resource.resourceType}`); // tslint:disable-line
- const container = document.getElementById(`resource-container-${resource.resourceType}`);
+ const resource: IResource = state.getResource(rkey);
+ const container: HTMLElement = document
+ .getElementById(`resource-container-${resource.resourceType}`);
if (resource.isUnlocked(state)) {
- let el = document.getElementById(`resource-details-${rkey}`);
+ let el: HTMLElement = document
+ .getElementById(`resource-details-${rkey}`);
if (el === null) {
el = document.createElement('div');
el.className = 'resource';
el.id = `resource-details-${rkey}`;
- let content = `
- ${resource.name}
-
+ let content: string = `
+
+ ${resource.name}
+
+
+
`;
if (resource.clickText !== null) {
- content += `
`;
+ content += `
+ `;
}
- if (resource.cost !== null && Object.keys(resource.cost) !== null) {
+ if (resource.cost !== null
+ && Object.keys(resource.cost) !== null) {
content += `
Cost: `;
}
el.innerHTML = content;
container.appendChild(el);
if (resource.clickAction !== null) {
- const btn = el.getElementsByClassName('resource-btn')[0];
- btn.addEventListener('click', () => state.performClick(rkey));
+ const btn: Element =
+ el.getElementsByClassName('resource-btn')[0];
+ btn.addEventListener('click', (): void =>
+ state.performClick(rkey));
}
}
- const elV = el.getElementsByClassName('resource-value')[0];
- const elT = el.getElementsByClassName('resource-max')[0];
+ const elV: Element =
+ el.getElementsByClassName('resource-value')[0];
+ const elT: Element =
+ el.getElementsByClassName('resource-max')[0];
elV.innerHTML = this.formatNumber(resource.value, 1);
- elT.innerHTML = resource.max !== null ? ` / ${this.formatNumber(resource.max, 1)}` : '';
+ elT.innerHTML = resource.max(state) !== null
+ ? ` / ${this.formatNumber(resource.max(state), 1)}`
+ : '';
if (this._handleClick) {
- if (resource.inc > 0) {
- const elI = el.getElementsByClassName('resource-inc')[0];
- elI.innerHTML = ` +${this.formatNumber(resource.inc, 1)}/s`;
+ if (resource.inc(state) > 0) {
+ const elI: Element =
+ el.getElementsByClassName('resource-inc')[0];
+ elI.innerHTML =
+ ` +${this.formatNumber(resource.inc(state), 1)}/s`;
}
- const elC = el.getElementsByClassName('resource-cost');
+ const elC: HTMLCollectionOf =
+ el.getElementsByClassName('resource-cost');
if (elC.length > 0) {
elC[0].innerHTML = this.getCostStr(resource, state);
}
@@ -72,15 +92,16 @@ class DebugRenderer implements IRenderer {
this._handleClick = false;
}
- private getCostStr (resource: IResource, state: GameState) {
- let cost = '';
+ private getCostStr (resource: IResource, state: GameState): string {
+ let cost: string = '';
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}`;
+ cost += `${this.formatNumber(resource.cost[rkey], 1)}
+ ${state.getResource(rkey).name}`;
}
}
}
@@ -88,7 +109,8 @@ class DebugRenderer implements IRenderer {
}
private formatNumber (num: number, digits: number): string {
- const lookup = [
+ type vlookup = { value: number, symbol: string };
+ const lookup: vlookup[] = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "K" },
{ value: 1e6, symbol: "M" },
@@ -97,8 +119,10 @@ class DebugRenderer implements IRenderer {
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" }
];
- const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
- const item = lookup.slice().reverse().find((i) => num >= i.value);
+ const rx: RegExp = /\.0+$|(\.[0-9]*[1-9])0+$/;
+ const item: vlookup =
+ lookup.slice().reverse()
+ .find((i: vlookup): boolean => num >= i.value);
return item
? (num / item.value).toFixed(digits).replace(rx, "$1") + item.symbol
: num.toFixed(digits).replace(rx, "$1");
diff --git a/src/render/IRenderer.ts b/src/render/IRenderer.ts
index ed0ce79..b4d6e6d 100644
--- a/src/render/IRenderer.ts
+++ b/src/render/IRenderer.ts
@@ -1,5 +1,5 @@
///
interface IRenderer {
- render (state: GameState);
+ render (state: GameState): void;
}
diff --git a/tslint.json b/tslint.json
index d1c81ab..fe04b0d 100644
--- a/tslint.json
+++ b/tslint.json
@@ -3,6 +3,13 @@
"rules": {
"no-reference": false,
"space-before-function-paren": true,
+ "triple-equals": true,
+ "max-line-length": [
+ true, {
+ "limit": 75,
+ "check-strings": true
+ }
+ ],
"whitespace": [
true,
"check-branch",
@@ -14,6 +21,16 @@
"check-type-operator",
"check-preblock",
"check-postbrace"
+ ],
+ "typedef": [
+ true,
+ "call-signature",
+ "arrow-call-signature",
+ "parameter",
+ "arrow-parameter",
+ "property-declaration",
+ "variable-declaration",
+ "member-variable-declaration"
]
}
}