feat:优化职业技能关联实现

This commit was merged in pull request #11.
This commit is contained in:
2026-10-13 18:19:20 +08:00
parent 927866d366
commit 66ff3ff63b
175 changed files with 14299 additions and 440 deletions
+17 -4
View File
@@ -3,6 +3,16 @@
技能关联由 `core/basic/skill_effects.py` 的 `SkillEffectEngine` 统一处理。
`lv`、VP 等输入变化不再通过历史倍率乘除更新目标,而是重新解析最终状态并汇总本次关联贡献。
## 技能身份与引用维护
职业模块导入自己的 Ref;技能类声明 `ref = Ref.XXX`,`code` 从 ref 派生,name 只负责显示。
角色初始化根据技能数据 prefix 校验职业范围,并创建独立的 ref/code 索引;输出圣骑士等职业变体复用数据来源范围。
SkillEffect.skills、except_skills、Read.skill、level_source 统一接受 Ref,旧名称字符串继续兼容。
公共技能引用只匹配当前角色实际绑定的技能实例。GetSkill(Ref.XXX) 查询引用,GetSkillByCode 查询原始 code;
字符串不会隐式当作 code。重复绑定、跨职业引用直接报错;新增规则应使用 Ref,旧失配名称继续保留 SKIP 诊断。
引用生成、公共分组、编辑器中文提示的维护说明见 core/character/refs/base.py。
SkillRef.name 只影响提示,不参与相等性和哈希;技能名称变更不会改变引用身份。
## 计算阶段
1. `SetSkills` 批量读取基础等级、VP、UP。传入的等级是基础等级,不含关联技能提供的等级;重复传入同一配置不会重复加级。
@@ -186,6 +196,7 @@ backend/.venv/bin/pytest backend/tests/smoke/test_calc_availability.py -k config
第二项验证技能配置顺序(包括搭配武器的场景),不验证装备执行顺序。修改公共引擎后再运行完整 `backend/tests` 回归。
"""
from core.character.refs.base import resolve_skill
from collections import defaultdict
from contextlib import contextmanager
from dataclasses import dataclass
@@ -331,14 +342,16 @@ class SkillEffectEngine:
for name in declaration.skills:
if name in declaration.except_skills:
continue
target = self.character.GetSkillByName(name)
target = resolve_skill(self.character, name)
if target is None:
if declaration.missing_target == MissingTargetPolicy.ERROR:
raise ValueError(f'类型化效果目标不存在: {source.name} -> {name}')
self.missing_target(source, name)
else:
targets.append(target)
targets = [target for target in targets if target.name not in declaration.except_skills]
excluded = {id(target) for ref in declaration.except_skills
if (target := resolve_skill(self.character, ref)) is not None}
targets = [target for target in targets if id(target) not in excluded]
for target in targets:
if attribute in STATE_ATTRIBUTES - {'lv'} and (target, attribute) not in self._state_base:
raise ValueError(f'类型化效果状态缺少默认值: {source.name} -> {target.name}.{attribute}')
@@ -428,7 +441,7 @@ class SkillEffectEngine:
graph = {skill: set() for skill in self.skills}
for skill in self.skills:
if skill.level_source is not None:
source = self.character.GetSkillByName(skill.level_source)
source = resolve_skill(self.character, skill.level_source)
if source is None:
raise ValueError(f'共享等级源技能不存在: {skill.name} -> {skill.level_source}')
graph[skill].add(source)
@@ -447,7 +460,7 @@ class SkillEffectEngine:
for skill in order:
# All upstream level/parameter contributions are ready now.
if skill.level_source is not None:
skill._lv = self.character.GetSkillByName(skill.level_source).lv
skill._lv = resolve_skill(self.character, skill.level_source).lv
contributions, reads = self._collect(skill)
if not reads.issubset(graph[skill]):
raise ValueError(f'技能关联状态依赖随计算改变: {skill.name}')