refactor normalize and resolution
This commit is contained in:
@@ -1,49 +1,192 @@
|
||||
from typing import Any, Tuple, Optional
|
||||
from __future__ import annotations
|
||||
from typing import Any, Type, Optional, List, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.classes.normalize import normalize_goods_name
|
||||
from src.classes.elixir import elixirs_by_name
|
||||
from src.classes.weapon import weapons_by_name
|
||||
from src.classes.auxiliary import auxiliaries_by_name
|
||||
from src.classes.item import items_by_name
|
||||
from src.classes.normalize import normalize_goods_name, normalize_name, normalize_avatar_name
|
||||
from src.classes.elixir import elixirs_by_name, Elixir
|
||||
from src.classes.weapon import weapons_by_name, Weapon
|
||||
from src.classes.auxiliary import auxiliaries_by_name, Auxiliary
|
||||
from src.classes.item import items_by_name, Item
|
||||
from src.classes.cultivation import Realm
|
||||
|
||||
def resolve_goods_by_name(target_name: str) -> Tuple[Any, str, str]:
|
||||
@dataclass
|
||||
class ResolutionResult:
|
||||
"""解析结果封装"""
|
||||
obj: Any | None # 解析出的对象实例
|
||||
resolved_type: Type | None # 对象的类型类
|
||||
is_valid: bool # 是否成功
|
||||
error_msg: str = "" # 错误信息
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""尝试获取对象的名称"""
|
||||
if self.obj and hasattr(self.obj, "name"):
|
||||
return self.obj.name
|
||||
return ""
|
||||
|
||||
def resolve_query(
|
||||
query: Any,
|
||||
world: Any = None,
|
||||
expected_types: Optional[list[Type]] = None
|
||||
) -> ResolutionResult:
|
||||
"""
|
||||
解析物品名称,返回 (对象, 类型, 显示名称)。
|
||||
如果未找到,返回 (None, "unknown", normalized_name)。
|
||||
统一解析入口。
|
||||
|
||||
类型字符串: "elixir", "item", "weapon", "auxiliary", "unknown"
|
||||
|
||||
查找顺序:
|
||||
1. 丹药 (Elixir)
|
||||
2. 兵器 (Weapon)
|
||||
3. 辅助装备 (Auxiliary)
|
||||
4. 普通物品 (Item)
|
||||
Args:
|
||||
query: 待解析的对象(字符串名 或 直接的对象实例)
|
||||
world: 世界对象上下文(用于查找 Avatar, Region)
|
||||
expected_types: 期望的类型列表。如果提供,将优先或仅尝试这些类型。
|
||||
支持的类型: Item, Weapon, Elixir, Auxiliary, Region, Avatar, Realm 等类对象
|
||||
必须直接传入类对象,不再支持字符串名。
|
||||
"""
|
||||
normalized_name = normalize_goods_name(target_name)
|
||||
if query is None:
|
||||
return ResolutionResult(None, None, False, "查询为空")
|
||||
|
||||
# 0. 快速通道:如果已经是期望的对象实例
|
||||
if expected_types:
|
||||
for t in expected_types:
|
||||
if isinstance(query, t):
|
||||
return _success(query, t)
|
||||
|
||||
# 1. 尝试作为丹药查找
|
||||
if normalized_name in elixirs_by_name:
|
||||
# elixirs_by_name 返回的是 list,我们取第一个作为对象
|
||||
# 注意:对于购买/显示信息来说,取第一个通常是没问题的,
|
||||
# 但如果有特定逻辑需要区分同名不同境界的丹药,可能需要更精细的处理。
|
||||
# 这里保持原有逻辑。
|
||||
elixir = elixirs_by_name[normalized_name][0]
|
||||
return elixir, "elixir", elixir.name
|
||||
# 如果不是字符串,且未命中上面的快速通道,可能输入了错误的对象
|
||||
# 严格模式:既然未命中 expected_types 中的任何类型,也必然无法通过下面的字符串查找逻辑。
|
||||
# 直接返回失败,除非 query 是字符串可以尝试查找。
|
||||
if not isinstance(query, str):
|
||||
return ResolutionResult(None, None, False, f"输入类型不支持且非字符串: {type(query)}")
|
||||
|
||||
# 2. 尝试作为兵器查找
|
||||
weapon = weapons_by_name.get(normalized_name)
|
||||
if weapon:
|
||||
return weapon, "weapon", weapon.name
|
||||
if not query:
|
||||
return ResolutionResult(None, None, False, "查询字符串为空")
|
||||
|
||||
# 3. 尝试作为辅助装备查找
|
||||
auxiliary = auxiliaries_by_name.get(normalized_name)
|
||||
if auxiliary:
|
||||
return auxiliary, "auxiliary", auxiliary.name
|
||||
# 准备检查列表
|
||||
checks = []
|
||||
|
||||
# 如果没有指定期望类型,则检查所有
|
||||
if not expected_types:
|
||||
checks = ["realm", "goods", "region", "avatar"]
|
||||
else:
|
||||
# 根据期望类型构建检查顺序
|
||||
for t in expected_types:
|
||||
# 必须是类对象
|
||||
if not isinstance(t, type):
|
||||
# 这里可以抛出异常,或者忽略。既然是重构,假设调用方已经修正。
|
||||
continue
|
||||
|
||||
# 4. 尝试作为普通物品查找
|
||||
item = items_by_name.get(normalized_name)
|
||||
if item:
|
||||
return item, "item", item.name
|
||||
t_name = t.__name__
|
||||
|
||||
if t_name in ["Item", "Weapon", "Elixir", "Auxiliary"]:
|
||||
if "goods" not in checks: checks.append("goods")
|
||||
elif t_name == "Region" or t_name == "CityRegion" or t_name == "SectRegion" or t_name == "CultivateRegion":
|
||||
if "region" not in checks: checks.append("region")
|
||||
elif t_name == "Avatar":
|
||||
if "avatar" not in checks: checks.append("avatar")
|
||||
elif t_name == "Realm":
|
||||
if "realm" not in checks: checks.append("realm")
|
||||
|
||||
return None, "unknown", normalized_name
|
||||
# 执行检查
|
||||
for check in checks:
|
||||
if check == "realm":
|
||||
res = _resolve_realm(query) # Realm 通常不需要 normalize 太多,或者在内部处理
|
||||
if res: return _success(res, Realm)
|
||||
|
||||
elif check == "goods":
|
||||
# 物品解析
|
||||
obj = _resolve_goods(query)
|
||||
if obj: return _success(obj, type(obj))
|
||||
|
||||
elif check == "region" and world:
|
||||
obj = _resolve_region(query, world)
|
||||
if obj: return _success(obj, type(obj))
|
||||
|
||||
elif check == "avatar" and world:
|
||||
obj = _resolve_avatar(query, world)
|
||||
if obj: return _success(obj, type(obj))
|
||||
|
||||
return ResolutionResult(None, None, False, f"无法解析: {query}")
|
||||
|
||||
def _success(obj: Any, type_cls: Type) -> ResolutionResult:
|
||||
return ResolutionResult(obj, type_cls, True)
|
||||
|
||||
# --- 内部具体的解析逻辑 ---
|
||||
|
||||
def _resolve_goods(name: str) -> Any | None:
|
||||
"""解析物品/装备/丹药"""
|
||||
norm = normalize_goods_name(name)
|
||||
|
||||
# 1. 丹药 (返回列表中的第一个)
|
||||
if norm in elixirs_by_name:
|
||||
return elixirs_by_name[norm][0]
|
||||
|
||||
# 2. 兵器
|
||||
if norm in weapons_by_name:
|
||||
return weapons_by_name[norm]
|
||||
|
||||
# 3. 辅助
|
||||
if norm in auxiliaries_by_name:
|
||||
return auxiliaries_by_name[norm]
|
||||
|
||||
# 4. 普通物品
|
||||
if norm in items_by_name:
|
||||
return items_by_name[norm]
|
||||
|
||||
return None
|
||||
|
||||
def _resolve_realm(name: str) -> Realm | None:
|
||||
"""解析境界"""
|
||||
try:
|
||||
# 尝试直接匹配值
|
||||
return Realm(name)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 尝试匹配枚举名
|
||||
for r in Realm:
|
||||
if r.name == name or r.value == name:
|
||||
return r
|
||||
return None
|
||||
|
||||
def _resolve_region(name: str, world: Any) -> Any | None:
|
||||
"""解析区域"""
|
||||
if not hasattr(world, 'map'):
|
||||
return None
|
||||
|
||||
# 1. 精确匹配
|
||||
by_name = getattr(world.map, "region_names", {})
|
||||
if name in by_name:
|
||||
return by_name[name]
|
||||
|
||||
# 2. 规范化匹配
|
||||
norm = normalize_name(name)
|
||||
if norm in by_name:
|
||||
return by_name[norm]
|
||||
|
||||
# 3. 包含匹配 (如果有唯一解)
|
||||
candidates = [k for k in by_name.keys() if (norm in k) or (name in k)]
|
||||
if len(candidates) == 1:
|
||||
return by_name[candidates[0]]
|
||||
|
||||
# 4. 宗门名称匹配 (解析到宗门驻地)
|
||||
# 避免循环引用,这里动态导入或假设 sects_by_name 可用
|
||||
from src.classes.sect import sects_by_name
|
||||
sect = sects_by_name.get(name) or sects_by_name.get(norm)
|
||||
if sect:
|
||||
sect_regions = getattr(world.map, "sect_regions", {})
|
||||
matched = [r for r in sect_regions.values() if getattr(r, "sect_name", None) == sect.name]
|
||||
if len(matched) == 1:
|
||||
return matched[0]
|
||||
|
||||
return None
|
||||
|
||||
def _resolve_avatar(name: str, world: Any) -> Any | None:
|
||||
"""解析角色"""
|
||||
if not hasattr(world, 'avatar_manager'):
|
||||
return None
|
||||
|
||||
norm = normalize_avatar_name(name)
|
||||
|
||||
# 遍历查找 (性能注意:如果角色极多可能需要优化为字典查找)
|
||||
# 假设 avatar_manager.avatars 是 dict[id, Avatar]
|
||||
for avatar in world.avatar_manager.avatars.values():
|
||||
if avatar.name == norm:
|
||||
return avatar
|
||||
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user