111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import { useMemo, useState } from 'react';
|
|
|
|
import type {
|
|
DragPuzzlePieceRequest,
|
|
SwapPuzzlePiecesRequest,
|
|
} from '../packages/shared/src/contracts/puzzleRuntimeSession';
|
|
import type { PuzzleWorkSummary } from '../packages/shared/src/contracts/puzzleWorkSummary';
|
|
import { PuzzleRuntimeShell } from './components/puzzle-runtime/PuzzleRuntimeShell';
|
|
import {
|
|
applyLocalPuzzleFreezeTime,
|
|
advanceLocalPuzzleLevel,
|
|
dragLocalPuzzlePiece,
|
|
setLocalPuzzlePaused,
|
|
startLocalPuzzleRun,
|
|
swapLocalPuzzlePieces,
|
|
} from './services/puzzle-runtime/puzzleLocalRuntime';
|
|
|
|
const PLACEHOLDER_PUZZLE_IMAGE =
|
|
'data:image/svg+xml;utf8,' +
|
|
encodeURIComponent(`
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
|
<defs>
|
|
<linearGradient id="sky" x1="0" y1="0" x2="1" y2="1">
|
|
<stop offset="0" stop-color="#fef3c7" />
|
|
<stop offset="0.45" stop-color="#fb7185" />
|
|
<stop offset="1" stop-color="#312e81" />
|
|
</linearGradient>
|
|
<radialGradient id="glow" cx="42%" cy="34%" r="46%">
|
|
<stop offset="0" stop-color="#ffffff" stop-opacity="0.78" />
|
|
<stop offset="1" stop-color="#ffffff" stop-opacity="0" />
|
|
</radialGradient>
|
|
</defs>
|
|
<rect width="1024" height="1024" fill="url(#sky)" />
|
|
<circle cx="378" cy="286" r="230" fill="url(#glow)" />
|
|
<path d="M0 690 C168 626 296 724 446 666 C596 606 744 628 1024 536 V1024 H0 Z" fill="#1e1b4b" opacity="0.9" />
|
|
<path d="M0 822 C190 760 328 850 516 790 C672 738 824 754 1024 704 V1024 H0 Z" fill="#111827" opacity="0.78" />
|
|
<path d="M138 328 C226 266 340 266 420 338 C518 426 632 390 740 326 C834 272 920 300 960 362" fill="none" stroke="#fff7ed" stroke-width="18" stroke-linecap="round" opacity="0.72" />
|
|
<path d="M190 548 h640" stroke="#ffffff" stroke-width="16" stroke-linecap="round" opacity="0.3" />
|
|
<path d="M268 628 h488" stroke="#ffffff" stroke-width="12" stroke-linecap="round" opacity="0.22" />
|
|
</svg>`);
|
|
|
|
function buildPlaceholderPuzzleWork(): PuzzleWorkSummary {
|
|
return {
|
|
workId: 'placeholder-puzzle-work',
|
|
profileId: 'placeholder-puzzle-profile',
|
|
ownerUserId: 'placeholder-user',
|
|
sourceSessionId: null,
|
|
authorDisplayName: '占位作者',
|
|
levelName: '暮色群山',
|
|
summary: '用于直达玩法调试的本地占位拼图。',
|
|
themeTags: ['占位', '风景', '调试'],
|
|
coverImageSrc: PLACEHOLDER_PUZZLE_IMAGE,
|
|
coverAssetId: null,
|
|
publicationStatus: 'published',
|
|
updatedAt: new Date(0).toISOString(),
|
|
publishedAt: new Date(0).toISOString(),
|
|
playCount: 0,
|
|
likeCount: 0,
|
|
publishReady: true,
|
|
};
|
|
}
|
|
|
|
export default function PuzzlePlaygroundApp() {
|
|
const placeholderWork = useMemo(() => buildPlaceholderPuzzleWork(), []);
|
|
const [run, setRun] = useState(() => startLocalPuzzleRun(placeholderWork));
|
|
|
|
const handleSwapPieces = (payload: SwapPuzzlePiecesRequest) => {
|
|
setRun((currentRun) => swapLocalPuzzlePieces(currentRun, payload));
|
|
};
|
|
|
|
const handleDragPiece = (payload: DragPuzzlePieceRequest) => {
|
|
setRun((currentRun) => dragLocalPuzzlePiece(currentRun, payload));
|
|
};
|
|
|
|
const handleRestart = () => {
|
|
setRun(startLocalPuzzleRun(placeholderWork));
|
|
};
|
|
|
|
const handleAdvanceNextLevel = () => {
|
|
setRun((currentRun) => advanceLocalPuzzleLevel(currentRun));
|
|
};
|
|
|
|
const handlePauseChange = async (paused: boolean) => {
|
|
setRun((currentRun) => setLocalPuzzlePaused(currentRun, paused));
|
|
};
|
|
|
|
const handleUseProp = async (
|
|
propKind: 'hint' | 'reference' | 'freezeTime',
|
|
) => {
|
|
setRun((currentRun) =>
|
|
propKind === 'freezeTime'
|
|
? applyLocalPuzzleFreezeTime(currentRun)
|
|
: setLocalPuzzlePaused(currentRun, propKind === 'reference'),
|
|
);
|
|
};
|
|
|
|
return (
|
|
<PuzzleRuntimeShell
|
|
run={run}
|
|
isBusy={false}
|
|
error={null}
|
|
onBack={handleRestart}
|
|
onSwapPieces={handleSwapPieces}
|
|
onDragPiece={handleDragPiece}
|
|
onAdvanceNextLevel={handleAdvanceNextLevel}
|
|
onPauseChange={handlePauseChange}
|
|
onUseProp={handleUseProp}
|
|
/>
|
|
);
|
|
}
|