Project_WL/Assets/WL/UI/Settings/WLSkillSlotPreset.cs

148 lines
7.9 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// WLSkillSlotPreset.cs — 전투 패드 4슬롯 장착 **로컬 오버라이드** 값의 단일 출처 (WL-813g §3 · #813)
//
// 813c 완료보고 §5 이월분 + 813n 완료보고(슬롯 SkillID 16행 · 쿨 6/8/12/20 s · MP 10/15/25/40).
// 기준서 v1 §B 요소 3 「4슬롯 · 쿨·자원 차등」.
//
// ■ 왜 필요한가 (813n §1 「슬롯 장착은 서버 프리셋이라 813c 와 묶어야 함」)
// 장착 슬롯의 SOT 는 서버(`ServerData.Equip.Skill[Preset]`)다. 서버 프리셋을 바꾸지 않고도
// 전투 패드가 813n 프리셋(클래스별 Active 3 + 광역 1)으로 도는지 보려면 **클라 로컬 오버라이드**가 필요하다.
//
// ■ 어디에 쓰는가
// WLSkillSlotOverride(UI) 가 이 표에서 내 클래스 행을 찾아 4슬롯 SkillID 를
// `ServerData.Set_Equip(eItem.Skill, id, slot)`(클라 전용 공개 API · 서버 호출 0)로 써 넣는다.
// 그 뒤 `PCActor` 가 `Get_EquipSkillID(i)` 로 읽는 경로 = 기존 `Use_Skill` 경로 그대로다(핫스팟 0).
//
// ■ 롤백(C8)
// 이 에셋이 없거나 enabled = false 면 아무 것도 쓰지 않는다 = **서버 프리셋 원본** 그대로.
// restoreOnDisable 이 켜져 있으면 패드가 꺼질 때 원래 SkillID 를 되돌린다.
//
// 🔴 이 오버라이드는 **클라이언트 메모리만** 바꾼다. 서버 저장 호출(`Save_*`)은 하지 않는다.
// ─────────────────────────────────────────────────────────────────────────────
using System;
using UnityEngine;
namespace WL.UI
{
/// <summary>한 클래스(ClassConfig n_ClassID)의 4슬롯 장착 SkillID.</summary>
[Serializable]
public struct WLSkillSlotRow
{
[Tooltip("ClassConfig 의 n_ClassID (예: 10101 / 10102 / 10501 / 10502).")]
public int classId;
[Tooltip("메모용 이름(런타임 미사용).")]
public string note;
[Tooltip("슬롯 0~3 에 장착할 SkillID. 0 이면 그 슬롯은 건드리지 않는다(서버 값 유지).")]
public int[] skillIds;
}
[CreateAssetMenu(fileName = "WLSkillSlotPreset", menuName = "WL/Skill Slot Preset", order = 32)]
public sealed class WLSkillSlotPreset : ScriptableObject
{
/// <summary>Resources 경로 — 런타임 로드 키.</summary>
public const string ResourcesPath = "WL/WLSkillSlotPreset";
private static WLSkillSlotPreset s_instance;
private static bool s_tried;
/// <summary>에셋이 없으면 null(호출부는 null 이면 서버 프리셋 원본 유지 = C8 롤백).</summary>
public static WLSkillSlotPreset Instance
{
get
{
if (s_instance == null && !s_tried)
{
s_tried = true;
s_instance = Resources.Load<WLSkillSlotPreset>(ResourcesPath);
}
return s_instance;
}
}
public static void ClearCache() { s_instance = null; s_tried = false; }
/// <summary>진단·A/B 전용 런타임 스위치.</summary>
public static bool RuntimeDisabled;
/// <summary>오버라이드가 살아 있는가 = 에셋 존재 + enabled + 런타임 스위치.</summary>
public static bool Enabled { get { var i = Instance; return i != null && i.overrideEnabled && !RuntimeDisabled; } }
// ── 스위치 ────────────────────────────────────────────────────────────
[Header("스위치")]
[Tooltip("끄면 서버 프리셋을 그대로 쓴다(오버라이드 0). 롤백 1순위.")]
public bool overrideEnabled = true;
[Tooltip("적용/차단 결정을 Console 에 남긴다(검증용).")]
public bool verboseLog = false;
[Tooltip("패드가 꺼질 때(씬 이동·컴포넌트 비활성) 원래 SkillID 를 되돌린다.")]
public bool restoreOnDisable = true;
[Tooltip("오버라이드할 슬롯 수(0~4). 원본 배열은 6칸이고 813c 가 쓰는 활성 슬롯은 4개다.")]
public int slotCount = 4;
// ── 클래스별 4슬롯 (813n 완료보고 16행) ───────────────────────────────
[Header("클래스별 4슬롯 SkillID (813n 프리셋 · 슬롯 1~3 = 클래스 Active · 슬롯 4 = 광역)")]
[Tooltip("클래스 행. 내 클래스(Actor.Get_ID())와 classId 가 같은 행을 쓴다. 없으면 오버라이드 0.")]
public WLSkillSlotRow[] rows = new WLSkillSlotRow[]
{
new WLSkillSlotRow { classId = 10101, note = "onehand · 703101/703102/703103 + 광역 702006 돌개바람", skillIds = new[] { 703101, 703102, 703103, 702006 } },
new WLSkillSlotRow { classId = 10102, note = "spellblade · 703106/703105/703104 + 광역 702009 마나폭발", skillIds = new[] { 703106, 703105, 703104, 702009 } },
new WLSkillSlotRow { classId = 10501, note = "bluntshield · 703502/703503/703501 + 광역 702005 파도", skillIds = new[] { 703502, 703503, 703501, 702005 } },
new WLSkillSlotRow { classId = 10502, note = "paladin · 703505/703506/703504 + 광역 702012 심판의빛", skillIds = new[] { 703505, 703506, 703504, 702012 } },
};
// ── MP 부족 시 버튼 흐림 (발주서 §3) ──────────────────────────────────
[Header("MP 게이트 — 부족하면 버튼 흐림 (Actor.cs:733-741 과 같은 식)")]
[Tooltip("MP 부족 표시 사용 여부.")]
public bool mpDimEnabled = true;
[Tooltip("MP 가 모자랄 때 버튼 알파(0~1 · 1 = 흐리지 않음).")]
public float mpDimAlpha = 0.4f;
[Tooltip("MP 가 충분할 때 버튼 알파(보통 1).")]
public float mpFullAlpha = 1f;
[Tooltip("알파가 바뀌는 데 걸리는 시간(초 · 0 이면 즉시).")]
public float mpDimLerpSeconds = 0.12f;
[Tooltip("MP 확인 주기(초). 매 프레임 확인하지 않는다.")]
public float mpCheckInterval = 0.1f;
[Tooltip("MP 부족 표시를 쿨타임 중에도 유지한다(off 면 쿨 중에는 쿨 링만 보여 준다).")]
public bool mpDimDuringCooldown = true;
// ── 헬퍼 ──────────────────────────────────────────────────────────────
/// <summary>클래스 ID 의 행을 찾는다. 없으면 -1.</summary>
public int FindRow(int classId)
{
if (rows == null) return -1;
for (int i = 0; i < rows.Length; i++) if (rows[i].classId == classId) return i;
return -1;
}
/// <summary>클래스 ID · 슬롯의 SkillID. 행이 없거나 슬롯이 비면 0(= 서버 값 유지).</summary>
public int SkillIdFor(int classId, int slot)
{
int r = FindRow(classId);
if (r < 0) return 0;
var ids = rows[r].skillIds;
if (ids == null || slot < 0 || slot >= ids.Length) return 0;
return ids[slot];
}
/// <summary>실제로 오버라이드할 슬롯 수(설정 값과 행 길이 중 작은 쪽).</summary>
public int SlotCountFor(int classId)
{
int r = FindRow(classId);
if (r < 0) return 0;
var ids = rows[r].skillIds;
return ids == null ? 0 : Mathf.Clamp(slotCount, 0, ids.Length);
}
}
}