Project_WL/AgentScripts/WL826_WarriorPackMapping.cs

253 lines
24 KiB
C#
Raw Normal View History

// WL826_WarriorPackMapping.cs — 무기 계열별 공격 모션을 ExplosiveLLC Warrior Pack Super Bundle 로 교체 (매핑 에셋 재작성 · 에디터 1회)
// PD 2026-10-09 「Warrior Pack Super Bundle 을 설치했어 — 무기별 모션을 이 에셋 모션으로 교체」
// 검 = Knight · 망치 = Hammer · 단검 = Ninja · 활 = Archer · 지팡이 = Mage (창 = 번들에 창 팩이 없어 Shinabro 유지 · 2Handed 는 대안)
// v2(23:5x · PD 「일부 모션이 제대로 안 보인다」): 타격 시점 = 팩 FBX 에 구워진 Hit 이벤트 시각(정규화 · 실측) · 전이 exit = 앞 클립 Hit + 0.35(0.55~0.90)
// 이 스크립트는 CombatAnimMapping.asset 의 행만 다시 쓴다. 실제 서브클립·이벤트·컨트롤러 교체는 기존 도구가 한다:
// unity command run_script --file AgentScripts/WL826_WarriorPackMapping.cs --entry WL826_WarriorPackMapping.Run --timeout 120
// unity command run_script --file AgentScripts/WL_CombatAnimSetup.cs --entry WL_CombatAnimSetup.Run --args '[["apply"]]' --timeout 500
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using WL.Combat;
public static class WL826_WarriorPackMapping
{
const string MappingPath = "Assets/WL/Combat/CombatAnimMapping.asset";
const string Root = "Assets/ExplosiveLLC/";
const string K = Root + "Knight Warrior Mecanim Animation Pack/Animations/Knight@";
const string H = Root + "Hammer Warrior Mecanim Animation Pack/Animations/Hammer@";
const string N = Root + "Ninja Warrior Mecanim Animation Pack/Animations/Ninja@";
const string A = Root + "Archer Warrior Mecanim Animation Pack/Animations/Archer@";
const string M = Root + "Mage Warrior Mecanim Animation Pack/Animations/Mage@";
// #838 대검 = 2Handed Warrior 팩 · 활 = FR(FirstRising) 휴머노이드 .anim 사본(Assets/WL/Combat/Anim/FR/ · 원본 C:\BurningTimes\FirstRising\Assets\Animation\Player\Weapon\Bow0~4.anim)
const string T = Root + "2 Handed Warrior Mecanim Animation Pack/Animations/2Handed@";
const string FR = "Assets/WL/Combat/Anim/FR/";
public const string GreatswordCtrl = "Assets/Res_Addr/Animations/pcanim_greatsword.controller";
// 팩 FBX 임포터의 원본 클립 Hit 이벤트 시각(정규화 · 2026-10-09 23:5x 실측 · 없는 클립은 0.45 가정)
const float K1 = 0.449f, K2 = 0.350f, K3 = 0.374f;
// #838 2Handed 팩 Attack1_S/2_S/3_S Hit 실측(임포터 원본 이벤트 · 2026-10-11)
const float T1 = 0.479f, T2 = 0.504f, T3 = 0.468f;
// #838 FR 활 클립(Bow0~4 = 동일 모션 · 0.8 s) — 양손 거리 실측(WL838_BowRelease): 당김 최대 f21(0.87) → 시위 놓음 f22~23(0.92) → 발사 0.92 · 다음 타 진입은 클립 끝(0.98)
const float B1 = 0.92f, BExit = 0.98f;
// #839 활 3타 — 기 모으기 이벤트 0.40(당김 구간 f6~f21 안). #840 PD 「발사 전까지 활시위 당김 유지」 → 이벤트부터 1.2 s 동안 애니메이터 0.35배(당김 자세 유지) 뒤 원속 → 발사 0.92 는 그 뒤. 3타 상태 속도는 원속(1.0).
// #843 PD 「조준이 끝난 직후 바로 발사」 — 유지(0.6 s · 속도 0.35)는 클립을 0.21 s = 0.2625 만큼 진행시키므로 발사 0.92 에서 역산한 0.66 에 시작 → 유지가 끝나는 프레임이 곧 발사 프레임(이전 0.40 은 0.2 s 더 기다렸다)
const float BCharge = 0.66f, BChargeDur = 0.6f, BSpeed3 = 1.0f; const string BHoldSpeed = "0.35"; // #841 PD 「모아쏘기 딜레이 절반」 유지 1.2 → 0.6 s · BSpeed3 1.0 = 원속(유지는 WLBowCharge)
// #839 해머 1타 재생 배율(PD 「첫 타 공격 속도를 좀 더 빠르게」) · #840 3타 진입 exit 0.55 + 3타 재생 1.3(2→3 딜레이 단축)
const float HS1 = 1.3f, HX3 = 0.55f, HS3 = 1.3f;
// #839 마법(소서리스) — Mage@Attack2_S 18~64f · 투사체 f41(0.504): 발사 구간 34~64f(31f · 1.03 s · 투사체 (41-34)/30 = 0.23) · 3타 진입 exit 0.85
const int MFirst = 34, MLast = 64; const float MF = 0.23f, MExit = 0.85f;
// #839 2Handed 4타·스킬 — MoveAttack1_S Hit 0.568 · SpecialAttack1(Hit 이벤트 없음 · 포즈 띠 찌르기 f5~9/10 → 0.50) · Run2-Attack1(0.53 s · 0.40 추정)
const float T4 = 0.568f, TSk1 = 0.50f, TSk2 = 0.40f;
// #839 Spearman 팩 Hit 실측(임포터 원본 이벤트): Attack1 0.439 · Attack2_S 0.425 · Attack3_S 0.523(Land 0.48)
const string SP = Root + "Spearman Warrior Mecanim Animation Pack/Animations/Spearman@";
const float SP1 = 0.439f, SP2 = 0.425f, SP3 = 0.523f;
// #840 창 2타 = Attack2_S(23~60f · Hit f38.7) 찌르기 구간 33~46f(14f · 0.47 s) → 타격 (38.7-33)/13 = 0.44 · 3연속(Attack2·2b·2c) · 재생 1.2배 · 다음 진입 exit 0.9
const int SPcFirst = 33, SPcLast = 46; const float SPc = 0.44f, SPcSpeed = 1.5f, SPcExit = 0.9f; // PD 「찌르기 모션을 매우 빠르게 3회」 → 1.5배(한 번 0.31 s · 3번 ≈ 0.95 s)
const float H1 = 0.370f, H2 = 0.422f, H3 = 0.448f;
const float N1 = 0.237f, N2 = 0.407f, N3 = 0.203f;
const float A1 = 0.393f, A2 = 0.45f, A3 = 0.45f;
const float M1 = 0.504f, M2 = 0.407f;
static AnimEventSpec Ev(string fn, float t, string s = "", int i = 0, float f = 0f)
{
return new AnimEventSpec { functionName = fn, useNormalized = true, normalizedTime = Mathf.Clamp01(t), time = 0f, stringParameter = s, intParameter = i, floatParameter = f };
}
// #828 검 — 베기 이펙트(ShowEffect)는 「실측 스윙 시작 프레임」에 둔다(2026-09-07 #804 · 09-15 #816v 와 같은 규칙).
// WL828_TrailBake 실측(60 Hz · 속도 트림 창 시작): Attack1 f12/50 → 0.24 · Attack2 f14/52 → 0.269 · Attack3 f16/64 → 0.25
// (Hit−0.12 규칙은 2타를 스윙보다 4프레임 앞서 띄웠다 → 호가 칼보다 먼저 돈다)
// #841 1타 정정: 60 Hz 전수 덤프(WL828 ProbeDump)로 보면 f12~f17 은 칼을 머리 뒤로 감아올리는 구간이고 실제 내려베기는 f16→f22(0.267→0.367 s · 한 샘플당 0.83~0.93 m 이동).
// ShowEffect = 베기 시작 f16/50 = 0.32 (Hit 0.449 = 베기 끝 f22 직후).
const float KS1 = 0.32f, KS2 = 0.269f, KS3 = 0.25f;
// #834 비수 3타 — 2타 타격(0.407) 직후 진입 · 재생 1.3배
const float DS3 = 0.5f, DS3Speed = 1.3f;
// #838 비수 4타(DualBlades_DodgeAttack · 원본 힘민지 클립) — 원본 이벤트 시각 유지(ShowEffect 0.155 · Projectile 0.186) · 진입 exit 0.55 · 재생 1.5배
const string AS = "Assets/Res_Addr/Animations/Animation/Assassin/";
const float D4 = 0.186f, D4Show = 0.155f, D4Exit = 0.55f, D4Speed = 1.5f;
/// <summary>근접 — 팩 Hit 시각 기준: 베기 이펙트 = Hit−0.12(showAt 지정 시 그 값) · 전진 = Hit−0.06 · 판정 = Hit · 2·3타는 LookAtTarget.</summary>
static List<AnimEventSpec> Melee(int step, float hit, float move, float showAt = -1f, int effectCid = 0)
{
var l = new List<AnimEventSpec>();
if (step > 1) l.Add(Ev("LookAtTarget", 0f));
// effectCid > 0 = 베기 이펙트·타격 상자 이름을 **템플릿 직업** 것으로(#838 대검 10801 → Effect_Slash_10101_N · Projectile_10101 · 창 10701 과 같은 재사용 규칙).
// WL 추가 직업은 템플릿 행을 복제해 들어가므로 투사체 사전(dic_projectile)에는 템플릿 이름만 있다 — Projectile_10801 로 쓰면 KeyNotFound(2026-10-11 실측 18건).
string cid = effectCid > 0 ? effectCid.ToString() : "{CID}";
l.Add(Ev("ShowEffect", showAt >= 0f ? showAt : Mathf.Max(0.05f, hit - 0.12f), "Effect_Slash_" + cid + "_" + step, 1));
if (move > 0f) l.Add(Ev("Move", Mathf.Max(0.05f, hit - 0.06f), "", 0, move));
l.Add(Ev("Projectile", hit, "Projectile_" + cid));
return l;
}
/// <summary>원거리(활·지팡이) — 조준 뒤 발사만.</summary>
static List<AnimEventSpec> Ranged(float hit)
{
return new List<AnimEventSpec> { Ev("LookAtTarget", 0f), Ev("Projectile", hit, "Projectile_{CID}") };
}
/// <summary>#839 활 3타 — 조준 → 기 모으기(WLBowCharge · float = 유지 초 · string = 유지 중 속도 #840) → 발사(당김 유지가 끝난 뒤 클립 진행대로).</summary>
static List<AnimEventSpec> RangedCharged(float hit)
{
return new List<AnimEventSpec> { Ev("LookAtTarget", 0f), Ev("WLBowCharge", BCharge, BHoldSpeed, 0, BChargeDur), Ev("Projectile", hit, "Projectile_{CID}") };
}
/// <summary>#838 대기 클립 — 힘민지 원본 대기 클립은 첫 프레임에 Event_Idle(논리 CurAnim 초기화)을 갖는다. 팩 Idle(Knight@Idle·Mage@Idle)로 바꾼 직업은 이 이벤트가 없어
/// 콤보 뒤 CurAnim 이 Attack 에 굳고(Play_Attack 가드) 두 번째 콤보가 안 나간다(2026-10-11 해머 실측: 애니메이터 idle · 논리 Attack). 서브클립에 되살린다.</summary>
static List<AnimEventSpec> IdleEv() { return new List<AnimEventSpec> { Ev("Event_Idle", 0f) }; }
/// <summary>앞 클립의 Hit 뒤 0.35 지점에서 다음 타로 넘어간다(0.55~0.90) — 타격과 뒤따르는 동작이 보이게.</summary>
static float Exit(float prevHit) { return Mathf.Clamp(prevHit + 0.35f, 0.55f, 0.90f); }
static StateClipSpec St(string state, string fbx, string sub, List<AnimEventSpec> ev, float incomingExit = 0f, float speed = 0f)
{
return new StateClipSpec { stateName = state, sourceFbxPath = fbx, subClipName = sub, sourceClipName = "", incomingExitTime = incomingExit, createIfMissing = false, speed = speed, events = ev };
}
/// <summary>#839 프레임 범위 지정(마법 발사 구간만 등) + 상태 신설(create) + 원본 클립 이름 지정.</summary>
static StateClipSpec StR(string state, string fbx, string sub, List<AnimEventSpec> ev, int first, int last, float incomingExit = 0f, float speed = 0f, bool create = false, string srcClip = "")
{
var s = St(state, fbx, sub, ev, incomingExit, speed);
s.firstFrame = first; s.lastFrame = last; s.createIfMissing = create; s.sourceClipName = srcClip;
return s;
}
/// <summary>#839 스킬 상태 — 힘민지 원본 스킬 클립 규약: SkillEvent(int 0) = 타격/발사 · (dashslash) ShowEffect Effect_Teleport i3 @0 · (combo01) SkillEvent@0 + Del_Magic@0.67.</summary>
static List<AnimEventSpec> Skill(float hit, bool teleport = false, bool delMagicAt067 = false)
{
var l = new List<AnimEventSpec>();
if (teleport) l.Add(Ev("ShowEffect", 0f, "Effect_Teleport", 3));
l.Add(Ev("SkillEvent", hit));
if (delMagicAt067) l.Add(Ev("Del_Magic", 0.67f));
return l;
}
static ClassCombatAnimSpec Row(int cid, string note, params StateClipSpec[] states)
{
var r = new ClassCombatAnimSpec { classId = cid, note = note, enabled = true, controllerPathOverride = "" };
r.states = new List<StateClipSpec>(states);
return r;
}
/// <summary>ClassConfig 에 없는 WL 추가 직업(대검 10801 등) — 컨트롤러 경로를 직접 준다.</summary>
static ClassCombatAnimSpec RowC(int cid, string ctrlPath, string note, params StateClipSpec[] states)
{
var r = Row(cid, note, states);
r.controllerPathOverride = ctrlPath;
return r;
}
public static string Run()
{
var mapping = AssetDatabase.LoadAssetAtPath<CombatAnimMapping>(MappingPath);
if (mapping == null) return "매핑 에셋 없음 " + MappingPath;
foreach (var p in new[] { K + "Attack1_S.FBX", H + "Attack1_S.FBX", N + "Attack1_S.FBX", A + "Attack1_S.FBX", M + "Attack2_S.FBX" })
if (AssetDatabase.LoadAssetAtPath<Object>(p) == null) return "팩 FBX 없음 " + p;
string bak;
try
{
string dir = Path.Combine(Path.GetDirectoryName(Application.dataPath), "AgentScripts/staging/_backup_WarriorPack_" + System.DateTime.Now.ToString("yyyyMMdd_HHmm"));
Directory.CreateDirectory(dir);
File.Copy(MappingPath, Path.Combine(dir, "CombatAnimMapping.asset"), true);
bak = dir;
}
catch (System.Exception ex) { bak = "백업 실패 " + ex.Message; }
var rows = new List<ClassCombatAnimSpec>
{
Row(10101, "검 · OneHand → Knight Warrior pack Attack1~3_S (Hit 실측 시각 · ShowEffect = 실측 스윙 시작 #828)",
St("attack", K + "Attack1_S.FBX", "Attack1_S_{CID}", Melee(1, K1, 0.3f, KS1)),
St("Attack2", K + "Attack2_S.FBX", "Attack2_S_{CID}", Melee(2, K2, 0.15f, KS2), Exit(K1)),
St("Attack3", K + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, K3, 0.31f, KS3), Exit(K2))),
Row(10102, "검(마검) · SpellBlade → Knight Warrior pack (MoveAttack1 4타는 현행 유지 · ShowEffect = 실측 스윙 시작 #828)",
St("attack", K + "Attack1_S.FBX", "Attack1_S_{CID}", Melee(1, K1, 0.3f, KS1)),
St("Attack2", K + "Attack2_S.FBX", "Attack2_S_{CID}", Melee(2, K2, 0.15f, KS2), Exit(K1)),
St("Attack3", K + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, K3, 0.31f, KS3), Exit(K2))),
// #838 PD 「둔기(해머) 공격 모션은 2타와 3타의 순서를 바꿔줘」 — 상태 Attack2 ← 팩 Attack3_S(회전 공격 · Hit H3) · 상태 Attack3 ← 팩 Attack2_S(Hit H2).
// 서브클립 이름(Attack2_S_{CID} 등)은 상태 기준으로 유지한다(프레임 표·검기 행 키가 상태 이름을 따르므로) → 표·검기 행은 재베이크.
// #839 PD 「현재 3타(팩 Attack2) → 1타 · 현재 1타(팩 Attack1) → 2타 · 현재 2타(팩 Attack3 회전) → 3타 · 첫 타 속도 더 빠르게」 → 1타 재생 1.3배
// #840 PD 「2타→3타 딜레이가 너무 크다」 — 3타(회전) 진입 exit 0.72 → 0.55(2타 타격 0.37 직후) · 3타 재생 1.3배(회전 감아올리기 단축). 2타 타격→3타 타격 1.49 → ≈0.9 s
Row(10501, "망치 · BluntShield → Hammer pack Attack2_S(1타 ×1.3) · Attack1_S(2타) · Attack3_S(3타 회전 ×1.3 · 빠른 진입) (#839 재배열 · #840 2→3 단축 · 대기 Event_Idle)",
St("idle", K + "Idle.FBX", "Idle_S_{CID}", IdleEv()),
St("attack", H + "Attack2_S.FBX", "Attack1_S_{CID}", Melee(1, H2, 0.15f), 0f, HS1),
St("Attack2", H + "Attack1_S.FBX", "Attack2_S_{CID}", Melee(2, H1, 0.3f), Exit(H2)),
St("Attack3", H + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, H3, 0.31f), HX3, HS3)),
Row(10502, "망치 · Paladin → Hammer pack (상태명 Sword&Shield_Attack2/3 · #839 재배열 · 1타 ×1.3 · #840 2→3 단축)",
St("attack", H + "Attack2_S.FBX", "Attack1_S_{CID}", Melee(1, H2, 0.15f), 0f, HS1),
St("Sword&Shield_Attack2", H + "Attack1_S.FBX", "Attack2_S_{CID}", Melee(2, H1, 0.3f), Exit(H2)),
St("Sword&Shield_Attack3", H + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, H3, 0.31f), HX3, HS3)),
// #834 PD 「비수 3타는 더 빠르게 발동되고 전진하며」 — 3타 진입 exit 0.5(2타 타격 0.407 직후) · 3타 재생 속도 1.3 · 전진은 프레임 표(Attack3_S_102 ×1.6)
// #838 PD 「단검(비수) 마지막 4타가 너무 느리게 동작 → 더 빠르게 발동」 — 4타 = 컨트롤러 체인 끝의 DualBlades_DodgeAttack(원본 힘민지 클립 · 1.167 s · 타격 0.186).
// 3타→4타 진입 exit 0.786 → 0.55(3타 타격 0.203 뒤 충분) · 4타 재생 1.5배. 이벤트는 원본 시각 유지(ShowEffect 0.155 · Projectile 0.186 · 검기 행 `_4` 그대로).
Row(10202, "단검 · Assassin → Ninja Warrior pack Attack1~3_S (3타 빠른 발동·속도 1.3 #834 · 4타 DodgeAttack 빠른 발동 #838)",
St("attack", N + "Attack1_S.FBX", "Attack1_S_{CID}", Melee(1, N1, 0.2f)),
St("DualBlades_Attack2", N + "Attack2_S.FBX", "Attack2_S_{CID}", Melee(2, N2, 0.1f), Exit(N1)),
St("DualBlades_Attack3", N + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, N3, 0.2f), DS3, DS3Speed),
St("DualBlades_DodgeAttack", AS + "Stander@DualBlades_DodgeAttack_Attack4.FBX", "Attack4_S_{CID}", Melee(4, D4, 0.3f, D4Show), D4Exit, D4Speed)),
Row(10201, "단검 · BladeDancer → Ninja Warrior pack (3타 빠른 발동·속도 1.3 #834)",
St("attack", N + "Attack1_S.FBX", "Attack1_S_{CID}", Melee(1, N1, 0.2f)),
St("DualBlades_Attack2", N + "Attack2_S.FBX", "Attack2_S_{CID}", Melee(2, N2, 0.1f), Exit(N1)),
St("DualBlades_Attack3", N + "Attack3_S.FBX", "Attack3_S_{CID}", Melee(3, N3, 0.2f), DS3, DS3Speed)),
// #834 PD 「활시위를 당기는 게 아니라 던지는 느낌」 — 2·3타에 쓰던 MoveAttack(달리며 쏘기 · 제자리 판)이 원인의 절반 → 3타 전부 서서 쏘는 Attack1_S.
// 나머지 절반(활이 팔 방향으로 누움)은 WLWeaponClasses.json weaponOrient(활 X+90°)가 런타임에 바로잡는다.
// #838 PD 「활 공격 모션은 FR 프로젝트의 활 공격 모션으로 대체」 — FR Bow0~4 는 수치 동일(한 모션) → 3타 전부 FR_Bow0 사본(.anim · 이벤트 초 단위). 활 X+90° 보정(weaponOrient)은 그대로.
// #839 PD 「3발 중 마지막 타 = 활에 기가 모이는 임팩트 후 더 큰 화살」 — 3타 클립에 WLBowCharge 이벤트(당김 시작 0.40 · 지속 = float) · 재생 0.85배(모았다 쏘는 느낌) · 발사는 그대로 0.92.
// 수신 = Assets/WL/Combat/Weapon/WLBowCharge.cs(PC 루트에 런타임 부착 · 활 소켓에 기 모으기 VFX · 다음 화살 = 1.6배 + 글로우 · WLProjectileVisual chargedVisual).
Row(10402, "활 · Archer → FR Bow 모션 ×3 (#838) · 3타 기 모으기 + 큰 화살 (#839)",
St("attack", FR + "FR_Bow0.anim", "FR_Bow1_{CID}", Ranged(B1)),
St("Bow_Attack2", FR + "FR_Bow0.anim", "FR_Bow2_{CID}", Ranged(B1), BExit),
St("Bow_Attack3", FR + "FR_Bow0.anim", "FR_Bow3_{CID}", RangedCharged(B1), BExit, BSpeed3)),
Row(10401, "활 · RookieArcher → FR Bow 모션 ×3 (#838) · 3타 기 모으기 (#839)",
St("attack", FR + "FR_Bow0.anim", "FR_Bow1_{CID}", Ranged(B1)),
St("Bow_Attack2", FR + "FR_Bow0.anim", "FR_Bow2_{CID}", Ranged(B1), BExit),
St("Bow_Attack3", FR + "FR_Bow0.anim", "FR_Bow3_{CID}", RangedCharged(B1), BExit, BSpeed3)),
// #839 PD 「마법: 1타 = 투사체 발사 모션만 · 2타 = 투사체 · 3타 = 손 드는 동작(현재 1타 앞부분) 뒤 투사체」 — Mage 팩 Attack2_S(18~64f · 발사 f41)를 발사 구간(34~64f)과 전체로 나눠 3상태.
// 소서리스(PD 지팡이 1071 → 10302) 컨트롤러는 attack 뿐 → attack2·attack3 신설(도구가 직전 상태 전이를 걷고 체인 연결 · attack→Atk_A1_wing 전이는 폐기).
// #840 PD 「지팡이 3타 동작이 너무 어색 → 다른 마법사 모션으로」 — 팩 후보 4종 포즈 띠 비교: Attack3_S(Cast2 · 지팡이를 앞으로 겨눠 쏘고 크게 휘두름 · 1.17 s · Hit 0.407) 채택.
// #841 PD 「지팡이 2타와 3타 위치 교체」 → attack2 = Attack3_S(Cast2) · attack3 = 발사 구간
Row(10302, "지팡이 · Sorceress → 발사 구간(1타) · Attack3_S Cast2(2타) · 발사 구간(3타) (#839 체인 · #840 Cast2 · #841 2↔3)",
StR("attack", M + "Attack2_S.FBX", "CastFire1_S_{CID}", Ranged(MF), MFirst, MLast),
StR("attack2", M + "Attack3_S.FBX", "Cast2_S_{CID}", Ranged(M2), 48, 83, MExit, 0f, true),
StR("attack3", M + "Attack2_S.FBX", "CastFire2_S_{CID}", Ranged(MF), MFirst, MLast, MExit, 0f, true)),
Row(10301, "지팡이 · Mage → Mage Warrior pack Attack2_S / Attack3_S (#838 대기 Event_Idle 복원)",
St("idle", M + "Idle.FBX", "Idle_S_{CID}", IdleEv()),
St("attack", M + "Attack2_S.FBX", "Cast1_S_{CID}", Ranged(M1)),
St("attack2", M + "Attack3_S.FBX", "Cast2_S_{CID}", Ranged(M2), Exit(M1))),
Row(10303, "지팡이 · Summoner → Mage Warrior pack Attack2_S (#838 대기 Event_Idle 복원)",
St("idle", M + "Idle.FBX", "Idle_S_{CID}", IdleEv()),
St("attack", M + "Attack2_S.FBX", "Cast1_S_{CID}", Ranged(M1))),
// #838 PD 「대검 아이템 + 대검 모션(2Handed Warrior)을 쓰는 공격 타입 추가」 — WL 추가 직업 10801(한손검 컨트롤러 복제 pcanim_greatsword · WL838_GreatswordSetup).
// 베기 이펙트 이름은 창(10701)과 같은 규칙으로 템플릿 한손검 것(Effect_Slash_10101_N)을 재사용 → 검기 행은 (10801, 그 이름) 키로 베이크.
// #839 PD 「대검 마지막 4타 추가 + 액티브 스킬 모션 적용」 — 4타 = MoveAttack1_S(돌진 베기 · Hit 0.568 · 상태 Attack4 신설) · 스킬 = 한손검 스킬 상태 3종(fatalblow·dashslash·combo01)에 2Handed 팩 클립.
// #841 PD 「대검 1타→3타 · 2타→1타 · 3타→2타」 → attack ← 팩 Attack2_S(T2) · Attack2 ← Attack3_S(T3) · Attack3 ← Attack1_S(T1) · 4타(점프) 유지
RowC(10801, GreatswordCtrl, "대검 · Greatsword(WL 10801) → 2Handed Attack2_S(1타) · Attack3_S(2타) · Attack1_S(3타) + 4타 Spearman 점프 + 스킬 3종 (#838 · #839 · #840 · #841 재배열)",
St("attack", T + "Attack2_S.FBX", "Attack1_S_{CID}", Melee(1, T2, 0.15f, -1f, 10101)),
St("Attack2", T + "Attack3_S.FBX", "Attack2_S_{CID}", Melee(2, T3, 0.31f, -1f, 10101), Exit(T2)),
St("Attack3", T + "Attack1_S.FBX", "Attack3_S_{CID}", Melee(3, T1, 0.3f, -1f, 10101), Exit(T3)),
// #840 PD 「대검 4타(MoveAttack1 돌진) 제거 → 창의 3타(Spearman 점프 찌르기)를 대검 4타로」
StR("Attack4", SP + "Attack3_S.FBX", "Attack4_S_{CID}", Melee(4, SP3, 0.5f, -1f, 10101), -1, -1, Exit(T1), 0f, true),
St("fatalblow", T + "SpecialAttack1.FBX", "SkillFatal_S_{CID}", Skill(TSk1)),
St("dashslash", T + "Run2-Attack1.FBX", "SkillDash_S_{CID}", Skill(TSk2, true)),
StR("combo01", T + "Jump-Attack1.FBX", "SkillCombo_S_{CID}", Skill(0f, false, true), -1, -1, 0f, 0f, false, "Jump-Attack1Hit")),
// #839 PD 「창의 모션은 새로 추가한 에셋의 스피어맨 모션으로 대체」 — Shinabro 07_Spear → Spearman Warrior pack Attack1(루트모션 판 · 제자리 재생) · Attack2_S · Attack3_S(점프 찌르기 · Land 0.48 · Hit 0.52).
// 소켓은 메이스 자리 1 유지(포즈 띠 실측: 소켓 2 는 창끝이 뒤). 런타임 json 이벤트(attackClipTokens)는 비움 — 이벤트는 서브클립에 굽는다.
// #840 PD 「3연속 찌르기를 2타에 · 현재 2타 → 1타 · 현재 1타 → 3타」 — 팩·Shinabro 에 3연속 찌르기 클립 없음(포즈 띠 10종 확인) → Attack2_S 의 찌르기 구간(33~46f)을 3연속 상태(Attack2·2b·2c · 1.2배)로 이어 붙임.
RowC(10701, "Assets/Res_Addr/Animations/pcanim_spear.controller", "창 · Spear(WL 10701) → Spearman Attack2_S(1타) · 찌르기 구간 ×3(2타) · Attack1(3타) (#840 재배열)",
St("attack", SP + "Attack2_S.FBX", "Attack1_S_{CID}", Melee(1, SP2, 0.2f, -1f, 10101)),
StR("Attack2", SP + "Attack2_S.FBX", "Attack2_S_{CID}", Melee(2, SPc, 0.1f, -1f, 10101), SPcFirst, SPcLast, Exit(SP2), SPcSpeed),
StR("Attack2b", SP + "Attack2_S.FBX", "Attack2b_S_{CID}", Melee(2, SPc, 0.1f, -1f, 10101), SPcFirst, SPcLast, SPcExit, SPcSpeed, true),
StR("Attack2c", SP + "Attack2_S.FBX", "Attack2c_S_{CID}", Melee(2, SPc, 0.1f, -1f, 10101), SPcFirst, SPcLast, SPcExit, SPcSpeed, true),
St("Attack3", SP + "Attack1.FBX", "Attack3_S_{CID}", Melee(3, SP1, 0.3f, -1f, 10101), SPcExit)),
};
mapping.classes = rows;
mapping.source = "PD 지시 2026-10-09 — 무기 계열별 모션을 ExplosiveLLC Warrior Pack Super Bundle 로 교체(검 Knight · 망치 Hammer · 단검 Ninja · 활 Archer · 지팡이 Mage · 창 = Shinabro 유지). 타격 시점 = 팩 FBX Hit 이벤트 실측(23:5x) · 전이 exit = 앞 Hit + 0.35";
EditorUtility.SetDirty(mapping);
AssetDatabase.SaveAssets();
var sb = new StringBuilder();
sb.Append("매핑 행 ").Append(rows.Count).Append(" 재작성(Hit 시각 반영) · 백업 ").Append(bak).Append(" · 다음 = WL_CombatAnimSetup apply");
return sb.ToString();
}
}