refactor: 收口 BangDream 通用卡牌列表规格
This commit is contained in:
parent
322938ae6f
commit
e1ace51d6e
@ -386,6 +386,7 @@ export interface TsuguHook {
|
||||
- 线上 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 输出非空。
|
||||
- 已新增 `render-blocks/list-card-icon-spec.ts`,收口通用卡牌图标列表默认行高、文本字号比例、卡牌间距比例、默认可见性标记和历史排序比较器;`list-card-icon-list.ts` 改为消费 spec,卡牌图标列表排序和输出结构保持不变;`list-card-icon-spec.spec.ts` 覆盖稀有度排序、优先卡牌类型排序、普通卡按 ID 排序和字号/间距计算,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
||||
|
||||
### Phase 6:策略 policy 和时间/档线规则
|
||||
|
||||
@ -2,7 +2,12 @@ import { drawList } from './list-frame';
|
||||
import { Canvas } from 'skia-canvas';
|
||||
import { drawCardIcon } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art';
|
||||
import { Card } from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
||||
import { BANGDREAM_CARD_PRIORITY_TYPES } from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-constants';
|
||||
import {
|
||||
BANGDREAM_CARD_ICON_LIST_SPEC,
|
||||
getCardIconListSpacing,
|
||||
getCardIconListTextSize,
|
||||
sortCardIconListCards,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-card-icon-spec';
|
||||
|
||||
interface CardIconInListOptions {
|
||||
key?: string;
|
||||
@ -22,33 +27,15 @@ interface CardIconInListOptions {
|
||||
export async function drawCardListInList({
|
||||
key,
|
||||
cardList,
|
||||
cardIdVisible = false,
|
||||
skillTypeVisible = true,
|
||||
cardTypeVisible = true,
|
||||
cardIdVisible = BANGDREAM_CARD_ICON_LIST_SPEC.card.showCardId,
|
||||
skillTypeVisible = BANGDREAM_CARD_ICON_LIST_SPEC.card.showSkillType,
|
||||
cardTypeVisible = BANGDREAM_CARD_ICON_LIST_SPEC.card.showCardType,
|
||||
trainingStatus,
|
||||
lineHeight = 200,
|
||||
lineHeight = BANGDREAM_CARD_ICON_LIST_SPEC.list.defaultLineHeight,
|
||||
}: CardIconInListOptions) {
|
||||
//cardList排序,稀有度高的在前面,其中cardId低的在前面
|
||||
const typeList: readonly string[] = BANGDREAM_CARD_PRIORITY_TYPES;
|
||||
cardList.sort((a, b) => {
|
||||
if (a.rarity == b.rarity) {
|
||||
if (typeList.includes(a.type) && !typeList.includes(b.type)) {
|
||||
return -1;
|
||||
} else if (!typeList.includes(a.type) && typeList.includes(b.type)) {
|
||||
return 1;
|
||||
} else if (
|
||||
typeList.indexOf(a.type) != -1 &&
|
||||
typeList.indexOf(b.type) != -1
|
||||
) {
|
||||
return typeList.indexOf(a.type) - typeList.indexOf(b.type);
|
||||
}
|
||||
|
||||
return a.cardId - b.cardId;
|
||||
}
|
||||
return b.rarity - a.rarity;
|
||||
});
|
||||
const textSize = (lineHeight / 200) * 180;
|
||||
const spacing = (lineHeight / 200) * 13;
|
||||
sortCardIconListCards(cardList);
|
||||
const textSize = getCardIconListTextSize(lineHeight);
|
||||
const spacing = getCardIconListSpacing(lineHeight);
|
||||
const list: Array<Canvas> = [];
|
||||
for (let i = 0; i < cardList.length; i++) {
|
||||
const element: Card = cardList[i];
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
import { BANGDREAM_CARD_PRIORITY_TYPES } from '@/qqbot/plugins/bangDream/tsugu/runtime/runtime-options';
|
||||
|
||||
export interface CardIconListSortTarget {
|
||||
cardId: number;
|
||||
rarity: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export const BANGDREAM_CARD_ICON_LIST_SPEC = {
|
||||
card: {
|
||||
priorityTypes: BANGDREAM_CARD_PRIORITY_TYPES,
|
||||
showCardId: false,
|
||||
showCardType: true,
|
||||
showSkillType: true,
|
||||
},
|
||||
list: {
|
||||
defaultLineHeight: 200,
|
||||
spacingRatio: 13 / 200,
|
||||
textSizeRatio: 180 / 200,
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 计算卡牌图标列表文本字号。
|
||||
*
|
||||
* @param lineHeight - 列表行高。
|
||||
*/
|
||||
export function getCardIconListTextSize(lineHeight: number) {
|
||||
return lineHeight * BANGDREAM_CARD_ICON_LIST_SPEC.list.textSizeRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算卡牌图标列表卡牌间距。
|
||||
*
|
||||
* @param lineHeight - 列表行高。
|
||||
*/
|
||||
export function getCardIconListSpacing(lineHeight: number) {
|
||||
return lineHeight * BANGDREAM_CARD_ICON_LIST_SPEC.list.spacingRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按历史列表规则比较卡牌图标展示顺序。
|
||||
*
|
||||
* @param left - 左侧卡牌。
|
||||
* @param right - 右侧卡牌。
|
||||
*/
|
||||
export function compareCardIconListCards(
|
||||
left: CardIconListSortTarget,
|
||||
right: CardIconListSortTarget,
|
||||
) {
|
||||
if (left.rarity !== right.rarity) {
|
||||
return right.rarity - left.rarity;
|
||||
}
|
||||
|
||||
const priorityTypes = BANGDREAM_CARD_ICON_LIST_SPEC.card
|
||||
.priorityTypes as readonly string[];
|
||||
const leftTypeIndex = priorityTypes.indexOf(left.type);
|
||||
const rightTypeIndex = priorityTypes.indexOf(right.type);
|
||||
const leftHasPriority = leftTypeIndex !== -1;
|
||||
const rightHasPriority = rightTypeIndex !== -1;
|
||||
|
||||
if (leftHasPriority && !rightHasPriority) {
|
||||
return -1;
|
||||
}
|
||||
if (!leftHasPriority && rightHasPriority) {
|
||||
return 1;
|
||||
}
|
||||
if (leftHasPriority && rightHasPriority) {
|
||||
return leftTypeIndex - rightTypeIndex;
|
||||
}
|
||||
|
||||
return left.cardId - right.cardId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按卡牌图标列表展示规则原地排序。
|
||||
*
|
||||
* @param cards - 待排序卡牌。
|
||||
*/
|
||||
export function sortCardIconListCards<T extends CardIconListSortTarget>(
|
||||
cards: T[],
|
||||
): T[] {
|
||||
return cards.sort(compareCardIconListCards);
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
import { BangDreamCardType } from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-protocol';
|
||||
import {
|
||||
BANGDREAM_CARD_ICON_LIST_SPEC,
|
||||
compareCardIconListCards,
|
||||
getCardIconListSpacing,
|
||||
getCardIconListTextSize,
|
||||
sortCardIconListCards,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-card-icon-spec';
|
||||
|
||||
describe('BangDream card icon list spec', () => {
|
||||
it('keeps card icon list text and spacing ratios stable', () => {
|
||||
const lineHeight = BANGDREAM_CARD_ICON_LIST_SPEC.list.defaultLineHeight;
|
||||
|
||||
expect(lineHeight).toBe(200);
|
||||
expect(getCardIconListTextSize(lineHeight)).toBe(180);
|
||||
expect(getCardIconListSpacing(lineHeight)).toBe(13);
|
||||
});
|
||||
|
||||
it('sorts higher rarity cards before lower rarity cards', () => {
|
||||
const cards = [
|
||||
{ cardId: 1, rarity: 3, type: BangDreamCardType.permanent },
|
||||
{ cardId: 2, rarity: 5, type: BangDreamCardType.permanent },
|
||||
{ cardId: 3, rarity: 4, type: BangDreamCardType.permanent },
|
||||
];
|
||||
|
||||
expect(sortCardIconListCards(cards).map((card) => card.cardId)).toEqual([
|
||||
2, 3, 1,
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps priority card types before normal cards at the same rarity', () => {
|
||||
const cards = [
|
||||
{ cardId: 1, rarity: 5, type: BangDreamCardType.permanent },
|
||||
{ cardId: 2, rarity: 5, type: BangDreamCardType.limited },
|
||||
{ cardId: 3, rarity: 5, type: BangDreamCardType.dreamfes },
|
||||
{ cardId: 4, rarity: 5, type: BangDreamCardType.kirafes },
|
||||
{ cardId: 5, rarity: 5, type: BangDreamCardType.birthday },
|
||||
];
|
||||
|
||||
expect(sortCardIconListCards(cards).map((card) => card.cardId)).toEqual([
|
||||
4, 3, 2, 5, 1,
|
||||
]);
|
||||
});
|
||||
|
||||
it('sorts normal cards by card id when rarity is the same', () => {
|
||||
const cards = [
|
||||
{ cardId: 10, rarity: 4, type: BangDreamCardType.event },
|
||||
{ cardId: 8, rarity: 4, type: BangDreamCardType.permanent },
|
||||
{ cardId: 9, rarity: 4, type: BangDreamCardType.initial },
|
||||
];
|
||||
|
||||
expect(sortCardIconListCards(cards).map((card) => card.cardId)).toEqual([
|
||||
8, 9, 10,
|
||||
]);
|
||||
});
|
||||
|
||||
it('exposes the same comparison result used by in-place sorting', () => {
|
||||
const limited = {
|
||||
cardId: 1,
|
||||
rarity: 5,
|
||||
type: BangDreamCardType.limited,
|
||||
};
|
||||
const permanent = {
|
||||
cardId: 2,
|
||||
rarity: 5,
|
||||
type: BangDreamCardType.permanent,
|
||||
};
|
||||
|
||||
expect(compareCardIconListCards(limited, permanent)).toBeLessThan(0);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user