using CodeStage.AntiCheat.ObscuredTypes; using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; public class MapData : MonoBehaviourSingletonTemplate { public Material material_skybox; public GameObject[] gos_map; // 미궁에서 사용 (여러 맵 중 하나 오픈) public ChapterPortal m_ChapterPortal; [SerializeField] public List m_MapZoneData; // 2026-09-05 #746 (배경 어두움 수정) 실측: Ingame 씬은 베이크 데이터가 없고 앰비언트 모드가 Skybox 라 런타임 앰비언트 프로브가 ≈0.04(검정)이었다. // - 로비는 material_skybox 가 null, 필드맵은 skybox 만 바꾸고 프로브를 재생성하지 않아 #744 의 ambientIntensity 조정은 무효였다. // - 값은 코드 상수가 아니라 맵 프리팹 필드로 둔다(C45 · 맵마다 인스펙터에서 조정). 색은 선형 값(스크립트 대입은 색공간 변환 없음). [Header("조명 · 런타임 앰비언트 (#746 · 맵 프리팹에서 조정)")] [Tooltip("Trilight/Flat 은 skybox 와 무관하게 즉시 적용. Skybox 모드는 material_skybox 가 있고 updateEnvironmentFromSkybox 가 켜진 경우에만 프로브가 생성된다.")] public UnityEngine.Rendering.AmbientMode ambientMode = UnityEngine.Rendering.AmbientMode.Trilight; // 기본값 = PD "더 밝게"(2026-09-05) 반영: 로비 실측 평균 휘도 .60/.42/.18 → 0.282, 백색 1.0/.71/.31 → 0.325. 백색 계열을 기본으로. public Color ambientSky = new Color(1.00f, 1.00f, 1.00f, 1f); public Color ambientEquator = new Color(0.71f, 0.58f, 0.48f, 1f); public Color ambientGround = new Color(0.31f, 0.31f, 0.45f, 1f); [Range(0f, 2f)] public float ambientIntensity = 1f; // Skybox 모드 전용 (Trilight/Flat 은 무시) [Range(0f, 1f)] public float reflectionIntensity = 0.3f; [Tooltip("Skybox 모드에서 DynamicGI.UpdateEnvironment() 를 호출해 skybox 기준 프로브를 만든다(맵 로딩 시 1회 비용).")] public bool updateEnvironmentFromSkybox = false; // 2026-09-06 #756: 힘민지 로비 모드는 PCActor.Set_Obj 가 무기 장착을 건너뛴다(휴식 공간 설계). 시작 맵(WL_Start)처럼 // 로비 타입이어도 무기를 보여야 하는 맵은 이 플래그를 켠다 → StartWakeSequence 가 플레이어 인식 직후 Change_Weapon() 호출. [Header("플레이어 표시 (#756)")] [Tooltip("로비 모드 맵에서도 플레이어 무기를 장착·표시한다 (WL_Start 등)")] public bool showWeaponInLobby = false; // 2026-09-06 #764 (PD: "몬스터도 플레이어가 갈 수 없는 지형을 뚫고 이동하거나 이동 불가 지형에 생성되지 않도록") // 실측(WL_Start · 4m 격자 1806점): NavMesh 위 1144점 중 플레이어 시작 지점에서 도달 가능한 곳은 427점(37%)뿐이다. // 기존 DSUtil.Get_RandomPos_onNavMesh 는 ① 스냅 반경으로 MobGenRange 를 그대로 써서 최대 2×range 까지 튀고 // ② NavMesh.CalculatePath 의 반환값만 보고 status(PathComplete)를 보지 않아 **부분 경로(벽 너머·다른 섬)를 통과**시킨다. // → 스포너 19개 × 30점 표집 실측에서 51%(295/570)가 플레이어 시작 지점에서 도달 불가였다. // 아래 값은 전부 **기본값 = 기존 동작**이라 이 값을 세팅하지 않은 기존 맵은 아무것도 바뀌지 않는다(C8). [Header("몬스터 스폰 · 이동 (#764 · 맵 프리팹에서 조정)")] [Tooltip("스포너가 몬스터 생성 위치를 뽑을 때 완전 경로(PathComplete)가 나오는 지점만 쓴다. " + "WL_Start 처럼 NavMesh 가 섬으로 끊긴 맵에서 켠다. 끄면 기존 표집 방식 그대로.")] public bool spawnRequireReachableFromPlayer = false; [Tooltip("스폰 후보의 무작위 점을 NavMesh 로 끌어당길 최대 거리(m). 크면 벽 너머 다른 섬으로 스냅된다. " + "0 이하면 기존 동작(MobGenRange 를 스냅 반경으로 사용). 실측 권장 = 2")] public float spawnSnapRadius = 0f; [Tooltip("스폰 후보 표집 재시도 횟수. 0 이하면 기존 기본값(50)을 쓴다.")] public int spawnSampleTries = 0; [Tooltip("몬스터가 넉백·끌어당김·점프로 NavMesh 밖으로 밀렸을 때 되돌릴 최대 거리(m). " + "0 이하면 스냅하지 않는다(기존 동작). 몬스터에만 적용되고 플레이어·펫·NPC 는 영향이 없다.")] public float mobNavSnapRadius = 0f; // 2026-09-06 #771 (WL_Nature 시작 맵): 힘민지 맵은 Start() 에서 RenderSettings.fog=false 로 안개를 껐다. // Demo_01(LMHPOLY) 처럼 원경이 살아 있는 야외 맵은 안개가 룩의 핵심이라 **맵 프리팹 필드**로 되살린다(#746 과 같은 방식). // 기본값 = fogEnabled false → 이 값을 켜지 않은 기존 맵은 동작이 전혀 바뀌지 않는다(C8). [Header("안개 (#771 · 맵 프리팹에서 조정)")] [Tooltip("끄면 기존 동작 그대로 RenderSettings.fog = false.")] public bool fogEnabled = false; public FogMode fogMode = FogMode.Linear; public Color fogColor = new Color(0.492f, 0.611f, 0.821f, 1f); [Tooltip("Linear 전용 — 이 거리부터 안개가 시작된다. 게임 카메라는 플레이어에서 3 m 뒤라 12 m 는 캐릭터 바로 뒤다.")] public float fogStart = 30f; [Tooltip("Linear 전용 — 이 거리에서 안개가 100%.")] public float fogEnd = 250f; [Tooltip("Exponential 계열 전용.")] public float fogDensity = 0.0017f; void Start() { RenderSettings.skybox = material_skybox; Apply_Lighting(); Apply_Fog(); if (m_ChapterPortal) { var mapData = MyValue.MyChoiceMapData; var isclear = ServerInfo.Ins.m_ServerData.MapData_isClearChaper(mapData.n_MapID, mapData.n_Difficult); m_ChapterPortal.Set_NextChapter(isclear); } } // #746: 프리팹 필드의 앰비언트 설정을 RenderSettings 에 적용한다(Start 에서 1회 · 인스펙터 튜닝 후 재호출 가능). public void Apply_Lighting() { RenderSettings.ambientMode = ambientMode; switch (ambientMode) { case UnityEngine.Rendering.AmbientMode.Trilight: RenderSettings.ambientSkyColor = ambientSky; RenderSettings.ambientEquatorColor = ambientEquator; RenderSettings.ambientGroundColor = ambientGround; break; case UnityEngine.Rendering.AmbientMode.Flat: RenderSettings.ambientLight = ambientSky; break; default: // Skybox: 프로브는 material_skybox 로부터 명시적으로 재생성해야 값이 바뀐다 RenderSettings.ambientIntensity = ambientIntensity; if (updateEnvironmentFromSkybox && material_skybox != null) DynamicGI.UpdateEnvironment(); break; } RenderSettings.reflectionIntensity = reflectionIntensity; } // #771: 안개도 맵 프리팹 필드에서 적용한다. fogEnabled=false 면 기존 동작(끔) 그대로. public void Apply_Fog() { RenderSettings.fog = fogEnabled; if (!fogEnabled) return; RenderSettings.fogMode = fogMode; RenderSettings.fogColor = fogColor; RenderSettings.fogStartDistance = fogStart; RenderSettings.fogEndDistance = fogEnd; RenderSettings.fogDensity = fogDensity; } public MapZoneData Get_MapZoneData(int index) { return m_MapZoneData[index]; } public void Set_MazeMap(int index) { DSUtil.InActivateGameObjects(gos_map, index); Set_MazeNextPortal(index, false); } public void Set_MazeNextPortal(int index, bool active) { var mzd = Get_MapZoneData(index); if (!DSUtil.CheckNull(mzd.go_mazeNextPortal)) mzd.go_mazeNextPortal.SetActive(active); } } [Serializable] public class MapZoneData { public GameObject go_portal; public float CamRot_Portal; public GameObject go_reincarnation; public float CamRot_Reincarnation; public GameObject go_mazeNextPortal; public Transform tf_Farmer; public Transform[] tf_Obj; } public class ChoiceMapData { ObscuredInt _Difficult; public int n_Difficult { get { return _Difficult; } set { _Difficult = value; _Difficult.RandomizeCryptoKey(); } } ObscuredInt _MapID; public int n_MapID { get { return _MapID; } set { _MapID = value; _MapID.RandomizeCryptoKey(); } } ObscuredInt _PortalIndex; public int n_PortalIndex { get { return _PortalIndex; } set { _PortalIndex = value; _PortalIndex.RandomizeCryptoKey(); } } ObscuredInt _DungeonLv; public int n_DungeonLv { get { return _DungeonLv; } set { _DungeonLv = value; _DungeonLv.RandomizeCryptoKey(); } } protected ObscuredDouble _NightmareBeforeDmg; public double d_NightmareBeforeDmg { get { return _NightmareBeforeDmg; } set { _NightmareBeforeDmg = value; _NightmareBeforeDmg.RandomizeCryptoKey(); } } public BattleMapConfigTableData Get_MapData() { return table_battlemapconfig.Ins.Get_Data(n_MapID); } public DungeonMapConfigTableData Get_DungeonMapData_orNull() { var mapData = Get_MapData(); return mapData.e_Dungeon == eDungeon.None ? null : table_dungeonmapconfig.Ins.Get_Data_orNull(mapData.e_Dungeon, n_DungeonLv); } public int Get_DungeonIndex() { var dgData = Get_DungeonMapData_orNull(); return DSUtil.CheckNull(dgData) ? 0 : MyEnumToInt.Ins.Get_Int(dgData.e_DungeonType) - 1; } public void Set_MazeMapIndex() { n_PortalIndex = Random.Range(0, table_GlobalValue.Ins.Get_Int("MazeMapCount")); } }