248 lines
7.4 KiB
Python
248 lines
7.4 KiB
Python
|
|
# -*- 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
|