refactor can_start logic of all actions
This commit is contained in:
@@ -68,7 +68,7 @@ class Assassinate(InstantAction, TargetingMixin):
|
||||
|
||||
self._last_result = (winner, loser, loser_damage, winner_damage)
|
||||
|
||||
def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, avatar_name: str) -> tuple[bool, str]:
|
||||
# 注意:cooldown_action 装饰器会覆盖这个方法并在调用此方法前检查 CD
|
||||
_, ok, reason = self.validate_target_avatar(avatar_name)
|
||||
return ok, reason
|
||||
|
||||
@@ -41,7 +41,7 @@ class Attack(InstantAction, TargetingMixin):
|
||||
|
||||
self._last_result = (winner, loser, loser_damage, winner_damage)
|
||||
|
||||
def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, avatar_name: str) -> tuple[bool, str]:
|
||||
_, ok, reason = self.validate_target_avatar(avatar_name)
|
||||
return ok, reason
|
||||
|
||||
|
||||
@@ -31,17 +31,11 @@ class Buy(InstantAction):
|
||||
DOABLES_REQUIREMENTS = "在城镇且金钱足够"
|
||||
PARAMS = {"target_name": "str"}
|
||||
|
||||
def can_start(self, target_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, target_name: str) -> tuple[bool, str]:
|
||||
region = self.avatar.tile.region
|
||||
if not isinstance(region, CityRegion):
|
||||
return False, "仅能在城市区域执行"
|
||||
|
||||
if target_name is None:
|
||||
# 用于动作空间检查
|
||||
# 理论上只要有钱就可以买东西,这里简单判定金钱>0
|
||||
ok = self.avatar.magic_stone > 0
|
||||
return (ok, "" if ok else "身无分文")
|
||||
|
||||
obj, obj_type, display_name = resolve_goods_by_name(target_name)
|
||||
if obj_type == "unknown":
|
||||
return False, f"未知物品: {target_name}"
|
||||
|
||||
@@ -50,7 +50,7 @@ class Cast(TimedAction):
|
||||
count += qty
|
||||
return count
|
||||
|
||||
def can_start(self, target_realm: str = "") -> tuple[bool, str]:
|
||||
def can_start(self, target_realm: str) -> tuple[bool, str]:
|
||||
if not target_realm:
|
||||
return False, "未指定目标境界"
|
||||
|
||||
@@ -67,7 +67,7 @@ class Cast(TimedAction):
|
||||
|
||||
return True, ""
|
||||
|
||||
def start(self, target_realm: str = "") -> Event:
|
||||
def start(self, target_realm: str) -> Event:
|
||||
self.target_realm = Realm(target_realm)
|
||||
cost = self._get_cost()
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class Escape(InstantAction):
|
||||
if start_event is not None:
|
||||
EventHelper.push_pair(start_event, initiator=self.avatar, target=target, to_sidebar_once=True)
|
||||
|
||||
def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, avatar_name: str) -> tuple[bool, str]:
|
||||
return True, ""
|
||||
|
||||
def start(self, avatar_name: str) -> Event:
|
||||
|
||||
@@ -47,7 +47,7 @@ class MoveAwayFromAvatar(TimedAction):
|
||||
dx, dy = clamp_manhattan_with_diagonal_priority(raw_dx, raw_dy, step)
|
||||
Move(self.avatar, self.world).execute(dx, dy)
|
||||
|
||||
def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, avatar_name: str) -> tuple[bool, str]:
|
||||
return True, ""
|
||||
|
||||
def start(self, avatar_name: str) -> Event:
|
||||
|
||||
@@ -34,9 +34,7 @@ class MoveAwayFromRegion(InstantAction):
|
||||
dx, dy = clamp_manhattan_with_diagonal_priority(away_dx, away_dy, step)
|
||||
Move(self.avatar, self.world).execute(dx, dy)
|
||||
|
||||
def can_start(self, region: str | None = None) -> tuple[bool, str]:
|
||||
if region is None:
|
||||
return True, ""
|
||||
def can_start(self, region: str) -> tuple[bool, str]:
|
||||
try:
|
||||
resolve_region(self.world, region)
|
||||
return True, ""
|
||||
|
||||
@@ -42,7 +42,7 @@ class MoveToAvatar(DefineAction, ActualActionMixin):
|
||||
dx, dy = clamp_manhattan_with_diagonal_priority(raw_dx, raw_dy, step)
|
||||
Move(self.avatar, self.world).execute(dx, dy)
|
||||
|
||||
def can_start(self, avatar_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, avatar_name: str) -> tuple[bool, str]:
|
||||
return True, ""
|
||||
|
||||
def start(self, avatar_name: str) -> Event:
|
||||
|
||||
@@ -73,9 +73,7 @@ class MoveToDirection(DefineAction, ActualActionMixin):
|
||||
self.start_monthstamp = None
|
||||
self.direction = None
|
||||
|
||||
def can_start(self, direction: str | None = None) -> tuple[bool, str]:
|
||||
if not direction:
|
||||
return False, "缺少方向参数"
|
||||
def can_start(self, direction: str) -> tuple[bool, str]:
|
||||
if not Direction.is_valid(direction):
|
||||
return False, f"无效的方向: {direction}"
|
||||
return True, ""
|
||||
|
||||
@@ -57,9 +57,7 @@ class MoveToRegion(DefineAction, ActualActionMixin):
|
||||
dx, dy = clamp_manhattan_with_diagonal_priority(raw_dx, raw_dy, step)
|
||||
Move(self.avatar, self.world).execute(dx, dy)
|
||||
|
||||
def can_start(self, region: Region | str | None = None) -> tuple[bool, str]:
|
||||
if region is None:
|
||||
return False, "缺少参数 region"
|
||||
def can_start(self, region: Region | str) -> tuple[bool, str]:
|
||||
try:
|
||||
r = resolve_region(self.world, region)
|
||||
|
||||
|
||||
@@ -23,18 +23,10 @@ class Sell(InstantAction):
|
||||
DOABLES_REQUIREMENTS = "在城镇且持有可出售物品/装备"
|
||||
PARAMS = {"target_name": "str"}
|
||||
|
||||
def can_start(self, target_name: str | None = None) -> tuple[bool, str]:
|
||||
def can_start(self, target_name: str) -> tuple[bool, str]:
|
||||
region = self.avatar.tile.region
|
||||
if not isinstance(region, CityRegion):
|
||||
return False, "仅能在城市区域执行"
|
||||
|
||||
if target_name is None:
|
||||
# 用于动作空间:只要有任何可卖东西即可
|
||||
has_items = bool(self.avatar.items)
|
||||
has_weapon = self.avatar.weapon is not None
|
||||
has_auxiliary = self.avatar.auxiliary is not None
|
||||
ok = has_items or has_weapon or has_auxiliary
|
||||
return (ok, "" if ok else "背包为空且无装备,无可出售物品")
|
||||
|
||||
# 使用通用解析逻辑获取物品原型和类型
|
||||
obj, obj_type, _ = resolve_goods_by_name(target_name)
|
||||
|
||||
@@ -47,11 +47,7 @@ class SwitchWeapon(InstantAction):
|
||||
# 切换兵器(使用 Avatar 的 change_weapon 方法)
|
||||
self.avatar.change_weapon(common_weapon)
|
||||
|
||||
def can_start(self, weapon_type_name: str | None = None) -> tuple[bool, str]:
|
||||
if weapon_type_name is None:
|
||||
# AI调用:总是可以切换兵器
|
||||
return True, ""
|
||||
|
||||
def can_start(self, weapon_type_name: str) -> tuple[bool, str]:
|
||||
# 处理卸下兵器的情况
|
||||
if weapon_type_name in ["无", "None", "none", ""]:
|
||||
if self.avatar.weapon is None:
|
||||
|
||||
@@ -136,7 +136,7 @@ class MutualAction(DefineAction, LLMAction, ActualActionMixin, TargetingMixin):
|
||||
pass
|
||||
|
||||
# 实现 ActualActionMixin 接口
|
||||
def can_start(self, target_avatar: "Avatar|str|None" = None) -> tuple[bool, str]:
|
||||
def can_start(self, target_avatar: "Avatar|str") -> tuple[bool, str]:
|
||||
"""
|
||||
检查互动动作能否启动:目标需在发起者的交互范围内。
|
||||
子类通过实现 _can_start 来添加额外检查。
|
||||
@@ -144,8 +144,6 @@ class MutualAction(DefineAction, LLMAction, ActualActionMixin, TargetingMixin):
|
||||
注意:此方法未使用 TargetingMixin.validate_target_avatar(),
|
||||
因为需要额外检查 target == self.avatar 和调用子类的 _can_start()。
|
||||
"""
|
||||
if target_avatar is None:
|
||||
return False, "缺少参数 target_avatar"
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
if target is None:
|
||||
return False, "目标不存在"
|
||||
|
||||
Reference in New Issue
Block a user