613 lines
23 KiB
Python
613 lines
23 KiB
Python
"""战斗系统基础插件类"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from PWF.Convention.Runtime.Architecture import *
|
||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||
from PWF.CoreModules.plugin_interface import DatabaseModel
|
||
|
||
from Plugins.WPSAPI import GuideEntry, GuideSection, WPSAPI
|
||
from Plugins.WPSBackpackSystem import BackpackItemTier, WPSBackpackSystem
|
||
from Plugins.WPSAlchemyGame import WPSAlchemyGame
|
||
from Plugins.WPSStoreSystem import WPSStoreSystem
|
||
from Plugins.WPSConfigSystem import WPSConfigAPI
|
||
from Plugins.WPSFortuneSystem import WPSFortuneSystem
|
||
|
||
from .combat_models import (
|
||
ADVENTURE_MATERIALS,
|
||
ADVENTURE_SEEDS,
|
||
ADVENTURE_SOUVENIRS,
|
||
COMBAT_POTIONS,
|
||
EQUIPMENT_REGISTRY,
|
||
SKILL_REGISTRY,
|
||
SPARK_DUST_ITEM_ID,
|
||
EquipmentDefinition,
|
||
get_combat_db_models,
|
||
)
|
||
from .combat_service import CombatService, get_combat_service
|
||
|
||
from Plugins.WPSGardenSystem import (
|
||
GardenCropDefinition,
|
||
GardenExtraReward,
|
||
register_crop,
|
||
)
|
||
|
||
|
||
logger: ProjectConfig = Architecture.Get(ProjectConfig)
|
||
|
||
|
||
class WPSCombatBase(WPSAPI):
|
||
"""战斗系统基础插件类"""
|
||
|
||
_service: CombatService | None = None
|
||
_initialized: bool = False
|
||
|
||
def collect_additional_sections(self) -> Sequence[GuideSection]:
|
||
sections = list(super().collect_additional_sections())
|
||
|
||
equipment_entries = self._build_equipment_entries()
|
||
if equipment_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="装备详单",
|
||
entries=equipment_entries,
|
||
layout="grid",
|
||
section_id="combat-equipment",
|
||
description="战斗系统预置的装备清单及属性效果。",
|
||
)
|
||
)
|
||
|
||
potion_entries = self._build_simple_item_entries(
|
||
COMBAT_POTIONS,
|
||
category="战斗药剂",
|
||
)
|
||
if potion_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="药剂与增益",
|
||
entries=potion_entries,
|
||
layout="grid",
|
||
section_id="combat-potions",
|
||
description="药剂品质影响价格与效果,部分由冒险掉落。",
|
||
)
|
||
)
|
||
|
||
material_entries = self._build_simple_item_entries(
|
||
ADVENTURE_MATERIALS,
|
||
category="冒险材料",
|
||
)
|
||
if material_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="冒险材料",
|
||
entries=material_entries,
|
||
layout="grid",
|
||
section_id="combat-materials",
|
||
description="材料主要来源于冒险战斗,稀有度决定获取概率。",
|
||
)
|
||
)
|
||
|
||
souvenir_entries = self._build_souvenir_entries()
|
||
if souvenir_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="纪念品一览",
|
||
entries=souvenir_entries,
|
||
layout="grid",
|
||
section_id="combat-souvenirs",
|
||
description="纪念品可在营地或商店出售兑换积分。",
|
||
)
|
||
)
|
||
|
||
seed_entries = self._build_simple_item_entries(
|
||
ADVENTURE_SEEDS,
|
||
category="冒险种子",
|
||
)
|
||
if seed_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="冒险种子",
|
||
entries=seed_entries,
|
||
layout="grid",
|
||
section_id="combat-seeds",
|
||
description="冒险专属种子,可在菜园种植获取增益或任务物资。",
|
||
)
|
||
)
|
||
|
||
skill_entries = self._build_skill_entries()
|
||
if skill_entries:
|
||
sections.append(
|
||
GuideSection(
|
||
title="技能图鉴",
|
||
entries=skill_entries,
|
||
layout="list",
|
||
section_id="combat-skills",
|
||
description="装备附带的技能可在战斗中释放,冷却时间以回合计算。",
|
||
)
|
||
)
|
||
|
||
return tuple(sections)
|
||
|
||
def _format_tier(self, tier: BackpackItemTier) -> str:
|
||
return f"{tier.display_name}"
|
||
|
||
def _build_equipment_entries(self) -> List[GuideEntry]:
|
||
entries: List[GuideEntry] = []
|
||
attr_names = {
|
||
"HP": "生命值",
|
||
"ATK": "攻击力",
|
||
"DEF": "防御力",
|
||
"SPD": "速度",
|
||
"CRIT": "暴击率",
|
||
"CRIT_DMG": "暴击伤害",
|
||
}
|
||
tier_icons = {
|
||
BackpackItemTier.COMMON: "⚔️",
|
||
BackpackItemTier.RARE: "🛡️",
|
||
BackpackItemTier.EPIC: "🔥",
|
||
BackpackItemTier.LEGENDARY: "🌟",
|
||
}
|
||
for eq in EQUIPMENT_REGISTRY.values():
|
||
attr_list = []
|
||
for key, value in eq.attributes.items():
|
||
label = attr_names.get(key, key)
|
||
suffix = "%" if key in ("CRIT", "CRIT_DMG") else ""
|
||
attr_list.append(f"{label}+{value}{suffix}")
|
||
skills = []
|
||
for skill_id in eq.skill_ids:
|
||
skill = SKILL_REGISTRY.get(skill_id)
|
||
if skill:
|
||
skills.append(f"{skill.icon} {skill.name}")
|
||
metadata = {
|
||
"槽位": eq.slot,
|
||
"稀有度": self._format_tier(eq.tier),
|
||
}
|
||
details: List[Dict[str, Any] | str] = []
|
||
if attr_list:
|
||
details.append({"type": "list", "items": attr_list})
|
||
if skills:
|
||
details.append({"type": "list", "items": [f"技能:{info}" for info in skills]})
|
||
if eq.description:
|
||
details.append(eq.description)
|
||
entries.append(
|
||
GuideEntry(
|
||
title=eq.name,
|
||
identifier=eq.item_id,
|
||
description=eq.description or "标准装备。",
|
||
category="装备",
|
||
metadata=metadata,
|
||
icon=tier_icons.get(eq.tier, "⚔️"),
|
||
tags=skills,
|
||
details=details,
|
||
)
|
||
)
|
||
return entries
|
||
|
||
def _build_simple_item_entries(
|
||
self,
|
||
registry: Dict[str, tuple],
|
||
*,
|
||
category: str,
|
||
) -> List[GuideEntry]:
|
||
entries: List[GuideEntry] = []
|
||
icon_map = {
|
||
"战斗药剂": "🧪",
|
||
"冒险材料": "🪨",
|
||
"纪念品": "🎖️",
|
||
"冒险种子": "🌱",
|
||
}
|
||
for item_id, payload in registry.items():
|
||
name, tier, *rest = payload
|
||
description = rest[-1] if rest else ""
|
||
metadata = {"稀有度": self._format_tier(tier)}
|
||
if category == "战斗药剂":
|
||
price_lookup = {
|
||
BackpackItemTier.COMMON: 50,
|
||
BackpackItemTier.RARE: 150,
|
||
BackpackItemTier.EPIC: 500,
|
||
}
|
||
price = price_lookup.get(tier)
|
||
if price:
|
||
metadata["默认售价"] = f"{price} 分"
|
||
entries.append(
|
||
GuideEntry(
|
||
title=name,
|
||
identifier=item_id,
|
||
description=description,
|
||
category=category,
|
||
metadata=metadata,
|
||
icon=icon_map.get(category, "📦"),
|
||
)
|
||
)
|
||
return entries
|
||
|
||
def _build_souvenir_entries(self) -> List[GuideEntry]:
|
||
entries: List[GuideEntry] = []
|
||
for item_id, (name, tier, price, desc) in ADVENTURE_SOUVENIRS.items():
|
||
entries.append(
|
||
GuideEntry(
|
||
title=name,
|
||
identifier=item_id,
|
||
description=desc,
|
||
category="纪念品",
|
||
metadata={
|
||
"稀有度": self._format_tier(tier),
|
||
"基础售价": f"{price} 分",
|
||
},
|
||
icon="🎖️",
|
||
)
|
||
)
|
||
return entries
|
||
|
||
def _build_skill_entries(self) -> List[GuideEntry]:
|
||
entries: List[GuideEntry] = []
|
||
for skill in SKILL_REGISTRY.values():
|
||
details: List[Union[str, Dict[str, Any]]] = [
|
||
{"type": "list", "items": [effect.get("description", str(effect)) for effect in skill.effects]},
|
||
]
|
||
if skill.cooldown:
|
||
details.append(f"冷却:{skill.cooldown} 回合")
|
||
entries.append(
|
||
GuideEntry(
|
||
title=skill.name,
|
||
identifier=skill.skill_id,
|
||
description=skill.description,
|
||
category="技能",
|
||
icon=skill.icon,
|
||
metadata={},
|
||
details=details,
|
||
)
|
||
)
|
||
return entries
|
||
|
||
def get_guide_subtitle(self) -> str:
|
||
return "冒险、战斗与装备体系的基础能力"
|
||
|
||
def get_guide_metadata(self) -> Dict[str, str]:
|
||
return {
|
||
"装备数量": str(len(EQUIPMENT_REGISTRY)),
|
||
"药剂数量": str(len(COMBAT_POTIONS)),
|
||
"纪念品数量": str(len(ADVENTURE_SOUVENIRS)),
|
||
"冒险材料": str(len(ADVENTURE_MATERIALS)),
|
||
}
|
||
|
||
def collect_item_entries(self) -> Sequence[GuideEntry]:
|
||
return (
|
||
{
|
||
"title": "装备库",
|
||
"description": f"{len(EQUIPMENT_REGISTRY)} 件装备,自动注册至背包并带有属性描述。",
|
||
},
|
||
{
|
||
"title": "药剂与材料",
|
||
"description": (
|
||
f"{len(COMBAT_POTIONS)} 种药剂、{len(ADVENTURE_MATERIALS)} 种冒险素材,"
|
||
"部分可在商店购买或冒险获得。"
|
||
),
|
||
},
|
||
{
|
||
"title": "纪念品",
|
||
"description": f"{len(ADVENTURE_SOUVENIRS)} 种纪念品可在营地出售换取积分。",
|
||
},
|
||
)
|
||
|
||
def collect_guide_entries(self) -> Sequence[GuideEntry]:
|
||
return (
|
||
{
|
||
"title": "冒险流程",
|
||
"description": "冒险按阶段推进,使用食物缩短时间并依赖运势与装备强度影响结果。",
|
||
},
|
||
{
|
||
"title": "装备体系",
|
||
"description": "装备提供属性与技能加成,通过 `装备`/`战斗属性` 等指令查看详情。",
|
||
},
|
||
{
|
||
"title": "积分与资源",
|
||
"description": "冒险和战斗会产出积分、装备、材料等,通过商店与营地完成循环。",
|
||
},
|
||
)
|
||
|
||
@classmethod
|
||
def service(cls) -> CombatService:
|
||
"""获取共享的战斗服务实例"""
|
||
if cls._service is None:
|
||
cls._service = get_combat_service()
|
||
return cls._service
|
||
|
||
def dependencies(self) -> List[Type]:
|
||
"""声明依赖的插件"""
|
||
return [
|
||
WPSAPI,
|
||
WPSConfigAPI,
|
||
WPSBackpackSystem,
|
||
WPSStoreSystem,
|
||
WPSFortuneSystem,
|
||
WPSAlchemyGame,
|
||
]
|
||
|
||
def register_db_model(self) -> List[DatabaseModel]:
|
||
"""注册数据库表"""
|
||
return get_combat_db_models()
|
||
|
||
def wake_up(self) -> None:
|
||
"""插件初始化(只执行一次)"""
|
||
if WPSCombatBase._initialized:
|
||
return
|
||
WPSCombatBase._initialized = True
|
||
|
||
logger.Log(
|
||
"Info",
|
||
f"{ConsoleFrontColor.GREEN}WPSCombat 系统开始初始化{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||
store: WPSStoreSystem = Architecture.Get(WPSStoreSystem)
|
||
|
||
# 1. 注册所有装备
|
||
for equipment in EQUIPMENT_REGISTRY.values():
|
||
# 生成包含属性数值和技能信息的描述
|
||
enhanced_description = self._generate_equipment_description(equipment)
|
||
self._safe_register_item(
|
||
backpack,
|
||
equipment.item_id,
|
||
equipment.name,
|
||
equipment.tier,
|
||
enhanced_description,
|
||
)
|
||
# 装备价格根据品质和属性计算
|
||
price = self._calculate_equipment_price(equipment)
|
||
self._safe_register_store(store, equipment.item_id, price, limit=3)
|
||
|
||
# 2. 注册材料
|
||
for item_id, (name, tier, desc) in ADVENTURE_MATERIALS.items():
|
||
self._safe_register_item(backpack, item_id, name, tier, desc)
|
||
# 材料可以在商店出售(但不购买)
|
||
|
||
# 3. 注册纪念品
|
||
for item_id, (name, tier, sell_price, desc) in ADVENTURE_SOUVENIRS.items():
|
||
self._safe_register_item(backpack, item_id, name, tier, desc)
|
||
# 纪念品只能出售
|
||
|
||
# 4. 注册药剂
|
||
for item_id, (name, tier, desc) in COMBAT_POTIONS.items():
|
||
self._safe_register_item(backpack, item_id, name, tier, desc)
|
||
# 药剂价格根据品质
|
||
potion_prices = {
|
||
BackpackItemTier.COMMON: 50,
|
||
BackpackItemTier.RARE: 150,
|
||
BackpackItemTier.EPIC: 500,
|
||
}
|
||
price = potion_prices.get(tier, 100)
|
||
self._safe_register_store(store, item_id, price, limit=10)
|
||
|
||
# 5. 注册冒险种子
|
||
for item_id, (name, tier, desc) in ADVENTURE_SEEDS.items():
|
||
self._safe_register_item(backpack, item_id, name, tier, desc)
|
||
# 种子只能通过冒险获得
|
||
|
||
# 5.1. 注册冒险种子到菜园系统
|
||
self._register_adventure_seeds_to_garden()
|
||
|
||
self._register_legendary_alchemy_recipes()
|
||
|
||
# 6. 恢复过期任务和超时战斗
|
||
try:
|
||
service = self.service()
|
||
service.recover_overdue_adventures()
|
||
service.check_battle_timeout()
|
||
except Exception as e:
|
||
logger.Log(
|
||
"Warning",
|
||
f"{ConsoleFrontColor.YELLOW}恢复任务时出错: {e}{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
logger.Log(
|
||
"Info",
|
||
f"{ConsoleFrontColor.GREEN}WPSCombat 系统初始化完成:{len(EQUIPMENT_REGISTRY)}件装备、"
|
||
f"{len(COMBAT_POTIONS)}种药剂已注册{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
# ========================================================================
|
||
# 辅助方法
|
||
# ========================================================================
|
||
|
||
def _safe_register_item(
|
||
self,
|
||
backpack: WPSBackpackSystem,
|
||
item_id: str,
|
||
name: str,
|
||
tier: BackpackItemTier,
|
||
description: str,
|
||
) -> None:
|
||
"""安全注册物品到背包系统"""
|
||
try:
|
||
backpack.register_item(item_id, name, tier, description)
|
||
except Exception as e:
|
||
logger.Log(
|
||
"Warning",
|
||
f"{ConsoleFrontColor.YELLOW}注册物品 {item_id} 时出错: {e}{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
def _safe_register_store(
|
||
self,
|
||
store: WPSStoreSystem,
|
||
item_id: str,
|
||
price: int,
|
||
*,
|
||
limit: int = 5,
|
||
) -> None:
|
||
"""安全注册物品到商店系统"""
|
||
try:
|
||
store.register_mode(
|
||
item_id=item_id,
|
||
price=price,
|
||
limit_amount=limit,
|
||
)
|
||
except Exception as e:
|
||
logger.Log(
|
||
"Warning",
|
||
f"{ConsoleFrontColor.YELLOW}注册商店物品 {item_id} 时出错: {e}{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
def _generate_equipment_description(self, equipment:EquipmentDefinition) -> str:
|
||
"""生成包含属性数值和技能信息的装备描述"""
|
||
parts = []
|
||
|
||
# 基础描述
|
||
if equipment.description:
|
||
parts.append(equipment.description)
|
||
|
||
# 属性信息
|
||
if equipment.attributes:
|
||
attr_parts = []
|
||
attr_names = {
|
||
"HP": "生命值",
|
||
"ATK": "攻击力",
|
||
"DEF": "防御力",
|
||
"SPD": "速度",
|
||
"CRIT": "暴击率",
|
||
"CRIT_DMG": "暴击伤害",
|
||
}
|
||
for attr_key, attr_value in sorted(equipment.attributes.items()):
|
||
attr_name = attr_names.get(attr_key, attr_key)
|
||
if attr_key in ["CRIT", "CRIT_DMG"]:
|
||
attr_parts.append(f"{attr_name}+{attr_value}%")
|
||
else:
|
||
attr_parts.append(f"{attr_name}+{attr_value}")
|
||
if attr_parts:
|
||
parts.append(f"属性:{', '.join(attr_parts)}")
|
||
|
||
# 技能信息
|
||
if equipment.skill_ids:
|
||
skill_names = []
|
||
for skill_id in equipment.skill_ids:
|
||
skill = SKILL_REGISTRY.get(skill_id)
|
||
if skill:
|
||
skill_names.append(skill.name)
|
||
if skill_names:
|
||
parts.append(f"附带技能:{', '.join(skill_names)}")
|
||
|
||
return " | ".join(parts) if parts else equipment.description
|
||
|
||
def _calculate_equipment_price(self, equipment: EquipmentDefinition) -> int:
|
||
"""根据装备品质和属性计算价格"""
|
||
# 基础价格
|
||
base_prices = {
|
||
BackpackItemTier.COMMON: 100,
|
||
BackpackItemTier.RARE: 500,
|
||
BackpackItemTier.EPIC: 2000,
|
||
BackpackItemTier.LEGENDARY: 10000,
|
||
}
|
||
base_price = base_prices.get(equipment.tier, 100)
|
||
|
||
# 属性加成
|
||
attr_sum = sum(equipment.attributes.values())
|
||
price = base_price + attr_sum * 5
|
||
|
||
# 技能加成
|
||
skill_bonus = len(equipment.skill_ids) * 200
|
||
|
||
return price + skill_bonus
|
||
|
||
def _register_adventure_seeds_to_garden(self) -> None:
|
||
"""注册冒险种子到菜园系统"""
|
||
try:
|
||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||
|
||
# 战斗之花种子(EPIC)
|
||
battle_flower_crop = GardenCropDefinition(
|
||
seed_id="combat_seed_battle_flower",
|
||
fruit_id="combat_fruit_battle_flower",
|
||
display_name="战斗之花",
|
||
tier="epic",
|
||
growth_minutes=240, # 4小时,比稀有树木更长
|
||
seed_price=300, # 比稀有树木更贵
|
||
base_yield=2, # 产量较低,体现稀有性
|
||
extra_reward=GardenExtraReward(
|
||
kind="points",
|
||
payload={"min": 500, "max": 2000},
|
||
base_rate=0.35, # 较低的基础概率,体现稀有性
|
||
),
|
||
wine_item_id=None, # 暂不设置果酒
|
||
wine_tier=None,
|
||
)
|
||
register_crop(battle_flower_crop)
|
||
# 注册果实到背包系统
|
||
self._safe_register_item(
|
||
backpack,
|
||
battle_flower_crop.fruit_id,
|
||
"战斗之花的果实",
|
||
BackpackItemTier.EPIC,
|
||
"战斗之花成熟后的果实,可食用或售出换取积分。",
|
||
)
|
||
|
||
# 胜利之树种子(LEGENDARY)
|
||
victory_tree_crop = GardenCropDefinition(
|
||
seed_id="combat_seed_victory_tree",
|
||
fruit_id="combat_fruit_victory_tree",
|
||
display_name="胜利之树",
|
||
tier="legendary",
|
||
growth_minutes=480, # 8小时,最长的生长时间
|
||
seed_price=800, # 最贵的种子
|
||
base_yield=1, # 最低产量,体现传说级稀有性
|
||
extra_reward=GardenExtraReward(
|
||
kind="points",
|
||
payload={"min": 2000, "max": 5000},
|
||
base_rate=0.25, # 最低的基础概率
|
||
),
|
||
wine_item_id=None, # 暂不设置果酒
|
||
wine_tier=None,
|
||
)
|
||
register_crop(victory_tree_crop)
|
||
# 注册果实到背包系统
|
||
self._safe_register_item(
|
||
backpack,
|
||
victory_tree_crop.fruit_id,
|
||
"胜利之树的果实",
|
||
BackpackItemTier.LEGENDARY,
|
||
"胜利之树成熟后的果实,可食用或售出换取积分。",
|
||
)
|
||
|
||
logger.Log(
|
||
"Info",
|
||
f"{ConsoleFrontColor.GREEN}成功注册 {len(ADVENTURE_SEEDS)} 种冒险种子到菜园系统{ConsoleFrontColor.RESET}"
|
||
)
|
||
except Exception as e:
|
||
logger.Log(
|
||
"Warning",
|
||
f"{ConsoleFrontColor.YELLOW}注册冒险种子到菜园系统时出错: {e}{ConsoleFrontColor.RESET}"
|
||
)
|
||
|
||
def _register_legendary_alchemy_recipes(self) -> None:
|
||
"""注册传说装备的炼金链条"""
|
||
alchemy: WPSAlchemyGame = Architecture.Get(WPSAlchemyGame)
|
||
|
||
recipe_definitions = (
|
||
# 护甲链
|
||
(("combat_material_ore", "garden_wood_maple", "combat_armor_chain"), "combat_armor_plate", 0.70),
|
||
(("combat_material_gem", "combat_material_crystal", "combat_armor_plate"), "combat_armor_sentinel", 0.50),
|
||
(("combat_armor_sentinel", "garden_wood_sakura", "combat_material_crystal"), "combat_armor_dragonheart", 0.30),
|
||
(("combat_armor_dragonheart", "combat_material_essence", "combat_armor_plate"), "combat_armor_guardian", 0.10),
|
||
# 鞋子链
|
||
(("combat_material_ore", "garden_wood_ginkgo", "combat_boots_leather"), "combat_boots_rapid", 0.70),
|
||
(("combat_boots_rapid", "combat_material_gem", "combat_material_crystal"), "combat_boots_wind", 0.50),
|
||
(("combat_boots_wind", "combat_material_crystal", "garden_wood_ginkgo"), "combat_boots_tempest", 0.30),
|
||
(("combat_boots_tempest", "combat_material_essence", "garden_wine_maple"), "combat_boots_starlight", 0.10),
|
||
# 饰品链
|
||
(("combat_accessory_ring_str", "combat_material_gem", "garden_wood_sakura"), "combat_accessory_barrier", 0.70),
|
||
(("combat_accessory_barrier", "combat_material_crystal", "garden_wood_sakura"), "combat_accessory_amulet", 0.50),
|
||
(("combat_accessory_amulet", "combat_material_crystal", "garden_wine_sakura"), "combat_accessory_sanctum", 0.30),
|
||
(("combat_accessory_sanctum", "combat_material_essence", "combat_souvenir_relic"), "combat_accessory_aegis", 0.10),
|
||
)
|
||
|
||
for materials, success_item_id, success_rate in recipe_definitions:
|
||
try:
|
||
alchemy.register_recipe(materials, success_item_id, SPARK_DUST_ITEM_ID, success_rate)
|
||
except Exception as exc:
|
||
logger.Log(
|
||
"Warning",
|
||
f"{ConsoleFrontColor.YELLOW}注册炼金配方 {materials} -> {success_item_id} 失败: {exc}{ConsoleFrontColor.RESET}",
|
||
)
|
||
|
||
|
||
__all__ = ["WPSCombatBase"]
|