changed cost into function; changed inc into ResourcNumber
This commit is contained in:
parent
a0daffbddf
commit
0b6e263cc7
14
TODO.md
14
TODO.md
|
@ -1,7 +1,5 @@
|
||||||
## In Progress
|
## In Progress
|
||||||
|
|
||||||
- [ ] change `inc` to a `ResourceNumber` instead of a `number`
|
|
||||||
- [ ] change `cost` into a function that computes based on `value`
|
|
||||||
|
|
||||||
## Initial Game Progression Plan
|
## Initial Game Progression Plan
|
||||||
|
|
||||||
|
@ -13,17 +11,16 @@
|
||||||
- [x] houses (+follower cap)
|
- [x] houses (+follower cap)
|
||||||
- [x] churches (+pastor cap)
|
- [x] churches (+pastor cap)
|
||||||
- [x] hire pastors to recruit and gather tithes (+money, +followers)
|
- [x] hire pastors to recruit and gather tithes (+money, +followers)
|
||||||
- [ ] hire compound managers to auto-build (+tents, +houses, +churches)
|
- [x] hire compound managers to auto-build (+tents, +houses, +churches)
|
||||||
- [ ] fluctuating crypto market value passive determines crypto value
|
- [x] fluctuating crypto market value passive determines crypto value
|
||||||
- [ ] prosperity gospel policy increases tithes, decreases recruitment rate
|
- [ ] prosperity gospel policy increases tithes, decreases recruitment rate
|
||||||
- main resource problems:
|
- main resource problems:
|
||||||
- [x] lose money on purchases
|
- [x] lose money on purchases
|
||||||
- [x] lose money for salaries
|
- [x] lose money for salaries
|
||||||
- [ ] lose money for compound maintenance
|
|
||||||
- [x] gain money from tithes
|
- [x] gain money from tithes
|
||||||
- [x] low credibility loses followers
|
- [x] low credibility loses followers
|
||||||
- [ ] low and decreasing money loses jobs
|
- [x] low and decreasing money loses jobs
|
||||||
- [ ] gain or lose money on crypto based on market passive
|
- [x] gain or lose money on crypto based on market passive
|
||||||
|
|
||||||
### Phase 2: 1K-100K Followers
|
### Phase 2: 1K-100K Followers
|
||||||
|
|
||||||
|
@ -84,10 +81,7 @@
|
||||||
## Short-Term Todos
|
## Short-Term Todos
|
||||||
|
|
||||||
- [ ] add `disabledHint` to `userActions` to generate hint about how to enable an action
|
- [ ] add `disabledHint` to `userActions` to generate hint about how to enable an action
|
||||||
- [x] add an action to sell purchasables and regain some portion of their cost
|
|
||||||
- [ ] add a `policy` resource type that can be toggled on or off
|
- [ ] add a `policy` resource type that can be toggled on or off
|
||||||
- [x] remove recruitment effects of credibility (that will be for notoriety instead)
|
|
||||||
- [x] change `value` to a getter that does `Math.floor` if it's a whole number resource
|
|
||||||
|
|
||||||
## Long-Term Ideas
|
## Long-Term Ideas
|
||||||
|
|
||||||
|
|
|
@ -89,12 +89,12 @@ class GameConfig {
|
||||||
public cfgCryptoMarketAdjustAmount = 0.1;
|
public cfgCryptoMarketAdjustAmount = 0.1;
|
||||||
public cfgCryptoMarketAdjustPeriod = 30000;
|
public cfgCryptoMarketAdjustPeriod = 30000;
|
||||||
public cfgCryptoMarketGrowthBias = 0.1;
|
public cfgCryptoMarketGrowthBias = 0.1;
|
||||||
public cfgDefaultSellMultiplier = 0.5;
|
|
||||||
public cfgFollowerGainLossLogTimer = 10000;
|
public cfgFollowerGainLossLogTimer = 10000;
|
||||||
public cfgNoMoneyQuitRate = 0.2;
|
public cfgNoMoneyQuitRate = 0.2;
|
||||||
public cfgNoMoneyQuitTime = 10000;
|
public cfgNoMoneyQuitTime = 10000;
|
||||||
public cfgPassiveMax = 100;
|
public cfgPassiveMax = 100;
|
||||||
public cfgPastorRecruitRate = 0.01;
|
public cfgPastorRecruitRate = 0.01;
|
||||||
|
public cfgSellCostBackMultiplier = 0.5;
|
||||||
public cfgTimeBetweenTithes = 10000;
|
public cfgTimeBetweenTithes = 10000;
|
||||||
public cfgTitheAmount = 10000;
|
public cfgTitheAmount = 10000;
|
||||||
public cfgTitheCredibilityHitFactor = 3;
|
public cfgTitheCredibilityHitFactor = 3;
|
||||||
|
@ -202,7 +202,7 @@ class GameConfig {
|
||||||
|
|
||||||
// add resources
|
// add resources
|
||||||
state.addResource(new Money(3.5));
|
state.addResource(new Money(3.5));
|
||||||
state.addResource(new CryptoCurrency(this));
|
state.addResource(new CryptoCurrency());
|
||||||
state.addResource(new Tent(this));
|
state.addResource(new Tent(this));
|
||||||
state.addResource(new House(this));
|
state.addResource(new House(this));
|
||||||
state.addResource(new Church(this));
|
state.addResource(new Church(this));
|
||||||
|
|
|
@ -58,7 +58,10 @@ class GameState {
|
||||||
resource.inc !== undefined &&
|
resource.inc !== undefined &&
|
||||||
(resource.max === undefined || resource.value < resource.max(this))
|
(resource.max === undefined || resource.value < resource.max(this))
|
||||||
) {
|
) {
|
||||||
resource.addValue((resource.inc(this) * time) / 1000, this);
|
resource.addValue(
|
||||||
|
(resourceNumberSum(resource.inc(this)) * time) / 1000,
|
||||||
|
this
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.max !== undefined && resource.value > resource.max(this)) {
|
if (resource.max !== undefined && resource.value > resource.max(this)) {
|
||||||
|
@ -138,7 +141,6 @@ class GameState {
|
||||||
if (resource === undefined) continue;
|
if (resource === undefined) continue;
|
||||||
const resSav: ResourceConfig = {
|
const resSav: ResourceConfig = {
|
||||||
value: resource.value,
|
value: resource.value,
|
||||||
cost: resource.cost,
|
|
||||||
};
|
};
|
||||||
if (resource.emitConfig !== undefined) {
|
if (resource.emitConfig !== undefined) {
|
||||||
resSav.config = resource.emitConfig();
|
resSav.config = resource.emitConfig();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference path="./resource/SharedTypes.ts" />
|
||||||
|
|
||||||
const numberFormatDigits = 1;
|
const numberFormatDigits = 1;
|
||||||
|
|
||||||
function formatNumber(num: number): string {
|
function formatNumber(num: number): string {
|
||||||
|
@ -26,3 +28,12 @@ function formatNumber(num: number): string {
|
||||||
|
|
||||||
return `${sign}${number}`;
|
return `${sign}${number}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resourceNumberSum(res: ResourceNumber): number {
|
||||||
|
let sum = 0;
|
||||||
|
for (const key in res) {
|
||||||
|
const rkey = <ResourceKey>key;
|
||||||
|
sum += res[rkey] ?? 0;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ class BuildingPermit extends Research {
|
||||||
'building permits',
|
'building permits',
|
||||||
'Unlocks several new buildings you can build outside of your compounds.'
|
'Unlocks several new buildings you can build outside of your compounds.'
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.buildingPermit;
|
this._baseCost.money = config.cfgInitialCost.buildingPermit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isUnlocked = (state: GameState): boolean => {
|
public isUnlocked = (state: GameState): boolean => {
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Church extends Infrastructure {
|
||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.churches;
|
this._baseCost.money = config.cfgInitialCost.churches;
|
||||||
this._costMultiplier.money = config.cfgCostMultiplier.churches;
|
this._costMultiplier.money = config.cfgCostMultiplier.churches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,15 @@ class Church extends Infrastructure {
|
||||||
(state.resource.compounds?.value ?? 0) *
|
(state.resource.compounds?.value ?? 0) *
|
||||||
(state.config.cfgCapacity.compounds?.churches ?? 0);
|
(state.config.cfgCapacity.compounds?.churches ?? 0);
|
||||||
|
|
||||||
public inc = (state: GameState): number => {
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
// compound managers
|
const inc: ResourceNumber = {};
|
||||||
return (
|
const compoundManagers =
|
||||||
(state.resource.compoundManagers?.value ?? 0) *
|
(state.resource.compoundManagers?.value ?? 0) *
|
||||||
(state.config.cfgBuySpeed.compoundManagers?.churches ?? 0)
|
(state.config.cfgBuySpeed.compoundManagers?.churches ?? 0);
|
||||||
);
|
if (compoundManagers > 0) {
|
||||||
|
inc.compoundManagers = compoundManagers;
|
||||||
|
}
|
||||||
|
return inc;
|
||||||
};
|
};
|
||||||
|
|
||||||
public isUnlocked = (state: GameState): boolean => {
|
public isUnlocked = (state: GameState): boolean => {
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Compound extends Infrastructure {
|
||||||
'Provides space for tents, houses, and churches and a place to hide more money.',
|
'Provides space for tents, houses, and churches and a place to hide more money.',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.compounds;
|
this._baseCost.money = config.cfgInitialCost.compounds;
|
||||||
this._costMultiplier.money = config.cfgCostMultiplier.compounds;
|
this._costMultiplier.money = config.cfgCostMultiplier.compounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ class Credibility extends Passive {
|
||||||
|
|
||||||
public max = (state: GameState): number => state.config.cfgPassiveMax;
|
public max = (state: GameState): number => state.config.cfgPassiveMax;
|
||||||
|
|
||||||
public inc = (state: GameState): number =>
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
state.config.cfgCredibilityRestoreRate;
|
const inc: ResourceNumber = {};
|
||||||
|
inc.credibility = state.config.cfgCredibilityRestoreRate;
|
||||||
|
return inc;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class CryptoCurrency extends Purchasable {
|
class CryptoCurrency extends Purchasable {
|
||||||
public readonly resourceKey = ResourceKey.cryptoCurrency;
|
public readonly resourceKey = ResourceKey.cryptoCurrency;
|
||||||
|
|
||||||
constructor(config: GameConfig) {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
'FaithCoin',
|
'FaithCoin',
|
||||||
'faithcoin',
|
'faithcoin',
|
||||||
|
@ -11,11 +11,21 @@ class CryptoCurrency extends Purchasable {
|
||||||
"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.",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.cryptoCurrency;
|
|
||||||
this.valueInWholeNumbers = false;
|
this.valueInWholeNumbers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isUnlocked = (_state: GameState): boolean => true;
|
public cost = (state: GameState): ResourceNumber => {
|
||||||
|
const cost: ResourceNumber = {};
|
||||||
|
const market = state.resource.cryptoMarket;
|
||||||
|
if (market !== undefined) {
|
||||||
|
cost.money = market.value;
|
||||||
|
} else {
|
||||||
|
cost.money = state.config.cfgInitialCost.cryptoCurrency;
|
||||||
|
}
|
||||||
|
return cost;
|
||||||
|
};
|
||||||
|
|
||||||
|
public isUnlocked = (): boolean => true;
|
||||||
|
|
||||||
public max = (state: GameState): number =>
|
public max = (state: GameState): number =>
|
||||||
state.config.cfgInitialMax.cryptoCurrency ?? 0;
|
state.config.cfgInitialMax.cryptoCurrency ?? 0;
|
||||||
|
|
|
@ -40,17 +40,13 @@ class CryptoMarket extends Hidden {
|
||||||
) {
|
) {
|
||||||
adjustment = state.config.cfgCryptoCurrencyMinimumValue - this.value;
|
adjustment = state.config.cfgCryptoCurrencyMinimumValue - this.value;
|
||||||
}
|
}
|
||||||
//if (Math.abs(adjustment) > 0) {
|
if (Math.abs(adjustment) > 0) {
|
||||||
this.addValue(adjustment, state);
|
this.addValue(adjustment, state);
|
||||||
state.log(
|
state.log(
|
||||||
`FaithCoin just ${
|
`FaithCoin just ${
|
||||||
adjustment > 0 ? 'increased' : 'decreased'
|
adjustment > 0 ? 'increased' : 'decreased'
|
||||||
} in value by $${formatNumber(Math.abs(adjustment))}.`
|
} in value by $${formatNumber(Math.abs(adjustment))}.`
|
||||||
);
|
);
|
||||||
//}
|
|
||||||
if (crypto?.cost !== undefined) {
|
|
||||||
crypto.cost.money = this.value;
|
|
||||||
state.autoAction(); // cause redraw
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,12 +39,14 @@ class Follower extends Resource {
|
||||||
return max;
|
return max;
|
||||||
};
|
};
|
||||||
|
|
||||||
public inc = (state: GameState): number => {
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
let inc = 0;
|
const inc: ResourceNumber = {};
|
||||||
|
|
||||||
// pastor recruiting
|
// pastor recruiting
|
||||||
const pastors = state.resource.pastors?.value ?? 0;
|
const pastors =
|
||||||
inc += pastors * state.config.cfgPastorRecruitRate;
|
(state.resource.pastors?.value ?? 0) * state.config.cfgPastorRecruitRate;
|
||||||
|
|
||||||
|
if (pastors > 0) inc.pastors = pastors;
|
||||||
|
|
||||||
// credibility adjustment
|
// credibility adjustment
|
||||||
// this should be based on notoriety instead
|
// this should be based on notoriety instead
|
||||||
|
|
|
@ -13,7 +13,7 @@ class House extends Infrastructure {
|
||||||
)} followers.`,
|
)} followers.`,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.houses;
|
this._baseCost.money = config.cfgInitialCost.houses;
|
||||||
this._costMultiplier.money = config.cfgCostMultiplier.houses;
|
this._costMultiplier.money = config.cfgCostMultiplier.houses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,12 +21,15 @@ class House extends Infrastructure {
|
||||||
(state.resource.compounds?.value ?? 0) *
|
(state.resource.compounds?.value ?? 0) *
|
||||||
(state.config.cfgCapacity.compounds?.houses ?? 0);
|
(state.config.cfgCapacity.compounds?.houses ?? 0);
|
||||||
|
|
||||||
public inc = (state: GameState): number => {
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
// compound managers
|
const inc: ResourceNumber = {};
|
||||||
return (
|
const compoundManagers =
|
||||||
(state.resource.compoundManagers?.value ?? 0) *
|
(state.resource.compoundManagers?.value ?? 0) *
|
||||||
(state.config.cfgBuySpeed.compoundManagers?.houses ?? 0)
|
(state.config.cfgBuySpeed.compoundManagers?.houses ?? 0);
|
||||||
);
|
if (compoundManagers > 0) {
|
||||||
|
inc.compoundManagers = compoundManagers;
|
||||||
|
}
|
||||||
|
return inc;
|
||||||
};
|
};
|
||||||
|
|
||||||
public isUnlocked = (state: GameState): boolean => {
|
public isUnlocked = (state: GameState): boolean => {
|
||||||
|
|
|
@ -4,10 +4,9 @@ abstract class Job extends Resource {
|
||||||
public readonly resourceType = ResourceType.job;
|
public readonly resourceType = ResourceType.job;
|
||||||
|
|
||||||
public readonly valueInWholeNumbers = true;
|
public readonly valueInWholeNumbers = true;
|
||||||
public readonly cost: ResourceNumber = {};
|
|
||||||
|
|
||||||
public max?: (state: GameState) => number = undefined;
|
public max?: (state: GameState) => number = undefined;
|
||||||
public inc?: (state: GameState) => number = undefined;
|
public inc?: (state: GameState) => ResourceNumber = undefined;
|
||||||
|
|
||||||
public userActions: ResourceAction[] = [
|
public userActions: ResourceAction[] = [
|
||||||
{
|
{
|
||||||
|
@ -31,7 +30,6 @@ abstract class Job extends Resource {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _costMultiplier: { [key in ResourceKey]?: number } = {};
|
|
||||||
protected _isUnlocked = false;
|
protected _isUnlocked = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -102,18 +100,9 @@ abstract class Job extends Resource {
|
||||||
|
|
||||||
private _promoteFollower(state: GameState): void {
|
private _promoteFollower(state: GameState): void {
|
||||||
if (Job.availableJobs(state) <= 0) return;
|
if (Job.availableJobs(state) <= 0) return;
|
||||||
if (
|
if (this.max !== undefined && this.value < this.max(state)) {
|
||||||
this.max !== undefined &&
|
|
||||||
this.value < this.max(state) &&
|
|
||||||
state.deductCost(this.cost)
|
|
||||||
) {
|
|
||||||
this.addValue(1, state);
|
this.addValue(1, state);
|
||||||
state.log(this._hireLog(1, state));
|
state.log(this._hireLog(1, state));
|
||||||
for (const key in this._costMultiplier) {
|
|
||||||
const rkey = <ResourceKey>key;
|
|
||||||
this.cost[rkey] =
|
|
||||||
(this.cost[rkey] ?? 0) * (this._costMultiplier[rkey] ?? 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +110,5 @@ abstract class Job extends Resource {
|
||||||
if (this.value <= 0) return;
|
if (this.value <= 0) return;
|
||||||
this.addValue(-1, state);
|
this.addValue(-1, state);
|
||||||
state.log(this._hireLog(-1, state));
|
state.log(this._hireLog(-1, state));
|
||||||
for (const key in this._costMultiplier) {
|
|
||||||
const rkey = <ResourceKey>key;
|
|
||||||
this.cost[rkey] =
|
|
||||||
(this.cost[rkey] ?? 0) / (this._costMultiplier[rkey] ?? 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Megachurch extends Infrastructure {
|
||||||
)} pastors`,
|
)} pastors`,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.megaChurches;
|
this._baseCost.money = config.cfgInitialCost.megaChurches;
|
||||||
this._costMultiplier.money = config.cfgCostMultiplier.megaChurches;
|
this._costMultiplier.money = config.cfgCostMultiplier.megaChurches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@ class Money extends Resource {
|
||||||
return max;
|
return max;
|
||||||
};
|
};
|
||||||
|
|
||||||
public inc = (state: GameState): number => {
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
let inc = 0;
|
const inc: ResourceNumber = {};
|
||||||
|
|
||||||
// tithings
|
// tithings
|
||||||
inc +=
|
const tithings =
|
||||||
((state.resource.pastors?.value ?? 0) *
|
((state.resource.pastors?.value ?? 0) *
|
||||||
(state.resource.followers?.value ?? 0) *
|
(state.resource.followers?.value ?? 0) *
|
||||||
(state.config.cfgTitheAmount ?? 0) *
|
(state.config.cfgTitheAmount ?? 0) *
|
||||||
|
@ -52,10 +52,13 @@ class Money extends Resource {
|
||||||
state.config.cfgTimeBetweenTithes;
|
state.config.cfgTimeBetweenTithes;
|
||||||
|
|
||||||
// salaries
|
// salaries
|
||||||
inc -=
|
const compoundManagers =
|
||||||
(state.resource.compoundManagers?.value ?? 0) *
|
(state.resource.compoundManagers?.value ?? 0) *
|
||||||
(state.config.cfgSalary.compoundManagers ?? 0);
|
(state.config.cfgSalary.compoundManagers ?? 0);
|
||||||
|
|
||||||
|
if (tithings > 0) inc.pastors = tithings;
|
||||||
|
if (compoundManagers > 0) inc.compoundManagers = compoundManagers * -1;
|
||||||
|
|
||||||
return inc;
|
return inc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,9 @@ abstract class Purchasable extends Resource {
|
||||||
public readonly resourceType: ResourceType = ResourceType.purchasable;
|
public readonly resourceType: ResourceType = ResourceType.purchasable;
|
||||||
|
|
||||||
public valueInWholeNumbers = true;
|
public valueInWholeNumbers = true;
|
||||||
public cost: ResourceNumber = {};
|
|
||||||
|
|
||||||
public inc?: (state: GameState) => number = undefined;
|
|
||||||
public max?: (state: GameState) => number = undefined;
|
public max?: (state: GameState) => number = undefined;
|
||||||
|
public inc?: (state: GameState) => ResourceNumber = undefined;
|
||||||
|
|
||||||
public userActions: ResourceAction[] = [
|
public userActions: ResourceAction[] = [
|
||||||
{
|
{
|
||||||
|
@ -15,19 +14,17 @@ abstract class Purchasable extends Resource {
|
||||||
description: this._purchaseDescription,
|
description: this._purchaseDescription,
|
||||||
isEnabled: (state: GameState): boolean =>
|
isEnabled: (state: GameState): boolean =>
|
||||||
(this.max === undefined || this.value < this.max(state)) &&
|
(this.max === undefined || this.value < this.max(state)) &&
|
||||||
state.isPurchasable(this.cost),
|
state.isPurchasable(this.cost(state)),
|
||||||
performAction: (state: GameState): void => {
|
performAction: (state: GameState): void => {
|
||||||
this._purchase(state);
|
this._purchase(state);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _costMultiplier: ResourceNumber = {};
|
protected readonly _baseCost: ResourceNumber = {};
|
||||||
protected _sellMultiplier?: number | ResourceNumber;
|
protected readonly _costMultiplier: ResourceNumber = {};
|
||||||
protected _isUnlocked = false;
|
protected _isUnlocked = false;
|
||||||
|
|
||||||
private _lastWholeNumberValue = 0;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly label: string,
|
public readonly label: string,
|
||||||
public readonly singularName: string,
|
public readonly singularName: string,
|
||||||
|
@ -52,8 +49,21 @@ abstract class Purchasable extends Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cost = (_: GameState): ResourceNumber => {
|
||||||
|
if (this.value <= 0) return this._baseCost;
|
||||||
|
|
||||||
|
const actualCost: ResourceNumber = {};
|
||||||
|
for (const key in this._baseCost) {
|
||||||
|
const rkey = <ResourceKey>key;
|
||||||
|
const baseCost = this._baseCost[rkey] ?? 0;
|
||||||
|
const multiplier = this._costMultiplier[rkey] ?? 1;
|
||||||
|
actualCost[rkey] = baseCost * Math.pow(multiplier, this.value);
|
||||||
|
}
|
||||||
|
return actualCost;
|
||||||
|
};
|
||||||
|
|
||||||
public isUnlocked = (state: GameState): boolean => {
|
public isUnlocked = (state: GameState): boolean => {
|
||||||
if (!this._isUnlocked && state.isPurchasable(this.cost)) {
|
if (!this._isUnlocked && state.isPurchasable(this.cost(state))) {
|
||||||
this._isUnlocked = true;
|
this._isUnlocked = true;
|
||||||
}
|
}
|
||||||
return this._isUnlocked;
|
return this._isUnlocked;
|
||||||
|
@ -73,21 +83,6 @@ abstract class Purchasable extends Resource {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public addValue = (amount: number, _: GameState): void => {
|
|
||||||
this.rawValue += amount;
|
|
||||||
const wholeNumberChange = this.value - this._lastWholeNumberValue;
|
|
||||||
if (wholeNumberChange > 0) {
|
|
||||||
for (const key in this._costMultiplier) {
|
|
||||||
const rkey = <ResourceKey>key;
|
|
||||||
this.cost[rkey] =
|
|
||||||
(this.cost[rkey] ?? 0) *
|
|
||||||
(this._costMultiplier[rkey] ?? 1) *
|
|
||||||
wholeNumberChange;
|
|
||||||
}
|
|
||||||
this._lastWholeNumberValue = this.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected _purchaseLog(amount: number, _state: GameState): string {
|
protected _purchaseLog(amount: number, _state: GameState): string {
|
||||||
let verb = 'purchased';
|
let verb = 'purchased';
|
||||||
if (amount < 0) {
|
if (amount < 0) {
|
||||||
|
@ -101,7 +96,7 @@ abstract class Purchasable extends Resource {
|
||||||
|
|
||||||
private _purchase(state: GameState): void {
|
private _purchase(state: GameState): void {
|
||||||
if (this.max !== undefined && this.value >= this.max(state)) return;
|
if (this.max !== undefined && this.value >= this.max(state)) return;
|
||||||
if (state.deductCost(this.cost)) {
|
if (state.deductCost(this.cost(state))) {
|
||||||
this.addValue(1, state);
|
this.addValue(1, state);
|
||||||
state.log(this._purchaseLog(1, state));
|
state.log(this._purchaseLog(1, state));
|
||||||
}
|
}
|
||||||
|
@ -109,25 +104,17 @@ abstract class Purchasable extends Resource {
|
||||||
|
|
||||||
private _sell(state: GameState): void {
|
private _sell(state: GameState): void {
|
||||||
if (this.value <= 0) return;
|
if (this.value <= 0) return;
|
||||||
const costBack: ResourceNumber = {};
|
|
||||||
for (const key in this.cost) {
|
|
||||||
const rkey = <ResourceKey>key;
|
|
||||||
let cost = this.cost[rkey];
|
|
||||||
if (cost === undefined) continue;
|
|
||||||
// revert cost multiplier
|
|
||||||
cost /= this._costMultiplier[rkey] ?? 1;
|
|
||||||
this.cost[rkey] = cost;
|
|
||||||
const multiplier =
|
|
||||||
this._sellMultiplier === undefined
|
|
||||||
? state.config.cfgDefaultSellMultiplier
|
|
||||||
: typeof this._sellMultiplier === 'number'
|
|
||||||
? this._sellMultiplier
|
|
||||||
: this._sellMultiplier[rkey] ?? state.config.cfgDefaultSellMultiplier;
|
|
||||||
// penalize return on used item
|
|
||||||
costBack[rkey] = cost * -1 * multiplier;
|
|
||||||
state.deductCost(costBack);
|
|
||||||
}
|
|
||||||
this.addValue(-1, state);
|
this.addValue(-1, state);
|
||||||
state.log(this._purchaseLog(-1, state));
|
state.log(this._purchaseLog(-1, state));
|
||||||
|
|
||||||
|
const costBack: ResourceNumber = {};
|
||||||
|
for (const key in this.cost(state)) {
|
||||||
|
const rkey = <ResourceKey>key;
|
||||||
|
const cost = this.cost(state)[rkey];
|
||||||
|
if (cost === undefined) continue;
|
||||||
|
costBack[rkey] = cost * state.config.cfgSellCostBackMultiplier * -1;
|
||||||
|
}
|
||||||
|
state.deductCost(costBack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
/// <reference path="./IResource.ts" />
|
/// <reference path="./IResource.ts" />
|
||||||
|
|
||||||
abstract class Resource implements IResource {
|
abstract class Resource implements IResource {
|
||||||
|
public inc?: (state: GameState) => ResourceNumber = undefined;
|
||||||
|
public cost?: (state: GameState) => ResourceNumber = undefined;
|
||||||
|
public max?: (state: GameState) => number = undefined;
|
||||||
|
|
||||||
protected rawValue = 0;
|
protected rawValue = 0;
|
||||||
|
|
||||||
public abstract readonly resourceType: ResourceType;
|
public abstract readonly resourceType: ResourceType;
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Tent extends Infrastructure {
|
||||||
)} followers.`,
|
)} followers.`,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.cost.money = config.cfgInitialCost.tents;
|
this._baseCost.money = config.cfgInitialCost.tents;
|
||||||
this._costMultiplier.money = config.cfgCostMultiplier.tents;
|
this._costMultiplier.money = config.cfgCostMultiplier.tents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +26,14 @@ class Tent extends Infrastructure {
|
||||||
return max;
|
return max;
|
||||||
};
|
};
|
||||||
|
|
||||||
public inc = (state: GameState): number =>
|
public inc = (state: GameState): ResourceNumber => {
|
||||||
// compound managers
|
const inc: ResourceNumber = {};
|
||||||
|
const compoundManagers =
|
||||||
(state.resource.compoundManagers?.value ?? 0) *
|
(state.resource.compoundManagers?.value ?? 0) *
|
||||||
(state.config.cfgBuySpeed.compoundManagers?.tents ?? 0);
|
(state.config.cfgBuySpeed.compoundManagers?.tents ?? 0);
|
||||||
|
if (compoundManagers > 0) {
|
||||||
|
inc.compoundManagers = compoundManagers;
|
||||||
|
}
|
||||||
|
return inc;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ class DebugRenderer implements IRenderer {
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
resource.cost !== undefined &&
|
resource.cost !== undefined &&
|
||||||
Object.keys(resource.cost).length !== 0
|
Object.keys(resource.cost(state)).length !== 0
|
||||||
) {
|
) {
|
||||||
content += "<br>Cost: <span class='resource-cost'></span>";
|
content += "<br>Cost: <span class='resource-cost'></span>";
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,6 @@ class DebugRenderer implements IRenderer {
|
||||||
resContainer.appendChild(el);
|
resContainer.appendChild(el);
|
||||||
if (resource.userActions !== undefined) {
|
if (resource.userActions !== undefined) {
|
||||||
for (let i = 0; i < resource.userActions.length; i++) {
|
for (let i = 0; i < resource.userActions.length; i++) {
|
||||||
const action = resource.userActions[i];
|
|
||||||
const btn = document.getElementById(`resource-btn-${rkey}-${i}`);
|
const btn = document.getElementById(`resource-btn-${rkey}-${i}`);
|
||||||
btn?.addEventListener('click', (): void => {
|
btn?.addEventListener('click', (): void => {
|
||||||
state.performAction(rkey, i);
|
state.performAction(rkey, i);
|
||||||
|
@ -135,13 +134,17 @@ class DebugRenderer implements IRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const inc =
|
if (resource.inc !== undefined) {
|
||||||
resource.inc !== undefined ? resource.inc(state) : undefined;
|
const resInc = resource.inc(state);
|
||||||
|
const inc = resourceNumberSum(resInc);
|
||||||
const elI = el.getElementsByClassName('resource-inc')[0];
|
const elI = el.getElementsByClassName('resource-inc')[0];
|
||||||
if (inc !== undefined && inc !== 0) {
|
if (inc !== 0) {
|
||||||
elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`;
|
elI.innerHTML = ` ${inc > 0 ? '+' : ''}${formatNumber(inc)}/s`;
|
||||||
|
elI.setAttribute('title', this._getIncDetails(resource, state));
|
||||||
} else if (elI.innerHTML !== '') {
|
} else if (elI.innerHTML !== '') {
|
||||||
elI.innerHTML = '';
|
elI.innerHTML = '';
|
||||||
|
elI.removeAttribute('title');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this._handleClick) {
|
if (this._handleClick) {
|
||||||
const elC = el.getElementsByClassName('resource-cost');
|
const elC = el.getElementsByClassName('resource-cost');
|
||||||
|
@ -177,17 +180,37 @@ class DebugRenderer implements IRenderer {
|
||||||
|
|
||||||
private _getCostStr(resource: IResource, state: GameState): string {
|
private _getCostStr(resource: IResource, state: GameState): string {
|
||||||
let cost = '';
|
let cost = '';
|
||||||
|
if (resource.cost === undefined) return cost;
|
||||||
for (const rkey of state.resources) {
|
for (const rkey of state.resources) {
|
||||||
if (resource.cost?.[rkey] !== undefined) {
|
const rcost = resource.cost(state)[rkey];
|
||||||
|
if (rcost !== undefined && rcost > 0) {
|
||||||
if (cost !== '') cost += ', ';
|
if (cost !== '') cost += ', ';
|
||||||
if (rkey === ResourceKey.money) {
|
if (rkey === ResourceKey.money) {
|
||||||
cost += `$${formatNumber(resource.cost[rkey] ?? 0)}`;
|
cost += `$${formatNumber(rcost)}`;
|
||||||
} else {
|
} else {
|
||||||
cost += `${formatNumber(resource.cost[rkey] ?? 0)}
|
cost += `${formatNumber(rcost)} ${
|
||||||
${state.resource[rkey]?.pluralName ?? rkey}`;
|
(rcost > 1
|
||||||
|
? state.resource[rkey]?.pluralName
|
||||||
|
: state.resource[rkey]?.singularName) ?? rkey
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getIncDetails(resource: IResource, state: GameState): string {
|
||||||
|
let inc = '';
|
||||||
|
if (resource.inc === undefined) return inc;
|
||||||
|
for (const rkey of state.resources) {
|
||||||
|
const incRes = state.resource[rkey];
|
||||||
|
if (incRes === undefined || incRes.label === undefined) continue;
|
||||||
|
const rinc = resource.inc(state)[rkey];
|
||||||
|
if (rinc !== undefined && rinc !== 0) {
|
||||||
|
if (inc !== '') inc += '\n';
|
||||||
|
inc += `${incRes.label}: ${rinc > 0 ? '+' : ''}${formatNumber(rinc)}/s`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue