docs(p15): 로비 맵 + 월드 NPC 설계 spec
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
104
docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md
Normal file
104
docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# 로비 맵 + 월드 NPC 설계 (P15)
|
||||||
|
|
||||||
|
작성일: 2026-06-14
|
||||||
|
브랜치: `feature/p15-lobby-map-npc`
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
기존 **UI 패널 로비**(P14-8 `LobbyHud` — 색칠된 `UIButton` 4개 행)를 폐기하고,
|
||||||
|
**전용 로비 맵**에 **월드 NPC 엔티티 4종**을 배치한다. NPC를 누르면 각 기능이 실행되며,
|
||||||
|
플레이어 **이동·공격 모션은 로비 맵에서만** 풀린다(전투/런 맵에서는 기존대로 잠김 유지).
|
||||||
|
|
||||||
|
요청 원문: "로비를 UI로 만들지 말고, map을 추가해서 로비에 각각 기능을 할 수 있는 npc를 추가하고,
|
||||||
|
그 npc를 누르면 각 기능을 할 수 있도록 추가. 플레이어는 반드시 로비맵에서만 이동과 공격 모션을 풀어줘."
|
||||||
|
|
||||||
|
## 확정된 결정 (브레인스토밍)
|
||||||
|
|
||||||
|
| 항목 | 결정 |
|
||||||
|
|---|---|
|
||||||
|
| NPC 상호작용 | **근접 프롬프트+키(Up/Space) AND 직접 클릭** 둘 다 지원 |
|
||||||
|
| NPC 기능 | **기존 4종 유지** + 외형을 **정식 maplestory NPC 스프라이트(공식 RUID)** 로 교체 |
|
||||||
|
| 로비 맵 | **새 전용 로비 맵 추가**(`map/lobby.map`), 런 맵(map01~) 인덱스 불변 |
|
||||||
|
|
||||||
|
## 현재 상태 (조사 결과)
|
||||||
|
|
||||||
|
- **로비 = UI 패널** `LobbyHud`. NPC 4종은 색칠 `UIButton`, 전부 `tools/deck/gen-slaydeck.mjs`에 하드코딩.
|
||||||
|
- `NpcRun`(모험가→런 시작), `NpcCodex`(사서→카드 도감), `NpcShop`(상인→영혼 상점), `NpcBoard`(안내원→게시판).
|
||||||
|
- 정의 ~`gen-slaydeck.mjs:2469-2487`, 클릭 바인딩 `BindLobbyButtons()` ~`2997-3014`.
|
||||||
|
- 기능 진입 메서드: `ShowCharacterSelect()`(~3147), `ShowCodex()`, `ShowSoulShop()`, `ShowBoard()` — **재사용 대상**.
|
||||||
|
- **이동/공격 이중 잠금**:
|
||||||
|
- `tools/player/freeze-turn-player.mjs` → `Global/DefaultPlayer.model` speed/jump/accel = 0 (**전역**).
|
||||||
|
- `tools/player/gen-player-lock.mjs` → `PlayerLock` codeblock(`pc.Enable=false`, `FixedLookAt=true`)를 map01~05에 주입.
|
||||||
|
- 공격은 순수 모션 `PlayerAttackMotion()`(StateComponent ATTACK→IDLE), 필드 몬스터와 무관(전투는 데이터 배열 UI 전투).
|
||||||
|
- 플레이어 스폰: `TeleportToActMap()` → `Vector3(-6, 0.03, 0)`, Floor별 map 선택.
|
||||||
|
- **맵 파이프라인**: map01 = 저작 템플릿, map02~05 = 복제 생성. 포털 없이 `TeleportToMapPosition` 전환.
|
||||||
|
- `Global/SectorConfig.config`의 valid 목록에 맵 등록(현재 gen-maps.mjs가 갱신).
|
||||||
|
- 컴포넌트 부착 패턴: `gen-combat-monster.mjs`가 맵 몬스터에 `script.CombatMonster`를 붙이고, 해당 codeblock이 OnBeginPlay에서 `/common` 컨트롤러에 자가등록.
|
||||||
|
- **흐름**: `OnBeginPlay`→`ShowLobby()`(UI). `EndRun(text)`→4초 후 `ShowLobby()`.
|
||||||
|
|
||||||
|
## 접근법
|
||||||
|
|
||||||
|
**A. 새 로비 맵 + 월드 NPC 엔티티 (채택)** — 맵 템플릿 복제 재사용, NPC를 월드 엔티티로 배치하고
|
||||||
|
각 NPC의 codeblock이 근접+클릭을 감지해 **기존 기능 패널**을 띄움. 이동/공격 해제는 로비 맵 전용 codeblock.
|
||||||
|
전투맵은 손대지 않아 잠금 유지. (B: 몬스터 배치기 재활용 → 로직 혼재로 비채택. C: 화면고정 UI 버튼 → 거부된 "UI 로비"라 제외.)
|
||||||
|
|
||||||
|
## 상세 설계
|
||||||
|
|
||||||
|
### 1) 로비 맵 — `tools/map/gen-lobby-map.mjs` → `map/lobby.map`
|
||||||
|
- map01 템플릿 deep clone → 경로 `/maps/lobby`로 치환, GUID 재발급(결정론 시드).
|
||||||
|
- 마을/타운 배경 RUID + 타일셋 적용. **`script.Monster`/`script.CombatMonster` 엔티티 전부 제거**(전투 없음).
|
||||||
|
- NPC 4종을 x축으로 벌려 월드 엔티티로 배치. 각 NPC:
|
||||||
|
- 스프라이트 = 공식 maplestory NPC RUID(계정 업로드 금지 — 흰 박스). 구현 단계에서 asset 검색으로 4개 확정.
|
||||||
|
- `script.LobbyNpc` 컴포넌트 + `NpcId`(`run`/`codex`/`shop`/`board`) + 머리 위 이름/`!` 프롬프트용 텍스트 노드.
|
||||||
|
- 플레이어 스폰 지점(맵 중앙-좌측).
|
||||||
|
- `Global/SectorConfig.config` valid 목록에 `map://lobby` 추가 — **SectorConfig 단일 소유자는 gen-maps.mjs**로 유지하고 lobby 항목을 그 상수에 포함(두 생성기 충돌 방지).
|
||||||
|
|
||||||
|
### 2) NPC 상호작용 — `tools/player/gen-lobby-npc.mjs` → `LobbyNpc` codeblock
|
||||||
|
- **근접+키**: 매 틱(타이머) 로컬 플레이어와의 x거리 측정 → 임계 거리 내면 `!` 프롬프트 노드 활성 + `Up`/`Space` 입력 시 트리거.
|
||||||
|
- **직접 클릭**: NPC 엔티티 클릭/터치 → 동일 트리거. (MSW 월드 엔티티 클릭 API는 구현 시 `mlua_api_retriever`로 확정: 엔티티 TouchEvent vs 스크린 오버레이 버튼 중 검증된 방식.)
|
||||||
|
- 트리거 시 `_EntityService:GetEntityByPath("/common").SlayDeckController:OnLobbyNpcInteract(NpcId)` 호출(경로 기반 크로스 codeblock — CombatMonster 자가등록과 동일 패턴).
|
||||||
|
- 한 번에 하나만 상호작용(다른 패널 열려 있으면 무시).
|
||||||
|
|
||||||
|
### 3) 이동·공격 잠금 해제 (로비 맵 한정) — `LobbyMobility` codeblock
|
||||||
|
- **`map/lobby.map`에만** 주입(전투맵 PlayerLock/전역 freeze는 불변).
|
||||||
|
- OnBeginPlay 런타임 복원: 로컬 플레이어 `MovementComponent`(InputSpeed/JumpForce) 정상값, `PlayerController.Enable=true`, `FixedLookAt=false`.
|
||||||
|
- 공격 입력(키/클릭) → 기존 `PlayerAttackMotion()`(코스메틱) 바인딩. **필드 타격 없음**.
|
||||||
|
- 전투맵 텔레포트 시 모델 기본값(speed=0)+PlayerLock 재적용 → **"로비맵에서만"을 구조적으로 보장**.
|
||||||
|
- 런타임 이동/공격 복원 정확한 API는 구현 단계에서 `mlua_api_retriever`로 확정.
|
||||||
|
- 생성기 배치는 `gen-lobby-npc.mjs`에 함께 둘지 별도 `gen-lobby-unlock.mjs`로 분리할지는 계획에서 결정(둘 다 lobby 맵 전용 codeblock).
|
||||||
|
|
||||||
|
### 4) 흐름 통합 — `tools/deck/gen-slaydeck.mjs`
|
||||||
|
- **OnBeginPlay**: `ShowLobby()`(UI) → **로비 맵 텔레포트** + 경량 "lobby" 상태(전투/상점/맵 HUD 숨김).
|
||||||
|
- **EndRun**: 4초 후 `ShowLobby()` → **로비 맵 텔레포트 복귀**.
|
||||||
|
- **OnLobbyNpcInteract(id)** 신규: `run`→`ShowCharacterSelect()`, `codex`→`ShowCodex()`, `shop`→`ShowSoulShop()`, `board`→`ShowBoard()`(전부 기존 메서드 재사용, 패널은 로비 맵 위 팝업).
|
||||||
|
- **제거**: `LobbyHud` 버튼-행 허브 패널 + `BindLobbyButtons`.
|
||||||
|
- **유지**: 영혼 포인트·승천 표시는 화면 모서리 **미니 HUD**(정보 표시 필요). 기능 패널 4종은 NPC 트리거.
|
||||||
|
- 런 시작(`StartRun`/`TeleportToActMap`)·전투 흐름은 불변.
|
||||||
|
|
||||||
|
### 5) 미러/테스트 영향
|
||||||
|
- 이동/공격 해제·NPC 배치는 **전투 규칙도 맵 그래프 생성 알고리즘도 아님** → `sim-balance.mjs`/`rogue-map.mjs` JS 미러 동기화 **불필요**(RULES.md §6은 그 둘만 요구).
|
||||||
|
- 검증(카운트만): `lobby.map` 내 NPC 엔티티 수, 산출물의 `LobbyNpc`/`LobbyMobility`/`OnLobbyNpcInteract` 개수, SectorConfig `map://lobby` 존재. 내용 출력 금지.
|
||||||
|
- 동작 검증: 메이커 플레이테스트.
|
||||||
|
|
||||||
|
## 검증 시나리오 (메이커)
|
||||||
|
1. 월드 시작 → **로비 맵에 스폰**, 이동 가능, 공격 모션 가능.
|
||||||
|
2. NPC 근접 → `!` 프롬프트 → `Up/Space`로 기능 패널 오픈. 직접 클릭으로도 오픈.
|
||||||
|
3. 4종 각각: 모험가→직업선택→런 시작, 사서→도감, 상인→영혼상점, 안내원→게시판.
|
||||||
|
4. 런 시작 → map01 텔레포트, **이동/공격 잠김**.
|
||||||
|
5. 런 종료(클리어/패배) → **로비 맵 복귀**, 이동/공격 재해제.
|
||||||
|
6. 미니 HUD에 영혼/승천 표시 정상.
|
||||||
|
|
||||||
|
## 리스크
|
||||||
|
- MSW 런타임 이동 재활성 API 가용성 → 계획 단계 `mlua_api_retriever` 검증.
|
||||||
|
- MSW 월드 엔티티 클릭 감지 방식 → 동일 검증(불가 시 근접+키만으로 폴백, 직접 클릭은 스크린 오버레이 버튼으로 구현).
|
||||||
|
- 텔레포트 복귀 좌표/스폰 위치 정합.
|
||||||
|
- 메이커 stale 상태 — git pull 후 로컬 워크스페이스 reload 필수(RULES.md §5).
|
||||||
|
|
||||||
|
## 생성기/파일 변경 요약
|
||||||
|
| 파일 | 변경 |
|
||||||
|
|---|---|
|
||||||
|
| `tools/map/gen-lobby-map.mjs` | **신규** — lobby.map(배경/타일/NPC 엔티티/스폰), SectorConfig 조율 |
|
||||||
|
| `tools/player/gen-lobby-npc.mjs` | **신규** — LobbyNpc 상호작용 codeblock(+LobbyMobility 또는 분리) |
|
||||||
|
| `tools/deck/gen-slaydeck.mjs` | OnBeginPlay/EndRun 로비맵 전환, OnLobbyNpcInteract, 버튼-행 허브 제거, 미니 HUD |
|
||||||
|
| `Global/SectorConfig.config` | map://lobby 등록(생성 산출물) |
|
||||||
|
| `map/lobby.map`, `ui/DefaultGroup.ui`, `*.codeblock` | 재생성 산출물 |
|
||||||
Reference in New Issue
Block a user