132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
export type UpgradeId =
|
||
| 'moveSpeed'
|
||
| 'chopSpeed'
|
||
| 'logValue'
|
||
| 'twigValue'
|
||
| 'meatValue'
|
||
| 'pickupRadius'
|
||
| 'coinSpeed'
|
||
| 'dayLength'
|
||
| 'maxHealth'
|
||
| 'attackDamage'
|
||
| 'healthRegenSpeed';
|
||
|
||
export interface UpgradeDef {
|
||
id: UpgradeId;
|
||
name: string;
|
||
baseCost: number;
|
||
costGrowth: number;
|
||
/** The upgrade's stat value at a given level (levels start at 1, unbounded above). */
|
||
effectAtLevel: (level: number) => number;
|
||
formatEffect: (value: number, level: number) => string;
|
||
}
|
||
|
||
export const UPGRADE_LIST: UpgradeDef[] = [
|
||
{
|
||
id: 'moveSpeed',
|
||
name: 'Скорость движения',
|
||
baseCost: 5,
|
||
costGrowth: 1.35,
|
||
effectAtLevel: (level) => 160 + (level - 1) * 20,
|
||
formatEffect: (v) => `${Math.round(v)} px/с`,
|
||
},
|
||
{
|
||
id: 'chopSpeed',
|
||
name: 'Скорость рубки',
|
||
baseCost: 6,
|
||
costGrowth: 1.35,
|
||
effectAtLevel: (level) => (1 / 0.6) * Math.pow(1.15, level - 1),
|
||
formatEffect: (v) => `${v.toFixed(2)} бревен/с`,
|
||
},
|
||
{
|
||
id: 'logValue',
|
||
name: 'Стоимость бревна',
|
||
baseCost: 8,
|
||
costGrowth: 1.4,
|
||
effectAtLevel: (level) => level,
|
||
formatEffect: (v) => `${v} монет/бревно`,
|
||
},
|
||
{
|
||
id: 'twigValue',
|
||
name: 'Стоимость ветки',
|
||
baseCost: 5,
|
||
costGrowth: 1.3,
|
||
effectAtLevel: (level) => level * 0.1,
|
||
formatEffect: (v) => `${v.toFixed(2)} монет/ветка`,
|
||
},
|
||
{
|
||
id: 'meatValue',
|
||
name: 'Стоимость мяса',
|
||
baseCost: 9,
|
||
costGrowth: 1.4,
|
||
effectAtLevel: (level) => level,
|
||
formatEffect: (v) => `${v} монет/мясо`,
|
||
},
|
||
{
|
||
id: 'pickupRadius',
|
||
name: 'Радиус подбора',
|
||
baseCost: 5,
|
||
costGrowth: 1.3,
|
||
effectAtLevel: (level) => 46 + (level - 1) * 6,
|
||
formatEffect: (v) => `${Math.round(v)} px`,
|
||
},
|
||
{
|
||
id: 'coinSpeed',
|
||
name: 'Скорость обмена на монеты',
|
||
baseCost: 7,
|
||
costGrowth: 1.35,
|
||
// Seconds to convert one unit of a resource into coins — every held resource type converts
|
||
// on this same cadence at once, so this is the only knob for how fast any of them drain.
|
||
effectAtLevel: (level) => 1 / Math.pow(1.15, level - 1),
|
||
formatEffect: (v) => `${v.toFixed(2)} с`,
|
||
},
|
||
{
|
||
id: 'dayLength',
|
||
name: 'Длина дня',
|
||
baseCost: 6,
|
||
costGrowth: 1.3,
|
||
// Game-hours per real second during morning/day/evening (16 game-hours by default spread
|
||
// over the default 10s day-phase of the 20s day-night cycle) — night has its own fixed speed.
|
||
effectAtLevel: (level) => 16 * Math.pow(1.15, level - 1),
|
||
formatEffect: (v) => `${v.toFixed(0)} с`,
|
||
},
|
||
{
|
||
id: 'maxHealth',
|
||
name: 'Живучесть',
|
||
baseCost: 10,
|
||
costGrowth: 1.45,
|
||
effectAtLevel: (level) => 4 + level,
|
||
formatEffect: (v) => `${v} здоровья`,
|
||
},
|
||
{
|
||
id: 'attackDamage',
|
||
name: 'Сила удара по кролику',
|
||
baseCost: 8,
|
||
costGrowth: 1.4,
|
||
// Damage dealt per hit — the swing cadence itself comes from chopSpeed, shared with
|
||
// wood-chopping, so this is purely how hard each swing lands. Health is allowed to be
|
||
// fractional, so this is left unrounded.
|
||
effectAtLevel: (level) => 0.5 * Math.pow(1.4, level - 1),
|
||
formatEffect: (v) => `${v.toFixed(1)} здоровья`,
|
||
},
|
||
{
|
||
id: 'healthRegenSpeed',
|
||
name: 'Скорость регена здоровья',
|
||
baseCost: 9,
|
||
costGrowth: 1.35,
|
||
// Seconds per regenerated hp — starts at 1 hp per 10s and shrinks (faster regen) with level,
|
||
// same interval-based shape as coinSpeed.
|
||
effectAtLevel: (level) => 10 / Math.pow(1.15, level - 1),
|
||
formatEffect: (v) => `${v.toFixed(1)} с/очко`,
|
||
},
|
||
];
|
||
|
||
export const UPGRADES: Record<UpgradeId, UpgradeDef> = Object.fromEntries(
|
||
UPGRADE_LIST.map((def) => [def.id, def]),
|
||
) as Record<UpgradeId, UpgradeDef>;
|
||
|
||
/** Cost (in coins) to go from `currentLevel` to `currentLevel + 1`. */
|
||
export function costForNextLevel(def: UpgradeDef, currentLevel: number): number {
|
||
return Math.ceil(def.baseCost * Math.pow(def.costGrowth, currentLevel - 1));
|
||
}
|