diff --git a/plugins/xiuxian/config.toml b/plugins/xiuxian/config.toml index 5489300..18b77d1 100644 --- a/plugins/xiuxian/config.toml +++ b/plugins/xiuxian/config.toml @@ -3,7 +3,7 @@ [Xiuxian] enable = true # 指令集合(无需前缀),按核心/经济/信息/社交/门派分类 -command = ["修仙帮助", "积分购石", "积分换灵石", "注册修仙", "我的状态", "闭关", "出关", "聚灵", "排行榜", "修仙签到", "坊市", "购买", "乾坤袋", "使用", "服用", "突破", "强行突破", "劫掠", "赠与", "赠送", "创建门派", "加入门派", "退出门派"] +command = ["修仙帮助", "积分购石", "积分换灵石", "注册修仙", "我的状态", "闭关", "出关", "聚灵", "排行榜", "修仙签到", "坊市", "购买", "乾坤袋", "使用", "服用", "突破", "强行突破", "劫掠", "赠与", "赠送", "创建门派", "加入门派", "退出门派", "出门历练", "炼丹"] # 用法提示:命令格式错误时的反馈文本 command-format = """ 📜修仙指令: @@ -19,6 +19,8 @@ command-format = """ 乾坤袋 使用 物品名 服用 回气丹 +出门历练 +炼丹 物品 数量 突破 - 需要丹药 强行突破 - 不需要丹药 劫掠 - 抢劫其他门派弟子 @@ -49,6 +51,10 @@ buy_seconds = 5 gift_seconds = 10 points_to_stone_seconds = 10 +# 副本与炼丹冷却 +expedition_seconds = 1800 +alchemy_seconds = 600 + # 修为结算参数:基础速率(每小时),灵根乘数(名称:倍率) [Xiuxian.cultivation] base_rate_per_hour = 100 @@ -102,3 +108,26 @@ realm_key = "xiuxian:zset:leaderboard:realm" [Xiuxian.points_exchange] point_to_stone_rate = 10 + +# 材料定义 +[Xiuxian.materials] +items = [ + "灵草:T1", + "玄土:T1", + "青木藤:T1", + "赤炎石:T2", + "寒晶:T2", + "紫电砂:T2", + "太一金精:T3", + "昆仑玉髓:T3" +] + +# 炼丹配方:物品:材料*数量,材料*数量;灵石消耗;成功率 +[Xiuxian.recipes] +items = [ + "聚灵符:灵草*3,玄土*1;10;0.9", + "回气丹:灵草*5,玄土*2;20;0.85", + "洗髓丹:灵草*10,玄土*5;50;0.6", + "改灵丹:赤炎石*8,寒晶*8;100;0.3", + "天灵露:太一金精*4,昆仑玉髓*4;200;0.1" +] diff --git a/plugins/xiuxian/main.py b/plugins/xiuxian/main.py index 7e06c40..41550e0 100644 --- a/plugins/xiuxian/main.py +++ b/plugins/xiuxian/main.py @@ -289,6 +289,7 @@ class XiuxianPlugin(MessagePluginInterface): "签到": rate_cfg.get("signin_seconds", 86400), "坊市": rate_cfg.get("shop_seconds", 10), "购买": rate_cfg.get("buy_seconds", 5), + "出售": rate_cfg.get("buy_seconds", 5), "乾坤袋": rate_cfg.get("bag_seconds", 3), "突破": rate_cfg.get("break_seconds", 60), "强行突破": rate_cfg.get("force_break_seconds", 60), @@ -300,6 +301,8 @@ class XiuxianPlugin(MessagePluginInterface): "退出门派": 604800, "积分购石": rate_cfg.get("points_to_stone_seconds", 10), "积分换灵石": rate_cfg.get("points_to_stone_seconds", 10), + "出门历练": rate_cfg.get("expedition_seconds", 1800), + "炼丹": rate_cfg.get("alchemy_seconds", 600), } cult_cfg = cfg.get("cultivation", {}) @@ -329,6 +332,38 @@ class XiuxianPlugin(MessagePluginInterface): except Exception as e: self.LOG.warning(f"解析商品配置失败: {s}, 错误: {e}") + materials_cfg = cfg.get("materials", {}) + self.material_tier = {} + self.materials_by_tier = {"T1": [], "T2": [], "T3": []} + for m in materials_cfg.get("items", []): + try: + n, tier = m.split(":") + self.material_tier[n] = tier + if tier in self.materials_by_tier: + self.materials_by_tier[tier].append(n) + except Exception as e: + self.LOG.warning(f"解析材料配置失败: {m}, 错误: {e}") + + recipes_cfg = cfg.get("recipes", {}) + self.recipes = {} + for r in recipes_cfg.get("items", []): + try: + name, rest = r.split(":") + mats_part, stone_part, rate_part = rest.split(";") + mats = {} + for kv in mats_part.split(","): + kv = kv.strip() + if "*" in kv: + mn, q = kv.split("*") + elif "x" in kv: + mn, q = kv.split("x") + else: + mn, q = kv, "1" + mats[mn.strip()] = int(q.strip()) + self.recipes[name] = {"materials": mats, "stone": int(stone_part.strip()), "rate": float(rate_part.strip())} + except Exception as e: + self.LOG.warning(f"解析配方失败: {r}, 错误: {e}") + pts_cfg = cfg.get("points_exchange", {}) self.point_to_stone_rate = int(pts_cfg.get("point_to_stone_rate", 10)) @@ -494,10 +529,16 @@ class XiuxianPlugin(MessagePluginInterface): return await self._cmd_shop(bot, sender, roomid) if cmd == "购买": return await self._cmd_buy(bot, sender, roomid, content) + if cmd == "出售": + return await self._cmd_sell(bot, sender, roomid, content) if cmd == "乾坤袋": return await self._cmd_bag(bot, sender, roomid) if cmd in ("使用", "服用"): return await self._cmd_use(bot, sender, roomid, content) + if cmd == "出门历练": + return await self._cmd_expedition(bot, sender, roomid) + if cmd == "炼丹": + return await self._cmd_alchemy(bot, sender, roomid, content) if cmd == "突破": return await self._cmd_breakthrough(bot, sender, roomid) if cmd == "强行突破": @@ -593,6 +634,9 @@ class XiuxianPlugin(MessagePluginInterface): lines.append("注册修仙 道号") lines.append("聚灵 数量") lines.append("购买 物品 数量") + lines.append("出售 物品 数量") + lines.append("出门历练") + lines.append("炼丹 物品 数量") lines.append("使用 物品名") lines.append("服用 回气丹") lines.append("赠与 目标wxid 数量") @@ -997,6 +1041,44 @@ class XiuxianPlugin(MessagePluginInterface): self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) return True, "购买成功" + async def _cmd_sell(self, bot: WechatAPIClient, sender: str, roomid: str, content: str) -> Tuple[bool, str]: + parts = content.split() + if len(parts) < 2: + return False, "命令格式错误" + item_name = parts[0] + try: + qty = int(parts[1]) + except Exception: + return False, "命令格式错误" + player = self._get_player(sender, roomid or "") + if not player: + return False, "未注册" + player = self._check_status_update(player) + inv = player.get("inventory") or {} + have = int(inv.get(item_name, 0)) + if qty <= 0 or have < qty: + return False, "物品不足" + item = next((i for i in self.shop_items if i["name"] == item_name), None) + if not item: + return False, "商品不存在" + sell_ratio = 0.5 + revenue = int(item["price"] * qty * sell_ratio) + inv[item_name] = have - qty + player["inventory"] = inv + player["spirit_stone"] = int(player.get("spirit_stone", 0)) + revenue + if self.xdb: + try: + self.xdb.remove_item(sender, item_name, qty) + self.xdb.update_player_fields(sender, {"spirit_stone": player["spirit_stone"]}) + except Exception: + pass + self._save_player(player) + self._rate_set(sender, roomid or "", "出售") + client_msg_id, create_time, new_msg_id = await bot.send_text_message(roomid or sender, f"✅ 出售成功,{item_name} × {qty},获得灵石{revenue}", sender) + if self.revoke: + self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) + return True, "出售成功" + async def _cmd_bag(self, bot: WechatAPIClient, sender: str, roomid: str) -> Tuple[bool, str]: player = self._get_player(sender, roomid or "") if not player: @@ -1023,6 +1105,82 @@ class XiuxianPlugin(MessagePluginInterface): self._rate_set(sender, roomid or "", "背包") return True, "背包" + async def _cmd_expedition(self, bot: WechatAPIClient, sender: str, roomid: str) -> Tuple[bool, str]: + player = self._get_player(sender, roomid or "") + if not player: + return False, "未注册" + player = self._check_status_update(player) + st = player.get("status", "Idle") + if st in ("Cultivating", "Injured"): + return False, "状态不可历练" + prefix, layer = self._parse_realm(player.get("realm", "炼气1层")) + rs = self.realm_score_map.get(prefix, 0) + realm_mult = 1.0 + (float(rs) / 100.0) + layer_bonus = 1.0 + (max(0, (layer or 1) - 1) * 0.02) + d = realm_mult * layer_bonus + jack_p = min(0.05 * d, 0.25) + succ_p = min(0.45 * d, 0.8) + back_p = max(0.10 / d, 0.02) + r = random.random() + stones_gain = 0 + mats_gain: Dict[str, int] = {} + if r < jack_p: + stones_gain = int(random.uniform(2000, 5000) * d) + n = random.randint(2, 3) + tiers = ["T3", "T2"] if rs >= 40 else ["T2", "T1"] + for _ in range(n): + tier = tiers[0] if random.random() < 0.6 else tiers[1] + pool = self.materials_by_tier.get(tier, []) + if not pool: + continue + name = random.choice(pool) + mats_gain[name] = mats_gain.get(name, 0) + 1 + elif r < jack_p + succ_p: + stones_gain = int(random.uniform(200, 800) * d) + n = random.randint(1, 2) + tiers = ["T2", "T1"] + for _ in range(n): + tier = tiers[0] if random.random() < min(0.3 * d, 0.7) else tiers[1] + pool = self.materials_by_tier.get(tier, []) + if not pool: + continue + name = random.choice(pool) + mats_gain[name] = mats_gain.get(name, 0) + 1 + elif r < jack_p + succ_p + back_p: + player["status"] = "Injured" + player["status_until"] = (datetime.now(timezone.utc) + timedelta(minutes=30)).isoformat() + self._save_player(player) + self._rate_set(sender, roomid or "", "出门历练") + client_msg_id, create_time, new_msg_id = await bot.send_text_message(roomid or sender, "❌ 历练失败,灵气反噬,受伤30分钟", sender) + if self.revoke: + self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) + return False, "历练失败" + else: + self._rate_set(sender, roomid or "", "出门历练") + client_msg_id, create_time, new_msg_id = await bot.send_text_message(roomid or sender, "⚠️ 历练无所得", sender) + if self.revoke: + self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) + return True, "历练无所得" + player["spirit_stone"] = int(player.get("spirit_stone", 0)) + stones_gain + inv = player.get("inventory") or {} + for k, v in mats_gain.items(): + inv[k] = int(inv.get(k, 0)) + v + player["inventory"] = inv + if self.xdb: + try: + self.xdb.update_player_fields(sender, {"spirit_stone": player["spirit_stone"]}) + for k, v in mats_gain.items(): + self.xdb.add_item(sender, k, "材料", v) + except Exception: + pass + self._save_player(player) + self._rate_set(sender, roomid or "", "出门历练") + mats_text = ", ".join([f"{k}×{v}" for k, v in mats_gain.items()]) if mats_gain else "无" + client_msg_id, create_time, new_msg_id = await bot.send_text_message(roomid or sender, f"✅ 历练成功,获得灵石{stones_gain},材料:{mats_text}", sender) + if self.revoke: + self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) + return True, "历练成功" + async def _cmd_use(self, bot: WechatAPIClient, sender: str, roomid: str, content: str) -> Tuple[bool, str]: item_name = content.strip() if not item_name: @@ -1117,6 +1275,66 @@ class XiuxianPlugin(MessagePluginInterface): return False, "灵根提升失败" return False, "不可使用的物品" + async def _cmd_alchemy(self, bot: WechatAPIClient, sender: str, roomid: str, content: str) -> Tuple[bool, str]: + parts = content.split() + if len(parts) < 2: + return False, "命令格式错误" + item_name = parts[0] + try: + qty = int(parts[1]) + except Exception: + return False, "命令格式错误" + player = self._get_player(sender, roomid or "") + if not player: + return False, "未注册" + player = self._check_status_update(player) + recipe = self.recipes.get(item_name) + if not recipe: + return False, "配方不存在" + total_stone = recipe["stone"] * qty + if int(player.get("spirit_stone", 0)) < total_stone: + return False, "灵石不足" + inv = player.get("inventory") or {} + need = {} + for mk, mv in recipe["materials"].items(): + need[mk] = mv * qty + for mk, nv in need.items(): + if int(inv.get(mk, 0)) < nv: + return False, "材料不足" + for mk, nv in need.items(): + inv[mk] = int(inv.get(mk, 0)) - nv + success = 0 + fail = 0 + item_type = next((i["type"] for i in self.shop_items if i["name"] == item_name), "丹药") + for _ in range(qty): + if random.random() < float(recipe["rate"]): + success += 1 + else: + fail += 1 + player["spirit_stone"] = int(player.get("spirit_stone", 0)) - total_stone + inv[item_name] = int(inv.get(item_name, 0)) + success + player["inventory"] = inv + if fail > 0: + player["status"] = "Unstable_Qi" + player["status_until"] = (datetime.now(timezone.utc) + timedelta(minutes=int(self.unstable_qi_minutes))).isoformat() + if self.xdb: + try: + self.xdb.update_player_fields(sender, {"spirit_stone": player["spirit_stone"], "status": player.get("status"), "status_until": player.get("status_until")}) + if success > 0: + self.xdb.add_item(sender, item_name, item_type, success) + for mk, nv in need.items(): + if nv > 0: + self.xdb.remove_item(sender, mk, nv) + except Exception: + pass + self._save_player(player) + self._rate_set(sender, roomid or "", "炼丹") + msg = f"✅ 炼丹完成,成功{success},失败{fail}" + client_msg_id, create_time, new_msg_id = await bot.send_text_message(roomid or sender, msg, sender) + if self.revoke: + self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10) + return True, "炼丹完成" + async def _cmd_breakthrough(self, bot: WechatAPIClient, sender: str, roomid: str) -> Tuple[bool, str]: player = self._get_player(sender, roomid or "") if not player: