diff --git a/src/classes/action.py b/src/classes/action.py index cffe7d1..5b70ebc 100644 --- a/src/classes/action.py +++ b/src/classes/action.py @@ -249,7 +249,7 @@ class MoveToAvatar(DefineAction, ActualActionMixin): for v in self.world.avatar_manager.avatars.values(): if v.name == avatar_name: return v - raise ValueError(f"未找到名为 {avatar_name} 的角色") + return None def _execute(self, avatar_name: str) -> None: target = self._get_target(avatar_name) @@ -265,6 +265,9 @@ class MoveToAvatar(DefineAction, ActualActionMixin): Move(self.avatar, self.world).execute(delta_x, delta_y) def can_start(self, avatar_name: str|None = None) -> bool: + target = self._get_target(avatar_name) + if target is None: + return False return True def start(self, avatar_name: str) -> Event: @@ -837,7 +840,7 @@ class Battle(DefineAction, ActualActionMixin): if isinstance(res, tuple) and len(res) == 2: winner, loser = res return [Event(self.world.month_stamp, f"{winner} 战胜了 {loser}")] - raise ValueError(f"Battle finish error: {res}") + return [] @long_action(step_month=3) diff --git a/src/classes/avatar.py b/src/classes/avatar.py index faf7740..efe703f 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -75,6 +75,8 @@ class Avatar: mp: MP = field(default_factory=lambda: MP(0, 0)) # 将在__post_init__中初始化 relations: dict["Avatar", Relation] = field(default_factory=dict) alignment: Alignment = field(default_factory=lambda: random.choice(list(Alignment))) + # 当月/当步新设动作标记:在 commit_next_plan 设为 True,首次 tick_action 后清为 False + _new_action_set_this_step: bool = False def __post_init__(self): """ @@ -165,6 +167,8 @@ class Avatar: # 启动 start_event = action.start(**plan.params) self.current_action = ActionInstance(action=action, params=plan.params, status="running") + # 标记为“本轮新设动作”,用于本月补充执行 + self._new_action_set_this_step = True return start_event return None @@ -205,6 +209,8 @@ class Avatar: for e in mid_events: self._pending_events.append(e) events, self._pending_events = self._pending_events, [] + # 本轮已执行过,清除“新设动作”标记 + self._new_action_set_this_step = False return events def update_cultivation(self, new_level: int): diff --git a/src/classes/mutual_action.py b/src/classes/mutual_action.py index d08fafb..db7021d 100644 --- a/src/classes/mutual_action.py +++ b/src/classes/mutual_action.py @@ -65,6 +65,17 @@ class MutualAction(DefineAction, LLMAction): """ 将反馈决定落地为目标角色的立即动作(清空后加载单步动作链)。 """ + # 若当前已是同类同参动作,直接跳过,避免重复“发起战斗”等事件刷屏 + try: + cur = target_avatar.current_action + if cur is not None: + cur_name = getattr(cur.action, "__class__", type(cur.action)).__name__ + if cur_name == action_name: + # 简单判断参数等价(键值相等) + if getattr(cur, "params", {}) == dict(action_params): + return + except Exception: + pass # 抢占:清空后续计划并中断其当前动作 self._preempt_avatar(target_avatar) # 先加载为计划 diff --git a/src/sim/simulator.py b/src/sim/simulator.py index 17ed8e9..1cb68e2 100644 --- a/src/sim/simulator.py +++ b/src/sim/simulator.py @@ -50,11 +50,27 @@ class Simulator: if start_event is not None and not is_null_event(start_event): events.append(start_event) - # 执行阶段:推进所有有当前动作的角色 - for avatar_id, avatar in self.world.avatar_manager.avatars.items(): - new_events = await avatar.tick_action() - if new_events: - events.extend(new_events) + # 执行阶段:推进当前动作,支持同月链式抢占即时结算 + # 采用最多3轮的小循环: + # - 每轮遍历现有角色执行一次 tick_action + # - 若本轮有角色在遍历过程中被抢占并新设了动作(标记 _new_action_set_this_step=True),下一轮继续执行 + # - 最多 3 轮以防极端互相抢占导致长链 + MAX_LOCAL_ROUNDS = 3 + for _ in range(MAX_LOCAL_ROUNDS): + new_action_happened = False + for avatar_id, avatar in list(self.world.avatar_manager.avatars.items()): + # 本轮执行前若标记为新设,则清理,执行后由 Avatar 再统一清除 + if getattr(avatar, "_new_action_set_this_step", False): + new_action_happened = True + new_events = await avatar.tick_action() + if new_events: + events.extend(new_events) + # 若在本次执行后产生了新的动作(被别人抢占设立),则标志位会在 commit_next_plan 时被置 True + if getattr(avatar, "_new_action_set_this_step", False): + new_action_happened = True + # 若本轮未检测到新动作产生,则结束本地循环 + if not new_action_happened: + break # 结算战斗等导致的死亡逻辑 for avatar_id, avatar in list(self.world.avatar_manager.avatars.items()): diff --git a/static/game_configs/persona.csv b/static/game_configs/persona.csv index 8c432ce..7ad232d 100644 --- a/static/game_configs/persona.csv +++ b/static/game_configs/persona.csv @@ -1,3 +1,3 @@ id,name,exclusion_ids,prompt ,,和本persona互斥的persona的id,输入给LLM的prompt -12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。 \ No newline at end of file +12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你要立刻复仇。 \ No newline at end of file