Files
cultivation-world-simulator/src/classes/sect.py
2025-11-13 01:34:09 +08:00

192 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from dataclasses import dataclass, field
from pathlib import Path
import json
from src.classes.alignment import Alignment
from src.utils.df import game_configs
from src.classes.effect import load_effect_from_str
from src.utils.config import CONFIG
"""
宗门、宗门总部基础数据。
驻地名称与描述已迁移到 sect_region.csv供地图区域系统使用。
此处仅保留宗门本体信息与头像编辑所需的静态字段。
"""
# 宗门驻地(基础展示数据,具体地图位置在 sect_region.csv 中定义)
@dataclass
class SectHeadQuarter:
"""
宗门总部
"""
name: str
desc: str
image: Path
@dataclass
class Sect:
"""
宗门
"""
id: int
name: str
desc: str
member_act_style: str
alignment: Alignment
headquarter: SectHeadQuarter
# 本宗关联的功法名称(来自 technique.csv 的 sect 列)
technique_names: list[str]
# 随机选择宗门时使用的权重默认1
weight: float = 1.0
# 宗门倾向的兵器类型(字符串,如"剑"、"刀"等)
preferred_weapon: str = ""
# 影响角色或系统的效果
effects: dict[str, object] = field(default_factory=dict)
# 宗门自定义职位名称可选SectRank -> 名称
rank_names: dict[str, str] = field(default_factory=dict)
def get_info(self) -> str:
hq = self.headquarter
return f"{self.name}(阵营:{self.alignment},驻地:{hq.name}"
def get_detailed_info(self) -> str:
# 详细描述:风格、阵营、驻地
hq = self.headquarter
return f"{self.name}(阵营:{self.alignment},风格:{self.member_act_style},驻地:{hq.name}"
def get_rank_name(self, rank: "SectRank") -> str:
"""
获取宗门的职位名称(支持自定义)
Args:
rank: 宗门职位枚举
Returns:
职位名称字符串
"""
from src.classes.sect_ranks import SectRank, DEFAULT_RANK_NAMES
# 优先使用自定义名称,否则使用默认名称
return self.rank_names.get(rank.value, DEFAULT_RANK_NAMES.get(rank, "弟子"))
def _split_names(value: object) -> list[str]:
raw = "" if value is None or str(value) == "nan" else str(value)
sep = CONFIG.df.ids_separator
parts = [x.strip() for x in raw.split(sep) if x.strip()] if raw else []
return parts
def _load_sects() -> tuple[dict[int, Sect], dict[str, Sect]]:
"""从配表加载 sect 数据"""
sects_by_id: dict[int, Sect] = {}
sects_by_name: dict[str, Sect] = {}
df = game_configs["sect"]
# 读取宗门驻地映射(优先从 sect_region.csv 获取驻地地名/描述)
sect_region_df = game_configs.get("sect_region")
hq_by_sect_id: dict[int, tuple[str, str]] = {}
if sect_region_df is not None:
for _, sr in sect_region_df.iterrows():
sid_str = str(sr.get("sect_id", "")).strip()
# 跳过说明行或空值
if not sid_str.isdigit():
continue
sid = int(sid_str)
hq_name = str(sr.get("headquarter_name", "")).strip()
hq_desc = str(sr.get("headquarter_desc", "")).strip()
hq_by_sect_id[sid] = (hq_name, hq_desc)
# 可能不存在 technique 配表或未添加 sect 列,做容错
tech_df = game_configs.get("technique")
assets_base = Path("assets/sects")
for _, row in df.iterrows():
image_path = assets_base / f"{row['name']}.png"
# 收集该宗门下配置的功法名称
technique_names: list[str] = []
if tech_df is not None and "sect" in tech_df.columns:
technique_names = [
str(tname).strip()
for tname in tech_df.loc[tech_df["sect"] == row["name"], "name"].tolist()
if str(tname).strip()
]
# 读取权重(缺省/NaN 则为 1.0
weight_val = row.get("weight", 1)
weight = float(str(weight_val)) if str(weight_val) != "nan" else 1.0
# 读取 effects兼容 JSON/单引号字面量/空)
effects = load_effect_from_str(row.get("effects", ""))
# 读取倾向兵器类型
preferred_weapon_val = row.get("preferred_weapon", "")
preferred_weapon = str(preferred_weapon_val).strip() if str(preferred_weapon_val) != "nan" else ""
# 从 sect_region.csv 中优先取驻地名称/描述;否则兼容旧列或退回宗门名
csv_hq = hq_by_sect_id.get(int(row["id"]))
hq_name_from_csv = (csv_hq[0] if csv_hq else "").strip() if csv_hq else ""
hq_desc_from_csv = (csv_hq[1] if csv_hq else "").strip() if csv_hq else ""
sect = Sect(
id=int(row["id"]),
name=str(row["name"]),
desc=str(row["desc"]),
member_act_style=str(row["member_act_style"]),
alignment=Alignment.from_str(row["alignment"]),
# 驻地:优先 sect_region.csv否则兼容旧列最终回退宗门名
headquarter=SectHeadQuarter(
name=(hq_name_from_csv or str(row.get("headquarter_name", "")).strip() or str(row["name"])) ,
desc=(hq_desc_from_csv or str(row.get("headquarter_desc", ""))),
image=image_path,
),
technique_names=technique_names,
weight=weight,
preferred_weapon=preferred_weapon,
effects=effects,
)
sects_by_id[sect.id] = sect
sects_by_name[sect.name] = sect
return sects_by_id, sects_by_name
# 导出:从配表加载 sect 数据
sects_by_id, sects_by_name = _load_sects()
def get_sect_info_with_rank(avatar: "Avatar", detailed: bool = False) -> str:
"""
获取包含职位的宗门信息字符串
Args:
avatar: 角色对象
detailed: 是否包含宗门详细信息(阵营、风格、驻地等)
Returns:
- 散修:返回"散修"
- detailed=False返回"明心剑宗长老"
- detailed=True返回"明心剑宗长老(阵营:正,风格:...,驻地:..."
"""
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from src.classes.avatar import Avatar
# 散修直接返回
if avatar.sect is None:
return "散修"
# 获取职位+宗门名(如"明心剑宗长老"
sect_rank_str = avatar.get_sect_str()
# 如果不需要详细信息,直接返回职位字符串
if not detailed:
return sect_rank_str
# 需要详细信息:拼接宗门的详细描述
sect_detail = avatar.sect.get_detailed_info() # "明心剑宗(阵营:正,..."
# 提取括号及其内容
if "" in sect_detail:
detail_part = sect_detail[sect_detail.index(""):]
return f"{sect_rank_str}{detail_part}"
# 如果没有括号(理论上不应该出现),直接返回职位字符串
return sect_rank_str