160 lines
9.8 KiB
C#
160 lines
9.8 KiB
C#
// WL890_Probe.cs — PD 지시 #890 · #891 시험(Play 중)
|
||
// ShieldInfo : 방패(왼손 소켓 0) · 팔(Elbow_L → Wrist_L) · 몸 중심(Chest_M) 기하 — 팔 바깥쪽 방향 · 방패 면 방향 · 그림자 설정
|
||
// ShieldOutward : 방패 래퍼 자식을 「팔 바깥쪽」으로 dist(월드 m) 만큼 옮긴다(실행 중만 · 파일 무수정) → 래퍼 루트 기준 로컬 값을 돌려준다(JSON 에 넣을 값)
|
||
// ShieldShadow : 방패 렌더러 그림자 받기 켬/끔(실행 중만)
|
||
// Q / Chain : Q 키와 같은 함수 호출 · 연결 상태
|
||
using System.Text;
|
||
using UnityEngine;
|
||
|
||
public static class WL890_Probe
|
||
{
|
||
static Transform Bone(PCActor pc, string name)
|
||
{
|
||
foreach (var t in pc.GetComponentsInChildren<Transform>(true)) if (t.name == name) return t;
|
||
return null;
|
||
}
|
||
|
||
static Transform ShieldChild(PCActor pc, out Transform root)
|
||
{
|
||
root = null;
|
||
var s = pc.tfs_weapon[0]; if (s == null || s.childCount == 0) return null;
|
||
root = s.GetChild(0);
|
||
return root.childCount > 0 ? root.GetChild(0) : root;
|
||
}
|
||
|
||
static Vector3 Outward(PCActor pc)
|
||
{
|
||
var elbow = Bone(pc, "Elbow_L"); var wrist = Bone(pc, "Wrist_L"); var chest = Bone(pc, "Chest_M");
|
||
if (elbow == null || wrist == null || chest == null) return Vector3.zero;
|
||
var arm = (wrist.position - elbow.position).normalized;
|
||
var o = wrist.position - chest.position;
|
||
o -= Vector3.Dot(o, arm) * arm; // 팔 방향 성분 제거 = 팔에 수직인 「몸 → 팔」 방향
|
||
return o.sqrMagnitude > 1e-8f ? o.normalized : Vector3.zero;
|
||
}
|
||
|
||
public static object ShieldInfo()
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null) return "방패 없음";
|
||
var wrist = Bone(pc, "Wrist_L"); var chest = Bone(pc, "Chest_M");
|
||
var outw = Outward(pc);
|
||
var sb = new StringBuilder();
|
||
var ci = pc.m_animation.GetCurrentAnimatorClipInfo(0);
|
||
sb.Append("clip=").Append(ci.Length > 0 ? ci[0].clip.name : "?").Append(" root=").Append(root.name).Append(" child=").Append(ch.name).Append(" childLocal=").Append(ch.localPosition.ToString("F3"));
|
||
sb.Append("\n 팔 바깥쪽(월드)=").Append(outw.ToString("F2")).Append(" · 방패 두께축(Z) · 바깥쪽 = ").Append(Vector3.Dot(ch.forward, outw).ToString("F2"));
|
||
var rends = ch.GetComponentsInChildren<Renderer>(true);
|
||
var b = rends.Length > 0 ? rends[0].bounds : new Bounds(ch.position, Vector3.zero); foreach (var r in rends) b.Encapsulate(r.bounds);
|
||
sb.Append("\n 방패 중심 − 손목 = ").Append((b.center - wrist.position).ToString("F3")).Append(" · 바깥쪽 성분 ").Append(Vector3.Dot(b.center - wrist.position, outw).ToString("F3"))
|
||
.Append(" m · 방패 크기 ").Append(b.size.ToString("F2")).Append(" · 손목 − 가슴 ").Append(Vector3.Distance(wrist.position, chest.position).ToString("F2"));
|
||
foreach (var r in rends) sb.Append("\n renderer ").Append(r.name).Append(" receive=").Append(r.receiveShadows).Append(" cast=").Append(r.shadowCastingMode).Append(" mat=").Append(r.sharedMaterial != null ? r.sharedMaterial.shader.name : "-");
|
||
return sb.ToString();
|
||
}
|
||
|
||
public static object ShieldOutward(float dist)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null || ch == root) return "방패 자식 없음";
|
||
var outw = Outward(pc); if (outw == Vector3.zero) return "뼈 없음";
|
||
var local = root.InverseTransformVector(outw * dist);
|
||
ch.localPosition = local;
|
||
return "자식 localPosition = (" + local.x.ToString("F3") + ", " + local.y.ToString("F3") + ", " + local.z.ToString("F3") + ") · 월드 " + dist + " m";
|
||
}
|
||
|
||
public static object ShieldLocal(float x, float y, float z)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null || ch == root) return "방패 자식 없음";
|
||
ch.localPosition = new Vector3(x, y, z);
|
||
return "자식 localPosition = " + ch.localPosition.ToString("F3");
|
||
}
|
||
|
||
/// <summary>자식 회전(로컬 오일러) + 「팔 바깥쪽」 dist(m) 위치를 한 번에(실행 중만). 돌려준 값 = JSON 에 넣을 로컬 위치 · 회전.</summary>
|
||
public static object ShieldPose(float ex, float ey, float ez, float dist)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null || ch == root) return "방패 자식 없음";
|
||
ch.localRotation = Quaternion.Euler(ex, ey, ez);
|
||
ch.localPosition = Vector3.zero;
|
||
// 회전 뒤 방패 중심이 손목보다 바깥쪽 dist 가 되게(중심 기준)
|
||
var wrist = Bone(pc, "Wrist_L"); var outw = Outward(pc);
|
||
var rends = ch.GetComponentsInChildren<Renderer>(true);
|
||
var b = rends[0].bounds; foreach (var r in rends) b.Encapsulate(r.bounds);
|
||
float now = Vector3.Dot(b.center - wrist.position, outw);
|
||
var local = root.InverseTransformVector(outw * (dist - now));
|
||
ch.localPosition = local;
|
||
return "euler(" + ex + "," + ey + "," + ez + ") 바깥 " + dist + " m(이전 " + now.ToString("F3") + ") · 로컬 위치 (" + local.x.ToString("F3") + ", " + local.y.ToString("F3") + ", " + local.z.ToString("F3") + ") · 장식면(-Z)·바깥쪽 " + Vector3.Dot(-ch.forward, outw).ToString("F2");
|
||
}
|
||
|
||
/// <summary>방패 재질(실행 중 인스턴스)의 어두운 면 색 · 그림자 받기를 바꾼다(파일 무수정).</summary>
|
||
public static object ShieldMat(float dark, int receive)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null) return "방패 없음";
|
||
var sb = new StringBuilder();
|
||
foreach (var r in ch.GetComponentsInChildren<Renderer>(true))
|
||
{
|
||
var m = r.material; // 인스턴스
|
||
if (m.HasProperty("_GoochDarkColor")) m.SetColor("_GoochDarkColor", new Color(dark, dark, dark, 1f));
|
||
if (m.HasProperty("_ReceiveShadows")) m.SetFloat("_ReceiveShadows", receive);
|
||
if (receive == 0) { m.DisableKeyword("_MK_RECEIVE_SHADOWS"); m.DisableKeyword("_RECEIVESHADOWS_ON"); } else { m.EnableKeyword("_MK_RECEIVE_SHADOWS"); m.EnableKeyword("_RECEIVESHADOWS_ON"); }
|
||
r.receiveShadows = receive != 0;
|
||
sb.Append(r.name).Append(" dark=").Append(dark).Append(" receive=").Append(receive).Append(' ');
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
public static object ShieldShadow(bool receive)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
Transform root; var ch = ShieldChild(pc, out root); if (ch == null) return "방패 없음";
|
||
int n = 0; foreach (var r in ch.GetComponentsInChildren<Renderer>(true)) { r.receiveShadows = receive; n++; }
|
||
return "receiveShadows=" + receive + " · " + n + "개";
|
||
}
|
||
|
||
public static object Q() { return WL.Combat.Weapon.WLSkillHotkey.Fire(); }
|
||
|
||
static readonly string[] States = { "shieldattack", "dashslash", "slashflurry_b1", "slashflurry_a1", "slashflurry_b2", "idle", "run", "dodge", "attack", "Attack2", "Attack3", "hit" };
|
||
|
||
/// <summary>Q 와 같은 함수로 주력 스킬 발동 → 0.1 s(게임 시간)마다 상태 · 방패 치기/연속 베기 타격 수를 파일에.</summary>
|
||
public static object QTimeline(string path, float seconds)
|
||
{
|
||
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
|
||
System.IO.File.WriteAllText(path, "Q → " + WL.Combat.Weapon.WLSkillHotkey.Fire() + "\n");
|
||
pc.StartCoroutine(Co(pc, path, seconds));
|
||
return "started";
|
||
}
|
||
|
||
static System.Collections.IEnumerator Co(PCActor pc, string path, float seconds)
|
||
{
|
||
int bash = 0, flurry = 0;
|
||
WL.Combat.Core.CombatHandler<WL.Combat.Core.HitConfirmedEvent> hh = (in WL.Combat.Core.HitConfirmedEvent e) =>
|
||
{
|
||
var s = e.projectile != null ? e.projectile.m_SkillListTableData : null; if (s == null) return;
|
||
if (s.n_SkillID == 703502) bash++; else if (s.n_SkillID == 703103) flurry++;
|
||
};
|
||
WL.Combat.Core.CombatEvents.HitConfirmed.Add(hh);
|
||
float t = 0f, next = 0f;
|
||
while (t < seconds && pc != null)
|
||
{
|
||
if (t >= next)
|
||
{
|
||
next += 0.1f;
|
||
var an = pc.m_animation; string st = "?";
|
||
var si = an.GetCurrentAnimatorStateInfo(0); foreach (var n in States) if (si.IsName(n)) { st = n; break; }
|
||
if (an.IsInTransition(0)) { var ni = an.GetNextAnimatorStateInfo(0); foreach (var n in States) if (ni.IsName(n)) { st += ">" + n; break; } }
|
||
System.IO.File.AppendAllText(path, "t=" + t.ToString("F2") + " anim=" + pc.Get_CurAnim() + " state=" + st + " 방패치기타격=" + bash + " 연속베기타격=" + flurry + "\n");
|
||
}
|
||
yield return null; t += Time.deltaTime;
|
||
}
|
||
WL.Combat.Core.CombatEvents.HitConfirmed.Remove(hh);
|
||
System.IO.File.AppendAllText(path, "END " + Chain() + " · dust " + WL.Combat.WLDoubleTapDodge.DustScaled + "\n");
|
||
}
|
||
|
||
public static object Chain()
|
||
{
|
||
return "chain 예약 " + WL.Combat.Weapon.WLSkillChain.Triggered + " 발동 " + WL.Combat.Weapon.WLSkillChain.Cast + " 실패 " + WL.Combat.Weapon.WLSkillChain.Failed + " · " + WL.Combat.Weapon.WLSkillChain.LastLog
|
||
+ " | hotkey " + WL.Combat.Weapon.WLSkillHotkey.Fired + "/" + WL.Combat.Weapon.WLSkillHotkey.Failed + " · " + WL.Combat.Weapon.WLSkillHotkey.LastLog
|
||
+ " | flurry " + WL.Combat.Weapon.WLSlashFlurry.Taken + " · vfx swings " + WL.Combat.Weapon.WLSkillVfx.Swings;
|
||
}
|
||
}
|