refactor effect system

This commit is contained in:
bridge
2026-01-04 22:03:05 +08:00
parent 806e2c1262
commit 276902bca0
17 changed files with 56 additions and 14 deletions

View File

@@ -40,7 +40,7 @@ from src.classes.nickname_data import Nickname
from src.utils.config import CONFIG
# Mixin 导入
from src.classes.avatar.effects_mixin import EffectsMixin
from src.classes.effect import EffectsMixin
from src.classes.avatar.inventory_mixin import InventoryMixin
from src.classes.avatar.action_mixin import ActionMixin

View File

@@ -1,206 +0,0 @@
"""
Avatar 效果计算 Mixin
负责角色效果的计算和应用。
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from src.classes.avatar.core import Avatar
from src.classes.effect import _merge_effects, _evaluate_conditional_effect
from src.classes.hp import HP_MAX_BY_REALM
class EffectsMixin:
"""效果计算相关方法"""
def _evaluate_values(self, effects: dict[str, Any]) -> dict[str, Any]:
"""
评估效果字典中的动态值(字符串表达式)。
支持明确的 'eval(...)' 格式,以及包含 'avatar.' 的隐式表达式。
"""
result = {}
# 安全的 eval 上下文
context = {
"__builtins__": {},
"avatar": self,
"max": max,
"min": min,
"int": int,
"float": float,
"round": round,
}
for k, v in effects.items():
if isinstance(v, str):
s = v.strip()
expr = None
# 检查是否为表达式
if s.startswith("eval(") and s.endswith(")"):
expr = s[5:-1]
elif "avatar." in s: # 启发式:包含 avatar. 则视为表达式
expr = s
if expr:
try:
result[k] = eval(expr, context)
except Exception:
# 评估失败,保留原值(可能是普通字符串,或者表达式有误)
result[k] = v
else:
result[k] = v
else:
result[k] = v
return result
@property
def effects(self: "Avatar") -> dict[str, object]:
"""
合并所有来源的效果:宗门、功法、灵根、特质、兵器、辅助装备、灵兽、天地灵机
"""
merged: dict[str, object] = {}
def _process_source(source_obj):
if source_obj is None:
return
# 1. 评估条件 (when)
evaluated = _evaluate_conditional_effect(source_obj.effects, self)
# 2. 评估动态值 (expressions)
evaluated = self._evaluate_values(evaluated)
# 3. 合并到总效果
nonlocal merged
merged = _merge_effects(merged, evaluated)
# 来自宗门
if self.sect is not None:
_process_source(self.sect)
# 来自功法
if self.technique is not None:
_process_source(self.technique)
# 来自灵根
if self.root is not None:
_process_source(self.root)
# 来自特质persona
for persona in self.personas:
_process_source(persona)
# 来自兵器
if self.weapon is not None:
_process_source(self.weapon)
# 来自辅助装备
if self.auxiliary is not None:
_process_source(self.auxiliary)
# 来自灵兽
if self.spirit_animal is not None:
_process_source(self.spirit_animal)
# 来自天地灵机世界级buff/debuff
if self.world.current_phenomenon is not None:
_process_source(self.world.current_phenomenon)
return merged
def get_effect_breakdown(self: "Avatar") -> list[tuple[str, dict[str, Any]]]:
"""
获取效果明细,返回 [(来源名称, 生效的效果字典), ...]
用于 get_desc 展示。
"""
breakdown = []
def _collect(name: str, source_obj):
if source_obj is None:
return
# 1. 评估条件 (when)
evaluated = _evaluate_conditional_effect(source_obj.effects, self)
# 2. 评估动态值 (expressions)
evaluated = self._evaluate_values(evaluated)
if evaluated:
breakdown.append((name, evaluated))
# 按照优先级或逻辑顺序收集
if self.sect:
_collect(f"宗门【{self.sect.name}", self.sect)
if self.technique:
_collect(f"功法【{self.technique.name}", self.technique)
if self.root:
_collect("灵根", self.root)
for p in self.personas:
_collect(f"特质【{p.name}", p)
if self.weapon:
_collect(f"兵器【{self.weapon.name}", self.weapon)
if self.auxiliary:
_collect(f"辅助【{self.auxiliary.name}", self.auxiliary)
if self.spirit_animal:
_collect(f"灵兽【{self.spirit_animal.name}", self.spirit_animal)
if self.world.current_phenomenon:
_collect("天地灵机", self.world.current_phenomenon)
return breakdown
def recalc_effects(self: "Avatar") -> None:
"""
重新计算所有长期效果
在装备更换、突破境界等情况下调用
当前包括:
- HP 最大值
- 寿命最大值
"""
# 计算基础最大值(基于境界)
base_max_hp = HP_MAX_BY_REALM.get(self.cultivation_progress.realm, 100)
# 访问 self.effects 会触发 @property重新 merge 所有 effects
effects = self.effects
extra_max_hp = int(effects.get("extra_max_hp", 0))
extra_max_lifespan = int(effects.get("extra_max_lifespan", 0))
# 计算新的最大值
new_max_hp = base_max_hp + extra_max_hp
# 更新最大值
self.hp.max = new_max_hp
# 更新寿命
if self.age:
self.age.max_lifespan = self.age.base_max_lifespan + extra_max_lifespan
# 调整当前值(不超过新的最大值)
if self.hp.cur > new_max_hp:
self.hp.cur = new_max_hp
def update_time_effect(self: "Avatar") -> None:
"""
随时间更新的被动效果。
当前实现:当 HP 未满时,回复最大生命值的 1%受HP恢复速率加成影响
"""
if self.hp.cur < self.hp.max:
base_recover = self.hp.max * 0.01
# 应用HP恢复速率加成
recovery_rate_raw = self.effects.get("extra_hp_recovery_rate", 0.0)
recovery_rate_multiplier = 1.0 + float(recovery_rate_raw or 0.0)
recover_amount = int(base_recover * recovery_rate_multiplier)
self.hp.recover(recover_amount)
@property
def move_step_length(self: "Avatar") -> int:
"""获取角色的移动步长"""
return self.cultivation_progress.get_move_step()

View File

@@ -17,7 +17,7 @@ from src.utils.config import CONFIG
def _get_effects_text(avatar: "Avatar") -> str:
"""获取格式化的效果文本"""
from src.utils.effect_desc import format_effects_to_text
from src.classes.effect import format_effects_to_text
breakdown = avatar.get_effect_breakdown()
effect_parts = []
for source_name, effects in breakdown:
@@ -383,7 +383,7 @@ def get_avatar_desc(avatar: "Avatar", detailed: bool = False) -> str:
lines.append("\n--- 当前效果明细 ---")
breakdown = avatar.get_effect_breakdown()
from src.utils.effect_desc import format_effects_to_text
from src.classes.effect import format_effects_to_text
if not breakdown:
lines.append("无额外效果")