purchasing resources with cost multipliers
This commit is contained in:
parent
65fe779d9a
commit
80c74ed86b
|
@ -26,12 +26,12 @@ function gameLoop (state: GameState, renderer: IRenderer): void {
|
||||||
const state = config.generateState();
|
const state = config.generateState();
|
||||||
|
|
||||||
// re-run main loop immediately on user clicks
|
// re-run main loop immediately on user clicks
|
||||||
state.onResourceClick = () => {
|
state.onResourceClick.push(() => {
|
||||||
if (globalTimeout !== null) {
|
if (globalTimeout !== null) {
|
||||||
clearTimeout(globalTimeout);
|
clearTimeout(globalTimeout);
|
||||||
gameLoop(state, renderer);
|
gameLoop(state, renderer);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (document.readyState !== 'loading') gameLoop(state, renderer);
|
if (document.readyState !== 'loading') gameLoop(state, renderer);
|
||||||
else document.addEventListener('DOMContentLoaded', () => gameLoop(state, renderer));
|
else document.addEventListener('DOMContentLoaded', () => gameLoop(state, renderer));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// <reference path="./GameState.ts" />
|
/// <reference path="./GameState.ts" />
|
||||||
/// <reference path="./resource/Money.ts" />
|
/// <reference path="./resource/Money.ts" />
|
||||||
/// <reference path="./resource/PlayerOrganization.ts" />
|
|
||||||
/// <reference path="./resource/Religion.ts" />
|
/// <reference path="./resource/Religion.ts" />
|
||||||
|
/// <reference path="./resource/SavingsBonds.ts" />
|
||||||
|
|
||||||
class GameConfig {
|
class GameConfig {
|
||||||
public worldPopulation: number = 790000000;
|
public worldPopulation: number = 790000000;
|
||||||
|
@ -20,7 +20,8 @@ class GameConfig {
|
||||||
const state = new GameState();
|
const state = new GameState();
|
||||||
|
|
||||||
// create player organization
|
// create player organization
|
||||||
state.addResource('plorg', new PlayerOrganization());
|
state.addResource('plorg', new Religion(
|
||||||
|
'Player', 'In you they trust.', 0));
|
||||||
|
|
||||||
// create world religions
|
// create world religions
|
||||||
state.addResource('xtian', new Religion(
|
state.addResource('xtian', new Religion(
|
||||||
|
@ -55,8 +56,9 @@ class GameConfig {
|
||||||
'Non-Religious', 'Atheists and agnostics.',
|
'Non-Religious', 'Atheists and agnostics.',
|
||||||
this.relNoneShare * this.worldPopulation));
|
this.relNoneShare * this.worldPopulation));
|
||||||
|
|
||||||
// add crafting resources
|
// add purchasable resources
|
||||||
state.addResource('money', new Money(0, 1000));
|
state.addResource('money', new Money(0, 1000));
|
||||||
|
state.addResource('bonds', new SavingsBonds(0));
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ class GameState {
|
||||||
private _resources: Record<string, IResource> = { };
|
private _resources: Record<string, IResource> = { };
|
||||||
private _resourceKeys: string[] = [];
|
private _resourceKeys: string[] = [];
|
||||||
|
|
||||||
public onResourceClick: () => void = null;
|
public onResourceClick: (() => void)[] = [];
|
||||||
|
|
||||||
public addResource (key: string, resource: IResource): void {
|
public addResource (key: string, resource: IResource): void {
|
||||||
this._resourceKeys.push(key);
|
this._resourceKeys.push(key);
|
||||||
|
@ -30,22 +30,28 @@ class GameState {
|
||||||
public performClick (resourceKey: string): void {
|
public performClick (resourceKey: string): void {
|
||||||
if (this._resources[resourceKey].clickAction !== null) {
|
if (this._resources[resourceKey].clickAction !== null) {
|
||||||
this._resources[resourceKey].clickAction(this);
|
this._resources[resourceKey].clickAction(this);
|
||||||
if (this.onResourceClick !== null) {
|
for (const callback of this.onResourceClick) {
|
||||||
this.onResourceClick();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public deductCost (cost: { [rkey: string]: number }): boolean {
|
public deductCost (cost: { [rkey: string]: number }): boolean {
|
||||||
if (cost === null || Object.keys(cost) === null) return true;
|
if (cost === null || Object.keys(cost) === null) return true;
|
||||||
for (const rkey of Object.keys(cost)) {
|
if (!this.isPurchasable(cost)) return false;
|
||||||
if (this._resources[rkey].value < cost[rkey]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const rkey of Object.keys(cost)) {
|
for (const rkey of Object.keys(cost)) {
|
||||||
this._resources[rkey].value -= cost[rkey];
|
this._resources[rkey].value -= cost[rkey];
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ interface IResource {
|
||||||
resourceType: ResourceType;
|
resourceType: ResourceType;
|
||||||
value: number;
|
value: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
unlocked: boolean;
|
cost: { [key: string]: number };
|
||||||
|
|
||||||
|
isUnlocked: (state: GameState) => boolean;
|
||||||
|
|
||||||
clickText: string;
|
clickText: string;
|
||||||
clickDescription: string;
|
clickDescription: string;
|
||||||
|
|
|
@ -5,9 +5,12 @@ class Money extends Purchasable {
|
||||||
public value: number,
|
public value: number,
|
||||||
public max: 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.clickText = 'Beg';
|
||||||
this.clickDescription = 'Alms for the poor.';
|
this.clickDescription = 'Alms for the poor.';
|
||||||
this.unlocked = true;
|
}
|
||||||
|
|
||||||
|
public isUnlocked (state: GameState): boolean {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
/// <reference path="./IResource.ts" />
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,25 +4,36 @@ abstract class Purchasable implements IResource {
|
||||||
public readonly resourceType = ResourceType.Infrastructure;
|
public readonly resourceType = ResourceType.Infrastructure;
|
||||||
public readonly max?: number = null;
|
public readonly max?: number = null;
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
public unlocked: boolean = false;
|
|
||||||
|
|
||||||
public clickText: string = "Purchase";
|
public clickText: string = 'Purchase';
|
||||||
public clickDescription: string = null;
|
public clickDescription: string = 'Purchase';
|
||||||
|
|
||||||
|
public cost: { [key: string]: number } = null;
|
||||||
|
protected _costMultiplier: { [key: string]: number } = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public readonly name: string,
|
public readonly name: string,
|
||||||
public readonly description: string,
|
public readonly description: string
|
||||||
private _cost: { [key: string]: number }
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public clickAction (state: GameState) {
|
public clickAction (state: GameState) {
|
||||||
if (this.max !== null && this.value >= this.max) return;
|
if (this.max !== null && this.value >= this.max) return;
|
||||||
if (state.deductCost(this._cost)) {
|
if (state.deductCost(this.cost)) {
|
||||||
this.value += 1;
|
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) {
|
public advanceAction (time: number, state: GameState): void {
|
||||||
// do nothing
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isUnlocked (state: GameState): boolean {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,19 @@
|
||||||
class Religion implements IResource {
|
class Religion implements IResource {
|
||||||
public readonly resourceType = ResourceType.Religion;
|
public readonly resourceType = ResourceType.Religion;
|
||||||
public readonly max?: number = null;
|
public readonly max?: number = null;
|
||||||
public readonly unlocked = true;
|
|
||||||
public readonly clickText: string = null;
|
public readonly clickText: string = null;
|
||||||
public readonly clickDescription: string = null;
|
public readonly clickDescription: string = null;
|
||||||
public readonly clickAction: () => void = null;
|
public readonly clickAction: () => void = null;
|
||||||
public readonly advanceAction: (time: number) => void = null;
|
public readonly advanceAction: (time: number) => void = null;
|
||||||
|
public readonly cost: { [key: string]: number } = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public readonly name: string,
|
public readonly name: string,
|
||||||
public readonly description: string,
|
public readonly description: string,
|
||||||
public value: number,
|
public value: number,
|
||||||
) {
|
) { }
|
||||||
|
|
||||||
|
public isUnlocked (state: GameState): boolean {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// <reference path="./Purchasable.ts" />
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
class DebugRenderer implements IRenderer {
|
class DebugRenderer implements IRenderer {
|
||||||
private _initialized = false;
|
private _initialized = false;
|
||||||
|
private _handleClick = true;
|
||||||
|
|
||||||
public render (state: GameState) {
|
public render (state: GameState) {
|
||||||
const container = document.getElementById('irreligious-game');
|
const container = document.getElementById('irreligious-game');
|
||||||
|
@ -11,15 +12,17 @@ class DebugRenderer implements IRenderer {
|
||||||
} else {
|
} else {
|
||||||
if (!this._initialized) {
|
if (!this._initialized) {
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
state.onResourceClick.push(() => this._handleClick = true);
|
||||||
const style = document.createElement('link');
|
const style = document.createElement('link');
|
||||||
style.setAttribute('rel', 'stylesheet');
|
style.setAttribute('rel', 'stylesheet');
|
||||||
style.setAttribute('href', 'css/debugger.css');
|
style.setAttribute('href', 'css/debugger.css');
|
||||||
const head = document.getElementsByTagName('head')[0];
|
const head = document.getElementsByTagName('head')[0];
|
||||||
head.appendChild(style);
|
head.appendChild(style);
|
||||||
}
|
}
|
||||||
for (const rkey of state.getResources()) {
|
const rkeys = state.getResources();
|
||||||
|
for (const rkey of rkeys) {
|
||||||
const resource = state.getResource(rkey);
|
const resource = state.getResource(rkey);
|
||||||
if (resource.unlocked) {
|
if (resource.isUnlocked(state)) {
|
||||||
let el = document.getElementById(`r_${rkey}`);
|
let el = document.getElementById(`r_${rkey}`);
|
||||||
if (el === null) {
|
if (el === null) {
|
||||||
el = document.createElement('div');
|
el = document.createElement('div');
|
||||||
|
@ -27,11 +30,14 @@ class DebugRenderer implements IRenderer {
|
||||||
el.id = `r_${rkey}`;
|
el.id = `r_${rkey}`;
|
||||||
let content = `
|
let content = `
|
||||||
<span class='resourceTitle' title='${resource.description}'>${resource.name}</span><br>
|
<span class='resourceTitle' title='${resource.description}'>${resource.name}</span><br>
|
||||||
<span class='value'></span><span class='max'></span>
|
<span class='value'></span><span class='max'></span><span class='inc'></span>
|
||||||
`;
|
`;
|
||||||
if (resource.clickText !== null) {
|
if (resource.clickText !== null) {
|
||||||
content += `<br><button class='btn' title='${resource.clickDescription}'>${resource.clickText}</button>`;
|
content += `<br><button class='btn' title='${resource.clickDescription}'>${resource.clickText}</button>`;
|
||||||
}
|
}
|
||||||
|
if (resource.cost !== null && Object.keys(resource.cost) !== null) {
|
||||||
|
content += `<br>Cost: <span class='cost'></span>`;
|
||||||
|
}
|
||||||
el.innerHTML = content;
|
el.innerHTML = content;
|
||||||
container.appendChild(el);
|
container.appendChild(el);
|
||||||
if (resource.clickAction !== null) {
|
if (resource.clickAction !== null) {
|
||||||
|
@ -43,10 +49,32 @@ class DebugRenderer implements IRenderer {
|
||||||
const elT = el.getElementsByClassName('max')[0];
|
const elT = el.getElementsByClassName('max')[0];
|
||||||
elV.innerHTML = this.formatNumber(resource.value, 1);
|
elV.innerHTML = this.formatNumber(resource.value, 1);
|
||||||
elT.innerHTML = resource.max !== null ? ` / ${this.formatNumber(resource.max, 2)}` : '';
|
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 {
|
private formatNumber (num: number, digits: number): string {
|
||||||
const lookup = [
|
const lookup = [
|
||||||
|
|
Reference in New Issue