# P8 — 로그라이크 절차 생성 맵·층 시스템·유물 방 설계 날짜: 2026-06-12 (사용자 승인 완료) 브랜치: `feature/p8-rogue-map` 선행: P7 (유물 19종 — 유물 방이 `PickNewRelic` 재사용) ## 범위 1. **절차 생성 맵** — 막 시작마다 8층×최대 4열 DAG를 Lua 런타임 생성 (런·막마다 다른 맵). `data/map.json` 정적 맵 제거 2. **층(depth) 시스템** — 노드를 지날 때마다 층 +1 (층 = 행), 층별 노드 타입 등장 규칙 3. **유물 방(보물 노드)** — 상자 열리는 모션 + 유물(`PickNewRelic`)·메소 획득, 노드 타입 `treasure` 추가 4. **맵 UI 정비** — 노드 연결 점선(도트 3개 보간), 타입별 아이콘 색/라벨, 상태 4단(방문/현재/도달 가능/잠김) 5. **메소 표기** — 화폐 표시 텍스트 "골드" → "메소" (메이플 IP, 표시만 — 내부 변수 Gold 유지) 비범위: 이벤트 노드(?), 경로 교차 방지(시각 교차 허용 — 점선이라 식별 가능), 저장. ## 절차 생성 알고리즘 (StS 방식) 그리드: 행 1~7 × 열 1~4 + 8행 보스 단일 노드. 노드 id = `"r{row}c{col}"`, 보스 = `"boss"`. 1. 시작 열: {1,2,3,4} 셔플 후 앞 2개 = 경로 1·2의 시작(서로 다름 보장), 경로 3·4는 랜덤 2. 경로 4개를 각각 1행→7행으로 걸어 올림: 다음 열 = `clamp(열 + random(-1..1), 1, 4)`. 지나는 칸마다 노드 생성(중복은 병합), `(r,c) → (r+1,c')` 간선 추가(중복 간선 병합) 3. 7행의 모든 노드 → 보스 간선 4. `MapStart` = 1행에 생성된 노드들 ### 타입 배정 (행 오름차순) | 행 | 규칙 | |---|---| | 1~2행 | combat 고정 | | 3행 | combat 45 / shop 12 / rest 12 (가중 추첨, 합계로 정규화) | | 4~6행 | combat 45 / elite 16 / shop 12 / rest 12 / treasure 15 | | 7행 (보스 직전) | rest 50 / combat 25 / shop 10 / elite 8 / treasure 7 | | 8행 | boss 고정 | 추가 제약: **부모(간선으로 들어오는 이전 행 노드) 중 elite가 있으면 해당 노드는 elite 금지** (연속 엘리트 방지). ### 층 카운터 `Depth` prop: `PickNode` 시 `Depth = node.row`. 막 전환·런 시작 시 0. TopBar Floor 텍스트를 `막 F/3 · D층`으로 확장. ### 노드 enemy 필드 제거 몬스터는 P4 이후 `node.type` 그룹 필터(`BuildMonsters`)로 결정되므로 `enemy` 필드·`CurrentEnemyId` 대입은 제거(`CurrentEnemyId = ""` 유지). `data/map.json`·`luaMapNodesTable`·`luaStartArray`·`MAX_ROW` 제거. ## 유물 방 (TreasureHud) - `PickNode` 분기에 `treasure` → `ShowTreasure` 추가 (`ShowState("treasure")`·`HideGameHud` 등록) - UI: 어두운 패널 + 타이틀("보물 상자") + 중앙 상자 버튼(닫힌 상자 RUID) + 보상 텍스트(초기 숨김) + 나가기 버튼 - `OpenChest`: 1회 가드(`ChestOpened`) → **흔들림 모션**(anchoredPosition ±8px, 0.08s × 6스텝 타이머 체인) → **열린 상자 RUID로 교체** → 보상 지급·표시 - 메소: `40 + random(0..20)` - 유물: `PickNewRelic()` — 미보유 추첨, 전부 보유 시 메소 +30 대체 - 보상 텍스트 예: `유물 획득: 황소 투구 · 메소 +52` - 상자 닫힘/열림 스프라이트는 공식 maplestory 리소스 검색("상자"/"보물상자") 후 메이커 선별 - 나가기 → `LeaveNode`(기존 → ShowMap) ## 맵 UI ### 정적 그리드 (생성기, MapHud 섹션 재작성) - 노드 버튼 28개(`Node_r{1..7}c{1..4}`, 56×56) + `Node_boss`(72×72, 상단 중앙) + 각 노드 `Label`(타입 한글) - 배치: 행 y = `-330 + (row-1)*105` (보스 y=405), 열 x = `-270 + (col-1)*180`, 보스 x=0 - 점선 도트: 모든 가능 간선(행 1~6: `c→c±1/c`, 행 7→보스)에 대해 도트 3개(8×8, t=0.25/0.5/0.75 보간 위치). 엔티티 `Dot_r{r}c{c}_{c'}_{k}` (보스행은 `Dot_r7c{c}_b_{k}`) - 모든 노드·도트는 기본 비활성, `RenderMap`이 토글 ### RenderMap 재작성 (상태 4단 + 점선) - 노드: 맵에 없으면 숨김. 있으면 Label = 타입명, 색: - 방문(`VisitedNodes`에 포함) → 어둡게 `(0.18,0.19,0.22)` - 현재 위치 → 골드 `(0.95,0.8,0.3)` - 도달 가능(IsReachable) → 타입색 밝게 + 버튼 활성 - 잠김 → 타입색 45% 어둡게 + 버튼 비활성 - 타입색: 전투 `(0.78,0.36,0.32)` / 엘리트 `(0.62,0.4,0.85)` / 상점 `(0.9,0.75,0.35)` / 휴식 `(0.4,0.75,0.45)` / 보물 `(0.35,0.7,0.75)` / 보스 `(0.85,0.25,0.25)` - 도트: 간선 존재 시 표시. 현재 노드(또는 시작 전 1행 진입선)에서 나가는 간선 = 골드, 그 외 = 회색 `(0.5,0.5,0.55)` - `VisitedNodes`(any prop): PickNode 시 추가 ### 메소 표기 표시 문자열 "골드" → "메소": TopBar Gold·ShopHud Gold·상점 가격(`N 메소`)·유물 소진 토스트. 내부 prop `Gold` 유지. ## 검증 1. **JS 미러 + 단위 테스트**: `tools/map/rogue-map.mjs`에 `generateMap(rng)` 동일 로직(시드 PRNG 주입) + `rogue-map.test.mjs` — 모든 노드가 시작점에서 도달 가능·보스 수렴·1~2행 combat만·elite/treasure 4행부터·간선 인접 열만·elite 부모 연속 금지·결정성(동일 시드 동일 맵). ⚠️ Lua `GenerateMap`과 로직 동기화 유지(sim-balance 패턴) 2. 기존 `sim-balance` 21건 유지 (맵과 무관) 3. 메이커: 빌드 0에러, 플레이테스트 — 맵 생성/점선/상태색, 노드 진행(층 증가), 유물 방 상자 연출·보상, 보스 클리어 → 다음 막 새 맵 ## 결정 사항 - 경로 4개·8층×4열 (사용자 승인 규모) - 점선 도트 방식 채택 (UI 회전 리스크 회피, StS 원작 미감) - 시각적 간선 교차는 허용 (점선이라 추적 가능 — YAGNI) - `RUN_LENGTH`/`ACT_MAPS` 막 시스템은 변경 없음 (막마다 새 맵 생성만 추가)