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-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-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-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 没有真实图片落盘时必须失败。
|
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
||||||
|
|
||||||
### Phase 6:策略 policy 和时间/档线规则
|
### 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,
|
limitBreakRankStat,
|
||||||
} from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
} from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
||||||
import { BANGDREAM_STAT_CONFIG } from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-constants';
|
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 }> =
|
export const statConfig: Record<string, { color: string; name: string }> =
|
||||||
BANGDREAM_STAT_CONFIG;
|
BANGDREAM_STAT_CONFIG;
|
||||||
@ -34,7 +39,12 @@ export async function drawCardStatInList(card: Card) {
|
|||||||
content: [`综合力: ${statTotal} + (${limitBreakstatTotal * 4})`],
|
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);
|
list.push(statImage);
|
||||||
return stackImage(list);
|
return stackImage(list);
|
||||||
}
|
}
|
||||||
@ -54,7 +64,12 @@ export async function drawStatInList(stat: Stat) {
|
|||||||
content: [`综合力: ${statTotal}`],
|
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);
|
list.push(statImage);
|
||||||
return stackImage(list);
|
return stackImage(list);
|
||||||
}
|
}
|
||||||
@ -72,8 +87,6 @@ async function drawCardStatDivided(
|
|||||||
statTotal: number,
|
statTotal: number,
|
||||||
limitBreakstat?: Stat,
|
limitBreakstat?: Stat,
|
||||||
): Promise<Canvas> {
|
): Promise<Canvas> {
|
||||||
const widthMax = 800;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在图片布局层中绘制数值线条。
|
* 在图片布局层中绘制数值线条。
|
||||||
*
|
*
|
||||||
@ -83,27 +96,33 @@ async function drawCardStatDivided(
|
|||||||
* @returns 渲染或资源结果。
|
* @returns 渲染或资源结果。
|
||||||
*/
|
*/
|
||||||
function drawStatLine(key: string, value: number, total: number): Canvas {
|
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');
|
const ctx = canvas.getContext('2d');
|
||||||
let text = `${statConfig[key].name}: ${Math.floor(value)}`;
|
const text = createStatLineText({
|
||||||
if (limitBreakstat) {
|
label: statConfig[key].name,
|
||||||
text += ` + (${limitBreakstat[key] * 4})`;
|
limitBreakValue: limitBreakstat?.[key],
|
||||||
}
|
value,
|
||||||
|
});
|
||||||
|
const textSpec = BANGDREAM_STAT_LIST_SPEC.line.text;
|
||||||
const textImage = drawText({
|
const textImage = drawText({
|
||||||
text,
|
text,
|
||||||
maxWidth: widthMax,
|
maxWidth: textSpec.maxWidth,
|
||||||
textSize: 30,
|
textSize: textSpec.textSize,
|
||||||
lineHeight: 30,
|
lineHeight: textSpec.lineHeight,
|
||||||
});
|
});
|
||||||
|
const barLayout = getStatLineBarLayout(value, total);
|
||||||
const roundedRect = drawRoundedRect({
|
const roundedRect = drawRoundedRect({
|
||||||
width: ((widthMax * value) / total) * 2,
|
width: barLayout.width,
|
||||||
height: 30,
|
height: barLayout.height,
|
||||||
radius: 15,
|
radius: barLayout.radius,
|
||||||
color: statConfig[key].color,
|
color: statConfig[key].color,
|
||||||
strokeWidth: 0,
|
strokeWidth: barLayout.strokeWidth,
|
||||||
});
|
});
|
||||||
ctx.drawImage(textImage, 20, 0);
|
ctx.drawImage(textImage, textSpec.x, textSpec.y);
|
||||||
ctx.drawImage(roundedRect, 20, 35);
|
ctx.drawImage(roundedRect, barLayout.x, barLayout.y);
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
const list = [];
|
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