"""战斗系统基础插件类""" from __future__ import annotations from typing import Dict, List, Type, Union from PWF.Convention.Runtime.Architecture import Architecture 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.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, 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 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, # 注:不强制依赖 WPSGardenSystem,果酒buff配置在 combat_models.py 中 ] 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. 注册冒险种子到菜园系统(如果可用) if GARDEN_SYSTEM_AVAILABLE: self._register_adventure_seeds_to_garden() # 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) -> 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: """注册冒险种子到菜园系统""" if not GARDEN_SYSTEM_AVAILABLE: return 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}" ) __all__ = ["WPSCombatBase"]