🧐 Render melee cards
|
@ -8,3 +8,4 @@ class Melee(db.Entity):
|
||||||
description = Optional(str)
|
description = Optional(str)
|
||||||
mechanism = Optional(str)
|
mechanism = Optional(str)
|
||||||
icon = Required(bytes)
|
icon = Required(bytes)
|
||||||
|
stats = Required(int)
|
||||||
|
|
|
@ -51,9 +51,16 @@ with db_session:
|
||||||
region_arr = io.BytesIO()
|
region_arr = io.BytesIO()
|
||||||
region.save(region_arr, format="PNG")
|
region.save(region_arr, format="PNG")
|
||||||
|
|
||||||
|
stats_map = {"Brutality": 1, "Survival": 2, "Tactic": 4}
|
||||||
|
stats = 0
|
||||||
|
for t in ["tier1", "tier2"]:
|
||||||
|
if t in i:
|
||||||
|
stats += stats_map[i[t]]
|
||||||
|
|
||||||
Melee(
|
Melee(
|
||||||
name=i["name"],
|
name=i["name"],
|
||||||
description=i["ambiantDesc"],
|
description=i["ambiantDesc"],
|
||||||
mechanism=i["gameplayDesc"],
|
mechanism=i["gameplayDesc"],
|
||||||
icon=region_arr.getbuffer().tobytes()
|
icon=region_arr.getbuffer().tobytes(),
|
||||||
|
stats=stats,
|
||||||
)
|
)
|
||||||
|
|
9
data/render/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# 数据渲染
|
||||||
|
|
||||||
|
将数据导出为图片
|
||||||
|
|
||||||
|
`server.py` 是用 Bottle 写的后端,运行 `./server.py` 然后打开 <http://localhost:8080/怨恨之刃> 就能看到怨恨之刃的网页。
|
||||||
|
|
||||||
|
之所以不直接用模板渲染,而要用 Web 服务器,是为了方便开发。
|
||||||
|
|
||||||
|
运行 `render_all.py` 会渲染所有近战武器,输出到 `output` 文件夹中。
|
BIN
data/render/output/串刺之剑.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
data/render/output/冥河提灯.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
data/render/output/利润匕首.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/刺剑.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/刺客匕首.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
data/render/output/化境.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/双匕首.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
data/render/output/双截锅.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/固化光剑.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
data/render/output/均衡之刃.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
data/render/output/墓碑.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
data/render/output/夜歌(手持).png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/大剑.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/对称长枪.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
data/render/output/巨人杀手.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/巨镰利爪.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
data/render/output/巨镰左爪.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
data/render/output/带刃双拐.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
data/render/output/平底锅.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
data/render/output/开发者武器.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
data/render/output/弩柄攻击.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
data/render/output/怨恨之刃.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/output/战矛.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
data/render/output/损坏的牙签.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
data/render/output/撬棍.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
data/render/output/斯巴达草鞋.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
data/render/output/施虐者匕首.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/服部的武士刀.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/油腻之剑.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
data/render/output/深海巨口.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/output/深渊三叉戟.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
data/render/output/火把.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/烤炉之斧.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/燧石.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
data/render/output/牙签.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
data/render/output/狂暴之刃.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
data/render/output/王后刺剑.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
data/render/output/瓦尔蒙特长鞭.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
data/render/output/生锈的刀.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
data/render/output/砍刀与手枪.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
data/render/output/破坏球.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/碎裂手斧.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
data/render/output/纯粹骨钉.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
data/render/output/缠绕鞭.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
data/render/output/胡桃夹子.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/节奏布祖基琴.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/output/蛇牙.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/output/血之刃.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
data/render/output/触手.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
data/render/output/诅咒之刃.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/迅捷之剑.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
data/render/output/钉入矛.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
data/render/output/钉鞋.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
data/render/output/铁扇.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/铁杖.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/铁钩手.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
data/render/output/铲子.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
data/render/output/隼之拳套.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
data/render/output/隼之靴.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
data/render/output/震地之刃.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
data/render/output/骨头.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
data/render/output/黄金匕首.png
Normal file
After Width: | Height: | Size: 23 KiB |
21
data/render/render_all.py
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# # -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
sys.path.append("../convert/31-0")
|
||||||
|
from db import *
|
||||||
|
|
||||||
|
db.bind(provider="sqlite", filename="../clean/31-0/data.sqlite3")
|
||||||
|
db.generate_mapping(create_tables=True)
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch()
|
||||||
|
page = browser.new_page()
|
||||||
|
with db_session:
|
||||||
|
for m in Melee.select():
|
||||||
|
print(f"rendering {m.name}...")
|
||||||
|
page.goto(f"http://localhost:8080/{m.name}")
|
||||||
|
page.locator(".container").screenshot(path=f"./output/{m.name}.png")
|
||||||
|
browser.close()
|
47
data/render/server.py
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# # -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
from bottle import route, run, static_file, view
|
||||||
|
|
||||||
|
sys.path.append("../convert/31-0")
|
||||||
|
from db import *
|
||||||
|
|
||||||
|
db.bind(provider="sqlite", filename="../clean/31-0/data.sqlite3")
|
||||||
|
db.generate_mapping(create_tables=True)
|
||||||
|
|
||||||
|
|
||||||
|
@route("/<name>")
|
||||||
|
@view("weapon")
|
||||||
|
def melee(name):
|
||||||
|
with db_session:
|
||||||
|
query = select(w for w in Melee if w.name == name)
|
||||||
|
weapon = query.first()
|
||||||
|
mechanism, _ = re.subn(
|
||||||
|
r"{(.*?)@(.*?)}", r"<span class='\2'>\1</span>", weapon.mechanism
|
||||||
|
)
|
||||||
|
stats_map = {
|
||||||
|
1: "brutality",
|
||||||
|
2: "survival",
|
||||||
|
3: "minotaur",
|
||||||
|
4: "tactics",
|
||||||
|
5: "assassin",
|
||||||
|
6: "guardian",
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"title": name,
|
||||||
|
"icon": base64.b64encode(weapon.icon),
|
||||||
|
"mechanism": mechanism,
|
||||||
|
"description": weapon.description,
|
||||||
|
"stats": stats_map[weapon.stats]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@route("/static/<filepath:path>")
|
||||||
|
def server_static(filepath):
|
||||||
|
return static_file(filepath, root="./static")
|
||||||
|
|
||||||
|
|
||||||
|
run(host="localhost", port=8080)
|
BIN
data/render/static/Infobox-background.webp
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
data/render/static/Table-border.webp
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
data/render/static/bg/assassin.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/static/bg/brutality.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/static/bg/guardian.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/static/bg/minotaur.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/static/bg/survival.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
data/render/static/bg/tactics.png
Normal file
After Width: | Height: | Size: 25 KiB |
131
data/render/static/style.css
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
body {
|
||||||
|
background: rgba(7, 12, 25, 0.85);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 350px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-image: url("/static/Table-border.webp");
|
||||||
|
border-image-width: 15px;
|
||||||
|
border-image-slice: 15 16;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 6px;
|
||||||
|
background: rgba(21, 30, 61, 0.7);
|
||||||
|
background-image: url("/static/Infobox-background.webp");
|
||||||
|
background-position: bottom right;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
background: rgba(6, 9, 19, 0.5);
|
||||||
|
font-family: "Noto Sans CJK SC";
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f7eb62;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.6;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-icon-bg {
|
||||||
|
margin: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-icon {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mechanism {
|
||||||
|
padding: 2px 16px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "Noto Sans CJK SC";
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CC {
|
||||||
|
color: #f0d60d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.BL {
|
||||||
|
color: #e03e35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RO {
|
||||||
|
color: #82a082;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TO {
|
||||||
|
color: #75dc2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FI {
|
||||||
|
color: #e5822b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.EL {
|
||||||
|
color: #37a3d7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.IC {
|
||||||
|
color: #91d5fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding: 2px 16px;
|
||||||
|
color: #90abe6;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "Noto Sans CJK SC";
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: #e0e0e0;
|
||||||
|
background: rgba(6, 9, 19, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
font-family: "Noto Sans CJK SC";
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.8;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dps {
|
||||||
|
padding: 10px 40px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
color: #e0e0e0;
|
||||||
|
margin: 2px auto;
|
||||||
|
text-align: center;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > tbody > tr:nth-child(odd) {
|
||||||
|
background: rgba(6, 9, 19, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > tbody > tr > td {
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: "Noto Sans CJK SC";
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary > td:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
26
data/render/views/weapon.tpl
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Template</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="title">{{ title }}</div>
|
||||||
|
<figure
|
||||||
|
class="weapon-icon-bg"
|
||||||
|
style="background-image: url('/static/bg/{{ stats }}.png');"
|
||||||
|
>
|
||||||
|
<img class="weapon-icon" src="data:image/png;base64,{{ icon }}" />
|
||||||
|
</figure>
|
||||||
|
% if mechanism:
|
||||||
|
<div class="mechanism">{{ !mechanism }}</div>
|
||||||
|
% end
|
||||||
|
% if description:
|
||||||
|
<div class="description">{{ description }}</div>
|
||||||
|
%end
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|