Files
maplecontest/docs/superpowers/specs/2026-06-14-lobby-map-npc-design.md
gahusb f36bc0d14e docs(p15): 로비 맵 + 월드 NPC 설계 spec
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 11:55:54 +09:00

8.2 KiB

로비 맵 + 월드 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.mjsGlobal/DefaultPlayer.model speed/jump/accel = 0 (전역).
    • tools/player/gen-player-lock.mjsPlayerLock 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 컨트롤러에 자가등록.
  • 흐름: OnBeginPlayShowLobby()(UI). EndRun(text)→4초 후 ShowLobby().

접근법

A. 새 로비 맵 + 월드 NPC 엔티티 (채택) — 맵 템플릿 복제 재사용, NPC를 월드 엔티티로 배치하고 각 NPC의 codeblock이 근접+클릭을 감지해 기존 기능 패널을 띄움. 이동/공격 해제는 로비 맵 전용 codeblock. 전투맵은 손대지 않아 잠금 유지. (B: 몬스터 배치기 재활용 → 로직 혼재로 비채택. C: 화면고정 UI 버튼 → 거부된 "UI 로비"라 제외.)

상세 설계

1) 로비 맵 — tools/map/gen-lobby-map.mjsmap/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.mjsLobbyNpc 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) 신규: runShowCharacterSelect(), codexShowCodex(), shopShowSoulShop(), boardShowBoard()(전부 기존 메서드 재사용, 패널은 로비 맵 위 팝업).
  • 제거: 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 재생성 산출물