fix: câbler tous les effets arbre + cleanup dette Sprint 2

- double_click_chance + crit_click_chance câblés dans applyClick (RNG)
- auto_click câblé dans le tick (auto-pontes/s)
- unlock_generator (Résilience) → 1 Lac Mystique gratuit au prestige
- ponte_critique requires double_ponte (fix branche morte)
- achievement_scaling retiré (nœud absent), full_tree + symbiose fixés
- Particule feedback coloré (crit=ambre, double=violet)
- 99 tests (tous passent)
This commit is contained in:
2026-03-28 12:41:12 +01:00
parent 2a242e97cc
commit 2c924c1e4a
6 changed files with 185 additions and 30 deletions

View File

@@ -8,6 +8,8 @@ import {
DEFAULT_STATE,
applyIdleGains,
applyClick,
getClickGain,
getAutoClicksPerSecond,
buyGenerator,
buyEvolutionNode,
resetEvolutionTree,
@@ -66,6 +68,11 @@ interface GameStore {
offlineReport: OfflineReport | null;
dismissOfflineReport: () => void;
// Last click result (for particle feedback)
lastClickGain: number;
lastClickDouble: boolean;
lastClickCrit: boolean;
// Derived (recalculated on tick)
canPrestige: boolean;
productionPerSecond: number;
@@ -135,6 +142,9 @@ export const useGameStore = create<GameStore>((set, get) => ({
playSeconds: 0,
ready: false,
offlineReport: null,
lastClickGain: 0,
lastClickDouble: false,
lastClickCrit: false,
canPrestige: false,
productionPerSecond: 0,
@@ -147,6 +157,14 @@ export const useGameStore = create<GameStore>((set, get) => ({
const updated = applyIdleGains(s.state, now);
updated.lastOnline = now;
// Auto-click from evolution tree
const autoClicks = getAutoClicksPerSecond(updated.evolutionTree);
if (autoClicks > 0) {
const autoGain = getClickGain(updated) * autoClicks;
updated.resources += autoGain;
updated.lifetimeTadpoles += autoGain;
}
// Check cosmetic unlocks every 5s
if (s.playSeconds % 5 === 0) {
const cosState = { inventory: updated.cosmeticInventory, equipped: updated.cosmeticEquipped };
@@ -170,12 +188,15 @@ export const useGameStore = create<GameStore>((set, get) => ({
click: () => {
if (!get().ready) return;
set((s) => {
const updated = applyClick(applyIdleGains(s.state, Date.now()));
saveLocal(updated);
const result = applyClick(applyIdleGains(s.state, Date.now()));
saveLocal(result.state);
return {
state: updated,
canPrestige: canPrestigeCheck(updated),
productionPerSecond: totalProductionPerSecond(updated),
state: result.state,
lastClickGain: result.gain,
lastClickDouble: result.isDouble,
lastClickCrit: result.isCrit,
canPrestige: canPrestigeCheck(result.state),
productionPerSecond: totalProductionPerSecond(result.state),
};
});
},