Project_WL/AgentScripts/WL813g_Apply.cs

210 lines
9.9 KiB
C#
Raw Permalink Normal View History

// WL-813g (#813) — 설정 에셋 2종 생성 + NewGameUI.prefab 에 텍스트 연출 노드 3개 · 슬롯 오버라이드 컴포넌트 추가.
// 실행: unity command run_script --file AgentScripts/WL813g_Apply.cs --entry WL813g_Apply.CreateSettings
// unity command run_script --file AgentScripts/WL813g_Apply.cs --entry WL813g_Apply.Resave
// unity command run_script --file AgentScripts/WL813g_Apply.cs --entry WL813g_Apply.Apply
// unity command run_script --file AgentScripts/WL813g_Apply.cs --entry WL813g_Apply.Revert
// 🔴 run_script 파일은 파일마다 독립 어셈블리 — 다른 AgentScript 상수 참조 불가(813c 교훈).
using System.Text;
using UnityEditor;
using UnityEngine;
public static class WL813g_Apply
{
public const string NewGameUIPath = "Assets/Res_Addr/MainUI/NewGameUI.prefab";
public const string SettingsDir = "Assets/WL/UI/Settings/Resources/WL";
public const string TextSettingsPath = SettingsDir + "/WLCombatTextSettings.asset";
public const string SlotPresetPath = SettingsDir + "/WLSkillSlotPreset.asset";
public const string HudPath = "IngameUIs/WL_HUD";
public const string PadPath = "IngameUIs/BattleUI";
public const string KillChainName = "WL_KillChainText";
public const string LootToastName = "WL_LootToast";
public const string StatPopupName = "WL_StatPopup";
const string TmpFontGuid = "a387e325271126742ad15569dba1ac57"; // 813c 가 실측한 SkillCard/t_cooltime 폰트
const int UILayer = 5;
static Transform FindByPath(Transform root, string path)
{
var cur = root;
foreach (var part in path.Split('/'))
{
Transform next = null;
for (int i = 0; i < cur.childCount; i++)
if (cur.GetChild(i).name == part) { next = cur.GetChild(i); break; }
if (next == null) return null;
cur = next;
}
return cur;
}
static void EnsureFolder(string dir)
{
var parts = dir.Split('/');
var cur = parts[0];
for (int i = 1; i < parts.Length; i++)
{
var next = cur + "/" + parts[i];
if (!AssetDatabase.IsValidFolder(next)) AssetDatabase.CreateFolder(cur, parts[i]);
cur = next;
}
}
/// <summary>설정 에셋 2종 생성(이미 있으면 값 유지 · 덮어쓰지 않는다).</summary>
public static object CreateSettings()
{
var sb = new StringBuilder();
EnsureFolder(SettingsDir);
var text = AssetDatabase.LoadAssetAtPath<WL.UI.WLCombatTextSettings>(TextSettingsPath);
if (text == null)
{
text = ScriptableObject.CreateInstance<WL.UI.WLCombatTextSettings>();
AssetDatabase.CreateAsset(text, TextSettingsPath);
sb.AppendLine("생성: " + TextSettingsPath);
}
else sb.AppendLine("이미 있음(값 유지): " + TextSettingsPath);
var slot = AssetDatabase.LoadAssetAtPath<WL.UI.WLSkillSlotPreset>(SlotPresetPath);
if (slot == null)
{
slot = ScriptableObject.CreateInstance<WL.UI.WLSkillSlotPreset>();
AssetDatabase.CreateAsset(slot, SlotPresetPath);
sb.AppendLine("생성: " + SlotPresetPath);
}
else sb.AppendLine("이미 있음(값 유지): " + SlotPresetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
WL.UI.WLCombatTextSettings.ClearCache();
WL.UI.WLSkillSlotPreset.ClearCache();
sb.AppendLine("── 데미지 리듬(기준서 §B 요소 2)");
sb.AppendLine(" normal=" + text.damageNormalSeconds + "s crit=" + text.damageCritSeconds +
"s critScale=" + text.damageCritScale + " maxConcurrent=" + text.damageMaxConcurrent);
sb.AppendLine("── 연쇄 문구 / 토스트 / 스탯 팝업");
sb.AppendLine(" killChain=" + text.killChainSeconds + "s punch=" + text.killChainPunchScale +
" topPx=" + text.killChainTopOffsetPx +
" · loot=" + text.lootToastSeconds + "s lines=" + text.lootToastLines +
" · stat=" + text.statPopupSeconds + "s rows=" + text.statPopupRows);
sb.AppendLine("── 슬롯 오버라이드(813n)");
sb.Append(WL.UI.WLSkillSlotOverride.ResolveDump(slot));
return sb.ToString();
}
/// <summary>무변경 재저장 — 프리팹 편집 전에 재직렬화 diff 가 생기는지 먼저 잰다(802b·813c 교훈).</summary>
public static object Resave()
{
var root = PrefabUtility.LoadPrefabContents(NewGameUIPath);
try { PrefabUtility.SaveAsPrefabAsset(root, NewGameUIPath); }
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.Refresh();
return NewGameUIPath + " : 무변경 재저장 (git diff 로 재직렬화 여부 확인)";
}
static Transform EnsureNode(Transform parent, string name, StringBuilder sb)
{
var t = parent.Find(name);
if (t == null)
{
var go = new GameObject(name, typeof(RectTransform));
go.layer = UILayer;
t = go.transform;
((RectTransform)t).SetParent((RectTransform)parent, false);
sb.AppendLine(parent.name + "/" + name + " : 노드 추가");
}
else sb.AppendLine(parent.name + "/" + name + " : 이미 있음");
return t;
}
public static object Apply()
{
var sb = new StringBuilder();
var text = AssetDatabase.LoadAssetAtPath<WL.UI.WLCombatTextSettings>(TextSettingsPath);
if (text == null) return "🔴 " + TextSettingsPath + " 없음 — CreateSettings 먼저";
TMPro.TMP_FontAsset font = null;
var fontPath = AssetDatabase.GUIDToAssetPath(TmpFontGuid);
if (!string.IsNullOrEmpty(fontPath)) font = AssetDatabase.LoadAssetAtPath<TMPro.TMP_FontAsset>(fontPath);
sb.AppendLine("폰트: " + (font != null ? fontPath : "「미확인」 — TMP 기본 폰트로 떨어진다"));
var root = PrefabUtility.LoadPrefabContents(NewGameUIPath);
try
{
// ── ① 텍스트 연출 3종 = IngameUIs/WL_HUD 아래(802b SafeAreaFitter 레이어 · 813c 규칙)
var hud = FindByPath(root.transform, HudPath);
if (hud == null) sb.AppendLine("🔴 " + HudPath + " 없음");
else
{
var kc = EnsureNode(hud, KillChainName, sb);
var kct = kc.GetComponent<WL.UI.KillChainText>();
if (kct == null) { kct = kc.gameObject.AddComponent<WL.UI.KillChainText>(); sb.AppendLine(" KillChainText 추가"); }
if (font != null) kct.SetFont(font);
sb.AppendLine(" build made=" + kct.BuildIfNeeded() + " · " + kct.ApplyLayout());
kct.Hide();
var lt = EnsureNode(hud, LootToastName, sb);
var ltc = lt.GetComponent<WL.UI.LootToast>();
if (ltc == null) { ltc = lt.gameObject.AddComponent<WL.UI.LootToast>(); sb.AppendLine(" LootToast 추가"); }
if (font != null) ltc.SetFont(font);
sb.AppendLine(" build made=" + ltc.BuildIfNeeded() + " · " + ltc.ApplyLayout());
ltc.HideAll();
var sp = EnsureNode(hud, StatPopupName, sb);
var spc = sp.GetComponent<WL.UI.StatPopup>();
if (spc == null) { spc = sp.gameObject.AddComponent<WL.UI.StatPopup>(); sb.AppendLine(" StatPopup 추가"); }
if (font != null) spc.SetFont(font);
sb.AppendLine(" build made=" + spc.BuildIfNeeded() + " · " + spc.ApplyLayout());
spc.Hide();
}
// ── ② 슬롯 장착 로컬 오버라이드 = IngameUIs/BattleUI 인스턴스 전용 추가 컴포넌트(813c 방식)
var pad = FindByPath(root.transform, PadPath);
if (pad == null) sb.AppendLine("🔴 " + PadPath + " 없음");
else
{
var ov = pad.GetComponent<WL.UI.WLSkillSlotOverride>();
if (ov == null) { ov = pad.gameObject.AddComponent<WL.UI.WLSkillSlotOverride>(); sb.AppendLine(PadPath + " : WLSkillSlotOverride 추가"); }
else sb.AppendLine(PadPath + " : WLSkillSlotOverride 이미 있음");
var so = new SerializedObject(ov);
so.FindProperty("target").objectReferenceValue = pad.GetComponent<BattleUI>();
so.ApplyModifiedPropertiesWithoutUndo();
sb.AppendLine(" target=BattleUI 주입 · BattleUI.prefab 원본 무수정(인스턴스 전용 추가 컴포넌트)");
}
PrefabUtility.SaveAsPrefabAsset(root, NewGameUIPath);
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return sb.ToString();
}
public static object Revert()
{
var sb = new StringBuilder();
var root = PrefabUtility.LoadPrefabContents(NewGameUIPath);
try
{
var hud = FindByPath(root.transform, HudPath);
if (hud != null)
foreach (var n in new[] { KillChainName, LootToastName, StatPopupName })
{
var t = hud.Find(n);
if (t != null) { Object.DestroyImmediate(t.gameObject); sb.AppendLine(HudPath + "/" + n + " : 제거"); }
}
var pad = FindByPath(root.transform, PadPath);
if (pad != null)
{
var ov = pad.GetComponent<WL.UI.WLSkillSlotOverride>();
if (ov != null) { Object.DestroyImmediate(ov, true); sb.AppendLine(PadPath + " : WLSkillSlotOverride 제거"); }
}
PrefabUtility.SaveAsPrefabAsset(root, NewGameUIPath);
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return sb.ToString();
}
}