koishi-plugin-vsinger-gacha 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/handbook.js +25 -12
- package/lib/render/kit.js +10 -6
- package/package.json +1 -1
package/lib/handbook.js
CHANGED
|
@@ -41,8 +41,8 @@ const LABEL_H = 62;
|
|
|
41
41
|
const GAP = 12;
|
|
42
42
|
const PAD = 24;
|
|
43
43
|
const HEAD = 128;
|
|
44
|
-
const POOL_TITLE_H =
|
|
45
|
-
const SERIES_H =
|
|
44
|
+
const POOL_TITLE_H = 46;
|
|
45
|
+
const SERIES_H = 50;
|
|
46
46
|
const COLS_PER_COL = 8;
|
|
47
47
|
const COLW = COLS_PER_COL * TW + (COLS_PER_COL - 1) * GAP;
|
|
48
48
|
const ROWH = TH + LABEL_H;
|
|
@@ -134,12 +134,17 @@ async function render({ game, store, uid, displayName, outDir, tagsDir, thumbsDi
|
|
|
134
134
|
|
|
135
135
|
// 每池一块(顺序填充)
|
|
136
136
|
const blocks = [];
|
|
137
|
+
let _lastAct = null;
|
|
137
138
|
for (const [act, pm] of seriesMap) {
|
|
138
139
|
for (const [pool, arr] of pm) {
|
|
139
140
|
arr.sort((a, b) => Number(b.v.rate_pct || 0) - Number(a.v.rate_pct || 0));
|
|
140
141
|
const rows = Math.max(1, Math.ceil(arr.length / COLS_PER_COL));
|
|
141
142
|
const got = arr.filter((c) => (owned[c.h] || 0) > 0).length;
|
|
142
|
-
|
|
143
|
+
const isNewSeries = act !== _lastAct;
|
|
144
|
+
// ★ 2026-11-05:系列头也要占高度,否则下一块会压到上一块的卡片上
|
|
145
|
+
const h = POOL_TITLE_H + rows * ROWH + 12 + (isNewSeries ? SERIES_H : 0);
|
|
146
|
+
blocks.push({ act, pool, arr, got, series: isNewSeries, h });
|
|
147
|
+
_lastAct = act;
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
const totalH = blocks.reduce((a, b) => a + b.h, 0);
|
|
@@ -231,24 +236,32 @@ async function render({ game, store, uid, displayName, outDir, tagsDir, thumbsDi
|
|
|
231
236
|
let prevAct = null;
|
|
232
237
|
for (const blk of grp) {
|
|
233
238
|
if (blk.act !== prevAct) {
|
|
234
|
-
|
|
239
|
+
const sh = SERIES_H - 8;
|
|
240
|
+
kit.panel(g, x0, y + 2, COLW, sh, { radius: 14, fill: 'rgba(255,255,255,0.88)' });
|
|
235
241
|
g.fillStyle = COLOR.accent;
|
|
236
|
-
kit.roundRect(g, x0 + 12, y +
|
|
242
|
+
kit.roundRect(g, x0 + 12, y + 2 + (sh - 20) / 2, 5, 20, 3);
|
|
237
243
|
g.fill();
|
|
238
244
|
g.fillStyle = COLOR.text;
|
|
239
|
-
g.font = 'bold
|
|
240
|
-
g.
|
|
245
|
+
g.font = 'bold 20px VSingerSans, sans-serif';
|
|
246
|
+
g.textBaseline = 'middle';
|
|
247
|
+
g.fillText(blk.act, x0 + 26, y + 2 + sh / 2);
|
|
248
|
+
g.textBaseline = 'top';
|
|
241
249
|
y += SERIES_H;
|
|
242
250
|
prevAct = blk.act;
|
|
243
251
|
}
|
|
244
252
|
// 池标题(玻璃条)
|
|
245
|
-
|
|
253
|
+
const ph = POOL_TITLE_H - 10;
|
|
254
|
+
kit.panel(g, x0, y + 1, COLW, ph, { radius: 12, fill: 'rgba(255,255,255,0.78)' });
|
|
255
|
+
g.textBaseline = 'middle';
|
|
246
256
|
g.fillStyle = COLOR.accent;
|
|
247
|
-
g.font = 'bold
|
|
248
|
-
g.fillText(
|
|
257
|
+
g.font = 'bold 18px VSingerSans, sans-serif';
|
|
258
|
+
g.fillText(kit.fit(g, blk.pool, 'bold 18px VSingerSans, sans-serif', COLW - 100), x0 + 12, y + 1 + ph / 2);
|
|
249
259
|
g.fillStyle = COLOR.textSub;
|
|
250
|
-
g.font = '
|
|
251
|
-
g.
|
|
260
|
+
g.font = '16px VSingerSans, sans-serif';
|
|
261
|
+
g.textAlign = 'right';
|
|
262
|
+
g.fillText(`${blk.got}/${blk.arr.length}`, x0 + COLW - 12, y + 1 + ph / 2);
|
|
263
|
+
g.textAlign = 'left';
|
|
264
|
+
g.textBaseline = 'top';
|
|
252
265
|
|
|
253
266
|
for (let i = 0; i < blk.arr.length; i++) {
|
|
254
267
|
const c = blk.arr[i];
|
package/lib/render/kit.js
CHANGED
|
@@ -76,16 +76,20 @@ function header(g, o) {
|
|
|
76
76
|
const W = o.W;
|
|
77
77
|
|
|
78
78
|
// 圆形图标
|
|
79
|
-
const R =
|
|
79
|
+
const R = 24;
|
|
80
|
+
const cx = x + R;
|
|
81
|
+
const cy = y + R;
|
|
80
82
|
g.save();
|
|
81
83
|
g.beginPath();
|
|
82
|
-
g.arc(
|
|
84
|
+
g.arc(cx, cy, R, 0, Math.PI * 2);
|
|
83
85
|
g.fillStyle = COLOR.accent;
|
|
84
86
|
g.fill();
|
|
85
87
|
g.fillStyle = '#fff';
|
|
86
|
-
g.font = `bold
|
|
88
|
+
g.font = `bold 26px ${FONT}`;
|
|
89
|
+
g.textAlign = 'center';
|
|
87
90
|
g.textBaseline = 'middle';
|
|
88
|
-
g.fillText('♪',
|
|
91
|
+
g.fillText('♪', cx, cy + 1);
|
|
92
|
+
g.textAlign = 'left';
|
|
89
93
|
g.restore();
|
|
90
94
|
|
|
91
95
|
g.textBaseline = 'top';
|
|
@@ -193,12 +197,12 @@ async function card(g, Canvas, o) {
|
|
|
193
197
|
|
|
194
198
|
g.fillStyle = o.owned === false ? COLOR.textDim : COLOR.text;
|
|
195
199
|
g.font = `bold ${o.nameSize || FONT_SIZE.h3}px ${FONT}`;
|
|
196
|
-
g.fillText(fit(g, o.name, `bold ${o.nameSize || FONT_SIZE.h3}px ${FONT}`, w), o.x, o.y + h +
|
|
200
|
+
g.fillText(fit(g, o.name, `bold ${o.nameSize || FONT_SIZE.h3}px ${FONT}`, w), o.x, o.y + h + 6);
|
|
197
201
|
if (o.rate != null || o.tierName) {
|
|
198
202
|
g.fillStyle = tc;
|
|
199
203
|
g.font = `${o.metaSize || FONT_SIZE.meta}px ${FONT}`;
|
|
200
204
|
const meta = `${o.tierName || (TIER[tier] || TIER[10]).name}${o.rate != null ? ' ' + o.rate + '%' : ''}`;
|
|
201
|
-
g.fillText(fit(g, meta, `${o.metaSize || FONT_SIZE.meta}px ${FONT}`, w), o.x, o.y + h +
|
|
205
|
+
g.fillText(fit(g, meta, `${o.metaSize || FONT_SIZE.meta}px ${FONT}`, w), o.x, o.y + h + 30);
|
|
202
206
|
}
|
|
203
207
|
}
|
|
204
208
|
|