// ───────────────────────────────────────────────────────────────────────────── // WLIslandSettings.cs — 섬(FarmingIsland) × 던전(아레나 복사본) 왕복의 단일 출처(SOT · C45) // // PD 지시 #816 (2026-09-13) · 발주서 WL-816d §1 / §1-B + Lead 추가 지시 2건 // SOT = `공유/기획팀_산출물/WL/게임구조_2026-09-13/FarmingIsland_기반_전환설계_v1.md`(816c 실측) // // ■ 이 에셋 하나로 되돌린다(C8) // enabled_ = 0 → 섬·던전 진입이 전부 사라지고 지금 게임 그대로(원본 1줄 훅도 -1 을 그대로 돌려준다). // startOnIsland = 0 → 로그인 후 로비(WL_Nature)로 간다 = 현재 동작 100 %. // // ■ 던전을 늘리는 법 = **표 한 줄** // dungeons[] 에 행 하나(씬 이름 · 맵 ID · 스테이지 인덱스 · 섬 위의 입구 좌표)를 추가하면 // 섬에 입구 더미가 하나 더 생기고, 그 문으로 들어가면 그 스테이지가 돈다. 코드 수정 0. // // 🔴 원본(Assets/Script/** · Assets/FarmingIsland/**)은 이 기능을 위해 고치지 않는다. // 유일한 예외 = `NewGameUI.cs:36` **1줄**(발주서가 허용한 1줄 훅 상한) — StartMapId() 하나만 부른다. // 🔴 Debug.LogError / Debug.LogException 을 쓰지 않는다(ErrorLogHookManager 가 플레이어 팝업을 띄운다). // 🔴 어셈블리 주의: Assets/WL/Island/ 에 .asmdef 를 만들지 말 것. // ───────────────────────────────────────────────────────────────────────────── using System; using UnityEngine; namespace WL.Island { /// 던전 1개 = 이 표의 한 행. 씬·스테이지·입구 위치가 전부 값이다. [Serializable] public class WLDungeonDef { [Tooltip("0 이면 이 행은 없는 것으로 친다(입구도 안 생긴다).")] public int enabled_ = 1; [Tooltip("던전 번호(로그·이름표용).")] public int id = 1; [Tooltip("입구 위에 뜨는 이름.")] public string displayName = "던전 1"; [Tooltip("던전 씬 이름(빌드 씬 목록에 등록돼 있어야 한다). 예: WL_Dungeon01")] public string sceneName = "WL_Dungeon01"; [Tooltip("BattleMapConfig.json 의 n_MapID — Load_Map(이 값) 으로 들어간다.")] public int mapId = 901; [Tooltip("WLStageTable 의 표 인덱스(0부터). 던전마다 달라야 보상·몹 수가 갈린다.")] public int stageIndex = 10; [Tooltip("섬 위 입구 더미의 월드 좌표.")] public Vector3 gatePosition = Vector3.zero; [Tooltip("입구 더미의 Y 회전(도).")] public float gateYaw; [Tooltip("입구 더미 크기 배수.")] public float gateScale = 1f; [Tooltip("입구 더미 색(더미라서 색으로만 구분한다 · 추후 프리팹으로 교체).")] public Color gateColor = new Color(0.45f, 0.32f, 0.62f, 1f); [Tooltip("비우면 내장 더미(문틀+포털판)를 만든다. 채우면 그 프리팹을 대신 놓는다(추후 교체용).")] public string gatePrefabPath = ""; } [CreateAssetMenu(fileName = "WLIslandSettings", menuName = "WL/Island Settings", order = 340)] public sealed class WLIslandSettings : ScriptableObject { public const string ResourcePath = "WL/WLIslandSettings"; static WLIslandSettings s_inst; static bool s_tried; /// 설정 에셋(없으면 null = 기능 전체 무동작). public static WLIslandSettings Instance { get { if (!s_tried) { s_inst = Resources.Load(ResourcePath); s_tried = true; } return s_inst; } } public static void Invalidate() { s_inst = null; s_tried = false; } /// 기능이 살아 있는가. public static bool Enabled { get { var c = Instance; return c != null && c.enabled_ != 0; } } // ───────────────────────────────── 전체 스위치 [Header("되돌리기 (PD 1순위)")] [Tooltip("0 이면 섬·던전이 전부 사라진다 = 지금 게임 그대로.")] public int enabled_ = 1; [Tooltip("1 이면 진행 상황을 Console 에 남긴다.")] public int verboseLog = 1; // ───────────────────────────────── 섬 [Header("섬 (FarmingIsland Level01)")] [Tooltip("1 이면 로그인 후 로비 대신 섬에서 시작한다(PD 2026-09-13). 0 이면 지금처럼 로비.")] public int startOnIsland = 1; [Tooltip("BattleMapConfig.json 에 넣은 섬 행의 n_MapID.")] public int islandMapId = 900; [Tooltip("섬 씬 이름(BattleMapConfig 의 s_Scene 과 같아야 한다).")] public string islandSceneName = "Level01"; [Tooltip("섬에서 WL 전투 PC(Actor)를 숨긴다 — 섬은 FI 플레이어가 걷는다(플레이어 2명 방지).")] public int hideWlPcOnIsland = 1; [Tooltip("섬 씬의 FI 카메라/AudioListener 중복을 정리한다(Additive 로 붙었을 때만).")] public int fixIslandDuplicates = 1; [Tooltip("FI 세이브 파일 이름을 이 값으로 고정한다(빈 문자열 = 원본 그대로 = 활성 씬 이름). " + "816c §A-3: 섹터마다 세이브가 갈리는 문제의 최소 수정 — 다만 원본 0줄 원칙 때문에 이번엔 기본 빈칸(미적용).")] public string islandSaveKey = ""; // ───────────────────────────────── 섬의 플레이어 = 우리 전투 캐릭터 [Header("섬 캐릭터 교체 (PD: 전투 캐릭터로 통일)")] [Tooltip("1 이면 FI 농부(Stickman) 대신 우리 치비 캐릭터가 섬을 걷는다. 조작·상호작용은 FI 것 그대로.")] public int swapIslandPlayer = 1; [Tooltip("섬에서 쓸 PC 프리팹 경로(Addressables 주소 겸 에디터 경로).")] public string pcPrefabPath = "Assets/Res_Addr/PC/LH_M05.prefab"; [Tooltip("치비 루트에 넣을 스케일. 프리팹의 WLPcScaleCompensator 가 ×1.5038 해서 " + "0.7 × 1.5038 = 1.0527 → 월드 높이 1.1912 m(= 인게임 PC 와 동일 · 814n 실측).")] public float islandPcScale = 0.7f; [Tooltip("1 이면 FI 애니메이터의 파라미터를 치비 애니메이터로 그대로 흘린다(둘 다 Humanoid · 리타깃). " + "FI 컨트롤러를 그대로 쓰므로 걷기·농사 동작이 전부 따라온다.")] public int mirrorFiAnimator = 1; [Tooltip("FI 농부 메시를 숨길 때 쓰는 자식 이름(대소문자 무시 · 비우면 SkinnedMeshRenderer 를 전부 끈다).")] public string fiPlayerModelName = "Stickman"; // ───────────────────────────────── 던전 입구 [Header("던전 입구(더미)")] [Tooltip("플레이어가 이 거리(m) 안에 들어오면 「들어가기」가 뜬다.")] public float interactRadius = 3.5f; [Tooltip("안내 문구 — {0} = 던전 이름.")] public string promptFormat = "{0}\n들어가기"; [Tooltip("버튼 문구.")] public string enterButtonText = "들어가기"; [Tooltip("키보드로도 들어갈 수 있게(에디터 검증용).")] public KeyCode enterKey = KeyCode.E; [Tooltip("입구 더미 기본 높이(m).")] public float gateHeight = 3.2f; [Tooltip("입구 더미 기본 폭(m).")] public float gateWidth = 2.4f; // ───────────────────────────────── 보상 [Header("보상 (던전 클리어 → 섬 코인)")] [Tooltip("0 이면 보상을 주지 않는다.")] public int rewardEnabled = 1; [Tooltip("처치 1마리당 코인.")] public int goldPerKill = 12; [Tooltip("클리어 기본 코인.")] public int goldClearBonus = 60; [Tooltip("보스 스테이지 추가 코인.")] public int goldBossBonus = 120; [Tooltip("1 이면 위 합계에 StageDef.goldMultiplier(WLStageTable.cs:103)를 곱한다.")] public int applyGoldMultiplier = 1; [Tooltip("실패(사망·시간 초과) 시 주는 코인. PD 지시 = 0.")] public int goldOnFail = 0; [Tooltip("보류 보상을 남기는 PlayerPrefs 키(플레이 중 씬 전환을 건너 살아남게).")] public string pendingPrefsKey = "WL_Island_PendingGold"; // ───────────────────────────────── 던전 씬 런타임 준비 [Header("던전 씬 준비(런타임)")] [Tooltip("1 이면 던전 씬에 NavMesh 를 런타임 베이크한다(아레나 복사본에는 베이크 데이터가 없다).")] public int bakeDungeonNavMesh = 1; [Tooltip("1 이면 스테이지 표가 요구하는 SpawnerID 로 스포너를 런타임 생성한다.")] public int spawnDungeonSpawners = 1; [Tooltip("생성한 스포너의 MobGenRange(m).")] public float dungeonSpawnerRange = 6f; [Tooltip("1 이면 아레나 복사본의 데모 리그(ArenaWalkerRig)·여분 카메라를 끈다(플레이어·카메라 중복 방지).")] public int fixDungeonDuplicates = 1; [Tooltip("던전 입장 후 StageDirector.Enter 를 부르기까지 기다리는 초(맵·PC 로딩 여유).")] public float dungeonEnterDelay = 1.5f; // ───────────────────────────────── 던전 표 [Header("던전 표 — 한 줄 = 던전 하나")] public WLDungeonDef[] dungeons = new WLDungeonDef[0]; // ───────────────────────────────────────────────────────────────── // 원본 1줄 훅이 부르는 유일한 진입점 (NewGameUI.cs:36) // ───────────────────────────────────────────────────────────────── /// /// 로그인 후 처음 들어갈 맵 ID. 기능이 꺼져 있으면 (= -1 로비)를 그대로 돌려준다. /// 🔴 원본 수정은 이 호출 **1줄**이 전부다. /// public static int StartMapId(int original) { var c = Instance; if (c == null || c.enabled_ == 0 || c.startOnIsland == 0) return original; return c.islandMapId; } /// 맵 ID 로 던전 행을 찾는다(없으면 null). public WLDungeonDef FindByMapId(int mapId) { if (dungeons == null) return null; for (int i = 0; i < dungeons.Length; i++) if (dungeons[i] != null && dungeons[i].enabled_ != 0 && dungeons[i].mapId == mapId) return dungeons[i]; return null; } /// 씬 이름으로 던전 행을 찾는다(같은 씬을 여러 던전이 써도 되므로 맵 ID 우선). public WLDungeonDef FindBySceneName(string sceneName) { if (dungeons == null || string.IsNullOrEmpty(sceneName)) return null; for (int i = 0; i < dungeons.Length; i++) if (dungeons[i] != null && dungeons[i].enabled_ != 0 && sceneName.Equals(dungeons[i].sceneName)) return dungeons[i]; return null; } public void Log(string msg) { if (verboseLog != 0) Debug.Log("[WL-816d] " + msg); } } }