feat(sprint1-step2): core economy TS + useEconomy hook (lazy calc) + 13 tests vitest

This commit is contained in:
2026-03-17 06:36:51 +01:00
parent c414cf2d07
commit c69da320cc
13 changed files with 2627 additions and 174 deletions

View File

@@ -0,0 +1,93 @@
// useEconomy.ts — Hook React avec lazy calculation + localStorage
// Pas de setInterval pour les gains passifs — tout est calculé au read
import { useState, useCallback, useEffect } from "react";
import {
GameState,
DEFAULT_STATE,
applyIdleGains,
applyClick,
buyGenerator,
applyPrestige,
canPrestige,
totalProductionPerSecond,
generatorCost,
} from "../core/economy";
const SAVE_KEY = "clickerz_state";
function loadState(): GameState {
try {
const raw = localStorage.getItem(SAVE_KEY);
if (!raw) return { ...DEFAULT_STATE, lastTick: Date.now() };
const saved = JSON.parse(raw) as GameState;
// Appliquer les gains idle accumulés pendant l'absence
return applyIdleGains(saved, Date.now());
} catch {
return { ...DEFAULT_STATE, lastTick: Date.now() };
}
}
function saveState(state: GameState): void {
localStorage.setItem(SAVE_KEY, JSON.stringify(state));
}
export function useEconomy() {
const [state, setState] = useState<GameState>(loadState);
// Auto-save + tick UI toutes les secondes (pour rafraîchir l'affichage uniquement)
// La vraie valeur est calculée lazily dans totalProductionPerSecond
useEffect(() => {
const id = setInterval(() => {
setState((prev) => {
const updated = applyIdleGains(prev, Date.now());
saveState(updated);
return updated;
});
}, 1000);
return () => clearInterval(id);
}, []);
const click = useCallback(() => {
setState((prev) => {
const updated = applyClick(applyIdleGains(prev, Date.now()));
saveState(updated);
return updated;
});
}, []);
const buy = useCallback((genId: string) => {
setState((prev) => {
const withIdle = applyIdleGains(prev, Date.now());
const updated = buyGenerator(withIdle, genId);
if (!updated) return prev;
saveState(updated);
return updated;
});
}, []);
const prestige = useCallback(() => {
setState((prev) => {
if (!canPrestige(prev)) return prev;
const updated = applyPrestige(prev);
saveState(updated);
return updated;
});
}, []);
const reset = useCallback(() => {
const fresh = { ...DEFAULT_STATE, lastTick: Date.now() };
saveState(fresh);
setState(fresh);
}, []);
return {
state,
click,
buy,
prestige,
canPrestige: canPrestige(state),
productionPerSecond: totalProductionPerSecond(state),
generatorCost,
};
}