mirror of
https://github.com/Shu-Ying/main_plugin_farm.git
synced 2026-10-31 23:09:58 +08:00
add # 新增种子商店界面
fixed # 修复数据库目录错误的BUG
This commit is contained in:
Binary file not shown.
@@ -6244,6 +6244,13 @@
|
||||
30
|
||||
]
|
||||
},
|
||||
"webgl/shop-board.png": {
|
||||
"sha256": "88730b82d88cb33fae45c7180a88954fcc0241b2b4f5de095fc6b22b30296645",
|
||||
"size": [
|
||||
1024,
|
||||
1536
|
||||
]
|
||||
},
|
||||
"webgl/wither.png": {
|
||||
"sha256": "1c87f05e0b7bd8287926351e07746bc9e36371a860afaef0e4b00c51536dc472",
|
||||
"size": [
|
||||
|
||||
@@ -4,6 +4,9 @@ import asyncio
|
||||
import logging
|
||||
from time import perf_counter
|
||||
import time
|
||||
import hashlib
|
||||
|
||||
from .shop import render_shop
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -60,13 +63,39 @@ class RenderService:
|
||||
return await asyncio.shield(task)
|
||||
|
||||
async def _run(self, *args):
|
||||
return await self._run_function(self.renderer.render, *args)
|
||||
|
||||
async def seed_shop(self, plants, account, page, pages):
|
||||
if self._closed:
|
||||
raise RuntimeError('Render service closed')
|
||||
key = 'shop:' + hashlib.sha256(
|
||||
repr((plants, account, page, pages)).encode(),
|
||||
).hexdigest()
|
||||
task = self._pending.get(key)
|
||||
if task is None:
|
||||
if len(self._pending) >= self._max_pending:
|
||||
raise RuntimeError('Render queue full')
|
||||
task = asyncio.create_task(self._run_function(
|
||||
render_shop, self.renderer, plants, account, page, pages,
|
||||
))
|
||||
self._pending[key] = task
|
||||
|
||||
def finished(done):
|
||||
self._pending.pop(key, None)
|
||||
if not done.cancelled():
|
||||
done.exception()
|
||||
|
||||
task.add_done_callback(finished)
|
||||
return await asyncio.shield(task)
|
||||
|
||||
async def _run_function(self, function, *args):
|
||||
started = perf_counter()
|
||||
async with self._semaphore:
|
||||
logger.info(
|
||||
"farm render queue_ms=%.2f", (perf_counter()-started)*1000,
|
||||
)
|
||||
worker = asyncio.create_task(asyncio.to_thread(
|
||||
self.renderer.render, *args,
|
||||
function, *args,
|
||||
))
|
||||
try:
|
||||
return await asyncio.shield(worker)
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
"""文字指令使用的木质种子价目板;全部价格来自离线目录。"""
|
||||
|
||||
import logging
|
||||
import shlex
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .canvas import Canvas, font
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
PAGE_SIZE = 6
|
||||
|
||||
|
||||
def shop_page(catalogue, page):
|
||||
plants = sorted((p for p in catalogue if p.buyable),
|
||||
key=lambda p: p.plant_id)
|
||||
pages = max(1, (len(plants) + PAGE_SIZE - 1) // PAGE_SIZE)
|
||||
if type(page) is not int or not 1 <= page <= pages:
|
||||
raise ValueError('Invalid shop page')
|
||||
start = (page - 1) * PAGE_SIZE
|
||||
return tuple(plants[start:start + PAGE_SIZE]), pages
|
||||
|
||||
|
||||
def duration(seconds):
|
||||
hours, remainder = divmod(seconds, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
return ''.join(f'{value}{unit}' for value, unit in (
|
||||
(hours, '小时'), (minutes, '分'), (seconds, '秒'),
|
||||
) if value) or '0秒'
|
||||
|
||||
|
||||
def render_shop(renderer, plants, account, page, pages):
|
||||
canvas = Canvas(renderer.assets.image('webgl/shop-board.png', (1024, 1536)))
|
||||
|
||||
def text(value, xy, size=28, color='#5b3015', width=850):
|
||||
value = canvas.fit_text(value, renderer.font_path, size, width)
|
||||
box = font(renderer.font_path, size).getbbox(value)
|
||||
canvas.text((xy[0], xy[1]-box[1]), value,
|
||||
renderer.font_path, size, color)
|
||||
|
||||
def centered(value, y, size, color='#5b3015'):
|
||||
x = round((1024 - canvas.measure_text(
|
||||
value, renderer.font_path, size,
|
||||
)) / 2)
|
||||
text(value, (x, y), size, color)
|
||||
|
||||
centered('种 子 商 店', 58, 54, '#fff5d3')
|
||||
centered('种子价目板 · 发送文字指令购买', 178, 25, '#7a6439')
|
||||
balances = ('未开通农场 · 可浏览种子,购买前请发送:开通农场'
|
||||
if account is None else
|
||||
f'Lv.{account.level} 农场币 {account.point:,} '
|
||||
f'点券 {account.vip_point:,}')
|
||||
text(balances, (142, 248), 29, width=745)
|
||||
for index, plant in enumerate(plants):
|
||||
y = 326 + index * 156
|
||||
icon = f'plant/{plant.name}/icon.png'
|
||||
if icon in renderer.assets.metadata['images']:
|
||||
image = renderer.assets.image(icon)
|
||||
image.thumbnail((104, 110), Image.Resampling.LANCZOS)
|
||||
canvas.paste(image, (175-image.width//2, y+67-image.height//2))
|
||||
else:
|
||||
text('暂无图', (133, y+55), 23, '#8b8057', width=92)
|
||||
logger.info('farm shop packaged icon unavailable id=%s',
|
||||
plant.plant_id)
|
||||
text(plant.name, (268, y+13), 34, width=490)
|
||||
text(f'#{plant.plant_id}', (774, y+17), 26, '#69783e', width=132)
|
||||
currency = '点券' if plant.is_vip else '农场币'
|
||||
text(f'{plant.cost:,} {currency} / 粒', (268, y+62), 32,
|
||||
'#b66522', width=480)
|
||||
locked = account is not None and account.level < plant.level
|
||||
text(f'{"需" if locked else "等级"} Lv.{plant.level}',
|
||||
(774, y+69), 23, '#ac542d' if locked else '#66733d', width=132)
|
||||
text(f'首熟 {duration(plant.growth_seconds)} · {plant.crop}季'
|
||||
f' · 每季{plant.harvest}个', (268, y+108), 23, width=635)
|
||||
if not plants:
|
||||
text('暂无出售中的种子', (340, 690), 34)
|
||||
example = plants[0] if plants else None
|
||||
if example:
|
||||
text(f'按编号:购买种子 #{example.plant_id} 5', (142, 1291), 27)
|
||||
name = example.name
|
||||
if any(c.isspace() for c in name) or any(c in name for c in '\"\''):
|
||||
name = shlex.quote(name)
|
||||
text(f'按名称:购买种子 {name} 5', (142, 1341), 27, width=750)
|
||||
else:
|
||||
text('购买种子 #编号 数量 或 购买种子 种子名 数量',
|
||||
(142, 1316), 25, width=750)
|
||||
navigation = f'第 {page} / {pages} 页'
|
||||
if page < pages:
|
||||
navigation += f' 下一页:种子商店 {page+1}'
|
||||
elif page > 1:
|
||||
navigation += f' 上一页:种子商店 {page-1}'
|
||||
text(navigation, (263, 1420), 23, width=510)
|
||||
return canvas.encode('PNG')
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
Reference in New Issue
Block a user