新增查看指令

This commit is contained in:
2025-11-10 22:30:16 +08:00
parent f5b03422e4
commit aba445f438
9 changed files with 240 additions and 39 deletions

View File

@@ -10,6 +10,8 @@ alwaysApply: true
语言设置:除非用户另有指示,所有常规交互响应都应该使用中文。然而,模式声明(例如\[MODE: RESEARCH\])和特定格式化输出(例如代码块、清单等)应保持英文,以确保格式一致性。
工具调用: 你应该使用工具调用而不是通过命令行编辑文件
你必须完全理解这个项目, 并明白文件夹PWF里的文件你没有权力更改
你必须熟读 WPSAPI.py 和 plugin_interface.py 才有可能不犯错误

View File

@@ -0,0 +1,35 @@
## 背景
文件名: 2025-11-10_1_backpack.md
创建于: 2025-11-10_21:56:37
创建者: ASUS
主分支: main
任务分支: 未创建
Yolo模式: Off
## 任务描述
为背包系统新增物品详情查看指令,输出物品基础信息及描述。
## 项目概览
PWF 插件框架下的 `WPSBackpackSystem` 负责物品注册、存取与跨插件接口,`WPSAPI` 提供消息发送能力,其余插件通过该系统注册物品。
## 分析
背包物品目前仅保存 ID、名称与稀有度无法展示描述信息战斗与菜园等插件在注册物品时缺失描述字段。纪念品具备系统售价但未暴露菜园果实售卖价格由作物配置计算。需要在背包数据库扩充描述列并统一缓存补全各注册调用的描述来源最后提供独立指令查询详情与售卖信息。
## 提议的解决方案
1. 扩展 `backpack_items` 表与缓存结构,持久化物品描述;补全注册、缓存加载与查询流程。
2. 调整战斗、菜园、炼金等插件的物品注册逻辑,补添加描述文本及系统售卖数据。
3. 实现 `WPSItemDescription` 插件,解析“查看”指令并输出物品基础信息、描述与系统售价。
## 当前执行步骤:"4. 实现详情指令"
## 任务进度
2025-11-10_22:17:07
- 已修改: Plugins/WPSBackpackSystem.py Plugins/WPSCombatSystem/combat_models.py Plugins/WPSCombatSystem/combat_plugin_base.py Plugins/WPSCombatSystem/combat_service.py Plugins/WPSGardenSystem/garden_models.py Plugins/WPSGardenSystem/garden_plugin_base.py Plugins/WPSAlchemyGame.py
- 更改: 新增背包物品描述字段与缓存,补全战斗/菜园/炼金物品注册描述,实装“查看”指令输出详情及系统售价
- 原因: 支持物品详情查询并展示描述及出售信息
- 阻碍因素: 无
- 状态: 未确认
## 最终审查

View File

