From 7dc55bccf1b9670547d37da948a989615382336d Mon Sep 17 00:00:00 2001 From: Art_Sakura <1754798088@qq.com> Date: Wed, 28 May 2025 19:48:12 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E7=A7=8D=E5=AD=90=E5=95=86?= =?UTF-8?q?=E5=BA=97=E6=96=B0=E5=A2=9E=E7=AD=9B=E9=80=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- README.md | 45 +++++++++++++++++--- __init__.py | 3 +- command.py | 75 +++++++++++++++++++++------------ config/sign_in.json | 78 ++++++++++++++++++++++++++++++++++ database/plant.py | 98 +++++++++++++++++++++++++++++++++++++++++++ database/userSign.py | 8 ++-- database/userSoil.py | 41 +++++++++++++++++- farm/farm.py | 51 +++++++++++++--------- farm/shop.py | 86 ++++++++++++++++++++++--------------- log/log.md | 14 +++++++ resource/db/plant.db | Bin 20480 -> 36864 bytes 12 files changed, 411 insertions(+), 92 deletions(-) create mode 100644 config/sign_in.json diff --git a/.gitignore b/.gitignore index ed8ebf5..430cafa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -__pycache__ \ No newline at end of file +__pycache__ + +./config/sign_in.json diff --git a/README.md b/README.md index 56b2fe7..606244d 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,42 @@ license + + Python + + + Nonebot + + + Python + +

+ +

