Project_WL/Assets/WL/Combat/Weapon/WLJumpSlam.cs

127 lines
6.3 KiB
C#
Raw Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLJumpSlam.cs — 점프 내려찍기 타이밍: 점프 중(최고점 무렵)에 내려찍기 동작으로 넘어가 착지 순간에 지면을 찍게 (#909 · 2026-10-17)
//
// PD 원문: 「망치 주력 스킬 사용시 점프 최고점에서 내려찍기 모션 상태로 지면을 찍도록 해줘. (지금은 점프가 끝난 후 지면에서 내려치는 모션이 나오고 있어서 어색해)」
// ■ 원본 = 점프 이동은 Actor.Co_JumpAttack(높이 1 · landAt 초) · 동작은 컨트롤러 jump(Armed-Jump-Flip) → 끝 무렵 jumpattack(Mace-Attack-R2 · SkillEvent = 타격)
// 으로 넘어가 내려치기 타격이 착지 뒤에 나왔다.
// ■ 처리 — 스킬 시작(CombatEvents.SkillCast)부터 slamAt 초에 slamState 로 바로 넘기고, 그 클립의 SkillEvent(타격) 시각이 정확히 landAt 초(착지)에 오도록
// 애니메이터 속도를 맞춘다(타격 이벤트 시각은 클립에서 읽음). 착지 뒤 속도 1 로 되돌림.
// ■ 값 = Resources/WL/WLJumpSlam.json (C45) · 끄기 = enabled 0 → 원본 동작 · 원본 수정 0줄
// 🔴 Debug.LogError 금지(원본 팝업).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
using WL.Combat.Core;
namespace WL.Combat.Weapon
{
[System.Serializable]
public class WLJumpSlamRule
{
public string skill = ""; // eEffect 이름(원본 스킬 클래스)
public string slamState = ""; // 내려찍기 상태 이름
public string slamClip = ""; // 그 상태의 클립 이름(타격 SkillEvent 시각을 읽는다)
public float slamAt = 0.3f; // 스킬 시작 뒤 내려찍기로 넘어가는 시각(초)
public float landAt = 0.8f; // 착지 시각(초) = 원본 Co_JumpAttack 지속 시간
public float crossFade = 0.08f; // 넘어가는 시간(초)
}
[System.Serializable] public class WLJumpSlamJson { public int enabled; public WLJumpSlamRule[] rules = new WLJumpSlamRule[0]; public string note = ""; }
public static class WLJumpSlam
{
public const string ResourcesPath = "WL/WLJumpSlam";
public static int Slams, Missing, Errors; public static string LastLog = "";
static WLJumpSlamJson s_cfg;
static WLJumpSlamJson Cfg
{
get
{
if (s_cfg != null) return s_cfg;
s_cfg = new WLJumpSlamJson();
var ta = Resources.Load<TextAsset>(ResourcesPath);
if (ta == null) { LastLog = "설정 없음 " + ResourcesPath; return s_cfg; }
try { JsonUtility.FromJsonOverwrite(ta.text, s_cfg); } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; }
return s_cfg;
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void Boot()
{
s_cfg = null;
CombatEvents.SkillCast.Remove(OnCast);
if (Cfg.enabled != 0) CombatEvents.SkillCast.Add(OnCast);
}
static void OnCast(in SkillCastEvent e)
{
try
{
var pc = e.actor as PCActor;
if (pc == null || !pc.IsMainPC() || e.skill == null || Cfg.rules == null) return;
string ef = e.skill.e_Skill.ToString();
foreach (var r in Cfg.rules)
{
if (r == null || r.skill != ef) continue;
var run = pc.GetComponent<WLJumpSlamRunner>(); if (run == null) run = pc.gameObject.AddComponent<WLJumpSlamRunner>();
run.Begin(pc, r);
return;
}
}
catch (System.Exception ex) { Errors++; LastLog = "시작 실패 — " + ex.Message; }
}
/// <summary>클립의 첫 SkillEvent 시각(초) · 없으면 -1.</summary>
internal static float ImpactTime(Animator an, string clipName)
{
if (an == null || an.runtimeAnimatorController == null || string.IsNullOrEmpty(clipName)) return -1f;
foreach (var c in an.runtimeAnimatorController.animationClips)
{
if (c == null || c.name != clipName) continue;
foreach (var ev in c.events) if (ev.functionName == "SkillEvent") return ev.time;
}
return -1f;
}
}
public sealed class WLJumpSlamRunner : MonoBehaviour
{
PCActor _pc; WLJumpSlamRule _r; float _t0, _impact; bool _on, _slammed;
public void Begin(PCActor pc, WLJumpSlamRule r)
{
_pc = pc; _r = r; _t0 = Time.time; _slammed = false;
_impact = WLJumpSlam.ImpactTime(pc.m_animation, r.slamClip);
_on = _impact > 0f;
if (!_on) { WLJumpSlam.Missing++; WLJumpSlam.LastLog = "타격 이벤트 없음 " + r.slamClip; }
}
void Update()
{
if (!_on) return;
var an = _pc != null ? _pc.m_animation : null;
if (an == null || _pc.IsDead() || _pc.Get_CurAnim() != eAnim.Skill) { End(an); return; }
float t = Time.time - _t0;
if (!_slammed && t >= _r.slamAt)
{
_slammed = true;
float remain = Mathf.Max(0.05f, _r.landAt - t);
an.CrossFadeInFixedTime(_r.slamState, Mathf.Max(0f, _r.crossFade), 0, 0f);
an.speed = Mathf.Clamp(_impact / remain, 0.3f, 3f);
WLJumpSlam.Slams++;
WLJumpSlam.LastLog = "내려찍기 " + _r.slamState + " · " + t.ToString("0.00") + " s · 속도 " + an.speed.ToString("0.00") + " · 타격 예정 " + _r.landAt.ToString("0.00") + " s";
}
if (_slammed && t >= _r.landAt + 0.05f) End(an);
}
void End(Animator an)
{
if (_on && _slammed && an != null) an.speed = 1f;
_on = false;
}
void OnDisable() { _on = false; }
}
}