diff --git a/Plugins/WPSAlchemyGame.py b/Plugins/WPSAlchemyGame.py index cb551f0..c567f1d 100644 --- a/Plugins/WPSAlchemyGame.py +++ b/Plugins/WPSAlchemyGame.py @@ -1321,5 +1321,201 @@ class WPSAlchemyGame(WPSAPI): ) +class WPSAlchemyRecipeLookup(WPSAPI): + def __init__(self) -> None: + super().__init__() + self._alchemy: Optional[WPSAlchemyGame] = None + self._backpack: Optional[WPSBackpackSystem] = None + + def get_guide_subtitle(self) -> str: + return "查询指定物品涉及的炼金配方" + + def collect_command_entries(self) -> Sequence[GuideEntry]: + return ( + { + "title": "炼金配方", + "identifier": "炼金配方", + "description": "展示物品作为材料、成功产物或失败产物的所有配方。", + "metadata": {"别名": "alchemy_recipe"}, + }, + ) + + def collect_guide_entries(self) -> Sequence[GuideEntry]: + return ( + { + "title": "查询格式", + "description": "`炼金配方 <物品ID>` 或 `炼金配方 <物品名称>`,忽略大小写。", + }, + { + "title": "输出结构", + "description": "结果按材料/成功/失败三类分组,列出配方材料与成功率。", + }, + ) + + def dependencies(self) -> List[type]: + return [WPSAlchemyGame, WPSBackpackSystem] + + def is_enable_plugin(self) -> bool: + return True + + def wake_up(self) -> None: + self._alchemy = Architecture.Get(WPSAlchemyGame) + self._backpack = Architecture.Get(WPSBackpackSystem) + self.register_plugin("炼金配方") + self.register_plugin("alchemy_recipe") + logger.Log( + "Info", + f"{ConsoleFrontColor.GREEN}WPSAlchemyRecipeLookup 插件已加载{ConsoleFrontColor.RESET}", + ) + + async def callback( + self, message: str, chat_id: int, user_id: int + ) -> Optional[str]: + payload = self.parse_message_after_at(message).strip() + if not payload: + return await self.send_markdown_message(self._help_text(), chat_id, user_id) + + backpack = self._backpack or Architecture.Get(WPSBackpackSystem) + definition = self._resolve_definition(payload, backpack) + if definition is None: + return await self.send_markdown_message( + f"❌ 未找到物品 `{payload}`,请确认输入的物品 ID 或名称。", + chat_id, + user_id, + ) + + alchemy = self._alchemy or Architecture.Get(WPSAlchemyGame) + material_recipes = alchemy.get_recipes_using_item(definition.item_id) + produce_map = alchemy.get_recipes_producing_item(definition.item_id) + success_recipes = produce_map["success"] + fail_recipes = produce_map["fail"] + + message_text = self._format_markdown( + definition, + material_recipes, + success_recipes, + fail_recipes, + backpack, + ) + return await self.send_markdown_message(message_text, chat_id, user_id) + + def _resolve_definition( + self, identifier: str, backpack: WPSBackpackSystem + ) -> Optional[BackpackItemDefinition]: + lowered = identifier.strip().lower() + cursor = get_db().conn.cursor() + cursor.execute( + f""" + SELECT item_id + FROM {WPSBackpackSystem.ITEMS_TABLE} + WHERE lower(item_id) = ? OR lower(name) = ? + LIMIT 1 + """, + (lowered, lowered), + ) + row = cursor.fetchone() + item_id = row["item_id"] if row else identifier.strip() + try: + return backpack._get_definition(item_id) # noqa: SLF001 + except Exception: + return None + + def _format_markdown( + self, + target: BackpackItemDefinition, + material_recipes: List[AlchemyRecipe], + success_recipes: List[AlchemyRecipe], + fail_recipes: List[AlchemyRecipe], + backpack: WPSBackpackSystem, + ) -> str: + lines = [ + f"# 🔍 炼金配方查询|{target.name}", + f"- 物品 ID:`{target.item_id}`", + "---", + ] + lines.append("## 作为配方材料") + lines.extend( + self._format_recipe_entries(material_recipes, backpack) + or ["- 暂无记录"] + ) + + lines.append("\n## 作为成功产物") + lines.extend( + self._format_recipe_entries(success_recipes, backpack, role="success") + or ["- 暂无记录"] + ) + + lines.append("\n## 作为失败产物") + lines.extend( + self._format_recipe_entries(fail_recipes, backpack, role="fail") + or ["- 暂无记录"] + ) + return "\n".join(lines) + + def _format_recipe_entries( + self, + recipes: List[AlchemyRecipe], + backpack: WPSBackpackSystem, + *, + role: str = "material", + ) -> List[str]: + if not recipes: + return [] + entries: List[str] = [] + for recipe in recipes: + materials = self._summarize_materials(recipe, backpack) + success_name = self._resolve_item_name(recipe.success_item_id, backpack) + fail_name = self._resolve_item_name(recipe.fail_item_id, backpack) + rate = f"{recipe.base_success_rate:.0%}" + if role == "material": + entry = ( + f"- 材料:{materials}|成功产物:`{success_name}`|" + f"失败产物:`{fail_name}`|成功率:{rate}" + ) + elif role == "success": + entry = ( + f"- 材料:{materials}|成功率:{rate}|" + f"失败产物:`{fail_name}`" + ) + else: + entry = ( + f"- 材料:{materials}|成功率:{rate}|" + f"成功产物:`{success_name}`" + ) + entries.append(entry) + return entries + + def _summarize_materials( + self, recipe: AlchemyRecipe, backpack: WPSBackpackSystem + ) -> str: + counter = Counter(recipe.materials) + parts: List[str] = [] + for item_id, count in sorted(counter.items()): + name = self._resolve_item_name(item_id, backpack) + if count == 1: + parts.append(f"`{name}`") + else: + parts.append(f"`{name}` × {count}") + return " + ".join(parts) + + def _resolve_item_name( + self, item_id: str, backpack: WPSBackpackSystem + ) -> str: + try: + definition = backpack._get_definition(item_id) # noqa: SLF001 + return definition.name + except Exception: + return item_id + + def _help_text(self) -> str: + return ( + "# ⚗️ 炼金配方查询帮助\n" + "- `炼金配方 <物品ID>`\n" + "- `炼金配方 <物品名称>`\n" + "> 输入需要精确匹配注册物品,名称不区分大小写。\n" + "> 积分炼金费用将进入奖池,根据挡位概率抽取奖池奖励;部分挡位会额外掉落普通物品。" + ) + + __all__ = ["WPSAlchemyGame", "WPSAlchemyRecipeLookup"] diff --git a/Plugins/WPSCombatSystem/combat_models.py b/Plugins/WPSCombatSystem/combat_models.py index 03aff62..c04ffe8 100644 --- a/Plugins/WPSCombatSystem/combat_models.py +++ b/Plugins/WPSCombatSystem/combat_models.py @@ -358,6 +358,42 @@ SKILL_REGISTRY: Dict[str, SkillDefinition] = { cooldown=3, icon="✝️" ), + + "skill_bastion_guard": SkillDefinition( + skill_id="skill_bastion_guard", + name="圣壁", + description="召唤防御之墙,立即获得80点护盾并提升25%防御力3回合", + effects=[ + {"type": "shield", "value": 80}, + {"type": "buff", "target": "self", "stat": "DEF", "value": 0.25, "duration": 3} + ], + cooldown=5, + icon="🛡️" + ), + + "skill_tempest_step": SkillDefinition( + skill_id="skill_tempest_step", + name="星辰疾行", + description="化作疾风,必定先手并提升30%速度2回合", + effects=[ + {"type": "special", "effect": "priority"}, + {"type": "buff", "target": "self", "stat": "SPD", "value": 0.3, "duration": 2} + ], + cooldown=4, + icon="🌠" + ), + + "skill_sanctuary_guard": SkillDefinition( + skill_id="skill_sanctuary_guard", + name="庇护", + description="赐下防护光辉,回复40点生命并获得60点护盾", + effects=[ + {"type": "heal", "value": 40}, + {"type": "shield", "value": 60} + ], + cooldown=4, + icon="🕊️" + ), } # 虚拟装备(提供默认技能) @@ -487,6 +523,36 @@ EQUIPMENT_REGISTRY: Dict[str, EquipmentDefinition] = { skill_ids=["skill_iron_wall"], description="厚重的全身板甲" ), + + "combat_armor_sentinel": EquipmentDefinition( + item_id="combat_armor_sentinel", + name="圣卫重甲", + tier=BackpackItemTier.EPIC, + slot="armor", + attributes={"HP": 160, "DEF": 60, "ATK": 15}, + skill_ids=["skill_iron_wall"], + description="经多次锻造强化的重甲,为传说防具的前置形态" + ), + + "combat_armor_dragonheart": EquipmentDefinition( + item_id="combat_armor_dragonheart", + name="龙心护甲", + tier=BackpackItemTier.LEGENDARY, + slot="armor", + attributes={"HP": 200, "DEF": 75, "ATK": 20}, + skill_ids=["skill_bastion_guard"], + description="蕴含龙心之力的护甲,距离传说防具仅一步之遥" + ), + + "combat_armor_guardian": EquipmentDefinition( + item_id="combat_armor_guardian", + name="星辉壁垒", + tier=BackpackItemTier.LEGENDARY, + slot="armor", + attributes={"HP": 240, "DEF": 90, "ATK": 20, "CRIT": 5}, + skill_ids=["skill_bastion_guard"], + description="传说级防御护甲,通过多阶段炼金与锻造而成" + ), # ===== 鞋子 ===== "combat_boots_leather": EquipmentDefinition( @@ -508,6 +574,36 @@ EQUIPMENT_REGISTRY: Dict[str, EquipmentDefinition] = { skill_ids=["skill_swift_move"], description="蕴含风之力的靴子" ), + + "combat_boots_rapid": EquipmentDefinition( + item_id="combat_boots_rapid", + name="迅捷行者", + tier=BackpackItemTier.RARE, + slot="boots", + attributes={"SPD": 15, "DEF": 5}, + skill_ids=[], + description="注入矿石与木材的轻便靴,是速度链的第一步" + ), + + "combat_boots_tempest": EquipmentDefinition( + item_id="combat_boots_tempest", + name="暴风逐影", + tier=BackpackItemTier.EPIC, + slot="boots", + attributes={"SPD": 28, "DEF": 12}, + skill_ids=["skill_swift_move"], + description="融合水晶之力的靴子,进一步提升疾风步的效率" + ), + + "combat_boots_starlight": EquipmentDefinition( + item_id="combat_boots_starlight", + name="星辉疾靴", + tier=BackpackItemTier.LEGENDARY, + slot="boots", + attributes={"SPD": 38, "DEF": 14, "CRIT": 5}, + skill_ids=["skill_tempest_step"], + description="传说级速度之靴,通过多段炼金融合获得" + ), # ===== 饰品 ===== "combat_accessory_ring_str": EquipmentDefinition( @@ -529,8 +625,40 @@ EQUIPMENT_REGISTRY: Dict[str, EquipmentDefinition] = { skill_ids=["skill_protect"], description="守护佩戴者的神秘护符" ), + + "combat_accessory_barrier": EquipmentDefinition( + item_id="combat_accessory_barrier", + name="壁垒护符", + tier=BackpackItemTier.RARE, + slot="accessory", + attributes={"HP": 60, "DEF": 15}, + skill_ids=[], + description="以宝石与木材铸成的护符,是传说饰品的起点" + ), + + "combat_accessory_sanctum": EquipmentDefinition( + item_id="combat_accessory_sanctum", + name="圣域结界", + tier=BackpackItemTier.EPIC, + slot="accessory", + attributes={"HP": 90, "DEF": 25, "CRIT": 5}, + skill_ids=["skill_protect"], + description="蕴含水晶能量的护符,提供更强防护" + ), + + "combat_accessory_aegis": EquipmentDefinition( + item_id="combat_accessory_aegis", + name="星辉守护", + tier=BackpackItemTier.LEGENDARY, + slot="accessory", + attributes={"HP": 130, "DEF": 35, "CRIT": 5, "CRIT_DMG": 20}, + skill_ids=["skill_sanctuary_guard"], + description="传说级守护饰品,散发星辉防护力量" + ), } +SPARK_DUST_ITEM_ID = "alchemy_spark_dust" + # 果酒buff映射 WINE_BUFFS: Dict[str, Dict[str, float]] = { # 普通草药果酒 @@ -567,6 +695,7 @@ ADVENTURE_MATERIALS: Dict[str, Tuple[str, BackpackItemTier, str]] = { "combat_material_gem": ("宝石", BackpackItemTier.RARE, "闪亮的宝石,适合在高级制作或兑换时使用。"), "combat_material_crystal": ("水晶", BackpackItemTier.EPIC, "蕴含魔力的晶体,是强化装备的稀有素材。"), "combat_material_essence": ("精华", BackpackItemTier.LEGENDARY, "远古战场遗留的精华,可构筑传说装备核心。"), + SPARK_DUST_ITEM_ID: ("闪烁粉尘", BackpackItemTier.COMMON, "炼金失败后残留的粉尘。"), } # 纪念品(item_id -> (name, tier, sell_price, description)) diff --git a/Plugins/WPSCombatSystem/combat_plugin_base.py b/Plugins/WPSCombatSystem/combat_plugin_base.py index 051a8dc..00818c2 100644 --- a/Plugins/WPSCombatSystem/combat_plugin_base.py +++ b/Plugins/WPSCombatSystem/combat_plugin_base.py @@ -2,14 +2,13 @@ from __future__ import annotations -from typing import Dict, List, Type, Union - -from PWF.Convention.Runtime.Architecture import Architecture +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 @@ -21,21 +20,17 @@ from .combat_models import ( COMBAT_POTIONS, EQUIPMENT_REGISTRY, SKILL_REGISTRY, + SPARK_DUST_ITEM_ID, EquipmentDefinition, get_combat_db_models, ) from .combat_service import CombatService, get_combat_service -# 尝试导入菜园系统(可选依赖) -try: - from Plugins.WPSGardenSystem import ( - GardenCropDefinition, - GardenExtraReward, - register_crop, - ) - GARDEN_SYSTEM_AVAILABLE = True -except ImportError: - GARDEN_SYSTEM_AVAILABLE = False +from Plugins.WPSGardenSystem import ( + GardenCropDefinition, + GardenExtraReward, + register_crop, +) logger: ProjectConfig = Architecture.Get(ProjectConfig) @@ -326,7 +321,7 @@ class WPSCombatBase(WPSAPI): WPSBackpackSystem, WPSStoreSystem, WPSFortuneSystem, - # 注:不强制依赖 WPSGardenSystem,果酒buff配置在 combat_models.py 中 + WPSAlchemyGame, ] def register_db_model(self) -> List[DatabaseModel]: @@ -389,9 +384,10 @@ class WPSCombatBase(WPSAPI): self._safe_register_item(backpack, item_id, name, tier, desc) # 种子只能通过冒险获得 - # 5.1. 注册冒险种子到菜园系统(如果可用) - if GARDEN_SYSTEM_AVAILABLE: - self._register_adventure_seeds_to_garden() + # 5.1. 注册冒险种子到菜园系统 + self._register_adventure_seeds_to_garden() + + self._register_legendary_alchemy_recipes() # 6. 恢复过期任务和超时战斗 try: @@ -492,7 +488,7 @@ class WPSCombatBase(WPSAPI): return " | ".join(parts) if parts else equipment.description - def _calculate_equipment_price(self, equipment) -> int: + def _calculate_equipment_price(self, equipment: EquipmentDefinition) -> int: """根据装备品质和属性计算价格""" # 基础价格 base_prices = { @@ -514,9 +510,6 @@ class WPSCombatBase(WPSAPI): def _register_adventure_seeds_to_garden(self) -> None: """注册冒险种子到菜园系统""" - if not GARDEN_SYSTEM_AVAILABLE: - return - try: backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem) @@ -584,5 +577,36 @@ class WPSCombatBase(WPSAPI): 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"] diff --git a/Plugins/WPSCrystalSystem/crystal_plugin_base.py b/Plugins/WPSCrystalSystem/crystal_plugin_base.py index 961540f..18ddb21 100644 --- a/Plugins/WPSCrystalSystem/crystal_plugin_base.py +++ b/Plugins/WPSCrystalSystem/crystal_plugin_base.py @@ -3,16 +3,16 @@ from __future__ import annotations from datetime import datetime, timezone -from typing import Dict, List, Mapping, Optional, Tuple -from PWF.Convention.Runtime.Architecture import Architecture +from PWF.Convention.Runtime.Architecture import * from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig from PWF.CoreModules.clock_scheduler import get_clock_scheduler from PWF.CoreModules.plugin_interface import DatabaseModel -from Plugins.WPSAPI import WPSAPI +from Plugins.WPSAPI import GuideEntry, WPSAPI from Plugins.WPSAlchemyGame import WPSAlchemyGame from Plugins.WPSBackpackSystem import BackpackItemTier, WPSBackpackSystem +from Plugins.WPSCombatSystem.combat_models import SPARK_DUST_ITEM_ID from Plugins.WPSStoreSystem import WPSStoreSystem from Plugins.WPSGardenSystem import ( GardenCropDefinition, @@ -32,6 +32,7 @@ from .crystal_models import ( CRYSTAL_TREE_FRUIT_ID, CRYSTAL_TREE_SEED_ID, CRYSTAL_TINT_POWDER_ID, + CRYSTAL_RESONANCE_POWDER_ID, ) from .crystal_service import get_crystal_db_models, get_crystal_service @@ -489,6 +490,7 @@ class WPSCrystalSystem(WPSAPI): self._try_register_wine_recipe(alchemy, crop) self._register_tint_powder_recipe(alchemy) + self._register_resonance_powder_recipe(alchemy) def _safe_register_backpack_item( self, @@ -550,6 +552,20 @@ class WPSCrystalSystem(WPSAPI): f"{ConsoleFrontColor.YELLOW}注册 {CRYSTAL_TREE_FRUIT_ID} 炼金配方失败: {exc}{ConsoleFrontColor.RESET}", ) + def _register_resonance_powder_recipe(self, alchemy: WPSAlchemyGame) -> None: + try: + alchemy.register_recipe( + (SPARK_DUST_ITEM_ID, SPARK_DUST_ITEM_ID, SPARK_DUST_ITEM_ID), + CRYSTAL_RESONANCE_POWDER_ID, + WPSAlchemyGame.ASH_ITEM_ID, + 0.45, + ) + except Exception as exc: # pylint: disable=broad-except + logger.Log( + "Warning", + f"{ConsoleFrontColor.YELLOW}注册闪烁粉尘共鸣配方失败: {exc}{ConsoleFrontColor.RESET}", + ) + def _item_display_name( self, item_id: str,