From 6fa7a421d34e67e29cadd9441e6227680260691d Mon Sep 17 00:00:00 2001 From: swrring Date: Sun, 13 Sep 2026 12:34:24 +0900 Subject: [PATCH] =?UTF-8?q?[WL-816d]=20=EC=84=AC=20=EC=B9=B4=EB=A9=94?= =?UTF-8?q?=EB=9D=BC=20=EC=86=8C=EC=9C=A0=EA=B6=8C=20+=20WL=20PC=20?= =?UTF-8?q?=EB=B9=84=EB=8F=99=EA=B8=B0=20=EC=83=9D=EC=84=B1=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91=20+=20NavMesh=20=EA=B8=B0=EB=B3=B8=EA=B0=92=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20(#816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NavMeshSurface 기본값(collectObjects=All · useGeometry=RenderMeshes)을 그대로 쓴다(게이트 PASS) - 섬 카메라 소유권 SO(islandCameraOwner 기본 1 = FI 카메라) · 떠날 때 되살림 - WL 전투 PC 는 Addressables 콜백으로 늦게 생긴다 → hidePcWatchSeconds 동안 감시해 숨김 - 프로브 전용 DebugSceneOverride(던전 복사본 씬 미등록 구간 검증용) Co-Authored-By: Claude Opus 5 --- .../Resources/WL/WLIslandSettings.asset | 2 + Assets/WL/Island/WLDungeonSceneSetup.cs | 5 +- Assets/WL/Island/WLIslandBridge.cs | 49 ++++++++++++++++- Assets/WL/Island/WLIslandSceneSetup.cs | 53 ++++++++++++++++--- Assets/WL/Island/WLIslandSettings.cs | 8 +++ Assets/WL/Island/WLIslandUi.cs | 4 +- 6 files changed, 106 insertions(+), 15 deletions(-) diff --git a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset index 5744f5dda..a5a83d19f 100644 --- a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset +++ b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset @@ -19,6 +19,8 @@ MonoBehaviour: islandSceneName: Level01 hideWlPcOnIsland: 1 fixIslandDuplicates: 1 + islandCameraOwner: 1 + hidePcWatchSeconds: 6 islandSaveKey: swapIslandPlayer: 1 pcPrefabPath: Assets/Res_Addr/PC/LH_M05.prefab diff --git a/Assets/WL/Island/WLDungeonSceneSetup.cs b/Assets/WL/Island/WLDungeonSceneSetup.cs index 69fba7a6d..36d9d252d 100644 --- a/Assets/WL/Island/WLDungeonSceneSetup.cs +++ b/Assets/WL/Island/WLDungeonSceneSetup.cs @@ -20,7 +20,6 @@ using System.Collections.Generic; using TMPro; using Unity.AI.Navigation; using UnityEngine; -using UnityEngine.AI; using UnityEngine.SceneManagement; using WL.Combat.Stage; @@ -100,8 +99,8 @@ namespace WL.Island var go = new GameObject("~WL_DungeonNavMesh"); SceneManager.MoveGameObjectToScene(go, scene); var surf = go.AddComponent(); - surf.collectObjects = CollectObjects.All; - surf.useGeometry = NavMeshCollectGeometry.RenderMeshes; // 아레나 바닥에 콜라이더가 없을 수 있다 + // collectObjects = All · useGeometry = RenderMeshes 는 NavMeshSurface 기본값 그대로 쓴다 + // (아레나 바닥에 콜라이더가 없어도 렌더 메시로 베이크된다 · 패키지 실측 NavMeshSurface.cs:54·66) surf.BuildNavMesh(); NavBakes++; WLIslandBridge.Log(cfg, "던전 NavMesh 런타임 베이크 완료"); diff --git a/Assets/WL/Island/WLIslandBridge.cs b/Assets/WL/Island/WLIslandBridge.cs index be6eb7bce..478214b67 100644 --- a/Assets/WL/Island/WLIslandBridge.cs +++ b/Assets/WL/Island/WLIslandBridge.cs @@ -35,6 +35,13 @@ namespace WL.Island public static int IslandLoads, DungeonLoads, ClearGrants, FailGrants, CoinGranted, GatesSpawned; public static string LastLog = ""; + /// + /// 프로브 전용 — 비어 있지 않으면 단독 Play 경로에서 이 씬 이름으로 대신 들어간다. + /// 🔴 던전 복사본 씬이 아직 빌드 목록에 없어(등록은 Lead·PD 승인 사항) 로그인 없는 검증에 쓴다. + /// 실기(InGameInfo 가 있는 흐름)에는 영향이 없다 — 그쪽은 Load_Map(mapId) 를 탄다. + /// + public static string DebugSceneOverride = ""; + static bool s_installed; static int s_pendingGold; static WLIslandRunner s_runner; @@ -123,6 +130,15 @@ namespace WL.Island float t = 0f; while (t < 5f && !WLIslandCoin.Ready) { t += Time.unscaledDeltaTime; yield return null; } GrantPending(cfg); + + // 🔴 WL 전투 PC 는 Addressables 콜백으로 **나중에** 생긴다(PCInfo.cs:16) — 나타나면 그때 숨긴다. + t = 0f; + while (t < cfg.hidePcWatchSeconds) + { + if (WLIslandSceneSetup.HideWlPc(cfg)) break; + t += Time.unscaledDeltaTime; + yield return null; + } } // ───────────────────────────────────────────────────────────────── @@ -153,6 +169,7 @@ namespace WL.Island EnsureRunner(); WLDungeonSceneSetup.Pending = d; + WLIslandSceneSetup.RestoreDuplicates(); if (InGameInfo.Ins != null) { @@ -162,8 +179,9 @@ namespace WL.Island else { // 에디터에서 섬 씬만 열고 Play 한 경우(로그인 없는 검증 경로). - Log(cfg, "던전 입장(단독) — " + d.sceneName); - SceneManager.LoadScene(d.sceneName, LoadSceneMode.Single); + string scn = string.IsNullOrEmpty(DebugSceneOverride) ? d.sceneName : DebugSceneOverride; + Log(cfg, "던전 입장(단독) — " + scn); + SceneManager.LoadScene(scn, LoadSceneMode.Single); } } @@ -268,6 +286,33 @@ namespace WL.Island return true; } + /// + /// 검증용: 클리어 1회를 **실제 계산식 그대로** 태운다(전투만 건너뛴다). + /// `ComputeGold`(표의 goldMultiplier 소비) → 보류 설정까지 실기와 같은 코드가 돈다. + /// + public static int DebugSimulateClear(int stageIndex, int killed, bool isBoss) + { + var cfg = WLIslandSettings.Instance; + if (cfg == null) return 0; + var t = WLStageTable.Instance; + var def = t != null ? t.Get(stageIndex) : null; + var r = new StageClearResult + { + index = stageIndex, + stageId = def != null ? def.id : 0, + win = true, + killed = killed, + total = def != null ? def.TotalCount : killed, + isBoss = isBoss + }; + int gold = ComputeGold(cfg, r); + SetPending(cfg, gold); + ClearGrants++; + Log(cfg, "검증 클리어 — 스테이지 " + stageIndex + " · 처치 " + killed + + " · 배수 " + MultiplierOf(stageIndex).ToString("F2") + " → 보류 " + gold + " 코인"); + return gold; + } + /// 검증용: 보상분을 직접 주입한다(전투 없이 ④⑤⑥ 경로만 태운다). public static void DebugSetPending(int gold) { diff --git a/Assets/WL/Island/WLIslandSceneSetup.cs b/Assets/WL/Island/WLIslandSceneSetup.cs index 7f54872c4..9e773dd9b 100644 --- a/Assets/WL/Island/WLIslandSceneSetup.cs +++ b/Assets/WL/Island/WLIslandSceneSetup.cs @@ -76,26 +76,63 @@ namespace WL.Island { if (cfg.fixIslandDuplicates == 0) return; - // WL 전투 PC 는 섬에서 숨긴다(플레이어 2명 방지) — 던전에 가면 다시 만들어진다. - if (cfg.hideWlPcOnIsland != 0 && MyValue.bMyPC && MyValue.MyPC != null) - { - MyValue.MyPC.gameObject.SetActive(false); - PcHidden++; - } + HideWlPc(cfg); // FI 카메라·AudioListener 중복 — Additive 로 붙었을 때만 정리한다. // · 섬 씬만 단독으로 열려 있으면(검증 Play) FI 카메라가 유일한 카메라라 끄면 안 된다. if (SceneManager.sceneCount <= 1) return; + // 카메라 소유권 — 섬은 FarmingIsland 처럼 보여야 한다(PD) → 기본은 FI 카메라가 이긴다. + if (cfg.islandCameraOwner != 0) + { + bool fiWins = cfg.islandCameraOwner == 1; + var cams = Object.FindObjectsByType(FindObjectsSortMode.None); + for (int i = 0; i < cams.Length; i++) + { + var c = cams[i]; + if (c == null) continue; + bool inIsland = c.gameObject.scene == scene; + bool want = fiWins ? inIsland : !inIsland; + if (c.enabled == want) continue; + if (!want) s_disabledCams.Add(c); // 섬을 떠날 때 되살린다 + c.enabled = want; + CamerasFixed++; + } + } + var listeners = Object.FindObjectsByType(FindObjectsSortMode.None); int alive = 0; for (int i = 0; i < listeners.Length; i++) { if (listeners[i] == null || !listeners[i].enabled) continue; alive++; - if (alive > 1 && listeners[i].gameObject.scene == scene) { listeners[i].enabled = false; ListenersFixed++; } + if (alive > 1) { listeners[i].enabled = false; s_disabledListeners.Add(listeners[i]); ListenersFixed++; } } - WLIslandBridge.Log(cfg, "중복 정리 — PC 숨김 " + PcHidden + " · AudioListener 해제 " + ListenersFixed); + WLIslandBridge.Log(cfg, "중복 정리 — PC 숨김 " + PcHidden + " · 카메라 " + CamerasFixed + + " · AudioListener 해제 " + ListenersFixed); + } + + static readonly List s_disabledCams = new List(4); + static readonly List s_disabledListeners = new List(4); + + /// 섬을 떠날 때 우리가 끈 것만 되살린다(C8 — 원래대로). + public static void RestoreDuplicates() + { + for (int i = 0; i < s_disabledCams.Count; i++) if (s_disabledCams[i] != null) s_disabledCams[i].enabled = true; + for (int i = 0; i < s_disabledListeners.Count; i++) if (s_disabledListeners[i] != null) s_disabledListeners[i].enabled = true; + s_disabledCams.Clear(); + s_disabledListeners.Clear(); + } + + /// WL 전투 PC 를 섬에서 숨긴다(플레이어 2명 방지). 던전에 가면 다시 만들어진다. + public static bool HideWlPc(WLIslandSettings cfg) + { + if (cfg.hideWlPcOnIsland == 0) return true; + if (!MyValue.bMyPC || MyValue.MyPC == null) return false; + if (!MyValue.MyPC.gameObject.activeSelf) return true; + MyValue.MyPC.gameObject.SetActive(false); + PcHidden++; + return true; } // ───────────────────────────────────────────────────────────────── diff --git a/Assets/WL/Island/WLIslandSettings.cs b/Assets/WL/Island/WLIslandSettings.cs index ad04ccbb8..f4e63f782 100644 --- a/Assets/WL/Island/WLIslandSettings.cs +++ b/Assets/WL/Island/WLIslandSettings.cs @@ -109,6 +109,14 @@ namespace WL.Island [Tooltip("섬 씬의 FI 카메라/AudioListener 중복을 정리한다(Additive 로 붙었을 때만).")] public int fixIslandDuplicates = 1; + [Tooltip("섬에서 어느 카메라가 화면을 그릴지. 0 = 아무것도 안 건드림(카메라 2대 = 겹쳐 그려짐) · " + + "1 = FI 카메라가 이긴다(섬을 FarmingIsland 처럼 · 기본) · 2 = WL 카메라가 이긴다. " + + "🔴 Additive 경로는 로그인이 필요해 실측하지 못했다 — 실기 첫 실행에서 확인할 값.")] + public int islandCameraOwner = 1; + + [Tooltip("WL 전투 PC 는 Addressables 로 비동기 생성된다 — 이 초 동안 지켜보며 나타나면 숨긴다.")] + public float hidePcWatchSeconds = 6f; + [Tooltip("FI 세이브 파일 이름을 이 값으로 고정한다(빈 문자열 = 원본 그대로 = 활성 씬 이름). " + "816c §A-3: 섹터마다 세이브가 갈리는 문제의 최소 수정 — 다만 원본 0줄 원칙 때문에 이번엔 기본 빈칸(미적용).")] public string islandSaveKey = ""; diff --git a/Assets/WL/Island/WLIslandUi.cs b/Assets/WL/Island/WLIslandUi.cs index 78cbf0289..832c85fe9 100644 --- a/Assets/WL/Island/WLIslandUi.cs +++ b/Assets/WL/Island/WLIslandUi.cs @@ -40,8 +40,8 @@ namespace WL.Island if (LitShader == null) return null; m = new Material(LitShader) { name = "WL_GateDummy", hideFlags = HideFlags.DontSave }; - m.SetColor("_BaseColor", c); - m.SetColor("_Color", c); + if (m.HasProperty("_BaseColor")) m.SetColor("_BaseColor", c); + if (m.HasProperty("_Color")) m.SetColor("_Color", c); if (m.HasProperty("_Smoothness")) m.SetFloat("_Smoothness", 0.15f); s_cache[key] = m; return m;