diff --git a/src/classes/action/battle.py b/src/classes/action/battle.py index 6a8a649..e42c9e0 100644 --- a/src/classes/action/battle.py +++ b/src/classes/action/battle.py @@ -4,6 +4,7 @@ from src.classes.action import InstantAction 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 class Battle(InstantAction): @@ -16,8 +17,13 @@ class Battle(InstantAction): ) def _get_target(self, avatar_name: str): + """ + 根据名字查找目标角色;找不到返回 None。 + 会自动规范化名字(去除括号等附加信息)以提高容错性。 + """ + normalized_name = normalize_avatar_name(avatar_name) for v in self.world.avatar_manager.avatars.values(): - if v.name == avatar_name: + if v.name == normalized_name: return v return None diff --git a/src/classes/action/escape.py b/src/classes/action/escape.py index 8ddf0f8..9582124 100644 --- a/src/classes/action/escape.py +++ b/src/classes/action/escape.py @@ -4,6 +4,7 @@ from src.classes.action import InstantAction from src.classes.event import Event from src.classes.battle import get_escape_success_rate from src.classes.action.event_helper import EventHelper +from src.classes.normalize import normalize_avatar_name class Escape(InstantAction): @@ -18,8 +19,13 @@ class Escape(InstantAction): PARAMS = {"avatar_name": "AvatarName"} def _find_avatar_by_name(self, name: str) -> "Avatar|None": + """ + 根据名字查找角色;找不到返回 None。 + 会自动规范化名字(去除括号等附加信息)以提高容错性。 + """ + normalized_name = normalize_avatar_name(name) for v in self.world.avatar_manager.avatars.values(): - if v.name == name: + if v.name == normalized_name: return v return None diff --git a/src/classes/action/move_away_from_avatar.py b/src/classes/action/move_away_from_avatar.py index c243059..adab989 100644 --- a/src/classes/action/move_away_from_avatar.py +++ b/src/classes/action/move_away_from_avatar.py @@ -3,6 +3,7 @@ from __future__ import annotations from src.classes.action import TimedAction, Move from src.classes.event import Event from src.classes.action.move_helper import clamp_manhattan_with_diagonal_priority +from src.classes.normalize import normalize_avatar_name from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -21,8 +22,13 @@ class MoveAwayFromAvatar(TimedAction): PARAMS = {"avatar_name": "AvatarName"} def _find_avatar_by_name(self, name: str) -> "Avatar | None": + """ + 根据名字查找角色;找不到返回 None。 + 会自动规范化名字(去除括号等附加信息)以提高容错性。 + """ + normalized_name = normalize_avatar_name(name) for v in self.world.avatar_manager.avatars.values(): - if v.name == name: + if v.name == normalized_name: return v return None diff --git a/src/classes/action/move_to_avatar.py b/src/classes/action/move_to_avatar.py index 95de781..a04119b 100644 --- a/src/classes/action/move_to_avatar.py +++ b/src/classes/action/move_to_avatar.py @@ -5,6 +5,7 @@ from src.classes.event import Event from src.classes.action import Move from src.classes.action_runtime import ActionResult, ActionStatus from src.classes.action.move_helper import clamp_manhattan_with_diagonal_priority +from src.classes.normalize import normalize_avatar_name class MoveToAvatar(DefineAction, ActualActionMixin): @@ -19,9 +20,11 @@ class MoveToAvatar(DefineAction, ActualActionMixin): def _get_target(self, avatar_name: str): """ 根据名字查找目标角色;找不到返回 None。 + 会自动规范化名字(去除括号等附加信息)以提高容错性。 """ + normalized_name = normalize_avatar_name(avatar_name) for v in self.world.avatar_manager.avatars.values(): - if v.name == avatar_name: + if v.name == normalized_name: return v return None diff --git a/src/classes/action/sold.py b/src/classes/action/sold.py index 8ea15f2..455cf55 100644 --- a/src/classes/action/sold.py +++ b/src/classes/action/sold.py @@ -5,6 +5,7 @@ from src.classes.event import Event from src.classes.region import CityRegion from src.classes.item import items_by_name from src.classes.prices import prices +from src.classes.normalize import normalize_item_name class SellItems(InstantAction): @@ -22,8 +23,11 @@ class SellItems(InstantAction): if not isinstance(region, CityRegion): return + # 规范化物品名称(去除境界等附加信息) + normalized_name = normalize_item_name(item_name) + # 找到物品 - item = items_by_name.get(item_name) + item = items_by_name.get(normalized_name) if item is None: return @@ -51,14 +55,22 @@ class SellItems(InstantAction): # 用于动作空间:只要背包非空即可 ok = bool(self.avatar.items) return (ok, "" if ok else "背包为空,无可出售物品") - item = items_by_name.get(item_name) + + # 规范化物品名称 + normalized_name = normalize_item_name(item_name) + item = items_by_name.get(normalized_name) if item is None: return False, f"未知物品: {item_name}" ok = self.avatar.get_item_quantity(item) > 0 return (ok, "" if ok else "该物品数量为0") def start(self, item_name: str) -> Event: - return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇出售 {item_name}", related_avatars=[self.avatar.id]) + # 规范化物品名称用于显示(与执行逻辑一致) + normalized_name = normalize_item_name(item_name) + # 尝试获取标准物品名(如果存在) + item = items_by_name.get(normalized_name) + display_name = item.name if item is not None else normalized_name + return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇出售 {display_name}", related_avatars=[self.avatar.id]) # InstantAction 已实现 step 完成 diff --git a/src/classes/action/targeting_mixin.py b/src/classes/action/targeting_mixin.py index e7d26db..f3d0525 100644 --- a/src/classes/action/targeting_mixin.py +++ b/src/classes/action/targeting_mixin.py @@ -4,6 +4,7 @@ from typing import Optional, Iterable from src.classes.tile import get_avatar_distance from src.classes.observe import get_observable_avatars +from src.classes.normalize import normalize_avatar_name class TargetingMixin: @@ -13,8 +14,15 @@ class TargetingMixin: 注意:不做异常吞噬,失败路径返回 None 或 False,由调用方决策。 """ def find_avatar_by_name(self, name: str) -> "Avatar|None": + """ + 根据名字查找角色。 + 会自动规范化名字(去除括号等附加信息)以提高容错性。 + + 例如:查找 "张三(元婴)" 会自动匹配到名为 "张三" 的角色 + """ + normalized_name = normalize_avatar_name(name) for v in self.world.avatar_manager.avatars.values(): - if v.name == name: + if v.name == normalized_name: return v return None diff --git a/src/classes/avatar.py b/src/classes/avatar.py index 00ae710..fdd90ab 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -516,7 +516,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): observed: list[str] = [] if co_region_avatars: for other in co_region_avatars[:8]: - observed.append(f"{other.name}(境界:{other.cultivation_progress.get_info()})") + observed.append(f"{other.name},境界:{other.cultivation_progress.get_info()}") # 历史事件改为从全局事件管理器查询 n = CONFIG.social.event_context_num diff --git a/src/classes/normalize.py b/src/classes/normalize.py new file mode 100644 index 0000000..b77a0aa --- /dev/null +++ b/src/classes/normalize.py @@ -0,0 +1,132 @@ +""" +名称规范化工具模块 + +提供统一的名称规范化函数,用于处理各类名称中的括号和附加信息。 +适用于:角色名、地区名、物品名等。 +""" + + +def _remove_parentheses(name: str) -> str: + """ + 通用的括号移除函数:去除字符串中首个括号及其内容。 + + 支持的括号类型:() () [] 【】 「」 『』 <> 《》 + + Args: + name: 原始字符串 + + Returns: + 去除首个括号后的字符串(去除前后空格) + + Examples: + >>> _remove_parentheses("张三(元婴)") + '张三' + >>> _remove_parentheses("青云林海(千年古松(金丹))") + '青云林海' + >>> _remove_parentheses("青云鹿角 -(练气)") + '青云鹿角 -' + """ + s = str(name).strip() + brackets = [ + ("(", ")"), ("(", ")"), + ("[", "]"), ("【", "】"), + ("「", "」"), ("『", "』"), + ("<", ">"), ("《", "》") + ] + + for left, right in brackets: + idx = s.find(left) + if idx != -1: + # 找到左括号,去除从此开始到字符串末尾的内容 + s = s[:idx].strip() + break + + return s + + +def normalize_avatar_name(name: str) -> str: + """ + 规范化角色名字:去除括号及其中的附加信息(如境界)。 + + Args: + name: 原始角色名字,可能包含境界等附加信息 + + Returns: + 规范化后的角色名字 + + Examples: + >>> normalize_avatar_name("张三(元婴)") + '张三' + >>> normalize_avatar_name("张三,境界:元婴") + '张三,境界:元婴' + """ + return _remove_parentheses(name) + + +def normalize_region_name(name: str) -> str: + """ + 规范化地区名称:去除括号及其中的附加信息(如灵气密度、动植物等)。 + + 处理多层括号:递归去除所有括号及其内容。 + + Args: + name: 原始地区名称,可能包含资源等附加信息 + + Returns: + 规范化后的地区名称 + + Examples: + >>> normalize_region_name("太白金府(金行灵气:10)") + '太白金府' + >>> normalize_region_name("青云林海(千年古松(金丹))") + '青云林海' + """ + s = str(name).strip() + brackets = [ + ("(", ")"), ("(", ")"), + ("[", "]"), ("【", "】"), + ("「", "」"), ("『", "』"), + ("<", ">"), ("《", "》") + ] + + # 递归去除所有括号(用于处理嵌套括号) + while True: + found = False + for left, right in brackets: + start = s.find(left) + end = s.rfind(right) + if start != -1 and end != -1 and end > start: + s = (s[:start] + s[end + 1:]).strip() + found = True + break + if not found: + break + + return s + + +def normalize_item_name(name: str) -> str: + """ + 规范化物品名称:去除境界标识等附加信息。 + + 处理格式: + - "青云鹿角 -(练气)" -> "青云鹿角" + - "风速马皮(筑基)" -> "风速马皮" + + Args: + name: 原始物品名称,可能包含境界等附加信息 + + Returns: + 规范化后的物品名称 + + Examples: + >>> normalize_item_name("青云鹿角 -(练气)") + '青云鹿角' + >>> normalize_item_name("风速马皮(筑基)") + '风速马皮' + """ + s = _remove_parentheses(name) + # 额外处理:去除尾部的 " -" 标记 + s = s.rstrip(" -").strip() + return s +