churches and pastors
This commit is contained in:
parent
4e1386225f
commit
03ffdf0380
|
@ -42,6 +42,9 @@ body, html {
|
||||||
#resource-container-religion .resource {
|
#resource-container-religion .resource {
|
||||||
background-color: #ccf;
|
background-color: #ccf;
|
||||||
}
|
}
|
||||||
|
#resource-container-job .resource {
|
||||||
|
background-color: #fcf;
|
||||||
|
}
|
||||||
#resource-container-consumable .resource {
|
#resource-container-consumable .resource {
|
||||||
background-color: #cfc;
|
background-color: #cfc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,11 @@ function startGame (state: GameState, renderer: IRenderer): void {
|
||||||
const config: GameConfig = new GameConfig();
|
const config: GameConfig = new GameConfig();
|
||||||
|
|
||||||
// debug values to make the game play faster while testing
|
// debug values to make the game play faster while testing
|
||||||
config.baseTitheAmount = 1000;
|
config.cfgTitheAmount = 1000;
|
||||||
config.baseCryptoReturnAmount = 100;
|
config.cfgTimeBetweenTithes = 5000;
|
||||||
config.baseCredibilityRestoreRate = 5;
|
config.cfgCryptoReturnAmount = 100;
|
||||||
|
config.cfgCredibilityRestoreRate = 5;
|
||||||
|
config.cfgPastorRecruitRate = 0.5;
|
||||||
|
|
||||||
const renderer: IRenderer = new DebugRenderer();
|
const renderer: IRenderer = new DebugRenderer();
|
||||||
const state: GameState = config.generateState();
|
const state: GameState = config.generateState();
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
/// <reference path="./GameState.ts" />
|
/// <reference path="./GameState.ts" />
|
||||||
|
/// <reference path="./resource/Church.ts" />
|
||||||
|
/// <reference path="./resource/Compound.ts" />
|
||||||
/// <reference path="./resource/Credibility.ts" />
|
/// <reference path="./resource/Credibility.ts" />
|
||||||
/// <reference path="./resource/CryptoCurrency.ts" />
|
/// <reference path="./resource/CryptoCurrency.ts" />
|
||||||
|
/// <reference path="./resource/House.ts" />
|
||||||
/// <reference path="./resource/Money.ts" />
|
/// <reference path="./resource/Money.ts" />
|
||||||
|
/// <reference path="./resource/Pastor.ts" />
|
||||||
/// <reference path="./resource/PlayerOrg.ts" />
|
/// <reference path="./resource/PlayerOrg.ts" />
|
||||||
/// <reference path="./resource/Religion.ts" />
|
/// <reference path="./resource/Religion.ts" />
|
||||||
/// <reference path="./resource/Tent.ts" />
|
/// <reference path="./resource/Tent.ts" />
|
||||||
/// <reference path="./resource/House.ts" />
|
|
||||||
/// <reference path="./resource/Compound.ts" />
|
|
||||||
|
|
||||||
class GameConfig {
|
class GameConfig {
|
||||||
public worldPopulation: number = 790000000;
|
public worldPopulation: number = 790000000;
|
||||||
|
@ -21,12 +23,15 @@ class GameConfig {
|
||||||
public relOtherShare: number = 0.02;
|
public relOtherShare: number = 0.02;
|
||||||
public relNoneShare: number = 0.16;
|
public relNoneShare: number = 0.16;
|
||||||
|
|
||||||
public baseTitheAmount: number = 10;
|
public cfgTitheAmount: number = 10;
|
||||||
public baseCryptoReturnAmount: number = 1;
|
public cfgTimeBetweenTithes: number = 30000;
|
||||||
public baseCredibilityRestoreRate: number = 0.25;
|
public cfgCryptoReturnAmount: number = 1;
|
||||||
|
public cfgCredibilityRestoreRate: number = 0.25;
|
||||||
|
public cfgPastorRecruitRate: number = 0.01;
|
||||||
|
|
||||||
public generateState (): GameState {
|
public generateState (): GameState {
|
||||||
const state: GameState = new GameState();
|
const state: GameState = new GameState();
|
||||||
|
state.config = this;
|
||||||
|
|
||||||
// create player organization
|
// create player organization
|
||||||
state.addResource('plorg', new PlayerOrg());
|
state.addResource('plorg', new PlayerOrg());
|
||||||
|
@ -64,17 +69,19 @@ class GameConfig {
|
||||||
'Non-Religious', 'Atheists and agnostics.',
|
'Non-Religious', 'Atheists and agnostics.',
|
||||||
this.relNoneShare * this.worldPopulation));
|
this.relNoneShare * this.worldPopulation));
|
||||||
|
|
||||||
|
// add jobs
|
||||||
|
state.addResource('pstor', new Pastor());
|
||||||
|
|
||||||
// add resources
|
// add resources
|
||||||
state.addResource('money', new Money(3.50,
|
state.addResource('money', new Money(3.50));
|
||||||
this.baseTitheAmount, this.baseCryptoReturnAmount));
|
|
||||||
state.addResource('crpto', new CryptoCurrency());
|
state.addResource('crpto', new CryptoCurrency());
|
||||||
state.addResource('tents', new Tent());
|
state.addResource('tents', new Tent());
|
||||||
state.addResource('house', new House());
|
state.addResource('house', new House());
|
||||||
state.addResource('cmpnd', new Compound());
|
state.addResource('cmpnd', new Compound());
|
||||||
|
state.addResource('chrch', new Church());
|
||||||
|
|
||||||
// add passive resources
|
// add passive resources
|
||||||
state.addResource('creds', new Credibility(
|
state.addResource('creds', new Credibility());
|
||||||
this.baseCredibilityRestoreRate));
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ class GameState {
|
||||||
private _versionMaj: number = 0;
|
private _versionMaj: number = 0;
|
||||||
private _versionMin: number = 1;
|
private _versionMin: number = 1;
|
||||||
|
|
||||||
|
public config: GameConfig;
|
||||||
|
|
||||||
private _timeSinceSave: number = 0;
|
private _timeSinceSave: number = 0;
|
||||||
private readonly _timeBetweenSaves: number = 10000;
|
private readonly _timeBetweenSaves: number = 10000;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/// <reference path="./Infrastructure.ts" />
|
||||||
|
|
||||||
|
class Church extends Infrastructure {
|
||||||
|
constructor () {
|
||||||
|
super('Churches',
|
||||||
|
'Preaching grounds for 2 pastors.');
|
||||||
|
this.cost.money = 10000;
|
||||||
|
this._costMultiplier.money = 1.01;
|
||||||
|
this._baseMax = 2;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ 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: IResource = state.getResource('tents');
|
const tents: IResource = state.getResource('tents');
|
||||||
if (tents.value === tents.max(state)) {
|
if (tents.value >= 5) {
|
||||||
this._isUnlocked = true;
|
this._isUnlocked = true;
|
||||||
}
|
}
|
||||||
return this._isUnlocked;
|
return this._isUnlocked;
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
class Credibility extends Passive {
|
class Credibility extends Passive {
|
||||||
private _lastValue: number = 100;
|
private _lastValue: number = 100;
|
||||||
|
|
||||||
constructor (
|
constructor () {
|
||||||
private _baseRestoreRate: number) {
|
|
||||||
super(
|
super(
|
||||||
'Credibility',
|
'Credibility',
|
||||||
'Affects your ability to recruit and retain followers.',
|
'Affects your ability to recruit and retain followers.',
|
||||||
|
@ -16,6 +15,6 @@ class Credibility extends Passive {
|
||||||
}
|
}
|
||||||
|
|
||||||
public inc (state: GameState): number {
|
public inc (state: GameState): number {
|
||||||
return this._baseRestoreRate;
|
return state.config.cfgCredibilityRestoreRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/// <reference path="./Purchasable.ts" />
|
/// <reference path="./Purchasable.ts" />
|
||||||
|
|
||||||
class CryptoCurrency extends Purchasable {
|
class CryptoCurrency extends Purchasable {
|
||||||
|
public readonly valueInWholeNumbers: boolean = false;
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super('Faithcoin',
|
super('Faithcoin',
|
||||||
"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.");
|
||||||
|
|
|
@ -17,8 +17,8 @@ class House extends Infrastructure {
|
||||||
|
|
||||||
public isUnlocked (state: GameState): boolean {
|
public isUnlocked (state: GameState): boolean {
|
||||||
if (this._isUnlocked) return true;
|
if (this._isUnlocked) return true;
|
||||||
const tents: IResource = state.getResource('tents');
|
const compounds: IResource = state.getResource('cmpnd');
|
||||||
if (tents.value === tents.max(state)) {
|
if (compounds.value > 0) {
|
||||||
this._isUnlocked = true;
|
this._isUnlocked = true;
|
||||||
}
|
}
|
||||||
return this._isUnlocked;
|
return this._isUnlocked;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
enum ResourceType {
|
enum ResourceType {
|
||||||
Religion = 'religion',
|
Religion = 'religion',
|
||||||
|
Job = 'job',
|
||||||
Consumable = 'consumable',
|
Consumable = 'consumable',
|
||||||
Infrastructure = 'infrastructure',
|
Infrastructure = 'infrastructure',
|
||||||
Passive = 'passive'
|
Passive = 'passive'
|
||||||
|
@ -11,6 +12,7 @@ interface IResource {
|
||||||
|
|
||||||
resourceType: ResourceType;
|
resourceType: ResourceType;
|
||||||
value: number;
|
value: number;
|
||||||
|
valueInWholeNumbers: boolean;
|
||||||
|
|
||||||
clickText: string;
|
clickText: string;
|
||||||
clickDescription: string;
|
clickDescription: string;
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/// <reference path="./IResource.ts" />
|
||||||
|
|
||||||
|
abstract class Job implements IResource {
|
||||||
|
public readonly resourceType: ResourceType = ResourceType.Job;
|
||||||
|
public value: number = 0;
|
||||||
|
public readonly valueInWholeNumbers: boolean = true;
|
||||||
|
|
||||||
|
public clickText: string = 'Hire';
|
||||||
|
public clickDescription: string = 'Promote one of your followers.';
|
||||||
|
|
||||||
|
public cost: { [key: string]: number } = { };
|
||||||
|
|
||||||
|
protected _costMultiplier: { [key: string]: number } = { };
|
||||||
|
protected _isUnlocked: boolean = false;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
public readonly name: string,
|
||||||
|
public readonly description: string
|
||||||
|
) { }
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
this.value++;
|
||||||
|
state.log(this._hireLog(1, state));
|
||||||
|
for (const rkey of Object.keys(this._costMultiplier)) {
|
||||||
|
this.cost[rkey] *= this._costMultiplier[rkey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inc (state: GameState): number | null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public max (state: GameState): number | null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public advanceAction (time: number, state: GameState): void {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isUnlocked (state: GameState): boolean {
|
||||||
|
return this._isUnlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 res: IResource = state.getResource(rkey);
|
||||||
|
return res.resourceType === ResourceType.Job
|
||||||
|
? tot + res.value
|
||||||
|
: tot;
|
||||||
|
}, 0);
|
||||||
|
let max: number = followers - hired;
|
||||||
|
if (max < 0) max = 0;
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _hireLog (amount: number, state: GameState): string {
|
||||||
|
return `You hired ${amount} x ${this.name}.`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,10 @@ class Money extends Purchasable {
|
||||||
|
|
||||||
public resourceType: ResourceType = ResourceType.Consumable;
|
public resourceType: ResourceType = ResourceType.Consumable;
|
||||||
public cost: { [key: string]: number } = { };
|
public cost: { [key: string]: number } = { };
|
||||||
|
public readonly valueInWholeNumbers: boolean = false;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public value: number,
|
public value: number
|
||||||
private readonly _baseTitheAmount: number,
|
|
||||||
private readonly _baseCryptoReturnAmount: number
|
|
||||||
) {
|
) {
|
||||||
super('Money', 'Used to purchase goods and services.');
|
super('Money', 'Used to purchase goods and services.');
|
||||||
this.clickText = 'Collect Tithes';
|
this.clickText = 'Collect Tithes';
|
||||||
|
@ -22,8 +21,13 @@ class Money extends Purchasable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public inc (state: GameState): number {
|
public inc (state: GameState): number {
|
||||||
|
let inc: number = 0;
|
||||||
|
|
||||||
// crypto currency
|
// crypto currency
|
||||||
return state.getResource('crpto').value * this._baseCryptoReturnAmount;
|
inc += state.getResource('crpto').value
|
||||||
|
* state.config.cfgCryptoReturnAmount;
|
||||||
|
|
||||||
|
return inc;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _purchaseAmount (state: GameState): number {
|
protected _purchaseAmount (state: GameState): number {
|
||||||
|
@ -33,12 +37,12 @@ class Money extends Purchasable {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const diff: number = state.now - this._lastCollectionTime;
|
const diff: number = state.now - this._lastCollectionTime;
|
||||||
if (diff < 30000) {
|
if (diff < state.config.cfgTimeBetweenTithes) {
|
||||||
const lost: number = 30000 / diff / 3;
|
const lost: number = state.config.cfgTimeBetweenTithes / diff / 3;
|
||||||
state.getResource('creds').value -= lost;
|
state.getResource('creds').value -= lost;
|
||||||
}
|
}
|
||||||
// each follower gives you $10
|
// each follower gives you $10
|
||||||
const tithings: number = plorg.value * this._baseTitheAmount;
|
const tithings: number = plorg.value * state.config.cfgTitheAmount;
|
||||||
this._lastCollectionTime = state.now;
|
this._lastCollectionTime = state.now;
|
||||||
return tithings;
|
return tithings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ abstract class Passive implements IResource {
|
||||||
public readonly clickDescription: null = null;
|
public readonly clickDescription: null = null;
|
||||||
public readonly cost: null = null;
|
public readonly cost: null = null;
|
||||||
public readonly clickAction: null = null;
|
public readonly clickAction: null = null;
|
||||||
|
public readonly valueInWholeNumbers: boolean = false;
|
||||||
|
|
||||||
protected _baseMax: number | null;
|
protected _baseMax: number | null;
|
||||||
protected _baseInc: number | null;
|
protected _baseInc: number | null;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/// <reference path="./Job.ts" />
|
||||||
|
|
||||||
|
class Pastor extends Job {
|
||||||
|
private _timeSinceLastTithe: number = 0;
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super('Pastors', 'Leaders of the faith.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public max (state: GameState): number {
|
||||||
|
return state.getResource('chrch').value * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isUnlocked (state: GameState): boolean {
|
||||||
|
if (this._isUnlocked) return true;
|
||||||
|
this._isUnlocked = state.getResource('chrch').isUnlocked(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public advanceAction (time: number, state: GameState): void {
|
||||||
|
this._timeSinceLastTithe += time;
|
||||||
|
if (this._timeSinceLastTithe >= state.config.cfgTimeBetweenTithes) {
|
||||||
|
const money: IResource = state.getResource('money');
|
||||||
|
const plorg: IResource = state.getResource('plorg');
|
||||||
|
// each pastor can collect from up to 100 followers
|
||||||
|
let tithed: number = this.value * 100;
|
||||||
|
if (plorg.value < tithed) tithed = plorg.value;
|
||||||
|
let collected: number = tithed * state.config.cfgTitheAmount;
|
||||||
|
if (collected > money.max(state) - money.value)
|
||||||
|
collected = money.max(state) - money.value;
|
||||||
|
if (collected > 0) {
|
||||||
|
money.value += collected;
|
||||||
|
state.log(`Your pastors collected $${state.formatNumber(collected)} in tithings from ${state.formatNumber(tithed)} followers.`);
|
||||||
|
}
|
||||||
|
this._timeSinceLastTithe = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,9 @@ class PlayerOrg implements IResource {
|
||||||
public readonly description: string = 'In you they trust.';
|
public readonly description: string = 'In you they trust.';
|
||||||
|
|
||||||
public cost: { [key: string]: number } = { };
|
public cost: { [key: string]: number } = { };
|
||||||
public readonly inc: null = null;
|
|
||||||
|
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
|
public readonly valueInWholeNumbers: boolean = true;
|
||||||
|
|
||||||
public clickText: string = 'Recruit';
|
public clickText: string = 'Recruit';
|
||||||
public clickDescription: string = 'Gather new followers.';
|
public clickDescription: string = 'Gather new followers.';
|
||||||
|
@ -53,6 +53,16 @@ class PlayerOrg implements IResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public inc (state: GameState): number {
|
||||||
|
let inc: number = 0;
|
||||||
|
|
||||||
|
// pastor auto-recruit
|
||||||
|
inc += state.getResource('pstor').value
|
||||||
|
* state.config.cfgPastorRecruitRate;
|
||||||
|
|
||||||
|
return inc;
|
||||||
|
}
|
||||||
|
|
||||||
public advanceAction (time: number, state: GameState): void {
|
public advanceAction (time: number, state: GameState): void {
|
||||||
// chance to lose some followers every 10s if credibility < 100%
|
// chance to lose some followers every 10s if credibility < 100%
|
||||||
if (state.now - this._lastLostTime > 10000) {
|
if (state.now - this._lastLostTime > 10000) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
abstract class Purchasable implements IResource {
|
abstract class Purchasable implements IResource {
|
||||||
public readonly resourceType: ResourceType = ResourceType.Consumable;
|
public readonly resourceType: ResourceType = ResourceType.Consumable;
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
|
public valueInWholeNumbers: boolean = true;
|
||||||
|
|
||||||
public clickText: string = 'Purchase';
|
public clickText: string = 'Purchase';
|
||||||
public clickDescription: string = 'Purchase';
|
public clickDescription: string = 'Purchase';
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Religion implements IResource {
|
||||||
public readonly cost: null = null;
|
public readonly cost: null = null;
|
||||||
public readonly max: null = null;
|
public readonly max: null = null;
|
||||||
public readonly inc: null = null;
|
public readonly inc: null = null;
|
||||||
|
public readonly valueInWholeNumbers: boolean = true;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public readonly name: string,
|
public readonly name: string,
|
||||||
|
|
|
@ -82,7 +82,10 @@ class DebugRenderer implements IRenderer {
|
||||||
el.getElementsByClassName('resource-value')[0];
|
el.getElementsByClassName('resource-value')[0];
|
||||||
const elT: Element =
|
const elT: Element =
|
||||||
el.getElementsByClassName('resource-max')[0];
|
el.getElementsByClassName('resource-max')[0];
|
||||||
elV.innerHTML = state.formatNumber(resource.value);
|
const value: number = resource.valueInWholeNumbers
|
||||||
|
? Math.floor(resource.value)
|
||||||
|
: resource.value;
|
||||||
|
elV.innerHTML = state.formatNumber(value);
|
||||||
elT.innerHTML = resource.max !== null
|
elT.innerHTML = resource.max !== null
|
||||||
&& resource.max(state) !== null
|
&& resource.max(state) !== null
|
||||||
? ` / ${state.formatNumber(resource.max(state))}`
|
? ` / ${state.formatNumber(resource.max(state))}`
|
||||||
|
|
Reference in New Issue