176 lines
6.0 KiB
Python
176 lines
6.0 KiB
Python
from __future__ import annotations
|
||
|
||
from enum import Enum
|
||
from typing import TYPE_CHECKING, List
|
||
from collections import defaultdict
|
||
|
||
|
||
class Relation(Enum):
|
||
# —— 血缘(先天) ——
|
||
PARENT = "parent" # 父/母 -> 子(有向)
|
||
CHILD = "child" # 子 -> 父/母(有向)
|
||
SIBLING = "sibling" # 兄弟姐妹(对称)
|
||
KIN = "kin" # 其他亲属(对称,泛化)
|
||
|
||
# —— 后天(社会/情感) ——
|
||
MASTER = "master" # 师傅 -> 徒弟(有向)
|
||
APPRENTICE = "apprentice" # 徒弟 -> 师傅(有向)
|
||
LOVERS = "lovers" # 道侣(对称)
|
||
FRIEND = "friend" # 朋友(对称)
|
||
ENEMY = "enemy" # 仇人/敌人(对称)
|
||
|
||
def __str__(self) -> str:
|
||
return relation_display_names.get(self, self.value)
|
||
|
||
@classmethod
|
||
def from_chinese(cls, name_cn: str) -> "Relation|None":
|
||
"""
|
||
依据中文显示名解析关系;无法解析返回 None。
|
||
"""
|
||
if not name_cn:
|
||
return None
|
||
s = str(name_cn).strip()
|
||
for rel, cn in relation_display_names.items():
|
||
if s == cn:
|
||
return rel
|
||
return None
|
||
|
||
|
||
relation_display_names = {
|
||
# 血缘(先天)
|
||
Relation.PARENT: "父母",
|
||
Relation.CHILD: "子女",
|
||
Relation.SIBLING: "兄弟姐妹",
|
||
Relation.KIN: "亲属",
|
||
|
||
# 后天(社会/情感)
|
||
Relation.MASTER: "师傅",
|
||
Relation.APPRENTICE: "徒弟",
|
||
Relation.LOVERS: "道侣",
|
||
Relation.FRIEND: "朋友",
|
||
Relation.ENEMY: "仇人",
|
||
}
|
||
|
||
# 关系是否属于“先天”(血缘),其余为“后天”
|
||
INNATE_RELATIONS: set[Relation] = {
|
||
Relation.PARENT, Relation.CHILD, Relation.SIBLING, Relation.KIN,
|
||
}
|
||
|
||
|
||
def is_innate(relation: Relation) -> bool:
|
||
return relation in INNATE_RELATIONS
|
||
|
||
|
||
# 有向关系的对偶映射;对称关系映射到自身
|
||
RECIPROCAL_RELATION: dict[Relation, Relation] = {
|
||
# 血缘
|
||
Relation.PARENT: Relation.CHILD, # 父母 -> 子女
|
||
Relation.CHILD: Relation.PARENT, # 子女 -> 父母
|
||
Relation.SIBLING: Relation.SIBLING, # 兄弟姐妹 -> 兄弟姐妹
|
||
Relation.KIN: Relation.KIN, # 亲属 -> 亲属
|
||
|
||
# 后天
|
||
Relation.MASTER: Relation.APPRENTICE, # 师傅 -> 徒弟
|
||
Relation.APPRENTICE: Relation.MASTER, # 徒弟 -> 师傅
|
||
Relation.LOVERS: Relation.LOVERS, # 道侣 -> 道侣
|
||
Relation.FRIEND: Relation.FRIEND, # 朋友 -> 朋友
|
||
Relation.ENEMY: Relation.ENEMY, # 仇人 -> 仇人
|
||
}
|
||
|
||
|
||
def get_reciprocal(relation: Relation) -> Relation:
|
||
"""
|
||
给定 A->B 的关系,返回应当写入 B->A 的关系。
|
||
对于对称关系(如 FRIEND/ENEMY/LOVERS/SIBLING/KIN),返回其本身。
|
||
"""
|
||
return RECIPROCAL_RELATION.get(relation, relation)
|
||
|
||
|
||
# ——— 新增:评估两名角色可能新增的后天关系 ———
|
||
if TYPE_CHECKING:
|
||
from src.classes.avatar import Avatar
|
||
|
||
|
||
|
||
|
||
# ——— 显示层:性别化称谓映射与标签工具 ———
|
||
# 基于对方性别的细化:
|
||
GENDERED_DISPLAY: dict[tuple[Relation, str], str] = {
|
||
# 我 -> 对方:CHILD(我为子,对方为父/母) → 显示对方为 父亲/母亲
|
||
(Relation.CHILD, "male"): "父亲",
|
||
(Relation.CHILD, "female"): "母亲",
|
||
# 我 -> 对方:PARENT(我为父/母,对方为子) → 显示对方为 儿子/女儿
|
||
(Relation.PARENT, "male"): "儿子",
|
||
(Relation.PARENT, "female"): "女儿",
|
||
}
|
||
|
||
|
||
def _label_from_self_perspective(relation: Relation, other_gender: object | None = None) -> str:
|
||
# 优先使用性别化细化
|
||
if other_gender is not None:
|
||
gender_value = getattr(other_gender, "value", None) or str(other_gender)
|
||
s = GENDERED_DISPLAY.get((relation, gender_value))
|
||
if s:
|
||
return s
|
||
# 其余关系:以“我”为参照,取对偶再显示(MASTER -> 徒弟)
|
||
counterpart = get_reciprocal(relation)
|
||
return relation_display_names.get(counterpart, str(counterpart))
|
||
|
||
|
||
def get_relations_strs(avatar: "Avatar", max_lines: int = 12) -> list[str]:
|
||
"""
|
||
以“我”的视角整理关系,输出若干行。
|
||
总是显示主要关系栏位(师傅、徒弟、道侣、父母、子女、兄弟姐妹、朋友、仇人),
|
||
若无则显示“无”。
|
||
"""
|
||
relations = getattr(avatar, "relations", {}) or {}
|
||
|
||
# 1. 收集并根据标签分组所有关系
|
||
grouped: dict[str, list[str]] = defaultdict(list)
|
||
for other, rel in relations.items():
|
||
label = _label_from_self_perspective(rel, getattr(other, "gender", None))
|
||
grouped[label].append(other.name)
|
||
|
||
lines: list[str] = []
|
||
|
||
# 2. 定义显示顺序配置:(兜底显示名, [该类别下的具体标签列表])
|
||
# 注意:"父母"和"子女"包含了性别化标签(父亲/母亲)和通用标签(父母)
|
||
display_config = [
|
||
("师傅", ["师傅"]),
|
||
("徒弟", ["徒弟"]),
|
||
("道侣", ["道侣"]),
|
||
("父母", ["父亲", "母亲", "父母"]),
|
||
("子女", ["儿子", "女儿", "子女"]),
|
||
("兄弟姐妹", ["兄弟姐妹"]),
|
||
("朋友", ["朋友"]),
|
||
("仇人", ["仇人"]),
|
||
]
|
||
|
||
processed_labels = set()
|
||
|
||
# 3. 遍历配置生成显示文本
|
||
for fallback_label, target_labels in display_config:
|
||
found_any = False
|
||
for label in target_labels:
|
||
if label in grouped:
|
||
names = ",".join(grouped[label])
|
||
lines.append(f"{label}:{names}")
|
||
found_any = True
|
||
processed_labels.add(label)
|
||
|
||
if not found_any:
|
||
lines.append(f"{fallback_label}:无")
|
||
|
||
# 4. 处理未在配置中列出的其他关系
|
||
for label in sorted(grouped.keys()):
|
||
if label not in processed_labels:
|
||
names = ",".join(grouped[label])
|
||
lines.append(f"{label}:{names}")
|
||
|
||
return lines[:max_lines]
|
||
|
||
|
||
def relations_to_str(avatar: "Avatar", sep: str = ";", max_lines: int = 6) -> str:
|
||
lines = get_relations_strs(avatar, max_lines=max_lines)
|
||
return sep.join(lines) if lines else "无"
|