408 lines
21 KiB
C#
408 lines
21 KiB
C#
|
|
// WL-813g (#813) — 에디트 모드 실측 프로브(Play 0 · 합성 이벤트).
|
|||
|
|
// 실행: unity command run_script --file AgentScripts/WL813g_Probe.cs --entry WL813g_Probe.DumpSlots
|
|||
|
|
// unity command run_script --file AgentScripts/WL813g_Probe.cs --entry WL813g_Probe.DumpText
|
|||
|
|
// unity command run_script --file AgentScripts/WL813g_Probe.cs --entry WL813g_Probe.DumpDamage
|
|||
|
|
// unity command run_script --file AgentScripts/WL813g_Probe.cs --entry WL813g_Probe.DumpPrefab
|
|||
|
|
// 🔴 이 파일은 독립 어셈블리로 컴파일된다 — Assembly-CSharp 의 internal(Dispatch 등)은 못 쓴다.
|
|||
|
|
// 합성 이벤트는 Assembly-CSharp 안의 헬퍼(KillChainText.RaiseFakeTier 등)를 통해 낸다(813c 교훈).
|
|||
|
|
// 🔴 씬은 저장하지 않는다. 만든 임시 오브젝트는 전부 DestroyImmediate 로 회수한다.
|
|||
|
|
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Text;
|
|||
|
|
using UnityEditor;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
public static class WL813g_Probe
|
|||
|
|
{
|
|||
|
|
const string LogDir = "Logs";
|
|||
|
|
public const string NewGameUIPath = "Assets/Res_Addr/MainUI/NewGameUI.prefab";
|
|||
|
|
public const string DmgPrefabPath = "Assets/Res_Addr/Ingame/HUDDMGUI.prefab";
|
|||
|
|
public const string SkillJson = "Assets/ResWork/Table/Export/_WL813n_SkillList.json";
|
|||
|
|
public const string GlobalJson = "Assets/ResWork/Table/Export/GlobalValue.json";
|
|||
|
|
const int UILayer = 5;
|
|||
|
|
|
|||
|
|
static void Write(string name, string body)
|
|||
|
|
{
|
|||
|
|
System.IO.Directory.CreateDirectory(LogDir);
|
|||
|
|
System.IO.File.WriteAllText(System.IO.Path.Combine(LogDir, name), body, new System.Text.UTF8Encoding(true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static string Desc(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
if (rt == null) return "(rt 없음)";
|
|||
|
|
return "aMin=" + rt.anchorMin + " aMax=" + rt.anchorMax + " pos=" + rt.anchoredPosition +
|
|||
|
|
" size=" + rt.sizeDelta + " pivot=" + rt.pivot + " scale=" + rt.localScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─────────────────────────────────────────── 표 읽기(에디트 모드에서 table_* 싱글턴이 없어서 JSON 직독)
|
|||
|
|
|
|||
|
|
// 표는 단일 라인 JSON 배열이고 값은 전부 문자열이다(813n 실측) → 행 단위로 잘라 정규식으로 읽는다.
|
|||
|
|
// (run_script 는 파일마다 독립 어셈블리라 Newtonsoft 참조를 가정하지 않는다.)
|
|||
|
|
static List<string> SplitRows(string path)
|
|||
|
|
{
|
|||
|
|
var rows = new List<string>();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var txt = System.IO.File.ReadAllText(path, System.Text.Encoding.UTF8).TrimStart('');
|
|||
|
|
foreach (System.Text.RegularExpressions.Match m in
|
|||
|
|
System.Text.RegularExpressions.Regex.Matches(txt, "\\{[^{}]*\\}"))
|
|||
|
|
rows.Add(m.Value);
|
|||
|
|
}
|
|||
|
|
catch (System.Exception e) { Debug.LogWarning("[813g] 표 읽기 실패 " + path + " : " + e.Message); }
|
|||
|
|
return rows;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static string Field(string row, string key)
|
|||
|
|
{
|
|||
|
|
var m = System.Text.RegularExpressions.Regex.Match(row, "\"" + key + "\"\\s*:\\s*\"([^\"]*)\"");
|
|||
|
|
return m.Success ? m.Groups[1].Value : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static Dictionary<int, int> ReadSkillMp()
|
|||
|
|
{
|
|||
|
|
var map = new Dictionary<int, int>();
|
|||
|
|
foreach (var row in SplitRows(SkillJson))
|
|||
|
|
{
|
|||
|
|
var id = Field(row, "n_SkillID"); var mp = Field(row, "n_MP");
|
|||
|
|
int i, v;
|
|||
|
|
if (id != null && mp != null && int.TryParse(id, out i) && int.TryParse(mp, out v)) map[i] = v;
|
|||
|
|
}
|
|||
|
|
return map;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static Dictionary<int, float> ReadSkillCool()
|
|||
|
|
{
|
|||
|
|
var map = new Dictionary<int, float>();
|
|||
|
|
foreach (var row in SplitRows(SkillJson))
|
|||
|
|
{
|
|||
|
|
var id = Field(row, "n_SkillID"); var cd = Field(row, "f_CoolTime");
|
|||
|
|
int i; float v;
|
|||
|
|
if (id != null && cd != null && int.TryParse(id, out i) && float.TryParse(cd, out v)) map[i] = v;
|
|||
|
|
}
|
|||
|
|
return map;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static float ReadGlobal(string id, float fallback)
|
|||
|
|
{
|
|||
|
|
foreach (var row in SplitRows(GlobalJson))
|
|||
|
|
{
|
|||
|
|
if (Field(row, "s_ID") != id) continue;
|
|||
|
|
float v;
|
|||
|
|
if (float.TryParse(Field(row, "n_Value"), out v)) return v;
|
|||
|
|
}
|
|||
|
|
return fallback;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─────────────────────────────────────────── ① 슬롯 오버라이드 (발주서 §3 검증 = 4×4 덤프)
|
|||
|
|
|
|||
|
|
public static object DumpSlots()
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
var preset = WL.UI.WLSkillSlotPreset.Instance;
|
|||
|
|
sb.AppendLine("=== WL-813g ① 슬롯 장착 로컬 오버라이드 (에디트 모드 · Play 0) ===");
|
|||
|
|
if (preset == null) { var s0 = "🔴 WLSkillSlotPreset.asset 로드 실패"; Write("WL813g_probe_slots.txt", s0); return s0; }
|
|||
|
|
|
|||
|
|
sb.AppendLine("[해석 표 4×4]");
|
|||
|
|
sb.Append(WL.UI.WLSkillSlotOverride.ResolveDump(preset));
|
|||
|
|
|
|||
|
|
var mp = ReadSkillMp();
|
|||
|
|
var cool = ReadSkillCool();
|
|||
|
|
float mpPool = ReadGlobal("MP", -1f);
|
|||
|
|
float mpRegen = ReadGlobal("MP_REGEN", -1f);
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[MP 게이트 · Actor.cs:733-741 과 같은 식 · SKILL_COST=0 가정 · MP 풀=" + mpPool + " (GlobalValue 실측) · 회복=" + mpRegen + "/s]");
|
|||
|
|
for (int r = 0; r < preset.rows.Length; r++)
|
|||
|
|
{
|
|||
|
|
var row = preset.rows[r];
|
|||
|
|
int n = preset.SlotCountFor(row.classId);
|
|||
|
|
sb.Append("class ").Append(row.classId).Append(" :");
|
|||
|
|
for (int s = 0; s < n; s++)
|
|||
|
|
{
|
|||
|
|
int id = preset.SkillIdFor(row.classId, s);
|
|||
|
|
int need = mp.ContainsKey(id) ? mp[id] : -1;
|
|||
|
|
float cd = cool.ContainsKey(id) ? cool[id] : -1f;
|
|||
|
|
bool dim = mpPool > 0f && need > mpPool;
|
|||
|
|
sb.Append(" [").Append(s).Append("] id=").Append(id).Append(" mp=").Append(need)
|
|||
|
|
.Append(" cool=").Append(cd).Append("s dim@만탱=").Append(dim);
|
|||
|
|
}
|
|||
|
|
sb.AppendLine();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 합성 SD_Equip 에 실제 ApplyTo 를 태워 before→after 를 잰다(서버 데이터 무접촉)
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[합성 SD_Equip 적용 · 실제 WLSkillSlotOverride.ApplyTo 경로]");
|
|||
|
|
foreach (var row in preset.rows)
|
|||
|
|
{
|
|||
|
|
var equip = new SD_Equip();
|
|||
|
|
equip.Preset = 0;
|
|||
|
|
equip.Equip = new Dictionary<eItem, CodeStage.AntiCheat.ObscuredTypes.ObscuredInt>();
|
|||
|
|
equip.Equip[eItem.PC] = row.classId;
|
|||
|
|
equip.Skill = new Dictionary<int, List<CodeStage.AntiCheat.ObscuredTypes.ObscuredInt>>();
|
|||
|
|
var lst = new List<CodeStage.AntiCheat.ObscuredTypes.ObscuredInt>();
|
|||
|
|
for (int i = 0; i < 6; i++) lst.Add(0);
|
|||
|
|
lst[0] = 700001; // 서버 프리셋 흉내(패시브·엉뚱한 값)
|
|||
|
|
lst[1] = 0; lst[2] = 0; lst[3] = 0;
|
|||
|
|
equip.Skill[0] = lst;
|
|||
|
|
|
|||
|
|
var restore = new List<int>();
|
|||
|
|
string log = WL.UI.WLSkillSlotOverride.ApplyTo(equip, preset, row.classId, restore);
|
|||
|
|
sb.AppendLine(" " + log);
|
|||
|
|
var after = new StringBuilder(" Equip.Skill[0] = ");
|
|||
|
|
for (int i = 0; i < lst.Count; i++) after.Append(lst[i]).Append(i < lst.Count - 1 ? ", " : "");
|
|||
|
|
after.Append(" (슬롯 4·5 = 물약/회피 예약 · 건드리지 않음)");
|
|||
|
|
sb.AppendLine(after.ToString());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[C8 롤백] overrideEnabled=false 또는 에셋 삭제 → ApplyTo 미호출 = 서버 프리셋 원본");
|
|||
|
|
var s1 = sb.ToString();
|
|||
|
|
Write("WL813g_probe_slots.txt", s1);
|
|||
|
|
return s1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─────────────────────────────────────────── ② 텍스트 연출 3종 (합성 이벤트)
|
|||
|
|
|
|||
|
|
static Canvas MakeCanvas(out GameObject go)
|
|||
|
|
{
|
|||
|
|
go = new GameObject("WL813g_TempCanvas", typeof(RectTransform), typeof(Canvas), typeof(UnityEngine.UI.CanvasScaler));
|
|||
|
|
go.layer = UILayer;
|
|||
|
|
var canvas = go.GetComponent<Canvas>();
|
|||
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|||
|
|
var scaler = go.GetComponent<UnityEngine.UI.CanvasScaler>();
|
|||
|
|
// 값을 가정하지 않는다 — 실제 NewGameUI.prefab 루트의 CanvasScaler 설정을 그대로 복제한다(802b/813c 규약).
|
|||
|
|
scaler.uiScaleMode = UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|||
|
|
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
|||
|
|
scaler.screenMatchMode = UnityEngine.UI.CanvasScaler.ScreenMatchMode.Expand;
|
|||
|
|
scaler.matchWidthOrHeight = 0f;
|
|||
|
|
var srcPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(NewGameUIPath);
|
|||
|
|
var src = srcPrefab != null ? srcPrefab.GetComponent<UnityEngine.UI.CanvasScaler>() : null;
|
|||
|
|
if (src != null)
|
|||
|
|
{
|
|||
|
|
scaler.uiScaleMode = src.uiScaleMode;
|
|||
|
|
scaler.referenceResolution = src.referenceResolution;
|
|||
|
|
scaler.screenMatchMode = src.screenMatchMode;
|
|||
|
|
scaler.matchWidthOrHeight = src.matchWidthOrHeight;
|
|||
|
|
}
|
|||
|
|
LastScalerInfo = "CanvasScaler(원본 " + (src != null ? "복제" : "「미확인」 기본값") + ") mode=" + scaler.uiScaleMode +
|
|||
|
|
" ref=" + scaler.referenceResolution + " match=" + scaler.screenMatchMode + "/" + scaler.matchWidthOrHeight;
|
|||
|
|
var rt = (RectTransform)go.transform;
|
|||
|
|
rt.sizeDelta = new Vector2(1080f, 1920f); // 에디트 모드에서 캔버스가 스스로 크기를 못 잡으므로 기준 화면 주입
|
|||
|
|
return canvas;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static string LastScalerInfo = "";
|
|||
|
|
|
|||
|
|
static RectTransform Child(RectTransform parent, string name)
|
|||
|
|
{
|
|||
|
|
var go = new GameObject(name, typeof(RectTransform));
|
|||
|
|
go.layer = UILayer;
|
|||
|
|
var rt = (RectTransform)go.transform;
|
|||
|
|
rt.SetParent(parent, false);
|
|||
|
|
return rt;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static object DumpText()
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
sb.AppendLine("=== WL-813g ② 텍스트 연출 3종 · 합성 이벤트 (에디트 모드 · Play 0) ===");
|
|||
|
|
var st = WL.UI.WLCombatTextSettings.Instance;
|
|||
|
|
if (st == null) { var s0 = "🔴 WLCombatTextSettings.asset 로드 실패"; Write("WL813g_probe_text.txt", s0); return s0; }
|
|||
|
|
sb.AppendLine("설정: enabled=" + WL.UI.WLCombatTextSettings.Enabled +
|
|||
|
|
" killChain=" + st.killChainSeconds + "s/punch " + st.killChainPunchScale +
|
|||
|
|
" loot=" + st.lootToastSeconds + "s/" + st.lootToastLines + "줄" +
|
|||
|
|
" stat=" + st.statPopupSeconds + "s/" + st.statPopupRows + "줄");
|
|||
|
|
|
|||
|
|
GameObject canvasGo;
|
|||
|
|
var canvas = MakeCanvas(out canvasGo);
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var croot = (RectTransform)canvas.transform;
|
|||
|
|
sb.AppendLine(LastScalerInfo + " → unitsPerPx=" + st.UnitsPerPx(canvas).ToString("F4") +
|
|||
|
|
" (802b 실측 1.7778 과 대조)");
|
|||
|
|
|
|||
|
|
// ── (A) 연쇄 처치 문구
|
|||
|
|
var kcRt = Child(croot, "WL_KillChainText");
|
|||
|
|
var kc = kcRt.gameObject.AddComponent<WL.UI.KillChainText>();
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[A] KillChainText");
|
|||
|
|
sb.AppendLine(" init: " + kc.Initialize());
|
|||
|
|
sb.AppendLine(" 구독자 수(KillChain.TierReached) = " + WL.Combat.Reaction.KillChain.TierReached.Count);
|
|||
|
|
var tiers = WL.Combat.Reaction.WLReactionSettings.Instance != null
|
|||
|
|
? WL.Combat.Reaction.WLReactionSettings.Instance.tiers : null;
|
|||
|
|
if (tiers == null) sb.AppendLine(" 🔴 WLReactionSettings.tiers 없음(811e 에셋 미로드)");
|
|||
|
|
else
|
|||
|
|
for (int i = 0; i < tiers.Length; i++)
|
|||
|
|
{
|
|||
|
|
sb.AppendLine(" ── 합성 " + WL.UI.KillChainText.RaiseFakeTier(i, tiers[i].name, tiers[i].kills));
|
|||
|
|
sb.Append(" ").Append(kc.Dump());
|
|||
|
|
}
|
|||
|
|
kc.Subscribe(false);
|
|||
|
|
sb.AppendLine(" 구독 해제 후 구독자 = " + WL.Combat.Reaction.KillChain.TierReached.Count);
|
|||
|
|
|
|||
|
|
// ── (B) 전리품 토스트
|
|||
|
|
var ltRt = Child(croot, "WL_LootToast");
|
|||
|
|
var lt = ltRt.gameObject.AddComponent<WL.UI.LootToast>();
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[B] LootToast");
|
|||
|
|
sb.AppendLine(" init: " + lt.Initialize());
|
|||
|
|
sb.AppendLine(" 구독자 수(CombatEvents.Killed) = " + WL.Combat.Core.CombatEvents.Killed.Count);
|
|||
|
|
sb.AppendLine(" ── " + WL.UI.LootToast.RaiseFakeKilled() + " → killedSeen=" + lt.KilledSeen);
|
|||
|
|
// 등급 1~6 을 순서대로 밀어 넣어 동시 줄 상한을 잰다(표 이름이 없으면 ID 를 이름으로 쓴다)
|
|||
|
|
int[] fakeItems = { 1001, 1002, 1003, 1004, 1005 };
|
|||
|
|
for (int i = 0; i < fakeItems.Length; i++)
|
|||
|
|
sb.AppendLine(" push[" + i + "] " + lt.Push(fakeItems[i], 1) + " → visible=" + lt.VisibleLines);
|
|||
|
|
sb.AppendLine(" 중복 병합 " + lt.Push(fakeItems[fakeItems.Length - 1], 2) + " → visible=" + lt.VisibleLines);
|
|||
|
|
sb.Append(lt.Dump());
|
|||
|
|
lt.Subscribe(false);
|
|||
|
|
sb.AppendLine(" 구독 해제 후 구독자 = " + WL.Combat.Core.CombatEvents.Killed.Count);
|
|||
|
|
|
|||
|
|
// ── (C) 레벨업 스탯 팝업
|
|||
|
|
var spRt = Child(croot, "WL_StatPopup");
|
|||
|
|
var sp = spRt.gameObject.AddComponent<WL.UI.StatPopup>();
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[C] StatPopup");
|
|||
|
|
sb.AppendLine(" init: " + sp.Initialize());
|
|||
|
|
sb.AppendLine(" 구독자 수(CombatEvents.LevelUp) = " + WL.Combat.Core.CombatEvents.LevelUp.Count);
|
|||
|
|
sb.AppendLine(" ── " + WL.UI.StatPopup.RaiseFakeLevelUp(7) + " (PC 없음 → 스냅샷 대체 경로도 값 0 · 표시는 제목만)");
|
|||
|
|
sb.Append(sp.Dump());
|
|||
|
|
sb.AppendLine(" ── 813k 페이로드 모양으로 직접 표시:");
|
|||
|
|
sb.AppendLine(" " + sp.ShowFake(8, new[] { "STR", "DEX", "INT" }, new double[] { 120, 80, 45 }, new double[] { 128, 84, 48 }));
|
|||
|
|
sb.Append(sp.Dump());
|
|||
|
|
sp.Subscribe(false);
|
|||
|
|
sb.AppendLine(" 구독 해제 후 구독자 = " + WL.Combat.Core.CombatEvents.LevelUp.Count);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
if (canvasGo != null) Object.DestroyImmediate(canvasGo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var s1 = sb.ToString();
|
|||
|
|
Write("WL813g_probe_text.txt", s1);
|
|||
|
|
return s1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─────────────────────────────────────────── ③ 데미지 숫자 리듬 (실제 HUDDMGUI 프리팹)
|
|||
|
|
|
|||
|
|
public static object DumpDamage()
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
sb.AppendLine("=== WL-813g ③ 데미지 숫자 리듬 · 실제 HUDDMGUI 프리팹 (에디트 모드 · Play 0) ===");
|
|||
|
|
sb.AppendLine("설정: " + HUDDMGUI.SettingsDump());
|
|||
|
|
|
|||
|
|
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(DmgPrefabPath);
|
|||
|
|
if (prefab == null) { var s0 = "🔴 " + DmgPrefabPath + " 없음"; Write("WL813g_probe_damage.txt", s0); return s0; }
|
|||
|
|
|
|||
|
|
GameObject canvasGo;
|
|||
|
|
var canvas = MakeCanvas(out canvasGo);
|
|||
|
|
var made = new List<GameObject>();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
HUDDMGUI.ClearActiveList();
|
|||
|
|
var st = WL.UI.WLCombatTextSettings.Instance;
|
|||
|
|
int cap = st != null ? st.damageMaxConcurrent : 0;
|
|||
|
|
var croot = (RectTransform)canvas.transform;
|
|||
|
|
|
|||
|
|
System.Func<HUDDMGUI> spawn = () =>
|
|||
|
|
{
|
|||
|
|
var go = (GameObject)Object.Instantiate(prefab, croot);
|
|||
|
|
go.name = "HUDDMGUI_" + made.Count;
|
|||
|
|
made.Add(go);
|
|||
|
|
return go.GetComponent<HUDDMGUI>();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// (1) 일반 vs 크리 — 표시 시간 · 배율(기준서 §B 요소 2 = 0.6 / 1.0 / 1.6배)
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[1] 일반 / 크리 규격");
|
|||
|
|
var a = spawn(); a.SetCore(null, "<color=#FFFFFF>", 1234, eStat.None);
|
|||
|
|
sb.AppendLine(" 일반 : " + a.DumpState());
|
|||
|
|
var b = spawn(); b.SetCore(null, "<color=#FFFFFF>", 1234, eStat.CRI);
|
|||
|
|
sb.AppendLine(" 크리 : " + b.DumpState());
|
|||
|
|
float sa = a.dmgs[0].transform.localScale.x, sbb = b.dmgs[1].transform.localScale.x;
|
|||
|
|
sb.AppendLine(" 크기 비 = " + (sa > 0f ? (sbb / sa).ToString("F3") : "?") + " (목표 " + (st != null ? st.damageCritScale : 1f) + ")");
|
|||
|
|
|
|||
|
|
// (2) 합산 윈도우 — 같은 대상 · 같은 색이면 새 숫자 대신 합산(811e 0.25 s)
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[2] 합산 윈도우 (같은 대상 · 같은 색)");
|
|||
|
|
var targetGo = new GameObject("WL813g_FakeVictim"); made.Add(targetGo);
|
|||
|
|
var t = targetGo.transform;
|
|||
|
|
HUDDMGUI.ClearActiveList();
|
|||
|
|
var m0 = spawn(); m0.SetCore(t, "<color=#FFFFFF>", 100, eStat.None);
|
|||
|
|
sb.AppendLine(" 1타 : " + m0.DumpState() + " | active=" + HUDDMGUI.ActiveCount);
|
|||
|
|
var m1 = spawn(); m1.SetCore(t, "<color=#FFFFFF>", 250, eStat.None);
|
|||
|
|
sb.AppendLine(" 2타 : " + m0.DumpState() + " | active=" + HUDDMGUI.ActiveCount + " merged=" + HUDDMGUI.MergedCount);
|
|||
|
|
var m2 = spawn(); m2.SetCore(t, "<color=#FFFFFF>", 900, eStat.CRI);
|
|||
|
|
sb.AppendLine(" 3타(크리) : " + m0.DumpState() + " | active=" + HUDDMGUI.ActiveCount + " merged=" + HUDDMGUI.MergedCount);
|
|||
|
|
sb.AppendLine(" → 합산 창 안에서는 인스턴스가 늘지 않고 합계·타수만 오른다(크리 섞이면 크리 규격으로 승격)");
|
|||
|
|
|
|||
|
|
// (3) 동시 상한 — 상한 초과 시 오래된 일반부터 회수
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[3] 동시 표시 상한 (설정 " + cap + ")");
|
|||
|
|
HUDDMGUI.ClearActiveList();
|
|||
|
|
int n = Mathf.Max(1, cap) + 5;
|
|||
|
|
for (int i = 0; i < n; i++)
|
|||
|
|
{
|
|||
|
|
var g = new GameObject("victim" + i); made.Add(g);
|
|||
|
|
var h = spawn();
|
|||
|
|
h.SetCore(g.transform, "<color=#FFFFFF>", 10 + i, (i % 4 == 0) ? eStat.CRI : eStat.None);
|
|||
|
|
}
|
|||
|
|
sb.AppendLine(" " + n + "개 요청 → active=" + HUDDMGUI.ActiveCount + " (상한 " + cap + ") evicted=" + HUDDMGUI.EvictedCount +
|
|||
|
|
" · 상한 준수=" + (cap <= 0 || HUDDMGUI.ActiveCount <= cap));
|
|||
|
|
|
|||
|
|
// (4) C8 롤백 — 설정 off 면 원본 상수(0.9 s · 배율 1)
|
|||
|
|
sb.AppendLine();
|
|||
|
|
sb.AppendLine("[4] C8 롤백 (RuntimeDisabled=true = 에셋 없음과 같은 경로)");
|
|||
|
|
HUDDMGUI.ClearActiveList();
|
|||
|
|
WL.UI.WLCombatTextSettings.RuntimeDisabled = true;
|
|||
|
|
var c = spawn(); c.SetCore(null, "<color=#FFFFFF>", 777, eStat.CRI);
|
|||
|
|
sb.AppendLine(" 크리(롤백) : " + c.DumpState());
|
|||
|
|
sb.AppendLine(" 설정 덤프 : " + HUDDMGUI.SettingsDump());
|
|||
|
|
WL.UI.WLCombatTextSettings.RuntimeDisabled = false;
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
for (int i = made.Count - 1; i >= 0; i--) if (made[i] != null) Object.DestroyImmediate(made[i]);
|
|||
|
|
if (canvasGo != null) Object.DestroyImmediate(canvasGo);
|
|||
|
|
HUDDMGUI.ClearActiveList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var s1 = sb.ToString();
|
|||
|
|
Write("WL813g_probe_damage.txt", s1);
|
|||
|
|
return s1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─────────────────────────────────────────── ④ 프리팹 노드 (통째 Read 금지 · 노드만)
|
|||
|
|
|
|||
|
|
public static object DumpPrefab()
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
sb.AppendLine("=== WL-813g ④ NewGameUI.prefab 추가 노드 실측 ===");
|
|||
|
|
var root = PrefabUtility.LoadPrefabContents(NewGameUIPath);
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var ingame = root.transform.Find("IngameUIs");
|
|||
|
|
if (ingame == null) { sb.AppendLine("🔴 IngameUIs 없음"); }
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var hud = ingame.Find("WL_HUD");
|
|||
|
|
sb.AppendLine("IngameUIs/WL_HUD : " + (hud != null ? "있음 children=" + hud.childCount : "🔴 없음"));
|
|||
|
|
if (hud != null)
|
|||
|
|
{
|
|||
|
|
sb.AppendLine(" SafeAreaFitter=" + (hud.GetComponent<WL.UI.SafeAreaFitter>() != null));
|
|||
|
|
foreach (Transform c in hud)
|
|||
|
|
sb.AppendLine(" child " + c.name + " " + Desc(c as RectTransform) +
|
|||
|
|
" comps=" + string.Join(",", System.Array.ConvertAll(c.GetComponents<Component>(), x => x.GetType().Name)));
|
|||
|
|
}
|
|||
|
|
var pad = ingame.Find("BattleUI");
|
|||
|
|
sb.AppendLine("IngameUIs/BattleUI : " + (pad != null ? "있음" : "🔴 없음"));
|
|||
|
|
if (pad != null)
|
|||
|
|
sb.AppendLine(" comps=" + string.Join(",", System.Array.ConvertAll(pad.GetComponents<Component>(), x => x.GetType().Name)));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
finally { PrefabUtility.UnloadPrefabContents(root); }
|
|||
|
|
|
|||
|
|
var s1 = sb.ToString();
|
|||
|
|
Write("WL813g_probe_prefab.txt", s1);
|
|||
|
|
return s1;
|
|||
|
|
}
|
|||
|
|
}
|