Compare commits
1 Commits
098571e9aa
...
feature/ge
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dd016e16d |
@@ -32,10 +32,10 @@
|
||||
{
|
||||
"@type": "script.SlayDeckController",
|
||||
"Enable": true,
|
||||
"Energy": 0,
|
||||
"MaxEnergy": 3,
|
||||
"Turn": 0,
|
||||
"TweenEventId": 0
|
||||
"Energy": 0.0,
|
||||
"MaxEnergy": 3.0,
|
||||
"Turn": 0.0,
|
||||
"TweenEventId": 0.0
|
||||
}
|
||||
],
|
||||
"@version": 1
|
||||
|
||||
3
RULES.md
3
RULES.md
@@ -23,9 +23,8 @@ Claude Code는 `CLAUDE.md`가 이 파일을 임포트하므로 자동 적용된
|
||||
|
||||
- `.claude/settings.json`의 permissions.deny가 위 파일의 Read/Edit/Write 도구 사용을 차단한다 (이 저장소를 열면 자동 적용). deny는 **glob** — `ui/*.ui`·`map/*.map`·`RootDesk/MyDesk/*.codeblock`·`Global/common.gamelogic`·`Global/SectorConfig.config`. 따라서 **메이커 저작 codeblock/UI**(`Monster`·`MonsterAttack`·`PlayerAttack`·`PlayerHit`·`UIPopup`·`UIToast`.codeblock, `ui/PopupGroup.ui`·`ui/ToastGroup.ui`)**도** Read/Edit 금지 — 이들은 생성기가 없으니 **메이커에서** 편집한다(텍스트 도구로 X). codeblock은 한 줄짜리 JSON이라 Read 시 토큰 폭발.
|
||||
- 게임 로직·UI 수정 = **`tools/deck/gen-slaydeck.mjs`(오케스트레이터 + codeblock Lua) 또는 `data/*.json`(데이터)을 수정** → 재생성 → 산출물은 통째로 커밋.
|
||||
- **UI emit은 HUD별 모듈** `tools/deck/hud/*.mjs`(shop·combat·map·deckall·soulshop 등 15종 — **charselect는 제외: Phase 2에서 메이커 저작 stock으로 이관**, 아래), **codeblock 메서드(Lua)는 기능별 모듈** `tools/deck/cb/*.mjs`(boot·state·combat·hand·deckview·items·map·shop 등 17종, 메서드 161개를 연속런으로). **공유분**: UI 헬퍼·상수·데이터·lua 테이블 = `tools/deck/lib/ui-helpers.mjs`·`tools/deck/lib/data.mjs`, method/prop/codeblock 헬퍼·writeCodeblocks 상수 = `tools/deck/lib/codeblock.mjs`. 특정 화면 UI 수정은 `hud/<name>.mjs`, 특정 메서드 수정은 `cb/<name>.mjs`만(의존: orchestrator→{hud,cb}→lib 단방향). prop 103개는 오케스트레이터 writeCodeblocks에 유지. **cb 모듈은 원본 메서드 순서 보존이 바이트동일 조건** — 새 메서드는 해당 기능 모듈의 알맞은 위치에 추가하고 writeCodeblocks의 spread 순서 유지.
|
||||
- **UI emit은 HUD별 모듈** `tools/deck/hud/*.mjs`(charselect·shop·combat·map·deckall·soulshop 등 16종), **codeblock 메서드(Lua)는 기능별 모듈** `tools/deck/cb/*.mjs`(boot·state·combat·hand·deckview·items·map·shop 등 17종, 메서드 161개를 연속런으로). **공유분**: UI 헬퍼·상수·데이터·lua 테이블 = `tools/deck/lib/ui-helpers.mjs`·`tools/deck/lib/data.mjs`, method/prop/codeblock 헬퍼·writeCodeblocks 상수 = `tools/deck/lib/codeblock.mjs`. 특정 화면 UI 수정은 `hud/<name>.mjs`, 특정 메서드 수정은 `cb/<name>.mjs`만(의존: orchestrator→{hud,cb}→lib 단방향). prop 103개는 오케스트레이터 writeCodeblocks에 유지. **cb 모듈은 원본 메서드 순서 보존이 바이트동일 조건** — 새 메서드는 해당 기능 모듈의 알맞은 위치에 추가하고 writeCodeblocks의 spread 순서 유지.
|
||||
- 리팩터 시 **출력 바이트-동일 검증**: `node tools/deck/gen-slaydeck.mjs` 후 `node tools/verify/diffcheck.mjs [ref]`(워킹트리 vs ref(기본 HEAD) 줄바꿈 정규화 비교 — 산출물 경로를 명령줄에 노출 안 해 deny 회피). 산출물 ` M`은 보통 autocrlf churn이니 `git checkout --`로 복원.
|
||||
- **하이브리드(메이커 저작) 화면 — charselect 파일럿(Phase 2)**: `CharacterSelectHud`는 `GENERATED_UI_SECTIONS`에서 빠져 **생성기가 안 만들고 안 덮음** = `ui/DefaultGroup.ui`의 charselect 엔티티는 **메이커에서 시각 편집하는 stock**. 컨트롤러(`cb/charselect.mjs`)가 경로(`CharacterSelectHud/{Warrior,Thief,Mage}Button/Art` 등 — 메이커가 이 경로 유지 필수)로 이미지(`self.ClassPortraits`, `data/characters.json` 시드)·선택테두리·상태를 **런타임 주입**. 즉 레이아웃=메이커, 내용=컨트롤러. charselect 레이아웃 수정은 `hud/`가 아니라 **메이커에서**.
|
||||
- **머지 충돌(gen-slaydeck.mjs)**: 다른 브랜치가 단일체를 수정해 충돌나면, 그쪽 버전(`git checkout --theirs tools/deck/gen-slaydeck.mjs`)을 취해 **콘텐츠 마커 기반으로 재모듈화**(라인인덱스 X — 줄 추가에 안전·export 이름 자동 파생·`const x=[]` 직전 전문 상수 walk-back 포함) 후 `node tools/verify/diffcheck.mjs origin/main`으로 ui·codeblock 바이트-동일 확인(손실 0 증명). codeblock 메서드·patchCommon은 오케스트레이터 잔류라 그쪽 변경은 자동 보존됨.
|
||||
- **보조 생성기**(각자 자기 산출물의 단일 소스 — 위 표의 메인 `gen-slaydeck.mjs` 외):
|
||||
- `tools/camera/gen-camera.mjs` → `MapCamera.codeblock` + map01~05 카메라 부착 (값 `data/camera.json`)
|
||||
|
||||
File diff suppressed because one or more lines are too long
267
data/cards.json
267
data/cards.json
@@ -378,8 +378,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 3 줍니다. 약화를 1 부여합니다.",
|
||||
"weak": 1,
|
||||
"damage": 3,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"damage": 3
|
||||
},
|
||||
"SilentStrike": {
|
||||
"name": "타격",
|
||||
@@ -388,8 +387,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 6 줍니다.",
|
||||
"damage": 6,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 6
|
||||
},
|
||||
"Survivor": {
|
||||
"name": "생존자",
|
||||
@@ -399,8 +397,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 8 얻습니다. 카드를 1장 버립니다.",
|
||||
"block": 8,
|
||||
"discard": 1,
|
||||
"image": "49c8f279bfa64bf3954037f17da0052d"
|
||||
"discard": 1
|
||||
},
|
||||
"SilentDefend": {
|
||||
"name": "수비",
|
||||
@@ -409,8 +406,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 5 얻습니다.",
|
||||
"block": 5,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"block": 5
|
||||
},
|
||||
"Slice": {
|
||||
"name": "칼질",
|
||||
@@ -419,8 +415,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 6 줍니다.",
|
||||
"damage": 6,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 6
|
||||
},
|
||||
"Shiv": {
|
||||
"name": "표창",
|
||||
@@ -431,8 +426,7 @@
|
||||
"desc": "피해를 4 줍니다. 소멸.",
|
||||
"damage": 4,
|
||||
"exhaust": true,
|
||||
"token": true,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"token": true
|
||||
},
|
||||
"DaggerSpray": {
|
||||
"name": "단검 분사",
|
||||
@@ -443,8 +437,7 @@
|
||||
"desc": "모든 적에게 피해를 4만큼 2번 줍니다.",
|
||||
"aoe": true,
|
||||
"damage": 4,
|
||||
"hits": 2,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"hits": 2
|
||||
},
|
||||
"DaggerThrow": {
|
||||
"name": "단검 투척",
|
||||
@@ -455,8 +448,7 @@
|
||||
"desc": "피해를 9 줍니다. 카드를 1장 뽑습니다. 카드를 1장 버립니다.",
|
||||
"draw": 1,
|
||||
"damage": 9,
|
||||
"discard": 1,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"discard": 1
|
||||
},
|
||||
"PoisonedStab": {
|
||||
"name": "독 찌르기",
|
||||
@@ -466,8 +458,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 6 줍니다. 중독을 3 부여합니다.",
|
||||
"poison": 3,
|
||||
"damage": 6,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"damage": 6
|
||||
},
|
||||
"SuckerPunch": {
|
||||
"name": "불의의 일격",
|
||||
@@ -477,8 +468,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 8 줍니다. 약화를 1 부여합니다.",
|
||||
"weak": 1,
|
||||
"damage": 8,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 8
|
||||
},
|
||||
"LeadingStrike": {
|
||||
"name": "선제 타격",
|
||||
@@ -488,8 +478,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 3 줍니다. 표창을 2장 손으로 가져옵니다.",
|
||||
"damage": 3,
|
||||
"addShiv": 2,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShiv": 2
|
||||
},
|
||||
"FollowThrough": {
|
||||
"name": "완수",
|
||||
@@ -498,8 +487,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "피해를 7 줍니다. 손에 다른 카드가 5장 이상 있다면, 1번 추가로 적중합니다.",
|
||||
"damage": 7,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 7
|
||||
},
|
||||
"FlickFlack": {
|
||||
"name": "재주넘기",
|
||||
@@ -510,8 +498,7 @@
|
||||
"desc": "교활. 모든 적에게 피해를 6 줍니다.",
|
||||
"aoe": true,
|
||||
"damage": 6,
|
||||
"sly": true,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"sly": true
|
||||
},
|
||||
"Ricochet": {
|
||||
"name": "도탄",
|
||||
@@ -522,8 +509,7 @@
|
||||
"desc": "교활. 무작위 적에게 피해를 3만큼 4번 줍니다.",
|
||||
"damage": 3,
|
||||
"hits": 4,
|
||||
"sly": true,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"sly": true
|
||||
},
|
||||
"Prepared": {
|
||||
"name": "예비",
|
||||
@@ -533,8 +519,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "카드를 1장 뽑습니다. 카드를 1장 버립니다.",
|
||||
"draw": 1,
|
||||
"discard": 1,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"discard": 1
|
||||
},
|
||||
"Anticipate": {
|
||||
"name": "예측",
|
||||
@@ -543,8 +528,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "이번 턴 동안 민첩을 2 얻습니다.",
|
||||
"dex": 2,
|
||||
"image": "49c8f279bfa64bf3954037f17da0052d"
|
||||
"dex": 2
|
||||
},
|
||||
"Deflect": {
|
||||
"name": "튕겨내기",
|
||||
@@ -553,8 +537,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 4 얻습니다.",
|
||||
"block": 4,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"block": 4
|
||||
},
|
||||
"BladeDance": {
|
||||
"name": "검무",
|
||||
@@ -564,8 +547,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "표창을 3장 손으로 가져옵니다. 소멸.",
|
||||
"addShiv": 3,
|
||||
"exhaust": true,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"exhaust": true
|
||||
},
|
||||
"Backflip": {
|
||||
"name": "공중제비",
|
||||
@@ -575,8 +557,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 5 얻습니다. 카드를 2장 뽑습니다.",
|
||||
"block": 5,
|
||||
"draw": 2,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"draw": 2
|
||||
},
|
||||
"DodgeAndRoll": {
|
||||
"name": "구르기",
|
||||
@@ -585,8 +566,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 4 얻습니다. 다음 턴에, 방어도를 4 얻습니다",
|
||||
"block": 4,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"block": 4
|
||||
},
|
||||
"PiercingWail": {
|
||||
"name": "귀를 찢는 비명",
|
||||
@@ -595,8 +575,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "이번 턴 동안 모든 적이 힘을 6 잃습니다. 소멸.",
|
||||
"draw": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"draw": 1
|
||||
},
|
||||
"CloakAndDagger": {
|
||||
"name": "망토와 단검",
|
||||
@@ -606,8 +585,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "방어도를 6 얻습니다. 표창을 1장 손으로 가져옵니다.",
|
||||
"block": 6,
|
||||
"addShiv": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"addShiv": 1
|
||||
},
|
||||
"DeadlyPoison": {
|
||||
"name": "맹독",
|
||||
@@ -616,8 +594,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "normal",
|
||||
"desc": "중독을 5 부여합니다.",
|
||||
"poison": 5,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"poison": 5
|
||||
},
|
||||
"Snakebite": {
|
||||
"name": "뱀 물기",
|
||||
@@ -627,8 +604,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "보존. 중독을 7 부여합니다.",
|
||||
"poison": 7,
|
||||
"retain": true,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"retain": true
|
||||
},
|
||||
"Untouchable": {
|
||||
"name": "범접 불가",
|
||||
@@ -638,8 +614,7 @@
|
||||
"rarity": "normal",
|
||||
"desc": "교활. 방어도를 6 얻습니다.",
|
||||
"block": 6,
|
||||
"sly": true,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"sly": true
|
||||
},
|
||||
"Skewer": {
|
||||
"name": "꼬챙이",
|
||||
@@ -648,8 +623,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 8만큼 X번 줍니다.",
|
||||
"draw": 1,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"draw": 1
|
||||
},
|
||||
"Backstab": {
|
||||
"name": "배신",
|
||||
@@ -658,8 +632,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "선천성. 피해를 11 줍니다. 소멸.",
|
||||
"damage": 11,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 11
|
||||
},
|
||||
"PreciseCut": {
|
||||
"name": "정밀한 베기",
|
||||
@@ -668,8 +641,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 13 줍니다. 손에 있는 다른 카드 1장당 피해량이 2 감소합니다.",
|
||||
"damage": 13,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 13
|
||||
},
|
||||
"Finisher": {
|
||||
"name": "마무리",
|
||||
@@ -678,8 +650,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "이번 턴에 사용한 공격 카드 1장당 피해를 6 줍니다.",
|
||||
"damage": 6,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 6
|
||||
},
|
||||
"MementoMori": {
|
||||
"name": "메멘토 모리",
|
||||
@@ -688,8 +659,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 9 줍니다. 이번 턴에 버린 카드 1장당 피해량이 4 증가합니다.",
|
||||
"damage": 9,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"damage": 9
|
||||
},
|
||||
"Strangle": {
|
||||
"name": "목 조르기",
|
||||
@@ -698,8 +668,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 8 줍니다. 이번 턴에 카드를 사용할 때마다, 대상 적이 체력을 2 잃습니다.",
|
||||
"damage": 8,
|
||||
"image": "92a5020c978c46bdabab910598118b86"
|
||||
"damage": 8
|
||||
},
|
||||
"Flechettes": {
|
||||
"name": "프레췌",
|
||||
@@ -708,8 +677,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "손에 있는 스킬 카드 1장당 피해를 5 줍니다.",
|
||||
"damage": 5,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"damage": 5
|
||||
},
|
||||
"Pounce": {
|
||||
"name": "덮치기",
|
||||
@@ -718,8 +686,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 12 줍니다. 다음에 사용하는 스킬 카드의 비용이 0 이 됩니다.",
|
||||
"damage": 12,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"damage": 12
|
||||
},
|
||||
"Dash": {
|
||||
"name": "돌진",
|
||||
@@ -729,8 +696,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "방어도를 10 얻습니다. 피해를 10 줍니다.",
|
||||
"block": 10,
|
||||
"damage": 10,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"damage": 10
|
||||
},
|
||||
"Predator": {
|
||||
"name": "천적",
|
||||
@@ -740,8 +706,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 15 줍니다. 다음 턴에, 카드를 2장 뽑습니다.",
|
||||
"draw": 2,
|
||||
"damage": 15,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 15
|
||||
},
|
||||
"Pinpoint": {
|
||||
"name": "정밀 사격",
|
||||
@@ -750,8 +715,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "피해를 15 줍니다. 이번 턴에 스킬을 사용할 때마다 비용이 1 감소합니다.",
|
||||
"damage": 15,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"damage": 15
|
||||
},
|
||||
"CalculatedGamble": {
|
||||
"name": "계산된 도박",
|
||||
@@ -760,8 +724,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "손에 있는 모든 카드를 버린 뒤, 버린 카드의 수만큼 카드를 뽑습니다. 소멸.",
|
||||
"draw": 1,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"draw": 1
|
||||
},
|
||||
"Expose": {
|
||||
"name": "들춰내기",
|
||||
@@ -770,8 +733,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "대상 적의 모든 인공물과 방어도를 제거합니다. 취약을 2 부여합니다. 소멸.",
|
||||
"vuln": 2,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"vuln": 2
|
||||
},
|
||||
"HiddenDaggers": {
|
||||
"name": "숨겨진 단검",
|
||||
@@ -781,8 +743,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "카드를 2장 버립니다. 표창을 2장 손으로 가져옵니다.",
|
||||
"discard": 2,
|
||||
"addShiv": 2,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShiv": 2
|
||||
},
|
||||
"EscapePlan": {
|
||||
"name": "탈출구",
|
||||
@@ -792,8 +753,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "카드를 1장 뽑습니다. 뽑은 카드가 스킬 카드라면, 방어도를 3 얻습니다.",
|
||||
"block": 3,
|
||||
"draw": 1,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"draw": 1
|
||||
},
|
||||
"Acrobatics": {
|
||||
"name": "곡예",
|
||||
@@ -803,8 +763,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "카드를 3장 뽑습니다. 카드를 1장 버립니다.",
|
||||
"draw": 3,
|
||||
"discard": 1,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"discard": 1
|
||||
},
|
||||
"HandTrick": {
|
||||
"name": "손기술",
|
||||
@@ -813,8 +772,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "방어도를 7 얻습니다. 이번 턴 동안 손에 있는 스킬 카드 1장에 교활을 추가합니다.",
|
||||
"block": 7,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"block": 7
|
||||
},
|
||||
"Mirage": {
|
||||
"name": "신기루",
|
||||
@@ -823,8 +781,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "모든 적에게 부여된 중독과 동일한 만큼의 방어도를 얻습니다. 소멸.",
|
||||
"draw": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"draw": 1
|
||||
},
|
||||
"Expertise": {
|
||||
"name": "전문성",
|
||||
@@ -833,8 +790,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "손에 있는 카드가 6장이 될 때까지 카드를 뽑습니다.",
|
||||
"draw": 1,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"draw": 1
|
||||
},
|
||||
"BubbleBubble": {
|
||||
"name": "차오르는 독",
|
||||
@@ -843,8 +799,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "적이 중독을 보유하고 있다면, 중독을 9 부여합니다.",
|
||||
"poison": 9,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"poison": 9
|
||||
},
|
||||
"Blur": {
|
||||
"name": "흐릿함",
|
||||
@@ -853,8 +808,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "방어도를 5 얻습니다. 다음 턴 시작 시 방어도가 사라지지 않습니다.",
|
||||
"block": 5,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"block": 5
|
||||
},
|
||||
"LegSweep": {
|
||||
"name": "다리 걸기",
|
||||
@@ -864,8 +818,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "약화를 2 부여합니다. 방어도를 11 얻습니다.",
|
||||
"block": 11,
|
||||
"weak": 2,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"weak": 2
|
||||
},
|
||||
"UpMySleeve": {
|
||||
"name": "비책",
|
||||
@@ -874,8 +827,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "표창을 3장 손으로 가져옵니다. 이 카드의 비용이 1 감소합니다.",
|
||||
"addShiv": 3,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShiv": 3
|
||||
},
|
||||
"BouncingFlask": {
|
||||
"name": "탄성 플라스크",
|
||||
@@ -884,8 +836,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "무작위 적에게 중독을 3만큼 3번 부여합니다.",
|
||||
"poison": 9,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"poison": 9
|
||||
},
|
||||
"Reflex": {
|
||||
"name": "반사신경",
|
||||
@@ -895,8 +846,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "교활. 카드를 2장 뽑습니다.",
|
||||
"draw": 2,
|
||||
"sly": true,
|
||||
"image": "49c8f279bfa64bf3954037f17da0052d"
|
||||
"sly": true
|
||||
},
|
||||
"Haze": {
|
||||
"name": "아지랑이",
|
||||
@@ -906,8 +856,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "교활. 모든 적에게 중독을 4 부여합니다.",
|
||||
"poison": 4,
|
||||
"sly": true,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"sly": true
|
||||
},
|
||||
"Tactician": {
|
||||
"name": "전략가",
|
||||
@@ -918,8 +867,7 @@
|
||||
"desc": "교활. 을 얻습니다.",
|
||||
"powerEffect": "energyPerTurn",
|
||||
"value": 1,
|
||||
"sly": true,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"sly": true
|
||||
},
|
||||
"WellLaidPlans": {
|
||||
"name": "괜찮은 전략",
|
||||
@@ -929,8 +877,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "내 턴 종료 시, 카드를 최대 1장까지 보존합니다.",
|
||||
"powerEffect": "blockPerTurn",
|
||||
"value": 2,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"value": 2
|
||||
},
|
||||
"InfiniteBlades": {
|
||||
"name": "무한의 검날",
|
||||
@@ -939,8 +886,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "내 턴 시작 시, 표창을 1장 손으로 가져옵니다.",
|
||||
"turnStartShiv": 1,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"turnStartShiv": 1
|
||||
},
|
||||
"Footwork": {
|
||||
"name": "발놀림",
|
||||
@@ -949,8 +895,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "unique",
|
||||
"desc": "민첩을 2 얻습니다.",
|
||||
"dex": 2,
|
||||
"image": "49c8f279bfa64bf3954037f17da0052d"
|
||||
"dex": 2
|
||||
},
|
||||
"Outbreak": {
|
||||
"name": "발병",
|
||||
@@ -962,8 +907,7 @@
|
||||
"aoe": true,
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"damage": 11,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"damage": 11
|
||||
},
|
||||
"NoxiousFumes": {
|
||||
"name": "유독 가스",
|
||||
@@ -974,8 +918,7 @@
|
||||
"desc": "내 턴 시작 시, 모든 적에게 중독을 2 부여합니다.",
|
||||
"poison": 2,
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"value": 1
|
||||
},
|
||||
"Accuracy": {
|
||||
"name": "정밀",
|
||||
@@ -985,8 +928,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "표창의 피해량이 4 증가합니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"value": 1
|
||||
},
|
||||
"PhantomBlades": {
|
||||
"name": "환영검",
|
||||
@@ -996,8 +938,7 @@
|
||||
"rarity": "unique",
|
||||
"desc": "표창이 보존을 얻습니다. 매 턴마다 처음으로 사용하는 표창의 피해량이 9 증가합니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"value": 1
|
||||
},
|
||||
"Speedster": {
|
||||
"name": "스피드스터",
|
||||
@@ -1009,8 +950,7 @@
|
||||
"aoe": true,
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"damage": 2,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"damage": 2
|
||||
},
|
||||
"GrandFinale": {
|
||||
"name": "대단원의 막",
|
||||
@@ -1020,8 +960,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "뽑을 카드 더미에 카드가 없을 때만 사용할 수 있습니다. 모든 적에게 피해를 60 줍니다.",
|
||||
"aoe": true,
|
||||
"damage": 60,
|
||||
"image": "dbdbb1b56ae54672ae68ac6882fff6a2"
|
||||
"damage": 60
|
||||
},
|
||||
"Assassinate": {
|
||||
"name": "암살",
|
||||
@@ -1031,8 +970,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "선천성. 피해를 10 줍니다. 취약을 1 부여합니다. 소멸.",
|
||||
"vuln": 1,
|
||||
"damage": 10,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 10
|
||||
},
|
||||
"EchoingSlash": {
|
||||
"name": "메아리 참격",
|
||||
@@ -1042,8 +980,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "모든 적에게 피해를 10 줍니다. 적을 처치할 때마다 이 효과를 반복합니다.",
|
||||
"aoe": true,
|
||||
"damage": 10,
|
||||
"image": "dbdbb1b56ae54672ae68ac6882fff6a2"
|
||||
"damage": 10
|
||||
},
|
||||
"TheHunt": {
|
||||
"name": "사냥",
|
||||
@@ -1052,8 +989,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "피해를 10 줍니다. 치명타라면, 카드 보상을 추가로 얻습니다. 소멸.",
|
||||
"damage": 10,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 10
|
||||
},
|
||||
"Murder": {
|
||||
"name": "살해",
|
||||
@@ -1062,8 +998,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "피해를 1 줍니다. 이번 전투 동안 뽑은 카드 1장당 피해량이 1 증가합니다.",
|
||||
"damage": 1,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 1
|
||||
},
|
||||
"Malaise": {
|
||||
"name": "불쾌",
|
||||
@@ -1072,8 +1007,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "적이 힘을 X 잃습니다. 약화를 X 부여합니다. 소멸.",
|
||||
"weak": 3,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"weak": 3
|
||||
},
|
||||
"Adrenaline": {
|
||||
"name": "아드레날린",
|
||||
@@ -1084,8 +1018,7 @@
|
||||
"desc": "를 얻습니다. 카드를 2장 뽑습니다. 소멸.",
|
||||
"draw": 2,
|
||||
"powerEffect": "energyPerTurn",
|
||||
"value": 1,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"value": 1
|
||||
},
|
||||
"StormOfSteel": {
|
||||
"name": "강철의 폭풍",
|
||||
@@ -1095,8 +1028,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "손에 있는 모든 카드를 버립니다. 버린 카드의 수만큼 표창을 손으로 가져옵니다.",
|
||||
"discardAll": true,
|
||||
"addShivPerDiscard": true,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShivPerDiscard": true
|
||||
},
|
||||
"ShadowStep": {
|
||||
"name": "그림자 걸음",
|
||||
@@ -1106,8 +1038,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "손에 있는 모든 카드를 버립니다. 다음 턴에, 공격 카드의 피해량이 2배가 됩니다.",
|
||||
"draw": 1,
|
||||
"discardAll": true,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"discardAll": true
|
||||
},
|
||||
"Shadowmeld": {
|
||||
"name": "그림자 은신",
|
||||
@@ -1116,8 +1047,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "이번 턴 동안 얻는 방어도가 2배가 됩니다.",
|
||||
"draw": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"draw": 1
|
||||
},
|
||||
"CorrosiveWave": {
|
||||
"name": "부식성 파도",
|
||||
@@ -1126,8 +1056,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "이번 턴에 카드를 뽑을 때마다, 모든 적에게 중독을 2 부여합니다.",
|
||||
"poison": 2,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"poison": 2
|
||||
},
|
||||
"BladeOfInk": {
|
||||
"name": "잉크 칼날",
|
||||
@@ -1136,8 +1065,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "잉크투성이 표창을 2장 손으로 가져옵니다.",
|
||||
"addShiv": 2,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShiv": 2
|
||||
},
|
||||
"Burst": {
|
||||
"name": "폭주",
|
||||
@@ -1146,8 +1074,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "이번 턴에 다음에 사용하는 스킬 카드가 1번 추가로 사용됩니다.",
|
||||
"draw": 1,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"draw": 1
|
||||
},
|
||||
"KnifeTrap": {
|
||||
"name": "칼날 함정",
|
||||
@@ -1156,8 +1083,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "대상 적에게 소멸된 카드 더미에 있는 모든 표창을 사용합니다.",
|
||||
"draw": 1,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"draw": 1
|
||||
},
|
||||
"BulletTime": {
|
||||
"name": "불릿 타임",
|
||||
@@ -1167,8 +1093,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "이번 턴 동안 더 이상 카드를 뽑을 수 없습니다. 이번 턴 동안 손에 있는 모든 카드를 비용 없이 사용할 수 있습니다.",
|
||||
"powerEffect": "energyPerTurn",
|
||||
"value": 1,
|
||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||
"value": 1
|
||||
},
|
||||
"Nightmare": {
|
||||
"name": "악몽",
|
||||
@@ -1177,8 +1102,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "카드를 1장 선택합니다. 다음 턴에, 그 카드의 복사본을 3장 손으로 가져옵니다. 소멸.",
|
||||
"draw": 1,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"draw": 1
|
||||
},
|
||||
"ToolsOfTheTrade": {
|
||||
"name": "작업 도구",
|
||||
@@ -1190,8 +1114,7 @@
|
||||
"draw": 1,
|
||||
"powerEffect": "energyPerTurn",
|
||||
"value": 1,
|
||||
"discard": 1,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"discard": 1
|
||||
},
|
||||
"Afterimage": {
|
||||
"name": "잔상",
|
||||
@@ -1202,8 +1125,7 @@
|
||||
"desc": "카드를 사용할 때마다, 방어도를 1 얻습니다.",
|
||||
"block": 1,
|
||||
"powerEffect": "blockPerTurn",
|
||||
"value": 2,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"value": 2
|
||||
},
|
||||
"Accelerant": {
|
||||
"name": "촉진제",
|
||||
@@ -1213,8 +1135,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "중독이 1번 추가로 발동합니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"value": 1
|
||||
},
|
||||
"Envenom": {
|
||||
"name": "독 바르기",
|
||||
@@ -1225,8 +1146,7 @@
|
||||
"desc": "공격 카드가 막히지 않은 피해를 줄 때마다, 중독을 1 부여합니다.",
|
||||
"poison": 1,
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"value": 1
|
||||
},
|
||||
"MasterPlanner": {
|
||||
"name": "설계의 대가",
|
||||
@@ -1236,8 +1156,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "스킬 카드를 사용 시, 그 카드가 교활을 얻습니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "c1e19219745e44c39ae6ac2f77e347d9"
|
||||
"value": 1
|
||||
},
|
||||
"Tracking": {
|
||||
"name": "추적",
|
||||
@@ -1247,8 +1166,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "약화 상태의 적이 공격 카드로 받는 피해가 2배가 됩니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"value": 1
|
||||
},
|
||||
"FanOfKnives": {
|
||||
"name": "칼날 부채",
|
||||
@@ -1259,8 +1177,7 @@
|
||||
"desc": "표창이 이제 모든 적을 대상으로 합니다. 표창을 4장 손으로 가져옵니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"addShiv": 4,
|
||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||
"addShiv": 4
|
||||
},
|
||||
"SerpentForm": {
|
||||
"name": "구렁이의 형상",
|
||||
@@ -1271,8 +1188,7 @@
|
||||
"desc": "카드를 사용할 때마다, 무작위 적에게 피해를 4 줍니다.",
|
||||
"powerEffect": "strengthPerTurn",
|
||||
"value": 1,
|
||||
"damage": 4,
|
||||
"image": "19361e72087946b1888684185b40d935"
|
||||
"damage": 4
|
||||
},
|
||||
"Abrasive": {
|
||||
"name": "연마",
|
||||
@@ -1283,8 +1199,7 @@
|
||||
"desc": "교활. 민첩을 1 얻습니다. 가시를 4 얻습니다.",
|
||||
"dex": 1,
|
||||
"thorns": 4,
|
||||
"sly": true,
|
||||
"image": "49c8f279bfa64bf3954037f17da0052d"
|
||||
"sly": true
|
||||
},
|
||||
"Suppress": {
|
||||
"name": "진압",
|
||||
@@ -1294,8 +1209,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "선천성. 피해를 11 줍니다. 약화를 3 부여합니다.",
|
||||
"weak": 3,
|
||||
"damage": 11,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"damage": 11
|
||||
},
|
||||
"WraithForm": {
|
||||
"name": "유령의 형상",
|
||||
@@ -1305,8 +1219,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "불가침을 2 얻습니다. 내 턴 종료 시 민첩을 1 잃습니다.",
|
||||
"powerEffect": "blockPerTurn",
|
||||
"value": 8,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"value": 8
|
||||
},
|
||||
"Flanking": {
|
||||
"name": "측면 공격",
|
||||
@@ -1315,8 +1228,7 @@
|
||||
"class": "bandit",
|
||||
"rarity": "legend",
|
||||
"desc": "이번 턴에 대상 적이 다른 플레이어에게 받는 피해량이 2배가 됩니다.",
|
||||
"draw": 1,
|
||||
"image": "b1360ed0c4b942309d240634b8f36872"
|
||||
"draw": 1
|
||||
},
|
||||
"Sneaky": {
|
||||
"name": "비열함",
|
||||
@@ -1326,8 +1238,7 @@
|
||||
"rarity": "legend",
|
||||
"desc": "교활. 다른 플레이어가 적을 공격할 때마다, 방어도를 1 얻습니다.",
|
||||
"block": 1,
|
||||
"sly": true,
|
||||
"image": "0946f69d84464df29b24b94c744c868d"
|
||||
"sly": true
|
||||
}
|
||||
},
|
||||
"starterDecks": {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
# Phase 2 — 캐릭터 선택 메이커 저작 파일럿 구현 계획
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:executing-plans 로 태스크 단위 구현.
|
||||
|
||||
**Goal:** charselect를 생성중단→stock화(메이커 편집)하고, 캐릭터 이미지를 컨트롤러가 런타임 경로 주입(ClassPortraits)하도록 바꿔 패턴 (b)를 검증한다.
|
||||
|
||||
**Architecture:** ① 이미지 런타임 주입 추가(ClassPortraits + luaCharsTable + RenderCharacterSelect) → ② charselect 생성 중단(GENERATED_UI_SECTIONS/emit 제거 → 기존 엔티티 stock 보존). 컨트롤러는 경로 구동 유지.
|
||||
|
||||
**Tech Stack:** Node ESM 생성기, MSW Lua. 검증 = **count(동작 검증)** + 메이커 플레이테스트(바이트동일 아님 — codeblock·ui 의도적 변경).
|
||||
|
||||
**의존:** Phase 1b(#71) 위 스택(`feature/charselect-maker-pilot`). #70·#71 머지 후 main 리타겟.
|
||||
|
||||
---
|
||||
|
||||
## 검증 메모
|
||||
Phase 2는 codeblock·ui를 **의도적으로 변경**(diffcheck-IDENTICAL 아님). 게이트:
|
||||
- `node tools/deck/gen-slaydeck.mjs` 성공(throw 없음).
|
||||
- `node tools/verify/count.mjs cb ClassPortraits 'ImageRUID = self.ClassPortraits'` → 주입 코드 존재.
|
||||
- `node tools/verify/count.mjs ui CharacterSelectHud/WarriorButton/Art` → charselect 엔티티 ui 잔류(stock).
|
||||
- 미러 테스트 무영향(회귀 확인차 실행).
|
||||
- **최종**: 사용자 메이커 플레이테스트.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: `luaCharsTable()` 신설 (lib/data.mjs)
|
||||
|
||||
**Files:** Modify `tools/deck/lib/data.mjs`
|
||||
|
||||
- [ ] **Step 1:** `luaNodeIconsTable`(:78-81) 바로 뒤에 추가:
|
||||
```js
|
||||
function luaCharsTable() {
|
||||
const rows = Object.entries(CHARS.portraits).map(([c, ruid]) => `\t${c} = ${luaStr(ruid)},`).join('\n');
|
||||
return `self.ClassPortraits = {\n${rows}\n}`;
|
||||
}
|
||||
```
|
||||
- [ ] **Step 2:** `export { ... }`에 `luaCharsTable` 추가.
|
||||
- [ ] **Step 3:** 커밋(아직 미사용 — import 시 검증).
|
||||
|
||||
---
|
||||
|
||||
### Task 2: ClassPortraits 시드 + prop
|
||||
|
||||
**Files:** Modify `tools/deck/cb/boot.mjs`, `tools/deck/cb/run.mjs`, `tools/deck/gen-slaydeck.mjs`
|
||||
|
||||
- [ ] **Step 1:** `cb/boot.mjs`·`cb/run.mjs`의 import에 `luaCharsTable` 추가(`luaNodeIconsTable` 옆, `from '../lib/data.mjs'`).
|
||||
- [ ] **Step 2:** `cb/boot.mjs:8`(`${luaNodeIconsTable()}`) 다음 줄에 `${luaCharsTable()}` 추가. `cb/run.mjs:34` 동일.
|
||||
- [ ] **Step 3:** `gen-slaydeck.mjs:311`(`prop('any', 'NodeIcons'),`) 다음 줄에 `prop('any', 'ClassPortraits'),` 추가.
|
||||
- [ ] **Step 4:** `node tools/deck/gen-slaydeck.mjs` 성공 + `node tools/verify/count.mjs cb ClassPortraits` → ≥2(시드 2회).
|
||||
- [ ] **Step 5:** 산출물 churn 복원(`git checkout --`) — codeblock은 이 시점 변경됨(ClassPortraits 추가)이므로 **복원 안 함**, ui/common만 churn이면 복원. 커밋(소스 + 재생성 codeblock 분리 또는 함께 "산출물 재생성" 명시).
|
||||
|
||||
---
|
||||
|
||||
### Task 3: RenderCharacterSelect 이미지 런타임 주입
|
||||
|
||||
**Files:** Modify `tools/deck/cb/charselect.mjs:13`(RenderCharacterSelect)
|
||||
|
||||
- [ ] **Step 1:** RenderCharacterSelect 본문 **맨 앞**에 3 Art 주입 추가(Python 치환 — 실탭). classId: Warrior→warrior, Mage→magician, Thief→bandit:
|
||||
```lua
|
||||
local warriorArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/WarriorButton/Art")
|
||||
if warriorArt ~= nil and warriorArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["warrior"] ~= nil then
|
||||
warriorArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["warrior"]
|
||||
end
|
||||
local mageArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton/Art")
|
||||
if mageArt ~= nil and mageArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["magician"] ~= nil then
|
||||
mageArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["magician"]
|
||||
end
|
||||
local thiefArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/ThiefButton/Art")
|
||||
if thiefArt ~= nil and thiefArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["bandit"] ~= nil then
|
||||
thiefArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["bandit"]
|
||||
end
|
||||
```
|
||||
(기존 border/status 로직 앞에 prepend. RenderCharacterSelect는 ShowCharacterSelect/SelectClass에서 호출 → 열림·선택 시 멱등 주입.)
|
||||
- [ ] **Step 2:** `node tools/deck/gen-slaydeck.mjs` + `node tools/verify/count.mjs cb 'ImageRUID = self.ClassPortraits'` → 3.
|
||||
- [ ] **Step 3:** 커밋(소스 + 재생성 codeblock).
|
||||
|
||||
---
|
||||
|
||||
### Task 4: charselect 생성 중단 → stock
|
||||
|
||||
**Files:** Modify `tools/deck/lib/ui-helpers.mjs`, `tools/deck/gen-slaydeck.mjs`; Delete `tools/deck/hud/charselect.mjs`
|
||||
|
||||
- [ ] **Step 1:** `lib/ui-helpers.mjs`의 `GENERATED_UI_SECTIONS`·`UI_APPEND_ORDER` 두 배열에서 `'CharacterSelectHud',` 줄 제거(2곳).
|
||||
- [ ] **Step 2:** `gen-slaydeck.mjs`에서 `import { buildCharSelect } from './hud/charselect.mjs';`(:38)와 `emit('CharacterSelectHud', buildCharSelect());`(:229) 제거.
|
||||
- [ ] **Step 3:** `git rm tools/deck/hud/charselect.mjs` (부트스트랩 완료, git 이력에 레퍼런스 잔존).
|
||||
- [ ] **Step 4:** `node tools/deck/gen-slaydeck.mjs` 성공 + `node tools/verify/count.mjs ui CharacterSelectHud/WarriorButton/Art` → **>0**(charselect 엔티티가 stock으로 ui에 잔류). `git status`로 ui 변경 확인(charselect가 생성→stock 전환, 위치 이동 가능 — 정상).
|
||||
- [ ] **Step 5:** 커밋(소스 + 재생성 산출물, 메시지에 "charselect 생성 중단·stock화" 명시).
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 마무리 — RULES·경로계약·회귀·PR
|
||||
|
||||
**Files:** Modify `RULES.md`
|
||||
|
||||
- [ ] **Step 1:** RULES §1에 한 줄: charselect는 **메이커 저작(stock)**이라 생성 안 함 — 컨트롤러가 `ClassPortraits`로 이미지 런타임 주입, 메이커 편집 시 §스펙 경로 유지. (다른 화면은 여전히 hud/cb 생성.)
|
||||
- [ ] **Step 2:** 회귀: `node --test tools/balance/sim-balance.test.mjs` · `node --test tools/map/rogue-map.test.mjs` (exit 0).
|
||||
- [ ] **Step 3:** push → PR(`node tools/git/gitea-pr.mjs create <spec.json>`, base=`feature/cb-modularization`, 한국어).
|
||||
- [ ] **Step 4:** **사용자 메이커 플레이테스트**(워크스페이스 reload 후): 로비→직업선택→3 이미지 컨트롤러 주입 표시→클릭 금색테두리·Status→시작 그 직업→**메이커에서 카드 위치 이동·저장 후 `node gen-slaydeck` 재생성해도 charselect 유지**(stock 비파괴) 확인. 이미지 비표시 시 ClassPortraits 시드/주입 경로 점검.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
- **스펙 커버리지**: ①stock화(T4) ②런타임주입(T1-3: luaCharsTable·시드·prop·RenderCharacterSelect) ③경로구동 유지(무변경) ④경로계약(T5·스펙). 누락 없음.
|
||||
- **플레이스홀더**: luaCharsTable·주입 Lua·제거 라인 구체. 검증=count+playtest(바이트동일 아님 명시).
|
||||
- **타입 일관성**: `self.ClassPortraits`(prop)↔`luaCharsTable`(self.ClassPortraits=)↔RenderCharacterSelect 참조 일치. classId Warrior→warrior/Mage→magician/Thief→bandit 일관.
|
||||
- **순서**: 추가(주입 T1-3) 먼저 → 중단(stock T4). 중단 전엔 생성+주입 공존(무해), 중단 후 stock+주입.
|
||||
- **리스크**: 메이커 경로 변경 시 계약 깨짐(isvalid 가드로 크래시 방지·해당부 미동작). stock 전환 시 ui 위치 이동(렌더 무관).
|
||||
@@ -1,73 +0,0 @@
|
||||
# Phase 2 — 캐릭터 선택 메이커 저작 파일럿 설계
|
||||
|
||||
작성일: 2026-06-16
|
||||
브랜치: `feature/charselect-maker-pilot` (Phase 1b `feature/cb-modularization`/PR #71 위에 스택)
|
||||
|
||||
## 목표
|
||||
|
||||
하이브리드 UI 로드맵의 **패턴 (b)**(메이커 시각 편집) 검증 파일럿. **캐릭터 선택 화면**을 "생성기 소유 → 메이커 소유"로 이관한다:
|
||||
- **레이아웃**(패널·카드 위치·버튼)은 메이커에서 시각 편집(생성기가 안 덮음).
|
||||
- **동적 내용**(캐릭터 이미지·선택 테두리·상태 텍스트)은 `SlayDeckController`가 런타임에 **경로로 주입** = 컨트롤러 내용주입.
|
||||
|
||||
성공 시 Phase 3에서 상점·전체덱 등으로 확장.
|
||||
|
||||
## 현재 구조 (조사 결과)
|
||||
|
||||
- charselect는 **생성 섹션**: `lib/ui-helpers.mjs`의 `GENERATED_UI_SECTIONS`(:17)·`UI_APPEND_ORDER`(:35)에 `'CharacterSelectHud'` 포함. `hud/charselect.mjs`의 `buildCharSelect()`가 엔티티 emit, `upsertUi`가 `emit('CharacterSelectHud', buildCharSelect())`.
|
||||
- **이미지 = 생성 시 주입**: `hud/charselect.mjs:86` `sprite({ dataId: CHARS.portraits[cls.classId], … })`. 런타임 주입 아님.
|
||||
- **컨트롤러는 경로 구동**: `cb/charselect.mjs`의 `RenderCharacterSelect`(각 `{Warrior,Mage,Thief}Button`의 `SpriteGUIRendererComponent.Color`로 선택 테두리 + Status 텍스트), `SelectClass`, `StartNewGame`. 바인딩은 `cb/state.mjs`의 `BindMenuButtons`(경로로 WarriorButton·BackButton·StartButton 등). 표시 토글은 `ShowState`(경로). **이미지 주입은 없음.**
|
||||
- **런타임 시드 모델**: `self.CardFrames`를 `${luaFramesTable()}`로 OnBeginPlay(cb/boot)·StartRun(cb/run)에서 주입 → `ClassPortraits`의 모델.
|
||||
- `upsertUi` 동작: 기존 `.ui` 로드 → 생성 섹션 엔티티 필터아웃 → emit 섹션 재추가. **생성 섹션에서 빠지면 `isGeneratedUiEntity=false`라 필터 안 됨 → 기존 엔티티 보존(stock)**.
|
||||
|
||||
## 상세 설계
|
||||
|
||||
### ① 생성 중단 → stock화 (generate-once-then-stop)
|
||||
- `lib/ui-helpers.mjs` `GENERATED_UI_SECTIONS`·`UI_APPEND_ORDER`에서 `'CharacterSelectHud'` 제거.
|
||||
- `gen-slaydeck.mjs`(upsertUi)에서 `emit('CharacterSelectHud', buildCharSelect())` + 관련 import 제거. `hud/charselect.mjs`는 **삭제**(부트스트랩 완료 — git 이력에 레퍼런스 남음).
|
||||
- 효과: 현재 `DefaultGroup.ui`의 charselect 엔티티가 그대로 **stock**으로 보존 → 메이커 시각 편집 가능, 재생성에 안 덮임.
|
||||
|
||||
### ② 이미지 런타임 주입 (컨트롤러 내용주입 = 패턴 b 핵심)
|
||||
- `lib/data.mjs`에 `luaCharsTable()` 신설(`data/characters.json`의 `portraits` 시드, `luaFramesTable`/`luaNodeIconsTable` 패턴; `self.ClassPortraits = { warrior="…", magician="…", bandit="…" }`).
|
||||
- 주입 지점: `cb/boot.mjs` OnBeginPlay·`cb/run.mjs` StartRun에 `${luaCharsTable()}`(CardFrames 시드 옆) + prop `ClassPortraits`(any) 선언.
|
||||
- `cb/charselect.mjs` `RenderCharacterSelect`에 이미지 주입 추가: 각 `{key}Button/Art` 엔티티의 `SpriteGUIRendererComponent.ImageRUID`를 `self.ClassPortraits[classId]`로 설정(경로별 isvalid 가드). → 메이커 레이아웃(빈/임의 Art)이어도 컨트롤러가 올바른 이미지 채움. **characters.json 데이터 구동 유지.**
|
||||
|
||||
### ③ 경로 구동 유지 (무변경)
|
||||
- 선택 테두리·Status·버튼 바인딩(`RenderCharacterSelect` 색/텍스트·`SelectClass`·`BindMenuButtons`·`StartNewGame`·`ShowState`)은 이미 경로 기반 → 변경 없음.
|
||||
|
||||
### ④ 엔티티 경로 계약 (docs 명시)
|
||||
메이커 편집 시 아래 경로 유지 필수(컨트롤러가 이 경로로 구동; 누락 시 isvalid 가드로 무시되되 그 부분 동작 안 함):
|
||||
```
|
||||
/ui/DefaultGroup/CharacterSelectHud (루트, ShowState 토글)
|
||||
/OpaqueBackdrop /Title /Status
|
||||
/WarriorButton (+ /Art ← 이미지 주입, /NameBanner, /Name)
|
||||
/ThiefButton (+ /Art, /NameBanner, /Name)
|
||||
/MageButton (+ /Art, /NameBanner, /Name)
|
||||
/StartButton /BackButton
|
||||
```
|
||||
(#67로 DeckButton 제거됨.) classId 매핑: Warrior→warrior, Thief→bandit, Mage→magician.
|
||||
|
||||
## 검증 (동작 — 바이트동일 아님)
|
||||
- 생성기: charselect 제거 후 `node tools/deck/gen-slaydeck.mjs` → **charselect 외 산출물 무영향**(`diffcheck`로 codeblock·common 확인; ui는 charselect 섹션만 stock으로 잔류·다른 섹션 동일). charselect 엔티티가 ui에 존재(`count.mjs`).
|
||||
- 메이커 플레이테스트: 로비→직업선택→**3 이미지가 컨트롤러 주입으로 표시**→클릭 시 금색테두리·Status→시작 시 그 직업으로 런→**메이커에서 카드 위치 이동 후 재생성해도 유지** 확인.
|
||||
|
||||
## 범위 밖
|
||||
- 상점·전체덱 등 다른 화면(Phase 3).
|
||||
- 새 UIGroup(.ui) 분리(경로·ShowState 재작업 큼) — DefaultGroup 내 stock으로 충분.
|
||||
- 게임 규칙·다른 화면 변경.
|
||||
|
||||
## 리스크
|
||||
- stock 전환 시 charselect 엔티티의 `.ui` 내 직렬화 위치 이동 가능 → 렌더는 경로/displayOrder 기반이라 무관하나 플레이테스트로 확인.
|
||||
- 메이커가 경로를 바꾸면 계약 깨짐 → 경로 표로 가드. isvalid 가드로 크래시는 방지.
|
||||
- 의존: Phase 1b(cb/charselect·boot·run) 위 스택. #70·#71 머지 후 main 리타겟.
|
||||
|
||||
## 변경 파일 요약
|
||||
| 파일 | 변경 |
|
||||
|---|---|
|
||||
| `tools/deck/lib/ui-helpers.mjs` | `GENERATED_UI_SECTIONS`·`UI_APPEND_ORDER`에서 CharacterSelectHud 제거 |
|
||||
| `tools/deck/gen-slaydeck.mjs` | upsertUi에서 charselect emit·import 제거 |
|
||||
| `tools/deck/hud/charselect.mjs` | **삭제** |
|
||||
| `tools/deck/lib/data.mjs` | `luaCharsTable()` 신설 |
|
||||
| `tools/deck/cb/boot.mjs`·`cb/run.mjs` | `${luaCharsTable()}` 시드 + ClassPortraits prop |
|
||||
| `tools/deck/cb/charselect.mjs` | `RenderCharacterSelect`에 Art ImageRUID 주입 |
|
||||
| `docs/...charselect 경로 계약` | 경로 표(이 스펙 §④) |
|
||||
| `ui/DefaultGroup.ui`·codeblock | 재생성(charselect는 stock 잔류) |
|
||||
@@ -1,12 +1,11 @@
|
||||
import { method, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from '../lib/codeblock.mjs';
|
||||
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaCharsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
||||
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
||||
import { UI_FILE, COMMON_FILE, UI_ROOT, GENERATED_UI_SECTIONS, UI_APPEND_ORDER, DISABLED_STOCK_CONTROLS, TRANSPARENT, DARK, GOLD, ATTACK, DEFEND, SKILL, DAMAGE_DIGIT_RUIDS, DAMAGE_POP_MAX_DIGITS, DAMAGE_POP_DIGIT_W, DAMAGE_POP_DIGIT_H, DAMAGE_POP_DIGIT_SPACING, MAX_MONSTERS, HEAD_OFFSET_Y, HP_BAR_W, WHITE, CARD_NAME_TEXT, CARD_DESC_TEXT, cardFaceLayout, CARD_W, CARD_H, CARD_SPACING, CARD_XS, ALIGN_CENTER, ALIGN_BOTTOM_CENTER, guid, transform, sprite, button, text, scrollLayoutGroup, popupLayerFor, uiOrderFor, displayOrderFor, applySortingOverride, entity, uiPath, sectionRoot, isGeneratedUiEntity, appendUiSection } from '../lib/ui-helpers.mjs';
|
||||
|
||||
export const bootMethods = [
|
||||
method('OnBeginPlay', `${luaCardsTable(CARDS.cards)}
|
||||
${luaFramesTable()}
|
||||
${luaNodeIconsTable()}
|
||||
${luaCharsTable()}
|
||||
${luaSoulShopTable(SOUL_UNLOCKS)}
|
||||
self.SoulUnlocks = {}
|
||||
self.SoulPoints = self.SoulPoints or 0
|
||||
@@ -18,24 +17,10 @@ if lp ~= nil then
|
||||
end
|
||||
_InputService:ConnectEvent(KeyDownEvent, function(e)
|
||||
if e.key == KeyboardKey.LeftControl then
|
||||
self.DebugCtrlDown = true
|
||||
local lp2 = _UserService.LocalPlayer
|
||||
if lp2 ~= nil and lp2.CurrentMapName == "${LOBBY_MAP}" and self.RunActive ~= true then
|
||||
self:PlayerAttackMotion()
|
||||
end
|
||||
elseif e.key == KeyboardKey.LeftShift or e.key == KeyboardKey.RightShift then
|
||||
self.DebugShiftDown = true
|
||||
elseif e.key == KeyboardKey.C then
|
||||
if self.DebugCtrlDown == true and self.DebugShiftDown == true then
|
||||
self:OpenDebugCardPicker()
|
||||
end
|
||||
end
|
||||
end)
|
||||
_InputService:ConnectEvent(KeyUpEvent, function(e)
|
||||
if e.key == KeyboardKey.LeftControl then
|
||||
self.DebugCtrlDown = false
|
||||
elseif e.key == KeyboardKey.LeftShift or e.key == KeyboardKey.RightShift then
|
||||
self.DebugShiftDown = false
|
||||
end
|
||||
end)`),
|
||||
method('ReqLoadAscension', `local ds = _DataStorageService:GetUserDataStorage(userId)
|
||||
|
||||
@@ -10,19 +10,7 @@ self:RenderCharacterSelect()`),
|
||||
self:RenderCharacterSelect()`, [
|
||||
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'className' },
|
||||
]),
|
||||
method('RenderCharacterSelect', `local warriorArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/WarriorButton/Art")
|
||||
if warriorArt ~= nil and warriorArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["warrior"] ~= nil then
|
||||
warriorArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["warrior"]
|
||||
end
|
||||
local mageArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton/Art")
|
||||
if mageArt ~= nil and mageArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["magician"] ~= nil then
|
||||
mageArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["magician"]
|
||||
end
|
||||
local thiefArt = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/ThiefButton/Art")
|
||||
if thiefArt ~= nil and thiefArt.SpriteGUIRendererComponent ~= nil and self.ClassPortraits ~= nil and self.ClassPortraits["bandit"] ~= nil then
|
||||
thiefArt.SpriteGUIRendererComponent.ImageRUID = self.ClassPortraits["bandit"]
|
||||
end
|
||||
local warrior = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/WarriorButton")
|
||||
method('RenderCharacterSelect', `local warrior = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/WarriorButton")
|
||||
if warrior ~= nil and warrior.SpriteGUIRendererComponent ~= nil then
|
||||
if self.SelectedClass == "warrior" then
|
||||
warrior.SpriteGUIRendererComponent.Color = Color(1, 0.82, 0.3, 1)
|
||||
|
||||
@@ -67,13 +67,6 @@ if allDeckClose ~= nil and allDeckClose.ButtonComponent ~= nil then
|
||||
self.AllDeckCloseHandler = allDeckClose:ConnectEvent(ButtonClickEvent, function() self:CloseAllDeck() end)
|
||||
end
|
||||
self:BindClassDeckTabs()
|
||||
for i = 1, 120 do
|
||||
local allCard = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud/Grid/Card" .. tostring(i))
|
||||
if allCard ~= nil and allCard.ButtonComponent ~= nil then
|
||||
local slot = i
|
||||
allCard:ConnectEvent(ButtonClickEvent, function() self:OnAllDeckCardButton(slot) end)
|
||||
end
|
||||
end
|
||||
for i = 1, 10 do
|
||||
local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i))
|
||||
if cardEntity ~= nil and cardEntity.UITouchReceiveComponent ~= nil then
|
||||
|
||||
@@ -94,31 +94,12 @@ if mageTab ~= nil and mageTab.ButtonComponent ~= nil then
|
||||
end`),
|
||||
method('OpenClassDeck', `self.CodexMode = false
|
||||
self.ClassDeckMode = true
|
||||
self.DebugCardPickerMode = false
|
||||
self.DeckAllOpen = true
|
||||
self:SetClassDeckTab(className)
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'className' }]),
|
||||
method('OpenDebugCardPicker', `if self.RunActive ~= true or self.CombatOver == true or self.Hand == nil then
|
||||
self:Toast("전투 중에만 테스트 카드를 추가할 수 있습니다")
|
||||
return
|
||||
end
|
||||
local className = self.SelectedClass
|
||||
if className ~= "warrior" and className ~= "magician" and className ~= "bandit" then
|
||||
className = "bandit"
|
||||
end
|
||||
self.CodexMode = false
|
||||
self.ClassDeckMode = true
|
||||
self.DebugCardPickerMode = true
|
||||
self.DeckAllOpen = true
|
||||
self:SetClassDeckTab(className)
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end
|
||||
self:Toast("테스트 카드 추가 모드")`),
|
||||
method('SetClassDeckTab', `if self.ClassDeckMode ~= true then
|
||||
return
|
||||
end
|
||||
@@ -190,7 +171,6 @@ end
|
||||
self.DeckInspectKind = ""
|
||||
self.ClassDeckMode = false
|
||||
self.ClassDeckClass = ""
|
||||
self.DebugCardPickerMode = false
|
||||
self:RenderClassDeckTabs()
|
||||
self.DeckAllOpen = true
|
||||
self:RenderAllDeck()
|
||||
@@ -209,7 +189,6 @@ if self.ClassDeckMode == true then
|
||||
self.ClassDeckTitle = ""
|
||||
self.ClassDeckClass = ""
|
||||
end
|
||||
self.DebugCardPickerMode = false
|
||||
self:RenderClassDeckTabs()
|
||||
if self.CodexMode == true then
|
||||
self.CodexMode = false
|
||||
@@ -220,9 +199,6 @@ local title = "모든 덱"
|
||||
if self.ClassDeckMode == true then
|
||||
pile = self.ClassDeckCards or {}
|
||||
title = self.ClassDeckTitle
|
||||
if self.DebugCardPickerMode == true then
|
||||
title = title .. " - 테스트 카드 추가"
|
||||
end
|
||||
elseif self.CodexMode == true then
|
||||
pile = self.CodexCards or {}
|
||||
title = "카드 도감"
|
||||
@@ -250,21 +226,4 @@ end`),
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
|
||||
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' },
|
||||
]),
|
||||
method('OnAllDeckCardButton', `if self.DebugCardPickerMode ~= true then
|
||||
return
|
||||
end
|
||||
if self.ClassDeckCards == nil then
|
||||
return
|
||||
end
|
||||
local cardId = self.ClassDeckCards[slot]
|
||||
if cardId == nil or self.Cards == nil or self.Cards[cardId] == nil then
|
||||
return
|
||||
end
|
||||
self:AddCardsToHand(cardId, 1)
|
||||
local c = self.Cards[cardId]
|
||||
local name = cardId
|
||||
if c.name ~= nil then name = c.name end
|
||||
self:Toast("테스트 카드 추가: " .. name)`, [
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
|
||||
]),
|
||||
];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { method, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from '../lib/codeblock.mjs';
|
||||
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaCharsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
||||
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
||||
import { UI_FILE, COMMON_FILE, UI_ROOT, GENERATED_UI_SECTIONS, UI_APPEND_ORDER, DISABLED_STOCK_CONTROLS, TRANSPARENT, DARK, GOLD, ATTACK, DEFEND, SKILL, DAMAGE_DIGIT_RUIDS, DAMAGE_POP_MAX_DIGITS, DAMAGE_POP_DIGIT_W, DAMAGE_POP_DIGIT_H, DAMAGE_POP_DIGIT_SPACING, MAX_MONSTERS, HEAD_OFFSET_Y, HP_BAR_W, WHITE, CARD_NAME_TEXT, CARD_DESC_TEXT, cardFaceLayout, CARD_W, CARD_H, CARD_SPACING, CARD_XS, ALIGN_CENTER, ALIGN_BOTTOM_CENTER, guid, transform, sprite, button, text, scrollLayoutGroup, popupLayerFor, uiOrderFor, displayOrderFor, applySortingOverride, entity, uiPath, sectionRoot, isGeneratedUiEntity, appendUiSection } from '../lib/ui-helpers.mjs';
|
||||
|
||||
export const runMethods = [
|
||||
@@ -32,7 +32,6 @@ self.PlayerJob = ""
|
||||
${luaJobsTable(JOBS)}
|
||||
${luaFramesTable()}
|
||||
${luaNodeIconsTable()}
|
||||
${luaCharsTable()}
|
||||
self:GenerateMap()
|
||||
self:BindButtons()
|
||||
self:AddRelic("${RELICS.startingRelic}")
|
||||
|
||||
@@ -35,6 +35,7 @@ import { buildLobby } from './hud/lobby.mjs';
|
||||
import { buildBoard } from './hud/board.mjs';
|
||||
import { buildSoulShop } from './hud/soulshop.mjs';
|
||||
import { buildMainMenu } from './hud/mainmenu.mjs';
|
||||
import { buildCharSelect } from './hud/charselect.mjs';
|
||||
|
||||
function upsertUi() {
|
||||
const ui = JSON.parse(readFileSync(UI_FILE, 'utf8'));
|
||||
@@ -225,6 +226,7 @@ function upsertUi() {
|
||||
emit('JobSelectHud', buildJobSelect());
|
||||
|
||||
emit('MainMenu', buildMainMenu());
|
||||
emit('CharacterSelectHud', buildCharSelect());
|
||||
|
||||
// ── LobbyHud — 반복 런의 허브. NPC 클릭으로 런시작/도감/영혼상점/게시판 ──
|
||||
emit('LobbyHud', buildLobby());
|
||||
@@ -296,9 +298,6 @@ function writeCodeblocks() {
|
||||
prop('boolean', 'CodexMode', 'false'),
|
||||
prop('any', 'CodexCards'),
|
||||
prop('boolean', 'ClassDeckMode', 'false'),
|
||||
prop('boolean', 'DebugCardPickerMode', 'false'),
|
||||
prop('boolean', 'DebugCtrlDown', 'false'),
|
||||
prop('boolean', 'DebugShiftDown', 'false'),
|
||||
prop('any', 'ClassDeckCards'),
|
||||
prop('string', 'ClassDeckTitle', '""'),
|
||||
prop('string', 'ClassDeckClass', '""'),
|
||||
@@ -310,7 +309,6 @@ function writeCodeblocks() {
|
||||
prop('any', 'Cards'),
|
||||
prop('any', 'CardFrames'),
|
||||
prop('any', 'NodeIcons'),
|
||||
prop('any', 'ClassPortraits'),
|
||||
prop('any', 'ClassToFrame'),
|
||||
prop('number', 'PlayerHp', '0'),
|
||||
prop('number', 'PlayerMaxHp', '80'),
|
||||
|
||||
171
tools/deck/hud/charselect.mjs
Normal file
171
tools/deck/hud/charselect.mjs
Normal file
@@ -0,0 +1,171 @@
|
||||
import { UI_FILE, COMMON_FILE, UI_ROOT, GENERATED_UI_SECTIONS, UI_APPEND_ORDER, DISABLED_STOCK_CONTROLS, TRANSPARENT, DARK, GOLD, ATTACK, DEFEND, SKILL, DAMAGE_DIGIT_RUIDS, DAMAGE_POP_MAX_DIGITS, DAMAGE_POP_DIGIT_W, DAMAGE_POP_DIGIT_H, DAMAGE_POP_DIGIT_SPACING, MAX_MONSTERS, HEAD_OFFSET_Y, HP_BAR_W, WHITE, CARD_NAME_TEXT, CARD_DESC_TEXT, cardFaceLayout, CARD_W, CARD_H, CARD_SPACING, CARD_XS, ALIGN_CENTER, ALIGN_BOTTOM_CENTER, guid, transform, sprite, button, text, scrollLayoutGroup, popupLayerFor, uiOrderFor, displayOrderFor, applySortingOverride, entity, uiPath, sectionRoot, isGeneratedUiEntity, appendUiSection } from '../lib/ui-helpers.mjs';
|
||||
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, luaSoulShopTable, CARDFRAMES, RARITIES, frameRuid, luaFramesTable, luaNodeIconsTable, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, luaRelicsTable, POTIONS, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
||||
|
||||
export function buildCharSelect() {
|
||||
const select = [];
|
||||
select.push(entity({
|
||||
id: guid('menu', 100),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 21,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.05, g: 0.07, b: 0.11, a: 1 }, type: 1, raycast: true }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 190),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud/OpaqueBackdrop',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: TRANSPARENT, type: 1, raycast: false }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 101),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud/Title',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 1,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 760, y: 72 }, pos: { x: 0, y: 355 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '\uCE90\uB9AD\uD130 \uC120\uD0DD', fontSize: 42, bold: true, color: GOLD, alignment: 0 }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 102),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud/Status',
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 2,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 680, y: 44 }, pos: { x: 0, y: 298 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '\uC804\uC0AC\uB97C \uC120\uD0DD\uD558\uACE0 \uC2DC\uC791\uD558\uC138\uC694', fontSize: 22, color: { r: 0.86, g: 0.9, b: 0.94, a: 1 }, alignment: 0 }),
|
||||
],
|
||||
}));
|
||||
const classCards = [
|
||||
{ key: 'Warrior', classId: 'warrior', label: '\uC804\uC0AC', desc: '\uAC15\uD55C \uACF5\uACA9\uACFC \uBC29\uC5B4', x: -360, enabled: true, tint: { r: 0.74, g: 0.32, b: 0.28, a: 1 } },
|
||||
{ key: 'Thief', classId: 'bandit', label: '\uB3C4\uC801', desc: '\uB3C5\u00B7\uB2E8\uAC80\u00B7\uB4DC\uB85C\uC6B0', x: 0, enabled: true, tint: { r: 0.26, g: 0.5, b: 0.34, a: 1 } },
|
||||
{ key: 'Mage', classId: 'magician', label: '\uB9C8\uBC95\uC0AC', desc: '\uB9C8\uBC95 \uC6D0\uAC70\uB9AC \uB51C\uB7EC', x: 360, enabled: true, tint: { r: 0.3, g: 0.4, b: 0.75, a: 1 } },
|
||||
];
|
||||
for (let i = 0; i < classCards.length; i++) {
|
||||
const cls = classCards[i];
|
||||
const base = `/ui/DefaultGroup/CharacterSelectHud/${cls.key}Button`;
|
||||
select.push(entity({
|
||||
id: guid('menu', 110 + i),
|
||||
path: base,
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: 10 + i,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 270, y: 330 }, pos: { x: cls.x, y: 40 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: cls.enabled ? { r: 0.16, g: 0.2, b: 0.26, a: 1 } : { r: 0.11, g: 0.12, b: 0.14, a: 1 }, type: 1, raycast: cls.enabled }),
|
||||
button({ enabled: cls.enabled }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 200 + i),
|
||||
path: `${base}/Art`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 0,
|
||||
components: [
|
||||
transform({ parentW: 270, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 258, y: 318 }, pos: { x: 0, y: 0 } }),
|
||||
sprite({ dataId: CHARS.portraits[cls.classId], color: { r: 1, g: 1, b: 1, a: 1 }, type: 0, raycast: false }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 210 + i),
|
||||
path: `${base}/NameBanner`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 1,
|
||||
components: [
|
||||
transform({ parentW: 270, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 258, y: 60 }, pos: { x: 0, y: -137 } }),
|
||||
sprite({ color: { r: 0, g: 0, b: 0, a: 0.55 }, type: 1, raycast: false }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 120 + i),
|
||||
path: `${base}/Name`,
|
||||
modelId: 'uitext',
|
||||
entryId: 'UIText',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 2,
|
||||
components: [
|
||||
transform({ parentW: 270, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 230, y: 54 }, pos: { x: 0, y: -137 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cls.label, fontSize: 34, bold: true, color: cls.enabled ? GOLD : { r: 0.55, g: 0.58, b: 0.62, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
if (!cls.enabled) {
|
||||
select.push(entity({
|
||||
id: guid('menu', 150 + i),
|
||||
path: `${base}/LockBody`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 3,
|
||||
components: [
|
||||
transform({ parentW: 270, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 76, y: 58 }, pos: { x: 0, y: 4 } }),
|
||||
sprite({ color: { r: 0.78, g: 0.69, b: 0.42, a: 1 }, type: 1 }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 160 + i),
|
||||
path: `${base}/LockShackle`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 4,
|
||||
components: [
|
||||
transform({ parentW: 270, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 54, y: 42 }, pos: { x: 0, y: 48 } }),
|
||||
sprite({ color: { r: 0.78, g: 0.69, b: 0.42, a: 1 }, type: 1 }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
select.push(entity({
|
||||
id: guid('menu', 180),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud/StartButton',
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 20,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 220, y: 68 }, pos: { x: 720, y: -360 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.15, g: 0.2, b: 0.26, a: 1 }, type: 1, raycast: true }),
|
||||
button(),
|
||||
text({ value: '\uC2DC\uC791', fontSize: 30, bold: true, color: GOLD, alignment: 0 }),
|
||||
],
|
||||
}));
|
||||
select.push(entity({
|
||||
id: guid('menu', 230),
|
||||
path: '/ui/DefaultGroup/CharacterSelectHud/BackButton',
|
||||
modelId: 'uibutton',
|
||||
entryId: 'UIButton',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
|
||||
displayOrder: 22,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 180, y: 56 }, pos: { x: -800, y: 430 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.15, g: 0.2, b: 0.26, a: 1 }, type: 1, raycast: true }),
|
||||
button(),
|
||||
text({ value: '\u2190 \uB4A4\uB85C', fontSize: 26, bold: true, color: GOLD, alignment: 0 }),
|
||||
],
|
||||
}));
|
||||
select[0].jsonString.enable = false;
|
||||
return select;
|
||||
}
|
||||
@@ -116,12 +116,11 @@ export function buildDeckAll() {
|
||||
path: cardPath,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 980, parentH: 620, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: ALL_DECK_CARD_W, y: ALL_DECK_CARD_H }, pos: { x: 0, y: 0 } }),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0, raycast: true }),
|
||||
button(),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0 }),
|
||||
],
|
||||
});
|
||||
card.jsonString.enable = false;
|
||||
|
||||
@@ -79,10 +79,6 @@ function luaNodeIconsTable() {
|
||||
const rows = Object.entries(NODEICONS.icons).map(([t, ruid]) => `\t${t} = ${luaStr(ruid)},`).join('\n');
|
||||
return `self.NodeIcons = {\n${rows}\n}`;
|
||||
}
|
||||
function luaCharsTable() {
|
||||
const rows = Object.entries(CHARS.portraits).map(([c, ruid]) => `\t${c} = ${luaStr(ruid)},`).join('\n');
|
||||
return `self.ClassPortraits = {\n${rows}\n}`;
|
||||
}
|
||||
|
||||
// 맵은 런타임 절차 생성(GenerateMap Lua ↔ tools/map/rogue-map.mjs 미러). 정적 data/map.json 제거됨.
|
||||
const MAP_ROWS = 6; // 걷는 행 1..6, 보스 row 7 (depth 최대 7)
|
||||
@@ -198,4 +194,4 @@ function luaDeckTable(deck) {
|
||||
return `self.DrawPile = { ${deck.map(luaStr).join(', ')} }`;
|
||||
}
|
||||
|
||||
export { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, luaSoulShopTable, CARDFRAMES, RARITIES, frameRuid, luaFramesTable, luaNodeIconsTable, luaCharsTable, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, luaRelicsTable, POTIONS, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable };
|
||||
export { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, luaSoulShopTable, CARDFRAMES, RARITIES, frameRuid, luaFramesTable, luaNodeIconsTable, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, luaRelicsTable, POTIONS, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable };
|
||||
|
||||
@@ -14,6 +14,7 @@ const GENERATED_UI_SECTIONS = [
|
||||
'JobChoiceHud',
|
||||
'JobSelectHud',
|
||||
'MainMenu',
|
||||
'CharacterSelectHud',
|
||||
'LobbyHud',
|
||||
'BoardHud',
|
||||
'SoulShopHud',
|
||||
@@ -31,6 +32,7 @@ const UI_APPEND_ORDER = [
|
||||
'DeckInspectHud',
|
||||
'DeckAllHud',
|
||||
'MainMenu',
|
||||
'CharacterSelectHud',
|
||||
'LobbyHud',
|
||||
'BoardHud',
|
||||
'SoulShopHud',
|
||||
|
||||
12324
ui/DefaultGroup.ui
12324
ui/DefaultGroup.ui
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user