Replaces the plain bottom SnackBar with a BoosterHint pill that floats in
the empty space above the board: season-accent coloured, a breathing glow
that pulses only while armed (idle otherwise — no wasted ticker), slides/
fades in, shows the booster icon + prompt, and cancels on tap. 3 widget
tests; full suite 234 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- daily claim: record the claim before granting boosters, so a crash
mid-claim forfeits at most one reward instead of allowing a re-claim
(booster farming) on next launch.
- game screen: disarm the booster target synchronously before awaiting,
so a rapid second board tap can't double-fire a use or stack a dialog.
- new players: seed one of each booster once (idempotent persisted flag),
fulfilling the spec's starting inventory. Wired in main().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The line-bomb axis chooser hardcoded Korean 가로/세로; route them through
new boosterLineRow/boosterLineCol keys (EN: Row/Column).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tapping an empty booster opens the get-one dialog; confirming watches a
rewarded ad and, on earn, grants +1 of that booster and logs booster_granted
(source: ad). Covered by a widget test using an uninitialized AdService whose
showRewarded() resolves true.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mount BoosterBar below the tray (only while playing), guarded so legacy
GameScreen tests without a SaveRepository keep passing. Tapping a booster
arms targeting: shuffle applies immediately; hammer/line-bomb arm a board
tap (hammer clears a cell, line-bomb opens a row/column chooser). An empty
booster opens a get-one dialog (ad grant lands in Task 15).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an in-app review prompt gated by ReviewPromptPolicy: only after a
3-star stage win, once the player has cleared >=5 stages, at most once
ever (persisted reviewRequested flag). ReviewService swallows all
failures and only burns the one-shot when the store actually shows the
sheet, so an unavailable store retries on a later win. StoreReviewer
wraps in_app_review behind a Reviewer seam so unit tests skip platform
channels. 13 new tests; full suite 194 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On device the floating top-left X (48px IconButton) overlapped the HUD moves
chip. Move it into the column flow above the HUD, left-aligned, so it never
collides. Found via on-device test build.
Expose engine.rescueUsed getter and surface it through GameViewState so
the result overlay can omit the watch-ad FilledButton after a rescue has
been consumed, preventing a second tap from calling useContinue /
addExtraMoves and hitting their StateError guard. Give-up is promoted to
FilledButton when rescue is unavailable for clear affordance.
Also emit stageStart / endlessStart analytics in restart() so every
attempt (not just the first) is bracketed by a matching start event.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add SeasonFlowNotifier.clear() to null out the flow state
- Call clear() in HomeScreen's Classic button before startStage()
- Broaden tutorial-end guard to next.phase != GamePhase.playing (covers stuck)
- Add regression test: clear() resets flow to null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A finished stage ends the tutorial; otherwise the overlay would sit
on top of the result card and leak into the next stage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add TutorialOverlay widget (dim veil, message bubble, animated hand on
dragPiece step, skip button) and wire it into game_screen: start on
flow.index==0 when tutorialDone is false, forward onPlaced/onLineCleared
events unconditionally from fxTick handler, and compute hand-path
coordinates from board/tray RenderBox geometry.
- Add ValueKey(i) to each TweenAnimationBuilder in the star animation loop
to prevent Flutter from reusing widget state during rebuild cycles
- Add maxLines: 2 and overflow: TextOverflow.ellipsis to the almostThere
Text widget to prevent overflow in the loss-state progress message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stars on win now appear sequentially with elastic-bounce via
TweenAnimationBuilder (400/650/900 ms). Lost overlay shows a
CircularProgressIndicator ring with "87% complete!" (l10n:
almostThere) when objectiveProgress > 0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure advanceStreak (1-day grace none, milestone flags at 3/7/14/30),
persisted in the save blob; StreakNotifier advances on every finished
attempt; home screen flame chip and milestone snackbar. Fix flutter
create's camelCased iOS bundle id and underscored Android application
id to the agreed lowercase form before any store registration.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SaveRepository (versioned JSON over prefs) with best-result merging and
unlock walking; ContentRepository loads the bundled pack; SeasonFlow/
Progress notifiers orchestrate stage start -> win record -> advance.
Season map grid with stars/locks, Home -> Map -> Game navigation,
close button, next-stage action on the win overlay.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure-Dart WAV synthesizer (tool/gen_sfx.dart) generates place/clear/
combo/win/lose effects; AudioService player pool fires on placement,
line clears, combo streaks, and phase transitions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CustomPainter board with gems/ghost/clear-flash, finger-lifted drag
with snap preview, combo text effect, HUD chips, phase overlays with
rescue stubs, demo stage. E2E widget test drives a real drag gesture.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>