// WL882_Probe.cs — PD 지시 #882 무기 기술 이동 · 대검 오류 스모크(Play 중) // unity command run_script --file AgentScripts/WL882_Probe.cs --entry WL882_Probe.WSkill using System.Reflection; using System.Text; using UnityEngine; public static class WL882_Probe { static readonly BindingFlags BF = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; /// PC 직업 · 동작 · 기다리는 스킬 · 슬롯 0~1 스킬(직업 칸) · 지금 클립 · 무기 스킬 슬롯 로그 · 스킬 투사체 수 · 시전 수. public static object WSkill() { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var sb = new StringBuilder(); var arr = typeof(PCActor).GetField("arr_magicData", BF).GetValue(pc) as SkillListTableData[]; var cur = typeof(Actor).GetProperty("CurAnim", BF).GetValue(pc, null); var mid = typeof(Actor).GetField("m_ID", BF).GetValue(pc); var magic = typeof(Actor).GetField("m_Magic", BF).GetValue(pc); sb.Append("class=").Append(mid).Append(" curAnim=").Append(cur).Append(" m_Magic=").Append(magic != null ? "set" : "null"); if (arr != null) for (int i = 0; i < 2 && i < arr.Length; i++) sb.Append(" [").Append(i).Append("]=").Append(arr[i] != null ? arr[i].n_SkillID + "/c" + arr[i].n_SkillClassID : "-"); var an = pc.m_animation; if (an != null) { var ci = an.GetCurrentAnimatorClipInfo(0); sb.Append(" clip=").Append(ci.Length > 0 ? ci[0].clip.name : "?").Append(" ctrl=").Append(an.runtimeAnimatorController != null ? an.runtimeAnimatorController.name : "-"); } sb.Append("\n slots: ").Append(WL.Combat.Weapon.WLWeaponSkillSlots.LastLog).Append(" applied=").Append(WL.Combat.Weapon.WLWeaponSkillSlots.Applied).Append(" skipped=").Append(WL.Combat.Weapon.WLWeaponSkillSlots.Skipped); sb.Append("\n objs: shield=").Append(Count()).Append(" fatal=").Append(CountByName("Skill_FatalBlow")).Append(" dash=").Append(Count()).Append(" jump=").Append(CountByName("Skill_JumpAttack")); sb.Append(" | SkillCast=").Append(WL.Combat.Core.CombatEvents.SkillCast.Raised).Append(" HitConfirmed=").Append(WL.Combat.Core.CombatEvents.HitConfirmed.Raised); return sb.ToString(); } static int Count() where T : Object { return Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None).Length; } /// #883 비수 시너지(힘민지 단검 투척) 발사 수 · 마지막 · 단검 투사체 수(켜진 것/전체) · 파츠 스킬 요약. public static object HmInfo() { int on = 0; foreach (var d in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) on++; return "hm shots=" + WL.PartSkill.SkWeaponSyn.HmShots + " last=" + WL.PartSkill.SkWeaponSyn.HmLast + " daggers on=" + on + "/" + Count() + " | state: " + WL.PartSkill.WLPartSkillState.LastLog + " | runner: " + WL.PartSkill.WLPartSkillRunner.LastLog; } static int CountByName(string typeName) { var t = System.Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return -1; return Object.FindObjectsByType(t, FindObjectsInactive.Include, FindObjectsSortMode.None).Length; } /// 스킬 1번 칸을 지금 바로 쓴다(자동 시전 대기 없이 · 대상 필요). public static object CastSlot(int index) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var arr = typeof(PCActor).GetField("arr_magicData", BF).GetValue(pc) as SkillListTableData[]; if (arr == null || index < 0 || index >= arr.Length || arr[index] == null) return "슬롯 비어 있음 " + index; bool ok = pc.Use_Skill(arr[index], true, index); return "Use_Skill(" + arr[index].n_SkillID + ") = " + ok; } /// 캡처용 — 오류를 화면 팝업으로 띄우는 원본 장치(ErrorLogHookManager)를 잠시 끄고/켠다(오류는 콘솔에 그대로 남는다) + 떠 있는 팝업 닫기. public static object ErrHook(bool on) { var go = GameObject.Find("ErrorLogHookManager"); if (go != null) go.SetActive(on); int closed = 0; try { if (!on && Popup.Ins != null && Popup.Ins.gameObject.activeInHierarchy) { Popup.Ins.OnClick_OK(); closed++; } } catch { } return "ErrorLogHookManager " + (go != null ? (on ? "켬" : "끔") : "없음") + " · 팝업 닫음 " + closed; } /// 캡처용 — 힘민지 선택 스킬 · 표에 없는 스킬 효과를 PC 에 바로 띄운다(InGameInfo.Shoot_Effect · 스탯 · UI 반영 없음). public static object SelectFx(string effectName) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; eEffect e; if (!System.Enum.TryParse(effectName, out e)) return "eEffect 없음 " + effectName; var ptd = new ProjectileData { m_Skill = e, shooter = pc }; if (e == eEffect.Select_LeafFairy) ptd.eStartPos = eProjectileStartPos.ShooterCenter; try { InGameInfo.Ins.Shoot_Effect(ptd); } catch (System.Exception ex) { return "실패 " + e + " — " + ex.Message; } return "shoot " + e; } /// 캡처 사이 정리 — 이름이 prefix 로 시작하는 스킬 투사체를 끈다. public static object ProjClear(string prefix) { int n = 0; var off = typeof(ProjectileBase).GetMethod("Off", BindingFlags.Instance | BindingFlags.NonPublic); foreach (var pb in Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { var d = pb.Get_ProjectTileData(); if (d == null || !d.m_Skill.ToString().StartsWith(prefix)) continue; try { off.Invoke(pb, new object[] { true, false }); } catch { pb.gameObject.SetActive(false); } n++; } return "off " + n; } /// 캡처용 — 무기 그룹의 직업 값을 메모리(공격 표 사본)에서만 바꾼다 → 무기 직업 적용기가 다음 폴링에 PC 직업을 바꾼다(파일 무수정 · Play 끝나면 원래대로). public static object SetClass(int weaponId, int classId) { var steps = WL.Combat.Weapon.WLWeaponAttackTable.Steps(weaponId); if (steps == null) return "공격 표에 없음 " + weaponId; int before = steps[0].classId; foreach (var s in steps) s.classId = classId; return weaponId + " 직업 " + before + " → " + classId; } /// 쿨 · MP 무시 시험 시전(Use_Skill testskill) — 슬롯 index 의 스킬 또는 skillId(>0)의 표 스킬(직업 칸은 PC 직업으로 맞춘 복사본). public static object CastTest(int index, int skillId) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; SkillListTableData data; if (skillId > 0) { var src = table_skilllist.Ins.Get_Data_orNull(skillId); if (src == null) return "표에 없음 " + skillId; data = (SkillListTableData)typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(src, null); data.n_SkillClassID = (int)typeof(Actor).GetField("m_ID", BF).GetValue(pc); data.n_MP = 0; } else { var arr = typeof(PCActor).GetField("arr_magicData", BF).GetValue(pc) as SkillListTableData[]; data = arr != null && index >= 0 && index < arr.Length ? arr[index] : null; if (data == null) return "슬롯 비어 있음 " + index; } bool ok = pc.Use_Skill(data, true, index, true); return "Use_Skill(test " + data.n_SkillID + " " + data.e_Skill + " anim=" + data.s_Animation + ") = " + ok; } }