202 lines
6.4 KiB
TypeScript
202 lines
6.4 KiB
TypeScript
/// <reference path="./IResource.ts" />
|
|
|
|
class Follower implements IResource {
|
|
public readonly resourceType = ResourceType.religion;
|
|
public readonly label = 'Your Followers';
|
|
public readonly singularName = 'follower';
|
|
public readonly pluralName = 'followers';
|
|
public readonly description = 'In you they trust.';
|
|
public readonly valueInWholeNumbers = true;
|
|
public value = 0;
|
|
|
|
public userActions: ResourceAction[] = [
|
|
{
|
|
name: 'Recruit',
|
|
description: 'Gather new followers.',
|
|
isEnabled: (state: GameState): boolean => this.value < this.max(state),
|
|
performAction: (state: GameState): void => {
|
|
this._recruitFollower(state);
|
|
},
|
|
},
|
|
];
|
|
|
|
private _timeSinceLastLost = 0;
|
|
private _lastRecruitmentLog = 0;
|
|
private _followerSources: ResourceNumber = {};
|
|
private _followerDests: ResourceNumber = {};
|
|
|
|
public max(state: GameState): number {
|
|
let max = state.config.cfgInitialMax.followers ?? 0;
|
|
max +=
|
|
(state.resource.tents?.value ?? 0) *
|
|
(state.config.cfgCapacity.tents?.followers ?? 0);
|
|
max +=
|
|
(state.resource.houses?.value ?? 0) *
|
|
(state.config.cfgCapacity.houses?.followers ?? 0);
|
|
return max;
|
|
}
|
|
|
|
public inc(state: GameState): number {
|
|
let inc = 0;
|
|
|
|
// pastor recruiting
|
|
const pastors = state.resource.pastors?.value ?? 0;
|
|
inc += pastors * state.config.cfgPastorRecruitRate;
|
|
|
|
// credibility adjustment
|
|
// this should be based on notoriety instead
|
|
/*const creds = state.resource.credibility;
|
|
if (creds?.max !== undefined) inc *= creds.value / creds.max(state);*/
|
|
|
|
return inc;
|
|
}
|
|
|
|
public addValue(amount: number, state: GameState): void {
|
|
const oldValue = this.value;
|
|
this.value += amount;
|
|
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 = this._getRandomReligion(state);
|
|
if (source !== null) {
|
|
source[1].addValue(-1, state);
|
|
const curFollowers = this._followerSources[source[0]] ?? 0;
|
|
this._followerSources[source[0]] = curFollowers + 1;
|
|
}
|
|
}
|
|
} else {
|
|
// lost followers must return to other faiths
|
|
for (let i = 0; i < diff * -1; i++) {
|
|
const dest = this._getRandomReligion(state);
|
|
if (dest !== null) {
|
|
dest[1].addValue(1, state);
|
|
const curFollowers = this._followerDests[dest[0]] ?? 0;
|
|
this._followerDests[dest[0]] = curFollowers + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public isUnlocked(_state: GameState): boolean {
|
|
return true;
|
|
}
|
|
|
|
public advanceAction(time: number, state: GameState): void {
|
|
// chance to lose some followers every 10s if credibility < 100%
|
|
this._timeSinceLastLost += time;
|
|
if (this._timeSinceLastLost > state.config.cfgCredibilityFollowerLossTime) {
|
|
if (this.value > 0) {
|
|
const creds = state.resource.credibility;
|
|
if (creds?.max !== undefined) {
|
|
const ratio = Math.ceil(creds.value) / creds.max(state);
|
|
if (Math.random() > ratio) {
|
|
const lost = Math.ceil(
|
|
this.value *
|
|
state.config.cfgCredibilityFollowerLossRatio *
|
|
(1 - ratio)
|
|
);
|
|
this.addValue(lost * -1, state);
|
|
}
|
|
}
|
|
}
|
|
this._timeSinceLastLost = 0;
|
|
}
|
|
|
|
// log lost and gained followers every 10s
|
|
if (
|
|
state.now - this._lastRecruitmentLog >
|
|
state.config.cfgFollowerGainLossLogTimer &&
|
|
(Object.keys(this._followerSources).length > 0 ||
|
|
Object.keys(this._followerDests).length > 0)
|
|
) {
|
|
if (Object.keys(this._followerDests).length > 0) {
|
|
let msg = '';
|
|
let total = 0;
|
|
for (const key in this._followerDests) {
|
|
const rkey = <ResourceKey>key;
|
|
const religion = state.resource[rkey];
|
|
const followers = this._followerDests[rkey];
|
|
if (religion !== undefined && followers !== undefined) {
|
|
if (msg !== '') msg += ', ';
|
|
msg += `${formatNumber(followers)} became ${
|
|
followers > 1 ? religion.pluralName : 'a ' + religion.singularName
|
|
}`;
|
|
total += followers;
|
|
delete this._followerDests[rkey];
|
|
}
|
|
}
|
|
state.log(
|
|
`You lost ${formatNumber(total)} ${
|
|
total > 1 ? this.pluralName : this.singularName
|
|
}: ${msg}`
|
|
);
|
|
}
|
|
if (Object.keys(this._followerSources).length > 0) {
|
|
let msg = '';
|
|
let total = 0;
|
|
for (const key in this._followerSources) {
|
|
const rkey = <ResourceKey>key;
|
|
const religion = state.resource[rkey];
|
|
const followers = this._followerSources[rkey];
|
|
if (religion !== undefined && followers !== undefined) {
|
|
if (msg !== '') msg += ', ';
|
|
msg += `${formatNumber(followers)} ${
|
|
followers > 1 ? religion.pluralName : religion.singularName
|
|
}`;
|
|
total += followers;
|
|
delete this._followerSources[rkey];
|
|
}
|
|
}
|
|
state.log(
|
|
`You gained ${formatNumber(total)} ${
|
|
total > 1 ? this.pluralName : this.singularName
|
|
}: ${msg}`
|
|
);
|
|
}
|
|
this._lastRecruitmentLog = state.now;
|
|
}
|
|
}
|
|
|
|
private _recruitFollower(state: GameState): void {
|
|
// don't exceed max
|
|
if (this.value >= this.max(state)) {
|
|
state.log('You have no room for more followers.');
|
|
return;
|
|
}
|
|
|
|
// chance to fail increases as credibility decreases
|
|
// this should be based on notoriety instead
|
|
/*const creds = state.resource.credibility;
|
|
if (creds?.max !== undefined) {
|
|
const ratio = 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
|
|
this.addValue(1, state);
|
|
}
|
|
|
|
private _getRandomReligion(
|
|
state: GameState
|
|
): [ResourceKey, IResource] | null {
|
|
const religs = [
|
|
ResourceKey.christianity,
|
|
ResourceKey.islam,
|
|
ResourceKey.hinduism,
|
|
ResourceKey.buddhism,
|
|
ResourceKey.sikhism,
|
|
ResourceKey.judaism,
|
|
ResourceKey.other,
|
|
ResourceKey.atheism,
|
|
];
|
|
const source = religs[Math.floor(Math.random() * 8)];
|
|
const resource = state.resource[source];
|
|
return resource !== undefined ? [source, resource] : null;
|
|
}
|
|
}
|