提交,加入了出门历练功能
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
[Xiuxian]
|
[Xiuxian]
|
||||||
enable = true
|
enable = true
|
||||||
# 指令集合(无需前缀),按核心/经济/信息/社交/门派分类
|
# 指令集合(无需前缀),按核心/经济/信息/社交/门派分类
|
||||||
command = ["修仙帮助", "积分购石", "积分换灵石", "注册修仙", "我的状态", "闭关", "出关", "聚灵", "排行榜", "修仙签到", "坊市", "购买", "乾坤袋", "使用", "服用", "突破", "强行突破", "劫掠", "赠与", "赠送", "创建门派", "加入门派", "退出门派"]
|
command = ["修仙帮助", "积分购石", "积分换灵石", "注册修仙", "我的状态", "闭关", "出关", "聚灵", "排行榜", "修仙签到", "坊市", "购买", "乾坤袋", "使用", "服用", "突破", "强行突破", "劫掠", "赠与", "赠送", "创建门派", "加入门派", "退出门派", "出门历练", "炼丹"]
|
||||||
# 用法提示:命令格式错误时的反馈文本
|
# 用法提示:命令格式错误时的反馈文本
|
||||||
command-format = """
|
command-format = """
|
||||||
📜修仙指令:
|
📜修仙指令:
|
||||||
@@ -19,6 +19,8 @@ command-format = """
|
|||||||
乾坤袋
|
乾坤袋
|
||||||
使用 物品名
|
使用 物品名
|
||||||
服用 回气丹
|
服用 回气丹
|
||||||
|
出门历练
|
||||||
|
炼丹 物品 数量
|
||||||
突破 - 需要丹药
|
突破 - 需要丹药
|
||||||
强行突破 - 不需要丹药
|
强行突破 - 不需要丹药
|
||||||
劫掠 - 抢劫其他门派弟子
|
劫掠 - 抢劫其他门派弟子
|
||||||
@@ -49,6 +51,10 @@ buy_seconds = 5
|
|||||||
gift_seconds = 10
|
gift_seconds = 10
|
||||||
points_to_stone_seconds = 10
|
points_to_stone_seconds = 10
|
||||||
|
|
||||||
|
# 副本与炼丹冷却
|
||||||
|
expedition_seconds = 1800
|
||||||
|
alchemy_seconds = 600
|
||||||
|
|
||||||
# 修为结算参数:基础速率(每小时),灵根乘数(名称:倍率)
|
# 修为结算参数:基础速率(每小时),灵根乘数(名称:倍率)
|
||||||
[Xiuxian.cultivation]
|
[Xiuxian.cultivation]
|
||||||
base_rate_per_hour = 100
|
base_rate_per_hour = 100
|
||||||
@@ -102,3 +108,26 @@ realm_key = "xiuxian:zset:leaderboard:realm"
|
|||||||
|
|
||||||
[Xiuxian.points_exchange]
|
[Xiuxian.points_exchange]
|
||||||
point_to_stone_rate = 10
|
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"
|
||||||
|
]
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ class XiuxianPlugin(MessagePluginInterface):
|
|||||||
"签到": rate_cfg.get("signin_seconds", 86400),
|
"签到": rate_cfg.get("signin_seconds", 86400),
|
||||||
"坊市": rate_cfg.get("shop_seconds", 10),
|
"坊市": rate_cfg.get("shop_seconds", 10),
|
||||||
"购买": rate_cfg.get("buy_seconds", 5),
|
"购买": rate_cfg.get("buy_seconds", 5),
|
||||||
|
"出售": rate_cfg.get("buy_seconds", 5),
|
||||||
"乾坤袋": rate_cfg.get("bag_seconds", 3),
|
"乾坤袋": rate_cfg.get("bag_seconds", 3),
|
||||||
"突破": rate_cfg.get("break_seconds", 60),
|
"突破": rate_cfg.get("break_seconds", 60),
|
||||||
"强行突破": rate_cfg.get("force_break_seconds", 60),
|
"强行突破": rate_cfg.get("force_break_seconds", 60),
|
||||||
@@ -300,6 +301,8 @@ class XiuxianPlugin(MessagePluginInterface):
|
|||||||
"退出门派": 604800,
|
"退出门派": 604800,
|
||||||
"积分购石": rate_cfg.get("points_to_stone_seconds", 10),
|
"积分购石": rate_cfg.get("points_to_stone_seconds", 10),
|
||||||
"积分换灵石": 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", {})
|
cult_cfg = cfg.get("cultivation", {})
|
||||||
@@ -329,6 +332,38 @@ class XiuxianPlugin(MessagePluginInterface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.warning(f"解析商品配置失败: {s}, 错误: {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", {})
|
pts_cfg = cfg.get("points_exchange", {})
|
||||||
self.point_to_stone_rate = int(pts_cfg.get("point_to_stone_rate", 10))
|
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)
|
return await self._cmd_shop(bot, sender, roomid)
|
||||||
if cmd == "购买":
|
if cmd == "购买":
|
||||||
return await self._cmd_buy(bot, sender, roomid, content)
|
return await self._cmd_buy(bot, sender, roomid, content)
|
||||||
|
if cmd == "出售":
|
||||||
|
return await self._cmd_sell(bot, sender, roomid, content)
|
||||||
if cmd == "乾坤袋":
|
if cmd == "乾坤袋":
|
||||||
return await self._cmd_bag(bot, sender, roomid)
|
return await self._cmd_bag(bot, sender, roomid)
|
||||||
if cmd in ("使用", "服用"):
|
if cmd in ("使用", "服用"):
|
||||||
return await self._cmd_use(bot, sender, roomid, content)
|
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 == "突破":
|
if cmd == "突破":
|
||||||
return await self._cmd_breakthrough(bot, sender, roomid)
|
return await self._cmd_breakthrough(bot, sender, roomid)
|
||||||
if cmd == "强行突破":
|
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("炼丹 物品 数量")
|
||||||
lines.append("使用 物品名")
|
lines.append("使用 物品名")
|
||||||
lines.append("服用 回气丹")
|
lines.append("服用 回气丹")
|
||||||
lines.append("赠与 目标wxid 数量")
|
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)
|
self.revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 10)
|
||||||
return True, "购买成功"
|
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]:
|
async def _cmd_bag(self, bot: WechatAPIClient, sender: str, roomid: str) -> Tuple[bool, str]:
|
||||||
player = self._get_player(sender, roomid or "")
|
player = self._get_player(sender, roomid or "")
|
||||||
if not player:
|
if not player:
|
||||||
@@ -1023,6 +1105,82 @@ class XiuxianPlugin(MessagePluginInterface):
|
|||||||
self._rate_set(sender, roomid or "", "背包")
|
self._rate_set(sender, roomid or "", "背包")
|
||||||
return True, "背包"
|
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]:
|
async def _cmd_use(self, bot: WechatAPIClient, sender: str, roomid: str, content: str) -> Tuple[bool, str]:
|
||||||
item_name = content.strip()
|
item_name = content.strip()
|
||||||
if not item_name:
|
if not item_name:
|
||||||
@@ -1117,6 +1275,66 @@ class XiuxianPlugin(MessagePluginInterface):
|
|||||||
return False, "灵根提升失败"
|
return False, "灵根提升失败"
|
||||||
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]:
|
async def _cmd_breakthrough(self, bot: WechatAPIClient, sender: str, roomid: str) -> Tuple[bool, str]:
|
||||||
player = self._get_player(sender, roomid or "")
|
player = self._get_player(sender, roomid or "")
|
||||||
if not player:
|
if not player:
|
||||||
|
|||||||
Reference in New Issue
Block a user