# -*- 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")