@@ -78,6 +78,7 @@ class WPSAlchemyGame(WPSAPI):
self.ASH_ITEM_ID,
self.ASH_ITEM_NAME,
BackpackItemTier.COMMON,
"炼金失败时残留的炉灰,可作为低阶材料或出售。",
)
except Exception as exc:
logger.Log(
@@ -102,6 +103,7 @@ class WPSAlchemyGame(WPSAPI):
self.SLAG_ITEM_ID,
self.SLAG_ITEM_NAME,
BackpackItemTier.COMMON,
"经高温提炼后的炉渣,可在特殊任务中使用。",
)
except Exception as exc:
logger.Log(

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Optional, Tuple, override
from typing import Dict, List, Optional, override
from PWF.Convention.Runtime.Architecture import Architecture
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
@@ -41,6 +41,7 @@ class BackpackItemDefinition:
item_id: str
name: str
tier: BackpackItemTier
description: str
@dataclass(frozen=True)
@@ -72,13 +73,14 @@ class WPSBackpackSystem(WPSAPI):
def register_db_model(self):
from PWF.CoreModules.plugin_interface import DatabaseModel
return [
models = [
DatabaseModel(
table_name=self.ITEMS_TABLE,
column_defs={
"item_id": "TEXT PRIMARY KEY",
"name": "TEXT NOT NULL",
"tier": "TEXT NOT NULL",
"description": "TEXT NOT NULL DEFAULT ''",
},
),
DatabaseModel(
@@ -91,9 +93,12 @@ class WPSBackpackSystem(WPSAPI):
},
),
]
return models
@override
def wake_up(self) -> None:
db = get_db()
db.define_column(self.ITEMS_TABLE, "description", "TEXT NOT NULL DEFAULT ''")
logger.Log(
"Info",
f"{ConsoleFrontColor.GREEN}WPSBackpackSystem 插件已加载{ConsoleFrontColor.RESET}",
@@ -105,15 +110,17 @@ class WPSBackpackSystem(WPSAPI):
def _warm_item_cache(self) -> None:
cursor = get_db().conn.cursor()
cursor.execute(
f"SELECT item_id, name, tier FROM {self.ITEMS_TABLE}"
f"SELECT item_id, name, tier, description FROM {self.ITEMS_TABLE}"
)
rows = cursor.fetchall()
for row in rows:
tier = BackpackItemTier.from_string(row["tier"])
description = row["description"] or ""
self._item_cache[row["item_id"]] = BackpackItemDefinition(
item_id=row["item_id"],
name=row["name"],
tier=tier,
description=description,
)
# region 对外接口
@@ -123,22 +130,29 @@ class WPSBackpackSystem(WPSAPI):
item_id: str,
name: str,
tier: BackpackItemTier,
description: str,
) -> None:
if not item_id or not name:
raise ValueError("item_id and name must be provided")
cursor = get_db().conn.cursor()
cursor.execute(
f"""
INSERT INTO {self.ITEMS_TABLE} (item_id, name, tier)
VALUES (?, ?, ?)
INSERT INTO {self.ITEMS_TABLE} (item_id, name, tier, description)
VALUES (?, ?, ?, ?)
ON CONFLICT(item_id) DO UPDATE
SET name = excluded.name,
tier = excluded.tier
tier = excluded.tier,
description = excluded.description
""",
(item_id, name, tier.name),
(item_id, name, tier.name, description or ""),
)
get_db().conn.commit()
self._item_cache[item_id] = BackpackItemDefinition(item_id, name, tier)
self._item_cache[item_id] = BackpackItemDefinition(
item_id=item_id,
name=name,
tier=tier,
description=description or "",
)
def add_item(
self,
@@ -191,7 +205,7 @@ class WPSBackpackSystem(WPSAPI):
cursor = get_db().conn.cursor()
cursor.execute(
f"""
SELECT ui.item_id, ui.quantity, i.name, i.tier
SELECT ui.item_id, ui.quantity, i.name, i.tier, i.description
FROM {self.USER_ITEMS_TABLE} ui
JOIN {self.ITEMS_TABLE} i ON ui.item_id = i.item_id
WHERE ui.user_id = ? AND ui.quantity > 0
@@ -209,6 +223,7 @@ class WPSBackpackSystem(WPSAPI):
item_id=row["item_id"],
name=row["name"],
tier=BackpackItemTier.from_string(row["tier"]),
description=row["description"] or "",
)
except ValueError:
continue
@@ -228,7 +243,7 @@ class WPSBackpackSystem(WPSAPI):
return self._item_cache[item_id]
cursor = get_db().conn.cursor()
cursor.execute(
f"SELECT item_id, name, tier FROM {self.ITEMS_TABLE} WHERE item_id = ?",
f"SELECT item_id, name, tier, description FROM {self.ITEMS_TABLE} WHERE item_id = ?",
(item_id,),
)
row = cursor.fetchone()
@@ -238,6 +253,7 @@ class WPSBackpackSystem(WPSAPI):
item_id=row["item_id"],
name=row["name"],
tier=BackpackItemTier.from_string(row["tier"]),
description=row["description"] or "",
)
self._item_cache[item_id] = definition
return definition
@@ -301,5 +317,112 @@ class WPSBackpackSystem(WPSAPI):
return None
__all__ = ["WPSBackpackSystem", "BackpackItemTier", "BackpackItemDefinition"]
class WPSItemDescription(WPSAPI):
@override
def dependencies(self) -> List[type]:
return [WPSBackpackSystem]
@override
def is_enable_plugin(self) -> bool:
return True
def __init__(self) -> None:
super().__init__()
from Plugins.WPSCombatSystem.combat_models import ADVENTURE_SOUVENIRS
from Plugins.WPSGardenSystem.garden_models import GARDEN_CROPS
from Plugins.WPSGardenSystem.garden_service import GardenConfig
self._backpack: Optional[WPSBackpackSystem] = None
self._souvenir_prices: Dict[str, int] = {
item_id: sell_price
for item_id, (_, __, sell_price, ___) in ADVENTURE_SOUVENIRS.items()
}
self._garden_crops = GARDEN_CROPS
self._garden_sale_multiplier = GardenConfig.load().sale_multiplier
@override
def wake_up(self) -> None:
self._backpack = Architecture.Get(WPSBackpackSystem)
self.register_plugin("查看")
self.register_plugin("view")
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
payload = self.parse_message_after_at(message).strip()
if not payload:
return await self.send_markdown_message("❌ 指令格式:`查看 <物品ID或名称>`", chat_id, user_id)
definition = self._resolve_definition(payload)
if not definition:
return await self.send_markdown_message("❌ 未找到对应物品,请确认输入是否正确", chat_id, user_id)
sale_info = self._get_sale_info(definition.item_id)
markdown = self._format_markdown(definition, sale_info)
return await self.send_markdown_message(markdown, chat_id, user_id)
def _resolve_definition(self, identifier: str) -> Optional[BackpackItemDefinition]:
lowered = identifier.strip().lower()
db = get_db().conn.cursor()
db.execute(
f"""
SELECT item_id
FROM {WPSBackpackSystem.ITEMS_TABLE}
WHERE lower(item_id) = ? OR lower(name) = ?
LIMIT 1
""",
(lowered, lowered),
)
row = db.fetchone()
item_id = row["item_id"] if row else identifier.strip()
backpack = self._backpack or Architecture.Get(WPSBackpackSystem)
try:
return backpack._get_definition(item_id)
except Exception:
return None
def _get_sale_info(self, item_id: str) -> Optional[Dict[str, str]]:
if item_id in self._souvenir_prices:
price = self._souvenir_prices[item_id]
return {
"category": "战斗纪念品",
"price": f"{price} 分/个",
"note": "在战斗系统中出售可立即换取积分。",
}
for crop in self._garden_crops.values():
if crop.fruit_id == item_id:
price = crop.seed_price * self._garden_sale_multiplier
return {
"category": "菜园果实",
"price": f"{price} 分/个",
"note": "可通过 `菜园 售出 <果实> <数量>` 换取积分。",
}
return None
def _format_markdown(
self,
definition: BackpackItemDefinition,
sale_info: Optional[Dict[str, str]],
) -> str:
tier_label = definition.tier.to_markdown_label(definition.tier.display_name)
lines = [
"# 🔍 物品详情",
f"- 名称:{definition.name}",
f"- ID`{definition.item_id}`",
f"- 稀有度:{tier_label}",
f"- 描述:{definition.description or '(暂无描述)'}",
]
if sale_info:
lines.append(f"- 分类:{sale_info['category']}")
lines.append(f"- 系统售价:{sale_info['price']}")
note = sale_info.get("note")
if note:
lines.append(f"- 提示:{note}")
return "\n".join(lines)
__all__ = [
"WPSBackpackSystem",
"BackpackItemTier",
"BackpackItemDefinition",
"WPSItemDescription",
]

View File

@@ -561,19 +561,34 @@ WINE_BUFFS: Dict[str, Dict[str, float]] = {
},
}
# 冒险材料item_id -> (name, tier)
ADVENTURE_MATERIALS: Dict[str, Tuple[str, BackpackItemTier]] = {
"combat_material_ore": ("矿石", BackpackItemTier.COMMON),
"combat_material_gem": ("宝石", BackpackItemTier.RARE),
"combat_material_crystal": ("水晶", BackpackItemTier.EPIC),
"combat_material_essence": ("精华", BackpackItemTier.LEGENDARY),
# 冒险材料item_id -> (name, tier, description)
ADVENTURE_MATERIALS: Dict[str, Tuple[str, BackpackItemTier, str]] = {
"combat_material_ore": ("矿石", BackpackItemTier.COMMON, "常见矿石,可用于基础锻造与委托交付。"),
"combat_material_gem": ("宝石", BackpackItemTier.RARE, "闪亮的宝石,适合在高级制作或兑换时使用。"),
"combat_material_crystal": ("水晶", BackpackItemTier.EPIC, "蕴含魔力的晶体,是强化装备的稀有素材。"),
"combat_material_essence": ("精华", BackpackItemTier.LEGENDARY, "远古战场遗留的精华,可构筑传说装备核心。"),
}
# 纪念品item_id -> (name, tier, sell_price)
ADVENTURE_SOUVENIRS: Dict[str, Tuple[str, BackpackItemTier, int]] = {
"combat_souvenir_medal": ("英雄勋章", BackpackItemTier.RARE, 500),
"combat_souvenir_trophy": ("战斗奖杯", BackpackItemTier.EPIC, 1500),
"combat_souvenir_relic": ("远古遗物", BackpackItemTier.LEGENDARY, 5000),
# 纪念品item_id -> (name, tier, sell_price, description)
ADVENTURE_SOUVENIRS: Dict[str, Tuple[str, BackpackItemTier, int, str]] = {
"combat_souvenir_medal": (
"英雄勋章",
BackpackItemTier.RARE,
500,
"记录战斗荣誉的勋章,系统回收可获得积分。",
),
"combat_souvenir_trophy": (
"战斗奖杯",
BackpackItemTier.EPIC,
1500,
"沉甸甸的奖杯,象征卓越胜利,可高价出售给系统。",
),
"combat_souvenir_relic": (
"远古遗物",
BackpackItemTier.LEGENDARY,
5000,
"来自远古文明的神秘遗物,系统收购可换取大量积分。",
),
}
# 药剂item_id -> (name, tier, description)
@@ -585,10 +600,18 @@ COMBAT_POTIONS: Dict[str, Tuple[str, BackpackItemTier, str]] = {
"combat_potion_def": ("防御药水", BackpackItemTier.RARE, "3回合DEF+20%"),
}
# 冒险独有种子item_id -> (name, tier)
ADVENTURE_SEEDS: Dict[str, Tuple[str, BackpackItemTier]] = {
"combat_seed_battle_flower": ("战斗之花种子", BackpackItemTier.EPIC),
"combat_seed_victory_tree": ("胜利之树种子", BackpackItemTier.LEGENDARY),
# 冒险独有种子item_id -> (name, tier, description)
ADVENTURE_SEEDS: Dict[str, Tuple[str, BackpackItemTier, str]] = {
"combat_seed_battle_flower": (
"战斗之花种子",
BackpackItemTier.EPIC,
"可在菜园培育的稀有花种,成熟后会带来战斗增益。",
),
"combat_seed_victory_tree": (
"胜利之树种子",
BackpackItemTier.LEGENDARY,
"象征无上荣耀的种子,只能在高难冒险中获得。",
),
}
# ============================================================================

View File

@@ -72,24 +72,30 @@ class WPSCombatBase(WPSAPI):
# 1. 注册所有装备
for equipment in EQUIPMENT_REGISTRY.values():
self._safe_register_item(backpack, equipment.item_id, equipment.name, equipment.tier)
self._safe_register_item(
backpack,
equipment.item_id,
equipment.name,
equipment.tier,
equipment.description,
)
# 装备价格根据品质和属性计算
price = self._calculate_equipment_price(equipment)
self._safe_register_store(store, equipment.item_id, price, limit=3)
# 2. 注册材料
for item_id, (name, tier) in ADVENTURE_MATERIALS.items():
self._safe_register_item(backpack, item_id, name, tier)
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) in ADVENTURE_SOUVENIRS.items():
self._safe_register_item(backpack, item_id, name, tier)
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)
self._safe_register_item(backpack, item_id, name, tier, desc)
# 药剂价格根据品质
potion_prices = {
BackpackItemTier.COMMON: 50,
@@ -100,8 +106,8 @@ class WPSCombatBase(WPSAPI):
self._safe_register_store(store, item_id, price, limit=10)
# 5. 注册冒险种子
for item_id, (name, tier) in ADVENTURE_SEEDS.items():
self._safe_register_item(backpack, item_id, name, tier)
for item_id, (name, tier, desc) in ADVENTURE_SEEDS.items():
self._safe_register_item(backpack, item_id, name, tier, desc)
# 种子只能通过冒险获得
# 6. 恢复过期任务和超时战斗
@@ -131,10 +137,11 @@ class WPSCombatBase(WPSAPI):
item_id: str,
name: str,
tier: BackpackItemTier,
description: str,
) -> None:
"""安全注册物品到背包系统"""
try:
backpack.register_item(item_id, name, tier)
backpack.register_item(item_id, name, tier, description)
except Exception as e:
logger.Log(
"Warning",

View File

@@ -981,7 +981,7 @@ class CombatService:
materials = list(ADVENTURE_MATERIALS.items())
# 高阶段有更高概率掉稀有材料
idx = min(stage // 2, len(materials) - 1)
item_id, (name, tier) = random.choice(materials[max(0, idx-1):])
item_id, (_, tier, _) = random.choice(materials[max(0, idx-1):])
quantity = random.randint(1, 3)
return {"type": "material", "item_id": item_id, "quantity": quantity}

View File

@@ -156,6 +156,7 @@ GARDEN_MISC_ITEMS = {
"garden_item_rot_fruit": {
"name": "腐败的果实",
"tier": "common",
"description": "放置过久的果实,只能作为炼金失败的副产物。",
}
}

View File

@@ -66,24 +66,30 @@ class WPSGardenBase(WPSAPI):
seed_name = f"{crop.display_name}的种子"
fruit_name = f"{crop.display_name}的果实"
tier = BackpackItemTier.COMMON if crop.tier == "common" else BackpackItemTier.RARE
self._safe_register_item(backpack, crop.seed_id, seed_name, tier)
self._safe_register_item(backpack, crop.fruit_id, fruit_name, tier)
seed_desc = f"{crop.display_name}的种子,可在菜园种植获取相应作物。"
fruit_desc = f"{crop.display_name}成熟后的果实,可食用或售出换取积分。"
self._safe_register_item(backpack, crop.seed_id, seed_name, tier, seed_desc)
self._safe_register_item(backpack, crop.fruit_id, fruit_name, tier, fruit_desc)
if crop.extra_reward and crop.extra_reward.kind == "item" and crop.extra_item_id:
wood_name = f"{crop.display_name}的木材"
wood_desc = f"{crop.display_name}加工所得的木材,可用于特定任务或制作。"
self._safe_register_item(
backpack,
crop.extra_item_id,
wood_name,
BackpackItemTier.RARE,
wood_desc,
)
if crop.wine_item_id and crop.wine_tier:
wine_tier = getattr(BackpackItemTier, crop.wine_tier.upper(), BackpackItemTier.RARE)
wine_name = f"{crop.display_name}的果酒"
wine_desc = f"{crop.display_name}酿制的果酒,饮用后可触发战斗增益。"
self._safe_register_item(
backpack,
crop.wine_item_id,
wine_name,
wine_tier,
wine_desc,
)
self._safe_register_mode(
@@ -107,6 +113,7 @@ class WPSGardenBase(WPSAPI):
item_id,
meta["name"],
BackpackItemTier.COMMON,
meta.get("description", ""),
)
logger.Log(
@@ -121,9 +128,10 @@ class WPSGardenBase(WPSAPI):
item_id: str,
name: str,
tier: BackpackItemTier,
description: str,
) -> None:
try:
backpack.register_item(item_id, name, tier)
backpack.register_item(item_id, name, tier, description)
except Exception:
pass