Files
cultivation-world-simulator/src/classes/avatar_manager.py
2026-01-06 21:23:06 +08:00

133 lines
4.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 __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, TYPE_CHECKING, Iterable
import itertools
if TYPE_CHECKING:
from src.classes.avatar import Avatar
from src.classes.observe import get_observable_avatars
@dataclass
class AvatarManager:
# 仅存储存活的角色,用于主循环遍历
avatars: Dict[str, "Avatar"] = field(default_factory=dict)
# 存储已死亡的角色(归档)
dead_avatars: Dict[str, "Avatar"] = field(default_factory=dict)
# --- 变更缓冲区 (不参与序列化) ---
_newly_dead_buffer: List[str] = field(default_factory=list, init=False)
_newly_born_buffer: List[str] = field(default_factory=list, init=False)
def register_avatar(self, avatar: "Avatar", is_newly_born: bool = False) -> None:
"""
注册一个角色到管理器中。
Args:
avatar: 角色对象
is_newly_born: 是否为新出生的角色(若是,则加入变更缓冲供前端同步)
"""
self.avatars[str(avatar.id)] = avatar
if is_newly_born:
self._newly_born_buffer.append(str(avatar.id))
def pop_newly_dead(self) -> List[str]:
"""获取并清空本帧刚死亡的角色ID列表"""
res = list(self._newly_dead_buffer)
self._newly_dead_buffer.clear()
return res
def pop_newly_born(self) -> List[str]:
"""获取并清空本帧刚出生的角色ID列表"""
res = list(self._newly_born_buffer)
self._newly_born_buffer.clear()
return res
def get_avatar(self, avatar_id: str) -> "Avatar | None":
"""
根据 ID 获取角色对象,优先查找活人,再查找死者
"""
aid = str(avatar_id)
return self.avatars.get(aid) or self.dead_avatars.get(aid)
def handle_death(self, avatar_id: str) -> None:
"""
处理角色死亡:将角色从活跃列表移动到墓地
"""
aid = str(avatar_id)
if aid in self.avatars:
avatar = self.avatars.pop(aid)
self.dead_avatars[aid] = avatar
# 断开地图连接,确保不出现在地图网格上
if hasattr(avatar, "tile"):
avatar.tile = None
# 记录变更
self._newly_dead_buffer.append(aid)
def get_avatars_in_same_region(self, avatar: "Avatar") -> List["Avatar"]:
"""
返回与给定 avatar 处于同一区域的其他【存活】角色列表(不含自己)。
"""
if avatar is None or getattr(avatar, "tile", None) is None or avatar.tile.region is None:
return []
region = avatar.tile.region
same_region: list["Avatar"] = []
# 只遍历活人
for other in self.avatars.values():
if other is avatar or getattr(other, "tile", None) is None:
continue
if other.tile.region == region:
same_region.append(other)
return same_region
def get_living_avatars(self) -> List["Avatar"]:
"""
返回所有存活的角色列表。
由于 avatars 现在只存活人,直接返回 values 即可。
"""
return list(self.avatars.values())
def get_observable_avatars(self, avatar: "Avatar") -> List["Avatar"]:
"""
返回处于 avatar 交互范围内的其他【存活】角色列表(不含自己)。
"""
return get_observable_avatars(avatar, self.avatars.values())
def _iter_all_avatars(self) -> Iterable["Avatar"]:
"""辅助方法:遍历所有角色(活人+死者)"""
return itertools.chain(self.avatars.values(), self.dead_avatars.values())
def remove_avatar(self, avatar_id: str) -> None:
"""
从管理器中彻底删除一个 avatar无论是死是活并清理所有与其相关的双向关系。
此操作不可逆。
"""
aid = str(avatar_id)
avatar = self.get_avatar(aid)
if avatar is None:
return
# 1. 清理与其直接记录的关系
related = list(getattr(avatar, "relations", {}).keys())
for other in related:
avatar.clear_relation(other)
# 2. 扫一遍所有角色(含死者),确保清除反向引用
for other in self._iter_all_avatars():
if other is avatar:
continue
if getattr(other, "relations", None) is not None and avatar in other.relations:
other.clear_relation(avatar)
# 3. 移除自身
self.avatars.pop(aid, None)
self.dead_avatars.pop(aid, None)
def remove_avatars(self, avatar_ids: List[str]) -> None:
"""
批量删除 avatars并清理所有关系。
"""
for aid in list(avatar_ids):
self.remove_avatar(aid)