191 lines
5.7 KiB
Python
191 lines
5.7 KiB
Python
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum
|
||
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
|
||
from src.classes.cultivation import Realm
|
||
|
||
|
||
class ElixirType(Enum):
|
||
"""丹药类型"""
|
||
Breakthrough = "Breakthrough" # 破境
|
||
Lifespan = "Lifespan" # 延寿
|
||
BurnBlood = "BurnBlood" # 燃血
|
||
Heal = "Heal" # 疗伤
|
||
Unknown = "Unknown"
|
||
|
||
|
||
@dataclass
|
||
class Elixir:
|
||
"""
|
||
丹药类
|
||
字段与 static/game_configs/elixir.csv 对应
|
||
"""
|
||
id: int
|
||
name: str
|
||
realm: Realm
|
||
type: ElixirType
|
||
desc: str
|
||
price: int
|
||
effects: Union[dict[str, object], list[dict[str, object]]] = field(default_factory=dict)
|
||
effect_desc: str = ""
|
||
|
||
def get_info(self, detailed: bool = False) -> str:
|
||
"""获取信息"""
|
||
if detailed:
|
||
return self.get_detailed_info()
|
||
return f"{self.name}"
|
||
|
||
def get_detailed_info(self) -> str:
|
||
"""获取详细信息"""
|
||
effect_part = f" 效果:{self.effect_desc}" if self.effect_desc else ""
|
||
return f"{self.name}({self.realm.value}·{self._get_type_name()},{self.desc}){effect_part}"
|
||
|
||
def _get_type_name(self) -> str:
|
||
type_names = {
|
||
ElixirType.Breakthrough: "破境",
|
||
ElixirType.Lifespan: "延寿",
|
||
ElixirType.BurnBlood: "燃血",
|
||
ElixirType.Heal: "疗伤",
|
||
}
|
||
return type_names.get(self.type, "未知")
|
||
|
||
def get_colored_info(self) -> str:
|
||
"""获取带颜色标记的信息,供前端渲染使用"""
|
||
# 使用对应境界的颜色
|
||
r, g, b = self.realm.color_rgb
|
||
return f"<color:{r},{g},{b}>{self.get_info()}</color>"
|
||
|
||
def get_structured_info(self) -> dict:
|
||
return {
|
||
"name": self.name,
|
||
"desc": self.desc,
|
||
"grade": self.realm.value,
|
||
"type": self.type.value,
|
||
"type_name": self._get_type_name(),
|
||
"price": self.price,
|
||
"color": self.realm.color_rgb,
|
||
"effect_desc": self.effect_desc,
|
||
}
|
||
|
||
|
||
@dataclass
|
||
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]]]:
|
||
"""
|
||
加载丹药配置
|
||
:return: (id索引字典, name索引字典(值为list))
|
||
"""
|
||
elixirs_by_id: Dict[int, Elixir] = {}
|
||
elixirs_by_name: Dict[str, List[Elixir]] = {}
|
||
|
||
if "elixir" not in game_configs:
|
||
return elixirs_by_id, elixirs_by_name
|
||
|
||
df = game_configs["elixir"]
|
||
for row in df:
|
||
elixir_id = get_int(row, "id")
|
||
name = get_str(row, "name")
|
||
desc = get_str(row, "desc")
|
||
price = get_int(row, "price")
|
||
|
||
# 解析境界
|
||
realm_str = get_str(row, "realm")
|
||
# 尝试匹配 Realm 枚举
|
||
realm = Realm.Qi_Refinement # 默认
|
||
for r in Realm:
|
||
if r.value == realm_str or r.name == realm_str:
|
||
realm = r
|
||
break
|
||
|
||
# 解析类型
|
||
elixir_type = ElixirType(get_str(row, "type"))
|
||
|
||
# 解析 effects
|
||
effects = load_effect_from_str(get_str(row, "effects"))
|
||
effect_desc = format_effects_to_text(effects)
|
||
|
||
elixir = Elixir(
|
||
id=elixir_id,
|
||
name=name,
|
||
realm=realm,
|
||
type=elixir_type,
|
||
desc=desc,
|
||
price=price,
|
||
effects=effects,
|
||
effect_desc=effect_desc
|
||
)
|
||
|
||
elixirs_by_id[elixir_id] = elixir
|
||
|
||
if name not in elixirs_by_name:
|
||
elixirs_by_name[name] = []
|
||
elixirs_by_name[name].append(elixir)
|
||
|
||
return elixirs_by_id, elixirs_by_name
|
||
|
||
|
||
# 导出全局变量
|
||
elixirs_by_id, elixirs_by_name = _load_elixirs()
|
||
|
||
|
||
def get_elixirs_by_realm(realm: Realm) -> List[Elixir]:
|
||
"""获取指定境界的所有丹药"""
|
||
return [e for e in elixirs_by_id.values() if e.realm == realm] |