refactor: 收口 BangDream 综合力列表规格
This commit is contained in:
parent
6bda3f306f
commit
8bc529b50f
@ -394,6 +394,7 @@ export interface TsuguHook {
|
||||
- 已新增 `render-blocks/list-character-detail-spec.ts`,收口玩家详情角色等级列表的头像缩放、正文宽度/行高、项画布和通用列表框架;`list-character-detail.ts` 改为消费 spec,玩家详情角色等级绘制顺序、角色数据读取和输出结构保持不变;`list-character-detail-spec.spec.ts` 覆盖头像/正文参数、项布局和列表框架参数,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-card-sd-character-spec.ts`,收口卡牌详情演出缩略图的 SD 角色 sprite 裁切网格、列表缩放行高、字号和间距;`list-card-sd-character.ts` 改为消费 spec,素材加载、四帧裁切和列表绘制顺序保持不变;`list-card-sd-character-spec.spec.ts` 覆盖四帧裁切坐标、单帧裁切计算和列表规格,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-card-prefix-spec.ts`,收口卡牌详情顶部标题块的画布、背景、乐队 Logo 等比缩放、卡牌标题和角色名文字布局;`list-card-prefix.ts` 改为消费 spec,乐队/角色/服务器优先级读取和绘制顺序保持不变;`list-card-prefix-spec.spec.ts` 覆盖标题块尺寸、背景、文字坐标和 Logo 缩放,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-stat-spec.ts`,收口卡牌/玩家综合力列表的间隔、数值行画布、文字规格、进度条布局和条宽算法;`list-stat.ts` 改为消费 spec,综合力计算、突破加成和绘制顺序保持不变;`list-stat-spec.spec.ts` 覆盖间隔/画布/文字/进度条规格、文本生成和条宽布局,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
||||
|
||||
### Phase 6:策略 policy 和时间/档线规则
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
export interface StatLineTextParams {
|
||||
label: string;
|
||||
limitBreakValue?: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export const BANGDREAM_STAT_LIST_SPEC = {
|
||||
line: {
|
||||
bar: {
|
||||
height: 30,
|
||||
radius: 15,
|
||||
strokeWidth: 0,
|
||||
widthScale: 2,
|
||||
x: 20,
|
||||
y: 35,
|
||||
},
|
||||
canvas: {
|
||||
height: 70,
|
||||
width: 800,
|
||||
},
|
||||
text: {
|
||||
lineHeight: 30,
|
||||
maxWidth: 800,
|
||||
textSize: 30,
|
||||
x: 20,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
spacer: {
|
||||
height: 5,
|
||||
width: 1,
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 创建综合力数值行展示文本。
|
||||
*
|
||||
* @param params - 数值行文本参数。
|
||||
*/
|
||||
export function createStatLineText(params: StatLineTextParams): string {
|
||||
const baseText = `${params.label}: ${Math.floor(params.value)}`;
|
||||
|
||||
if (params.limitBreakValue == null) {
|
||||
return baseText;
|
||||
}
|
||||
|
||||
return `${baseText} + (${params.limitBreakValue * 4})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算综合力数值条宽度。
|
||||
*
|
||||
* @param value - 当前数值。
|
||||
* @param total - 综合力总数值。
|
||||
*/
|
||||
export function getStatLineBarWidth(value: number, total: number): number {
|
||||
return (
|
||||
((BANGDREAM_STAT_LIST_SPEC.line.canvas.width * value) / total) *
|
||||
BANGDREAM_STAT_LIST_SPEC.line.bar.widthScale
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成综合力数值条绘制布局。
|
||||
*
|
||||
* @param value - 当前数值。
|
||||
* @param total - 综合力总数值。
|
||||
*/
|
||||
export function getStatLineBarLayout(value: number, total: number) {
|
||||
const barSpec = BANGDREAM_STAT_LIST_SPEC.line.bar;
|
||||
|
||||
return {
|
||||
height: barSpec.height,
|
||||
radius: barSpec.radius,
|
||||
strokeWidth: barSpec.strokeWidth,
|
||||
width: getStatLineBarWidth(value, total),
|
||||
x: barSpec.x,
|
||||
y: barSpec.y,
|
||||
};
|
||||
}
|
||||
@ -9,6 +9,11 @@ import {
|
||||
limitBreakRankStat,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
||||
import { BANGDREAM_STAT_CONFIG } from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-constants';
|
||||
import {
|
||||
BANGDREAM_STAT_LIST_SPEC,
|
||||
createStatLineText,
|
||||
getStatLineBarLayout,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-stat-spec';
|
||||
|
||||
export const statConfig: Record<string, { color: string; name: string }> =
|
||||
BANGDREAM_STAT_CONFIG;
|
||||
@ -34,7 +39,12 @@ export async function drawCardStatInList(card: Card) {
|
||||
content: [`综合力: ${statTotal} + (${limitBreakstatTotal * 4})`],
|
||||
}),
|
||||
);
|
||||
list.push(new Canvas(1, 5));
|
||||
list.push(
|
||||
new Canvas(
|
||||
BANGDREAM_STAT_LIST_SPEC.spacer.width,
|
||||
BANGDREAM_STAT_LIST_SPEC.spacer.height,
|
||||
),
|
||||
);
|
||||
list.push(statImage);
|
||||
return stackImage(list);
|
||||
}
|
||||
@ -54,7 +64,12 @@ export async function drawStatInList(stat: Stat) {
|
||||
content: [`综合力: ${statTotal}`],
|
||||
}),
|
||||
);
|
||||
list.push(new Canvas(1, 5));
|
||||
list.push(
|
||||
new Canvas(
|
||||
BANGDREAM_STAT_LIST_SPEC.spacer.width,
|
||||
BANGDREAM_STAT_LIST_SPEC.spacer.height,
|
||||
),
|
||||
);
|
||||
list.push(statImage);
|
||||
return stackImage(list);
|
||||
}
|
||||
@ -72,8 +87,6 @@ async function drawCardStatDivided(
|
||||
statTotal: number,
|
||||
limitBreakstat?: Stat,
|
||||
): Promise<Canvas> {
|
||||
const widthMax = 800;
|
||||
|
||||
/**
|
||||
* 在图片布局层中绘制数值线条。
|
||||
*
|
||||
@ -83,27 +96,33 @@ async function drawCardStatDivided(
|
||||
* @returns 渲染或资源结果。
|
||||
*/
|
||||
function drawStatLine(key: string, value: number, total: number): Canvas {
|
||||
const canvas = new Canvas(800, 70);
|
||||
const canvas = new Canvas(
|
||||
BANGDREAM_STAT_LIST_SPEC.line.canvas.width,
|
||||
BANGDREAM_STAT_LIST_SPEC.line.canvas.height,
|
||||
);
|
||||
const ctx = canvas.getContext('2d');
|
||||
let text = `${statConfig[key].name}: ${Math.floor(value)}`;
|
||||
if (limitBreakstat) {
|
||||
text += ` + (${limitBreakstat[key] * 4})`;
|
||||
}
|
||||
const text = createStatLineText({
|
||||
label: statConfig[key].name,
|
||||
limitBreakValue: limitBreakstat?.[key],
|
||||
value,
|
||||
});
|
||||
const textSpec = BANGDREAM_STAT_LIST_SPEC.line.text;
|
||||
const textImage = drawText({
|
||||
text,
|
||||
maxWidth: widthMax,
|
||||
textSize: 30,
|
||||
lineHeight: 30,
|
||||
maxWidth: textSpec.maxWidth,
|
||||
textSize: textSpec.textSize,
|
||||
lineHeight: textSpec.lineHeight,
|
||||
});
|
||||
const barLayout = getStatLineBarLayout(value, total);
|
||||
const roundedRect = drawRoundedRect({
|
||||
width: ((widthMax * value) / total) * 2,
|
||||
height: 30,
|
||||
radius: 15,
|
||||
width: barLayout.width,
|
||||
height: barLayout.height,
|
||||
radius: barLayout.radius,
|
||||
color: statConfig[key].color,
|
||||
strokeWidth: 0,
|
||||
strokeWidth: barLayout.strokeWidth,
|
||||
});
|
||||
ctx.drawImage(textImage, 20, 0);
|
||||
ctx.drawImage(roundedRect, 20, 35);
|
||||
ctx.drawImage(textImage, textSpec.x, textSpec.y);
|
||||
ctx.drawImage(roundedRect, barLayout.x, barLayout.y);
|
||||
return canvas;
|
||||
}
|
||||
const list = [];
|
||||
|
||||
65
test/qqbot/plugins/bangDream/tsugu/list-stat-spec.spec.ts
Normal file
65
test/qqbot/plugins/bangDream/tsugu/list-stat-spec.spec.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
BANGDREAM_STAT_LIST_SPEC,
|
||||
createStatLineText,
|
||||
getStatLineBarLayout,
|
||||
getStatLineBarWidth,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/list-stat-spec';
|
||||
|
||||
describe('BangDream stat list spec', () => {
|
||||
it('keeps the historical stat list spacer stable', () => {
|
||||
expect(BANGDREAM_STAT_LIST_SPEC.spacer).toEqual({
|
||||
height: 5,
|
||||
width: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the historical stat line canvas, text and bar layout stable', () => {
|
||||
expect(BANGDREAM_STAT_LIST_SPEC.line.canvas).toEqual({
|
||||
height: 70,
|
||||
width: 800,
|
||||
});
|
||||
expect(BANGDREAM_STAT_LIST_SPEC.line.text).toEqual({
|
||||
lineHeight: 30,
|
||||
maxWidth: 800,
|
||||
textSize: 30,
|
||||
x: 20,
|
||||
y: 0,
|
||||
});
|
||||
expect(BANGDREAM_STAT_LIST_SPEC.line.bar).toEqual({
|
||||
height: 30,
|
||||
radius: 15,
|
||||
strokeWidth: 0,
|
||||
widthScale: 2,
|
||||
x: 20,
|
||||
y: 35,
|
||||
});
|
||||
});
|
||||
|
||||
it('formats stat line text with optional limit break bonus', () => {
|
||||
expect(
|
||||
createStatLineText({
|
||||
label: '演出',
|
||||
value: 10654.9,
|
||||
}),
|
||||
).toBe('演出: 10654');
|
||||
expect(
|
||||
createStatLineText({
|
||||
label: '演出',
|
||||
limitBreakValue: 800,
|
||||
value: 10654.9,
|
||||
}),
|
||||
).toBe('演出: 10654 + (3200)');
|
||||
});
|
||||
|
||||
it('computes the doubled historical stat bar width and layout', () => {
|
||||
expect(getStatLineBarWidth(100, 400)).toBe(400);
|
||||
expect(getStatLineBarLayout(100, 400)).toEqual({
|
||||
height: 30,
|
||||
radius: 15,
|
||||
strokeWidth: 0,
|
||||
width: 400,
|
||||
x: 20,
|
||||
y: 35,
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user