Files
NewWPSBot/Plugins/WPSGardenSystem/garden_plugin_base.py
2025-11-12 14:36:50 +08:00

311 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Shared base class for garden plugins."""
from __future__ import annotations
import json
from typing import List, Optional, Type
from PWF.Convention.Runtime.Architecture import Architecture
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
from PWF.CoreModules.plugin_interface import DatabaseModel
from Plugins.WPSAPI import WPSAPI
from Plugins.WPSBackpackSystem import (
BackpackItemTier,
WPSBackpackSystem,
)
from Plugins.WPSStoreSystem import WPSStoreSystem
from Plugins.WPSConfigSystem import WPSConfigAPI
from Plugins.WPSFortuneSystem import WPSFortuneSystem
from Plugins.WPSAlchemyGame import WPSAlchemyGame
from .garden_models import (
GARDEN_CROPS,
GARDEN_FRUITS,
GARDEN_MISC_ITEMS,
GardenCropDefinition,
get_garden_db_models,
)
from .garden_service import GardenService
class WPSGardenBase(WPSAPI):
_service: GardenService | None = None
_initialized: bool = False
@classmethod
def service(cls) -> GardenService:
if cls._service is None:
cls._service = GardenService()
cls._service.recover_overdue_plots()
return cls._service
def dependencies(self) -> List[Type]:
return [
WPSConfigAPI,
WPSBackpackSystem,
WPSStoreSystem,
WPSFortuneSystem,
WPSAlchemyGame,
]
def register_db_model(self) -> List[DatabaseModel]:
return get_garden_db_models()
def wake_up(self) -> None:
if WPSGardenBase._initialized:
return
WPSGardenBase._initialized = True
logger: ProjectConfig = Architecture.Get(ProjectConfig)
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
store: WPSStoreSystem = Architecture.Get(WPSStoreSystem)
alchemy: WPSAlchemyGame = Architecture.Get(WPSAlchemyGame)
service = self.service()
for crop in GARDEN_CROPS.values():
seed_name = f"{crop.display_name}的种子"
fruit_name = f"{crop.display_name}的果实"
# 支持所有tier等级common, rare, epic, legendary
tier_map = {
"common": BackpackItemTier.COMMON,
"rare": BackpackItemTier.RARE,
"epic": BackpackItemTier.EPIC,
"legendary": BackpackItemTier.LEGENDARY,
}
tier = tier_map.get(crop.tier.lower(), BackpackItemTier.RARE)
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 = self._generate_wine_description(crop.display_name, crop.wine_item_id)
self._safe_register_item(
backpack,
crop.wine_item_id,
wine_name,
wine_tier,
wine_desc,
)
self._safe_register_mode(
store,
crop,
limit_amount=service.config.seed_store_limit,
)
if crop.wine_item_id and crop.wine_tier:
wine_price = crop.seed_price * service.config.sale_multiplier * 5
self._safe_register_wine_mode(
store,
crop.wine_item_id,
wine_price,
limit_amount=service.config.seed_store_limit,
)
self._safe_register_recipe(alchemy, crop)
for item_id, meta in GARDEN_MISC_ITEMS.items():
self._safe_register_item(
backpack,
item_id,
meta["name"],
BackpackItemTier.COMMON,
meta.get("description", ""),
)
logger.Log(
"Info",
f"{ConsoleFrontColor.GREEN}WPSGarden 系统完成物品与商店初始化{ConsoleFrontColor.RESET}",
)
# region Helpers
def _safe_register_item(
self,
backpack: WPSBackpackSystem,
item_id: str,
name: str,
tier: BackpackItemTier,
description: str,
) -> None:
try:
backpack.register_item(item_id, name, tier, description)
except Exception:
pass
def _safe_register_mode(
self,
store: WPSStoreSystem,
crop: GardenCropDefinition,
*,
limit_amount: int,
) -> None:
try:
store.register_mode(
item_id=crop.seed_id,
price=crop.seed_price,
limit_amount=limit_amount,
)
except Exception:
pass
def _safe_register_wine_mode(
self,
store: WPSStoreSystem,
item_id: str,
price: int,
*,
limit_amount: int,
) -> None:
try:
store.register_mode(
item_id=item_id,
price=price,
limit_amount=limit_amount,
)
except Exception:
pass
def _generate_wine_description(self, crop_name: str, wine_item_id: str) -> str:
"""生成包含buff加成信息的果酒描述"""
# 尝试导入战斗系统的WINE_BUFFS可选依赖
try:
from Plugins.WPSCombatSystem.combat_models import WINE_BUFFS
buffs = WINE_BUFFS.get(wine_item_id, {})
except ImportError:
buffs = {}
parts = [f"{crop_name}酿制的果酒,饮用后可触发战斗增益。"]
if buffs:
buff_parts = []
buff_names = {
"time_reduction": "冒险时间",
"reward_boost": "冒险收益",
"success_rate": "冒险成功率",
"atk_boost": "攻击力",
"def_boost": "防御力",
"crit_boost": "暴击率",
}
for buff_key, buff_value in sorted(buffs.items()):
buff_name = buff_names.get(buff_key, buff_key)
if buff_key == "time_reduction":
buff_parts.append(f"{buff_name}-{buff_value*100:.0f}%")
else:
buff_parts.append(f"{buff_name}+{buff_value*100:.0f}%")
if buff_parts:
parts.append(f"效果:{', '.join(buff_parts)}")
return " | ".join(parts)
def _safe_register_recipe(
self,
alchemy: WPSAlchemyGame,
crop: GardenCropDefinition,
) -> None:
try:
if not crop.wine_item_id:
return
success_rate = 0.75
alchemy.register_recipe(
(crop.fruit_id, crop.fruit_id, crop.fruit_id),
crop.wine_item_id,
"garden_item_rot_fruit",
success_rate,
)
except Exception:
pass
async def _clock_mark_mature(self, user_id: int, chat_id: int, plot_index: int) -> None:
service = self.service()
plot = service.get_plot(user_id, plot_index)
if not plot:
return
if int(plot["is_mature"]) == 1:
return
service.mark_mature(user_id, plot_index)
crop = GARDEN_CROPS.get(plot["seed_id"])
if crop is None:
return
message = (
"# 🌾 作物成熟提醒\n"
f"- 地块 {plot_index}{crop.display_name} 已成熟,记得收获!"
)
await self.send_markdown_message(message, chat_id, user_id)
def _format_timestamp(self, ts: str) -> str:
return self.service().format_display_time(ts)
def resolve_seed_id(self, keyword: str) -> Optional[GardenCropDefinition]:
key = keyword.strip().lower()
for crop in GARDEN_CROPS.values():
if crop.seed_id.lower() == key:
return crop
if crop.display_name.lower() == key:
return crop
if f"{crop.display_name}的种子".lower() == key:
return crop
return None
def resolve_fruit_id(self, keyword: str) -> Optional[GardenCropDefinition]:
key = keyword.strip().lower()
for crop in GARDEN_FRUITS.values():
if crop.fruit_id.lower() == key:
return crop
if crop.display_name.lower() == key:
return crop
if f"{crop.display_name}的果实".lower() == key:
return crop
return None
def format_garden_overview(self, user_id: int) -> str:
service = self.service()
plots = service.list_plots(user_id)
config = service.config
lines = ["# 🌱 菜园概览"]
if not plots:
lines.append("> 尚未种植任何作物,使用 `种植 <种子>` 开始耕种。")
else:
for plot in plots:
crop = GARDEN_CROPS.get(plot["seed_id"], None)
name = crop.display_name if crop else plot["seed_id"]
idx = plot["plot_index"]
is_mature = bool(plot["is_mature"])
mature_at = plot["mature_at"]
formatted_time = self._format_timestamp(mature_at)
if is_mature:
remaining = plot["remaining_fruit"]
theft_users = len(json.loads(plot["theft_users"])) if plot.get("theft_users") else 0
status = f"✅ 已成熟(成熟于 {formatted_time}"
lines.append(
f"- 地块 {idx}{name}{status}|剩余果实 {remaining}|被偷次数 {theft_users}"
)
else:
status = f"⌛ 生长中,预计成熟 {formatted_time}"
lines.append(f"- 地块 {idx}{name}{status}")
available = config.max_plots - len(plots)
if available > 0:
lines.append(f"\n> 尚有 {available} 块空地可用。")
lines.append(
"\n---\n- `种植 <种子>`:消耗种子种下作物\n"
"- `收获 <地块序号>`:收成成熟作物\n"
"- `偷取 <用户> <地块序号>`:从他人成熟作物中偷取果实\n"
"- `铲除 <地块序号>`:立即清空指定地块\n"
"- `菜园 售出 <果实> <数量>`:出售果实换取积分"
)
return "\n".join(lines)
# endregion
__all__ = ["WPSGardenBase"]