feat(sprint1-step5): migration Tailwind v4 + Zustand — suppression WildCoinContext
- Install tailwindcss @tailwindcss/vite zustand - useGameStore.ts : Zustand store wrappant economy.ts (tick, click, buy, prestige, buyNode, loadFromServer) - GameTick.tsx : composant timer 1s - GeneratorShop.tsx : boutique générateurs Tailwind (remplace Amelioration.jsx) - EvolutionTree, PrestigePanel, MilestoneBar : rewrite Zustand + Tailwind - Hud.jsx : rewrite Zustand + Tailwind (suppression Hud.scss) - BoutiqueCard, Achievements : migrés vers Zustand - Supprimé : WildCoin/ (4 fichiers), timer/Timer.jsx, useEconomy.ts, Hud.scss - WildCoinProvider retiré de main.jsx
This commit is contained in:
146
Frontend/src/store/useGameStore.ts
Normal file
146
Frontend/src/store/useGameStore.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
// useGameStore.ts — Zustand store, source unique de l'état game
|
||||
// Lazy calculation pattern : gains passifs calculés au read depuis lastTick
|
||||
|
||||
import { create } from "zustand";
|
||||
import {
|
||||
GameState,
|
||||
DEFAULT_STATE,
|
||||
applyIdleGains,
|
||||
applyClick,
|
||||
buyGenerator,
|
||||
buyEvolutionNode,
|
||||
applyPrestige,
|
||||
canPrestige as canPrestigeCheck,
|
||||
totalProductionPerSecond,
|
||||
generatorCost as genCost,
|
||||
} 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;
|
||||
return applyIdleGains(saved, Date.now());
|
||||
} catch {
|
||||
return { ...DEFAULT_STATE, lastTick: Date.now() };
|
||||
}
|
||||
}
|
||||
|
||||
function saveState(state: GameState): void {
|
||||
localStorage.setItem(SAVE_KEY, JSON.stringify(state));
|
||||
}
|
||||
|
||||
interface GameStore {
|
||||
// State
|
||||
state: GameState;
|
||||
playSeconds: number;
|
||||
|
||||
// Derived (recalculated on tick)
|
||||
canPrestige: boolean;
|
||||
productionPerSecond: number;
|
||||
|
||||
// Actions
|
||||
tick: () => void;
|
||||
click: () => void;
|
||||
buy: (genId: string) => void;
|
||||
buyNode: (nodeId: string) => void;
|
||||
prestige: () => void;
|
||||
reset: () => void;
|
||||
loadFromServer: (serverState: GameState) => void;
|
||||
generatorCost: typeof genCost;
|
||||
}
|
||||
|
||||
export const useGameStore = create<GameStore>((set, get) => ({
|
||||
state: loadState(),
|
||||
playSeconds: 0,
|
||||
canPrestige: canPrestigeCheck(loadState()),
|
||||
productionPerSecond: totalProductionPerSecond(loadState()),
|
||||
|
||||
tick: () => {
|
||||
set((s) => {
|
||||
const updated = applyIdleGains(s.state, Date.now());
|
||||
saveState(updated);
|
||||
return {
|
||||
state: updated,
|
||||
playSeconds: s.playSeconds + 1,
|
||||
canPrestige: canPrestigeCheck(updated),
|
||||
productionPerSecond: totalProductionPerSecond(updated),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
click: () => {
|
||||
set((s) => {
|
||||
const updated = applyClick(applyIdleGains(s.state, Date.now()));
|
||||
saveState(updated);
|
||||
return {
|
||||
state: updated,
|
||||
canPrestige: canPrestigeCheck(updated),
|
||||
productionPerSecond: totalProductionPerSecond(updated),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
buy: (genId: string) => {
|
||||
set((s) => {
|
||||
const withIdle = applyIdleGains(s.state, Date.now());
|
||||
const updated = buyGenerator(withIdle, genId);
|
||||
if (!updated) return s;
|
||||
saveState(updated);
|
||||
return {
|
||||
state: updated,
|
||||
productionPerSecond: totalProductionPerSecond(updated),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
buyNode: (nodeId: string) => {
|
||||
set((s) => {
|
||||
const updated = buyEvolutionNode(s.state, nodeId);
|
||||
if (!updated) return s;
|
||||
saveState(updated);
|
||||
return {
|
||||
state: updated,
|
||||
productionPerSecond: totalProductionPerSecond(updated),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
prestige: () => {
|
||||
set((s) => {
|
||||
if (!canPrestigeCheck(s.state)) return s;
|
||||
const updated = applyPrestige(s.state);
|
||||
saveState(updated);
|
||||
return {
|
||||
state: updated,
|
||||
canPrestige: canPrestigeCheck(updated),
|
||||
productionPerSecond: totalProductionPerSecond(updated),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
reset: () => {
|
||||
const fresh = { ...DEFAULT_STATE, lastTick: Date.now() };
|
||||
saveState(fresh);
|
||||
set({
|
||||
state: fresh,
|
||||
playSeconds: 0,
|
||||
canPrestige: false,
|
||||
productionPerSecond: 0,
|
||||
});
|
||||
},
|
||||
|
||||
loadFromServer: (serverState: GameState) => {
|
||||
const hydrated = applyIdleGains(serverState, Date.now());
|
||||
saveState(hydrated);
|
||||
set({
|
||||
state: hydrated,
|
||||
canPrestige: canPrestigeCheck(hydrated),
|
||||
productionPerSecond: totalProductionPerSecond(hydrated),
|
||||
});
|
||||
},
|
||||
|
||||
generatorCost: genCost,
|
||||
}));
|
||||
Reference in New Issue
Block a user