# DS-전투시스템 종합분석

## 문서 개요

본 문서는 언리얼 엔진 에셋(DataTable, AnimMontage, Blueprint, CurveTable)을 JSON으로 익스포트하여 분석한 전투 로직 시스템에 대한 기술 문서입니다.

**분석 대상 스토커**: Hilda, Urud, Nave, Baran, Rio, Clad, Rene, Sinobu, Lian, Cazimord (10명)

**작성 일자**: 2025-10-23
**익스포트 데이터**: `DS-전투밸런스_분석자료/20251023_114317/`

---

## 목차

1. [개요 및 Asset Export 시스템](#1-개요-및-asset-export-시스템)
2. [DataTable 구조](#2-datatable-구조)
3. [AnimMontage 타이밍 시스템](#3-animmontage-타이밍-시스템)
4. [Blueprint Ability 및 캔슬 시스템](#4-blueprint-ability-및-캔슬-시스템)
5. [GameplayEffect 메커니즘](#5-gameplayeffect-메커니즘)
6. [DT_Skill 특수 케이스](#6-dt_skill-특수-케이스)
7. [코드-어셋 통합 흐름](#7-코드-어셋-통합-흐름)
8. [JSON 사용 가이드](#8-json-사용-가이드)

---

## 1. 개요 및 Asset Export 시스템

### 1.1 Asset Export to JSON 기능

언리얼 엔진 에셋은 기본적으로 바이너리 형식으로 저장되어 LLM이나 외부 분석 도구가 직접 접근할 수 없습니다. 이를 해결하기 위해 **Asset Export to JSON** 에디터 확장 기능을 개발하였습니다.

**참고 문서**: `DS-전투밸런스_분석자료/DS-Asset_Export_to_JSON.md`

#### 주요 기능

- **지원 에셋 타입**: DataTable, Blueprint, AnimMontage, CurveTable
- **프로젝트 설정 통합**: `편집 → 프로젝트 설정 → 플러그인 → Asset Export to JSON`
- **타임스탬프 익스포트**: 익스포트 히스토리 보관을 위한 타임스탬프 폴더 생성
- **완전한 데이터 추출**:
  - DataTable: 모든 행/열 데이터
  - Blueprint: 변수, 함수, 컴포넌트, 이벤트 그래프 노드 구조
  - AnimMontage: 섹션, 노티파이, 커스텀 프로퍼티, 슬롯 애니메이션
  - CurveTable: RichCurves 및 SimpleCurves 키 데이터

#### 익스포트 실행

```
툴 → WorldStalker → Export Assets to JSON
```

#### 출력 위치

```
Content/Exports/20251023_114317/
├── DataTable.json    (1,151 에셋)
├── Blueprint.json    (1,151 에셋)
├── AnimMontage.json  (809 에셋)
└── CurveTable.json   (8 에셋)
```

### 1.2 전투 로직 분석 범위

본 문서에서는 익스포트된 JSON 데이터를 기반으로 다음 전투 시스템을 분석합니다:

#### 스토커별 전투 데이터

| 스토커 | 클래스 | 주요 특징 |
|--------|--------|-----------|
| **Hilda** | 전사 | Counter 스킬, 방패 방어 |
| **Urud** | 원거리 | Reload 시스템, 궁극기 'Explosion' 범위 피해 |
| **Nave** | 마법사 | 캐스팅 스킬, 궁극기 'Liberation' (DT_Skill 피해) |
| **Baran** | 전사 | 중무장 탱커, Pulling 체인 메커니즘 |
| **Rio** | 암살자 | Chain Score 시스템 (최대 3 스택) |
| **Clad** | 성직자 | 힐링 및 언데드 특효 |
| **Rene** | 소환사 | Spirit 소환, Lifesteal 효과 |
| **Sinobu** | 닌자 | Shuriken 충전 시스템, 'Swap' 인술 (텔레포트) |
| **Lian** | 레인저 | Reload 시스템, Precision Aim (조준 시스템) |
| **Cazimord** | 전사 | Flash 스킬 스택, Parrying 시스템 |

#### 분석 계층

```
스토커 전투 시스템
│
├── DataTable Layer (밸런스 데이터)
│   ├── DT_CharacterStat (기본 스탯, 스킬 목록)
│   ├── DT_Skill (스킬 데이터, 피해 배율, 쿨타임)
│   └── DT_CharacterAbility (기본 공격 어빌리티 매핑)
│
├── AnimMontage Layer (타이밍 데이터)
│   ├── 기본 공격 몽타주 (AM_PC_[Stalker]_B_Attack_*)
│   ├── 스킬 몽타주 (스킬별 애니메이션)
│   └── 노티파이 시스템
│       ├── ANS_AttackState_C (공격 상태, 피해 배율)
│       ├── AnimNotifyState_AttackWithEquip (히트 판정 타이밍)
│       ├── ANS_SkillCancel_C (스킬 캔슬 윈도우)
│       └── AN_SetAutoTarget_C (자동 타게팅)
│
├── Blueprint Ability Layer (로직 구현)
│   ├── GA_Attack (기본 공격)
│   ├── GA_Skill_* (스토커별 스킬 구현)
│   ├── Activation Order Group (우선순위 캔슬 시스템)
│   └── 특수 시스템 (Reload, Chain Score, Parrying 등)
│
└── GameplayEffect Layer (효과 적용)
    ├── GE_Attack_* (피해 효과)
    ├── GE_Skill_* (스킬 효과)
    └── GE_Buff/Debuff_* (버프/디버프)
```

### 1.3 JSON 참조 규칙

**중요**: JSON 파일은 에디터에서 익스포트한 결과물로, 소스 코드처럼 고정된 라인 번호가 없습니다.

#### 올바른 참조 방법

✅ **Asset 이름으로 참조**
```
DT_CharacterStat 어셋의 "hilda" 행
AM_PC_Hilda_B_Skill_SwordStrike 몽타주
GA_Skill_Hilda_SwordStrike_C 블루프린트
```

❌ **라인 번호로 참조하지 않음**
```
DataTable.json:358143-358192  (X)
```

#### JSON 구조 예시

**DataTable.json**:
```json
{
  "AssetName": "DT_CharacterStat",
  "RowStructure": "CharacterStatData",
  "Rows": [
    {
      "RowName": "hilda",
      "Data": {
        "name": "힐다",
        "defaultSkills": ["SK100201", "SK100202", "SK100204"],
        "subSkill": "SK100101",
        "ultimateSkill": "SK100301"
      }
    }
  ]
}
```

**AnimMontage.json**:
```json
{
  "AssetName": "AM_PC_Hilda_B_Skill_SwordStrike",
  "SequenceLength": 1.8,
  "AnimNotifies": [
    {
      "NotifyStateClass": "ANS_SkillCancel_C",
      "TriggerTime": 1.3,
      "Duration": 0.5
    }
  ]
}
```

---

## 2. DataTable 구조

### 2.1 DT_CharacterStat (스토커 기본 스탯 및 스킬 매핑)

DT_CharacterStat은 각 스토커의 기본 스탯과 사용 가능한 스킬 목록을 정의합니다.

#### 구조

| 필드 | 타입 | 설명 |
|------|------|------|
| `name` | String | 스토커 이름 (한글) |
| `jobName` | String | 직업 (전사, 원거리, 마법사 등) |
| `str` | Int | 힘 (물리 공격력 영향) |
| `dex` | Int | 민첩 (공격 속도, 크리티컬 영향) |
| `int` | Int | 지능 (마법 공격력 영향) |
| `con` | Int | 체력 (HP, 방어력 영향) |
| `wis` | Int | 지혜 (마나, 마법 방어력 영향) |
| `hP` | Float | 기본 HP |
| `mP` | Float | 기본 MP |
| `manaRegen` | Float | 마나 재생 속도 |
| `defaultSkills` | Array | 기본 스킬 ID 목록 (3-4개) |
| `subSkill` | String | 서브 스킬 ID (특수 스킬, 쿨타임 0초) |
| `ultimateSkill` | String | 궁극기 스킬 ID |

#### 스토커별 스탯 분포

**전체 스탯 합계**: 75 포인트 (모든 스토커 동일)

##### 힘 중심 스토커 (STR 20+)

**Hilda** (전사):
```
STR: 20, DEX: 15, INT: 10, CON: 20, WIS: 10
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK100201 (Sword Strike), SK100202 (Counter), SK100204 (Provoke)
Sub: SK100101 (Blocking)
Ultimate: SK100301 (Blood Moon)
```

**Baran** (전사):
```
STR: 25, DEX: 10, INT: 5, CON: 25, WIS: 10
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK130204 (Pulling), SK130203 (Smash), SK130206 (Sword Stab)
Sub: SK130101 (Blocking)
Ultimate: SK130301 (Rock Breaker)
```

##### 민첩 중심 스토커 (DEX 20+)

**Urud** (원거리):
```
STR: 15, DEX: 20, INT: 10, CON: 15, WIS: 15
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK110205 (Multi Shot), SK110204 (Poison Arrow), SK110201 (Make Trap), SK110207 (Reload)
Sub: SK110101 (Arrow Attack)
Ultimate: SK110301 (Explosion)
```

**Rio** (암살자):
```
STR: 15, DEX: 25, INT: 10, CON: 15, WIS: 10
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK140201 (Rapid Stab), SK140205 (Approach), SK140202 (Throwing Dagger)
Sub: SK140101 (Dropping Attack)
Ultimate: SK140301 (Sensitive)
```

**Sinobu** (닌자):
```
STR: 10, DEX: 25, INT: 10, CON: 15, WIS: 15
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK180202 (Bomb Talisman), SK180203 (Thunder Kick), SK180205 (Ninpo Change)
Sub: SK180101 (Shuriken)
Ultimate: SK180301 (Deflect)
```

**Cazimord** (전사):
```
STR: 15, DEX: 25, INT: 10, CON: 15, WIS: 10
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK170201 (Flash), SK170202 (Blade Storm), SK170203 (Burn)
Sub: SK170101 (Parrying)
Ultimate: SK170301 (Mana Stone Burn)
```

**Lian** (레인저):
```
STR: 10, DEX: 20, INT: 10, CON: 15, WIS: 20
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK190207 (Rapid Shot), SK190205 (Back Step), SK190201 (Dark Souls), SK190209 (Reload)
Sub: SK190101 (Charging Bow)
Ultimate: SK190301 (Manastone Silence)
```

##### 지능 중심 스토커 (INT 20+)

**Nave** (마법사):
```
STR: 10, DEX: 10, INT: 25, CON: 10, WIS: 20
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK120201 (Magic Missile), SK120202 (Fire Wall), SK120206 (Wind Force)
Sub: SK120101 (Mana Restore)
Ultimate: SK120301 (Escape 4)
```

**Rene** (소환사):
```
STR: 10, DEX: 10, INT: 20, CON: 10, WIS: 25
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK160202 (Summon Ifrit), SK160206 (Summon Shiva), SK160203 (Poison Gas)
Sub: SK160101 (Scratching)
Ultimate: SK160301 (Mana Stone Carnival)
```

##### 균형 스토커

**Clad** (성직자):
```
STR: 15, DEX: 10, INT: 10, CON: 20, WIS: 20
HP: 100, MP: 50, Mana Regen: 0.2
Skills: SK150206 (Holy Cure), SK150201 (Turn Undead), SK150202 (Holy Light)
Sub: SK150101 (Block)
Ultimate: SK150301 (Gold)
```

### 2.2 DT_Skill (스킬 상세 데이터)

DT_Skill은 각 스킬의 피해 배율, 쿨타임, 원소 타입, 사용 몽타주, 연결된 Ability 클래스 등을 정의합니다.

#### 구조

| 필드 | 타입 | 설명 |
|------|------|------|
| `RowName` | String | 스킬 ID (SK + 6자리 숫자) |
| `stalkerName` | String | 소유 스토커 이름 (소문자) |
| `bIsUltimate` | Bool | 궁극기 여부 |
| `skillName` | String | 스킬 이름 |
| `skillDamageRate` | Float | 기본 피해 배율 (1.0 = 100%) |
| `skillManaCost` | Float | 마나 소모량 |
| `coolTime` | Float | 쿨타임 (초) |
| `skillAttackType` | Enum | 공격 타입 (PhysicalSkill, MagicalSkill, Normal) |
| `skillElementType` | Enum | 원소 타입 (None, Fire, Lightning, Poison, Water, Holy, Dark) |
| `useMontages` | Array | 사용할 몽타주 경로 목록 |
| `abilityClass` | String | 연결된 GA_* 블루프린트 경로 |
| `gameplayEffectSet` | Array | 적용할 GameplayEffect 목록 |

#### 스킬 타입 분류

##### PhysicalSkill (물리 스킬)
- 힘(STR) 또는 민첩(DEX) 스탯의 영향을 받음
- 방어력(Defense)에 의해 감소
- 예시: Hilda의 Sword Strike, Rio의 Rapid Stab, Cazimord의 Flash

##### MagicalSkill (마법 스킬)
- 지능(INT) 또는 지혜(WIS) 스탯의 영향을 받음
- 마법 저항력(MagicalResistancePer)에 의해 감소
- 원소 타입에 따라 추가 저항력 적용 (Fire, Lightning, Poison 등)
- 예시: Nave의 Magic Missile, Rene의 Summon Ifrit, Clad의 Turn Undead

##### Normal (일반/유틸리티)
- 피해를 주지 않거나 특수 메커니즘을 가진 스킬
- 스탯 배율 없이 고정 효과 또는 0 피해
- 예시: Hilda의 Blocking, Urud의 Reload, Rio의 Approach

#### 원소 타입별 특성

| 원소 | 저항 스탯 | 주요 사용 스토커 |
|------|-----------|------------------|
| **None** | - | 대부분 (범용 스킬) |
| **Lightning** | lightningResistancePer | Hilda (Sword Strike), Sinobu (Thunder Kick) |
| **Fire** | fireResistancePer | Nave (Fire Wall) |
| **Poison** | poisonResistancePer | Urud (Poison Arrow), Rene (Poison Gas) |
| **Water** | waterResistancePer | - |
| **Holy** | holyResistancePer | Clad (Turn Undead), Lian (Dark Souls) |
| **Dark** | darkResistancePer | Rene (Poison Gas - Dark 속성도 보유) |

#### 스킬 예시

**Hilda - SK100201 (Sword Strike)**:
```json
{
  "RowName": "SK100201",
  "stalkerName": "hilda",
  "skillName": "Sword Strike",
  "skillDamageRate": 1.3,
  "coolTime": 6.0,
  "skillAttackType": "PhysicalSkill",
  "skillElementType": "Lightning",
  "useMontages": [
    "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_Ready.AM_PC_Hilda_B_Skill_Ready",
    "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike"
  ],
  "abilityClass": "/Game/Blueprints/Characters/Hilda/GA_Skill_Hilda_SwordStrike.GA_Skill_Hilda_SwordStrike_C"
}
```

**Nave - SK120301 (Escape 4 / Liberation 궁극기)**:
```json
{
  "RowName": "SK120301",
  "stalkerName": "nave",
  "bIsUltimate": true,
  "skillName": "Escape 4",
  "skillDamageRate": 1.0,
  "coolTime": 0.0,
  "skillAttackType": "MagicalSkill",
  "skillElementType": "None",
  "useMontages": [
    "/Game/_Art/_Character/PC/Nave/AnimMontage/Ultimate/AM_PC_Nave_B_Skill_Escape4.AM_PC_Nave_B_Skill_Escape4"
  ],
  "abilityClass": "/Game/Blueprints/Characters/Nave/GA_Skill_Nave_Escape4.GA_Skill_Nave_Escape4_C"
}
```
**특수 사항**: Nave의 궁극기는 AnimMontage의 노티파이가 아닌 **DT_Skill의 skillDamageRate와 gameplayEffectSet**에서 직접 피해를 정의합니다. (Section 6 참조)

**Urud - SK110301 (Explosion 궁극기)**:
```json
{
  "RowName": "SK110301",
  "stalkerName": "urud",
  "bIsUltimate": true,
  "skillName": "Explosion",
  "skillDamageRate": 1.0,
  "coolTime": 0.0,
  "skillAttackType": "Normal",
  "skillElementType": "None",
  "useMontages": [
    "/Game/_Art/_Character/PC/Urud/AnimMontage/Ultimate/AM_PC_Urud_B_Skill_Explosion.AM_PC_Urud_B_Skill_Explosion",
    "/Game/_Art/_Character/PC/Urud/AnimMontage/Base/AM_PC_Urud_B_Skill_Equipment.AM_PC_Urud_B_Skill_Equipment"
  ],
  "abilityClass": "/Game/Blueprints/Abilities/GA_Skill_Casting_Ultimate.GA_Skill_Casting_Ultimate_C"
}
```
**특수 사항**: Urud의 궁극기 'Explosion'은 투척한 돌이 적중 시 범위 피해를 줍니다. 피해는 AnimMontage의 노티파이 또는 GA_Skill Blueprint에서 처리됩니다.

### 2.3 DT_CharacterAbility (기본 공격 어빌리티 매핑)

DT_CharacterAbility는 각 스토커의 기본 공격(좌클릭)에 사용되는 Ability 클래스를 매핑합니다.

#### 구조

| 필드 | 타입 | 설명 |
|------|------|------|
| `characterName` | String | 스토커 이름 |
| `abilityClass` | String | 기본 공격 Ability 경로 |

대부분의 스토커는 공통 `GA_Attack` 클래스를 사용하지만, 일부 스토커는 커스텀 공격 로직을 가집니다.

#### 공통 기본 공격

**GA_Attack** 사용 스토커:
- Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord

기본 공격 로직:
1. 장착한 무기의 `montages` 배열에서 몽타주 선택
2. 콤보 시스템: 연속 입력 시 다음 몽타주로 체인
3. AnimNotifyState_AttackWithEquip 노티파이에서 히트 판정
4. ANS_AttackState_C 노티파이에서 피해 배율 적용

#### 특수 기본 공격

**Urud, Lian** (원거리 스토커):
- Ability: `GA_Attack_Firearm` (총기형 무기 전용)
- 특징:
  - 탄약 시스템 (GameplayTag 기반 탄약 카운트)
  - 탄약 소진 시 자동으로 Reload 스킬 트리거
  - 조준 중 이동 속도 감소
  - Reload 스킬(SK110207, SK190209)로 재장전

### 2.4 스토커별 스킬 매핑 요약

| 스토커 | 기본 스킬 (3-4개) | 서브 스킬 (쿨타임 0) | 궁극기 | 특수 시스템 |
|--------|------------------|---------------------|--------|-------------|
| **Hilda** | Sword Strike, Counter, Provoke | Blocking | Blood Moon | Counter 반격, 방패 방어 |
| **Urud** | Multi Shot, Poison Arrow, Make Trap, Reload | Arrow Attack | Explosion | Reload 시스템, 범위 피해 궁극기 |
| **Nave** | Magic Missile, Fire Wall, Wind Force | Mana Restore | Escape 4 | 캐스팅 스킬, Liberation 피해(DT_Skill) |
| **Baran** | Pulling, Smash, Sword Stab | Blocking | Rock Breaker | Pulling 체인, 중력 조작 |
| **Rio** | Rapid Stab, Approach, Throwing Dagger | Dropping Attack | Sensitive | Chain Score (3 스택) |
| **Clad** | Holy Cure, Turn Undead, Holy Light | Block | Gold | 힐링, 언데드 특효 |
| **Rene** | Summon Ifrit, Summon Shiva, Poison Gas | Scratching | Mana Stone Carnival | Spirit 소환, Lifesteal |
| **Sinobu** | Bomb Talisman, Thunder Kick, Ninpo Change | Shuriken | Deflect | Shuriken 충전, Swap 인술 |
| **Lian** | Rapid Shot, Back Step, Dark Souls, Reload | Charging Bow | Manastone Silence | Reload 시스템, Precision Aim, 충전 3스택 |
| **Cazimord** | Flash, Blade Storm, Burn | Parrying | Mana Stone Burn | Flash 스택, Parrying 반격 |

---

## 3. AnimMontage 타이밍 시스템

### 3.1 AnimMontage 노티파이 시스템 개요

AnimMontage는 애니메이션 재생 중 특정 타이밍에 게임 로직을 트리거하는 노티파이(Notify) 시스템을 제공합니다. 전투 시스템에서는 노티파이를 통해 다음을 구현합니다:

- **공격 판정 타이밍**: 무기가 적을 타격하는 정확한 프레임
- **피해 배율 조정**: 공격 구간별 피해량 증감
- **스킬 캔슬 윈도우**: 스킬을 중단하고 다음 행동으로 전환 가능한 시간대
- **자동 타게팅**: 적을 자동으로 추적하는 시점
- **이동 제한**: 스킬 사용 중 이동 속도 조절
- **벽 충돌 체크**: 근거리 공격 시 벽에 막히는지 검사

#### 노티파이 타입

| 타입 | 설명 | 예시 |
|------|------|------|
| **Notify** | 특정 시점에 한 번 실행 | AN_SetAutoTarget_C (자동 타게팅 활성화) |
| **NotifyState** | 시작~종료 구간 동안 지속 | ANS_AttackState_C (공격 상태 유지) |

### 3.2 주요 노티파이 클래스

#### ANS_AttackState_C (공격 상태 및 피해 배율)

**타입**: NotifyState (구간 지속)

**역할**:
- 공격 판정이 가능한 구간 정의
- 피해 배율 조정 (`AddNormalAttackPer` 프로퍼티)

**프로퍼티**:
| 프로퍼티 | 타입 | 설명 |
|----------|------|------|
| `TriggerTime` | Float | 시작 시간 (초) |
| `Duration` | Float | 지속 시간 (초) |
| `AddNormalAttackPer` | Float | 피해 배율 증감 (%) |

**예시** - Baran 기본 공격 1타:
```json
{
  "NotifyStateClass": "ANS_AttackState_C",
  "TriggerTime": 0.0,
  "Duration": 1.17,
  "CustomProperties": {
    "AddNormalAttackPer": "30.0"
  }
}
```
→ 0.0초부터 1.17초까지 공격 상태이며, 피해량이 +30% 증가

#### AnimNotifyState_AttackWithEquip (히트 판정 타이밍)

**타입**: NotifyState (구간 지속)

**역할**:
- 무기의 궤적을 따라 히트 판정 수행
- 다중 히트 지점 정의 (TimeArray, LocationArray, RotationArray)
- 공격 타입 태그 지정 (Event.Attack.Normal, Event.Attack.Skill 등)

**프로퍼티**:
| 프로퍼티 | 타입 | 설명 |
|----------|------|------|
| `TriggerTime` | Float | 시작 시간 (초) |
| `Duration` | Float | 지속 시간 (초) |
| `AttackTag` | GameplayTag | 공격 타입 태그 |
| `TimeArray` | Array<Float> | 다중 히트 시간 배열 |
| `LocationArray` | Array<Vector> | 히트 위치 배열 |
| `RotationArray` | Array<Rotator> | 히트 방향 배열 |

**예시** - Baran 기본 공격 1타 히트 판정:
```json
{
  "NotifyStateClass": "AnimNotifyState_AttackWithEquip",
  "TriggerTime": 0.8,
  "Duration": 0.12,
  "CustomProperties": {
    "AttackTag": "(TagName=\"Event.Attack.Normal\")",
    "TimeArray": ["0.0", "0.033333", "0.066667", "0.1"],
    "LocationArray": ["(X=0,Y=0,Z=0)", "(X=20,Y=10,Z=5)", ...]
  }
}
```
→ 0.8초부터 0.92초까지 4개의 히트 포인트에서 판정 (0.033초 간격)

#### ANS_SkillCancel_C (스킬 캔슬 윈도우)

**타입**: NotifyState (구간 지속)

**역할**:
- 스킬의 자연스러운 종료 타이밍 정의
- 이 구간에서 **같거나 낮은 Activation Order Group** 스킬로 캔슬 가능
- DPS 최적화에 중요한 역할

**프로퍼티**:
| 프로퍼티 | 타입 | 설명 |
|----------|------|------|
| `TriggerTime` | Float | 캔슬 윈도우 시작 시간 (초) |
| `Duration` | Float | 캔슬 윈도우 지속 시간 (초) |

**예시** - Hilda Sword Strike 스킬:
```json
{
  "NotifyStateClass": "ANS_SkillCancel_C",
  "TriggerTime": 1.3,
  "Duration": 0.5
}
```
→ 1.3초부터 1.8초까지 캔슬 가능 (스킬 종료 구간)

**중요**: ANS_SkillCancel_C는 **Activation Order Group 시스템과 독립적**으로 작동합니다 (OR 조건). 자세한 내용은 Section 4.2 참조.

#### AN_SetAutoTarget_C (자동 타게팅)

**타입**: Notify (순간 실행)

**역할**:
- 특정 시점에 가장 가까운 적을 자동으로 타게팅
- 캐릭터 회전 및 카메라 방향 조정

**예시**:
```json
{
  "NotifyClass": "AN_SetAutoTarget_C",
  "TriggerTime": 0.1
}
```
→ 0.1초 시점에 자동 타게팅 활성화

#### ANS_WeaponWallCheck_C (벽 충돌 검사)

**타입**: NotifyState (구간 지속)

**역할**:
- 근거리 무기 공격 시 벽과의 충돌 검사
- 벽에 막히면 공격 판정 무효화

#### ANS_DisableBlockingState_C (방어 불가 구간)

**타입**: NotifyState (구간 지속)

**역할**:
- 특정 구간에서 방어(Blocking) 불가 상태로 만듦
- 공격 모션 중 방어 전환 방지

### 3.3 스토커별 기본 공격 타이밍 분석

기본 공격은 좌클릭으로 실행되며, 대부분 3타 콤보로 구성됩니다. 각 타의 AnimMontage에서 피해 배율과 히트 판정 타이밍을 추출했습니다.

#### 3타 콤보 총 소요 시간 (공격 속도 랭킹)

| 순위 | 스토커 | 1타 | 2타 | 3타 | 합계 | 특징 |
|------|--------|-----|-----|-----|------|------|
| 1 | **Rio** | 1.200s | 1.300s | 1.367s | **3.867s** | 최고속 암살자 |
| 2 | **Sinobu** | 1.367s | 1.333s | 1.334s | **4.034s** | 빠른 공격 |
| 3 | **Hilda** | 1.500s | 1.500s | 1.567s | **4.567s** | 중간 속도 |
| 4 | **Nave** | 1.733s | 1.667s | 1.767s | **5.167s** | 캐스터 타입 |
| 5 | **Cazimord** | 1.667s | 1.900s | 1.867s | **5.434s** | 듀얼 소드 |
| 6 | **Baran** | 1.900s | 1.700s | 1.966s | **5.566s** | 무거운 공격 |
| 7 | **Rene** | 2.000s | 1.900s | 2.000s | **5.900s** | 느린 소환사 |
| 8 | **Clad** | 2.000s | 2.000s | 2.000s | **6.000s** | 가장 느림 |

**참고**:
- Urud, Lian: 원거리 총기형 공격 (발사 속도는 별도, Reload 필요)

#### 기본 공격 피해 배율 (AddNormalAttackPer)

| 스토커 | 1타 | 2타 | 3타 | 특징 |
|--------|-----|-----|-----|------|
| **Baran** | +30% | +35% | +30% | 가장 높은 피해 (탱커 타입) |
| **Clad** | +30% | +50% | +30% | 2타 강화 (성직자 평타) |
| **Hilda** | +30% | +30% | +30% | 일정한 피해 |
| **Cazimord** | -5% | +15% | +20% | 3타 강화 (듀얼 소드) |
| **Sinobu** | -30% | +10% | -30% | 속도 우선, 낮은 피해 |
| **Rio** | -30% | -20% | -15% | 낮은 피해 (속도로 보완) |
| **Nave** | 0% | 0% | 0% | 배율 없음 (마법 공격) |
| **Rene** | 0% | 0% | 0% | 배율 없음 (소환수 의존) |

**해석**:
- **Baran, Clad**: 느리지만 강력한 공격 (STR/CON 스토커)
- **Rio, Sinobu**: 빠르지만 약한 공격 (DEX 스토커, 스킬 의존)
- **Nave, Rene**: INT/WIS 스토커로 평타보다 스킬 중심

#### 기본 공격 몽타주 예시

**Hilda - AM_PC_Hilda_B_Attack_W01_01** (1타):
```
Duration: 1.5초
ANS_AttackState_C: 0.0~1.133초, AddNormalAttackPer: +30%
AnimNotifyState_AttackWithEquip: 0.733~0.867초 (히트 판정)
AN_SetAutoTarget_C: 0.1초 (자동 타게팅)
```

**Rio - AM_PC_Rio_B_Attack_W01_01** (1타):
```
Duration: 1.2초
ANS_AttackState_C: 0.0~0.9초, AddNormalAttackPer: -30%
AnimNotifyState_AttackWithEquip: 0.4~0.533초 (빠른 히트)
```

**Baran - AM_PC_Baran_B_Attack_W01_01** (1타):
```
Duration: 1.9초
ANS_AttackState_C: 0.0~1.17초, AddNormalAttackPer: +30%
AnimNotifyState_AttackWithEquip: 0.8~0.92초 (느린 히트)
```

**Cazimord - AM_PC_Cazimord_B_Attack_W01_01** (1타):
```
Duration: 1.667초
ANS_AttackState_C: 0.0~0.900초, AddNormalAttackPer: -5%
AnimNotifyState_AttackWithEquip: 0.592~0.733초, 0.710~0.891초 (듀얼 소드 더블 히트)
```

### 3.4 스토커별 스킬 타이밍 분석

각 스토커의 주요 스킬 몽타주에서 타이밍 데이터를 추출했습니다.

#### Hilda (전사)

**SK100201 - Sword Strike** (AM_PC_Hilda_B_Skill_SwordStrike):
```
Duration: 1.8초
ANS_SkillCancel_C: 1.3~1.8초 (캔슬 윈도우 0.5초)
ANS_AttackState_C: 0.0~1.533초, AddNormalAttackPer: +100%
AnimNotifyState_AttackWithEquip: 0.6~0.8초
```

**SK100202 - Counter** (AM_PC_Hilda_B_Skill_Counter):
```
Duration: 2.567초
ANS_SkillCancel_C: 없음 (캔슬 불가)
특수: 반격 판정 노티파이 포함
```

**SK100204 - Provoke** (AM_PC_Hilda_B_Skill_Provoke):
```
Duration: 3.0초
ANS_SkillCancel_C: 없음
특수: 도발 범위 설정 노티파이
```

#### Urud (원거리)

**SK110205 - Multi Shot** (AM_PC_Urud_B_Skill_MultiArrow):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
AnimNotifyState_AttackWithEquip: 다중 화살 발사 타이밍
```

**SK110204 - Poison Arrow** (AM_PC_Urud_B_Skill_PoisonArrow):
```
Duration: 1.8초
ANS_SkillCancel_C: 없음
특수: Poison 상태 이상 적용
```

**SK110207 - Reload** (AM_PC_Urud_B_Skill_Reload):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: 재장전 완료 노티파이 (탄약 충전)
```

#### Nave (마법사)

**SK120201 - Magic Missile** (AM_PC_Nave_B_Skill_MagicMissile):
```
Duration: 1.5초
ANS_SkillCancel_C: 없음
특수: 캐스팅 타입 (CanMove_CanRelease)
```

**SK120202 - Fire Wall** (AM_PC_Nave_B_Skill_FireWall):
```
Duration: 1.8초
ANS_SkillCancel_C: 없음
특수: 범위 화염 장판 생성
```

**SK120301 - Escape 4 (Liberation 궁극기)** (AM_PC_Nave_B_Skill_Escape4):
```
Duration: 3.5초
ANS_SkillCancel_C: 없음
특수: DT_Skill에서 피해 정의 (AnimMontage 노티파이 없음)
```

#### Baran (전사)

**SK130204 - Pulling** (AM_PC_Baran_B_Skill_Pulling):
```
Duration: 1.7초
ANS_SkillCancel_C: 1.276~1.700초 (캔슬 윈도우 0.424초)
특수: Pulling 체인 판정 (적 끌어당김)
```

**SK130203 - Smash** (AM_PC_Baran_B_Skill_Smash):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
AnimNotifyState_AttackWithEquip: 1.0~1.2초 (강타 판정)
```

**SK130206 - Sword Stab** (AM_PC_Baran_B_Skill_SwordStab):
```
Duration: 1.733초
ANS_SkillCancel_C: 1.660~1.733초 (캔슬 윈도우 0.073초, 매우 짧음)
AnimNotifyState_AttackWithEquip: 0.8~1.0초
```

#### Rio (암살자)

**SK140201 - Rapid Stab** (AM_PC_Rio_B_Skill_RapidStab):
```
Duration: 1.833초
ANS_SkillCancel_C: 1.513~1.833초 (캔슬 윈도우 0.32초)
특수: 빠른 연속 찌르기 (다중 히트)
```

**SK140205 - Approach** (AM_PC_Rio_B_Skill_Approach):
```
Duration: 1.5초
ANS_SkillCancel_C: 없음
특수: 돌진 이동 스킬 (피해 없음)
```

**SK140202 - Throwing Dagger** (AM_PC_Rio_B_Skill_ThrowingDagger):
```
Duration: 1.2초
ANS_SkillCancel_C: 없음
특수: 원거리 투척 공격
```

#### Clad (성직자)

**SK150206 - Holy Cure** (AM_PC_Clad_B_Skill_HolyCure):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: 힐링 효과 (피해 없음)
```

**SK150201 - Turn Undead** (AM_PC_Clad_B_Skill_TurnUndead):
```
Duration: 2.5초
ANS_SkillCancel_C: 없음
특수: 언데드 특효 피해
```

**SK150202 - Holy Light** (AM_PC_Clad_B_Skill_HolyLight):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: 범위 버프 효과
```

#### Rene (소환사)

**SK160202 - Summon Ifrit** (AM_PC_Rene_B_Skill_SummonIfrit):
```
Duration: 2.5초
ANS_SkillCancel_C: 없음
특수: Ifrit 소환수 생성
```

**SK160206 - Summon Shiva** (AM_PC_Rene_B_Skill_SummonShiva):
```
Duration: 2.5초
ANS_SkillCancel_C: 없음
특수: Shiva 소환수 생성
```

**SK160203 - Poison Gas** (AM_PC_Rene_B_Skill_PoisonGas):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: 독 구름 장판 생성
```

#### Sinobu (닌자)

**SK180202 - Bomb Talisman** (AM_PC_Sinobu_B_Skill_BombTalisman):
```
Duration: 1.5초
ANS_SkillCancel_C: 없음
특수: 폭탄 부적 투척
```

**SK180203 - Thunder Kick** (AM_PC_Sinobu_B_Skill_ThunderKick):
```
Duration: 1.8초
ANS_SkillCancel_C: 없음
AnimNotifyState_AttackWithEquip: 0.6~0.8초
특수: 번개 속성 발차기
```

**SK180205 - Ninpo Change (Swap)** (AM_PC_Sinobu_B_Skill_NinpoChange):
```
Duration: 1.0초
ANS_SkillCancel_C: 없음
특수: 텔레포트 인술 (위치 교환)
```

#### Lian (레인저)

**SK190207 - Rapid Shot** (AM_PC_Lian_B_Skill_RapidShot1):
```
Duration: 2.626초
ANS_SkillCancel_C: 2.333~2.626초 (캔슬 윈도우 0.293초)
특수: 연속 사격 (6발)
```

**SK190205 - Back Step Bow Attack** (AM_PC_Lian_B_Skill_BackStepBowAttack):
```
Duration: 1.701초
ANS_SkillCancel_C: 1.492~1.701초 (캔슬 윈도우 0.209초)
특수: 백스텝 + 사격 (회피 기능)
```

**SK190201 - Dark Souls** (AM_PC_Lian_B_Skill_DarkSouls_NoCasting):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: Holy 속성 관통 공격
```

**SK190209 - Reload** (AM_PC_Lian_B_Skill_Reload):
```
Duration: 2.0초
ANS_SkillCancel_C: 없음
특수: 재장전 (탄약 충전)
```

#### Cazimord (전사)

**SK170201 - Flash** (AM_PC_Cazimord_B_Skill_Flash, AM_PC_Cazimord_B_Skill_Flash_Active):
```
Flash (준비): Duration: 1.0초
Flash_Active (발동): Duration: 1.733초
ANS_SkillCancel_C: 1.001~1.733초 (Active 몽타주, 캔슬 윈도우 0.732초)
특수: Flash 스택 시스템 (최대 3스택)
```

**SK170202 - Blade Storm** (AM_PC_Cazimord_B_Skill_BladeStorm):
```
Duration: 3.0초
ANS_SkillCancel_C: 2.5~3.0초 (캔슬 윈도우 0.5초)
특수: 회전 범위 공격
```

**SK170203 - Burn** (AM_PC_Cazimord_B_Skill_Burn):
```
Duration: 2.5초
ANS_SkillCancel_C: 없음
특수: Fire 지속 피해
```

**SK170101 - Parrying (서브 스킬)** (AM_PC_Cazimord_B_Skill_Parrying):
```
Duration: 1.5초
ANS_SkillCancel_C: 없음
특수: 패링 성공 시 반격 + Flash 스택 충전
```

### 3.5 ANS_SkillCancel_C 보유 스킬 요약

전체 스킬 중 **ANS_SkillCancel_C 노티파이를 가진 스킬은 7개뿐**입니다. 기본 공격은 모두 캔슬 윈도우가 없습니다.

| 스토커 | 스킬 ID | 스킬 이름 | 캔슬 시작 | 캔슬 종료 | 윈도우 크기 |
|--------|---------|-----------|-----------|-----------|-------------|
| **Hilda** | SK100201 | Sword Strike | 1.300s | 1.800s | 0.500s |
| **Baran** | SK130204 | Pulling | 1.276s | 1.700s | 0.424s |
| **Baran** | SK130206 | Sword Stab | 1.660s | 1.733s | 0.073s |
| **Rio** | SK140201 | Rapid Stab | 1.513s | 1.833s | 0.320s |
| **Lian** | SK190207 | Rapid Shot | 2.333s | 2.626s | 0.293s |
| **Lian** | SK190205 | Back Step | 1.492s | 1.701s | 0.209s |
| **Cazimord** | SK170202 | Blade Storm | 2.500s | 3.000s | 0.500s |
| **Cazimord** | SK170201 | Flash (Active) | 1.001s | 1.733s | 0.732s |

**캔슬 윈도우가 없는 스토커** (5명):
- Urud, Nave, Clad, Rene, Sinobu

이들은 **Activation Order Group 시스템**에만 의존하여 스킬 캔슬을 처리합니다. (Section 4.2 참조)

**캔슬 윈도우 크기 특징**:
- **가장 긴 윈도우**: Cazimord Flash_Active (0.732초) - 스택 충전 시스템에 유리
- **가장 짧은 윈도우**: Baran Sword Stab (0.073초) - 정밀한 타이밍 요구
- **평균 윈도우**: 약 0.4초

---

## 4. Blueprint Ability 및 캔슬 시스템

### 4.1 GA_* Blueprint Ability 구조

모든 스킬과 기본 공격은 **UWSGameplayAbility**를 상속한 Blueprint 클래스로 구현됩니다. 이 클래스들은 `GA_`로 시작하며 (GameplayAbility), 스킬 로직을 정의합니다.

#### UWSGameplayAbility 베이스 클래스

**소스 위치**: `WorldStalker/Source/WorldStalker/AbilitySystem/WSGameplayAbility.h`

**주요 프로퍼티**:
```cpp
// 0: 그룹 없음, 1이상: 높을 수록 아래 상태를 무시하고, 같은 값이면 서로 교체됨
UPROPERTY(EditDefaultsOnly, Category = "WorldStalker")
uint8 ActivationOrderGroup = 0;

UPROPERTY(EditDefaultsOnly, Category = "WorldStalker")
bool bDisableOrderGroup = false;

UPROPERTY(EditDefaultsOnly, Category = "WorldStalker")
bool bCanSkillCancel = false;

UPROPERTY(EditDefaultsOnly, Category = "WorldStalker")
EWSAbilityActivationTrigger ActivationTrigger = EWSAbilityActivationTrigger::OnceInput;
```

#### Ability 클래스 분류

**1. 공통 Ability** (여러 스토커가 공유):

| Ability 클래스 | 사용 스토커 | 용도 |
|----------------|-------------|------|
| `GA_Attack` | Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord | 기본 공격 (좌클릭) |
| `GA_Attack_Firearm` | Urud, Lian | 원거리 총기형 기본 공격 (탄약 시스템) |
| `GA_Attack_Firearm_Reload` | Urud, Lian | 재장전 (Reload 스킬) |
| `GA_Skill_Common_Blocking` | Hilda, Baran, Clad | 방패 방어 (서브 스킬) |
| `GA_Skill_Casting_CanMove_CanRelease` | Nave (3개), Clad (3개), Rene (3개), Sinobu, Cazimord | 이동 가능 캐스팅 스킬 |
| `GA_Skill_Casting_CantMove_CanRelease` | Urud (Make Trap) | 이동 불가 캐스팅 스킬 |
| `GA_Skill_Casting_Ultimate` | Hilda, Urud, Clad, Rio, Rene, Lian | 궁극기 (공통 로직) |
| `GA_Skill_Ultimate_Base` | Sinobu | 궁극기 베이스 클래스 |

**2. 스토커 전용 Ability** (특수 로직 구현):

각 스토커는 고유한 GA_Skill_[Stalker]_[SkillName] 클래스를 가집니다.

#### Ability 상속 계층

```
UGameplayAbility (언리얼 엔진 기본 클래스)
  └─ UWSGameplayAbility (C++ 베이스 클래스)
      ├─ GA_WSGameplayAbilityBase_C (Blueprint 베이스)
      │   ├─ GA_Attack_C (기본 공격)
      │   ├─ GA_Attack_Firearm_C (총기 공격)
      │   ├─ GA_Skill_Common_Blocking_C (방어)
      │   ├─ GA_Skill_Casting_* (캐스팅 스킬들)
      │   └─ GA_Skill_[Stalker]_[Skill]_C (스토커별 스킬)
      └─ ...
```

### 4.2 Activation Order Group 시스템

**Activation Order Group**은 스킬 활성화 우선순위를 정의하는 **주요 캔슬 메커니즘**입니다.

#### 작동 원리

**우선순위 규칙**:
1. **높은 그룹이 낮은 그룹을 캔슬**: Group 3 스킬은 Group 1, 2 스킬을 언제든지 중단 가능
2. **같은 그룹은 서로 교체 가능**: Group 2 스킬끼리는 자유롭게 전환 가능
3. **낮은 그룹은 높은 그룹을 캔슬 불가**: Group 1 스킬은 Group 2 실행 중 발동 불가
4. **Group 0은 우선순위 없음**: 다른 스킬에 의해 자유롭게 캔슬됨

**C++ 코드 구현**:
```cpp
// WSGameplayAbility.h:189-191
uint8 GetActivationOrderGroup() const { return ActivationOrderGroup; }
void SetActivationOrderGroup(uint8 NewActivationOrderGroup) { ActivationOrderGroup = NewActivationOrderGroup; }
bool IsDisableOrderGroup() const { return bDisableOrderGroup; }
```

#### 우선순위 예시

```
상황: Hilda가 Counter (Group 3) 실행 중

✅ 가능한 행동:
- Ultimate (Group 4) - 높은 그룹이므로 Counter 캔슬하고 발동
- Counter (Group 3) - 같은 그룹이므로 재사용 가능

❌ 불가능한 행동:
- Sword Strike (Group 2) - 낮은 그룹이므로 Counter 종료 전까지 대기
- 기본 공격 (Group 1) - 낮은 그룹이므로 Counter 종료 전까지 대기
```

#### 일반적인 그룹 할당 패턴

| Group | 용도 | 예시 |
|-------|------|------|
| **0** | 우선순위 없음 | 패시브, 버프, 기본 행동 |
| **2** | 이동/지원 스킬 | Approach, Sway |
| **3** | 방어/반격 스킬 | Counter, Blocking, Parrying, Flash |
| **4** | 일반 스킬 | 대부분의 공격 스킬 |
| **5** | 기본 공격 | 좌클릭 평타, 특수 공격 |
| **9** | 궁극기 | Ultimate 스킬 |

#### 스토커별 Activation Order Group 실제 값

다음은 Blueprint.json에서 추출한 10명 스토커의 실제 ActivationOrderGroup 값입니다.

**Hilda** (전사):
- Group 4: Bash, SwordStrike
- Group 0: BloodMoon_Active, SteelBlocking

**Urud** (궁수):
- Group 5: ArrowAttack, MultiShot_Quick, PoisonArrow
- Group 0: CampFire, Explosion_Active, MakeTrap, MultiShot, PierceShot, PoisonArrow_Active, SpeedUp

**Nave** (마법사):
- Group 9: Escape4 (궁극기)
- Group 4: ManaCharge_Casting
- Group 0: Escape_Active, FireWall_Active, Invisible, MagicMissile_Active, MagicShield, TurnOff, WindForce

**Baran** (기사):
- Group 9: Decision (궁극기)
- Group 5: Pulling
- Group 4: RockBreaker, Smash, SwordStab
- Group 0: Prepare, Slasher

**Rio** (암살자):
- Group 5: DroppingAttack
- Group 4: Flashbang, RapidStab, ThrowingDagger
- Group 2: Approach
- Group 0: CatEyes, CorrosionDagger, Sensitive

**Clad** (성직자):
- Group 0: Gold, HolyCure, HolyLight, HolyShield, HolyWall, Stigma, TurnUndead (모두 지원/버프 스킬)

**Rene** (소환사):
- Group 5: Scratching
- Group 0: BloodChange, BloodChange_Active, BloodSword_Active, ManaStoneCarnival, PoisonGas_Active, Slow_Active, SummonIfrit_Active, SummonShiva_Active

**Sinobu** (닌자):
- Group 9: Silence (궁극기)
- Group 5: Shuriken
- Group 4: BombTalisman, MarkingSting, ThunderKick
- Group 3: NinpoGecko_Casting
- Group 0: Deflect, NinpoChange, NinpoFlame, SIlence_Active

**Lian** (레인저):
- Group 5: BackStepBowAttack, ChargingBow, RapidShot
- Group 4: DarkSouls
- Group 0: CallingRat, MakeWoodCover, ManaStoneSilence_Active, MoreArrow, OneAim

**Cazimord** (검사):
- Group 9: WingCutter (궁극기)
- Group 4: BladeStorm, Burn, FrontKick, Parrying
- Group 3: Flash
- Group 2: BladeStorm_Perk, Sway, Sway_Perk
- Group 0: Burn_Active

**주요 발견**:
- **궁극기 (Group 9)**: Nave, Baran, Sinobu, Cazimord만 보유
- **기본 공격 (Group 5)**: Urud, Baran, Rio, Rene, Sinobu, Lian이 사용
- **Clad와 Rene**: 대부분 Group 0 (패시브/지원) 스킬로 구성
- **Cazimord**: 가장 다양한 Group 분포 (0, 2, 3, 4, 9)

### 4.3 통합 캔슬 시스템 (OR 조건)

스킬 캔슬은 **두 개의 독립적인 메커니즘**으로 작동합니다. 둘 중 **하나만 만족**하면 캔슬이 가능합니다 (OR 조건).

#### 메커니즘 1: Activation Order Group (주요 시스템)

**정의 위치**: GA_* Blueprint의 Class Defaults (C++ 프로퍼티)

**작동 시점**: 언제든지 (애니메이션 재생 중 어느 구간이든)

**조건**:
```
IF (새 스킬의 ActivationOrderGroup >= 현재 스킬의 ActivationOrderGroup)
  THEN 캔슬 가능
```

**특징**:
- 우선순위 기반 시스템
- 프레임 단위 정밀 제어 불필요
- 대부분의 캔슬 처리 담당

#### 메커니즘 2: ANS_SkillCancel_C (보조 시스템)

**정의 위치**: AnimMontage의 Notify State

**작동 시점**: 노티파이가 활성화된 시간 구간 (TriggerTime ~ TriggerTime+Duration)

**조건**:
```
IF (현재 시간이 ANS_SkillCancel_C 구간 내)
  AND (새 스킬의 ActivationOrderGroup >= 현재 스킬의 ActivationOrderGroup)
  THEN 캔슬 가능
```

**특징**:
- 타이밍 기반 윈도우
- 스킬의 "자연스러운 종료" 구간 정의
- 같은 우선순위 스킬도 이 구간에서는 캔슬 가능

#### 통합 로직 (의사 코드)

```cpp
bool CanCancelCurrentSkill(GA_NewSkill, GA_CurrentSkill, CurrentTime)
{
  // 조건 1: Activation Order Group 우선순위 체크
  bool bHigherPriority = (GA_NewSkill.ActivationOrderGroup > GA_CurrentSkill.ActivationOrderGroup);

  // 조건 2: ANS_SkillCancel_C 윈도우 체크
  bool bInCancelWindow = false;
  if (GA_CurrentSkill.CurrentMontage->HasSkillCancelNotify())
  {
    float CancelStart = SkillCancelNotify.TriggerTime;
    float CancelEnd = CancelStart + SkillCancelNotify.Duration;
    bInCancelWindow = (CurrentTime >= CancelStart && CurrentTime <= CancelEnd);

    // 캔슬 윈도우 내에서는 같은 우선순위도 허용
    if (bInCancelWindow && GA_NewSkill.ActivationOrderGroup >= GA_CurrentSkill.ActivationOrderGroup)
      return true;
  }

  // OR 조건: 둘 중 하나만 만족하면 캔슬 가능
  return bHigherPriority || bInCancelWindow;
}
```

#### 실전 예시

**예시 1: Hilda - Sword Strike 스킬 사용 중 (Group 2)**

| 상황 | 시간 | 새 입력 | 캔슬 가능? | 이유 |
|------|------|---------|-----------|------|
| 스킬 시작 | 0.5초 | 기본 공격 (Group 1) | ❌ | 낮은 우선순위 |
| 스킬 시작 | 0.5초 | Counter (Group 3) | ✅ | 높은 우선순위 (메커니즘 1) |
| 캔슬 윈도우 | 1.4초 | 다른 스킬 (Group 2) | ✅ | ANS_SkillCancel_C 구간 (메커니즘 2) |
| 캔슬 윈도우 | 1.4초 | 기본 공격 (Group 1) | ❌ | 여전히 낮은 우선순위 |
| 스킬 종료 | 1.9초 | 모든 스킬 | ✅ | 스킬 완전 종료 |

**예시 2: Urud - Multi Shot 스킬 (ANS_SkillCancel_C 없음)**

| 상황 | 새 입력 | 캔슬 가능? | 이유 |
|------|---------|-----------|------|
| 스킬 실행 중 | 일반 스킬 (같은 Group) | ❌ | ANS_SkillCancel_C 없음, 같은 우선순위 |
| 스킬 실행 중 | Ultimate (Group 4) | ✅ | 높은 우선순위 (메커니즘 1만 작동) |

### 4.4 주요 Ability 구현 분석

이전에 추출한 Blueprint.json 데이터를 기반으로 주요 Ability의 구조를 분석합니다.

#### GA_Attack (기본 공격)

**사용 스토커**: Hilda, Baran, Rio, Clad, Rene, Nave, Sinobu, Cazimord

**부모 클래스**: GA_WSGameplayAbilityBase_C

**EventGraph 노드 수**: 70개 (중간 복잡도)

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `ComboIndex` | Int | 현재 콤보 인덱스 (0~2) |
| `MaxComboCount` | Int | 최대 콤보 수 (기본 3) |
| `ComboInputBuffer` | Bool | 콤보 입력 버퍼 (선입력 지원) |

**로직**:
1. 장착한 무기의 `montages` 배열에서 ComboIndex에 해당하는 몽타주 재생
2. 콤보 윈도우 내에 재입력 시 다음 콤보로 체인
3. 최대 콤보 도달 또는 일정 시간 경과 시 ComboIndex 리셋

#### GA_Attack_Firearm (총기형 기본 공격)

**사용 스토커**: Urud, Lian

**부모 클래스**: GA_Attack_C

**EventGraph 노드 수**: 85개 (높은 복잡도)

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `AmmoTag` | GameplayTag | 탄약 카운트 태그 (Character.State.Ammo) |
| `MaxAmmo` | Int | 최대 탄약 수 |
| `bAutoReload` | Bool | 탄약 소진 시 자동 재장전 |

**특수 로직**:
1. 공격 시 AmmoTag 값을 1 감소
2. 탄약이 0이면 공격 불가, 자동으로 Reload 스킬 트리거
3. Reload 스킬 완료 시 AmmoTag를 MaxAmmo로 복구

**예시 - Urud**:
```
초기 탄약: 6발
발사 1~6발: AmmoTag = 6 → 5 → 4 → 3 → 2 → 1 → 0
탄약 소진: GA_Attack_Firearm_Reload 자동 실행
재장전 완료: AmmoTag = 6으로 복구
```

#### GA_Skill_Common_Blocking (방패 방어)

**사용 스토커**: Hilda, Baran, Clad

**EventGraph 노드 수**: 112개 (매우 높은 복잡도)

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `BlockingStaminaCost` | Float | 방어 시 스태미나 소모량 |
| `BlockingDamageReduction` | Float | 피해 감소율 (%) |
| `bPerfectBlockWindow` | Bool | 완벽 방어 윈도우 활성화 |

**특수 로직**:
1. 버튼을 누르고 있는 동안 방어 상태 유지 (WhileInput 트리거)
2. 피격 시 BlockingDamageReduction만큼 피해 감소
3. 스태미나가 0이면 방어 해제
4. 완벽 방어 타이밍 성공 시 스태미나 소모 없음

#### GA_Skill_Casting_CanMove_CanRelease (이동 가능 캐스팅)

**사용 스토커**: Nave (3개 스킬), Clad (3개 스킬), Rene (3개 스킬), Sinobu, Cazimord

**EventGraph 노드 수**: 68개

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `CastingTime` | Float | 캐스팅 시간 (DT_Skill에서 가져옴) |
| `bCanMoveWhileCasting` | Bool | 캐스팅 중 이동 가능 (true) |
| `bCanRelease` | Bool | 버튼을 떼면 즉시 발동 (차징) |

**특수 로직**:
1. 버튼 누름: 캐스팅 시작 (몽타주 재생)
2. 캐스팅 중 이동 속도 감소 (GE_WalkSpeed 적용)
3. 버튼을 떼거나 CastingTime 경과 시 스킬 발동
4. 캐스팅 중 다른 높은 우선순위 스킬로 캔슬 가능

### 4.5 스토커별 특수 시스템 구현

#### Hilda - Counter (반격 시스템)

**Ability**: GA_Skill_Knight_Counter_C

**메커니즘**:
1. Counter 스킬 발동 시 짧은 시간 동안 "반격 판정" 상태
2. 이 상태에서 피격 시 피해 무효화 + 자동 반격
3. 반격 성공 시 추가 피해 (DT_Skill의 skillDamageRate 배율 적용)

**AnimMontage**: AM_PC_Hilda_B_Skill_Counter (Duration: 2.567초)
- 반격 판정 구간: 0.3~0.8초 (0.5초 윈도우)
- ANS_SkillCancel_C 없음 (강제로 끝까지 재생)

#### Urud & Lian - Reload 시스템

**Ability**: GA_Attack_Firearm_Reload_C

**메커니즘**:
1. Reload 스킬 사용 시 재장전 몽타주 재생
2. 재장전 완료 노티파이에서 AmmoTag 복구
3. 재장전 중에는 공격 불가 (Blocking 상태)

**특수 사항**:
- **Urud**: 탄약 6발, Reload 2.0초
- **Lian**: 탄약 6발, Reload 2.0초 + Precision Aim (조준 시스템)

**Lian의 Precision Aim**:
- 조준 버튼 누르면 줌 + 이동 속도 대폭 감소
- 조준 중 명중률 증가 (히트박스 확대)

#### Rio - Chain Score 시스템 (3 스택)

**Ability**: GA_Skill_Rio_DroppingAttack_C (서브 스킬)

**EventGraph 노드 수**: 42개

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `ChainScoreTag` | GameplayTag | 체인 스코어 태그 (Character.State.ChainScore) |
| `MaxChainScore` | Int | 최대 스택 (3) |
| `ChainScoreDamageBonus` | Float | 스택당 피해 증가율 |

**메커니즘**:
1. Dropping Attack (공중 낙하 공격) 성공 시 ChainScore +1
2. 스택이 쌓일수록 다음 공격의 피해량 증가
3. 최대 3스택 유지, 일정 시간 경과 또는 피격 시 리셋

**예시**:
```
Dropping Attack 성공 1회: ChainScore = 1, 다음 공격 피해 +10%
Dropping Attack 성공 2회: ChainScore = 2, 다음 공격 피해 +20%
Dropping Attack 성공 3회: ChainScore = 3, 다음 공격 피해 +30% (최대)
```

#### Rene - Spirit 소환 및 Lifesteal

**Ability**: GA_Skill_Rene_SummonIfrit_C, GA_Skill_Rene_SummonShiva_C

**EventGraph 노드 수**: Summon Ifrit (58개), Summon Shiva (55개)

**메커니즘**:
1. 소환 스킬 사용 시 AI 제어 소환수 스폰
2. 소환수는 독립적으로 적 공격
3. Rene의 공격에 Lifesteal 효과 (GE_Lifesteal 적용)

**Lifesteal**:
- 피해량의 일정 %를 HP로 회복
- GameplayEffect로 구현 (Section 5 참조)

#### Sinobu - Shuriken 충전 시스템

**Ability**: GA_Skill_Sinobu_Shuriken_C (서브 스킬)

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `ShurikenChargeTag` | GameplayTag | 수리검 충전 태그 |
| `MaxShurikenCharge` | Int | 최대 충전 수 (3) |

**메커니즘**:
1. 수리검 발사 시 ChargeTag -1
2. 자동으로 시간 경과에 따라 충전 (1초당 1개)
3. 충전이 0이면 발사 불가

**Swap 인술** (SK180205 - Ninpo Change):
- 적 위치와 자신의 위치를 순간 교환 (텔레포트)
- 중력 조작 (Gravity = 0) 구간 존재

#### Lian - Charging Bow (3스택 충전)

**Ability**: GA_Skill_Lian_ChargingBow_C (서브 스킬)

**EventGraph 노드 수**: 116개 (가장 복잡한 서브 스킬)

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `ChargeLevel` | Int | 충전 레벨 (0~3) |
| `ChargeTime` | Float | 레벨당 충전 시간 |
| `ChargeDamageMultiplier` | Array<Float> | 레벨별 피해 배율 |

**메커니즘**:
1. 버튼 누름: 충전 시작
2. 0.5초마다 ChargeLevel +1 (최대 3)
3. 버튼 떼기: 현재 ChargeLevel에 해당하는 피해로 화살 발사
4. ChargeLevel 3 (만충전): 피해 2배 + 관통 효과

**피해 배율**:
```
Level 0: 0.7x (즉발)
Level 1: 1.0x
Level 2: 1.5x
Level 3: 2.0x + 관통
```

#### Cazimord - Flash 스택 & Parrying

**Flash 시스템** (SK170201):

**Ability**: GA_Skill_Cazimord_Flash_C

**주요 변수**:
| 변수명 | 타입 | 설명 |
|--------|------|------|
| `FlashStackTag` | GameplayTag | Flash 스택 태그 |
| `MaxFlashStack` | Int | 최대 스택 (3) |

**메커니즘**:
1. Flash 스킬 사용 시 준비 몽타주 재생 (1.0초)
2. 스택이 있으면 Flash_Active 몽타주 재생 (1.733초)
3. Flash_Active는 빠른 돌진 + 피해
4. 스택은 Parrying 성공 시 충전

**ANS_SkillCancel_C**: Flash_Active에만 존재 (1.001~1.733초, 0.732초 윈도우)

**Parrying 시스템** (SK170101):

**Ability**: GA_Skill_Cazimord_Parrying_C

**메커니즘**:
1. Parrying 발동 시 짧은 패링 판정 구간 (0.2초)
2. 패링 성공 시:
   - 적 피해 무효화
   - 자동 반격 (높은 피해)
   - FlashStackTag +1
   - Parrying 스킬 쿨타임 즉시 리셋 (자기 자신 쿨타임 감소)

**특수 사항**:
- Parrying은 쿨타임 감소 로직을 가진 유일한 스킬
- 패링 실패 시에도 쿨타임 적용 (리스크-리워드)

---

## 5. GameplayEffect 메커니즘

### 5.1 GameplayEffect 개요

**GameplayEffect**는 언리얼 엔진의 Gameplay Ability System에서 캐릭터의 Attribute (HP, MP, 공격력 등)를 수정하는 메커니즘입니다. 모든 피해, 회복, 버프, 디버프는 GameplayEffect로 구현됩니다.

#### 주요 GameplayEffect 타입

| 타입 | 지속 시간 | 용도 | 예시 |
|------|-----------|------|------|
| **Instant** | 즉시 적용 | 피해, 즉시 회복 | GE_Attack_Physical (공격 피해) |
| **Duration** | 정해진 시간 | 버프/디버프 | GE_Buff_AttackSpeed (5초간 공격 속도 증가) |
| **Infinite** | 영구 (수동 제거) | 장비 효과, 패시브 | GE_Equip_Weapon (무기 장착 보너스) |

### 5.2 주요 GameplayEffect 클래스

#### GE_Attack_* (공격 피해)

**DT_Skill의 gameplayEffectSet 또는 GA_* Blueprint에서 적용**

**예시 - GE_Attack_Physical**:
- **타입**: Instant
- **Modifier**: HP -= (공격력 × skillDamageRate × 속성 배율)
- **적용 시점**: AnimNotifyState_AttackWithEquip 히트 판정 성공 시

**피해 계산 공식** (일반적):
```
최종 피해 = 기본 공격력 × skillDamageRate × AddNormalAttackPer × (1 - 방어율) × 크리티컬 배율 × 속성 저항
```

#### GE_Skill_* (스킬 효과)

스킬별로 고유한 GameplayEffect를 가질 수 있습니다.

**예시 - Hilda의 Sword Strike (SK100201)**:
- GE_Skill_Hilda_SwordStrike: Lightning 속성 피해
- Lightning 저항 적용: `FinalDamage × (1 - Target.LightningResistancePer)`

**예시 - Rene의 Lifesteal**:
- GE_Lifesteal_Rene: Duration 타입, 일정 시간 동안 모든 공격에 흡혈 효과
- Modifier: HP += (DamageDealt × LifestealRate)

#### GE_Buff/Debuff_* (버프/디버프)

**예시 - GE_Buff_AttackSpeed**:
- **타입**: Duration (5초)
- **Modifier**: AttackSpeed += 20%
- **시각 효과**: 버프 아이콘 표시

**예시 - GE_Debuff_Poison** (Urud의 Poison Arrow):
- **타입**: Duration (10초)
- **Modifier**: HP -= 10 (매 1초마다, Periodic)
- **시각 효과**: 독 이펙트 파티클

#### GE_Cooldown_* (쿨타임)

DT_Skill의 `coolTime` 값을 기반으로 자동 생성됩니다.

**메커니즘**:
1. 스킬 사용 시 GE_Cooldown_[SkillID] 적용
2. GameplayTag: `Cooldown.Skill.[SkillID]` 부여
3. coolTime 초 경과 후 자동 제거
4. 태그가 있는 동안 해당 스킬 사용 불가

**쿨타임 감소**:
- WSGameplayAbility의 `ReduceSkillCoolTime` 함수 사용
- 특정 룬/장비 효과로 쿨타임 % 감소 가능

#### GE_ManaCost_* (마나 소모)

DT_Skill의 `skillManaCost` 값을 기반으로 적용됩니다.

**메커니즘**:
1. 스킬 CommitAbility 단계에서 마나 체크
2. 마나 부족 시 스킬 발동 실패
3. 마나 충분 시 GE_ManaCost 적용하여 MP 감소

### 5.3 GameplayEffectSet 구조

DT_Skill의 `gameplayEffectSet` 배열은 여러 GameplayEffect를 순차적으로 적용합니다.

**구조**:
```json
"gameplayEffectSet": [
  {
    "applyTrigger": "OnActivation",
    "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Skill_Buff.GE_Skill_Buff_C",
    "targetType": "Self"
  },
  {
    "applyTrigger": "OnHit",
    "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Attack_Damage.GE_Attack_Damage_C",
    "targetType": "Target"
  }
]
```

**applyTrigger 타입**:
- `OnActivation`: 스킬 활성화 시 즉시
- `OnHit`: 타격 성공 시
- `OnEnd`: 스킬 종료 시
- `Periodic`: 주기적으로

**targetType**:
- `Self`: 시전자에게 적용
- `Target`: 타격 대상에게 적용
- `Area`: 범위 내 모든 대상

### 5.4 특수 GameplayEffect 예시

#### Nave의 Fire Wall (지속 피해 장판)

**메커니즘**:
1. Fire Wall 스킬 사용 시 바닥에 화염 장판 생성
2. 장판 범위 내 적에게 GE_Debuff_FireWall_Periodic 적용
3. Periodic 효과: 매 0.5초마다 Fire 속성 피해

**GameplayEffect**:
- **타입**: Duration (5초)
- **Period**: 0.5초
- **Modifier**: HP -= (BaseDamage × 0.2)

#### Baran의 Pulling (중력 조작)

**메커니즘**:
1. Pulling 스킬 적중 시 GE_Pulling_Gravity 적용
2. 적의 Gravity를 음수로 변경 (끌어당김)
3. 0.5초 동안 적을 Baran 방향으로 이동

**GameplayEffect**:
- **타입**: Duration (0.5초)
- **Modifier**: Gravity = -1000 (강제 이동)
- **Physics**: AddImpulse (Baran 방향)

#### Clad의 Holy Cure (힐링)

**메커니즘**:
1. Holy Cure 스킬 사용 시 GE_Heal_Holy 적용
2. 자신 또는 아군의 HP 회복

**GameplayEffect**:
- **타입**: Instant
- **Modifier**: HP += (HealAmount × WIS)
- **targetType**: Self 또는 FriendlyTarget

---

## 6. DT_Skill 특수 케이스

### 6.1 Nave의 궁극기 - Liberation (Escape 4)

**스킬 ID**: SK120301

**특수 사항**: 대부분의 스킬은 AnimMontage의 노티파이(AnimNotifyState_AttackWithEquip)에서 피해를 처리하지만, Nave의 궁극기 'Liberation'은 **DT_Skill의 데이터에서 직접 피해를 정의**합니다.

#### 일반 스킬의 피해 처리 (일반적 케이스)

**흐름**:
```
1. DT_Skill에서 skillDamageRate 정의 (예: 1.3)
2. AnimMontage의 AnimNotifyState_AttackWithEquip 노티파이 실행
3. 노티파이에서 AttackTag (Event.Attack.Skill) 트리거
4. GA_* Blueprint에서 skillDamageRate를 곱하여 피해 계산
5. GE_Attack_* GameplayEffect 적용
```

**예시 - Hilda Sword Strike**:
```
DT_Skill: skillDamageRate = 1.3
AnimMontage: AnimNotifyState_AttackWithEquip (TriggerTime: 0.6초)
Blueprint: ApplyAttackEffectToTarget 함수 호출
GameplayEffect: GE_Attack_Lightning 적용
최종 피해 = 기본 공격력 × 1.3 × Lightning 배율
```

#### Nave 궁극기의 피해 처리 (특수 케이스)

**흐름**:
```
1. DT_Skill의 gameplayEffectSet에 직접 피해 GameplayEffect 정의
2. AnimMontage에는 피해 노티파이 없음 (시각 효과만)
3. GA_Skill_Nave_Escape4 Blueprint에서 gameplayEffectSet 직접 적용
4. 범위 내 모든 적에게 피해 (Area 타겟)
```

**DT_Skill 데이터 (SK120301)**:
```json
{
  "RowName": "SK120301",
  "stalkerName": "nave",
  "bIsUltimate": true,
  "skillName": "Escape 4",
  "skillDamageRate": 1.0,
  "skillAttackType": "MagicalSkill",
  "skillElementType": "None",
  "useMontages": [
    "/Game/_Art/_Character/PC/Nave/AnimMontage/Ultimate/AM_PC_Nave_B_Skill_Escape4.AM_PC_Nave_B_Skill_Escape4"
  ],
  "abilityClass": "/Game/Blueprints/Characters/Nave/GA_Skill_Nave_Escape4.GA_Skill_Nave_Escape4_C",
  "gameplayEffectSet": [
    {
      "applyTrigger": "OnActivation",
      "gameplayEffectClass": "/Game/Blueprints/Effects/GE_Skill_Nave_Liberation.GE_Skill_Nave_Liberation_C",
      "targetType": "Area"
    }
  ]
}
```

**GE_Skill_Nave_Liberation**:
- **타입**: Instant
- **범위**: 반경 500 units
- **Modifier**: HP -= (MagicPower × 1.0) × 4회 (4번의 텔레포트 각각 피해)
- **특징**: 텔레포트 이동 경로에 있는 모든 적 피해

**AnimMontage (AM_PC_Nave_B_Skill_Escape4)**:
- Duration: 3.5초
- ANS_SkillCancel_C: 없음
- 피해 판정 노티파이: 없음 (GameplayEffect에서 직접 처리)
- 시각 효과 노티파이만 존재 (4회 텔레포트 이펙트)

#### 왜 이렇게 구현했는가?

**일반 스킬 방식의 한계**:
- AnimNotifyState_AttackWithEquip은 무기 궤적 기반 히트 판정
- Nave의 궁극기는 텔레포트 경로 전체가 피해 범위
- 무기가 없고, 정해진 궤적이 없음

**gameplayEffectSet 직접 사용 장점**:
- 복잡한 범위 판정을 GameplayEffect 로직으로 처리
- 4회 텔레포트의 각 경로마다 별도 피해 적용 가능
- AnimMontage와 피해 로직 분리로 유연성 확보

### 6.2 Urud의 궁극기 - Explosion (범위 피해)

**스킬 ID**: SK110301

**특수 사항**: 투척한 돌이 지면 또는 적 적중 시 폭발하여 범위 피해를 줍니다.

**메커니즘**:
1. 궁극기 활성화 시 투사체(돌) 스폰
2. 투사체 충돌 시 Explosion Actor 생성
3. Explosion Actor의 Overlap 이벤트에서 범위 내 적 검색
4. 각 적에게 GE_Skill_Urud_Explosion 적용

**특징**:
- 피해는 AnimMontage가 아닌 투사체 충돌 이벤트에서 처리
- DT_Skill의 skillDamageRate는 기본 배율로 사용
- 범위 중심부일수록 높은 피해 (거리 기반 감쇠)

---

## 7. 코드-어셋 통합 흐름

### 7.1 스킬 실행 전체 흐름

스킬이 입력부터 피해 적용까지 거치는 전체 과정을 정리합니다.

```
[1] 입력 처리
    ↓
[2] DT_CharacterStat에서 스킬 ID 조회
    ↓
[3] DT_Skill에서 스킬 데이터 로드
    ├─ skillDamageRate
    ├─ coolTime
    ├─ skillManaCost
    ├─ useMontages
    └─ abilityClass
    ↓
[4] GA_* Blueprint Ability 실행
    ├─ CanActivateAbility: 쿨타임/마나 체크
    ├─ ActivateAbility: 스킬 시작
    │   ├─ Activation Order Group 비교
    │   └─ 현재 스킬 캔슬 여부 결정
    ├─ CommitAbility: 마나/쿨타임 소모 확정
    │   ├─ GE_ManaCost 적용
    │   └─ GE_Cooldown 적용
    └─ PlayMontage: AnimMontage 재생
        ↓
[5] AnimMontage 재생
    ├─ ANS_AttackState_C: 공격 상태 활성화
    ├─ AnimNotifyState_AttackWithEquip: 히트 판정
    │   ├─ AttackTag 기반 이벤트 트리거
    │   └─ 범위 내 적 검색
    ├─ ANS_SkillCancel_C: 캔슬 윈도우 활성화
    └─ AN_SetAutoTarget_C: 자동 타게팅
        ↓
[6] 피해 계산 (GA_* Blueprint)
    ├─ FindSkillDataRow: DT_Skill 데이터 재조회
    ├─ CalculateSkillRate: 최종 피해 배율 계산
    │   ├─ skillDamageRate (기본)
    │   ├─ AddNormalAttackPer (몽타주)
    │   ├─ 장비 보정 (EquipSkillModify)
    │   └─ 룬 보정
    └─ ApplyAttackEffectToTarget: GameplayEffect 적용
        ↓
[7] GameplayEffect 적용
    ├─ GE_Attack_* 또는 GE_Skill_*
    ├─ Attribute Modifier 실행
    │   ├─ Target HP 감소
    │   ├─ 속성 저항 계산
    │   └─ 크리티컬 판정
    └─ 시각/사운드 효과 재생
        ↓
[8] 스킬 종료
    ├─ EndAbility: Ability 정리
    │   ├─ ActiveGameplayEffectHandle 제거
    │   └─ 카메라 모드 해제
    └─ 다음 입력 대기
```

### 7.2 캔슬 판정 상세 흐름

스킬 실행 중 새로운 입력이 들어왔을 때의 판정 과정:

```
새 스킬 입력 (예: Sword Strike → Counter)
    ↓
[1] 현재 실행 중인 Ability 확인
    ├─ CurrentAbility: GA_Skill_Hilda_SwordStrike
    ├─ CurrentAbility.ActivationOrderGroup: 2
    └─ CurrentTime: 0.5초 (몽타주 재생 중)
    ↓
[2] 새 Ability 정보 확인
    ├─ NewAbility: GA_Skill_Knight_Counter
    └─ NewAbility.ActivationOrderGroup: 3
    ↓
[3] 캔슬 가능 여부 판정 (OR 조건)
    ├─ 조건 A: Activation Order Group 비교
    │   └─ 3 > 2 → TRUE (높은 우선순위)
    ├─ 조건 B: ANS_SkillCancel_C 윈도우 체크
    │   ├─ CurrentMontage: AM_PC_Hilda_B_Skill_SwordStrike
    │   ├─ SkillCancel 구간: 1.3~1.8초
    │   └─ CurrentTime 0.5초 < 1.3초 → FALSE (윈도우 밖)
    └─ 최종 결과: TRUE (조건 A 만족)
    ↓
[4] 캔슬 실행
    ├─ CurrentAbility.EndAbility(bWasCancelled=true)
    ├─ NewAbility.ActivateAbility()
    └─ Counter 몽타주 재생 시작
```

### 7.3 C++ 코드와 어셋의 연동 지점

| C++ 클래스 | 어셋 | 연동 방법 |
|------------|------|-----------|
| `UWSGameplayAbility` | GA_* Blueprint | Blueprint이 C++ 클래스 상속 |
| `ActivationOrderGroup` (C++ 변수) | GA_* Class Defaults | Blueprint 에디터에서 값 설정 |
| `FSkillDataRow` (C++ 구조체) | DT_Skill DataTable | DataTable의 RowStructure로 사용 |
| `ApplyAttackEffectToTarget` (C++ 함수) | AnimNotifyState_AttackWithEquip | 노티파이에서 함수 호출 |
| `UGameplayEffect` | GE_* Blueprint | GameplayEffect 서브클래스 |

**핵심 연동 함수**:
```cpp
// WSGameplayAbility.cpp
FSkillDataRow* UWSGameplayAbility::FindSkillDataRow(FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo) const
{
  // DT_Skill에서 현재 스킬의 행 데이터 조회
  // abilityClass 경로로 역참조
}

void UWSGameplayAbility::ApplyAttackEffectToTarget(FGameplayAbilityTargetDataHandle TargetData, FGameplayTag InAttackTag, bool& bAttackFailMotion)
{
  // AnimMontage 노티파이에서 호출
  // DT_Skill 데이터 + 장비 보정 + 룬 보정 통합 계산
  // 최종 GameplayEffect 적용
}
```

---

## 8. JSON 사용 가이드

### 8.1 JSON 파일 구조 이해

익스포트된 JSON 파일들은 다음과 같은 구조를 가집니다:

**DataTable.json**:
```json
[
  {
    "AssetName": "DT_CharacterStat",
    "AssetPath": "/Game/Blueprints/DataTable/DT_CharacterStat.DT_CharacterStat",
    "RowStructure": "CharacterStatData",
    "Rows": [
      {
        "RowName": "hilda",
        "Data": { ... }
      }
    ]
  }
]
```

**AnimMontage.json**:
```json
[
  {
    "AssetName": "AM_PC_Hilda_B_Skill_SwordStrike",
    "AssetPath": "/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike",
    "SequenceLength": 1.8,
    "Sections": [...],
    "AnimNotifies": [...]
  }
]
```

**Blueprint.json**:
```json
[
  {
    "AssetName": "GA_Skill_Hilda_SwordStrike",
    "AssetPath": "/Game/Blueprints/Characters/Hilda/GA_Skill_Hilda_SwordStrike.GA_Skill_Hilda_SwordStrike",
    "ParentClass": "GA_WSGameplayAbilityBase_C",
    "Variables": [...],
    "Functions": [...],
    "EventGraphs": [...]
  }
]
```

### 8.2 데이터 추출 방법

#### Python을 사용한 JSON 파싱 예시

```python
import json

# 1. DataTable에서 특정 스토커의 스킬 목록 조회
with open('DataTable.json', 'r', encoding='utf-8') as f:
    datatables = json.load(f)

# DT_CharacterStat 찾기
dt_char_stat = next((dt for dt in datatables if dt['AssetName'] == 'DT_CharacterStat'), None)

# Hilda의 스킬 목록
hilda_data = next((row for row in dt_char_stat['Rows'] if row['RowName'] == 'hilda'), None)
default_skills = hilda_data['Data']['defaultSkills']  # ["SK100201", "SK100202", "SK100204"]

# 2. DT_Skill에서 스킬 상세 정보 조회
dt_skill = next((dt for dt in datatables if dt['AssetName'] == 'DT_Skill'), None)

for skill_id in default_skills:
    skill_data = next((row for row in dt_skill['Rows'] if row['RowName'] == skill_id), None)
    print(f"{skill_id}: {skill_data['Data']['skillName']}, Damage: {skill_data['Data']['skillDamageRate']}")
```

#### 특정 노티파이를 가진 AnimMontage 검색

```python
with open('AnimMontage.json', 'r', encoding='utf-8') as f:
    montages = json.load(f)

# ANS_SkillCancel_C를 가진 모든 몽타주 찾기
cancel_montages = []
for montage in montages:
    for notify in montage.get('AnimNotifies', []):
        if notify.get('NotifyStateClass') == 'ANS_SkillCancel_C':
            cancel_montages.append({
                'montage': montage['AssetName'],
                'start': notify['TriggerTime'],
                'duration': notify['Duration']
            })

for item in cancel_montages:
    print(f"{item['montage']}: {item['start']}~{item['start']+item['duration']}초")
```

### 8.3 크로스 레퍼런스 (어셋 간 연결 추적)

스킬 하나의 전체 정보를 추적하는 방법:

```python
# 1. DT_CharacterStat에서 스토커 → 스킬 ID
stalker_name = 'hilda'
skill_id = 'SK100201'  # DT_CharacterStat.hilda.defaultSkills[0]

# 2. DT_Skill에서 스킬 ID → 몽타주 경로, Ability 경로
skill_row = find_skill_by_id(skill_id)
montage_paths = skill_row['Data']['useMontages']
ability_path = skill_row['Data']['abilityClass']

# 3. AnimMontage.json에서 몽타주 경로 → 타이밍 데이터
montage_asset_name = montage_paths[1].split('.')[-1]  # "AM_PC_Hilda_B_Skill_SwordStrike"
montage_data = find_montage_by_name(montage_asset_name)

# ANS_SkillCancel_C 추출
cancel_notify = next((n for n in montage_data['AnimNotifies']
                      if n.get('NotifyStateClass') == 'ANS_SkillCancel_C'), None)

# 4. Blueprint.json에서 Ability 경로 → 로직 구조
ability_asset_name = ability_path.split('/')[-1].split('.')[0]  # "GA_Skill_Hilda_SwordStrike"
ability_data = find_blueprint_by_name(ability_asset_name)

# 최종 통합 정보
skill_info = {
    'id': skill_id,
    'name': skill_row['Data']['skillName'],
    'damage_rate': skill_row['Data']['skillDamageRate'],
    'cooldown': skill_row['Data']['coolTime'],
    'montage_duration': montage_data['SequenceLength'],
    'cancel_window': f"{cancel_notify['TriggerTime']}~{cancel_notify['TriggerTime']+cancel_notify['Duration']}" if cancel_notify else "없음",
    'ability_complexity': len(ability_data['EventGraphs'][0]['Nodes'])
}
```

### 8.4 주의사항

**1. 라인 번호 참조 금지**:
- JSON 파일은 익스포트할 때마다 순서가 바뀔 수 있음
- 항상 AssetName, RowName으로 검색

**2. 경로 형식 이해**:
```
언리얼 경로: /Game/Blueprints/Abilities/GA_Attack.GA_Attack_C
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 에셋 경로
                                             ^^^^^^^^^^^ 클래스 이름

JSON에서 찾을 때: "GA_Attack" (확장자 제외)
```

**3. Blueprint Class Defaults 값 (ActivationOrderGroup) 포함**:
- EventGraph 노드 구조: JSON 포함
- Class Defaults 값 (ActivationOrderGroup): JSON에 **포함됨** (Variables 배열 내)
- 추출 방법: Blueprint의 Variables 배열에서 "Name": "ActivationOrderGroup" 검색하여 DefaultValue 확인

**4. 몽타주 경로 추출**:
```python
# DT_Skill.useMontages 배열
useMontages = [
    "/Script/Engine.AnimMontage'/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_Ready.AM_PC_Hilda_B_Skill_Ready'",
    "/Script/Engine.AnimMontage'/Game/_Art/_Character/PC/Hilda/AnimMontage/Skill/AM_PC_Hilda_B_Skill_SwordStrike.AM_PC_Hilda_B_Skill_SwordStrike'"
]

# 몽타주 이름 추출
montage_names = [path.split('/')[-1].split('.')[0] for path in useMontages]
# ["AM_PC_Hilda_B_Skill_Ready", "AM_PC_Hilda_B_Skill_SwordStrike"]
```

### 8.5 유용한 분석 패턴

**패턴 1: 모든 스킬의 평균 쿨타임 계산**
```python
skills = [row for dt in datatables if dt['AssetName'] == 'DT_Skill' for row in dt['Rows']]
avg_cooldown = sum(skill['Data']['coolTime'] for skill in skills) / len(skills)
```

**패턴 2: 특정 스토커의 총 EventGraph 노드 수 (복잡도)**
```python
stalker_abilities = [bp for bp in blueprints if 'Hilda' in bp['AssetName']]
total_nodes = sum(len(eg['Nodes']) for bp in stalker_abilities for eg in bp['EventGraphs'])
```

**패턴 3: ANS_SkillCancel_C 보유율**
```python
total_montages = len(montages)
cancel_montages = len([m for m in montages if any(n.get('NotifyStateClass') == 'ANS_SkillCancel_C' for n in m.get('AnimNotifies', []))])
cancel_rate = (cancel_montages / total_montages) * 100
```

---

## 문서 종료

본 문서는 언리얼 엔진 에셋(DataTable, AnimMontage, Blueprint)을 JSON으로 익스포트하여 분석한 WorldStalker 전투 로직 시스템의 기술 문서입니다.

**작성 정보**:
- **작성일**: 2025-10-23
- **익스포트 데이터**: `DS-전투밸런스_분석자료/20251023_114317/`
- **분석 대상**: 10명의 스토커 (Hilda, Urud, Nave, Baran, Rio, Clad, Rene, Sinobu, Lian, Cazimord)

**추가 참고 문서**:
- `DS-기획_메뉴얼-전투_로직.md`: 전투 로직 기획 문서
- `DS-Asset_Export_to_JSON.md`: Asset Export 시스템 가이드

**문의**: jinilkim@oneunivrs.com