Files
game-forest/src/game/upgrades.ts
T

132 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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));
}