diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index dc200a9..0e12c52 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,5 +1,5 @@ # Circular Dependencies -Generated: 2026-06-10T20:56:06.167Z +Generated: 2026-06-10T20:57:50.802Z Found: 3 circular chain(s) — these MUST be fixed before modifying involved files. 1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index ef51e63..49b25a9 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-06-10T20:56:04.043Z", + "generated": "2026-06-10T20:57:48.663Z", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." }, diff --git a/src/lib/game/__tests__/bug-354-unlock-attunement.test.ts b/src/lib/game/__tests__/bug-354-unlock-attunement.test.ts index 4243a02..6f93a23 100644 --- a/src/lib/game/__tests__/bug-354-unlock-attunement.test.ts +++ b/src/lib/game/__tests__/bug-354-unlock-attunement.test.ts @@ -50,8 +50,17 @@ describe('Issue #354 — unlockAttunement ReferenceError', () => { expect(state.attunements['invoker']?.level).toBe(1); }); - it('should return false for fabricator (gating not implemented)', () => { - const result = useAttunementStore.getState().unlockAttunement('fabricator', [10, 20, 30]); + it('should return false for fabricator when floor 20 guardian not defeated', () => { + const result = useAttunementStore.getState().unlockAttunement('fabricator', [10, 30]); expect(result).toBe(false); }); + + it('should unlock fabricator when floor 20 guardian is defeated', () => { + const result = useAttunementStore.getState().unlockAttunement('fabricator', [10, 20, 30]); + expect(result).toBe(true); + + const state = useAttunementStore.getState(); + expect(state.attunements['fabricator']?.active).toBe(true); + expect(state.attunements['fabricator']?.level).toBe(1); + }); }); diff --git a/src/lib/game/stores/attunementStore.ts b/src/lib/game/stores/attunementStore.ts index 3692082..bbd21f9 100644 --- a/src/lib/game/stores/attunementStore.ts +++ b/src/lib/game/stores/attunementStore.ts @@ -88,8 +88,8 @@ export const useAttunementStore = create()( // Invoker requires defeating the first guardian (floor 10) if (!defeatedGuardians.includes(10)) return false; } else if (attunementId === 'fabricator') { - // Fabricator: no specific gating condition implemented - return false; + // Fabricator requires defeating the second guardian (floor 20) + if (!defeatedGuardians.includes(20)) return false; } else { // Unknown attunement — don't unlock return false; diff --git a/src/lib/game/stores/pipelines/combat-tick.ts b/src/lib/game/stores/pipelines/combat-tick.ts index 1eae53f..efdd969 100644 --- a/src/lib/game/stores/pipelines/combat-tick.ts +++ b/src/lib/game/stores/pipelines/combat-tick.ts @@ -130,6 +130,15 @@ export function buildCombatCallbacks(params: BuildCombatCallbacksParams) { params.addLog('💜 The path of the Invoker is now available!'); } } + + // Auto-unlock Fabricator when the second guardian (floor 20) is defeated + if (floor === 20) { + const prestigeState = usePrestigeStore.getState(); + const unlocked = useAttunementStore.getState().unlockAttunement('fabricator', prestigeState.defeatedGuardians); + if (unlocked) { + params.addLog('⚒️ The path of the Fabricator is now available!'); + } + } } else if (floor % 5 === 0) { params.addLog('Floor ' + floor + ' cleared!'); }