diff --git a/docs/superpowers/plans/2026-06-12-job-advancement.md b/docs/superpowers/plans/2026-06-12-job-advancement.md new file mode 100644 index 0000000..342c165 --- /dev/null +++ b/docs/superpowers/plans/2026-06-12-job-advancement.md @@ -0,0 +1,89 @@ +# P9 — 전직 시스템 코어 + 전사 2차 구현 계획 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 카드 클래스 모델·전직 선택 흐름·전사 2차 3직업(전용 카드 9종 + 신규 메커니즘 4종). + +**Architecture:** cards.json `class`/`hits`/`pierce`/`selfVuln` 스키마 확장 → gen-slaydeck.mjs (직렬화·CardPool 필터·전투 메커니즘·JobChoiceHud/JobSelectHud·ContinueAfterBoss 추출) → sim-balance 동기화. RULES.md 하네스 준수 (산출물 검증은 grep -c). + +설계: `docs/superpowers/specs/2026-06-12-job-advancement-design.md` (승인 완료) + +--- + +### Task 1: 카드 이미지 RUID 9종 선별 (메이커) + +- [ ] **Step 1**: asset_search(source=maplestory, sprite) 쿼리 — "콤보", "버서크", "라이징", "썬더", "블리자드", "파워 가드", "창", "철벽", "하이퍼" (빈약 시 보조 쿼리) +- [ ] **Step 2**: SkillFx 복제 격자 미리보기 → 9종 확정 → 정리·종료 (기존 절차) + +### Task 2: 데이터 — cards.json 확장 + +- [ ] **Step 1**: 기존 카드 9종 전부에 `"class": "warrior"` 추가 +- [ ] **Step 2**: 신규 9종 추가 (설계 표 그대로, image=Task 1 선별값): + +```json +"ComboAttack": { "name": "콤보 어택", "cost": 1, "kind": "Attack", "class": "fighter", "damage": 5, "hits": 2, "desc": "피해 5 × 2회", "image": "" }, +"Berserk": { "name": "버서크", "cost": 2, "kind": "Power", "class": "fighter", "powerEffect": "energyPerTurn", "value": 1, "selfVuln": 1, "desc": "매턴 에너지 +1, 취약 1 자가", "image": "" }, +"RisingAttack": { "name": "라이징 어택", "cost": 2, "kind": "Attack", "class": "fighter", "damage": 12, "desc": "피해 12", "image": "" }, +"ThunderCharge": { "name": "썬더 차지", "cost": 1, "kind": "Attack", "class": "page", "damage": 7, "weak": 1, "desc": "피해 7, 약화 1", "image": "" }, +"BlizzardCharge": { "name": "블리자드 차지", "cost": 1, "kind": "Attack", "class": "page", "damage": 7, "vuln": 1, "desc": "피해 7, 취약 1", "image": "" }, +"PowerGuard": { "name": "파워 가드", "cost": 1, "kind": "Skill", "class": "page", "block": 10, "desc": "방어도 10", "image": "" }, +"Pierce": { "name": "피어스", "cost": 1, "kind": "Attack", "class": "spearman", "damage": 9, "pierce": true, "desc": "피해 9, 방어 무시", "image": "" }, +"IronWall": { "name": "아이언 월", "cost": 2, "kind": "Skill", "class": "spearman", "block": 12, "desc": "방어도 12", "image": "" }, +"HyperBody": { "name": "하이퍼 바디", "cost": 1, "kind": "Power", "class": "spearman", "powerEffect": "blockPerTurn", "value": 3, "desc": "매턴 방어도 +3", "image": "" } +``` + +- [ ] **Step 3**: 커밋 `feat(job): 전사 2차 카드 9종·클래스 필드 데이터` + +### Task 3: 생성기 — 직렬화·전투 메커니즘 + +**Files:** Modify `tools/deck/gen-slaydeck.mjs` + +- [ ] **Step 1**: luaCardsTable에 `class`(필수 — 누락 시 throw)·`hits`·`pierce`·`selfVuln` 직렬화 +- [ ] **Step 2**: prop `PlayerJob`(string "") 추가, StartRun에 `self.PlayerJob = ""` 리셋 +- [ ] **Step 3**: PlayCard Attack 분기 — 다단히트·pierce·selfVuln: + +```lua +if c.kind == "Attack" then + if c.damage ~= nil then + local total = 0 + local n = c.hits or 1 + for h = 1, n do + total = total + self:CalcPlayerAttack(c.damage) + end + self:PlayAttackFx(self.TargetIndex, c.image, total, c.pierce == true) + end + ... +end +-- 공통부 (버프 적용 옆): if c.selfVuln ~= nil then self.PlayerVuln = self.PlayerVuln + c.selfVuln end +``` + +- [ ] **Step 4**: `PlayAttackFx(targetIndex, image, damage, pierce)` / `DealDamageToTarget(amount, pierce)` 시그니처 확장 — pierce면 block 차감 생략. 기존 호출부(물약 화염병 포함) `false` 전달 +- [ ] **Step 5**: StartPlayerTurn 파워 루프 확장 — `energyPerTurn`→Energy, `blockPerTurn`→PlayerBlock (ClayBlockNext 처리 뒤) +- [ ] **Step 6**: 커밋 `feat(job): 다단히트·방어무시·자가취약·파워 2종 (생성기)` + +### Task 4: 생성기 — 풀 필터·전직 흐름·UI + +- [ ] **Step 1**: `CardPool()` 헬퍼 (정렬된 id 배열 반환 — class 필터), OfferReward·ShowShop이 사용 +- [ ] **Step 2**: CheckCombatEnd 보스 분기 → `ContinueAfterBoss()` 추출. 분기: `PlayerJob == "" and Floor < RunLength` → `ShowJobChoice()`, else 유물+`ContinueAfterBoss()` +- [ ] **Step 3**: `ShowJobChoice`/`PickJobReward(kind)` (relic→유물+Continue / job→ShowJobSelect), `ShowJobSelect`/`SetJob(jobId)` (PlayerJob·대표 카드 지급·토스트·Continue), `JobLabel()` 헬퍼 (전사/파이터/페이지/스피어맨) +- [ ] **Step 4**: UI — guid 'job'=0xe4: `JobChoiceHud`(타이틀+버튼 2)·`JobSelectHud`(3패널: 직업명·설명·대표 카드명). HideGameHud·BindButtons 등록 +- [ ] **Step 5**: PlayerPanel/Name 갱신 — StartCombat·SetJob에서 `JobLabel()` +- [ ] **Step 6**: 커밋 `feat(job): 클래스 풀 필터·전직 선택 흐름·전직 HUD (생성기)` + +### Task 5: 시뮬 동기화 (TDD) + +- [ ] **Step 1**: 실패 테스트 — hits 합산(힘 타격마다)·pierce(블록 무시)·selfVuln·energyPerTurn·blockPerTurn 5건 +- [ ] **Step 2**: sim-balance.mjs 재현 → 전체 PASS (기존 21+5, rogue-map 9) +- [ ] **Step 3**: 커밋 `feat(job): 시뮬 신규 메커니즘 동기화` + +### Task 6: 재생성·메이커 검증·PR + +- [ ] **Step 1**: 재생성 + `grep -c "PlayerJob\|JobChoiceHud" 산출물` 카운트 확인 + 전체 테스트 +- [ ] **Step 2**: 메이커 refresh→빌드 0에러→플레이테스트: 보스 클리어→선택 화면→전직(파이터)→전용 카드 풀 편입·직업명 표기·콤보/피어스 동작 스크린샷 +- [ ] **Step 3**: 커밋·푸시 → `gitea-pr.mjs`로 PR(UTF-8 spec)·머지 → main pull + +## Self-Review + +- 설계 전 항목 매핑 ✓ (클래스 모델 T2/T4, 전직 흐름 T4, 카드 9종 T1/T2, 메커니즘 T3/T5, 표기 T4) +- 시그니처 일관성: PlayAttackFx/DealDamageToTarget pierce 전 호출부 갱신 명시 ✓ +- 하네스: 산출물 검증 카운트만 ✓ diff --git a/docs/superpowers/specs/2026-06-12-job-advancement-design.md b/docs/superpowers/specs/2026-06-12-job-advancement-design.md new file mode 100644 index 0000000..6b508fe --- /dev/null +++ b/docs/superpowers/specs/2026-06-12-job-advancement-design.md @@ -0,0 +1,64 @@ +# P9 — 전직 시스템 코어 + 전사 2차 설계 + +날짜: 2026-06-12 (사용자 승인 완료 — P9/P10/P11 3단계 중 1단계) +브랜치: `feature/p9-job-advancement` + +## 범위 + +1. **클래스 모델** — 카드 `class` 필드, 클래스별 카드 풀 필터 (보상·상점) +2. **전직 선택 흐름** — 보스 클리어 시 1차 상태면 [유물] vs [2차 전직] 선택, 전직 시 파이터/페이지/스피어맨 3택 +3. **전사 2차 전용 카드 9종** + 신규 메커니즘: 다단히트(`hits`)·방어 무시(`pierce`)·자가 디버프(`selfVuln`)·파워 2종(`energyPerTurn`/`blockPerTurn`) +4. 플레이어 패널·캐릭터 선택의 직업명 표기 + +비범위: 법사(P10), 승천(P11), 3차 전직. + +## 데이터 (data/cards.json) + +- 모든 카드에 `class` 필드. 기존 9종 → `"warrior"`. +- 신규 필드: `hits`(타격 횟수), `pierce`(true=방어 무시), `selfVuln`(사용 시 자신에게 취약 N), powerEffect 추가값 `energyPerTurn`/`blockPerTurn`. + +신규 카드 9종 (메이플 2차 스킬명 × StS 효과): + +| id | 직업 | 이름 | 코스트 | 효과 | StS 참조 | +|----|------|------|--------|------|----------| +| ComboAttack | fighter | 콤보 어택 | 1 | 피해 5 × 2회 | Twin Strike | +| Berserk | fighter | 버서크 | 2 | Power: 매턴 에너지 +1, 사용 시 취약 1 자가 | Berserk | +| RisingAttack | fighter | 라이징 어택 | 2 | 피해 12 | Carnage(경량) | +| ThunderCharge | page | 썬더 차지 | 1 | 피해 7, 약화 1 | Clothesline(경량) | +| BlizzardCharge | page | 블리자드 차지 | 1 | 피해 7, 취약 1 | Bash(경량) | +| PowerGuard | page | 파워 가드 | 1 | 방어도 10 | Shrug It Off(경량) | +| Pierce | spearman | 피어스 | 1 | 피해 9, **방어 무시** | — | +| IronWall | spearman | 아이언 월 | 2 | 방어도 12 | Impervious(경량) | +| HyperBody | spearman | 하이퍼 바디 | 1 | Power: 매턴 방어도 +3 | Metallicize | + +전직 시 대표 카드 1장 즉시 지급: fighter→콤보 어택, page→썬더 차지, spearman→피어스. + +## 전투 규칙 확장 (Lua + sim 동기화) + +- **다단히트**: `total = Σ CalcPlayerAttack(c.damage)` (hits회 반복 — 힘이 타격마다 적용, 펜닙 카운터도 타격마다 증가), 이펙트·팝업은 합산 1회. 취약 배수는 합산값에 적용(단순화 명시). +- **방어 무시**: `DealDamageToTarget(amount, pierce)` — pierce면 block 차감 생략. `PlayAttackFx`에 pierce 전달. +- **selfVuln**: 카드 사용 시 `PlayerVuln += selfVuln`. +- **파워 확장**: StartPlayerTurn 파워 루프에 `energyPerTurn`(Energy +v) · `blockPerTurn`(PlayerBlock +v — 블록 리셋·점토 처리 후). + +## 전직 흐름 + +- 컨트롤러 prop: `PlayerJob`(string, ""=1차). StartRun에서 리셋. +- **카드 풀 필터** (`CardPool` 헬퍼 신설): `c.class == self.SelectedClass or (PlayerJob ~= "" and c.class == PlayerJob)`. OfferReward·ShowShop이 사용. +- **보스 클리어 분기** (CheckCombatEnd): 보스 진행 로직을 `ContinueAfterBoss()`로 추출. + - `PlayerJob == "" and Floor < RunLength` → `ShowJobChoice()` (선택 후 ContinueAfterBoss) + - 그 외 → 기존 유물 지급 + ContinueAfterBoss (최종 막 클리어 시 전직 무의미 — 생략) +- **JobChoiceHud**: "보스 보상 선택" — [유물 획득](PickNewRelic+AddRelic) / [2차 전직] 버튼 2개. +- **JobSelectHud**: 파이터/페이지/스피어맨 3패널 (직업명·설명·대표 카드명). 선택 → `SetJob(jobId)`: PlayerJob 설정, 대표 카드 RunDeck 추가, 토스트, 패널 닫고 ContinueAfterBoss. +- guid 네임스페이스 `'job'` = 0xe4 (JobChoiceHud·JobSelectHud). +- **직업명 표기**: PlayerPanel/Name = "전사" → 전직 후 "파이터/페이지/스피어맨" (`JobLabel` 헬퍼, StartCombat·SetJob에서 갱신). + +## 검증 + +1. sim-balance: hits/pierce/selfVuln/energyPerTurn/blockPerTurn 재현 + 신규 테스트 5건. rogue-map 9건·기존 21건 유지. +2. 메이커: 빌드 0에러 + 플레이테스트 — 보스 클리어→선택 화면→전직→전용 카드 보상 풀 편입·패널 직업명, 유물 선택 경로, 다단히트/방어무시 동작. + +## 결정 사항 + +- 전직은 런당 1회 (PlayerJob 비가역), 최종 막 보스에선 선택 생략 +- 카드 이미지 9종: 공식 maplestory 리소스 메이커 선별 (기존 절차) +- 클래스 필터로 "해당 클래스만 획득" 충족 — 사용 제한은 별도 불요 (얻을 수 없으면 못 씀)