add elixir

This commit is contained in:
bridge
2026-01-05 23:04:55 +08:00
parent 2a68f352bc
commit 8d7e11b021
12 changed files with 502 additions and 103 deletions

View File

@@ -135,14 +135,41 @@ class Avatar(
if elixir.realm > self.cultivation_progress.realm:
return False
# 2. 记录服用状态
# 2. 重复服用校验:若已服用过同种且未失效的丹药,则无效
# 因为延寿丹药都是无限持久的,所以所有延寿丹药都只能服用一次。
for consumed in self.elixirs:
if consumed.elixir.id == elixir.id:
if not consumed.is_completely_expired(int(self.world.month_stamp)):
return False
# 3. 记录服用状态
self.elixirs.append(ConsumedElixir(elixir, int(self.world.month_stamp)))
# 3. 立即触发属性重算因为可能有立即生效的数值变化或者MaxHP/Lifespan改变
# 4. 立即触发属性重算因为可能有立即生效的数值变化或者MaxHP/Lifespan改变
self.recalc_effects()
return True
def process_elixir_expiration(self, current_month: int) -> None:
"""
处理丹药过期:
1. 移除已完全过期的丹药
2. 如果有移除,触发属性重算
"""
if not self.elixirs:
return
original_count = len(self.elixirs)
# 过滤掉完全过期的
self.elixirs = [
e for e in self.elixirs
if not e.is_completely_expired(current_month)
]
# 如果数量减少说明有过期重算属性主要是寿命、MaxHP
if len(self.elixirs) < original_count:
self.recalc_effects()
def join_sect(self, sect: Sect, rank: "SectRank") -> None:
"""加入宗门"""
if self.is_dead:

View File

@@ -61,56 +61,14 @@ class EffectsMixin:
def effects(self: "Avatar") -> dict[str, object]:
"""
合并所有来源的效果:宗门、功法、灵根、特质、兵器、辅助装备、灵兽、天地灵机、丹药
直接复用 get_effect_breakdown 的逻辑,确保显示与实际效果一致。
"""
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)
# 来自已服用的丹药
# 简化逻辑:直接 merge 所有丹药的效果
for consumed in self.elixirs:
_process_source(consumed.elixir)
# get_effect_breakdown 已经完成了条件评估(when)和动态值计算(expressions)
# 我们只需要合并结果即可
for _, effect_dict in self.get_effect_breakdown():
merged = _merge_effects(merged, effect_dict)
return merged
@@ -121,11 +79,21 @@ class EffectsMixin:
"""
breakdown = []
def _collect(name: str, source_obj):
if source_obj is None:
def _collect(name: str, source_obj=None, explicit_effects=None):
"""
收集效果。
source_obj: 包含 .effects 的对象
explicit_effects: 直接传入的 effects (dict or list)
"""
raw_effects = explicit_effects
if raw_effects is None and source_obj is not None:
raw_effects = getattr(source_obj, "effects", {})
if not raw_effects:
return
# 1. 评估条件 (when)
evaluated = _evaluate_conditional_effect(source_obj.effects, self)
evaluated = _evaluate_conditional_effect(raw_effects, self)
# 2. 评估动态值 (expressions)
evaluated = self._evaluate_values(evaluated)
@@ -134,31 +102,33 @@ class EffectsMixin:
# 按照优先级或逻辑顺序收集
if self.sect:
_collect(f"宗门【{self.sect.name}", self.sect)
_collect(f"宗门【{self.sect.name}", source_obj=self.sect)
if self.technique:
_collect(f"功法【{self.technique.name}", self.technique)
_collect(f"功法【{self.technique.name}", source_obj=self.technique)
if self.root:
_collect("灵根", self.root)
_collect("灵根", source_obj=self.root)
for p in self.personas:
_collect(f"特质【{p.name}", p)
_collect(f"特质【{p.name}", source_obj=p)
if self.weapon:
_collect(f"兵器【{self.weapon.name}", self.weapon)
_collect(f"兵器【{self.weapon.name}", source_obj=self.weapon)
if self.auxiliary:
_collect(f"辅助【{self.auxiliary.name}", self.auxiliary)
_collect(f"辅助【{self.auxiliary.name}", source_obj=self.auxiliary)
if self.spirit_animal:
_collect(f"灵兽【{self.spirit_animal.name}", self.spirit_animal)
_collect(f"灵兽【{self.spirit_animal.name}", source_obj=self.spirit_animal)
if self.world.current_phenomenon:
_collect("天地灵机", self.world.current_phenomenon)
_collect("天地灵机", source_obj=self.world.current_phenomenon)
for consumed in self.elixirs:
_collect(f"丹药【{consumed.elixir.name}", consumed.elixir)
# 使用 get_active_effects 获取当前生效的效果
active = consumed.get_active_effects(int(self.world.month_stamp))
_collect(f"丹药【{consumed.elixir.name}", explicit_effects=active)
return breakdown
@@ -212,4 +182,3 @@ class EffectsMixin:
def move_step_length(self: "Avatar") -> int:
"""获取角色的移动步长"""
return self.cultivation_progress.get_move_step()

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List
from typing import Dict, List, Union
from src.utils.df import game_configs, get_str, get_int
from src.classes.effect import load_effect_from_str, format_effects_to_text
@@ -30,7 +30,7 @@ class Elixir:
type: ElixirType
desc: str
price: int
effects: dict[str, object] = field(default_factory=dict)
effects: Union[dict[str, object], list[dict[str, object]]] = field(default_factory=dict)
effect_desc: str = ""
def get_info(self, detailed: bool = False) -> str:
@@ -79,6 +79,53 @@ class ConsumedElixir:
"""
elixir: Elixir
consume_time: int # 服用时的 MonthStamp
_expire_time: Union[int, float] = field(init=False)
def __post_init__(self):
self._expire_time = self.consume_time + self._get_max_duration()
def _get_max_duration(self) -> Union[int, float]:
"""获取丹药的最长持续时间"""
effects = self.elixir.effects
if isinstance(effects, dict):
effects = [effects]
max_d = 0
for eff in effects:
# 如果没有 duration_month 字段,视为永久效果
if "duration_month" not in eff:
return float('inf')
max_d = max(max_d, int(eff.get("duration_month", 0)))
return max_d
def is_completely_expired(self, current_month: int) -> bool:
"""
判断丹药是否彻底失效(所有效果都过期)
"""
return current_month >= self._expire_time
def get_active_effects(self, current_month: int) -> List[dict[str, object]]:
"""
获取当前时间点仍然有效的 effects 列表
"""
active = []
effects = self.elixir.effects
if isinstance(effects, dict):
effects = [effects]
for eff in effects:
# 永久效果
if "duration_month" not in eff:
active.append(eff)
continue
# 有时限效果
duration = int(eff.get("duration_month", 0))
if duration > 0:
if current_month < self.consume_time + duration:
active.append(eff)
return active
def _load_elixirs() -> tuple[Dict[int, Elixir], Dict[str, List[Elixir]]]:

View File

@@ -60,6 +60,7 @@ class World():
"灵石": "修仙界的通用货币。可用于购买法宝丹药,通过采集、交易或掠夺获取。",
"宗门": "修士的庇护所。加入宗门可习得独门功法、获同门庇护;散修自由但资源匮乏。",
"战斗": "弱肉强食。境界压制极大,高境界者对低境界者有绝对优势。若对方死亡,胜者可掠夺败者财物。",
"动作": "你有一系列可以执行的动作。要注意动作的效果、限制条件、区域和时间。"
"动作": "你有一系列可以执行的动作。要注意动作的效果、限制条件、区域和时间。",
"装备与丹药": "通过兵器、辅助装备、丹药等装备,可以获得额外的属性加成,获得或小或大的增益。拥有好的装备或者服用好的丹药,能获得很大好处。",
}
return desc