Project_WL/Assets/WL/Island/WLIslandSettings.cs

249 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ─────────────────────────────────────────────────────────────────────────────
// 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
{
/// <summary>던전 1개 = 이 표의 한 행. 씬·스테이지·입구 위치가 전부 값이다.</summary>
[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;
/// <summary>설정 에셋(없으면 null = 기능 전체 무동작).</summary>
public static WLIslandSettings Instance
{
get
{
if (!s_tried) { s_inst = Resources.Load<WLIslandSettings>(ResourcePath); s_tried = true; }
return s_inst;
}
}
public static void Invalidate() { s_inst = null; s_tried = false; }
/// <summary>기능이 살아 있는가.</summary>
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("섬에서 어느 카메라가 화면을 그릴지. 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 = "";
// ───────────────────────────────── 섬의 플레이어 = 우리 전투 캐릭터
[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;
[Tooltip("이름표·버튼에 쓸 TMP 폰트(한글 포함). 비우면 TMP 기본 폰트 — 기본 폰트에는 한글 글리프가 없어 □ 로 나온다.")]
public string fontAssetPath = "Assets/ThirdParty/TextMesh Pro/Addressables/Fonts & Materials/Font SDF.asset";
// ───────────────────────────────── 보상
[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)
// ─────────────────────────────────────────────────────────────────
/// <summary>
/// 로그인 후 처음 들어갈 맵 ID. 기능이 꺼져 있으면 <paramref name="original"/>(= -1 로비)를 그대로 돌려준다.
/// 🔴 원본 수정은 이 호출 **1줄**이 전부다.
/// </summary>
public static int StartMapId(int original)
{
var c = Instance;
if (c == null || c.enabled_ == 0 || c.startOnIsland == 0) return original;
return c.islandMapId;
}
/// <summary>맵 ID 로 던전 행을 찾는다(없으면 null).</summary>
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;
}
/// <summary>씬 이름으로 던전 행을 찾는다(같은 씬을 여러 던전이 써도 되므로 맵 ID 우선).</summary>
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);
}
}
}