Files
main_plugin_farm/utils/darw/drawFarm.py
T

492 lines
16 KiB
Python
Raw Normal View History

2025-11-30 16:45:13 +08:00
from zhenxun.configs.config import Config
from zhenxun.services.log import logger
from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import ImageTemplate
from zhenxun.utils.platform import PlatformUtils
from ...core import CPlayer
2025-11-30 16:45:13 +08:00
from ...core.dbService import g_pDBService
from .. import config, getJsonManager, getToolManager, utils
2025-11-30 16:45:13 +08:00
class CDarwFarm:
@classmethod
async def drawFarmByUid(cls, uid: str) -> bytes:
"""绘制用户农场
Args:
uid (str): 用户UID
Returns:
bytes: 返回绘制结果
"""
dynamic = Config.get_config("zhenxun_plugin_farm", "动态背景")
img, soilSize, soilPos, grass = await cls.createBaseCanvas(dynamic)
2025-11-30 16:45:13 +08:00
player = await getToolManager().getPlayerByUid(uid)
if not player:
return img.pic2bytes()
soilUnlock = int(player.user.get("soil", 3))
isFirstExpansion = True # 首次添加扩建图片
isFirstRipe = True
for index in range(0, 30):
x = soilPos[str(index + 1)]["x"]
y = soilPos[str(index + 1)]["y"]
# 已解锁格子
if index < soilUnlock:
isRipe = await cls.drawUnlockedTile(img, uid, index + 1, x, y, soilSize)
if isRipe and isFirstRipe:
await cls.pasteRipeOnce(img, x, y, soilSize)
isFirstRipe = False
else:
addedExpansion = await cls.drawLockedTile(
img, x, y, soilSize, grass, isFirstExpansion
)
if addedExpansion:
isFirstExpansion = False
await cls.drawUserInfo(img, player, uid)
2025-11-30 16:45:13 +08:00
await cls.applyDefinitionResize(img)
return img.pic2bytes()
@classmethod
async def createBaseCanvas(
cls, dynamic: bool
) -> tuple[BuildImage, tuple, dict, BuildImage]:
2025-11-30 16:45:13 +08:00
"""创建基础画布并返回 (img, soilSize, soilPos, grass)
Returns:
tuple: (主画布, 土地尺寸, 土地坐标映射, 草地图)
"""
if dynamic:
season = getToolManager().getSeason()
backgroundPath = config.g_sResourcePath / "background/decorateIcon"
# 节日优先级更高
if season == utils.Season.SPRING:
bgFile = backgroundPath / "decorate_spring_1.jpg"
elif season == utils.Season.SUMMER:
bgFile = backgroundPath / "decorate_summer_1.jpg"
elif season == utils.Season.AUTUMN:
bgFile = backgroundPath / "decorate_autumn_1.jpg"
elif season == utils.Season.WINTER:
bgFile = backgroundPath / "decorate_winter_1.jpg"
else:
bgFile = backgroundPath / "decorate_default_1.jpg"
img = BuildImage(background=bgFile)
else:
img = BuildImage(
background=config.g_sResourcePath
/ "background/decorateIcon/decorate_default_1jpg"
)
2025-11-30 16:45:13 +08:00
soil = await getJsonManager().getSoil()
soilSize = soil["size"]
soilPos = soil["soil"]
grass = BuildImage(background=config.g_sResourcePath / "soil/草土地.png")
await grass.resize(0, soilSize[0], soilSize[1])
return img, soilSize, soilPos, grass
@classmethod
async def drawUnlockedTile(
cls, img: BuildImage, uid: str, soilIndex: int, x: int, y: int, soilSize
) -> bool:
"""绘制已解锁土地格(包含土地贴图与作物),返回该格是否可收获
2025-11-30 16:45:13 +08:00
Args:
img (BuildImage): 主画布
uid (str): 用户UID
soilIndex (int): 土地索引(从1开始)
x (int), y (int): 贴图位置
soilSize: 土地贴图尺寸([w,h] 或 (w,h))
2025-11-30 16:45:13 +08:00
Returns:
bool: 是否有可收获作物(isRipe)
2025-11-30 16:45:13 +08:00
"""
soilUrl = "soil/普通土地.png"
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
if soilInfo:
soilLevel = soilInfo.get("soilLevel", 0)
if soilLevel == 1:
soilUrl = "soil/红土地.png"
elif soilLevel == 2:
soilUrl = "soil/黑土地.png"
elif soilLevel == 3:
soilUrl = "soil/金土地.png"
soil = BuildImage(background=config.g_sResourcePath / soilUrl)
await soil.resize(0, soilSize[0], soilSize[1])
await img.paste(soil, (x, y))
isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant(
uid, soilIndex
)
if isPlant and plant is not None:
await img.paste(
plant,
(
x + soilSize[0] // 2 - plant.width // 2 + offsetX,
y + soilSize[1] // 2 - plant.height // 2 + offsetY,
),
)
return bool(isRipe)
@classmethod
async def drawLockedTile(
cls,
img: BuildImage,
x: int,
y: int,
soilSize,
grass: BuildImage,
isFirstExpansion: bool,
) -> bool:
"""绘制未解锁格子并在首次出现时绘制扩建图标
2025-11-30 16:45:13 +08:00
Returns:
bool: 是否在此格绘制了扩建图标(仅首次返回 True)
2025-11-30 16:45:13 +08:00
"""
await img.paste(grass, (x, y))
if isFirstExpansion:
expansion = BuildImage(
background=config.g_sResourcePath / "background/expansion.png"
)
await expansion.resize(0, 69, 69)
await img.paste(
expansion,
(
x + soilSize[0] // 2 - expansion.width // 2,
y + soilSize[1] // 2 - expansion.height,
),
)
return True
return False
@classmethod
async def pasteRipeOnce(cls, img: BuildImage, x: int, y: int, soilSize):
"""首次发现成熟作物时贴上成熟提示图(ripe)"""
2025-11-30 16:45:13 +08:00
ripe = BuildImage(background=config.g_sResourcePath / "background/ripe.png")
await img.paste(
ripe,
(x + soilSize[0] // 2 - ripe.width // 2, y - ripe.height // 2),
)
@classmethod
async def drawUserInfo(cls, img: BuildImage, player: CPlayer, uid: str):
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)
2025-11-30 16:45:13 +08:00
TODO 需要详细的绘制头像框
Args:
img (BuildImage): 主画布
player(CPlayer): 用户
uid (str): 用户Uid
2025-11-30 16:45:13 +08:00
"""
# 背景板
background = BuildImage(
background=config.g_sResourcePath / "background/userInfo.png"
)
await img.paste(background, (150, 30))
# 头像
2025-11-30 16:45:13 +08:00
image = await PlatformUtils.get_user_avatar(uid, "qq")
if image:
avatar = BuildImage(background=image)
await avatar.resize(0, 140, 150)
await img.paste(avatar, (125, 85))
# 头像框
frame = BuildImage(
background=config.g_sResourcePath / "background/frame/frame.png"
)
2025-11-30 16:45:13 +08:00
await img.paste(frame, (75, 44))
nameImg = await BuildImage.build_text_image(
player.user["name"], size=24, font_color=(77, 35, 4)
)
await img.paste(nameImg, (300, 92))
level = await player.getUserLevel()
beginX = 309
endX = 627
width = int((level[2] / level[1]) * (endX - beginX)) if level[1] else 0
await img.rectangle((beginX, 188, beginX + width, 222), (171, 194, 41))
# 经验
expImg = await BuildImage.build_text_image(
f"{level[2]} / {level[1]}", size=24, font_color=(102, 120, 19)
)
await img.paste(expImg, (390, 193))
# 等级
levelImg = await BuildImage.build_text_image(
str(level[0]), size=32, font_color=(214, 111, 1)
)
await img.paste(levelImg, (660, 187))
# 农场币
pointImg = await BuildImage.build_text_image(
str(player.user.get("point", 0)), size=24, font_color=(253, 253, 253)
)
await img.paste(pointImg, (330, 255))
# 点券
bondsImg = await BuildImage.build_text_image(
"0", size=24, font_color=(253, 253, 253)
)
await img.paste(bondsImg, (570, 255))
@classmethod
async def drawUserDecorateIcon(cls, img: BuildImage, player: CPlayer) -> bool:
"""绘制用户装饰图标
Args:
img (BuildImage): 主画布
player (CPlayer): 用户
Returns:
bool: 是否成功绘制
"""
decorateIconId = player.user.get("decorateIcon", 0)
if decorateIconId <= 0:
return False
iconPath = (
config.g_sResourcePath / f"decorateIcon/DecorateIcon{decorateIconId}.png"
)
if not iconPath.exists():
return False
icon = BuildImage(background=iconPath)
await icon.resize(0, 100, 100)
await img.paste(icon, (x, y))
return True
2025-11-30 16:45:13 +08:00
@classmethod
async def applyDefinitionResize(cls, img: BuildImage):
"""根据配置调整绘制清晰度(original/medium/hight/其他)"""
2025-11-30 16:45:13 +08:00
definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度")
if definition == "medium":
await img.resize(0.6)
elif definition == "hight":
await img.resize(0.8)
elif definition == "original":
pass
else:
await img.resize(0.4)
@classmethod
async def drawDetailFarmByUid(cls, uid: str) -> list:
"""绘制用户详细信息
Args:
uid (str): 用户Uid
Returns:
list:
"""
info = []
farm = await cls.drawFarmByUid(uid)
player = await getToolManager().getPlayerByUid(uid)
if not player:
return info
info.append(BuildImage.open(farm))
dataList = []
columnName = [
"-",
"土地ID",
"土地等级",
"作物名称",
"成熟时间",
"土地状态",
"被偷数量",
"剩余产出",
]
icon = ""
soilNumber = player.user.get("soil", 3)
for i in range(1, soilNumber + 1):
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
if not soilInfo:
continue
match soilInfo.get("soilLevel", 0):
case 1:
name = "红土地.png"
case 2:
name = "黑土地.png"
case 3:
name = "金土地.png"
case _:
name = "普通土地.png"
iconPath = config.g_sResourcePath / "soil" / name
if iconPath.exists():
icon = (iconPath, 33, 33)
plantName = soilInfo.get("plantName", "-")
if plantName == "-":
matureTime = "-"
soilStatus = "-"
totalNumber = "-"
plantNumber = "-"
else:
matureTime = (
getToolManager()
.dateTime()
.fromtimestamp(int(soilInfo.get("matureTime", 0)))
.strftime("%Y-%m-%d %H:%M:%S")
)
soilStatus = await g_pDBService.userSoil.getUserSoilStatus(uid, i)
totalNumber = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
planInfo = await g_pDBService.plant.getPlantByName(plantName)
if not planInfo:
plantNumber = "None"
else:
plantNumber = f"{planInfo['harvest'] - totalNumber}"
dataList.append(
[
icon,
i,
await g_pDBService.userSoil.getSoilLevelText(soilInfo["soilLevel"]),
plantName,
matureTime,
soilStatus,
totalNumber,
plantNumber,
]
)
if len(dataList) >= 15:
result = await ImageTemplate.table_page(
"土地详细信息",
"",
columnName,
dataList,
)
info.append(result.copy())
dataList.clear()
if i >= soilNumber:
result = await ImageTemplate.table_page(
"土地详细信息",
"",
columnName,
dataList,
)
info.append(result.copy())
dataList.clear()
return info
@classmethod
async def drawSoilPlant(
cls, uid: str, soilIndex: int
) -> tuple[bool, BuildImage, bool, int, int]:
"""绘制植物资源
Args:
uid (str): 用户Uid
soilIndex (int): 土地索引 从1开始
Returns:
tuple[bool, BuildImage]: [绘制是否成功,资源图片, 是否成熟]
"""
plant = None
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
if not soilInfo:
return False, None, False, 0, 0 # type: ignore
# 是否枯萎
if int(soilInfo.get("wiltStatus", 0)) == 1:
plant = BuildImage(background=config.g_sResourcePath / "plant/basic/9.png")
await plant.resize(0, 150, 212)
return True, plant, False, 0, 0
# 获取作物详细信息
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"])
if not plantInfo:
logger.error(f"绘制植物资源失败: {soilInfo['plantName']}")
return False, None, False, 0, 0 # type: ignore
offsetX = plantInfo.get("officX", 0)
offsetY = plantInfo.get("officY", 0)
offsetW = plantInfo.get("officW", 0)
offsetH = plantInfo.get("officH", 0)
currentTime = getToolManager().dateTime().now().timestamp()
phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo["plantName"])
# 如果当前时间大于成熟时间 说明作物成熟
if currentTime >= soilInfo["matureTime"]:
plant = BuildImage(
background=config.g_sResourcePath
/ f"plant/{soilInfo['plantName']}/{len(phaseList)}.png"
)
return True, plant, True, offsetX, offsetY
else:
# 如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片
# if soilInfo["harvestCount"] >= 1:
# plant = BuildImage(
# background=g_sResourcePath
# / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1}.png"
# )
# return True, plant, False, offsetX, offsetY
# 如果没有成熟 则根据当前阶段进行绘制
elapsedTime = currentTime - soilInfo["plantTime"]
currentStage = currentStage = sum(
1 for thr in phaseList if elapsedTime >= thr
)
if currentStage <= 0:
if not plantInfo["general"]:
plant = BuildImage(
background=config.g_sResourcePath
/ f"plant/{soilInfo['plantName']}/0.png"
)
else:
plant = BuildImage(
background=config.g_sResourcePath / "plant/basic/0.png"
)
await plant.resize(0, 35 + offsetW, 58 + offsetH)
else:
plant = BuildImage(
background=config.g_sResourcePath
/ f"plant/{soilInfo['plantName']}/{currentStage}.png"
)
return True, plant, False, offsetX, offsetY