feat(sprint1-step2): core economy TS + useEconomy hook (lazy calc) + 13 tests vitest
This commit is contained in:
93
Frontend/src/hooks/useEconomy.ts
Normal file
93
Frontend/src/hooks/useEconomy.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user