docs(spec): boosters & daily reward design
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<void> grantBooster(BoosterType, [int n = 1])`,
|
||||
`Future<bool> consumeBooster(BoosterType)`(0이면 false).
|
||||
- 데일리: `String? dailyLastClaimedYmd`, `int dailyCalendarDay`(1~7),
|
||||
`Future<void> 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 기간에도 게임 흐름 유지.
|
||||
- 새 빌드 필요(부스터·데일리는 다음 릴리스부터). 빌드는 오너 명령 시에만.
|
||||
Reference in New Issue
Block a user