From 9f1e0d2cd5697da4d4dfcc24394e95984c903df3 Mon Sep 17 00:00:00 2001 From: airkjw Date: Thu, 18 Jun 2026 11:50:54 +0900 Subject: [PATCH] docs(spec): boosters & daily reward design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brainstormed design for a lightweight booster economy (hammer/shuffle/ line-bomb) earned via a 7-day login calendar and rewarded ads, used in a stage via a booster bar. Boosters mutate the grid only — no move cost, no score/combo, no objective credit — so stage balance is preserved while they can rescue a dead board. Approved by owner; next: impl plan. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...2026-06-18-boosters-daily-reward-design.md | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-18-boosters-daily-reward-design.md diff --git a/docs/superpowers/specs/2026-06-18-boosters-daily-reward-design.md b/docs/superpowers/specs/2026-06-18-boosters-daily-reward-design.md new file mode 100644 index 0000000..ebe28de --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-boosters-daily-reward-design.md @@ -0,0 +1,141 @@ +# 부스터 & 데일리 보상 — 설계 (Boosters & Daily Reward) + +작성일: 2026-06-18 · 상태: 승인됨(오너) → 구현 계획 단계로 + +## 목표 / 맥락 + +광고 수익형 퍼즐게임 Block Seasons의 **리텐션(복귀) + 광고 노출**을 동시에 키우는 +키스톤 기능. 현재 게임엔 플레이어에게 줄 "보상 대상"이 없어 데일리 보상·보상형 +광고를 붙일 곳이 없었음 → **가벼운 부스터(파워업) 경제**를 도입해 둘 다 해금한다. + +사업자등록이 없어 IAP는 막혀 있으므로 **재화/상점 없이** 부스터를 직접 주고받는다. + +## 확정된 결정 (브레인스토밍) + +1. **부스터 3종**: 🔨해머 / 🔀셔플 / 💥줄폭탄 +2. **획득**: 재화 없음 — 데일리 보상 + 보상형 광고로 부스터를 직접 받아 **비축** +3. **데일리**: 7일 출석 캘린더 (점증, Day7 잭팟) + "광고 보고 2배" +4. **사용**: 게임 중 **부스터 바 → 대상 지정** + +## 부스터 규칙 (밸런스, 오너 승인) + +- **이동 수 미소모** — 부스터는 보조라 무브 카운터를 깎지 않는다. +- **점수·콤보 미부여, 목표 미반영** — 그리드만 직접 바꾸고 점수/목표 이벤트 파이프라인을 + 타지 않는다. 줄폭탄으로 "줄 N개" 목표를 깨거나 해머로 보석을 제거해 "보석 N개" + 목표를 달성할 수 없다(스테이지 난이도 보존). 콤보 상태도 건드리지 않는다(전진·리셋 둘 다 없음). +- **막힌 보드 되살리기 허용** — 부스터 사용 후 phase를 재평가(`_checkStuck`)해, 죽은 판 + (boardDead)을 다시 playing으로 되돌릴 수 있다 → 보상형 '컨티뉴'의 대안. +- **사용 가능 시점** — phase가 `playing` 또는 `stuck`일 때만. `won`/`lost` 후엔 불가. +- **사용 한도** — 별도 횟수 제한 없음. 보유량이 곧 한도(가진 만큼만 사용). +- **엔드리스 모드 포함** — 엔드리스에서도 사용 가능. + +### 부스터별 동작 +| 부스터 | 동작 | 대상 | +|---|---|---| +| 🔨 해머 | 채워진 칸 1개를 비움 | 칸 1개 탭 | +| 🔀 셔플 | 현재 트레이(3조각)를 새로 교체 | 즉시(대상 없음) | +| 💥 줄폭탄 | 가로 또는 세로 한 줄 전체를 비움 | 줄(행/열) 선택 | + +## 아키텍처 (기존 레이어 준수: ui → state → game|data|services) + +### 1) 엔진 (순수 Dart, `lib/game/engine/game_engine.dart`) +세 메서드 추가. 성공 시 `true`, 잘못된 대상/시점이면 `false`(보유량 차감 안 하도록). + +``` +bool useHammer(int x, int y) // (x,y)가 채워진 칸이면 비움 +bool useShuffle() // 트레이 재추첨 +bool useLineBomb({int? row, int? col}) // row 또는 col 중 하나의 줄을 비움 +``` + +공통: 이동/점수/콤보/목표 불변, 마지막에 `_checkStuck()` 호출. `playing`/`stuck`에서만 허용. +줄폭탄은 `row`와 `col` 중 정확히 하나만 지정(둘 다/둘 다 없음이면 `false`). + +### 2) 모델 (순수 Dart, `lib/game/models/booster.dart`) +``` +enum BoosterType { hammer, shuffle, lineBomb } +``` + +### 3) 저장 (`lib/data/save_repository.dart`, JSON blob 확장) +- 보유량: `int boosterCount(BoosterType)`, `Future grantBooster(BoosterType, [int n = 1])`, + `Future consumeBooster(BoosterType)`(0이면 false). +- 데일리: `String? dailyLastClaimedYmd`, `int dailyCalendarDay`(1~7), + `Future recordDailyClaim(String ymd, int day)`. +- JSON에 `boosters: {hammer, shuffle, lineBomb}` 와 `daily: {lastYmd, day}` 추가 + (기존 streak의 ymd 유틸 재사용). + +### 4) 상태 (Riverpod, `lib/state/`) +- `BoosterInventoryNotifier` — 보유량 노출 + `grant/consume`. +- `DailyRewardNotifier` — 오늘 받을 수 있는지 + 캘린더 day 계산 + claim. +- `GameSessionNotifier`에 사용 메서드 추가: `useHammer(x,y)` / `useShuffle()` / + `useLineBomb(...)`. 흐름: **엔진 먼저 호출 → 성공 시에만 인벤토리 차감 → 뷰 갱신**. + 보유 0이면 호출하지 않음(UI가 광고 제안). +- `GameViewState`에 grid가 이미 있어 부스터 후 UI 재렌더 가능. + +### 5) 데일리 캘린더 로직 (순수, 테스트 가능) +- 오늘 ymd 계산. `lastClaimedYmd == 오늘` → 이미 받음(비활성). +- 받을 수 있는 경우의 day: + - `lastClaimedYmd == 어제` → `day = (이전 day % 7) + 1`(연속, 7→1 순환). + - 그 외(하루 이상 빠짐/최초) → `day = 1`(리셋). +- claim 시 해당 day 보상 지급 + `lastClaimedYmd = 오늘`, `calendarDay = day` 저장. +- **보상표(제안, 튜닝 가능)**: + | Day | 보상 | + |---|---| + | 1 | 🔨×1 | + | 2 | 🔀×1 | + | 3 | 💥×1 | + | 4 | 🔨×1 🔀×1 | + | 5 | 🔀×1 💥×1 | + | 6 | 🔨×1 💥×1 | + | 7 | 🔨×2 🔀×2 💥×2 (잭팟) | +- 시작 보유량: 각 1개(최초 1회). + +### 6) UI (`lib/ui/`) +- **부스터 바 위젯** — 보드 아래 3버튼(아이콘+개수). 탭 시 타겟팅 모드 진입. + - 해머: 채워진 칸 탭 → 제거. + - 줄폭탄: 보드 가장자리에 행/열 핸들 표시 → 핸들 탭으로 줄 선택(가장 명확한 UX, + 구현 중 세부 조정 가능). + - 셔플: 즉시 적용. + - 개수 0인 버튼 탭 → "광고 보고 1개 받기" 다이얼로그. +- **데일리 팝업** — 홈 화면 진입 시 받을 수 있으면 표시. 7칸(과거=체크/딤, 오늘=하이라이트, + 미래=잠금) + [받기] + [광고 보고 2배]. + +### 7) 광고 (`lib/services/ad_service.dart` 재사용) +- 부스터 0개 → 보상형 광고 → 성공 시 해당 부스터 +1. +- 데일리 2배 → 보상형 광고 → 성공 시 보상 2배 지급. +- `showRewarded()`는 광고 미로드 시에도 true(기존 플레이어 친화 폴백) → 부스터는 지급됨. + +### 8) 분석 (`lib/services/analytics_service.dart`) +- `booster_used {type}` +- `booster_granted {type, count, source: start|daily|ad}` +- `daily_reward_claimed {day, doubled}` + +### 9) l10n (`lib/l10n/`) +- 부스터 이름·설명, 데일리 보상 UI, "광고 보고 받기/2배" CTA — EN/KO. + +## 테스트 전략 (TDD) +- **엔진**: 해머(칸 제거·이동/점수/목표 불변·빈 칸이면 false·죽은 판 되살림), + 셔플(새 트레이·불변·재stuck), 줄폭탄(행/열 제거·불변·재stuck·row^col 검증), + won/lost 후 사용 차단. +- **저장**: grant/consume/영속, 0에서 consume=false, 데일리 ymd/day 영속. +- **데일리 로직**: 연속 시 day+1, 빠짐 시 리셋, 같은 날 재수령 불가, 보상표 지급, 2배. +- **세션 노티파이어**: 성공 시 차감, 잘못된 대상/0개면 미차감. +- **위젯**: 부스터 바 개수 렌더·타겟팅, 데일리 팝업 상태. + +## 구현 단계 (계획에서 상세화) +1. 엔진 부스터 3종 (TDD, 순수) +2. `BoosterType` + SaveRepository 인벤토리 (TDD) +3. 인벤토리/세션 노티파이어 + 사용 흐름 (TDD) +4. 부스터 바 UI + 타겟팅 (위젯 테스트) +5. 데일리 캘린더 로직 + 노티파이어 (TDD) +6. 데일리 팝업 UI +7. 보상형 광고 지급(0-상태 + 데일리 2배) +8. 분석 + l10n +9. 통합 + 전체 테스트 그린 + +## 비목표 (YAGNI) +- 코인/재화/상점, 부스터 회전·되돌리기, IAP 부스터 판매, 부스터 합성/업그레이드. + +## 리스크 / 메모 +- 부스터가 점수·목표에 반영되지 않으므로 기존 스테이지 밸런스는 그대로 유효. +- 보상형 광고 미로드 시에도 부스터를 지급(폴백) → 신규 앱 no-fill 기간에도 게임 흐름 유지. +- 새 빌드 필요(부스터·데일리는 다음 릴리스부터). 빌드는 오너 명령 시에만.