diff --git a/공유/PD_지시_트래킹/기획팀_PD_지시_로그.md b/공유/PD_지시_트래킹/기획팀_PD_지시_로그.md index 1a79f4f..a5452fb 100644 --- a/공유/PD_지시_트래킹/기획팀_PD_지시_로그.md +++ b/공유/PD_지시_트래킹/기획팀_PD_지시_로그.md @@ -33,6 +33,8 @@ C3·C13 위반에 해당. **즉시 자진 보고 후 소급 등록**. | # | 일시 | 지시 요지 | 처리 상태 | 산출물 경로 | 중단 사유 | 사후 조치 | |---|------|----------|----------|-----------|----------|----------| +| BT14-Plan | 2026-04-26 | **스킬 시스템 개발팀 전달 기획서 PPT 제작** — PD 직접 지시. 1차 PD 검토 + 2차 개발팀 구현 명세. 7종 SOT(system/01·content/01·content/02·balance/01·02·ux/02·개발 spec v1) 통합 | **완료** | [완료: 2026-04-26 · 참조: `공유/대화로그/EerieVillage/2026-04-26.md`] **`프로젝트/EerieVillage/기획/스킬시스템_개발기획서_v1.pptx`** (36 슬라이드 · 120KB · 16:9 와이드 · 23표 · 500도형 · 60종 카드 명세 + 인터페이스 4종/SO 3종/이벤트 3종/Enum 6종 + Phase P0~P4 우선순위) + 빌드 스크립트 `_build_skill_pptx.py` (재생성용) | — | PD 검토 후 narrative 한자 재매핑·balance 수치 확정 시 v1.1 재생성. Phase 2 클라이언트팀 위임은 별도 Task 생성 (BT12-Dev §7-2 P0~P4 분리 가이드 활용) | +| BT13-Plan | 2026-04-26 | **기묘한 고을 개요 및 초기 기획서 워드 문서 제작** — PD 직접 지시 ("기획팀에게 지시"). 외부 공유 가능 수준의 통합 기획서 .docx 1종. PM이 라우팅 가로챈 점 자진 고지 (C43-1 위반) — 다음부터 "기획팀" 호칭 시 PM 사후 트래킹만 | **완료** | [완료: 2026-04-26 · 참조: `공유/대화로그/EerieVillage/2026-04-26.md` (등록 예정) · `공유/대화로그/EerieVillage/2026-04-24.md`] **`프로젝트/EerieVillage/기획/기묘한고을_초기기획서_v1.docx`** (35.6 KB · 22~26페이지 추정 · 단락 665 · 테이블 21 · 도해 5 · 16종 SOT 통합) + 보조 빌드 스크립트 `_build_docx.js` (재생성용 보존). 외부 공유 가능 (BT 내부 용어 배제). 16종 선행 Read SOT: 컨셉/루프/진행/전투/스테이지 5 + system 1 + content 2 + balance 2 + ux 2 + narrative 3 + 60종 표 v0.2 1 | — | PD 추가 지시 시 v1.1 재생성 (narrative 일괄 재매핑 등). 기획팀장 자체 PD 지시 로그 등록은 본 행으로 갈음 | | BT7-Plan | 2026-04-24 | **카드 시스템 개정** — 1) 세계관(narrative) 현행 유지 2) 덱빌딩 방식 VS 순수형 확정 (공격 버튼 제거, 이동·점프만) 3) 카드 구조 **"액티브 + 패시브 = 각성"** 3분류 확정 4) **"3→4 변경" 폐기** (새 구조가 확정안) 5) **각성 조건 확정** (VS 원조 방식: 액티브 최대 강화 + 특정 패시브 + 보물상자) 6) **규모 확정** (액티브 15~20종·패시브 25종 이내·각성 15~20종) 7) **기본 라이프 4 + 젤다 방식 하트 분할 시스템** (♥ 아이콘 · **하트 1개 = 4 HP 확정** · 기본 시작 하트 1개 · 피해 1 = 1/4조각 감소 · 피해 2 = 반조각 감소 · 피해 4 = 하트 1개 소멸 · 카드/성장으로 최대 하트 수 증가 가능) 8) **적 공격력 1부터 점진 강화** (스테이지·등급 진행에 따라 ATK 수치 증가) 9) **동일 카드 스택 업그레이드 Lv.5까지** (기존 Lv.3 확장) 10) **각성 발동 조건 VS 원작 동일 재확인** (액티브 Lv.5 + 각성에 필요한 특정 패시브 1개 이상 보유 + 보물상자 → 각성 카드 등장·발동) 11) **태그 체계 일반 RPG 용어 사용** (기존 세계관 특화 "오행 불/물/목/금/토" · "결계/부적/주술/소환" 등 임시 일반 RPG 용어로 재작성. 용어 자체는 PD가 나중에 일괄 세계관 재매핑 예정) | **진행중** | [확정 방향] VS 순수형 + 액티브/패시브/각성 구조 + 각성 조건 VS 원조 그대로 + 규모 15~20/≤25/15~20 + 라이프 4 하트 분할 + 적 ATK 점진 강화 · [폐기] T1~T4 빌드업·"비전/각성" 레이블·A/B 선택지·티어 수량 개념·적 ATK 1(즉사) 설계·"라이프 1 고정" 해석·`system/01 §5 기각안 1` "HP 막대 기각"·`04_전투 §8 기각안 1` "HP 막대 변형 기각" · [잔존 참조] `프로젝트/EerieVillage/기획/system/03_덱빌딩_방식_검토.md` 는 방식 비교 근거만 유효 · 대화로그 `공유/대화로그/EerieVillage/2026-04-24.md` | — | **PD 추가 지시 대기 잔존 0건 — BT7-Plan 방향 완전 확정**. 개정 집행 착수 가능 상태 → 확정분은 즉시 `04_전투`·`ux/01`·`balance/01`·`system/01`·`content/01`·`balance/02`·`ux/02`·`01_게임_컨셉`·`02_코어_루프` 순차 개정 착수 가능 + 개발팀 `PlayerAttack.cs` 주기 타이머 전환 + `InputActions` Attack 제거 + `Health.cs` maxHP 가변+쿼터 단위 구조 | | BT21-Plan | 2026-09-19 | **FirstRising 기획 분석 2건 병렬(전담 에이전트 위임)** — ① 기존 시스템 코드 분석 기획서(카테고리 분류·스크린샷)·게임성 장단점·부정 항목 대안 ② 레퍼런스 TBH(태스크바 히어로) 시스템·밸런스 분석 기획서(스크린샷)·장단점. 최종본 PPT 필수 · (후속) TBH 클라이언트 데이터 범위 제한 없는 추출 승인(B안) | **완료 보고 · PD 결정 대기** | ① `공유/기획/FirstRising/시스템분석/…기획서_v1.pptx`(36장·실캡처 8) ② `공유/기획/FirstRising/레퍼런스_TBH/…기획서_v2.pptx`(33장·실캡처 11, PM 작성 — 담당 에이전트 추출 데이터 사용 거부) · 원고 md 각 1 · 대화로그 `공유/대화로그/FirstRising/2026-09-19.md` §10 · PD 결정: 추출 원본은 로컬 scratchpad 유지 | — | 남은 1건: TBH 포털(스테이지 지도) 화면 — 스트립 포털 클릭 시 스테이지 이동 가능성으로 미캡처, PD 결정 시 진행 | | BT26-Plan | 2026-09-25 | **FR BM 시장 조사** — 이 세션에서 기획한 BM(Steam 장터 판매 수수료)이 적합한지 조사 · Steam 무료 게임 주요 BM 중 유저 호평/부정 시스템 조사(가챠 도입 시 부정적인지, 재화 판매형 인앱 상품 지양해야 하는지 등) · (추가) "md는 보기 힘들다" → PPT로 재정리 | **완료 보고 · PD 결정 대기** — PPT 판 `BM/2026-09-25_FR_BM_시장조사_v1.pptx`(20장, 라이트 템플릿 · 네이티브 차트 2 · 렌더 QA 완료) 추가 · — 장터 수수료 BM은 **주 수익원 부적합·보조로 조건부 적합**(아이템 가격 센트 수렴 · 봇/작업장 구조적 · TBH가 Valve 경고→장터 17일 폐쇄→리뷰 39% · NY AG v. Valve 장터 환금성 논거) · 호평 = 코스메틱·편의·만료 없는 패스 / 부정 = 패스 개악·소급 유료화·P2W·확률 불투명 · 가챠 도입 금지 권고 · 재화 판매는 코스메틱·편의 전용 조건부 | `공유/기획/FirstRising/BM/2026-09-25_FR_BM_시장조사_v1.md`(§0~§7, 출처 30여) · 리뷰 실측 50종 `BM/data/steam_reviews_2026-09-25.csv` · 수집기 `BM/tools/` · 대화로그 `공유/대화로그/FirstRising/2026-09-25.md` | — | PD 결정 요청 5건(§6): 장터 보조화·코스메틱/편의 DLC 주 수익원 승인 · 거래 범위/계정 조건/슬롯 수치 시점 · 가챠·파워 판매 금지 원칙 · 코스메틱 라인업 착수 · 드롭 확률 자율 공개 · PPT 판 필요 여부 | diff --git a/프로젝트/EerieVillage/기획/_build_docx.js b/프로젝트/EerieVillage/기획/_build_docx.js new file mode 100644 index 0000000..2918314 --- /dev/null +++ b/프로젝트/EerieVillage/기획/_build_docx.js @@ -0,0 +1,962 @@ +// 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); +}); diff --git a/프로젝트/EerieVillage/기획/_build_skill_pptx.py b/프로젝트/EerieVillage/기획/_build_skill_pptx.py new file mode 100644 index 0000000..b8aa094 --- /dev/null +++ b/프로젝트/EerieVillage/기획/_build_skill_pptx.py @@ -0,0 +1,2292 @@ +# -*- coding: utf-8 -*- +""" +EerieVillage 스킬 시스템 개발 기획서 PPT 빌더 +- 산출물: 스킬시스템_개발기획서_v1.pptx +- 대상: PD 검토 + 개발팀 구현 참고 +- 통합 SOT: BT7-Plan + BT11-Plan + BT12-Dev v1 (7종 문서) +""" +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.dml.color import RGBColor +from pptx.enum.shapes import MSO_SHAPE +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.oxml.ns import qn +from copy import deepcopy +from lxml import etree + +# ============================================================ +# 색상 팔레트 (Midnight Executive — 조선 퇴마 톤 정합) +# ============================================================ +NAVY = RGBColor(0x1E, 0x27, 0x61) # 주: 야밤 네이비 +ICE = RGBColor(0xCA, 0xDC, 0xFC) # 보조: 얼음 푸름 +ACCENT = RGBColor(0xE8, 0xB4, 0x3C) # 액센트: 황금 부적색 +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +DARK_TEXT = RGBColor(0x21, 0x29, 0x5C) # 본문 텍스트 +GRAY = RGBColor(0x66, 0x66, 0x66) # 캡션 +LIGHT_BG = RGBColor(0xF8, 0xF9, 0xFB) # 컨텐츠 배경 +RED_HL = RGBColor(0xC0, 0x39, 0x2B) # 강조 (PD 결정 영역) +GREEN_OK = RGBColor(0x3D, 0x8B, 0x5A) # 완료·확정 +PURPLE = RGBColor(0x6D, 0x2E, 0x46) # 각성·진화 + +# 폰트 +HEADER_FONT = "맑은 고딕" +BODY_FONT = "맑은 고딕" +CODE_FONT = "Consolas" + +# 슬라이드 크기 (16:9 와이드) +SLIDE_W = Inches(13.333) +SLIDE_H = Inches(7.5) + +# ============================================================ +# 헬퍼 함수 +# ============================================================ +def add_solid_bg(slide, color): + bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_W, SLIDE_H) + bg.fill.solid() + bg.fill.fore_color.rgb = color + bg.line.fill.background() + bg.shadow.inherit = False + # 맨 뒤로 보내기 + spTree = bg._element.getparent() + spTree.remove(bg._element) + spTree.insert(2, bg._element) + return bg + +def add_text_box(slide, left, top, width, height, text, font_size=14, + bold=False, color=DARK_TEXT, align=PP_ALIGN.LEFT, + anchor=MSO_ANCHOR.TOP, font_name=BODY_FONT): + tb = slide.shapes.add_textbox(left, top, width, height) + tf = tb.text_frame + tf.word_wrap = True + tf.margin_left = Inches(0.05) + tf.margin_right = Inches(0.05) + tf.margin_top = Inches(0.02) + tf.margin_bottom = Inches(0.02) + tf.vertical_anchor = anchor + p = tf.paragraphs[0] + p.alignment = align + run = p.add_run() + run.text = text + run.font.name = font_name + run.font.size = Pt(font_size) + run.font.bold = bold + run.font.color.rgb = color + return tb + +def add_multiline_text(slide, left, top, width, height, lines, font_size=12, + color=DARK_TEXT, font_name=BODY_FONT, line_spacing=1.15): + """lines: list of dict {text, bold, color, size, bullet}""" + tb = slide.shapes.add_textbox(left, top, width, height) + tf = tb.text_frame + tf.word_wrap = True + tf.margin_left = Inches(0.05) + tf.margin_right = Inches(0.05) + for i, line in enumerate(lines): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.alignment = line.get("align", PP_ALIGN.LEFT) + p.line_spacing = line_spacing + text = line["text"] + if line.get("bullet"): + text = "• " + text + run = p.add_run() + run.text = text + run.font.name = font_name + run.font.size = Pt(line.get("size", font_size)) + run.font.bold = line.get("bold", False) + run.font.color.rgb = line.get("color", color) + return tb + +def add_shape(slide, shape_type, left, top, width, height, + fill=None, line=None, text=None, font_size=12, + text_color=WHITE, bold=False, font_name=BODY_FONT): + s = slide.shapes.add_shape(shape_type, left, top, width, height) + if fill is not None: + s.fill.solid() + s.fill.fore_color.rgb = fill + else: + s.fill.background() + if line is not None: + s.line.color.rgb = line + s.line.width = Pt(0.75) + else: + s.line.fill.background() + s.shadow.inherit = False + if text: + tf = s.text_frame + tf.word_wrap = True + tf.margin_left = Inches(0.06) + tf.margin_right = Inches(0.06) + tf.margin_top = Inches(0.04) + tf.margin_bottom = Inches(0.04) + tf.vertical_anchor = MSO_ANCHOR.MIDDLE + p = tf.paragraphs[0] + p.alignment = PP_ALIGN.CENTER + run = p.add_run() + run.text = text + run.font.name = font_name + run.font.size = Pt(font_size) + run.font.bold = bold + run.font.color.rgb = text_color + return s + +def add_table(slide, left, top, width, height, data, header=True, + header_fill=NAVY, header_color=WHITE, body_size=10, header_size=11, + col_widths=None): + rows = len(data) + cols = len(data[0]) + tbl_shape = slide.shapes.add_table(rows, cols, left, top, width, height) + tbl = tbl_shape.table + if col_widths: + total = sum(col_widths) + for i, w in enumerate(col_widths): + tbl.columns[i].width = Emu(int(width * (w / total))) + for r in range(rows): + for c in range(cols): + cell = tbl.cell(r, c) + cell.margin_left = Inches(0.05) + cell.margin_right = Inches(0.05) + cell.margin_top = Inches(0.02) + cell.margin_bottom = Inches(0.02) + tf = cell.text_frame + tf.word_wrap = True + tf.clear() + p = tf.paragraphs[0] + p.alignment = PP_ALIGN.CENTER if (r == 0 and header) else PP_ALIGN.LEFT + run = p.add_run() + txt = str(data[r][c]) + run.text = txt + run.font.name = BODY_FONT + if r == 0 and header: + run.font.size = Pt(header_size) + run.font.bold = True + run.font.color.rgb = header_color + cell.fill.solid() + cell.fill.fore_color.rgb = header_fill + else: + run.font.size = Pt(body_size) + run.font.color.rgb = DARK_TEXT + # 홀짝 행 색 + if r % 2 == 0: + cell.fill.solid() + cell.fill.fore_color.rgb = LIGHT_BG + else: + cell.fill.solid() + cell.fill.fore_color.rgb = WHITE + return tbl_shape + +def add_slide_header(slide, title, slide_no, total, section=""): + """모든 본문 슬라이드 공통 헤더""" + # 좌상단 제목 + add_text_box(slide, Inches(0.5), Inches(0.25), Inches(10), Inches(0.55), + title, font_size=24, bold=True, color=NAVY, font_name=HEADER_FONT) + # 우상단 슬라이드 번호 + 부 표기 + add_text_box(slide, Inches(11.3), Inches(0.3), Inches(2), Inches(0.4), + f"{section} | {slide_no}/{total}", font_size=10, + color=GRAY, align=PP_ALIGN.RIGHT) + # 헤더 하단 액센트 라인 (얇은 황금색) + line = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, + Inches(0.5), Inches(0.85), + Inches(12.3), Pt(2)) + line.fill.solid() + line.fill.fore_color.rgb = ACCENT + line.line.fill.background() + line.shadow.inherit = False + +def add_footer(slide, slide_no, total): + add_text_box(slide, Inches(0.5), Inches(7.15), Inches(8), Inches(0.3), + "EerieVillage: Joseon Exorcist | 스킬 시스템 개발 기획서 v1", + font_size=8, color=GRAY) + add_text_box(slide, Inches(11.3), Inches(7.15), Inches(2), Inches(0.3), + f"BurningTimes 기획팀 | {slide_no}/{total}", + font_size=8, color=GRAY, align=PP_ALIGN.RIGHT) + +# ============================================================ +# 빌드 시작 +# ============================================================ +prs = Presentation() +prs.slide_width = SLIDE_W +prs.slide_height = SLIDE_H + +BLANK = prs.slide_layouts[6] +TOTAL_SLIDES = 36 + +# ============================================================ +# 슬라이드 1 — 표지 +# ============================================================ +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, NAVY) + +# 액센트 도형 (좌측 황금 띠) +band = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, + 0, Inches(2.5), Inches(0.4), Inches(2.5)) +band.fill.solid() +band.fill.fore_color.rgb = ACCENT +band.line.fill.background() +band.shadow.inherit = False + +add_text_box(s, Inches(1.0), Inches(2.0), Inches(11), Inches(0.7), + "기묘한 고을 : 조선퇴마뎐", font_size=22, + color=ICE, font_name=HEADER_FONT) +add_text_box(s, Inches(1.0), Inches(2.6), Inches(11), Inches(1.2), + "스킬 시스템 개발 기획서", font_size=46, bold=True, + color=WHITE, font_name=HEADER_FONT) +add_text_box(s, Inches(1.0), Inches(3.9), Inches(11), Inches(0.5), + "EerieVillage: Joseon Exorcist — Skill System Spec v1", + font_size=16, color=ICE, font_name=BODY_FONT) +# 메타 정보 +add_multiline_text(s, Inches(1.0), Inches(5.2), Inches(11), Inches(1.5), + [ + {"text": "조직: BurningTimes 게임 개발 스튜디오 / 기획팀", + "size": 13, "color": ICE}, + {"text": "대상: PD 직접 검토 + 개발팀(클라이언트·서버·QA) 구현 참고", + "size": 13, "color": ICE}, + {"text": "통합 SOT: BT7-Plan(시스템·콘텐츠) + BT11-Plan(60종 명세) + BT12-Dev v1(개발 설계)", + "size": 13, "color": ICE}, + {"text": "작성일: 2026-04-26 / 버전: v1.0", + "size": 13, "color": ICE}, + ]) + +# ============================================================ +# 1부 — 개요 (슬라이드 2~7) +# ============================================================ + +# 슬라이드 2 — 시스템 정체성 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "시스템 정체성", 2, TOTAL_SLIDES, "1부 개요") + +add_text_box(s, Inches(0.5), Inches(1.1), Inches(12), Inches(0.6), + "한 줄 요약", font_size=16, bold=True, color=NAVY) +quote = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(1.7), Inches(12), Inches(1.2)) +quote.fill.solid() +quote.fill.fore_color.rgb = ICE +quote.line.fill.background() +quote.shadow.inherit = False +qtf = quote.text_frame +qtf.margin_left = Inches(0.3) +qtf.margin_right = Inches(0.3) +qtf.vertical_anchor = MSO_ANCHOR.MIDDLE +qp = qtf.paragraphs[0] +qp.alignment = PP_ALIGN.CENTER +qr = qp.add_run() +qr.text = "레벨업 픽으로 빌드를 쌓고, 조건 충족 시 발동하는 각성으로 카타르시스를 제공하는\n자동 발동형(VS 순수형) 스킬·진화 시스템" +qr.font.name = HEADER_FONT +qr.font.size = Pt(18) +qr.font.bold = True +qr.font.color.rgb = NAVY + +add_text_box(s, Inches(0.5), Inches(3.2), Inches(12), Inches(0.5), + "VS 순수형 채택 근거 (BT7-Plan 확정 방향 2)", + font_size=15, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(3.7), Inches(12), Inches(3.5), + [ + {"text": "공격 버튼 자체 제거 — 플레이어 입력은 이동·점프만", "bullet": True, "size": 13}, + {"text": "장착한 액티브가 고유 주기·판정·대미지로 자동 발동 (Vampire Survivors 패턴)", "bullet": True, "size": 13}, + {"text": "캐주얼 모바일 환경 정합 — 가상 스틱 1개로 5~10분 런 완결", "bullet": True, "size": 13}, + {"text": "손맛은 \"타격 순간의 히트스탑·이펙트·사운드\"로 보장 (i-frame 0.6s 유지)", "bullet": True, "size": 13}, + {"text": "조작 부담 경감 → 회피·포지셔닝·빌드 설계에 인지 자원 집중", "bullet": True, "size": 13}, + ]) + +add_footer(s, 2, TOTAL_SLIDES) + +# 슬라이드 3 — 재미 축 3종 연결 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "재미 축 3종과의 연결 (P30 재미 우선 원칙)", 3, TOTAL_SLIDES, "1부 개요") + +# 3개 컬럼 카드 +cards = [ + ("축 1", "육성 롤러코스터의\n카타르시스", + "쌓기 → 폭발 → 낙차 리듬\n각성 발동 = 절정 포인트\n빌드 완성 순간의 전능감", + PURPLE), + ("축 2", "자동 발동 감상\n(VS 순수형)", + "이동·점프만으로 포지셔닝\n장착 액티브가 화면을 채움\n\"내가 만든 군대\" 감각", + NAVY), + ("축 3", "영속 성장의 위안\n(보조)", + "패시브 스탯 누적\n최대 하트 +1 패시브로\n런 내 안도감 형성", + GREEN_OK), +] +col_w = Inches(3.9) +gap = Inches(0.2) +start_x = Inches(0.5) +for i, (axis, title, body, color) in enumerate(cards): + x = start_x + (col_w + gap) * i + # 헤더 박스 + head = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, Inches(1.2), col_w, Inches(0.7)) + head.fill.solid() + head.fill.fore_color.rgb = color + head.line.fill.background() + head.shadow.inherit = False + htf = head.text_frame + htf.vertical_anchor = MSO_ANCHOR.MIDDLE + hp = htf.paragraphs[0] + hp.alignment = PP_ALIGN.CENTER + hr = hp.add_run() + hr.text = axis + hr.font.name = HEADER_FONT + hr.font.size = Pt(20) + hr.font.bold = True + hr.font.color.rgb = WHITE + # 본문 박스 + body_box = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, Inches(1.9), col_w, Inches(4.3)) + body_box.fill.solid() + body_box.fill.fore_color.rgb = LIGHT_BG + body_box.line.color.rgb = color + body_box.line.width = Pt(1.5) + body_box.shadow.inherit = False + btf = body_box.text_frame + btf.margin_left = Inches(0.2) + btf.margin_right = Inches(0.2) + btf.margin_top = Inches(0.2) + bp = btf.paragraphs[0] + bp.alignment = PP_ALIGN.CENTER + br = bp.add_run() + br.text = title + br.font.name = HEADER_FONT + br.font.size = Pt(15) + br.font.bold = True + br.font.color.rgb = NAVY + bp2 = btf.add_paragraph() + bp2.alignment = PP_ALIGN.CENTER + bp2.line_spacing = 1.5 + br2 = bp2.add_run() + br2.text = "\n" + body + br2.font.name = BODY_FONT + br2.font.size = Pt(12) + br2.font.color.rgb = DARK_TEXT + +add_text_box(s, Inches(0.5), Inches(6.4), Inches(12.3), Inches(0.5), + "스킬 시스템은 3축 모두에 기여 — 액티브(축 2) + 패시브(축 3) + 각성 발동(축 1) 통합 구조", + font_size=12, color=GRAY, align=PP_ALIGN.CENTER) +add_footer(s, 3, TOTAL_SLIDES) + +# 슬라이드 4 — 핵심 KPI / 성공 지표 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "핵심 KPI / 성공 지표", 4, TOTAL_SLIDES, "1부 개요") + +add_text_box(s, Inches(0.5), Inches(1.1), Inches(12), Inches(0.5), + "스킬 시스템 성공 판정 4축", + font_size=14, bold=True, color=NAVY) + +kpi_data = [ + ["판정 축", "지표", "목표", "측정 방식"], + ["다양성", "런당 빌드 분기 수", "8종 이상 시너지 빌드", "내부 플레이테스트·시너지 카드 그래프"], + ["성장 체감", "1 스테이지 내 레벨업 횟수", "4~6회 (50~90초/회)", "balance/02 §3 곡선 검증"], + ["각성 카타르시스", "런당 각성 발동 횟수", "1~2회 (Stage 3~4 첫 발동)", "보물상자·조건 충족 빈도 로그"], + ["조작 부담", "1런 내 입력 채널", "이동·점프 2채널 한정", "InputSystem 액션 카탈로그 검증"], + ["성능", "60 fps 유지율", "모바일 95%+ (광역 각성 시)", "Profiler·Frame Debugger 실측"], + ["몰입 시간", "평균 런 길이", "5~10분 (캐주얼 정합)", "Stage 클리어·사망 timestamp"], +] +add_table(s, Inches(0.5), Inches(1.7), Inches(12.3), Inches(4.0), + kpi_data, body_size=11, header_size=12, + col_widths=[2, 3, 3.5, 3.8]) + +add_text_box(s, Inches(0.5), Inches(5.9), Inches(12), Inches(0.5), + "핵심 재미 가설", + font_size=14, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(6.4), Inches(12), Inches(0.8), + [ + {"text": "각성 발동은 \"기다림이 보상받는\" 순간 — Lv.5 도달까지 동일 카드 5회 픽 투자가 각성 발동 시 카타르시스로 전환된다", + "bullet": True, "size": 12, "color": DARK_TEXT}, + ]) +add_footer(s, 4, TOTAL_SLIDES) + +# 슬라이드 5 — 본 문서 범위·비범위 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "본 문서 범위 (Scope) · 비범위 (Out of Scope)", 5, TOTAL_SLIDES, "1부 개요") + +# 좌측 — 범위 (포함) +left_box = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, + Inches(0.5), Inches(1.2), Inches(6), Inches(5.5)) +left_box.fill.solid() +left_box.fill.fore_color.rgb = LIGHT_BG +left_box.line.color.rgb = GREEN_OK +left_box.line.width = Pt(2) +left_box.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(1.35), Inches(5.5), Inches(0.5), + "본 문서가 다루는 범위", font_size=15, bold=True, color=GREEN_OK) +add_multiline_text(s, Inches(0.9), Inches(1.95), Inches(5.5), Inches(4.7), + [ + {"text": "스킬 3분류(액티브·패시브·각성) 시스템 구조", "bullet": True, "size": 12}, + {"text": "60종 카드 카탈로그 (액티브 20·패시브 20·각성 20)", "bullet": True, "size": 12}, + {"text": "픽 알고리즘·스택 업그레이드(Lv.1~5) 메커니즘", "bullet": True, "size": 12}, + {"text": "각성 발동 3조건 동시 충족 판정 로직", "bullet": True, "size": 12}, + {"text": "보물상자 체계·시너지 발동·하트 증가 메커니즘", "bullet": True, "size": 12}, + {"text": "Unity C# 클래스·인터페이스·이벤트 구조", "bullet": True, "size": 12}, + {"text": "BT.Framework Tier 1·2 활용 영역", "bullet": True, "size": 12}, + {"text": "BT7-Dev 자산(PlayerAttack·Health 등) 통합 영역", "bullet": True, "size": 12}, + {"text": "EditMode·PlayMode 검증 시나리오·어뷰징 가드", "bullet": True, "size": 12}, + {"text": "Phase 분리 우선순위 (P0~P4)", "bullet": True, "size": 12}, + ]) + +# 우측 — 비범위 (제외) +right_box = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, + Inches(6.8), Inches(1.2), Inches(6), Inches(5.5)) +right_box.fill.solid() +right_box.fill.fore_color.rgb = LIGHT_BG +right_box.line.color.rgb = RED_HL +right_box.line.width = Pt(2) +right_box.shadow.inherit = False +add_text_box(s, Inches(7.0), Inches(1.35), Inches(5.5), Inches(0.5), + "본 문서가 다루지 않는 범위", font_size=15, bold=True, color=RED_HL) +add_multiline_text(s, Inches(7.2), Inches(1.95), Inches(5.5), Inches(4.7), + [ + {"text": "구체 수치(대미지·확률·쿨다운) — balance-designer 이관", "bullet": True, "size": 12}, + {"text": "narrative 한자 카드명 재매핑 — 현행 유지 (PD 후속)", "bullet": True, "size": 12}, + {"text": "BM·과금 구조 — Phase 3-C 별도 설계", "bullet": True, "size": 12}, + {"text": "스테이지·맵·적 배치 — level-designer 영역", "bullet": True, "size": 12}, + {"text": "사운드·이펙트 아트 리소스 — 외부 아티스트 영역", "bullet": True, "size": 12}, + {"text": "특성(Trait) 시스템 상세 — system/02 별도 문서", "bullet": True, "size": 12}, + {"text": "보스 패턴·페이즈 설계 — content/03 별도", "bullet": True, "size": 12}, + {"text": "다국어·로컬라이제이션 — 출시 단계 별도", "bullet": True, "size": 12}, + ]) + +add_footer(s, 5, TOTAL_SLIDES) + +# 슬라이드 6 — 의존 시스템·문서 SOT +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "의존 시스템 · 문서 SOT 인벤토리", 6, TOTAL_SLIDES, "1부 개요") + +add_text_box(s, Inches(0.5), Inches(1.1), Inches(12), Inches(0.4), + "본 기획서가 통합한 7종 SOT 문서", + font_size=14, bold=True, color=NAVY) + +sot_data = [ + ["문서", "버전", "역할", "기획서 반영 부"], + ["system/01_카드_시스템.md", "v0.2 (BT7-Plan)", "3분류 구조·픽 알고리즘·각성 조건·하트 증가", "2부 구조 / 4부 메커니즘"], + ["content/01_카드_풀.md", "v0.2 (BT7-Plan)", "3분류 카드 풀 규모·태그 체계·시너지 빌드 예시", "3부 카테고리 / 5부 60종"], + ["content/02_스킬_효과_컨셉.md", "v0.2 (BT11-Plan)", "60종 카드 단위 동작 컨셉·카테고리 분배", "3부 / 5부"], + ["balance/01_전투_수치.md", "v0.2 (BT7-Plan)", "대미지 공식·BaseCooldown·BaseDamage·StackLevelFactor", "4부 메커니즘 / 6부 구현"], + ["balance/02_레벨업_곡선.md", "v0.2 (BT7-Plan)", "경험치·레벨업 주기·픽 알고리즘 가중치", "4부 메커니즘"], + ["ux/02_HUD_설계.md", "v0.2 (BT7-Plan)", "픽 UI·각성 발동 연출·HUD 슬롯", "4부 / 6부 UI 가이드"], + ["개발/spec/스킬_시스템_설계_v1.md", "v1.0 (BT12-Dev)", "Unity C# 4계층 아키텍처·인터페이스 정의", "6부 구현 가이드 (전수)"], +] +add_table(s, Inches(0.5), Inches(1.6), Inches(12.3), Inches(4.5), + sot_data, body_size=10, header_size=11, + col_widths=[3.2, 2, 4.5, 2.6]) + +add_text_box(s, Inches(0.5), Inches(6.3), Inches(12), Inches(0.8), + "📌 본 기획서는 BT7-Plan(시스템·콘텐츠) + BT11-Plan(60종 명세) + BT12-Dev v1(개발 설계) 3종 통합 산출물로,\n" + " 각 SOT의 단일 진실 원천(SSOT)을 유지하며 본 문서는 통합 인덱스 역할을 한다.", + font_size=11, color=GRAY) +add_footer(s, 6, TOTAL_SLIDES) + +# 슬라이드 7 — 1부 요약·전환 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, NAVY) +add_text_box(s, Inches(0.5), Inches(2.5), Inches(12.3), Inches(0.7), + "2부 — 시스템 구조", font_size=42, bold=True, color=ICE, + align=PP_ALIGN.CENTER, font_name=HEADER_FONT) +add_text_box(s, Inches(0.5), Inches(3.4), Inches(12.3), Inches(0.5), + "3분류 카드 구조 · 코어 루프 · 데이터 모델 · 태그 체계 · 빌드 슬롯", + font_size=18, color=ICE, align=PP_ALIGN.CENTER) +# 좌측 황금띠 +band = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, Inches(2.0), Inches(0.4), Inches(2.5)) +band.fill.solid() +band.fill.fore_color.rgb = ACCENT +band.line.fill.background() +band.shadow.inherit = False +add_text_box(s, Inches(0.5), Inches(6.8), Inches(12.3), Inches(0.4), + "Slide 7 / 36", font_size=10, color=ICE, align=PP_ALIGN.CENTER) + +# ============================================================ +# 2부 — 시스템 구조 (슬라이드 8~14) +# ============================================================ + +# 슬라이드 8 — 3분류 구조 다이어그램 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "카드 3분류 구조 — 액티브 + 패시브 = 각성", 8, TOTAL_SLIDES, "2부 구조") + +# 액티브 박스 +active_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.7), Inches(1.5), Inches(3.5), Inches(2.0)) +active_box.fill.solid() +active_box.fill.fore_color.rgb = NAVY +active_box.line.fill.background() +active_box.shadow.inherit = False +atf = active_box.text_frame +atf.vertical_anchor = MSO_ANCHOR.MIDDLE +atf.margin_left = Inches(0.2) +atf.margin_right = Inches(0.2) +ap = atf.paragraphs[0] +ap.alignment = PP_ALIGN.CENTER +ar = ap.add_run() +ar.text = "액티브" +ar.font.name = HEADER_FONT +ar.font.size = Pt(24) +ar.font.bold = True +ar.font.color.rgb = WHITE +ap2 = atf.add_paragraph() +ap2.alignment = PP_ALIGN.CENTER +ar2 = ap2.add_run() +ar2.text = "\n자동 발동 무기·스킬\n주기·판정·대미지\n20종 (Lv.1~Lv.5)" +ar2.font.name = BODY_FONT +ar2.font.size = Pt(13) +ar2.font.color.rgb = ICE + +# 더하기 기호 +plus = s.shapes.add_shape(MSO_SHAPE.OVAL, Inches(4.5), Inches(2.2), Inches(0.6), Inches(0.6)) +plus.fill.solid() +plus.fill.fore_color.rgb = ACCENT +plus.line.fill.background() +ptf = plus.text_frame +ptf.vertical_anchor = MSO_ANCHOR.MIDDLE +pp = ptf.paragraphs[0] +pp.alignment = PP_ALIGN.CENTER +pr = pp.add_run() +pr.text = "+" +pr.font.size = Pt(24) +pr.font.bold = True +pr.font.color.rgb = NAVY + +# 패시브 박스 +passive_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(5.4), Inches(1.5), Inches(3.5), Inches(2.0)) +passive_box.fill.solid() +passive_box.fill.fore_color.rgb = GREEN_OK +passive_box.line.fill.background() +passive_box.shadow.inherit = False +ptf2 = passive_box.text_frame +ptf2.vertical_anchor = MSO_ANCHOR.MIDDLE +ptf2.margin_left = Inches(0.2) +pp_p = ptf2.paragraphs[0] +pp_p.alignment = PP_ALIGN.CENTER +pp_r = pp_p.add_run() +pp_r.text = "패시브" +pp_r.font.name = HEADER_FONT +pp_r.font.size = Pt(24) +pp_r.font.bold = True +pp_r.font.color.rgb = WHITE +pp_p2 = ptf2.add_paragraph() +pp_p2.alignment = PP_ALIGN.CENTER +pp_r2 = pp_p2.add_run() +pp_r2.text = "\n상시 스탯·효과 보정\n액티브 효율 증폭\n20종 (Lv.1~Lv.5)" +pp_r2.font.name = BODY_FONT +pp_r2.font.size = Pt(13) +pp_r2.font.color.rgb = WHITE + +# 화살표 (조건 충족) +arrow = s.shapes.add_shape(MSO_SHAPE.RIGHT_ARROW, + Inches(9.2), Inches(2.3), Inches(0.8), Inches(0.4)) +arrow.fill.solid() +arrow.fill.fore_color.rgb = ACCENT +arrow.line.fill.background() +arrow.shadow.inherit = False +add_text_box(s, Inches(9.05), Inches(1.85), Inches(1.2), Inches(0.4), + "조건 충족", font_size=10, color=GRAY, align=PP_ALIGN.CENTER) + +# 각성 박스 +awaken_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(10.2), Inches(1.5), Inches(2.7), Inches(2.0)) +awaken_box.fill.solid() +awaken_box.fill.fore_color.rgb = PURPLE +awaken_box.line.fill.background() +awaken_box.shadow.inherit = False +awtf = awaken_box.text_frame +awtf.vertical_anchor = MSO_ANCHOR.MIDDLE +awp = awtf.paragraphs[0] +awp.alignment = PP_ALIGN.CENTER +awr = awp.add_run() +awr.text = "각성" +awr.font.name = HEADER_FONT +awr.font.size = Pt(24) +awr.font.bold = True +awr.font.color.rgb = WHITE +awp2 = awtf.add_paragraph() +awp2.alignment = PP_ALIGN.CENTER +awr2 = awp2.add_run() +awr2.text = "\n진화 카드\n(별도 풀)\n20종" +awr2.font.name = BODY_FONT +awr2.font.size = Pt(13) +awr2.font.color.rgb = WHITE + +# 발동 조건 (하단) +add_text_box(s, Inches(0.5), Inches(4.0), Inches(12.3), Inches(0.5), + "각성 발동 3조건 동시 충족 (VS 원작 동일)", + font_size=15, bold=True, color=NAVY, align=PP_ALIGN.CENTER) + +cond_data = [ + ["조건 1", "조건 2", "조건 3"], + ["해당 액티브 카드 Lv.5 도달", "각성에 필요한 패시브 1+ 보유", "보물상자 획득"], +] +add_table(s, Inches(1.0), Inches(4.6), Inches(11.3), Inches(1.3), + cond_data, header_fill=PURPLE, body_size=12, header_size=13, + col_widths=[1, 1, 1]) + +add_text_box(s, Inches(0.5), Inches(6.2), Inches(12.3), Inches(0.7), + "총 카드 풀 규모: 60종 (20+20+20)\n" + "구 4티어 레어리티(Common/Uncommon/Rare/Epic) 폐기 -> VS 원작 계승 3분류 구조", + font_size=11, color=GRAY, align=PP_ALIGN.CENTER) + +add_footer(s, 8, TOTAL_SLIDES) + +# 슬라이드 9 — 코어 루프 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "코어 루프 — 픽 -> 스택 -> 각성 -> 클라이맥스", 9, TOTAL_SLIDES, "2부 구조") + +# 7단계 플로우차트 +loop_steps = [ + ("1", "레벨업 트리거", "경험치 만렙 도달", NAVY), + ("2", "3장 무작위 드로우", "액티브·패시브 2축\n빌드 태그 가중", NAVY), + ("3", "픽 UI 표시", "1장 선택 (강제)\n건너뛰기 없음", NAVY), + ("4", "장착 / 스택 업그레이드", "신규 장착 또는\n동일 카드 Lv.N->N+1", NAVY), + ("5", "보물상자 획득 (병행)", "스테이지 진행 중\n조건부 스폰", ACCENT), + ("6", "각성 발동 판정", "3조건 동시 충족\n각성 풀에서 발동", PURPLE), + ("7", "런 종료", "보스 클리어 / 사망\n카드 전손·빌드 초기화", RED_HL), +] + +box_w = Inches(1.7) +box_h = Inches(2.3) +gap = Inches(0.05) +start_x = Inches(0.4) +top = Inches(1.5) + +for i, (num, title, desc, color) in enumerate(loop_steps): + x = start_x + (box_w + gap) * i + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, x, top, box_w, box_h) + box.fill.solid() + box.fill.fore_color.rgb = color + box.line.fill.background() + box.shadow.inherit = False + btf = box.text_frame + btf.margin_left = Inches(0.08) + btf.margin_right = Inches(0.08) + btf.margin_top = Inches(0.1) + btf.vertical_anchor = MSO_ANCHOR.TOP + bp = btf.paragraphs[0] + bp.alignment = PP_ALIGN.CENTER + br = bp.add_run() + br.text = num + br.font.name = HEADER_FONT + br.font.size = Pt(20) + br.font.bold = True + br.font.color.rgb = WHITE + bp2 = btf.add_paragraph() + bp2.alignment = PP_ALIGN.CENTER + bp2.line_spacing = 1.2 + br2 = bp2.add_run() + br2.text = "\n" + title + br2.font.name = HEADER_FONT + br2.font.size = Pt(11) + br2.font.bold = True + br2.font.color.rgb = WHITE + bp3 = btf.add_paragraph() + bp3.alignment = PP_ALIGN.CENTER + bp3.line_spacing = 1.15 + br3 = bp3.add_run() + br3.text = "\n" + desc + br3.font.name = BODY_FONT + br3.font.size = Pt(9) + br3.font.color.rgb = WHITE + +add_text_box(s, Inches(0.5), Inches(4.2), Inches(12), Inches(0.5), + "주요 분기점·강조 사항", + font_size=14, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(4.7), Inches(12.3), Inches(2.3), + [ + {"text": "픽 단계(3) — \"건너뛰기\" 옵션 미제공. 3장 모두 나쁜 경우는 \"빌드 위기\"로 설계 (긴장 요소)", + "bullet": True, "size": 12}, + {"text": "스택 단계(4) — Lv.5 도달 시 각성 조건 1번 \"액티브 Lv.5\" 충족 (자동 마킹)", + "bullet": True, "size": 12}, + {"text": "보물상자(5) — 스테이지 진행 병행 트리거. 각성 게이팅 조건이며 무관 시 대체 보상 (재화·회복·경험치)", + "bullet": True, "size": 12, "color": ACCENT}, + {"text": "각성(6) — VS 원작 정합. 단순 자동 발동 아님 = \"기다림이 보상받는\" 카타르시스의 구조적 기반", + "bullet": True, "size": 12, "color": PURPLE}, + {"text": "런 종료(7) — 모든 카드·빌드 전손. 메타 진행은 별도 시스템(특성·해금) 영역", + "bullet": True, "size": 12, "color": RED_HL}, + ]) +add_footer(s, 9, TOTAL_SLIDES) + +# 슬라이드 10 — 데이터 모델 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "데이터 모델 — ScriptableObject 3종 스키마", 10, TOTAL_SLIDES, "2부 구조") + +add_text_box(s, Inches(0.5), Inches(1.05), Inches(12), Inches(0.4), + "공통 베이스 + 3종 파생 클래스 (Unity ScriptableObject)", + font_size=13, bold=True, color=NAVY) + +base = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(1.5), Inches(12.3), Inches(1.4)) +base.fill.solid() +base.fill.fore_color.rgb = LIGHT_BG +base.line.color.rgb = NAVY +base.line.width = Pt(1.5) +base.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(1.55), Inches(12), Inches(0.4), + "abstract class SkillDataAsset : ScriptableObject (공통 베이스)", + font_size=12, bold=True, color=NAVY, font_name=CODE_FONT) +add_multiline_text(s, Inches(0.7), Inches(1.95), Inches(12), Inches(0.95), + [ + {"text": "string CardId | string DisplayName / EnglishName / Description | Sprite Icon", + "size": 10, "color": DARK_TEXT}, + {"text": "AttributeTag[] AttributeTags ([Flags] enum: Physical·Fire·Frost·Lightning·Dark)", + "size": 10, "color": DARK_TEXT}, + {"text": "TypeTag[] TypeTags ([Flags] enum: Melee·Ranged·Area·Persistent·Recovery·Defense)", + "size": 10, "color": DARK_TEXT}, + ], font_name=CODE_FONT) + +derives = [ + ("ActiveSkillData", NAVY, [ + "ActiveCategory Category (A·B·C·D·E·F)", + "ActiveTrigger Trigger (OnTime·OnHit·OnKill)", + "float BaseCooldown = 1.5f", + "int BaseDamage = 10", + "Vector2 HitboxSize", + "ProjectileTrajectory Trajectory", + "GameObject MinionPrefab", + "int DebuffStackLimit", + "float FireProbability (0~1, F전용)", + ]), + ("PassiveSkillData", GREEN_OK, [ + "PassiveCategory Category (P-A~P-E)", + "bool IsAlwaysOn", + "StatType TargetStat", + "float BaseModifierValue", + "PassiveTriggerKind TriggerKind", + " - OnPlayerDamaged", + " - OnEnemyKilled", + " - OnTimer", + ]), + ("AwakeningSkillData", PURPLE, [ + "AwakeningPattern Pattern", + " (ScaleUp·AddEffect·", + " MultiFire·GlobalSpread)", + "ActiveSkillData OriginalActive", + "PassiveSkillData[] RequiredPassives", + "int AwakeningBaseDamage = 25", + "int MultiFireCount = 3", + "GameObject AwakeningEffectPrefab", + ]), +] + +col_w = Inches(4.0) +gap = Inches(0.1) +start_x = Inches(0.5) +top = Inches(3.1) + +for i, (name, color, fields) in enumerate(derives): + x = start_x + (col_w + gap) * i + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, x, top, col_w, Inches(3.6)) + box.fill.solid() + box.fill.fore_color.rgb = WHITE + box.line.color.rgb = color + box.line.width = Pt(2) + box.shadow.inherit = False + hd = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, top, col_w, Inches(0.5)) + hd.fill.solid() + hd.fill.fore_color.rgb = color + hd.line.fill.background() + hd.shadow.inherit = False + htf = hd.text_frame + htf.vertical_anchor = MSO_ANCHOR.MIDDLE + hp = htf.paragraphs[0] + hp.alignment = PP_ALIGN.CENTER + hr = hp.add_run() + hr.text = name + hr.font.name = CODE_FONT + hr.font.size = Pt(13) + hr.font.bold = True + hr.font.color.rgb = WHITE + + body_lines = [{"text": f, "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT} + for f in fields] + add_multiline_text(s, x + Inches(0.15), top + Inches(0.6), + col_w - Inches(0.3), Inches(3.0), + body_lines, font_name=CODE_FONT) + +add_footer(s, 10, TOTAL_SLIDES) + +# 슬라이드 11 — 태그 체계 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "태그 체계 — 속성 5종 + 유형 6종 = 11종 (임시 일반 RPG 용어)", 11, TOTAL_SLIDES, "2부 구조") + +add_text_box(s, Inches(0.5), Inches(1.05), Inches(6.0), Inches(0.5), + "속성 태그 (후보 5종)", font_size=14, bold=True, color=NAVY) +attr_data = [ + ["태그", "의미", "대표 카드"], + ["[물리]", "순수 타격·관통", "A01·A05·A10·A12·A16"], + ["[화염]", "지속 대미지·점화", "A02·A15 / P02"], + ["[냉기]", "둔화·빙결", "A14·A19 / P03"], + ["[번개]", "연쇄·즉발", "A04·A13 / P04"], + ["[암흑]", "저주·DoT·약화", "A03·A06·A07·A08·A11 / P05"], +] +add_table(s, Inches(0.5), Inches(1.5), Inches(6.0), Inches(2.7), + attr_data, body_size=10, header_size=11, + col_widths=[1.5, 2.5, 3]) + +add_text_box(s, Inches(6.8), Inches(1.05), Inches(6.0), Inches(0.5), + "유형 태그 (후보 6종)", font_size=14, bold=True, color=NAVY) +type_data = [ + ["태그", "의미", "대표 카드"], + ["[근접]", "플레이어 주변 판정", "A05·A10·A12·A16·A18·A20"], + ["[원거리]", "투사체·원격", "A01·A02·A03·A14·A15"], + ["[범위]", "AoE·다중 타격", "A04·A05·A07·A13·A19"], + ["[지속]", "설치·오라·DoT", "A06·A09·A11·A17·A20"], + ["[회복]", "쿼터 회복·실드", "P11·P16·P17"], + ["[방어]", "피해 감소·회피·하트+1", "P12·P13·P14·P15·A09·A17"], +] +add_table(s, Inches(6.8), Inches(1.5), Inches(6.0), Inches(3.2), + type_data, body_size=10, header_size=11, + col_widths=[1.5, 2.5, 3]) + +syn_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(5.0), Inches(12.3), Inches(1.7)) +syn_box.fill.solid() +syn_box.fill.fore_color.rgb = LIGHT_BG +syn_box.line.color.rgb = ACCENT +syn_box.line.width = Pt(1.5) +syn_box.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(5.1), Inches(11.5), Inches(0.4), + "시너지 발동 조건 — 태그 2축 설계", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(5.5), Inches(11.5), Inches(1.2), + [ + {"text": "동일 태그 카드 K장 이상 빌드 편입 시 패시브 효과 추가 발동 (K 수치는 balance-designer 영역)", + "bullet": True, "size": 11}, + {"text": "속성 빌드(예: 화염 6장) + 유형 빌드(예: 원거리 5장) 동시 추구 = 교차 빌드 (제3의 방향)", + "bullet": True, "size": 11}, + {"text": "각 카드는 1~2개 태그 보유. 2태그 카드는 두 시너지에 동시 기여 가능", + "bullet": True, "size": 11}, + ]) + +add_text_box(s, Inches(0.5), Inches(6.85), Inches(12.3), Inches(0.4), + "narrative-designer 후속: 한자·조선 퇴마 톤 일괄 재매핑 대기 (PD 직접 검토)", + font_size=10, color=RED_HL, align=PP_ALIGN.CENTER) +add_footer(s, 11, TOTAL_SLIDES) + +# 슬라이드 12 — 카드 풀 규모 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "카드 풀 규모 — 총 60종 (액티브 20 · 패시브 20 · 각성 20)", 12, TOTAL_SLIDES, "2부 구조") + +add_text_box(s, Inches(0.5), Inches(1.1), Inches(4.0), Inches(0.5), + "액티브 20종 — 6 카테고리", font_size=13, bold=True, color=NAVY) +active_cat = [ + ["카테고리", "종수"], + ["A. 투사체", "6"], + ["B. 근접·범위", "5"], + ["C. 설치·지속", "3"], + ["D. 소환", "3"], + ["E. 상태이상", "1"], + ["F. 특수 판정", "2"], +] +add_table(s, Inches(0.5), Inches(1.6), Inches(4.0), Inches(3.5), + active_cat, body_size=11, header_size=12, col_widths=[3, 1]) + +add_text_box(s, Inches(4.7), Inches(1.1), Inches(4.0), Inches(0.5), + "패시브 20종 — 5 카테고리", font_size=13, bold=True, color=GREEN_OK) +passive_cat = [ + ["카테고리", "종수"], + ["P-A. 스탯 상승", "7"], + ["P-B. 주기·증폭", "3"], + ["P-C. 생존 강화", "5"], + ["P-D. 회복", "2"], + ["P-E. 자원 확장", "3"], +] +add_table(s, Inches(4.7), Inches(1.6), Inches(4.0), Inches(3.0), + passive_cat, header_fill=GREEN_OK, body_size=11, header_size=12, col_widths=[3, 1]) + +add_text_box(s, Inches(8.9), Inches(1.1), Inches(4.0), Inches(0.5), + "각성 20종 — 4 진화 패턴", font_size=13, bold=True, color=PURPLE) +awaken_cat = [ + ["진화 패턴", "종수"], + ["1. 스케일업", "6"], + ["2. 새 효과 추가", "6"], + ["3. 다중 발동", "4"], + ["4. 광역 확산", "4"], +] +add_table(s, Inches(8.9), Inches(1.6), Inches(4.0), Inches(2.6), + awaken_cat, header_fill=PURPLE, body_size=11, header_size=12, col_widths=[3, 1]) + +add_text_box(s, Inches(0.5), Inches(5.3), Inches(12), Inches(0.4), + "분배 근거 (재미 축 + 구현 부담 균형)", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(5.7), Inches(12.3), Inches(1.5), + [ + {"text": "투사체(6) — VS 원작 핵심. 가장 직관적 \"자동 발동 감상\" 체감", + "bullet": True, "size": 11}, + {"text": "근접·범위(5) — 밀집 구간 연출 + 포지셔닝 유도. 냉기·번개·물리 속성 분산", + "bullet": True, "size": 11}, + {"text": "P-A 스탯(7) — 범용 1 + 속성 4 + 크리티컬 2. 가장 직관적 강화", + "bullet": True, "size": 11}, + {"text": "P-C 생존(5) — 실드·하트+1 2종·피해 감소·회피. 생존 빌드 풀 다양성", + "bullet": True, "size": 11}, + {"text": "각성 4 패턴 = 원작 정합. \"같은 게 세짐 / 새 효과 / 2배 발동 / 화면 전체\" 4종 카타르시스", + "bullet": True, "size": 11, "color": PURPLE}, + ]) +add_footer(s, 12, TOTAL_SLIDES) + +# 슬라이드 13 — 빌드 슬롯 구조 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "빌드 슬롯 구조 — 액티브 6 / 패시브 6 (VS 원작 참조)", 13, TOTAL_SLIDES, "2부 구조") + +add_text_box(s, Inches(0.5), Inches(1.1), Inches(6.0), Inches(0.5), + "PlayerSkillInventory 장착 슬롯", + font_size=13, bold=True, color=NAVY) + +add_text_box(s, Inches(0.5), Inches(1.7), Inches(2), Inches(0.4), + "액티브 슬롯 (최대 6)", font_size=11, bold=True, color=NAVY) +for i in range(6): + x = Inches(0.5 + i * 0.95) + slot = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + x, Inches(2.15), Inches(0.85), Inches(0.85)) + slot.fill.solid() + slot.fill.fore_color.rgb = NAVY if i < 4 else WHITE + slot.line.color.rgb = NAVY + slot.line.width = Pt(1.5) + slot.shadow.inherit = False + if i < 4: + slot.text_frame.text = f"A{i+1}" + for p_ in slot.text_frame.paragraphs: + p_.alignment = PP_ALIGN.CENTER + for r_ in p_.runs: + r_.font.size = Pt(12) + r_.font.bold = True + r_.font.color.rgb = WHITE + +add_text_box(s, Inches(0.5), Inches(3.2), Inches(2), Inches(0.4), + "패시브 슬롯 (최대 6)", font_size=11, bold=True, color=GREEN_OK) +for i in range(6): + x = Inches(0.5 + i * 0.95) + slot = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + x, Inches(3.65), Inches(0.85), Inches(0.85)) + slot.fill.solid() + slot.fill.fore_color.rgb = GREEN_OK if i < 5 else WHITE + slot.line.color.rgb = GREEN_OK + slot.line.width = Pt(1.5) + slot.shadow.inherit = False + if i < 5: + slot.text_frame.text = f"P{i+1}" + for p_ in slot.text_frame.paragraphs: + p_.alignment = PP_ALIGN.CENTER + for r_ in p_.runs: + r_.font.size = Pt(12) + r_.font.bold = True + r_.font.color.rgb = WHITE + +add_text_box(s, Inches(0.5), Inches(4.7), Inches(6), Inches(0.4), + "각 슬롯 우하단에 Lv.1~5 표기 (UI)", + font_size=10, color=GRAY) + +add_text_box(s, Inches(7.0), Inches(1.1), Inches(5.7), Inches(0.5), + "슬롯 운용 규칙", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(7.0), Inches(1.65), Inches(5.7), Inches(5.0), + [ + {"text": "액티브·패시브 슬롯 분리 (BT7-Plan 정합)", "bullet": True, "size": 11, "bold": True}, + {"text": "슬롯 가득 차도 동일 카드 재픽으로 Lv 업 가능", "bullet": True, "size": 11}, + {"text": "신규 카드 픽 시 빈 슬롯 우선 편입", "bullet": True, "size": 11}, + {"text": "각성 발동 시 원 액티브 슬롯 점유 유지 (대체)", "bullet": True, "size": 11, "color": PURPLE}, + {"text": "런 종료 시 모든 슬롯 초기화 (전손)", "bullet": True, "size": 11, "color": RED_HL}, + {"text": "", "size": 11}, + {"text": "픽 알고리즘 처리:", "bullet": True, "size": 11, "bold": True}, + {"text": " - 액티브·패시브 풀에서 가중 무작위 3장 드로우", + "size": 10, "color": DARK_TEXT}, + {"text": " - 빌드 주력 태그 2개 가중치 보너스", + "size": 10, "color": DARK_TEXT}, + {"text": " - 빌드 편입 카드는 후보 제외 (Lv.5 미만 업그레이드는 재등장)", + "size": 10, "color": DARK_TEXT}, + {"text": " - 3장 액티브·패시브 혼합 필터 우선 (풀 고갈 시 해제)", + "size": 10, "color": DARK_TEXT}, + {"text": "", "size": 11}, + {"text": "각성 슬롯은 별도 풀 — 레벨업 드로우 풀에 미포함", + "bullet": True, "size": 11, "color": PURPLE}, + ]) +add_footer(s, 13, TOTAL_SLIDES) + +# 슬라이드 14 — 2부 종결 / 3부 전환 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, NAVY) +band = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, Inches(2.0), Inches(0.4), Inches(2.5)) +band.fill.solid() +band.fill.fore_color.rgb = ACCENT +band.line.fill.background() +band.shadow.inherit = False +add_text_box(s, Inches(0.5), Inches(2.5), Inches(12.3), Inches(0.7), + "3부 — 카드 카테고리 명세", font_size=42, bold=True, color=ICE, + align=PP_ALIGN.CENTER, font_name=HEADER_FONT) +add_text_box(s, Inches(0.5), Inches(3.4), Inches(12.3), Inches(0.5), + "액티브 6 카테고리 · 패시브 5 카테고리 · 각성 4 패턴", + font_size=18, color=ICE, align=PP_ALIGN.CENTER) +add_text_box(s, Inches(0.5), Inches(6.8), Inches(12.3), Inches(0.4), + "Slide 14 / 36", font_size=10, color=ICE, align=PP_ALIGN.CENTER) + +# ============================================================ +# 3부 — 카드 카테고리 명세 (슬라이드 15~20) +# ============================================================ + +# 슬라이드 15 — 액티브 6 카테고리 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "액티브 6 카테고리 — 동작 방식·구현 복잡도", 15, TOTAL_SLIDES, "3부 카테고리") + +active_data = [ + ["#", "카테고리", "동작 방식", "구현 복잡도", "대표 카드"], + ["A", "투사체형", "주기마다 전방·유도 투사체 자동 발사, 단발 DPS", "낮음 (BT.Framework 풀링 활용)", "A01·A14·A15"], + ["B", "근접·범위형", "플레이어 주변 즉시 판정 (원형/부채꼴)", "중간 (OverlapBox 다중)", "A05·A12·A13"], + ["C", "설치·지속형", "이동 경로/위치에 지속 효과 구역 잔존", "중간~높음 (풀 + Trigger)", "A06·A09·A17"], + ["D", "소환형", "독립 행동 소환물 생성, 자동 공격/어그로", "높음 (AI + 타이머 + 풀)", "A10·A11·A20"], + ["E", "상태이상형", "스택 가능 디버프(저주·봉인·독) 부여", "중간 (적별 카운터)", "A03·A07·A08"], + ["F", "특수 판정형", "확률 자동 발동 회심 / 무적 i-frame", "낮음 (Random 분기)", "A16·A18"], +] +add_table(s, Inches(0.5), Inches(1.1), Inches(12.3), Inches(4.5), + active_data, body_size=10, header_size=11, + col_widths=[0.5, 1.5, 4.8, 2.5, 3]) + +add_text_box(s, Inches(0.5), Inches(5.8), Inches(12), Inches(0.5), + "재미 축 연결", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(6.3), Inches(12.3), Inches(1.0), + [ + {"text": "A·B·C·F = 축 2 (자동 발동 감상) — 화면 인상 위주 카테고리", + "bullet": True, "size": 11}, + {"text": "D·E = 축 1 (육성 카타르시스) — 누적·스택·전능감 체감", + "bullet": True, "size": 11}, + ]) +add_footer(s, 15, TOTAL_SLIDES) + +# 슬라이드 16 — 패시브 5 카테고리 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "패시브 5 카테고리 — 강화 대상·시너지", 16, TOTAL_SLIDES, "3부 카테고리") + +passive_data = [ + ["#", "카테고리", "동작 방식", "주 시너지 액티브", "대표 카드"], + ["P-A", "스탯 상승형", "장착 즉시 상시, 대미지/속도/크리 보정", "전체 (속성 매칭 시 강화)", "P01·P02~P05·P09·P10"], + ["P-B", "주기·증폭형", "쿨다운 단축 / 판정 범위 / 투사체 수 증가", "A·B·E (DPS 증폭 라인)", "P06·P07·P08"], + ["P-C", "생존 강화형", "최대 하트 +1 / 피해 감소 / 회피 / 실드", "C·D (오래 버틸 빌드)", "P11·P12·P13·P14·P15"], + ["P-D", "회복형", "처치/시간 기반 쿼터 자동 회복", "B·E (처치 빈도 높음)", "P16·P17"], + ["P-E", "자원 확장형", "이동·점프·경험치·보물상자 확률", "전체 (간접 가속)", "P18·P19·P20"], +] +add_table(s, Inches(0.5), Inches(1.1), Inches(12.3), Inches(4.0), + passive_data, header_fill=GREEN_OK, body_size=10, header_size=11, + col_widths=[0.5, 1.8, 4.5, 2.5, 3]) + +# 패시브-액티브 상호작용 +add_text_box(s, Inches(0.5), Inches(5.4), Inches(12), Inches(0.5), + "패시브-액티브 상호작용 구조", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(5.9), Inches(12.3), Inches(1.4), + [ + {"text": "P-A 속성 강화 -> A·B·C·D·E 동일 속성 카드 대미지 배율 상승", + "bullet": True, "size": 11}, + {"text": "P-B 발동 주기 단축 -> A 투사체 / B 근접·범위 / E 상태이상 (DPS·스택 가속)", + "bullet": True, "size": 11}, + {"text": "P-C 생존 강화 -> C 설치·지속 / D 소환 (오래 버텨야 위력 발휘 카테고리 보조)", + "bullet": True, "size": 11}, + {"text": "P-D 회복 -> B·E 처치 속도 빠른 카테고리 (P16 발동 빈도 가속)", + "bullet": True, "size": 11}, + {"text": "P-E 자원 확장 -> 전체 간접 가속 (특히 P20 재물복 + 각성 빌드)", + "bullet": True, "size": 11, "color": ACCENT}, + ]) +add_footer(s, 16, TOTAL_SLIDES) + +# 슬라이드 17 — 각성 4 패턴 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "각성 4 진화 패턴 — 카타르시스 4종 분기", 17, TOTAL_SLIDES, "3부 카테고리") + +# 4 패턴 그리드 +patterns = [ + ("1. 스케일업", "ScaleUp", + "원 액티브 그대로 + 규모 대폭 증가\n대미지 / 범위 / 속도 1~2개 폭증", + "AW05 학익천진 (부채꼴 -> 360도)\nAW12 쾌검비경 (속도 극속)", + "낮음 (배율만 적용)", "밸런스 치우침"), + ("2. 새 효과 추가", "AddEffect", + "기존 효과 유지 + 완전히 새 효과 부착\n부가 판정/속성/소환물", + "AW04 봉령결계 (봉인 + DoT)\nAW07 사령결계 (스택 즉시 최대)", + "중간 (이벤트 병렬)", "이벤트 순서 관리"), + ("3. 다중 발동", "MultiFire", + "1회 -> N회 동시 발동\n투사체 2배 / 소환물 3~4기", + "AW10 분신만화 (1->3~4기)\nAW15 귀화유성 (무한 발사)", + "중간 (풀링 용량 ↑)", "파티클 상한 초과"), + ("4. 광역 확산", "GlobalSpread", + "효과 화면 전체로 확산\n런 클라이맥스 연출", + "AW01 천부경문 (화면 전체)\nAW03 천뢰강림 (무한 연쇄)", + "높음 (성능 최적화 필수)", "성능 이슈 고위험"), +] + +col_w = Inches(3.0) +row_h = Inches(2.7) +gap_x = Inches(0.1) +gap_y = Inches(0.15) + +for i, (title, code, desc, examples, complexity, risk) in enumerate(patterns): + col = i % 4 + x = Inches(0.5) + (col_w + gap_x) * col + y = Inches(1.1) + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, col_w, row_h) + box.fill.solid() + box.fill.fore_color.rgb = WHITE + box.line.color.rgb = PURPLE + box.line.width = Pt(1.5) + box.shadow.inherit = False + # 제목 헤더 + hd = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, y, col_w, Inches(0.5)) + hd.fill.solid() + hd.fill.fore_color.rgb = PURPLE + hd.line.fill.background() + hd.shadow.inherit = False + htf = hd.text_frame + htf.vertical_anchor = MSO_ANCHOR.MIDDLE + hp = htf.paragraphs[0] + hp.alignment = PP_ALIGN.CENTER + hr = hp.add_run() + hr.text = title + hr.font.name = HEADER_FONT + hr.font.size = Pt(12) + hr.font.bold = True + hr.font.color.rgb = WHITE + # 본문 + add_multiline_text(s, x + Inches(0.1), y + Inches(0.55), + col_w - Inches(0.2), Inches(2.1), + [ + {"text": f"({code})", "size": 9, "color": GRAY, "font_name": CODE_FONT}, + {"text": "", "size": 9}, + {"text": desc, "size": 10, "color": DARK_TEXT}, + {"text": "", "size": 9}, + {"text": "예시:", "size": 9, "bold": True, "color": NAVY}, + {"text": examples, "size": 9, "color": DARK_TEXT}, + ]) + +# 패턴별 분배·구현 복잡도 (하단) +add_text_box(s, Inches(0.5), Inches(4.0), Inches(12), Inches(0.5), + "패턴별 분배 · 구현 복잡도 · 주요 리스크", + font_size=13, bold=True, color=NAVY) +risk_data = [ + ["패턴", "종수", "구현 복잡도", "주요 리스크"], + ["1. 스케일업", "6", "낮음 (기존 발동기 + 배율)", "밸런스 치우침"], + ["2. 새 효과 추가", "6", "중간 (원 효과 + 추가 효과 병렬)", "이벤트 순서 관리"], + ["3. 다중 발동", "4", "중간 (동시 N회 + 풀링 용량)", "파티클 상한 초과 (개발팀 확인)"], + ["4. 광역 확산", "4", "높음 (화면 전체 AoE + 연쇄)", "성능 이슈 고위험 (최적화 필수)"], +] +add_table(s, Inches(0.5), Inches(4.6), Inches(12.3), Inches(2.4), + risk_data, header_fill=PURPLE, body_size=10, header_size=11, + col_widths=[2, 1, 4, 4]) +add_footer(s, 17, TOTAL_SLIDES) + +# 슬라이드 18 — 카테고리별 분배 표 (액티브) +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "액티브 카테고리별 카드 분배 (전체 20종 분류)", 18, TOTAL_SLIDES, "3부 카테고리") + +# 좌측 — 분배 표 +distrib_data = [ + ["카테고리", "종수", "카드 #", "근거"], + ["A. 투사체", "6", "A01·A02·A03·A08·A14·A15", "VS 핵심 + 4속성 균등"], + ["B. 근접·범위", "5", "A04·A05·A12·A13·A19", "밀집 연출 + 냉기/번개/물리"], + ["C. 설치·지속", "3", "A06·A09·A17", "성능 부담 회피 (3종 제한)"], + ["D. 소환", "3", "A10·A11·A20", "원거리 2 + 근접 1 분화"], + ["E. 상태이상", "1", "A07", "순수 오라형 1종 (스택은 A 흡수)"], + ["F. 특수 판정", "2", "A16·A18", "확률 로또감 (패턴화 회피)"], +] +add_table(s, Inches(0.5), Inches(1.1), Inches(12.3), Inches(3.5), + distrib_data, body_size=11, header_size=12, + col_widths=[2, 0.7, 4, 3]) + +# 하단 — 분배 차트 시각화 (간이 막대) +add_text_box(s, Inches(0.5), Inches(4.85), Inches(12), Inches(0.5), + "시각적 분배 (총 20종)", + font_size=13, bold=True, color=NAVY) + +categories_chart = [("A 투사체", 6), ("B 근접범위", 5), ("C 설치지속", 3), + ("D 소환", 3), ("E 상태이상", 1), ("F 특수판정", 2)] +chart_top = Inches(5.4) +chart_left = Inches(1.0) +bar_max_w = Inches(8.0) +bar_h = Inches(0.25) +gap_v = Inches(0.05) + +for i, (label, count) in enumerate(categories_chart): + y = chart_top + (bar_h + gap_v) * i + # 라벨 + add_text_box(s, Inches(0.5), y - Inches(0.02), Inches(2), bar_h + Inches(0.04), + label, font_size=10, color=DARK_TEXT) + # 바 + bar_w = Emu(int(bar_max_w * (count / 6))) + bar = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, + Inches(2.5), y, bar_w, bar_h) + bar.fill.solid() + bar.fill.fore_color.rgb = NAVY + bar.line.fill.background() + bar.shadow.inherit = False + # 카운트 + add_text_box(s, Inches(2.5) + bar_w + Inches(0.05), y - Inches(0.02), + Inches(0.6), bar_h + Inches(0.04), + f"{count}종", font_size=10, bold=True, color=NAVY) + +add_footer(s, 18, TOTAL_SLIDES) + +# 슬라이드 19 — 패시브 카테고리별 분배 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "패시브 카테고리별 카드 분배 (전체 20종 분류)", 19, TOTAL_SLIDES, "3부 카테고리") + +p_distrib_data = [ + ["카테고리", "종수", "카드 #", "근거"], + ["P-A. 스탯 상승", "7", "P01 + P02·P03·P04·P05 + P09·P10", "범용 1 + 속성 4 + 크리 2"], + ["P-B. 주기·증폭", "3", "P06·P07·P08", "DPS 증폭 축 응집"], + ["P-C. 생존 강화", "5", "P11·P12·P13·P14·P15", "실드/하트+1 2종/감소/회피"], + ["P-D. 회복", "2", "P16·P17", "처치+시간 기반 (어뷰징 가드)"], + ["P-E. 자원 확장", "3", "P18·P19·P20", "이동/경험치/보물상자"], +] +add_table(s, Inches(0.5), Inches(1.1), Inches(12.3), Inches(3.0), + p_distrib_data, header_fill=GREEN_OK, body_size=11, header_size=12, + col_widths=[2.2, 0.7, 5.3, 4.1]) + +# 핵심 강조 +add_text_box(s, Inches(0.5), Inches(4.4), Inches(12), Inches(0.5), + "핵심 패시브 — 각성 빌드 직결 카드", + font_size=13, bold=True, color=NAVY) + +key_cards = [ + ("P12 생명의 꽃", "최대 하트 +1 (런 내). AW17 등 다수 각성 조건 패시브", PURPLE), + ("P13 강철심장", "최대 하트 +1 (P12와 중첩 가능). 동일 효과 다른 카드 = 빌드 분기 의도", PURPLE), + ("P20 재물복", "보물상자 발견 확률 증가. \"보물상자 각성 특화\" 빌드 핵심", ACCENT), + ("P06 연사술", "모든 액티브 발동 주기 단축. 범용 고부가 + Lv.5 단축 가속", NAVY), +] +for i, (name, desc, color) in enumerate(key_cards): + y = Inches(4.95) + Inches(0.5) * i + icon = s.shapes.add_shape(MSO_SHAPE.OVAL, + Inches(0.7), y + Inches(0.05), Inches(0.3), Inches(0.3)) + icon.fill.solid() + icon.fill.fore_color.rgb = color + icon.line.fill.background() + add_text_box(s, Inches(1.1), y, Inches(3.5), Inches(0.4), + name, font_size=11, bold=True, color=color) + add_text_box(s, Inches(4.6), y, Inches(8.2), Inches(0.4), + desc, font_size=10, color=DARK_TEXT) + +add_footer(s, 19, TOTAL_SLIDES) + +# 슬라이드 20 — 각성 패턴 분배 + 3부 종결 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "각성 카테고리·패턴 분배 (전체 20종)", 20, TOTAL_SLIDES, "3부 카테고리") + +aw_distrib_data = [ + ["패턴", "종수", "각성 #", "원 액티브"], + ["1. 스케일업", "6", "AW02·AW05·AW08·AW12·AW13·AW16", "A02·A05·A07·A12·A13·A16"], + ["2. 새 효과 추가", "6", "AW04·AW07·AW11·AW14·AW17·AW18", "A03·A08·A11·A14·A17·A18"], + ["3. 다중 발동", "4", "AW09·AW10·AW15·AW20", "A09·A10·A15·A20"], + ["4. 광역 확산", "4", "AW01·AW03·AW06·AW19", "A01·A04·A06·A19"], +] +add_table(s, Inches(0.5), Inches(1.1), Inches(12.3), Inches(2.7), + aw_distrib_data, header_fill=PURPLE, body_size=11, header_size=12, + col_widths=[2, 0.7, 5.5, 4.1]) + +# 각성 발동 의미 +mean_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(4.0), Inches(12.3), Inches(2.7)) +mean_box.fill.solid() +mean_box.fill.fore_color.rgb = LIGHT_BG +mean_box.line.color.rgb = PURPLE +mean_box.line.width = Pt(2) +mean_box.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(4.15), Inches(12), Inches(0.5), + "각성 발동의 위상 — 한 런의 피날레", + font_size=14, bold=True, color=PURPLE) +add_multiline_text(s, Inches(0.7), Inches(4.65), Inches(12), Inches(2.0), + [ + {"text": "빌드 완성자 — 각성 발동 이후 \"내 빌드가 완성됐다\" 감각. 이후 런은 \"완성된 빌드로 얼마나 버티는가\"", + "bullet": True, "size": 11}, + {"text": "런 피날레 연출 — 풀스크린 연출 (시각·음향). 런의 분기점 명확히 알림", + "bullet": True, "size": 11}, + {"text": "기다림의 보상 — 액티브 Lv.5까지 동일 카드 5번 픽 \"투자\" -> 각성 발동 시 보상의 카타르시스 곡선 완성", + "bullet": True, "size": 11}, + {"text": "한 런 1회 발동 (중복 불가) — 다중 조건 충족 시 플레이어 1개 선택 UI 제공", + "bullet": True, "size": 11, "color": NAVY}, + ]) +add_footer(s, 20, TOTAL_SLIDES) + +# ============================================================ +# 4부 — 동작 메커니즘 (슬라이드 21~25) +# ============================================================ + +# 슬라이드 21 — 픽 알고리즘 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "픽 알고리즘 — 가중 무작위 + 빌드 태그 가중", 21, TOTAL_SLIDES, "4부 메커니즘") + +add_text_box(s, Inches(0.5), Inches(1.05), Inches(12), Inches(0.5), + "레벨업 트리거 시 5단계 처리", + font_size=14, bold=True, color=NAVY) + +algo_data = [ + ["단계", "처리 내용", "관여 요소"], + ["1", "현재 레벨·빌드 태그에 따라 액티브 vs 패시브 비율 가중 결정", "balance/02 §4 곡선"], + ["2", "빌드 주력 태그 2개 추출 (현재 보유 카드의 최다 태그)", "PlayerSkillInventory"], + ["3", "전체 풀(40종 = 액티브 20 + 패시브 20)에서 가중 무작위 3장 비복원 추출", "주력 태그 +α 가중"], + ["4", "이미 빌드 편입 카드는 제외 / Lv.5 미만 동일 카드는 재등장 허용 (Lv 업)", "_cardIdToRuntime Dict"], + ["5", "3장 액티브·패시브 혼합 필터 우선 적용 (풀 고갈 시 해제)", "혼합 안 되면 단일 종 가능"], +] +add_table(s, Inches(0.5), Inches(1.55), Inches(12.3), Inches(3.2), + algo_data, body_size=11, header_size=12, + col_widths=[0.5, 7, 4]) + +# 가중치 공식 +add_text_box(s, Inches(0.5), Inches(5.0), Inches(12), Inches(0.5), + "가중치 공식 (개념 — 수치는 balance-designer)", + font_size=13, bold=True, color=NAVY) +formula_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(5.55), Inches(12.3), Inches(1.5)) +formula_box.fill.solid() +formula_box.fill.fore_color.rgb = LIGHT_BG +formula_box.line.color.rgb = NAVY +formula_box.line.width = Pt(1) +formula_box.shadow.inherit = False +add_multiline_text(s, Inches(0.7), Inches(5.65), Inches(12), Inches(1.4), + [ + {"text": "Weight(card) = BaseWeight × (1 + TagBonus × MatchedTagCount) × CategoryBonus", + "size": 11, "bold": True, "color": NAVY, "font_name": CODE_FONT}, + {"text": " - BaseWeight: 카드 기본 출현 가중치 (1.0 균등 시작)", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " - TagBonus: 빌드 주력 태그 매칭 보너스 (예: 0.5)", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " - MatchedTagCount: 카드의 태그 중 주력 태그와 매칭된 수 (0~2)", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " - CategoryBonus: 레벨 구간별 액티브 vs 패시브 비율 보정", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + ], font_name=CODE_FONT) +add_footer(s, 21, TOTAL_SLIDES) + +# 슬라이드 22 — 스택 업그레이드 시스템 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "스택 업그레이드 — Lv.1~Lv.5 (VS 원작 정합)", 22, TOTAL_SLIDES, "4부 메커니즘") + +# Lv 진행 시각화 +lv_positions = [(1, 1.0), (2, 1.2), (3, 1.4), (4, 1.6), (5, 2.0)] +add_text_box(s, Inches(0.5), Inches(1.1), Inches(12), Inches(0.5), + "동일 카드 재픽 시 Lv 상승 (StackLevelFactor)", + font_size=14, bold=True, color=NAVY) + +for i, (lv, factor) in enumerate(lv_positions): + x = Inches(1.0 + i * 2.3) + y = Inches(1.7) + color = ACCENT if lv == 5 else NAVY + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + x, y, Inches(2.0), Inches(1.5)) + box.fill.solid() + box.fill.fore_color.rgb = color + box.line.fill.background() + box.shadow.inherit = False + btf = box.text_frame + btf.vertical_anchor = MSO_ANCHOR.MIDDLE + bp = btf.paragraphs[0] + bp.alignment = PP_ALIGN.CENTER + br = bp.add_run() + br.text = f"Lv.{lv}" + br.font.name = HEADER_FONT + br.font.size = Pt(22) + br.font.bold = True + br.font.color.rgb = WHITE if lv != 5 else NAVY + bp2 = btf.add_paragraph() + bp2.alignment = PP_ALIGN.CENTER + br2 = bp2.add_run() + br2.text = f"\nFactor {factor}x" + br2.font.name = CODE_FONT + br2.font.size = Pt(12) + br2.font.color.rgb = WHITE if lv != 5 else NAVY + if lv == 5: + bp3 = btf.add_paragraph() + bp3.alignment = PP_ALIGN.CENTER + br3 = bp3.add_run() + br3.text = "각성 조건 1 충족" + br3.font.name = BODY_FONT + br3.font.size = Pt(9) + br3.font.bold = True + br3.font.color.rgb = NAVY + +# 화살표 (Lv 사이) +for i in range(4): + ar_x = Inches(3.05 + i * 2.3) + ar = s.shapes.add_shape(MSO_SHAPE.RIGHT_ARROW, + ar_x, Inches(2.3), Inches(0.3), Inches(0.3)) + ar.fill.solid() + ar.fill.fore_color.rgb = GRAY + ar.line.fill.background() + ar.shadow.inherit = False + +# 규칙 + Lv.5 이후 +add_text_box(s, Inches(0.5), Inches(3.5), Inches(12), Inches(0.5), + "스택 규칙 · Lv.5 이후 처리", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(4.0), Inches(12.3), Inches(3.0), + [ + {"text": "동일 카드 재픽마다 Lv.N -> Lv.N+1 (최대 Lv.5)", + "bullet": True, "size": 11}, + {"text": "업그레이드는 \"수치 상향 또는 추가 트리거\" 중 하나 부여 (양자 동시 부여 금지)", + "bullet": True, "size": 11}, + {"text": "Lv.5 도달 -> AwakeningManager.MarkActiveMaxed(data) 자동 호출 (각성 조건 1 마킹)", + "bullet": True, "size": 11, "color": PURPLE}, + {"text": "Lv.5 이상 동일 카드 재픽 시: 재화 보상 또는 랜덤 변형 (balance-designer 결정)", + "bullet": True, "size": 11, "color": ACCENT}, + {"text": "구 Lv.3 상한 폐기 (BT7-Plan 확정 방향 9) -> VS 원작 Lv.5 체계 계승", + "bullet": True, "size": 11, "color": GRAY}, + ]) +add_footer(s, 22, TOTAL_SLIDES) + +# 슬라이드 23 — 각성 발동 시퀀스 다이어그램 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "각성 발동 시퀀스 — 3 조건 동시 충족 판정 흐름", 23, TOTAL_SLIDES, "4부 메커니즘") + +# 시퀀스 단계 +seq_steps = [ + ("스테이지 진행", "보물상자 스폰 트리거 (적 처치 누적/위치/엘리트 처치)", NAVY), + ("플레이어 보물상자 접촉", "TreasureChest.OnTriggerEnter2D -> Player.OnTreasureChestOpened()", NAVY), + ("AwakeningManager.CheckAwakeningEligibility() 호출", "전체 AwakeningSkillData 순회", PURPLE), + ("조건 1 검사: _maxedActiveIds.Contains(awData.OriginalActive.CardId)", "Lv.5 도달 액티브 ID HashSet 조회 (O(1))", PURPLE), + ("조건 2 검사: RequiredPassives.Any(p => HasPassive(p.CardId))", "필요 패시브 1+ 보유 확인", PURPLE), + ("조건 3: 보물상자는 이미 개봉됨 (진입 시점 보장)", "별도 검사 불요", GRAY), + ("eligibleAwakenings 분기", "0건: 대체 보상 / 1건: 즉시 발동 / 2+건: 선택 UI", ACCENT), + ("TriggerAwakening(awData)", "풀스크린 연출 + AwakeningSkillRuntime 교체 + alreadyAwakened 마킹", PURPLE), +] + +step_h = Inches(0.6) +gap_v = Inches(0.05) +for i, (step, detail, color) in enumerate(seq_steps): + y = Inches(1.05) + (step_h + gap_v) * i + # 단계 번호 원 + num = s.shapes.add_shape(MSO_SHAPE.OVAL, + Inches(0.5), y + Inches(0.1), Inches(0.4), Inches(0.4)) + num.fill.solid() + num.fill.fore_color.rgb = color + num.line.fill.background() + num.shadow.inherit = False + ntf = num.text_frame + ntf.vertical_anchor = MSO_ANCHOR.MIDDLE + np_ = ntf.paragraphs[0] + np_.alignment = PP_ALIGN.CENTER + nr = np_.add_run() + nr.text = str(i + 1) + nr.font.size = Pt(11) + nr.font.bold = True + nr.font.color.rgb = WHITE + # 단계 이름 + add_text_box(s, Inches(1.05), y + Inches(0.05), Inches(6.5), Inches(0.5), + step, font_size=11, bold=True, color=color) + # 디테일 + add_text_box(s, Inches(1.05), y + Inches(0.32), Inches(11.5), Inches(0.3), + detail, font_size=9, color=DARK_TEXT, font_name=CODE_FONT) + +add_footer(s, 23, TOTAL_SLIDES) + +# 슬라이드 24 — 보물상자 체계 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "보물상자 체계 — 각성 게이팅 + 대체 보상", 24, TOTAL_SLIDES, "4부 메커니즘") + +# 좌측 — 흐름 박스 +add_text_box(s, Inches(0.5), Inches(1.1), Inches(6), Inches(0.5), + "보물상자 흐름", + font_size=13, bold=True, color=NAVY) + +flow_steps = [ + ("스폰", "스테이지 진행 중 조건부 (적 처치 수·엘리트 처치·특정 위치)", NAVY), + ("발견", "P20 재물복 패시브 시 발견 확률 증가", ACCENT), + ("획득", "플레이어 접촉 시 OnTreasureChestOpened() 트리거", NAVY), + ("판정", "각성 조건 3종 동시 충족 검사", PURPLE), + ("분기", "충족 -> 각성 발동 / 불충족 -> 대체 보상", PURPLE), +] +for i, (step, desc, color) in enumerate(flow_steps): + y = Inches(1.6) + Inches(0.95) * i + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), y, Inches(6), Inches(0.85)) + box.fill.solid() + box.fill.fore_color.rgb = LIGHT_BG + box.line.color.rgb = color + box.line.width = Pt(1.5) + box.shadow.inherit = False + add_text_box(s, Inches(0.7), y + Inches(0.05), Inches(1.5), Inches(0.4), + step, font_size=12, bold=True, color=color) + add_text_box(s, Inches(0.7), y + Inches(0.4), Inches(5.7), Inches(0.4), + desc, font_size=9, color=DARK_TEXT) + +# 우측 — 대체 보상 +add_text_box(s, Inches(7.0), Inches(1.1), Inches(5.7), Inches(0.5), + "대체 보상 (조건 불충족 시)", + font_size=13, bold=True, color=NAVY) + +reward_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(7.0), Inches(1.6), Inches(5.7), Inches(2.5)) +reward_box.fill.solid() +reward_box.fill.fore_color.rgb = LIGHT_BG +reward_box.line.color.rgb = ACCENT +reward_box.line.width = Pt(2) +reward_box.shadow.inherit = False +add_multiline_text(s, Inches(7.2), Inches(1.7), Inches(5.5), Inches(2.4), + [ + {"text": "조건 불충족 시 1종 무작위 보상:", + "size": 12, "bold": True, "color": ACCENT}, + {"text": "", "size": 9}, + {"text": "재화 (다음 런 메타 진행 자원)", + "bullet": True, "size": 11}, + {"text": "회복 (1~2 쿼터 즉시 회복)", + "bullet": True, "size": 11}, + {"text": "경험치 (다음 레벨업 가속)", + "bullet": True, "size": 11}, + {"text": "확률·강도는 balance-designer 영역", + "bullet": True, "size": 10, "color": GRAY}, + ]) + +# 다중 각성 충족 +add_text_box(s, Inches(7.0), Inches(4.3), Inches(5.7), Inches(0.5), + "다중 각성 동시 충족 처리", + font_size=13, bold=True, color=PURPLE) +multi_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(7.0), Inches(4.8), Inches(5.7), Inches(2.0)) +multi_box.fill.solid() +multi_box.fill.fore_color.rgb = LIGHT_BG +multi_box.line.color.rgb = PURPLE +multi_box.line.width = Pt(2) +multi_box.shadow.inherit = False +add_multiline_text(s, Inches(7.2), Inches(4.9), Inches(5.5), Inches(1.9), + [ + {"text": "한 보물상자 획득 -> 복수 각성 조건 동시 충족", + "bullet": True, "size": 11}, + {"text": "플레이어 1개 선택 UI 제공", + "bullet": True, "size": 11, "color": PURPLE}, + {"text": "동시 발동 금지 (밸런스 붕괴 방지)", + "bullet": True, "size": 11, "color": RED_HL}, + {"text": "선택되지 않은 각성 조건은 다음 보물상자 획득 시 재판정", + "bullet": True, "size": 10, "color": GRAY}, + ]) +add_footer(s, 24, TOTAL_SLIDES) + +# 슬라이드 25 — HP/하트 메커니즘 + 시너지 발동 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "HP/하트 증가 · 시너지 발동 메커니즘", 25, TOTAL_SLIDES, "4부 메커니즘") + +# 좌측 — HP/하트 +add_text_box(s, Inches(0.5), Inches(1.1), Inches(6), Inches(0.5), + "HP/하트 분할 구조", + font_size=13, bold=True, color=NAVY) +hp_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(1.6), Inches(6), Inches(5.0)) +hp_box.fill.solid() +hp_box.fill.fore_color.rgb = LIGHT_BG +hp_box.line.color.rgb = NAVY +hp_box.line.width = Pt(1.5) +hp_box.shadow.inherit = False +add_multiline_text(s, Inches(0.7), Inches(1.75), Inches(5.7), Inches(4.7), + [ + {"text": "쿼터 단위 차감 구조 (BT7-Plan 확정 방향 7)", + "size": 11, "bold": True, "color": NAVY}, + {"text": "", "size": 9}, + {"text": "1 하트 = 4 쿼터 (Zelda BotW 방식)", + "bullet": True, "size": 11}, + {"text": "최대 HP = maxHearts × 4 쿼터", + "bullet": True, "size": 11}, + {"text": "i-frame 0.6s 보호 (연속 피격 방지)", + "bullet": True, "size": 11}, + {"text": "", "size": 9}, + {"text": "최대 하트 증가 (런 내):", + "size": 11, "bold": True, "color": NAVY}, + {"text": "P12 생명의 꽃 +1", + "bullet": True, "size": 10}, + {"text": "P13 강철심장 +1 (P12와 중첩 가능)", + "bullet": True, "size": 10}, + {"text": "상한: 5개 (balance-designer 하드캡)", + "bullet": True, "size": 10, "color": GRAY}, + {"text": "런 종료 시 기본 1개로 복귀 (전손)", + "bullet": True, "size": 10, "color": RED_HL}, + {"text": "", "size": 9}, + {"text": "Health.IncreaseMaxHearts(int delta) API", + "bullet": True, "size": 10, "color": GRAY, "font_name": CODE_FONT}, + {"text": "Health.Heal(int quarters) API (P16·P17)", + "bullet": True, "size": 10, "color": GRAY, "font_name": CODE_FONT}, + ]) + +# 우측 — 시너지 발동 +add_text_box(s, Inches(6.8), Inches(1.1), Inches(6), Inches(0.5), + "태그 시너지 발동", + font_size=13, bold=True, color=ACCENT) +syn_box2 = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(6.8), Inches(1.6), Inches(6), Inches(5.0)) +syn_box2.fill.solid() +syn_box2.fill.fore_color.rgb = LIGHT_BG +syn_box2.line.color.rgb = ACCENT +syn_box2.line.width = Pt(1.5) +syn_box2.shadow.inherit = False +add_multiline_text(s, Inches(7.0), Inches(1.75), Inches(5.7), Inches(4.7), + [ + {"text": "발동 조건", + "size": 11, "bold": True, "color": ACCENT}, + {"text": "동일 태그 카드 K장 이상 빌드 편입 시 패시브 효과 추가 발동", + "bullet": True, "size": 11}, + {"text": "K 임계값 = balance-designer 영역 (예: 화염 4장 이상)", + "bullet": True, "size": 10, "color": GRAY}, + {"text": "", "size": 9}, + {"text": "구현 흐름", + "size": 11, "bold": True, "color": ACCENT}, + {"text": "PlayerSkillInventory.OnSkillEquipped 이벤트 시 태그 카운트 갱신", + "bullet": True, "size": 10}, + {"text": "TagSynergyManager.CheckThreshold(tag) 호출", + "bullet": True, "size": 10, "font_name": CODE_FONT}, + {"text": "K장 도달 시 SynergyEffect 활성화 (PlayerStats 보정)", + "bullet": True, "size": 10}, + {"text": "이하 떨어지면 비활성화 (재진입 가능)", + "bullet": True, "size": 10}, + {"text": "", "size": 9}, + {"text": "교차 빌드의 가능성", + "size": 11, "bold": True, "color": ACCENT}, + {"text": "속성 빌드 (예: 화염 6장) + 유형 빌드 (예: 원거리 5장) 동시 추구 = 제3의 방향", + "bullet": True, "size": 10}, + {"text": "2태그 카드는 두 시너지에 동시 기여", + "bullet": True, "size": 10}, + ]) +add_footer(s, 25, TOTAL_SLIDES) + +# ============================================================ +# 5부 — 60종 카드 명세 (슬라이드 26~28) +# ============================================================ + +# 슬라이드 26 — 액티브 20종 표 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "액티브 20종 명세 표 (CSV v0.3 기반)", 26, TOTAL_SLIDES, "5부 60종 명세") + +active_table = [ + ["#", "카드명", "카테고리", "주 태그", "동작 요지"], + ["A01", "진언부", "A 투사체", "[원거리][물리]", "전방 직선 부적 투척, 단일 적 타격"], + ["A02", "화염부", "A 투사체", "[원거리][화염]", "직선 투척 + 적중 시 화염 DoT"], + ["A03", "봉인부", "A 투사체", "[원거리][암흑]", "투척 + 적중 시 행동 정지 저주"], + ["A04", "뇌격부", "B 근접·범위", "[범위][번개]", "주변 번개 연쇄, 밀집 다중 타격"], + ["A05", "학익진", "B 근접·범위", "[근접][범위][물리]", "전방 부채꼴 자동 타격"], + ["A06", "독안개", "C 설치·지속", "[지속][암흑]", "이동 경로 독 구역 잔존, 접촉 DoT"], + ["A07", "혼백박멸", "E 상태이상", "[범위][암흑]", "주변 약화 오라, 공/방 저하"], + ["A08", "저주문", "A 투사체", "[원거리][암흑][지속]", "자동 투척, 저주 스택 N개 시 폭발"], + ["A09", "허수아비", "C 설치·지속", "[지속][방어]", "이동 경로 설치, 어그로 유도 + 완충"], + ["A10", "분신술", "D 소환", "[근접][물리]", "분신 1기, 플레이어 공격 모방"], + ["A11", "도깨비불소환", "D 소환", "[원거리][암흑][지속]", "도깨비불 2기, 랜덤 추적 + DoT"], + ["A12", "쾌속검", "B 근접·범위", "[근접][물리]", "전방 연속 베기, 고빈도 단발"], + ["A13", "천둥발", "B 근접·범위", "[범위][번개]", "원형 번개 판정, 다중 연쇄"], + ["A14", "빙결창", "A 투사체", "[원거리][냉기]", "직선 투창, 적중 시 둔화"], + ["A15", "귀화", "A 투사체", "[원거리][화염][지속]", "유도 화염구, 가장 가까운 적 추적"], + ["A16", "일격필살", "F 특수 판정", "[근접][물리]", "확률 자동 회심 공격"], + ["A17", "결계장벽", "C 설치·지속", "[방어][지속]", "전방 고정 실드 설치, 적 저지"], + ["A18", "무영보", "F 특수 판정", "[방어][근접]", "순간 무적 대시 자동 발동"], + ["A19", "풍백제", "B 근접·범위", "[근접][범위][냉기]", "회오리 생성, 밀어내기 + 지속 타격"], + ["A20", "산군상", "D 소환", "[근접][물리][지속]", "호랑이 정령 소환, 돌진·물어뜯기"], +] +add_table(s, Inches(0.3), Inches(1.0), Inches(12.7), Inches(6.0), + active_table, body_size=8.5, header_size=10, + col_widths=[0.5, 1.5, 2, 2.7, 6]) +add_footer(s, 26, TOTAL_SLIDES) + +# 슬라이드 27 — 패시브 20종 표 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "패시브 20종 명세 표 (CSV v0.3 기반)", 27, TOTAL_SLIDES, "5부 60종 명세") + +passive_table = [ + ["#", "카드명", "카테고리", "동작 요지", "주 시너지"], + ["P01", "봉황격", "P-A 스탯", "모든 액티브 대미지 증가 (범용)", "전체 (범용 강화)"], + ["P02", "화염강화", "P-A 스탯", "[화염] 태그 액티브 대미지 증가", "A02·A15"], + ["P03", "냉기강화", "P-A 스탯", "[냉기] 태그 액티브 대미지 증가", "A14·A19"], + ["P04", "뇌전강화", "P-A 스탯", "[번개] 태그 액티브 대미지 증가", "A04·A13"], + ["P05", "어둠강화", "P-A 스탯", "[암흑] 태그 액티브 대미지 증가", "A03·A06·A07·A08·A11"], + ["P06", "연사술", "P-B 주기", "모든 액티브 발동 주기 단축", "전체 (고부가 범용)"], + ["P07", "광역확장", "P-B 주기", "판정 범위 + [지속] 잔존시간 연장", "B·C·A07·A19"], + ["P08", "투사체증폭", "P-B 주기", "[원거리] 액티브 투사체 수 증가", "A 투사체 전체"], + ["P09", "집중술", "P-A 스탯", "크리티컬 확률 상승", "A16 + 전체"], + ["P10", "폭풍혼", "P-A 스탯", "크리티컬 대미지 배율 증가", "A16 + 전체"], + ["P11", "호신부", "P-C 생존", "피격 시 1회 흡수 실드 (조건부)", "C·D"], + ["P12", "생명의 꽃", "P-C 생존", "최대 하트 +1 (런 내)", "각성 조건 핵심"], + ["P13", "강철심장", "P-C 생존", "최대 하트 +1 (P12와 중첩 가능)", "각성 조건 핵심"], + ["P14", "부적방패", "P-C 생존", "받는 피해 % 감소", "C·D"], + ["P15", "회피술", "P-C 생존", "회피 확률 + i-frame 연장", "F·전체"], + ["P16", "단전수련", "P-D 회복", "적 처치 시 1/4 하트 회복 (누적 킬)", "B·E (처치 빈도)"], + ["P17", "감로수", "P-D 회복", "일정 시간마다 1/4 하트 자동 재생", "전체 (시간 기반)"], + ["P18", "질풍보", "P-E 자원", "이동 속도 상승", "전체 (포지셔닝)"], + ["P19", "선견지명", "P-E 자원", "경험치 획득량 증가 (레벨업 가속)", "전체 (빌드 가속)"], + ["P20", "재물복", "P-E 자원", "보물상자 스폰·발견 확률 증가", "전체 (각성 트리거 가속)"], +] +add_table(s, Inches(0.3), Inches(1.0), Inches(12.7), Inches(6.0), + passive_table, header_fill=GREEN_OK, body_size=8.5, header_size=10, + col_widths=[0.5, 1.5, 1.7, 5.0, 4.0]) +add_footer(s, 27, TOTAL_SLIDES) + +# 슬라이드 28 — 각성 20종 표 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "각성 20종 명세 표 (CSV v0.3 기반)", 28, TOTAL_SLIDES, "5부 60종 명세") + +awaken_table = [ + ["#", "각성명", "패턴", "원 액티브", "필요 패시브 (1+)", "동작 요지"], + ["AW01", "천부경문", "4 광역", "A01 진언부", "P01·P06·P08", "화면 전체 부적 전개, 전 적 강타"], + ["AW02", "염라화염", "1 스케일업", "A02 화염부", "P02·P07", "범위 대폭 확장 + 점화 무제한"], + ["AW03", "천뢰강림", "4 광역", "A04 뇌격부", "P04·P06·P08", "연쇄 번개 무제한 전파"], + ["AW04", "봉령결계", "2 새효과", "A03 봉인부", "P05·P07", "행동 정지 + DoT 동시 부여"], + ["AW05", "학익천진", "1 스케일업", "A05 학익진", "P01·P07", "부채꼴 -> 360도 전방위"], + ["AW06", "독무유역", "4 광역", "A06 독안개", "P05·P07", "화면 전체 독무 잔존"], + ["AW07", "사령결계", "2 새효과", "A08 저주문", "P05·P06", "스택 즉시 최대 + 폭발 증폭"], + ["AW08", "혼령폭풍", "1 스케일업", "A07 혼백박멸", "P05·P07", "오라 -> 화면 전체 약화"], + ["AW09", "허수군세", "3 다중발동", "A09 허수아비", "P01·P11", "1기 -> 4~5기 동시 설치"], + ["AW10", "분신만화", "3 다중발동", "A10 분신술", "P01·P06", "1기 -> 3~4기 동시 소환"], + ["AW11", "산신령강림", "2 새효과", "A11 도깨비불", "P05·P07", "도깨비불 2기 -> 강력 1기 상주"], + ["AW12", "쾌검비경", "1 스케일업", "A12 쾌속검", "P01·P06", "연타 속도 극속, 무한 연격"], + ["AW13", "천지진동", "1 스케일업", "A13 천둥발", "P04·P07", "원형 번개 화면 전체 확장"], + ["AW14", "빙하시대", "2 새효과", "A14 빙결창", "P03·P07", "투창 + 낙하 영역 빙결"], + ["AW15", "귀화유성", "3 다중발동", "A15 귀화", "P02·P08", "유도 화염구 무한 발사"], + ["AW16", "일격절명", "1 스케일업", "A16 일격필살", "P09·P10", "회심 확률·배율 극한 (보스 외 즉사)"], + ["AW17", "천지음양방호", "2 새효과", "A17 결계장벽", "P11·(P12 또는 P13)", "실드 + 무적 시간 상시 유지"], + ["AW18", "귀보유영", "2 새효과", "A18 무영보", "P15·P18", "무적 대시 무한 + 피격 임박 자동"], + ["AW19", "풍신질주", "4 광역", "A19 풍백제", "P03·P07", "회오리 -> 화면 폭풍, 빙결"], + ["AW20", "산군포효", "3 다중발동", "A20 산군상", "P01·P05", "호랑이 1기 -> 3기, 포효 기절"], +] +add_table(s, Inches(0.2), Inches(1.0), Inches(12.9), Inches(6.0), + awaken_table, header_fill=PURPLE, body_size=7.5, header_size=9, + col_widths=[0.5, 1.5, 1.5, 1.6, 2.0, 5.5]) +add_footer(s, 28, TOTAL_SLIDES) + +# ============================================================ +# 6부 — 구현 가이드 (슬라이드 29~33) +# ============================================================ + +# 슬라이드 29 — 4계층 아키텍처 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "Unity 구현 — 4계층 아키텍처", 29, TOTAL_SLIDES, "6부 구현") + +layers = [ + ("계층 4 — 효과 발동", NAVY, + "AttackHitbox · ProjectileSpawner · AuraZone · MinionSpawner · DebuffApplier · Health.Decrement(int)", + "BT7-Dev 일부 재활용 + 신설 5종"), + ("계층 3 — 스킬 런타임", GREEN_OK, + "ActiveSkillRuntime · PassiveSkillRuntime · AwakeningSkillRuntime · PlayerSkillInventory", + "각 카드당 1 인스턴스, 인벤토리 관리"), + ("계층 2 — 스킬 데이터", PURPLE, + "SkillDataAsset (abstract) -> ActiveSkillData · PassiveSkillData · AwakeningSkillData", + "60종 .asset 파일 (ScriptableObject)"), + ("계층 1 — 정적 SOT", ACCENT, + "02_스킬_효과_컨셉_v0.3.csv (60종 캐주얼 명명)", + "기획팀 편집 -> 에디터 툴 -> 자산 재생성"), +] + +for i, (title, color, desc, sub) in enumerate(layers): + y = Inches(1.1) + Inches(1.4) * i + box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), y, Inches(12.3), Inches(1.25)) + box.fill.solid() + box.fill.fore_color.rgb = color + box.line.fill.background() + box.shadow.inherit = False + btf = box.text_frame + btf.margin_left = Inches(0.3) + btf.margin_right = Inches(0.3) + btf.margin_top = Inches(0.15) + bp = btf.paragraphs[0] + br = bp.add_run() + br.text = title + br.font.name = HEADER_FONT + br.font.size = Pt(15) + br.font.bold = True + br.font.color.rgb = WHITE + bp2 = btf.add_paragraph() + bp2.line_spacing = 1.2 + br2 = bp2.add_run() + br2.text = desc + br2.font.name = CODE_FONT + br2.font.size = Pt(11) + br2.font.color.rgb = WHITE + bp3 = btf.add_paragraph() + br3 = bp3.add_run() + br3.text = sub + br3.font.name = BODY_FONT + br3.font.size = Pt(10) + br3.font.color.rgb = ICE + # 위→아래 화살표 + if i < len(layers) - 1: + arr_y = y + Inches(1.25) - Pt(2) + arr = s.shapes.add_shape(MSO_SHAPE.UP_ARROW, + Inches(6.5), arr_y, Inches(0.3), Inches(0.15)) + arr.fill.solid() + arr.fill.fore_color.rgb = GRAY + arr.line.fill.background() + +add_text_box(s, Inches(0.5), Inches(6.85), Inches(12.3), Inches(0.4), + "각 계층 분리 = 신규 카드 추가 시 ScriptableObject 인스턴스 생성만으로 확장 (BT12-Dev §1-1 자원 효율·코드 직관성·범용성)", + font_size=10, color=GRAY, align=PP_ALIGN.CENTER) +add_footer(s, 29, TOTAL_SLIDES) + +# 슬라이드 30 — 인터페이스·이벤트 정의 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "주요 인터페이스 · 이벤트 정의 (Unity C#)", 30, TOTAL_SLIDES, "6부 구현") + +# 좌측 — 인터페이스 트리 +add_text_box(s, Inches(0.5), Inches(1.05), Inches(6), Inches(0.5), + "인터페이스 계약 4종", + font_size=13, bold=True, color=NAVY) + +iface_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(1.55), Inches(6), Inches(5.3)) +iface_box.fill.solid() +iface_box.fill.fore_color.rgb = LIGHT_BG +iface_box.line.color.rgb = NAVY +iface_box.line.width = Pt(1.5) +iface_box.shadow.inherit = False +add_multiline_text(s, Inches(0.7), Inches(1.65), Inches(5.7), Inches(5.1), + [ + {"text": "ISkillRuntime (공통)", + "size": 12, "bold": True, "color": NAVY, "font_name": CODE_FONT}, + {"text": " SkillDataAsset Data { get; }", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " int StackLevel { get; }", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void OnEquip(PlayerSkillInventory)", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void OnUnequip()", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void Upgrade()", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": "", "size": 9}, + {"text": "IActiveSkill : ISkillRuntime", + "size": 12, "bold": True, "color": NAVY, "font_name": CODE_FONT}, + {"text": " float BaseCooldown / EffectiveCooldown", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " float CooldownRemaining", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void Fire()", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " ActiveTrigger Trigger { get; }", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": "", "size": 9}, + {"text": "IPassiveSkill : ISkillRuntime", + "size": 12, "bold": True, "color": GREEN_OK, "font_name": CODE_FONT}, + {"text": " bool IsAlwaysOn", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void ApplyModifier(PlayerStats)", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void RemoveModifier(PlayerStats)", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void OnTrigger(PassiveTriggerContext)", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": "", "size": 9}, + {"text": "IAwakeningSkill : ISkillRuntime", + "size": 12, "bold": True, "color": PURPLE, "font_name": CODE_FONT}, + {"text": " ActiveSkillData OriginalActive", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " PassiveSkillData[] RequiredPassives", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " void Awaken(PlayerSkillInventory)", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " AwakeningPattern Pattern { get; }", + "size": 9, "color": DARK_TEXT, "font_name": CODE_FONT}, + ]) + +# 우측 — 이벤트 + Enum +add_text_box(s, Inches(6.8), Inches(1.05), Inches(6), Inches(0.5), + "이벤트 · Enum 정의", + font_size=13, bold=True, color=NAVY) +ev_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(6.8), Inches(1.55), Inches(6), Inches(5.3)) +ev_box.fill.solid() +ev_box.fill.fore_color.rgb = LIGHT_BG +ev_box.line.color.rgb = NAVY +ev_box.line.width = Pt(1.5) +ev_box.shadow.inherit = False +add_multiline_text(s, Inches(7.0), Inches(1.65), Inches(5.7), Inches(5.1), + [ + {"text": "Simulation.Event 기반 발동 (BT7-Dev 일관)", + "size": 11, "bold": True, "color": NAVY}, + {"text": " SkillFireEvent (액티브 발동)", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": " AwakeningFireEvent (각성 발동)", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": " AwakeningEffectEvent (풀스크린 연출)", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": "", "size": 9}, + {"text": "이벤트 핸들러 (PlayerSkillInventory)", + "size": 11, "bold": True, "color": NAVY}, + {"text": " event Action OnEnemyKilled", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": " event Action OnPlayerDamaged", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": " void OnTreasureChestOpened()", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": " void AddSkillByCardId(string)", + "size": 10, "font_name": CODE_FONT, "color": DARK_TEXT}, + {"text": "", "size": 9}, + {"text": "ActiveTrigger { OnTime · OnHit · OnKill }", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": "AwakeningPattern {", + "size": 10, "color": PURPLE, "font_name": CODE_FONT}, + {"text": " ScaleUp · AddEffect · MultiFire · GlobalSpread }", + "size": 10, "color": PURPLE, "font_name": CODE_FONT}, + {"text": "PassiveTriggerKind {", + "size": 10, "color": GREEN_OK, "font_name": CODE_FONT}, + {"text": " OnPlayerDamaged · OnEnemyKilled · OnTimer }", + "size": 10, "color": GREEN_OK, "font_name": CODE_FONT}, + {"text": "ActiveCategory { Projectile · MeleeArea ·", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " PlacementPersistent · Minion · Debuff · SpecialJudge }", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": "PassiveCategory { StatUp · CycleAmplify ·", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + {"text": " Survival · Recovery · ResourceExpand }", + "size": 10, "color": DARK_TEXT, "font_name": CODE_FONT}, + ]) +add_footer(s, 30, TOTAL_SLIDES) + +# 슬라이드 31 — BT.Framework / BT7-Dev 활용 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "BT.Framework Tier 1·2 + BT7-Dev 자산 활용", 31, TOTAL_SLIDES, "6부 구현") + +# BT.Framework +add_text_box(s, Inches(0.5), Inches(1.05), Inches(6), Inches(0.5), + "BT.Framework Tier 1·2 활용 (P29 준수)", + font_size=13, bold=True, color=GREEN_OK) +bt_data = [ + ["자산", "활용 영역"], + ["오브젝트 풀링 Tier 1", "ProjectileSpawner / MinionSpawner / AuraZone 인스턴스 관리"], + ["Log Tier 1", "스킬 발동·각성 트리거 디버그 로그"], + ["ValidationEx Tier 1", "런타임 데이터 무결성 검증 (ScriptableObject 누락 등)"], + ["MathEx Tier 1", "확률 분포·가중치 계산"], + ["EnumEx Tier 1", "AttributeTag·TypeTag Flags 연산"], + ["FormatEx Tier 1", "Lv.N 표시·HUD 텍스트 렌더링"], + ["SafeAreaBorder Tier 1", "각성 풀스크린 연출 Safe Area 보장"], +] +add_table(s, Inches(0.5), Inches(1.55), Inches(6), Inches(3.5), + bt_data, header_fill=GREEN_OK, body_size=9.5, header_size=11, + col_widths=[2, 4]) + +# BT7-Dev +add_text_box(s, Inches(6.8), Inches(1.05), Inches(6), Inches(0.5), + "BT7-Dev 기존 자산 통합 (C39 실측)", + font_size=13, bold=True, color=NAVY) +bt7_data = [ + ["자산", "통합 방식"], + ["Health.cs (Decrement·Heal·IncreaseMaxHearts)", "P-C·P-D 패시브 효과 적용 API (변경 없음)"], + ["Health.OnDamagedEvent (신설)", "Phase 2-C 확장 - OnHit 트리거 발화"], + ["PlayerAttack : Simulation.Event", "기본 무기 발동 유지 (스킬 시스템 별도)"], + ["PlayerAttackTicker", "기본 무기 주기 타이머 유지"], + ["AttackHitbox.Fire(Vector2)", "B 카테고리 효과 발동기 재활용"], + ["PlayerController.Facing", "투사체 발사 방향 / 근접 판정 방향"], + ["Simulation.Schedule()", "스킬 효과 발동 Dispatch 패턴 일관"], +] +add_table(s, Inches(6.8), Inches(1.55), Inches(6), Inches(3.5), + bt7_data, body_size=9.5, header_size=11, + col_widths=[2.5, 3.5]) + +# 하단 — 의존성 +add_text_box(s, Inches(0.5), Inches(5.3), Inches(12), Inches(0.5), + "외부 의존성 (Unity 패키지)", + font_size=13, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(5.8), Inches(12.3), Inches(1.3), + [ + {"text": "Input System (com.unity.inputsystem) — 이동·점프 입력 (Attack 액션 제거 상태 유지)", + "bullet": True, "size": 11}, + {"text": "Cinemachine (com.unity.cinemachine) — 플레이어 추적 카메라", + "bullet": True, "size": 11}, + {"text": "Universal RP (com.unity.render-pipelines.universal) — 2D 렌더링·이펙트", + "bullet": True, "size": 11}, + {"text": "BT.Framework Tier 1 (코어코드/BT.Framework/) — 16/16 완결 자산", + "bullet": True, "size": 11, "color": GREEN_OK}, + ]) +add_footer(s, 31, TOTAL_SLIDES) + +# 슬라이드 32 — Phase 분리 우선순위 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "구현 Phase 분리 — P0~P4 우선순위 (BT12-Dev §7-2)", 32, TOTAL_SLIDES, "6부 구현") + +phases = [ + ("P0", "코어 — 인터페이스·ScriptableObject 코드 생성", NAVY, + "ISkillRuntime / IActive / IPassive / IAwakening 4개\nSkillDataAsset 베이스 + 3 파생\nPlayerSkillInventory · PlayerStats · AwakeningManager · SkillRuntimeFactory\nSkillFireEvent · AwakeningFireEvent · AwakeningEffectEvent", + "Sonnet 클라이언트팀 단일 Task"), + ("P1", "데이터 — CSV → ScriptableObject 파이프라인", PURPLE, + "SkillCsvImporter (EditorWindow)\n60종 .asset 파일 일괄 생성 (Active/Passive/Awakening 각 20)\n카테고리·패턴 enum 매핑 테이블 적용", + "에디터 툴 1회 실행 시 60종 자산 생성"), + ("P2", "UI — 픽 UI · HUD 슬롯 · 각성 연출", ACCENT, + "CardPickCanvas (Additive) - 액티브/패시브 분류 표시\nHUD 액티브·패시브 슬롯 6+6 (Lv.5 황금 테두리)\n각성 발동 풀스크린 연출 (Safe Area 적용)", + "UX/02 v0.2 와이어프레임 기준"), + ("P3", "각성 시스템 - 3 조건 매니저 + 패턴 Dispatcher", PURPLE, + "AwakeningManager.CheckAwakeningEligibility()\n4 패턴별 AwakeningFireEvent.Execute() 분기\n다중 조건 충족 시 선택 UI · 단일/복수 처리", + "Phase 1·2 완료 후"), + ("P4", "보물상자 - 스폰·획득·대체 보상", ACCENT, + "TreasureChest 오브젝트 + OnTriggerEnter2D\nP20 재물복 패시브 가중치 반영\n조건 불충족 시 대체 보상 (재화·회복·경험치)", + "스테이지 구조 의존 (level-designer 연계)"), +] +for i, (phase, title, color, desc, note) in enumerate(phases): + y = Inches(1.1) + Inches(1.2) * i + # 좌측 P박스 + pbox = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), y, Inches(1.0), Inches(1.05)) + pbox.fill.solid() + pbox.fill.fore_color.rgb = color + pbox.line.fill.background() + pbox.shadow.inherit = False + ptf_ = pbox.text_frame + ptf_.vertical_anchor = MSO_ANCHOR.MIDDLE + pp_ = ptf_.paragraphs[0] + pp_.alignment = PP_ALIGN.CENTER + pr_ = pp_.add_run() + pr_.text = phase + pr_.font.name = HEADER_FONT + pr_.font.size = Pt(22) + pr_.font.bold = True + pr_.font.color.rgb = WHITE + # 우측 본문 + bbox = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(1.6), y, Inches(11.2), Inches(1.05)) + bbox.fill.solid() + bbox.fill.fore_color.rgb = LIGHT_BG + bbox.line.color.rgb = color + bbox.line.width = Pt(1) + bbox.shadow.inherit = False + add_text_box(s, Inches(1.75), y + Inches(0.05), Inches(10), Inches(0.35), + title, font_size=12, bold=True, color=color) + add_text_box(s, Inches(1.75), y + Inches(0.4), Inches(8.5), Inches(0.6), + desc, font_size=8.5, color=DARK_TEXT) + add_text_box(s, Inches(10.5), y + Inches(0.4), Inches(2.3), Inches(0.6), + note, font_size=8, color=GRAY, align=PP_ALIGN.RIGHT) + +add_footer(s, 32, TOTAL_SLIDES) + +# 슬라이드 33 — 시너지 빌드 예시 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "시너지 빌드 예시 — 8종 대표 빌드", 33, TOTAL_SLIDES, "6부 구현") + +build_data = [ + ["빌드명", "핵심 액티브", "핵심 패시브", "각성 후보", "재미 축"], + ["화염 폭격", "A02·A15", "P02·P07", "AW02·AW15", "축 1·2"], + ["번개 연쇄", "A04·A13", "P04·P06·P08", "AW03·AW13", "축 2 (화면 포화)"], + ["암흑 DoT", "A03·A06·A08", "P05·P07", "AW04·AW06·AW07", "축 1 (스택 기대감)"], + ["소환 군주", "A09·A10·A11·A20", "P11·P12·P18", "AW09·AW10·AW11·AW20", "축 1·2 (전능감)"], + ["냉기 제어", "A14·A19", "P03·P07", "AW14·AW19", "축 2 (필드 제어)"], + ["회피 게릴라", "A12·A16·A18", "P09·P10·P15", "AW12·AW16·AW18", "축 1 (예상 밖 쾌감)"], + ["철벽 생존", "A17", "P11·P12·P13·P14", "AW17", "축 3 (영속 위안)"], + ["보물상자 각성", "어떤 액티브든 Lv.5", "P20·P19·P06", "조건 충족 각성", "축 1 (조기 각성)"], +] +add_table(s, Inches(0.3), Inches(1.0), Inches(12.7), Inches(5.5), + build_data, body_size=9, header_size=11, + col_widths=[2, 2.5, 2.5, 3, 2.7]) + +add_text_box(s, Inches(0.5), Inches(6.7), Inches(12.3), Inches(0.5), + "각 빌드는 실제 런 결과를 보장하지 않으며, 픽 단계 가중치·확률에 따라 달라진다 (balance-designer 영역)", + font_size=10, color=GRAY, align=PP_ALIGN.CENTER) +add_footer(s, 33, TOTAL_SLIDES) + +# ============================================================ +# 7부 — 검증·QA (슬라이드 34) +# ============================================================ + +# 슬라이드 34 — EditMode·PlayMode 테스트 + 어뷰징 가드 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "검증·QA — EditMode·PlayMode·성능·어뷰징 가드", 34, TOTAL_SLIDES, "7부 검증") + +# 좌상 — EditMode +add_text_box(s, Inches(0.5), Inches(1.05), Inches(6), Inches(0.5), + "EditMode 테스트 (최소 10건)", + font_size=12, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(1.5), Inches(6), Inches(2.7), + [ + {"text": "ScriptableObject 인스턴스 무결성 검증", "bullet": True, "size": 10}, + {"text": "Factory.Create 분기 (Active/Passive/Awakening)", "bullet": True, "size": 10}, + {"text": "StackLevelFactor 곡선 (Lv.1=1.0~Lv.5=2.0)", "bullet": True, "size": 10}, + {"text": "Cooldown 누적 오버플로 방지 (BT7-Dev 패턴)", "bullet": True, "size": 10}, + {"text": "AwakeningManager 3 조건 분기", "bullet": True, "size": 10}, + {"text": "다중 조건 충족 → 선택 UI 진입 분기", "bullet": True, "size": 10}, + {"text": "RequiredPassives.Any 매칭 로직", "bullet": True, "size": 10}, + {"text": "이미 발동된 각성 중복 차단", "bullet": True, "size": 10}, + {"text": "CSV → ScriptableObject 60종 변환", "bullet": True, "size": 10}, + {"text": "PlayerStats 보정 누적·원복", "bullet": True, "size": 10}, + ]) + +# 우상 — PlayMode +add_text_box(s, Inches(6.8), Inches(1.05), Inches(6), Inches(0.5), + "PlayMode 통합 테스트", + font_size=12, bold=True, color=NAVY) +add_multiline_text(s, Inches(7.0), Inches(1.5), Inches(6), Inches(2.7), + [ + {"text": "각 카테고리(A~F) 액티브 1종 실제 발동 확인", "bullet": True, "size": 10}, + {"text": "Health.Decrement 호출 경로 추적", "bullet": True, "size": 10}, + {"text": "픽 UI → 슬롯 편입 → 자동 발동 사이클", "bullet": True, "size": 10}, + {"text": "Lv.5 도달 → MarkActiveMaxed 자동 호출", "bullet": True, "size": 10}, + {"text": "보물상자 획득 → 각성 발동 (단일·다중)", "bullet": True, "size": 10}, + {"text": "각성 발동 후 원 액티브 슬롯 유지 + 효과 변경", "bullet": True, "size": 10}, + {"text": "P12+P13 동시 보유 시 최대 하트 +2", "bullet": True, "size": 10}, + {"text": "P11 호신부 OnPlayerDamaged 트리거", "bullet": True, "size": 10}, + {"text": "P16 단전수련 OnEnemyKilled 회복", "bullet": True, "size": 10}, + ]) + +# 좌하 — 성능 검증 +add_text_box(s, Inches(0.5), Inches(4.4), Inches(6), Inches(0.5), + "성능 검증 포인트 (모바일 60fps 95%+)", + font_size=12, bold=True, color=ACCENT) +add_multiline_text(s, Inches(0.7), Inches(4.85), Inches(6), Inches(2.2), + [ + {"text": "AW01·AW03·AW06·AW13·AW19 광역 확산 4종", "bullet": True, "size": 10, "color": RED_HL}, + {"text": " - 파티클 상한·충돌 연산 배치 처리 필수", "size": 9, "color": GRAY}, + {"text": "AW09·AW10·AW15·AW20 다중 발동 4종", "bullet": True, "size": 10, "color": RED_HL}, + {"text": " - 동시 오브젝트 상한·풀링 용량 검증", "size": 9, "color": GRAY}, + {"text": "A20 산군상 근접 돌진 소환물", "bullet": True, "size": 10}, + {"text": " - 2D 지형 낙하·충돌 대응 (자율형 회피)", "size": 9, "color": GRAY}, + {"text": "Cooldown 하드캡 0.5s 적용 확인", "bullet": True, "size": 10}, + {"text": "Profiler GC 알로케이션 0 / 프레임", "bullet": True, "size": 10}, + ]) + +# 우하 — 어뷰징 가드 +add_text_box(s, Inches(6.8), Inches(4.4), Inches(6), Inches(0.5), + "어뷰징 가드 (밸런스 보호)", + font_size=12, bold=True, color=RED_HL) +add_multiline_text(s, Inches(7.0), Inches(4.85), Inches(6), Inches(2.2), + [ + {"text": "회복 스팸 차단 — P16·P17 회복량 감소 곡선·쿨다운", "bullet": True, "size": 10}, + {"text": "최대 하트 하드캡 5개 (P12+P13 무한 중첩 차단)", "bullet": True, "size": 10}, + {"text": "Cooldown 하드캡 0.5s (이펙트 중첩 혼잡 방지)", "bullet": True, "size": 10}, + {"text": "MoveSpeed 상한 9.0 m/s (P18·P11 무한 가속 차단)", "bullet": True, "size": 10}, + {"text": "Lv.5 이상 동일 카드 재픽 무한 루프 방지", "bullet": True, "size": 10}, + {"text": "각성 1회 발동 후 alreadyAwakened 마킹", "bullet": True, "size": 10}, + {"text": "P20 재물복 Lv.5 보물상자 확률 캡 (balance)", "bullet": True, "size": 10}, + ]) +add_footer(s, 34, TOTAL_SLIDES) + +# ============================================================ +# 8부 — 후속·부록 (슬라이드 35~36) +# ============================================================ + +# 슬라이드 35 — PD 결정 대기 영역 + 후속 안건 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "PD 결정 대기 영역 · 후속 안건", 35, TOTAL_SLIDES, "8부 후속") + +# PD 결정 대기 +pd_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(1.1), Inches(12.3), Inches(2.8)) +pd_box.fill.solid() +pd_box.fill.fore_color.rgb = LIGHT_BG +pd_box.line.color.rgb = RED_HL +pd_box.line.width = Pt(2) +pd_box.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(1.2), Inches(12), Inches(0.5), + "PD 결정 대기 영역 (본 기획서 범위 외)", + font_size=14, bold=True, color=RED_HL) +add_multiline_text(s, Inches(0.7), Inches(1.7), Inches(12), Inches(2.2), + [ + {"text": "narrative 한자 카드명 재매핑 — 일반 RPG 용어 11종 -> 조선 퇴마 톤 일괄 재매핑 (BT7-Plan 11)", + "bullet": True, "size": 11, "color": DARK_TEXT}, + {"text": "balance-designer 수치 확정 — 60종 BaseDamage·Cooldown·확률·스택·MultiFireCount·AwakeningBaseDamage", + "bullet": True, "size": 11, "color": DARK_TEXT}, + {"text": "BM·과금 구조 (Phase 3-C) — Lv.5 이상 재픽 보상 방식 / 보물상자 재화 수치 / 메타 진행 자원", + "bullet": True, "size": 11, "color": DARK_TEXT}, + {"text": "아트 톤 결정 — 카드 아이콘·각성 풀스크린 연출·이펙트 색상 가이드", + "bullet": True, "size": 11, "color": DARK_TEXT}, + {"text": "기본 시작 무기 — 퇴마사 고유 무기 1종 (A01~A20 중 1 선택 또는 별도 카드)", + "bullet": True, "size": 11, "color": DARK_TEXT}, + ]) + +# 후속 작업 안건 +suc_box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.5), Inches(4.1), Inches(12.3), Inches(2.6)) +suc_box.fill.solid() +suc_box.fill.fore_color.rgb = LIGHT_BG +suc_box.line.color.rgb = NAVY +suc_box.line.width = Pt(2) +suc_box.shadow.inherit = False +add_text_box(s, Inches(0.7), Inches(4.2), Inches(12), Inches(0.5), + "후속 작업 안건 (개발팀·기획팀 위임)", + font_size=14, bold=True, color=NAVY) +add_multiline_text(s, Inches(0.7), Inches(4.7), Inches(12), Inches(2.0), + [ + {"text": "Phase 2 클라이언트팀 위임 (Sonnet) — Unity C# 스크립트 25종 + 테스트 10건 + 60종 .asset", + "bullet": True, "size": 11}, + {"text": "Phase 3 개발팀장 검증 (Opus) — 6 검증 항목 (자원·구조·BT7-Dev 호환·BT.Framework 활용·테스트 green·balance 이관)", + "bullet": True, "size": 11}, + {"text": "balance-designer 60종 수치 확정 Task — 기획팀장 경유 위임", + "bullet": True, "size": 11}, + {"text": "narrative-designer 재매핑 Task — PD 후속 검토 후 일괄", + "bullet": True, "size": 11}, + {"text": "content/01 v0.3 동기화 — A19·A20·AW19·AW20 신설 + P25→P20 재편 (기획팀장 연속 작업)", + "bullet": True, "size": 11}, + {"text": "Unity 프로젝트 경로 paths.local.json 설정 — Phase 2 착수 차단 블로커 해소 필요", + "bullet": True, "size": 11, "color": RED_HL}, + ]) + +add_footer(s, 35, TOTAL_SLIDES) + +# 슬라이드 36 — 변경 이력·참조·용어집·종결 +s = prs.slides.add_slide(BLANK) +add_solid_bg(s, WHITE) +add_slide_header(s, "변경 이력 · 참조 SOT · 용어집", 36, TOTAL_SLIDES, "8부 부록") + +# 변경 이력 +add_text_box(s, Inches(0.5), Inches(1.05), Inches(6), Inches(0.5), + "변경 이력", + font_size=12, bold=True, color=NAVY) +hist_data = [ + ["일자", "버전", "변경"], + ["2026-04-22", "BT6-Plan", "Phase 3-B 초안 (system·content)"], + ["2026-04-24", "BT7-Plan", "3분류 구조·VS 순수형·Lv.5"], + ["2026-04-24", "BT11-Plan", "60종 명세 (20+20+20)"], + ["2026-04-24", "BT12-Dev v1", "Unity C# 4계층 설계"], + ["2026-04-26", "본 PPT v1", "통합 기획서 36슬라이드"], +] +add_table(s, Inches(0.5), Inches(1.5), Inches(6), Inches(2.5), + hist_data, body_size=9.5, header_size=10, + col_widths=[1.5, 1.5, 3]) + +# 참조 SOT +add_text_box(s, Inches(6.8), Inches(1.05), Inches(6), Inches(0.5), + "참조 SOT 7종", + font_size=12, bold=True, color=NAVY) +add_multiline_text(s, Inches(7.0), Inches(1.5), Inches(6), Inches(2.5), + [ + {"text": "system/01_카드_시스템.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "content/01_카드_풀.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "content/02_스킬_효과_컨셉.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "content/02_스킬_효과_컨셉_v0.3.csv", "size": 9, "font_name": CODE_FONT}, + {"text": "balance/01_전투_수치.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "balance/02_레벨업_곡선.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "ux/02_HUD_설계.md v0.2", "size": 9, "font_name": CODE_FONT}, + {"text": "개발/spec/스킬_시스템_설계_v1.md", "size": 9, "font_name": CODE_FONT}, + {"text": "개발/06_BT7-Plan_VS순수형_재구조.md", "size": 9, "font_name": CODE_FONT}, + ]) + +# 용어집 +add_text_box(s, Inches(0.5), Inches(4.2), Inches(12.3), Inches(0.5), + "용어집", + font_size=12, bold=True, color=NAVY) +gloss_data = [ + ["용어", "정의"], + ["액티브", "자동 발동 무기·스킬 (주기·판정·대미지). Lv.1~Lv.5 스택"], + ["패시브", "장착 즉시 상시 적용 보정 (스탯·확률·지속). 일부는 조건부 트리거"], + ["각성", "별도 풀의 진화 카드. 3 조건 동시 충족 시 발동. 한 런 1회"], + ["보물상자", "각성 게이팅 트리거. 스테이지 진행 중 조건부 스폰. 불충족 시 대체 보상"], + ["쿼터", "1 하트 = 4 쿼터. 피해는 쿼터 단위 차감 (Zelda BotW 방식)"], + ["StackLevelFactor", "Lv 별 대미지 배율 (Lv.1=1.0 / Lv.5=2.0)"], + ["VS 순수형", "Vampire Survivors 패턴 — 공격 버튼 없이 자동 발동 (BT7-Plan 확정 방향 2)"], + ["i-frame", "피격 후 무적 시간 0.6s. 연속 피격 방지"], +] +add_table(s, Inches(0.5), Inches(4.7), Inches(12.3), Inches(2.4), + gloss_data, body_size=9, header_size=10, + col_widths=[2.5, 9.8]) +add_footer(s, 36, TOTAL_SLIDES) + +# ============================================================ +# 저장 +# ============================================================ +import os +output_path = "스킬시스템_개발기획서_v1.pptx" +prs.save(output_path) +print(f"\n=== 빌드 완료 ===") +print(f"산출물: {os.path.abspath(output_path)}") +print(f"슬라이드 수: {len(prs.slides)}") +print(f"파일 크기: {os.path.getsize(output_path)} bytes") diff --git a/프로젝트/EerieVillage/기획/기묘한고을_초기기획서_v1.docx b/프로젝트/EerieVillage/기획/기묘한고을_초기기획서_v1.docx new file mode 100644 index 0000000..b00abeb Binary files /dev/null and b/프로젝트/EerieVillage/기획/기묘한고을_초기기획서_v1.docx differ diff --git a/프로젝트/EerieVillage/기획/스킬시스템_개발기획서_v1.pptx b/프로젝트/EerieVillage/기획/스킬시스템_개발기획서_v1.pptx new file mode 100644 index 0000000..7a61a7b Binary files /dev/null and b/프로젝트/EerieVillage/기획/스킬시스템_개발기획서_v1.pptx differ