docs(A): README·framework 문서를 실제 구현에 맞게 정정

미존재 3컴포넌트(SlayCardCatalog/SlayRunState/SlayCombatManager)를 구현된 것처럼 서술하던 부분을 정정.

- '게임 프레임워크 현황'을 실제 구현(SlayDeckController 기반 카드 전투 + B 결과)으로 재작성
- 3대 컴포넌트는 '향후 설계(미구현 — 목표 아키텍처)' 섹션으로 분리 보존
- 디렉토리 트리·codeblock 목록·생성기(tools/) 반영, SlayDeckController 추가
- 스크립트 호출 예시를 실제 메서드(PlayCard/EndPlayerTurn/StartCombat)로 교체
- '다음 구현 단계'에서 전투 UI·전투 루프 완료 표시, D/F/E 반영
- 수치는 임시 placeholder임을 명시
- framework 문서도 동일 기조로 재작성 + Planned 섹션에 3컴포넌트 보존

검증: 문서에 적힌 컴포넌트명을 코드와 grep 교차확인 (SlayDeckController 실재, 3컴포넌트 코드 0건).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 01:10:35 +09:00
parent 9206018fbe
commit e8ed467cda
2 changed files with 118 additions and 52 deletions

View File

@@ -43,14 +43,15 @@ git pull
``` ```
slaymaple/ slaymaple/
├── Global/ # 월드 전역 설정 · 공용 모델 · 게임로직 ├── Global/ # 월드 전역 설정 · 공용 모델 · 게임로직
│ ├── common.gamelogic # SlayCardCatalog / SlayRunState / SlayCombatManager 부착 지점 │ ├── common.gamelogic # SlayDeckController 부착 지점 (카드 UI 전투)
│ ├── Player.model # 플레이어 모델 │ ├── Player.model # 플레이어 모델
│ ├── *.model # 몬스터 등 공용 모델 │ ├── *.model # 몬스터 등 공용 모델
│ ├── WorldConfig.config # 월드 설정 │ ├── WorldConfig.config # 월드 설정
│ └── ... │ └── ...
├── RootDesk/ ├── RootDesk/
│ └── MyDesk/ # 작업용 책상 — codeblock(스크립트)·모델·타일셋 │ └── MyDesk/ # 작업용 책상 — codeblock(스크립트)·모델·타일셋
│ ├── Monster.codeblock │ ├── SlayDeckController.codeblock # 카드 UI 전투 컨트롤러 (생성물)
│ ├── Monster.codeblock # 필드 액션 몬스터 (HP·피격·리스폰, 카드 전투와 별개)
│ ├── MonsterAttack.codeblock │ ├── MonsterAttack.codeblock
│ ├── PlayerAttack.codeblock │ ├── PlayerAttack.codeblock
│ ├── PlayerHit.codeblock │ ├── PlayerHit.codeblock
@@ -58,7 +59,11 @@ slaymaple/
│ ├── UIToast.codeblock │ ├── UIToast.codeblock
│ └── RectTileData_Henesys.tileset │ └── RectTileData_Henesys.tileset
├── map/ ├── map/
── map01.map # 메인 맵 ── map01.map ~ map11.map # 맵 11종 (공식 배경 + STS풍 우측 배치)
├── tools/ # 결정적 생성기 (단일 소스)
│ ├── gen-slaydeck.mjs # 카드/덱 UI · SlayDeckController · common 생성
│ ├── gen-cardhand.mjs # 손패 카드 엔티티 생성
│ └── gen-maps.mjs # 맵 생성
├── ui/ # UI 그룹 (Default / Popup / Toast) ├── ui/ # UI 그룹 (Default / Popup / Toast)
├── docs/ ├── docs/
│ └── slaymaple_basic_framework.md # 전투 프레임워크 설계 문서 │ └── slaymaple_basic_framework.md # 전투 프레임워크 설계 문서
@@ -71,42 +76,61 @@ slaymaple/
## 게임 프레임워크 현황 ## 게임 프레임워크 현황
`Global/common.gamelogic``/common` 엔티티에 부착된 세 컴포넌트가 전투의 핵심입니다. 현재 전투는 `Global/common.gamelogic``/common` 엔티티에 부착된 **`SlayDeckController` 단일 컴포넌트**로 동작합니다. 모든 카드/덱/전투 관련 산출물(`ui/DefaultGroup.ui` · `RootDesk/MyDesk/SlayDeckController.codeblock` · `common.gamelogic`)은 **`tools/gen-slaydeck.mjs` 단일 소스에서 생성**됩니다(직접 편집 금지, 결정적 출력).
| 컴포넌트 | 역할 | | 컴포넌트 | 상태 | 역할 |
|---|---| |---|---|---|
| `SlayCardCatalog` | 카드 데이터, 시작 덱 구성, 보상 풀, 카드 복제 정의 | | `SlayDeckController` | ✅ 구현됨 | 카드 손패 UI 전투 — 드로우/버림/재셔플, 에너지, 카드 효과(데미지/방어), 적 HP·방어·의도, 턴 진행, 승패 |
| `SlayRunState` | HP·골드·층수·덱·유물·카드 보상 등 런(run) 영속 데이터 관리 | | `Monster.codeblock` | ✅ 구현됨 | 필드 액션 몬스터(HP·피격·리스폰) — 카드 전투와는 **별개** 시스템 |
| `SlayCombatManager` | 턴 진행, 드로우/버림/소멸 더미, 에너지, 적 의도, 방어도, 데미지, 승패 처리 |
### 프로토타입 흐름 ### 구현된 카드 전투 (단일 전투 루프)
1. `SlayRunState`가 HP 80 · 10장 시작 덱으로 새 런 시작 - **카드 손패 UI**: 에너지 3, 매 턴 5장 드로우, 버림 더미·재셔플, 카드 클릭 사용, 종류별 색상.
2. `SlayCombatManager`가 데모 전투 자동 시작 - **카드 3종**: 타격(피해 6) · 방어(방어도 5) · 강타(피해 10). 각 카드에 `damage`/`block` 수치 필드. 시작 덱 10장.
3. 매 플레이어 턴: 에너지 3 회복, 방어도 초기화, 적 의도 갱신, 5장 드로우 - **전투 상태**: 플레이어 `HP`/`Block`, 적 `HP`/`Block`/`Intent(의도)`. 적 의도는 **결정적 사이클**(공격10 → 공격6 → 방어8)로 다음 행동을 미리 표시.
4. 카드 사용 시 에너지 소모 → 데미지/방어/드로우/에너지/상태이상 적용 → 버림 또는 소멸 - **규칙**: 데미지는 방어도 먼저 차감 후 잔여만 HP에 적용. 플레이어 Block은 턴 시작 시 리셋.
5. 턴 종료 시 손패 버림, 적 의도 실행, 상태이상 처리, 다음 턴 시작 - **턴 흐름**: 카드 사용(`Attack`→적 HP↓, `Skill`→플레이어 Block↑) → 턴 종료 → 적 턴(의도 실행) → 다음 플레이어 턴.
6. 전투 승리 시 잔여 HP 저장, 골드 15 지급, 카드 보상 3종 생성 - **승패**: 적 HP 0 → 승리, 플레이어 HP 0 → 패배. 승패 시 입력 잠금 + 결과 표시(전투 보상 훅 자리 = E 예정).
- **UI(CombatHud)**: 적 패널(이름·HP·방어·의도)·플레이어 패널(HP·방어)·승패 결과 텍스트.
> ⚠️ 플레이어 HP(80)·적 HP(45)·의도 수치(10·6·8)는 **루프 검증용 임시 placeholder**입니다.
> 향후 캐릭터 특성별/몬스터별 데이터로 분리할 예정입니다(아래 D 참조).
### 유용한 스크립트 호출 ### 유용한 스크립트 호출
`/common` 엔티티에 붙은 스크립트에서: `/common` 엔티티(또는 Play Test 컨텍스트)에서:
```lua ```lua
self.Entity.SlayCombatManager:PlayCard(1, 1) local c = _EntityService:GetEntityByPath("/common").SlayDeckController
self.Entity.SlayCombatManager:EndPlayerTurn() c:PlayCard(1) -- 손패 slot 카드 사용
self.Entity.SlayCombatManager:DebugPlayFirstPlayable() c:EndPlayerTurn() -- 턴 종료 → 적 턴 → 다음 턴
self.Entity.SlayRunState:PickReward(1) c:StartCombat() -- 전투 재시작(상태 초기화)
self.Entity.SlayCombatManager:StartCombat("elite")
``` ```
상세 설계는 [`docs/slaymaple_basic_framework.md`](docs/slaymaple_basic_framework.md) 참조. 상세 설계는 [`docs/slaymaple_basic_framework.md`](docs/slaymaple_basic_framework.md) 참조.
--- ---
## 향후 설계 (미구현 — 목표 아키텍처)
아래는 로그라이크 메타까지 확장했을 때의 **목표 컴포넌트 구조**로, 현재는 미구현입니다. (현재는 `SlayDeckController` 하나가 카드 전투만 담당)
| 컴포넌트 (계획) | 역할 |
|---|---|
| `SlayCardCatalog` | 카드 데이터, 시작 덱 구성, 보상 풀, 카드 복제 정의 |
| `SlayRunState` | HP·골드·층수·덱·유물·카드 보상 등 런(run) 영속 데이터 관리 |
| `SlayCombatManager` | 턴 진행, 드로우/버림/소멸 더미, 에너지, 적 의도, 방어도, 데미지, 승패 처리 |
> 위 구조로 가더라도 카드/적 데이터는 `tools/`의 결정적 생성기를 단일 소스로 유지하는 방향을 권장합니다.
---
## 다음 구현 단계 ## 다음 구현 단계
- [ ] HP·방어도·에너지·적 의도·손패 5버튼을 렌더링하는 전투 UI - [x] HP·방어도·에너지·적 의도·손패 카드를 렌더링하는 전투 UI **(완료 — `SlayDeckController` + CombatHud)**
- [ ] 전투/엘리트/상점/휴식/이벤트/보스 노드를 가진 맵 노드 UI - [x] 카드 사용이 실제 데미지/방어/적 의도/승패에 반영되는 단일 전투 루프 **(완료)**
- [ ] `OnCombatStart` / `OnCardPlayed` / `OnTurnStart` / `OnCombatReward` 훅을 가진 유물 시스템 - [ ] 카드/적 데이터를 `data/cards.json` · `data/enemies.json`로 외부화 (D)
- [ ] 적 행동 패턴을 데이터로 정의 (현재 단순 의도 패턴 → 무브셋) - [ ] 전투를 N회 자동 시뮬레이션하는 밸런스 검증 도구 `tools/sim-balance.mjs` (F, D 선행)
- [ ] 런 루프 완성 후 저장/불러오기 - [ ] 전투/엘리트/상점/휴식/이벤트/보스 노드를 가진 맵 노드 UI (E)
- [ ] `OnCombatStart` / `OnCardPlayed` / `OnTurnStart` / `OnCombatReward` 훅을 가진 유물 시스템 (E)
- [ ] 적 행동 패턴을 데이터로 정의 (현재 단순 결정적 의도 사이클 → 무브셋)
- [ ] 런 영속(HP/골드/층/덱/유물) + 저장/불러오기 (E, 루프 end-to-end 후)
--- ---

