import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle'; import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime'; import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks'; import { isBarkBattleGalleryEntry, isBigFishGalleryEntry, isEdutainmentGalleryEntry, isJumpHopGalleryEntry, isMatch3DGalleryEntry, isPuzzleGalleryEntry, isSquareHoleGalleryEntry, isVisualNovelGalleryEntry, isWoodenFishGalleryEntry, type PlatformPublicGalleryCard, } from '../rpg-entry/rpgEntryWorldPresentation'; import { mapBarkBattlePublicDetailToWorkSummary, mapPublicWorkDetailToBigFishWork, mapPublicWorkDetailToPuzzleWork, mapPublicWorkDetailToSquareHoleWork, } from './platformPublicWorkDetailFlow'; export type RecommendRuntimeKind = | 'bark-battle' | 'big-fish' | 'edutainment' | 'jump-hop' | 'match3d' | 'puzzle' | 'square-hole' | 'wooden-fish' | 'visual-novel' | 'rpg'; export type PlatformRecommendRuntimeStartErrorTarget = | 'bark-battle' | 'big-fish' | 'match3d' | 'puzzle' | 'square-hole'; export type PlatformRecommendRuntimeStartIntent = | { type: 'blocked'; errorTarget: PlatformRecommendRuntimeStartErrorTarget; errorMessage: string; } | { type: 'start-big-fish'; work: BigFishWorkSummary; returnStage: 'platform'; embedded: true; } | { type: 'start-puzzle'; work: PuzzleWorkSummary; returnStage: 'platform'; embedded: true; } | { type: 'start-jump-hop'; profileId: string; returnStage: 'platform'; embedded: true; } | { type: 'start-wooden-fish'; profileId: string; returnStage: 'platform'; embedded: true; } | { type: 'start-match3d'; work: Match3DWorkSummary; returnStage: 'work-detail'; embedded: true; } | { type: 'start-square-hole'; work: SquareHoleWorkSummary; returnStage: 'platform'; embedded: true; } | { type: 'start-visual-novel'; profileId: string; returnStage: 'platform'; embedded: true; } | { type: 'start-bark-battle'; work: BarkBattleWorkSummary; returnStage: 'platform'; embedded: true; } | { type: 'start-edutainment'; entry: PlatformPublicGalleryCard; returnStage: 'platform'; embedded: true; } | { type: 'mark-ready'; }; export type PlatformRecommendRuntimeStartIntentDeps = { selectedPuzzleDetail?: PuzzleWorkSummary | null; barkBattleGalleryEntries?: readonly BarkBattleWorkSummary[]; mapMatch3DWork: ( entry: PlatformPublicGalleryCard, ) => Match3DWorkSummary | null; }; export type PlatformRecommendRuntimeReadyState = { activeKind: RecommendRuntimeKind | null; hasBabyObjectMatchDraft?: boolean; hasBigFishRun?: boolean; hasJumpHopRun?: boolean; hasMatch3DRun?: boolean; hasSquareHoleRun?: boolean; hasVisualNovelRun?: boolean; hasWoodenFishRun?: boolean; puzzleRunEntryProfileId?: string | null; puzzleRunCurrentLevelProfileId?: string | null; }; export function getPlatformPublicGalleryEntryTime( entry: PlatformPublicGalleryCard, ) { const rawTime = entry.publishedAt ?? entry.updatedAt; const timestamp = new Date(rawTime).getTime(); return Number.isNaN(timestamp) ? 0 : timestamp; } export function getPlatformPublicGalleryEntryKey( entry: PlatformPublicGalleryCard, ) { // 同一作品身份由玩法、作者与 profile 共同确定,避免不同玩法共享 profileId 时误合并。 const kind = isBigFishGalleryEntry(entry) ? 'big-fish' : isPuzzleGalleryEntry(entry) ? 'puzzle' : isJumpHopGalleryEntry(entry) ? 'jump-hop' : isWoodenFishGalleryEntry(entry) ? 'wooden-fish' : isMatch3DGalleryEntry(entry) ? 'match3d' : isSquareHoleGalleryEntry(entry) ? 'square-hole' : isVisualNovelGalleryEntry(entry) ? 'visual-novel' : isBarkBattleGalleryEntry(entry) ? 'bark-battle' : isEdutainmentGalleryEntry(entry) ? `edutainment:${entry.templateId}` : 'rpg'; return `${kind}:${entry.ownerUserId}:${entry.profileId}`; } export function getPlatformRecommendRuntimeKind( entry: PlatformPublicGalleryCard, ): RecommendRuntimeKind { if (isBigFishGalleryEntry(entry)) { return 'big-fish'; } if (isPuzzleGalleryEntry(entry)) { return 'puzzle'; } if (isJumpHopGalleryEntry(entry)) { return 'jump-hop'; } if (isWoodenFishGalleryEntry(entry)) { return 'wooden-fish'; } if (isMatch3DGalleryEntry(entry)) { return 'match3d'; } if (isSquareHoleGalleryEntry(entry)) { return 'square-hole'; } if (isVisualNovelGalleryEntry(entry)) { return 'visual-novel'; } if (isBarkBattleGalleryEntry(entry)) { return 'bark-battle'; } if (isEdutainmentGalleryEntry(entry)) { return 'edutainment'; } return 'rpg'; } export function resolvePlatformRecommendRuntimeStartIntent( entry: PlatformPublicGalleryCard, deps: PlatformRecommendRuntimeStartIntentDeps, ): PlatformRecommendRuntimeStartIntent { if (isBigFishGalleryEntry(entry)) { const work = mapPublicWorkDetailToBigFishWork(entry); if (!work) { return { type: 'blocked', errorTarget: 'big-fish', errorMessage: '当前作品缺少会话信息,暂时无法进入玩法。', }; } return { type: 'start-big-fish', work, returnStage: 'platform', embedded: true, }; } if (isPuzzleGalleryEntry(entry)) { const work = deps.selectedPuzzleDetail?.profileId === entry.profileId ? deps.selectedPuzzleDetail : mapPublicWorkDetailToPuzzleWork(entry); if (!work) { return { type: 'blocked', errorTarget: 'puzzle', errorMessage: '当前拼图作品信息不完整,暂时无法进入玩法。', }; } return { type: 'start-puzzle', work, returnStage: 'platform', embedded: true, }; } if (isJumpHopGalleryEntry(entry)) { return { type: 'start-jump-hop', profileId: entry.profileId, returnStage: 'platform', embedded: true, }; } if (isWoodenFishGalleryEntry(entry)) { return { type: 'start-wooden-fish', profileId: entry.profileId, returnStage: 'platform', embedded: true, }; } if (isMatch3DGalleryEntry(entry)) { // 中文注释:抓大鹅推荐 runtime 仍接 Match3D Module 的 Adapter,避免复制素材归一规则。 const work = deps.mapMatch3DWork(entry); if (!work) { return { type: 'blocked', errorTarget: 'match3d', errorMessage: '当前抓大鹅作品信息不完整,暂时无法进入玩法。', }; } return { type: 'start-match3d', work, returnStage: 'work-detail', embedded: true, }; } if (isSquareHoleGalleryEntry(entry)) { const work = mapPublicWorkDetailToSquareHoleWork(entry); if (!work) { return { type: 'blocked', errorTarget: 'square-hole', errorMessage: '当前方洞挑战作品信息不完整,暂时无法进入玩法。', }; } return { type: 'start-square-hole', work, returnStage: 'platform', embedded: true, }; } if (isVisualNovelGalleryEntry(entry)) { return { type: 'start-visual-novel', profileId: entry.profileId, returnStage: 'platform', embedded: true, }; } if (isBarkBattleGalleryEntry(entry)) { const work = deps.barkBattleGalleryEntries?.find( (item) => item.workId === entry.workId, ) ?? mapBarkBattlePublicDetailToWorkSummary(entry); if (!work) { return { type: 'blocked', errorTarget: 'bark-battle', errorMessage: '当前汪汪声浪作品信息不完整,暂时无法进入玩法。', }; } return { type: 'start-bark-battle', work, returnStage: 'platform', embedded: true, }; } if (isEdutainmentGalleryEntry(entry)) { return { type: 'start-edutainment', entry, returnStage: 'platform', embedded: true, }; } return { type: 'mark-ready', }; } export function isPlatformRecommendRuntimeReadyForEntry( entry: PlatformPublicGalleryCard, state: PlatformRecommendRuntimeReadyState, ) { const expectedKind = getPlatformRecommendRuntimeKind(entry); if (state.activeKind !== expectedKind) { return false; } if (expectedKind === 'big-fish') { return Boolean(state.hasBigFishRun); } if (expectedKind === 'jump-hop') { return Boolean(state.hasJumpHopRun); } if (expectedKind === 'wooden-fish') { return Boolean(state.hasWoodenFishRun); } if (expectedKind === 'match3d') { return Boolean(state.hasMatch3DRun); } if (expectedKind === 'puzzle') { return ( state.puzzleRunEntryProfileId === entry.profileId || state.puzzleRunCurrentLevelProfileId === entry.profileId ); } if (expectedKind === 'square-hole') { return Boolean(state.hasSquareHoleRun); } if (expectedKind === 'visual-novel') { return Boolean(state.hasVisualNovelRun); } if (expectedKind === 'bark-battle') { return true; } if (expectedKind === 'edutainment') { return Boolean(state.hasBabyObjectMatchDraft); } return true; } export function isSamePlatformPublicGalleryEntry( left: PlatformPublicGalleryCard, right: PlatformPublicGalleryCard, ) { return ( getPlatformPublicGalleryEntryKey(left) === getPlatformPublicGalleryEntryKey(right) ); } export function mergePlatformPublicGalleryEntries( rpgEntries: CustomWorldGalleryCard[], puzzleEntries: PlatformPublicGalleryCard[], ) { const entryMap = new Map(); [...rpgEntries, ...puzzleEntries].forEach((entry) => { entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry); }); return Array.from(entryMap.values()).sort( (left, right) => getPlatformPublicGalleryEntryTime(right) - getPlatformPublicGalleryEntryTime(left), ); }