80 lines
4.6 KiB
Markdown
80 lines
4.6 KiB
Markdown
# 시스템 갭 보완 (P5) 구현 계획
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development. T3·T5는 컨트롤러 직접.
|
|
|
|
**Goal:** 경제 밸런스(첫 상점 구매 가능), 신규 카드 2종+복합 효과, 적 패턴 보강, 런 종료 후 메뉴 복귀.
|
|
|
|
---
|
|
|
|
## Task 1: 데이터+상수 (경제·적 패턴·신규 카드 골격)
|
|
|
|
**Files:** `data/enemies.json`, `data/cards.json`, `tools/deck/gen-slaydeck.mjs`(상수·elite 골드)
|
|
|
|
- [ ] enemies.json 의도 패턴 교체(스펙 §2.C 표 그대로; slime 3종·king_slime 유지).
|
|
- [ ] cards.json에 추가(이미지는 T3에서 채움 — 일단 필드 생략):
|
|
```json
|
|
"WarLeap": { "name": "워 리프", "cost": 1, "kind": "Attack", "damage": 4, "block": 3, "desc": "피해 4, 방어도 3" },
|
|
"Brandish": { "name": "브랜디시", "cost": 2, "kind": "Attack", "damage": 13, "desc": "피해 13" }
|
|
```
|
|
- [ ] gen-slaydeck: `GOLD_PER_WIN = 15` → `25`; CheckCombatEnd elite 분기(AddRelic 줄 옆)에 `self.Gold = self.Gold + 15` 추가.
|
|
- [ ] 검증: JSON 파스, gen-slaydeck 실행 OK(산출물 복원), sim 통과(기존 fixture 무관).
|
|
- [ ] Commit: `feat(system-gaps): 경제 상향(승리25·엘리트+15)·적 패턴 보강·신규 카드 2종 데이터`
|
|
|
|
## Task 2: 복합 카드 로직 + EndRun 복귀 + sim
|
|
|
|
**Files:** `tools/deck/gen-slaydeck.mjs`, `tools/balance/sim-balance.mjs`, `tools/balance/sim-balance.test.mjs`
|
|
|
|
- [ ] **sim 테스트 먼저** (test 파일에 추가):
|
|
```js
|
|
test('simulateCombat: Attack 카드의 block 필드도 적용(복합 카드)', () => {
|
|
const data = {
|
|
cards: { Combo: { name: '콤보', cost: 1, kind: 'Attack', damage: 4, block: 3 } },
|
|
starterDeck: ['Combo', 'Combo', 'Combo', 'Combo', 'Combo'],
|
|
monsters: [{ name: '적', maxHp: 9, intents: [{ kind: 'Attack', value: 5 }] }],
|
|
};
|
|
const r = simulateCombat(data, mulberry32(1));
|
|
assert.equal(r.win, true); // 3코스트 내 3장: 12딤>9 → 1턴 승리(블록 적용 여부와 무관하게 승리하지만)
|
|
assert.equal(r.playerHpRemaining, 80); // 피해 받기 전 승리 — 블록 검증은 아래 시나리오로
|
|
});
|
|
test('simulateCombat: 복합 카드 블록이 적 공격을 흡수', () => {
|
|
const data = {
|
|
cards: { Combo: { name: '콤보', cost: 1, kind: 'Attack', damage: 1, block: 3 } },
|
|
starterDeck: ['Combo', 'Combo', 'Combo', 'Combo', 'Combo'],
|
|
monsters: [{ name: '적', maxHp: 100, intents: [{ kind: 'Attack', value: 9 }] }],
|
|
};
|
|
const r = simulateCombat(data, mulberry32(1));
|
|
// 1턴: 3장 사용 → 블록 9 → 적 공격 9 전부 흡수 → 2턴 시작 HP 80 유지 확인 위해 2턴 후 비교 불가(루프) — 간접: MAX_TURNS 도달 draw, hp가 (80 - 0*몇턴)...
|
|
// 단순 명제: 블록 미적용이면 매턴 -9 → 100/9≈11턴 내 사망. 블록 적용이면 매턴 9블록=무피해 → draw.
|
|
assert.equal(r.draw, true);
|
|
assert.equal(r.playerHpRemaining, 80);
|
|
});
|
|
```
|
|
- [ ] sim 구현: simulateCombat Attack 분기에 `if (c.block) pBlock += c.block;` 추가(스탯 bump의 block 합산도 `c.block || 0`로). 테스트 통과.
|
|
- [ ] gen-slaydeck PlayCard Attack 분기에 추가(PlayAttackFx 호출 다음 줄):
|
|
```
|
|
if c.block ~= nil then
|
|
self.PlayerBlock = self.PlayerBlock + c.block
|
|
end
|
|
```
|
|
- [ ] gen-slaydeck EndRun: 신규 메서드 + CheckCombatEnd 두 지점 교체:
|
|
```js
|
|
method('EndRun', `self:ShowResult(text)
|
|
self.RunActive = false
|
|
_TimerService:SetTimerOnce(function() self:ShowMainMenu() end, 4)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'text' }]),
|
|
```
|
|
교체: `self:ShowResult("런 클리어!")` + `self.RunActive = false` → `self:EndRun("런 클리어!")`; `self:ShowResult("패배...")` + `self.RunActive = false` → `self:EndRun("패배...")`.
|
|
- [ ] 검증: sim 전체 통과(16개), gen 실행·심볼 확인 후 산출물 복원.
|
|
- [ ] Commit: `feat(system-gaps): 복합 카드(피해+방어)·런 종료 후 메뉴 복귀(EndRun)`
|
|
|
|
## Task 3 (컨트롤러 직접): 신규 카드 이미지 수확
|
|
- "워 리프"/"브랜디시" 검색→메이커 선별→cards.json image 채움→커밋.
|
|
|
|
## Task 4: 재생성·검증·산출물 커밋 (T3 이후)
|
|
- 표준 절차 + `node tools/balance/sim-balance.mjs 2000` 결과 기록(참고).
|
|
|
|
## Task 5 (컨트롤러 직접): 메이커 검증+푸시+PR+머지
|
|
- 보상/상점 신규 카드(이미지)·복합 카드 효과·엘리트 골드·패배→4s 메뉴 복귀. 스크린샷.
|
|
|
|
## Self-Review
|
|
- §2.A→T1, §2.B→T1(데이터)+T2(로직)+T3(이미지), §2.C→T1, §2.D→T2. 시그니처 일관(EndRun(text)). 복합 카드 sim 테스트는 블록 적용을 draw/hp로 결정적으로 판별.
|