4.9 KiB
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.mjs에 rarityForRoll(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디스크립터 자체가 등록 메커니즘.