From b661d16bb1e42532b93125dba29dbaba458ff133 Mon Sep 17 00:00:00 2001 From: swrring Date: Sun, 13 Sep 2026 16:41:43 +0900 Subject: [PATCH] =?UTF-8?q?[WL-816g]=20=EB=B0=9C=ED=8C=90=201=ED=9A=8C?= =?UTF-8?q?=EC=84=B1(=EC=9E=90=EB=A6=AC=EB=A7=88=EB=8B=A4=20=ED=95=9C=20?= =?UTF-8?q?=EB=B2=88)=20+=20=EC=83=88=20=ED=83=80=EC=9D=BC=20=EC=99=B8?= =?UTF-8?q?=EA=B3=BD=EC=84=A0=20=EB=A7=9E=EC=B6=A4=20+=20=EB=8D=98?= =?UTF-8?q?=EC=A0=84=20=EB=A1=9C=EB=94=A9=20=EB=B3=B4=EC=A0=95=20(#816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PD 추가 지시 2건. ① 「던전 열기는 한 번만 · 이후 같은 자리에 또 등장하면 안 된다」 던전 한 줄 = 발판 한 자리(`WLDungeonDef.platformUseFarm`·`platformPosition`). 값을 치르면 그 자리는 영영 비고 다음 발판은 표가 지정한 **다른 자리**에 선다. 해금 수는 PlayerPrefs 에 남겨 껐다 켜도 유지된다(실측: Play 재시작 후에도 던전 1 그대로 · 밭 자리 발판 없음 · 다음 발판만 (-2,0,-2) 가격 30). FI `RefreshIsland` 의 가격표 재생성이 우리 발판을 되살리지 않는 것도 실측 (섬 확장 4→5칸 후 발판 수 1 유지 · 자리 그대로). 덤으로, 골드가 많을 때 가만히 서서 던전이 연달아 팔리던 것도 막았다. ② 던전 진입 멈춤 던전 씬에는 `MobControlMgrList` 가 0개다(실측: WL_Dungeon01·02·ArenaProto 전부 0). 섬과 같은 이유로 로딩 칸 0 이 안 찬다 → 던전 표의 씬 이름을 로딩 보정 대상에 포함. ③ §1 남은 몫 — 새로 연 타일이 `WL/HullOutline` 패스를 못 받아(슬롯 1 vs 2) 경계에서 테두리 유무가 갈렸다. 열린 직후 기존 타일의 머티리얼 배열을 그대로 물려준다(새 머티리얼 0개). 실측: 8칸 전부 재질 조합 1가지. Co-Authored-By: Claude Opus 5 --- .../Resources/WL/WLIslandSettings.asset | 1 + Assets/WL/Island/WLIslandGateFlow.cs | 89 ++++++++++++++++++- Assets/WL/Island/WLIslandSettings.cs | 8 +- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset index c057992d5..9edb9e85c 100644 --- a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset +++ b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset @@ -48,6 +48,7 @@ MonoBehaviour: gateFlowEnabled: 1 hideFarms: 1 farmReHideDelaySeconds: 3 + matchNewTileMaterials: 1 dungeonUnlockPrefsKey: WL_Island_DungeonsUnlocked dungeonUnlockPrices: - 10 diff --git a/Assets/WL/Island/WLIslandGateFlow.cs b/Assets/WL/Island/WLIslandGateFlow.cs index 8bf037f0a..b238950e8 100644 --- a/Assets/WL/Island/WLIslandGateFlow.cs +++ b/Assets/WL/Island/WLIslandGateFlow.cs @@ -292,10 +292,10 @@ namespace WL.Island return haveCenter; } - /// 섬이 나중에 열리면 그 섬의 밭도 다시 숨긴다(확장해도 밭이 안 보이게). + /// 섬이 나중에 열리면 ① 그 섬의 밭을 다시 숨기고 ② 타일 재질을 기존 타일과 맞춘다(§1). static void HookIslandActivation(WLIslandSettings cfg, Scene scene) { - if (cfg.hideFarms == 0) return; + if (cfg.hideFarms == 0 && cfg.matchNewTileMaterials == 0) return; var islands = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < islands.Length; i++) { @@ -311,7 +311,8 @@ namespace WL.Island static void OnIslandActivated(WLIslandSettings cfg, FIIsland isl) { - if (cfg == null || cfg.gateFlowEnabled == 0 || cfg.hideFarms == 0) return; + if (cfg == null || cfg.gateFlowEnabled == 0) return; + if (cfg.hideFarms == 0 && cfg.matchNewTileMaterials == 0) return; var runner = WLIslandBridge.Runner; if (runner == null || isl == null) return; runner.StartCoroutine(Co_ReHide(cfg, isl)); @@ -319,10 +320,14 @@ namespace WL.Island static IEnumerator Co_ReHide(WLIslandSettings cfg, FIIsland isl) { - // 확장 애니메이션(DOScale 1초) + 소품 배치(AnimateSoils 0.05초 × 30칸)가 끝나기를 기다린다 + // 확장 애니메이션(DOScale 1초) + 소품 배치(AnimateSoils 0.05초 × 30칸) + 룩 재적용 + // (`WLIslandLookSettings.rebuildDelaySeconds` = 1.4 실측)이 끝나기를 기다린다. yield return new WaitForSeconds(cfg.farmReHideDelaySeconds); if (isl == null) yield break; + MatchTileMaterials(cfg, isl); + + if (cfg.hideFarms == 0) yield break; var farms = isl.GetComponentsInChildren(true); for (int i = 0; i < farms.Length; i++) { @@ -336,6 +341,82 @@ namespace WL.Island } } + // ───────────────────────────────────────────────────────────────── + // §1 새 타일 경계 색차 — 남은 몫(외곽선 패스) + // + // 실측(2026-09-13 · Play): + // · **바닥 색 자체는 Lead 의 `b1e58d795`(대비 복제 제외)로 해결됐다** — 새로 연 타일도 + // 기존 타일과 **같은 머티리얼 인스턴스** `Farm_IslandTop_Demo#83780` 을 쓴다. + // · 다만 새로 연 타일은 슬롯이 **1개**뿐이다(기존 타일은 2개 = 바닥 + `WL/HullOutline`). + // `WLReferenceLook.Apply` 가 씬 로드 때 **활성 렌더러만** 훑는데 잠긴 타일은 꺼져 있어 + // 외곽선 패스를 못 받는다. 그래서 **경계에서 테두리 유무가 갈린다.** + // 조치: 열린 직후, 이미 열려 있는 타일의 머티리얼 배열을 **그대로** 물려준다(새 머티리얼 0개). + // ───────────────────────────────────────────────────────────────── + public static int TilesMatched, SlotsMatched; + + static void MatchTileMaterials(WLIslandSettings cfg, FIIsland isl) + { + if (cfg.matchNewTileMaterials == 0 || isl == null) return; + + var mine = isl.GetComponentsInChildren(true); + if (mine.Length == 0) return; + + // 기준 = 이미 열려 있고 **슬롯이 더 많은** 타일(= 외곽선을 받은 타일) + FIIsland refIsl = null; + int bestSlots = 0; + var all = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); + for (int i = 0; i < all.Length; i++) + { + var o = all[i]; + if (o == null || o == isl || !o.IsUnlocked || !o.gameObject.activeInHierarchy) continue; + if (o.gameObject.scene != isl.gameObject.scene) continue; + var r = o.GetComponent(); + if (r == null) continue; + int n = r.sharedMaterials != null ? r.sharedMaterials.Length : 0; + if (n > bestSlots) { bestSlots = n; refIsl = o; } + } + if (refIsl == null) return; + + // 🔴 이름으로 짝을 지으면 **타일 본체**(GameObject 이름이 `Island (3)` / `Island (6)` 처럼 다 다르다)가 + // 안 맞는다(실측 — 첫 시도에서 본체만 슬롯 1개로 남았다). 루트 기준 **상대 경로**로 짝 짓는다. + var refs = refIsl.GetComponentsInChildren(true); + var byPath = new Dictionary(refs.Length); + for (int j = 0; j < refs.Length; j++) + if (refs[j] != null) byPath[RelPath(refIsl.transform, refs[j].transform)] = refs[j]; + + int changed = 0; + for (int i = 0; i < mine.Length; i++) + { + var m = mine[i]; + if (m == null || m is ParticleSystemRenderer) continue; + var mm = m.sharedMaterials; + if (mm == null || mm.Length == 0 || mm[0] == null) continue; + + Renderer r; + if (!byPath.TryGetValue(RelPath(isl.transform, m.transform), out r) || r == null) continue; + var rm = r.sharedMaterials; + if (rm == null || rm.Length <= mm.Length || rm[0] != mm[0]) continue; + m.sharedMaterials = rm; // 같은 인스턴스 배열 = 픽셀이 같아진다 + changed++; SlotsMatched += rm.Length - mm.Length; + } + if (changed > 0) + { + TilesMatched++; + WLIslandBridge.Log(cfg, "새 타일 재질 맞춤 — " + isl.name + " 렌더러 " + changed + + "개를 기존 타일(" + refIsl.name + ")과 같은 배열로(외곽선 포함)"); + } + } + + /// 타일 루트 기준 상대 경로(프리팹 인스턴스끼리 짝 짓는 열쇠 · 루트 자신은 빈 문자열). + static string RelPath(Transform root, Transform t) + { + if (t == root) return ""; + string s = t.name; + var p = t.parent; + while (p != null && p != root) { s = p.name + "/" + s; p = p.parent; } + return s; + } + /// 우리가 끈 것만 되살린다(C8). public static void RestoreFarms() { diff --git a/Assets/WL/Island/WLIslandSettings.cs b/Assets/WL/Island/WLIslandSettings.cs index 5cf18f2e6..65ab83674 100644 --- a/Assets/WL/Island/WLIslandSettings.cs +++ b/Assets/WL/Island/WLIslandSettings.cs @@ -220,9 +220,15 @@ namespace WL.Island "꺼진 오브젝트에서 코루틴을 시작해 콘솔 에러가 난다.")] public int hideFarms = 1; - [Tooltip("섬이 나중에 열렸을 때 그 밭을 다시 숨기기까지 기다리는 초(확장 연출 + 흙칸 연출이 끝날 시간).")] + [Tooltip("섬이 나중에 열렸을 때 그 밭을 다시 숨기고 타일 재질을 맞추기까지 기다리는 초 " + + "(확장 연출 1초 + 흙칸 연출 + 룩 재적용 1.4초가 끝날 시간).")] public float farmReHideDelaySeconds = 3.0f; + [Tooltip("🔴 §1 — 1 이면 **새로 열린 타일**의 머티리얼 배열을 이미 열려 있는 타일과 **같게** 만든다. " + + "실측: 바닥 색 자체는 이미 같지만(main b1e58d795), 새 타일만 외곽선 패스(WL/HullOutline)를 " + + "못 받아 경계에서 테두리 유무가 갈린다. 새 머티리얼을 만들지 않고 기존 배열을 물려준다.")] + public int matchNewTileMaterials = 1; + [Tooltip("해금된 던전 수를 적어 두는 PlayerPrefs 키(FI 세이브 파일은 건드리지 않는다).")] public string dungeonUnlockPrefsKey = "WL_Island_DungeonsUnlocked";