Files
maplecontest/docs/superpowers/specs/2026-06-12-card-frames-design.md

4.9 KiB
Raw Permalink Blame History

P13 — 커스텀 카드 프레임 설계

날짜: 2026-06-12 (사용자 승인 완료) 브랜치: feature/p13-card-frames

범위

사용자 제작 카드 프레임 이미지(직업 3종 × 등급 3종)를 인게임 카드 UI 전체(손패·보상·상점·덱 조회)에 적용한다. 카드에 등급(rarity)을 도입하고 전투 보상 추첨 확률에 반영한다.

리소스 (임포트 완료 — RUID 수확됨)

원본: C:\Users\jaeoh\Desktop\workspace\source\images\maple\card\*.png (263×366, 카드 비율 180×250과 동일한 0.72) 메이커 로컬 임포트 → RootDesk/MyDesk/<name>.sprite 디스크립터 9종 (커밋 대상).

프레임 normal unique legend
warior 4bb57ef88ef449fdaf958f6cf37fe44b 4f71c124c8bc4e13b5e9fad392995f68 6d741a60c60743cb98ee740a1e2dbfed
mage d788d09f6f50467ebc67f01dec45f9e2 f5def2e8022b4e59a17d3c16414034fe cff71f2e472041ce80c6fbd296f42e2d
bandit 9487b06867bc46269ed1d855420f457f b3081fb2fb1445fa90b12b01481a78ef c357d2daf31a489d95b8fa47e50dd879

bandit은 RUID 등록만 하고 보류 (도적 클래스 추가 시 사용).

프레임 슬롯 구조: 좌상단 육각 코스트 · 상단 이름 배너 · 중앙 아트 영역 · 하단 설명 박스.

데이터

data/cardframes.json (신설)

{
  "frames": {
    "warrior":  { "normal": "4bb57ef88ef449fdaf958f6cf37fe44b", "unique": "4f71c124c8bc4e13b5e9fad392995f68", "legend": "6d741a60c60743cb98ee740a1e2dbfed" },
    "magician": { "normal": "d788d09f6f50467ebc67f01dec45f9e2", "unique": "f5def2e8022b4e59a17d3c16414034fe", "legend": "cff71f2e472041ce80c6fbd296f42e2d" },
    "bandit":   { "normal": "9487b06867bc46269ed1d855420f457f", "unique": "b3081fb2fb1445fa90b12b01481a78ef", "legend": "c357d2daf31a489d95b8fa47e50dd879" }
  },
  "classToFrame": {
    "warrior": "warrior", "fighter": "warrior", "page": "warrior", "spearman": "warrior",
    "magician": "magician", "firepoison": "magician", "icelightning": "magician", "cleric": "magician"
  },
  "rewardWeights": { "normal": 70, "unique": 25, "legend": 5 }
}

data/cards.json — 전 카드에 rarity 추가

등급 카드 (32종)
normal (10) Strike, Defend, Bash, WarLeap, Threaten, EnergyBolt, MagicGuard, MagicClaw, Teleport, Slow
unique (17) Brandish, ChargedBlow, Enrage, ComboAttack, RisingAttack, ThunderCharge, BlizzardCharge, PowerGuard, Pierce, IronWall, FireArrow, PoisonBreath, ColdBeam, ChillingStep, Heal, Bless, HolyArrow
legend (5) Rage, Berserk, HyperBody, ElementAmp, ThunderBolt

기준: 시작 덱·기본기 = normal / 강화·2차 전직 주력기 = unique / 파워 카드·전체 공격 = legend.

생성기 검증: rarity 누락 또는 normal|unique|legend 외 값이면 throw. 카드 class가 classToFrame에 없으면 throw.

렌더링 (생성기 — A안: 카드 배경 교체)

  • 카드 루트 스프라이트: 단색 틴트(kind별) → 프레임 ImageRUID(Type 0, 흰색). NamePlate/CostPlate 단색판 제거 — RewardHud 등 생성 섹션은 생성 중단으로 충분, CardHand는 .ui에 잔존하므로 upsert 시 경로 매칭으로 명시 제거.
  • ApplyCardFace(Lua): kind 틴트 분기 제거 → self.CardFrames[self.ClassToFrame[c.class]][c.rarity] 적용. CardFrames/ClassToFrame는 OnBeginPlay에서 Lua 테이블 주입 + prop('any', …) 선언(LIA 1114 예방).
  • 자식 레이아웃 공용 헬퍼 cardFaceLayout(W) 신설 — 중복 5곳(손패 523·조회 787·전체덱 928·보상 1443·상점 1660 부근) 일괄 적용. 180×250 기준값(스케일 s=W/180):
    • Cost: pos(-68, 103)·size 44·font 26 (현 위치와 거의 일치)
    • Name: pos(4, 97)·size 150×26·font 18 — 상단 배너로 이동
    • Art: pos(0, 16)·size 110 — 중앙 아트 영역 확대
    • Desc: pos(0, -85)·size 152×64·font 16 — 하단 박스
    • 초깃값이며 메이커 스크린샷으로 미세 튜닝.
  • 정적 프리뷰(Card1~5)도 동일 프레임 적용.

보상 가중 추첨

OfferReward(Lua): 풀을 rarity 버킷으로 분류 후 1~100 롤 — ≤70 normal / ≤95 unique / >95 legend. 해당 버킷이 비면 전체 풀 폴백. 상점·전투 계산은 변경 없음 (sim-balance 전투 미러 무관).

JS 미러: tools/balance/sim-balance.mjsrarityForRoll(roll) export + 경계 테스트(70/71/95/96).

검증

재생성 → grep -c 카운트(CardFrames·rarity) → 기존 테스트 40건 + 신규 통과 → 메이커 refresh·빌드 0에러 → 플레이 스크린샷(손패 프레임·등급 색 구분·보상·덱 조회) → 텍스트 위치 튜닝.

주의 (이번 세션 실측)

  • maker_save 시 메이커가 산출물을 재직렬화(0→0.0 등)하고 Mislocated/로 엔티티를 옮길 수 있음 → 임포트 후 .sprite만 남기고 산출물은 git restore로 복원했음. 재발 시 동일 절차.
  • sprite RUID는 map01.map에 등록되지 않고 .sprite 디스크립터 자체가 등록 메커니즘.