View File

@@ -1,42 +1,84 @@
# SlayMaple Basic Framework # SlayMaple Basic Framework
This project now has a small deckbuilder roguelike foundation inspired by turn-based card combat games. This project has a working single-combat deckbuilder loop inspired by turn-based
card combat games. Card play is wired to real combat state (enemy/player HP,
block, enemy intent, win/lose).
## Components ## Current Components (implemented)
- `SlayCardCatalog`: Defines card data, starter deck composition, reward pool, and card cloning. - `SlayDeckController`: The single combat component, attached to the `/common`
- `SlayRunState`: Owns persistent run data such as HP, gold, floor, deck, relics, and card rewards. entity in `Global/common.gamelogic`. Handles the card-hand UI (draw/discard/
- `SlayCombatManager`: Runs combat turns, draw/discard/exhaust piles, energy, enemy intents, block, damage, victory, and defeat. reshuffle, energy, card-click play), card effects (damage/block), enemy
HP/block/intent, turn flow, and victory/defeat.
- `Monster.codeblock`: A separate field-action monster system (HP, hit event,
respawn). **Not** part of the card combat.
All three components are attached to the `/common` entity in `Global/common.gamelogic`. All card/deck/combat artifacts (`ui/DefaultGroup.ui`,
`RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic`) are
**generated from a single source, `tools/gen-slaydeck.mjs`** (deterministic
output — do not hand-edit; change the generator and re-run `node
tools/gen-slaydeck.mjs`).
If the Maker session was already open before these files were added, reopen or reload the local workspace so the new codeblock files are imported into the editor state. If the Maker session was already open before these files changed, reload the
local workspace so the regenerated codeblock/UI files are imported into the
editor state.
## Prototype Flow ## Implemented Combat Loop
1. `SlayRunState` starts a new run with 80 HP and a 10-card starter deck. 1. `StartCombat` initializes player (HP 80, Block 0) and a single enemy
2. `SlayCombatManager` starts a demo combat automatically. (HP 45, Block 0) with a deterministic 3-step intent cycle
3. Each player turn refreshes energy to 3, clears block, rolls enemy intent, and draws 5 cards. (Attack 10 → Attack 6 → Defend 8), then starts the first player turn.
4. Playing a card spends energy, applies damage/block/draw/energy/status effects, then sends the card to discard or exhaust. 2. Each player turn refreshes energy to 3, resets player block, and draws 5
5. Ending the turn discards the hand, resolves enemy intent, ticks statuses, and starts the next turn. cards. The enemy's upcoming intent is shown in advance.
6. Winning combat stores the remaining HP back into the run, grants 15 gold, and generates 3 card reward options. 3. Cards: Strike (damage 6), Defend (block 5), Bash (damage 10). Each card has
numeric `damage`/`block` fields; starter deck is 10 cards.
4. Playing a card spends energy: `Attack` reduces enemy HP (block absorbs
first); `Skill` adds player block. The card moves to the discard pile.
5. Ending the turn discards the hand, runs the enemy turn (executes the current
intent — attack the player or gain block), advances the intent index, then
starts the next player turn.
6. Enemy HP 0 → victory; player HP 0 → defeat. On combat end, input is locked
and a result text is shown (combat-reward hook reserved for the roguelike
meta — see Planned below).
> Player HP (80), enemy HP (45), and intent values (10/6/8) are temporary
> placeholders for loop verification. They will move to per-character /
> per-enemy data (see "Data externalization" under Planned).
## Useful Script Calls ## Useful Script Calls
From a script attached to the same `/common` entity: From the `/common` entity (or a Play Test context):
```lua ```lua
self.Entity.SlayCombatManager:PlayCard(1, 1) local c = _EntityService:GetEntityByPath("/common").SlayDeckController
self.Entity.SlayCombatManager:EndPlayerTurn() c:PlayCard(1) -- play the hand card in the given slot
self.Entity.SlayCombatManager:DebugPlayFirstPlayable() c:EndPlayerTurn() -- end turn → enemy turn → next turn
self.Entity.SlayRunState:PickReward(1) c:StartCombat() -- restart combat (reset state)
self.Entity.SlayCombatManager:StartCombat("elite")
``` ```
## Planned (not yet implemented) — Target Architecture
The originally envisioned component split for the full roguelike. Currently
**none of these exist**; `SlayDeckController` covers only the card combat above.
- `SlayCardCatalog`: Card data, starter deck composition, reward pool, card cloning.
- `SlayRunState`: Persistent run data — HP, gold, floor, deck, relics, card rewards.
- `SlayCombatManager`: Turn flow, draw/discard/exhaust piles, energy, enemy
intents, block, damage, victory, and defeat (the role currently played by
`SlayDeckController`).
## Next Implementation Steps ## Next Implementation Steps
- Add a combat UI that renders HP, block, energy, enemy intent, and 5 hand-card buttons. - [x] Combat UI rendering HP, block, energy, enemy intent, and hand cards
- Add a map node UI with combat, elite, shop, rest, event, and boss node types. (done — `SlayDeckController` + CombatHud).
- Add relic definitions and hooks such as `OnCombatStart`, `OnCardPlayed`, `OnTurnStart`, and `OnCombatReward`. - [x] Card play wired to real damage/block/intent/win-lose (done).
- Add enemy move sets as data instead of the current simple intent pattern. - [ ] Externalize card/enemy data to `data/cards.json` / `data/enemies.json`,
- Add save/load once the run loop is playable end to end. injected by the generator.
- [ ] Monte-Carlo balance simulator `tools/sim-balance.mjs` (requires the data
externalization above).
- [ ] Map node UI with combat, elite, shop, rest, event, and boss node types.
- [ ] Relic definitions and hooks (`OnCombatStart`, `OnCardPlayed`,
`OnTurnStart`, `OnCombatReward`).
- [ ] Enemy move sets as data instead of the current deterministic intent cycle.
- [ ] Run persistence (HP/gold/floor/deck/relics) + save/load once the loop is
playable end to end.