import { ArrowLeft, CheckCircle2, ImagePlus, Loader2, Play, Sparkles, Waves, } from 'lucide-react'; import { useMemo, useState } from 'react'; import type { BigFishAssetSlotResponse, BigFishGameDraftResponse, BigFishLevelBlueprintResponse, BigFishSessionSnapshotResponse, ExecuteBigFishActionRequest, } from '../../../packages/shared/src/contracts/bigFish'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; type BigFishAssetStudioTarget = | { kind: 'level_main_image'; level: BigFishLevelBlueprintResponse; } | { kind: 'level_motion'; level: BigFishLevelBlueprintResponse; motionKey: 'idle_float' | 'move_swim'; } | { kind: 'stage_background'; }; type BigFishResultViewProps = { session: BigFishSessionSnapshotResponse; isBusy?: boolean; error?: string | null; onBack: () => void; onExecuteAction: (payload: ExecuteBigFishActionRequest) => void; onStartTestRun: () => void; }; function findAssetSlot( slots: BigFishAssetSlotResponse[], assetKind: string, level?: number, motionKey?: string, ) { return slots.find((slot) => { if (slot.assetKind !== assetKind) { return false; } if (level !== undefined && slot.level !== level) { return false; } if (motionKey !== undefined && slot.motionKey !== motionKey) { return false; } return true; }); } function assetReadyLabel(slot: BigFishAssetSlotResponse | undefined) { if (slot?.status !== 'ready') { return '待生成'; } return isBigFishPlaceholderAsset(slot) ? '占位已生成' : '已生成'; } function buildLevelAssetPreview(slot: BigFishAssetSlotResponse | undefined) { if (slot?.assetUrl) { return slot.assetUrl; } return null; } function isBigFishPlaceholderAsset(slot: BigFishAssetSlotResponse | undefined) { return Boolean(slot?.assetUrl?.includes('/generated-big-fish/')); } function buildStudioAssetPreview( slots: BigFishAssetSlotResponse[], target: BigFishAssetStudioTarget, ) { if (target.kind === 'stage_background') { return buildLevelAssetPreview(findAssetSlot(slots, 'stage_background')); } if (target.kind === 'level_main_image') { return buildLevelAssetPreview( findAssetSlot(slots, 'level_main_image', target.level.level), ); } return buildLevelAssetPreview( findAssetSlot( slots, 'level_motion', target.level.level, target.motionKey, ), ); } function BigFishAssetStudioModal({ draft, target, previewUrl, isBusy, onClose, onExecuteAction, }: { draft: BigFishGameDraftResponse; target: BigFishAssetStudioTarget; previewUrl?: string | null; isBusy: boolean; onClose: () => void; onExecuteAction: (payload: ExecuteBigFishActionRequest) => void; }) { const title = target.kind === 'stage_background' ? '场地背景工坊' : target.kind === 'level_main_image' ? `Lv.${target.level.level} 主图工坊` : `Lv.${target.level.level} 动作工坊`; const prompt = target.kind === 'stage_background' ? draft.background.backgroundPromptSeed : target.kind === 'level_main_image' ? target.level.visualPromptSeed : `${target.level.motionPromptSeed} / ${target.motionKey}`; const execute = () => { if (target.kind === 'stage_background') { onExecuteAction({ action: 'big_fish_generate_stage_background' }); return; } if (target.kind === 'level_main_image') { onExecuteAction({ action: 'big_fish_generate_level_main_image', level: target.level.level, }); return; } onExecuteAction({ action: 'big_fish_generate_level_motion', level: target.level.level, motionKey: target.motionKey, }); }; return (
{title}
{target.kind === 'stage_background' ? draft.background.theme : target.level.oneLineFantasy}
PROMPT
{prompt}
{previewUrl ? ( ) : ( 'AI 资产候选预览' )}
); } function BigFishLevelCard({ level, slots, isBusy, onOpenStudio, }: { level: BigFishLevelBlueprintResponse; slots: BigFishAssetSlotResponse[]; isBusy: boolean; onOpenStudio: (target: BigFishAssetStudioTarget) => void; }) { const mainImageSlot = findAssetSlot( slots, 'level_main_image', level.level, ); const idleSlot = findAssetSlot( slots, 'level_motion', level.level, 'idle_float', ); const moveSlot = findAssetSlot( slots, 'level_motion', level.level, 'move_swim', ); const previewUrl = buildLevelAssetPreview(mainImageSlot); return (
{previewUrl ? ( ) : ( )}
LV.{level.level}
{level.name}
{level.isFinalLevel ? ( 终局 ) : null}
{level.oneLineFantasy}
猎物 {level.preyWindow.join('/') || '-'} 威胁 {level.threatWindow.join('/') || '-'} 主图 {assetReadyLabel(mainImageSlot)} 动作 {[assetReadyLabel(idleSlot), assetReadyLabel(moveSlot)].join('/')}
); } export function BigFishResultView({ session, isBusy = false, error = null, onBack, onExecuteAction, onStartTestRun, }: BigFishResultViewProps) { const [studioTarget, setStudioTarget] = useState(null); const draft = session.draft; const backgroundSlot = findAssetSlot(session.assetSlots, 'stage_background'); const backgroundPreviewUrl = buildLevelAssetPreview(backgroundSlot); const blockers = useMemo( () => session.assetCoverage.blockers.filter(Boolean), [session.assetCoverage.blockers], ); const studioPreviewUrl = useMemo(() => { if (!studioTarget) { return null; } return buildStudioAssetPreview(session.assetSlots, studioTarget); }, [session.assetSlots, studioTarget]); if (!draft) { return (
还没有可编辑的玩法草稿
); } return (
{draft.title}
{draft.subtitle}
{draft.coreFun} {draft.ecologyTheme} {draft.runtimeParams.levelCount} 级
{error ? (
{error}
) : null}
{draft.levels.map((level) => ( ))}
{studioTarget ? ( { setStudioTarget(null); }} onExecuteAction={(payload) => { onExecuteAction(payload); setStudioTarget(null); }} /> ) : null}
); } export default BigFishResultView;