From a78642d73c7c74101e7416ab83014525a7190776 Mon Sep 17 00:00:00 2001 From: swrring Date: Thu, 22 Oct 2026 19:39:16 +0900 Subject: [PATCH] =?UTF-8?q?WL=20#970=20PD=20=EC=A7=80=EC=8B=9C=203?= =?UTF-8?q?=EA=B1=B4=20=E2=80=94=20=E2=91=A0=20=EC=8B=9C=EC=A0=84=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=EC=9D=B4=20=EC=9E=88=EB=8A=94=20=EC=8A=A4?= =?UTF-8?q?=ED=82=AC(=EC=8A=A4=ED=82=AC=20=ED=91=9C=20s=5FNextAnimation=20?= =?UTF-8?q?=C2=B7=20f=5FCastingTime)=20=3D=20=EB=B0=9C=EB=8F=99(SkillFired?= =?UTF-8?q?)=EA=B9=8C=EC=A7=80=20=EC=9D=B4=EB=8F=99=20=C2=B7=20=EA=B5=AC?= =?UTF-8?q?=EB=A5=B4=EA=B8=B0=20=EC=9E=A0=EA=B8=88=20+=20=EB=B0=9C?= =?UTF-8?q?=EB=8F=99=20=EB=92=A4=20=EC=9D=B4=EB=8F=99=ED=95=B4=EB=8F=84=20?= =?UTF-8?q?=EC=8A=A4=ED=82=B5=20=EC=95=88=20=ED=95=A8(WLSkillCancel=20cast?= =?UTF-8?q?Lock=20=C2=B7=20=EC=9E=A0=EA=B8=88=20=EC=83=81=ED=95=9C=20=3D?= =?UTF-8?q?=20holdMaxSeconds=20+=20=EC=8B=9C=EC=A0=84=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=20=C2=B7=20=EC=8B=A4=EC=B8=A1=20=EA=B3=A8=EB=A0=98=20=C2=B7=20?= =?UTF-8?q?=EA=BD=83=20=C2=B7=20=EC=9A=B4=EC=84=9D=20=C2=B7=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=ED=99=80=20=EB=B0=9C=EB=8F=99=20=EC=A0=84=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EB=A7=89=EC=9D=8C=2057=20=C2=B7=2057=20=C2=B7=2080?= =?UTF-8?q?=20=C2=B7=2077=20=C2=B7=20=EC=8A=A4=ED=82=B5=200=20=C2=B7=20?= =?UTF-8?q?=EC=9A=B4=EC=84=9D=20=C2=B7=20=EB=B8=94=EB=9E=99=ED=99=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EB=92=A4=202.40=20=C2=B7=202.61=20s=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80)=20=E2=91=A1=20=EB=B9=84=EC=88=98=20?= =?UTF-8?q?=EC=A3=BC=EB=A0=A5=20=EC=A4=91=20=EC=A0=81=20=EC=88=9C=EA=B0=84?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=3D=20=EC=9B=90=EB=B3=B8=20=EB=81=9D=20Set?= =?UTF-8?q?=5FWarp(PC=20=EB=8C=80=EC=83=81=20=EB=92=A4=201.5=20m=20=C2=B7?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20=EC=86=8D=20=EC=A0=81=200.32=20?= =?UTF-8?q?=ED=8A=90)=20+=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=94=AC=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=20=EC=9E=90=EB=8F=99=20=EB=B3=B5=EA=B7=80(?= =?UTF-8?q?=ED=8C=90=EC=A0=95=EC=9D=B4=20=EB=8D=94=EB=AF=B8=EB=A5=BC=202.4?= =?UTF-8?q?~3.6=20m=20=EB=B0=80=EC=96=B4=202.00=20=C2=B7=203.55=20m=20?= =?UTF-8?q?=EC=88=9C=EA=B0=84=EC=9D=B4=EB=8F=99)=20=E2=86=92=20WLShadowSla?= =?UTF-8?q?sh=20endWarp=200(=EC=9B=90=EB=B3=B8=20=ED=9B=85=20=C2=B7=20?= =?UTF-8?q?=EA=B7=B8=EB=95=8C=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20=EB=8F=8C?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=EB=8F=84=20=EC=83=9D=EB=9E=B5)=20+=20WLSkill?= =?UTF-8?q?Test=20autoResetPauseInSkill=201=20=C2=B7=20=EC=8B=A4=EC=B8=A1?= =?UTF-8?q?=202=ED=9A=8C=206=20s=20=ED=8A=90=200=20=E2=91=A2=20=EB=A7=88?= =?UTF-8?q?=EB=B2=95=EC=B1=85=20=EA=B8=B0=EB=B3=B8=20=EA=B3=B5=EA=B2=A9=20?= =?UTF-8?q?=EA=B2=80=EC=9D=80=20=EA=BC=AC=EB=A6=AC=20=E2=86=92=20=EB=B0=9D?= =?UTF-8?q?=EC=9D=80=20=EB=B3=B4=EB=9D=BC(WL=5FFx=5FShootCard=20helix=5Fda?= =?UTF-8?q?rk=20=EC=8B=9C=EC=9E=91=EC=83=89=200.08,0.00,0.30=20=E2=86=92?= =?UTF-8?q?=200.60,0.38,1.00=20=C2=B7=20=EC=88=98=EB=AA=85=20=EB=81=9D=20?= =?UTF-8?q?=EA=B2=80=EC=A0=95=20=E2=86=92=200.70=20=C2=B7=20=EB=AA=85?= =?UTF-8?q?=EC=A4=91=20WL=5FFx=5FShootCardHit=20smoke=20=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EB=81=9D=20=EA=B2=80=EC=A0=95=20=E2=86=92=200.70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../Projectile/Skill/Skill_ShadowSlash.cs | 9 +++-- .../Resources/WL/Fx/WL_Fx_ShootCard.prefab | 4 +- .../Resources/WL/Fx/WL_Fx_ShootCardHit.prefab | 8 ++-- .../Weapon/Resources/WL/WLShadowSlash.json | 2 + .../Weapon/Resources/WL/WLSkillCancel.json | 4 +- Assets/WL/Combat/Weapon/WLShadowSlash.cs | 11 ++++++ Assets/WL/Combat/Weapon/WLSkillCancel.cs | 38 +++++++++++++------ .../SkillTest/Resources/WL/WLSkillTest.json | 2 + Assets/WL/SkillTest/WLSkillTestHarness.cs | 4 +- 9 files changed, 58 insertions(+), 24 deletions(-) diff --git a/Assets/Script/Character/Projectile/Skill/Skill_ShadowSlash.cs b/Assets/Script/Character/Projectile/Skill/Skill_ShadowSlash.cs index 740ea75d4..0fd86fe22 100644 --- a/Assets/Script/Character/Projectile/Skill/Skill_ShadowSlash.cs +++ b/Assets/Script/Character/Projectile/Skill/Skill_ShadowSlash.cs @@ -22,10 +22,11 @@ public class Skill_ShadowSlash : ProjectileBase if (WL.Combat.Weapon.WLShadowSlash.SkipEnd(_data.shooter)) return; // WL #961 구르기로 캔슬된 시전 — 끝 대기 · 대상 뒤 순간이동 · 연기 · 카메라 돌리기 생략 _data.shooter.Play_Idle(); - if (!DSUtil.CheckNull(_data.target)) + bool warp = WL.Combat.Weapon.WLShadowSlash.EndWarp(); // WL #970 끝 순간이동 끔(WLShadowSlash.json endWarp 0) — 화면에서 적이 반대편으로 튀던 원인 + if (!DSUtil.CheckNull(_data.target) && warp) _data.shooter.Set_Warp(_data.target.Get_PositionBack(1.5f)); - Show_InvisibleEffect(); + Show_InvisibleEffect(warp); }); WL.Combat.Weapon.WLShadowSlash.OnSet(this); // WL #943-⑧ 훅(1줄) — 숨김 직후 렌더러 · 무기 다시 켬 + 베기 · 연기 크기 배율(WLShadowSlash.json) · 무적 · 2.1 초 뒤 순간이동은 원본 그대로 @@ -33,13 +34,13 @@ public class Skill_ShadowSlash : ProjectileBase StartCoroutine(Co_Off(4f)); } - void Show_InvisibleEffect() + void Show_InvisibleEffect(bool turnCam = true) // WL #970 turnCam false = 끝 순간이동을 끈 때 카메라 돌리기도 생략 { go_invisibleEffect.SetActive(false); go_invisibleEffect.SetActive(true); go_invisibleEffect.transform.position = m_ProjecTileData.shooter.Get_position(); - if (MyValue.m_RealCamera != null) MyValue.m_RealCamera.RotateCam_TargetBack(); // WL #881 섬(메인 카메라 = FarmingIsland · RealCamera 없음)에서 NRE → 카메라 연출만 건너뜀 + if (turnCam && MyValue.m_RealCamera != null) MyValue.m_RealCamera.RotateCam_TargetBack(); // WL #881 섬(메인 카메라 = FarmingIsland · RealCamera 없음)에서 NRE → 카메라 연출만 건너뜀 } IEnumerator Co_Update() diff --git a/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCard.prefab b/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCard.prefab index 37424c559..c3fa6a04a 100644 --- a/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCard.prefab +++ b/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCard.prefab @@ -10056,7 +10056,7 @@ ParticleSystem: serializedVersion: 2 minMaxState: 0 minColor: {r: 1, g: 1, b: 1, a: 1} - maxColor: {r: 0.0808081, g: 0, b: 0.3018868, a: 1} + maxColor: {r: 0.6, g: 0.38, b: 1, a: 1} maxGradient: serializedVersion: 2 key0: {r: 1, g: 1, b: 1, a: 1} @@ -11209,7 +11209,7 @@ ParticleSystem: maxGradient: serializedVersion: 2 key0: {r: 1, g: 1, b: 1, a: 1} - key1: {r: 0, g: 0, b: 0, a: 1} + key1: {r: 0.7, g: 0.7, b: 0.7, a: 1} key2: {r: 0, g: 0, b: 0, a: 0} key3: {r: 0, g: 0, b: 0, a: 0} key4: {r: 0, g: 0, b: 0, a: 0} diff --git a/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCardHit.prefab b/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCardHit.prefab index a4e6014fc..e0d7af77b 100644 --- a/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCardHit.prefab +++ b/Assets/WL/Combat/Weapon/Resources/WL/Fx/WL_Fx_ShootCardHit.prefab @@ -20383,7 +20383,7 @@ ParticleSystem: serializedVersion: 2 minMaxState: 0 minColor: {r: 1, g: 1, b: 1, a: 1} - maxColor: {r: 0.8008504, g: 0.5803922, b: 1, a: 1} + maxColor: {r: 0.8, g: 0.58, b: 1, a: 1} maxGradient: serializedVersion: 2 key0: {r: 1, g: 1, b: 1, a: 1} @@ -21536,8 +21536,8 @@ ParticleSystem: maxGradient: serializedVersion: 2 key0: {r: 1, g: 1, b: 1, a: 0.29411766} - key1: {r: 0, g: 0, b: 0, a: 1} - key2: {r: 1, g: 0.23016784, b: 0, a: 0} + key1: {r: 0.7, g: 0.7, b: 0.7, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} key3: {r: 0, g: 0, b: 0, a: 0} key4: {r: 0, g: 0, b: 0, a: 0} key5: {r: 0, g: 0, b: 0, a: 0} @@ -21545,7 +21545,7 @@ ParticleSystem: key7: {r: 0, g: 0, b: 0, a: 0} ctime0: 9830 ctime1: 32768 - ctime2: 52428 + ctime2: 0 ctime3: 0 ctime4: 0 ctime5: 0 diff --git a/Assets/WL/Combat/Weapon/Resources/WL/WLShadowSlash.json b/Assets/WL/Combat/Weapon/Resources/WL/WLShadowSlash.json index c15525143..94d885f90 100644 --- a/Assets/WL/Combat/Weapon/Resources/WL/WLShadowSlash.json +++ b/Assets/WL/Combat/Weapon/Resources/WL/WLShadowSlash.json @@ -30,6 +30,8 @@ "slashMatchFx": 1, "invisibleMatch": 1, "slashHitEven": 1, + "endWarp": 0, + "_note_970": "#970 PD 「비수 주력 스킬 사용 시 적이 갑자기 순간이동 하는 현상아 있는데 왜 그런건지 살펴보고 순간이동하지 못하게 해줘」 — 원인 = 적이 아니라 PC 가 움직임: 원본 끝 콜백(Set_Invisible 끝)이 PC 를 대상 뒤 1.5 m(Get_PositionBack)로 Set_Warp → 카메라가 PC 를 따라가 화면에서는 적이 PC 반대편으로 튄 것처럼 보임(Play 캡처 970_dg_a → b · 더미가 PC 오른쪽 → 위·왼쪽) → endWarp 0 = 끝 순간이동 · 그때의 카메라 돌리기 생략(원본 훅 1줄 · PC 는 난사한 자리 그대로)", "_note_962": "#962 PD 「비수 주력 사용 시 이펙트가 두 번 연속 재생되는 것 같다 → 이펙트는 한 번만 재생하고 모션을 이펙트에 맞춰」 · 「주력 사용 시 판정이 발생되지 않는데 왜 그런지 체크 · 수정」 — 이펙트 = 정면 사본 1개만(fxRepeatInterval 0 · 이전 = 0.5 s 부터 0.2 s 마다 6개가 겹쳐 같은 6연속 초승달이 되풀이) · 시전과 동시에(fxDelay 0) · 난사 시간 = 사본 1개가 보이는 시간(slashMatchFx · 입자 분출 0~0.75 s + 수명 0.3 = 1.05 s) · 휘두름 = 초승달 간격 0.15 s 마다(배속 2.1 = 휘두름마다 클립 0.32 s · #960 과 같은 길이) · 원본 숨김 2.1 s(이동 금지 · 무적 · 끝 대기 · 대상 뒤 순간이동)도 1.05 s 로(invisibleMatch · 훅 1줄) · 판정 10회를 1.05 s 에 고르게(slashHitEven) · 판정 진단(Play 실측 · 962_probe Q): 판정은 10/10 나지만 ① 비수 스킬 타격은 적 몸에 뜨는 피격 이펙트 칸이 없고 ② 맞을 때마다 더미가 약 0.25 m 씩 밀려 2 s 동안 1.0 → 3.4 m(이펙트 밖) ③ 4.0 m 를 넘으면 실제로 빗나감(4.5 m 조건 6/10) → 피격 이펙트 WLImpactFx skillHitFx(10202 · Skill_ShadowSlash) + 넉백 끔(WLSkillHitFeel) + 판정 반경 3.5 → 4.0 m(+ 몸 반지름 ≈ 원본 판정 상자 앞 4.4 m)", "_note_961": "#961 PD 「주력 중 캐릭터가 움직이면 이펙트만 그 자리에 남음 → 못 움직이게 · 구르기로 캔슬하면 이펙트도 같이 소멸」 · 「이펙트 소멸 타이밍에 맞게 모션도 종료 · 최소 0.5 s 딜레이 후 공격」 — 이동 = 원본이 PCMove 를 끄지만 하네스 WASD · 가상패드가 Run_byJoystick 으로 움직였음 → moveHold(WLSkillCancel.HoldMove 2.0 s · 구르기는 됨) · 구르기 = 난사 · 반복 · 잔상 · 판정 멈춤 + 살아 있는 정면 사본 지움 · 정면 사본 반복을 0.3 s(초승달 보이는 시간) 일찍 끝내 마지막 이펙트가 난사 끝 2.0 s 에 함께 사라짐 · 끝난 뒤 0.5 s 공격 금지(IsStop 매 프레임 · 하네스 공격 키 IsHolding) · endIdle = 난사 끝(마지막 이펙트 소멸 2.0 s)에 바로 대기 동작(원본은 2.1 s 콜백에서 대기 · 그사이 마지막 휘두름이 이어짐) · 구르기 캔슬이면 원본 2.1 s 끝 처리(대기 · 대상 뒤 순간이동 · 연기 · 카메라 돌리기)를 건너뜀(Skill_ShadowSlash 훅 1줄 SkipEnd) · Play 실측(961_verify): ⓐ 이동 입력은 막혔는데(WASD 막힘 72 · 패드 0 m) 시전 중 0.65~1.06 m 전진 = 강제 재생하는 1·2타 클립의 공격 전진 표(AttackRootMotion) → 난사 중 Gate 차단(IsSlashing) ⓑ 사본 1개 = 초승달 6개를 0~0.75 s 에 나눠 뿜어 약 1.05 s 보임(fxVisibleLife 0.3 가정이 틀려 입자가 2.74 s 까지 남음) → 반복을 멈추는 1.7 s 에 살아 있는 사본의 분출도 멈춤(이미 나온 초승달 수명 0.3 s 만) = 2.0 s 에 함께 소멸", "slashHits": 10, diff --git a/Assets/WL/Combat/Weapon/Resources/WL/WLSkillCancel.json b/Assets/WL/Combat/Weapon/Resources/WL/WLSkillCancel.json index f84d93f97..350610de5 100644 --- a/Assets/WL/Combat/Weapon/Resources/WL/WLSkillCancel.json +++ b/Assets/WL/Combat/Weapon/Resources/WL/WLSkillCancel.json @@ -26,5 +26,7 @@ "Skill_DashSlash" ], "holdUntilFire": 1, - "holdMaxSeconds": 1.5 + "holdMaxSeconds": 1.5, + "_note_970": "#970 PD 「시전하는 모션이 존재하는 스킬은 일정 타이밍(실제 스킬 발동이 확정 된 시점)까지 못움직이게 하고, 움직여 진 경우에는 발동 된 스킬이 스킵되지 않도록 (현재는 시전 직후 움직이면 스킬 이펙트가 스킵되는 현상)」 — castLock 1 = SkillList s_NextAnimation(원본 시전 단계) 또는 f_CastingTime 이 있는 스킬은 lockUntilFire + noSkip 으로(위 목록은 표에 시전 단계가 없는 Skill_ArrowRain 등 그대로) · 잠금 상한 = holdMaxSeconds + 시전 시간", + "castLock": 1 } diff --git a/Assets/WL/Combat/Weapon/WLShadowSlash.cs b/Assets/WL/Combat/Weapon/WLShadowSlash.cs index 8219fd0de..f449f7529 100644 --- a/Assets/WL/Combat/Weapon/WLShadowSlash.cs +++ b/Assets/WL/Combat/Weapon/WLShadowSlash.cs @@ -71,6 +71,9 @@ namespace WL.Combat.Weapon // slashHitEven 1 = 판정 slashHits 회를 난사 시간에 고르게(휘두름 수와 무관 · 그전엔 휘두름마다 1회 + 모자란 만큼 끝에 몰아서) public int slashMatchFx = 0, invisibleMatch = 0, slashHitEven = 0; public int endIdle = 0; // #961 1 = 난사가 끝나는 순간(= 마지막 이펙트 소멸) 대기 동작으로(원본은 0.1 s 뒤 2.1 s 콜백에서 대기 · 그사이 마지막 휘두름이 이어졌다) + // #970 PD 「비수 주력 스킬 사용 시 적이 갑자기 순간이동 하는 현상 … 순간이동하지 못하게」 — 원인 = 원본 끝 콜백이 PC 를 대상 뒤 1.5 m 로 옮겨(Set_Warp) 카메라가 따라가는 화면에서 적이 반대편으로 튐 · + // 0 = 끝 순간이동과 그때의 카메라 돌리기(RotateCam_TargetBack)를 건너뛴다(PC 는 난사한 자리 그대로) · 1 = 원본 + public int endWarp = 1; } public static class WLShadowSlash @@ -353,6 +356,14 @@ namespace WL.Combat.Weapon if (Cfg.dodgeCancel == 0 || s_castSeq <= 0 || s_cancelSeq != s_castSeq || !ReferenceEquals(shooter, s_castShooter)) return false; EndSkips++; return true; } + public static int EndWarps, EndWarpSkips; + /// #970 원본 Skill_ShadowSlash 끝 콜백 훅 — false = 대상 뒤 순간이동 · 그때의 카메라 돌리기를 건너뜀(endWarp 0). + public static bool EndWarp() + { + var c = Cfg; + if (c.enabled == 0 || c.endWarp != 0) { EndWarps++; return true; } + EndWarpSkips++; return false; + } static PropertyInfo s_isStop; public static bool IsHolding(Actor a) { return a != null && ReferenceEquals(a, s_holdActor) && Time.time < s_holdUntil; } static void SetStop(Actor a, bool v) diff --git a/Assets/WL/Combat/Weapon/WLSkillCancel.cs b/Assets/WL/Combat/Weapon/WLSkillCancel.cs index a3d90fb58..63b7f795b 100644 --- a/Assets/WL/Combat/Weapon/WLSkillCancel.cs +++ b/Assets/WL/Combat/Weapon/WLSkillCancel.cs @@ -14,6 +14,7 @@ // 무시한 동안 들어온 PCActor.Set_AutoBraking(누름 false · 뗌 true)은 미뤘다가 잠금이 풀릴 때 마지막 값을 적용한다. // ④ #912 HoldMove — 파츠 스킬이 정한 시간 동안 이동 입력 무시(해머 각성 「내려친 상태로 잠시 멈추게」 · 구르기는 허용 = 스킵) · 그동안의 뗌은 EndHoldMove 에서 적용. // ⑤ #914 endWithMotion — 스킬 동작이 입력 없이 끝나면(대기 등) 그 스킬 이펙트(투사체 · 연출)도 바로 끈다(PD 「검 각성 … 연속 공격 모션을 끝나는 시점에 맞춰서 이펙트도 소멸」). +// ⑥ #970 castLock — 시전 동작이 있는 스킬(표 s_NextAnimation · f_CastingTime)은 목록 없이 발동(SkillFired)까지 이동 · 구르기 무시 + 발동 뒤 이동해도 스킵 안 함. // ■ 원본 훅 = PCActor.Run_byJoystick · Set_AutoBraking 첫 줄 각 1줄 · 값 = Resources/WL/WLSkillCancel.json (C45) · 끄기 = enabled 0 // 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업). // ───────────────────────────────────────────────────────────────────────────── @@ -35,21 +36,24 @@ namespace WL.Combat.Weapon public string[] endWithMotion = new string[0]; // #914 스킬 동작이 입력 없이 끝나면(대기로) 그 스킬 이펙트도 바로 끈다 — 남은 이펙트 때문에 서 있지 않게 public string[] lockUntilFire = new string[0]; // #942 시전 뒤 첫 발동(SkillFired)까지 이동 · 구르기 입력 무시(holdMaxSeconds 상한) — 발동 뒤엔 자유(투사체는 noSkip 으로 유지) · PD 「플레이어가 이동해도 발동 된 스킬이 끊겨선 안돼」(실측: 이동이 발동 이벤트 전에 시전 동작을 끊어 투사체가 아예 안 나왔다) public int holdUntilFire = 1; // 1 = 이동 키를 누른 채 쓴 스킬은 첫 발동까지 그 입력을 무시 - public float holdMaxSeconds = 1.5f; // ① 의 상한(발동 이벤트가 없는 스킬 대비) + public float holdMaxSeconds = 1.5f; // ① 의 상한(발동 이벤트가 없는 스킬 대비) · 시전 시간(f_CastingTime)이 있는 스킬은 상한 = 이 값 + 시전 시간 + // #970 PD 「시전하는 모션이 존재하는 스킬은 일정 타이밍(실제 스킬 발동이 확정 된 시점)까지 못움직이게 하고, 움직여 진 경우에는 발동 된 스킬이 스킵되지 않도록」 — + // 1 = 시전 동작이 있는 스킬(SkillList s_NextAnimation 있음 = 원본 Co_Casting 시전 단계 · 또는 f_CastingTime > 0)은 이름 목록 없이 lockUntilFire + noSkip 으로 취급(발동 = SkillFired · 원본 SkillEvent 의 Shoot_Skill 직전) + public int castLock = 0; public string note = ""; } public static class WLSkillCancel { public const string ResourcesPath = "WL/WLSkillCancel"; - public static int Casts, Skips, SkippedObjects, MoveBlocked, DodgeBlocked, BrakeDeferred, BrakeApplied, Errors, MotionEnds; + public static int Casts, Skips, SkippedObjects, MoveBlocked, DodgeBlocked, BrakeDeferred, BrakeApplied, Errors, MotionEnds, CastLocks, SkipsAvoided; public static string LastLog = ""; static WLSkillCancelJson s_cfg; static PCActor s_pc; static string s_effect = ""; - static float s_castAt; - static bool s_active, s_fired, s_heldAtCast, s_lock, s_hold, s_lockFire; // s_lockFire = #942 발동까지 잠금 + static float s_castAt, s_cap = 1.5f; // s_cap = 발동까지 잠금 상한(holdMaxSeconds + 시전 시간 · #970) + static bool s_active, s_fired, s_heldAtCast, s_lock, s_hold, s_lockFire, s_noSkip; // s_lockFire = #942 발동까지 잠금 · s_noSkip = 이동해도 이펙트 유지(목록 또는 #970 시전 동작) static int s_pendingBrake = -1; // -1 없음 · 0 = Set_AutoBraking(false) · 1 = Set_AutoBraking(true) static GameObject s_runner; static FieldInfo s_fDicSkill; @@ -103,9 +107,18 @@ namespace WL.Combat.Weapon s_heldAtCast = pc.JoystickStatus == 1; s_lock = Has(c.lockMove, s_effect); s_hold = Has(c.holdMove, s_effect); - s_lockFire = Has(c.lockUntilFire, s_effect); - Casts++; - LastLog = "시작 " + s_effect + (s_lock ? " · 이동 잠금" : "") + (s_lockFire ? " · 발동까지 잠금" : "") + (s_heldAtCast ? " · 이동 키 누른 채" : ""); + bool castMotion = c.castLock != 0 && HasCastMotion(e.skill); // #970 시전 동작이 있는 스킬 = 발동까지 잠금 + 발동 뒤 이동해도 유지 + s_lockFire = Has(c.lockUntilFire, s_effect) || castMotion; + s_noSkip = Has(c.noSkip, s_effect) || castMotion; + s_cap = c.holdMaxSeconds + (e.skill != null ? Mathf.Max(0f, e.skill.f_CastingTime) : 0f); + Casts++; if (castMotion) CastLocks++; + LastLog = "시작 " + s_effect + (s_lock ? " · 이동 잠금" : "") + (s_lockFire ? " · 발동까지 잠금(상한 " + s_cap.ToString("F1") + " s)" : "") + (castMotion ? " · 시전 동작" : "") + (s_heldAtCast ? " · 이동 키 누른 채" : ""); + } + + /// #970 시전 동작이 있는 스킬 — 원본 Actor.Play_Skill 이 Co_Casting(시전 → s_NextAnimation)을 도는 스킬(s_NextAnimation 있음) 또는 시전 시간이 있는 스킬. + static bool HasCastMotion(SkillListTableData s) + { + return s != null && (!string.IsNullOrEmpty(s.s_NextAnimation) || s.f_CastingTime > 0f); } static void OnFired(in SkillFiredEvent e) { if (s_active && ReferenceEquals(e.actor, s_pc)) s_fired = true; } @@ -114,7 +127,7 @@ namespace WL.Combat.Weapon public static void ResetFor(PCActor pc) { if (pc == null) return; - if (ReferenceEquals(pc, s_pc)) { s_active = false; s_lock = s_hold = s_lockFire = s_heldAtCast = false; s_pendingBrake = -1; } + if (ReferenceEquals(pc, s_pc)) { s_active = false; s_lock = s_hold = s_lockFire = s_noSkip = s_heldAtCast = false; s_pendingBrake = -1; } if (ReferenceEquals(pc, s_extPc)) { s_extPc = null; s_extPending = false; } LastLog = "초기화(직업 전환) " + s_effect; } @@ -152,8 +165,8 @@ namespace WL.Combat.Weapon if (!s_active || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false; if (s_lock) return true; var c = Cfg; - if (s_lockFire && !s_fired && Time.time - s_castAt < c.holdMaxSeconds) return true; // #942 발동 전엔 새 이동 입력도 무시 - return c.holdUntilFire != 0 && s_heldAtCast && !s_fired && Time.time - s_castAt < c.holdMaxSeconds; + if (s_lockFire && !s_fired && Time.time - s_castAt < s_cap) return true; // #942 발동 전엔 새 이동 입력도 무시 + return c.holdUntilFire != 0 && s_heldAtCast && !s_fired && Time.time - s_castAt < s_cap; } /// PCActor.Run_byJoystick 첫 줄 훅 — true = 이 이동 입력을 무시한다. @@ -168,7 +181,7 @@ namespace WL.Combat.Weapon public static bool BlockDodge(PCActor pc) { if (pc == null || !s_active || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false; - if (!s_lock && !(s_lockFire && !s_fired && Time.time - s_castAt < Cfg.holdMaxSeconds)) return false; // #942 발동 전 구르기도 무시 + if (!s_lock && !(s_lockFire && !s_fired && Time.time - s_castAt < s_cap)) return false; // #942 발동 전 구르기도 무시 DodgeBlocked++; return true; } @@ -222,7 +235,8 @@ namespace WL.Combat.Weapon // 스킬 상태에서 바로 달리기 · 구르기 = 입력으로 끊김(자연 종료는 Event_Idle → 대기). 원본 구르기는 같은 프레임에 달리기로 넘어가 Dodge 를 못 볼 때가 있다(실측) bool byInput = cur == eAnim.Run || cur == eAnim.Dodge; var c = Cfg; - if (byInput && c.skipOnMove != 0 && !Has(c.noSkip, s_effect)) Skip(pc, s_effect, cur == eAnim.Dodge ? "구르기" : "이동"); + if (byInput && c.skipOnMove != 0 && !s_noSkip) Skip(pc, s_effect, cur == eAnim.Dodge ? "구르기" : "이동"); + else if (byInput && s_noSkip) { SkipsAvoided++; LastLog = "유지 " + s_effect + " · " + (cur == eAnim.Dodge ? "구르기" : "이동") + (s_fired ? " · 발동 뒤" : " · 발동 전"); } else if (!byInput && Has(c.endWithMotion, s_effect)) { MotionEnds++; Skip(pc, s_effect, "동작 끝"); } // #914 동작 끝 = 이펙트 끝(이동 잠금 holdMove 도 함께 풀림) if (s_pendingBrake >= 0 && !Blocking(pc)) ApplyBrake(pc); } diff --git a/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json index e3b73b250..a6444a06b 100644 --- a/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json +++ b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json @@ -11,6 +11,8 @@ "gameMode": "Lobby", "combatFormCheckSeconds": 0.5, "autoResetDummySeconds": 2.0, + "autoResetPauseInSkill": 1, + "_note_970": "#970 PD 「비수 주력 스킬 사용 시 적이 갑자기 순간이동」 — Play 실측 원인 2가지: ① 원본 끝 순간이동(PC 가 대상 뒤 1.5 m · WLShadowSlash.json endWarp 0 으로 끔) ② 이 씬의 더미 자동 복귀 — 주력 판정 10회가 더미를 2.4~3.1 m 밀어 사거리 밖 2 s 가 차면 더미가 PC 앞으로 순간이동(실측 2.00 · 3.55 m) → autoResetPauseInSkill 1 = PC 가 스킬 상태인 동안은 사거리 밖 시간을 세지 않음(스킬이 끝난 뒤부터 다시 셈 · 0 = 이전처럼)", "moveKeys": 1, "attackKey": "Space", "weaponPrevKey": "O", diff --git a/Assets/WL/SkillTest/WLSkillTestHarness.cs b/Assets/WL/SkillTest/WLSkillTestHarness.cs index f19011b6f..e9000ca65 100644 --- a/Assets/WL/SkillTest/WLSkillTestHarness.cs +++ b/Assets/WL/SkillTest/WLSkillTestHarness.cs @@ -63,6 +63,7 @@ namespace WL.SkillTest public string gameMode = "Lobby"; // InGameInfo 게임 모드 — 섬과 같은 Lobby(자동 전투·대상 찾기가 로비 규칙). Stage 로 두면 PC 가 대상 없이 달린다(#948 실측). 로비 컨트롤러(pcanim_lobby) 문제는 Set_Obj 동안만 Stage 로 바꾸는 전투 형태 적용(섬 WLIslandAvatar.ApplyCombatForm 과 같은 꼴)으로 푼다 public float combatFormCheckSeconds = 0.5f; // 이 간격으로 PC 가 로비 컨트롤러면 전투 형태 다시 적용 · 대상이 없으면 더미를 다시 겨눔 public float autoResetDummySeconds = 2.0f; // 더미가 공격 사거리 밖(넉백 누적)에 이만큼(초) 머물면 PC 앞 기본 자리로(R 키와 같음) → 콤보가 끊기지 않고 반복된다(#948 · 0 = 끔) + public int autoResetPauseInSkill = 0; // #970 1 = PC 가 스킬 상태인 동안은 사거리 밖 시간을 세지 않음(0 으로) — 비수 주력 판정이 더미를 2.4~3.1 m 밀어 스킬 중에 더미가 PC 앞으로 순간이동하던 것 · 스킬이 끝난 뒤부터 다시 셈 public int moveKeys = 1; // 1 = WASD/방향키도 이동(가상패드와 함께 · PD #947 「가상패드 + 키 입력」) public string attackKey = "Space"; public string weaponPrevKey = "O"; // PD #947: O = 무기 왼쪽 순환 @@ -275,7 +276,8 @@ namespace WL.SkillTest if (auto > 0f && Dummy != null) { var d = Dummy.Get_position() - pc.Get_position(); d.y = 0f; - if (d.magnitude > pc.WL_AttackRange + 0.3f) { _outOfRange += Mathf.Max(0.1f, Cfg.combatFormCheckSeconds); if (_outOfRange >= auto) { _outOfRange = 0f; _msg = "더미 사거리 밖 " + auto + " s → " + ResetDummy(); } } + if (Cfg.autoResetPauseInSkill != 0 && pc.Get_CurAnim() == eAnim.Skill) _outOfRange = 0f; // #970 스킬 중엔 셈 멈춤(스킬 중 더미 순간이동 방지) + else if (d.magnitude > pc.WL_AttackRange + 0.3f) { _outOfRange += Mathf.Max(0.1f, Cfg.combatFormCheckSeconds); if (_outOfRange >= auto) { _outOfRange = 0f; _msg = "더미 사거리 밖 " + auto + " s → " + ResetDummy(); } } else _outOfRange = 0f; } }