- 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
98 lines
3.2 KiB
TypeScript
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",
|
|
};
|