# 런 루프 코어 (TODO E1+E2) — 설계 > 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO.md 항목 E(분해) + SlayDeckController 분석. > E(로그라이크 메타)의 첫 하위 프로젝트. 선행: B·D 완료. 후속: E3(맵)·E4(상점)·E5(유물)·E6(보스/저장). ## 문제 단일 전투(B)는 닫혔으나 승리 후 보상·다음 전투·덱 성장이 없다(보상 훅 자리만 비어 있음). 전투를 한 "런"으로 확장해야 덱빌딩 로그라이크가 된다. ## 범위 (이 슬라이스) 전투를 **연속 N전투 런**으로 확장: 런 상태 영속(HP/골드/덱) + 승리 후 카드 1택 보상 + 다음 전투 연결 + 고정 N전투 후 "런 클리어". **맵 노드·상점·유물·보스·저장은 범위 밖**(후속 E3~E6). 아키텍처: 기존 `SlayDeckController` 확장(별도 RunState 분리는 후속). ## 설계 ### 런 파라미터 (생성기 상수 — 향후 외부화) - `RUN_LENGTH = 3` (런당 전투 수), `GOLD_PER_WIN = 15`. ### 새 상태 (SlayDeckController 속성) - `RunDeck`(any) — 보유 카드 id 누적 배열(영속). - `Gold`(number) — 누적 골드. - `Floor`(number) — 현재 전투 번호(1-base). - `RunLength`(number) — 런당 전투 수. - `RewardChoices`(any) — 현재 제시 중인 보상 카드 id 3개. - `RunActive`(boolean) — 런 진행 중. - 플레이어 HP는 전투 간 **유지**(StartCombat에서 리셋 안 함). ### 메서드 - `OnBeginPlay` → `self:StartRun()`. - **`StartRun`**(신규): `PlayerMaxHp=80`, `PlayerHp=PlayerMaxHp`, `Gold=0`, `Floor=0`, `RunLength=RUN_LENGTH`, `RunDeck = starterDeck 복사`, `RunActive=true` → `BindButtons()`(1회) → `StartCombat()`. - **`StartCombat`**(수정): `Floor += 1`; 적 데이터(activeEnemy) 세팅; 전투별 리셋(Energy/Turn/Block/ EnemyHp/EnemyBlock/EnemyIntentIndex/DiscardPile/Hand/CombatOver); `DrawPile = RunDeck 복사` → Shuffle; `Cards` 테이블 세팅. **HP·Gold·RunDeck 보존, BindButtons 호출 제거.** → RenderCombat → StartPlayerTurn. - **`BindButtons`**(수정): EndTurn·카드5·**보상카드3·건너뛰기** 버튼을 1회 바인딩(StartRun에서 호출). - **`CheckCombatEnd`**(수정): - 적 HP≤0(승리): `Gold += GOLD_PER_WIN`; `CombatOver=true`; `Floor >= RunLength`이면 `ShowResult("런 클리어!")` + `RunActive=false`; 아니면 `self:OfferReward()`. - 플레이어 HP≤0(패배): `CombatOver=true`; `ShowResult("패배...")`; `RunActive=false`. - **`OfferReward`**(신규): `RewardChoices = 카드풀에서 3개 무작위`(math.random); 각 보상 카드 UI 갱신 (이름/코스트/설명/색); RewardHud 표시(Enable). - **`PickReward(slot)`**(신규): `slot`(1~3)이면 `RewardChoices[slot]`을 `RunDeck`에 추가; `slot=0`(건너뛰기)이면 추가 안 함; RewardHud 숨김 → `StartCombat()`(다음 층). - **`RenderRun`**(신규): `층 Floor/RunLength`·`골드 Gold` 텍스트 갱신. RenderCombat에서 호출. ### UI (생성기 신규) - `RewardHud`(평소 숨김): 제목 "보상 카드 선택" + 보상 카드 3장(UISprite+버튼, 이름/코스트/설명 자식) + "건너뛰기" 버튼. - HUD 표시 추가: `/ui/DefaultGroup/CombatHud/Floor`("층 1/3"), `/Gold`("골드 0"). - 보상 카드 클릭 → `PickReward(slot)`, 건너뛰기 → `PickReward(0)`. ### 버그 예방 - `BindButtons`가 매 전투(StartCombat)마다 카드 버튼에 `ConnectEvent` → 런에서 핸들러 중첩. **StartRun에서 1회만 바인딩**으로 이동(StartCombat의 BindButtons 호출 제거). ## 검증 (메이커 Play) - 전투 승리 → RewardHud에 카드 3장 표시; 골드 +15·층 표시. - 보상 1택 → RunDeck +1(다음 전투 손패/덱에 등장 가능), RewardHud 숨김, 다음 전투 시작(HP 유지). - 건너뛰기 → 덱 변화 없이 다음 전투. - 3전투째 승리 → "런 클리어!"·런 종료. 도중 패배 → "패배..."·런 종료. - 카드/보상 버튼 클릭은 런타임(MCP는 `PlayCard`/`EndPlayerTurn`/`PickReward` 직접 호출로 검증). - 생성기 결정적, JSON 유효. ## 범위 밖 (금지) - 맵 노드(E3)·상점/휴식(E4)·유물(E5)·보스/층전환/저장(E6). 골드 소비(E4). 보상 풀 확장(메이플 IP 추후).