+ +[![tencent-qq](https://img.shields.io/badge/%E7%BE%A4-%E7%9C%9F%E5%AF%BB%E5%86%9C%E5%9C%BA%E6%B5%8B%E8%AF%95-%23FF99CC +)](https://qm.qq.com/q/7hsOD4rOw2) +

你是说可以种地对吧🤔? +--- +## 目录 +- [真寻农场(zhenxun\_plugin\_farm)](#真寻农场zhenxun_plugin_farm) + - [目录](#目录) + - [如何安装](#如何安装) + - [使用指令](#使用指令) + - [更新日志(详细):](#更新日志详细) + - [用户方面](#用户方面) + - [代码方面](#代码方面) + - [待办事宜 `Todo` 列表](#待办事宜-todo-列表) + - [关于](#关于) + - [致谢](#致谢) + - [许可证](#许可证) + --- ## 如何安装 @@ -26,7 +56,7 @@ | --- | --- | --- | | @小真寻 开通农场 | 首次开通农场 | | | 我的农场币 | 查询农场币 | | -| 种子商店 [页数] | 查看种子商店 | 数量不填默认为1 | +| 种子商店 [筛选关键字] [页数] or [页数] | 查看种子商店 | 当第一个参数为非整数时,会默认进入筛选状态。页数不填默认为1 | | 购买种子 [种子名称] [数量] | 购买种子 | 数量不填默认为1 | | 我的种子 | 查询仓库种子 | | | 播种 [种子名称] [数量] | 播种种子 | 数量不填默认将最大可能播种 | @@ -37,20 +67,23 @@ | @美波理 偷菜 | 偷别人的菜 | 每人每天只能偷5次 | | 购买农场币 | 将真寻金币兑换成农场币 | 兑换比例默认为1:2 手续费默认20% | | 更改农场名 [新的农场名] | 改名 | +| 农场签到 | 签到 | 需要注意,该项会从服务器拉取签到数据 | --- ## 更新日志[(详细)](./log/log.md): 用户方面 --- -- 修复重大BUG,该BUG会导致用户经验等级计算异常 -- 修正种子价格,现在不会以单价的形式购买种子了,而是新增的种子价格 +- 新增种子商店筛选功能(如果没有BUG的话后续我的种子、我的作物等也会加入筛选功能 +- 新增签到功能(测试 +- 新增农场详述功能,能通过该功能更加详细的观看农场数据 +- 修复了迁移旧数据库无法正常迁移的BUG 代码方面 --- -- 彻底重构数据库,但是不会像V1.1一样导致用户数据丢失 -- 迁移部分代码结构,使文件名和代码功能更加匹配 -- 加入事件机制,采用类似Qt信号槽机制 +- 修正了多阶段作物成长素材计算逻辑 +- 修正了作物数据库字段错乱的问题 +- 作物新增offset字段,用于以后偏移坐标(尚未加入代码 TODO --- ## 待办事宜 `Todo` 列表 diff --git a/__init__.py b/__init__.py index 12193d0..76914e2 100644 --- a/__init__.py +++ b/__init__.py @@ -22,7 +22,7 @@ __plugin_meta__ = PluginMetadata( at 开通农场 我的农场 我的农场币 - 种子商店 [页数] + 种子商店 [筛选关键字] [页数] or [页数] 购买种子 [作物/种子名称] [数量] 我的种子 播种 [作物/种子名称] [数量] (数量不填默认将最大可能播种 @@ -34,6 +34,7 @@ __plugin_meta__ = PluginMetadata( 开垦 购买农场币 [数量] 数量为消耗金币的数量 更改农场名 [新农场名] + 农场签到 """.strip(), extra=PluginExtraData( author="Art_Sakura", diff --git a/command.py b/command.py index 53cded2..5ed1044 100644 --- a/command.py +++ b/command.py @@ -1,3 +1,5 @@ +import inspect + from nonebot.adapters import Event, MessageTemplate from nonebot.rule import to_me from nonebot_plugin_alconna import (Alconna, AlconnaMatch, AlconnaQuery, Args, @@ -125,7 +127,7 @@ diuse_farm = on_alconna( Option("--all", action=store_true), Subcommand("detail", help_text="农场详述"), Subcommand("my-point", help_text="我的农场币"), - Subcommand("seed-shop", Args["num?", int], help_text="种子商店"), + Subcommand("seed-shop", Args["res?", MultiVar(str)], help_text="种子商店"), Subcommand("buy-seed", Args["name?", str]["num?", int], help_text="购买种子"), Subcommand("my-seed", help_text="我的种子"), Subcommand("sowing", Args["name?", str]["num?", int], help_text="播种"), @@ -147,7 +149,7 @@ diuse_farm = on_alconna( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return image = await g_pFarmManager.drawFarmByUid(uid) @@ -164,7 +166,7 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return info = await g_pFarmManager.drawDetailFarmByUid(uid) @@ -197,13 +199,35 @@ diuse_farm.shortcut( ) @diuse_farm.assign("seed-shop") -async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 1)): +async def _(session: Uninfo, res: Match[tuple[str, ...]]): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return - image = await g_pShopManager.getSeedShopImage(num.result) + if res.result is inspect._empty: + raw = [] + else: + raw = res.result + + filterKey: str | int | None = None + page: int = 1 + + if len(raw) >= 1 and raw[0] is not None: + first = raw[0] + if isinstance(first, str) and first.isdigit(): + page = int(first) + else: + filterKey = first + + if len(raw) >= 2 and raw[1] is not None and isinstance(raw[1], str) and raw[1].isdigit(): + page = int(raw[1]) + + if filterKey is None: + image = await g_pShopManager.getSeedShopImage(page) + else: + image = await g_pShopManager.getSeedShopImage(filterKey, page) + await MessageUtils.build_message(image).send() diuse_farm.shortcut( @@ -214,7 +238,7 @@ diuse_farm.shortcut( ) @diuse_farm.assign("buy-seed") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1),): +async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1)): if not name.available: await MessageUtils.build_message( "请在指令后跟需要购买的种子名称" @@ -222,7 +246,7 @@ async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("n uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pShopManager.buySeed(uid, name.result, num.result) @@ -239,7 +263,7 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.getUserSeedByUid(uid) @@ -253,7 +277,7 @@ diuse_farm.shortcut( ) @diuse_farm.assign("sowing") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1),): +async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)): if not name.available: await MessageUtils.build_message( "请在指令后跟需要播种的种子名称" @@ -261,7 +285,7 @@ async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("n uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.sowing(uid, name.result, num.result) @@ -279,7 +303,7 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.harvest(uid) @@ -296,7 +320,7 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.eradicate(uid) @@ -314,7 +338,7 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.getUserPlantByUid(uid) @@ -331,7 +355,7 @@ reclamation = on_alconna( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return condition = await g_pFarmManager.reclamationCondition(uid) @@ -360,10 +384,10 @@ diuse_farm.shortcut( ) @diuse_farm.assign("sell-plant") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1),): +async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result) @@ -380,7 +404,7 @@ diuse_farm.shortcut( async def _(session: Uninfo, target: Match[At]): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return if not target.available: @@ -412,7 +436,7 @@ async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return result = await g_pFarmManager.buyPointByUid(uid, num.result) @@ -430,12 +454,12 @@ diuse_farm.shortcut( async def _(session: Uninfo, name: Match[str]): if not name.available: await MessageUtils.build_message( - "请在指令后跟需要更改的用户名" + "请在指令后跟需要更改的农场名" ).finish(reply_to=True) uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return safeName = sanitize_username(name.result) @@ -443,9 +467,9 @@ async def _(session: Uninfo, name: Match[str]): result = await g_pDBService.user.updateUserNameByUid(uid, safeName) if result == True: - await MessageUtils.build_message("更新用户名成功!").send(reply_to=True) + await MessageUtils.build_message("更新农场名成功!").send(reply_to=True) else: - await MessageUtils.build_message("更新用户名失败!").send(reply_to=True) + await MessageUtils.build_message("更新农场名失败!").send(reply_to=True) diuse_farm.shortcut( "农场签到", @@ -458,11 +482,11 @@ diuse_farm.shortcut( async def _(session: Uninfo): uid = str(session.user.id) - if await isRegisteredByUid(uid) == False: + if not await isRegisteredByUid(uid): return #判断签到是否正常加载 - if g_bSignStatus == False: + if not g_bSignStatus: await MessageUtils.build_message("签到功能异常!").send() return @@ -475,7 +499,6 @@ async def _(session: Uninfo): if status == 1 or status == 2: #获取签到总天数 signDay = await g_pDBService.userSign.getUserSignCountByDate(uid, toDay.strftime("%Y-%m")) - exp, point = await g_pDBService.userSign.getUserSignRewardByDate(uid, toDay.strftime("%Y-%m-%d")) message += f"签到成功!累计签到天数:{signDay}\n获得经验{exp},获得金币{point}" diff --git a/config/sign_in.json b/config/sign_in.json new file mode 100644 index 0000000..331e318 --- /dev/null +++ b/config/sign_in.json @@ -0,0 +1,78 @@ +{ + "date": "202505", + "exp_max": 50, + "exp_min": 5, + "point_max": 2000, + "point_min": 200, + "continuou": + { + "1": + { + "point": 3000, + "exp": 20 + }, + "3": + { + "point": 5000, + "exp": 25, + "plant": + { + "胡萝卜": 3 + } + }, + "5": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + }, + "7": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + }, + "10": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + }, + "15": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + }, + "20": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + }, + "25": + { + "point": 7000, + "exp": 50, + "plant": + { + "胡萝卜": 3 + } + } + } +} diff --git a/database/plant.py b/database/plant.py index 75c0432..216dc35 100644 --- a/database/plant.py +++ b/database/plant.py @@ -1,6 +1,8 @@ +import ast import os import re from contextlib import asynccontextmanager +from unittest import result import aiosqlite @@ -87,6 +89,102 @@ class CPlantManager: logger.warning(f"查询作物失败: {name}", e=e) return None + @classmethod + async def getPlantPhaseByName(cls, name: str) -> list: + """根据作物名称获取作物各个阶段 + + Args: + name (str): 作物名称 + + Returns: + list: 阶段数组 + """ + try: + async with cls.m_pDB.execute( + "SELECT phase FROM plant WHERE name = ?", (name,) + ) as cursor: + row = await cursor.fetchone() + + if not row: + return [] + + phase = row[0].split(',') + + seen = set() + result = [] + for x in phase: + if x not in seen: + seen.add(x) + result.append(x) + + return result + except Exception as e: + logger.warning(f"查询作物阶段失败: {name}", e=e) + return [] + + @classmethod + async def getPlantPhaseNumberByName(cls, name: str) -> int: + """根据作物名称获取作物总阶段数 + + Args: + name (str): 作物名称 + + Returns: + int: 总阶段数 + """ + try: + async with cls.m_pDB.execute( + "SELECT phase FROM plant WHERE name = ?", (name,) + ) as cursor: + row = await cursor.fetchone() + + if not row: + return -1 + + phase = row[0].split(',') + + #去重 + seen = set() + result = [] + for x in phase: + if x not in seen: + seen.add(x) + result.append(x) + + return len(result) + except Exception as e: + logger.warning(f"查询作物阶段失败: {name}", e=e) + return -1 + + @classmethod + async def getPlantAgainByName(cls, name: str) -> int: + """根据作物名称获取作物再次成熟时间 + + Args: + name (str): 作物名称 + + Returns: + int: 再次成熟时间 单位:h + """ + + try: + async with cls.m_pDB.execute( + "SELECT phase FROM plant WHERE name = ?", (name,) + ) as cursor: + row = await cursor.fetchone() + + if not row: + return -1 + + phase = row[0].split(',') + again = phase[-1] - phase[3] / 60 / 60 + + return again + + except Exception as e: + logger.warning(f"查询作物阶段失败: {name}", e=e) + return -1 + @classmethod async def existsPlant(cls, name: str) -> bool: """判断作物是否存在 diff --git a/database/userSign.py b/database/userSign.py index e43b053..dea9c8e 100644 --- a/database/userSign.py +++ b/database/userSign.py @@ -8,8 +8,8 @@ from zhenxun.utils._build_image import BuildImage from ..dbService import g_pDBService from ..json import g_pJsonManager -from .database import CSqlManager from ..tool import g_pToolManager +from .database import CSqlManager class CUserSignDB(CSqlManager): @@ -22,7 +22,7 @@ class CUserSignDB(CSqlManager): "isSupplement": "TINYINT NOT NULL DEFAULT 0", #是否补签 "exp": "INT NOT NULL DEFAULT 0", #当天签到经验 "point": "INT NOT NULL DEFAULT 0", #当天签到金币 - "createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')",#创建时间 + "createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))",#创建时间 "PRIMARY KEY": "(uid, signDate)" } @@ -35,7 +35,7 @@ class CUserSignDB(CSqlManager): "lastSignDate": "DATE DEFAULT NULL", #上次签到日期 "continuousDays": "INT NOT NULL DEFAULT 0", #连续签到天数 "supplementCount": "INT NOT NULL DEFAULT 0", #补签次数 - "updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')"#更新时间 + "updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))"#更新时间 } await cls.ensureTableSchema("userSignLog", userSignLog) @@ -188,7 +188,7 @@ class CUserSignDB(CSqlManager): ) #计算累签奖励 - reward = g_pJsonManager.m_pSign['continuou'].get(f"{totalSignDays + 1}", None) + reward = g_pJsonManager.m_pSign['continuou'].get(f"{totalSignDays}", None) if reward: point += reward.get('point', 0) diff --git a/database/userSoil.py b/database/userSoil.py index 5500637..9702930 100644 --- a/database/userSoil.py +++ b/database/userSoil.py @@ -4,8 +4,8 @@ from zhenxun.services.log import logger from ..dbService import g_pDBService from ..json import g_pJsonManager -from .database import CSqlManager from ..tool import g_pToolManager +from .database import CSqlManager class CUserSoilDB(CSqlManager): @@ -241,6 +241,45 @@ class CUserSoilDB(CSqlManager): (value, uid, soilIndex), ) + @classmethod + async def updateUserSoilFields(cls, uid: str, soilIndex: int, updates: dict) -> bool: + """批量更新指定用户土地的多个字段 + + Args: + uid (str): 用户ID + soilIndex (int): 土地索引 + updates (dict): 字段-新值的字典 + + Returns: + bool: 如果无可更新字段则返回 False,否则更新成功返回 True + """ + #允许更新的列白名单 + allowedFields = { + "plantName", "plantTime", "matureTime", "soilLevel", + "wiltStatus", "fertilizerStatus", "bugStatus", + "weedStatus", "waterStatus", "harvestCount" + } + setClauses = [] + values = [] + for field, value in updates.items(): + if field not in allowedFields: + continue + setClauses.append(f'"{field}" = ?') + values.append(value) + if not setClauses: + return False + + values.extend([uid, soilIndex]) + sql = f'UPDATE userSoil SET {", ".join(setClauses)} WHERE uid = ? AND soilIndex = ?' + + try: + async with cls._transaction(): + await cls.m_pDB.execute(sql, tuple(values)) + return True + except Exception as e: + logger.error(f"批量更新土地字段失败: {e}") + return False + @classmethod async def deleteUserSoil(cls, uid: str, soilIndex: int): """删除指定用户的土地记录 diff --git a/farm/farm.py b/farm/farm.py index 6708c55..306c28d 100644 --- a/farm/farm.py +++ b/farm/farm.py @@ -289,18 +289,19 @@ class CFarmManager: currentTime = g_pToolManager.dateTime().now() matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['matureTime'])) + phase = await g_pDBService.plant.getPlantPhaseNumberByName(soilInfo['plantName']) + #如果当前时间大于成熟时间 说明作物成熟 if currentTime >= matureTime: - phase = int(plantInfo['phase']) - plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{phase - 1}.png") + plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{phase}.png") return True, plant, True else: - #如果是多阶段作物 且没有成熟 - if soilInfo['harvestCount'] >= 1: - plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 2}.png") + #如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片 + # if soilInfo['harvestCount'] >= 1: + # plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1s}.png") - return True, plant, False + # return True, plant, False #如果没有成熟 则根据当前阶段进行绘制 plantedTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['plantTime'])) @@ -308,7 +309,7 @@ class CFarmManager: elapsedTime = currentTime - plantedTime elapsedHour = elapsedTime.total_seconds() / 3600 - currentStage = int(elapsedHour / (plantInfo['time'] / (plantInfo['phase'] - 1))) + currentStage = int(elapsedHour / (plantInfo['time'] / phase)) if currentStage <= 0: if plantInfo['general'] == False: @@ -334,7 +335,6 @@ class CFarmManager: "收获数量", "成熟时间(小时)", "收获次数", - "再次成熟时间(小时)", "是否可以上架交易行" ] @@ -358,7 +358,7 @@ class CFarmManager: iconPath = g_sResourcePath / f"plant/{seedName}/icon.png" icon = (iconPath, 33, 33) if iconPath.exists() else "" - sellable = "可以" if plantInfo['again'] else "不可以" + sellable = "可以" if plantInfo['sell'] else "不可以" dataList.append([ icon, @@ -368,7 +368,6 @@ class CFarmManager: plantInfo['harvest'], plantInfo['time'], plantInfo['crop'], - plantInfo['again'], sellable ]) except KeyError: @@ -509,11 +508,18 @@ class CFarmManager: if soilInfo['harvestCount'] + 1 >= plantInfo['crop']: await g_pDBService.userSoil.updateUserSoil(uid, i, "wiltStatus", 1) else: - matureTs = int(currentTime.timestamp()) + int(plantInfo.get("again", 0)) * 3600 + phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) - await g_pDBService.userSoil.updateUserSoil(uid, i, "harvestCount", soilInfo['harvestCount'] + 1) - await g_pDBService.userSoil.updateUserSoil(uid, i, "lastResetTime", int(currentTime.timestamp())) - await g_pDBService.userSoil.updateUserSoil(uid, i, "matureTime", matureTs) + ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1 + p1, p2, *rest = phase + + await g_pDBService.userSoil.updateUserSoilFields(uid, i, + { + "harvestCount": hc, + "plantTime": ts - p1 - p2, + "matureTime": ts + p2 + sum(rest), + } + ) await g_pEventManager.m_afterHarvest.emit(uid=uid, name=soilInfo['plantName'], num=number, soilIndex=i) @@ -619,7 +625,7 @@ class CFarmManager: if icon_path.exists(): icon = (icon_path, 33, 33) - if plantInfo['again'] == True: + if plantInfo['sell'] == True: sell = "可以" else: sell = "不可以" @@ -722,11 +728,18 @@ class CFarmManager: if soilInfo['harvestCount'] + 1 >= plantInfo['crop']: await g_pDBService.userSoil.updateUserSoil(target, i, "wiltStatus", 1) else: - matureTs = int(currentTime.timestamp()) + int(plantInfo.get("again", 0)) * 3600 + phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) - await g_pDBService.userSoil.updateUserSoil(uid, i, "harvestCount", soilInfo['harvestCount'] + 1) - await g_pDBService.userSoil.updateUserSoil(uid, i, "lastResetTime", int(currentTime.timestamp())) - await g_pDBService.userSoil.updateUserSoil(uid, i, "matureTime", matureTs) + ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1 + p1, p2, *rest = phase + + await g_pDBService.userSoil.updateUserSoilFields(uid, i, + { + "harvestCount": hc, + "plantTime": ts - p1 - p2, + "matureTime": ts + p2 + sum(rest), + } + ) await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(g_pToolManager.dateTime().now().timestamp())) diff --git a/farm/shop.py b/farm/shop.py index d5f024a..1843b0d 100644 --- a/farm/shop.py +++ b/farm/shop.py @@ -12,16 +12,28 @@ from ..json import g_pJsonManager class CShopManager: - @classmethod - async def getSeedShopImage(cls, num: int = 1) -> bytes: + async def getSeedShopImage(cls, filterKey: str|int = 1, num: int = 1) -> bytes: """获取商店页面 + Args: + filterKey (str|int): + - 字符串: 根据关键字筛选种子名称 + - 整数: 翻至对应页(无筛选) + num (int, optional): 当 filterKey 为字符串时,用于指定页码。Defaults to 1. + Returns: bytes: 返回商店图片bytes """ + # 解析参数:区分筛选关键字和页码 + filterStr = None + if isinstance(filterKey, int): + page = filterKey + else: + filterStr = filterKey + page = num - dataList = [] + # 表头定义 columnName = [ "-", "种子名称", @@ -32,59 +44,65 @@ class CShopManager: "收获数量", "成熟时间(小时)", "收获次数", - "再次成熟时间(小时)", "是否可以上架交易行" ] - sell = "" + # 查询所有可购买作物,并根据筛选关键字过滤 plants = await g_pDBService.plant.listPlants() - plantSize = await g_pDBService.plant.countPlants(True) - - start = (num - 1) * 15 - items = islice(plants, start, start + 15) - - for plant in items: + filteredPlants = [] + for plant in plants: + # 跳过未解锁购买的种子 if plant['isBuy'] == 0: continue + # 字符串筛选 + if filterStr and filterStr not in plant['name']: + continue + filteredPlants.append(plant) + # 计算分页 + totalCount = len(filteredPlants) + pageCount = math.ceil(totalCount / 15) if totalCount else 1 + startIndex = (page - 1) * 15 + pageItems = filteredPlants[startIndex: startIndex + 15] + + # 构建数据行 + dataList = [] + for plant in pageItems: + # 图标处理 icon = "" iconPath = g_sResourcePath / f"plant/{plant['name']}/icon.png" if iconPath.exists(): icon = (iconPath, 33, 33) - if plant['again'] == True: - sell = "可以" - else: - sell = "不可以" + # 交易行标记 + sell = "可以" if plant['sell'] else "不可以" - dataList.append( - [ - icon, - plant['name'], - plant['buy'], - plant['level'], - plant['price'], - plant['experience'], - plant['harvest'], - plant['time'], - plant['crop'], - plant['again'], - sell - ] - ) + dataList.append([ + icon, + plant['name'], # 种子名称 + plant['buy'], # 种子单价 + plant['level'], # 解锁等级 + plant['price'], # 果实单价 + plant['experience'], # 收获经验 + plant['harvest'], # 收获数量 + plant['time'], # 成熟时间(小时) + plant['crop'], # 收获次数 + sell # 是否可上架交易行 + ]) - count = math.ceil(plantSize / 15) - title = f"种子商店 页数: {num}/{count}" + # 页码标题 + title = f"种子商店 页数: {page}/{pageCount}" + # 渲染表格并返回图片bytes result = await ImageTemplate.table_page( title, "购买示例:@小真寻 购买种子 大白菜 5", columnName, dataList, ) - return result.pic2bytes() + @classmethod async def buySeed(cls, uid: str, name: str, num: int = 1) -> str: """购买种子 @@ -120,7 +138,7 @@ class CShopManager: else: await g_pDBService.user.updateUserPointByUid(uid, point - total) - if await g_pDBService.userSeed.addUserSeedByUid(uid, name, num) == False: + if not await g_pDBService.userSeed.addUserSeedByUid(uid, name, num): return "购买失败,执行数据库错误!" return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币" diff --git a/log/log.md b/log/log.md index acc7f9b..b0243c3 100644 --- a/log/log.md +++ b/log/log.md @@ -1,5 +1,19 @@ # 真寻农场更新日志 +## V1.4 +用户方面 +--- +- 新增种子商店筛选功能(如果没有BUG的话后续我的种子、我的作物等也会加入筛选功能 +- 新增签到功能(测试 +- 新增农场详述功能,能通过该功能更加详细的观看农场数据 +- 修复了迁移旧数据库无法正常迁移的BUG + +代码方面 +--- +- 修正了多阶段作物成长素材计算逻辑 +- 修正了作物数据库字段错乱的问题 +- 作物新增offset字段,用于以后偏移坐标(尚未加入代码 TODO + ## V1.3 用户方面 --- diff --git a/resource/db/plant.db b/resource/db/plant.db index 32e81630ef928f9e0dfdc95369428e3fcfe1b965..46975e9c86e34275cbaeb86d5c59b42935eb9252 100644 GIT binary patch literal 36864 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 DUDQCR literal 20480 zcmeHOZEV}d873)-`XG5H$&w}65_xhyX4$gik0eGLw5SsZT~OCaQyFMdG=`d}tp>+- zY^BJy2E&>(fZZld6B}7jH%^x{Zk7eJn{I1Tr(4?r`>_E7hG9Q8v_Fbjl!5J6H|$4% zV#lK-o2Mw%?%T08@Y|z!YE#Fa^GG z1zu^hIYXfkKlwmD^XyP|bSN{NCm;50yHdUB6iWAQ?MorD3pMBVqwW3a)T60gXy>l& zkN57{jUG$wM!f^+9ozeHhsRU>=_DG?9LS<{>Zvs9-+|YGzP=zN87%g z+V&V~HqnP4LLG@DdiKzFYZ48tL>nK;)ecw!QFBKUbtX|)5_KnV={u>4yf0hx z2&*Wi1HF(Ldoeqnui2xFm*}Hoxx0in(GoSE!$M)1`zk_}1tCa@wQp=>wC2GHSR!Vg z&*X;h9M6Qx-|&UZcx?ej35uzmSg_}_!`ZP+ZJk$eQ))xx@$69T4kd7fog3eJm+M_d z%lyPnK@7FF^1I31I(~2n|NS~Ro;`TTxTe&m&2k4*V6$Qpa}o%Hbce`I^DkoQc&C){G_ zp%BmIhWBR=my$ShC_h5B2g!43u#^14|E}_##sl-^^-gDK%NG9VzEz{dc>d7-+{j=) zdth|%Zo92nZyvC!@5(TcT%G8~MkQ;FjZz94&GO^QXs>$4;Ol4XDL+F>o0VsXfmNOo z#s(ShORSag-ayQ8y9BL_%OrexNH%!oAvNtul>fSbpTkA?7W^LE10R54@Ne*E@N@76 z7zH~(J5ZFrE5B1ND=#Y<_~IwI|nddl(GZ>9iK zfGNNfUqA8p7sUH=m&MJL|YREB(yOO00O4rfev_dZTcD#v^R?hr&U@Eyf#ZN_+9dMDgSqkMK;O zF&q{oQEOPCxLrKfxUF#E1FWH&|4uL*^NCu7CM1_moW7NQ9)#g8r)E}gCr>S;m(3x^Nk;=^lV(Ws~eX+p5L@ETUu$$LWejeb!J z(1bwoK3{M@0VkY<;ltDJ+MNK9WzL{z>t*VUYg)tp1p?Us1P7cfcVlI z1YTGdZNO&>Xad0T@jdO}{t*mByrQPi1O=Z=;rzu4L8b}v(#$N@%g!wZ{9!0INwgf& z(){cCxm);a*tt)kKPWjg4;{p#PtBTyNHAO{YHpg~F1%-4csut=EEM)RG#5>9=_e=j zh3hu%XLY_15H%-FaN@7AeDkuvy<6uGVuENPxLj_rF#o>(?uR9ULv>(++Z-ayYvx$G zG*>)5!(FNK)#J>z;}f;uc8r@RijRRQj+hOHaPef6F+D5r8?xNopR2&K(lzI3s4*#%)DZmt93NQtj0!#s> z08@Y|z!YE#Fa?+bOo4kz0XJ_8@x(CEWBmWWR@%ei_y4!xEIb1D!iS*>z5<_vkHDMY z``~G?1vDzRmA@*#RHl@?l2$e<0r{WupXB%D)AFd?FRz#F(ihUNrSsBDI1pQy0!#s> z08@Y|z!YE#Fa?+b_oV`;$IDqQ0<=Q_ZLVTa1_88*X3+isw6Tgo*#l5F&7j=@sH=)W znFH2$dO52(;LhFv)Imq0tpR956@zjHp!O;TWeiZ;Rv6}MQL8UNP13m47NEA$xYZS) zwp8I3Q-GSFajPdl4br&P5}*dEaEl`#Ugzbkj({pdK$G9gSq%ZzegM@+AI542P`xy6 zbpxo7#;s-m6;$CCFMz7hxYY`v$~10u0>mX+hAJa~>Y+neeE_PP#;rC0)kWi07l7)d zajOYH6)iY60a!f%F$cyi@Axei0A$B!NW&HfK-7i?r%k_-v495-QNFu z@YuW-?B!q^_zWI|zlR^gm*7?KN00-1;V>HBi1XD7`RUBG1vyQciG)ci~qtP@gQ{ALVN>E*1+d6)At*O(%%%V~UX_D&E2GS&DqIRQc z)`QxNrdj$)5(1g~(Mp=6pwnVB&Du?Z>?Jcdf}}|*G6BV=@s;9BootKo