diff --git a/src/classes/action/battle.py b/src/classes/action/battle.py index 9af7067..dd64372 100644 --- a/src/classes/action/battle.py +++ b/src/classes/action/battle.py @@ -5,6 +5,7 @@ from src.classes.event import Event 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 class Battle(InstantAction): @@ -45,7 +46,7 @@ class Battle(InstantAction): if target is not None: target.increase_weapon_proficiency(proficiency_gain) - self._last_result = (winner.name, loser.name, loser_damage, winner_damage) + self._last_result = (winner, loser, loser_damage, winner_damage) def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]: if avatar_name is None: @@ -77,21 +78,31 @@ class Battle(InstantAction): return [] winner, loser = res[0], res[1] loser_damage, winner_damage = res[2], res[3] - result_text = f"{winner} 战胜了 {loser},{loser} 受伤{loser_damage}点,{winner} 也受伤{winner_damage}点" + + # 判定是否致死 + is_fatal = loser.hp <= 0 + if is_fatal: + result_text = f"{winner.name} 战胜了 {loser.name},造成{loser_damage}点伤害。{loser.name} 遭受重创,当场陨落。" + else: + result_text = f"{winner.name} 战胜了 {loser.name},{loser.name} 受伤{loser_damage}点,{winner.name} 也受伤{winner_damage}点" + rel_ids = [self.avatar.id] + target = self._get_target(avatar_name) try: - t = self._get_target(avatar_name) - if t is not None: - rel_ids.append(t.id) + if target is not None: + rel_ids.append(target.id) except Exception: pass result_event = Event(self.world.month_stamp, result_text, related_avatars=rel_ids, is_major=True) # 生成战斗小故事 - target = self._get_target(avatar_name) start_text = self._start_event_content if hasattr(self, '_start_event_content') else result_event.content # 战斗强制双人模式,允许改变关系 story = await StoryTeller.tell_story(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT, allow_relation_changes=True) story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True) + # 如果死亡,执行死亡清理(在故事生成后,保证关系数据可用) + if is_fatal: + handle_death(self.world, loser) + return [result_event, story_event] diff --git a/src/classes/death.py b/src/classes/death.py new file mode 100644 index 0000000..ac86ba6 --- /dev/null +++ b/src/classes/death.py @@ -0,0 +1,16 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from src.classes.world import World + from src.classes.avatar import Avatar + +def handle_death(world: World, avatar: Avatar) -> None: + """ + 处理角色死亡的统一入口。 + 负责将角色从世界管理器中移除,并处理相关的清理工作(如关系解除已在 remove_avatar 中实现)。 + 注意:本函数不负责生成死亡事件文本,调用者应在调用前生成相应的 Event。 + """ + # 从管理器中移除角色(remove_avatar 内部会自动清理双向关系) + world.avatar_manager.remove_avatar(avatar.id) + diff --git a/src/sim/simulator.py b/src/sim/simulator.py index 7c3acb9..997fbcb 100644 --- a/src/sim/simulator.py +++ b/src/sim/simulator.py @@ -15,6 +15,7 @@ from src.run.log import get_logger from src.classes.fortune import try_trigger_fortune from src.classes.celestial_phenomenon import get_random_celestial_phenomenon from src.classes.long_term_objective import process_avatar_long_term_objective +from src.classes.death import handle_death class Simulator: def __init__(self, world: World): @@ -76,21 +77,30 @@ class Simulator: def _phase_resolve_death(self): """ - 结算战斗等导致的死亡以及寿终正寝,移除死亡角色,返回死亡事件集合。 + 结算死亡: + - 战斗死亡已在 Action 中结算,此处不再重复(因为已从 avatars 中移除) + - 此时剩下的 avatars 都是存活的,只需检查非战斗因素(如老死、被动掉血) """ events = [] - death_avatar_ids = [] + # 遍历时可能修改字典,使用 list() 复制 for avatar_id, avatar in list(self.world.avatar_manager.avatars.items()): + is_dead = False + reason = "" + + # 优先判定重伤(可能是被动效果导致) if avatar.hp <= 0: - death_avatar_ids.append(avatar_id) - event = Event(self.world.month_stamp, f"{avatar.name} 因重伤身亡", related_avatars=[avatar.id]) + is_dead = True + reason = f"{avatar.name} 因重伤不治身亡" + # 其次判定寿元 + elif avatar.death_by_old_age(): + is_dead = True + reason = f"{avatar.name} 老死了,时年{avatar.age.get_age()}岁" + + if is_dead: + event = Event(self.world.month_stamp, reason, related_avatars=[avatar.id]) events.append(event) - if avatar.death_by_old_age(): - death_avatar_ids.append(avatar_id) - event = Event(self.world.month_stamp, f"{avatar.name} 老死了,时年{avatar.age.get_age()}岁", related_avatars=[avatar.id]) - events.append(event) - if death_avatar_ids: - self.world.avatar_manager.remove_avatars(death_avatar_ids) + handle_death(self.world, avatar) + return events def _phase_update_age_and_birth(self):