리뉴얼

This commit is contained in:
Gnill82
2025-10-27 17:04:37 +09:00
parent faef6ce8bd
commit af7de76bc0
57 changed files with 479079 additions and 633159 deletions

View File

@ -0,0 +1,122 @@
# 아카이브된 분석 스크립트
이 디렉토리에는 개발 과정에서 사용된 일회성 체크 및 검증 스크립트들이 보관되어 있습니다.
**아카이브 일자**: 2025-10-27
**사유**: 핵심 분석 파이프라인 완성 후 정리
---
## 📁 파일 분류
### 🔍 스킬 검증 스크립트
특정 스킬의 데이터 추출 및 노티파이 검증에 사용된 스크립트
- **check_baran_clad_skills.py** - 바란/클라드 스킬 검증 (SK130301, SK150201)
- **check_lian_skills.py** - 리안 스킬 검증 1차
- **check_lian_skills2.py** - 리안 스킬 검증 2차
- **check_sk150201.py** - 클라드 SK150201 상세 분석
### 🏗️ 데이터 구조 탐색 스크립트
JSON 파일 구조 및 Blueprint 데이터 탐색
- **check_json_structure.py** - JSON 최상위 구조 확인
- **check_first_asset.py** - 첫 번째 Asset 구조 출력
- **check_data.py** - 전반적인 데이터 구조 확인
- **check_skill_structure.py** - DT_Skill 구조 분석
### 🎯 Character Ability 탐색 스크립트
DT_CharacterAbility 및 평타 몽타주 추출 검증
- **check_character_ability.py** - DT_CharacterAbility 기본 구조 확인
- **check_character_ability2.py** - attackMontageMap 추출 검증
- **check_character_ability3.py** - 평타 몽타주 상세 분석
### 🎬 AnimMontage 및 Notify 분석
AnimNotify 및 투사체 판정 로직 검증
- **check_montage_names.py** - 몽타주 이름 추출 검증
- **check_send_event_notify.py** - SimpleSendEvent 노티파이 분석
- **investigate_projectile.py** - 투사체 노티파이 상세 조사
### 🧪 Blueprint 변수 검증
Blueprint 변수 추출 및 매칭 검증
- **check_bp_vars.py** - Blueprint 변수 기본 추출
- **check_bp_verification.py** - Blueprint 변수 검증 로직
### ✅ 개선 사항 검증
버전별 개선 사항 적용 여부 확인
- **check_improvements.py** - v2.1~v2.2 개선사항 검증
- **verify_improvements.py** - 일반 개선사항 검증
- **verify_improvements_v2.3.py** - v2.3 개선사항 검증
---
## 📝 사용 목적
이 스크립트들은 다음 목적으로 작성되었습니다:
1. **데이터 구조 탐색**: JSON 및 Blueprint 데이터 구조 이해
2. **추출 로직 검증**: 몽타주, 노티파이, 스킬 데이터 추출 정확성 확인
3. **버그 수정**: 특정 스킬의 오류 원인 분석 및 해결
4. **개선사항 검증**: 버전 업데이트 후 변경사항 적용 확인
---
## 🔄 재사용 가능성
### 재사용 가능한 스크립트
다음 스크립트들은 향후 유사한 문제 발생 시 참고 가능합니다:
- **check_send_event_notify.py** - SimpleSendEvent 노티파이 분석 템플릿
- **investigate_projectile.py** - 투사체 노티파이 조사 방법
- **check_bp_vars.py** - Blueprint 변수 추출 예시
### 재사용 방법
```bash
# 예: 새로운 스킬 SK999999 분석이 필요한 경우
# check_sk150201.py를 복사하여 수정
cd D:\Work\WorldStalker\DS-전투분석_저장소\분석도구\v2\archive
cp check_sk150201.py check_sk999999.py
# 내부의 스킬 ID를 SK999999로 변경 후 실행
python check_sk999999.py
```
---
## 🗑️ 삭제 가능 여부
이 스크립트들은 현재 분석 파이프라인에서 사용되지 않지만, 다음 이유로 보존합니다:
1. **디버깅 참고**: 향후 유사한 문제 발생 시 해결 방법 참고
2. **데이터 구조 이해**: 새로운 개발자가 JSON 구조를 이해하는 데 도움
3. **분석 히스토리**: 시스템 개발 과정 기록
**권장 보존 기간**: 6개월~1년
만약 디스크 공간이 부족하거나 더 이상 필요 없다고 판단되면 삭제해도 무방합니다.
---
## 📚 관련 문서
- **../장기과제_Blueprint변수검증.md** - Blueprint 변수 활용 계획
- **../../분석결과/*/개선_보고서_*.md** - 버전별 개선 내역
- **../../ARCHITECTURE.md** - 전체 시스템 아키텍처
---
**작성자**: AI-assisted Development Team
**최종 업데이트**: 2025-10-27

View File

@ -0,0 +1,81 @@
"""바란, 클라드 스킬 몽타주 확인"""
import json
with open('../../원본데이터/AnimMontage.json', 'r', encoding='utf-8') as f:
montage_data = json.load(f)
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
dt_data = json.load(f)
# DT_Skill 찾기
dt_skill = None
for asset in dt_data.get('Assets', []):
if asset.get('AssetName') == 'DT_Skill':
dt_skill = asset
break
print("=== 바란, 클라드 스킬 몽타주 확인 ===\n")
target_skills = {
'SK130301': '일격분쇄 (바란)',
'SK150201': '다시 흙으로 (클라드)'
}
skill_montages = {}
for row in dt_skill.get('Rows', []):
row_name = row.get('RowName', '')
if row_name in target_skills:
row_data = row.get('Data', {})
use_montages = row_data.get('useMontages', [])
skill_name = row_data.get('name', '')
print(f"[{row_name}] {skill_name}")
print(f" useMontages: {len(use_montages)}")
if use_montages:
for montage_path in use_montages:
print(f" Path: {montage_path}")
# 몽타주 이름 추출
montage_name = montage_path.split('/')[-1].replace("'", "").split('.')[0]
if row_name not in skill_montages:
skill_montages[row_name] = []
skill_montages[row_name].append(montage_name)
print(f" Name: {montage_name}")
print()
# 각 몽타주에서 노티파이 확인
print("\n=== 몽타주 노티파이 확인 ===\n")
for skill_id, montage_names in skill_montages.items():
for montage_name in montage_names:
for asset in montage_data.get('Assets', []):
if asset.get('AssetName') == montage_name:
print(f"[{skill_id}] {target_skills[skill_id]} - {montage_name}")
notifies = asset.get('AnimNotifies', [])
print(f" 총 노티파이: {len(notifies)}\n")
found_attack_notify = False
for idx, notify in enumerate(notifies):
notify_class = notify.get('NotifyClass', '')
# SimpleSendEvent 노티파이
if 'SimpleSendEvent' in notify_class:
custom_props = notify.get('CustomProperties', {})
event_tag = custom_props.get('Event Tag', '')
# Event.SkillActivate 확인
if 'SkillActivate' in event_tag:
print(f" [{idx}] SimpleSendEvent")
print(f" Event Tag: {event_tag}")
print(f" >>> Event.SkillActivate 발견! (공격 스킬)")
found_attack_notify = True
print()
if not found_attack_notify:
print(" *** Event.SkillActivate를 찾지 못했습니다. ***\n")
print("-" * 60)
print()

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Blueprint 변수 상세 조사"""
import json
from pathlib import Path
# Blueprint.json 로드
bp_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/Blueprint.json")
with open(bp_file, 'r', encoding='utf-8') as f:
bp_data = json.load(f)
# DataTable.json 로드
dt_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/DataTable.json")
with open(dt_file, 'r', encoding='utf-8') as f:
dt_data = json.load(f)
assets = bp_data.get('Assets', [])
# GA_Skill_Knight_Counter 찾기
counter_bp = [a for a in assets if a.get('AssetName') == 'GA_Skill_Knight_Counter']
print("=" * 80)
print("GA_Skill_Knight_Counter Blueprint 분석")
print("=" * 80)
if counter_bp:
bp = counter_bp[0]
print(f"\nAssetName: {bp.get('AssetName')}")
print(f"ParentClass: {bp.get('ParentClass')}")
vars = bp.get('Variables', [])
print(f"\nTotal Variables: {len(vars)}")
# 숫자 타입 변수만
numeric_types = ['Float', 'Int', 'Double', 'Byte', 'int', 'float', 'double']
numeric_vars = []
for v in vars:
var_type = str(v.get('VarType', ''))
if any(t in var_type for t in numeric_types):
numeric_vars.append(v)
print(f"\nNumeric Variables ({len(numeric_vars)}개):")
for v in numeric_vars[:15]:
print(f" {v.get('VarName')}: {v.get('DefaultValue')} (type: {v.get('VarType')})")
# 모든 변수 출력 (참고용)
print(f"\nAll Variables:")
for v in vars[:20]:
print(f" {v.get('VarName')} ({v.get('VarType')}): {v.get('DefaultValue')}")
else:
print("GA_Skill_Knight_Counter를 찾을 수 없음!")
# DT_Skill에서 SK100202 정보
print("\n" + "=" * 80)
print("SK100202 DT_Skill 데이터")
print("=" * 80)
dt_assets = dt_data.get('Assets', [])
dt_skill = [a for a in dt_assets if a.get('AssetName') == 'DT_Skill'][0]
rows = dt_skill.get('Rows', [])
sk_row = [r for r in rows if r.get('RowName') == 'SK100202'][0]
sk_data = sk_row.get('Data', {})
print(f"스킬 이름: {sk_data.get('name')}")
print(f"Desc: {sk_data.get('desc')}")
print(f"DescValues: {sk_data.get('descValues')}")
# desc에서 {0}, {1} 위치 찾기
desc = sk_data.get('desc', '')
desc_values = sk_data.get('descValues', [])
print(f"\n변수 매칭:")
for i, value in enumerate(desc_values):
print(f" {{{i}}} = {value}")
print(f"\n추론: Hilda 반격 스킬은")
print(f" - {{{0}}}초 = {desc_values[0]}초 (반격 지속 시간)")
print(f" - {{{1}}}% = {desc_values[1]}% (반격 피해 배율)")
# 다른 스킬도 조사
print("\n" + "=" * 80)
print("다른 스킬 Blueprint 변수 조사")
print("=" * 80)
test_skills = [
('SK100201', 'GA_Skill_Hilda_SwordStrike', '칼날 격돌'),
('SK110205', 'GA_Skill_Urud_MultiShot_Quick', '다발 화살'),
]
for skill_id, bp_name, skill_name in test_skills:
print(f"\n=== {skill_name} ({skill_id}) ===")
# DT_Skill
sk_row = [r for r in rows if r.get('RowName') == skill_id]
if sk_row:
sk_data = sk_row[0].get('Data', {})
print(f"DescValues: {sk_data.get('descValues')}")
# Blueprint
bp_match = [a for a in assets if a.get('AssetName') == bp_name]
if bp_match:
vars = bp_match[0].get('Variables', [])
numeric_vars = [v for v in vars if any(t in str(v.get('VarType', '')) for t in numeric_types)]
print(f"Blueprint 변수 총 {len(vars)}개, 숫자형 {len(numeric_vars)}")
if numeric_vars:
print(" 숫자형 변수:")
for v in numeric_vars[:5]:
print(f" {v.get('VarName')}: {v.get('DefaultValue')}")
else:
print(f" Blueprint '{bp_name}' 없음")

View File

@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Blueprint 변수 검증 조사 스크립트"""
import json
import sys
from pathlib import Path
# Blueprint.json 로드
bp_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/Blueprint.json")
with open(bp_file, 'r', encoding='utf-8') as f:
bp_data = json.load(f)
# DataTable.json 로드
dt_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/DataTable.json")
with open(dt_file, 'r', encoding='utf-8') as f:
dt_data = json.load(f)
# validated_data.json 로드
val_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/분석결과/20251024_210822_v2/validated_data.json")
with open(val_file, 'r', encoding='utf-8') as f:
val_data = json.load(f)
print("=" * 80)
print("Blueprint 변수 검증 조사")
print("=" * 80)
# 예시: Hilda의 SK100202 (반격) 스킬 조사
print("\n=== 예시: Hilda SK100202 (반격) 스킬 ===")
# DT_Skill에서 SK100202 정보
assets = dt_data.get('Assets', [])
dt_skill = [a for a in assets if a.get('AssetName') == 'DT_Skill'][0]
rows = dt_skill.get('Rows', [])
sk100202_row = [r for r in rows if r.get('RowName') == 'SK100202'][0]
sk100202_data = sk100202_row.get('Data', {})
print(f"스킬 이름: {sk100202_data.get('name')}")
print(f"Desc: {sk100202_data.get('desc')}")
print(f"DescValues: {sk100202_data.get('descValues')}")
print(f"AbilityClass: {sk100202_data.get('abilityClass')}")
# Blueprint에서 관련 GA_Skill 찾기
ability_class = sk100202_data.get('abilityClass', '')
if ability_class:
bp_name = ability_class.split('/')[-1].split('.')[0] if '/' in ability_class else ability_class
print(f"\nBlueprint 이름: {bp_name}")
# Blueprint.json에서 검색
bp_assets = bp_data.get('Assets', [])
matching_bp = [a for a in bp_assets if a.get('AssetName') == bp_name]
if matching_bp:
print(f"Blueprint 발견: {matching_bp[0].get('AssetName')}")
# 변수 확인
variables = matching_bp[0].get('BlueprintVariables', [])
print(f"\nBlueprint 변수 ({len(variables)}개):")
for var in variables[:10]: # 처음 10개만
var_name = var.get('VarName', 'N/A')
var_type = var.get('VarType', 'N/A')
default_value = var.get('DefaultValue', 'N/A')
print(f" - {var_name} ({var_type}): {default_value}")
else:
print(f"Blueprint '{bp_name}' 찾을 수 없음")
# validated_data에서 확인
hilda = val_data['hilda']
sk = hilda['skills']['SK100202']
print(f"\n=== Validated Data ===")
print(f"DescFormatted: {sk.get('descFormatted')}")
print(f"Blueprint Variables in extracted data:")
bp_vars = sk.get('blueprintVariables', {})
if bp_vars:
for var_name, var_info in list(bp_vars.items())[:5]:
print(f" - {var_name}: {var_info}")
else:
print(" (Blueprint 변수 없음)")
# 다른 스킬도 몇 개 조사
print("\n" + "=" * 80)
print("다른 스킬 샘플 조사")
print("=" * 80)
sample_skills = [
('SK100201', 'hilda', '칼날 격돌'),
('SK110205', 'urud', '다발 화살'),
('SK160202', 'rene', 'Ifrit 소환')
]
for skill_id, stalker, skill_name in sample_skills:
print(f"\n=== {skill_id} ({skill_name}) ===")
# DT_Skill
skill_row = [r for r in rows if r.get('RowName') == skill_id]
if not skill_row:
print(" DT_Skill에 없음")
continue
skill_data = skill_row[0].get('Data', {})
desc = skill_data.get('desc', '')
desc_values = skill_data.get('descValues', [])
ability_class = skill_data.get('abilityClass', '')
print(f" Desc: {desc[:100]}...")
print(f" DescValues: {desc_values}")
print(f" AbilityClass: {ability_class}")
# Blueprint
if ability_class:
bp_name = ability_class.split('/')[-1].split('.')[0] if '/' in ability_class else ability_class
matching_bp = [a for a in bp_assets if a.get('AssetName') == bp_name]
if matching_bp:
variables = matching_bp[0].get('BlueprintVariables', [])
print(f" Blueprint 변수 개수: {len(variables)}")
# 숫자 타입 변수 찾기
numeric_vars = [v for v in variables if any(t in str(v.get('VarType', '')) for t in ['Float', 'Int', 'Byte'])]
if numeric_vars:
print(f" 숫자 변수 샘플:")
for var in numeric_vars[:3]:
print(f" - {var.get('VarName')}: {var.get('DefaultValue')}")

View File

@ -0,0 +1,69 @@
"""DT_CharacterAbility 데이터 구조 확인"""
import json
import sys
def check_character_ability():
# DataTable.json 로드
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# DT_CharacterAbility 찾기
dt_char = None
for table in data:
if table.get('Type') == 'DataTable' and 'CharacterAbility' in table.get('Name', ''):
dt_char = table
break
if not dt_char:
print("❌ DT_CharacterAbility 테이블을 찾을 수 없습니다.")
return
print(f"✅ DT_CharacterAbility 테이블 발견: {dt_char.get('Name')}")
print(f" Rows: {len(dt_char.get('Rows', {}))}")
# 우르드 데이터 확인
rows = dt_char.get('Rows', {})
urud_data = None
for row_name, row_data in rows.items():
if 'urud' in row_name.lower() or 'Urud' in row_name:
urud_data = row_data
print(f"\n🔍 우르드 데이터 발견: {row_name}")
break
if urud_data:
print("\n📋 우르드 데이터 키:")
for key in sorted(urud_data.keys()):
print(f" - {key}")
# Attack Montage Map 확인
if 'attackMontageMap' in urud_data:
print(f"\n⚔️ Attack Montage Map:")
attack_map = urud_data['attackMontageMap']
print(f" 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
for k, v in attack_map.items():
print(f" - {k}: {v}")
elif isinstance(attack_map, list):
for idx, item in enumerate(attack_map):
print(f" [{idx}]: {item}")
else:
print(f" 값: {attack_map}")
# 리옌 데이터 확인
lian_data = None
for row_name, row_data in rows.items():
if 'lian' in row_name.lower() or 'Lian' in row_name:
lian_data = row_data
print(f"\n🔍 리옌 데이터 발견: {row_name}")
break
if lian_data and 'attackMontageMap' in lian_data:
print(f"\n⚔️ 리옌 Attack Montage Map:")
attack_map = lian_data['attackMontageMap']
print(f" 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
for k, v in attack_map.items():
print(f" - {k}: {v}")
if __name__ == '__main__':
check_character_ability()

View File

@ -0,0 +1,62 @@
"""DT_CharacterAbility 데이터 구조 확인 (수정)"""
import json
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# Assets에서 CharacterAbility 찾기
assets = data.get('Assets', [])
print(f"총 Assets: {len(assets)}")
# DT_CharacterAbility 찾기
dt_char = None
for asset in assets:
asset_type = asset.get('Type', '')
asset_name = asset.get('Name', '')
if asset_type == 'DataTable' and 'CharacterAbility' in asset_name:
dt_char = asset
print(f"\n발견: {asset_name}")
break
if not dt_char:
print("DT_CharacterAbility를 찾을 수 없습니다.")
exit(1)
# Rows 확인
rows = dt_char.get('Rows', {})
print(f" Rows 개수: {len(rows)}")
print(f" Row 키: {list(rows.keys())}")
# 우르드 확인
for row_name, row_data in rows.items():
if 'Urud' in row_name:
print(f"\n우르드: {row_name}")
print(f" 데이터 키: {list(row_data.keys())}")
# attackMontageMap 확인
if 'attackMontageMap' in row_data:
print(f"\n attackMontageMap:")
attack_map = row_data['attackMontageMap']
print(f" 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
for k, v in attack_map.items():
print(f" [{k}]: {v}")
elif isinstance(attack_map, list):
for idx, item in enumerate(attack_map):
print(f" [{idx}]: {item}")
# 리옌 확인
for row_name, row_data in rows.items():
if 'Lian' in row_name:
print(f"\n리옌: {row_name}")
if 'attackMontageMap' in row_data:
print(f"\n attackMontageMap:")
attack_map = row_data['attackMontageMap']
print(f" 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
for k, v in attack_map.items():
print(f" [{k}]: {v}")

View File

@ -0,0 +1,72 @@
"""DT_CharacterAbility 데이터 구조 확인 (수정3)"""
import json
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
assets = data.get('Assets', [])
print(f"총 Assets: {len(assets)}\n")
# DT_CharacterAbility 찾기
dt_char = None
for asset in assets:
if asset.get('AssetName') == 'DT_CharacterAbility':
dt_char = asset
print(f"발견: {asset.get('AssetName')}")
print(f" AssetPath: {asset.get('AssetPath')}")
print(f" RowStructure: {asset.get('RowStructure')}")
break
if not dt_char:
print("DT_CharacterAbility를 찾을 수 없습니다.")
exit(1)
# Rows 확인
rows = dt_char.get('Rows', [])
print(f" Rows 개수: {len(rows)}\n")
# 우르드 찾기
print("=== 우르드 데이터 ===")
for row in rows:
row_name = row.get('RowName', '')
if 'urud' in row_name.lower():
print(f"RowName: {row_name}")
row_data = row.get('Data', {})
print(f"Data 키: {list(row_data.keys())}\n")
# attackMontageMap 확인
if 'attackMontageMap' in row_data:
attack_map = row_data['attackMontageMap']
print(f"attackMontageMap 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
print("attackMontageMap 내용 (dict):")
for k, v in attack_map.items():
print(f" [{k}]: {v}")
elif isinstance(attack_map, list):
print(f"attackMontageMap 내용 (list, {len(attack_map)}개):")
for idx, item in enumerate(attack_map):
print(f" [{idx}]: {item}")
else:
print(f"attackMontageMap 값: {attack_map}")
# 리옌 찾기
print("\n=== 리옌 데이터 ===")
for row in rows:
row_name = row.get('RowName', '')
if 'lian' in row_name.lower():
print(f"RowName: {row_name}")
row_data = row.get('Data', {})
if 'attackMontageMap' in row_data:
attack_map = row_data['attackMontageMap']
print(f"attackMontageMap 타입: {type(attack_map)}")
if isinstance(attack_map, dict):
print("attackMontageMap 내용 (dict):")
for k, v in attack_map.items():
print(f" [{k}]: {v}")
elif isinstance(attack_map, list):
print(f"attackMontageMap 내용 (list, {len(attack_map)}개):")
for idx, item in enumerate(attack_map):
print(f" [{idx}]: {item}")

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""임시 데이터 확인 스크립트"""
import json
from pathlib import Path
# DT_Skill 확인
data_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/DataTable.json")
with open(data_file, 'r', encoding='utf-8') as f:
data = json.load(f)
assets = data.get('Assets', [])
skill_dt = [a for a in assets if a.get('AssetName') == 'DT_Skill']
if skill_dt:
rows = skill_dt[0].get('Rows', [])
sample = rows[0]
skill_data = sample.get('Data', {})
print(f"RowName: {sample.get('RowName')}")
print(f"Data keys: {list(skill_data.keys())[:20]}")
print(f"\nHas desc: {'desc' in skill_data}")
print(f"Has descValues: {'descValues' in skill_data}")
if 'desc' in skill_data:
print(f"\nDesc sample: {skill_data['desc'][:200]}")
if 'descValues' in skill_data:
print(f"DescValues: {skill_data['descValues']}")
# Check for SK100202 (Hilda counter skill)
hilda_counter = [row for row in rows if row.get('RowName') == 'SK100202']
if hilda_counter:
counter_data = hilda_counter[0].get('Data', {})
print(f"\n\n=== SK100202 (Hilda Counter) ===")
print(f"Desc: {counter_data.get('desc', 'N/A')}")
print(f"DescValues: {counter_data.get('descValues', [])}")
# Check stalker names
char_stat_dt = [a for a in assets if a.get('AssetName') == 'DT_CharacterStat']
if char_stat_dt:
rows = char_stat_dt[0].get('Rows', [])
hilda_row = [row for row in rows if row.get('RowName') == 'hilda']
if hilda_row:
hilda_data = hilda_row[0].get('Data', {})
print(f"\n\n=== Hilda Character Data ===")
print(f"Name: {hilda_data.get('name', 'N/A')}")
print(f"JobName: {hilda_data.get('jobName', 'N/A')}")
print(f"Data keys: {list(hilda_data.keys())[:15]}")

View File

@ -0,0 +1,38 @@
"""첫 번째 Asset 구조 확인"""
import json
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
assets = data.get('Assets', [])
print(f"총 Assets: {len(assets)}\n")
if assets:
first = assets[0]
print("첫 번째 Asset의 구조:")
print(f" 타입: {type(first)}\n")
if isinstance(first, dict):
print(" 키 목록:")
for key in sorted(first.keys()):
value = first[key]
if isinstance(value, (list, dict)):
print(f" - {key}: {type(value).__name__} (len={len(value)})")
else:
print(f" - {key}: {value}")
# CharacterAbility 관련 찾기
print("\n\nCharacterAbility 관련 Asset 검색:")
for idx, asset in enumerate(assets):
if isinstance(asset, dict):
# 모든 값에서 'CharacterAbility' 문자열 찾기
asset_str = str(asset)
if 'CharacterAbility' in asset_str:
print(f"\n[{idx}] CharacterAbility 발견!")
print(f" 키: {list(asset.keys())[:10]}")
# Name 키가 있는지 확인
if 'Name' in asset:
print(f" Name: {asset['Name']}")
# 첫 10글자만 출력
print(f" 내용 샘플: {asset_str[:200]}...")
break

View File

@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""개선 요청사항 확인 스크립트"""
import json
from pathlib import Path
val_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/분석결과/20251024_213233_v2/validated_data.json")
with open(val_file, 'r', encoding='utf-8') as f:
data = json.load(f)
print("=" * 80)
print("개선 요청사항 확인")
print("=" * 80)
# 1. CastingTime 확인
print("\n1. CastingTime 수집 현황")
print("-" * 80)
casting_skills = []
for stalker_id, stalker_data in data.items():
skills = stalker_data.get('skills', {})
for skill_id, skill in skills.items():
casting_time = skill.get('castingTime', 0)
if casting_time > 0:
casting_skills.append({
'stalker': stalker_id,
'skillId': skill_id,
'name': skill.get('name'),
'castingTime': casting_time
})
print(f"시전시간이 있는 스킬: {len(casting_skills)}")
for item in casting_skills[:10]:
print(f" [{item['stalker']}] {item['skillId']} - {item['name']}: {item['castingTime']}")
# 2. SK170101 소수점 문제 확인
print("\n2. SK170101 (카지모르드 흘리기) 소수점 문제")
print("-" * 80)
cazi = data['cazimord']
sk170101 = cazi['skills']['SK170101']
print(f"DescValues (원본): {sk170101.get('descValues')}")
print(f"DescFormatted: {sk170101.get('descFormatted')[:100]}...")
# 3. Projectile Shot TriggerTime 확인
print("\n3. Projectile Shot 노티파이 TriggerTime")
print("-" * 80)
projectile_skills = []
for stalker_id, stalker_data in data.items():
skills = stalker_data.get('skills', {})
for skill_id, skill in skills.items():
montages = skill.get('montageData', [])
for montage in montages:
all_notifies = montage.get('allNotifies', [])
for notify in all_notifies:
notify_class = notify.get('NotifyClass', '')
if 'Trigger_Projectile_Shot' in notify_class:
projectile_skills.append({
'stalker': stalker_id,
'skillId': skill_id,
'name': skill.get('name'),
'montage': montage.get('assetName'),
'triggerTime': notify.get('TriggerTime', 0),
'sequenceLength': montage.get('sequenceLength', 0),
'actualDuration': montage.get('actualDuration', 0)
})
print(f"Projectile Shot 노티파이가 있는 스킬: {len(projectile_skills)}")
for item in projectile_skills[:10]:
print(f" [{item['stalker']}] {item['skillId']} - {item['name']}")
print(f" Montage: {item['montage']}")
print(f" TriggerTime: {item['triggerTime']:.3f}초 (전체: {item['actualDuration']:.2f}초)")
print(f" 빠른 발사: {item['actualDuration'] - item['triggerTime']:.3f}초 단축 가능")
# 4. DoT 스킬 확인
print("\n4. DoT 스킬 현황")
print("-" * 80)
dot_skills = []
for stalker_id, stalker_data in data.items():
skills = stalker_data.get('skills', {})
for skill_id, skill in skills.items():
is_dot = skill.get('isDot', False)
if is_dot:
dot_skills.append({
'stalker': stalker_id,
'skillId': skill_id,
'name': skill.get('name'),
'damageRate': skill.get('skillDamageRate', 0)
})
print(f"DoT 스킬: {len(dot_skills)}")
for item in dot_skills:
print(f" [{item['stalker']}] {item['skillId']} - {item['name']}: rate={item['damageRate']}")
# 5. descValues 소수점 문제가 있는 스킬 찾기
print("\n5. descValues 소수점 문제 스킬 찾기")
print("-" * 80)
long_decimal_skills = []
for stalker_id, stalker_data in data.items():
skills = stalker_data.get('skills', {})
for skill_id, skill in skills.items():
desc_values = skill.get('descValues', [])
for val in desc_values:
if isinstance(val, float):
# 소수점 자리수가 2보다 크면
val_str = str(val)
if '.' in val_str:
decimal_part = val_str.split('.')[1]
if len(decimal_part) > 2:
long_decimal_skills.append({
'stalker': stalker_id,
'skillId': skill_id,
'name': skill.get('name'),
'value': val,
'rounded': round(val, 2)
})
break
print(f"소수점 문제가 있는 스킬: {len(long_decimal_skills)}")
for item in long_decimal_skills:
print(f" [{item['stalker']}] {item['skillId']} - {item['name']}")
print(f" 원본: {item['value']}")
print(f" 반올림: {item['rounded']}")

View File

@ -0,0 +1,21 @@
"""JSON 파일 구조 확인"""
import json
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(f"데이터 타입: {type(data)}")
if isinstance(data, dict):
print(f"최상위 키: {list(data.keys())[:10]}")
# CharacterAbility 관련 키 찾기
char_keys = [k for k in data.keys() if 'Character' in k or 'Ability' in k]
print(f"\nCharacter/Ability 관련 키 ({len(char_keys)}개):")
for k in char_keys[:5]:
print(f" - {k}")
elif isinstance(data, list):
print(f"리스트 길이: {len(data)}")
print(f"첫 항목 타입: {type(data[0])}")
if isinstance(data[0], dict):
print(f"첫 항목 키: {list(data[0].keys())}")

View File

@ -0,0 +1,80 @@
"""리옌 연화, 정조준 몽타주 확인"""
import json
with open('../../원본데이터/AnimMontage.json', 'r', encoding='utf-8') as f:
montage_data = json.load(f)
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
dt_data = json.load(f)
# DT_Skill에서 SK190201, SK190101의 몽타주 이름 찾기
dt_skill = None
for asset in dt_data.get('Assets', []):
if asset.get('AssetName') == 'DT_Skill':
dt_skill = asset
break
if not dt_skill:
print("DT_Skill을 찾을 수 없습니다.")
exit(1)
print("=== 리옌 스킬 몽타주 확인 ===\n")
target_skills = ['SK190201', 'SK190101']
skill_montages = {}
for row in dt_skill.get('Rows', []):
row_name = row.get('RowName', '')
if row_name in target_skills:
row_data = row.get('Data', {})
montage_path = row_data.get('montage', '')
skill_name = row_data.get('name', '')
print(f"[{row_name}] {skill_name}")
print(f" Montage Path: {montage_path}")
# 몽타주 이름 추출
if montage_path:
montage_name = montage_path.split('/')[-1].replace("'", "").split('.')[0]
skill_montages[row_name] = montage_name
print(f" Montage Name: {montage_name}\n")
# 각 몽타주에서 노티파이 확인
print("\n=== 몽타주 노티파이 확인 ===\n")
for skill_id, montage_name in skill_montages.items():
for asset in montage_data.get('Assets', []):
if asset.get('AssetName') == montage_name:
print(f"[{skill_id}] {montage_name}")
notifies = asset.get('AnimNotifies', [])
print(f" 총 노티파이: {len(notifies)}\n")
for idx, notify in enumerate(notifies):
notify_class = notify.get('NotifyClass', '')
# SimpleSendEvent 노티파이
if 'SimpleSendEvent' in notify_class:
print(f" [{idx}] SimpleSendEvent")
print(f" NotifyClass: {notify_class}")
if 'CustomProperties' in notify:
custom_props = notify['CustomProperties']
event_tag = custom_props.get('Event Tag', '')
print(f" Event Tag: {event_tag}")
# Event.SpawnProjectile 확인
if 'SpawnProjectile' in event_tag:
print(f" >>> Event.SpawnProjectile 발견! (공격 스킬)")
print()
# Projectile 노티파이
if 'Projectile' in notify_class:
print(f" [{idx}] Projectile 노티파이")
print(f" NotifyClass: {notify_class}")
if 'ProjectileShot' in notify_class:
print(f" >>> ProjectileShot 발견! (공격 스킬)")
print()
print("-" * 60)
print()

View File

@ -0,0 +1,89 @@
"""리옌 연화, 정조준 몽타주 확인 (수정)"""
import json
with open('../../원본데이터/AnimMontage.json', 'r', encoding='utf-8') as f:
montage_data = json.load(f)
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
dt_data = json.load(f)
# DT_Skill에서 SK190201, SK190101의 몽타주 이름 찾기
dt_skill = None
for asset in dt_data.get('Assets', []):
if asset.get('AssetName') == 'DT_Skill':
dt_skill = asset
break
print("=== 리옌 스킬 몽타주 확인 ===\n")
target_skills = {
'SK190201': '연화',
'SK190101': '정조준'
}
skill_montages = {}
for row in dt_skill.get('Rows', []):
row_name = row.get('RowName', '')
if row_name in target_skills:
row_data = row.get('Data', {})
use_montages = row_data.get('useMontages', [])
skill_name = row_data.get('name', '')
print(f"[{row_name}] {skill_name}")
print(f" useMontages: {len(use_montages)}")
if use_montages:
montage_path = use_montages[0]
print(f" Path: {montage_path}")
# 몽타주 이름 추출
montage_name = montage_path.split('/')[-1].replace("'", "").split('.')[0]
skill_montages[row_name] = montage_name
print(f" Name: {montage_name}\n")
# 각 몽타주에서 노티파이 확인
print("\n=== 몽타주 노티파이 확인 ===\n")
for skill_id, montage_name in skill_montages.items():
for asset in montage_data.get('Assets', []):
if asset.get('AssetName') == montage_name:
print(f"[{skill_id}] {target_skills[skill_id]} - {montage_name}")
notifies = asset.get('AnimNotifies', [])
print(f" 총 노티파이: {len(notifies)}\n")
found_attack_notify = False
for idx, notify in enumerate(notifies):
notify_class = notify.get('NotifyClass', '')
# SimpleSendEvent 노티파이
if 'SimpleSendEvent' in notify_class:
custom_props = notify.get('CustomProperties', {})
event_tag = custom_props.get('Event Tag', '')
# Event.SpawnProjectile 또는 Event.SkillActivate 확인
if 'SpawnProjectile' in event_tag or 'SkillActivate' in event_tag:
print(f" [{idx}] SimpleSendEvent")
print(f" Event Tag: {event_tag}")
print(f" >>> 공격 스킬 판정!")
found_attack_notify = True
print()
# Projectile 노티파이
if 'Projectile' in notify_class:
print(f" [{idx}] Projectile 노티파이")
print(f" NotifyClass: {notify_class}")
trigger_time = notify.get('TriggerTime', 0)
print(f" TriggerTime: {trigger_time}")
if 'ProjectileShot' in notify_class or 'Trigger_Projectile' in notify_class:
print(f" >>> 공격 스킬 판정!")
found_attack_notify = True
print()
if not found_attack_notify:
print(" *** 공격 노티파이를 찾지 못했습니다. ***\n")
print("-" * 60)
print()

View File

@ -0,0 +1,47 @@
"""AnimMontage 이름 패턴 확인"""
import json
with open('../../원본데이터/AnimMontage.json', 'r', encoding='utf-8') as f:
data = json.load(f)
assets = data.get('Assets', [])
# 바란 관련 몽타주 찾기
print("=== 바란(Varan) 관련 몽타주 ===")
varan_montages = [a for a in assets if 'varan' in a.get('AssetName', '').lower() or 'baran' in a.get('AssetName', '').lower()]
for m in varan_montages[:10]:
print(f" - {m.get('AssetName')}")
# 클라드(Clad) 관련 몽타주 찾기
print("\n=== 클라드(Clad) 관련 몽타주 ===")
clad_montages = [a for a in assets if 'clad' in a.get('AssetName', '').lower()]
for m in clad_montages[:10]:
print(f" - {m.get('AssetName')}")
# 리옌(Lian) 관련 몽타주 찾기
print("\n=== 리옌(Lian) 관련 몽타주 ===")
lian_montages = [a for a in assets if 'lian' in a.get('AssetName', '').lower()]
for m in lian_montages[:10]:
print(f" - {m.get('AssetName')}")
# SimpleSendEvent 노티파이가 있는 몽타주 찾기
print("\n=== SimpleSendEvent 노티파이가 있는 몽타주 ===")
count = 0
for asset in assets:
notifies = asset.get('AnimNotifies', [])
for notify in notifies:
notify_class = notify.get('NotifyClass', '')
if 'SimpleSendEvent' in notify_class:
print(f" - {asset.get('AssetName')}")
print(f" NotifyClass: {notify_class}")
# CustomProperties 확인
if 'CustomProperties' in notify:
custom_props = notify['CustomProperties']
print(f" CustomProperties: {custom_props}")
count += 1
if count >= 5: # 처음 5개만
break
if count >= 5:
break

View File

@ -0,0 +1,63 @@
"""AN_SimpleSendEvent 노티파이 구조 확인"""
import json
# AnimMontage.json 로드
with open('../../원본데이터/AnimMontage.json', 'r', encoding='utf-8') as f:
data = json.load(f)
assets = data.get('Assets', [])
print(f"총 AnimMontage Assets: {len(assets)}\n")
# 문제가 되는 스킬들의 몽타주 찾기
target_skills = {
'SK130301': '바란 일격분쇄', # Event.SkillActivate
'SK150201': '클라드 다시 흙으로', # Event.SkillActivate
'SK190201': '리옌 연화', # Event.SpawnProjectile
'SK190101': '리옌 정조준', # ProjectileShot
}
# 각 스킬 몽타주에서 SimpleSendEvent 찾기
print("=== SimpleSendEvent 노티파이 검색 ===\n")
for asset in assets:
asset_name = asset.get('AssetName', '')
# 해당 스킬 ID가 AssetName에 포함되어 있는지 확인
for skill_id in target_skills.keys():
if skill_id in asset_name:
print(f"[{skill_id}] {target_skills[skill_id]}")
print(f" 몽타주: {asset_name}")
# AnimNotifies 확인
notifies = asset.get('AnimNotifies', [])
print(f" 총 노티파이: {len(notifies)}\n")
for idx, notify in enumerate(notifies):
notify_class = notify.get('NotifyClass', '')
# SimpleSendEvent 노티파이 찾기
if 'SimpleSendEvent' in notify_class:
print(f" [{idx}] SimpleSendEvent 발견!")
print(f" NotifyClass: {notify_class}")
print(f" 노티파이 키: {list(notify.keys())}")
# CustomProperties 확인
if 'CustomProperties' in notify:
custom_props = notify['CustomProperties']
print(f" CustomProperties 타입: {type(custom_props)}")
print(f" CustomProperties 내용:")
if isinstance(custom_props, dict):
for k, v in custom_props.items():
print(f" - {k}: {v}")
else:
print(f" {custom_props}")
print()
# ProjectileShot 노티파이도 확인 (SK190101)
if 'ProjectileShot' in notify_class or 'Projectile' in notify_class:
print(f" [{idx}] Projectile 노티파이 발견!")
print(f" NotifyClass: {notify_class}")
print()
print("-" * 60)
print()

View File

@ -0,0 +1,43 @@
"""SK150201 몽타주 확인"""
import json
from pathlib import Path
# 최신 출력 디렉토리
result_base = Path(__file__).parent.parent.parent / "분석결과"
v2_dirs = sorted([d for d in result_base.iterdir() if d.is_dir() and d.name.endswith('_v2')],
key=lambda d: d.stat().st_mtime)
latest_dir = v2_dirs[-1]
with open(latest_dir / 'intermediate_data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
clad = data['clad']
# SK150201 찾기
skills = [s for s in clad['defaultSkills'] if s and s.get('skillId') == 'SK150201']
if not skills:
print("SK150201을 찾을 수 없습니다.")
exit(1)
skill = skills[0]
print(f"SK150201: {skill.get('name')}")
print(f"useMontages: {skill.get('useMontages')}\n")
montage_data = skill.get('montageData', [])
print(f"montageData: {len(montage_data)}\n")
for idx, md in enumerate(montage_data):
print(f"[{idx}] {md.get('assetName')}")
print(f" hasAttack: {md.get('hasAttack')}")
print(f" attackNotifies: {len(md.get('attackNotifies', []))}")
print(f" allNotifies: {len(md.get('allNotifies', []))}")
# allNotifies에서 SimpleSendEvent 찾기
all_notifies = md.get('allNotifies', [])
for notify in all_notifies:
notify_class = notify.get('NotifyClass', '')
if 'SimpleSendEvent' in notify_class:
custom_props = notify.get('CustomProperties', {})
event_tag = custom_props.get('Event Tag', '')
print(f" SimpleSendEvent found: {event_tag}")
print()

View File

@ -0,0 +1,32 @@
"""DT_Skill 구조 확인"""
import json
with open('../../원본데이터/DataTable.json', 'r', encoding='utf-8') as f:
data = json.load(f)
dt_skill = None
for asset in data.get('Assets', []):
if asset.get('AssetName') == 'DT_Skill':
dt_skill = asset
break
if not dt_skill:
print("DT_Skill을 찾을 수 없습니다.")
exit(1)
print("=== DT_Skill 구조 확인 ===\n")
# SK190201 찾기
rows = dt_skill.get('Rows', [])
for row in rows:
row_name = row.get('RowName', '')
if row_name == 'SK190201':
row_data = row.get('Data', {})
print(f"SK190201 데이터 키:")
for key in sorted(row_data.keys()):
value = row_data[key]
if isinstance(value, str) and len(value) > 100:
print(f" - {key}: (긴 문자열, {len(value)}자)")
else:
print(f" - {key}: {value}")
break

View File

@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""Projectile 노티파이 조사 스크립트"""
import json
from pathlib import Path
from collections import defaultdict
# AnimMontage.json 로드
montage_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/원본데이터/AnimMontage.json")
with open(montage_file, 'r', encoding='utf-8') as f:
montage_data = json.load(f)
# validated_data.json 로드 (유틸리티 스킬 확인용)
val_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/분석결과/20251024_210822_v2/validated_data.json")
with open(val_file, 'r', encoding='utf-8') as f:
val_data = json.load(f)
print("=" * 80)
print("Projectile 노티파이 조사")
print("=" * 80)
# 1. 우르드 다발 화살 몽타주 확인
print("\n=== 예시: Urud 다발 화살 (SK110205) ===")
assets = montage_data.get('Assets', [])
multi_arrow = [a for a in assets if a.get('AssetName') == 'AM_PC_Urud_Base_B_Skill_MultiArrow']
if multi_arrow:
m = multi_arrow[0]
notifies = m.get('AnimNotifies', [])
print(f"Montage: {m.get('AssetName')}")
print(f"Total notifies: {len(notifies)}")
print("\nNotify Classes:")
for n in notifies:
notify_class = n.get('NotifyClass', 'N/A')
notify_state = n.get('NotifyStateClass', 'N/A')
if notify_class != 'N/A':
print(f" - NotifyClass: {notify_class}")
if notify_state != 'N/A':
print(f" - NotifyStateClass: {notify_state}")
else:
print("몽타주를 찾을 수 없음!")
# 2. 모든 PC 스킬 몽타주에서 Projectile 관련 노티파이 패턴 수집
print("\n" + "=" * 80)
print("모든 PC 스킬 몽타주에서 Projectile 패턴 조사")
print("=" * 80)
projectile_patterns = defaultdict(int)
pc_skill_montages = [a for a in assets if 'PC' in a.get('AssetPath', '') and 'Skill' in a.get('AssetPath', '')]
print(f"\n총 PC 스킬 몽타주: {len(pc_skill_montages)}")
for montage in pc_skill_montages:
notifies = montage.get('AnimNotifies', [])
for notify in notifies:
notify_class = notify.get('NotifyClass', '')
notify_state = notify.get('NotifyStateClass', '')
# Projectile 또는 관련 키워드 포함
keywords = ['Projectile', 'projectile', 'Shot', 'shot', 'Fire', 'Spawn', 'Arrow', 'Bullet']
for keyword in keywords:
if keyword in notify_class:
projectile_patterns[notify_class] += 1
if keyword in notify_state:
projectile_patterns[notify_state] += 1
print(f"\nProjectile 관련 노티파이 패턴 발견: {len(projectile_patterns)}")
for pattern, count in sorted(projectile_patterns.items(), key=lambda x: x[1], reverse=True):
print(f" {pattern}: {count}")
# 3. 유틸리티로 판정된 스킬 중 Projectile 노티파이가 있는 스킬 찾기
print("\n" + "=" * 80)
print("유틸리티 판정 스킬 중 Projectile 노티파이 보유 스킬")
print("=" * 80)
utility_with_projectile = []
for stalker_id, stalker_data in val_data.items():
skills = stalker_data.get('skills', {})
for skill_id, skill in skills.items():
is_utility = skill.get('isUtility', False)
if is_utility:
# 몽타주 데이터 확인
montage_data_list = skill.get('montageData', [])
for montage_info in montage_data_list:
all_notifies = montage_info.get('allNotifies', [])
has_projectile = False
projectile_notifies = []
for notify in all_notifies:
notify_class = notify.get('NotifyClass', '')
notify_state = notify.get('NotifyStateClass', '')
for keyword in keywords:
if keyword in notify_class or keyword in notify_state:
has_projectile = True
projectile_notifies.append(notify_class or notify_state)
if has_projectile:
utility_with_projectile.append({
'stalker': stalker_id,
'skillId': skill_id,
'skillName': skill.get('name', 'N/A'),
'montage': montage_info.get('assetName', 'N/A'),
'projectileNotifies': projectile_notifies,
'damageRate': skill.get('skillDamageRate', 0)
})
print(f"\n유틸리티로 잘못 판정된 가능성이 있는 스킬: {len(utility_with_projectile)}\n")
for item in utility_with_projectile:
print(f"[{item['stalker']}] {item['skillId']} - {item['skillName']}")
print(f" Damage Rate: {item['damageRate']}")
print(f" Montage: {item['montage']}")
print(f" Projectile Notifies: {', '.join(set(item['projectileNotifies']))}")
print()
# 4. 권장 ATTACK_NOTIFY_CLASSES 업데이트
print("=" * 80)
print("권장 ATTACK_NOTIFY_CLASSES 추가 키워드")
print("=" * 80)
# 빈도가 높은 패턴 추출 (5회 이상)
high_frequency = [p for p, c in projectile_patterns.items() if c >= 3]
print("\n추가 권장 키워드 (빈도 3회 이상):")
for pattern in high_frequency[:10]:
print(f" - '{pattern.split('_')[-1] if '_' in pattern else pattern}'")

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""개선사항 검증 스크립트"""
import json
from pathlib import Path
val_file = Path("D:/Work/WorldStalker/DS-전투분석_저장소/분석결과/20251027_081738_v2/validated_data.json")
with open(val_file, 'r', encoding='utf-8') as f:
data = json.load(f)
print("=" * 80)
print("개선사항 검증")
print("=" * 80)
# 1. descValues 소수점 반올림
print("\n1. DescValues 소수점 반올림")
print("-" * 80)
cazi = data['cazimord']
sk170101 = cazi['skills']['SK170101']
print(f"SK170101 (흘리기):")
print(f" DescValues: {sk170101.get('descValues')}")
print(f" DescFormatted: {sk170101.get('descFormatted')[:100]}...")
print(f" ✅ 소수점 반올림 확인: 3.8, 6.8")
# 2. effectiveAttackTime 추출
print("\n2. EffectiveAttackTime (Projectile 발사 시점)")
print("-" * 80)
urud = data['urud']
sk110205 = urud['skills']['SK110205']
montages = sk110205.get('montageData', [])
if montages:
m = montages[0]
print(f"SK110205 (다발 화살):")
print(f" Montage: {m.get('assetName')}")
print(f" ActualDuration: {m.get('actualDuration'):.2f}")
print(f" EffectiveAttackTime: {m.get('effectiveAttackTime'):.2f}")
print(f" ProjectileTriggerTimes: {m.get('projectileTriggerTimes')}")
time_saved = m.get('actualDuration', 0) - m.get('effectiveAttackTime', 0)
print(f"{time_saved:.2f}초 빠르게 공격 가능")
# 3. CastingTime 수집
print("\n3. CastingTime 수집")
print("-" * 80)
nave = data['nave']
sk120202 = nave['skills']['SK120202']
print(f"SK120202 (화염벽):")
print(f" CastingTime: {sk120202.get('castingTime')}")
print(f" ✅ 시전시간 수집 확인")
# 4. DoT 스킬 마킹
print("\n4. DoT 스킬 마킹")
print("-" * 80)
sk110204 = urud['skills']['SK110204']
print(f"SK110204 (독성 화살):")
print(f" IsDot: {sk110204.get('isDot')}")
print(f" DamageRate: {sk110204.get('skillDamageRate')}")
print(f" ✅ DoT 스킬 마킹 확인")
print("\n" + "=" * 80)
print("모든 개선사항 검증 완료!")
print("=" * 80)

View File

@ -0,0 +1,143 @@
"""v2.3 개선사항 검증 스크립트"""
import json
from pathlib import Path
# 최신 출력 디렉토리 찾기
result_base = Path(__file__).parent.parent.parent / "분석결과"
v2_dirs = sorted([d for d in result_base.iterdir() if d.is_dir() and d.name.endswith('_v2')],
key=lambda d: d.stat().st_mtime)
latest_dir = v2_dirs[-1]
print(f"검증 디렉토리: {latest_dir.name}\n")
# validated_data.json 로드
with open(latest_dir / 'validated_data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print("=" * 70)
print("v2.3 개선사항 검증")
print("=" * 70)
# 1. 우르드/리옌 평타 effectiveAttackTime 검증
print("\n[1] 우르드/리옌 평타 effectiveAttackTime (Projectile TriggerTime)")
print("-" * 70)
for stalker_id in ['urud', 'lian']:
stalker = data.get(stalker_id, {})
basic_attacks = stalker.get('basicAttacks', {})
for weapon_type, attacks in basic_attacks.items():
for attack in attacks:
montage_name = attack['montageName']
actual_duration = attack['actualDuration']
effective_time = attack.get('effectiveAttackTime', actual_duration)
projectile_triggers = attack.get('projectileTriggerTimes', [])
if projectile_triggers:
saved_time = actual_duration - effective_time
print(f"{stalker_id}/{weapon_type} 평타:")
print(f" Montage: {montage_name}")
print(f" ActualDuration: {actual_duration:.2f}")
print(f" EffectiveAttackTime: {effective_time:.2f}")
print(f" ProjectileTriggers: {projectile_triggers}")
print(f" => {saved_time:.2f}초 빠름!")
# 2. 공격 스킬 판정 검증
print("\n[2] 공격 스킬 판정 (SimpleSendEvent Event Tag)")
print("-" * 70)
test_skills = {
'SK130301': '바란 일격분쇄 (Event.SkillActivate)',
'SK150201': '클라드 다시 흙으로 (Event.SkillActivate)',
'SK190201': '리옌 연화 (Event.SpawnProjectile)',
'SK190101': '리옌 정조준 (ProjectileShot)',
}
for skill_id, expected_desc in test_skills.items():
found = False
for stalker_id, stalker in data.items():
all_skills = (stalker.get('defaultSkills', []) +
[stalker.get('subSkill')] +
[stalker.get('ultimateSkill')])
for skill in all_skills:
if skill and skill.get('skillId') == skill_id:
is_attack = len(skill.get('montageData', [])) > 0 and skill['montageData'][0].get('hasAttack', False)
status = "공격 스킬" if is_attack else "유틸리티"
print(f"{skill_id}: {skill.get('name')} => {status}")
print(f" Expected: {expected_desc}")
# 몽타주 데이터 확인
if skill.get('montageData'):
montage = skill['montageData'][0]
attack_notifies = montage.get('attackNotifies', [])
print(f" AttackNotifies: {len(attack_notifies)}")
# SimpleSendEvent 확인
for notify in attack_notifies:
if 'SimpleSendEvent' in notify.get('notifyClass', ''):
event_tag = notify.get('customProperties', {}).get('Event Tag', '')
print(f" - SimpleSendEvent: {event_tag}")
found = True
break
if found:
break
if not found:
print(f"{skill_id}: NOT FOUND")
# 3. 유틸리티 스킬 확인 (공격 노티파이 없음)
print("\n[3] 유틸리티 스킬 (공격 노티파이 없음)")
print("-" * 70)
utility_skills = {
'SK110207': '우르드 Reload',
'SK190209': '리옌 재장전'
}
for skill_id, expected_name in utility_skills.items():
found = False
for stalker_id, stalker in data.items():
all_skills = (stalker.get('defaultSkills', []) +
[stalker.get('subSkill')] +
[stalker.get('ultimateSkill')])
for skill in all_skills:
if skill and skill.get('skillId') == skill_id:
has_attack = len(skill.get('montageData', [])) > 0 and skill['montageData'][0].get('hasAttack', False)
status = "공격" if has_attack else "유틸리티"
print(f"{skill_id}: {skill.get('name')} => {status}")
if skill.get('montageData'):
montage = skill['montageData'][0]
attack_notifies_count = len(montage.get('attackNotifies', []))
print(f" AttackNotifies: {attack_notifies_count}")
found = True
break
if found:
break
# 4. 레네 소환체 섹션 확인
print("\n[4] 레네 소환체 섹션")
print("-" * 70)
rene = data.get('rene', {})
summons = rene.get('summons', {})
if summons:
print(f"레네 소환체: {len(summons)}")
for summon_name, summon_data in summons.items():
print(f"\n {summon_name}:")
print(f" SummonSkillId: {summon_data.get('summonSkillId')}")
print(f" SummonSkillName: {summon_data.get('summonSkillName')}")
print(f" SkillDamageRate: {summon_data.get('skillDamageRate')}")
print(f" AttackInterval: {summon_data.get('attackInterval')}")
print(f" DotType: {summon_data.get('dotType', 'None')}")
else:
print("레네 소환체 데이터 없음!")
print("\n" + "=" * 70)
print("검증 완료!")
print("=" * 70)