开始添加炼金冷却
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
import random
|
import random
|
||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from typing import Dict, List, Optional, Sequence, Set, Tuple, override
|
from typing import Dict, List, Optional, Sequence, Set, Tuple, override
|
||||||
|
|
||||||
from PWF.Convention.Runtime.Architecture import Architecture
|
from PWF.Convention.Runtime.Architecture import Architecture
|
||||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||||
from PWF.CoreModules.database import get_db
|
from PWF.CoreModules.database import get_db, STATUS_COMPLETED
|
||||||
|
from PWF.CoreModules.plugin_interface import DatabaseModel
|
||||||
|
|
||||||
from .WPSAPI import WPSAPI
|
from .WPSAPI import WPSAPI
|
||||||
from .WPSBackpackSystem import (
|
from .WPSBackpackSystem import (
|
||||||
@@ -60,11 +63,38 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
self._success_index: Dict[str, Set[Tuple[str, str, str]]] = defaultdict(set)
|
self._success_index: Dict[str, Set[Tuple[str, str, str]]] = defaultdict(set)
|
||||||
self._fail_index: Dict[str, Set[Tuple[str, str, str]]] = defaultdict(set)
|
self._fail_index: Dict[str, Set[Tuple[str, str, str]]] = defaultdict(set)
|
||||||
self._fortune_coeff = FORTUNE_COEFF
|
self._fortune_coeff = FORTUNE_COEFF
|
||||||
|
# 从配置读取冷却时间(分钟)
|
||||||
|
from PWF.CoreModules.flags import get_internal_debug
|
||||||
|
cooldown_minutes = logger.FindItem("alchemy_cooldown_minutes", 2)
|
||||||
|
if get_internal_debug():
|
||||||
|
cooldown_minutes = 0
|
||||||
|
self._cooldown_minutes = cooldown_minutes
|
||||||
|
self._cooldown_ms = int(cooldown_minutes * 60 * 1000)
|
||||||
|
logger.SaveProperties()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def dependencies(self) -> List[type]:
|
def dependencies(self) -> List[type]:
|
||||||
return [WPSAPI, WPSBackpackSystem, WPSConfigAPI, WPSFortuneSystem, WPSStoreSystem]
|
return [WPSAPI, WPSBackpackSystem, WPSConfigAPI, WPSFortuneSystem, WPSStoreSystem]
|
||||||
|
|
||||||
|
@override
|
||||||
|
def register_db_model(self) -> DatabaseModel:
|
||||||
|
"""注册炼金记录数据库表"""
|
||||||
|
return DatabaseModel(
|
||||||
|
table_name="alchemy_records",
|
||||||
|
column_defs={
|
||||||
|
"alchemy_id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||||
|
"user_id": "INTEGER NOT NULL",
|
||||||
|
"chat_id": "INTEGER NOT NULL",
|
||||||
|
"alchemy_type": "TEXT NOT NULL",
|
||||||
|
"input_data": "TEXT NOT NULL",
|
||||||
|
"start_time": "TEXT NOT NULL",
|
||||||
|
"expected_end_time": "TEXT NOT NULL",
|
||||||
|
"status": "TEXT NOT NULL",
|
||||||
|
"result_data": "TEXT",
|
||||||
|
"scheduled_task_id": "INTEGER",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def wake_up(self) -> None:
|
def wake_up(self) -> None:
|
||||||
logger.Log(
|
logger.Log(
|
||||||
@@ -74,6 +104,8 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
self.register_plugin("alchemy")
|
self.register_plugin("alchemy")
|
||||||
self.register_plugin("炼金")
|
self.register_plugin("炼金")
|
||||||
self._register_alchemy_items()
|
self._register_alchemy_items()
|
||||||
|
# 恢复过期炼金
|
||||||
|
self.recover_overdue_alchemy()
|
||||||
|
|
||||||
def _register_alchemy_items(self) -> None:
|
def _register_alchemy_items(self) -> None:
|
||||||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||||||
@@ -231,6 +263,11 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
self._help_message(), chat_id, user_id
|
self._help_message(), chat_id, user_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 处理状态查询命令
|
||||||
|
if len(tokens) == 1 and tokens[0] in ["状态", "status"]:
|
||||||
|
response = await self._handle_status_query(chat_id, user_id)
|
||||||
|
return await self.send_markdown_message(response, chat_id, user_id)
|
||||||
|
|
||||||
if len(tokens) == 1 and tokens[0].isdigit():
|
if len(tokens) == 1 and tokens[0].isdigit():
|
||||||
points = int(tokens[0])
|
points = int(tokens[0])
|
||||||
response = await self._handle_point_alchemy(
|
response = await self._handle_point_alchemy(
|
||||||
@@ -262,45 +299,73 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
) -> str:
|
) -> str:
|
||||||
if points <= 0:
|
if points <= 0:
|
||||||
return "❌ 投入积分必须大于 0"
|
return "❌ 投入积分必须大于 0"
|
||||||
|
|
||||||
|
# 检查冷却
|
||||||
|
is_on_cooldown, cooldown_msg = self._check_cooldown(user_id)
|
||||||
|
if is_on_cooldown:
|
||||||
|
return cooldown_msg
|
||||||
|
|
||||||
config_api: WPSConfigAPI = Architecture.Get(WPSConfigAPI)
|
config_api: WPSConfigAPI = Architecture.Get(WPSConfigAPI)
|
||||||
current_points = config_api.get_user_points(user_id)
|
current_points = config_api.get_user_points(user_id)
|
||||||
if current_points < points:
|
if current_points < points:
|
||||||
return f"❌ 积分不足,需要 {points} 分,当前仅有 {current_points} 分"
|
return f"❌ 积分不足,需要 {points} 分,当前仅有 {current_points} 分"
|
||||||
|
|
||||||
|
# 扣除积分
|
||||||
await config_api.adjust_user_points(
|
await config_api.adjust_user_points(
|
||||||
chat_id, user_id, -points, "炼金消耗"
|
chat_id, user_id, -points, "炼金消耗"
|
||||||
)
|
)
|
||||||
|
|
||||||
fortune_system: WPSFortuneSystem = Architecture.Get(WPSFortuneSystem)
|
# 创建炼金记录
|
||||||
fortune_value = fortune_system.get_fortune_value(user_id)
|
input_data = {"type": "point", "points": points}
|
||||||
multiplier, phase_label, phase_text = self._draw_point_multiplier(
|
alchemy_id = self._create_alchemy_record(
|
||||||
fortune_value
|
user_id, chat_id, "point", input_data
|
||||||
)
|
)
|
||||||
reward = int(points * multiplier)
|
|
||||||
if reward:
|
|
||||||
await config_api.adjust_user_points(
|
|
||||||
chat_id, user_id, reward, f"炼金收益({phase_label})"
|
|
||||||
)
|
|
||||||
ash_reward = 0
|
|
||||||
if multiplier == 0.0:
|
|
||||||
ash_reward = min(points // 10, 99)
|
|
||||||
if ash_reward > 0:
|
|
||||||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
|
||||||
backpack.add_item(user_id, self.ASH_ITEM_ID, ash_reward)
|
|
||||||
|
|
||||||
final_points = config_api.get_user_points(user_id)
|
# 注册定时任务
|
||||||
extra_line = ""
|
task_id = None
|
||||||
if ash_reward > 0:
|
if self._cooldown_ms > 0:
|
||||||
extra_line = (
|
task_id = self.register_clock(
|
||||||
f"- 额外获得:{self.ASH_ITEM_NAME} × `{ash_reward}`\n"
|
self._settle_alchemy_callback,
|
||||||
|
self._cooldown_ms,
|
||||||
|
kwargs={
|
||||||
|
"alchemy_id": alchemy_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"chat_id": chat_id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
# 更新记录的任务ID
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"UPDATE alchemy_records SET scheduled_task_id = ? WHERE alchemy_id = ?",
|
||||||
|
(task_id, alchemy_id),
|
||||||
|
)
|
||||||
|
get_db().conn.commit()
|
||||||
|
else:
|
||||||
|
# Debug模式,立即结算
|
||||||
|
success, msg, rewards = self.settle_alchemy(alchemy_id)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
# 计算预计完成时间
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT expected_end_time FROM alchemy_records WHERE alchemy_id = ?",
|
||||||
|
(alchemy_id,),
|
||||||
|
)
|
||||||
|
record = cursor.fetchone()
|
||||||
|
expected_end_time = datetime.fromisoformat(record["expected_end_time"])
|
||||||
|
|
||||||
|
from PWF.CoreModules.flags import get_internal_debug
|
||||||
|
debug_hint = " **[DEBUG模式]**" if get_internal_debug() else ""
|
||||||
|
|
||||||
|
time_str = "立即结算" if self._cooldown_minutes == 0 else f"{self._cooldown_minutes} 分钟"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
"# 🔮 炼金结算\n"
|
f"# ⚗️ 炼金开始{debug_hint}\n"
|
||||||
|
f"- 类型:积分炼金\n"
|
||||||
f"- 投入积分:`{points}`\n"
|
f"- 投入积分:`{points}`\n"
|
||||||
f"- 结果:{phase_text}\n"
|
f"- 预计耗时:{time_str}\n"
|
||||||
f"- 获得倍率:×{multiplier:.1f},返还 `+{reward}` 积分\n"
|
f"- 预计完成:{expected_end_time.strftime('%Y-%m-%d %H:%M')}\n"
|
||||||
f"{extra_line}"
|
f"- 状态:炼金进行中..."
|
||||||
f"- 当前积分:`{final_points}`"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _draw_point_multiplier(
|
def _draw_point_multiplier(
|
||||||
@@ -326,6 +391,11 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
if times > self.MAX_BATCH_TIMES:
|
if times > self.MAX_BATCH_TIMES:
|
||||||
return f"❌ 每次最多只能炼金 {self.MAX_BATCH_TIMES} 次"
|
return f"❌ 每次最多只能炼金 {self.MAX_BATCH_TIMES} 次"
|
||||||
|
|
||||||
|
# 检查冷却
|
||||||
|
is_on_cooldown, cooldown_msg = self._check_cooldown(user_id)
|
||||||
|
if is_on_cooldown:
|
||||||
|
return cooldown_msg
|
||||||
|
|
||||||
resolved: List[BackpackItemDefinition] = []
|
resolved: List[BackpackItemDefinition] = []
|
||||||
for identifier in materials:
|
for identifier in materials:
|
||||||
resolved_item = self._resolve_item(identifier)
|
resolved_item = self._resolve_item(identifier)
|
||||||
@@ -342,15 +412,255 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
f"❌ 材料 `{item.name}` 数量不足,需要 {times} 个,当前仅有 {owned} 个"
|
f"❌ 材料 `{item.name}` 数量不足,需要 {times} 个,当前仅有 {owned} 个"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 扣除材料
|
||||||
for item in resolved:
|
for item in resolved:
|
||||||
current = self._get_user_quantity(user_id, item.item_id)
|
current = self._get_user_quantity(user_id, item.item_id)
|
||||||
backpack.set_item_quantity(
|
backpack.set_item_quantity(
|
||||||
user_id, item.item_id, current - times
|
user_id, item.item_id, current - times
|
||||||
)
|
)
|
||||||
|
|
||||||
recipe = self._recipes.get(tuple(sorted(material_ids)))
|
# 创建炼金记录
|
||||||
|
input_data = {
|
||||||
|
"type": "item",
|
||||||
|
"materials": material_ids,
|
||||||
|
"times": times,
|
||||||
|
}
|
||||||
|
alchemy_id = self._create_alchemy_record(
|
||||||
|
user_id, chat_id, "item", input_data
|
||||||
|
)
|
||||||
|
|
||||||
|
# 注册定时任务
|
||||||
|
task_id = None
|
||||||
|
if self._cooldown_ms > 0:
|
||||||
|
task_id = self.register_clock(
|
||||||
|
self._settle_alchemy_callback,
|
||||||
|
self._cooldown_ms,
|
||||||
|
kwargs={
|
||||||
|
"alchemy_id": alchemy_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"chat_id": chat_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# 更新记录的任务ID
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"UPDATE alchemy_records SET scheduled_task_id = ? WHERE alchemy_id = ?",
|
||||||
|
(task_id, alchemy_id),
|
||||||
|
)
|
||||||
|
get_db().conn.commit()
|
||||||
|
else:
|
||||||
|
# Debug模式,立即结算
|
||||||
|
success, msg, rewards = self.settle_alchemy(alchemy_id)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
# 计算预计完成时间
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT expected_end_time FROM alchemy_records WHERE alchemy_id = ?",
|
||||||
|
(alchemy_id,),
|
||||||
|
)
|
||||||
|
record = cursor.fetchone()
|
||||||
|
expected_end_time = datetime.fromisoformat(record["expected_end_time"])
|
||||||
|
|
||||||
|
from PWF.CoreModules.flags import get_internal_debug
|
||||||
|
debug_hint = " **[DEBUG模式]**" if get_internal_debug() else ""
|
||||||
|
|
||||||
|
time_str = "立即结算" if self._cooldown_minutes == 0 else f"{self._cooldown_minutes} 分钟"
|
||||||
|
material_names = "、".join([item.name for item in resolved])
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"# ⚗️ 炼金开始{debug_hint}\n"
|
||||||
|
f"- 类型:物品炼金\n"
|
||||||
|
f"- 投入材料:{material_names} × {times}\n"
|
||||||
|
f"- 预计耗时:{time_str}\n"
|
||||||
|
f"- 预计完成:{expected_end_time.strftime('%Y-%m-%d %H:%M')}\n"
|
||||||
|
f"- 状态:炼金进行中..."
|
||||||
|
)
|
||||||
|
|
||||||
|
def _resolve_item(
|
||||||
|
self, identifier: str
|
||||||
|
) -> Optional[BackpackItemDefinition]:
|
||||||
|
identifier_lower = 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
|
||||||
|
""",
|
||||||
|
(identifier_lower, identifier_lower),
|
||||||
|
)
|
||||||
|
row = cursor.fetchone()
|
||||||
|
item_id = row["item_id"] if row else identifier.strip()
|
||||||
|
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||||||
|
try:
|
||||||
|
return backpack._get_definition(item_id)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_user_quantity(self, user_id: int, item_id: str) -> int:
|
||||||
|
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||||||
|
for item in backpack.get_user_items(user_id):
|
||||||
|
if item.item_id == item_id:
|
||||||
|
return item.quantity
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _check_cooldown(self, user_id: int) -> Tuple[bool, Optional[str]]:
|
||||||
|
"""检查用户是否在冷却中"""
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT alchemy_id, expected_end_time, alchemy_type
|
||||||
|
FROM alchemy_records
|
||||||
|
WHERE user_id = ? AND status = 'in_progress'
|
||||||
|
ORDER BY start_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
""",
|
||||||
|
(user_id,),
|
||||||
|
)
|
||||||
|
record = cursor.fetchone()
|
||||||
|
|
||||||
|
if not record:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
expected_end = datetime.fromisoformat(record["expected_end_time"])
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
if now >= expected_end:
|
||||||
|
# 已过期,自动结算
|
||||||
|
try:
|
||||||
|
self.settle_alchemy(record["alchemy_id"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.Log(
|
||||||
|
"Error",
|
||||||
|
f"{ConsoleFrontColor.RED}自动结算过期炼金失败: {e}{ConsoleFrontColor.RESET}",
|
||||||
|
)
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# 仍在冷却中
|
||||||
|
remaining = expected_end - now
|
||||||
|
remaining_minutes = int(remaining.total_seconds() / 60) + 1
|
||||||
|
alchemy_type_name = "积分炼金" if record["alchemy_type"] == "point" else "物品炼金"
|
||||||
|
|
||||||
|
return True, (
|
||||||
|
f"❌ 炼金冷却中\n"
|
||||||
|
f"- 上次炼金类型:{alchemy_type_name}\n"
|
||||||
|
f"- 预计完成:{expected_end.strftime('%Y-%m-%d %H:%M')}\n"
|
||||||
|
f"- 剩余时间:约 {remaining_minutes} 分钟\n"
|
||||||
|
f"- 请等待冷却结束后再试"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_alchemy_record(
|
||||||
|
self, user_id: int, chat_id: int, alchemy_type: str, input_data: Dict
|
||||||
|
) -> int:
|
||||||
|
"""创建炼金记录"""
|
||||||
|
start_time = datetime.now()
|
||||||
|
expected_end_time = start_time + timedelta(minutes=self._cooldown_minutes)
|
||||||
|
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO alchemy_records
|
||||||
|
(user_id, chat_id, alchemy_type, input_data, start_time, expected_end_time, status)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, 'in_progress')
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
user_id,
|
||||||
|
chat_id,
|
||||||
|
alchemy_type,
|
||||||
|
json.dumps(input_data),
|
||||||
|
start_time.isoformat(),
|
||||||
|
expected_end_time.isoformat(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
alchemy_id = cursor.lastrowid
|
||||||
|
get_db().conn.commit()
|
||||||
|
return alchemy_id
|
||||||
|
|
||||||
|
def settle_alchemy(self, alchemy_id: int) -> Tuple[bool, str, Optional[Dict]]:
|
||||||
|
"""结算炼金"""
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT * FROM alchemy_records WHERE alchemy_id = ?",
|
||||||
|
(alchemy_id,),
|
||||||
|
)
|
||||||
|
record = cursor.fetchone()
|
||||||
|
|
||||||
|
if not record:
|
||||||
|
return False, "❌ 炼金记录不存在", None
|
||||||
|
|
||||||
|
if record["status"] != "in_progress":
|
||||||
|
return False, f"❌ 炼金已结算(状态:{record['status']})", None
|
||||||
|
|
||||||
|
user_id = record["user_id"]
|
||||||
|
chat_id = record["chat_id"]
|
||||||
|
alchemy_type = record["alchemy_type"]
|
||||||
|
input_data = json.loads(record["input_data"])
|
||||||
|
|
||||||
|
config_api: WPSConfigAPI = Architecture.Get(WPSConfigAPI)
|
||||||
|
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||||||
fortune_system: WPSFortuneSystem = Architecture.Get(WPSFortuneSystem)
|
fortune_system: WPSFortuneSystem = Architecture.Get(WPSFortuneSystem)
|
||||||
fortune_value = fortune_system.get_fortune_value(user_id)
|
fortune_value = fortune_system.get_fortune_value(user_id)
|
||||||
|
|
||||||
|
result_data: Dict = {}
|
||||||
|
message_lines = ["# 🔮 炼金结算\n"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if alchemy_type == "point":
|
||||||
|
# 积分炼金结算
|
||||||
|
points = input_data["points"]
|
||||||
|
multiplier, phase_label, phase_text = self._draw_point_multiplier(
|
||||||
|
fortune_value
|
||||||
|
)
|
||||||
|
reward = int(points * multiplier)
|
||||||
|
|
||||||
|
if reward:
|
||||||
|
config_api.adjust_user_points_sync(
|
||||||
|
user_id, reward, f"炼金收益({phase_label})"
|
||||||
|
)
|
||||||
|
|
||||||
|
ash_reward = 0
|
||||||
|
if multiplier == 0.0:
|
||||||
|
ash_reward = min(points // 10, 99)
|
||||||
|
if ash_reward > 0:
|
||||||
|
backpack.add_item(user_id, self.ASH_ITEM_ID, ash_reward)
|
||||||
|
|
||||||
|
final_points = config_api.get_user_points(user_id)
|
||||||
|
extra_line = ""
|
||||||
|
if ash_reward > 0:
|
||||||
|
extra_line = f"- 额外获得:{self.ASH_ITEM_NAME} × `{ash_reward}`\n"
|
||||||
|
|
||||||
|
message_lines.extend([
|
||||||
|
f"- 投入积分:`{points}`\n",
|
||||||
|
f"- 结果:{phase_text}\n",
|
||||||
|
f"- 获得倍率:×{multiplier:.1f},返还 `+{reward}` 积分\n",
|
||||||
|
extra_line,
|
||||||
|
f"- 当前积分:`{final_points}`",
|
||||||
|
])
|
||||||
|
|
||||||
|
result_data = {
|
||||||
|
"multiplier": multiplier,
|
||||||
|
"reward_points": reward,
|
||||||
|
"ash_reward": ash_reward,
|
||||||
|
}
|
||||||
|
|
||||||
|
elif alchemy_type == "item":
|
||||||
|
# 物品炼金结算
|
||||||
|
materials = input_data["materials"]
|
||||||
|
times = input_data["times"]
|
||||||
|
|
||||||
|
# 解析材料
|
||||||
|
resolved: List[BackpackItemDefinition] = []
|
||||||
|
for material_id in materials:
|
||||||
|
try:
|
||||||
|
definition = backpack._get_definition(material_id)
|
||||||
|
resolved.append(definition)
|
||||||
|
except Exception:
|
||||||
|
# 如果材料不存在,使用ID
|
||||||
|
pass
|
||||||
|
|
||||||
|
recipe = self._recipes.get(tuple(sorted(materials)))
|
||||||
adjusted_rate = (
|
adjusted_rate = (
|
||||||
clamp01(recipe.base_success_rate + fortune_value * self._fortune_coeff)
|
clamp01(recipe.base_success_rate + fortune_value * self._fortune_coeff)
|
||||||
if recipe
|
if recipe
|
||||||
@@ -360,6 +670,7 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
success_count = 0
|
success_count = 0
|
||||||
fail_count = 0
|
fail_count = 0
|
||||||
rewards: Dict[str, int] = {}
|
rewards: Dict[str, int] = {}
|
||||||
|
|
||||||
for _ in range(times):
|
for _ in range(times):
|
||||||
if recipe and random.random() < adjusted_rate:
|
if recipe and random.random() < adjusted_rate:
|
||||||
reward_id = recipe.success_item_id
|
reward_id = recipe.success_item_id
|
||||||
@@ -398,51 +709,153 @@ class WPSAlchemyGame(WPSAPI):
|
|||||||
)
|
)
|
||||||
rewards_block = "\n".join(details) if details else "- (无物品获得)"
|
rewards_block = "\n".join(details) if details else "- (无物品获得)"
|
||||||
|
|
||||||
return (
|
material_names = "、".join([item.name for item in resolved])
|
||||||
"# ⚗️ 物品炼金结果\n"
|
message_lines.extend([
|
||||||
f"- 投入材料:{'、'.join([item.name for item in resolved])} × {times}\n"
|
f"- 投入材料:{material_names} × {times}\n",
|
||||||
f"{success_line}\n"
|
f"{success_line}\n",
|
||||||
f"{fail_line}\n"
|
f"{fail_line}\n",
|
||||||
f"{rate_line}\n"
|
f"{rate_line}\n",
|
||||||
"- 获得物品:\n"
|
"- 获得物品:\n",
|
||||||
f"{rewards_block}"
|
f"{rewards_block}",
|
||||||
|
])
|
||||||
|
|
||||||
|
result_data = {
|
||||||
|
"success_count": success_count,
|
||||||
|
"fail_count": fail_count,
|
||||||
|
"rewards": rewards,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return False, f"❌ 未知的炼金类型:{alchemy_type}", None
|
||||||
|
|
||||||
|
# 更新记录状态
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
UPDATE alchemy_records
|
||||||
|
SET status = 'completed', result_data = ?
|
||||||
|
WHERE alchemy_id = ?
|
||||||
|
""",
|
||||||
|
(json.dumps(result_data), alchemy_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _resolve_item(
|
# 更新定时任务状态
|
||||||
self, identifier: str
|
scheduled_task_id = record.get("scheduled_task_id")
|
||||||
) -> Optional[BackpackItemDefinition]:
|
if scheduled_task_id:
|
||||||
identifier_lower = identifier.strip().lower()
|
try:
|
||||||
|
get_db().update_task_status(int(scheduled_task_id), STATUS_COMPLETED)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
get_db().conn.commit()
|
||||||
|
|
||||||
|
return True, "".join(message_lines), result_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.Log(
|
||||||
|
"Error",
|
||||||
|
f"{ConsoleFrontColor.RED}结算炼金失败: {e}{ConsoleFrontColor.RESET}",
|
||||||
|
)
|
||||||
|
# 标记为失败
|
||||||
|
cursor.execute(
|
||||||
|
"UPDATE alchemy_records SET status = 'failed' WHERE alchemy_id = ?",
|
||||||
|
(alchemy_id,),
|
||||||
|
)
|
||||||
|
get_db().conn.commit()
|
||||||
|
return False, f"❌ 结算失败:{str(e)}", None
|
||||||
|
|
||||||
|
async def _settle_alchemy_callback(
|
||||||
|
self, alchemy_id: int, user_id: int, chat_id: int
|
||||||
|
) -> None:
|
||||||
|
"""炼金结算回调(时钟任务)"""
|
||||||
|
success, msg, rewards = self.settle_alchemy(alchemy_id)
|
||||||
|
await self.send_markdown_message(msg, chat_id, user_id)
|
||||||
|
|
||||||
|
def _get_user_alchemy_status(self, user_id: int) -> Optional[Dict]:
|
||||||
|
"""获取用户当前炼金状态"""
|
||||||
cursor = get_db().conn.cursor()
|
cursor = get_db().conn.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"""
|
"""
|
||||||
SELECT item_id
|
SELECT * FROM alchemy_records
|
||||||
FROM {WPSBackpackSystem.ITEMS_TABLE}
|
WHERE user_id = ? AND status = 'in_progress'
|
||||||
WHERE lower(item_id) = ? OR lower(name) = ?
|
ORDER BY start_time DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
""",
|
""",
|
||||||
(identifier_lower, identifier_lower),
|
(user_id,),
|
||||||
)
|
)
|
||||||
row = cursor.fetchone()
|
record = cursor.fetchone()
|
||||||
item_id = row["item_id"] if row else identifier.strip()
|
return dict(record) if record else None
|
||||||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
|
||||||
try:
|
|
||||||
return backpack._get_definition(item_id)
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _get_user_quantity(self, user_id: int, item_id: str) -> int:
|
async def _handle_status_query(self, chat_id: int, user_id: int) -> str:
|
||||||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
"""处理状态查询"""
|
||||||
for item in backpack.get_user_items(user_id):
|
record = self._get_user_alchemy_status(user_id)
|
||||||
if item.item_id == item_id:
|
|
||||||
return item.quantity
|
if not record:
|
||||||
return 0
|
return (
|
||||||
|
"# ⚗️ 炼金状态\n"
|
||||||
|
"- 状态:无进行中的炼金\n"
|
||||||
|
"- 可以开始新的炼金"
|
||||||
|
)
|
||||||
|
|
||||||
|
alchemy_type = record["alchemy_type"]
|
||||||
|
alchemy_type_name = "积分炼金" if alchemy_type == "point" else "物品炼金"
|
||||||
|
start_time = datetime.fromisoformat(record["start_time"])
|
||||||
|
expected_end_time = datetime.fromisoformat(record["expected_end_time"])
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
if now >= expected_end_time:
|
||||||
|
remaining_str = "已完成,等待结算"
|
||||||
|
else:
|
||||||
|
remaining = expected_end_time - now
|
||||||
|
remaining_minutes = int(remaining.total_seconds() / 60) + 1
|
||||||
|
remaining_str = f"约 {remaining_minutes} 分钟"
|
||||||
|
|
||||||
|
return (
|
||||||
|
"# ⚗️ 炼金状态\n"
|
||||||
|
f"- 状态:进行中\n"
|
||||||
|
f"- 类型:{alchemy_type_name}\n"
|
||||||
|
f"- 开始时间:{start_time.strftime('%Y-%m-%d %H:%M')}\n"
|
||||||
|
f"- 预计完成:{expected_end_time.strftime('%Y-%m-%d %H:%M')}\n"
|
||||||
|
f"- 剩余时间:{remaining_str}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def recover_overdue_alchemy(self) -> None:
|
||||||
|
"""恢复过期但未结算的炼金"""
|
||||||
|
cursor = get_db().conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT alchemy_id FROM alchemy_records
|
||||||
|
WHERE status = 'in_progress' AND expected_end_time < ?
|
||||||
|
""",
|
||||||
|
(datetime.now().isoformat(),),
|
||||||
|
)
|
||||||
|
|
||||||
|
overdue_records = cursor.fetchall()
|
||||||
|
|
||||||
|
for record in overdue_records:
|
||||||
|
logger.Log(
|
||||||
|
"Warning",
|
||||||
|
f"{ConsoleFrontColor.YELLOW}发现过期炼金 {record['alchemy_id']},执行恢复结算{ConsoleFrontColor.RESET}",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.settle_alchemy(record["alchemy_id"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.Log(
|
||||||
|
"Error",
|
||||||
|
f"{ConsoleFrontColor.RED}恢复炼金 {record['alchemy_id']} 失败: {e}{ConsoleFrontColor.RESET}",
|
||||||
|
)
|
||||||
|
|
||||||
|
if overdue_records:
|
||||||
|
logger.Log(
|
||||||
|
"Info",
|
||||||
|
f"{ConsoleFrontColor.GREEN}恢复了 {len(overdue_records)} 个过期炼金{ConsoleFrontColor.RESET}",
|
||||||
|
)
|
||||||
|
|
||||||
def _help_message(self) -> str:
|
def _help_message(self) -> str:
|
||||||
return (
|
return (
|
||||||
"# ⚗️ 炼金指令帮助\n"
|
"# ⚗️ 炼金指令帮助\n"
|
||||||
"- `炼金 <积分>`:投入积分尝试炼金\n"
|
"- `炼金 <积分>`:投入积分尝试炼金\n"
|
||||||
"- `炼金 <材料1> <材料2> <材料3> [次数]`:使用三件材料进行炼金(可选次数,默认 1)\n"
|
"- `炼金 <材料1> <材料2> <材料3> [次数]`:使用三件材料进行炼金(可选次数,默认 1)\n"
|
||||||
"> 建议提前备足材料及积分,谨慎开启炼金流程。"
|
"- `炼金 状态`:查询当前炼金状态\n"
|
||||||
|
"> 建议提前备足材料及积分,谨慎开启炼金流程。炼金需要等待一定时间后才会获得结果。"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user