Commit Graph

26 Commits

Author SHA1 Message Date
airkjw 410182cf7d feat(ui): floating pulse hint for booster targeting
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>
2026-06-18 20:53:52 +09:00
airkjw fa2784519b fix(boosters): address final-review findings
- 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>
2026-06-18 19:36:24 +09:00
airkjw 412cc08167 fix(l10n): localize line-bomb row/column chooser labels
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>
2026-06-18 19:27:41 +09:00
airkjw b8bfa00196 feat(ui): rewarded-ad grant for an empty booster
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>
2026-06-18 12:41:27 +09:00
airkjw 1ba30028b5 feat(ui): booster bar targeting in the game screen
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>
2026-06-18 12:40:13 +09:00
airkjw cec4c3e427 feat(review): request a store review after a 3-star win, once
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>
2026-06-18 11:13:55 +09:00
airkjw 2310aabdb9 fix(ui): move game-screen close button to its own top row (was overlapping HUD)
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.
2026-06-14 09:02:01 +09:00
airkjw 3ca038ec65 feat(settings): soundEnabled provider gates SFX and haptics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:59:08 +09:00
airkjw 1ec59ba80d fix(ads): reset ad round in startStage so nextStage advance counts (review fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:09:02 +09:00
airkjw 297449ccce feat(ads): stage-end interstitial gated by policy; restart resets round 2026-06-13 14:03:52 +09:00
airkjw 3943653a23 feat(ads): rewarded ad gates the continue/extra-moves rescue 2026-06-13 14:02:31 +09:00
airkjw 9763968db9 fix: hide spent rescue to prevent StateError crash; log per-attempt starts
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>
2026-06-12 13:41:59 +09:00
airkjw 074a21ea2b feat: analytics abstraction with debug backend and game event wiring
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:35:31 +09:00
airkjw bf7720ebd3 fix: clear season flow when starting Classic so tutorial/theme can't leak
- 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>
2026-06-12 07:33:41 +09:00
airkjw fea8336391 feat: endless mode UI - game over card, best score, HUD 2026-06-12 07:06:30 +09:00
airkjw ee364cc2e2 fix: dismiss tutorial when the stage ends
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>
2026-06-11 23:11:13 +09:00
airkjw 963d0d5dd6 feat: first-play interactive tutorial overlay
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.
2026-06-11 22:58:14 +09:00
airkjw 944c5733c9 fix: defensive keys and overflow guard in result overlay
- 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>
2026-06-11 22:27:14 +09:00
airkjw 677a09f8cb feat: staggered star reveal and near-miss progress ring
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>
2026-06-11 22:20:29 +09:00
airkjw 8c3c2ae9a9 feat: juice kit - sparks, score popups, combo banners, confetti, haptics, shake
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 22:05:14 +09:00
airkjw 6e4d3b60df feat: procedural season background with drifting petals 2026-06-11 21:17:12 +09:00
airkjw 607278928b Add daily streak system and normalize bundle id to com.airkjw.blockseasons
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>
2026-06-11 17:25:39 +09:00
airkjw 7bc26447f7 Wire season flow: map screen, progress save, win recording, next stage
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>
2026-06-11 17:04:45 +09:00
airkjw ad6689b42f Add synthesized SFX and audio wiring
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>
2026-06-11 13:28:02 +09:00
airkjw 3138fc4b08 Add playable core UI: board painter, drag-and-drop, HUD, result overlay
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>
2026-06-11 13:19:34 +09:00
airkjw 40528238b2 Scaffold Block Seasons Flutter app
flutter create (com.airkjw.blockseasons, iOS+Android), Riverpod,
shared_preferences, audioplayers, gen-l10n EN/KO wiring, app shell
with Home -> Game placeholder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:55:59 +09:00