refactor conversation
This commit is contained in:
@@ -18,8 +18,9 @@ if TYPE_CHECKING:
|
||||
|
||||
@cooldown_action
|
||||
class Assassinate(InstantAction):
|
||||
COMMENT = "暗杀目标,失败则变为战斗"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行;需要冷却"
|
||||
ACTION_NAME = "暗杀"
|
||||
DESC = "暗杀目标,失败则变为战斗"
|
||||
DOABLES_REQUIREMENTS = "无限制;需要冷却"
|
||||
PARAMS = {"avatar_name": "AvatarName"}
|
||||
ACTION_CD_MONTHS = 12
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ from src.classes.death_reason import DeathReason
|
||||
from src.classes.kill_and_grab import kill_and_grab
|
||||
|
||||
class Attack(InstantAction):
|
||||
COMMENT = "攻击目标,进行对战"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "发起战斗"
|
||||
DESC = "攻击目标,进行对战"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"avatar_name": "AvatarName"}
|
||||
# 提供用于故事生成的提示词:不出现血量/伤害等数值描述
|
||||
STORY_PROMPT: str | None = (
|
||||
|
||||
@@ -25,7 +25,8 @@ class Breakthrough(TimedAction):
|
||||
失败时按 `CultivationProgress.get_breakthrough_fail_reduce_lifespan()` 减少寿元(年)。
|
||||
"""
|
||||
|
||||
COMMENT = "尝试突破境界(成功增加寿元上限,失败折损寿元上限;境界越高,成功率越低。)"
|
||||
ACTION_NAME = "突破"
|
||||
DESC = "尝试突破境界(成功增加寿元上限,失败折损寿元上限;境界越高,成功率越低。)"
|
||||
DOABLES_REQUIREMENTS = "角色处于瓶颈时;不能连续执行"
|
||||
PARAMS = {}
|
||||
# 冷却:突破应当有CD,避免连刷
|
||||
|
||||
@@ -23,7 +23,8 @@ class Catch(TimedAction):
|
||||
- 按动物境界映射成功率尝试捕捉,成功则成为灵兽(覆盖旧灵兽)。
|
||||
"""
|
||||
|
||||
COMMENT = "尝试驯服一只灵兽,成为自身灵兽。只能有一只灵兽,但是可以高级替换低级。"
|
||||
ACTION_NAME = "御兽"
|
||||
DESC = "尝试驯服一只灵兽,成为自身灵兽。只能有一只灵兽,但是可以高级替换低级。"
|
||||
DOABLES_REQUIREMENTS = "仅百兽宗;在有动物的普通区域;目标动物境界不高于角色"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ def cooldown_action(cls: type) -> type:
|
||||
- 仅当类定义了 ACTION_CD_MONTHS 且 >0 时生效
|
||||
- 在 can_start 前置检查冷却;在 finish 后记录冷却开始月戳
|
||||
- 冷却记录存放于 avatar._action_cd_last_months[ClassName]
|
||||
- 同时在 COMMENT 中追加“(冷却:X月)”便于 UI 显示
|
||||
- 同时在 DESC 中追加“(冷却:X月)”便于 UI 显示
|
||||
"""
|
||||
|
||||
cd = int(getattr(cls, "ACTION_CD_MONTHS", 0) or 0)
|
||||
|
||||
@@ -11,7 +11,8 @@ class Cultivate(TimedAction):
|
||||
修炼动作,可以增加修仙进度。
|
||||
"""
|
||||
|
||||
COMMENT = "修炼,增进修为。在修炼区域(洞府)且灵气匹配时效果最佳,否则效果很差。"
|
||||
ACTION_NAME = "修炼"
|
||||
DESC = "修炼,增进修为。在修炼区域(洞府)且灵气匹配时效果最佳,否则效果很差。"
|
||||
DOABLES_REQUIREMENTS = "角色未到瓶颈;若在洞府区域,则该洞府需无主或归自己所有。"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ class DevourMortals(TimedAction):
|
||||
吞噬凡人:需持有万魂幡,吞噬魂魄可较多增加战力。
|
||||
"""
|
||||
|
||||
COMMENT = "吞噬凡人,较多增加战力"
|
||||
ACTION_NAME = "吞噬凡人"
|
||||
DESC = "吞噬凡人,较多增加战力"
|
||||
DOABLES_REQUIREMENTS = "持有万魂幡"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ class Escape(InstantAction):
|
||||
失败:抢占并进入 Attack。
|
||||
"""
|
||||
|
||||
COMMENT = "逃离对方(基于成功率判定)"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "逃离"
|
||||
DESC = "逃离对方(基于成功率判定)"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"avatar_name": "AvatarName"}
|
||||
|
||||
def _find_avatar_by_name(self, name: str) -> "Avatar|None":
|
||||
|
||||
@@ -12,7 +12,8 @@ class Harvest(TimedAction):
|
||||
可以获得植物对应的物品
|
||||
"""
|
||||
|
||||
COMMENT = "在当前区域采集植物,获取植物材料"
|
||||
ACTION_NAME = "采集"
|
||||
DESC = "在当前区域采集植物,获取植物材料"
|
||||
DOABLES_REQUIREMENTS = "在有植物的普通区域,且avatar的境界必须大于等于植物的境界"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ class HelpMortals(TimedAction):
|
||||
仅正阵营可执行。
|
||||
"""
|
||||
|
||||
COMMENT = "在城镇帮助凡人,消耗少量灵石"
|
||||
ACTION_NAME = "帮助凡人"
|
||||
DESC = "在城镇帮助凡人,消耗少量灵石"
|
||||
DOABLES_REQUIREMENTS = "仅限城市区域,且角色阵营为‘正’,并且灵石足够"
|
||||
PARAMS = {}
|
||||
COST = 10
|
||||
|
||||
@@ -12,7 +12,8 @@ class Hunt(TimedAction):
|
||||
可以获得动物对应的物品
|
||||
"""
|
||||
|
||||
COMMENT = "在当前区域狩猎动物,获取动物材料"
|
||||
ACTION_NAME = "狩猎"
|
||||
DESC = "在当前区域狩猎动物,获取动物材料"
|
||||
DOABLES_REQUIREMENTS = "在有动物的普通区域,且avatar的境界必须大于等于动物的境界"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ class Move(DefineAction, ChunkActionMixin):
|
||||
最基础的移动动作,在tile之间进行切换。
|
||||
"""
|
||||
|
||||
COMMENT = "移动到某个相对位置"
|
||||
ACTION_NAME = "移动"
|
||||
DESC = "移动到某个相对位置"
|
||||
PARAMS = {"delta_x": "int", "delta_y": "int"}
|
||||
|
||||
def _execute(self, delta_x: int, delta_y: int) -> None:
|
||||
|
||||
@@ -17,8 +17,9 @@ class MoveAwayFromAvatar(TimedAction):
|
||||
- 任何时候都可以启动
|
||||
"""
|
||||
|
||||
COMMENT = "持续远离指定角色"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "远离角色"
|
||||
DESC = "持续远离指定角色"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"avatar_name": "AvatarName"}
|
||||
|
||||
def _find_avatar_by_name(self, name: str) -> "Avatar | None":
|
||||
|
||||
@@ -8,8 +8,9 @@ from src.utils.distance import euclidean_distance
|
||||
|
||||
|
||||
class MoveAwayFromRegion(InstantAction):
|
||||
COMMENT = "离开指定区域"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "离开区域"
|
||||
DESC = "离开指定区域"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"region": "RegionName"}
|
||||
|
||||
def _execute(self, region: str) -> None:
|
||||
|
||||
@@ -13,8 +13,9 @@ class MoveToAvatar(DefineAction, ActualActionMixin):
|
||||
朝另一个角色当前位置移动。
|
||||
"""
|
||||
|
||||
COMMENT = "移动到某个角色所在位置"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "移动到角色"
|
||||
DESC = "移动到某个角色所在位置"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"avatar_name": "str"}
|
||||
|
||||
def _get_target(self, avatar_name: str):
|
||||
|
||||
@@ -57,8 +57,9 @@ class MoveToDirection(DefineAction, ActualActionMixin):
|
||||
向某个方向移动探索(固定时长6个月)
|
||||
"""
|
||||
|
||||
COMMENT = "向某个方向探索未知区域"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "移动探索"
|
||||
DESC = "向某个方向探索未知区域"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"direction": "direction (North/South/East/West)"}
|
||||
IS_MAJOR = False
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ class MoveToRegion(DefineAction, ActualActionMixin):
|
||||
移动到某个region
|
||||
"""
|
||||
|
||||
COMMENT = "移动到某个区域"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "移动到区域"
|
||||
DESC = "移动到某个区域"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {"region": "region_name"}
|
||||
|
||||
def __init__(self, avatar, world):
|
||||
|
||||
@@ -10,8 +10,9 @@ class NurtureWeapon(TimedAction):
|
||||
温养兵器:花时间温养兵器,可以较多增加熟练度
|
||||
"""
|
||||
|
||||
COMMENT = "温养兵器,增加兵器熟练度"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "温养兵器"
|
||||
DESC = "温养兵器,增加兵器熟练度"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {}
|
||||
|
||||
duration_months = 3
|
||||
|
||||
@@ -9,8 +9,9 @@ class Play(TimedAction):
|
||||
消遣动作,持续半年时间
|
||||
"""
|
||||
|
||||
COMMENT = "消遣,放松身心"
|
||||
DOABLES_REQUIREMENTS = "任何时候都可以执行"
|
||||
ACTION_NAME = "消遣"
|
||||
DESC = "消遣,放松身心"
|
||||
DOABLES_REQUIREMENTS = "无限制"
|
||||
PARAMS = {}
|
||||
|
||||
duration_months = 6
|
||||
|
||||
@@ -12,7 +12,8 @@ class PlunderMortals(TimedAction):
|
||||
仅邪阵营可执行。
|
||||
"""
|
||||
|
||||
COMMENT = "在城镇搜刮凡人,获取少量灵石"
|
||||
ACTION_NAME = "搜刮凡人"
|
||||
DESC = "在城镇搜刮凡人,获取少量灵石"
|
||||
DOABLES_REQUIREMENTS = "仅限城市区域,且角色阵营为‘邪’"
|
||||
PARAMS = {}
|
||||
GAIN = 20
|
||||
|
||||
@@ -11,7 +11,8 @@ class SelfHeal(TimedAction):
|
||||
单月动作,执行后HP直接回满。
|
||||
"""
|
||||
|
||||
COMMENT = "在宗门总部静养疗伤,回满HP"
|
||||
ACTION_NAME = "疗伤"
|
||||
DESC = "在宗门总部静养疗伤,回满HP"
|
||||
DOABLES_REQUIREMENTS = "自己是宗门弟子,且位于本宗门总部区域,且当前HP未满"
|
||||
PARAMS = {}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ class SellItems(InstantAction):
|
||||
收益为 item_price * item_num,动作耗时1个月。
|
||||
"""
|
||||
|
||||
COMMENT = "在城镇出售持有的某类物品的全部"
|
||||
ACTION_NAME = "出售物品"
|
||||
DESC = "在城镇出售持有的某类物品的全部"
|
||||
DOABLES_REQUIREMENTS = "在城镇且背包非空"
|
||||
PARAMS = {"item_name": "str"}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ class SwitchWeapon(InstantAction):
|
||||
熟练度重置为0。
|
||||
"""
|
||||
|
||||
COMMENT = "切换到指定类型的凡品兵器,或卸下兵器。当前兵器会丧失,熟练度会重置为0。适用于想要更换兵器类型或从头修炼新兵器的情况。"
|
||||
ACTION_NAME = "切换兵器"
|
||||
DESC = "切换到指定类型的凡品兵器,或卸下兵器。当前兵器会丧失,熟练度会重置为0。适用于想要更换兵器类型或从头修炼新兵器的情况。"
|
||||
DOABLES_REQUIREMENTS = "无前置条件"
|
||||
PARAMS = {"weapon_type_name": "str"}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ ALL_ACTUAL_ACTION_NAMES = [cls.__name__ for cls in ALL_ACTUAL_ACTION_CLASSES]
|
||||
|
||||
def _build_action_info(action):
|
||||
info = {
|
||||
"comment": action.COMMENT,
|
||||
"requirements": action.DOABLES_REQUIREMENTS,
|
||||
"desc": action.DESC,
|
||||
"require": action.DOABLES_REQUIREMENTS,
|
||||
}
|
||||
if action.PARAMS:
|
||||
info["params"] = action.PARAMS
|
||||
|
||||
@@ -156,3 +156,29 @@ class ActionMixin:
|
||||
if to_sidebar:
|
||||
self._pending_events.append(event)
|
||||
|
||||
def get_planned_actions_str(self: "Avatar") -> str:
|
||||
"""
|
||||
获取易读的计划动作列表字符串。
|
||||
"""
|
||||
if not self.planned_actions:
|
||||
return "无"
|
||||
|
||||
lines = []
|
||||
for i, plan in enumerate(self.planned_actions, 1):
|
||||
try:
|
||||
action_cls = ActionRegistry.get(plan.action_name)
|
||||
# 优先取 ACTION_NAME,否则用类名
|
||||
display_name = getattr(action_cls, "ACTION_NAME", plan.action_name)
|
||||
except Exception:
|
||||
display_name = plan.action_name
|
||||
|
||||
# 简化参数显示,只保留基本类型
|
||||
simple_params = {k: v for k, v in plan.params.items() if isinstance(v, (str, int, float, bool))}
|
||||
info = f"{i}. {display_name}"
|
||||
if simple_params:
|
||||
info += f" {simple_params}"
|
||||
|
||||
lines.append(info)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class MutualAttack(MutualAction):
|
||||
"""攻击另一个NPC"""
|
||||
|
||||
ACTION_NAME = "攻击"
|
||||
COMMENT = "对目标进行攻击。"
|
||||
DESC = "对目标进行攻击。"
|
||||
DOABLES_REQUIREMENTS = "目标在交互范围内;不能连续执行"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS = ["Escape", "Attack"]
|
||||
|
||||
@@ -26,7 +26,7 @@ class Conversation(MutualAction):
|
||||
"""
|
||||
|
||||
ACTION_NAME = "交谈"
|
||||
COMMENT = "与对方进行一段交流对话"
|
||||
DESC = "与对方进行一段交流对话"
|
||||
DOABLES_REQUIREMENTS = "目标在交互范围内"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS: list[str] = [] # Conversation 自动触发,不需要对方决策
|
||||
@@ -51,12 +51,20 @@ class Conversation(MutualAction):
|
||||
# 获取关系上下文
|
||||
possible_new_relations, possible_cancel_relations = get_relation_change_context(self.avatar, target_avatar)
|
||||
|
||||
# 获取后续计划
|
||||
p1 = self.avatar.get_planned_actions_str()
|
||||
p2 = target_avatar.get_planned_actions_str()
|
||||
planned_actions_str = {
|
||||
avatar_name_1: p1,
|
||||
avatar_name_2: p2,
|
||||
}
|
||||
return {
|
||||
"avatar_infos": avatar_infos,
|
||||
"avatar_name_1": avatar_name_1,
|
||||
"avatar_name_2": avatar_name_2,
|
||||
"possible_new_relations": possible_new_relations,
|
||||
"possible_cancel_relations": possible_cancel_relations,
|
||||
"planned_actions": planned_actions_str,
|
||||
}
|
||||
|
||||
def _can_start(self, target: "Avatar") -> tuple[bool, str]:
|
||||
|
||||
@@ -13,7 +13,7 @@ class DriveAway(MutualAction):
|
||||
"""驱赶:试图让对方离开当前区域。"""
|
||||
|
||||
ACTION_NAME = "驱赶"
|
||||
COMMENT = "以武力威慑对方离开此地。"
|
||||
DESC = "以武力威慑对方离开此地。"
|
||||
DOABLES_REQUIREMENTS = "目标在交互范围内;不能连续执行"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS = ["MoveAwayFromRegion", "Attack"]
|
||||
|
||||
@@ -26,7 +26,7 @@ class DualCultivation(MutualAction):
|
||||
"""
|
||||
|
||||
ACTION_NAME = "双修"
|
||||
COMMENT = "以情入道的双修之术,仅合欢宗弟子可发起,对象可接受或拒绝"
|
||||
DESC = "以情入道的双修之术,仅合欢宗弟子可发起,对象可接受或拒绝"
|
||||
DOABLES_REQUIREMENTS = "发起者为合欢宗;目标在交互范围内;不能连续执行"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
@@ -21,7 +21,7 @@ class GiftSpiritStone(MutualAction):
|
||||
"""
|
||||
|
||||
ACTION_NAME = "赠送灵石"
|
||||
COMMENT = "向对方赠送灵石,一次赠送100灵石"
|
||||
DESC = "向对方赠送灵石,一次赠送100灵石"
|
||||
DOABLES_REQUIREMENTS = "发起者至少有100灵石;目标在交互范围内"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
@@ -25,7 +25,7 @@ class Impart(MutualAction):
|
||||
"""
|
||||
|
||||
ACTION_NAME = "传道"
|
||||
COMMENT = "师傅向徒弟传授修炼经验,徒弟可获得大量修为"
|
||||
DESC = "师傅向徒弟传授修炼经验,徒弟可获得大量修为"
|
||||
DOABLES_REQUIREMENTS = "发起者是目标的师傅;师傅等级 > 徒弟等级 + 20;目标在交互范围内;不能连续执行"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
@@ -25,14 +25,14 @@ class MutualAction(DefineAction, LLMAction, ActualActionMixin, TargetingMixin):
|
||||
互动动作:A 对 B 发起动作,B 可以给出反馈(由 LLM 决策)。
|
||||
子类需要定义:
|
||||
- ACTION_NAME: 当前动作名(给模板展示)
|
||||
- COMMENT: 动作语义说明(给模板展示)
|
||||
- DESC: 动作语义说明(给模板展示)
|
||||
- FEEDBACK_ACTIONS: 反馈可选的 action name 列表(直接可执行)
|
||||
- PARAMS: 参数,需要包含 target_avatar
|
||||
- FEEDBACK_ACTIONS: 反馈可选的 action name 列表(直接可执行)
|
||||
"""
|
||||
|
||||
ACTION_NAME: str = "MutualAction"
|
||||
COMMENT: str = ""
|
||||
DESC: str = ""
|
||||
DOABLES_REQUIREMENTS: str = "交互范围内可互动"
|
||||
PARAMS: dict = {"target_avatar": "Avatar"}
|
||||
FEEDBACK_ACTIONS: list[str] = []
|
||||
@@ -72,14 +72,14 @@ class MutualAction(DefineAction, LLMAction, ActualActionMixin, TargetingMixin):
|
||||
}
|
||||
|
||||
feedback_actions = self.FEEDBACK_ACTIONS
|
||||
comment = self.COMMENT
|
||||
desc = self.DESC
|
||||
action_name = self.ACTION_NAME
|
||||
return {
|
||||
"avatar_infos": avatar_infos,
|
||||
"avatar_name_1": avatar_name_1,
|
||||
"avatar_name_2": avatar_name_2,
|
||||
"action_name": action_name,
|
||||
"action_info": comment,
|
||||
"action_info": desc,
|
||||
"feedback_actions": feedback_actions,
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class Occupy(MutualAction):
|
||||
对方拒绝则进入战斗,进攻方胜利则洞府易主。
|
||||
"""
|
||||
ACTION_NAME = "抢夺洞府"
|
||||
COMMENT = "占据或抢夺洞府"
|
||||
DESC = "占据或抢夺洞府"
|
||||
PARAMS = {"region_name": "str"}
|
||||
FEEDBACK_ACTIONS = ["Yield", "Reject"]
|
||||
FEEDBACK_LABELS = {"Yield": "让步", "Reject": "拒绝"}
|
||||
|
||||
@@ -21,7 +21,7 @@ class Spar(MutualAction):
|
||||
切磋动作:双方切磋,不造成伤害,增加武器熟练度。
|
||||
"""
|
||||
ACTION_NAME = "切磋"
|
||||
COMMENT = "与目标切磋武艺,点到为止(大幅增加武器熟练度,不造成伤害)"
|
||||
DESC = "与目标切磋武艺,点到为止(大幅增加武器熟练度,不造成伤害)"
|
||||
DOABLES_REQUIREMENTS = "交互范围内可互动;不能连续执行"
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class Talk(MutualAction):
|
||||
"""
|
||||
|
||||
ACTION_NAME = "攀谈"
|
||||
COMMENT = "向对方发起攀谈"
|
||||
DESC = "向对方发起攀谈"
|
||||
DOABLES_REQUIREMENTS = "目标在交互范围内"
|
||||
PARAMS = {"target_avatar": "AvatarName"}
|
||||
FEEDBACK_ACTIONS: list[str] = ["Talk", "Reject"]
|
||||
|
||||
Reference in New Issue
Block a user