feat: 完善敲木鱼结果页元信息补录
This commit is contained in:
@@ -407,7 +407,7 @@ describe('miniGameDraftGenerationProgress', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('wooden fish draft generation exposes hit object, background and sound pipeline', () => {
|
||||
test('wooden fish draft generation exposes hit object, background and back button pipeline', () => {
|
||||
const state = createMiniGameDraftGenerationState('wooden-fish');
|
||||
|
||||
const progress = buildMiniGameDraftGenerationProgress(
|
||||
@@ -419,12 +419,40 @@ describe('miniGameDraftGenerationProgress', () => {
|
||||
'wooden-fish-draft',
|
||||
'wooden-fish-hit-object',
|
||||
'wooden-fish-background',
|
||||
'wooden-fish-hit-sound',
|
||||
'wooden-fish-back-button',
|
||||
'wooden-fish-write-draft',
|
||||
]);
|
||||
expect(progress?.phaseId).toBe('wooden-fish-hit-object');
|
||||
expect(progress?.phaseLabel).toBe('生成敲击物图案');
|
||||
expect(progress?.estimatedRemainingMs).toBe(272_000);
|
||||
expect(progress?.estimatedRemainingMs).toBe(530_000);
|
||||
});
|
||||
|
||||
test('wooden fish draft generation follows hit object, background, back button and writeback', () => {
|
||||
const state = createMiniGameDraftGenerationState('wooden-fish');
|
||||
|
||||
const hitObjectProgress = buildMiniGameDraftGenerationProgress(
|
||||
state,
|
||||
state.startedAtMs + 20_000,
|
||||
);
|
||||
const backgroundProgress = buildMiniGameDraftGenerationProgress(
|
||||
state,
|
||||
state.startedAtMs + 200_000,
|
||||
);
|
||||
const backButtonProgress = buildMiniGameDraftGenerationProgress(
|
||||
state,
|
||||
state.startedAtMs + 390_000,
|
||||
);
|
||||
const writeBackProgress = buildMiniGameDraftGenerationProgress(
|
||||
state,
|
||||
state.startedAtMs + 575_000,
|
||||
);
|
||||
|
||||
expect(hitObjectProgress?.phaseId).toBe('wooden-fish-hit-object');
|
||||
expect(backgroundProgress?.phaseId).toBe('wooden-fish-background');
|
||||
expect(backButtonProgress?.phaseId).toBe('wooden-fish-back-button');
|
||||
expect(writeBackProgress?.phaseId).toBe('wooden-fish-write-draft');
|
||||
expect(writeBackProgress?.estimatedRemainingMs).toBe(0);
|
||||
expect(writeBackProgress?.steps[4]?.status).toBe('completed');
|
||||
});
|
||||
|
||||
test('wooden fish generation anchors expose hit object, sound and words', () => {
|
||||
|
||||
@@ -70,7 +70,7 @@ export type MiniGameDraftGenerationPhase =
|
||||
| 'wooden-fish-draft'
|
||||
| 'wooden-fish-hit-object'
|
||||
| 'wooden-fish-background'
|
||||
| 'wooden-fish-hit-sound'
|
||||
| 'wooden-fish-back-button'
|
||||
| 'wooden-fish-write-draft'
|
||||
| 'puzzle-cover-image'
|
||||
| 'puzzle-level-scene'
|
||||
@@ -415,30 +415,36 @@ const WOODEN_FISH_STEPS = [
|
||||
{
|
||||
id: 'wooden-fish-hit-object',
|
||||
label: '生成敲击物图案',
|
||||
detail: '使用 image2 生成最终运行态敲击物图案。',
|
||||
weight: 34,
|
||||
detail: '调用 image2 生成绿幕敲击物并去绿透明化,预计约 3 分钟。',
|
||||
weight: 32,
|
||||
},
|
||||
{
|
||||
id: 'wooden-fish-background',
|
||||
label: '生成背景环境图',
|
||||
detail: '使用 image2 生成敲击背景环境图。',
|
||||
weight: 34,
|
||||
detail: '使用透明敲击物作参考生成 9:16 背景环境图,预计约 3 分钟。',
|
||||
weight: 32,
|
||||
},
|
||||
{
|
||||
id: 'wooden-fish-hit-sound',
|
||||
label: '准备敲击音效',
|
||||
detail: '写回上传、录音或默认短促敲击音效资产。',
|
||||
weight: 16,
|
||||
id: 'wooden-fish-back-button',
|
||||
label: '生成返回按钮图',
|
||||
detail: '使用敲击物和背景作参考生成主题圆形返回按钮,预计约 3 分钟。',
|
||||
weight: 20,
|
||||
},
|
||||
{
|
||||
id: 'wooden-fish-write-draft',
|
||||
label: '写入正式草稿',
|
||||
detail: '保存图案、背景、音效、飘字和封面摘要。',
|
||||
detail: '保存图案、背景、返回按钮、音效、飘字和封面摘要。',
|
||||
weight: 8,
|
||||
},
|
||||
] as const satisfies ReadonlyArray<MiniGameStepDefinition>;
|
||||
|
||||
const WOODEN_FISH_ESTIMATED_WAIT_MS = 5 * 60_000;
|
||||
const WOODEN_FISH_COMPILE_EXPECTED_MS = 8_000;
|
||||
const WOODEN_FISH_IMAGE_GENERATION_EXPECTED_MS = 180_000;
|
||||
const WOODEN_FISH_WRITE_DRAFT_EXPECTED_MS = 10_000;
|
||||
const WOODEN_FISH_ESTIMATED_WAIT_MS =
|
||||
WOODEN_FISH_COMPILE_EXPECTED_MS +
|
||||
WOODEN_FISH_IMAGE_GENERATION_EXPECTED_MS * 3 +
|
||||
WOODEN_FISH_WRITE_DRAFT_EXPECTED_MS;
|
||||
|
||||
function clampProgress(value: number) {
|
||||
return Math.max(0, Math.min(100, Math.round(value)));
|
||||
@@ -486,15 +492,16 @@ function buildMiniGameProgressSteps(
|
||||
return steps.map((step, index) => {
|
||||
// 中文注释:拼图草稿编译的 action 回包才代表可进入结果页;
|
||||
// 但预计写入时长已耗尽时,最后一步自身应呈现已完成,避免出现“进行中 100%”。
|
||||
const isPuzzleWriteStepCompleted =
|
||||
state.kind === 'puzzle' &&
|
||||
const isTimedWriteStepCompleted =
|
||||
(state.kind === 'puzzle' || state.kind === 'wooden-fish') &&
|
||||
state.phase !== 'failed' &&
|
||||
step.id === 'puzzle-select-image' &&
|
||||
(step.id === 'puzzle-select-image' ||
|
||||
step.id === 'wooden-fish-write-draft') &&
|
||||
clampProgress(activeStepProgressRatio * 100) >= 100;
|
||||
const isCompleted =
|
||||
state.phase === 'ready' ||
|
||||
index < activeStepIndex ||
|
||||
isPuzzleWriteStepCompleted;
|
||||
isTimedWriteStepCompleted;
|
||||
const isActive =
|
||||
state.phase !== 'failed' && !isCompleted && index === activeStepIndex;
|
||||
const isAssetStep = step.id === state.phase && state.totalAssetCount > 0;
|
||||
@@ -618,22 +625,64 @@ function resolveJumpHopPhaseByElapsedMs(
|
||||
return 'jump-hop-draft';
|
||||
}
|
||||
|
||||
function resolveWoodenFishPhaseByElapsedMs(
|
||||
elapsedMs: number,
|
||||
): MiniGameDraftGenerationPhase {
|
||||
if (elapsedMs >= 270_000) {
|
||||
return 'wooden-fish-write-draft';
|
||||
function buildWoodenFishPhaseTimeline(): Array<{
|
||||
phase: Extract<
|
||||
MiniGameDraftGenerationPhase,
|
||||
| 'wooden-fish-draft'
|
||||
| 'wooden-fish-hit-object'
|
||||
| 'wooden-fish-background'
|
||||
| 'wooden-fish-back-button'
|
||||
| 'wooden-fish-write-draft'
|
||||
>;
|
||||
durationMs: number;
|
||||
}> {
|
||||
return [
|
||||
{
|
||||
phase: 'wooden-fish-draft',
|
||||
durationMs: WOODEN_FISH_COMPILE_EXPECTED_MS,
|
||||
},
|
||||
{
|
||||
phase: 'wooden-fish-hit-object',
|
||||
durationMs: WOODEN_FISH_IMAGE_GENERATION_EXPECTED_MS,
|
||||
},
|
||||
{
|
||||
phase: 'wooden-fish-background',
|
||||
durationMs: WOODEN_FISH_IMAGE_GENERATION_EXPECTED_MS,
|
||||
},
|
||||
{
|
||||
phase: 'wooden-fish-back-button',
|
||||
durationMs: WOODEN_FISH_IMAGE_GENERATION_EXPECTED_MS,
|
||||
},
|
||||
{
|
||||
phase: 'wooden-fish-write-draft',
|
||||
durationMs: WOODEN_FISH_WRITE_DRAFT_EXPECTED_MS,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function resolveWoodenFishTimelineByElapsedMs(elapsedMs: number) {
|
||||
let elapsedBeforePhase = 0;
|
||||
|
||||
for (const item of buildWoodenFishPhaseTimeline()) {
|
||||
const elapsedInPhase = elapsedMs - elapsedBeforePhase;
|
||||
|
||||
if (elapsedInPhase < item.durationMs) {
|
||||
return {
|
||||
phase: item.phase,
|
||||
activeStepProgressRatio: Math.max(
|
||||
0,
|
||||
Math.min(1, elapsedInPhase / item.durationMs),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
elapsedBeforePhase += item.durationMs;
|
||||
}
|
||||
if (elapsedMs >= 240_000) {
|
||||
return 'wooden-fish-hit-sound';
|
||||
}
|
||||
if (elapsedMs >= 120_000) {
|
||||
return 'wooden-fish-background';
|
||||
}
|
||||
if (elapsedMs >= 12_000) {
|
||||
return 'wooden-fish-hit-object';
|
||||
}
|
||||
return 'wooden-fish-draft';
|
||||
|
||||
return {
|
||||
phase: 'wooden-fish-write-draft' as const,
|
||||
activeStepProgressRatio: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePuzzleTimelineByElapsedMs(
|
||||
@@ -683,12 +732,23 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
state.phase !== 'ready'
|
||||
? resolvePuzzleTimelineByElapsedMs(elapsedMs, state)
|
||||
: null;
|
||||
const woodenFishTimeline =
|
||||
state.kind === 'wooden-fish' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? resolveWoodenFishTimelineByElapsedMs(elapsedMs)
|
||||
: null;
|
||||
const normalizedState =
|
||||
puzzleTimeline != null
|
||||
? {
|
||||
...state,
|
||||
phase: puzzleTimeline.phase,
|
||||
}
|
||||
: woodenFishTimeline != null
|
||||
? {
|
||||
...state,
|
||||
phase: woodenFishTimeline.phase,
|
||||
}
|
||||
: state.kind === 'big-fish' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
@@ -724,13 +784,6 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
...state,
|
||||
phase: resolveJumpHopPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'wooden-fish' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveWoodenFishPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state;
|
||||
|
||||
const steps =
|
||||
@@ -766,7 +819,7 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? 0.5
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? 0.5
|
||||
? (woodenFishTimeline?.activeStepProgressRatio ?? 0)
|
||||
: 0;
|
||||
const overallProgress =
|
||||
normalizedState.phase === 'failed'
|
||||
@@ -779,6 +832,8 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
? overallProgress
|
||||
: normalizedState.kind === 'puzzle'
|
||||
? Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress)
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress)
|
||||
: overallProgress;
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user