update technique by sect
This commit is contained in:
@@ -13,7 +13,7 @@ from src.classes.tile import Tile
|
||||
from src.classes.region import Region
|
||||
from src.classes.cultivation import CultivationProgress
|
||||
from src.classes.root import Root
|
||||
from src.classes.technique import Technique, get_random_technique_for_avatar
|
||||
from src.classes.technique import Technique, get_random_technique_for_avatar, get_technique_by_sect
|
||||
from src.classes.age import Age
|
||||
from src.classes.event import NULL_EVENT, Event
|
||||
from src.classes.typings import ACTION_NAME, ACTION_PARAMS, ACTION_NAME_PARAMS_PAIRS, ACTION_NAME_PARAMS_PAIR
|
||||
@@ -102,9 +102,11 @@ class Avatar:
|
||||
if not self.personas:
|
||||
self.personas = get_random_compatible_personas(persona_num, avatar=self)
|
||||
|
||||
# 出生即随机赋予功法(与灵根/阵营/条件兼容)
|
||||
# 出生即按宗门分配功法:
|
||||
# - 散修:仅从无宗门功法抽样
|
||||
# - 有宗门:从“无宗门 + 本宗门”集合抽样
|
||||
if self.technique is None:
|
||||
self.technique = get_random_technique_for_avatar(self)
|
||||
self.technique = get_technique_by_sect(self.sect)
|
||||
|
||||
# 若未设定阵营,则依据宗门/无门无派规则设置,避免后续为 None
|
||||
if self.alignment is None:
|
||||
|
||||
@@ -36,6 +36,8 @@ class Sect:
|
||||
male_sect_given_names: list[str]
|
||||
female_sect_given_names: list[str]
|
||||
headquarter: SectHeadQuarter
|
||||
# 本宗关联的功法名称(来自 technique.csv 的 sect 列)
|
||||
technique_names: list[str]
|
||||
# 功法:在technique.csv中配置
|
||||
# TODO:法宝
|
||||
# TODO:宗内等级和称谓
|
||||
@@ -52,12 +54,23 @@ def _load_sects() -> tuple[dict[int, Sect], dict[str, Sect]]:
|
||||
sects_by_name: dict[str, Sect] = {}
|
||||
|
||||
df = game_configs["sect"]
|
||||
# 可能不存在 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"
|
||||
male_given_names = _split_names(row["male_sect_given_names"])
|
||||
female_given_names = _split_names(row["female_sect_given_names"])
|
||||
|
||||
# 收集该宗门下配置的功法名称
|
||||
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()
|
||||
]
|
||||
|
||||
sect = Sect(
|
||||
id=int(row["id"]),
|
||||
name=str(row["name"]),
|
||||
@@ -73,6 +86,7 @@ def _load_sects() -> tuple[dict[int, Sect], dict[str, Sect]]:
|
||||
desc=str(row.get("headquarter_desc", "")),
|
||||
image=image_path,
|
||||
),
|
||||
technique_names=technique_names,
|
||||
)
|
||||
sects_by_id[sect.id] = sect
|
||||
sects_by_name[sect.name] = sect
|
||||
|
||||
@@ -49,6 +49,8 @@ class Technique:
|
||||
prompt: str
|
||||
weight: float
|
||||
condition: str
|
||||
# 归属宗门名称;None/空表示无宗门要求(散修可修)
|
||||
sect: Optional[str] = None
|
||||
|
||||
def is_allowed_for(self, avatar) -> bool:
|
||||
if not self.condition:
|
||||
@@ -85,6 +87,8 @@ def loads() -> tuple[dict[int, Technique], dict[str, Technique]]:
|
||||
condition = "" if str(cond_val) == "nan" else str(cond_val).strip()
|
||||
weight_val = row.get("weight", 1)
|
||||
weight = float(str(weight_val)) if str(weight_val) != "nan" else 1.0
|
||||
sect_val = row.get("sect", "")
|
||||
sect = None if str(sect_val) == "nan" or str(sect_val).strip() == "" else str(sect_val).strip()
|
||||
t = Technique(
|
||||
id=int(row["id"]),
|
||||
name=name,
|
||||
@@ -93,6 +97,7 @@ def loads() -> tuple[dict[int, Technique], dict[str, Technique]]:
|
||||
prompt=str(row.get("prompt", "")),
|
||||
weight=weight,
|
||||
condition=condition,
|
||||
sect=sect,
|
||||
)
|
||||
techniques_by_id[t.id] = t
|
||||
techniques_by_name[t.name] = t
|
||||
@@ -157,6 +162,35 @@ def get_random_technique_for_avatar(avatar) -> Technique:
|
||||
return random.choices(candidates, weights=weights, k=1)[0]
|
||||
|
||||
|
||||
def get_technique_by_sect(sect) -> Technique:
|
||||
"""
|
||||
简化版:仅按宗门筛选并按权重抽样,不考虑灵根与 condition。
|
||||
- 散修(sect 为 None/空):只从无宗门要求(sect 为空)的功法中抽样;
|
||||
- 有宗门:从“无宗门 + 该宗门”的功法中抽样;
|
||||
若集合为空,则退回全量功法。
|
||||
"""
|
||||
import random
|
||||
|
||||
sect_name: Optional[str] = None
|
||||
if sect is not None:
|
||||
sect_name = getattr(sect, "name", sect)
|
||||
if isinstance(sect_name, str):
|
||||
sect_name = sect_name.strip() or None
|
||||
|
||||
allowed_sects: set[Optional[str]] = {None, ""}
|
||||
if sect_name is not None:
|
||||
allowed_sects.add(sect_name)
|
||||
|
||||
def _in_allowed_sect(t: Technique) -> bool:
|
||||
return (t.sect in allowed_sects) or (t.sect is None) or (t.sect == "")
|
||||
|
||||
candidates: List[Technique] = [t for t in techniques_by_id.values() if _in_allowed_sect(t)]
|
||||
if not candidates:
|
||||
candidates = list(techniques_by_id.values())
|
||||
weights = [max(0.0, t.weight) for t in candidates]
|
||||
return random.choices(candidates, weights=weights, k=1)[0]
|
||||
|
||||
|
||||
def get_grade_bonus(grade: TechniqueGrade) -> float:
|
||||
if grade is TechniqueGrade.UPPER:
|
||||
return 0.10
|
||||
@@ -198,3 +232,18 @@ def get_grade_advantage_bonus(attacker_grade: Optional[TechniqueGrade], defender
|
||||
return bonus
|
||||
|
||||
|
||||
|
||||
# 将功法属性映射为默认的灵根(邪功法不返回)
|
||||
def attribute_to_root(attr: TechniqueAttribute) -> Optional[Root]:
|
||||
mapping: dict[TechniqueAttribute, Root] = {
|
||||
TechniqueAttribute.GOLD: Root.GOLD,
|
||||
TechniqueAttribute.WOOD: Root.WOOD,
|
||||
TechniqueAttribute.WATER: Root.WATER,
|
||||
TechniqueAttribute.FIRE: Root.FIRE,
|
||||
TechniqueAttribute.EARTH: Root.EARTH,
|
||||
TechniqueAttribute.THUNDER: Root.THUNDER,
|
||||
TechniqueAttribute.ICE: Root.ICE,
|
||||
TechniqueAttribute.WIND: Root.WIND,
|
||||
TechniqueAttribute.DARK: Root.DARK,
|
||||
}
|
||||
return mapping.get(attr)
|
||||
|
||||
Reference in New Issue
Block a user