This commit is contained in:
2026-05-08 20:48:29 +08:00
parent abf1f1ebea
commit 94975e4735
82 changed files with 7786 additions and 1012 deletions

View File

@@ -53,23 +53,37 @@ const PUZZLE_STEPS = [
{
id: 'compile',
label: '编译首关草稿',
detail: '根据画面描述生成首关名称和结果页草稿。',
weight: 34,
detail: '理解画面描述生成首关名称与可编辑草稿。',
weight: 20,
},
{
id: 'puzzle-images',
label: '生成首关画面',
detail: '按画面描述和参考图生成第一张拼图图。',
weight: 33,
detail: '调用图片模型生成适合切块的正方形首图。',
weight: 70,
},
{
id: 'puzzle-select-image',
label: '写入正式草稿',
detail: '把首图设为正式图并同步到结果页。',
weight: 33,
detail: '确认首图并同步关卡数据,准备进入结果页。',
weight: 10,
},
] as const satisfies ReadonlyArray<MiniGameStepDefinition>;
const PUZZLE_ESTIMATED_WAIT_MS = 60_000;
const PUZZLE_NON_READY_MAX_PROGRESS = 98;
const PUZZLE_PHASE_TIMELINE: Array<{
phase: Extract<
MiniGameDraftGenerationPhase,
'compile' | 'puzzle-images' | 'puzzle-select-image'
>;
durationMs: number;
}> = [
{ phase: 'compile', durationMs: 12_000 },
{ phase: 'puzzle-images', durationMs: 42_000 },
{ phase: 'puzzle-select-image', durationMs: 6_000 },
];
const BIG_FISH_STEPS = [
{
id: 'big-fish-draft',
@@ -141,6 +155,7 @@ function buildMiniGameProgressSteps(
steps: ReadonlyArray<MiniGameStepDefinition>,
activeStepIndex: number,
state: MiniGameDraftGenerationState,
activeStepProgressRatio: number,
) {
return steps.map((step, index) => {
const isCompleted = state.phase === 'ready' || index < activeStepIndex;
@@ -152,7 +167,13 @@ function buildMiniGameProgressSteps(
id: step.id,
label: step.label,
detail: step.detail,
completed: isCompleted ? 1 : isAssetStep ? state.completedAssetCount : 0,
completed: isCompleted
? 1
: isAssetStep
? state.completedAssetCount
: isActive
? activeStepProgressRatio
: 0,
total: isAssetStep ? state.totalAssetCount : 1,
status: isCompleted ? 'completed' : isActive ? 'active' : 'pending',
} satisfies CustomWorldGenerationStep;
@@ -201,6 +222,31 @@ function resolveSquareHolePhaseByElapsedMs(
return 'square-hole-draft';
}
function resolvePuzzleTimelineByElapsedMs(elapsedMs: number) {
let elapsedBeforePhase = 0;
for (const item of PUZZLE_PHASE_TIMELINE) {
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;
}
return {
phase: 'puzzle-select-image' as const,
activeStepProgressRatio: 1,
};
}
export function buildMiniGameDraftGenerationProgress(
state: MiniGameDraftGenerationState | null,
nowMs = Date.now(),
@@ -210,8 +256,19 @@ export function buildMiniGameDraftGenerationProgress(
}
const elapsedMs = Math.max(0, nowMs - state.startedAtMs);
const puzzleTimeline =
state.kind === 'puzzle' &&
state.phase !== 'failed' &&
state.phase !== 'ready'
? resolvePuzzleTimelineByElapsedMs(elapsedMs)
: null;
const normalizedState =
state.kind === 'big-fish' &&
puzzleTimeline != null
? {
...state,
phase: puzzleTimeline.phase,
}
: state.kind === 'big-fish' &&
state.phase !== 'failed' &&
state.phase !== 'ready'
? {
@@ -244,6 +301,8 @@ export function buildMiniGameDraftGenerationProgress(
)
: normalizedState.phase === 'ready'
? 1
: normalizedState.kind === 'puzzle'
? (puzzleTimeline?.activeStepProgressRatio ?? 0)
: normalizedState.kind === 'big-fish'
? 0.55
: normalizedState.kind === 'square-hole'
@@ -255,6 +314,12 @@ export function buildMiniGameDraftGenerationProgress(
: normalizedState.phase === 'ready'
? 100
: completedWeight + activeStep.weight * assetRatio;
const cappedOverallProgress =
normalizedState.phase === 'ready' || normalizedState.phase === 'failed'
? overallProgress
: normalizedState.kind === 'puzzle'
? Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress)
: overallProgress;
return {
phaseId: normalizedState.phase,
@@ -272,20 +337,27 @@ export function buildMiniGameDraftGenerationProgress(
: '首关草稿与正式图已准备完成,可进入结果页补作品信息。'
: activeStep.detail),
batchLabel: activeStep.label,
overallProgress: clampProgress(overallProgress),
completedWeight: clampProgress(overallProgress),
overallProgress: clampProgress(cappedOverallProgress),
completedWeight: clampProgress(cappedOverallProgress),
totalWeight: 100,
elapsedMs,
estimatedRemainingMs:
normalizedState.phase === 'ready'
? 0
: normalizedState.kind === 'puzzle'
? Math.max(0, PUZZLE_ESTIMATED_WAIT_MS - elapsedMs)
: normalizedState.kind === 'big-fish'
? Math.max(0, 7_000 - elapsedMs)
: normalizedState.kind === 'square-hole'
? Math.max(0, 12_000 - elapsedMs)
: null,
activeStepIndex,
steps: buildMiniGameProgressSteps(steps, activeStepIndex, normalizedState),
steps: buildMiniGameProgressSteps(
steps,
activeStepIndex,
normalizedState,
assetRatio,
),
};
}