diff --git a/apps/api/src/data/initialState.ts b/apps/api/src/data/initialState.ts index 6ce59f7..e3562a7 100644 --- a/apps/api/src/data/initialState.ts +++ b/apps/api/src/data/initialState.ts @@ -76,6 +76,8 @@ const initialGameState = ( achievements: structuredClone(defaultAchievements), adventurers: structuredClone(defaultAdventurers), apotheosis: { ...initialApotheosis }, + autoBoss: false, + autoQuest: false, baseClickPower: 1, bosses: structuredClone(defaultBosses), companions: { activeCompanionId: null, unlockedCompanionIds: [] }, diff --git a/apps/web/src/components/game/bossPanel.tsx b/apps/web/src/components/game/bossPanel.tsx index d3619cc..c324a61 100644 --- a/apps/web/src/components/game/bossPanel.tsx +++ b/apps/web/src/components/game/bossPanel.tsx @@ -226,7 +226,14 @@ const computePartyStats = ( * @returns The JSX element. */ const BossPanel = (): JSX.Element => { - const { state, challengeBoss, formatNumber, toggleAutoBoss } = useGame(); + const { + state, + challengeBoss, + formatNumber, + toggleAutoBoss, + autoBossLastResult, + autoBossError, + } = useGame(); const [ challengingBossId, setChallengingBossId ] = useState( null, ); @@ -346,6 +353,23 @@ const BossPanel = (): JSX.Element => { + {autoBossError === null + ? null + :

+ {"⚠️ Auto-boss stopped: "} + {autoBossError} +

+ } + {autoBossLastResult !== null && autoBossError === null + ?

+ {"🤖 Last fight: "} + {autoBossLastResult.bossName} + {autoBossLastResult.won + ? " — ✅ Won" + : " — ❌ Lost"} +

+ : null} + Promise; + + /** + * Last auto-boss fight result — null until the first auto fight completes or + * when auto-boss is toggled off. + */ + autoBossLastResult: { bossName: string; won: boolean; at: number } | null; + + /** + * Error message set when auto-boss stopped due to a critical failure (null + * when no error). Cleared automatically when the player re-enables auto-boss. + */ + autoBossError: string | null; } export interface BattleResult { @@ -588,6 +600,12 @@ export const GameProvider = ({ const [ lastSavedAt, setLastSavedAt ] = useState(null); const [ isSyncing, setIsSyncing ] = useState(false); const [ syncError, setSyncError ] = useState(null); + const [ autoBossLastResult, setAutoBossLastResult ] = useState<{ + bossName: string; + won: boolean; + at: number; + } | null>(null); + const [ autoBossError, setAutoBossError ] = useState(null); const syncErrorTimerReference = useRef | null>( null, ); @@ -1059,6 +1077,14 @@ export const GameProvider = ({ }, ); + // Quest failure — turn off auto-quest so the player can reassess + if ( + newlyFailedQuestsReference.current.length > 0 + && next.autoQuest === true + ) { + next = { ...next, autoQuest: false }; + } + return next; }); @@ -1200,14 +1226,32 @@ export const GameProvider = ({ if (previous === null) { return previous; } - return applyBossResult(previous, bossId, result); + const afterBoss = applyBossResult(previous, bossId, result); + // Defeat — turn off auto-boss so the player can reassess + if (!result.won) { + return { ...afterBoss, autoBoss: false }; + } + return afterBoss; + }); + setAutoBossLastResult({ + at: Date.now(), + bossName: bossName, + won: result.won, }); - setBattleResult({ bossName, result }); }). catch((error_: unknown) => { logError("auto_boss", error_); - - /* Silently ignore — will retry next tick */ + const message + = error_ instanceof Error + ? error_.message + : String(error_); + setAutoBossError(message); + setState((previous) => { + if (previous === null) { + return previous; + } + return { ...previous, autoBoss: false }; + }); }). finally(() => { isAutoBossingReference.current = false; @@ -1782,6 +1826,8 @@ export const GameProvider = ({ }, []); const toggleAutoBoss = useCallback(() => { + setAutoBossError(null); + setAutoBossLastResult(null); setState((previous) => { if (previous === null) { return previous; @@ -1974,6 +2020,8 @@ export const GameProvider = ({ const contextValue = useMemo(() => { return { apotheosis, + autoBossError, + autoBossLastResult, battleResult, buyAdventurer, buyEchoUpgrade, @@ -2040,6 +2088,8 @@ export const GameProvider = ({ }; }, [ apotheosis, + autoBossError, + autoBossLastResult, battleResult, completedQuestToasts, failedQuestToasts,