refactor: 收口 BangDream 玩家卡牌列表规格

This commit is contained in:
sunlei 2026-06-07 04:09:52 +08:00
parent 68b55764c2
commit 322938ae6f
4 changed files with 92 additions and 13 deletions

View File

@ -385,6 +385,7 @@ export interface TsuguHook {
- 已新增 `render-blocks/event-stage-spec.ts`,收口试炼列表最大列高、歌曲单元格/封面/ID/难度条尺寸、试炼类型顶部文字规格和换列判断;`event-stage.ts` 与 `list-event-stage.ts` 改为消费 spec活动/歌曲选择和输出结构保持不变。
- 线上 smoke 暴露 `/查试炼 310 -m` 将所有列横向拼成一张巨大 canvas 会触发 Pod `OOMKilled exit=137`;已改为按小批次绘制 stage、边分列边输出多张 CQ 图片,不再创建最终横向巨图,普通版和 meta 版均拆成 5 张输出,同时避免完全串行导致冷缓存首条命令耗时过长。
- 已新增 `event-stage-spec.spec.ts`,覆盖试炼歌曲行尺寸、类型顶部文字规格、换列判断和按列高拆分算法;本地生成 `event-stage-split-310*.jpg`、`event-stage-split-310-meta*.jpg`,验证 `/查试炼 310``/查试炼 310 -m` 图片输出正常。
- 已新增 `render-blocks/list-player-card-icon-spec.ts`,收口玩家详情主卡组展示顺序、默认行高、文本字号比例、卡牌间距比例和卡牌图标可见性标记;`list-player-card-icon-list.ts` 改为消费 spec主卡组渲染顺序和输出结构保持不变`list-player-card-icon-spec.spec.ts` 覆盖历史卡牌顺序、缺失条目跳过和字号/间距计算,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
- smoke/Jenkins/远程调试卡点继续按已固化规则处理同一卡点第二次尝试前必须改变可验证变量Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值smoke 没有真实图片落盘时必须失败。
### Phase 6策略 policy 和时间/档线规则

View File

@ -3,6 +3,12 @@ import { Player } from '@/qqbot/plugins/bangDream/tsugu/models/player';
import { Canvas } from 'skia-canvas';
import { drawCardIcon } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art';
import { drawList } from './list-frame';
import {
BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC,
getPlayerCardIconListSpacing,
getPlayerCardIconListTextSize,
sortPlayerMainDeckEntries,
} from './list-player-card-icon-spec';
/**
* In列表
@ -17,19 +23,14 @@ export async function drawPlayerCardInList(
player: Player,
key?: string,
cardIdVisible = false,
lineHeight = 184,
lineHeight = BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.list.defaultLineHeight,
): Promise<Canvas> {
const textSize = (lineHeight / 200) * 180;
const spacing = (lineHeight / 200) * 13;
const textSize = getPlayerCardIconListTextSize(lineHeight);
const spacing = getPlayerCardIconListSpacing(lineHeight);
const promiseList: Promise<Canvas>[] = [];
const tempCardDataList = player.profile.mainDeckUserSituations.entries;
//将tempCardDataList调整顺序为3,1,0,2,4
const defaultCardSort = [3, 1, 0, 2, 4];
const cardDataList = [];
for (let i = 0; i < defaultCardSort.length; i++) {
const tempCardData = tempCardDataList[defaultCardSort[i]];
cardDataList.push(tempCardData);
}
const cardDataList = sortPlayerMainDeckEntries(
player.profile.mainDeckUserSituations.entries,
);
const cardIconList: Array<Canvas> = [];
for (const i in cardDataList) {
const tempCardData = cardDataList[i];
@ -40,8 +41,9 @@ export async function drawPlayerCardInList(
illustrationTrainingStatus: tempCardData.illust == 'after_training',
limitBreakRank: tempCardData.limitBreakRank,
cardIdVisible: cardIdVisible,
skillTypeVisible: true,
cardTypeVisible: false,
skillTypeVisible:
BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.card.showSkillType,
cardTypeVisible: BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.card.showCardType,
skillLevel: tempCardData.skillLevel,
}),
);

View File

@ -0,0 +1,41 @@
export const BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC = {
card: {
defaultOrder: [3, 1, 0, 2, 4],
showCardType: false,
showSkillType: true,
},
list: {
defaultLineHeight: 184,
spacingRatio: 13 / 200,
textSizeRatio: 180 / 200,
},
} as const;
/**
*
*
* @param lineHeight -
*/
export function getPlayerCardIconListTextSize(lineHeight: number) {
return lineHeight * BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.list.textSizeRatio;
}
/**
*
*
* @param lineHeight -
*/
export function getPlayerCardIconListSpacing(lineHeight: number) {
return lineHeight * BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.list.spacingRatio;
}
/**
*
*
* @param entries -
*/
export function sortPlayerMainDeckEntries<T>(entries: T[]): T[] {
return BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.card.defaultOrder
.map((index) => entries[index])
.filter((entry): entry is T => entry !== undefined);
}

View File

@ -0,0 +1,35 @@
import {
BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC,
getPlayerCardIconListSpacing,
getPlayerCardIconListTextSize,
sortPlayerMainDeckEntries,
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-player-card-icon-spec';
describe('BangDream player card icon list spec', () => {
it('keeps player card list text and spacing ratios stable', () => {
const lineHeight =
BANGDREAM_PLAYER_CARD_ICON_LIST_SPEC.list.defaultLineHeight;
expect(lineHeight).toBe(184);
expect(getPlayerCardIconListTextSize(lineHeight)).toBe(165.6);
expect(getPlayerCardIconListSpacing(lineHeight)).toBe(11.96);
});
it('keeps historical player deck display order', () => {
expect(sortPlayerMainDeckEntries(['0', '1', '2', '3', '4'])).toEqual([
'3',
'1',
'0',
'2',
'4',
]);
});
it('skips missing deck entries without changing the remaining order', () => {
expect(sortPlayerMainDeckEntries(['0', '1', '2'])).toEqual([
'1',
'0',
'2',
]);
});
});