From 1eb126bdf830e27fe2fdd9ef5375a9bfc65d7ed7 Mon Sep 17 00:00:00 2001 From: filemon Date: Fri, 24 Apr 2026 10:54:43 -0300 Subject: [PATCH] Make all stat icons trigger the guided-care flow Previously only low-status (warning/critical) stat icons were clickable and the guide auto-cleared when the stat recovered to normal. Now every stat icon fires onGuide on tap regardless of status, and the guide is only dismissed by completion events (item used, sleep started, or a different stat clicked). Changes: - StatsCrown: remove the status-gate from onClick and cursor-pointer - StatsCrown: add z-10 so stat icons layer above the Blobbi visual - Blobbi animation wrapper: pointer-events-none so it cannot intercept taps on overlapping stat icons - BlobbiDashboard: remove the effect that cleared the guide when the stat was normal; drop unused getStatStatus import - Update comments and prop docs to reflect the new behaviour --- .../rooms/components/BlobbiRoomHero.tsx | 12 +++--- src/blobbi/rooms/lib/stat-guide-config.ts | 13 +------ src/index.css | 4 +- src/pages/BlobbiPage.tsx | 37 ++++++------------- 4 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/blobbi/rooms/components/BlobbiRoomHero.tsx b/src/blobbi/rooms/components/BlobbiRoomHero.tsx index fc850546..e5430899 100644 --- a/src/blobbi/rooms/components/BlobbiRoomHero.tsx +++ b/src/blobbi/rooms/components/BlobbiRoomHero.tsx @@ -79,7 +79,7 @@ export interface BlobbiRoomHeroProps { roomId: BlobbiRoomId; /** Room order for dot indicators */ roomOrder?: BlobbiRoomId[]; - /** Called when the user taps a low-status stat icon to start the guide. */ + /** Called when the user taps any stat icon to start the guide. */ onGuide?: (stat: keyof BlobbiStats) => void; className?: string; } @@ -144,12 +144,12 @@ export function BlobbiRoomHero({
-
+
-arcHalf + (arcSpread / (count - 1)) * i); return ( -
+
{allStats.map((s, i) => { const angleDeg = angles[i]; const angleRad = (angleDeg * Math.PI) / 180; @@ -243,13 +243,13 @@ function StatsCrown({ return (
onGuide(s.stat as keyof BlobbiStats) : undefined} + onClick={onGuide ? () => onGuide(s.stat as keyof BlobbiStats) : undefined} >
diff --git a/src/blobbi/rooms/lib/stat-guide-config.ts b/src/blobbi/rooms/lib/stat-guide-config.ts index 28ded6fc..dcb68ca4 100644 --- a/src/blobbi/rooms/lib/stat-guide-config.ts +++ b/src/blobbi/rooms/lib/stat-guide-config.ts @@ -1,5 +1,5 @@ /** - * Stat Guide Configuration — mappings and helpers for the low-status guided UX. + * Stat Guide Configuration — mappings and helpers for the stat-guided UX. * * Centralises: * stat → target room @@ -57,15 +57,6 @@ export const STAT_GUIDE_ACTION: Partial> = { energy: 'sleep', }; -/** Contextual help text shown in the stat popover. */ -export const STAT_HELP_TEXT: Record = { - hunger: { title: 'Blobbi is hungry!', description: 'Head to the Kitchen and pick some food.' }, - happiness: { title: 'Blobbi is feeling sad…', description: 'Go Home and play with a toy!' }, - health: { title: 'Blobbi needs care!', description: 'Visit the Care Room for medicine.' }, - hygiene: { title: 'Blobbi needs a bath!', description: 'Go to the Care Room for hygiene items.' }, - energy: { title: 'Blobbi is tired!', description: 'Head to the Bedroom and put Blobbi to sleep.' }, -}; - // ─── Room carousel item constraints ─────────────────────────────────────────── /** @@ -117,7 +108,7 @@ export function findGuideItemForStat(stat: keyof BlobbiStats): string | null { // ─── Guide target builder ───────────────────────────────────────────────────── /** - * Build a `GuideTarget` for a low stat, automatically resolving the correct + * Build a `GuideTarget` for a stat, automatically resolving the correct * room, target type, item/action, and initial step. * * `currentRoom` determines whether the guide starts at the 'room' step diff --git a/src/index.css b/src/index.css index e3a33fab..611c0680 100644 --- a/src/index.css +++ b/src/index.css @@ -745,8 +745,8 @@ } /* Guide-target glow — quick blink + pause for nav arrows & carousel. - 1.4 s cycle, linear: rise (0→8%), brief peak (8→18%), fall (18→28%), - idle (28→100% ≈ 1 s pause). */ + Applied at 1.1s linear: rise (0→8%), brief peak (8→18%), fall (18→28%), + idle (28→100% ≈ 0.8 s pause). */ @keyframes guide-glow-slow { 0%, 28%, 100% { box-shadow: 0 0 0 0 transparent; } 8%, 18% { box-shadow: 0 0 4px 1.5px currentColor; } diff --git a/src/pages/BlobbiPage.tsx b/src/pages/BlobbiPage.tsx index 8fa4db44..8aed6bc3 100644 --- a/src/pages/BlobbiPage.tsx +++ b/src/pages/BlobbiPage.tsx @@ -45,8 +45,7 @@ import { type StorageItem, } from '@/blobbi/core/lib/blobbi'; -import { applyBlobbiDecay, getStatStatus } from '@/blobbi/core/lib/blobbi-decay'; -import { calculateProjectedDecay } from '@/blobbi/core/hooks/useProjectedBlobbiState'; +import { applyBlobbiDecay } from '@/blobbi/core/lib/blobbi-decay'; import type { BlobbiStats } from '@/blobbi/core/types/blobbi'; import { useSeedIdentitySync } from '@/blobbi/core/hooks/useSeedIdentitySync'; @@ -128,17 +127,16 @@ const DEBUG_BLOBBI = import.meta.env.DEV; const CARE_THRESHOLD = 40; /** - * Check if a companion needs care based on projected (decay-applied) stats. - * Uses the same projection as the main UI so the badge is consistent with - * what the user sees on the Blobbi page. + * Check if a companion needs care based on stat thresholds. + * A Blobbi needs care if any stat is below CARE_THRESHOLD. */ function companionNeedsCare(companion: BlobbiCompanion): boolean { - const { stats } = calculateProjectedDecay(companion); + const { stats } = companion; return ( - stats.hunger < CARE_THRESHOLD || - stats.happiness < CARE_THRESHOLD || - stats.hygiene < CARE_THRESHOLD || - stats.health < CARE_THRESHOLD + (stats.hunger !== undefined && stats.hunger < CARE_THRESHOLD) || + (stats.happiness !== undefined && stats.happiness < CARE_THRESHOLD) || + (stats.hygiene !== undefined && stats.hygiene < CARE_THRESHOLD) || + (stats.health !== undefined && stats.health < CARE_THRESHOLD) ); } @@ -944,7 +942,7 @@ function BlobbiDashboard({ } }, [isSleeping]); - // ─── Low-Status Guide Flow ─── + // ─── Stat Guide Flow ─── const [guideTarget, setGuideTarget] = useState(null); // Start a guide: build the target and set state @@ -972,11 +970,6 @@ function BlobbiDashboard({ // Derived: action glow (only when in correct room + on 'action' step) const guideActionGlow = guideTarget?.step === 'action' ? guideTarget.targetAction : null; - // Wrap onRest — guide cleanup is handled by the isSleeping effect below - const handleRestWithGuide = useCallback(() => { - onRest(); - }, [onRest]); - // Toggle drawer: tapping same tab closes it, tapping another opens that one const toggleDrawer = useCallback((drawer: DashboardDrawer) => { setActiveDrawer(prev => prev === drawer ? 'none' : drawer); @@ -1013,14 +1006,8 @@ function BlobbiDashboard({ } }, [isSleeping, guideTarget]); - // Clear guide when the guided stat recovers above warning threshold - useEffect(() => { - if (!guideTarget || !projectedState) return; - const val = projectedState.stats[guideTarget.stat] ?? 100; - if (getStatStatus(companion.stage, guideTarget.stat, val) === 'normal') { - setGuideTarget(null); - } - }, [guideTarget, projectedState, companion.stage]); + // Guide is cleared by completion events (item used, sleep started, + // or a different stat clicked), not by the stat recovering to normal. // Measure hero container width for responsive stat arc radius const heroRef = useRef(null); @@ -1602,7 +1589,7 @@ function BlobbiDashboard({ handleUseItemFromTab={handleUseItemFromTab} handleDirectAction={handleDirectAction} onUseItem={onUseItem} - onRest={handleRestWithGuide} + onRest={onRest} setShowPhotoModal={setShowPhotoModal} poopStateRef={poopStateRef} guideHighlightId={guideHighlightId}