Files
maplecontest/docs/superpowers/specs/2026-06-09-data-externalization-design.md
2026-06-09 01:25:25 +09:00

4.1 KiB

카드/적 데이터 외부화 (TODO 항목 D) — 설계

작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO.md 항목 D + gen-slaydeck.mjs 분석. 선행: B(전투 통합)·A(문서 정합) 완료. F(밸런스 시뮬레이터)의 선행 조건.

문제

카드 정의(self.Cards)·시작 덱·적 정의(이름/HP/의도)가 gen-slaydeck.mjsStartCombat Lua 문자열에 하드코딩돼 있다. 카드/적 추가·밸런싱이 생성기 코드 수정을 요구한다.

목표

카드·적 데이터를 외부 JSON으로 분리하고, 생성기가 읽어 codeblock·UI에 주입한다. 데이터만 바꿔 재생성하면 게임에 반영(코드 수정 없이).

향후 방향 (참고)

추후 카드·적 공격은 메이플스토리 IP에 맞춰 디벨롭 예정. 본 스키마는 명시적 desc와 키 기반 확장으로 이를 수용한다(새 카드/적은 JSON 항목 추가로 확장). 본 작업은 현 3종+적1 기준 최소 스키마까지만 — 새 효과 필드(상태이상/드로우 등)는 추가하지 않는다(YAGNI).

단일 소스 원칙

생성물(SlayDeckController.codeblock · ui/DefaultGroup.ui · common.gamelogic)은 gen-slaydeck.mjs가 생성한다. D 이후 데이터의 단일 소스는 data/*.json, 생성 로직의 단일 소스는 gen-slaydeck.mjs. 결정적 출력 유지.

설계

신규 파일

data/cards.json

{
  "cards": {
    "Strike": { "name": "타격", "cost": 1, "kind": "Attack", "damage": 6,  "desc": "피해 6" },
    "Defend": { "name": "방어", "cost": 1, "kind": "Skill",  "block": 5,  "desc": "방어도 5" },
    "Bash":   { "name": "강타", "cost": 2, "kind": "Attack", "damage": 10, "desc": "피해 10" }
  },
  "starterDeck": ["Strike","Strike","Strike","Strike","Strike","Defend","Defend","Defend","Defend","Bash"]
}

data/enemies.json

{
  "enemies": {
    "slime": {
      "name": "슬라임", "maxHp": 45,
      "intents": [
        { "kind": "Attack", "value": 10 },
        { "kind": "Attack", "value": 6 },
        { "kind": "Defend", "value": 8 }
      ]
    }
  },
  "activeEnemy": "slime"
}
  • desc는 명시적(작성자 작성). kind"Attack" 또는 "Skill". 효과는 damage/block.
  • activeEnemy로 현재 단일 전투의 적을 데이터에서 지정. 향후 E(맵 노드)에서 노드별 선택으로 확장.

생성기(gen-slaydeck.mjs) 변경

  1. 상단에서 readFileSync+JSON.parsedata/cards.json·data/enemies.json 로드.
  2. 검증(fail-fast): starterDeck의 모든 id가 cards에 존재해야 함; activeEnemyenemies에 존재해야 함. 아니면 명확한 에러로 process.exit(1)(또는 throw).
  3. writeCodeblocks()StartCombat에서:
    • self.Cards = {...}cards에서 생성(Lua 테이블 직렬화 헬퍼).
    • self.DrawPile = {...}starterDeck에서 생성.
    • self.EnemyName/EnemyMaxHp/EnemyIntents/EnemyIntentIndexenemies[activeEnemy]에서 생성.
    • codeblock 속성 EnemyMaxHp DefaultValue도 데이터 값으로.
  4. upsertUi()의 DeckHud 카드 미리보기 배열·CombatHud 초기 텍스트(적 이름·HP n/n·첫 의도)를 동일 데이터에서 파생.
  5. Lua 문자열 직렬화 시 한글/따옴표 이스케이프 주의(데이터 값은 따옴표·역슬래시 없는 단순 문자열 가정, 필요 시 escape 헬퍼).

데이터 흐름

data/*.jsongen-slaydeck.mjs(로드·검증·직렬화) → SlayDeckController.codeblock(Lua 테이블)

  • ui/DefaultGroup.ui(초기 텍스트) → 메이커 런타임.

검증

  • node tools/gen-slaydeck.mjs 정상; JSON 유효; 2회 실행 결정적.
  • data/cards.json에서 카드 1장 수치만 변경 → 재생성 → codeblock의 해당 카드 수치 변경 (생성기/codeblock 직접 수정 없이).
  • 잘못된 데이터(starterDeck에 없는 id, 잘못된 activeEnemy) → 생성기가 명확히 실패.
  • 메이커 Play: 기존 B 동작과 동일(데이터 동치이므로 회귀 없음).

범위 밖 (금지)

  • 새 효과 필드(상태이상·드로우·복합효과) 추가. 새 카드 종류 대량 추가. F(시뮬레이터)·E(메타).