update relationship
This commit is contained in:
@@ -36,7 +36,7 @@ from src.classes.magic_stone import MagicStone
|
|||||||
from src.classes.hp_and_mp import HP, HP_MAX_BY_REALM
|
from src.classes.hp_and_mp import HP, HP_MAX_BY_REALM
|
||||||
from src.utils.id_generator import get_avatar_id
|
from src.utils.id_generator import get_avatar_id
|
||||||
from src.utils.config import CONFIG
|
from src.utils.config import CONFIG
|
||||||
from src.classes.relation import Relation, get_reciprocal
|
from src.classes.relation import Relation, get_reciprocal, get_relation_label
|
||||||
from src.run.log import get_logger
|
from src.run.log import get_logger
|
||||||
from src.classes.alignment import Alignment
|
from src.classes.alignment import Alignment
|
||||||
from src.utils.params import filter_kwargs_for_callable
|
from src.utils.params import filter_kwargs_for_callable
|
||||||
@@ -384,7 +384,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
|
|||||||
relations_list.append({
|
relations_list.append({
|
||||||
"target_id": other.id,
|
"target_id": other.id,
|
||||||
"name": other.name,
|
"name": other.name,
|
||||||
"relation": str(relation),
|
"relation": get_relation_label(relation, self, other),
|
||||||
# 可以加更多 info,比如境界,用于列表中展示
|
# 可以加更多 info,比如境界,用于列表中展示
|
||||||
"realm": other.cultivation_progress.realm.value,
|
"realm": other.cultivation_progress.realm.value,
|
||||||
"sect": other.sect.name if other.sect else "散修"
|
"sect": other.sect.name if other.sect else "散修"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, List
|
from typing import TYPE_CHECKING, List, Optional
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
@@ -86,15 +86,12 @@ def get_reciprocal(relation: Relation) -> Relation:
|
|||||||
return RECIPROCAL_RELATION.get(relation, relation)
|
return RECIPROCAL_RELATION.get(relation, relation)
|
||||||
|
|
||||||
|
|
||||||
# ——— 新增:评估两名角色可能新增的后天关系 ———
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from src.classes.avatar import Avatar
|
from src.classes.avatar import Avatar
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ——— 显示层:性别化称谓映射与标签工具 ———
|
# ——— 显示层:性别化称谓映射与标签工具 ———
|
||||||
# 基于对方性别的细化:
|
|
||||||
GENDERED_DISPLAY: dict[tuple[Relation, str], str] = {
|
GENDERED_DISPLAY: dict[tuple[Relation, str], str] = {
|
||||||
# 我 -> 对方:CHILD(我为子,对方为父/母) → 显示对方为 父亲/母亲
|
# 我 -> 对方:CHILD(我为子,对方为父/母) → 显示对方为 父亲/母亲
|
||||||
(Relation.CHILD, "male"): "父亲",
|
(Relation.CHILD, "male"): "父亲",
|
||||||
@@ -104,72 +101,92 @@ GENDERED_DISPLAY: dict[tuple[Relation, str], str] = {
|
|||||||
(Relation.PARENT, "female"): "女儿",
|
(Relation.PARENT, "female"): "女儿",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 显示顺序配置
|
||||||
|
DISPLAY_ORDER = [
|
||||||
|
"师傅", "徒弟", "道侣",
|
||||||
|
"父亲", "母亲",
|
||||||
|
"儿子", "女儿",
|
||||||
|
"哥哥", "弟弟", "姐姐", "妹妹",
|
||||||
|
"兄弟", "姐妹", "兄弟姐妹", # 兜底
|
||||||
|
"朋友", "仇人",
|
||||||
|
"亲属"
|
||||||
|
]
|
||||||
|
|
||||||
def _label_from_self_perspective(relation: Relation, other_gender: object | None = None) -> str:
|
def get_relation_label(relation: Relation, self_avatar: "Avatar", other_avatar: "Avatar") -> str:
|
||||||
# 优先使用性别化细化
|
"""
|
||||||
if other_gender is not None:
|
获取 self_avatar 视角的 other_avatar 的称谓。
|
||||||
gender_value = getattr(other_gender, "value", None) or str(other_gender)
|
例如:relation=CHILD (self是子), other是男 -> "父亲"
|
||||||
s = GENDERED_DISPLAY.get((relation, gender_value))
|
relation=SIBLING, other比self大, other是女 -> "姐姐"
|
||||||
if s:
|
"""
|
||||||
return s
|
# 1. 处理兄弟姐妹 (涉及长幼比较)
|
||||||
# 其余关系:以“我”为参照,取对偶再显示(MASTER -> 徒弟)
|
if relation == Relation.SIBLING:
|
||||||
counterpart = get_reciprocal(relation)
|
is_older = False
|
||||||
return relation_display_names.get(counterpart, str(counterpart))
|
# 比较出生时间 (MonthStamp 越小越早出生,年龄越大)
|
||||||
|
if hasattr(other_avatar, "birth_month_stamp") and hasattr(self_avatar, "birth_month_stamp"):
|
||||||
|
if other_avatar.birth_month_stamp < self_avatar.birth_month_stamp:
|
||||||
|
is_older = True
|
||||||
|
elif other_avatar.birth_month_stamp == self_avatar.birth_month_stamp:
|
||||||
|
# 同月生,简单按 ID 排序保证一致性
|
||||||
|
is_older = str(other_avatar.id) < str(self_avatar.id)
|
||||||
|
|
||||||
|
gender_val = getattr(getattr(other_avatar, "gender", None), "value", "male")
|
||||||
|
|
||||||
|
if gender_val == "male":
|
||||||
|
return "哥哥" if is_older else "弟弟"
|
||||||
|
else:
|
||||||
|
return "姐姐" if is_older else "妹妹"
|
||||||
|
|
||||||
|
# 2. 查表处理通用性别化称谓
|
||||||
|
other_gender = getattr(other_avatar, "gender", None)
|
||||||
|
gender_val = getattr(other_gender, "value", "male")
|
||||||
|
|
||||||
|
label = GENDERED_DISPLAY.get((relation, gender_val))
|
||||||
|
if label:
|
||||||
|
return label
|
||||||
|
|
||||||
|
# 3. 回退到默认显示名
|
||||||
|
return relation_display_names.get(relation, relation.value)
|
||||||
|
|
||||||
|
|
||||||
def get_relations_strs(avatar: "Avatar", max_lines: int = 12) -> list[str]:
|
def get_relations_strs(avatar: "Avatar", max_lines: int = 12) -> list[str]:
|
||||||
"""
|
"""
|
||||||
以“我”的视角整理关系,输出若干行。
|
以“我”的视角整理关系,输出若干行。
|
||||||
总是显示主要关系栏位(师傅、徒弟、道侣、父母、子女、兄弟姐妹、朋友、仇人),
|
|
||||||
若无则显示“无”。
|
|
||||||
"""
|
"""
|
||||||
relations = getattr(avatar, "relations", {}) or {}
|
relations = getattr(avatar, "relations", {}) or {}
|
||||||
|
|
||||||
# 1. 收集并根据标签分组所有关系
|
# 1. 收集并根据标签分组所有关系
|
||||||
grouped: dict[str, list[str]] = defaultdict(list)
|
grouped: dict[str, list[str]] = defaultdict(list)
|
||||||
for other, rel in relations.items():
|
for other, rel in relations.items():
|
||||||
label = _label_from_self_perspective(rel, getattr(other, "gender", None))
|
label = get_relation_label(rel, avatar, other)
|
||||||
grouped[label].append(other.name)
|
grouped[label].append(other.name)
|
||||||
|
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
|
|
||||||
# 2. 定义显示顺序配置:(兜底显示名, [该类别下的具体标签列表])
|
|
||||||
# 注意:"父母"和"子女"包含了性别化标签(父亲/母亲)和通用标签(父母)
|
|
||||||
display_config = [
|
|
||||||
("师傅", ["师傅"]),
|
|
||||||
("徒弟", ["徒弟"]),
|
|
||||||
("道侣", ["道侣"]),
|
|
||||||
("父母", ["父亲", "母亲", "父母"]),
|
|
||||||
("子女", ["儿子", "女儿", "子女"]),
|
|
||||||
("兄弟姐妹", ["兄弟姐妹"]),
|
|
||||||
("朋友", ["朋友"]),
|
|
||||||
("仇人", ["仇人"]),
|
|
||||||
]
|
|
||||||
|
|
||||||
processed_labels = set()
|
processed_labels = set()
|
||||||
|
|
||||||
# 3. 遍历配置生成显示文本
|
# 2. 按照预定义顺序输出
|
||||||
for fallback_label, target_labels in display_config:
|
for label in DISPLAY_ORDER:
|
||||||
found_any = False
|
if label in grouped:
|
||||||
for label in target_labels:
|
names = ",".join(grouped[label])
|
||||||
if label in grouped:
|
lines.append(f"{label}:{names}")
|
||||||
names = ",".join(grouped[label])
|
processed_labels.add(label)
|
||||||
lines.append(f"{label}:{names}")
|
|
||||||
found_any = True
|
|
||||||
processed_labels.add(label)
|
|
||||||
|
|
||||||
if not found_any:
|
# 3. 处理未在配置中列出的其他关系(按字典序)
|
||||||
lines.append(f"{fallback_label}:无")
|
|
||||||
|
|
||||||
# 4. 处理未在配置中列出的其他关系
|
|
||||||
for label in sorted(grouped.keys()):
|
for label in sorted(grouped.keys()):
|
||||||
if label not in processed_labels:
|
if label not in processed_labels:
|
||||||
names = ",".join(grouped[label])
|
names = ",".join(grouped[label])
|
||||||
lines.append(f"{label}:{names}")
|
lines.append(f"{label}:{names}")
|
||||||
|
processed_labels.add(label)
|
||||||
|
|
||||||
|
# 4. 若无任何关系
|
||||||
|
if not lines:
|
||||||
|
return ["无"]
|
||||||
|
|
||||||
return lines[:max_lines]
|
return lines[:max_lines]
|
||||||
|
|
||||||
|
|
||||||
def relations_to_str(avatar: "Avatar", sep: str = ";", max_lines: int = 6) -> str:
|
def relations_to_str(avatar: "Avatar", sep: str = ";", max_lines: int = 6) -> str:
|
||||||
lines = get_relations_strs(avatar, max_lines=max_lines)
|
lines = get_relations_strs(avatar, max_lines=max_lines)
|
||||||
return sep.join(lines) if lines else "无"
|
# 如果只有一行且是"无",直接返回
|
||||||
|
if len(lines) == 1 and lines[0] == "无":
|
||||||
|
return "无"
|
||||||
|
return sep.join(lines)
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ class PopulationPlanner:
|
|||||||
planned_surname[a] = surname
|
planned_surname[a] = surname
|
||||||
planned_surname[b] = surname
|
planned_surname[b] = surname
|
||||||
planned_gender[a] = Gender.MALE
|
planned_gender[a] = Gender.MALE
|
||||||
planned_relations[(a, b)] = Relation.PARENT
|
planned_relations[(a, b)] = Relation.CHILD
|
||||||
else:
|
else:
|
||||||
mother = a if random.random() < 0.5 else b
|
mother = a if random.random() < 0.5 else b
|
||||||
child = b if mother == a else a
|
child = b if mother == a else a
|
||||||
@@ -354,7 +354,7 @@ class PopulationPlanner:
|
|||||||
if s != mom_surname:
|
if s != mom_surname:
|
||||||
planned_surname[child] = s
|
planned_surname[child] = s
|
||||||
break
|
break
|
||||||
planned_relations[(mother, child)] = Relation.PARENT
|
planned_relations[(mother, child)] = Relation.CHILD
|
||||||
|
|
||||||
leftover = unused_indices[:]
|
leftover = unused_indices[:]
|
||||||
|
|
||||||
@@ -392,7 +392,8 @@ class PopulationPlanner:
|
|||||||
if random.random() < MASTER_PAIR_PROB:
|
if random.random() < MASTER_PAIR_PROB:
|
||||||
master, apprentice = members[j], members[j + 1]
|
master, apprentice = members[j], members[j + 1]
|
||||||
if (master, apprentice) not in planned_relations and (apprentice, master) not in planned_relations:
|
if (master, apprentice) not in planned_relations and (apprentice, master) not in planned_relations:
|
||||||
planned_relations[(master, apprentice)] = Relation.MASTER
|
# MASTER -> APPRENTICE (Master.relations[Apprentice] = APPRENTICE)
|
||||||
|
planned_relations[(master, apprentice)] = Relation.APPRENTICE
|
||||||
j += 2
|
j += 2
|
||||||
|
|
||||||
# — 朋友/仇人 —
|
# — 朋友/仇人 —
|
||||||
@@ -551,9 +552,17 @@ class AvatarFactory:
|
|||||||
|
|
||||||
if attach_relations:
|
if attach_relations:
|
||||||
if plan.parent_avatar is not None:
|
if plan.parent_avatar is not None:
|
||||||
plan.parent_avatar.set_relation(avatar, Relation.PARENT)
|
# plan.parent_avatar 是长辈
|
||||||
|
# 设置关系:长辈.set_relation(自己, CHILD)
|
||||||
|
# 底层逻辑:长辈.relations[自己] = CHILD (长辈认为自己是孩子)
|
||||||
|
# 自己.relations[长辈] = PARENT (自己认为长辈是父母)
|
||||||
|
plan.parent_avatar.set_relation(avatar, Relation.CHILD)
|
||||||
if plan.master_avatar is not None:
|
if plan.master_avatar is not None:
|
||||||
plan.master_avatar.set_relation(avatar, Relation.MASTER)
|
# plan.master_avatar 是师傅
|
||||||
|
# 设置关系:师傅.set_relation(自己, APPRENTICE)
|
||||||
|
# 底层逻辑:师傅.relations[自己] = APPRENTICE (师傅认为自己是徒弟)
|
||||||
|
# 自己.relations[师傅] = MASTER (自己认为师傅是师傅)
|
||||||
|
plan.master_avatar.set_relation(avatar, Relation.APPRENTICE)
|
||||||
|
|
||||||
if avatar.technique is not None:
|
if avatar.technique is not None:
|
||||||
mapped = attribute_to_root(avatar.technique.attribute)
|
mapped = attribute_to_root(avatar.technique.attribute)
|
||||||
@@ -583,19 +592,19 @@ class AvatarFactory:
|
|||||||
levels: list[int] = [random.randint(LEVEL_MIN, LEVEL_MAX) for _ in range(n)]
|
levels: list[int] = [random.randint(LEVEL_MIN, LEVEL_MAX) for _ in range(n)]
|
||||||
|
|
||||||
for (a, b), rel in list(planned_relations.items()):
|
for (a, b), rel in list(planned_relations.items()):
|
||||||
if rel is Relation.PARENT:
|
if rel is Relation.CHILD:
|
||||||
if ages[a] <= ages[b] + (PARENT_MIN_DIFF - 1):
|
if ages[a] <= ages[b] + (PARENT_MIN_DIFF - 1):
|
||||||
ages[a] = min(PARENT_AGE_CAP, ages[b] + random.randint(PARENT_MIN_DIFF, PARENT_MAX_DIFF))
|
ages[a] = min(PARENT_AGE_CAP, ages[b] + random.randint(PARENT_MIN_DIFF, PARENT_MAX_DIFF))
|
||||||
|
|
||||||
for (a, b), rel in list(planned_relations.items()):
|
for (a, b), rel in list(planned_relations.items()):
|
||||||
if rel is Relation.PARENT:
|
if rel is Relation.CHILD:
|
||||||
if levels[a] <= levels[b]:
|
if levels[a] <= levels[b]:
|
||||||
levels[a] = min(LEVEL_MAX, levels[b] + 1)
|
levels[a] = min(LEVEL_MAX, levels[b] + 1)
|
||||||
if levels[a] < levels[b] + PARENT_LEVEL_MIN_DIFF:
|
if levels[a] < levels[b] + PARENT_LEVEL_MIN_DIFF:
|
||||||
levels[a] = min(LEVEL_MAX, levels[b] + PARENT_LEVEL_MIN_DIFF + random.randint(0, PARENT_LEVEL_EXTRA_MAX))
|
levels[a] = min(LEVEL_MAX, levels[b] + PARENT_LEVEL_MIN_DIFF + random.randint(0, PARENT_LEVEL_EXTRA_MAX))
|
||||||
|
|
||||||
for (a, b), rel in list(planned_relations.items()):
|
for (a, b), rel in list(planned_relations.items()):
|
||||||
if rel is Relation.MASTER:
|
if rel is Relation.APPRENTICE:
|
||||||
if levels[a] < levels[b] + MASTER_LEVEL_MIN_DIFF:
|
if levels[a] < levels[b] + MASTER_LEVEL_MIN_DIFF:
|
||||||
levels[a] = min(LEVEL_MAX, levels[b] + MASTER_LEVEL_MIN_DIFF + random.randint(0, MASTER_LEVEL_EXTRA_MAX))
|
levels[a] = min(LEVEL_MAX, levels[b] + MASTER_LEVEL_MIN_DIFF + random.randint(0, MASTER_LEVEL_EXTRA_MAX))
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ async function handleClearObjective() {
|
|||||||
v-for="rel in data.relations"
|
v-for="rel in data.relations"
|
||||||
:key="rel.target_id"
|
:key="rel.target_id"
|
||||||
:name="rel.name"
|
:name="rel.name"
|
||||||
:meta="rel.relation"
|
:meta="`${data.name}的${rel.relation}`"
|
||||||
:sub="`${rel.sect} · ${rel.realm}`"
|
:sub="`${rel.sect} · ${rel.realm}`"
|
||||||
@click="jumpToAvatar(rel.target_id)"
|
@click="jumpToAvatar(rel.target_id)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user