Project_WL/Assets/WL/Character/WLWeaponFitSettings.cs

123 lines
6.0 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// WLWeaponFitSettings.cs — 치비 PC 의 무기 크기 자동 보정 단일 출처(SOT · C45)
//
// 발주서 WL-814p (#814 잔여 결함) · 근거 = 완료보고 WL-814n §1 ⑰
// 치비 교체 후 `Elven_Sword_01` 월드 최대변 1.341 m = 캐릭터 키(1.1912 m)의 **113 %**
// → 어른 몸(Ai01 1.7017 m) 기준으로 만든 무기가 치비 손에 그대로 붙어 과대.
//
// ■ 어디를 고치나 — 원본 수정 0
// 원본이 무기를 다는 지점은 `Assets/Script/Character/PCActor.cs:358`
// Make_Weapon(fullpath, tfs_weapon[index], Vector3.zero, Vector3.one)
// = Addressables 비동기 콜백에서 소켓에 **localScale 1** 로 붙인다(실측).
// 814n 에서 원본 1줄 예외를 이미 소진했으므로 훅을 더 넣지 않는다 →
// **새 프리팹 `LH_M05` 에만 붙는 `WLWeaponFitter`** 가 소켓을 감시해서 보정한다.
// Ai01 경로에는 이 컴포넌트가 없으므로 구조적으로 영향 0.
//
// ■ 되돌리기(C8)
// 이 에셋의 `enabled_` 를 끄거나 에셋을 지우면 배수 1.0000 = 컴포넌트 no-op = 지금 상태 100 %.
//
// 에셋 경로(고정): Assets/WL/Settings/Resources/WL/WLWeaponFitSettings.asset
// 🔴 Assets/WL/Character/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
// ─────────────────────────────────────────────────────────────────────────────
using System;
using System.Collections.Generic;
using UnityEngine;
namespace WL.Character
{
[CreateAssetMenu(fileName = "WLWeaponFitSettings", menuName = "WL/Weapon Fit Settings", order = 332)]
public sealed class WLWeaponFitSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLWeaponFitSettings";
static WLWeaponFitSettings s_cached;
static bool s_lookupDone;
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null = 무동작.</summary>
public static WLWeaponFitSettings Instance
{
get
{
if (!s_lookupDone) { s_cached = Resources.Load<WLWeaponFitSettings>(ResourcesPath); s_lookupDone = true; }
return s_cached;
}
}
/// <summary>QA·A/B 전용 런타임 스위치. 0 = 에셋의 enabled_ · 1 = 강제 on · -1 = 강제 off.</summary>
public static int RuntimeOverride;
/// <summary>보정이 살아 있는가. false 면 무기 스케일에 손대지 않는다(C8).</summary>
public static bool Enabled
{
get
{
var i = Instance;
if (i == null || RuntimeOverride < 0) return false;
return RuntimeOverride > 0 || i.enabled_;
}
}
/// <summary>테스트용: 캐시를 비운다.</summary>
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
[Serializable]
public sealed class WeaponRatio
{
[Tooltip("무기 프리팹 이름(Assets/Res_Addr/Weapon/<이름>.prefab). \"(Clone)\" 은 무시하고 비교한다.")]
public string weaponName;
[Tooltip("이 무기만 쓸 비율(무기 최대변 ÷ 캐릭터 키). 0 이하면 targetRatio 를 쓴다.")]
public float ratio;
}
[Header("전체 스위치 (PD 되돌리기 1순위)")]
[Tooltip("끄면 무기 스케일에 아무것도 하지 않는다 = 지금 상태 100 %. 에셋을 지워도 같다.")]
public bool enabled_ = true;
[Tooltip("보정이 일어날 때 Debug.Log 를 남긴다(A/B 확인용). 기본 끔.")]
public bool verboseLog;
[Header("목표 비율")]
[Tooltip("보정 후 무기 최대변 ÷ 캐릭터 키. 0.60 = 캐릭터 키의 60 %.\n" +
"치비 교체 전 어른 Ai01(1.7017 m) 기준 Elven_Sword_01 은 0.79 였다(1.341/1.7017).")]
public float targetRatio = 0.60f;
[Tooltip("보정 배수 하한(원본 크기 대비). 0.3 = 아무리 커도 30 % 미만으로는 줄이지 않는다.")]
public float minScale = 0.3f;
[Tooltip("보정 배수 상한(원본 크기 대비). 1.0 = 무기를 키우지는 않는다.")]
public float maxScale = 1.0f;
[Header("무기별 예외 (비어 있으면 전부 targetRatio)")]
[Tooltip("이름 → 비율 override. 방패·오브처럼 길이 기준이 다른 무기를 따로 잡을 때만 쓴다.")]
public List<WeaponRatio> perWeapon = new List<WeaponRatio>();
/// <summary>무기 이름에 맞는 목표 비율. 스위치가 꺼져 있으면 0(= 무보정).</summary>
public static float ResolveRatio(string weaponName)
{
var i = Instance;
if (!Enabled || i == null) return 0f;
if (i.perWeapon != null && !string.IsNullOrEmpty(weaponName))
{
var key = Strip(weaponName);
for (int n = 0; n < i.perWeapon.Count; n++)
{
var row = i.perWeapon[n];
if (row == null || string.IsNullOrEmpty(row.weaponName) || row.ratio <= 0f) continue;
if (string.Equals(Strip(row.weaponName), key, StringComparison.OrdinalIgnoreCase)) return row.ratio;
}
}
return i.targetRatio > 0f ? i.targetRatio : 0f;
}
/// <summary>"Elven_Sword_01(Clone)" → "Elven_Sword_01" (원본 `DSUtil.Get_Clone` 이 Instantiate 기본 이름을 쓴다).</summary>
public static string Strip(string n)
{
if (string.IsNullOrEmpty(n)) return n;
int i = n.IndexOf("(Clone)", StringComparison.Ordinal);
return i >= 0 ? n.Substring(0, i).Trim() : n.Trim();
}
}
}