1
This commit was merged in pull request #1.
This commit is contained in:
+82
-43
@@ -1,50 +1,77 @@
|
||||
from copy import deepcopy
|
||||
from copy import copy
|
||||
from collections.abc import Callable
|
||||
from functools import cache
|
||||
import json
|
||||
import re
|
||||
from typing import Literal, TYPE_CHECKING
|
||||
from typing import Any, Literal, TYPE_CHECKING
|
||||
from .formula import 武器冷却惩罚
|
||||
from database.connect import get_redis
|
||||
from database.connect import BACKEND_DIR, get_redis_client
|
||||
from api.core.Redis import get_redis_info
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.basic.character import Character
|
||||
|
||||
characterLv = 115 + 5
|
||||
OPENAPI_DIR = BACKEND_DIR / 'openapi'
|
||||
|
||||
|
||||
def _identity(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
|
||||
@cache
|
||||
def get_data(prefix: str, key: int | str, func = lambda x: x):
|
||||
redis = next(get_redis())
|
||||
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'))
|
||||
|
||||
|
||||
@cache
|
||||
def _get_skill_damage_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('dataplus'))
|
||||
|
||||
|
||||
@cache
|
||||
def _get_skill_dataplus_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||
return tuple(key.replace('dataplus', '') for key in dir(skill_class) if key.startswith('dataplus'))
|
||||
|
||||
|
||||
@cache
|
||||
def get_data(prefix: str, key: int | str, func: Callable[[Any], Any] = _identity):
|
||||
prefix_parts = prefix.split('/')
|
||||
job = prefix_parts[0]
|
||||
jobGrow = prefix_parts[1]
|
||||
skill = prefix_parts[-1]
|
||||
|
||||
def get_custom_cn():
|
||||
with open('./openapi/custom_cn.json', encoding='utf-8') as f:
|
||||
with (OPENAPI_DIR / 'custom_cn.json').open(encoding='utf-8') as f:
|
||||
custom_cn = json.load(f)
|
||||
return custom_cn
|
||||
|
||||
def get_skill_info():
|
||||
# This function should retrieve the skill info based on job and jobGrow
|
||||
# For now, we return a placeholder dictionary
|
||||
with open(f'./openapi/data/{prefix}.json', encoding='utf-8') as f:
|
||||
with (OPENAPI_DIR / 'data' / f'{prefix}.json').open(encoding='utf-8') as f:
|
||||
skill_data = json.load(f)
|
||||
custom_cn = get_redis_info(redis, 'dcalc:custom_cn', get_custom_cn, without_gzip=True)
|
||||
job = prefix.split('/')[0]
|
||||
jobGrow = prefix.split('/')[1]
|
||||
skill = prefix.split('/')[-1]
|
||||
try:
|
||||
if isinstance(key, int):
|
||||
data = [0] + list(map(lambda x: x["optionValue"].get("value" + str(key+1), 0), skill_data["levelInfo"]["rows"]))
|
||||
data = [0] + [row["optionValue"].get("value" + str(key+1), 0) for row in skill_data["levelInfo"]["rows"]]
|
||||
data = [0 if x is None or x == "-" else x for x in data]
|
||||
elif key == "vps":
|
||||
data = [ {"name":x["name"],"desc":x["desc"]} for x in skill_data.get("evolution",[])]
|
||||
elif key == "custom":
|
||||
data = next((item.get('custom', 1.0) for item in custom_cn
|
||||
data = next((item.get('custom', 1.0) for item in custom_cn or []
|
||||
if item['job'] == job and item['jobGrow'] == jobGrow and item['skillId'] == skill), 1.0)
|
||||
except Exception as e:
|
||||
# print(f"get skill {prefix}/{key} data error",e)
|
||||
except Exception:
|
||||
# print(f"get skill {prefix}/{key} data error")
|
||||
data = None
|
||||
if isinstance(key, int) and not data:
|
||||
return [0]
|
||||
if key == 'vps' and data is None:
|
||||
return []
|
||||
if key == 'custom' and data is None:
|
||||
return 1.0
|
||||
return data
|
||||
|
||||
data = get_redis_info(redis, f'dcalc:skill:{prefix}:{key}', get_skill_info, without_gzip=True)
|
||||
with get_redis_client() as redis:
|
||||
data = get_redis_info(redis, f'dcalc:skill:{prefix}:{key}', get_skill_info, without_gzip=True)
|
||||
return func(data)
|
||||
|
||||
|
||||
@@ -174,65 +201,76 @@ class Skill:
|
||||
self._lv = value
|
||||
|
||||
def effect(self, old: int, new: int):
|
||||
weapon_type = self.char.GetWeaponType()[0]
|
||||
weapon_options = self.char.武器选项
|
||||
for assoc in self.associate:
|
||||
type = assoc.get('type', '*skillDamage')
|
||||
skills = assoc.get('skills', '*')
|
||||
data = assoc.get('data', [0] * self.maxLv)
|
||||
ratio = assoc.get('ratio', 100)
|
||||
weapon = assoc.get('weapon', self.char.武器选项)
|
||||
weapon = assoc.get('weapon', weapon_options)
|
||||
exceptSkills = assoc.get('exceptSkills', [])
|
||||
if type[0] not in ['*', '+', '=', '$']:
|
||||
if self.precondition() and (self.char.GetWeaponType()[0] in weapon or len(weapon) == len(self.char.武器选项)):
|
||||
if self.precondition() and (weapon_type in weapon or len(weapon) == len(weapon_options)):
|
||||
getattr(self, type)(old, new, data, skills, exceptSkills, ratio, weapon)
|
||||
continue
|
||||
if self.precondition():
|
||||
self.apply_association(type, old, new, data, skills, exceptSkills, ratio, weapon)
|
||||
self.apply_association(type, old, new, data, skills, exceptSkills, ratio, weapon, weapon_type, weapon_options)
|
||||
|
||||
def precondition(self):
|
||||
"""effect的前置条件"""
|
||||
return True
|
||||
|
||||
def apply_association(self, type, old, new, data, skills, exceptSkills, ratio=100, weapon=[]):
|
||||
def apply_association(self, type, old, new, data, skills, exceptSkills, ratio=100, weapon=(), weapon_type=None, weapon_options=()):
|
||||
try:
|
||||
if (self.char.GetWeaponType()[0] not in weapon) and not (self.char.GetWeaponType()[0] is None and len(weapon) == len(self.char.武器选项)):
|
||||
if (weapon_type not in weapon) and not (weapon_type is None and len(weapon) == len(weapon_options)):
|
||||
return
|
||||
if type.startswith('$*'):
|
||||
if 'cdReduce' in type:
|
||||
value = (1 - data[new] / ratio) / (1 - data[old] / ratio)
|
||||
value = (1 - self.get_data_value(data, new) / ratio) / (1 - self.get_data_value(data, old) / ratio)
|
||||
else:
|
||||
value = (1 + data[new] / ratio) / (1 + data[old] / ratio)
|
||||
eval(f'self.char.SetStatus({type[2:]}={value})')
|
||||
value = (1 + self.get_data_value(data, new) / ratio) / (1 + self.get_data_value(data, old) / ratio)
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
elif type.startswith('$+'):
|
||||
value = data[new] / ratio - data[old] / ratio
|
||||
eval(f'self.char.SetStatus({type[2:]}={value})')
|
||||
value = self.get_data_value(data, new) / ratio - self.get_data_value(data, old) / ratio
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
else:
|
||||
if skills == '*':
|
||||
skills = self.char.GetSkillNames('all', True)
|
||||
exceptSkills = set(exceptSkills)
|
||||
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)
|
||||
self.update_skill_attribute(skill, type, old, new, data, ratio, weapon, weapon_type, weapon_options)
|
||||
except Exception as e:
|
||||
# print(f"Error applying association {type} for skill {self.name}: {e}")
|
||||
raise e
|
||||
|
||||
def update_skill_attribute(self, skill, type, old, new, data, ratio=100, weapon=[]):
|
||||
if (self.char.GetWeaponType()[0] not in weapon) and not (self.char.GetWeaponType()[0] is None and len(weapon) == len(self.char.武器选项)):
|
||||
def update_skill_attribute(self, skill, type, old, new, data, ratio=100, weapon=(), weapon_type=None, weapon_options=()):
|
||||
if (weapon_type not in weapon) and not (weapon_type is None and len(weapon) == len(weapon_options)):
|
||||
return
|
||||
old_value = self.get_data_value(data, old)
|
||||
new_value = self.get_data_value(data, new)
|
||||
if type.startswith('*'):
|
||||
if 'cdReduce' in type or type == 'cd' or 'cdRatio' in type:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 - data[new] / ratio) / (1 - data[old] / ratio))
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 - new_value / ratio) / (1 - old_value / ratio))
|
||||
else:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 + data[new] / ratio) / (1 + data[old] / ratio))
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) * (1 + new_value / ratio) / (1 + old_value / ratio))
|
||||
elif type.startswith('+'):
|
||||
if type[1:] == 'lv' and self.char.setInfo.get('skills', {}).get(str(skill.id),{}).get('lv',0) == 0:
|
||||
return
|
||||
else:
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) + data[new] / ratio - data[old] / ratio)
|
||||
setattr(skill, type[1:], getattr(skill, type[1:]) + new_value / ratio - old_value / ratio)
|
||||
elif type.startswith('='):
|
||||
setattr(skill, type[1:], data[new])
|
||||
setattr(skill, type[1:], new_value)
|
||||
|
||||
@staticmethod
|
||||
def get_data_value(data, index: int):
|
||||
if 0 <= index < len(data):
|
||||
return data[index]
|
||||
return 0
|
||||
|
||||
def skillInfo(self, mode: str | None = None):
|
||||
pass
|
||||
@@ -252,8 +290,7 @@ class ActiveSkill(Skill):
|
||||
|
||||
def __init__(self, char):
|
||||
super().__init__(char)
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('plus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_data_suffixes(self.__class__):
|
||||
power = getattr(self, f'power{i}', None)
|
||||
if power is None:
|
||||
setattr(self, f'power{i}', 1)
|
||||
@@ -262,7 +299,12 @@ class ActiveSkill(Skill):
|
||||
setattr(self, f'hit{i}', 0)
|
||||
|
||||
def skillInfo(self, mode: str | None = None):
|
||||
basic = deepcopy(self)
|
||||
basic = copy(self)
|
||||
for key, value in self.__dict__.items():
|
||||
if isinstance(value, list):
|
||||
setattr(basic, key, value.copy())
|
||||
elif isinstance(value, dict):
|
||||
setattr(basic, key, value.copy())
|
||||
basic.setUP()
|
||||
if mode is not None:
|
||||
basic.currentMode = mode
|
||||
@@ -298,8 +340,7 @@ class ActiveSkill(Skill):
|
||||
|
||||
def getSkillData(self, lv: int = 0):
|
||||
res = 0
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('dataplus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_damage_suffixes(self.__class__):
|
||||
data = getattr(self, f'data{i}', [])
|
||||
plus = getattr(self, f'plus{i}', 0)
|
||||
hit = getattr(self, f'hit{i}', 0)
|
||||
@@ -309,8 +350,7 @@ class ActiveSkill(Skill):
|
||||
hit = getattr(self, f'hit{i}', 0)
|
||||
power = getattr(self, f'power{i}', 1)
|
||||
res += hit * power * (data[lv] + plus)
|
||||
keys = [key.replace('dataplus', '') for key in dir(self) if key.startswith('dataplus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_dataplus_suffixes(self.__class__):
|
||||
data = getattr(self, f'dataplus{i}', 0)
|
||||
hit = getattr(self, f'hitplus{i}', 1)
|
||||
power = getattr(self, f'powerplus{i}', 1)
|
||||
@@ -397,8 +437,7 @@ class BuffSkill(Skill):
|
||||
|
||||
def __init__(self, char):
|
||||
super().__init__(char)
|
||||
keys = [key.replace('data', '') for key in dir(self) if key.startswith('data') and not key.startswith('plus')]
|
||||
for i in keys:
|
||||
for i in _get_skill_data_suffixes(self.__class__):
|
||||
power = getattr(self, f'power{i}', None)
|
||||
if power is None:
|
||||
setattr(self, f'power{i}', 1)
|
||||
|
||||
Reference in New Issue
Block a user