417 lines
10 KiB
TypeScript
417 lines
10 KiB
TypeScript
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<string, PlatformPublicGalleryCard>();
|
|
|
|
[...rpgEntries, ...puzzleEntries].forEach((entry) => {
|
|
entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
|
|
});
|
|
|
|
return Array.from(entryMap.values()).sort(
|
|
(left, right) =>
|
|
getPlatformPublicGalleryEntryTime(right) -
|
|
getPlatformPublicGalleryEntryTime(left),
|
|
);
|
|
}
|