"""冒险系统插件 - PVE冒险模式""" from __future__ import annotations from datetime import datetime from typing import Optional, Sequence from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig from Plugins.WPSAPI import GuideEntry, GuideSection from .combat_plugin_base import WPSCombatBase from .combat_models import CombatConfig logger: ProjectConfig = ProjectConfig() class WPSCombatAdventure(WPSCombatBase): """冒险系统插件""" def get_guide_subtitle(self) -> str: return "阶段式 PVE 冒险,产出装备与素材" def collect_command_entries(self) -> Sequence[GuideEntry]: return ( GuideEntry( title="冒险 开始", identifier="冒险 开始 [食物...]", description="启动第 1 阶段冒险,可额外投入食物缩短时间并提升成功率。", metadata={"别名": "start"}, icon="🚀", tags=("阶段1", "需未受伤"), details=[ { "type": "steps", "items": [ "检查自身状态,确认未处于受伤或其他冒险中。", "可选:准备食物 `food_id` 列表;每个食物将被立即消耗。", "系统计算预计耗时、成功率并生成冒险记录。", "创建时钟任务,时间到后自动结算。", ], }, "结算时会根据装备强度与运势发放奖励。", ], ), GuideEntry( title="继续冒险", identifier="冒险 / 继续冒险", description="在已有链路下进入下一阶段或查看剩余时间。", metadata={"别名": "adventure / continue"}, icon="⏱️", tags=("阶段推进",), details=[ { "type": "steps", "items": [ "查询当前冒险记录,若已在倒计时阶段则返回剩余时间。", "如冒险链完成并准备进入下一阶段,可再次投喂食物并启动。", "系统保持阶段编号,累计奖励与时间。", ], } ], ), GuideEntry( title="冒险 停止", identifier="冒险 停止 / 放弃", description="放弃当前冒险链,结算状态并清空倒计时。", metadata={"别名": "放弃 / 停止"}, icon="🛑", tags=("风险",), details=[ "放弃后当前阶段奖励作废,未来可从第 1 阶段重开。", "若系统已开始结算,则命令会提示等待完成。", ], ), ) def collect_guide_entries(self) -> Sequence[GuideEntry]: return ( GuideEntry( title="冒险阶段", description="Adventure 链路包含多个阶段,难度逐渐提升,奖励也随之增加。", icon="⚙️", details=[ { "type": "list", "items": [ "阶段时间:基础 15 分钟,最高不超过 24 小时。", "投喂食物:每份食物提供额外时间与成功率加成。", "装备影响:装备强度越高,成功率越高且时间越短。", "运势影响:由运势系统提供当前整点的幸运值,用于修正成功率。", ], } ], ), GuideEntry( title="奖励构成", description="冒险完成后会发放积分、装备、材料、纪念品等。", icon="🎁", details=[ { "type": "list", "items": [ "基础积分奖励随阶段提升。", "装备掉落根据稀有度加权抽取。", "部分阶段触发事件可获得药剂、种子或纪念品。", ], } ], ), ) def collect_additional_sections(self) -> Sequence[GuideSection]: sections = list(super().collect_additional_sections()) adventure_config_entries: List[GuideEntry] = [] config_labels = { "combat_adventure_base_time": "阶段起始时间 (分钟)", "combat_adventure_max_time": "时间上限 (分钟)", "combat_food_support_time": "单个食物提供时间 (分钟)", "combat_adventure_base_success_rate": "基础成功率", "combat_adventure_stage_penalty": "阶段成功率衰减", "combat_adventure_equipment_coeff": "装备强度加成系数", "combat_adventure_fortune_coeff": "运势加成系数", "combat_time_reduction_divisor": "时间缩减除数", } for key, label in config_labels.items(): value = CombatConfig.get(key) adventure_config_entries.append( GuideEntry( title=label, identifier=key, description=f"{value}", category="配置项", icon="📐", ) ) sections.append( GuideSection( title="冒险参数一览", entries=adventure_config_entries, layout="grid", section_id="adventure-config", description="核心公式与系数决定冒险耗时、成功率与奖励结构。", ) ) return tuple(sections) def is_enable_plugin(self) -> bool: return True def wake_up(self) -> None: super().wake_up() logger.Log( "Info", f"{ConsoleFrontColor.GREEN}WPSCombatAdventure 插件已加载{ConsoleFrontColor.RESET}" ) self.register_plugin("冒险") self.register_plugin("adventure") self.register_plugin("继续冒险") # 恢复过期冒险 service = self.service() service.recover_overdue_adventures() async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]: """ 处理冒险命令 命令格式: - 冒险 开始 [食物1] [食物2] ... - 继续冒险 [食物1] [食物2] ... """ message = self.parse_message_after_at(message).strip() tokens = message.split() if not tokens: # 默认视为继续冒险,支持直接命令 `继续冒险` return await self._handle_continue_adventure(chat_id, user_id, []) # 判断是开始新冒险、继续或结束 command = tokens[0].lower() if command in ["放弃", "停止"]: return await self._handle_finish_adventure(chat_id, user_id) if command in ["开始", "start"]: # 开始新冒险(第1阶段) food_items = tokens[1:] if len(tokens) > 1 else [] return await self._handle_start_adventure(chat_id, user_id, food_items) elif command in ["继续", "continue"]: food_items = tokens[1:] if len(tokens) > 1 else [] return await self._handle_continue_adventure(chat_id, user_id, food_items) else: # 默认视为继续冒险,tokens 即为食物列表 food_items = tokens return await self._handle_continue_adventure(chat_id, user_id, food_items) async def _handle_start_adventure( self, chat_id: int, user_id: int, food_items: list ) -> Optional[str]: """处理开始新冒险""" service = self.service() # 第1阶段 stage = 1 success, msg, adventure_id = service.start_adventure( user_id=user_id, chat_id=chat_id, stage=stage, food_items=food_items, register_callback=self ) return await self.send_markdown_message(msg, chat_id, user_id) async def _handle_finish_adventure( self, chat_id: int, user_id: int ) -> Optional[str]: """处理结束冒险(不再继续)""" service = self.service() status = service.get_player_status(user_id) if status.get("current_adventure_id"): _success, msg = service.abort_current_adventure(user_id) return await self.send_markdown_message(msg, chat_id, user_id) last_record = service.get_last_adventure_record(user_id) if not last_record: return await self.send_markdown_message( "ℹ️ 你尚未开始冒险,可使用 `冒险 开始`。", chat_id, user_id ) record_status = last_record["status"] stage = last_record["stage"] if record_status == "success": return await self.send_markdown_message( ( f"✅ 第 {stage} 阶段冒险已完成并发放奖励。\n" "若不再继续,冒险链已自然结束;未来可随时使用 `冒险 开始` 重新开启。" ), chat_id, user_id ) if record_status == "failed": return await self.send_markdown_message( "❌ 最近一次冒险失败,奖励已结算且需要治疗或重新开始。", chat_id, user_id ) if record_status == "abandoned": return await self.send_markdown_message( "ℹ️ 上一次冒险已放弃,无需额外操作。如需重新开启,请使用 `冒险 开始`。", chat_id, user_id ) return await self.send_markdown_message( f"ℹ️ 最近一次冒险状态为 {record_status},无需执行放弃指令。", chat_id, user_id ) async def _handle_continue_adventure( self, chat_id: int, user_id: int, food_items: list ) -> Optional[str]: """处理继续冒险""" service = self.service() # 获取当前冒险状态 status = service.get_player_status(user_id) current_adventure_id = status.get("current_adventure_id") if current_adventure_id: adventure = service.get_adventure_by_id(current_adventure_id) if not adventure: return await self.send_markdown_message( ( "❌ 你已经在冒险中,但未找到相关记录,请稍后重试或联系管理员。" f"\n- 冒险ID:{current_adventure_id}" ), chat_id, user_id ) expected_end_str = adventure.get("expected_end_time") try: expected_end = datetime.fromisoformat(expected_end_str) if expected_end_str else None except (TypeError, ValueError): expected_end = None if expected_end: remaining_seconds = (expected_end - datetime.now()).total_seconds() if remaining_seconds <= 0: message = [ "⏳ 当前冒险已进入结算,请稍候等待系统发放结果。", f"- 冒险ID:{current_adventure_id}", f"- 预计完成:{expected_end.strftime('%Y-%m-%d %H:%M')}" ] else: remaining_minutes = int((remaining_seconds + 59) // 60) remaining_text = ( "不到 1 分钟" if remaining_minutes == 0 else f"约 {remaining_minutes} 分钟" ) message = [ "⏳ 你已经在冒险中,请等待当前冒险完成。", f"- 冒险ID:{current_adventure_id}", f"- 剩余时间:{remaining_text}", f"- 预计完成:{expected_end.strftime('%Y-%m-%d %H:%M')}" ] else: message = [ "❌ 你已经在冒险中,但无法解析预计结束时间,请稍后重试。", f"- 冒险ID:{current_adventure_id}" ] return await self.send_markdown_message("\n".join(message), chat_id, user_id) last_record = service.get_last_adventure_record(user_id) if not last_record: return await self.send_markdown_message( "❌ 你还没有完成任何冒险,请使用 `冒险 开始 [食物...]` 开始第1阶段", chat_id, user_id ) if last_record["status"] == "failed": return await self.send_markdown_message( "❌ 最近一次冒险失败,冒险已结束。请先使用 `冒险 开始 [食物...]` 重新从第1阶段开始", chat_id, user_id ) if last_record["status"] == "abandoned": return await self.send_markdown_message( "⚠️ 最近一次冒险已被你放弃,需要重新从第1阶段开始", chat_id, user_id ) if last_record["status"] != "success": return await self.send_markdown_message( f"❌ 最近一次冒险状态为 {last_record['status']},无法继续。请使用 `冒险 开始 [食物...]` 重新冒险", chat_id, user_id ) # 下一阶段 next_stage = last_record["stage"] + 1 success, msg, adventure_id = service.start_adventure( user_id=user_id, chat_id=chat_id, stage=next_stage, food_items=food_items, register_callback=self ) return await self.send_markdown_message(msg, chat_id, user_id) async def _settle_adventure_callback( self, adventure_id: int, user_id: int, chat_id: int ) -> None: """冒险结算回调(时钟任务)""" service = self.service() success, msg, rewards = service.settle_adventure(adventure_id) # 发送结算消息 await self.send_markdown_message(msg, chat_id, user_id) def _help_message(self) -> str: """帮助信息""" return """# 🗺️ 冒险系统 **命令格式:** - `冒险 开始 [食物1] [食物2] ...`:开始第1阶段冒险 - `继续冒险 [食物1] [食物2] ...` 或 `冒险 继续 ...`:继续下一阶段 - `冒险 放弃`:结束当前冒险链(阶段奖励已结算) - `停止冒险` / `放弃冒险`:在冒险进行中立即终止当前阶段(无奖励) **说明:** - 每个阶段耗时翻倍(15min → 30min → 60min...) - 食物(果酒)是可选的,可提供buff加成(时间缩减、收益提升等) - 阶段结束时立即判定成功/失败并发放奖励 - 完成后可选择继续下一阶段或直接放弃 - 冒险失败会受伤,需要消耗100积分治疗 **示例:** - `冒险 开始`:不使用食物开始第1阶段 - `冒险 开始 薄荷果酒`:使用1个薄荷果酒(时间缩减10%) - `继续冒险 银杏果酒`:继续下一阶段并使用银杏果酒 - `冒险 放弃`:冒险阶段已结算但不再继续 """ class WPSCombatAdventureAbort(WPSCombatBase): """冒险放弃指令插件""" def is_enable_plugin(self) -> bool: return True def wake_up(self) -> None: super().wake_up() logger.Log( "Info", f"{ConsoleFrontColor.GREEN}WPSCombatAdventureAbort 插件已加载{ConsoleFrontColor.RESET}" ) self.register_plugin("停止冒险") self.register_plugin("放弃冒险") self.register_plugin("abort_adventure") async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]: """直接放弃当前冒险""" service = self.service() status = service.get_player_status(user_id) if status.get("current_adventure_id"): _success, msg = service.abort_current_adventure(user_id) return await self.send_markdown_message(msg, chat_id, user_id) last_record = service.get_last_adventure_record(user_id) if not last_record: return await self.send_markdown_message( "ℹ️ 你尚未开始冒险,可使用 `冒险 开始`。", chat_id, user_id ) record_status = last_record["status"] stage = last_record["stage"] if record_status == "success": return await self.send_markdown_message( ( f"✅ 第 {stage} 阶段冒险已完成并发放奖励。\n" "若不再继续,冒险链已自然结束;未来可随时使用 `冒险 开始` 重新开启。" ), chat_id, user_id ) if record_status == "failed": return await self.send_markdown_message( "❌ 最近一次冒险失败,奖励已结算且需要治疗或重新开始。", chat_id, user_id ) if record_status == "abandoned": return await self.send_markdown_message( "ℹ️ 上一次冒险已放弃,无需额外操作。如需重新开启,请使用 `冒险 开始`。", chat_id, user_id ) return await self.send_markdown_message( f"ℹ️ 最近一次冒险状态为 {record_status},无需执行放弃指令。", chat_id, user_id ) __all__ = ["WPSCombatAdventure", "WPSCombatAdventureAbort"]