73 lines
2.4 KiB
Python
73 lines
2.4 KiB
Python
from copy import deepcopy
|
|
import re
|
|
from typing import Any
|
|
|
|
|
|
# Only numeric data placeholders are normalized. Decorative Korean placeholders such as <소수1+>
|
|
# stay untouched and do not consume a value index.
|
|
DATA_PLACEHOLDER_PATTERN = re.compile(r'<(?:int|float\d*)[^>]*>')
|
|
DECORATIVE_KOREAN_PLACEHOLDER_PATTERN = re.compile(r'<[가-힣]+\d+\+>')
|
|
VALUE_PATTERN = re.compile(r'\{value\d+\}')
|
|
SKILL_DETAIL_NORMALIZE_VERSION = 'decorative-korean-placeholder-v1'
|
|
|
|
|
|
def replace_placeholders(template: str) -> str:
|
|
idx = 0
|
|
|
|
def repl(_: re.Match[str]) -> str:
|
|
nonlocal idx
|
|
idx += 1
|
|
return f'{{value{idx}}}'
|
|
|
|
protected = DECORATIVE_KOREAN_PLACEHOLDER_PATTERN.sub(lambda match: f'@@DDECORATIVE_{len(match.group(0))}_{match.start()}@@', template)
|
|
replacements = {
|
|
f'@@DDECORATIVE_{len(match.group(0))}_{match.start()}@@': match.group(0)
|
|
for match in DECORATIVE_KOREAN_PLACEHOLDER_PATTERN.finditer(template)
|
|
}
|
|
normalized = DATA_PLACEHOLDER_PATTERN.sub(repl, protected)
|
|
for token, placeholder in replacements.items():
|
|
normalized = normalized.replace(token, placeholder)
|
|
return normalized
|
|
|
|
|
|
def normalize_skill_detail(skill_data: dict[str, Any]) -> dict[str, Any]:
|
|
level_info = skill_data.get('levelInfo')
|
|
if not isinstance(level_info, dict):
|
|
return skill_data
|
|
|
|
option_desc = level_info.get('optionDesc')
|
|
if option_desc is not None:
|
|
level_info['optionDesc'] = replace_placeholders(option_desc)
|
|
return skill_data
|
|
|
|
|
|
def skill_detail_for_level(skill_data: dict[str, Any], level: int | None) -> dict[str, Any]:
|
|
result = deepcopy(skill_data)
|
|
if level is None:
|
|
return result
|
|
|
|
level = max(0, min(level, result.get('maxLevel', 0)))
|
|
level_info = result.get('levelInfo')
|
|
if not isinstance(level_info, dict):
|
|
return result
|
|
|
|
rows = level_info.get('rows')
|
|
if not isinstance(rows, list):
|
|
return result
|
|
|
|
detail = next((row for row in rows if row.get('level') == level), None)
|
|
if not detail:
|
|
return result
|
|
|
|
attribute = {**level_info, **detail}
|
|
attribute['detail'] = attribute.get('optionDesc', '')
|
|
|
|
for key, value in (attribute.get('optionValue') or {}).items():
|
|
attribute['detail'] = attribute['detail'].replace(f'{{{key}}}', str(value))
|
|
attribute['detail'] = VALUE_PATTERN.sub('-', attribute['detail'])
|
|
|
|
attribute.pop('rows', None)
|
|
result['attribute'] = attribute
|
|
result.pop('levelInfo', None)
|
|
return result
|