2026-10-18 18:48:42 +00:00
// ─────────────────────────────────────────────────────────────────────────────
// WLShadowSlash.cs — 단검(비수 1043 · Dagger 10202) 주력 「그림자 베기」(Skill_ShadowSlash) — 시전 중 캐릭터 보이기 · 이펙트 크기 배율
/ /
// PD 원문(#943-⑧ · 2026-10-19): 「단검 주력 스킬 시전 시 캐릭터의 모습이 보이도록 수정하고, 이펙트의 크기를 절반으로 줄여줘.」
/ /
// ■ 안 보이던 원인(코드 실측) — 원본 Skill_ShadowSlash.Set 이 shooter.Set_Invisible(2.1 초)를 부른다 → Actor.Co_Invisible 이
// 애니메이터 배속 0 · 무적 · Model_OnOff(false)(파티클 외 렌더러 전부 끔) · Weapon_OnOff(false) · 이동 금지 → 2.1 초 뒤 되돌린 다음 대상 뒤 1.5 m 로 순간이동.
// 이펙트가 덮거나 카메라 문제가 아니라 원본 스킬이 모델을 끈다.
// ■ 처리 — 원본 Set 의 Set_Invisible 뒤 훅 1줄(OnSet): Co_Invisible 이 끈 렌더러 · 무기를 바로 다시 켠다(원본이 2.1 초 뒤 부르는 같은 함수).
// freezeAnim 0 이면 애니메이터 배속도 1 로 되돌려 원본 상태 사슬 DualBlades_Jump_Start → JumpAttack1·2·3 → Jump_End → idle 이 보인다
// (JumpAttack1~3 클립 이벤트 0 개 · Jump_Start 의 SkillEvent 는 시각 0 하나 · 종료 전이 0.7 이라 다시 안 돈다 — 2026-10-19 컨트롤러 · FBX 메타 실측).
// 무적 · 이동 금지 · 2.1 초 뒤 대상 뒤 순간이동 · 판정은 원본 그대로.
// ■ 이펙트 — 베기(go_effect = Slash · 파티클 6 층 · 크기 2.6 ~ 7.7)와 연기(go_invisibleEffect = FX_DarkSkill_SmokeExplosion)의 Transform 을
// 「처음 크기 × 배율」로(풀 재사용 때 누적 없음 · 처음 크기는 WLShadowSlashBase 에 기억) · 파티클 크기 모드 Hierarchy
// (베기 팩 원본이 Local 이라 Transform 배율이 안 먹는다). 베기 판정 상자(BoxCollider 5.25 × 9 × 5.9)는 keepHitBox 1 이면 배율의 역수로 키워 월드 크기 그대로.
// ■ 값 = Resources/WL/WLShadowSlash.json (C45) · 끄기 = enabled 0(원본 그대로)
// 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업) — 실패는 LastLog
// ─────────────────────────────────────────────────────────────────────────────
using System.Reflection ;
using UnityEngine ;
namespace WL.Combat.Weapon
{
[System.Serializable]
public class WLShadowSlashJson
{
public int enabled = 0 ;
public int keepVisible = 1 ; // 1 = 시전 중 PC 렌더러 · 무기를 켠다(원본 = 2.1 초 숨김)
public int freezeAnim = 0 ; // 1 = 원본처럼 애니메이터 배속 0 유지(보이면 2.1 초 정지 자세) · 0 = 원본 상태 사슬 재생
public float slashScale = 1f ; // 베기(go_effect) 보이는 크기 배율
public float smokeScale = 1f ; // 연기(go_invisibleEffect) 크기 배율
public int keepHitBox = 1 ; // 1 = 베기 판정 상자 월드 크기 유지(보이는 크기만 바꿈)
2026-10-20 16:54:40 +00:00
// #954 PD 「비수 주력 = Slash Effects FREE 의 녹색 난사(Multiple Slash 2)로」 — 원본 베기 렌더러를 끄고(판정 상자 그대로) WL 사본을 같은 자리·시각에 띄운다
public string fxPrefab = "" ; // Resources 경로(비우면 원본 베기 그대로)
public float fxScale = 1f , fxDelay = 0.5f , fxForward = 1.5f , fxYOffset = 0f , fxLife = 2.5f ; // 배율 · 시전 뒤 시각(원본 go_effect 0.5 s) · PC 정면 거리(원본 1.5) · 높이 · 수명
public int hideOriginalFx = 0 ; // 1 = 원본 베기(go_effect) 렌더러 끔
2026-10-18 18:48:42 +00:00
}
public static class WLShadowSlash
{
public const string ResourcesPath = "WL/WLShadowSlash" ;
public static int Casts , Shown , Scaled , Errors ;
public static string LastLog = "" ;
static WLShadowSlashJson s_cfg ;
static MethodInfo s_model , s_weapon ;
static WLShadowSlashJson Cfg
{
get
{
if ( s_cfg ! = null ) return s_cfg ;
s_cfg = new WLShadowSlashJson ( ) ;
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 ; Casts = Shown = Scaled = Errors = 0 ; LastLog = "" ; }
public static string Describe ( ) { return "casts " + Casts + " · shown " + Shown + " · scaled " + Scaled + " · err " + Errors + " · " + LastLog ; }
/// <summary>원본 Skill_ShadowSlash.Set 훅(Set_Invisible 바로 뒤 1줄) — 캐릭터 보이기 · 베기 · 연기 크기 배율.</summary>
public static void OnSet ( Skill_ShadowSlash skill )
{
try
{
var c = Cfg ;
if ( skill = = null | | c . enabled = = 0 ) return ;
Casts + + ;
var pd = skill . Get_ProjectTileData ( ) ;
var shooter = pd ! = null ? pd . shooter : null ;
if ( c . keepVisible ! = 0 & & ! DSUtil . CheckNull ( shooter ) )
{
bool model = Call ( ref s_model , "Model_OnOff" , shooter ) ;
bool weapon = Call ( ref s_weapon , "Weapon_OnOff" , shooter ) ;
if ( model & & weapon ) Shown + + ;
if ( c . freezeAnim = = 0 & & shooter . m_animation ! = null ) shooter . m_animation . speed = 1f ;
}
if ( skill . go_effect ! = null ) Scale ( skill . go_effect . transform , c . slashScale , c . keepHitBox ! = 0 ) ;
if ( skill . go_invisibleEffect ! = null ) Scale ( skill . go_invisibleEffect . transform , c . smokeScale , false ) ;
2026-10-20 16:54:40 +00:00
if ( ! string . IsNullOrEmpty ( c . fxPrefab ) & & ! DSUtil . CheckNull ( shooter ) ) // #954
{
if ( c . hideOriginalFx ! = 0 & & skill . go_effect ! = null ) foreach ( var rd in skill . go_effect . GetComponentsInChildren < Renderer > ( true ) ) rd . enabled = false ; // 풀 인스턴스라 한 번 끄면 유지
Runner . StartCoroutine ( CoFx ( shooter , c ) ) ;
}
2026-10-18 18:48:42 +00:00
LastLog = "cast " + Casts + " · shown " + Shown + " · scaled " + Scaled ;
}
catch ( System . Exception ex ) { Errors + + ; LastLog = "실패 — " + ex . Message ; }
}
2026-10-20 16:54:40 +00:00
public static int FxShown ;
static WLShadowSlashRunner s_runner ;
static WLShadowSlashRunner Runner { get { if ( s_runner = = null ) { var go = new GameObject ( "[WL954] ShadowSlashFx" ) ; go . hideFlags = HideFlags . HideAndDontSave ; Object . DontDestroyOnLoad ( go ) ; s_runner = go . AddComponent < WLShadowSlashRunner > ( ) ; } return s_runner ; } }
/// <summary>#954 원본 베기가 켜지는 시각(fxDelay)에 WL 사본(녹색 난사)을 PC 정면 fxForward 에 띄운다(PC 방향 · fxLife 뒤 파괴).</summary>
static System . Collections . IEnumerator CoFx ( Actor shooter , WLShadowSlashJson c )
{
if ( c . fxDelay > 0f ) yield return new WaitForSeconds ( c . fxDelay ) ;
if ( DSUtil . CheckNull ( shooter ) | | shooter . IsDead ( ) ) yield break ;
var p = Resources . Load < GameObject > ( c . fxPrefab ) ; if ( p = = null ) { LastLog = "fx 프리팹 없음 " + c . fxPrefab ; yield break ; }
var fwd = shooter . transform . forward ; fwd . y = 0f ; if ( fwd . sqrMagnitude < 1e-4f ) fwd = Vector3 . forward ; fwd . Normalize ( ) ;
var pos = shooter . Get_CenterPositionFoward ( c . fxForward ) + Vector3 . up * c . fxYOffset ;
var go = Object . Instantiate ( p , pos , Quaternion . LookRotation ( fwd , Vector3 . up ) ) ; go . transform . localScale = p . transform . localScale * c . fxScale ;
foreach ( var ps in go . GetComponentsInChildren < ParticleSystem > ( true ) ) { var m = ps . main ; m . scalingMode = ParticleSystemScalingMode . Hierarchy ; ps . Play ( true ) ; }
Object . Destroy ( go , Mathf . Max ( 0.5f , c . fxLife ) ) ; FxShown + + ; LastLog = "fx " + c . fxPrefab + " @" + pos . ToString ( "F2" ) + " × " + c . fxScale ;
}
2026-10-18 18:48:42 +00:00
/// <summary>Actor 의 protected 켜기 함수(Model_OnOff · Weapon_OnOff(bool))를 true 로 부른다 — 원본 Co_Invisible 끝과 같은 호출(가상 함수는 PCActor 쪽이 불린다).</summary>
static bool Call ( ref MethodInfo m , string name , Actor a )
{
if ( m = = null ) m = typeof ( Actor ) . GetMethod ( name , BindingFlags . Instance | BindingFlags . NonPublic | BindingFlags . Public , null , new [ ] { typeof ( bool ) } , null ) ;
if ( m = = null ) { LastLog = "함수 없음 " + name ; return false ; }
m . Invoke ( a , new object [ ] { true } ) ;
return true ;
}
/// <summary>Transform 을 처음 크기 × k 로 · 파티클 크기 모드 Hierarchy · 판정 상자는 keepBox 면 1/k 로(월드 크기 유지).</summary>
static void Scale ( Transform t , float k , bool keepBox )
{
if ( t = = null | | k < = 0f ) return ;
var b = t . GetComponent < WLShadowSlashBase > ( ) ;
if ( b = = null )
{
if ( Mathf . Approximately ( k , 1f ) ) return ; // 손댄 적 없고 배율 1 = 원본 그대로
b = t . gameObject . AddComponent < WLShadowSlashBase > ( ) ;
b . scale = t . localScale ;
var bc0 = t . GetComponent < BoxCollider > ( ) ;
if ( bc0 ! = null ) { b . hasBox = true ; b . size = bc0 . size ; b . center = bc0 . center ; }
foreach ( var ps in t . GetComponentsInChildren < ParticleSystem > ( true ) ) { var m = ps . main ; m . scalingMode = ParticleSystemScalingMode . Hierarchy ; }
}
t . localScale = b . scale * k ;
if ( b . hasBox )
{
var bc = t . GetComponent < BoxCollider > ( ) ;
if ( bc ! = null ) { float inv = keepBox ? 1f / k : 1f ; bc . size = b . size * inv ; bc . center = b . center * inv ; }
}
Scaled + + ;
}
}
/// <summary>#943-⑧ 이 Transform(원본 스킬 풀 인스턴스)의 처음 크기 · 판정 상자 — 배율 누적 방지.</summary>
public sealed class WLShadowSlashBase : MonoBehaviour { public Vector3 scale , size , center ; public bool hasBox ; }
2026-10-20 16:54:40 +00:00
/// <summary>#954 코루틴 숙주(씬 밖 · 숨김).</summary>
public sealed class WLShadowSlashRunner : MonoBehaviour { }
2026-10-18 18:48:42 +00:00
}