Files
cultivation-world-simulator/src/classes/relations.py
2025-11-26 15:06:41 +08:00

184 lines
6.7 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 __future__ import annotations
from typing import TYPE_CHECKING, List
from src.classes.relation import Relation, INNATE_RELATIONS, get_reciprocal, is_innate, relation_display_names
from src.classes.event import Event
from src.classes.action.event_helper import EventHelper
if TYPE_CHECKING:
from src.classes.avatar import Avatar
def get_possible_new_relations(from_avatar: "Avatar", to_avatar: "Avatar") -> List[Relation]:
"""
评估"to_avatar 相对于 from_avatar"可能新增的后天关系集合(方向性明确)。
清晰规则:
- LOVERS(道侣):要求男女异性;若已存在 to->from 的相同关系则不重复
- MASTER(师傅):要求 to.level >= from.level + 20
- APPRENTICE(徒弟):要求 to.level <= from.level - 20
- FRIEND(朋友):始终可能(若未已存在)
- ENEMY(仇人):始终可能(若未已存在)
说明:本函数只判断"是否可能",不做概率与人格相关控制;概率留给上层逻辑。
返回的是 Relation 列表,均为 to_avatar 相对于 from_avatar 的候选。
"""
# 方向相关:检查 to->from 已有关系,避免重复推荐
existing_to_from = to_avatar.get_relation(from_avatar)
candidates: list[Relation] = []
# 基础信息Avatar 定义确保存在)
level_from = from_avatar.cultivation_progress.level
level_to = to_avatar.cultivation_progress.level
# - FRIEND
if existing_to_from != Relation.FRIEND:
candidates.append(Relation.FRIEND)
# - ENEMY
if existing_to_from != Relation.ENEMY:
candidates.append(Relation.ENEMY)
# - LOVERS异性Avatar 定义确保性别存在)
if from_avatar.gender != to_avatar.gender and existing_to_from != Relation.LOVERS:
candidates.append(Relation.LOVERS)
# - 师徒(方向性):
# MASTERto 是 from 的师傅 → to.level >= from.level + 20
# APPRENTICEto 是 from 的徒弟 → to.level <= from.level - 20
if level_to >= level_from + 20 and existing_to_from != Relation.MASTER:
candidates.append(Relation.MASTER)
if level_to <= level_from - 20 and existing_to_from != Relation.APPRENTICE:
candidates.append(Relation.APPRENTICE)
return candidates
def set_relation(from_avatar: "Avatar", to_avatar: "Avatar", relation: Relation) -> None:
"""
设置 from_avatar 对 to_avatar 的关系。
- 对称关系(如 FRIEND/ENEMY/LOVERS/SIBLING/KIN会在对方处写入相同的关系。
- 有向关系(如 MASTER、APPRENTICE、PARENT、CHILD会在对方处写入对偶关系。
"""
if to_avatar is from_avatar:
return
from_avatar.relations[to_avatar] = relation
# 写入对方的对偶关系(对称关系会得到同一枚举值)
to_avatar.relations[from_avatar] = get_reciprocal(relation)
def get_relation(from_avatar: "Avatar", to_avatar: "Avatar") -> Relation | None:
"""
获取 from_avatar 对 to_avatar 的关系。
"""
return from_avatar.relations.get(to_avatar)
def clear_relation(from_avatar: "Avatar", to_avatar: "Avatar") -> None:
"""
清除 from_avatar 和 to_avatar 之间的关系(双向清除)。
"""
from_avatar.relations.pop(to_avatar, None)
to_avatar.relations.pop(from_avatar, None)
def cancel_relation(from_avatar: "Avatar", to_avatar: "Avatar", relation: Relation) -> bool:
"""
取消指定的后天关系。
- 只能取消后天关系INNATE_RELATIONS 不可取消)
- 检查该关系是否存在且匹配
- 双向清除
返回:是否成功取消
"""
# 先天关系不可取消
if is_innate(relation):
return False
# 检查关系是否存在且匹配
existing = get_relation(from_avatar, to_avatar)
if existing != relation:
return False
# 清除关系
clear_relation(from_avatar, to_avatar)
return True
def get_possible_cancel_relations(from_avatar: "Avatar", to_avatar: "Avatar") -> List[Relation]:
"""
获取可能取消的关系列表(仅后天关系)。
返回from_avatar 对 to_avatar 的可取消关系列表
"""
existing = get_relation(from_avatar, to_avatar)
if existing is None:
return []
# 只有后天关系可以取消
if is_innate(existing):
return []
return [existing]
def get_relation_change_context(avatar1: "Avatar", avatar2: "Avatar") -> tuple[list[str], list[str]]:
"""
获取两角色间可能的新增关系和取消关系的中文显示列表。
用于构建 Prompt 上下文。
返回:(possible_new_relations, possible_cancel_relations)
"""
# 计算 avatar2 相对于 avatar1 的可能关系
new_rels = get_possible_new_relations(avatar1, avatar2)
cancel_rels = get_possible_cancel_relations(avatar1, avatar2)
new_strs = [relation_display_names[r] for r in new_rels]
cancel_strs = [relation_display_names[r] for r in cancel_rels]
return new_strs, cancel_strs
def process_relation_changes(initiator: "Avatar", target: "Avatar", result_dict: dict, month_stamp: int) -> None:
"""
处理 LLM 返回的关系变更请求。
兼容 Conversation 和 StoryTeller 的通用逻辑。
"""
new_relation_str = str(result_dict.get("new_relation", "")).strip()
# 兼容模板中的拼写错误 (cancal -> cancel)
cancel_relation_str = str(result_dict.get("cancel_relation", "")).strip()
if not cancel_relation_str:
cancel_relation_str = str(result_dict.get("cancal_relation", "")).strip()
# 处理进入新关系
if new_relation_str:
rel = Relation.from_chinese(new_relation_str)
if rel is not None:
set_relation(target, initiator, rel)
set_event = Event(
month_stamp,
f"{target.name}{initiator.name} 的关系变为:{relation_display_names.get(rel, str(rel))}",
related_avatars=[initiator.id, target.id],
is_major=True
)
EventHelper.push_pair(set_event, initiator=initiator, target=target, to_sidebar_once=True)
# 处理取消关系
if cancel_relation_str:
rel = Relation.from_chinese(cancel_relation_str)
if rel is not None:
success = cancel_relation(target, initiator, rel)
if success:
cancel_event = Event(
month_stamp,
f"{target.name}{initiator.name} 取消了关系:{relation_display_names.get(rel, str(rel))}",
related_avatars=[initiator.id, target.id],
is_major=True
)
EventHelper.push_pair(cancel_event, initiator=initiator, target=target, to_sidebar_once=True)