Compare commits

...

2 Commits

Author SHA1 Message Date
9f39ff3fd2 20251117_104033 기준 데이터 2025-11-17 16:56:36 +09:00
e23e2b6e2c 문서추가 2025-11-05 19:23:33 +09:00
34 changed files with 219631 additions and 180863 deletions

149
데이터수집/README.md Normal file
View File

@ -0,0 +1,149 @@
# DS 전투 데이터 수집 시스템
## 📁 폴더 구조
```
데이터수집/
├── 수집스크립트/
│ ├── collect_combat_data.py # 메인 실행 스크립트
│ ├── data_extractors.py # DataTable/AnimMontage 데이터 추출
│ ├── custom_property_parser.py # CustomProperties 파싱
│ ├── markdown_formatter.py # 마크다운 생성
│ ├── validators.py # 데이터 검증
│ └── validate_result.py # 결과 검증 스크립트
└── 수집결과/
├── all_stalkers_combat_data.json # 통합 JSON 결과
├── collection_log.txt # 수집 로그
└── markdown/ # 스토커별 마크다운 (11개)
```
## 🚀 실행 방법
### 데이터 수집 실행
```bash
cd "D:\Work\WorldStalker\DS-전투분석_저장소\데이터수집\수집스크립트"
python collect_combat_data.py
```
### 결과 검증
```bash
cd "D:\Work\WorldStalker\DS-전투분석_저장소\데이터수집\수집스크립트"
python validate_result.py
```
## 📊 수집 데이터 항목
### DT_CharacterStat (캐릭터 기본 스탯)
- 기본 스탯: Str, Dex, Int, Con, Wis (합계 75)
- 체력/마나: HP, MP, ManaRegen, Stamina
- 공격: PhysicalDamage, MagicalDamage, CriticalPer, CriticalDamage
- 방어: Defense, 각종 저항력
- 스킬 ID 목록: defaultSkills, subSkill, ultimateSkill
- 장비 타입: equipableTypes
- 궁극기 포인트: ultimatePoint
**소수점 처리**: 모든 숫자 필드를 소수점 2자리로 반올림
### DT_Skill (스킬 상세 정보)
- 기본 정보: name, desc, descValues
- 속성: skillAttackType, skillElementType, skillDamageRate
- 코스트: manaCost, coolTime, castingTime
- 몽타주: useMontages (제외 키워드: ready, Equip, Equipment, _E)
- 어빌리티: abilityClass, activeAbilityClass
- 효과: gameplayEffectSet (trigger, gEClass)
### DT_CharacterAbility (기본 공격 몽타주)
- attackMontageMap: 무기 타입별 몽타주 배열
### AnimMontage (애니메이션 타이밍)
- 기본 정보: AssetName, SequenceLength, RateScale
- 섹션: Sections (SectionName, StartTime)
- AnimNotifies (4가지 타입만 수집):
- **ANS_AttackState_C**: 공격 상태 (AddNormalAttackPer, AddPhysicalAttackPer)
- **AnimNotifyState_AttackWithEquip**: 히트 판정 (AttackTag)
- **ANS_SkillCancel_C**: 스킬 캔슬 윈도우
- **AN_Trigger_Projectile_Shot_C**: 발사체 발사 (EventTag)
## 📝 결과물 형식
### 통합 JSON (all_stalkers_combat_data.json)
```json
{
"collection_metadata": {
"collected_at": "2025-11-05T20:04:25.894098",
"total_stalkers": 11
},
"stalkers": {
"hilda": {
"basic_info": { "name": "힐다", "jobName": "전사" },
"stats": { "str": 20.0, "dex": 15.0, ... },
"skills": {
"default": ["SK100201", "SK100202", "SK100204"],
"sub": "SK100101",
"ultimate": "SK100301"
},
"skill_details": { ... },
"basic_attacks": { ... }
}
}
}
```
### 마크다운 (각 스토커별)
- 기본 정보 (직업, 궁극기 포인트)
- 기본 스탯 테이블
- 스킬 상세 정보
- 설명 (desc + descValues 치환 완료)
- 스킬 속성 테이블
- 어빌리티 클래스
- Gameplay Effects
- 몽타주 타이밍 정보
- 기본 공격 정보
## ⚠️ 주의사항
1. **원본 데이터 경로**
- `DS-전투분석_저장소/원본데이터/DataTable.json`
- `DS-전투분석_저장소/원본데이터/AnimMontage.json`
2. **스킬 몽타주 제외 규칙**
- DT_Skill의 useMontages에만 적용
- 제외 키워드: ready, Equip, Equipment, _E
- 기본 공격 몽타주는 제외 규칙 미적용
3. **descValues 처리**
- JSON: 배열 그대로 보존
- Markdown: desc와 합쳐서 완성된 문장 생성
- 줄바꿈 태그 제거: `\r\n`, `\n`, `<br>`
4. **에러 처리**
- 경고 로그만 출력하고 계속 진행
- 누락 데이터는 빈 값으로 표시
## 🔄 재실행 방법
데이터가 업데이트되면:
1. 원본데이터 폴더에 최신 JSON 파일 배치
2. 수집 스크립트 재실행
3. 수집결과 폴더의 파일들이 자동으로 덮어쓰기됨
## 📈 수집 통계 (2025-11-05)
- 총 스토커: 11명
- 평균 스킬 수: 5개 (기본 3~4 + 서브 1 + 궁극기 1)
- 총 스킬 몽타주: 64개
- 총 기본 공격 몽타주: 24개
- 스탯 합계 검증: 모든 스토커 75.0 ✓
## 🛠️ 향후 보완 계획
- [ ] Blueprint 내부 정보 수집 (변수, 함수 등)
- [ ] GameplayEffect 상세 정보 수집
- [ ] 모든 AnimNotifies 수집 (현재는 4가지만)
- [ ] CurveTable 데이터 수집
- [ ] 데이터 비교/분석 도구 추가

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
================================================================================
DS 전투 데이터 수집 결과 요약
================================================================================
수집 시각: 2025-11-17T10:53:43.311961
총 스토커 수: 11
--------------------------------------------------------------------------------
스토커: hilda
--------------------------------------------------------------------------------
이름: 힐다
직업: 전사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK100101
궁극기: SK100301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 6
기본 공격 몽타주 총 수: 3
--------------------------------------------------------------------------------
스토커: urud
--------------------------------------------------------------------------------
이름: 우르드
직업: 원거리
스탯 합계: 75.0
기본 스킬 수: 4
서브 스킬: SK110101
궁극기: SK110301
수집된 스킬 상세 정보 수: 6
스킬 몽타주 총 수: 5
기본 공격 몽타주 총 수: 1
--------------------------------------------------------------------------------
스토커: nave
--------------------------------------------------------------------------------
이름: 네이브
직업: 마법사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK120101
궁극기: SK120301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 4
기본 공격 몽타주 총 수: 2
--------------------------------------------------------------------------------
스토커: baran
--------------------------------------------------------------------------------
이름: 바란
직업: 전사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK130101
궁극기: SK130301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 5
기본 공격 몽타주 총 수: 3
--------------------------------------------------------------------------------
스토커: rio
--------------------------------------------------------------------------------
이름: 리오
직업: 암살자
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK140101
궁극기: SK140301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 5
기본 공격 몽타주 총 수: 3
--------------------------------------------------------------------------------
스토커: clad
--------------------------------------------------------------------------------
이름: 클라드
직업: 성직자
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK150101
궁극기: SK150301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 8
기본 공격 몽타주 총 수: 2
--------------------------------------------------------------------------------
스토커: rene
--------------------------------------------------------------------------------
이름: 레네
직업: 소환사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK160101
궁극기: SK160301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 6
기본 공격 몽타주 총 수: 3
--------------------------------------------------------------------------------
스토커: sinobu
--------------------------------------------------------------------------------
이름: 시노부
직업: 닌자
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK180101
궁극기: SK180301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 6
기본 공격 몽타주 총 수: 2
--------------------------------------------------------------------------------
스토커: lian
--------------------------------------------------------------------------------
이름: 리옌
직업: 레인저
스탯 합계: 75.0
기본 스킬 수: 4
서브 스킬: SK190101
궁극기: SK190301
수집된 스킬 상세 정보 수: 6
스킬 몽타주 총 수: 7
기본 공격 몽타주 총 수: 1
--------------------------------------------------------------------------------
스토커: cazimord
--------------------------------------------------------------------------------
이름: 카지모르드
직업: 전사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK170101
궁극기: SK170301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 6
기본 공격 몽타주 총 수: 3
--------------------------------------------------------------------------------
스토커: blackmaria
--------------------------------------------------------------------------------
이름: 블랙마리아
직업: 전사
스탯 합계: 75.0
기본 스킬 수: 3
서브 스킬: SK200101
궁극기: SK200301
수집된 스킬 상세 정보 수: 5
스킬 몽타주 총 수: 5
기본 공격 몽타주 총 수: 3
================================================================================
수집 완료
================================================================================

View File

