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:
@@ -17,6 +17,7 @@ import {
|
||||
getStartBonusFromTree,
|
||||
getPrestigeDnaBonus,
|
||||
getCostReduction,
|
||||
getAutoClicksPerSecond,
|
||||
offlineEfficiency,
|
||||
computeOfflineGains,
|
||||
DEFAULT_STATE,
|
||||
@@ -173,13 +174,15 @@ describe("computeIdleGains (lazy calculation)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// --- Click ---
|
||||
// --- Click (avec double + crit) ---
|
||||
|
||||
describe("applyClick", () => {
|
||||
it("augmente les ressources du clickMultiplier × prestigeMultiplier", () => {
|
||||
const state = { ...DEFAULT_STATE, clickMultiplier: 3, prestigeMultiplier: 2 };
|
||||
const result = applyClick(state);
|
||||
expect(result.resources).toBe(6);
|
||||
const result = applyClick(state, 0.99); // rng high → no double, no crit
|
||||
expect(result.state.resources).toBe(6);
|
||||
expect(result.isDouble).toBe(false);
|
||||
expect(result.isCrit).toBe(false);
|
||||
});
|
||||
|
||||
it("applique le multiplicateur click de l'arbre", () => {
|
||||
@@ -191,14 +194,58 @@ describe("applyClick", () => {
|
||||
n.id === "ponte_amelioree" ? { ...n, unlocked: true } : n
|
||||
),
|
||||
};
|
||||
const result = applyClick(state);
|
||||
expect(result.resources).toBe(2); // ×2 depuis Ponte Améliorée
|
||||
const result = applyClick(state, 0.99);
|
||||
expect(result.state.resources).toBe(2);
|
||||
});
|
||||
|
||||
it("incrémente lifetimeTadpoles", () => {
|
||||
const state = { ...DEFAULT_STATE, clickMultiplier: 5, prestigeMultiplier: 1 };
|
||||
const result = applyClick(state);
|
||||
expect(result.lifetimeTadpoles).toBe(5);
|
||||
const result = applyClick(state, 0.99);
|
||||
expect(result.state.lifetimeTadpoles).toBe(5);
|
||||
});
|
||||
|
||||
it("double ponte x2 quand rng < doubleClickChance", () => {
|
||||
const state = {
|
||||
...DEFAULT_STATE,
|
||||
clickMultiplier: 1,
|
||||
prestigeMultiplier: 1,
|
||||
evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
|
||||
n.id === "double_ponte" ? { ...n, unlocked: true } : n
|
||||
),
|
||||
};
|
||||
// double_ponte = 10% chance, rng=0.05 < 0.10 → double
|
||||
const result = applyClick(state, 0.05);
|
||||
expect(result.isDouble).toBe(true);
|
||||
expect(result.gain).toBe(2);
|
||||
});
|
||||
|
||||
it("pas de double ponte quand rng > doubleClickChance", () => {
|
||||
const state = {
|
||||
...DEFAULT_STATE,
|
||||
clickMultiplier: 1,
|
||||
evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
|
||||
n.id === "double_ponte" ? { ...n, unlocked: true } : n
|
||||
),
|
||||
};
|
||||
const result = applyClick(state, 0.50);
|
||||
expect(result.isDouble).toBe(false);
|
||||
expect(result.gain).toBe(1);
|
||||
});
|
||||
|
||||
it("crit x10 quand critRng < critClickChance", () => {
|
||||
const state = {
|
||||
...DEFAULT_STATE,
|
||||
clickMultiplier: 1,
|
||||
prestigeMultiplier: 1,
|
||||
evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
|
||||
n.id === "ponte_critique" ? { ...n, unlocked: true } : n
|
||||
),
|
||||
};
|
||||
// ponte_critique = 5% chance, need critRng = (rng * 7.13) % 1 < 0.05
|
||||
// rng = 0.007 → critRng = 0.04991 < 0.05 → crit!
|
||||
const result = applyClick(state, 0.007);
|
||||
expect(result.isCrit).toBe(true);
|
||||
expect(result.gain).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -444,6 +491,46 @@ describe("Evolution Tree (3 branches)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("unlock_generator (Résilience)", () => {
|
||||
it("prestige avec Résilience donne 1 Lac Mystique", () => {
|
||||
const state = {
|
||||
...DEFAULT_STATE,
|
||||
resources: 2_000_000,
|
||||
generators: DEFAULT_STATE.generators.map((g) => ({ ...g, owned: 5 })),
|
||||
evolutionTree: DEFAULT_STATE.evolutionTree.map((n) =>
|
||||
n.id === "resilience" ? { ...n, unlocked: true } : n
|
||||
),
|
||||
};
|
||||
const result = applyPrestige(state);
|
||||
const lac = result.generators.find((g) => g.id === "lac");
|
||||
expect(lac!.owned).toBe(1);
|
||||
});
|
||||
|
||||
it("prestige sans Résilience donne 0 Lac Mystique", () => {
|
||||
const state = {
|
||||
...DEFAULT_STATE,
|
||||
resources: 2_000_000,
|
||||
generators: DEFAULT_STATE.generators.map((g) => ({ ...g, owned: 5 })),
|
||||
};
|
||||
const result = applyPrestige(state);
|
||||
const lac = result.generators.find((g) => g.id === "lac");
|
||||
expect(lac!.owned).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("auto_click (getAutoClicksPerSecond)", () => {
|
||||
it("retourne 0 si auto_ponte non débloqué", () => {
|
||||
expect(getAutoClicksPerSecond(DEFAULT_EVOLUTION_TREE)).toBe(0);
|
||||
});
|
||||
|
||||
it("retourne 1 si auto_ponte débloqué", () => {
|
||||
const tree = DEFAULT_EVOLUTION_TREE.map((n) =>
|
||||
n.id === "auto_ponte" ? { ...n, unlocked: true } : n
|
||||
);
|
||||
expect(getAutoClicksPerSecond(tree)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("prestige threshold reduction", () => {
|
||||
it("Transcendance réduit le seuil de 50%", () => {
|
||||
const state = {
|
||||
|
||||
Reference in New Issue
Block a user