377 lines
37 KiB
Python
377 lines
37 KiB
Python
|
|
# FirstRising 리뉴얼 기획서 v2 — 라이트 템플릿(PD 레퍼런스: 포트폴리오 요약 라이트) · 내용은 v1과 같음
|
|||
|
|
import os, csv, sys
|
|||
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|||
|
|
from deckkit_light import *
|
|||
|
|
|
|||
|
|
BASE = r"E:\BurningTimes\공유\기획\FirstRising"
|
|||
|
|
FR = lambda n: os.path.join(BASE, "시스템분석", "img", n)
|
|||
|
|
TB = lambda n: os.path.join(BASE, "레퍼런스_TBH", "img", n)
|
|||
|
|
MK = lambda n: os.path.join(BASE, "리뉴얼", "img", n)
|
|||
|
|
OUT = os.path.join(BASE, "리뉴얼", "2026-09-19_FirstRising_리뉴얼_기획서_v2.pptx")
|
|||
|
|
D = Deck()
|
|||
|
|
|
|||
|
|
def why(sl, x, y, w, h, items, title="왜 바꾸나", size=10):
|
|||
|
|
D.panel(sl, x, y, w, h, radius=0.05)
|
|||
|
|
D.text(sl, x + 0.2, y + 0.14, w - 0.4, 0.26, title, size=10.5, bold=True, color=INK)
|
|||
|
|
yy = y + 0.5
|
|||
|
|
step = (h - 0.62) / max(len(items), 1)
|
|||
|
|
for i, t in enumerate(items, 1):
|
|||
|
|
D.num(sl, x + 0.2, yy + 0.02, i, d=0.22, size=8)
|
|||
|
|
D.text(sl, x + 0.52, yy - 0.02, w - 0.72, step, t, size=size, color=BODY)
|
|||
|
|
yy += step
|
|||
|
|
|
|||
|
|
def asis_tobe(sl, x, y, w, h, asis, tobe, split=0.42):
|
|||
|
|
aw = w * split - 0.15; tw = w - aw - 0.3
|
|||
|
|
D.panel(sl, x, y, aw, h, radius=0.05)
|
|||
|
|
D.pill(sl, x + 0.18, y + 0.16, "지금", fill=DARK, w=0.6)
|
|||
|
|
D.bullets(sl, x + 0.18, y + 0.52, aw - 0.3, h - 0.6, asis, size=10, color=SUB, gap=3)
|
|||
|
|
D.arrow(sl, x + aw + 0.04, y + h / 2, x + aw + 0.26, y + h / 2)
|
|||
|
|
tx = x + aw + 0.3
|
|||
|
|
D.rect(sl, tx, y, tw, h, fill=WHITE, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.05)
|
|||
|
|
D.pill(sl, tx + 0.18, y + 0.16, "바뀐 뒤", fill=RED, w=0.75)
|
|||
|
|
D.bullets(sl, tx + 0.18, y + 0.52, tw - 0.3, h - 0.6, tobe, size=10.5, color=INK, gap=3)
|
|||
|
|
|
|||
|
|
# 01 표지 ===============================================================================
|
|||
|
|
D.page += 1
|
|||
|
|
sl = D.prs.slides.add_slide(D.blank)
|
|||
|
|
D.rect(sl, 0, 0, 0.19, 7.5, fill=RED)
|
|||
|
|
D.rect(sl, 7.22, 0, 6.11, 7.5, fill=PANEL)
|
|||
|
|
D.text(sl, 7.72, 0.25, 5.27, 0.4, "근거 화면 — FirstRising 실캡처 · TBH 실행 화면 · 목업", size=10, bold=True, color=SUB, align=PP_ALIGN.CENTER)
|
|||
|
|
grid = [(MK("mock_taskbar_concept.png"), "컨셉 목업", "작업표시줄 상주"), (FR("capture_01_wave_combat.jpg"), "FirstRising", "3D 물량 전투"),
|
|||
|
|
(FR("capture_02_battlefence.jpg"), "FirstRising", "필드 기믹·배틀펜스"), (MK("cover_hero.jpg"), "TBH", "디아블로식 장비"),
|
|||
|
|
(MK("cover_trade.jpg"), "TBH", "Steam 교역선"), (MK("cover_rune.jpg"), "TBH", "방치 효율 룬 트리")]
|
|||
|
|
for i, (p, t, s) in enumerate(grid):
|
|||
|
|
gx = 7.61 + (i % 2) * 2.75; gy = 0.86 + (i // 2) * 1.88
|
|||
|
|
D.image(sl, p, gx, gy, 2.58, 1.46, fill=WHITE)
|
|||
|
|
D.caption(sl, gx, gy + 1.52, 2.58, t, s)
|
|||
|
|
D.hline(sl, 7.61, 6.5, 5.33, color=RGBColor(0xDD, 0xDD, 0xDD))
|
|||
|
|
D.text(sl, 7.61, 6.6, 5.33, 0.18, "RENEWAL OVERVIEW", size=7.5, color=SUB)
|
|||
|
|
D.text(sl, 7.61, 6.8, 5.33, 0.32, "3대 핵심 · 9개 변경 영역 · 승인 요청 4건", size=13, bold=True, color=INK)
|
|||
|
|
D.text(sl, 0.81, 0.81, 6.1, 0.2, "FIRSTRISING RENEWAL PROPOSAL", size=9, bold=True, color=SUB)
|
|||
|
|
D.text(sl, 0.81, 1.15, 6.4, 0.3, "개발 승인 요청 · FirstRising 리뉴얼 기획서", size=12.5, color=RED, face=F_MED)
|
|||
|
|
D.text(sl, 0.75, 1.52, 6.45, 1.45, ["작업표시줄 위의", [("3D 다크 판타지", {"color": RED})]], size=34, color=INK, face=F_BLACK, spacing=1.05)
|
|||
|
|
D.text(sl, 0.81, 3.08, 6.1, 0.85, "업무 중 작업표시줄에 상주하며 성장하는 방치형 파밍 게임으로 FirstRising을 리뉴얼합니다. TBH에서 검증된 3요소를 핵심으로 삼고, 3D 물량 전투와 타격감으로 차별화합니다.",
|
|||
|
|
size=10.5, color=BODY, spacing=1.3)
|
|||
|
|
rows = [("핵심 1", "작업표시줄 상주 방치형 — 켜두면 자동 전투·성장"), ("핵심 2", "디아블로식 파밍 — 등급·랜덤 옵션·고유 옵션"), ("핵심 3", "Steam 장터 거래 — 드롭에 현실 가치")]
|
|||
|
|
D.hline(sl, 0.81, 4.03, 6.11)
|
|||
|
|
for i, (k, v) in enumerate(rows):
|
|||
|
|
yy = 4.1 + i * 0.345
|
|||
|
|
D.text(sl, 0.81, yy, 1.17, 0.22, k, size=9.5, bold=True, color=RED)
|
|||
|
|
D.text(sl, 1.97, yy, 4.94, 0.22, v, size=9.5, color=BODY)
|
|||
|
|
D.hline(sl, 0.81, yy + 0.28, 6.11)
|
|||
|
|
D.text(sl, 0.81, 5.33, 1.2, 0.4, "총괄PM", size=18, bold=True, color=INK)
|
|||
|
|
D.text(sl, 2.0, 5.42, 3.0, 0.3, "BurningTimes", size=12.5, color=SUB, face=F_LIGHT)
|
|||
|
|
D.text(sl, 0.81, 5.8, 6.1, 0.2, "2026-09-19 · BT22-Plan", size=9, color=SUB)
|
|||
|
|
D.text(sl, 0.81, 6.02, 6.1, 0.2, "근거 자료 FirstRising 시스템 분석 기획서 · TBH 레퍼런스 분석 v2", size=9, color=RED)
|
|||
|
|
D.text(sl, 0.81, 6.61, 6.25, 0.42, "01 요약 · 02 왜 바꾸나 · 03 컨셉 · 04 변경 9개 영역 · 05 로드맵·리스크 · 06 승인 요청", size=8, color=RED)
|
|||
|
|
|
|||
|
|
# 02 한 장 요약 =========================================================================
|
|||
|
|
sl = D.slide("한 장 요약", "SUMMARY | 한 장 요약", "무엇을 제안하고, 왜 바꾸며, 무엇이 바뀌고, 무엇을 결정해야 하는지 한 장으로 정리했습니다.",
|
|||
|
|
"근거 FirstRising 시스템 분석 · TBH 레퍼런스 분석 v2")
|
|||
|
|
D.panel(sl, X0, Y0, XW, 4.38)
|
|||
|
|
D.hline(sl, 1.9, 2.14, 9.5, color=RED, width=1.0)
|
|||
|
|
cols = [("01", "제안", ["모바일 서바이벌 → Steam PC", "작업표시줄 상주형 3D 방치 파밍", "3요소를 핵심 콘텐츠로"]),
|
|||
|
|
("02", "근거", ["현 구조의 게임성 약점 4가지", "TBH가 3요소 조합을 시장에서 입증", "우리 3D·물량·타격감이 차별점"]),
|
|||
|
|
("03", "바뀌는 것", ["플랫폼 · 코어 루프 · 파티", "아이템 · 순환 허브 · 메타 성장", "경제 · BM · 서버/보안 (9개 영역)"]),
|
|||
|
|
("04", "요청", ["Steam PC 우선 전환", "모바일 BM 폐기", "P0 프로토타입 착수 · 법률 검토"])]
|
|||
|
|
cw = (XW - 0.34 - 0.48) / 4
|
|||
|
|
for i, (n, t, lines) in enumerate(cols):
|
|||
|
|
cx = X0 + 0.17 + i * (cw + 0.16)
|
|||
|
|
D.rect(sl, cx + cw / 2 - 0.3, 1.84, 0.6, 0.6, fill=WHITE, line=RED, lw=1.25, shape=MSO_SHAPE.OVAL)
|
|||
|
|
D.text(sl, cx + cw / 2 - 0.3, 1.84, 0.6, 0.6, n, size=15, bold=True, color=RED, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
|
|||
|
|
D.text(sl, cx, 2.55, cw, 0.3, t, size=12, bold=True, color=INK, align=PP_ALIGN.CENTER)
|
|||
|
|
D.card(sl, cx, 2.95, cw, 2.9)
|
|||
|
|
D.bullets(sl, cx + 0.2, 3.12, cw - 0.4, 2.6, lines, size=10.5, color=BODY, gap=8)
|
|||
|
|
D.banner(sl, "핵심", "새로 만드는 게임이 아니라, 검증된 조합 위에 우리 자산(3D 다크 판타지·ECS 물량·타격감)을 얹는 전환입니다.")
|
|||
|
|
|
|||
|
|
# 03 왜 바꾸나 ==========================================================================
|
|||
|
|
sl = D.slide("왜 바꾸는가 — 지금 구조의 한계", "WHY | 지금의 한계", "짧게 끊어 하는 모바일 서바이벌 구조는 '오래 켜두고 쌓는' 성장 경험과 맞지 않습니다.",
|
|||
|
|
"근거 FirstRising 시스템 분석 — 게임성 평가(부정 요인) · 실캡처 · MapTable · PlayerTable")
|
|||
|
|
D.kpis(sl, X0, Y0, XW, 1.0, [("2종 · 스탯 동일", "플레이어블 캐릭터 — 정체성 없음", True), ("128 : 5", "맵 수 : 전투 환경 — 반복 위험", True),
|
|||
|
|
("에너지 5", "챕터 1회 입장 — 5~6판이면 소진", True), ("광고·가챠·IAP", "모바일 BM에 의존", True)])
|
|||
|
|
D.image(sl, FR("capture_03_main_chapter.jpg"), X0, 2.82, 5.85, 3.0, fill=WHITE)
|
|||
|
|
D.caption(sl, X0, 5.87, 5.85, "FirstRising 챕터 입장", "게임 시작 버튼에 에너지 ×5")
|
|||
|
|
D.image(sl, FR("capture_07_shop_gacha.jpg"), 6.78, 2.82, 5.85, 3.0, fill=WHITE)
|
|||
|
|
D.caption(sl, 6.78, 5.87, 5.85, "FirstRising 상점·가챠", "광고 무료 횟수 · 10/50회 천장")
|
|||
|
|
D.banner(sl, "결론", "지금 구조를 다듬는 것으로는 '켜두는 게임'이 되지 않습니다 — 구조 전환이 필요합니다.", y=6.2)
|
|||
|
|
|
|||
|
|
# 04 왜 이 3요소인가 ===================================================================
|
|||
|
|
sl = D.slide("왜 이 3요소인가 — TBH가 검증한 조합", "EVIDENCE | TBH 검증", "TBH는 세 요소를 한 게임 안에서 결합해 Steam에서 흥행했습니다 — 수치는 클라이언트 데이터·실행 화면 기준입니다.",
|
|||
|
|
"근거 TBH 레퍼런스 분석 v2(클라이언트 데이터·실행 화면) · 공개 기사(Inven Global 기사 제목)")
|
|||
|
|
D.panel(sl, X0, Y0, XW, 2.95)
|
|||
|
|
cards = [(MK("thumb_strip.jpg"), "① 작업표시줄 상주 방치", "룬 골드의 94%가 '방치 효율'", "드롭률 48% · 자동화 25% · 보관 12% — 전투 스탯은 6%"),
|
|||
|
|
(MK("thumb_hero.jpg"), "② 디아블로식 파밍", "장비 5,954종 · 10등급", "옵션 57종 × 10티어 · 고유 옵션 89 · 클래스 맞춤 드롭 75%"),
|
|||
|
|
(MK("thumb_trade.jpg"), "③ Steam 장터 거래", "거래 가능 1,093종(전설 이상)", "교역선에 실어 장터 등록 · 후가공 아이템은 거래 금지")]
|
|||
|
|
cw = (XW - 0.28 - 0.28) / 3
|
|||
|
|
for i, (p, h1, h2, h3) in enumerate(cards):
|
|||
|
|
cx = X0 + 0.14 + i * (cw + 0.14)
|
|||
|
|
D.rect(sl, cx, 1.78, cw, 2.67, fill=WHITE, shadow=True)
|
|||
|
|
D.image(sl, p, cx, 1.78, cw, 1.25, border=False)
|
|||
|
|
D.text(sl, cx + 0.14, 3.1, cw - 0.28, 0.28, h1, size=12, bold=True, color=RED)
|
|||
|
|
D.text(sl, cx + 0.14, 3.42, cw - 0.28, 0.24, h2, size=9.5, bold=True, color=INK)
|
|||
|
|
D.text(sl, cx + 0.14, 3.7, cw - 0.28, 0.65, h3, size=8.5, color=SUB, spacing=1.25)
|
|||
|
|
D.kpis(sl, X0, 4.75, XW, 1.2, [("Steam 동시접속 2위", "공개 기사 제목 기준", True), ("동시접속 14만", "공개 기사 제목 기준", True),
|
|||
|
|
("스테이지 189", "3액트 × 10 × 4난이도 + 역병·오염", False), ("Steam 통계 68종", "온보딩 퍼널을 계측", False)], big=15)
|
|||
|
|
D.banner(sl, "결론", "'켜두면 쌓이고, 쌓인 것이 돈이 되는' 조합이 통했습니다 — 우리는 이 구조를 3D로 가져옵니다.", y=6.2)
|
|||
|
|
|
|||
|
|
# 05 컨셉 ===============================================================================
|
|||
|
|
sl = D.slide("리뉴얼 컨셉 — 작업표시줄 위의 3D 다크 판타지", "CONCEPT | 리뉴얼 컨셉", "일하는 동안 작업표시줄 위에서 파티가 싸우고, 돌아오면 전리품을 감정·합성·거래합니다.",
|
|||
|
|
"근거 FirstRising 시스템 분석(SpawnSystem 동시 2,000 · DamageSystem 판정 4종 · eGimmickType 8종) · TBH 분석 v2")
|
|||
|
|
D.image(sl, MK("mock_taskbar_concept.png"), X0, Y0, 7.9, 4.44, fill=WHITE)
|
|||
|
|
D.pill(sl, X0 + 0.15, Y0 + 0.15, "컨셉 목업", fill=RED, w=0.95)
|
|||
|
|
D.caption(sl, X0, 6.14, 7.9, "컨셉 목업", "FirstRising 실캡처를 가로 띠로 합성한 이미지 — 실제 구현 화면이 아닙니다")
|
|||
|
|
D.panel(sl, 8.8, Y0, 3.83, 4.44, radius=0.04)
|
|||
|
|
D.text(sl, 8.98, Y0 + 0.14, 3.5, 0.28, "TBH와 무엇이 다른가", size=11, bold=True, color=INK)
|
|||
|
|
D.table(sl, 8.98, Y0 + 0.55, 3.47, 2.3, [["", "TBH", "FirstRising"], ["그래픽", "2D 픽셀", "3D 다크 판타지"], ["물량", "소수 몬스터", "동시 2,000"],
|
|||
|
|
["타격감", "기본 연출", "판정 4종 연출"], ["필드", "평면 스트립", "기믹 8종·보스전"]],
|
|||
|
|
colw=[0.9, 1.1, 1.47], size=9.5, col_colors=[None, SUB, RED], col_bold=[True, False, True])
|
|||
|
|
D.text(sl, 8.98, 4.62, 3.47, 1.4, [[("가져오는 것 ", {"bold": True, "color": RED}), ("상주 방치 · 디아블로식 파밍 · Steam 장터", {})], "",
|
|||
|
|
[("차별화하는 것 ", {"bold": True, "color": RED}), ("'보는 재미' — 3D·물량·타격감", {})]], size=10, color=BODY)
|
|||
|
|
|
|||
|
|
# 06 9개 영역 ===========================================================================
|
|||
|
|
sl = D.slide("무엇을 바꾸나 — 9개 영역", "CHANGE MAP | As-Is → To-Be", "영역마다 '지금 → 바뀐 뒤 → 이유'를 한 줄로 정리했습니다. 상세 근거는 다음 쪽부터입니다.",
|
|||
|
|
"근거 FirstRising 시스템 분석 · TBH 레퍼런스 분석 v2")
|
|||
|
|
rows = [["영역", "지금 (As-Is)", "바뀐 뒤 (To-Be)", "이유"],
|
|||
|
|
["① 플랫폼", "Android 모바일", "Steam PC · 작업표시줄 상주 모드", "상주 방치는 데스크톱에서 성립"],
|
|||
|
|
["② 코어 루프", "수동 이동 서바이벌 · 판당 스킬 선택", "무개입 자동 전투 · 지속 빌드", "켜둔 동안 입력이 없어야 한다"],
|
|||
|
|
["③ 파티", "2종(스탯 동일)", "3인 파티 · 역할 클래스", "정체성 + 클래스 맞춤 파밍"],
|
|||
|
|
["④ 아이템", "5등급 · 6부위 · 고정 옵션", "등급 확장 · 랜덤 옵션 티어 · 고유 옵션", "예측 불가 드롭 = 파밍의 재미"],
|
|||
|
|
["⑤ 순환 허브", "합성·룬합성·진화 분산", "큐브형 허브 하나로 통합", "쌓이는 드롭을 한곳에서 처리"],
|
|||
|
|
["⑥ 메타 성장", "진화 306 · 특별훈련(준비 중)", "방치 효율 트리(자동화 해금)", "방치할수록 방치가 편해진다"],
|
|||
|
|
["⑦ 경제", "게임 내 폐쇄 경제", "Steam 장터 거래(상위 등급만)", "드롭의 현실 가치 = 강한 동기"],
|
|||
|
|
["⑧ BM", "에너지 · 광고 · 가챠 · IAP", "DLC(클래스·펫·치장) + 장터 수수료", "상주·거래 구조와의 충돌 제거"],
|
|||
|
|
["⑨ 서버·보안", "로컬/서버 혼용 · TLS 검증 꺼짐", "서버 권한 인벤토리 · 보안 선결", "현금 가치 = 복제·해킹 피해"]]
|
|||
|
|
D.table(sl, X0, Y0, XW, 4.95, rows, colw=[1.55, 3.3, 3.85, 3.24], size=10, hsize=10, col_colors=[None, SUB, RED, BODY], col_bold=[True, False, True, False])
|
|||
|
|
|
|||
|
|
# 07 ① 플랫폼 ===========================================================================
|
|||
|
|
sl = D.slide("① 플랫폼 — Android → Steam PC 상주 모드", "CHANGE 01 | PLATFORM", "상주 방치는 '업무 중 켜 둔 PC'에서만 성립합니다. 모바일은 화면을 켜 둘 수 없습니다.",
|
|||
|
|
"근거 TBH 설정 화면·PlayerPrefs(창 위치·확대) · FirstRising TestVisual 성능 실측(2026-09-19)")
|
|||
|
|
D.image(sl, TB("01_settings_general.jpg"), X0, Y0, 4.4, 4.1, fill=PANEL)
|
|||
|
|
D.caption(sl, X0, 5.8, 4.4, "TBH 설정", "항상 위 · 데스크톱 시작 시 자동 실행")
|
|||
|
|
asis_tobe(sl, 5.35, Y0, 7.28, 2.2, ["Android 모바일", "짧게 끊어 하는 세션", "화면을 켜 둘 수 없음"],
|
|||
|
|
["Steam PC · 작업표시줄 상주 모드", "투명 창 + UI 밖 클릭 통과", "창 위치·확대 저장 · 항상 위(선택)", "시작 시 자동 실행 · 저전력 모드"])
|
|||
|
|
why(sl, 5.35, 4.0, 7.28, 1.95, ["상주 방치는 '업무 중 PC'에서만 성립합니다.",
|
|||
|
|
"TestVisual 전투 부하 Draw 250~270 · SetPass 최대 79 → 상시 실행용 저전력 예산이 필요합니다(목표치는 P0에서 실측).",
|
|||
|
|
"TBH도 창 제어·자동 실행·항상 위를 '선택'으로 제공합니다."])
|
|||
|
|
D.banner(sl, "결론", "PC(Steam) 우선 — 모바일은 리뉴얼 검증 후 '확인용 동반 앱' 여부를 따로 판단합니다.")
|
|||
|
|
|
|||
|
|
# 08 ② 코어 루프 =========================================================================
|
|||
|
|
sl = D.slide("② 코어 루프 — 수동 서바이벌 → 무개입 자동 전투", "CHANGE 02 | CORE LOOP", "우리 스킬은 이미 자동으로 발동합니다. 바꿀 것은 '이동'과 '판마다 고르는 성장'뿐입니다.",
|
|||
|
|
"근거 FirstRising SkillTable CoolTime(자동 발동) · PlayerMoveInputSystem(수동 이동) · 스킬 선택 UI · TBH 스킬 125종 전부 규칙 발동")
|
|||
|
|
D.image(sl, FR("capture_01_wave_combat.jpg"), X0, Y0, 5.2, 2.93, fill=WHITE)
|
|||
|
|
D.caption(sl, X0, 4.62, 5.2, "FirstRising 웨이브 전투", "스킬 자동 발동 · 이동만 수동")
|
|||
|
|
D.kpis(sl, X0, 4.95, 5.2, 1.0, [("작음", "스킬 발동 · 실패 처리", False), ("중간", "이동 AI · 지속 빌드 · 진행", True)], big=15)
|
|||
|
|
D.table(sl, 6.1, Y0, 6.53, 2.45, [["요소", "지금", "바뀐 뒤", "변경량"], ["스킬 발동", "쿨타임 자동", "그대로 유지", "작음"],
|
|||
|
|
["이동", "플레이어 조작", "AI 자동 이동(접근·회피)", ("중간", RED)], ["성장 선택", "판마다 레벨업 3택", "지속 빌드(장비·스킬 칸)", ("중간", RED)],
|
|||
|
|
["진행", "챕터 입장·종료", "웨이브 연속 → 포털로 다음 스테이지", ("중간", RED)], ["실패", "부활 1회 후 종료", "자동 재도전 · 오프라인 보상", "작음"]],
|
|||
|
|
colw=[1.2, 1.55, 2.95, 0.83], size=9.5, col_colors=[None, SUB, INK, INK], col_bold=[True, False, True, True])
|
|||
|
|
why(sl, 6.1, 4.25, 6.53, 1.7, ["상주형은 켜둔 동안 입력이 0이어야 합니다.", "스킬은 이미 쿨타임 자동 발동 — 입력만 걷어내면 됩니다.",
|
|||
|
|
"전투 연출·물량·기믹은 그대로 재사용합니다."])
|
|||
|
|
D.banner(sl, "결론", "입력만 걷어내 '보는 전투'로 바꿉니다 — 연출·물량·기믹은 그대로 둡니다.")
|
|||
|
|
|
|||
|
|
# 09 ③ 파티 =============================================================================
|
|||
|
|
sl = D.slide("③ 파티 — 2종(스탯 동일) → 3인 파티 · 역할 클래스", "CHANGE 03 | PARTY", "지금 캐릭터 선택은 외형 선택일 뿐입니다. 클래스가 있어야 '내 파티에 맞는 장비'라는 파밍 목표가 생깁니다.",
|
|||
|
|
"근거 FirstRising PlayerTable(Ariel·Barbarian 전 필드 동일) · TBH 3인 파티(실행 화면) · 드롭 행 75% 클래스 조건 · DLC 영웅 3")
|
|||
|
|
D.image(sl, TB("02_taskbar_strip.jpg"), X0, Y0, 5.6, 1.21, fill=WHITE)
|
|||
|
|
D.caption(sl, X0, 2.9, 5.6, "TBH 작업표시줄 스트립", "3인 파티 자동 전투")
|
|||
|
|
D.table(sl, X0, 3.25, 5.6, 2.7, [["역할(안)", "전투 방식", "파밍에서의 의미"], ["방패 탱커", "근접 · 막기 · 방어", "방어·저항 장비 수요"],
|
|||
|
|
["근접 딜러", "광역 · 출혈", "주무기·치명 옵션 수요"], ["원거리 딜러", "투사체 · 관통", "활·석궁·투사체 옵션 수요"], ["서포터", "버프 · 회복 · 부활", "파티 전체 옵션 수요"]],
|
|||
|
|
colw=[1.35, 1.75, 2.5], size=9.5)
|
|||
|
|
asis_tobe(sl, 6.55, Y0, 6.08, 2.2, ["플레이어블 2종", "전 스탯 동일 — 선택은 외형뿐"], ["3인 파티 · 역할 클래스", "탱커 · 근접 · 원거리 · 서포터", "기본 3클래스 무료, 추가는 DLC"], split=0.4)
|
|||
|
|
why(sl, 6.55, 4.0, 6.08, 1.95, ["클래스가 파밍 목표를 만듭니다 — TBH 드롭 행의 75%가 클래스 조건입니다.", "파티 조합이 방치 중 '다음에 볼 이유'가 됩니다.",
|
|||
|
|
"추가 클래스는 DLC 판매 축이 됩니다(TBH: 6종 중 3종 DLC)."])
|
|||
|
|
D.banner(sl, "결론", "기본 3클래스로 무료 파티를 완성하고, 추가 클래스는 DLC로 판매합니다.")
|
|||
|
|
|
|||
|
|
# 10 ④ 아이템 ============================================================================
|
|||
|
|
sl = D.slide("④ 아이템 — 고정 옵션 → 디아블로식 랜덤 파밍", "CHANGE 04 | ITEMIZATION", "같은 아이템이 두 번 나오지 않는 '감정의 기대감'이 방치 중 접속 동기입니다. 고정 옵션은 한 번 맞추면 목표가 끝납니다.",
|
|||
|
|
"근거 FirstRising ItemOptionTable(등급 1~5 · 고정/조건부 능력) · 부위 6 · TBH 10등급 · 옵션 57×10티어 · 고유 옵션 89 · 아이템 레벨 1~100")
|
|||
|
|
D.image(sl, MK("mock_item_tooltip.png"), X0, Y0, 3.7, 4.1, fill=PANEL)
|
|||
|
|
D.caption(sl, X0, 5.8, 3.7, "목업", "FirstRising 아이템 툴팁 예시(이름·수치는 예시)")
|
|||
|
|
D.table(sl, 4.65, Y0, 7.98, 2.9, [["항목", "지금 (FirstRising)", "바뀐 뒤", "참고 (TBH)"], ["등급", "5단계", "8~10단계", "10단계"],
|
|||
|
|
["부위", "6", "8~10 (무기·보조·방어구·장신구)", "10"], ["옵션", "등급별 고정·조건부", "랜덤 옵션 + 티어", "57종 × 10티어"],
|
|||
|
|
["아이템 레벨", "없음", "스테이지 레벨과 연동", "1~100"], ["고유 옵션", "없음", "스킬 변형(원소·범위·투사체)", "89종"],
|
|||
|
|
["드롭", "웨이브별 젬 가중치", "클래스 맞춤 + 등급 추첨", "행 75% 클래스 조건"]],
|
|||
|
|
colw=[1.35, 2.0, 2.85, 1.78], size=9.5, col_colors=[None, SUB, RED, SUB], col_bold=[True, False, True, False])
|
|||
|
|
why(sl, 4.65, 4.7, 7.98, 1.25, ["드롭 하나하나를 '열어볼 이유'로 — 랜덤 옵션 · 티어 · 스킬을 바꾸는 고유 옵션.",
|
|||
|
|
"등급이 오를수록 거래 가능·고가 — Steam 장터(⑦)와 직결됩니다."], title="왜 바꾸나")
|
|||
|
|
D.banner(sl, "결론", "고정 옵션을 걷어내고 '같은 게 두 번 없는' 드롭으로 — 파밍이 곧 방치의 목적이 됩니다.")
|
|||
|
|
|
|||
|
|
# 11 ⑤ 순환 허브 ==========================================================================
|
|||
|
|
sl = D.slide("⑤ 순환 허브 — 흩어진 강화 시스템 → 큐브형 하나로", "CHANGE 05 | LOOP HUB", "방치로 쌓이는 드롭을 한곳에서 합성·분해해야 '보관 한도'가 접속 동기로 작동합니다.",
|
|||
|
|
"근거 FirstRising ItemComposeTable · RuneComposeTable · EvolutionAbilityTable · SpecialTraining · TBH 큐브 9기능 · 합성 확률(GradeInfoData)")
|
|||
|
|
D.image(sl, TB("06_cube_panel.jpg"), X0, Y0, 3.6, 4.1, fill=PANEL)
|
|||
|
|
D.caption(sl, X0, 5.8, 3.6, "TBH 큐브", "연금술 레시피 · 큐브 Lv.93")
|
|||
|
|
asis_tobe(sl, 4.55, Y0, 8.08, 2.75, ["장비 합성", "룬 합성", "진화(306항목)", "특별 훈련(준비 중)", "쓸모없는 드롭을 처리할 곳이 없음"],
|
|||
|
|
["합성 — 같은 등급 N개 → 상위 1개", "분해 — 모든 아이템 → 골드", "재설정 — 옵션 다시 굴리기", "후가공·제거 — 슬롯 부여·해제", "제작 — 부위 지정 · 허브 레벨로 해금"], split=0.4)
|
|||
|
|
why(sl, 4.55, 4.55, 8.08, 1.4, ["쌓인 드롭 → 허브에서 합성·분해 → 보관 공간이 다시 비는 순환이 방치 루프를 닫습니다.",
|
|||
|
|
"TBH 큐브는 9기능을 한 화면에 — 합성은 전설까지 확정 승급, 불멸부터 확률로 바뀝니다."])
|
|||
|
|
D.banner(sl, "결론", "강화처를 하나로 모아 '정리가 곧 성장'이 되게 합니다.")
|
|||
|
|
|
|||
|
|
# 12 ⑥ 메타 ===============================================================================
|
|||
|
|
sl = D.slide("⑥ 메타 성장 — '강해지는 트리' → '방치가 편해지는 트리'", "CHANGE 06 | META", "방치 게임의 성장 목표는 '더 세게'보다 '더 편하게 더 많이'입니다. 자동화 자체를 보상으로 팝니다.",
|
|||
|
|
"근거 TBH RuneLevelInfoData 833행(골드 499.5억) 분류 · 자동화 해금 순서 · FirstRising 진화 306 · 특별훈련 준비 중(실캡처)")
|
|||
|
|
D.image(sl, MK("cover_rune.jpg"), X0, Y0, 5.6, 2.55, fill=WHITE)
|
|||
|
|
D.caption(sl, X0, 4.24, 5.6, "TBH 룬 트리", "248노드 — 대부분이 방치 효율")
|
|||
|
|
with open(os.path.join(BASE, "레퍼런스_TBH", "data", "derived_rune_categories.csv"), encoding="utf-8-sig") as fh:
|
|||
|
|
rc = sorted(csv.DictReader(fh), key=lambda r: -int(r["gold"]))
|
|||
|
|
tot = sum(int(r["gold"]) for r in rc)
|
|||
|
|
D.card(sl, X0, 4.55, 5.6, 1.45)
|
|||
|
|
D.rect(sl, X0, 4.55, 5.6, 1.45, fill=None, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.03)
|
|||
|
|
D.bar_chart(sl, X0 + 0.1, 4.6, 5.4, 1.35, [r["category"] for r in rc[:4]], [round(int(r["gold"]) / tot * 100, 1) for r in rc[:4]])
|
|||
|
|
D.kpis(sl, 6.55, Y0, 6.08, 1.0, [("94%", "TBH 룬 골드 중 전투 외", True), ("6%", "전투 스탯", False)], big=18)
|
|||
|
|
D.panel(sl, 6.55, 2.8, 6.08, 3.2, radius=0.04)
|
|||
|
|
D.pill(sl, 6.75, 2.95, "FirstRising 방치 효율 트리(안)", fill=RED, w=2.4)
|
|||
|
|
steps = ["오프라인 보상 해금", "상자 자동 개봉", "일괄 분해 필터", "파티 슬롯 +1", "스테이지 웨이브 단축", "보관 한도 확장"]
|
|||
|
|
for i, s in enumerate(steps):
|
|||
|
|
col = i % 2; row = i // 2
|
|||
|
|
sx = 6.75 + col * 2.95; sy = 3.4 + row * 0.62
|
|||
|
|
D.card(sl, sx, sy, 2.75, 0.5)
|
|||
|
|
D.num(sl, sx + 0.12, sy + 0.13, i + 1, d=0.24, size=8.5)
|
|||
|
|
D.text(sl, sx + 0.45, sy + 0.1, 2.25, 0.3, s, size=10.5, bold=True, color=INK)
|
|||
|
|
D.text(sl, 6.75, 5.3, 5.7, 0.6, "진화·특별훈련 자리를 이 트리로 바꿉니다 — 방치로 번 재화를 '더 잘 방치하는 능력'에 재투자하는 구조.", size=9.5, color=SUB)
|
|||
|
|
D.banner(sl, "결론", "자동화를 성장 보상으로 — 방치할수록 방치가 편해지는 사다리를 설계합니다.")
|
|||
|
|
|
|||
|
|
# 13 ⑦ 경제 ===============================================================================
|
|||
|
|
sl = D.slide("⑦ 경제 — 폐쇄 경제 → Steam 장터 거래", "CHANGE 07 | ECONOMY", "드롭에 '현실 가치'가 붙는 순간 파밍 동기가 가장 강해집니다. 대신 통제 장치를 처음부터 설계합니다.",
|
|||
|
|
"근거 TBH ItemInfoData 거래 가능 1,093종 · 교역선 10칸 · 후가공 거래 금지 · 장터 구매품 우편함 수령(로컬라이즈)")
|
|||
|
|
D.image(sl, TB("09_trade_ship.jpg"), X0, Y0, 3.4, 4.1, fill=PANEL)
|
|||
|
|
D.caption(sl, X0, 5.8, 3.4, "TBH 교역선", "싣기 → Steam 장터")
|
|||
|
|
flow = [("드롭·감정", "등급·옵션 확정"), ("보관", "가방·창고"), ("교역선 적재", "거래 가능만"), ("Steam 장터", "유저 간 판매"), ("우편함 수령", "구매품 도착")]
|
|||
|
|
fw = (8.18 - 4 * 0.22) / 5
|
|||
|
|
for i, (t, s) in enumerate(flow):
|
|||
|
|
fx = 4.45 + i * (fw + 0.22)
|
|||
|
|
D.rect(sl, fx, Y0, fw, 0.9, fill=WHITE if i % 2 else PANEL, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.08)
|
|||
|
|
D.text(sl, fx, Y0 + 0.12, fw, 0.3, t, size=10.5, bold=True, color=RED if i == 3 else INK, align=PP_ALIGN.CENTER)
|
|||
|
|
D.text(sl, fx, Y0 + 0.47, fw, 0.3, s, size=8.5, color=SUB, align=PP_ALIGN.CENTER)
|
|||
|
|
if i < 4:
|
|||
|
|
D.arrow(sl, fx + fw + 0.02, Y0 + 0.45, fx + fw + 0.2, Y0 + 0.45)
|
|||
|
|
D.table(sl, 4.45, 2.75, 8.18, 3.2, [["규칙(안)", "이유"], ["상위 등급만 거래 가능", "저가 아이템 스팸 · 시세 붕괴 방지"],
|
|||
|
|
["후가공(장식 등) 아이템은 거래 불가", "거래 대상 단순화 — 제거하면 거래 가능"], ["교역선 경유 · 칸 제한", "거래량 통제 · 봇 속도 제한"],
|
|||
|
|
["서버가 인벤토리의 원본", "복제·조작 차단(현금 가치)"], ["거래마다 수수료", "외부 매출원 — Steam 수수료 구조는 확인 필요"]],
|
|||
|
|
colw=[3.4, 4.78], size=10, col_colors=[None, BODY])
|
|||
|
|
D.banner(sl, "결론", "Steam 장터로 드롭에 현실 가치를 — 상위 등급 한정·후가공 거래 금지·서버 권한으로 통제합니다.")
|
|||
|
|
|
|||
|
|
# 14 ⑧ BM =================================================================================
|
|||
|
|
sl = D.slide("⑧ BM — 에너지·광고·가챠 → DLC·치장·장터 수수료", "CHANGE 08 | BUSINESS MODEL", "상주 방치와 장터 거래 구조에 맞지 않는 모바일 BM을 걷어내고, 콘텐츠·치장 판매와 거래 수수료로 옮깁니다.",
|
|||
|
|
"근거 FirstRising ShopTable(광고 무료 횟수) · EventGachaTable · MapTable Energy · 코스튬 6세트 · TBH 상점 DLC 번들 · DLC 영웅 3 · 펫 3")
|
|||
|
|
D.image(sl, FR("capture_07_shop_gacha.jpg"), X0, Y0, 4.9, 2.76, fill=WHITE)
|
|||
|
|
D.caption(sl, X0, 4.45, 4.9, "FirstRising 지금", "가챠·광고 상점")
|
|||
|
|
D.arrow(sl, 5.68, 3.0, 6.05, 3.0, width=2.5)
|
|||
|
|
D.image(sl, TB("10_shop.jpg"), 6.15, Y0, 2.1, 2.76, fill=PANEL)
|
|||
|
|
D.caption(sl, 6.15, 4.45, 2.1, "TBH", "DLC 번들 상점")
|
|||
|
|
D.panel(sl, 8.55, Y0, 4.08, 1.3, radius=0.06)
|
|||
|
|
D.pill(sl, 8.73, Y0 + 0.14, "없앤다", fill=DARK, w=0.72)
|
|||
|
|
D.text(sl, 8.73, Y0 + 0.5, 3.75, 0.7, "에너지 · 광고 · 유료 랜덤 박스(가챠)", size=11, bold=True, color=SUB)
|
|||
|
|
D.rect(sl, 8.55, 3.05, 4.08, 1.35, fill=WHITE, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.06)
|
|||
|
|
D.pill(sl, 8.73, 3.19, "판다", fill=RED, w=0.6)
|
|||
|
|
D.text(sl, 8.73, 3.55, 3.75, 0.8, "DLC 클래스·펫 · 치장(코스튬 재활용) · 시즌 치장 · 장터 수수료", size=11, bold=True, color=INK)
|
|||
|
|
D.panel(sl, X0, 4.8, XW, 1.15, radius=0.08)
|
|||
|
|
reasons = [("에너지", "상주 방치와 정면 충돌 — 켜둔 게임이 멈추면 안 됩니다"), ("광고", "PC 상주형에는 광고 시청 동선이 없습니다"),
|
|||
|
|
("가챠", "유료 랜덤 + 장터 현금 가치 = 사행성 규제 위험 — 법률 검토 전 배제")]
|
|||
|
|
rw = XW / 3
|
|||
|
|
for i, (t, s) in enumerate(reasons):
|
|||
|
|
rx = X0 + i * rw
|
|||
|
|
D.num(sl, rx + 0.2, 4.97, i + 1, d=0.24, size=8.5)
|
|||
|
|
D.text(sl, rx + 0.52, 4.93, rw - 0.7, 0.3, t, size=10.5, bold=True, color=INK)
|
|||
|
|
D.text(sl, rx + 0.52, 5.24, rw - 0.7, 0.65, s, size=9.5, color=BODY)
|
|||
|
|
if i:
|
|||
|
|
D.vline(sl, rx, 4.95, 0.85)
|
|||
|
|
D.banner(sl, "결론", "매출 축을 '확률 판매'에서 '콘텐츠(DLC)·치장 판매 + 거래 수수료'로 옮깁니다.")
|
|||
|
|
|
|||
|
|
# 15 ⑨ 서버·보안 ==========================================================================
|
|||
|
|
sl = D.slide("⑨ 서버·보안 — 현금 가치 경제의 선결 조건", "CHANGE 09 | SERVER & SECURITY", "아이템이 Steam 장터에서 돈이 되는 순간, 복제·조작은 곧 금전 피해입니다. 경제를 열기 전에 보안부터 닫습니다.",
|
|||
|
|
"근거 FirstRising GameHub.cs:326-328(SelfSigned 무조건 통과) · GameHub local 분기 · TBH 안티치트·어뷰징 팝업·인벤토리 복구 UI · 공개 기사(출시 초 골드파밍)")
|
|||
|
|
D.panel(sl, X0, Y0, 7.5, 4.36, radius=0.03)
|
|||
|
|
checks = [("TLS 인증서 검증 복구", "지금은 조건 없이 통과(중간자 공격 노출) — 가장 먼저 수정", True), ("서버 권한 인벤토리·드롭 판정", "클라이언트는 표시만, 생성·삭제·거래는 서버가 결정", True),
|
|||
|
|
("Steam 인증·인벤토리 연동", "Google 로그인 → Steam 계정, 장터용 아이템 정의 등록", False), ("안티치트·봇 탐지·속도 제한", "비정상 획득량·자동 입력 탐지, 위반 시 거래 제한", False),
|
|||
|
|
("거래·획득 감사 로그", "복제·롤백 사고 추적과 복구", False)]
|
|||
|
|
for i, (t, s, crit) in enumerate(checks):
|
|||
|
|
cy = Y0 + 0.2 + i * 0.82
|
|||
|
|
D.card(sl, X0 + 0.18, cy, 7.14, 0.68)
|
|||
|
|
D.num(sl, X0 + 0.34, cy + 0.2, i + 1, d=0.28, size=9)
|
|||
|
|
D.text(sl, X0 + 0.78, cy + 0.07, 5.2, 0.3, t, size=11, bold=True, color=INK)
|
|||
|
|
D.text(sl, X0 + 0.78, cy + 0.36, 6.3, 0.28, s, size=9, color=SUB)
|
|||
|
|
if crit:
|
|||
|
|
D.pill(sl, X0 + 6.3, cy + 0.08, "최우선", fill=RED, w=0.75)
|
|||
|
|
D.rect(sl, 8.4, Y0, 4.23, 4.36, fill=WHITE, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.04)
|
|||
|
|
D.pill(sl, 8.6, Y0 + 0.18, "왜 먼저인가", fill=RED, w=1.1)
|
|||
|
|
D.bullets(sl, 8.6, Y0 + 0.6, 3.85, 3.6, ["아이템이 장터에서 돈이 되면 복제·조작은 곧 금전 피해입니다.", "TBH도 안티치트 감지·장터 이용 제한·인벤토리 복구 장치를 따로 운영합니다.",
|
|||
|
|
"출시 초 골드파밍·치터로 서버 장애가 보도됐습니다(공개 기사).", "FirstRising은 TLS 검증이 꺼져 있어 지금 상태로는 경제를 열 수 없습니다."],
|
|||
|
|
size=10.5, color=BODY, gap=9)
|
|||
|
|
D.banner(sl, "결론", "경제를 여는 날보다 보안을 닫는 날이 먼저 — P2 착수 조건으로 둡니다.")
|
|||
|
|
|
|||
|
|
# 16 유지 자산 =============================================================================
|
|||
|
|
sl = D.slide("그대로 쓰는 것 — 바꾸지 않는 우리 자산", "KEEP & REUSE | 유지·재활용", "보는 재미를 만드는 전투·연출·데이터는 그대로 씁니다. 새로 만드는 것은 '상주·파밍·거래' 층입니다.",
|
|||
|
|
"근거 FirstRising 시스템 분석(SpawnSystem 2,000 · DamageSystem 판정 4종 · eGimmickType 8종 · ChapterWave · 코스튬) · TestVisual 비주얼 개선")
|
|||
|
|
imgs = [(FR("capture_01_wave_combat.jpg"), "ECS 대규모 물량", "동시 2,000마리"), (FR("capture_02_battlefence.jpg"), "필드 기믹·배틀펜스", "기믹 8종"),
|
|||
|
|
(FR("capture_05_item_costume.jpg"), "코스튬 6세트", "치장 BM으로 재활용")]
|
|||
|
|
iw = (XW - 0.3) / 3
|
|||
|
|
for i, (p, t, s) in enumerate(imgs):
|
|||
|
|
ix = X0 + i * (iw + 0.15)
|
|||
|
|
D.image(sl, p, ix, Y0, iw, 2.2, fill=WHITE)
|
|||
|
|
D.caption(sl, ix, 3.9, iw, t, s)
|
|||
|
|
D.panel(sl, X0, 4.25, XW, 1.7, radius=0.05)
|
|||
|
|
keep = [("전투 연출", "타격 판정 4종 · 데미지 숫자"), ("웨이브 데이터", "ChapterWave 가중치 구조"), ("비주얼", "TestVisual 다크 판타지 렌더"),
|
|||
|
|
("데이터 파이프라인", "JSON 테이블 89종"), ("오프라인 구동", "local 모드(서버 없이 테스트)")]
|
|||
|
|
kw = (XW - 0.34 - 0.48) / 5
|
|||
|
|
for i, (t, s) in enumerate(keep):
|
|||
|
|
kx = X0 + 0.17 + i * (kw + 0.12)
|
|||
|
|
D.card(sl, kx, 4.42, kw, 1.36)
|
|||
|
|
D.pill(sl, kx + 0.14, 4.56, "유지", fill=DARK, w=0.55)
|
|||
|
|
D.text(sl, kx + 0.14, 4.92, kw - 0.28, 0.3, t, size=11, bold=True, color=INK)
|
|||
|
|
D.text(sl, kx + 0.14, 5.24, kw - 0.28, 0.5, s, size=9, color=SUB)
|
|||
|
|
D.banner(sl, "결론", "새로 만드는 게임이 아니라 '바꾸는 게임' — 재활용 자산이 개발 비용과 위험을 줄입니다.")
|
|||
|
|
|
|||
|
|
# 17 로드맵 ================================================================================
|
|||
|
|
sl = D.slide("로드맵 — 단계마다 게이트로 판단", "ROADMAP | 단계와 게이트", "기간·인력은 개발팀 산정 후 확정합니다. 여기서는 순서와 판단 기준(게이트)만 제안합니다.",
|
|||
|
|
"기간·인력 미포함 — 개발팀 산정 후 확정")
|
|||
|
|
ph = [("P0", "상주 프로토타입", ["TestVisual 기반", "투명 창 · 스트립 카메라", "자동 전투 · 저전력 모드"], "30분 상주 시 부하·가독성·'보는 재미' 사내 테스트"),
|
|||
|
|
("P1", "파밍 코어", ["아이템화(등급·랜덤 옵션)", "클래스 3종 · 드롭", "순환 허브 · 방치 트리 · 오프라인"], "사내 방치 플레이 3일 — 접속 동기·보관 압력 확인"),
|
|||
|
|
("P2", "경제 · 보안", ["TLS · 서버 권한 인벤토리", "Steam 인증 · 아이템 정의", "교역 · 안티치트 · 감사 로그"], "법률 검토 완료 + 보안 점검 통과"),
|
|||
|
|
("P3", "Steam 출시", ["체험판 / 앞서 해보기", "DLC 1차(클래스·펫)", "Steam 통계로 퍼널 계측"], "체험판 지표로 정식 출시 판단")]
|
|||
|
|
pw = (XW - 0.45) / 4
|
|||
|
|
for i, (p, t, items, gate) in enumerate(ph):
|
|||
|
|
px = X0 + i * (pw + 0.15)
|
|||
|
|
D.panel(sl, px, Y0, pw, 4.36, radius=0.04)
|
|||
|
|
D.rect(sl, px + 0.18, Y0 + 0.2, 0.62, 0.62, fill=RED if i == 0 else WHITE, line=RED, lw=1.25, shape=MSO_SHAPE.OVAL)
|
|||
|
|
D.text(sl, px + 0.18, Y0 + 0.2, 0.62, 0.62, p, size=13, bold=True, color=WHITE if i == 0 else RED, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
|
|||
|
|
D.text(sl, px + 0.92, Y0 + 0.33, pw - 1.0, 0.35, t, size=13, bold=True, color=INK)
|
|||
|
|
D.card(sl, px + 0.15, Y0 + 1.0, pw - 0.3, 1.75)
|
|||
|
|
D.bullets(sl, px + 0.3, Y0 + 1.13, pw - 0.55, 1.55, items, size=10, color=BODY, gap=6)
|
|||
|
|
D.pill(sl, px + 0.15, Y0 + 2.95, "게이트", fill=DARK, w=0.72)
|
|||
|
|
D.text(sl, px + 0.15, Y0 + 3.3, pw - 0.3, 0.95, gate, size=9.5, color=INK)
|
|||
|
|
if i < 3:
|
|||
|
|
D.arrow(sl, px + pw - 0.02, Y0 + 0.51, px + pw + 0.13, Y0 + 0.51)
|
|||
|
|
D.banner(sl, "요청", "P0만 먼저 승인 요청 — 상주 모드에서 3D 전투가 '보이고 가볍다'가 확인돼야 다음 단계가 의미 있습니다.")
|
|||
|
|
|
|||
|
|
# 18 리스크 ================================================================================
|
|||
|
|
sl = D.slide("리스크와 대응", "RISKS | 리스크", "리스크마다 대응과 확인 시점(게이트)을 정했습니다. 법률 항목은 전문가 검토 전 가정이며 결론이 아닙니다.",
|
|||
|
|
"법률 관련 항목은 전문가 검토 전 가정 — 결론 아님")
|
|||
|
|
D.table(sl, X0, Y0, XW, 4.1, [["리스크", "내용", "대응", "확인 시점"],
|
|||
|
|
["법·규제", "아이템 현금 거래 · 유료 확률형과의 결합(국내 법 해석 필요)", "법률 검토 선행 · 유료 랜덤 박스 배제", "P2 전"],
|
|||
|
|
["경제 어뷰징", "봇 파밍 · 복제로 시세 붕괴", "서버 권한 · 거래 등급 제한 · 봇 탐지", "P2"],
|
|||
|
|
["3D 가독성", "좁은 스트립에서 3D가 뭉개질 위험", "카메라 · 실루엣 · 연출 검증", "P0"],
|
|||
|
|
["상주 성능", "상시 실행 부하(현재 Draw 250~270)", "저전력 모드 · 스트립 영역만 렌더", "P0"],
|
|||
|
|
["매출 공백", "모바일 BM 폐기 후 PC 매출 전까지", "게이트로 투자 단계화", "P0·P1"],
|
|||
|
|
["차별성", "TBH 아류로 보일 위험", "3D 다크 판타지 · 물량 · 타격감을 전면에", "P1"]],
|
|||
|
|
colw=[1.5, 4.6, 4.24, 1.6], size=10, col_colors=[None, BODY, RED, INK], col_bold=[True, False, True, True], hl_rows=(1,))
|
|||
|
|
D.banner(sl, "주의", "법·규제는 결론이 아니라 확인할 항목입니다 — P2 착수 전 전문가 검토를 받습니다.", y=6.0)
|
|||
|
|
|
|||
|
|
# 19 승인 요청 =============================================================================
|
|||
|
|
sl = D.slide("승인 요청", "DECISION | 승인 요청", "아래 4건을 승인해 주시면, 개발팀 P0 산정과 기획팀 P1 상세 설계를 바로 시작합니다.", "")
|
|||
|
|
asks = [("플랫폼 전환", "Steam PC(작업표시줄 상주) 우선 개발"), ("모바일 BM 폐기", "에너지·광고·가챠 제거, DLC·치장·수수료로 전환"),
|
|||
|
|
("P0 프로토타입 착수", "TestVisual 기반 상주 모드 — 게이트 통과 시 P1 진행"), ("법률 검토 착수", "Steam 장터 거래 · 아이템 현금 가치 관련")]
|
|||
|
|
for i, (t, s) in enumerate(asks):
|
|||
|
|
ay = Y0 + 0.1 + i * 0.95
|
|||
|
|
D.rect(sl, X0 + 0.1, ay, 0.68, 0.68, fill=RED, shape=MSO_SHAPE.OVAL)
|
|||
|
|
D.text(sl, X0 + 0.1, ay, 0.68, 0.68, str(i + 1), size=22, bold=True, color=WHITE, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
|
|||
|
|
D.text(sl, X0 + 1.05, ay + 0.02, 10.5, 0.4, t, size=17, bold=True, color=INK)
|
|||
|
|
D.text(sl, X0 + 1.05, ay + 0.4, 10.5, 0.3, s, size=11, color=SUB)
|
|||
|
|
D.hline(sl, X0, ay + 0.84, XW)
|
|||
|
|
D.rect(sl, 0, 6.2, 13.333, 0.5, fill=RED)
|
|||
|
|
D.text(sl, X0, 6.2, XW, 0.5, "승인 시 다음 단계 개발팀 P0 범위·기간 산정 → 기획팀 P1 상세 설계(아이템화 · 순환 허브 · 방치 트리) 병행",
|
|||
|
|
size=11, bold=True, color=WHITE, anchor=MSO_ANCHOR.MIDDLE)
|
|||
|
|
|
|||
|
|
n = D.save(OUT)
|
|||
|
|
print("saved", OUT, "slides:", n)
|