From f36bc0d14eb20b067ef5119ec1d8d4e453da9953 Mon Sep 17 00:00:00 2001 From: gahusb Date: Sun, 14 Jun 2026 11:55:54 +0900 Subject: [PATCH] =?UTF-8?q?docs(p15):=20=EB=A1=9C=EB=B9=84=20=EB=A7=B5=20+?= =?UTF-8?q?=20=EC=9B=94=EB=93=9C=20NPC=20=EC=84=A4=EA=B3=84=20spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../specs/2026-06-14-lobby-map-npc-design.md | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md diff --git a/docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md b/docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md new file mode 100644 index 0000000..3cfa71a --- /dev/null +++ b/docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md @@ -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` | 재생성 산출물 |