fix bug
This commit is contained in:
311
src/classes/avatar/core.py
Normal file
311
src/classes/avatar/core.py
Normal file
@@ -0,0 +1,311 @@
|
||||
"""
|
||||
Avatar 核心类
|
||||
|
||||
精简后的 Avatar 类,通过 Mixin 组合完整功能。
|
||||
"""
|
||||
import random
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.sect_ranks import SectRank
|
||||
|
||||
from src.classes.calendar import MonthStamp
|
||||
from src.classes.world import World
|
||||
from src.sim.save.avatar_save_mixin import AvatarSaveMixin
|
||||
from src.sim.load.avatar_load_mixin import AvatarLoadMixin
|
||||
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_technique_by_sect
|
||||
from src.classes.age import Age
|
||||
from src.classes.event import Event
|
||||
from src.classes.action_runtime import ActionPlan, ActionInstance
|
||||
from src.classes.alignment import Alignment
|
||||
from src.classes.persona import Persona, get_random_compatible_personas
|
||||
from src.classes.item import Item
|
||||
from src.classes.weapon import Weapon
|
||||
from src.classes.auxiliary import Auxiliary
|
||||
from src.classes.magic_stone import MagicStone
|
||||
from src.classes.hp_and_mp import HP, HP_MAX_BY_REALM
|
||||
from src.classes.relation import Relation
|
||||
from src.classes.sect import Sect
|
||||
from src.classes.appearance import Appearance, get_random_appearance
|
||||
from src.classes.spirit_animal import SpiritAnimal
|
||||
from src.classes.long_term_objective import LongTermObjective
|
||||
from src.classes.nickname_data import Nickname
|
||||
from src.utils.config import CONFIG
|
||||
|
||||
# Mixin 导入
|
||||
from src.classes.avatar.effects_mixin import EffectsMixin
|
||||
from src.classes.avatar.inventory_mixin import InventoryMixin
|
||||
from src.classes.avatar.action_mixin import ActionMixin
|
||||
|
||||
persona_num = CONFIG.avatar.persona_num
|
||||
|
||||
|
||||
class Gender(Enum):
|
||||
MALE = "male"
|
||||
FEMALE = "female"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return gender_strs.get(self, self.value)
|
||||
|
||||
|
||||
gender_strs = {
|
||||
Gender.MALE: "男",
|
||||
Gender.FEMALE: "女",
|
||||
}
|
||||
|
||||
# 历史事件的最大数量
|
||||
MAX_HISTORY_EVENTS = 10
|
||||
|
||||
|
||||
@dataclass
|
||||
class Avatar(
|
||||
AvatarSaveMixin,
|
||||
AvatarLoadMixin,
|
||||
EffectsMixin,
|
||||
InventoryMixin,
|
||||
ActionMixin,
|
||||
):
|
||||
"""
|
||||
NPC的类。
|
||||
包含了这个角色的一切信息。
|
||||
"""
|
||||
world: World
|
||||
name: str
|
||||
id: str
|
||||
birth_month_stamp: MonthStamp
|
||||
age: Age
|
||||
gender: Gender
|
||||
cultivation_progress: CultivationProgress = field(default_factory=lambda: CultivationProgress(0))
|
||||
pos_x: int = 0
|
||||
pos_y: int = 0
|
||||
tile: Optional[Tile] = None
|
||||
|
||||
root: Root = field(default_factory=lambda: random.choice(list(Root)))
|
||||
personas: List[Persona] = field(default_factory=list)
|
||||
technique: Technique | None = None
|
||||
history_events: List[Event] = field(default_factory=list)
|
||||
_pending_events: List[Event] = field(default_factory=list)
|
||||
current_action: Optional[ActionInstance] = None
|
||||
planned_actions: List[ActionPlan] = field(default_factory=list)
|
||||
thinking: str = ""
|
||||
short_term_objective: str = ""
|
||||
long_term_objective: Optional[LongTermObjective] = None
|
||||
magic_stone: MagicStone = field(default_factory=lambda: MagicStone(0))
|
||||
items: dict[Item, int] = field(default_factory=dict)
|
||||
hp: HP = field(default_factory=lambda: HP(0, 0))
|
||||
relations: dict["Avatar", Relation] = field(default_factory=dict)
|
||||
alignment: Alignment | None = None
|
||||
sect: Sect | None = None
|
||||
sect_rank: "SectRank | None" = None
|
||||
appearance: Appearance = field(default_factory=get_random_appearance)
|
||||
weapon: Optional[Weapon] = None
|
||||
weapon_proficiency: float = 0.0
|
||||
auxiliary: Optional[Auxiliary] = None
|
||||
spirit_animal: Optional[SpiritAnimal] = None
|
||||
nickname: Optional[Nickname] = None
|
||||
custom_pic_id: Optional[int] = None
|
||||
|
||||
is_dead: bool = False
|
||||
death_info: Optional[dict] = None
|
||||
|
||||
_new_action_set_this_step: bool = False
|
||||
_action_cd_last_months: dict[str, int] = field(default_factory=dict)
|
||||
|
||||
known_regions: set[int] = field(default_factory=set)
|
||||
|
||||
# ========== 宗门相关 ==========
|
||||
|
||||
def join_sect(self, sect: Sect, rank: "SectRank") -> None:
|
||||
"""加入宗门"""
|
||||
if self.is_dead:
|
||||
return
|
||||
if self.sect:
|
||||
self.leave_sect()
|
||||
self.sect = sect
|
||||
self.sect_rank = rank
|
||||
sect.add_member(self)
|
||||
|
||||
def leave_sect(self) -> None:
|
||||
"""退出宗门"""
|
||||
if self.sect:
|
||||
self.sect.remove_member(self)
|
||||
self.sect = None
|
||||
self.sect_rank = None
|
||||
|
||||
def get_sect_str(self) -> str:
|
||||
"""获取宗门显示名:有宗门则返回"宗门名+职位",否则返回"散修"。"""
|
||||
if self.sect is None:
|
||||
return "散修"
|
||||
if self.sect_rank is None:
|
||||
return self.sect.name
|
||||
from src.classes.sect_ranks import get_rank_display_name
|
||||
rank_name = get_rank_display_name(self.sect_rank, self.sect)
|
||||
return f"{self.sect.name}{rank_name}"
|
||||
|
||||
def get_sect_rank_name(self) -> str:
|
||||
"""获取宗门职位的显示名称"""
|
||||
if self.sect is None or self.sect_rank is None:
|
||||
return "散修"
|
||||
from src.classes.sect_ranks import get_rank_display_name
|
||||
return get_rank_display_name(self.sect_rank, self.sect)
|
||||
|
||||
# ========== 死亡相关 ==========
|
||||
|
||||
def set_dead(self, reason: str, time: MonthStamp) -> None:
|
||||
"""设置角色死亡状态。"""
|
||||
if self.is_dead:
|
||||
return
|
||||
|
||||
self.is_dead = True
|
||||
self.death_info = {
|
||||
"time": int(time),
|
||||
"reason": reason,
|
||||
"location": (self.pos_x, self.pos_y)
|
||||
}
|
||||
|
||||
self.planned_actions.clear()
|
||||
self.current_action = None
|
||||
self._pending_events.clear()
|
||||
self.thinking = ""
|
||||
self.short_term_objective = ""
|
||||
|
||||
if self.sect:
|
||||
self.sect.remove_member(self)
|
||||
|
||||
def death_by_old_age(self) -> bool:
|
||||
"""检查是否老死"""
|
||||
return self.age.death_by_old_age(self.cultivation_progress.realm)
|
||||
|
||||
# ========== 年龄与修为 ==========
|
||||
|
||||
def update_age(self, current_month_stamp: MonthStamp):
|
||||
"""更新年龄"""
|
||||
self.age.update_age(current_month_stamp, self.birth_month_stamp)
|
||||
|
||||
def update_cultivation(self, new_level: int):
|
||||
"""更新修仙进度,并在境界提升时更新寿命和宗门职位"""
|
||||
old_realm = self.cultivation_progress.realm
|
||||
self.cultivation_progress.level = new_level
|
||||
self.cultivation_progress.realm = self.cultivation_progress.get_realm(new_level)
|
||||
|
||||
if self.cultivation_progress.realm != old_realm:
|
||||
self.age.update_realm(self.cultivation_progress.realm)
|
||||
self.recalc_effects()
|
||||
from src.classes.sect_ranks import check_and_promote_sect_rank
|
||||
check_and_promote_sect_rank(self, old_realm, self.cultivation_progress.realm)
|
||||
|
||||
# ========== 区域与位置 ==========
|
||||
|
||||
def is_in_region(self, region: Region | None) -> bool:
|
||||
current_region = self.tile.region
|
||||
if current_region is None:
|
||||
tile = self.world.map.get_tile(self.pos_x, self.pos_y)
|
||||
current_region = tile.region
|
||||
return current_region == region
|
||||
|
||||
def get_co_region_avatars(self, avatars: List["Avatar"]) -> List["Avatar"]:
|
||||
"""返回与自己处于同一区域的角色列表(不含自己)。"""
|
||||
if self.tile is None:
|
||||
return []
|
||||
same_region: list[Avatar] = []
|
||||
for other in avatars:
|
||||
if other is self or other.tile is None:
|
||||
continue
|
||||
if other.tile.region == self.tile.region:
|
||||
same_region.append(other)
|
||||
return same_region
|
||||
|
||||
def _init_known_regions(self):
|
||||
"""初始化已知区域:当前位置 + 宗门驻地"""
|
||||
if self.tile and self.tile.region:
|
||||
self.known_regions.add(self.tile.region.id)
|
||||
|
||||
if self.sect:
|
||||
for r in self.world.map.sect_regions.values():
|
||||
if r.sect_id == self.sect.id:
|
||||
self.known_regions.add(r.id)
|
||||
break
|
||||
|
||||
# ========== 关系相关 ==========
|
||||
|
||||
def set_relation(self, other: "Avatar", relation: Relation) -> None:
|
||||
"""设置与另一个角色的关系。"""
|
||||
from src.classes.relations import set_relation
|
||||
set_relation(self, other, relation)
|
||||
|
||||
def get_relation(self, other: "Avatar") -> Optional[Relation]:
|
||||
"""获取与另一个角色的关系。"""
|
||||
from src.classes.relations import get_relation
|
||||
return get_relation(self, other)
|
||||
|
||||
def clear_relation(self, other: "Avatar") -> None:
|
||||
"""清除与另一个角色的关系。"""
|
||||
from src.classes.relations import clear_relation
|
||||
clear_relation(self, other)
|
||||
|
||||
# ========== 信息展示(委托) ==========
|
||||
|
||||
def get_info(self, detailed: bool = False) -> dict:
|
||||
from src.classes.avatar.info_presenter import get_avatar_info
|
||||
return get_avatar_info(self, detailed)
|
||||
|
||||
def get_structured_info(self) -> dict:
|
||||
from src.classes.avatar.info_presenter import get_avatar_structured_info
|
||||
return get_avatar_structured_info(self)
|
||||
|
||||
def get_hover_info(self) -> list[str]:
|
||||
from src.classes.avatar.info_presenter import get_avatar_hover_info
|
||||
return get_avatar_hover_info(self)
|
||||
|
||||
def get_expanded_info(
|
||||
self,
|
||||
co_region_avatars: Optional[List["Avatar"]] = None,
|
||||
other_avatar: Optional["Avatar"] = None,
|
||||
detailed: bool = False
|
||||
) -> dict:
|
||||
from src.classes.avatar.info_presenter import get_avatar_expanded_info
|
||||
return get_avatar_expanded_info(self, co_region_avatars, other_avatar, detailed)
|
||||
|
||||
def get_other_avatar_info(self, other_avatar: "Avatar") -> str:
|
||||
from src.classes.avatar.info_presenter import get_other_avatar_info
|
||||
return get_other_avatar_info(self, other_avatar)
|
||||
|
||||
# ========== 魔法方法 ==========
|
||||
|
||||
def __post_init__(self):
|
||||
"""在Avatar创建后自动初始化tile和HP"""
|
||||
self.tile = self.world.map.get_tile(self.pos_x, self.pos_y)
|
||||
|
||||
max_hp = HP_MAX_BY_REALM.get(self.cultivation_progress.realm, 100)
|
||||
self.hp = HP(max_hp, max_hp)
|
||||
|
||||
if not self.personas:
|
||||
self.personas = get_random_compatible_personas(persona_num, avatar=self)
|
||||
|
||||
if self.technique is None:
|
||||
self.technique = get_technique_by_sect(self.sect)
|
||||
|
||||
if self.sect:
|
||||
self.sect.add_member(self)
|
||||
|
||||
if self.alignment is None:
|
||||
if self.sect is not None:
|
||||
self.alignment = self.sect.alignment
|
||||
else:
|
||||
self.alignment = random.choice(list(Alignment))
|
||||
|
||||
self.recalc_effects()
|
||||
self._init_known_regions()
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.get_info(detailed=False))
|
||||
|
||||
Reference in New Issue
Block a user