185 lines
9.6 KiB
C#
185 lines
9.6 KiB
C#
|
|
// WL760_Play.cs — PD 지시 #760 2-A Play 검증 probe (런타임 전용 · 서버 쓰기 없음 · Assets 무수정)
|
||
|
|
// unity command run_script --file AgentScripts/WL760_Play.cs --entry WL760_Play.State
|
||
|
|
// unity command run_script --file AgentScripts/WL760_Play.cs --entry WL760_Play.SetClass --args '[10101]'
|
||
|
|
// unity command run_script --file AgentScripts/WL760_Play.cs --entry WL760_Play.Attack
|
||
|
|
// unity command run_script --file AgentScripts/WL760_Play.cs --entry WL760_Play.Cap --args '["10101_1"]'
|
||
|
|
// unity command run_script --file AgentScripts/WL760_Play.cs --entry WL760_Play.Mask
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Reflection;
|
||
|
|
using System.Text;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
public static class WL760_Play
|
||
|
|
{
|
||
|
|
const string OutDir = "Screenshots_WL/combat2";
|
||
|
|
const BindingFlags BF = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||
|
|
|
||
|
|
static MyActor Me()
|
||
|
|
{
|
||
|
|
return UnityEngine.Object.FindObjectsByType<MyActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None)
|
||
|
|
.FirstOrDefault();
|
||
|
|
}
|
||
|
|
|
||
|
|
static double HP(Actor a)
|
||
|
|
{
|
||
|
|
var mi = typeof(Actor).GetMethod("Get_HP", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||
|
|
if (mi == null) return double.NaN;
|
||
|
|
return Convert.ToDouble(mi.Invoke(a, null));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 현재 상태 ────────────────────────────────────────────────────────────
|
||
|
|
public static object State()
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
sb.AppendLine("playing=" + Application.isPlaying + " scene=" +
|
||
|
|
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name +
|
||
|
|
" loaded=" + string.Join("+", Enumerable.Range(0, UnityEngine.SceneManagement.SceneManager.sceneCount)
|
||
|
|
.Select(i => UnityEngine.SceneManagement.SceneManager.GetSceneAt(i).name)));
|
||
|
|
var me = Me();
|
||
|
|
if (me == null) { sb.AppendLine("MyActor: none"); return sb.ToString(); }
|
||
|
|
|
||
|
|
var anim = me.GetComponentInChildren<Animator>();
|
||
|
|
sb.AppendLine("MyActor pos=" + me.transform.position.ToString("F1") +
|
||
|
|
" ctrl=" + (anim != null && anim.runtimeAnimatorController != null ? anim.runtimeAnimatorController.name : "(null)") +
|
||
|
|
" hp=" + HP(me).ToString("F0"));
|
||
|
|
if (anim != null && anim.layerCount > 0)
|
||
|
|
{
|
||
|
|
var ci = anim.GetCurrentAnimatorClipInfo(0);
|
||
|
|
sb.AppendLine(" playing clip=" + (ci.Length > 0 ? ci[0].clip.name : "(none)"));
|
||
|
|
}
|
||
|
|
|
||
|
|
var mobs = UnityEngine.Object.FindObjectsByType<MobActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
|
||
|
|
sb.AppendLine("MobActor alive=" + mobs.Length);
|
||
|
|
foreach (var m in mobs.OrderBy(m => Vector3.Distance(m.transform.position, me.transform.position)).Take(4))
|
||
|
|
sb.AppendLine(" " + m.name + " d=" + Vector3.Distance(m.transform.position, me.transform.position).ToString("F1") +
|
||
|
|
" hp=" + HP(m).ToString("F0"));
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 클래스 전환 (서버 쓰기 없음 · #747 선례) ─────────────────────────────
|
||
|
|
public static object SetClass(int classId)
|
||
|
|
{
|
||
|
|
var me = Me();
|
||
|
|
if (me == null) return "MyActor none";
|
||
|
|
var pc = me as PCActor;
|
||
|
|
if (pc == null) return "MyActor is not PCActor: " + me.GetType().Name;
|
||
|
|
|
||
|
|
var data = table_classconfig.Ins.Get_Data_orNull(classId);
|
||
|
|
if (data == null) return "class data null: " + classId;
|
||
|
|
|
||
|
|
pc.Change_Class(data, true);
|
||
|
|
var anim = me.GetComponentInChildren<Animator>();
|
||
|
|
return "Change_Class(" + classId + ") ok ctrl=" +
|
||
|
|
(anim != null && anim.runtimeAnimatorController != null ? anim.runtimeAnimatorController.name : "(null)");
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 공격 1회 (자동 타겟) ─────────────────────────────────────────────────
|
||
|
|
public static object Attack()
|
||
|
|
{
|
||
|
|
var me = Me();
|
||
|
|
if (me == null) return "MyActor none";
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
|
||
|
|
var mobs = UnityEngine.Object.FindObjectsByType<MobActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None)
|
||
|
|
.OrderBy(m => Vector3.Distance(m.transform.position, me.transform.position)).ToArray();
|
||
|
|
if (mobs.Length == 0) return "no MobActor";
|
||
|
|
var t = mobs[0];
|
||
|
|
|
||
|
|
// 사거리 안으로 이동 + 바라보기 (입력 주입 없이 트랜스폼만 — 서버 쓰기 없음)
|
||
|
|
var dir = (t.transform.position - me.transform.position); dir.y = 0f;
|
||
|
|
if (dir.magnitude > 1.6f)
|
||
|
|
me.transform.position = t.transform.position - dir.normalized * 1.5f;
|
||
|
|
me.transform.LookAt(new Vector3(t.transform.position.x, me.transform.position.y, t.transform.position.z));
|
||
|
|
|
||
|
|
var setT = typeof(Actor).GetMethod("Change_Target", BF);
|
||
|
|
if (setT != null) { try { setT.Invoke(me, new object[] { false, (Actor)t, false }); } catch { } }
|
||
|
|
|
||
|
|
sb.AppendLine("target=" + t.name + " hpBefore=" + HP(t).ToString("F0") +
|
||
|
|
" d=" + Vector3.Distance(me.transform.position, t.transform.position).ToString("F2"));
|
||
|
|
|
||
|
|
double aspd = 1.0;
|
||
|
|
try {
|
||
|
|
var fi = typeof(Actor).GetField("m_Stat", BF);
|
||
|
|
var stat = fi != null ? fi.GetValue(me) : null;
|
||
|
|
if (stat != null) {
|
||
|
|
var gm = stat.GetType().GetMethod("Get_Stat", new[] { typeof(eStat) });
|
||
|
|
if (gm != null) aspd = Convert.ToDouble(gm.Invoke(stat, new object[] { eStat.FinalAttackSpeed }));
|
||
|
|
}
|
||
|
|
} catch { }
|
||
|
|
me.Play_Attack(0, (float)aspd);
|
||
|
|
sb.AppendLine("Play_Attack(0," + aspd.ToString("F2") + ") issued");
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>공격 후 대상 HP 를 다시 읽는다 (판정 ⓒ 데미지 0 아님).</summary>
|
||
|
|
public static object Hp()
|
||
|
|
{
|
||
|
|
var me = Me();
|
||
|
|
if (me == null) return "MyActor none";
|
||
|
|
var mobs = UnityEngine.Object.FindObjectsByType<MobActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None)
|
||
|
|
.OrderBy(m => Vector3.Distance(m.transform.position, me.transform.position)).Take(3).ToArray();
|
||
|
|
var anim = me.GetComponentInChildren<Animator>();
|
||
|
|
string clip = "(none)";
|
||
|
|
if (anim != null && anim.layerCount > 0)
|
||
|
|
{
|
||
|
|
var ci = anim.GetCurrentAnimatorClipInfo(0);
|
||
|
|
if (ci.Length > 0) clip = ci[0].clip.name + " n=" + anim.GetCurrentAnimatorStateInfo(0).normalizedTime.ToString("F2");
|
||
|
|
}
|
||
|
|
return "clip=" + clip + " " + string.Join(" | ", mobs.Select(m => m.name + " hp=" + HP(m).ToString("F0")));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 캡처 (Assets 밖 · LightProbe.Render 패턴) ────────────────────────────
|
||
|
|
public static object Cap(string tag)
|
||
|
|
{
|
||
|
|
var cam = Camera.main;
|
||
|
|
if (cam == null) return "no Camera.main";
|
||
|
|
System.IO.Directory.CreateDirectory(OutDir);
|
||
|
|
int w = 1280, h = 720;
|
||
|
|
var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32);
|
||
|
|
var go = new GameObject("__wl760cam");
|
||
|
|
var c = go.AddComponent<Camera>();
|
||
|
|
c.CopyFrom(cam); c.targetTexture = rt; c.enabled = false; c.Render();
|
||
|
|
var prev = RenderTexture.active; RenderTexture.active = rt;
|
||
|
|
var tex = new Texture2D(w, h, TextureFormat.RGB24, false);
|
||
|
|
tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply();
|
||
|
|
RenderTexture.active = prev;
|
||
|
|
|
||
|
|
// 마젠타(셰이더 미컴파일) 픽셀 카운트 — 판정 ⓓ
|
||
|
|
var px = tex.GetPixels32();
|
||
|
|
int magenta = 0;
|
||
|
|
foreach (var p in px) if (p.r > 200 && p.b > 200 && p.g < 60) magenta++;
|
||
|
|
|
||
|
|
var path = System.IO.Path.Combine(OutDir, tag + ".png");
|
||
|
|
System.IO.File.WriteAllBytes(path, tex.EncodeToPNG());
|
||
|
|
UnityEngine.Object.DestroyImmediate(tex);
|
||
|
|
c.targetTexture = null; UnityEngine.Object.DestroyImmediate(go);
|
||
|
|
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
|
||
|
|
return path + " magentaPx=" + magenta + "/" + px.Length;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>씬 안의 살아 있는 슬래시 이펙트 인스턴스별 이미터 on/off 전수 (판정 ⓓ).</summary>
|
||
|
|
public static object Mask()
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
var masks = UnityEngine.Object.FindObjectsByType<WL.Combat.SlashCrescentMask>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||
|
|
sb.AppendLine("SlashCrescentMask instances=" + masks.Length +
|
||
|
|
" last=" + (WL.Combat.WeaponTrailDriver.LastMaskPrefab ?? "(none)"));
|
||
|
|
foreach (var m in masks)
|
||
|
|
sb.AppendLine(" " + m.gameObject.name + " active=" + m.gameObject.activeInHierarchy + " :: " + m.DumpState());
|
||
|
|
|
||
|
|
var trails = UnityEngine.Object.FindObjectsByType<WL.Combat.BladeTrail>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||
|
|
sb.AppendLine("BladeTrail instances=" + trails.Length);
|
||
|
|
foreach (var t in trails)
|
||
|
|
{
|
||
|
|
var mr = t.GetComponent<MeshRenderer>();
|
||
|
|
sb.AppendLine(" " + t.gameObject.name + " enabled=" + t.enabled +
|
||
|
|
" rendererEnabled=" + (mr != null ? mr.enabled.ToString() : "(no MR)") +
|
||
|
|
" mat=" + (mr != null && mr.sharedMaterial != null ? mr.sharedMaterial.name + "/" +
|
||
|
|
(mr.sharedMaterial.shader != null ? mr.sharedMaterial.shader.name : "?") : "(none)"));
|
||
|
|
}
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
}
|