feat:优化代码
This commit is contained in:
@@ -1,24 +1,33 @@
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
import json
|
import json
|
||||||
|
from pathlib import Path
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
from api.core.Gzip import gzip_zip
|
from api.core.Gzip import gzip_zip
|
||||||
|
from api.utils.skill_detail import normalize_skill_detail
|
||||||
from core.basic.character import createCharacter
|
from core.basic.character import createCharacter
|
||||||
|
from core.basic.equipment import get_equipment
|
||||||
from core.character.adventure import adv, get_adv_list
|
from core.character.adventure import adv, get_adv_list
|
||||||
from database.connect import RedisPool
|
from database.connect import BACKEND_DIR, RedisPool
|
||||||
|
|
||||||
|
|
||||||
PreheatLoader = Callable[[], object]
|
PreheatLoader = Callable[[], object]
|
||||||
PreheatTask = tuple[str, PreheatLoader]
|
PreheatTask = tuple[str, PreheatLoader]
|
||||||
PREHEAT_LOCK_KEY = 'dcalc_sys:preheat:lock'
|
PREHEAT_LOCK_KEY = 'dcalc_sys:preheat:lock'
|
||||||
PREHEAT_LOCK_EXPIRE_SECONDS = 15 * 60
|
PREHEAT_LOCK_EXPIRE_SECONDS = 15 * 60
|
||||||
|
SKILL_DETAIL_PARTS = ('cn', 'skillDetail')
|
||||||
|
|
||||||
|
|
||||||
def _load_skills():
|
def _load_skills():
|
||||||
with open('./openapi/data/skills.json', encoding='utf-8') as file:
|
with (BACKEND_DIR / 'openapi' / 'data' / 'skills.json').open(encoding='utf-8') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_custom_cn():
|
||||||
|
with (BACKEND_DIR / 'openapi' / 'custom_cn.json').open(encoding='utf-8') as file:
|
||||||
return json.load(file)
|
return json.load(file)
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +53,125 @@ def _build_character_preheat_tasks(equ_version: str = '0') -> list[PreheatTask]:
|
|||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_skill_detail_files() -> list[Path]:
|
||||||
|
data_dir = BACKEND_DIR / 'openapi' / 'data'
|
||||||
|
return sorted(data_dir.glob('*/' + '*/' + '/'.join(SKILL_DETAIL_PARTS) + '/*.json'))
|
||||||
|
|
||||||
|
|
||||||
|
def _skill_prefix_from_path(path: Path) -> str:
|
||||||
|
relative = path.relative_to(BACKEND_DIR / 'openapi' / 'data').with_suffix('')
|
||||||
|
return relative.as_posix()
|
||||||
|
|
||||||
|
|
||||||
|
def _openapi_skill_detail_key(prefix: str) -> str:
|
||||||
|
parts = prefix.split('/')
|
||||||
|
return f'openapi:{parts[0]}:{parts[1]}:{parts[-1]}'
|
||||||
|
|
||||||
|
|
||||||
|
def _load_skill_detail(path: Path):
|
||||||
|
with path.open(encoding='utf-8') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
|
||||||
|
def _option_value_count(skill_data: dict) -> int:
|
||||||
|
rows = skill_data.get('levelInfo', {}).get('rows') or []
|
||||||
|
if not rows:
|
||||||
|
return 0
|
||||||
|
return max(
|
||||||
|
(
|
||||||
|
int(key.replace('value', ''))
|
||||||
|
for row in rows
|
||||||
|
for key in (row.get('optionValue') or {})
|
||||||
|
if key.startswith('value') and key.replace('value', '').isdigit()
|
||||||
|
),
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _skill_data_value(skill_data: dict, index: int) -> list:
|
||||||
|
key = f'value{index + 1}'
|
||||||
|
rows = skill_data.get('levelInfo', {}).get('rows') or []
|
||||||
|
data = [0] + [(row.get('optionValue') or {}).get(key, 0) for row in rows]
|
||||||
|
return [0 if item is None or item == '-' else item for item in data]
|
||||||
|
|
||||||
|
|
||||||
|
def _custom_value(custom_cn: list[dict], prefix: str):
|
||||||
|
parts = prefix.split('/')
|
||||||
|
job = parts[0]
|
||||||
|
job_grow = parts[1]
|
||||||
|
skill_id = parts[-1]
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
item.get('custom', 1.0)
|
||||||
|
for item in custom_cn
|
||||||
|
if item.get('job') == job and item.get('jobGrow') == job_grow and item.get('skillId') == skill_id
|
||||||
|
),
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_skill_data_preheat_tasks(custom_cn: list[dict]) -> list[PreheatTask]:
|
||||||
|
tasks: list[PreheatTask] = []
|
||||||
|
for path in _iter_skill_detail_files():
|
||||||
|
prefix = _skill_prefix_from_path(path)
|
||||||
|
skill_data = _load_skill_detail(path)
|
||||||
|
|
||||||
|
tasks.append(
|
||||||
|
(
|
||||||
|
_openapi_skill_detail_key(prefix),
|
||||||
|
lambda skill_data=skill_data: normalize_skill_detail(skill_data),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tasks.append((f'dcalc:skill:{prefix}:custom', lambda prefix=prefix: _custom_value(custom_cn, prefix)))
|
||||||
|
|
||||||
|
if skill_data.get('evolution'):
|
||||||
|
tasks.append(
|
||||||
|
(
|
||||||
|
f'dcalc:skill:{prefix}:vps',
|
||||||
|
lambda skill_data=skill_data: [{'name': item['name'], 'desc': item['desc']} for item in skill_data.get('evolution', [])],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for index in range(_option_value_count(skill_data)):
|
||||||
|
tasks.append((f'dcalc:skill:{prefix}:{index}', lambda skill_data=skill_data, index=index: _skill_data_value(skill_data, index)))
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
def _build_skill_tree_preheat_tasks() -> list[PreheatTask]:
|
||||||
|
tasks: list[PreheatTask] = []
|
||||||
|
data_dir = BACKEND_DIR / 'openapi' / 'data'
|
||||||
|
for path in sorted(data_dir.glob('*/' + '*/cn/skill_tree.json')):
|
||||||
|
relative = path.relative_to(data_dir)
|
||||||
|
job_id = relative.parts[0]
|
||||||
|
job_grow_id = relative.parts[1]
|
||||||
|
tasks.append(
|
||||||
|
(
|
||||||
|
f'openapi:{job_id}:{job_grow_id}:skills',
|
||||||
|
lambda path=path: json.loads(path.read_text(encoding='utf-8')),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
def _build_oath_preheat_tasks(equ_version: str = '0') -> list[PreheatTask]:
|
||||||
|
tasks: list[PreheatTask] = []
|
||||||
|
equ = get_equipment(equ_version)
|
||||||
|
for oath in equ.oath_suits:
|
||||||
|
for skill in oath.skills:
|
||||||
|
key = f'dcalc:oath:{oath.suitId}:{oath.rarity}:{oath.level}:{skill.skillId}'
|
||||||
|
tasks.append(
|
||||||
|
(
|
||||||
|
key,
|
||||||
|
lambda oath=oath, skill=skill: {
|
||||||
|
**skill.__dict__,
|
||||||
|
'point': oath.points.split(',')[oath.level - 1],
|
||||||
|
'imageUrl': oath.imageUrl,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
def _write_preheated_value(redis_conn: redis.Redis, key: str, value: object, expire: int = 0, without_gzip: bool = False):
|
def _write_preheated_value(redis_conn: redis.Redis, key: str, value: object, expire: int = 0, without_gzip: bool = False):
|
||||||
if value is None:
|
if value is None:
|
||||||
return
|
return
|
||||||
@@ -60,9 +188,14 @@ def _run_preheat_task(key: str, loader: PreheatLoader) -> tuple[str, object]:
|
|||||||
|
|
||||||
|
|
||||||
def delete_keys_with_prefix(redis_conn: redis.Redis, prefix: str):
|
def delete_keys_with_prefix(redis_conn: redis.Redis, prefix: str):
|
||||||
keys = redis_conn.keys(f'{prefix}*')
|
batch: list[bytes] = []
|
||||||
if keys:
|
for key in redis_conn.scan_iter(f'{prefix}*', count=500):
|
||||||
redis_conn.delete(*keys)
|
batch.append(key)
|
||||||
|
if len(batch) >= 500:
|
||||||
|
redis_conn.delete(*batch)
|
||||||
|
batch.clear()
|
||||||
|
if batch:
|
||||||
|
redis_conn.delete(*batch)
|
||||||
|
|
||||||
|
|
||||||
def _acquire_preheat_lock(redis_conn: redis.Redis) -> str | None:
|
def _acquire_preheat_lock(redis_conn: redis.Redis) -> str | None:
|
||||||
@@ -91,15 +224,14 @@ def preheat_redis_cache() -> list[str]:
|
|||||||
# if config.DEBUG_MODE:
|
# if config.DEBUG_MODE:
|
||||||
# print('Skip redis preheat in debug mode.')
|
# print('Skip redis preheat in debug mode.')
|
||||||
# return []
|
# return []
|
||||||
base_preheat_tasks: list[PreheatTask] = [
|
|
||||||
('dcalc:adventure', get_adv_list),
|
|
||||||
('openapi:skills', _load_skills),
|
|
||||||
]
|
|
||||||
character_preheat_tasks = _build_character_preheat_tasks()
|
|
||||||
warmed_keys: list[str] = []
|
warmed_keys: list[str] = []
|
||||||
|
|
||||||
with redis.Redis(connection_pool=RedisPool) as redis_conn:
|
with redis.Redis(connection_pool=RedisPool) as redis_conn:
|
||||||
lock_token = _acquire_preheat_lock(redis_conn)
|
try:
|
||||||
|
lock_token = _acquire_preheat_lock(redis_conn)
|
||||||
|
except redis.RedisError as exc:
|
||||||
|
print(f'Skip redis preheat: redis unavailable ({exc}).')
|
||||||
|
return []
|
||||||
if not lock_token:
|
if not lock_token:
|
||||||
print('Skip redis preheat: another instance is warming cache.')
|
print('Skip redis preheat: another instance is warming cache.')
|
||||||
return []
|
return []
|
||||||
@@ -108,13 +240,44 @@ def preheat_redis_cache() -> list[str]:
|
|||||||
delete_keys_with_prefix(redis_conn, 'dcalc:')
|
delete_keys_with_prefix(redis_conn, 'dcalc:')
|
||||||
delete_keys_with_prefix(redis_conn, 'openapi:')
|
delete_keys_with_prefix(redis_conn, 'openapi:')
|
||||||
|
|
||||||
|
custom_cn = _load_custom_cn()
|
||||||
|
base_preheat_tasks: list[PreheatTask] = [
|
||||||
|
('dcalc:adventure', get_adv_list),
|
||||||
|
('dcalc:custom_cn', lambda: custom_cn),
|
||||||
|
('openapi:skills', _load_skills),
|
||||||
|
]
|
||||||
|
shared_preheat_tasks = [
|
||||||
|
*_build_skill_tree_preheat_tasks(),
|
||||||
|
*_build_skill_data_preheat_tasks(custom_cn),
|
||||||
|
*_build_oath_preheat_tasks(),
|
||||||
|
]
|
||||||
|
character_preheat_tasks = _build_character_preheat_tasks()
|
||||||
|
|
||||||
for key, loader in base_preheat_tasks:
|
for key, loader in base_preheat_tasks:
|
||||||
try:
|
try:
|
||||||
_write_preheated_value(redis_conn, key, loader())
|
_write_preheated_value(redis_conn, key, loader(), without_gzip=key == 'dcalc:custom_cn')
|
||||||
warmed_keys.append(key)
|
warmed_keys.append(key)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f'Redis preheat failed for {key}: {exc}')
|
print(f'Redis preheat failed for {key}: {exc}')
|
||||||
|
|
||||||
|
max_workers = min(8, len(shared_preheat_tasks))
|
||||||
|
if max_workers > 0:
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
|
future_map = {executor.submit(_run_preheat_task, key, loader): key for key, loader in shared_preheat_tasks}
|
||||||
|
for future in as_completed(future_map):
|
||||||
|
key = future_map[future]
|
||||||
|
try:
|
||||||
|
_, value = future.result()
|
||||||
|
_write_preheated_value(
|
||||||
|
redis_conn,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
without_gzip=key.startswith('dcalc:skill:'),
|
||||||
|
)
|
||||||
|
warmed_keys.append(key)
|
||||||
|
except Exception as exc:
|
||||||
|
print(f'Redis preheat failed for {key}: {exc}')
|
||||||
|
|
||||||
max_workers = min(8, len(character_preheat_tasks))
|
max_workers = min(8, len(character_preheat_tasks))
|
||||||
if max_workers > 0:
|
if max_workers > 0:
|
||||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
|
|||||||
@@ -9,6 +9,25 @@ from ..roleinfo import CharacterEmblemInfo, CharacterEquipInfo, get_key_by_value
|
|||||||
from .jade import Jade
|
from .jade import Jade
|
||||||
|
|
||||||
|
|
||||||
|
ELEMENT_STATUS_KEYS = {
|
||||||
|
'ElementA',
|
||||||
|
'ElementDB',
|
||||||
|
'ElementR',
|
||||||
|
'火强',
|
||||||
|
'冰强',
|
||||||
|
'光强',
|
||||||
|
'暗强',
|
||||||
|
'火抗',
|
||||||
|
'冰抗',
|
||||||
|
'光抗',
|
||||||
|
'暗抗',
|
||||||
|
'火属性攻击',
|
||||||
|
'冰属性攻击',
|
||||||
|
'光属性攻击',
|
||||||
|
'暗属性攻击',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CharacterBase(CharacterProperty):
|
class CharacterBase(CharacterProperty):
|
||||||
# region 额外类属性(CharacterProperty 中未声明的)
|
# region 额外类属性(CharacterProperty 中未声明的)
|
||||||
|
|
||||||
@@ -19,6 +38,7 @@ class CharacterBase(CharacterProperty):
|
|||||||
skills_id_dict: dict[str, 'Skill']
|
skills_id_dict: dict[str, 'Skill']
|
||||||
skills_uuid_dict: dict[str, 'Skill']
|
skills_uuid_dict: dict[str, 'Skill']
|
||||||
skill_names_cache: dict[tuple[str, bool | str], list[str]]
|
skill_names_cache: dict[tuple[str, bool | str], list[str]]
|
||||||
|
skills_cache: dict[tuple[str, bool | str], list['Skill']]
|
||||||
equVersion: str = '0'
|
equVersion: str = '0'
|
||||||
"""装备版本"""
|
"""装备版本"""
|
||||||
charEquipInfo: dict[str, 'CharacterEquipInfo']
|
charEquipInfo: dict[str, 'CharacterEquipInfo']
|
||||||
@@ -153,6 +173,7 @@ class CharacterBase(CharacterProperty):
|
|||||||
self.skills_id_dict = {}
|
self.skills_id_dict = {}
|
||||||
self.skills_uuid_dict = {}
|
self.skills_uuid_dict = {}
|
||||||
self.skill_names_cache = {}
|
self.skill_names_cache = {}
|
||||||
|
self.skills_cache = {}
|
||||||
self.load_skills()
|
self.load_skills()
|
||||||
|
|
||||||
# region 角色属性设置
|
# region 角色属性设置
|
||||||
@@ -249,33 +270,38 @@ class CharacterBase(CharacterProperty):
|
|||||||
# frame = sys._getframe(1) # 1 表示上一级调用帧
|
# frame = sys._getframe(1) # 1 表示上一级调用帧
|
||||||
# caller_name = frame.f_code.co_name
|
# caller_name = frame.f_code.co_name
|
||||||
# print(caller_name, kwargs.get('SkillAttack', 0))
|
# print(caller_name, kwargs.get('SkillAttack', 0))
|
||||||
self.STR += 力量 + kwargs.get('STR', 0) + 四维 + 力智
|
kget = kwargs.get
|
||||||
self.INT += 智力 + kwargs.get('INT', 0) + 四维 + 力智
|
self.STR += 力量 + kget('STR', 0) + 四维 + 力智
|
||||||
self.Spirit += 精神 + kwargs.get('Spirit', 0) + 四维 + 体精
|
self.INT += 智力 + kget('INT', 0) + 四维 + 力智
|
||||||
self.Vitality += 体力 + kwargs.get('Vitality', 0) + 四维 + 体精
|
self.Spirit += 精神 + kget('Spirit', 0) + 四维 + 体精
|
||||||
self.AtkP += 物攻 + kwargs.get('AtkP', 0) + 三攻
|
self.Vitality += 体力 + kget('Vitality', 0) + 四维 + 体精
|
||||||
self.AtkM += 魔攻 + kwargs.get('AtkM', 0) + 三攻
|
self.AtkP += 物攻 + kget('AtkP', 0) + 三攻
|
||||||
self.AtkI += 独立 + kwargs.get('AtkI', 0) + 三攻
|
self.AtkM += 魔攻 + kget('AtkM', 0) + 三攻
|
||||||
self.PAtkP *= kwargs.get('PAtkP', 1.0)
|
self.AtkI += 独立 + kget('AtkI', 0) + 三攻
|
||||||
self.PAtkM *= kwargs.get('PAtkM', 1.0)
|
self.PAtkP *= kget('PAtkP', 1.0)
|
||||||
self.PAtkI *= kwargs.get('PAtkI', 1.0)
|
self.PAtkM *= kget('PAtkM', 1.0)
|
||||||
for elem in ['火', '冰', '光', '暗']:
|
self.PAtkI *= kget('PAtkI', 1.0)
|
||||||
self.ElementA[elem] = kwargs.get(f'{elem}属性攻击', False) or kwargs.get('ElementA', {}).get(elem, False) or self.ElementA[elem]
|
if 全属强 or 全属抗 or ELEMENT_STATUS_KEYS.intersection(kwargs):
|
||||||
self.ElementDB[elem] += kwargs.get(f'{elem}强', 0) + kwargs.get('ElementDB', {}).get(elem, 0) + 全属强
|
element_a = kget('ElementA', {})
|
||||||
self.ElementR[elem] += kwargs.get(f'{elem}抗', 0) + kwargs.get('ElementR', {}).get(elem, 0) + 全属抗
|
element_db = kget('ElementDB', {})
|
||||||
self.CriticalM += kwargs.get('CriticalM', 0) + 魔暴
|
element_r = kget('ElementR', {})
|
||||||
self.CriticalP += kwargs.get('CriticalP', 0) + 物暴
|
for elem in ['火', '冰', '光', '暗']:
|
||||||
self.SkillAttack *= (1 + 技攻) * (1 + kwargs.get('SkillAttack', 0))
|
self.ElementA[elem] = kget(f'{elem}属性攻击', False) or element_a.get(elem, False) or self.ElementA[elem]
|
||||||
self.SpeedA += 攻击速度 + kwargs.get('SpeedA', 0) + 三速
|
self.ElementDB[elem] += kget(f'{elem}强', 0) + element_db.get(elem, 0) + 全属强
|
||||||
self.SpeedM += 移动速度 + kwargs.get('SpeedM', 0) + 三速
|
self.ElementR[elem] += kget(f'{elem}抗', 0) + element_r.get(elem, 0) + 全属抗
|
||||||
self.SpeedR += 施放速度 + kwargs.get('SpeedR', 0) + 三速
|
self.CriticalM += kget('CriticalM', 0) + 魔暴
|
||||||
self.HitP += 命中率 + kwargs.get('HitP', 0)
|
self.CriticalP += kget('CriticalP', 0) + 物暴
|
||||||
self.Hit += 命中 + kwargs.get('Hit', 0)
|
self.SkillAttack *= (1 + 技攻) * (1 + kget('SkillAttack', 0))
|
||||||
self.Attack += 攻击强化 + kwargs.get('Attack', 0)
|
self.SpeedA += 攻击速度 + kget('SpeedA', 0) + 三速
|
||||||
self.AttackP += 攻击强化P + kwargs.get('AttackP', 0.0)
|
self.SpeedM += 移动速度 + kget('SpeedM', 0) + 三速
|
||||||
self.Buffer += 增益量 + kwargs.get('Buffer', 0)
|
self.SpeedR += 施放速度 + kget('SpeedR', 0) + 三速
|
||||||
self.BufferP += 增益量P + kwargs.get('BufferP', 0)
|
self.HitP += 命中率 + kget('HitP', 0)
|
||||||
self.EquEffectRatio += kwargs.get('EquEffectRatio', 0.0)
|
self.Hit += 命中 + kget('Hit', 0)
|
||||||
|
self.Attack += 攻击强化 + kget('Attack', 0)
|
||||||
|
self.AttackP += 攻击强化P + kget('AttackP', 0.0)
|
||||||
|
self.Buffer += 增益量 + kget('Buffer', 0)
|
||||||
|
self.BufferP += 增益量P + kget('BufferP', 0)
|
||||||
|
self.EquEffectRatio += kget('EquEffectRatio', 0.0)
|
||||||
|
|
||||||
def AddElementDB(self, element: str, value: float, type: int = 0) -> None:
|
def AddElementDB(self, element: str, value: float, type: int = 0) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -343,10 +369,14 @@ class CharacterBase(CharacterProperty):
|
|||||||
|
|
||||||
def GetSkillNames(self, type: Literal['active', 'passive', 'all'] = 'all', damage: Literal[True, False, 'all'] = 'all') -> list[str]:
|
def GetSkillNames(self, type: Literal['active', 'passive', 'all'] = 'all', damage: Literal[True, False, 'all'] = 'all') -> list[str]:
|
||||||
"""获取技能名字"""
|
"""获取技能名字"""
|
||||||
|
return [skill.name for skill in self.GetSkills(type, damage)]
|
||||||
|
|
||||||
|
def GetSkills(self, type: Literal['active', 'passive', 'all'] = 'all', damage: Literal[True, False, 'all'] = 'all') -> list['Skill']:
|
||||||
|
"""获取技能对象"""
|
||||||
key = (type, damage)
|
key = (type, damage)
|
||||||
if key not in self.skill_names_cache:
|
if key not in self.skills_cache:
|
||||||
self.skill_names_cache[key] = [skill.name for skill in self.skills if (type == 'all' or skill.type == type) and (damage == 'all' or skill.damage == damage)]
|
self.skills_cache[key] = [skill for skill in self.skills if (type == 'all' or skill.type == type) and (damage == 'all' or skill.damage == damage)]
|
||||||
return self.skill_names_cache[key]
|
return self.skills_cache[key]
|
||||||
|
|
||||||
def GetWeaponType(self) -> tuple[str, str]:
|
def GetWeaponType(self) -> tuple[str, str]:
|
||||||
"""获取武器类型"""
|
"""获取武器类型"""
|
||||||
|
|||||||
+25
-15
@@ -19,6 +19,18 @@ def _identity(value: Any) -> Any:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def _load_custom_cn() -> list[dict[str, Any]]:
|
||||||
|
with (OPENAPI_DIR / 'custom_cn.json').open(encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def _load_skill_detail(prefix: str) -> dict[str, Any]:
|
||||||
|
with (OPENAPI_DIR / 'data' / f'{prefix}.json').open(encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def _get_skill_data_suffixes(skill_class: type) -> tuple[str, ...]:
|
def _get_skill_data_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||||
return tuple(key.replace('data', '') for key in dir(skill_class) if key.startswith('data') and not key.startswith('plus'))
|
return tuple(key.replace('data', '') for key in dir(skill_class) if key.startswith('data') and not key.startswith('plus'))
|
||||||
@@ -41,15 +53,9 @@ def get_data(prefix: str, key: int | str, func: Callable[[Any], Any] = _identity
|
|||||||
jobGrow = prefix_parts[1]
|
jobGrow = prefix_parts[1]
|
||||||
skill = prefix_parts[-1]
|
skill = prefix_parts[-1]
|
||||||
|
|
||||||
def get_custom_cn():
|
|
||||||
with (OPENAPI_DIR / 'custom_cn.json').open(encoding='utf-8') as f:
|
|
||||||
custom_cn = json.load(f)
|
|
||||||
return custom_cn
|
|
||||||
|
|
||||||
def get_skill_info():
|
def get_skill_info():
|
||||||
with (OPENAPI_DIR / 'data' / f'{prefix}.json').open(encoding='utf-8') as f:
|
skill_data = _load_skill_detail(prefix)
|
||||||
skill_data = json.load(f)
|
custom_cn = get_redis_info(redis, 'dcalc:custom_cn', _load_custom_cn, without_gzip=True)
|
||||||
custom_cn = get_redis_info(redis, 'dcalc:custom_cn', get_custom_cn, without_gzip=True)
|
|
||||||
try:
|
try:
|
||||||
if isinstance(key, int):
|
if isinstance(key, int):
|
||||||
data = [0] + [row["optionValue"].get("value" + str(key+1), 0) for row in skill_data["levelInfo"]["rows"]]
|
data = [0] + [row["optionValue"].get("value" + str(key+1), 0) for row in skill_data["levelInfo"]["rows"]]
|
||||||
@@ -235,15 +241,19 @@ class Skill:
|
|||||||
value = self.get_data_value(data, new) / ratio - self.get_data_value(data, old) / ratio
|
value = self.get_data_value(data, new) / ratio - self.get_data_value(data, old) / ratio
|
||||||
self.char.SetStatus(**{type[2:]: value})
|
self.char.SetStatus(**{type[2:]: value})
|
||||||
else:
|
else:
|
||||||
if skills == '*':
|
|
||||||
skills = self.char.GetSkillNames('all', True)
|
|
||||||
exceptSkills = set(exceptSkills)
|
exceptSkills = set(exceptSkills)
|
||||||
for name in skills:
|
if skills == '*':
|
||||||
if name in exceptSkills:
|
for skill in self.char.GetSkills('all', True):
|
||||||
continue
|
if skill.name in exceptSkills:
|
||||||
skill = self.char.GetSkillByName(name)
|
continue
|
||||||
if skill is not None:
|
|
||||||
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon, weapon_type, weapon_options)
|
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon, weapon_type, weapon_options)
|
||||||
|
else:
|
||||||
|
for name in skills:
|
||||||
|
if name in exceptSkills:
|
||||||
|
continue
|
||||||
|
skill = self.char.GetSkillByName(name)
|
||||||
|
if skill is not None:
|
||||||
|
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon, weapon_type, weapon_options)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# print(f"Error applying association {type} for skill {self.name}: {e}")
|
# print(f"Error applying association {type} for skill {self.name}: {e}")
|
||||||
raise e
|
raise e
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import inspect
|
import sys
|
||||||
|
|
||||||
from core.abstract.character import CharacterProperty
|
from core.abstract.character import CharacterProperty
|
||||||
from core.basic.equipment import EquEffect
|
from core.basic.equipment import EquEffect
|
||||||
@@ -6,8 +6,7 @@ from .register import register
|
|||||||
|
|
||||||
|
|
||||||
def check_suit(char: CharacterProperty) -> bool:
|
def check_suit(char: CharacterProperty) -> bool:
|
||||||
caller_frame = inspect.stack()[1]
|
fun_name = sys._getframe(1).f_code.co_name
|
||||||
fun_name = caller_frame.function
|
|
||||||
suitId = int(int(fun_name.split('_')[-1]) / 10000) + 16000
|
suitId = int(int(fun_name.split('_')[-1]) / 10000) + 16000
|
||||||
result = next((x for x in char.suitInfo if str(x.suitId) == str(suitId)), None) is not None
|
result = next((x for x in char.suitInfo if str(x.suitId) == str(suitId)), None) is not None
|
||||||
return result
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user