90 lines
6.2 KiB
Markdown
90 lines
6.2 KiB
Markdown
# 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": "<RUID>" },
|
||
"Berserk": { "name": "버서크", "cost": 2, "kind": "Power", "class": "fighter", "powerEffect": "energyPerTurn", "value": 1, "selfVuln": 1, "desc": "매턴 에너지 +1, 취약 1 자가", "image": "<RUID>" },
|
||
"RisingAttack": { "name": "라이징 어택", "cost": 2, "kind": "Attack", "class": "fighter", "damage": 12, "desc": "피해 12", "image": "<RUID>" },
|
||
"ThunderCharge": { "name": "썬더 차지", "cost": 1, "kind": "Attack", "class": "page", "damage": 7, "weak": 1, "desc": "피해 7, 약화 1", "image": "<RUID>" },
|
||
"BlizzardCharge": { "name": "블리자드 차지", "cost": 1, "kind": "Attack", "class": "page", "damage": 7, "vuln": 1, "desc": "피해 7, 취약 1", "image": "<RUID>" },
|
||
"PowerGuard": { "name": "파워 가드", "cost": 1, "kind": "Skill", "class": "page", "block": 10, "desc": "방어도 10", "image": "<RUID>" },
|
||
"Pierce": { "name": "피어스", "cost": 1, "kind": "Attack", "class": "spearman", "damage": 9, "pierce": true, "desc": "피해 9, 방어 무시", "image": "<RUID>" },
|
||
"IronWall": { "name": "아이언 월", "cost": 2, "kind": "Skill", "class": "spearman", "block": 12, "desc": "방어도 12", "image": "<RUID>" },
|
||
"HyperBody": { "name": "하이퍼 바디", "cost": 1, "kind": "Power", "class": "spearman", "powerEffect": "blockPerTurn", "value": 3, "desc": "매턴 방어도 +3", "image": "<RUID>" }
|
||
```
|
||
|
||
- [ ] **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 전 호출부 갱신 명시 ✓
|
||
- 하네스: 산출물 검증 카운트만 ✓
|