update
This commit is contained in:
+18
-169
@@ -8,9 +8,9 @@ from core.character.refs.base import SkillRef, resolve_skill
|
||||
from .formula import 武器冷却惩罚
|
||||
from database.connect import get_redis_client
|
||||
from api.core.Redis import get_redis_info
|
||||
from api.utils.openapi_data import OPENAPI_CUSTOM_CN_PATH, logical_skill_detail_path
|
||||
from .skill_effects import CHARACTER_ATTRIBUTES, INPUT_ATTRIBUTES
|
||||
from .skill_effect_types import CharacterEffect, EffectDeclaration, SkillEffect
|
||||
from api.utils.openapi_data import logical_skill_detail_path
|
||||
from .skill_effects import INPUT_ATTRIBUTES
|
||||
from .skill_effect_types import EffectDeclaration
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.basic.character import Character
|
||||
@@ -22,12 +22,6 @@ def _identity(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
|
||||
@cache
|
||||
def _load_custom_cn() -> list[dict[str, Any]]:
|
||||
with OPENAPI_CUSTOM_CN_PATH.open(encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
@cache
|
||||
def _load_skill_detail(prefix: str) -> dict[str, Any]:
|
||||
with logical_skill_detail_path(prefix).open(encoding='utf-8') as f:
|
||||
@@ -51,23 +45,14 @@ def _get_skill_dataplus_suffixes(skill_class: type) -> tuple[str, ...]:
|
||||
|
||||
@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_skill_info():
|
||||
skill_data = _load_skill_detail(prefix)
|
||||
custom_cn = get_redis_info(redis, 'dcalc:custom_cn', _load_custom_cn, without_gzip=True)
|
||||
try:
|
||||
if isinstance(key, int):
|
||||
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 or []
|
||||
if item['job'] == job and item['jobGrow'] == jobGrow and item['skillId'] == skill), 1.0)
|
||||
elif key == "cdList":
|
||||
data = [0] + [row.get("coolTime", 0) for row in skill_data["levelInfo"]["rows"]]
|
||||
except Exception:
|
||||
@@ -77,8 +62,6 @@ def get_data(prefix: str, key: int | str, func: Callable[[Any], Any] = _identity
|
||||
return [0]
|
||||
if key == 'vps' and data is None:
|
||||
return []
|
||||
if key == 'custom' and data is None:
|
||||
return 1.0
|
||||
return data
|
||||
|
||||
with get_redis_client() as redis:
|
||||
@@ -153,18 +136,12 @@ class Skill:
|
||||
"""技能冷却倍率,直接体现在面板上,不被上限影响"""
|
||||
cdRecover: float = 1
|
||||
"""技能冷却恢复"""
|
||||
associate: list[dict[str, Any] | EffectDeclaration] = []
|
||||
"""关联声明;维护示例和扩展边界见 core/basic/skill_effects.py 文件顶部说明。
|
||||
associate: list[EffectDeclaration] = []
|
||||
"""类型化关联声明;仅接受 SkillEffect/CharacterEffect。
|
||||
|
||||
新规则使用 SkillEffect/CharacterEffect 和 Ref 目标数组,兼容旧字典及名称字符串。
|
||||
新声明 value 是实际倍率/增量/覆盖值,以下 type、data、ratio 约定仅适用于旧字典。
|
||||
type 默认 '*skillDamage':*乘算、+加算、=覆盖;$*PAtkP 等作用于角色。
|
||||
skills 默认 '*',表示所有伤害技能;显式列表使用技能名称,可指定被动技能。
|
||||
exceptSkills 默认 [];weapon 默认职业武器选项;precondition() 控制前置条件。
|
||||
data 按来源等级取值;ratio 是数据除数,默认 100,与技能的 ratio 属性不同。
|
||||
+lv 的整数加级通常设置 ratio=1;=覆盖直接使用 data 值,不除以 ratio。
|
||||
动态 effect 以 (0, 最终等级) 多次收集,只构造声明并调用 super().effect。
|
||||
自定义 type 方法仍有兼容入口,但直接修改目标会绕过贡献收集,新增规则勿照搬。
|
||||
value 是实际倍率、增量或覆盖值,目标优先使用 Ref 数组。
|
||||
动态 effect 只构造声明并调用 super().effect,由引擎统一收集和应用。
|
||||
维护示例见 core/basic/skill_effects.py 文件顶部说明。
|
||||
"""
|
||||
mode: list[str] = ['']
|
||||
currentMode: str = ''
|
||||
@@ -179,8 +156,6 @@ class Skill:
|
||||
"""是否有技能强化"""
|
||||
upType: Literal['damage', 'buff', 'heal'] = 'damage'
|
||||
vps: list = []
|
||||
custom: float = 1.0
|
||||
"""国服特色加强"""
|
||||
|
||||
def __init__(self, char):
|
||||
self.char = char
|
||||
@@ -249,118 +224,17 @@ 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:
|
||||
if isinstance(assoc, (SkillEffect, CharacterEffect)):
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
if engine is None:
|
||||
raise RuntimeError('类型化效果需要 SkillEffectEngine')
|
||||
engine.collect_effect(self, assoc)
|
||||
continue
|
||||
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', weapon_options)
|
||||
exceptSkills = assoc.get('exceptSkills', [])
|
||||
if type[0] not in ['*', '+', '=', '$']:
|
||||
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, weapon_type, weapon_options)
|
||||
"""收集类型化声明;动态目标规则可重写此入口。"""
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
for declaration in self.associate:
|
||||
if engine is None:
|
||||
raise RuntimeError('类型化效果需要 SkillEffectEngine')
|
||||
engine.collect_effect(self, declaration)
|
||||
|
||||
def precondition(self):
|
||||
"""effect的前置条件"""
|
||||
"""效果的前置条件。"""
|
||||
return True
|
||||
|
||||
def apply_association(self, type, old, new, data, skills, exceptSkills, ratio=100, weapon=(), weapon_type=None, weapon_options=()):
|
||||
try:
|
||||
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 - self.get_data_value(data, new) / ratio) / (1 - self.get_data_value(data, old) / ratio)
|
||||
else:
|
||||
value = (1 + self.get_data_value(data, new) / ratio) / (1 + self.get_data_value(data, old) / ratio)
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
if engine is not None and engine.collecting is self:
|
||||
if type[2:] in CHARACTER_ATTRIBUTES:
|
||||
engine.record(self, self.char, type[2:], '*', value)
|
||||
else:
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
elif type.startswith('$+'):
|
||||
value = self.get_data_value(data, new) / ratio - self.get_data_value(data, old) / ratio
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
if engine is not None and engine.collecting is self:
|
||||
if type[2:] in CHARACTER_ATTRIBUTES:
|
||||
engine.record(self, self.char, type[2:], '+', value)
|
||||
else:
|
||||
self.char.SetStatus(**{type[2:]: value})
|
||||
else:
|
||||
exceptSkills = set(exceptSkills)
|
||||
if skills == '*':
|
||||
for skill in self.char.GetSkills('all', True):
|
||||
if skill.name in exceptSkills:
|
||||
continue
|
||||
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)
|
||||
else:
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
if engine is not None and engine.collecting is self:
|
||||
engine.missing_target(self, name)
|
||||
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=(), 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)
|
||||
engine = getattr(self.char, 'skill_effects', None)
|
||||
if engine is not None and engine.collecting is self:
|
||||
if type[1:] == 'lv' and skill.level_source is not None:
|
||||
return
|
||||
if type.startswith('*'):
|
||||
if 'cdReduce' in type or 'cdRatio' in type:
|
||||
value = (1 - new_value / ratio) / (1 - old_value / ratio)
|
||||
else:
|
||||
value = (1 + new_value / ratio) / (1 + old_value / ratio)
|
||||
elif type.startswith('+'):
|
||||
if type[1:] == 'lv' and not engine.is_learned(skill):
|
||||
return
|
||||
value = (new_value - old_value) / ratio
|
||||
else:
|
||||
value = new_value
|
||||
engine.record(self, skill, type[1:], type[0], value)
|
||||
return
|
||||
if type.startswith('*'):
|
||||
if 'cdReduce' in type or type == 'cd' or 'cdRatio' in type:
|
||||
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 + 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:]) + new_value / ratio - old_value / ratio)
|
||||
elif type.startswith('='):
|
||||
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
|
||||
|
||||
@@ -401,7 +275,7 @@ class ActiveSkill(Skill):
|
||||
basic.setVP()
|
||||
date = basic.getSkillData(self.lv)
|
||||
cd = basic.getSkillCD(mode)
|
||||
return date * basic.skillRation * basic.custom, basic.skillDamage, cd
|
||||
return date * basic.skillRation, basic.skillDamage, cd
|
||||
|
||||
def setMode(self, mode: str):
|
||||
pass
|
||||
@@ -453,23 +327,11 @@ class ActiveSkill(Skill):
|
||||
else:
|
||||
return 武器冷却惩罚(weapon.itemDetailType, self.char.输出类型)
|
||||
|
||||
def getQuickCDRatio(self):
|
||||
return 1.0
|
||||
cdr = 0
|
||||
if 15 <= self.learnLv <= 30:
|
||||
cdr = 0.01
|
||||
elif 35 <= self.learnLv <= 70:
|
||||
cdr = 0.02
|
||||
elif 75 <= self.learnLv <= 100:
|
||||
cdr = 0.05
|
||||
if self.learnLv in [50, 85, 100]:
|
||||
cdr = 0.05
|
||||
return 1 - cdr
|
||||
|
||||
def getSkillCD(self, mode=None):
|
||||
if not isinstance(self.cd, (int, float)) or not self.cd:
|
||||
return '-'
|
||||
return max(0, round(max(self.cd * 0.3, self.cd * self.cdReduce / self.cdRecover * self.getWeaponCDRatio() * self.getQuickCDRatio()) * self.cdRatio - self.cdCut / self.cdRecover , 2))
|
||||
return max(0, round(max(self.cd * 0.3, self.cd * self.cdReduce / self.cdRecover * self.getWeaponCDRatio()) * self.cdRatio - self.cdCut / self.cdRecover , 2))
|
||||
|
||||
def vp_1(self):
|
||||
pass
|
||||
@@ -600,18 +462,5 @@ class ActiveBufferSkill(BuffSkill):
|
||||
else:
|
||||
return 武器冷却惩罚(weapon.itemDetailType, self.char.输出类型)
|
||||
|
||||
def getQuickCDRatio(self):
|
||||
return 1.0
|
||||
cdr = 0
|
||||
if 15 <= self.learnLv <= 30:
|
||||
cdr = 0.01
|
||||
elif 35 <= self.learnLv <= 70:
|
||||
cdr = 0.02
|
||||
elif 75 <= self.learnLv <= 100:
|
||||
cdr = 0.05
|
||||
if self.learnLv in [50, 85, 100]:
|
||||
cdr = 0.05
|
||||
return 1 - cdr
|
||||
|
||||
def getSkillCD(self, mode=None):
|
||||
return max(0, round(max(self.cd * 0.3, self.cd * self.cdReduce / self.cdRecover * self.getWeaponCDRatio() * self.getQuickCDRatio(), 1) - self.cdCut, 2))
|
||||
return max(0, round(max(self.cd * 0.3, self.cd * self.cdReduce / self.cdRecover * self.getWeaponCDRatio(), 1) - self.cdCut, 2))
|
||||
|
||||
Reference in New Issue
Block a user