추상 단일 적 → 맵 실제 몬스터 멀티 전투(클릭 타겟·각자 HP/의도·전체 처치 시 승리). 컨트롤러 단일 소유 + script.CombatMonster(EnemyId) 매핑 + 월드 HP바 슬롯. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.3 KiB
8.3 KiB
맵 몬스터 카드 전투 — 설계
- 날짜: 2026-06-10
- 대상:
tools/deck/gen-slaydeck.mjs(SlayDeckController + UI),data/enemies.json, 신규tools/monster/gen-combat-monster.mjs(+CombatMonster.codeblock), 맵 몬스터 엔티티,tools/balance/sim-balance.mjs - 상태: 승인됨 (브레인스토밍 → 본 스펙)
1. 배경
현재 카드 전투는 SlayDeckController 내부의 추상 단일 적으로 동작한다.
- 상태:
EnemyHp/EnemyMaxHp/EnemyBlock/EnemyName/EnemyIntents/EnemyIntentIndex(단일 적 1체). - 공격 카드 →
DealDamageToEnemy(damage)→EnemyHp차감. CheckCombatEnd:EnemyHp<=0→ 승리(골드·보상·노드/막 진행),PlayerHp<=0→ 패배.- 적 데이터는
data/enemies.json(slime/elite/boss), 노드의CurrentEnemyId가 어떤 적인지 결정,Floor배율 적용. - CombatHud는 단일 적 패널(EnemyName/EnemyHp/EnemyBlock/EnemyIntent)을 텍스트로 표시.
맵에는 실제 몬스터 엔티티(script.Monster + script.MonsterAttack, 예: map01의 주황버섯)가 있으나, 이는 물리 액션 전투용이라 카드 전투와 완전히 분리되어 있다.
2. 목표
카드 공격이 추상 슬라임이 아니라 맵의 실제 몬스터에 적용되고, 맵의 모든 몬스터가 쓰러지면 전투 승리가 되도록 한다. 승리 이후 흐름(보상·노드·상점·휴식·막/보스)은 기존 런 시스템을 그대로 재사용한다.
3. 확정 요구사항 (브레인스토밍 결과)
- 타겟팅: 몬스터를 클릭하면 그 몬스터가 "현재 타겟"이 되고, 이후 공격 카드가 그 타겟에 적용된다.
- 전투원 모델: 각 몬스터가 개별 HP와 의도(공격/방어)를 가지며, 적 턴에 생존 몬스터가 각자 플레이어를 공격한다(멀티 적).
- 런 연동: 전투 노드 = 현재 물리 맵의 몬스터들. 전부 처치 시 노드 클리어 → 기존 보상/맵/상점/막 흐름 유지.
- 스탯 소스: 각 맵 몬스터가 적 타입 id를 보유하고 HP/의도를
data/enemies.json에서 읽는다. 배율은 막 배율(필수, 기존1+(Floor-1)*0.6재사용) + 노드 타입 배율(선택, 기본 1; 엘리트/보스만 가산). - 상태 표시: 각 몬스터 머리 위에 월드(화면) HP바 + 의도 표시.
- 아키텍처: 컨트롤러 중심. 전투 상태는
SlayDeckController가 단일 소유, 몬스터 엔티티는 타겟·시각(애니메이션) 역할만. 사망 연출은 기존script.Monster자산 재사용. - 몬스터↔적ID: 전용 경량 스크립트
script.CombatMonster의EnemyId(string) 속성으로 명시.
4. 데이터 모델
4.1 data/enemies.json
맵 몬스터용 적 타입을 추가한다(기존 slime/elite/boss는 유지). 예:
"orange_mushroom": {
"name": "주황버섯",
"maxHp": 16,
"intents": [ { "kind": "Attack", "value": 5 }, { "kind": "Defend", "value": 4 } ]
}
스탯 수치는 placeholder이며 sim-balance.mjs로 추후 튜닝한다.
4.2 script.CombatMonster (신규 코드블록)
맵 몬스터 엔티티에 부착하는 경량 마커. 속성 EnemyId(string) 1개. 런타임 로직 없음(컨트롤러가 읽기만). script.Monster를 보유한 엔티티에 함께 부착한다.
4.3 런타임 전투 상태 (SlayDeckController)
단일 Enemy* 속성군을 제거하고 리스트로 교체한다.
Monsters(any 리스트). 원소:{ path, enemyId, name, hp, maxHp, block, intents, intentIndex, alive }.TargetIndex(number). 현재 선택된 타겟의Monsters인덱스.- 상수
MAX_MONSTERS(예: 4). UI 슬롯 수 = 이 값. 맵 몬스터가 더 많으면 앞에서MAX_MONSTERS마리만 전투에 참여(초과분은 미지원, 로그 경고).
5. 전투 흐름 (SlayDeckController 메서드 변경)
- StartCombat:
- 현재 맵에서
script.CombatMonster를 가진 몬스터 엔티티를 스캔(맵 루트 하위, 최대MAX_MONSTERS). - 각 몬스터의
EnemyId로enemies.json스탯을 읽어 배율(막 배율 필수 + 노드 타입 배율 선택, §3-4)을 적용해Monsters리스트 구성. - 몬스터 부활: 가시성 on,
StateComponentIDLE, HP 리셋. - 각 몬스터 화면 위치에 UI 슬롯(HP바·의도·타겟버튼) 배치·활성화.
TargetIndex= 첫 생존자. 손패/턴 시작.
- 현재 맵에서
- SetTarget(i):
TargetIndex갱신 + 타겟 하이라이트 갱신. - PlayCard(Attack):
Monsters[TargetIndex]에 방어도→HP 차감. HP≤0 → 해당 몬스터 사망 처리(§6), UI 슬롯 숨김, 자동으로 다음 생존 타겟 선택. 이후CheckCombatEnd. - PlayCard(Skill): 기존대로 플레이어 방어도 증가(변경 없음).
- EnemyTurn: 생존 몬스터 각자
intentIndex진행 — Attack→DealDamageToPlayer, Defend→자기block증가. (각 몬스터는 독립 의도 사이클) - CheckCombatEnd: 생존 몬스터 0 → 승리(기존 보상/노드/막 분기 재사용).
PlayerHp<=0→ 패배. - RenderCombat: 각 생존 몬스터 UI 슬롯의 HP바·의도 갱신, 플레이어 패널 갱신. 기존 단일 적 패널(EnemyName/EnemyHp/EnemyIntent)은 제거 또는 숨김.
6. 사망 / 부활 연출
컨트롤러가 직접 관리하여 노드 간 몬스터 영속(엔티티 Destroy 안 함):
- 사망: 타겟 몬스터의
StateComponent를 DEAD로 전환(die 애니메이션) 후 짧은 지연 뒤 가시성 off.alive=false. - 부활:
StartCombat에서 가시성 on, IDLE 상태, HP 리셋.
기존 Monster.codeblock의 hit/die 애니메이션 자산을 활용하되, Destroy/Respawn 타이머에 의존하지 않고 컨트롤러가 생사 시점을 통제한다.
7. UI — 몬스터 슬롯 (DefaultGroup.ui)
카메라가 고정(MapCamera)이라 몬스터의 화면상 위치가 불변 → UI 슬롯을 전투 시작 시 한 번 배치하면 된다.
gen-slaydeck.mjs가CombatHud아래 MonsterSlot ×MAX_MONSTERS를 사전 생성(평소 비활성):- HP바 스프라이트(배경+채움), 의도 텍스트, 투명 타겟 버튼(클릭→
SetTarget).
- HP바 스프라이트(배경+채움), 의도 텍스트, 투명 타겟 버튼(클릭→
- 위치 결정: 런타임 world→screen 변환을 우선 시도(카메라 고정이므로 전투 시작 시 1회 계산). 변환 API가 여의치 않으면
data에 슬롯 화면좌표를 명시(현재 map01 몬스터 배치 기준)하는 폴백을 사용한다. → 구현 단계에서 변환 가용성 검증.
8. 변경 파일 요약
| 파일 | 변경 |
|---|---|
data/enemies.json |
맵 몬스터 적 타입 추가(orange_mushroom 등) |
신규 tools/monster/gen-combat-monster.mjs |
CombatMonster.codeblock 생성 + 11개 맵 몬스터 엔티티에 script.CombatMonster(EnemyId) 부착(idempotent) |
RootDesk/MyDesk/CombatMonster.codeblock |
신규 생성물 |
tools/deck/gen-slaydeck.mjs |
전투 멀티 몬스터화(상태·PlayCard·EnemyTurn·CheckCombatEnd·RenderCombat·StartCombat·타겟 바인딩) + UI 몬스터 슬롯 생성 |
tools/balance/sim-balance.mjs |
멀티 몬스터 규칙으로 동기화 |
map/map01.map~map11.map |
몬스터 엔티티에 script.CombatMonster 부착(생성기 재실행 산출) |
ui/DefaultGroup.ui |
몬스터 슬롯 추가(생성기 산출) |
9. 알려진 한계 (MVP)
- 모든 전투 노드가 같은 물리 맵 몬스터를 재사용한다(막 배율로 난이도 차등). 노드별 다른 적 구성/맵 이동은 후속 과제.
MAX_MONSTERS초과 몬스터는 전투에 미참여.- 보스 노드도 동일 맵 몬스터를 사용(테마 불일치)는 후속 콘텐츠 확장에서 해결.
10. 리스크
- world→screen 변환 가용성: 미지원 시 슬롯 좌표 데이터 폴백으로 대응(§7).
- 외부 엔티티 스크립트 메서드/상태 접근: 컨트롤러가 몬스터 엔티티의
StateComponent·가시성을 제어할 수 있어야 함(구현 단계 검증). - 생성물 단일 소스 유지: 전투/HUD 산출물은
gen-slaydeck.mjs에서만 생성(직접 편집 금지) 규칙 유지.
11. 검증
node tools/balance/sim-balance.test.mjs통과 + 멀티 몬스터 규칙 반영.- 생성기 2회 실행 결과 동일(결정적).
- 메이커 플레이: 카드로 특정 몬스터 타겟 공격 → HP 감소·사망 애니 → 전체 처치 시 승리 → 기존 보상/노드 흐름 진입. 적 턴에 생존 몬스터가 플레이어 공격.