198 lines
9.8 KiB
C#
198 lines
9.8 KiB
C#
using CodeStage.AntiCheat.ObscuredTypes;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using Random = UnityEngine.Random;
|
||
|
||
public class MapData : MonoBehaviourSingletonTemplate<MapData>
|
||
{
|
||
public Material material_skybox;
|
||
public GameObject[] gos_map; // 미궁에서 사용 (여러 맵 중 하나 오픈)
|
||
public ChapterPortal m_ChapterPortal;
|
||
[SerializeField] public List<MapZoneData> 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"));
|
||
}
|
||
} |