@@ -132,6 +132,8 @@ import {
|
||||
remixPuzzleGalleryWork,
|
||||
} from '../../services/puzzle-gallery';
|
||||
import {
|
||||
advancePuzzleNextLevel,
|
||||
startPuzzleRun,
|
||||
submitPuzzleLeaderboard,
|
||||
} from '../../services/puzzle-runtime';
|
||||
import {
|
||||
@@ -141,6 +143,7 @@ import {
|
||||
extendLocalPuzzleTime,
|
||||
isLocalPuzzleRun,
|
||||
refreshLocalPuzzleTimer,
|
||||
resolvePuzzleRestartLevelId,
|
||||
restartLocalPuzzleLevel,
|
||||
setLocalPuzzlePaused,
|
||||
startLocalPuzzleRun,
|
||||
@@ -876,31 +879,20 @@ function mergePuzzleServiceRuntimeState(
|
||||
}
|
||||
|
||||
const serviceLevel = serviceRun.currentLevel;
|
||||
if (
|
||||
currentRun.currentLevel.status === 'cleared' &&
|
||||
serviceLevel.status !== 'cleared'
|
||||
) {
|
||||
return {
|
||||
...currentRun,
|
||||
recommendedNextProfileId: serviceRun.recommendedNextProfileId,
|
||||
nextLevelMode: serviceRun.nextLevelMode,
|
||||
nextLevelProfileId: serviceRun.nextLevelProfileId,
|
||||
nextLevelId: serviceRun.nextLevelId,
|
||||
recommendedNextWorks: serviceRun.recommendedNextWorks,
|
||||
leaderboardEntries:
|
||||
currentRun.currentLevel.leaderboardEntries.length > 0
|
||||
? currentRun.currentLevel.leaderboardEntries
|
||||
: currentRun.leaderboardEntries,
|
||||
};
|
||||
}
|
||||
|
||||
const leaderboardEntries =
|
||||
serviceLevel.leaderboardEntries.length > 0
|
||||
? serviceLevel.leaderboardEntries
|
||||
: serviceRun.leaderboardEntries;
|
||||
|
||||
// 中文注释:拼块布局和通关状态由前端即时裁决;后端快照只合并榜单与下一关 handoff。
|
||||
return {
|
||||
...currentRun,
|
||||
runId: serviceRun.runId,
|
||||
entryProfileId: serviceRun.entryProfileId,
|
||||
clearedLevelCount: Math.max(
|
||||
currentRun.clearedLevelCount,
|
||||
serviceRun.clearedLevelCount,
|
||||
),
|
||||
recommendedNextProfileId: serviceRun.recommendedNextProfileId,
|
||||
nextLevelMode: serviceRun.nextLevelMode,
|
||||
nextLevelProfileId: serviceRun.nextLevelProfileId,
|
||||
@@ -909,18 +901,10 @@ function mergePuzzleServiceRuntimeState(
|
||||
leaderboardEntries,
|
||||
currentLevel: {
|
||||
...currentRun.currentLevel,
|
||||
status: serviceLevel.status,
|
||||
startedAtMs: serviceLevel.startedAtMs,
|
||||
clearedAtMs: serviceLevel.clearedAtMs,
|
||||
elapsedMs: serviceLevel.elapsedMs,
|
||||
timeLimitMs: serviceLevel.timeLimitMs,
|
||||
remainingMs: serviceLevel.remainingMs,
|
||||
pausedAccumulatedMs: serviceLevel.pausedAccumulatedMs,
|
||||
pauseStartedAtMs: serviceLevel.pauseStartedAtMs,
|
||||
freezeAccumulatedMs: serviceLevel.freezeAccumulatedMs,
|
||||
freezeStartedAtMs: serviceLevel.freezeStartedAtMs,
|
||||
freezeUntilMs: serviceLevel.freezeUntilMs,
|
||||
leaderboardEntries,
|
||||
leaderboardEntries:
|
||||
leaderboardEntries.length > 0
|
||||
? leaderboardEntries
|
||||
: currentRun.currentLevel.leaderboardEntries,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -2181,8 +2165,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
setPuzzleError(null);
|
||||
|
||||
try {
|
||||
const item = detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
|
||||
const run = startLocalPuzzleRun(item, levelId ?? null);
|
||||
const item =
|
||||
detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
|
||||
const { run } = await startPuzzleRun({
|
||||
profileId: item.profileId,
|
||||
levelId: levelId ?? null,
|
||||
});
|
||||
setSelectedPuzzleDetail(item);
|
||||
setPuzzleRun(run);
|
||||
setPuzzleRuntimeReturnStage(returnStage);
|
||||
@@ -2411,14 +2399,20 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
try {
|
||||
setPuzzleRun(swapLocalPuzzlePieces(puzzleRun, payload));
|
||||
setPuzzleRun(
|
||||
swapLocalPuzzlePieces(
|
||||
puzzleRun,
|
||||
payload,
|
||||
isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '交换拼图块失败。'));
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
},
|
||||
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage],
|
||||
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage, selectedPuzzleDetail],
|
||||
);
|
||||
|
||||
const dragPuzzlePiece = useCallback(
|
||||
@@ -2430,14 +2424,20 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
try {
|
||||
setPuzzleRun(dragLocalPuzzlePiece(puzzleRun, payload));
|
||||
setPuzzleRun(
|
||||
dragLocalPuzzlePiece(
|
||||
puzzleRun,
|
||||
payload,
|
||||
isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '拖动拼图块失败。'));
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
},
|
||||
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage],
|
||||
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage, selectedPuzzleDetail],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -2515,18 +2515,46 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
|
||||
const restartPuzzleCurrentLevel = useCallback(async () => {
|
||||
const currentLevel = puzzleRun?.currentLevel ?? null;
|
||||
if (!puzzleRun || !currentLevel || isPuzzleBusy) {
|
||||
const currentRun = puzzleRunRef.current ?? puzzleRun;
|
||||
const currentLevel = currentRun?.currentLevel ?? null;
|
||||
if (!currentRun || !currentLevel || isPuzzleBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPuzzleError(null);
|
||||
const nextRun = restartLocalPuzzleLevel(puzzleRunRef.current ?? puzzleRun);
|
||||
puzzleRunRef.current = nextRun;
|
||||
setPuzzleRun(nextRun);
|
||||
setIsPuzzleBusy(true);
|
||||
try {
|
||||
if (isLocalPuzzleRun(currentRun)) {
|
||||
const nextRun = restartLocalPuzzleLevel(currentRun);
|
||||
puzzleRunRef.current = nextRun;
|
||||
setPuzzleRun(nextRun);
|
||||
return;
|
||||
}
|
||||
|
||||
const detailItem =
|
||||
selectedPuzzleDetail?.profileId === currentLevel.profileId
|
||||
? selectedPuzzleDetail
|
||||
: await getPuzzleGalleryDetail(currentLevel.profileId).then(
|
||||
(response) => response.item,
|
||||
);
|
||||
const { run } = await startPuzzleRun({
|
||||
profileId: currentLevel.profileId,
|
||||
levelId: resolvePuzzleRestartLevelId(currentRun, detailItem),
|
||||
});
|
||||
setSelectedPuzzleDetail(detailItem);
|
||||
puzzleRunRef.current = run;
|
||||
setPuzzleRun(run);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '重新开始拼图关卡失败。'));
|
||||
} finally {
|
||||
setIsPuzzleBusy(false);
|
||||
}
|
||||
}, [
|
||||
isPuzzleBusy,
|
||||
puzzleRun,
|
||||
resolvePuzzleErrorMessage,
|
||||
selectedPuzzleDetail,
|
||||
setIsPuzzleBusy,
|
||||
setPuzzleError,
|
||||
]);
|
||||
|
||||
@@ -2565,14 +2593,19 @@ export function PlatformEntryFlowShellImpl({
|
||||
gameState.currentLevelId.trim()
|
||||
? gameState.currentLevelId
|
||||
: null;
|
||||
const item = selectedPuzzleDetail?.profileId === profileId
|
||||
? selectedPuzzleDetail
|
||||
: await getPuzzleGalleryDetail(profileId).then((response) => response.item);
|
||||
const nextRun = startLocalPuzzleRun(item, levelId);
|
||||
setSelectedPuzzleDetail(item);
|
||||
setPuzzleRun(nextRun);
|
||||
setPuzzleRuntimeReturnStage('platform');
|
||||
setSelectionStage('puzzle-runtime');
|
||||
const item =
|
||||
selectedPuzzleDetail?.profileId === profileId
|
||||
? selectedPuzzleDetail
|
||||
: await getPuzzleGalleryDetail(profileId).then(
|
||||
(response) => response.item,
|
||||
);
|
||||
await startPuzzleRunFromProfile(
|
||||
item.profileId,
|
||||
'platform',
|
||||
item,
|
||||
false,
|
||||
levelId,
|
||||
);
|
||||
} catch (error) {
|
||||
platformBootstrap.setSaveError(
|
||||
resolvePuzzleErrorMessage(error, '恢复拼图存档失败。'),
|
||||
@@ -2587,7 +2620,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
selectedPuzzleDetail,
|
||||
resolvePuzzleErrorMessage,
|
||||
setPuzzleError,
|
||||
setSelectionStage,
|
||||
startPuzzleRunFromProfile,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -2651,7 +2684,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
]);
|
||||
|
||||
const advancePuzzleLevel = useCallback(
|
||||
async (target?: { profileId?: string; levelId?: string | null }) => {
|
||||
async (_target?: { profileId?: string; levelId?: string | null }) => {
|
||||
if (!puzzleRun || isPuzzleBusy || isPuzzleLeaderboardBusy) {
|
||||
return;
|
||||
}
|
||||
@@ -2665,12 +2698,43 @@ export function PlatformEntryFlowShellImpl({
|
||||
setPuzzleError(null);
|
||||
|
||||
try {
|
||||
const nextRun = advanceLocalPuzzleLevel(
|
||||
puzzleRun,
|
||||
selectedPuzzleDetail,
|
||||
target,
|
||||
);
|
||||
setPuzzleRun(nextRun);
|
||||
if (isLocalPuzzleRun(puzzleRun)) {
|
||||
const nextRun = advanceLocalPuzzleLevel(
|
||||
puzzleRun,
|
||||
selectedPuzzleDetail,
|
||||
_target,
|
||||
);
|
||||
setPuzzleRun(nextRun);
|
||||
return;
|
||||
}
|
||||
|
||||
const targetProfileId = _target?.profileId?.trim() ?? '';
|
||||
if (puzzleRun.nextLevelMode === 'similarWorks' && targetProfileId) {
|
||||
const itemPromise =
|
||||
selectedPuzzleDetail?.profileId === targetProfileId
|
||||
? Promise.resolve(selectedPuzzleDetail)
|
||||
: getPuzzleGalleryDetail(targetProfileId).then(
|
||||
(response) => response.item,
|
||||
);
|
||||
const [{ run }, item] = await Promise.all([
|
||||
advancePuzzleNextLevel(puzzleRun.runId, {
|
||||
targetProfileId,
|
||||
}),
|
||||
itemPromise,
|
||||
]);
|
||||
setSelectedPuzzleDetail(item);
|
||||
setPuzzleRun(run);
|
||||
pushAppHistoryPath(
|
||||
buildPublicWorkStagePath(
|
||||
'puzzle-runtime',
|
||||
buildPuzzlePublicWorkCode(item.profileId),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { run } = await advancePuzzleNextLevel(puzzleRun.runId);
|
||||
setPuzzleRun(run);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
|
||||
} finally {
|
||||
@@ -3563,7 +3627,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
|
||||
if (isPuzzleGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
|
||||
const work =
|
||||
selectedPuzzleDetail?.profileId === selectedPublicWorkDetail.profileId
|
||||
? selectedPuzzleDetail
|
||||
: mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError(
|
||||
'当前拼图作品信息不完整,暂时无法进入玩法。',
|
||||
@@ -3628,10 +3695,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
isPublicWorkDetailBusy,
|
||||
runProtectedAction,
|
||||
selectedDetailEntry,
|
||||
selectedPuzzleDetail,
|
||||
selectedPublicWorkDetail,
|
||||
startBigFishRunFromWork,
|
||||
startMatch3DRunFromProfile,
|
||||
startPuzzleRunFromProfile,
|
||||
startMatch3DRunFromProfile,
|
||||
]);
|
||||
|
||||
const remixPublicWork = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user