Skip to content

Commit 7f2142f

Browse files
committed
fix:修复俄罗斯方块拼图中撤销会导致部分内容空白的问题
1 parent 225b5d0 commit 7f2142f

File tree

1 file changed

+211
-45
lines changed

1 file changed

+211
-45
lines changed

src/hooks/usePuzzleGame.ts

Lines changed: 211 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -737,25 +737,40 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
737737
newAnswerGrid[lastMove.toSlot] = null;
738738
}
739739
}
740+
741+
// 更新拼图块状态,将其移回原位置
740742
updatedPieces = updatedPieces.map(piece =>
741743
piece.id === lastMove.pieceId
742744
? { ...piece, currentSlot: lastMove.fromSlot || null }
743745
: piece
744746
);
745-
// 如果从其他槽位移动,需要恢复原槽位
747+
748+
// 如果从其他槽位移动,需要恢复原槽位的状态
746749
if (lastMove.fromSlot !== null && lastMove.fromSlot !== undefined) {
747750
const originalPiece = updatedPieces.find(p => p.id === lastMove.pieceId);
748751
if (originalPiece) {
749752
const fromSlot = lastMove.fromSlot as number;
750-
if (prev.config.pieceShape === 'tetris' && originalPiece.correctSlots) {
751-
// 俄罗斯方块恢复到所有原始槽位
752-
originalPiece.correctSlots.forEach(slotIndex => {
753-
if (slotIndex === fromSlot) {
754-
newAnswerGrid[slotIndex] = { ...originalPiece, currentSlot: fromSlot };
755-
} else {
753+
754+
if (prev.config.pieceShape === 'tetris' && originalPiece.occupiedPositions) {
755+
// 俄罗斯方块:计算原位置占据的所有槽位
756+
const gridCols = prev.config.gridSize.cols;
757+
const fromRow = Math.floor(fromSlot / gridCols);
758+
const fromCol = fromSlot % gridCols;
759+
760+
// 计算相对位置偏移
761+
const minRow = Math.min(...originalPiece.occupiedPositions.map(pos => pos[0]));
762+
const minCol = Math.min(...originalPiece.occupiedPositions.map(pos => pos[1]));
763+
764+
// 计算并恢复所有原始槽位
765+
for (const [relRow, relCol] of originalPiece.occupiedPositions) {
766+
const slotRow = fromRow + (relRow - minRow);
767+
const slotCol = fromCol + (relCol - minCol);
768+
const slotIndex = slotRow * gridCols + slotCol;
769+
770+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
756771
newAnswerGrid[slotIndex] = { ...originalPiece, currentSlot: fromSlot };
757772
}
758-
});
773+
}
759774
} else {
760775
// 普通拼图块恢复到单个槽位
761776
newAnswerGrid[fromSlot] = { ...originalPiece, currentSlot: fromSlot };
@@ -770,11 +785,27 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
770785
if (piece) {
771786
const fromSlot = lastMove.fromSlot as number;
772787

773-
if (prev.config.pieceShape === 'tetris' && piece.correctSlots) {
774-
// 俄罗斯方块:恢复到所有占据的槽位
775-
piece.correctSlots.forEach(slotIndex => {
776-
newAnswerGrid[slotIndex] = { ...piece, currentSlot: fromSlot };
777-
});
788+
if (prev.config.pieceShape === 'tetris' && piece.occupiedPositions) {
789+
// 俄罗斯方块:计算并恢复到所有占据的槽位
790+
const gridCols = prev.config.gridSize.cols;
791+
const fromRow = Math.floor(fromSlot / gridCols);
792+
const fromCol = fromSlot % gridCols;
793+
794+
// 计算相对位置偏移
795+
const minRow = Math.min(...piece.occupiedPositions.map(pos => pos[0]));
796+
const minCol = Math.min(...piece.occupiedPositions.map(pos => pos[1]));
797+
798+
// 计算并恢复所有槽位
799+
for (const [relRow, relCol] of piece.occupiedPositions) {
800+
const slotRow = fromRow + (relRow - minRow);
801+
const slotCol = fromCol + (relCol - minCol);
802+
const slotIndex = slotRow * gridCols + slotCol;
803+
804+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
805+
newAnswerGrid[slotIndex] = { ...piece, currentSlot: fromSlot };
806+
}
807+
}
808+
778809
updatedPieces = updatedPieces.map(p =>
779810
p.id === lastMove.pieceId ? { ...p, currentSlot: fromSlot } : p
780811
);
@@ -820,15 +851,31 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
820851
if (lastMove.replacedPieceId) {
821852
const replacedPiece = updatedPieces.find(p => p.id === lastMove.replacedPieceId);
822853
if (replacedPiece) {
823-
if (prev.config.pieceShape === 'tetris' && replacedPiece.correctSlots) {
824-
// 俄罗斯方块:恢复到所有原始槽位
825-
replacedPiece.correctSlots.forEach(slotIndex => {
826-
newAnswerGrid[slotIndex] = { ...replacedPiece, currentSlot: toSlot };
827-
});
854+
if (prev.config.pieceShape === 'tetris' && replacedPiece.occupiedPositions) {
855+
// 俄罗斯方块:计算并恢复到所有槽位
856+
const gridCols = prev.config.gridSize.cols;
857+
const toRow = Math.floor(toSlot / gridCols);
858+
const toCol = toSlot % gridCols;
859+
860+
// 计算相对位置偏移
861+
const minRow = Math.min(...replacedPiece.occupiedPositions.map(pos => pos[0]));
862+
const minCol = Math.min(...replacedPiece.occupiedPositions.map(pos => pos[1]));
863+
864+
// 计算并恢复所有槽位
865+
for (const [relRow, relCol] of replacedPiece.occupiedPositions) {
866+
const slotRow = toRow + (relRow - minRow);
867+
const slotCol = toCol + (relCol - minCol);
868+
const slotIndex = slotRow * gridCols + slotCol;
869+
870+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
871+
newAnswerGrid[slotIndex] = { ...replacedPiece, currentSlot: toSlot };
872+
}
873+
}
828874
} else {
829875
// 普通拼图块:恢复到单个槽位
830876
newAnswerGrid[toSlot] = { ...replacedPiece, currentSlot: toSlot };
831877
}
878+
832879
updatedPieces = updatedPieces.map(p =>
833880
p.id === lastMove.replacedPieceId ? { ...p, currentSlot: toSlot } : p
834881
);
@@ -847,11 +894,27 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
847894
const originalPiece = updatedPieces.find(p => p.id === lastMove.pieceId);
848895
if (originalPiece) {
849896
const fromSlot = lastMove.fromSlot as number;
850-
if (prev.config.pieceShape === 'tetris' && originalPiece.correctSlots) {
851-
// 俄罗斯方块:恢复到所有原始槽位
852-
originalPiece.correctSlots.forEach(slotIndex => {
853-
newAnswerGrid[slotIndex] = { ...originalPiece, currentSlot: fromSlot };
854-
});
897+
898+
if (prev.config.pieceShape === 'tetris' && originalPiece.occupiedPositions) {
899+
// 俄罗斯方块:计算并恢复到所有原始槽位
900+
const gridCols = prev.config.gridSize.cols;
901+
const fromRow = Math.floor(fromSlot / gridCols);
902+
const fromCol = fromSlot % gridCols;
903+
904+
// 计算相对位置偏移
905+
const minRow = Math.min(...originalPiece.occupiedPositions.map(pos => pos[0]));
906+
const minCol = Math.min(...originalPiece.occupiedPositions.map(pos => pos[1]));
907+
908+
// 计算并恢复所有原始槽位
909+
for (const [relRow, relCol] of originalPiece.occupiedPositions) {
910+
const slotRow = fromRow + (relRow - minRow);
911+
const slotCol = fromCol + (relCol - minCol);
912+
const slotIndex = slotRow * gridCols + slotCol;
913+
914+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
915+
newAnswerGrid[slotIndex] = { ...originalPiece, currentSlot: fromSlot };
916+
}
917+
}
855918
} else {
856919
// 普通拼图块:恢复到单个槽位
857920
newAnswerGrid[fromSlot] = { ...originalPiece, currentSlot: fromSlot };
@@ -902,11 +965,26 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
902965
if (piece) {
903966
const toSlot = moveToRedo.toSlot as number;
904967

905-
if (prev.config.pieceShape === 'tetris' && piece.correctSlots) {
906-
// 俄罗斯方块:放置到所有槽位
907-
piece.correctSlots.forEach(slotIndex => {
908-
newAnswerGrid[slotIndex] = { ...piece, currentSlot: toSlot };
909-
});
968+
if (prev.config.pieceShape === 'tetris' && piece.occupiedPositions) {
969+
// 俄罗斯方块:计算并放置到所有槽位
970+
const gridCols = prev.config.gridSize.cols;
971+
const toRow = Math.floor(toSlot / gridCols);
972+
const toCol = toSlot % gridCols;
973+
974+
// 计算相对位置偏移
975+
const minRow = Math.min(...piece.occupiedPositions.map(pos => pos[0]));
976+
const minCol = Math.min(...piece.occupiedPositions.map(pos => pos[1]));
977+
978+
// 计算并放置到所有槽位
979+
for (const [relRow, relCol] of piece.occupiedPositions) {
980+
const slotRow = toRow + (relRow - minRow);
981+
const slotCol = toCol + (relCol - minCol);
982+
const slotIndex = slotRow * gridCols + slotCol;
983+
984+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
985+
newAnswerGrid[slotIndex] = { ...piece, currentSlot: toSlot };
986+
}
987+
}
910988
} else {
911989
// 普通拼图块:放置到单个槽位
912990
newAnswerGrid[toSlot] = { ...piece, currentSlot: toSlot };
@@ -917,13 +995,51 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
917995
);
918996
}
919997
}
998+
920999
// 如果从其他槽位移动,清空原槽位
9211000
if (moveToRedo.fromSlot !== null && moveToRedo.fromSlot !== undefined) {
922-
if (prev.config.pieceShape === 'tetris') {
923-
// 俄罗斯方块:清空所有原始槽位
924-
for (let i = 0; i < newAnswerGrid.length; i++) {
925-
if (newAnswerGrid[i]?.id === moveToRedo.pieceId && i !== moveToRedo.toSlot) {
926-
newAnswerGrid[i] = null;
1001+
const piece = updatedPieces.find(p => p.id === moveToRedo.pieceId);
1002+
if (piece && prev.config.pieceShape === 'tetris' && piece.occupiedPositions) {
1003+
// 俄罗斯方块:清空所有原始槽位(但保留目标槽位)
1004+
const gridCols = prev.config.gridSize.cols;
1005+
const fromRow = Math.floor(moveToRedo.fromSlot / gridCols);
1006+
const fromCol = moveToRedo.fromSlot % gridCols;
1007+
1008+
// 计算相对位置偏移
1009+
const minRow = Math.min(...piece.occupiedPositions.map(pos => pos[0]));
1010+
const minCol = Math.min(...piece.occupiedPositions.map(pos => pos[1]));
1011+
1012+
// 清空原始位置的所有槽位
1013+
for (const [relRow, relCol] of piece.occupiedPositions) {
1014+
const slotRow = fromRow + (relRow - minRow);
1015+
const slotCol = fromCol + (relCol - minCol);
1016+
const slotIndex = slotRow * gridCols + slotCol;
1017+
1018+
// 只有当槽位不是目标槽位的一部分时才清空
1019+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length &&
1020+
slotIndex !== moveToRedo.toSlot &&
1021+
newAnswerGrid[slotIndex]?.id === moveToRedo.pieceId) {
1022+
1023+
// 检查该槽位是否也是目标位置的一部分
1024+
const toSlot = moveToRedo.toSlot as number;
1025+
const toRow = Math.floor(toSlot / gridCols);
1026+
const toCol = toSlot % gridCols;
1027+
let isTargetSlot = false;
1028+
1029+
for (const [targetRelRow, targetRelCol] of piece.occupiedPositions) {
1030+
const targetSlotRow = toRow + (targetRelRow - minRow);
1031+
const targetSlotCol = toCol + (targetRelCol - minCol);
1032+
const targetSlotIndex = targetSlotRow * gridCols + targetSlotCol;
1033+
1034+
if (slotIndex === targetSlotIndex) {
1035+
isTargetSlot = true;
1036+
break;
1037+
}
1038+
}
1039+
1040+
if (!isTargetSlot) {
1041+
newAnswerGrid[slotIndex] = null;
1042+
}
9271043
}
9281044
}
9291045
} else {
@@ -989,11 +1105,26 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
9891105
}
9901106

9911107
// 放置新拼图块
992-
if (prev.config.pieceShape === 'tetris' && piece.correctSlots) {
993-
// 俄罗斯方块:放置到所有槽位
994-
piece.correctSlots.forEach(slotIndex => {
995-
newAnswerGrid[slotIndex] = { ...piece, currentSlot: toSlot };
996-
});
1108+
if (prev.config.pieceShape === 'tetris' && piece.occupiedPositions) {
1109+
// 俄罗斯方块:计算并放置到所有槽位
1110+
const gridCols = prev.config.gridSize.cols;
1111+
const toRow = Math.floor(toSlot / gridCols);
1112+
const toCol = toSlot % gridCols;
1113+
1114+
// 计算相对位置偏移
1115+
const minRow = Math.min(...piece.occupiedPositions.map(pos => pos[0]));
1116+
const minCol = Math.min(...piece.occupiedPositions.map(pos => pos[1]));
1117+
1118+
// 计算并放置到所有槽位
1119+
for (const [relRow, relCol] of piece.occupiedPositions) {
1120+
const slotRow = toRow + (relRow - minRow);
1121+
const slotCol = toCol + (relCol - minCol);
1122+
const slotIndex = slotRow * gridCols + slotCol;
1123+
1124+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length) {
1125+
newAnswerGrid[slotIndex] = { ...piece, currentSlot: toSlot };
1126+
}
1127+
}
9971128
} else {
9981129
// 普通拼图块:放置到单个槽位
9991130
newAnswerGrid[toSlot] = { ...piece, currentSlot: toSlot };
@@ -1005,17 +1136,52 @@ export function usePuzzleGame({ userId, preloadedGameState }: UsePuzzleGameProps
10051136

10061137
// 如果从其他槽位移动,清空原槽位
10071138
if (moveToRedo.fromSlot !== null && moveToRedo.fromSlot !== undefined) {
1008-
if (prev.config.pieceShape === 'tetris') {
1009-
// 俄罗斯方块:确保不清空目标槽位
1010-
for (let i = 0; i < newAnswerGrid.length; i++) {
1011-
if (newAnswerGrid[i]?.id === moveToRedo.pieceId &&
1012-
!piece.correctSlots?.includes(i)) {
1013-
newAnswerGrid[i] = null;
1139+
const fromSlot = moveToRedo.fromSlot as number;
1140+
1141+
if (prev.config.pieceShape === 'tetris' && piece.occupiedPositions) {
1142+
// 俄罗斯方块:清空原始位置的所有槽位(但保留目标槽位)
1143+
const gridCols = prev.config.gridSize.cols;
1144+
const fromRow = Math.floor(fromSlot / gridCols);
1145+
const fromCol = fromSlot % gridCols;
1146+
1147+
// 计算相对位置偏移
1148+
const minRow = Math.min(...piece.occupiedPositions.map(pos => pos[0]));
1149+
const minCol = Math.min(...piece.occupiedPositions.map(pos => pos[1]));
1150+
1151+
// 清空原始位置的所有槽位
1152+
for (const [relRow, relCol] of piece.occupiedPositions) {
1153+
const slotRow = fromRow + (relRow - minRow);
1154+
const slotCol = fromCol + (relCol - minCol);
1155+
const slotIndex = slotRow * gridCols + slotCol;
1156+
1157+
// 只有当槽位不是目标槽位的一部分时才清空
1158+
if (slotIndex >= 0 && slotIndex < newAnswerGrid.length &&
1159+
newAnswerGrid[slotIndex]?.id === moveToRedo.pieceId) {
1160+
1161+
// 检查该槽位是否也是目标位置的一部分
1162+
const toRow = Math.floor(toSlot / gridCols);
1163+
const toCol = toSlot % gridCols;
1164+
let isTargetSlot = false;
1165+
1166+
for (const [targetRelRow, targetRelCol] of piece.occupiedPositions) {
1167+
const targetSlotRow = toRow + (targetRelRow - minRow);
1168+
const targetSlotCol = toCol + (targetRelCol - minCol);
1169+
const targetSlotIndex = targetSlotRow * gridCols + targetSlotCol;
1170+
1171+
if (slotIndex === targetSlotIndex) {
1172+
isTargetSlot = true;
1173+
break;
1174+
}
1175+
}
1176+
1177+
if (!isTargetSlot) {
1178+
newAnswerGrid[slotIndex] = null;
1179+
}
10141180
}
10151181
}
10161182
} else {
10171183
// 普通拼图块:清空原始槽位
1018-
newAnswerGrid[moveToRedo.fromSlot] = null;
1184+
newAnswerGrid[fromSlot] = null;
10191185
}
10201186
}
10211187
}

0 commit comments

Comments
 (0)