import type { Match3DGeneratedItemAsset } from '../../../packages/shared/src/contracts/match3dWorks'; import type { CreatePuzzleAgentSessionRequest, PuzzleAgentSessionSnapshot, } from '../../../packages/shared/src/contracts/puzzleAgentSession'; import { createMiniGameDraftGenerationState, type MiniGameDraftGenerationKind, type MiniGameDraftGenerationPhase, type MiniGameDraftGenerationState, resolveMiniGameDraftGenerationStartedAtMs, } from '../../services/miniGameDraftGenerationProgress'; import type { SelectionStage } from './platformEntryTypes'; export type MiniGameGenerationProgressTickStateMap = Partial< Record >; export function resolveMiniGameGenerationProgressTickState( selectionStage: SelectionStage, states: MiniGameGenerationProgressTickStateMap, ) { const stageKindMap: Partial< Record > = { 'puzzle-generating': 'puzzle', 'big-fish-generating': 'big-fish', 'square-hole-generating': 'square-hole', 'match3d-generating': 'match3d', 'baby-object-match-generating': 'baby-object-match', 'jump-hop-generating': 'jump-hop', 'puzzle-clear-generating': 'puzzle-clear', 'wooden-fish-generating': 'wooden-fish', }; const kind = stageKindMap[selectionStage]; return kind ? (states[kind] ?? null) : null; } export function resolveMiniGameGenerationViewBusy( isBusy: boolean, state: MiniGameDraftGenerationState | null | undefined, ) { return isBusy || isMiniGameDraftGenerating(state ?? null); } export function createMiniGameDraftGenerationStateForRestoredDraft( kind: MiniGameDraftGenerationKind, metadata?: MiniGameDraftGenerationState['metadata'], startedAtMs = Date.now(), ): MiniGameDraftGenerationState { return { ...createMiniGameDraftGenerationState(kind, startedAtMs), ...(metadata ? { metadata } : {}), }; } export function createFailedMiniGameDraftGenerationStateForRestoredDraft( kind: MiniGameDraftGenerationKind, updatedAt: string | null | undefined, error: string, metadata?: MiniGameDraftGenerationState['metadata'], ): MiniGameDraftGenerationState { return resolveFinishedMiniGameDraftGenerationState( createMiniGameDraftGenerationStateForRestoredDraft( kind, metadata, resolveMiniGameDraftGenerationStartedAtMs(updatedAt), ), 'failed', { error }, ); } /** 清理生成态完成时间,避免返回生成页后继续沿用结束态计时。 */ export function rebaseMiniGameDraftGenerationStateForDisplay( state: MiniGameDraftGenerationState, ): MiniGameDraftGenerationState { return { ...state, finishedAtMs: undefined, }; } export function rebaseMiniGameDraftBackgroundCompileTaskForDisplay< T extends { generationState: MiniGameDraftGenerationState }, >(task: T): T { return { ...task, generationState: rebaseMiniGameDraftGenerationStateForDisplay( task.generationState, ), }; } export function createPuzzleDraftGenerationStateFromPayload( payload: CreatePuzzleAgentSessionRequest | null | undefined, session: PuzzleAgentSessionSnapshot | null | undefined = null, ): MiniGameDraftGenerationState { const puzzleProgressPercent = session?.draft && !session.draft.formDraft ? session.progressPercent : undefined; return { ...createMiniGameDraftGenerationState( 'puzzle', resolveMiniGameDraftGenerationStartedAtMs(session?.updatedAt), ), metadata: { puzzleAiRedraw: payload?.aiRedraw ?? true, puzzleActivePhaseId: typeof puzzleProgressPercent === 'number' ? 'compile' : undefined, puzzleActiveStepStartedAtMs: typeof puzzleProgressPercent === 'number' ? Date.now() : undefined, puzzleProgressPercent, }, }; } export function resolvePuzzlePhaseFromSessionProgress( state: MiniGameDraftGenerationState, session: PuzzleAgentSessionSnapshot, ): MiniGameDraftGenerationPhase { if (session.progressPercent >= 96) { return 'puzzle-select-image'; } if (session.progressPercent >= 94) { return 'puzzle-ui-assets'; } if (session.progressPercent >= 88) { return state.metadata?.puzzleAiRedraw === false ? 'puzzle-level-scene' : 'puzzle-cover-image'; } return 'compile'; } export function mergePuzzleSessionProgressIntoGenerationState( state: MiniGameDraftGenerationState, session: PuzzleAgentSessionSnapshot, ): MiniGameDraftGenerationState { const isCompiledGenerationSession = Boolean( session.draft && !session.draft.formDraft, ); const nextPhaseId = isCompiledGenerationSession ? resolvePuzzlePhaseFromSessionProgress(state, session) : state.metadata?.puzzleActivePhaseId; const shouldResetActiveStepStart = isCompiledGenerationSession && nextPhaseId != null && nextPhaseId !== state.metadata?.puzzleActivePhaseId; return { ...state, metadata: { ...state.metadata, puzzleActivePhaseId: nextPhaseId, puzzleActiveStepStartedAtMs: shouldResetActiveStepStart ? resolveMiniGameDraftGenerationStartedAtMs(session.updatedAt) : state.metadata?.puzzleActiveStepStartedAtMs, puzzleProgressPercent: isCompiledGenerationSession ? session.progressPercent : state.metadata?.puzzleProgressPercent, }, }; } export function mergeMatch3DGeneratedAssetsIntoGenerationState( current: MiniGameDraftGenerationState | null, assets: readonly Match3DGeneratedItemAsset[] | null | undefined, ): MiniGameDraftGenerationState | null { if (!current || current.phase === 'ready' || current.phase === 'failed') { return current; } const assetList = assets ?? []; const imageReadyCount = assetList.filter( (asset) => asset.imageViews?.some( (view) => view.imageObjectKey?.trim() || view.imageSrc?.trim(), ) || asset.imageObjectKey?.trim() || asset.imageSrc?.trim(), ).length; const totalAssetCount = Math.max(5, assetList.length); const failedAsset = assetList.find((asset) => asset.error?.trim()); return { ...current, phase: imageReadyCount > 0 ? 'match3d-generate-views' : current.phase, completedAssetCount: imageReadyCount, totalAssetCount, error: failedAsset?.error?.trim() || current.error, }; } export function resolveFinishedMiniGameDraftGenerationState( state: MiniGameDraftGenerationState, phase: 'ready' | 'failed', options: { error?: string | null; completedAssetCount?: number; totalAssetCount?: number; } = {}, ): MiniGameDraftGenerationState { return { ...state, phase, finishedAtMs: Date.now(), error: options.error ?? state.error, completedAssetCount: options.completedAssetCount ?? state.completedAssetCount, totalAssetCount: options.totalAssetCount ?? state.totalAssetCount, }; } export function isMiniGameDraftReady( state: MiniGameDraftGenerationState | null, ) { return state?.phase === 'ready'; } export function isMiniGameDraftGenerating( state: MiniGameDraftGenerationState | null, ) { return Boolean(state && state.phase !== 'ready' && state.phase !== 'failed'); }