Refine rogue progression and card pools

This commit is contained in:
2026-06-29 23:34:19 +09:00
parent b4a4560678
commit 95d6155086
13 changed files with 276 additions and 380 deletions

View File

@@ -6,7 +6,7 @@ const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
// 검증 (fail-fast): 잘못된 데이터면 생성 중단
const CLASSES = {
warrior: { label: '전사', maxHp: 80 },
bandit: { label: '도적', maxHp: 70 },
rogue: { label: '도적', maxHp: 70 },
magician: { label: '마법사', maxHp: 70 },
};
for (const cls of Object.keys(CLASSES)) {
@@ -27,18 +27,35 @@ const JOBS = {
{ id: 'icelightning', name: '위자드(썬·콜)', desc: '광역·빙결 특화\n썬더 볼트(전체)\n콜드 빔 · 칠링 스텝', starter: 'ThunderBolt' },
{ id: 'cleric', name: '클레릭', desc: '회복·축복 특화\n힐 · 블레스\n홀리 애로우', starter: 'Heal' },
],
bandit: [
{ id: 'shiv', name: 'Shiv', desc: 'Many small attacks\nBlade Dance\nAccuracy · After Image', starter: 'BladeDance' },
{ id: 'poisoner', name: 'Poison', desc: 'Poison scaling\nDeadly Poison\nCatalyst · Noxious Fumes', starter: 'DeadlyPoison' },
{ id: 'trickster', name: 'Trickster', desc: 'Draw and tempo\nAcrobatics\nAdrenaline · Tools', starter: 'Acrobatics' },
rogue: [
{ id: 'assassin', name: '어쌔신', desc: '단일 폭딜\n표창 · 검무\n치명타 연계', starter: 'BladeDance' },
{ id: 'thief', name: '시프', desc: '독과 순환\n중독 · 플라스크\n손패 운영', starter: 'DeadlyPoison' },
],
assassin: [
{ id: 'hermit', name: '허밋', desc: '교활한 마무리\n회피 · 잔상\n턴 순환', starter: 'Footwork' },
],
thief: [
{ id: 'thiefmaster', name: '시프마스터', desc: '고급 운영\n교활 · 반복\n스킬 연계', starter: 'MasterPlanner' },
],
};
const CLASS_POOLS = {
warrior: ['warrior', 'fighter', 'page', 'spearman'],
magician: ['magician', 'firepoison', 'icelightning', 'cleric'],
rogue: ['rogue'],
assassin: ['rogue', 'assassin'],
thief: ['rogue', 'thief'],
hermit: ['rogue', 'assassin', 'hermit'],
thiefmaster: ['rogue', 'thief', 'thiefmaster'],
};
for (const [cls, jobs] of Object.entries(JOBS)) {
for (const j of jobs) {
if (!CARDS.cards[j.starter]) throw new Error(`[gen-slaydeck] JOBS.${cls}.${j.id} 대표 카드 없음: ${j.starter}`);
if (!CARDS.cards[j.starter]) throw new Error(`[gen-slaydeck] JOBS.${cls}.${j.id} starter 카드 없음: ${j.starter}`);
}
}
// 영혼(soul) 메타 해금 — 2차 전직 후 보스 클리어로 영혼 적립, 로비 영혼상점에서 구매 → 다음 런 이점
function luaClassPoolsTable() {
const rows = Object.entries(CLASS_POOLS).map(([cls, list]) => '\t' + cls + ' = { ' + list.map((c) => c + ' = true').join(', ') + ' },').join('\n');
return 'self.ClassPools = {\n' + rows + '\n}';
}
const SOUL_UNLOCKS = [
{ key: 'meso', name: '두둑한 지갑', desc: '런 시작 시 메소 +60', cost: 3 },
{ key: 'hp', name: '단련된 육체', desc: '시작 최대 HP +15', cost: 4 },
@@ -101,7 +118,7 @@ if (!/^[0-9a-f]{32}$/.test(NODEICONS.background || '')) throw new Error('[gen-sl
// 캐릭터 선택 초상화 (메이커 임포트 RUID, data/characters.json 단일 소스 — 교체 시 이 파일만 수정 후 재생성)
const CHARS = JSON.parse(readFileSync('data/characters.json', 'utf8'));
for (const c of ['warrior', 'magician', 'bandit']) {
for (const c of ['warrior', 'magician', 'rogue']) {
if (!/^[0-9a-f]{32}$/.test((CHARS.portraits || {})[c] || '')) throw new Error(`[gen-slaydeck] characters.json portraits.${c} RUID 누락/형식오류`);
}
@@ -262,4 +279,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, CLASS_POOLS, SOUL_UNLOCKS, luaSoulShopTable, CARDFRAMES, RARITIES, frameRuid, luaFramesTable, luaNodeIconsTable, luaCharsTable, luaClassPoolsTable, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, luaRelicsTable, POTIONS, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable };