BurningTimesAi/프로젝트/EerieVillage/기획/_build_docx.js

963 lines
53 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// EerieVillage 초기 기획서 v1 — Word 문서 생성 스크립트
// PD 2026-04-26 직접 지시 산출물
// 기획팀장 작성 / anthropic-skills:docx 가이드 준수
const fs = require('fs');
const path = require('path');
// 글로벌 docx 패키지 경로
const docxPath = 'C:\\Users\\PC\\AppData\\Roaming\\npm\\node_modules\\docx';
const {
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
Header, Footer, AlignmentType, PageOrientation, LevelFormat,
TabStopType, TabStopPosition, HeadingLevel, BorderStyle, WidthType,
ShadingType, PageNumber, PageBreak
} = require(docxPath);
// === 폰트·페이지 설정 (한글) ===
const FONT_KO = 'Malgun Gothic';
const FONT_KO_HEAD = 'Malgun Gothic';
// === 공용 헬퍼 ===
const makeRun = (text, opts = {}) => new TextRun({
text,
font: FONT_KO,
size: opts.size || 22, // 11pt
bold: opts.bold || false,
italics: opts.italics || false,
color: opts.color || '000000',
break: opts.break,
});
const para = (text, opts = {}) => new Paragraph({
children: Array.isArray(text)
? text.map(t => typeof t === 'string' ? makeRun(t, opts) : t)
: [makeRun(text, opts)],
spacing: { after: opts.after !== undefined ? opts.after : 120, before: opts.before || 0, line: 320 },
alignment: opts.align || AlignmentType.LEFT,
pageBreakBefore: opts.pageBreakBefore || false,
});
const h1 = (text) => new Paragraph({
heading: HeadingLevel.HEADING_1,
children: [new TextRun({ text, font: FONT_KO_HEAD, size: 36, bold: true, color: '1F3864' })],
spacing: { before: 360, after: 240, line: 360 },
pageBreakBefore: true,
});
const h2 = (text) => new Paragraph({
heading: HeadingLevel.HEADING_2,
children: [new TextRun({ text, font: FONT_KO_HEAD, size: 28, bold: true, color: '2E75B6' })],
spacing: { before: 280, after: 160, line: 340 },
});
const h3 = (text) => new Paragraph({
heading: HeadingLevel.HEADING_3,
children: [new TextRun({ text, font: FONT_KO_HEAD, size: 24, bold: true, color: '375E97' })],
spacing: { before: 200, after: 120, line: 320 },
});
const bullet = (text, opts = {}) => new Paragraph({
numbering: { reference: 'bullets', level: opts.level || 0 },
children: [makeRun(text, opts)],
spacing: { after: 80, line: 320 },
});
const numbered = (text, opts = {}) => new Paragraph({
numbering: { reference: 'numbers', level: opts.level || 0 },
children: [makeRun(text, opts)],
spacing: { after: 80, line: 320 },
});
// === 테이블 헬퍼 ===
const TABLE_WIDTH = 9360; // Letter content width
const cellBorder = { style: BorderStyle.SINGLE, size: 4, color: 'B0B0B0' };
const cellBorders = { top: cellBorder, bottom: cellBorder, left: cellBorder, right: cellBorder };
const makeCell = (content, opts = {}) => {
const children = Array.isArray(content) ? content : [content];
const paragraphs = children.map(c => {
if (c instanceof Paragraph) return c;
return new Paragraph({
children: [new TextRun({ text: String(c), font: FONT_KO, size: 20, bold: opts.bold || false })],
spacing: { after: 0, line: 280 },
alignment: opts.align || AlignmentType.LEFT,
});
});
return new TableCell({
children: paragraphs,
borders: cellBorders,
width: { size: opts.width || 1000, type: WidthType.DXA },
shading: opts.shading
? { fill: opts.shading, type: ShadingType.CLEAR, color: 'auto' }
: undefined,
margins: { top: 80, bottom: 80, left: 120, right: 120 },
verticalAlign: 'center',
});
};
const makeTable = (headers, rows, columnWidths, opts = {}) => {
const headerCells = headers.map((h, i) =>
makeCell(h, { bold: true, shading: 'D5E8F0', width: columnWidths[i], align: AlignmentType.CENTER })
);
const dataRows = rows.map(row =>
new TableRow({
children: row.map((c, i) => makeCell(c, { width: columnWidths[i] })),
})
);
return new Table({
width: { size: TABLE_WIDTH, type: WidthType.DXA },
columnWidths,
rows: [
new TableRow({ children: headerCells, tableHeader: true }),
...dataRows,
],
});
};
// === 본문 콘텐츠 ===
const sections = [];
// --- 표지 ---
sections.push(
new Paragraph({
children: [new TextRun({ text: '', font: FONT_KO, size: 22 })],
spacing: { before: 2400 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '기묘한 고을 : 조선퇴마뎐', font: FONT_KO_HEAD, size: 56, bold: true, color: '1F3864' })],
spacing: { after: 200, line: 480 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: 'EerieVillage: Joseon Exorcist', font: FONT_KO_HEAD, size: 36, bold: true, color: '2E75B6', italics: true })],
spacing: { after: 600, line: 400 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '한 번의 죽음이 곧 성장의 한 사이클이 되는,', font: FONT_KO, size: 26, italics: true })],
spacing: { after: 80, line: 360 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '조선 퇴마사의 Vampire Survivors 순수형 횡스크롤 로그라이크', font: FONT_KO, size: 26, italics: true })],
spacing: { after: 1600, line: 360 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '초기 기획서 v1', font: FONT_KO, size: 32, bold: true, color: '595959' })],
spacing: { after: 1200, line: 360 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '작성일: 2026-04-26', font: FONT_KO, size: 22 })],
spacing: { after: 60, line: 320 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '발행: BurningTimes Studio · 기획팀', font: FONT_KO, size: 22 })],
spacing: { after: 60, line: 320 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '엔진: Unity 6000.3.13f1 LTS · 2D PlatformerMicrogame', font: FONT_KO, size: 22 })],
spacing: { after: 60, line: 320 },
}),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [new TextRun({ text: '플랫폼: 모바일 (iOS · Android)', font: FONT_KO, size: 22 })],
spacing: { after: 0, line: 320 },
}),
);
// --- 목차 ---
sections.push(h1('목차'));
const tocLines = [
'1. 게임 개요',
'2. 재미의 축',
'3. 세계관',
'4. 코어 루프',
'5. 카드 시스템',
'6. 전투 · 라이프',
'7. 진행 시스템 (3축 육성)',
'8. 스테이지 구조',
'9. 모바일 UX',
'10. 향후 계획',
'11. 부록 — 변경 이력 · 기각안 요지 · 참조 문서',
];
tocLines.forEach((line, i) => {
sections.push(new Paragraph({
children: [
new TextRun({ text: line, font: FONT_KO, size: 24 }),
new TextRun({ text: '\t', font: FONT_KO }),
],
tabStops: [{ type: TabStopType.RIGHT, position: TabStopPosition.MAX }],
spacing: { after: 120, line: 360 },
}));
});
// === §1 게임 개요 ===
sections.push(h1('1. 게임 개요'));
sections.push(h2('1.1 정체성'));
sections.push(para('"한 번의 죽음이 곧 성장의 한 사이클이 되는, 조선 퇴마사의 Vampire Survivors 순수형 횡스크롤 로그라이크."', { italics: true, bold: true }));
sections.push(para('플레이어는 이승과 저승의 경계가 허물어진 조선 어느 고을의 마지막 퇴마사가 되어, 안개 너머 요괴들이 출몰하는 경계 영역으로 출정한다. 이동·점프만으로 적을 회피하며 장착한 부적·술식이 자동 발동되는 장면을 감상하는 캐주얼 로그라이크.'));
sections.push(h2('1.2 타겟 유저'));
sections.push(bullet('1차: 20~40대 캐주얼·미드코어 모바일 유저. Vampire Survivors · Brotato 류 "한 판" 루프와 액션 플랫포머 조작감을 모두 원하는 층.'));
sections.push(bullet('2차: 조선·퇴마·민속 호러 세계관에 반응하는 한국·동아시아 유저.'));
sections.push(h2('1.3 플랫폼 · 엔진'));
sections.push(bullet('플랫폼: 모바일 우선 (iOS · Android). PC 빌드는 후속 단계 검토.'));
sections.push(bullet('엔진: Unity 6000.3.13f1 LTS, 2D PlatformerMicrogame 템플릿 기반.'));
sections.push(bullet('해상도 · 디바이스 사양 등 세부는 PD 후속 결정 영역.'));
sections.push(h2('1.4 레퍼런스 게임'));
sections.push(makeTable(
['게임', '활용 강점', '경계할 단점'],
[
['Dead Cells', '2D 액션 플랫포머 + 로그라이크의 금본위. 조작·페이싱·사망 루프 표준', '난이도 깊이가 하드코어 — 본작은 "캐주얼 로그라이크" 포지션 유지'],
['Hades', '달릴 때마다 쌓이는 영속 성장(특성과 동형). 스토리·보스 네이밍 톤의 깊이', '의존 리소스 양이 AAA급 — 파일럿 스코프 초과'],
['Vampire Survivors', '레벨업 카드 3픽의 감정 곡선. 자동 발동 무기의 감상 재미', '입력 복잡도 최소화 — 본작은 점프 조작감은 유지'],
['Brotato', '스테이지 클리어 → 마을 복귀 → 재도전 루프', '탑뷰 고정 — 본작은 횡스크롤로 대체'],
],
[1700, 3800, 3860],
));
sections.push(h2('1.5 세일즈 포인트'));
sections.push(numbered('한 사이클 3~10분의 압축 카타르시스 — 캐주얼 로그라이크 포지션. 모바일 자투리 플레이 친화.'));
sections.push(numbered('"액티브 + 패시브 = 각성" 3분류 덱빌딩 — 자동 발동 무기 + 패시브 강화 + 조건 충족 시 각성 카드 진화.'));
sections.push(numbered('하트 분할 · 각성 진화의 2중 성장 긴장감 — 기본 라이프 4 쿼터 + 젤다 방식 하트 분할 + 카드·성장으로 최대 하트 수 증가.'));
sections.push(numbered('조선·퇴마 세계관 차별화 — 서양식 판타지 레드오션에서 동아시아 로컬 IP로 포지셔닝.'));
// === §2 재미의 축 ===
sections.push(h1('2. 재미의 축'));
sections.push(para('본 게임이 강화하는 재미를 세 축으로 정의한다. 축 1·2를 핵심, 축 3을 보조로 둔다. 모든 기획·수치·컨텐츠 결정은 "어떤 재미를 강화하는가"를 먼저 정의한 뒤 진행한다.'));
sections.push(h2('축 1 — 육성 롤러코스터의 카타르시스'));
sections.push(para('레벨업마다 액티브·패시브 카드 픽으로 빌드가 급격히 성장한다. 액티브가 Lv.5에 도달하고 필요 패시브를 갖춘 상태에서 보물상자를 획득하는 순간, 각성 카드가 발동하며 전능감의 절정을 경험한다. 한 번의 사망으로 런 내 성장이 초기화되는 낙차가 짧은 사이클 안에서 "쌓고·폭발하고·잃는" 감정 곡선을 만든다.'));
sections.push(para('하트 수 증가와 각성 진화라는 2중 성장 축이 빌드 감정 곡선을 고도화한다.'));
sections.push(h2('축 2 — "보며 즐기는" 액션 플랫포머'));
sections.push(para('이동·점프만으로 적을 회피하고, 장착한 액티브 무기가 자동 발동되는 장면을 감상한다. Vampire Survivors 원작의 핵심 재미를 계승하되, 횡스크롤 환경에서 점프 회피·포지셔닝·보물상자 획득 타이밍 관리가 플레이어 스킬을 형성한다. 조작 복잡도는 최소화하지만, 그 안의 의사결정 밀도는 캐주얼 유저에게도 의미 있게 유지된다.'));
sections.push(h2('축 3 — (보조) 영속 성장의 위안'));
sections.push(para('특성 시스템이 "완전 무(無)로 돌아가지 않는다"는 안도감을 제공한다. 축 1·2의 고조와 낙차가 절망으로 굳지 않도록 받쳐주는 축이다. 마을과 NPC, 사당의 로어 텍스트도 이 축을 보강한다.'));
sections.push(h2('재미 판단 근거'));
sections.push(para('기본 라이프 4 쿼터 + 젤다 BotW 방식 하트 분할 구조는 "피격 즉사"의 긴장을 유지하면서도 카드·성장으로 최대 하트 수를 늘리는 육성 보상을 제공하여 재미 축 1을 2중화한다. 공격 버튼 제거(VS 순수형) 결정은 재미 축 2를 "조작의 물성감"에서 "감상·포지셔닝의 물성감"으로 재정의하되, 이동·점프의 즉각 반응은 끝까지 유지한다.'));
// === §3 세계관 ===
sections.push(h1('3. 세계관'));
sections.push(h2('3.1 한 줄 설정'));
sections.push(para('"저승과 이승의 경계가 허물어진 조선 어느 고을에서, 혼자인 퇴마사는 오늘도 죽고 오늘도 다시 일어선다."', { italics: true, bold: true }));
sections.push(h2('3.2 시대 · 지역'));
sections.push(bullet('시대: 조선 후기 가상 역사 — "요괴 성행기". 공식 역사와 분기된 세계로, 모종의 사건으로 이승과 저승의 문이 균열되었다.'));
sections.push(bullet('지역: 마을 명칭 "안개골". 사방이 짙은 안개로 막힌 경계 마을. 이승에도 저승에도 완전히 속하지 않는다.'));
sections.push(bullet('주변 지형: 산신이 머무는 산, 원혼이 쌓인 무덤 골짜기, 수신이 깃든 강, 버려진 관아 등. 각 지형이 스테이지 테마가 된다.'));
sections.push(h2('3.3 톤앤매너'));
sections.push(makeTable(
['영역', '비중', '구체 기준'],
[
['해학 · 캐주얼', '60%', '요괴가 허당스러운 이유로 등장, 주민 대사에 과장·익살, 보스 처치 대사 유머 허용'],
['민속 · 신비', '30%', '설화 원전 어휘 활용, 시각 연출의 이질감(낯선 귀신 형상) 유지'],
['진지 · 공포', '10%', '배경 분위기(어두운 조명·음향)에 한정. 직접적 고어·신체 훼손 금지'],
],
[1700, 1100, 6560],
));
sections.push(h2('3.4 퇴마 체계 — 사방신 결계법'));
sections.push(para('무속(샤머니즘)을 중심 뼈대로, 불교·유교·도교 어휘를 장식 층위로 활용한다. 한국 민속의 체험적 중심은 무속이며, 부적·제례·술식은 실제 조선 민간신앙에서 혼합 사용되었다. 단일 체계보다 혼합이 설화 호기심(축 3)을 극대화한다.'));
sections.push(bullet('퇴마사는 "신들린(접신)" 상태를 스스로 제어하는 능력자. 수호신의 힘을 빌려 싸운다.'));
sections.push(bullet('스킬 카드 = 퇴마 술식. 각 카드는 특정 신격(산신·수신·화신 등)의 힘을 단편적으로 빌리는 행위다.'));
sections.push(bullet('죽음과 부활: 퇴마사는 고을이 존재하는 한 이승으로 귀환한다. "완전한 죽음 없이 기억만 일부 지워진 채 되돌아오는" 설정 → 로그라이크 사망·복귀 루프의 내러티브 정당화.'));
sections.push(bullet('특성 = 신탁(神託). 신들이 퇴마사에게 남긴 흔적이며, 죽어도 신탁은 사라지지 않는다.'));
sections.push(h2('3.5 주인공'));
sections.push(bullet('명칭: "퇴마사" (고유 이름은 후속 단계에서 결정 — 유저 자기투영 여지 보존).'));
sections.push(bullet('계보: 대대로 이 고을을 지키던 무격(巫覡) 가문의 마지막 후손. 스승은 이미 이 싸움에서 목숨을 잃었다.'));
sections.push(bullet('능력 기반: 신 접신 + 부적 술식. 몸이 신의 그릇이 되어 힘을 빌리는 방식.'));
sections.push(h2('3.6 안개골 마을 (코어 루프 허브)'));
sections.push(para('안개골은 단순한 UI 허브가 아니라 "감정 회복 공간"이다. 사망 직후 돌아왔을 때 따뜻한 불빛과 NPC의 환영 대사로 낙차 후 위안을 즉시 제공한다.'));
sections.push(makeTable(
['공간', '역할', '시각 톤'],
[
['주막 "돌아온 이 쉬어가는 곳"', '특성 확인·관리 UI 진입점, 정보꾼 NPC 위치', '허름하지만 따뜻한 불빛'],
['무당집 "신당"', '스킬 카드 관련 특수 서비스, 스승 NPC 위치 (서사 허브)', '오색 천·방울·장식. 마을에서 가장 화려'],
['사당 "고을 수호신 신위"', '귀신 도감·설화 로어 열람', '작은 기와 사당 + 촛불'],
['대장간 "쇠꾼 영감네"', '아이템 강화·교체 서비스', '불빛·연기 이펙트, 망치 소리'],
['스테이지 입구 "경계 문"', '스테이지 선택 UI 진입점', '낡은 성문, 문 너머 짙은 안개'],
],
[2700, 4400, 2260],
));
sections.push(h3('주요 NPC 3종'));
sections.push(bullet('주모 "복선댁" — 정보꾼·안도 NPC. "돌아왔구나" 한 마디로 사망을 "귀환"으로 재정의.'));
sections.push(bullet('스승 혼령 "선사(先師)" — 서사·카드 NPC. 매 귀환마다 한 문장씩 새 정보를 흘림.'));
sections.push(bullet('상인 "약재 행상 이 서방" — 아이템·대장간 NPC. 마을 외 세계 정보 단편을 흘림.'));
sections.push(h2('3.7 보스 3종 — 스테이지별 짝짓기'));
sections.push(makeTable(
['순서', '이름', '원전 모티프', '스테이지 테마', '재미 역할'],
[
['1', '달걀귀신 "알머리 영감"', '달걀귀신 설화', '폐주막', '입문 · 해학. 패턴 학습 유도'],
['2', '두억시니 "솟대 장군"', '두억시니 + 솟대 신앙', '경계 솟대 숲', '위협감 상승. 빌드 검증'],
['3', '이무기 "흑룡 미완성"', '이무기 설화', '강변 · 늪지', '서사 클라이맥스. 연민 + 긴장'],
],
[700, 2600, 2200, 1900, 1960],
));
sections.push(para('각 보스의 외형 모티프는 공격 패턴의 "읽기 단서"를 제공해야 한다는 원칙으로 설계된다. 거대한 통나무 다리 = 지면 진동 공격, 긴 뱀 형태 = S자 횡스크롤 이동 등 시각이 곧 패턴이다.', { italics: true }));
// === §4 코어 루프 ===
sections.push(h1('4. 코어 루프'));
sections.push(h2('4.1 사이클 단위'));
sections.push(numbered('1 스테이지 사이클: 마을 → 스테이지 입장 → 보스 처치 → 보상 → 선택 분기. 성공 사이클.'));
sections.push(numbered('1 런(run) 사이클: 마을 → ... → 사망 → 마을 복귀. 실패 사이클 (레벨·카드 전손, 특성·아이템 잔존).'));
sections.push(numbered('메타 사이클: 여러 런 반복 → 특성 누적 → 점점 더 먼 스테이지까지 도달 가능. 장기 성장 축.'));
sections.push(h2('4.2 플로우 다이어그램'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`[마을 안개골] ← 시작 · 복귀 허브
│
│ 스테이지 선택
▼
[전투 스테이지: 1~M구간] ← 횡스크롤 진행
│
├─ 일반 구간 진행
│ ├─ 적 처치 → 경험치 (액티브 무기 자동 발동으로 처치)
│ │ └─ 레벨업 → 카드 3픽 (액티브/패시브 2축 드로우) → 1장 선택
│ │
│ ├─ 보물상자 획득 → 각성 발동 판정
│ │ └─ 액티브 Lv.5 + 필요 패시브 1개 이상 보유 시 → 각성 카드 발동
│ │
│ ├─ 아이템 획득 → 장착 / 교체 / 포기 (파츠별 1개 제한)
│ │
│ └─ 피격 → 하트 쿼터 차감 → 하트 전소 시 사망 분기
│
└─ 최종 구간: 보스 전투
├─ 보스 처치 → 보상 → [마을 복귀 OR 다음 스테이지] 선택
└─ 보스전 사망 → 사망 분기
[사망 분기]
├─ 캐릭터 레벨 → 1 초기화
├─ 습득 카드 (액티브·패시브·각성) → 전부 소실
├─ 최대 하트 수 → 기본 1개로 복귀 (런 내 증가분 소실)
├─ 장착 아이템 → 유지
├─ 특성 → 유지 (영속)
└─ 강제 [마을 복귀]`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(h2('4.3 사망 시 상태 보존 매트릭스'));
sections.push(makeTable(
['자산', '사망 시 처리', '근거 / 기능'],
[
['레벨', '1로 초기화', '런 내 성장 핵심'],
['스킬 카드 (액티브·패시브·각성)', '전부 소실', '낙차 형성, 재미 축 1'],
['최대 하트 수', '기본 1개로 복귀 (런 내 증가분 소실)', '하트 성장은 런 내만 유효'],
['장착 아이템 (기본 무기 포함)', '유지', '준영속 발자취'],
['특성', '유지 (영구)', '메타 성장 · 재미 축 3'],
['HP (라이프)', '기본 라이프 4 쿼터 (하트 1개 만땅)', '젤다 방식 하트 분할'],
],
[2700, 3300, 3360],
));
sections.push(h2('4.4 분기 포인트 (의사결정 감정)'));
sections.push(numbered('레벨업 카드 3픽 (액티브 · 패시브 2축 드로우) — 매 레벨업마다 발생하는 육성 방향 결정의 핵심 긴장.'));
sections.push(numbered('보물상자 발견·획득 타이밍 — 각성 발동 트리거. 조건 충족 전 vs 후 획득 가치 차이를 따져 포지셔닝.'));
sections.push(numbered('아이템 획득 시 교체·포기 — 파츠 1개 제한의 희소성 부담.'));
sections.push(numbered('보스 처치 후 마을 복귀 vs 다음 스테이지 — 안전 귀환(보상 확정) vs 욕심(더 강한 빌드 · 더 큰 보상).'));
sections.push(numbered('스테이지 선택 (마을) — 특성 · 아이템 보유 상태 기준 도전 난이도 결정.'));
// === §5 카드 시스템 ===
sections.push(h1('5. 카드 시스템'));
sections.push(h2('5.1 시스템 목적'));
sections.push(para('"레벨업마다 무언가 의미 있는 선택을 했다는 느낌, 내 빌드가 점점 나만의 것이 되는 느낌, 조건을 만족했을 때 발동하는 각성의 카타르시스"를 제공한다.'));
sections.push(h2('5.2 카드 3분류 구조'));
sections.push(makeTable(
['분류', '정의', '역할', '규모'],
[
['액티브', '자동 발동 무기·스킬 (판정 박스 · 주기 · 대미지 보유)', '적 처치의 실질 DPS 공급', '15~20종'],
['패시브', '스탯·효과 상시 보정 (공격력 · 속도 · 하트 수 · 회피 등)', '액티브 효율 증폭 + 각성 조건 형성', '25종 이내'],
['각성', '액티브+패시브 조합 충족 시 발동하는 진화 카드', '빌드 완성자 · 클라이맥스', '15~20종'],
],
[1100, 3700, 3360, 1200],
));
sections.push(para('총 카드 풀 규모: 50~65장. 본 v1 시점 60종 컨셉 표(액티브 20 · 패시브 20 · 각성 20)는 Phase 3-B에서 완비됨.'));
sections.push(h2('5.3 코어 루프'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`레벨업 트리거
↓
카드 풀에서 3장 무작위 드로우 (액티브·패시브 2축, 각성은 별도 풀)
↓
픽 UI 표시 — 플레이어가 1장 선택 (강제, 건너뛰기 없음)
↓
선택 카드 → 빌드 슬롯 장착 / 동일 카드 재픽 시 → 스택 업그레이드 (최대 Lv.5)
↓
[병행] 스테이지 진행 중 보물상자 발견·획득
↓
각성 발동 판정: 액티브 Lv.5 + 필요 패시브 1개 이상 보유 시 → 각성 카드 풀에서 드로우·발동
↓
런 종료 (보스 클리어 or 사망) → 카드 전손 · 빌드 초기화`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(h2('5.4 픽 알고리즘 — 액티브 · 패시브 2축 드로우'));
sections.push(makeTable(
['레벨', '액티브 비율', '패시브 비율', '근거'],
[
['Lv 1~2', '70%', '30%', '초반 자동 발동 무기 확보 우선 (DPS 기반 형성)'],
['Lv 3~4', '50%', '50%', '액티브 스택 + 패시브 속성 강화 병행'],
['Lv 5~6', '35%', '65%', '기존 액티브 Lv 업 + 각성 조건 패시브 수집 중심'],
['Lv 7~8', '25%', '75%', '각성 발동 대기 · 추가 각성 준비'],
['Lv 9+', '20%', '80%', '빌드 완성 + 각성 다중 발동 조건 충족 노림'],
],
[1100, 1500, 1500, 5260],
));
sections.push(h2('5.5 업그레이드 — 동일 카드 스택 Lv.1 → Lv.5'));
sections.push(bullet('같은 액티브·패시브 카드를 재픽할 때마다 Lv.N → Lv.N+1 업그레이드 (최대 Lv.5).'));
sections.push(bullet('Lv.5 도달 시 해당 액티브는 각성 조건 중 "액티브 Lv.5"를 충족.'));
sections.push(bullet('업그레이드는 카드 기본 효과의 수치 상향 또는 추가 트리거 중 하나 부여 (양자 동시 부여 금지).'));
sections.push(bullet('Lv.5 이상에서 동일 카드 재픽 시 재화 보상 또는 랜덤 변형으로 처리.'));
sections.push(h2('5.6 각성 발동 조건 (VS 원작 동일)'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`각성 발동 조건 (3종 동시 충족):
1. 해당 액티브 카드가 Lv.5 도달
2. 각성에 필요한 특정 패시브 카드 1개 이상 보유
3. 보물상자 획득
↓
세 조건 동시 충족 시 각성 카드 자동 발동 (액티브가 각성 형태로 진화)`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(bullet('각 각성 카드마다 "필요 액티브 + 필요 패시브 배열" 쌍이 명시된다.'));
sections.push(bullet('각성 발동 시 원 액티브 슬롯은 유지되고, 액티브가 각성 형태로 대체된다.'));
sections.push(bullet('한 런에서 동일 각성 중복 발동 불가.'));
sections.push(bullet('다중 각성 조건 동시 충족 시 플레이어에게 1개 선택 UI 제공.'));
sections.push(h2('5.7 카드 효과 컨셉 — 액티브 카테고리 6종'));
sections.push(makeTable(
['카테고리', '동작', '대표 예시', '재미 축'],
[
['A. 투사체형', '주기마다 전방 또는 가장 가까운 적 방향으로 자동 투사체 발사', '진언부 · 빙결창 · 귀화', '축 2 (감상)'],
['B. 근접·범위형', '플레이어 주변 일정 범위에 주기 판정 생성, 즉시 다중 히트', '학익진 · 쾌속검 · 천둥발', '축 2 (밀집 진입 욕구)'],
['C. 설치·지속형', '이동 경로에 지속 효과 구역 잔존, 진입 적에 반복 피해', '독안개 · 허수아비 · 결계장벽', '축 2 (잔상 감상)'],
['D. 소환형', '소환물(분신·정령) 생성, 독립적으로 자동 공격 또는 어그로 유도', '분신술 · 도깨비불 · 산군상', '축 1·2 (전능감)'],
['E. 상태이상형', '스택 가능한 상태이상(저주·봉인) 부여 → 누적 시 폭발', '봉인부 · 저주문 · 혼백박멸', '축 1 (스택 기대감)'],
['F. 특수 판정형', '확률 기반 강타 또는 순간 무적 자동 발동', '일격필살 · 무영보', '축 1 (예상 밖 쾌감)'],
],
[1500, 3500, 2660, 1700],
));
sections.push(h2('5.8 카드 효과 컨셉 — 패시브 카테고리 5종'));
sections.push(makeTable(
['카테고리', '동작', '대표 예시', '강화 대상'],
[
['P-A. 스탯 상승', '범용 또는 속성 한정으로 대미지·크리티컬 등 상시 상향', '봉황격 · 화염강화 · 집중술', '전체 또는 동일 속성 액티브'],
['P-B. 주기·증폭', '액티브 발동 쿨다운 단축 또는 판정 범위 · 투사체 수 증가', '연사술 · 광역확장 · 투사체증폭', '전체 액티브 (DPS 증폭)'],
['P-C. 생존 강화', '최대 하트 수 +1, 피해 감소 %, 회피 확률, 실드 등', '생명의 꽃 · 강철심장 · 부적방패', 'C 설치형 · D 소환형 보조'],
['P-D. 회복', '적 처치 또는 시간 경과 조건으로 쿼터 자동 회복', '단전수련 · 감로수', '처치 빈도 높은 B · E 카테고리'],
['P-E. 자원 확장', '이동 속도 · 경험치 · 보물상자 확률 등 진행 자원 확장', '질풍보 · 선견지명 · 재물복', '전체 (간접 가속, 각성 트리거)'],
],
[1700, 3500, 2660, 1500],
));
sections.push(h2('5.9 각성 진화 패턴 4종'));
sections.push(numbered('스케일업형 — 원 효과 그대로, 규모(대미지 · 범위 · 속도) 대폭 증가. "갑자기 이펙트가 커졌다." (예: 학익천진, 쾌검비경)'));
sections.push(numbered('새 효과 추가형 — 기존 효과 + 완전히 새 판정·부가 효과 추가. "이게 이제 여기서 이것도 한다고?" (예: 봉령결계, 사령결계)'));
sections.push(numbered('2중·다중 발동형 — 1회 → 2~4회·복수 소환으로 분기. 화면 시각 포화감. (예: 분신만화, 귀화유성, 산군포효)'));
sections.push(numbered('광역 확산·화면 전체형 — 효과가 화면 전체로 확산. 런 클라이맥스. 성능 최적화 필수. (예: 천부경문, 천뢰강림, 독무유역)'));
// === §6 전투 · 라이프 ===
sections.push(h1('6. 전투 · 라이프'));
sections.push(h2('6.1 기본 액션'));
sections.push(makeTable(
['액션', 'PC 입력', '모바일 입력'],
[
['이동', 'WASD / 방향키', '화면 좌측 가상 스틱'],
['점프', 'Space', '화면 우측 점프 버튼'],
],
[1500, 3700, 4160],
));
sections.push(para('공격은 플레이어 수동 입력이 아닌 장착 액티브 카드의 자동 발동으로 처리된다. 공격 버튼 · 공격 키는 존재하지 않는다 (VS 순수형).'));
sections.push(h2('6.2 라이프 — 기본 라이프 4 + 젤다 방식 하트 분할'));
sections.push(para('젤다의 전설 BotW 방식 하트 분할 구조를 계승한다.'));
sections.push(makeTable(
['요소', '값 · 규칙'],
[
['기본 라이프 (최소 단위 HP)', '4 쿼터 (1 하트 = 4 HP)'],
['시작 시 보유 하트 수', '기본 1개 (최대 HP = 4)'],
['피해 1', '1 쿼터 감소 (3/4 하트 표시)'],
['피해 2', '반 하트 감소 (2/4 하트 표시)'],
['피해 3', '3 쿼터 감소 (1/4 하트 표시)'],
['피해 4', '하트 1개 완전 소멸'],
['최대 하트 수 증가', '카드·성장으로 런 내 추가 획득 가능 (예: 하트 2개 = 최대 HP 8)'],
['상한 (하드캡)', '초기 권고 3~5개'],
['사망 조건', '모든 하트 쿼터 0 도달 시'],
],
[3500, 5860],
));
sections.push(h3('하트 분할 시각화 5단계'));
sections.push(makeTable(
['상태', '남은 쿼터', '시각 표현'],
[
['풀', '4/4', '♥ (완전 채워짐)'],
['3/4', '3/4', '♥ (3/4 채워짐, 우하단 1/4 비움)'],
['반', '2/4', '♥ (절반 채워짐, 좌측만)'],
['1/4', '1/4', '♥ (1/4만 채워짐, 좌상단)'],
['빈', '0/4', '♡ (외곽선만)'],
],
[1500, 1500, 6360],
));
sections.push(h2('6.3 무적 시간 (i-frame)'));
sections.push(bullet('피격 직후 무적 시간 0.6초 — 하트 다단 구조 하에서도 연속 히트 방지의 핵심.'));
sections.push(bullet('적 ATK 1~4 범위에서 i-frame 0.6초는 "1회 피격 → 회피 이동 착수" 윈도우 확보.'));
sections.push(bullet('패시브 P15 회피술(심안 통합) Lv.5 풀 스택 시 +0.2초 연장 가능.'));
sections.push(bullet('무적 시각화: 캐릭터 깜박임 (점멸 주기 80ms, 7.5회). 깜박임 종료 = 무적 해제.'));
sections.push(h2('6.4 적 ATK 점진 강화 (스테이지별)'));
sections.push(makeTable(
['스테이지', 'ATK 배율', '예시 (일반 적 약)'],
[
['Stage 1', '× 1.0', 'ATK 1 (1/4 하트 피해)'],
['Stage 2', '× 1.0 (중반부터 +1)', 'ATK 1~2'],
['Stage 3', '× 1.5 (올림)', 'ATK 2 (반 하트)'],
['Stage 4', '× 1.5 (올림)', 'ATK 2~3'],
['Stage 5', '× 2.0 (올림)', 'ATK 4 = 하트 1개 전소'],
],
[1700, 3500, 4160],
));
sections.push(para('설계 의도: 초반은 관대(실수 허용), 후반은 가혹(회피·빌드 필수). 성장 축(하트 수 증가·액티브 강화)과 위협 축이 동반 상승한다.'));
sections.push(h2('6.5 대미지 공식'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`// 플레이어 액티브 → 적
EnemyHPResult = EnemyHP - (
ActiveBaseDamage
× CardMultiplier (1.0~2.5)
× StackLevelFactor (Lv.1=1.0 / Lv.2=1.2 / Lv.3=1.4 / Lv.4=1.6 / Lv.5=2.0)
× AttributePassiveBonus (P02~P05 속성 강화 보유 시, 1.0~1.5)
)
// 각성 발동 시
EnemyHPResult = EnemyHP - (AwakeningBaseDamage × CardMultiplier × AttributePassiveBonus)
// AwakeningBaseDamage = Lv.5 액티브 대비 2~3배
// 적 → 플레이어
PlayerHP -= EnemyATK (쿼터 단위 차감, i-frame 내 피격 무효)
// 최대 하트 수 × 4 쿼터 = 총 HP`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(para('단순성이 설계 의도다. 방어 수치(DEF)는 도입하지 않으며, 패시브 [방어] 카드의 피해 감소 %가 그 역할을 대체한다.', { italics: true }));
// === §7 진행 시스템 ===
sections.push(h1('7. 진행 시스템 (3축 육성)'));
sections.push(para('EerieVillage의 육성 축은 3종이며, 각 축은 손실 범위가 다르게 설계되어 플레이어의 리스크 인지와 감정 곡선을 층화한다.'));
sections.push(makeTable(
['축', '획득 시점', '사망 시 처리', '재미 기여'],
[
['A. 스킬 카드', '런 내 레벨업', '전손', '즉시 육성 · 빌드 실험 · 낙차 핵심'],
['B. 아이템', '런 내 드롭', '유지', '준영속 기반 · 축적된 발자취'],
['C. 특성', '메타 누적', '유지', '영속 성장 · 도전 지속성'],
],
[1700, 2300, 1500, 3860],
));
sections.push(h2('7.1 축 A — 스킬 카드'));
sections.push(bullet('§5 카드 시스템 본문 참조. 레벨업 시 3장 중 1장 선택 강제, 동일 카드 재픽으로 Lv.5까지 스택.'));
sections.push(bullet('태그 기반 시너지 — 동일 태그 K장 이상 빌드 편입 시 패시브 효과 추가 발동. K 임계값과 효과 강도는 밸런스 영역.'));
sections.push(h2('7.2 축 B — 아이템 (파츠 1개 교체식)'));
sections.push(bullet('전투 중 아이템 드롭 → 동일 파츠 기존 장착 시 교체 또는 포기 선택.'));
sections.push(bullet('장착 아이템은 사망 후에도 유지된다.'));
sections.push(bullet('파츠별 1개 제한으로 재화·슬롯 팽창을 방지하고 선택 압박을 유지.'));
sections.push(h2('7.3 축 C — 특성 (영속 성장)'));
sections.push(bullet('특정 조건 충족 시 특성 획득 (조건은 후속 단계에서 확정 — 메타 재화·보스 처치·업적 등 옵션).'));
sections.push(bullet('특성은 사망·레벨 초기화에도 유지되며, 신규 런 시작 시 자동 적용된다.'));
sections.push(bullet('축 C는 "하한 상승"이 아니라 "도전 지속성" 제공이 본질 — 강해지는 느낌과 동시에 더 깊은 스테이지 도전 유도.'));
sections.push(h2('7.4 3축 상호 관계'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`축 A (카드) — 단기 쾌감 (런 내 폭발·전손)
↓ 영향
축 B (아이템) — 중기 기반 (런 간 유지·발자취)
↓ 영향
축 C (특성) — 장기 성장 (영속·도전 확장)`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(bullet('축 A의 전손은 축 C가 있기에 절망이 아니라 "다시 빌드할 준비"로 느껴진다.'));
sections.push(bullet('축 B는 "이번 런은 실패했지만 아이템은 남았다"는 감정의 안전판.'));
sections.push(bullet('축 C는 "여러 런 누적이 의미 있다"는 거시 동기.'));
// === §8 스테이지 구조 ===
sections.push(h1('8. 스테이지 구조'));
sections.push(h2('8.1 단일 스테이지 3단 구성'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`[입장]
▼
[구간 1: 도입] ← 빌드 시드 형성
- 약한 적 중심
- 기본 플랫폼 지형
- 1~2회 레벨업 예상
▼
[구간 2: 전개] ← 빌드 확장
- 중간 강도 적 · 엘리트 1~2기
- 점프 퍼즐 1~2개
- 2~3회 레벨업 예상
- 아이템 드롭 기회 중심
▼
[구간 3: 보스 방] ← 빌드 검증
- 보스 (전용 지형, 고정 카메라 권장)
- 진입 시 후방 차단
- 처치 후 보상 → 분기 선택: 마을 복귀 / 다음 스테이지`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(h2('8.2 보스 방 설계 원칙'));
sections.push(bullet('진입 시 후방 차단으로 긴장 집중.'));
sections.push(bullet('고정 카메라로 횡스크롤 해제 후 아레나 모드 (연출상 임팩트).'));
sections.push(bullet('가독성 높은 텔레그래프 의무 — "랜덤 사망" 방지로 축 2 재미 보호.'));
sections.push(bullet('보스 1종 고정 (페이즈 구조로 깊이 확보). 다수 보스 동시 출현 금지.'));
sections.push(h2('8.3 난이도 곡선 — 레벨업 빈도와 보스 TTK'));
sections.push(makeTable(
['스테이지 종료', '예상 누적 XP', '예상 레벨', '보유 카드 수 (액티브 + 패시브)'],
[
['Stage 1', '~292', 'Lv 3~4', '3~4장'],
['Stage 2', '~584', 'Lv 5~6', '5~6장'],
['Stage 3', '~876', 'Lv 6~7', '6~7장'],
['Stage 4', '~1168', 'Lv 7~8', '7~8장'],
['Stage 5', '~1460', 'Lv 8~9', '8~9장'],
],
[2300, 1900, 1900, 3260],
));
sections.push(para('각성 발동 기대치: Stage 3~4 구간에서 첫 각성 조건 충족 가능 (액티브 Lv.5 도달 + 필요 패시브 1개 + 보물상자 획득).'));
sections.push(para('보스 TTK 목표: 50~90초 (캐주얼 5~10분 런의 15~20% 시간). 빌드 없는 초반 약 75초, 카드 2~3장 빌드 후 약 50초.'));
sections.push(h2('8.4 적 등급 체계 (초안)'));
sections.push(makeTable(
['등급', 'HP', 'ATK (쿼터 피해)', '쿨다운', '이동 속도', '비고'],
[
['일반 적 (약)', '40', '1', '1.2 s', '2.0 m/s', '첫 구간 전용. 3~4타 처치'],
['일반 적 (중)', '60', '1~2', '0.9 s', '3.0 m/s', '구간 2 주력'],
['엘리트', '120', '2', '0.7 s', '3.5 m/s', '구간 2 후반~보스 전'],
['보스 페이즈 1', '400', '2~3', '1.5 s', '2.5 m/s', '스테이지 보스'],
['보스 페이즈 2', '(HP 50% 이하)', '3~4', '1.2 s', '3.0 m/s', '위험 구간'],
],
[1900, 1100, 1900, 1100, 1300, 2060],
));
// === §9 모바일 UX ===
sections.push(h1('9. 모바일 UX'));
sections.push(h2('9.1 조작 스킴 (VS 순수형)'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`┌──────────────────────────────┐ ← 화면 상단 (HUD 영역)
│ [HUD] │
├──────────────────────────────┤
│ │
│ 플레이 영역 │
│ │
├────────────┬─────────────────┤
│ [가상스틱] │ [점프] │ ← 입력 영역 (공격 버튼 없음)
│ 왼손 엄지 │ 오른손 엄지 │
└────────────┴─────────────────┘
Safe Area (Home Indicator 회피)`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(bullet('가상 스틱 반지름 60dp 후보. 조이스틱 중심은 터치 다운 위치 추종.'));
sections.push(bullet('점프 버튼은 우측 중앙 단독 대형 배치 (시인성·터치 성공률).'));
sections.push(bullet('입력 영역 투명도 70% / 입력 시 100%. Safe Area 패딩 의무.'));
sections.push(bullet('Input 감지 후 0.1초 이내 캐릭터 반응 의무 — 지연 시 입력 불인식 오인.'));
sections.push(h2('9.2 인게임 HUD'));
sections.push(makeTable(
['우선순위', '요소', '이유'],
[
['1', '하트 UI (생존 여부 + 잔량)', '하트 분할 → 쿼터 단위 잔량 즉시 인지 필수'],
['2', '레벨 + 경험치 게이지', '레벨업 카드 픽 트리거 예고'],
['3', '액티브 슬롯 (장착 무기 + Lv 표기)', '자동 발동 감상 재미의 기준 정보'],
['4', '패시브 슬롯 (장착 패시브 + Lv 표기)', '빌드 확인용. 전투 흐름 방해 금지'],
['5', '각성 조건 진행도', '액티브 Lv.5 도달 시 "각성 대기" 표시'],
],
[1300, 3700, 4360],
));
sections.push(h3('HUD 와이어프레임'));
sections.push(new Paragraph({
children: [new TextRun({
text:
`┌──────────────────────────────┐
│ [Lv.3 ████████░░] [♥ ♥] │ ← 좌상단: 레벨·경험치 + 하트 분할 UI
│ [⚙] │ ← 우상단: 일시정지
├──────────────────────────────┤
│ 플레이 영역 │
├────────────┬─────────────────┤
│ 액티브: │ 패시브: │ ← 하단: 카드 슬롯 분리 (반투명)
│ [A1][A2] │ [P1][P2][P3] │
│ [A3][★★] │ [P4][P5][P6] │ ← ★ = 각성 대기
└────────────┴─────────────────┘`,
font: 'Consolas', size: 18,
})],
spacing: { after: 200, line: 280 },
}));
sections.push(h2('9.3 레벨업 카드 픽 UI'));
sections.push(bullet('트리거: 경험치 만렙 → Time.timeScale = 0 일시정지 → 카드 픽 화면 진입.'));
sections.push(bullet('Canvas: CardPickCanvas.prefab (Additive Load), Unscaled Time 사용으로 timeScale 0에서도 정상 동작.'));
sections.push(bullet('각 카드 상단에 액티브·패시브 분류 아이콘 명시 (색상·기호) — "어떤 슬롯으로 들어갈 카드인가" 즉시 인지.'));
sections.push(bullet('이미 장착한 카드는 "Lv.N → Lv.N+1" 표기, 미장착은 "신규" 표기.'));
sections.push(h2('9.4 피드백 — 피해량별 차등'));
sections.push(makeTable(
['피해량', '진동 (모바일)', '시각 피드백'],
[
['1 (1 쿼터)', '짧은 진동 40ms', '테두리 연한 섬광'],
['2 (반 하트)', '중간 진동 80ms', '테두리 중간 섬광'],
['3 (3/4 하트)', '긴 진동 120ms', '테두리 강한 섬광'],
['4 (하트 전소)', '강한 진동 160ms', '테두리 최대 섬광 + 화면 살짝 흔들림'],
],
[2300, 3300, 3760],
));
// === §10 향후 계획 ===
sections.push(h1('10. 향후 계획'));
sections.push(h2('10.1 PD 후속 검토 영역'));
sections.push(numbered('BM · 과금 구조 — 인앱 결제 모델, 광고 도입 여부, 유료 재화 설계.'));
sections.push(numbered('아트 톤 · UI 톤 — 일러스트 방향성, UI 컬러 팔레트, 폰트, 마을·스테이지 배경 비주얼 가이드.'));
sections.push(numbered('퇴마사 고유 이름 확정 — 현재는 "유저 자기투영" 보존을 위해 미정. 시나리오 대본 단계에서 결정.'));
sections.push(numbered('태그 세계관 재매핑 — 일반 RPG 용어 11종(물리/화염/냉기/번개/암흑/근접/원거리/범위/지속/회복/방어)을 조선 퇴마 특화 용어로 일괄 재매핑 (시나리오 영역).'));
sections.push(numbered('출시 일정 · 빌드 마일스톤 — 현 시점 미정.'));
sections.push(h2('10.2 코어 프레임워크 (BT.Framework)'));
sections.push(para('BurningTimes 조직의 자산인 BT.Framework Tier 1 16종이 본 프로젝트에서 1순위 도입 검토 대상이다.'));
sections.push(bullet('Tier 1 즉시 활용: SafeAreaBorder, Log, ValidationEx, FormatEx, EnumEx, MathEx 등.'));
sections.push(bullet('Tier 2 신규 구현 후보: UITouchHandler (가상 스틱 멀티터치), BackKeyDispatcher (Android 뒤로가기 통합).'));
sections.push(bullet('Tier 3 (전투·네트워크·UI 고급)은 본작 기획 확정 후 재평가.'));
sections.push(h2('10.3 검증 시나리오 (밸런스)'));
sections.push(makeTable(
['ID', '시나리오', '기대 결과'],
[
['V-1', '일반 적(약) 1vs1 교전', '액티브 자동 발동 3~4타 처치, 피격 시 1/4 하트 감소'],
['V-2', '피격 직후 연속 히트', 'i-frame 0.6초 내 2번째 적 공격 무효'],
['V-3', '하트 분할 시각화', 'ATK 1 피격 시 풀 하트 → 3/4 하트 전환 표시'],
['V-4', '최대 하트 수 증가', 'P15 생명의 꽃 Lv.1 획득 시 최대 HP 4 → 8 쿼터 변환'],
['V-6', '보스 TTK 상한', '카드 0장 기준 보스 HP 400 → DPS 기준 75~100초'],
['V-7', '각성 발동 조건', '액티브 Lv.5 + 필요 패시브 1개 + 보물상자 획득 → 각성 발동'],
['V-8', '적 ATK 점진 강화', 'Stage 5 진입 시 일반 적 ATK 2 (반 하트) 확인'],
],
[800, 3500, 5060],
));
// === §11 부록 ===
sections.push(h1('11. 부록'));
sections.push(h2('11.1 변경 이력'));
sections.push(makeTable(
['일시', '단계', '주요 변경'],
[
['2026-04-23', 'v0.1 — 파일럿 초기 골격', '핵심 게임 룰 9개 기반 5종 골격 문서 작성 (게임 컨셉·코어 루프·진행 시스템·전투 스펙·스테이지 구조)'],
['2026-04-24', 'v0.2 — BT7-Plan 개정', 'PD 직접 지시 11건 반영. VS 순수형 (공격 버튼 제거), 액티브/패시브/각성 3분류, 하트 분할, 카드 50~65장 확장, Lv.5 스택, 보물상자 각성 트리거'],
['2026-04-24', 'v0.2 — BT11-Plan 60종 확장', '액티브 20 · 패시브 20 · 각성 20 컨셉 표 완비'],
['2026-04-26', 'v1 — 초기 기획서 통합', '본 문서. 16종 기획 SOT 통합 정리'],
],
[1300, 2700, 5360],
));
sections.push(h2('11.2 주요 기각안 요지'));
sections.push(para('본 절은 설계 과정에서 검토 후 기각된 안의 요지를 모아 둔다. 차후 같은 안건이 재논의될 때 "왜 이 방향을 선택하지 않았는지"를 빠르게 확인하기 위함이다.'));
sections.push(makeTable(
['영역', '기각안', '기각 사유'],
[
['게임 컨셉', '하드코어 로그라이크 포지션', '캐주얼 로그라이크 포지션 고수. 하드코어는 타겟 유저 확장 포기 의미'],
['게임 컨셉', '탑뷰 로그라이크 변형', '횡스크롤 플랫포머 확정. 템플릿(2D PlatformerMicrogame)과 정합'],
['전투', '수동 공격 유지 (액티브 + 수동 병행)', 'VS 순수형 명시. 수동 공격 병행은 복잡도 증가 + 자동 발동 감상 재미 저해'],
['전투', '4티어 레어리티 (Common/Uncommon/Rare/Epic)', '"액티브+패시브=각성" 3분류로 통일. 레어리티 체계 폐기'],
['전투', '적 ATK = 1 (즉사) 단일 설계', '하트 분할 + 점진 강화로 전환. 초반 관대·후반 가혹의 곡선 형성'],
['전투', 'i-frame 1.0초 / 0.3초', '1.0초는 보스 공격 대부분 무효화, 0.3초는 ATK 4 연타 위험. 0.6초 유지'],
['전투', 'DEF 스탯 도입', '패시브 [방어] % 감소로 역할 대체. 복잡도만 증가'],
['카드', '카드 합성 시스템 (2장 합쳐 상위 카드)', '동일 카드 스택 Lv.5가 합성의 재미를 단순 구조로 대체'],
['카드', '카드도 사망 후 일부 유지', '축 A 낙차가 없으면 재미 축 1 성립 불가. 카드 전손 고수'],
['카드', '각성을 레벨업 드로우 풀에 포함', '조건 미충족 각성 카드가 무의미 카드로 등장하여 픽 UX 저해. 별도 풀 + 보물상자 트리거'],
['진행', '카드 3픽이 아닌 2픽', '3이 선택 긴장·변동성·시너지 탐색의 최소 임계'],
['진행', '특성이 런 내에서도 증가', '축 C는 메타 영역. 런 내 증가 시 축 A와 역할 충돌'],
['스테이지', '완전 랜덤 프로시저럴 맵', '캐주얼 로그라이크에서 학습 곡선·제작 비용 과다. 부분 랜덤 중심 고정 맵'],
['스테이지', '보스 스킵 옵션', '"스테이지 마지막은 항상 보스" 고수. 보스가 빌드 검증·보상 분기의 핵심'],
['스테이지', '보스 여러 마리 동시 출현', '가독성 낮은 보스전은 축 2 붕괴 위험. 보스 1종 고정 (페이즈 구조로 깊이)'],
['세계관', '조선 고증형 리얼 세계관', '역사 오류 리스크 높고 고증 의무가 창작 자유 제한. 가상 역사 "요괴 성행기"'],
['세계관', '불교 중심 단일 퇴마 체계', '시각 언어가 절 배경으로 쏠림. 무속 중심 혼합 체계가 다양성 우위'],
['마을', '대규모 탐험형 마을', '마을 자체가 별도 게임 루프가 되어 재미 축 1·2를 분산. 선형 단층 마을 유지'],
],
[1500, 3500, 4360],
));
sections.push(h2('11.3 참조 문서 (내부 SOT)'));
sections.push(para('본 기획서는 다음 16종 SOT를 통합 정리한 것이다. 각 항목별 상세는 해당 원본 문서 참조.'));
sections.push(bullet('프로젝트/EerieVillage/기획/01_게임_컨셉.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/02_코어_루프.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/03_진행_시스템_초안.md (v0.1)'));
sections.push(bullet('프로젝트/EerieVillage/기획/04_전투_기본_스펙.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/05_스테이지_구조_초안.md (v0.1)'));
sections.push(bullet('프로젝트/EerieVillage/기획/system/01_카드_시스템.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/content/01_카드_풀.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/content/02_스킬_효과_컨셉.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/balance/01_전투_수치.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/balance/02_레벨업_곡선.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/ux/01_모바일_터치_UX.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/ux/02_HUD_설계.md (v0.2)'));
sections.push(bullet('프로젝트/EerieVillage/기획/narrative/01_세계관_SOT.md (v0.1)'));
sections.push(bullet('프로젝트/EerieVillage/기획/narrative/02_보스_네이밍.md (v0.1)'));
sections.push(bullet('프로젝트/EerieVillage/기획/narrative/03_마을_컨셉.md (v0.1)'));
sections.push(h2('11.4 작성 정보'));
sections.push(bullet('작성: BurningTimes Studio 기획팀장'));
sections.push(bullet('작성일: 2026-04-26'));
sections.push(bullet('대상: 프로듀서·디렉터(PD), 외부 공유 가능'));
sections.push(bullet('단계: Phase 3-A·B 통합 정리. Phase 3-C(BM·아트 톤·세부 대본)는 후속 단계 이관.'));
// === Document ===
const doc = new Document({
creator: 'BurningTimes Studio · 기획팀',
title: '기묘한 고을 : 조선퇴마뎐 — 초기 기획서 v1',
description: 'EerieVillage: Joseon Exorcist — Initial Design Document v1',
styles: {
default: {
document: { run: { font: FONT_KO, size: 22 } },
},
paragraphStyles: [
{
id: 'Heading1', name: 'Heading 1', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 36, bold: true, font: FONT_KO_HEAD, color: '1F3864' },
paragraph: { spacing: { before: 360, after: 240 }, outlineLevel: 0 },
},
{
id: 'Heading2', name: 'Heading 2', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 28, bold: true, font: FONT_KO_HEAD, color: '2E75B6' },
paragraph: { spacing: { before: 280, after: 160 }, outlineLevel: 1 },
},
{
id: 'Heading3', name: 'Heading 3', basedOn: 'Normal', next: 'Normal', quickFormat: true,
run: { size: 24, bold: true, font: FONT_KO_HEAD, color: '375E97' },
paragraph: { spacing: { before: 200, after: 120 }, outlineLevel: 2 },
},
],
},
numbering: {
config: [
{
reference: 'bullets',
levels: [
{
level: 0, format: LevelFormat.BULLET, text: '•', alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 540, hanging: 270 } } },
},
{
level: 1, format: LevelFormat.BULLET, text: '◦', alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 1080, hanging: 270 } } },
},
],
},
{
reference: 'numbers',
levels: [
{
level: 0, format: LevelFormat.DECIMAL, text: '%1.', alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 540, hanging: 270 } } },
},
],
},
],
},
sections: [
{
properties: {
page: {
size: { width: 12240, height: 15840 }, // US Letter
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
headers: {
default: new Header({
children: [new Paragraph({
alignment: AlignmentType.RIGHT,
children: [new TextRun({ text: '기묘한 고을 : 조선퇴마뎐 — 초기 기획서 v1', font: FONT_KO, size: 18, color: '808080' })],
})],
}),
},
footers: {
default: new Footer({
children: [new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: 'BurningTimes Studio · 기획팀 | ', font: FONT_KO, size: 18, color: '808080' }),
new TextRun({ text: '', font: FONT_KO, size: 18, color: '808080', children: [PageNumber.CURRENT] }),
new TextRun({ text: ' / ', font: FONT_KO, size: 18, color: '808080' }),
new TextRun({ text: '', font: FONT_KO, size: 18, color: '808080', children: [PageNumber.TOTAL_PAGES] }),
],
})],
}),
},
children: sections,
},
],
});
const outPath = path.join(__dirname, '기묘한고을_초기기획서_v1.docx');
Packer.toBuffer(doc).then(buffer => {
fs.writeFileSync(outPath, buffer);
const stats = fs.statSync(outPath);
console.log('OK ' + outPath);
console.log('Size: ' + (stats.size / 1024).toFixed(1) + ' KB');
}).catch(err => {
console.error('FAIL', err);
process.exit(1);
});