Files
Aquarium/src/game/economy.ts
T
kamilkrzywda eb50f3b56b feat: game state, persistence, UI framework (Alpine+Tailwind), economy system (#10, #11, #12)
- Game state with localStorage save/load + reset (type DELETE to confirm)
- Alpine.js + Tailwind CSS + Lucide UI overlay (HUD bar, shop modal, settings, toasts)
- Economy: fish prices, tank tier costs/capacities, rarity multipliers, sell value formula
- Genetic system: hue/saturation/size/speed/pattern/rarity/gender + breeding inheritance
- Fish selection via raycaster (click fish = select, click empty = drop food)
- Buy/Sell/Breed/Upgrade actions wired to game state
2026-10-07 12:38:18 +00:00

98 lines
3.2 KiB
TypeScript

import type { FishData, FishSpecies, Rarity } from "./types";
import { RARITY_MULT } from "./types";
/** Base prices for buying fish (shop) */
export const FISH_PRICES: Record<FishSpecies, number> = {
angelfish: 100,
goldfish: 80,
guppy: 60,
fish1: 50,
fish2: 70,
fish3: 65,
};
/** Cost to upgrade TO tank tier (index = target tier) */
export const TANK_TIER_COSTS: number[] = [0, 500, 1500, 4000];
/** Tank tier capacities */
export const TANK_CAPACITY: number[] = [12, 20, 30, 45];
/** Tank tier names */
export const TANK_TIER_NAMES: string[] = ["Small", "Medium", "Large", "XL"];
/** Tank tier dimensions */
export const TANK_TIER_DIMS: { w: number; h: number; d: number; camZ: number; cap: number }[] = [
{ w: 4, h: 2.5, d: 3, camZ: 6, cap: 12 },
{ w: 6, h: 3.5, d: 4, camZ: 8, cap: 20 },
{ w: 8, h: 4.5, d: 5, camZ: 10, cap: 30 },
{ w: 10, h: 5, d: 6, camZ: 12, cap: 45 },
];
/** Food prices */
export const FOOD_PRICES: Record<string, number> = {
basic: 10,
premium: 30,
special: 75,
};
/** Decor prices */
export const DECOR_PRICES: Record<string, number> = {
plant0: 25,
plant1: 40,
rock0: 15,
rock1: 20,
rock2: 20,
};
/**
* Calculate sell value of a fish.
* Formula: basePrice * 0.6 * sizeMult * rarityMult * ageMult
*/
export function getFishSellValue(fish: FishData): number {
const base = FISH_PRICES[fish.species] || 50;
const ageMult = getAgeMultiplier(fish.age);
const value = base * 0.6 * fish.genetics.sizeMult * RARITY_MULT[fish.genetics.rarity] * ageMult;
return Math.round(value);
}
/** Age-based value multiplier */
function getAgeMultiplier(ageHours: number): number {
if (ageHours < 24) return 0.5;
if (ageHours < 72) return 0.8;
return 1.0;
}
/** Get the display value breakdown for UI */
export function getFishValueBreakdown(fish: FishData): { label: string; value: number }[] {
const base = FISH_PRICES[fish.species] || 50;
return [
{ label: `Base (${fish.species})`, value: base },
{ label: `Size (${fish.genetics.sizeMult.toFixed(2)}x)`, value: Math.round(base * 0.6 * fish.genetics.sizeMult) },
{ label: `Rarity (${fish.genetics.rarity}, ${RARITY_MULT[fish.genetics.rarity]}x)`, value: Math.round(base * 0.6 * fish.genetics.sizeMult * RARITY_MULT[fish.genetics.rarity]) },
{ label: "Total", value: getFishSellValue(fish) },
];
}
/** Species rarity distributions for shop rolls */
export const SPECIES_RARITY_DIST: Record<FishSpecies, Record<Rarity, number>> = {
angelfish: { common: 60, uncommon: 25, rare: 10, epic: 4, legendary: 1 },
goldfish: { common: 65, uncommon: 20, rare: 9, epic: 5, legendary: 1 },
guppy: { common: 70, uncommon: 18, rare: 7, epic: 4, legendary: 1 },
fish1: { common: 75, uncommon: 15, rare: 6, epic: 3, legendary: 1 },
fish2: { common: 65, uncommon: 20, rare: 10, epic: 4, legendary: 1 },
fish3: { common: 68, uncommon: 18, rare: 9, epic: 4, legendary: 1 },
};
/** Available species in shop */
export const SHOP_SPECIES: FishSpecies[] = ["angelfish", "goldfish", "guppy", "fish1", "fish2", "fish3"];
/** Species display names */
export const SPECIES_NAMES: Record<FishSpecies, string> = {
angelfish: "Angelfish",
goldfish: "Goldfish",
guppy: "Guppy",
fish1: "Cute Fish I",
fish2: "Cute Fish II",
fish3: "Cute Fish III",
};