update death

This commit is contained in:
bridge
2025-12-01 02:05:11 +08:00
parent f047251c0d
commit 39f158bbe8
18 changed files with 185 additions and 66 deletions

View File

@@ -9,6 +9,7 @@ from src.classes.battle import decide_battle, get_assassination_success_rate
from src.classes.story_teller import StoryTeller
from src.classes.normalize import normalize_avatar_name
from src.classes.death import handle_death
from src.classes.death_reason import DeathReason
from src.classes.kill_and_grab import kill_and_grab
if TYPE_CHECKING:
@@ -116,7 +117,7 @@ class Assassinate(InstantAction):
story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True)
# 死亡清理
handle_death(self.world, target)
handle_death(self.world, target, DeathReason.BATTLE)
return [result_event, story_event]
@@ -153,7 +154,7 @@ class Assassinate(InstantAction):
story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True)
if is_fatal:
handle_death(self.world, loser)
handle_death(self.world, loser, DeathReason.BATTLE)
return [result_event, story_event]

View File

@@ -7,6 +7,7 @@ from src.classes.battle import decide_battle, get_effective_strength_pair
from src.classes.story_teller import StoryTeller
from src.classes.normalize import normalize_avatar_name
from src.classes.death import handle_death
from src.classes.death_reason import DeathReason
from src.classes.kill_and_grab import kill_and_grab
class Attack(InstantAction):
@@ -109,6 +110,6 @@ class Attack(InstantAction):
# 如果死亡,执行死亡清理(在故事生成后,保证关系数据可用)
if is_fatal:
handle_death(self.world, loser)
handle_death(self.world, loser, DeathReason.BATTLE)
return [result_event, story_event]

View File

@@ -84,7 +84,8 @@ class Age:
years_over_lifespan = self.age - expected
# 基础概率每超过1年增加0.01的概率
death_probability = min(years_over_lifespan * 0.01, 0.1)
prob_add = 0.01
death_probability = min(years_over_lifespan * prob_add, 0.1)
return death_probability

View File

@@ -114,6 +114,12 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
nickname: Optional[Nickname] = None
# 自定义头像ID如果设置优先使用此ID显示头像
custom_pic_id: Optional[int] = None
# 死亡状态
is_dead: bool = False
# 死亡信息:{ "time": MonthStamp, "reason": str, "location": (x, y) }
death_info: Optional[dict] = None
# 当月/当步新设动作标记:在 commit_next_plan 设为 True首次 tick_action 后清为 False
_new_action_set_this_step: bool = False
# 动作冷却:记录动作类名 -> 上次完成月戳
@@ -122,6 +128,8 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
def join_sect(self, sect: Sect, rank: "SectRank") -> None:
"""加入宗门"""
if self.is_dead:
return
if self.sect:
self.leave_sect()
self.sect = sect
@@ -137,6 +145,38 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
self.sect = None
self.sect_rank = None
def set_dead(self, reason: str, time: MonthStamp) -> None:
"""
设置角色死亡状态。
Args:
reason: 死亡原因
time: 死亡时间
"""
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 = ""
# 退出宗门(保留职位记录还是清除?通常死人不再担任职位)
# 但为了历史记录,也许可以保留 sect 字段,但从宗门成员列表中移除
if self.sect:
self.sect.remove_member(self)
# 不清除 self.sect 和 self.sect_rank作为生平记录保留
def __post_init__(self):
"""
在Avatar创建后自动初始化tile和HP
@@ -319,6 +359,8 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
"long_term_objective": self.long_term_objective.content if self.long_term_objective else "",
"nickname": self.nickname.value if self.nickname else None,
"nickname_reason": self.nickname.reason if self.nickname else None,
"is_dead": self.is_dead,
"death_info": self.death_info,
}
# 复杂对象结构化

View File

@@ -26,6 +26,12 @@ class AvatarManager:
same_region.append(other)
return same_region
def get_living_avatars(self) -> List["Avatar"]:
"""
返回所有存活的角色列表。
"""
return [avatar for avatar in self.avatars.values() if not avatar.is_dead]
def get_observable_avatars(self, avatar: "Avatar") -> List["Avatar"]:
"""
返回处于 avatar 交互范围内的其他角色列表(不含自己)。

View File

@@ -1,16 +1,27 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from src.classes.death_reason import DeathReason
if TYPE_CHECKING:
from src.classes.world import World
from src.classes.avatar import Avatar
def handle_death(world: World, avatar: Avatar) -> None:
def handle_death(world: World, avatar: Avatar, reason: Union[str, DeathReason] = DeathReason.UNKNOWN) -> None:
"""
处理角色死亡的统一入口。
负责将角色从世界管理器中移除,并处理相关的清理工作(如关系解除已在 remove_avatar 中实现)
注意:本函数不负责生成死亡事件文本,调用者应在调用前生成相应的 Event。
负责将角色标记为死亡,清理行动队列,但保留角色数据
Args:
world: 世界对象
avatar: 死亡的角色
reason: 死亡原因DeathReason枚举或字符串
"""
# 从管理器中移除角色remove_avatar 内部会自动清理双向关系)
world.avatar_manager.remove_avatar(avatar.id)
# 如果传入的是枚举,转为字符串值
reason_str = reason.value if isinstance(reason, DeathReason) else str(reason)
# 标记为死亡(软删除)
avatar.set_dead(reason_str, world.month_stamp)
# 可以在这里触发其他逻辑,比如检查是否有继承人等

View File

@@ -0,0 +1,11 @@
from enum import Enum
class DeathReason(Enum):
OLD_AGE = "老死"
BATTLE = "战死"
SERIOUS_INJURY = "重伤"
UNKNOWN = "未知"
def __str__(self) -> str:
return self.value