@ -0,0 +1,227 @@
# 바란 (Baran) - 전투 데이터
## 기본 정보
- **직업**: 전사
- **궁극기 포인트**: 2780
- **장착 가능 장비**: TwoHandWeapon, Heavy, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 25.0 |
| 민첩 (Dex) | 10.0 |
| 지능 (Int) | 5.0 |
| 체력 (Con) | 25.0 |
| 지혜 (Wis) | 10.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK130204 - 갈고리 투척
**설명**: 갈고리를 던져 25%만큼 물리 피해를 입히고, 대상을 끌어당깁니다. 적중된 대상은 잠시 경직됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 25% |
| 이동 속도 배율 | 0.7x |
| 마나 비용 | 14 |
| 재사용 대기시간 | 13초 |
**어빌리티 클래스**: `GA_Skill_Baran_Pulling → GA_Skill_Baran_Pulling_C`
**Gameplay Effects**
- `OnProjectileHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnProjectileHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnProjectileHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_Baran_B_Skill_Pulling**
- 시퀀스 길이: 1.70초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.51초
- 이벤트 태그: `Event.Effect.Shot`
- **ANS_SkillCancel**: 1.28~1.70초 (지속: 0.42초)
#### SK130203 - 후려치기
**설명**: 대검을 크게 휘둘러 두 번 연속으로 120%만큼 물리 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 120% |
| 이동 속도 배율 | 0.7x |
| 마나 비용 | 9 |
| 재사용 대기시간 | 8초 |
| 지속 시간 | 5초 |
**어빌리티 클래스**: `GA_Skill_Baran_Smash → GA_Skill_Baran_Smash_C`
---
**몽타주 1: AM_PC_Baran_B_Skill_Smash**
- 시퀀스 길이: 1.89초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.49~0.70초 (지속: 0.22초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.78~0.97초 (지속: 0.19초)
- 공격 태그: `Event.Attack.Skill`
#### SK130206 - 깊게 찌르기
**설명**: 대검을 깊게 찔러 넣어 120%만큼 물리 피해를 입힙니다. 문을 파괴할 수 있습니다. 적중된 대상은 잠시 경직됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 110% |
| 이동 속도 배율 | 0.7x |
| 마나 비용 | 10 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Baran_SwordStab → GA_Skill_Baran_SwordStab_C`
**Gameplay Effects**
- `OnHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_Baran_B_Skill_SwordStab**
- 시퀀스 길이: 1.75초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.68~0.90초 (지속: 0.22초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 1.66~1.73초 (지속: 0.07초)
### 서브 스킬
#### SK130101 - 무기 막기
**설명**: 무기로 공격을 방어합니다. 방어 유지 시 지구력이 소모됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.7x |
**어빌리티 클래스**: `GA_Skill_Common_Blocking → GA_Skill_Common_Blocking_C`
---
**몽타주 1: AM_PC_Baran_B_Skill_Blocking**
- 시퀀스 길이: 3.57초
- 재생 속도: 1x
### 궁극기
#### SK130301 - 마석 '일격분쇄'
**설명**: 대검을 내리찍어 4m의 균열을 생성합니다. 균열 범위 내 170%의 물리 피해를 주며, 적중된 대상은 기절합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 170% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 10초 |
| 궁극기 | O |
| 지속 시간 | 2초 |
**어빌리티 클래스**: `GA_Skill_Baran_Decision → GA_Skill_Baran_Decision_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `CustomEventTarget1`: GE_StunMotion → GE_StunMotion_C
- `CustomEventTarget1`: GE_Attack_Splash_Physical → GE_Attack_Splash_Physical_C
---
**몽타주 1: AM_PC_Baran_B_Skill_RockBraker2**
- 시퀀스 길이: 2.97초
- 재생 속도: 1.5x
## 기본 공격
### 기본 공격: twoHandWeapon
#### 콤보 1: AM_PC_Baran_B_Attack_W01_01
- 시퀀스 길이: 1.90초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.17초 (지속: 1.17초)
- 일반 공격력 증가: 10.0%
- **AttackWithEquip**: 0.80~0.92초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Baran_B_Attack_W01_02
- 시퀀스 길이: 1.93초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.17초 (지속: 1.17초)
- 일반 공격력 증가: 15.0%
- **AttackWithEquip**: 0.80~0.95초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_Baran_B_Attack_W01_03
- 시퀀스 길이: 1.73초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.00초 (지속: 1.00초)
- 일반 공격력 증가: 10.0%
- **AttackWithEquip**: 0.70~0.85초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,238 @@
# 블랙마리아 (Blackmaria) - 전투 데이터
## 기본 정보
- **직업**: 전사
- **궁극기 포인트**: 2780
- **장착 가능 장비**: TwoHandWeapon, Heavy, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 25.0 |
| 민첩 (Dex) | 10.0 |
| 지능 (Int) | 5.0 |
| 체력 (Con) | 25.0 |
| 지혜 (Wis) | 10.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK200201 - 용맹의 쇄도
**설명**: 대검을 넓게 세워 정면을 방어하면서 전진합니다. 전진 중에 적에게 충돌하면 130%만큼의 물리 피해를 입히고 멈춥니다. 문을 파괴할 수 있습니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 130% |
| 마나 비용 | 11 |
| 재사용 대기시간 | 10초 |
**어빌리티 클래스**: `GA_Skill_BlackMaria_Strike → GA_Skill_BlackMaria_Strike_C`
**Gameplay Effects**
- `OnHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_BlackMaria_Base_000_Skill_ValiantCharge**
- 시퀀스 길이: 1.84초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.52~1.15초 (지속: 0.63초)
- 공격 태그: `Event.Attack.Skill`
#### SK200202 - 승천의 일격
**설명**: 대검을 올려쳐서 적들을 1.5초 동안 공중으로 띄워 올리고 110%만큼의 물리 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 110% |
| 마나 비용 | 8 |
| 재사용 대기시간 | 9초 |
**어빌리티 클래스**: `GA_Skill_BlackMaria_RisingSlash → GA_Skill_BlackMaria_RisingSlash_C`
**Gameplay Effects**
- `OnHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_BlackMaria_Base_000_Skill_AscensionStrike**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.59~0.79초 (지속: 0.20초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 1.00~1.50초 (지속: 0.50초)
#### SK200203 - 결의의 방벽
**설명**: 10초간 피해를 입어도 방어구의 내구도가 1 이하로 떨어지지 않는 상태에 돌입합니다. 만약 방어구의 내구도가 0이라면 즉시 전체 내구도의 30%만큼을 회복합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 1초 |
| 마나 비용 | 11 |
| 재사용 대기시간 | 30초 |
| 지속 시간 | 10초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_BlackMaria_IgnoreArmorDamage → GA_Skill_BlackMaria_IgnoreArmorDamage_C`
---
**몽타주 1: AM_PC_BlackMaria_Base_000_Skill_AegisOfResolve**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
### 서브 스킬
#### SK200101 - 투구 깨기
**설명**: 대검으로 내리찍어 90%만큼의 물리 피해를 입힙니다. 충전 단계에 따라 피해량과 범위가 증가합니다. 1단계 충전을 하면 150%, 2단계 충전을 하면 225%만큼의 물리 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 90% |
| 이동 속도 배율 | 1x |
**어빌리티 클래스**: `GA_Skill_BlackMaria_HelmBreaker → GA_Skill_BlackMaria_HelmBreaker_C`
**Gameplay Effects**
- `CustomEventTarget1`: GE_Attack_Splash_Physical → GE_Attack_Splash_Physical_C
---
**몽타주 1: AM_PC_BlackMaria_Base_000_HelmBreaker2**
- 시퀀스 길이: 5.54초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 2.03~2.22초 (지속: 0.19초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 2.53~2.87초 (지속: 0.34초)
- **ANS_AttackState**: 3.14~3.72초 (지속: 0.58초)
- 물리 공격력 증가: 60.0%
- **AttackWithEquip**: 3.33~3.52초 (지속: 0.19초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 3.86~4.21초 (지속: 0.34초)
- **ANS_AttackState**: 4.52~5.10초 (지속: 0.58초)
- 물리 공격력 증가: 135.0%
- **AttackWithEquip**: 4.67~4.92초 (지속: 0.25초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 5.29~5.54초 (지속: 0.25초)
### 궁극기
#### SK200301 - 마석 '땅울림'
**설명**: 대검을 2번 땅에 내리 꽂습니다. 대검이 첫번째 꽂힐 때는 주변 6미터의 적을 대검쪽으로 끌어들이고, 두번째 꽂힐 때는 130%만큼의 물리 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 130% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 10초 |
| 궁극기 | O |
| 지속 시간 | 2초 |
**어빌리티 클래스**: `GA_Skill_BlackMaria_ShockWave → GA_Skill_BlackMaria_ShockWave_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `CustomEventTarget1`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `CustomEventTarget1`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `CustomEventTarget1`: GE_Attack_Splash_Physical → GE_Attack_Splash_Physical_C
---
**몽타주 1: AM_PC_BlackMaria_Base_000_Skill_Bladequake**
- 시퀀스 길이: 2.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: twoHandWeapon
#### 콤보 1: AM_PC_BlackMaria_Base_000_Attack_GreatSword1
- 시퀀스 길이: 1.67초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.00초 (지속: 1.00초)
- 일반 공격력 증가: 8.0%
- **AttackWithEquip**: 0.80~0.90초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_BlackMaria_Base_000_Attack_GreatSword2
- 시퀀스 길이: 1.67초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.27초 (지속: 1.27초)
- 일반 공격력 증가: 12.0%
- **AttackWithEquip**: 0.60~0.83초 (지속: 0.23초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_BlackMaria_Base_000_Attack_GreatSword3
- 시퀀스 길이: 1.73초
- 재생 속도: 1.2000000476837158x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.73초 (지속: 1.73초)
- 일반 공격력 증가: 40.0%
- **AttackWithEquip**: 0.68~0.83초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,257 @@
# 카지모르드 (Cazimord) - 전투 데이터
## 기본 정보
- **직업**: 전사
- **궁극기 포인트**: 2368
- **장착 가능 장비**: WeaponShield, Light, Cloth
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 15.0 |
| 민첩 (Dex) | 25.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 15.0 |
| 지혜 (Wis) | 10.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK170201 - 섬광
**설명**: 정면으로 4m 돌진하며, 베기 공격으로 공격력의 100%의 피해를 가합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 50% |
| 이동 속도 배율 | 1x |
| 마나 비용 | 5 |
| 재사용 대기시간 | 15.5초 |
| 스택 | 가능 (최대 2) |
**어빌리티 클래스**: `GA_Skill_Cazimord_Flash → GA_Skill_Cazimord_Flash_C`
**Gameplay Effects**
- `InActive`: GE_Ignore_Shock → GE_Ignore_Shock_C
---
**몽타주 1: AM_PC_Cazimord_B_Skill_Flash**
- 시퀀스 길이: 3.62초
- 재생 속도: 1x
**몽타주 2: AM_PC_Cazimord_B_Skill_Flash_Active**
- 시퀀스 길이: 1.73초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.37~0.57초 (지속: 0.20초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.37~0.57초 (지속: 0.20초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 1.00~1.73초 (지속: 0.73초)
#### SK170202 - 날개 베기
**설명**: 4번 베기로 공격력의 30% 피해를 입힙니다. 스킬 사용 중에는 경직에 면역 됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 30% |
| 이동 속도 배율 | 1x |
| 마나 비용 | 10 |
| 재사용 대기시간 | 15.5초 |
**어빌리티 클래스**: `GA_Skill_Cazimord_BladeStorm → GA_Skill_Cazimord_BladeStorm_C`
---
**몽타주 1: AM_PC_Cazimord_B_Skill_BladeStorm**
- 시퀀스 길이: 3.00초
- 재생 속도: 1.5x
**주요 타이밍**
- **AttackWithEquip**: 0.64~0.87초 (지속: 0.23초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.68~0.91초 (지속: 0.23초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.61~1.76초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.64~1.82초 (지속: 0.19초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 2.50~3.00초 (지속: 0.50초)
#### SK170203 - 작열
**설명**: 15초간 무기에 불을 붙여서 적중시킨 적에게 물리 피해의 20%만큼을 추가 마법 피해로 주고, 화상 상태로 만듭니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 1x |
| 시전 시간 | 2초 |
| 마나 비용 | 3 |
| 재사용 대기시간 | 27.5초 |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Cazimord_Burn_Active → GA_Skill_Cazimord_Burn_Active_C`
---
**몽타주 1: AM_PC_Cazimord_B_Skill_Burn**
- 시퀀스 길이: 2.43초
- 재생 속도: 1x
### 서브 스킬
#### SK170101 - 흘리기
**설명**: 무기로 적의 공격을 흘려냅니다. 흘리기에 성공하면 적의 공격을 막아냅니다. 그리고 스킬의 재사용 대기시간 일부가 감소됩니다. 섬광 및 날개베기는 각각 3.799999952316284초, 작열은 6.800000190734863초씩 감소 합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 1x |
**어빌리티 클래스**: `GA_Skill_Cazimord_Parrying → GA_Skill_Cazimord_Parrying_C`
---
**몽타주 1: AM_PC_Cazimord_B_Skill_Parrying**
- 시퀀스 길이: 1.61초
- 재생 속도: 1x
### 궁극기
#### SK170301 - 마석 '칼날폭풍'
**설명**: 마석의 힘을 빌려 빠르게 정면을 12회 공격해 각각 80%의 물리 피해를 입힙니다. 마지막 2회의 타격은 100%의 물리 피해를 입힙니다. 시전 중에는 천천히 이동할 수 있지만, 마지막 타격때는 이동할 수 없습니다. 또한 스킬 사용 중에는 경직에 면역 됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 80% |
| 이동 속도 배율 | 0.3x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_SKill_Cazimord_ManaStoneBurn → GA_SKill_Cazimord_ManaStoneBurn_C`
---
**몽타주 1: AM_PC_Cazimord_B_Skill_ManaStoneBurn**
- 시퀀스 길이: 3.50초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.49~0.58초 (지속: 0.09초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.60~0.71초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.76~0.88초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.76~0.87초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.04~1.16초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.11~1.22초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.41~1.52초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.41~1.53초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.65~1.88초 (지속: 0.23초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 1.66~1.87초 (지속: 0.21초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 2.07~2.18초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Ultimate`
- **AttackWithEquip**: 2.07~2.18초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Ultimate`
## 기본 공격
### 기본 공격: weaponShield
#### 콤보 1: AM_PC_Cazimord_B_Attack_W01_01
- 시퀀스 길이: 1.67초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.90초 (지속: 0.90초)
- 일반 공격력 증가: -5.0%
- **AttackWithEquip**: 0.59~0.73초 (지속: 0.14초)
- 공격 태그: `Event.Attack.Normal`
- **AttackWithEquip**: 0.71~0.89초 (지속: 0.18초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Cazimord_B_Attack_W01_02
- 시퀀스 길이: 1.90초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.94초 (지속: 0.94초)
- 일반 공격력 증가: 10.0%
- **AttackWithEquip**: 0.55~0.68초 (지속: 0.13초)
- 공격 태그: `Event.Attack.Normal`
- **AttackWithEquip**: 0.67~0.84초 (지속: 0.17초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_Cazimord_B_Attack_W01_03
- 시퀀스 길이: 1.87초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.85초 (지속: 0.85초)
- **AttackWithEquip**: 0.57~0.67초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Normal`
- **AttackWithEquip**: 0.59~0.67초 (지속: 0.08초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,217 @@
# 클라드 (Clad) - 전투 데이터
## 기본 정보
- **직업**: 성직자
- **궁극기 포인트**: 2325
- **장착 가능 장비**: Mace, Heavy, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 15.0 |
| 민첩 (Dex) | 10.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 20.0 |
| 지혜 (Wis) | 20.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK150206 - 치유
**설명**: 대상의 체력의 60 회복합니다. 대상이 없을 경우 자신에게 시전합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 1초 |
| 마나 비용 | 12 |
| 재사용 대기시간 | 3초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Clad_HolyCure → GA_Skill_Clad_HolyCure_C`
---
**몽타주 1: AM_PC_Clad_Base_B_Skill_Ready**
- 시퀀스 길이: 2.23초
- 재생 속도: 1x
**몽타주 2: AM_PC_Clad_Base_B_Skill_HolyCure**
- 시퀀스 길이: 1.17초
- 재생 속도: 1x
#### SK150201 - 다시 흙으로
**설명**: 3m 내의 적에게 150% 빛 속성 마법 피해를 주고, 3초 동안 5 방어력을 감소 시킵니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | Holy |
| 피해 배율 | 150% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 0.5초 |
| 마나 비용 | 9 |
| 재사용 대기시간 | 5초 |
| 지속 시간 | 5초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Clad_TurnUndead → GA_Skill_Clad_TurnUndead_C`
---
**몽타주 1: AM_PC_Clad_Base_B_Skill_Ready**
- 시퀀스 길이: 2.23초
- 재생 속도: 1x
**몽타주 2: AM_PC_Clad_Base_B_Skill_TurnUndead**
- 시퀀스 길이: 1.20초
- 재생 속도: 1x
#### SK150202 - 신성한 빛
**설명**: 주변 아군의 지속 피해 효과를 제거하고. 6초 동안 면역 효과를 제공합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 0% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 1초 |
| 마나 비용 | 15 |
| 재사용 대기시간 | 7.5초 |
| 지속 시간 | 30초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Clad_HolyLight → GA_Skill_Clad_HolyLight_C`
---
**몽타주 1: AM_PC_Clad_Base_B_Skill_Ready**
- 시퀀스 길이: 2.23초
- 재생 속도: 1x
**몽타주 2: AM_PC_Clad_Base_B_Skill_HolyLight**
- 시퀀스 길이: 1.17초
- 재생 속도: 1x
### 서브 스킬
#### SK150101 - 방패 방어
**설명**: 방패로 공격을 방어합니다. 방어 유지 시 지구력이 소모됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.7x |
**어빌리티 클래스**: `GA_Skill_Common_Blocking → GA_Skill_Common_Blocking_C`
---
**몽타주 1: AM_PC_Clad_Base_B_Skill_Block**
- 시퀀스 길이: 5.30초
- 재생 속도: 1x
### 궁극기
#### SK150301 - 마석 ‘황금’
**설명**: 마석의 힘을 해방하여 5초 동안 자신과 아군에게 300의 보호막을 생성합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 30000% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 0.55초 |
| 궁극기 | O |
| 지속 시간 | 6초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Clad_Gold → GA_Skill_Clad_Gold_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
---
**몽타주 1: AM_PC_Clad_Base_B_Skill_Gold**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: mace
#### 콤보 1: AM_PC_Clad_Base_Attack_Mace1
- 시퀀스 길이: 1.90초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.90초 (지속: 0.90초)
- 일반 공격력 증가: 5.0%
- **AttackWithEquip**: 0.56~0.71초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Clad_Base_Attack_Mace2
- 시퀀스 길이: 2.27초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.00초 (지속: 1.00초)
- 일반 공격력 증가: 5.0%
- **AttackWithEquip**: 0.56~0.71초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,234 @@
# 힐다 (Hilda) - 전투 데이터
## 기본 정보
- **직업**: 전사
- **궁극기 포인트**: 2495
- **장착 가능 장비**: WeaponShield, Heavy, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 20.0 |
| 민첩 (Dex) | 15.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 20.0 |
| 지혜 (Wis) | 10.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK100201 - 칼날 격돌
**설명**: 검을 휘둘러 130%만큼 번개 속성 물리 피해를 입힙니다. 적중된 대상은 잠시 경직됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | Lightning |
| 피해 배율 | 130% |
| 마나 비용 | 11 |
| 재사용 대기시간 | 6초 |
**어빌리티 클래스**: `GA_Skill_Hilda_SwordStrike → GA_Skill_Hilda_SwordStrike_C`
**Gameplay Effects**
- `OnHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_Hilda_B_Skill_Ready**
- 시퀀스 길이: 5.43초
- 재생 속도: 1x
**몽타주 2: AM_PC_Hilda_B_Skill_SwordStrike**
- 시퀀스 길이: 1.80초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.58~0.72초 (지속: 0.14초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 1.30~1.80초 (지속: 0.50초)
#### SK100202 - 반격
**설명**: 방패를 들어 5초 동안 반격 자세를 취합니다. 반격 성공 시 80%만큼 물리 피해를 줍니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 80% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 10 |
| 재사용 대기시간 | 4초 |
| 지속 시간 | 5초 |
**어빌리티 클래스**: `GA_Skill_Knight_Counter → GA_Skill_Knight_Counter_C`
**활성 어빌리티**: `GA_Skill_ActiveBase → GA_Skill_ActiveBase_C`
**Gameplay Effects**
- `Instant`: GE_Skill_Hilda_Counter_Duration → GE_Skill_Hilda_Counter_Duration_C
---
**몽타주 1: AM_PC_Hilda_B_Skill_Counter**
- 시퀀스 길이: 3.37초
- 재생 속도: 1.2000000476837158x
**주요 타이밍**
- **AttackWithEquip**: 1.98~2.21초 (지속: 0.23초)
- 공격 태그: `Event.Attack.Skill`
#### SK100204 - 도발
**설명**: 주변 5m 내의 몬스터를 도발하여 위협 수치를 획득하고 대상의 이동속도를 3초 동안 25% 감속시킵니다.10초 동안 방어력이 15 증가합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 25000% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 8 |
| 재사용 대기시간 | 10초 |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_Skill_Common_Provoke → GA_Skill_Common_Provoke_C`
**활성 어빌리티**: `GA_Skill_Common_Provoke_Active → GA_Skill_Common_Provoke_Active_C`
**Gameplay Effects**
- `InstantTarget`: GE_Provoked → GE_Provoked_C
- `InActive`: GE_HildaTauntDefense → GE_HildaTauntDefense_C
- `InstantTarget`: GE_ProvokeSlow → GE_ProvokeSlow_C
---
**몽타주 1: AM_PC_Hilda_B_Skill_Provoke**
- 시퀀스 길이: 2.00초
- 재생 속도: 1x
### 서브 스킬
#### SK100101 - 방패 방어
**설명**: 방패로 공격을 방어합니다.방어 유지 시 지구력이 소모됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
**어빌리티 클래스**: `GA_Skill_Common_Blocking → GA_Skill_Common_Blocking_C`
---
**몽타주 1: AM_PC_Hilda_B_Skill_Blocking**
- 시퀀스 길이: 4.34초
- 재생 속도: 1x
### 궁극기
#### SK100301 - 마석 ‘핏빛 달’
**설명**: 마석의 힘을 해방하여 20초 동안 공격력 15, 방어력 25 증가합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 50% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 20초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Hilda_BloodMoon_Active → GA_Skill_Hilda_BloodMoon_Active_C`
**Gameplay Effects**
- `InActive`: GE_Skill_Hilda_BloodMoon_Active → GE_Skill_Hilda_BloodMoon_Active_C
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
---
**몽타주 1: AM_PC_Hilda_B_Skill_BloodMoon**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: weaponShield
#### 콤보 1: AM_PC_Hilda_B_Attack_W01_01
- 시퀀스 길이: 1.60초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.80초 (지속: 0.80초)
- 일반 공격력 증가: -5.0%
- **AttackWithEquip**: 0.60~0.72초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Hilda_B_Attack_W01_02
- 시퀀스 길이: 1.60초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.77초 (지속: 0.77초)
- 일반 공격력 증가: -5.0%
- **AttackWithEquip**: 0.57~0.67초 (지속: 0.09초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_Hilda_B_Attack_W01_03
- 시퀀스 길이: 1.37초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.00초 (지속: 1.00초)
- 일반 공격력 증가: 20.0%
- **AttackWithEquip**: 0.49~0.57초 (지속: 0.08초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,230 @@
# 리옌 (Lian) - 전투 데이터
## 기본 정보
- **직업**: 레인저
- **궁극기 포인트**: 2775
- **장착 가능 장비**: Bow, Light, Cloth
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 10.0 |
| 민첩 (Dex) | 20.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 15.0 |
| 지혜 (Wis) | 20.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK190207 - 속사
**설명**: 4발의 화살을 빠르게 발사하여 각각 85%만큼 물리 피해를 입힙니다. 화살을 4개 소모합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 85% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 16 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Lian_RapidShot → GA_Skill_Lian_RapidShot_C`
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_RapidShot1**
- 시퀀스 길이: 2.67초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.83초
- 이벤트 태그: `Event.Effect.Shot`
- **AN_Trigger_Projectile_Shot**: 1.20초
- 이벤트 태그: `Event.Effect.Shot`
- **AN_Trigger_Projectile_Shot**: 1.59초
- 이벤트 태그: `Event.Effect.Shot`
- **AN_Trigger_Projectile_Shot**: 2.00초
- 이벤트 태그: `Event.Effect.Shot`
- **ANS_SkillCancel**: 2.33~2.63초 (지속: 0.29초)
#### SK190205 - 비연사
**설명**: 뒤로 빠지며 화살을 발사하여 150%만큼 물리 피해를 입힙니다. 화살을 1개 소모합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 150% |
| 마나 비용 | 15 |
| 재사용 대기시간 | 10초 |
**어빌리티 클래스**: `GA_Skill_Lian_BackStepBowAttack → GA_Skill_Lian_BackStepBowAttack_C`
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_BackStepBowAttack**
- 시퀀스 길이: 1.74초
- 재생 속도: 1.2999999523162842x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.83초
- 이벤트 태그: `Event.Effect.Shot`
- **ANS_SkillCancel**: 1.49~1.70초 (지속: 0.21초)
**몽타주 2: AM_PC_Lian_Base_000_Skill_BackStepBowAttack**
- 시퀀스 길이: 1.74초
- 재생 속도: 1.2999999523162842x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.83초
- 이벤트 태그: `Event.Effect.Shot`
- **ANS_SkillCancel**: 1.49~1.70초 (지속: 0.21초)
#### SK190201 - 연화
**설명**: 60초 동안 적을 천천히 추적하는 연꽃을 만들어 발사합니다. 연꽃은 120%만큼 빛 속성 물리 피해를 입히며, 적중된 대상은 10초 동안 25%의 주는 피해 감소 효과를 받습니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | Holy |
| 피해 배율 | 120% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 12 |
| 재사용 대기시간 | 7.5초 |
**어빌리티 클래스**: `GA_Skill_Lian_DarkSouls → GA_Skill_Lian_DarkSouls_C`
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_DarkSouls_NoCasting**
- 시퀀스 길이: 2.20초
- 재생 속도: 1x
#### SK190209 - 재장전
**설명**: 화살을 화살통에 장전 합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.9x |
| 시전 시간 | 5초 |
| 지속 시간 | 1초 |
**어빌리티 클래스**: `GA_Attack_Firearm_Reload → GA_Attack_Firearm_Reload_C`
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_Reload**
- 시퀀스 길이: 1.43초
- 재생 속도: 1x
### 서브 스킬
#### SK190101 - 정조준
**설명**: 조준하는 동안 물리 피해가 증가하는 화살을 발사합니다. 최대 150%까지 물리 피해가 증가합니다. 화살을 1개 소모합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 70% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 1.5초 |
**어빌리티 클래스**: `GA_Skill_Lian_ChargingBow → GA_Skill_Lian_ChargingBow_C`
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_ChargingBow**
- 시퀀스 길이: 4.93초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 4.20초
- 이벤트 태그: `Event.Effect.Shot`
### 궁극기
#### SK190301 - 마석 '폭우'
**설명**: 마석의 힘을 개방하여 15초 동안 화살을 소모하지 않으며, 쿨타임이 50% 감소합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 5000% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 1.5초 |
| 궁극기 | O |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Lian_ManaStoneSilence_Active → GA_Skill_Lian_ManaStoneSilence_Active_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `InActive`: GE_Skill_Lian_ManaStoneSilence → GE_Skill_Lian_ManaStoneSilence_C
---
**몽타주 1: AM_PC_Lian_Base_000_Skill_ManastoneSilence**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: bow
#### 콤보 1: AM_PC_Lian_Base_000_Attack_Bow
- 시퀀스 길이: 3.27초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 2.50초

View File

@ -0,0 +1,201 @@
# 네이브 (Nave) - 전투 데이터
## 기본 정보
- **직업**: 마법사
- **궁극기 포인트**: 2728
- **장착 가능 장비**: Staff, Light, Cloth
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 10.0 |
| 민첩 (Dex) | 10.0 |
| 지능 (Int) | 25.0 |
| 체력 (Con) | 10.0 |
| 지혜 (Wis) | 20.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK120201 - 마법 화살
**설명**: 최대 3개의 마법 화살을 생성하여 일반 공격으로 발사합니다. 마법 화살은 각각 80%만큼 마법 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 80% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 마나 비용 | 18 |
| 재사용 대기시간 | 3.5초 |
| 지속 시간 | 600초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Nave_MagicMissile_Active → GA_Skill_Nave_MagicMissile_Active_C`
---
**몽타주 1: AM_PC_Nave_B_Skill_MagicMissile**
- 시퀀스 길이: 3.33초
- 재생 속도: 1x
#### SK120202 - 화염구
**설명**: 화염구를 생성하여 일반 공격으로 발사합니다. 화염구는 200% 화염 속성 마법 피해와 주변에 150% 추가 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | Fire |
| 피해 배율 | 200% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 4초 |
| 마나 비용 | 25 |
| 재사용 대기시간 | 5초 |
| 지속 시간 | 600초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Nave_FireWall_Active → GA_Skill_Nave_FireWall_Active_C`
**Gameplay Effects**
- `OnProjectileHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnProjectileHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnProjectileHitRangedTarget`: GE_Attack_Projectile_Splash_Fire → GE_Attack_Projectile_Splash_Fire_C
---
**몽타주 1: AM_PC_Nave_B_Skill_FireWall**
- 시퀀스 길이: 3.33초
- 재생 속도: 1x
#### SK120206 - 노대바람
**설명**: 강한 바람으로 밀쳐내고 50%만큼 마법 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 50% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 9 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Nave_WindForce → GA_Skill_Nave_WindForce_C`
**Gameplay Effects**
- `OnActiveRangedTarget`: GE_Attack_Ability → GE_Attack_Ability_C
---
**몽타주 1: AM_PC_Nave_B_Skill_WindForce**
- 시퀀스 길이: 1.33초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.05~1.11초 (지속: 1.06초)
### 서브 스킬
#### SK120101 - 마력 충전
**설명**: 시전하는 동안 1의 마나를 추가로 회복합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 500% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 9999초 |
| 재사용 대기시간 | 1초 |
**어빌리티 클래스**: `GA_Skill_Nave_ManaCharge_Casting → GA_Skill_Nave_ManaCharge_Casting_C`
---
**몽타주 1: AM_PC_Nave_B_Skill_ManaRestore**
- 시퀀스 길이: 2.80초
- 재생 속도: 1x
### 궁극기
#### SK120301 - 마석 ‘해방’
**설명**: 마석의 힘으로 5초 동안 적을 관통하는 광선을 발사합니다. 광선은 0.5초 간격마다 100%의 마법 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 5초 |
**어빌리티 클래스**: `GA_Skill_Nave_Escape4 → GA_Skill_Nave_Escape4_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `InActive`: GE_Skill_Nave_Escape_Active → GE_Skill_Nave_Escape_Active_C
- `OnActiveRangedTarget`: GE_Skill_Nave_Escape → GE_Skill_Nave_Escape_C
## 기본 공격
### 기본 공격: staff
#### 콤보 1: AM_PC_Nave_B_Attack_W01_01
- 시퀀스 길이: 1.60초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.90초 (지속: 0.90초)
- **AttackWithEquip**: 0.49~0.59초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Nave_B_Attack_W01_02
- 시퀀스 길이: 1.70초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.90초 (지속: 0.90초)
- **AttackWithEquip**: 0.41~0.48초 (지속: 0.07초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,221 @@
# 레네 (Rene) - 전투 데이터
## 기본 정보
- **직업**: 소환사
- **궁극기 포인트**: 2305
- **장착 가능 장비**: Staff, Light, Cloth
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 10.0 |
| 민첩 (Dex) | 10.0 |
| 지능 (Int) | 20.0 |
| 체력 (Con) | 10.0 |
| 지혜 (Wis) | 25.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK160202 - 정령 소환 : 화염
**설명**: 20초 동안 유지되는 화염의 정령을 소환합니다. 정령은 이동하지 않고 화염 화살을 발사하여 120% 화염 속성 마법 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 120% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 8 |
| 재사용 대기시간 | 7초 |
| 지속 시간 | 20초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Rene_SummonIfrit_Active → GA_Skill_Rene_SummonIfrit_Active_C`
---
**몽타주 1: AM_PC_Rene_B_Skill_SummonIfrit**
- 시퀀스 길이: 2.33초
- 재생 속도: 1.600000023841858x
#### SK160206 - 정령 소환 : 냉기
**설명**: 60초 동안 유지되는 냉기의 정령을 소환합니다. 정령은 레네를 따라 이동하며 얼음 송곳을 소환합니다. 얼음 송곳은 80%만큼 물 속성 마법 피해를 입히며, 적중된 적은 둔화됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 80% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 15 |
| 재사용 대기시간 | 10초 |
| 지속 시간 | 60초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Rene_SummonShiva_Active → GA_Skill_Rene_SummonShiva_Active_C`
---
**몽타주 1: AM_PC_Rene_B_Skill_SummonShiva**
- 시퀀스 길이: 3.50초
- 재생 속도: 1.2999999523162842x
#### SK160203 - 독기 화살
**설명**: 방어력을 무시하고 30만큼 암흑 속성 마법 피해를 입힙니다. 적중된 적은 출혈 상태가 됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | Dark |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 마나 비용 | 15 |
| 재사용 대기시간 | 10초 |
| 지속 시간 | 600초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Rene_PoisonGas_Active → GA_Skill_Rene_PoisonGas_Active_C`
---
**몽타주 1: AM_PC_Rene_B_Skill_PoisonGas**
- 시퀀스 길이: 4.67초
- 재생 속도: 1x
### 서브 스킬
#### SK160101 - 할퀴기
**설명**: 손톱을 휘둘러 75%만큼 마법 피해를 입히고 흡혈합니다. 피해의 30%만큼 체력을 회복합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 75% |
| 이동 속도 배율 | 0.8x |
**어빌리티 클래스**: `GA_Skill_Rene_Scratching → GA_Skill_Rene_Scratching_C`
---
**몽타주 1: AM_PC_Rene_B_Skill_Scratching**
- 시퀀스 길이: 1.67초
- 재생 속도: 1.5x
**주요 타이밍**
- **AttackWithEquip**: 0.58~0.73초 (지속: 0.15초)
- 공격 태그: `Event.Attack.Sub`
**몽타주 2: AM_PC_Rene_B_Skill_Scratching2**
- 시퀀스 길이: 1.93초
- 재생 속도: 1.2000000476837158x
**주요 타이밍**
- **AttackWithEquip**: 0.61~0.75초 (지속: 0.14초)
- 공격 태그: `Event.Attack.Sub`
### 궁극기
#### SK160301 - 마석 ‘붉은 축제’
**설명**: 마석의 힘을 해방하여 20초 동안 자신과 아군의 모든 공격에 흡혈 효과를 부여합니다. 피해의 50%만큼 체력을 회복합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | MagicalSkill |
| 원소 타입 | None |
| 피해 배율 | 5000% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 20초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Rene_ManaStoneCarnival_Active → GA_Skill_Rene_ManaStoneCarnival_Active_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
---
**몽타주 1: AM_PC_Rene_B_Skill_ManaStoneCarnival**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: staff
#### 콤보 1: AM_PC_Rene_B_Attack_W01_01
- 시퀀스 길이: 1.90초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.85초 (지속: 0.85초)
- **AttackWithEquip**: 0.57~0.69초 (지속: 0.11초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Rene_B_Attack_W01_02
- 시퀀스 길이: 1.80초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.90초 (지속: 0.90초)
- **AttackWithEquip**: 0.63~0.75초 (지속: 0.12초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_Rene_B_Attack_W01_03
- 시퀀스 길이: 2.20초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.01~0.91초 (지속: 0.90초)
- **AttackWithEquip**: 0.62~0.72초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,216 @@
# 리오 (Rio) - 전투 데이터
## 기본 정보
- **직업**: 암살자
- **궁극기 포인트**: 2368
- **장착 가능 장비**: ShortSword, Cloth, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 15.0 |
| 민첩 (Dex) | 25.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 15.0 |
| 지혜 (Wis) | 10.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK140201 - 연속 찌르기
**설명**: 단검을 빠르게 2번 찔러 각각 100%만큼 암흑 속성 물리 피해를 입힙니다. 각 공격은 25%의 추가 치명타 확률을 가집니다. 타격당 연계 점수 1점을 획득합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.9x |
| 마나 비용 | 9 |
| 재사용 대기시간 | 3.5초 |
**어빌리티 클래스**: `GA_Skill_Rio_RapidStab → GA_Skill_Rio_RapidStab_C`
**Gameplay Effects**
- `Instant`: GE_Skill_Rio_RapidStab_Buff → GE_Skill_Rio_RapidStab_Buff_C
---
**몽타주 1: AM_PC_Rio_B_Skill_RapidStab**
- 시퀀스 길이: 1.83초
- 재생 속도: 1.2999999523162842x
**주요 타이밍**
- **AttackWithEquip**: 0.38~0.48초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Skill`
- **AttackWithEquip**: 0.88~0.96초 (지속: 0.08초)
- 공격 태그: `Event.Attack.Skill`
- **ANS_SkillCancel**: 1.51~1.83초 (지속: 0.32초)
#### SK140205 - 접근
**설명**: 낮은 자세로 돌진합니다. 돌진 중 피격되지 않으며, 돌진 후 3초 동안 30%만큼 물리 피해가 증가합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.9x |
| 마나 비용 | 8 |
| 재사용 대기시간 | 4초 |
**어빌리티 클래스**: `GA_Skill_Rio_Approach → GA_Skill_Rio_Approach_C`
---
**몽타주 1: AM_PC_Rio_B_Skill_Approach**
- 시퀀스 길이: 0.57초
- 재생 속도: 1x
#### SK140202 - 단검 투척
**설명**: 단검을 던져 100%만큼 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.7x |
| 시전 시간 | 1초 |
| 마나 비용 | 10 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Rio_ThrowingDagger → GA_Skill_Rio_ThrowingDagger_C`
---
**몽타주 1: AM_PC_Rio_B_Skill_ThrowingDagger**
- 시퀀스 길이: 1.63초
- 재생 속도: 1x
### 서브 스킬
#### SK140101 - 내려 찍기
**설명**: 단검으로 내려 찍어 70%만큼 물리 피해를 입힙니다. 연계 점수에 따라 50/100/150% 추가 피해를 입힙니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 70% |
| 이동 속도 배율 | 0.9x |
**어빌리티 클래스**: `GA_Skill_Rio_DroppingAttack → GA_Skill_Rio_DroppingAttack_C`
---
**몽타주 1: AM_PC_Rio_B_Skill_DroppingAttack**
- 시퀀스 길이: 1.30초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.47~0.56초 (지속: 0.09초)
- 공격 태그: `Event.Attack.Sub`
### 궁극기
#### SK140301 - 마석 ‘민감’
**설명**: 마석의 힘을 개방하여 연계 점수 3점을 획득하고, 15초 동안 은신 및 투시 효과를 획득합니다. 대상의 뒤를 공격 시 '약점' 판정이 적용됩니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 30% |
| 이동 속도 배율 | 0.9x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Rio_Sensitive → GA_Skill_Rio_Sensitive_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `InActive`: GE_Skill_Rio_Sensitive_Active → GE_Skill_Rio_Sensitive_Active_C
---
**몽타주 1: AM_PC_Rio_B_Skill_Sensitive**
- 시퀀스 길이: 1.50초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: shortSword
#### 콤보 1: AM_PC_Rio_B_Attack_W01_01
- 시퀀스 길이: 1.17초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.62초 (지속: 0.62초)
- 일반 공격력 증가: -30.0%
- **AttackWithEquip**: 0.36~0.44초 (지속: 0.08초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Rio_B_Attack_W01_02
- 시퀀스 길이: 1.33초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~1.00초 (지속: 1.00초)
- 일반 공격력 증가: 10.0%
- **AttackWithEquip**: 0.52~0.62초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 3: AM_PC_Rio_B_Attack_W01_03
- 시퀀스 길이: 1.37초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.97초 (지속: 0.97초)
- 일반 공격력 증가: 15.0%
- **AttackWithEquip**: 0.57~0.62초 (지속: 0.06초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,212 @@
# 시노부 (Sinobu) - 전투 데이터
## 기본 정보
- **직업**: 닌자
- **궁극기 포인트**: 2035
- **장착 가능 장비**: ShortSword, Cloth, Light
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 10.0 |
| 민첩 (Dex) | 25.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 15.0 |
| 지혜 (Wis) | 15.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK180202 - 기폭찰
**설명**: 뒤로 점프하며 기폭찰 쿠나이를 설치합니다. 기폭찰 쿠나이는 적이 근처에 오면 폭발하여 130%만큼 물리 피해를 입힙니다. 사용 시 표창 1개를 충전합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 130% |
| 시전 시간 | 1초 |
| 마나 비용 | 10 |
| 재사용 대기시간 | 6초 |
**어빌리티 클래스**: `GA_Skill_Sinobu_BombTalisman → GA_Skill_Sinobu_BombTalisman_C`
---
**몽타주 1: AM_PC_Sinobu_B_Skill_BombTalisman**
- 시퀀스 길이: 3.00초
- 재생 속도: 1.399999976158142x
#### SK180203 - 비뢰각
**설명**: 대각선으로 날아차기를 하여 110%만큼 번개 속성 물리 피해를 입힙니다. 점프 상태에서만 사용 가능하며, 적중된 대상은 잠시 경직됩니다. 적중 시 표창 1개를 충전합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | Lightning |
| 피해 배율 | 110% |
| 이동 속도 배율 | 0.9x |
| 마나 비용 | 11 |
| 재사용 대기시간 | 8초 |
**어빌리티 클래스**: `GA_Skill_Sinobu_ThunderKick → GA_Skill_Sinobu_ThunderKick_C`
**Gameplay Effects**
- `OnHitTarget`: GE_Enemy_ShockMotion → GE_Enemy_ShockMotion_C
- `OnHitTarget`: GE_ShockMotion_Weak → GE_ShockMotion_Weak_C
- `OnHitTarget`: GE_Breakdown_Add → GE_Breakdown_Add_C
---
**몽타주 1: AM_PC_Sinobu_B_Skill_ThunderKick**
- 시퀀스 길이: 0.60초
- 재생 속도: 1x
**주요 타이밍**
- **AttackWithEquip**: 0.42~0.59초 (지속: 0.17초)
- 공격 태그: `Event.Attack.Skill`
#### SK180205 - 인술 ‘바꿔치기’
**설명**: 7초 동안 유지되는 '바꿔치기'를 사용합니다. '바꿔치기' 상태 중 피격 시 피해가 50% 감소하며, 3초 동안 투명화와 이동속도 증가 효과를 얻습니다. 효과 발동 시 표창 1개를 충전합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 90% |
| 이동 속도 배율 | 0.9x |
| 시전 시간 | 1.5초 |
| 마나 비용 | 12 |
| 재사용 대기시간 | 11초 |
| 지속 시간 | 10초 |
**어빌리티 클래스**: `GA_Skill_Casting_CanMove_CanRelease → GA_Skill_Casting_CanMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Sinobu_NinpoChange → GA_Skill_Sinobu_NinpoChange_C`
**Gameplay Effects**
- `InActive`: GE_Skill_Sinobu_NinpoChange → GE_Skill_Sinobu_NinpoChange_C
---
**몽타주 1: AM_PC_Sinobu_B_Skill_NinpoChange**
- 시퀀스 길이: 3.57초
- 재생 속도: 1x
**몽타주 2: AM_PC_Sinobu_B_Skill_NinpoChange_Active**
- 시퀀스 길이: 1.00초
- 재생 속도: 1x
### 서브 스킬
#### SK180101 - 표창
**설명**: 표창을 던져 120%만큼 물리 피해를 입힙니다. 일반 공격이 적중하거나 스킬을 사용하면 충전됩니다. 최대 3개까지 충전 가능합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 120% |
| 이동 속도 배율 | 0.9x |
| 시전 시간 | 1초 |
| 지속 시간 | 60초 |
**어빌리티 클래스**: `GA_Skill_Sinobu_Shuriken → GA_Skill_Sinobu_Shuriken_C`
---
**몽타주 1: AM_PC_Sinobu_B_Skill_Shuriken**
- 시퀀스 길이: 1.23초
- 재생 속도: 1.399999976158142x
### 궁극기
#### SK180301 - 마석 '반환'
**설명**: 마석의 힘을 개방하여 7초 동안 전방의 투사체 공격을 튕겨내고 근접 공격을 막아냅니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 0% |
| 이동 속도 배율 | 0.9x |
| 궁극기 | O |
| 지속 시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Ultimate_Base → GA_Skill_Ultimate_Base_C`
**활성 어빌리티**: `GA_Skill_Sinobu_Deflect → GA_Skill_Sinobu_Deflect_C`
**Gameplay Effects**
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `InActive`: GE_Skill_Sinobu_Silence → GE_Skill_Sinobu_Silence_C
---
**몽타주 1: AM_PC_Sinobu_Base_Skill_Deflect**
- 시퀀스 길이: 2.33초
- 재생 속도: 1x
## 기본 공격
### 기본 공격: shortSword
#### 콤보 1: AM_PC_Sinobu_B_Attack_W01_03
- 시퀀스 길이: 1.07초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.80초 (지속: 0.80초)
- 일반 공격력 증가: -5.0%
- **AttackWithEquip**: 0.27~0.40초 (지속: 0.13초)
- 공격 태그: `Event.Attack.Normal`
#### 콤보 2: AM_PC_Sinobu_B_Attack_W01_01
- 시퀀스 길이: 1.20초
- 재생 속도: 1x
**주요 타이밍**
- **ANS_AttackState**: 0.00~0.80초 (지속: 0.80초)
- 일반 공격력 증가: -15.0%
- **AttackWithEquip**: 0.35~0.45초 (지속: 0.10초)
- 공격 태그: `Event.Attack.Normal`

View File

@ -0,0 +1,211 @@
# 우르드 (Urud) - 전투 데이터
## 기본 정보
- **직업**: 원거리
- **궁극기 포인트**: 2623
- **장착 가능 장비**: Bow, Light, Cloth
## 기본 스탯
| 스탯 | 값 |
|------|-----|
| 힘 (Str) | 15.0 |
| 민첩 (Dex) | 20.0 |
| 지능 (Int) | 10.0 |
| 체력 (Con) | 15.0 |
| 지혜 (Wis) | 15.0 |
| HP | 100.0 |
| MP | 50.0 |
| 마나 재생 | 0.2 |
| 지구력 (Stamina) | 100.0 |
| 크리티컬 확률 (%) | 5.0 |
## 스킬
### 기본 스킬
#### SK110205 - 다발 화살
**설명**: 3발의 화살을 동시에 발사하여 각각 90%만큼 물리 피해를 입힙니다. 화살 3개 소모합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 90% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 14 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Urud_MultiShot_Quick → GA_Skill_Urud_MultiShot_Quick_C`
---
**몽타주 1: AM_PC_Urud_Base_B_Skill_MultiArrow**
- 시퀀스 길이: 1.62초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.85초
- 이벤트 태그: `Event.Effect.Shot`
#### SK110204 - 독성 화살
**설명**: 화살에 독을 발라 발사합니다. 적중된 대상은 중독됩니다. 화살을 1개 소모합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | Poison |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
| 마나 비용 | 9 |
| 재사용 대기시간 | 7초 |
**어빌리티 클래스**: `GA_Skill_Urud_PoisonArrow → GA_Skill_Urud_PoisonArrow_C`
**Gameplay Effects**
- `OnProjectileHitTarget`: GE_Attach_Poison → GE_Attach_Poison_C
---
**몽타주 1: AM_PC_Urud_Base_B_Skill_PoisonArrow**
- 시퀀스 길이: 1.62초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 0.85초
- 이벤트 태그: `Event.Effect.Shot`
#### SK110201 - 덫 설치
**설명**: 60초 동안 유지되는 덫을 최대 4개 설치할 수 있습니다. 덫을 밟은 대상은 기절합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 0% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 마나 비용 | 9 |
| 재사용 대기시간 | 5초 |
| 지속 시간 | 3초 |
**어빌리티 클래스**: `GA_Skill_Casting_CantMove_CanRelease → GA_Skill_Casting_CantMove_CanRelease_C`
**활성 어빌리티**: `GA_Skill_Urud_MakeTrap → GA_Skill_Urud_MakeTrap_C`
---
**몽타주 1: AM_PC_Urud_B_Skill_MakeTrap**
- 시퀀스 길이: 6.15초
- 재생 속도: 1x
#### SK110207 - 재장전
**설명**: 화살을 화살통에 장전 합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.9x |
| 시전 시간 | 5초 |
| 지속 시간 | 1초 |
**어빌리티 클래스**: `GA_Attack_Firearm_Reload → GA_Attack_Firearm_Reload_C`
---
**몽타주 1: AM_PC_Urud_B_Reload**
- 시퀀스 길이: 2.53초
- 재생 속도: 1x
### 서브 스킬
#### SK110101 - 화살 찌르기
**설명**: 화살로 찔러 75%만큼 물리 피해를 입힙니다. 적중 시 다음 일반 공격이 50% 증가합니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | PhysicalSkill |
| 원소 타입 | None |
| 피해 배율 | 70% |
| 이동 속도 배율 | 0.8x |
**어빌리티 클래스**: `GA_Skill_Urud_ArrowAttack → GA_Skill_Urud_ArrowAttack_C`
---
**몽타주 1: AM_PC_Urud_Base_B_Skill_ArrowStab**
- 시퀀스 길이: 2.00초
- 재생 속도: 1.7999999523162842x
**주요 타이밍**
- **AttackWithEquip**: 0.59~0.76초 (지속: 0.17초)
- 공격 태그: `Event.Attack.Sub`
### 궁극기
#### SK110301 - 마석 ‘폭쇄’
**설명**: 마석의 힘을 해방하여 15초 동안 화살에 범위 피해 효과를 부여합니다. 적중된 대상은 30% 확률로 화상에 걸립니다.
**스킬 속성**
| 항목 | 값 |
|------|-----|
| 공격 타입 | Normal |
| 원소 타입 | None |
| 피해 배율 | 100% |
| 이동 속도 배율 | 0.8x |
| 시전 시간 | 2초 |
| 궁극기 | O |
| 지속 시간 | 15초 |
**어빌리티 클래스**: `GA_Skill_Casting_Ultimate → GA_Skill_Casting_Ultimate_C`
**활성 어빌리티**: `GA_Skill_Urud_Explosion_Active → GA_Skill_Urud_Explosion_Active_C`
**Gameplay Effects**
- `InActive`: GE_Skill_Urud_Explosion → GE_Skill_Urud_Explosion_C
- `InCasting`: GE_Ignore_Shock → GE_Ignore_Shock_C
- `InCasting`: GE_Ignore_Stun → GE_Ignore_Stun_C
- `OnProjectileHitRangedTarget`: GE_Attack_Projectile_Splash → GE_Attack_Projectile_Splash_C
## 기본 공격
### 기본 공격: bow
#### 콤보 1: AM_PC_Urud_Base_B_Attack_N
- 시퀀스 길이: 3.28초
- 재생 속도: 1x
**주요 타이밍**
- **AN_Trigger_Projectile_Shot**: 2.52초

View File

@ -0,0 +1,247 @@
# -*- coding: utf-8 -*-
"""
DS 전투 데이터 수집 메인 스크립트
실행 방법:
python collect_combat_data.py
"""
import json
import logging
import os
from datetime import datetime
from typing import Dict, List
from data_extractors import (
TARGET_STALKERS,
extract_character_stat,
extract_attack_montages,
extract_skill_data,
extract_montage_info
)
from markdown_formatter import generate_stalker_markdown
from validators import validate_collection_result, log_collection_summary
# 로깅 설정
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
# 경로 설정
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SOURCE_DATA_DIR = os.path.join(os.path.dirname(BASE_DIR), '원본데이터')
OUTPUT_DIR = os.path.join(BASE_DIR, '수집결과')
MARKDOWN_DIR = os.path.join(OUTPUT_DIR, 'markdown')
DATATABLE_JSON = os.path.join(SOURCE_DATA_DIR, 'DataTable.json')
ANIMMONTAGE_JSON = os.path.join(SOURCE_DATA_DIR, 'AnimMontage.json')
OUTPUT_JSON = os.path.join(OUTPUT_DIR, 'all_stalkers_combat_data.json')
LOG_FILE = os.path.join(OUTPUT_DIR, 'collection_log.txt')
def load_json_file(file_path: str) -> dict:
"""JSON 파일 로드"""
logger.info(f"JSON 파일 로드 중: {file_path}")
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
logger.info(f" - 로드 완료")
return data
def collect_stalker_data(
stalker_name: str,
datatable_assets: List[Dict],
animmontage_assets: List[Dict]
) -> Dict:
"""
단일 스토커의 전투 데이터 수집
Args:
stalker_name: 스토커 이름 (소문자)
datatable_assets: DataTable.json의 Assets 배열
animmontage_assets: AnimMontage.json의 Assets 배열
Returns:
스토커 전투 데이터
"""
logger.info(f" [{stalker_name}] 데이터 수집 시작")
# 1. 기본 스탯 추출
stats = extract_character_stat(datatable_assets, stalker_name)
if not stats:
logger.error(f" [{stalker_name}] 스탯 정보 추출 실패")
return {}
# 2. 기본 정보 분리
basic_info = {
'name': stats.pop('name', ''),
'jobName': stats.pop('jobName', '')
}
# 3. 스킬 정보 분리
skills = {
'default': stats.pop('defaultSkills', []),
'sub': stats.pop('subSkill', ''),
'ultimate': stats.pop('ultimateSkill', ''),
'ultimatePoint': stats.pop('ultimatePoint', 0)
}
# 4. 스킬 상세 정보 및 몽타주 수집
skill_details = {}
all_skill_ids = skills['default'] + [skills['sub'], skills['ultimate']]
for skill_id in all_skill_ids:
if not skill_id:
continue
skill_data = extract_skill_data(datatable_assets, skill_id)
if not skill_data:
logger.warning(f" [{stalker_name}] 스킬 {skill_id} 데이터 추출 실패")
continue
# 스킬 몽타주 정보 수집
montage_paths = skill_data.get('useMontages', [])
montages = []
for montage_path in montage_paths:
montage_info = extract_montage_info(animmontage_assets, montage_path)
if montage_info:
montages.append(montage_info)
skill_data['montages'] = montages
skill_details[skill_id] = skill_data
# 5. 기본 공격 몽타주 수집
attack_montages_map = extract_attack_montages(datatable_assets, stalker_name)
basic_attacks = {}
for weapon_type, montage_paths in attack_montages_map.items():
montages = []
for montage_path in montage_paths:
montage_info = extract_montage_info(animmontage_assets, montage_path)
if montage_info:
montages.append(montage_info)
if montages:
basic_attacks[weapon_type] = montages
# 6. 최종 데이터 구성
stalker_data = {
'basic_info': basic_info,
'stats': stats,
'skills': skills,
'skill_details': skill_details,
'basic_attacks': basic_attacks
}
logger.info(f" [{stalker_name}] 데이터 수집 완료")
logger.info(f" - 스킬 {len(skill_details)}")
logger.info(f" - 무기 타입 {len(basic_attacks)}")
return stalker_data
def main():
"""메인 실행 함수"""
logger.info("=" * 80)
logger.info("DS 전투 데이터 수집 시작")
logger.info("=" * 80)
# 1. 원본 데이터 로드
logger.info("1. 원본 데이터 로드 중...")
datatable_data = load_json_file(DATATABLE_JSON)
animmontage_data = load_json_file(ANIMMONTAGE_JSON)
datatable_assets = datatable_data.get('Assets', [])
animmontage_assets = animmontage_data.get('Assets', [])
logger.info(f" - DataTable 에셋 수: {len(datatable_assets)}")
logger.info(f" - AnimMontage 에셋 수: {len(animmontage_assets)}")
logger.info("")
# 2. 스토커별 데이터 수집
logger.info(f"2. 스토커 데이터 수집 중 (총 {len(TARGET_STALKERS)}명)...")
stalkers_data = {}
for stalker_name in TARGET_STALKERS:
stalker_data = collect_stalker_data(
stalker_name,
datatable_assets,
animmontage_assets
)
if stalker_data:
stalkers_data[stalker_name] = stalker_data
logger.info("")
# 3. 통합 JSON 생성
logger.info("3. 통합 JSON 생성 중...")
collection_data = {
'collection_metadata': {
'collected_at': datetime.now().isoformat(),
'source_files': [
'DataTable.json',
'AnimMontage.json'
],
'total_stalkers': len(stalkers_data)
},
'stalkers': stalkers_data
}
with open(OUTPUT_JSON, 'w', encoding='utf-8') as f:
json.dump(collection_data, f, ensure_ascii=False, indent=2)
logger.info(f" - 통합 JSON 생성 완료: {OUTPUT_JSON}")
logger.info("")
# 4. 스토커별 마크다운 생성
logger.info(f"4. 스토커별 마크다운 생성 중 ({len(stalkers_data)}명)...")
for stalker_name, stalker_data in stalkers_data.items():
markdown_content = generate_stalker_markdown(stalker_name, stalker_data)
markdown_path = os.path.join(MARKDOWN_DIR, f'{stalker_name}.md')
with open(markdown_path, 'w', encoding='utf-8') as f:
f.write(markdown_content)
logger.info(f" - {stalker_name}.md 생성 완료")
logger.info("")
# 5. 데이터 검증
logger.info("5. 데이터 검증 중...")
is_valid = validate_collection_result(collection_data)
if is_valid:
logger.info(" ✓ 모든 데이터 검증 통과")
else:
logger.warning(" ⚠ 일부 데이터 검증 실패 (상세 내용은 로그 참조)")
logger.info("")
# 6. 수집 요약 로그 생성
logger.info("6. 수집 요약 로그 생성 중...")
log_collection_summary(collection_data, LOG_FILE)
logger.info("")
# 7. 완료
logger.info("=" * 80)
logger.info("DS 전투 데이터 수집 완료!")
logger.info("=" * 80)
logger.info(f"결과 파일:")
logger.info(f" - 통합 JSON: {OUTPUT_JSON}")
logger.info(f" - 마크다운: {MARKDOWN_DIR}")
logger.info(f" - 수집 로그: {LOG_FILE}")
logger.info("")
if __name__ == '__main__':
try:
main()
except Exception as e:
logger.exception("수집 중 예외 발생:")
raise

View File

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
"""
CustomProperties 문자열 파싱 유틸리티
AnimMontage의 AnimNotifies에 있는 CustomProperties는 모두 문자열로 저장되어 있어서
타입 변환 및 파싱이 필요합니다.
"""
import re
from typing import Any, Optional
def parse_float(value: str) -> Optional[float]:
"""문자열을 float로 변환 (실패 시 None)"""
try:
return float(value.strip())
except (ValueError, AttributeError):
return None
def parse_tag_name(value: str) -> Optional[str]:
"""
(TagName="Event.Attack.Normal") 형태에서 태그 이름 추출
Example:
'(TagName="Event.Attack.Normal")' -> 'Event.Attack.Normal'
"""
if not value:
return None
match = re.search(r'TagName="([^"]+)"', value)
if match:
return match.group(1)
return None
def parse_vector(value: str) -> Optional[dict]:
"""
(X=0.0,Y=1.0,Z=0.0) 형태를 dict로 변환
Example:
'(X=0.0,Y=1.0,Z=0.0)' -> {'X': 0.0, 'Y': 1.0, 'Z': 0.0}
"""
if not value:
return None
try:
x_match = re.search(r'X=([-\d.]+)', value)
y_match = re.search(r'Y=([-\d.]+)', value)
z_match = re.search(r'Z=([-\d.]+)', value)
if x_match and y_match and z_match:
return {
'X': float(x_match.group(1)),
'Y': float(y_match.group(1)),
'Z': float(z_match.group(1))
}
except (ValueError, AttributeError):
pass
return None
def parse_array(value: str) -> Optional[list]:
"""
(0.1,0.2,0.3) 형태를 리스트로 변환
Example:
'(0.1,0.2,0.3)' -> [0.1, 0.2, 0.3]
"""
if not value:
return None
try:
# 괄호 제거
cleaned = value.strip().strip('()')
if not cleaned:
return []
# 쉼표로 분리
parts = cleaned.split(',')
return [float(p.strip()) for p in parts if p.strip()]
except (ValueError, AttributeError):
return None
def parse_custom_property(value: str, field_name: str) -> Any:
"""
CustomProperties 필드의 값을 적절한 타입으로 변환
Args:
value: 원본 문자열 값
field_name: 필드 이름 (타입 추론에 사용)
Returns:
파싱된 값 (float, str, dict, list 등)
"""
if not isinstance(value, str):
return value
value = value.strip()
# 빈 문자열
if not value or value == '':
return None
# TagName 파싱
if 'TagName=' in value:
tag = parse_tag_name(value)
if tag and tag != 'None':
return tag
return None
# Vector 파싱
if value.startswith('(') and ('X=' in value or 'Y=' in value or 'Z=' in value):
vec = parse_vector(value)
if vec:
return vec
# Array 파싱 (TimeArray, LocationArray 등)
if 'Array' in field_name and value.startswith('('):
arr = parse_array(value)
if arr is not None:
return arr
# Boolean 파싱
if value.lower() in ['true', 'false']:
return value.lower() == 'true'
# Float 파싱 시도
float_val = parse_float(value)
if float_val is not None:
return float_val
# 기본: 문자열 그대로 반환
return value
def extract_notify_properties(notify: dict, target_fields: list) -> dict:
"""
AnimNotify에서 CustomProperties의 특정 필드만 추출
Args:
notify: AnimNotify 딕셔너리
target_fields: 추출할 필드 이름 리스트
Returns:
추출된 프로퍼티 딕셔너리
"""
custom_props = notify.get('CustomProperties', {})
result = {}
for field in target_fields:
if field in custom_props:
value = custom_props[field]
parsed = parse_custom_property(value, field)
if parsed is not None:
result[field] = parsed
return result

View File

@ -0,0 +1,377 @@
# -*- coding: utf-8 -*-
"""
DataTable 및 AnimMontage 데이터 추출 모듈
"""
import logging
from typing import Dict, List, Optional, Any
from custom_property_parser import extract_notify_properties, parse_custom_property
logger = logging.getLogger(__name__)
# 수집 대상 스토커 목록 (소문자)
TARGET_STALKERS = [
'hilda', 'urud', 'nave', 'baran', 'rio', 'clad',
'rene', 'sinobu', 'lian', 'cazimord', 'blackmaria'
]
def round_float(value: Any) -> Any:
"""소수점 2자리로 반올림 (float만 처리)"""
if isinstance(value, (int, float)):
return round(float(value), 2)
return value
def extract_character_stat(data_table_assets: List[Dict], stalker_name: str) -> Dict:
"""
DT_CharacterStat에서 스토커의 기본 스탯 추출
Args:
data_table_assets: DataTable.json의 Assets 배열
stalker_name: 스토커 이름 (소문자)
Returns:
스탯 딕셔너리
"""
# DT_CharacterStat 찾기
dt_char_stat = None
for asset in data_table_assets:
if asset.get('AssetName') == 'DT_CharacterStat':
dt_char_stat = asset
break
if not dt_char_stat:
logger.warning("DT_CharacterStat not found")
return {}
# 해당 스토커 행 찾기
rows = dt_char_stat.get('Rows', [])
stalker_row = None
for row in rows:
if row.get('RowName', '').lower() == stalker_name.lower():
stalker_row = row
break
if not stalker_row:
logger.warning(f"Stalker '{stalker_name}' not found in DT_CharacterStat")
return {}
data = stalker_row.get('Data', {})
# 모든 숫자 필드를 소수점 2자리로 반올림
stats = {
'name': data.get('name', ''),
'jobName': data.get('jobName', ''),
# 기본 스탯
'str': round_float(data.get('str', 0)),
'dex': round_float(data.get('dex', 0)),
'int': round_float(data.get('int', 0)),
'con': round_float(data.get('con', 0)),
'wis': round_float(data.get('wis', 0)),
# 체력/마나
'hp': round_float(data.get('hP', 0)),
'mp': round_float(data.get('mP', 0)),
'manaRegen': round_float(data.get('manaRegen', 0)),
'stamina': round_float(data.get('stamina', 0)),
# 공격
'physicalDamage': round_float(data.get('physicalDamage', 0)),
'magicalDamage': round_float(data.get('magicalDamage', 0)),
'criticalPer': round_float(data.get('criticalPer', 0)),
'criticalDamage': round_float(data.get('criticalDamage', 0)),
'backAttackDamage': round_float(data.get('backAttackDamage', 0)),
# 방어
'defense': round_float(data.get('defense', 0)),
'physicalResistancePer': round_float(data.get('physicalResistancePer', 0)),
'rangedResistancePer': round_float(data.get('rangedResistancePer', 0)),
'magicalResistancePer': round_float(data.get('magicalResistancePer', 0)),
# 속성 저항
'fireResistancePer': round_float(data.get('fireResistancePer', 0)),
'poisonResistancePer': round_float(data.get('poisonResistancePer', 0)),
'waterResistancePer': round_float(data.get('waterResistancePer', 0)),
'lightningResistancePer': round_float(data.get('lightningResistancePer', 0)),
'holyResistancePer': round_float(data.get('holyResistancePer', 0)),
'darkResistancePer': round_float(data.get('darkResistancePer', 0)),
'dotReduceRatePer': round_float(data.get('dOTReduceRatePer', 0)),
# 이동
'walkSpeed': round_float(data.get('walkSpeed', 0)),
# 스킬
'defaultSkills': data.get('defaultSkills', []),
'subSkill': data.get('subSkill', ''),
'ultimateSkill': data.get('ultimateSkill', ''),
'ultimatePoint': data.get('ultimatePoint', 0),
# 장비
'equipableTypes': data.get('equipableTypes', []),
# 기타
'hitRadius': round_float(data.get('hitRadius', 0))
}
return stats
def extract_attack_montages(data_table_assets: List[Dict], stalker_name: str) -> Dict[str, List[str]]:
"""
DT_CharacterAbility에서 기본 공격 몽타주 추출
Args:
data_table_assets: DataTable.json의 Assets 배열
stalker_name: 스토커 이름 (소문자)
Returns:
무기타입별 몽타주 경로 리스트 딕셔너리
"""
# DT_CharacterAbility 찾기
dt_char_ability = None
for asset in data_table_assets:
if asset.get('AssetName') == 'DT_CharacterAbility':
dt_char_ability = asset
break
if not dt_char_ability:
logger.warning("DT_CharacterAbility not found")
return {}
# 해당 스토커 행 찾기
rows = dt_char_ability.get('Rows', [])
stalker_row = None
for row in rows:
if row.get('RowName', '').lower() == stalker_name.lower():
stalker_row = row
break
if not stalker_row:
logger.warning(f"Stalker '{stalker_name}' not found in DT_CharacterAbility")
return {}
data = stalker_row.get('Data', {})
attack_montage_map = data.get('attackMontageMap', {})
# 무기타입별 몽타주 배열 추출
result = {}
for weapon_type, weapon_data in attack_montage_map.items():
if isinstance(weapon_data, dict):
montage_array = weapon_data.get('montageArray', [])
if montage_array:
result[weapon_type] = montage_array
return result
def should_exclude_skill_montage(montage_path: str) -> bool:
"""
스킬 몽타주 제외 규칙 적용
제외 키워드: ready, Equip, Equipment, _E
"""
if not montage_path:
return True
exclude_keywords = ['ready', 'Equip', 'Equipment', '_E']
montage_name = montage_path.split('/')[-1].split('.')[0]
for keyword in exclude_keywords:
if keyword in montage_name:
return True
return False
def extract_skill_data(data_table_assets: List[Dict], skill_id: str) -> Optional[Dict]:
"""
DT_Skill에서 스킬 데이터 추출
Args:
data_table_assets: DataTable.json의 Assets 배열
skill_id: 스킬 ID (예: SK100201)
Returns:
스킬 데이터 딕셔너리
"""
# DT_Skill 찾기
dt_skill = None
for asset in data_table_assets:
if asset.get('AssetName') == 'DT_Skill':
dt_skill = asset
break
if not dt_skill:
logger.warning("DT_Skill not found")
return None
# 해당 스킬 행 찾기
rows = dt_skill.get('Rows', [])
skill_row = None
for row in rows:
if row.get('RowName') == skill_id:
skill_row = row
break
if not skill_row:
logger.warning(f"Skill '{skill_id}' not found in DT_Skill")
return None
data = skill_row.get('Data', {})
# 스킬 몽타주 필터링 (제외 규칙 적용)
use_montages = data.get('useMontages', [])
filtered_montages = [m for m in use_montages if not should_exclude_skill_montage(m)]
# GameplayEffect 정보 추출 (trigger와 gEClass만)
gameplay_effects = []
for ge in data.get('gameplayEffectSet', []):
if isinstance(ge, dict):
gameplay_effects.append({
'trigger': ge.get('trigger', ''),
'gEClass': ge.get('gEClass', '')
})
skill_data = {
'skillId': skill_id,
'name': data.get('name', ''),
'desc': data.get('desc', ''),
'descValues': data.get('descValues', []),
'simpleDesc': data.get('simpleDesc', ''),
'skillAttackType': data.get('skillAttackType', ''),
'skillElementType': data.get('skillElementType', ''),
'skillDamageRate': data.get('skillDamageRate', 1.0),
'walkSpeedMultiplier': data.get('walkSpeedMultiplier', 0),
'castingTime': data.get('castingTime', 0),
'manaCost': data.get('manaCost', 0),
'coolTime': data.get('coolTime', 0),
'useMontages': filtered_montages,
'bIsStackable': data.get('bIsStackable', False),
'maxStackCount': data.get('maxStackCount', 0),
'bIsUltimate': data.get('bIsUltimate', False),
'abilityClass': data.get('abilityClass', ''),
'activeAbilityClass': data.get('activeAbilityClass', ''),
'activeDuration': data.get('activeDuration', 0),
'gameplayEffectSet': gameplay_effects
}
return skill_data
def extract_montage_asset_name(montage_path: str) -> str:
"""
몽타주 경로에서 에셋 이름 추출
Example:
'/Script/Engine.AnimMontage'/Game/.../AM_PC_Hilda_B_Attack_W01_01.AM_PC_Hilda_B_Attack_W01_01''
-> 'AM_PC_Hilda_B_Attack_W01_01'
"""
if not montage_path:
return ''
# 마지막 .과 마지막 ' 사이의 문자열 추출
parts = montage_path.split('.')
if len(parts) >= 2:
asset_name = parts[-1].rstrip("'")
return asset_name
return montage_path
def extract_anim_notifies(montage_data: Dict) -> List[Dict]:
"""
AnimMontage에서 주요 AnimNotifies 추출
수집 대상:
- ANS_AttackState_C
- AnimNotifyState_AttackWithEquip
- ANS_SkillCancel_C
- AN_Trigger_Projectile_Shot_C
Args:
montage_data: AnimMontage 에셋 딕셔너리
Returns:
추출된 AnimNotifies 리스트
"""
target_classes = {
'ANS_AttackState_C': ['AddNormalAttackPer', 'AddPhysicalAttackPer'],
'AnimNotifyState_AttackWithEquip': ['AttackTag'],
'ANS_SkillCancel_C': [],
'AN_Trigger_Projectile_Shot_C': ['EventTag']
}
anim_notifies = montage_data.get('AnimNotifies', [])
result = []
for notify in anim_notifies:
notify_class = notify.get('NotifyStateClass') or notify.get('NotifyClass')
if notify_class in target_classes:
extracted = {
'notifyClass': notify_class,
'triggerTime': notify.get('TriggerTime', 0),
'duration': notify.get('Duration', 0)
}
# CustomProperties에서 필요한 필드 추출
target_fields = target_classes[notify_class]
if target_fields:
props = extract_notify_properties(notify, target_fields)
extracted['properties'] = props
result.append(extracted)
return result
def find_montage_data(anim_montage_assets: List[Dict], montage_path: str) -> Optional[Dict]:
"""
몽타주 경로로 AnimMontage 에셋 찾기
Args:
anim_montage_assets: AnimMontage.json의 Assets 배열
montage_path: 몽타주 경로
Returns:
찾은 몽타주 데이터 또는 None
"""
asset_name = extract_montage_asset_name(montage_path)
if not asset_name:
return None
for montage in anim_montage_assets:
if montage.get('AssetName') == asset_name:
return montage
return None
def extract_montage_info(anim_montage_assets: List[Dict], montage_path: str) -> Optional[Dict]:
"""
몽타주 경로로 몽타주 정보 추출 (AnimNotifies 포함)
Args:
anim_montage_assets: AnimMontage.json의 Assets 배열
montage_path: 몽타주 경로
Returns:
몽타주 정보 딕셔너리
"""
montage_data = find_montage_data(anim_montage_assets, montage_path)
if not montage_data:
asset_name = extract_montage_asset_name(montage_path)
logger.warning(f"Montage not found: {asset_name}")
return None
sections = montage_data.get('Sections', [])
section_info = []
for section in sections:
section_info.append({
'sectionName': section.get('SectionName', ''),
'startTime': section.get('StartTime', 0)
})
montage_info = {
'assetName': montage_data.get('AssetName', ''),
'sequenceLength': montage_data.get('SequenceLength', 0),
'rateScale': montage_data.get('RateScale', 1.0),
'sections': section_info,
'animNotifies': extract_anim_notifies(montage_data)
}
return montage_info

View File

@ -0,0 +1,339 @@
# -*- coding: utf-8 -*-
"""
마크다운 문서 생성 모듈
"""
import re
from typing import Dict, List
def format_skill_description(desc: str, desc_values: List) -> str:
"""
스킬 설명 문자열의 {0}, {1} 등을 descValues로 치환하고 줄바꿈 태그 제거
Args:
desc: 원본 설명 문자열 (예: "검을 휘둘러 {0}%만큼 번개 속성 물리 피해를 입힙니다.")
desc_values: 치환할 값들의 배열 (예: [130, 0])
Returns:
완성된 설명 문자열
"""
if not desc:
return ''
result = desc
# {0}, {1}, {2} 등을 descValues로 치환
for i, value in enumerate(desc_values):
placeholder = '{' + str(i) + '}'
result = result.replace(placeholder, str(value))
# 줄바꿈 태그 제거 (\r\n, \n, <br>, <br/> 등)
result = result.replace('\\r\\n', ' ')
result = result.replace('\\n', ' ')
result = result.replace('\r\n', ' ')
result = result.replace('\n', ' ')
result = re.sub(r'<br\s*/?>', ' ', result)
# 연속된 공백을 하나로
result = re.sub(r'\s+', ' ', result)
return result.strip()
def format_stat_table(stats: Dict) -> str:
"""기본 스탯 테이블 생성"""
stat_rows = [
('힘 (Str)', stats.get('str', 0)),
('민첩 (Dex)', stats.get('dex', 0)),
('지능 (Int)', stats.get('int', 0)),
('체력 (Con)', stats.get('con', 0)),
('지혜 (Wis)', stats.get('wis', 0)),
('HP', stats.get('hp', 0)),
('MP', stats.get('mp', 0)),
('마나 재생', stats.get('manaRegen', 0)),
('지구력 (Stamina)', stats.get('stamina', 0)),
('크리티컬 확률 (%)', stats.get('criticalPer', 0))
]
lines = ['| 스탯 | 값 |', '|------|-----|']
for name, value in stat_rows:
lines.append(f'| {name} | {value} |')
return '\n'.join(lines)
def format_skill_section(skill_data: Dict, skill_montages: List[Dict]) -> str:
"""개별 스킬 섹션 생성"""
lines = []
# 스킬 기본 정보
skill_id = skill_data.get('skillId', '')
skill_name = skill_data.get('name', '')
lines.append(f"#### {skill_id} - {skill_name}")
lines.append('')
# 스킬 설명 (descValues 적용)
desc = skill_data.get('desc', '')
desc_values = skill_data.get('descValues', [])
formatted_desc = format_skill_description(desc, desc_values)
if formatted_desc:
lines.append(f'**설명**: {formatted_desc}')
lines.append('')
# 스킬 속성
lines.append('**스킬 속성**')
lines.append('| 항목 | 값 |')
lines.append('|------|-----|')
lines.append(f"| 공격 타입 | {skill_data.get('skillAttackType', '')} |")
lines.append(f"| 원소 타입 | {skill_data.get('skillElementType', '')} |")
damage_rate = skill_data.get('skillDamageRate', 1.0)
lines.append(f"| 피해 배율 | {int(damage_rate * 100)}% |")
if skill_data.get('walkSpeedMultiplier', 0) != 0:
walk_speed = skill_data.get('walkSpeedMultiplier', 0)
lines.append(f"| 이동 속도 배율 | {walk_speed}x |")
casting_time = skill_data.get('castingTime', 0)
if casting_time > 0:
lines.append(f"| 시전 시간 | {casting_time}초 |")
mana_cost = skill_data.get('manaCost', 0)
if mana_cost > 0:
lines.append(f"| 마나 비용 | {mana_cost} |")
cool_time = skill_data.get('coolTime', 0)
if cool_time > 0:
lines.append(f"| 재사용 대기시간 | {cool_time}초 |")
# 스택 정보
if skill_data.get('bIsStackable', False):
max_stack = skill_data.get('maxStackCount', 0)
lines.append(f"| 스택 | 가능 (최대 {max_stack}) |")
# 궁극기 여부
if skill_data.get('bIsUltimate', False):
lines.append(f"| 궁극기 | O |")
# 지속 시간
active_duration = skill_data.get('activeDuration', 0)
if active_duration > 0:
lines.append(f"| 지속 시간 | {active_duration}초 |")
lines.append('')
# 어빌리티 클래스
ability_class = skill_data.get('abilityClass', '')
if ability_class and ability_class != 'None':
class_name = ability_class.split('/')[-1].replace('.', '')
lines.append(f'**어빌리티 클래스**: `{class_name}`')
lines.append('')
active_ability = skill_data.get('activeAbilityClass', '')
if active_ability and active_ability != 'None':
class_name = active_ability.split('/')[-1].replace('.', '')
lines.append(f'**활성 어빌리티**: `{class_name}`')
lines.append('')
# GameplayEffect
ge_set = skill_data.get('gameplayEffectSet', [])
if ge_set:
lines.append('**Gameplay Effects**')
for ge in ge_set:
trigger = ge.get('trigger', '')
ge_class = ge.get('gEClass', '')
ge_name = ge_class.split('/')[-1].replace('.', '') if ge_class else ''
lines.append(f'- `{trigger}`: {ge_name}')
lines.append('')
# 몽타주 정보
if skill_montages:
lines.append('---')
lines.append('')
for i, montage in enumerate(skill_montages, 1):
asset_name = montage.get('assetName', '')
lines.append(f'**몽타주 {i}: {asset_name}**')
lines.append('')
seq_len = montage.get('sequenceLength', 0)
rate_scale = montage.get('rateScale', 1.0)
lines.append(f'- 시퀀스 길이: {seq_len:.2f}')
lines.append(f'- 재생 속도: {rate_scale}x')
lines.append('')
# AnimNotifies
anim_notifies = montage.get('animNotifies', [])
if anim_notifies:
lines.append('**주요 타이밍**')
for notify in anim_notifies:
notify_class = notify.get('notifyClass', '')
trigger_time = notify.get('triggerTime', 0)
duration = notify.get('duration', 0)
notify_name = notify_class.replace('_C', '').replace('AnimNotifyState_', '')
if duration > 0:
end_time = trigger_time + duration
lines.append(f'- **{notify_name}**: {trigger_time:.2f}~{end_time:.2f}초 (지속: {duration:.2f}초)')
else:
lines.append(f'- **{notify_name}**: {trigger_time:.2f}')
# Properties
props = notify.get('properties', {})
if props:
for key, value in props.items():
if key == 'AttackTag':
lines.append(f' - 공격 태그: `{value}`')
elif key == 'EventTag':
lines.append(f' - 이벤트 태그: `{value}`')
elif key == 'AddNormalAttackPer':
lines.append(f' - 일반 공격력 증가: {value}%')
elif key == 'AddPhysicalAttackPer':
lines.append(f' - 물리 공격력 증가: {value}%')
lines.append('')
lines.append('')
lines.append('')
return '\n'.join(lines)
def format_basic_attack_section(weapon_type: str, montages: List[Dict]) -> str:
"""기본 공격 섹션 생성"""
lines = []
lines.append(f'### 기본 공격: {weapon_type}')
lines.append('')
for i, montage in enumerate(montages, 1):
asset_name = montage.get('assetName', '')
lines.append(f'#### 콤보 {i}: {asset_name}')
lines.append('')
seq_len = montage.get('sequenceLength', 0)
rate_scale = montage.get('rateScale', 1.0)
lines.append(f'- 시퀀스 길이: {seq_len:.2f}')
lines.append(f'- 재생 속도: {rate_scale}x')
lines.append('')
# AnimNotifies
anim_notifies = montage.get('animNotifies', [])
if anim_notifies:
lines.append('**주요 타이밍**')
for notify in anim_notifies:
notify_class = notify.get('notifyClass', '')
trigger_time = notify.get('triggerTime', 0)
duration = notify.get('duration', 0)
notify_name = notify_class.replace('_C', '').replace('AnimNotifyState_', '')
if duration > 0:
end_time = trigger_time + duration
lines.append(f'- **{notify_name}**: {trigger_time:.2f}~{end_time:.2f}초 (지속: {duration:.2f}초)')
else:
lines.append(f'- **{notify_name}**: {trigger_time:.2f}')
# Properties
props = notify.get('properties', {})
if props:
for key, value in props.items():
if key == 'AttackTag':
lines.append(f' - 공격 태그: `{value}`')
elif key == 'AddNormalAttackPer':
lines.append(f' - 일반 공격력 증가: {value}%')
elif key == 'AddPhysicalAttackPer':
lines.append(f' - 물리 공격력 증가: {value}%')
lines.append('')
lines.append('')
return '\n'.join(lines)
def generate_stalker_markdown(stalker_name: str, stalker_data: Dict) -> str:
"""
스토커 개별 마크다운 문서 생성
Args:
stalker_name: 스토커 이름 (소문자)
stalker_data: 스토커 전투 데이터
Returns:
마크다운 문서 문자열
"""
lines = []
# 타이틀
display_name = stalker_data.get('basic_info', {}).get('name', stalker_name.capitalize())
job_name = stalker_data.get('basic_info', {}).get('jobName', '')
lines.append(f'# {display_name} ({stalker_name.capitalize()}) - 전투 데이터')
lines.append('')
# 기본 정보
lines.append('## 기본 정보')
lines.append('')
lines.append(f'- **직업**: {job_name}')
ultimate_point = stalker_data.get('skills', {}).get('ultimatePoint', 0)
lines.append(f'- **궁극기 포인트**: {ultimate_point}')
equip_types = stalker_data.get('stats', {}).get('equipableTypes', [])
if equip_types:
lines.append(f'- **장착 가능 장비**: {", ".join(equip_types)}')
lines.append('')
# 기본 스탯
lines.append('## 기본 스탯')
lines.append('')
stats = stalker_data.get('stats', {})
lines.append(format_stat_table(stats))
lines.append('')
# 스킬
lines.append('## 스킬')
lines.append('')
# 기본 스킬
default_skills = stalker_data.get('skills', {}).get('default', [])
if default_skills:
lines.append('### 기본 스킬')
lines.append('')
skill_details = stalker_data.get('skill_details', {})
for skill_id in default_skills:
skill_data = skill_details.get(skill_id)
if skill_data:
skill_montages = skill_data.get('montages', [])
lines.append(format_skill_section(skill_data, skill_montages))
# 서브 스킬
sub_skill = stalker_data.get('skills', {}).get('sub', '')
if sub_skill:
lines.append('### 서브 스킬')
lines.append('')
skill_details = stalker_data.get('skill_details', {})
skill_data = skill_details.get(sub_skill)
if skill_data:
skill_montages = skill_data.get('montages', [])
lines.append(format_skill_section(skill_data, skill_montages))
# 궁극기
ultimate_skill = stalker_data.get('skills', {}).get('ultimate', '')
if ultimate_skill:
lines.append('### 궁극기')
lines.append('')
skill_details = stalker_data.get('skill_details', {})
skill_data = skill_details.get(ultimate_skill)
if skill_data:
skill_montages = skill_data.get('montages', [])
lines.append(format_skill_section(skill_data, skill_montages))
# 기본 공격
basic_attacks = stalker_data.get('basic_attacks', {})
if basic_attacks:
lines.append('## 기본 공격')
lines.append('')
for weapon_type, montages in basic_attacks.items():
if montages:
lines.append(format_basic_attack_section(weapon_type, montages))
return '\n'.join(lines)

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""결과 검증 스크립트"""
import json
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
JSON_FILE = os.path.join(BASE_DIR, '수집결과', 'all_stalkers_combat_data.json')
with open(JSON_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
hilda = data['stalkers']['hilda']
print('=== Hilda 기본 정보 ===')
print(f"이름: {hilda['basic_info']['name']}")
print(f"직업: {hilda['basic_info']['jobName']}")
print(f"\n=== 기본 스탯 (일부) ===")
stats = hilda['stats']
print(f"Str: {stats['str']}, Dex: {stats['dex']}, Int: {stats['int']}")
print(f"스탯 합계: {stats['str'] + stats['dex'] + stats['int'] + stats['con'] + stats['wis']}")
print(f"크리티컬: {stats['criticalPer']}%")
print(f"\n=== 스킬 ===")
skills = hilda['skills']
print(f"기본 스킬: {skills['default']}")
print(f"서브 스킬: {skills['sub']}")
print(f"궁극기: {skills['ultimate']}")
skill_201 = hilda['skill_details']['SK100201']
print(f"\n=== SK100201 정보 ===")
print(f"이름: {skill_201['name']}")
print(f"설명: {skill_201['desc']}")
print(f"descValues: {skill_201['descValues']}")
print(f"피해 배율: {skill_201['skillDamageRate']}")
print(f"몽타주 수: {len(skill_201['montages'])}")
if skill_201['montages']:
montage = skill_201['montages'][0]
print(f"첫 몽타주: {montage['assetName']}")
print(f" - 시퀀스 길이: {montage['sequenceLength']}")
print(f" - AnimNotifies 수: {len(montage['animNotifies'])}")
if montage['animNotifies']:
print(f"\n 첫 번째 AnimNotify:")
notify = montage['animNotifies'][0]
print(f" - Class: {notify['notifyClass']}")
print(f" - TriggerTime: {notify['triggerTime']}")
print(f" - Duration: {notify['duration']}")
if 'properties' in notify:
print(f" - Properties: {notify['properties']}")
print(f"\n=== 기본 공격 ===")
basic_attacks = hilda['basic_attacks']
for weapon_type, montages in basic_attacks.items():
print(f"{weapon_type}: {len(montages)}개 몽타주")
if montages:
first = montages[0]
print(f" - {first['assetName']}")
print(f" - AnimNotifies: {len(first['animNotifies'])}")

View File

@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
"""
데이터 검증 모듈
"""
import logging
from typing import Dict, List
logger = logging.getLogger(__name__)
def validate_stalker_data(stalker_name: str, stalker_data: Dict) -> bool:
"""
스토커 데이터 검증
Args:
stalker_name: 스토커 이름
stalker_data: 스토커 데이터
Returns:
검증 성공 여부
"""
warnings = []
errors = []
# 기본 정보 검증
basic_info = stalker_data.get('basic_info', {})
if not basic_info.get('name'):
warnings.append(f"[{stalker_name}] 이름 정보 누락")
# 스탯 검증
stats = stalker_data.get('stats', {})
if not stats:
errors.append(f"[{stalker_name}] 스탯 정보 누락")
else:
# 스탯 합계 확인 (75여야 함)
stat_sum = (
stats.get('str', 0) +
stats.get('dex', 0) +
stats.get('int', 0) +
stats.get('con', 0) +
stats.get('wis', 0)
)
if stat_sum != 75:
warnings.append(f"[{stalker_name}] 스탯 합계가 75가 아님: {stat_sum}")
# 스킬 검증
skills = stalker_data.get('skills', {})
default_skills = skills.get('default', [])
if not default_skills:
warnings.append(f"[{stalker_name}] 기본 스킬 정보 누락")
sub_skill = skills.get('sub', '')
if not sub_skill:
warnings.append(f"[{stalker_name}] 서브 스킬 정보 누락")
ultimate_skill = skills.get('ultimate', '')
if not ultimate_skill:
warnings.append(f"[{stalker_name}] 궁극기 정보 누락")
# 스킬 상세 정보 검증
skill_details = stalker_data.get('skill_details', {})
all_skill_ids = default_skills + [sub_skill, ultimate_skill]
for skill_id in all_skill_ids:
if skill_id and skill_id not in skill_details:
warnings.append(f"[{stalker_name}] 스킬 상세 정보 누락: {skill_id}")
# 기본 공격 검증
basic_attacks = stalker_data.get('basic_attacks', {})
if not basic_attacks:
warnings.append(f"[{stalker_name}] 기본 공격 몽타주 정보 누락")
# 경고 및 에러 출력
for warning in warnings:
logger.warning(warning)
for error in errors:
logger.error(error)
return len(errors) == 0
def validate_collection_result(collection_data: Dict) -> bool:
"""
전체 수집 결과 검증
Args:
collection_data: 전체 수집 데이터
Returns:
검증 성공 여부
"""
stalkers = collection_data.get('stalkers', {})
if not stalkers:
logger.error("수집된 스토커 데이터가 없습니다")
return False
total_stalkers = len(stalkers)
logger.info(f"{total_stalkers}명의 스토커 데이터 수집 완료")
# 각 스토커별 검증
all_valid = True
for stalker_name, stalker_data in stalkers.items():
if not validate_stalker_data(stalker_name, stalker_data):
all_valid = False
return all_valid
def log_collection_summary(collection_data: Dict, log_file_path: str):
"""
수집 결과 요약 로그 생성
Args:
collection_data: 전체 수집 데이터
log_file_path: 로그 파일 경로
"""
stalkers = collection_data.get('stalkers', {})
with open(log_file_path, 'w', encoding='utf-8') as f:
f.write("=" * 80 + "\n")
f.write("DS 전투 데이터 수집 결과 요약\n")
f.write("=" * 80 + "\n\n")
metadata = collection_data.get('collection_metadata', {})
f.write(f"수집 시각: {metadata.get('collected_at', '')}\n")
f.write(f"총 스토커 수: {len(stalkers)}\n\n")
for stalker_name, stalker_data in stalkers.items():
f.write("-" * 80 + "\n")
f.write(f"스토커: {stalker_name}\n")
f.write("-" * 80 + "\n")
basic_info = stalker_data.get('basic_info', {})
f.write(f" 이름: {basic_info.get('name', '')}\n")
f.write(f" 직업: {basic_info.get('jobName', '')}\n")
stats = stalker_data.get('stats', {})
stat_sum = (
stats.get('str', 0) +
stats.get('dex', 0) +
stats.get('int', 0) +
stats.get('con', 0) +
stats.get('wis', 0)
)
f.write(f" 스탯 합계: {stat_sum}\n")
skills = stalker_data.get('skills', {})
default_skills = skills.get('default', [])
f.write(f" 기본 스킬 수: {len(default_skills)}\n")
f.write(f" 서브 스킬: {skills.get('sub', '')}\n")
f.write(f" 궁극기: {skills.get('ultimate', '')}\n")
skill_details = stalker_data.get('skill_details', {})
f.write(f" 수집된 스킬 상세 정보 수: {len(skill_details)}\n")
# 각 스킬별 몽타주 수 집계
total_skill_montages = 0
for skill_id, skill_data in skill_details.items():
montages = skill_data.get('montages', [])
total_skill_montages += len(montages)
f.write(f" 스킬 몽타주 총 수: {total_skill_montages}\n")
basic_attacks = stalker_data.get('basic_attacks', {})
total_basic_montages = sum(len(montages) for montages in basic_attacks.values())
f.write(f" 기본 공격 몽타주 총 수: {total_basic_montages}\n")
f.write("\n")
f.write("=" * 80 + "\n")
f.write("수집 완료\n")
f.write("=" * 80 + "\n")
logger.info(f"수집 요약 로그 생성 완료: {log_file_path}")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
# DS-전투시스템-데이터수집
## 분석 대상 스토커 11명
- Hilda (힐다)
- Urud (우르드)
- Nave (네이브)
- Baran (바란)
- Rio (리오)
- Clad (클라드)
- Rene (레네)
- Sinobu (시노부)
- Lian (리옌)
- Cazimord (카지모르드)
- Blackmaria (블랙마리아)
## 1. Asset Export to JSON 소개
언리얼 엔진 에셋은 기본적으로 바이너리 형식으로 저장되어 LLM이나 외부 분석 도구가 직접 접근할 수 없습니다. 이를 해결하기 위해 **Asset Export to JSON** 에디터 확장 기능을 개발하였습니다.
**참고 문서**: `DS-전투분석_저장소\분석참고자료\DS-Asset_Export_to_JSON.md`
### 1.1 주요 내용
- **지원 에셋 타입**: DataTable, Blueprint, AnimMontage, CurveTable
- **프로젝트 설정 통합**: `편집 → 프로젝트 설정 → 플러그인 → Asset Export to JSON`
- **타임스탬프 익스포트**: 익스포트 히스토리 보관을 위한 타임스탬프 폴더 생성
- **완전한 데이터 추출**:
- DataTable: 모든 행/열 데이터
- Blueprint: 변수, 함수, 컴포넌트, 이벤트 그래프 노드 구조
- AnimMontage: 섹션, 노티파이, 커스텀 프로퍼티, 슬롯 애니메이션
- CurveTable: RichCurves 및 SimpleCurves 키 데이터
### 1.2 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. JSON 전투 데이터 수집
익스포트된 JSON 데이터에서 어떤 정보를 수집하는지 설명.
### 2.1 DataTable 어셋
언리얼의 데이터 테이블 어셋이 DataTable.json 형태로 익스포트 되어 있으며 주로 스토커의 밸런스 데이터가 있다.
- DT_CharacterStat 필드 정보
- 기본 스탯 값: Str, Dex, Int, Con, Wis, HP, MP, Mana Regen, Stamina, criticalPer // 소수점 2째자리 까지만 수집
- 전체 스탯 합계는 75로써 모든 스토커가 동일하다
- 스킬 ID 목록
- 기본 스킬 ID 정보: defaultSkills
- 보조 스킬 ID 정보: subSkill
- 궁극기 스킬 ID 정보: ultimateSkill
- 장착 가능 장비 타입: equipableTypes
- 궁극기 발동 필요 포인트: ultimatePoint
- DT_CharacterAbility 필드 정보
- 기본 공격(평타) 몽타주 정보: attackMontageMap > {무기정보} > montageArray
- DT_Skill 필드 정보
- 스킬 ID: RowName
- 스킬 이름: name
- 스킬 설명: desc // {} 안의 정보는 변수
- 스킬 설명에 포함된 변수 값: descValues // 유저에게 텍스트로 전달하기 위한 정보일 뿐 실제 데이터 값은 아니다
- 스킬의 공격 타입: skillAttackType
- 스킬의 원소 타입: skillElementType
- 스킬의 피해 배율: skillDamageRate
- 스킬 시전 중 걸음 속도 배율: walkSpeedMultiplier // 0이면 원래 속도로 걸어감
- 스킬 시전 시간: castingTime // 0이면 시전시간 없음
- 스킬의 마나 비용: manaCost
- 스킬 재사용 대기시간(초): coolTime
- 스킬에 지정된 몽타주 정보: useMontages
- 지정된 몽타주가 2개 이상일 경우 제외할 몽타주: ready, Equip, Equipment, _E, // 몽타주 이름에 해당 키워드가 있다면 수집, 분석에서 제외
- 스킬 스택 여부: bIsStackable
- 스킬의 최대 스택 수: maxStackCount // 0이면 스택 기능 없음
- 스킬의 궁극기 여부: bIsUltimate
- 스킬 시전(캐스팅포함) 결과에 따른 BP 정보: abilityClass
- 스킬의 캐스팅 결과에 따른 BP: activeAbilityClass // 즉, 캐스팅 로직을 탄 스킬은 이 BP까지 실행 된다
- 스킬 효과 유지 시간(초): activeDuration // 발동 효과가 특정 시간 동안 유지되는 경우에 사용
- 스킬에 적용할 GameplayEffect 목록: gameplayEffectSet
### 2.2 AnimMontage 어셋
언리얼의 애니메이션 몽타주 어셋이 AnimMontage.json 형태로 익스포트 되어 있으며, 스토커의 실질적인 모션 타이밍 데이터가 있다.
- 기본 공격 몽타주: `DT_CharacterAbility` 데이터 테이블에서 지정 된다.
- 스킬 공격 몽타주: `DT_Skill` 데이터 테이블에서 지정 된다.
#### 2.2.1 공통 수집 필드
- 어셋 이름: AssetName
- 시퀀스 길이(초): SequenceLength
- 재생 속도: RateScale
- 섹션 목록: Sections
- 섹션 이름: SectionName
- 섹션 시작 시간: StartTime
- 주요 AnimNotifies 정보: 모든 AnimNotifies를 수집하는 것이 아니라 지정한 AnimNotifies에 대해서만 정보를 수집 한다.
#### 2.2.2 수집 할 주요 AnimNotifies 및 CustomProperties
해당 AnimNotifies가 AnimMontage 내에 있을 경우 정보를 수집한다.
- ANS_AttackState_C: 공격 상태 및 피해 배율
- TriggerTime: 시작 시간 (초)
- Duration: 지속 시간 (초)
- AddNormalAttackPer: 피해 배율 증감 (%)
- AnimNotifyState_AttackWithEquip: 히트 판정 타이밍
- TriggerTime: 시작 시간 (초)
- Duration: 지속 시간 (초)
- AttackTag: 공격 타입 태그
- ANS_SkillCancel_C: 스킬 캔슬 윈도우
- TriggerTime: 시작 시간 (초)
- Duration: 지속 시간 (초)
- AN_Trigger_Projectile_Shot_C: 발사체 발사
- TriggerTime: 시작 시간 (초)
- EventTag

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff