fix bug
This commit is contained in:
@@ -16,7 +16,7 @@ from src.classes.action import (
|
||||
)
|
||||
from src.classes.mutual_action import (
|
||||
DriveAway,
|
||||
AttackInteract,
|
||||
Attack,
|
||||
MoveAwayFromAvatar,
|
||||
MoveAwayFromRegion,
|
||||
)
|
||||
@@ -35,7 +35,7 @@ ALL_ACTION_CLASSES = [
|
||||
Sold,
|
||||
# 互动相关动作(实际执行的反馈动作也纳入)
|
||||
DriveAway,
|
||||
AttackInteract,
|
||||
Attack,
|
||||
MoveAwayFromAvatar,
|
||||
MoveAwayFromRegion,
|
||||
]
|
||||
@@ -49,7 +49,8 @@ ALL_ACTUAL_ACTION_CLASSES = [
|
||||
Hunt,
|
||||
Harvest,
|
||||
Sold,
|
||||
# 互动类/反馈类作为即时落地,不进入可选择动作空间
|
||||
DriveAway,
|
||||
Attack,
|
||||
]
|
||||
|
||||
ALL_ACTION_NAMES = [action.__name__ for action in ALL_ACTION_CLASSES]
|
||||
|
||||
@@ -58,7 +58,8 @@ def get_escape_success_rate(attacker: "Avatar", defender: "Avatar") -> float:
|
||||
|
||||
def get_damage(winner: "Avatar", loser: "Avatar") -> int:
|
||||
"""
|
||||
根据胜负双方境界差距估算伤害:基础30,差一大境界+10,上限80。
|
||||
根据胜负双方境界差距估算伤害:基础100,差一大境界+100,上限500。
|
||||
"""
|
||||
gap = max(0, _realm_order(winner.cultivation_progress.realm) - _realm_order(loser.cultivation_progress.realm))
|
||||
return min(80, 30 + 10 * gap)
|
||||
# return min(500, 100 + 100 * gap)
|
||||
return 500
|
||||
@@ -3,17 +3,20 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src.classes.action import DefineAction, ActualActionMixin, LLMAction
|
||||
from src.classes.action import DefineAction, ActualActionMixin, LLMAction, StepStatus
|
||||
from src.classes.battle import get_escape_success_rate
|
||||
from src.classes.tile import get_avatar_distance
|
||||
import random
|
||||
from src.classes.event import Event
|
||||
from src.utils.llm import get_prompt_and_call_llm
|
||||
from src.utils.config import CONFIG
|
||||
from src.classes.action import long_action
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
|
||||
|
||||
# 默认所有的互动都是1个月时间
|
||||
@long_action(step_month=1)
|
||||
class MutualAction(DefineAction, LLMAction):
|
||||
"""
|
||||
互动动作:A 对 B 发起动作,B 可以给出反馈(由 LLM 决策)。
|
||||
@@ -80,17 +83,27 @@ class MutualAction(DefineAction, LLMAction):
|
||||
# 默认不额外记录,由事件系统承担
|
||||
return
|
||||
|
||||
def _execute(self, target_avatar: "Avatar|str") -> None:
|
||||
# 允许传入名字字符串
|
||||
def _get_target_avatar(self, target_avatar: "Avatar|str") -> "Avatar|None":
|
||||
"""
|
||||
获取目标角色,支持传入角色对象或名字字符串
|
||||
|
||||
Returns:
|
||||
目标角色,如果找不到返回 None
|
||||
"""
|
||||
if isinstance(target_avatar, str):
|
||||
name = target_avatar
|
||||
target_avatar = None
|
||||
for v in self.world.avatar_manager.avatars.values():
|
||||
if v.name == name:
|
||||
target_avatar = v
|
||||
break
|
||||
if target_avatar is None:
|
||||
return
|
||||
return v
|
||||
return None
|
||||
return target_avatar
|
||||
|
||||
def _execute(self, target_avatar: "Avatar|str") -> None:
|
||||
# 允许传入名字字符串
|
||||
target_avatar = self._get_target_avatar(target_avatar)
|
||||
if target_avatar is None:
|
||||
return
|
||||
|
||||
infos = self._build_prompt_infos(target_avatar)
|
||||
res = self._call_llm_feedback(infos)
|
||||
# LLM 只返回 {avatar_name_2: {thinking, feedback}}
|
||||
@@ -100,10 +113,6 @@ class MutualAction(DefineAction, LLMAction):
|
||||
|
||||
# 挂到目标的thinking上(面向UI/日志),并执行反馈落地
|
||||
target_avatar.thinking = thinking
|
||||
# 发起事件(进入侧边栏与双方历史)
|
||||
start_event = Event(self.world.month_stamp, f"{self.avatar.name} 对 {target_avatar.name} 发起 {getattr(self, 'ACTION_NAME', self.name)}")
|
||||
self.avatar.add_event(start_event)
|
||||
target_avatar.add_event(start_event)
|
||||
# 1) 先清空目标后续计划(仅清空队列,不动当前动作)
|
||||
if hasattr(target_avatar, "clear_plans"):
|
||||
target_avatar.clear_plans()
|
||||
@@ -122,9 +131,45 @@ class MutualAction(DefineAction, LLMAction):
|
||||
# 4) 记录历史(文本记录)
|
||||
self._apply_feedback(target_avatar, feedback)
|
||||
|
||||
# 互动行为一般是一次性的即时动作
|
||||
# 互动类行为仍保持一次性效果,由自身执行时发事件
|
||||
# 不接入新的调度器接口
|
||||
# 实现 ActualActionMixin 接口
|
||||
def can_start(self, target_avatar: "Avatar|str|None" = None) -> bool:
|
||||
"""
|
||||
检查互动动作能否启动:两个角色距离必须小于等于2
|
||||
"""
|
||||
if target_avatar is None:
|
||||
return False
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
if target is None:
|
||||
return False
|
||||
distance = get_avatar_distance(self.avatar, target)
|
||||
return distance <= 2
|
||||
|
||||
def start(self, target_avatar: "Avatar|str") -> Event:
|
||||
"""
|
||||
启动互动动作,返回开始事件
|
||||
"""
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
target_name = target.name if target is not None else str(target_avatar)
|
||||
action_name = getattr(self, 'ACTION_NAME', self.name)
|
||||
event = Event(self.world.month_stamp, f"{self.avatar.name} 对 {target_name} 发起 {action_name}")
|
||||
# 将事件添加到双方历史
|
||||
self.avatar.add_event(event)
|
||||
if target is not None:
|
||||
target.add_event(event)
|
||||
return event
|
||||
|
||||
def step(self, target_avatar: "Avatar|str") -> tuple[StepStatus, list[Event]]:
|
||||
"""
|
||||
执行互动动作,互动动作是即时完成的
|
||||
"""
|
||||
self.execute(target_avatar=target_avatar)
|
||||
return StepStatus.COMPLETED, []
|
||||
|
||||
def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
"""
|
||||
完成互动动作,事件已在 step 中处理,无需额外事件
|
||||
"""
|
||||
return []
|
||||
|
||||
|
||||
class DriveAway(MutualAction, ActualActionMixin):
|
||||
@@ -144,8 +189,8 @@ class DriveAway(MutualAction, ActualActionMixin):
|
||||
params = {"avatar_name": self.avatar.name}
|
||||
self._set_target_immediate_action(target_avatar, fb, params)
|
||||
|
||||
class AttackInteract(MutualAction, ActualActionMixin):
|
||||
"""攻击互动:被攻击者的反馈。"""
|
||||
class Attack(MutualAction, ActualActionMixin):
|
||||
"""攻击另一个NPC"""
|
||||
ACTION_NAME = "攻击"
|
||||
COMMENT = "对目标进行攻击。"
|
||||
DOABLES_REQUIREMENTS = "与目标处于同一区域"
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.region import Region
|
||||
from src.classes.avatar import Avatar
|
||||
|
||||
class TileType(Enum):
|
||||
PLAIN = "plain" # 平原
|
||||
@@ -32,3 +33,17 @@ class Tile():
|
||||
y: int
|
||||
region: 'Region' = None # 可以是一个region的一部分,也可以不属于任何region
|
||||
|
||||
|
||||
def get_avatar_distance(avatar1: 'Avatar', avatar2: 'Avatar') -> int:
|
||||
"""
|
||||
计算两个 Avatar 之间的汉明距离(曼哈顿距离)
|
||||
|
||||
Args:
|
||||
avatar1: 第一个角色
|
||||
avatar2: 第二个角色
|
||||
|
||||
Returns:
|
||||
两个角色之间的距离
|
||||
"""
|
||||
return abs(avatar1.pos_x - avatar2.pos_x) + abs(avatar1.pos_y - avatar2.pos_y)
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ df:
|
||||
ids_separator: ";"
|
||||
|
||||
avatar:
|
||||
persona_num: 3
|
||||
persona_num: 1
|
||||
@@ -1,21 +1,3 @@
|
||||
id,name,exclusion_ids,prompt
|
||||
,,和本persona互斥的persona的id,输入给LLM的prompt
|
||||
1,理性,2;5,你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动。
|
||||
2,无常,1;9,你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。
|
||||
3,怠惰,4,你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。
|
||||
4,冒险,3;10,你是一个冒险的人,你总是会冒险,喜欢刺激,总想放手一搏。
|
||||
5,随性,1,你是一个随性的人,你总是会随机应变,性子到哪里了就是哪里,没有一定之规。
|
||||
6,贪财,,你是一个贪财的人,你对灵石和财富有着强烈的渴望。
|
||||
7,采药,,你是一个热爱采集的人,喜欢在山林中寻找各种奇花异草和灵药,对植物有着敏锐的直觉和深厚的兴趣。你认为大自然的恩赐需要用心去发现和珍惜。
|
||||
8,猎者,,你是一个热爱狩猎的人,享受在野外追踪猎物的刺激感,对各种动物的习性了如指掌。你相信通过狩猎能够磨练自己的意志和技能,获得更强大的力量。
|
||||
9,沉思,2,你是一个沉思的人,你总是会深思熟虑,思考问题比较有哲理。
|
||||
10,惜命,4,你是一个惜命的人,你总是会珍惜自己的生命,不会轻易冒险。
|
||||
11,友爱,13;14;15;12,你是一个友爱的人,你重视同伴与和谐,乐于助人,倾向通过协作与沟通化解矛盾。
|
||||
12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。
|
||||
13,孤僻,11,你是一个孤僻的人,你喜欢独处,避免与人深交,更信赖自己的判断与行动。
|
||||
14,淡漠,11;12;15,你是一个淡漠的人,你情感克制,对外界冷静疏离,不轻易被他人或事件影响。
|
||||
15,好斗,11;14;10;17,你是一个好斗的人,你直面冲突,偏好以力量与对抗解决问题,越挫越勇。
|
||||
16,鲁莽,1;9;10,你是一个鲁莽的人,你行事冲动、少考虑后果,常凭直觉立刻行动。
|
||||
17,胆小,4;15;12,你是一个胆小的人,你谨小慎微,容易畏惧风险,倾向回避正面冲突。
|
||||
18,霸道,11;17,你是一个霸道的人,你行事强势,不讲道理,习惯以自己的利益为先,倾向多吃多占、压人一步,对他人的反对不以为意。
|
||||
19,修行痴迷,2;3;5,你是一个对修行极度痴迷的人,你将绝大多数时间用于修炼,厌恶与修行无关的社交与享乐。
|
||||
12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你要立刻复仇。
|
||||
|
||||
|
Reference in New Issue
Block a user