Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 6x 6x 6x 6x 6x 6x 7x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 6x 6x 9x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 4x 4x 9x 7x 1x 1x 1x 1x 1x 7x 2x 2x 7x 7x 7x 7x 7x 7x | import type { Tile } from '@/components/GameCanvas/GameCanvas.types';
import { animateFlip } from '@/shared/utils/animationUtils';
import { drawBoard } from '@/shared/utils/canvasRenderer';
import { handleMatchPair } from '@/shared/utils/handlers/handleMatchPair';
import { handleMismatchPair } from '@/shared/utils/handlers/handleMismatchPair';
import { useGameStore } from '@/stores/useGameStore';
import type { Ref } from 'vue';
import { ref } from 'vue';
import { useClickLock } from './useClickLock';
import { useGameOverChecker } from './useGameOverChecker';
import { useSoundEffects } from './useSoundEffects';
import { useTileEngine } from './useTileEngine';
interface TileInteractionCallbacks {
onGameOver: () => void;
onMatch?: (a: Tile, b: Tile) => void;
onMismatch?: (a: Tile, b: Tile) => void;
}
export function useTileInteractions(
canvasRef: Ref<HTMLCanvasElement | null>,
tiles: Ref<Tile[]>,
mouseX: Ref<number>,
mouseY: Ref<number>,
hoveredTileId: Ref<number | null>,
callbacks: TileInteractionCallbacks,
tileSize: Ref<number>
) {
const { onGameOver, onMatch, onMismatch } = callbacks;
const flippedTiles = ref<Tile[]>([]);
const gameOver = ref(false);
const engine = useTileEngine(tiles, tileSize);
const { playFlip, playMatch, playFail, playWin } = useSoundEffects();
const { isLocked, lock, unlock } = useClickLock();
const { checkIfGameOver } = useGameOverChecker(tiles);
const gameStore = useGameStore();
async function flipAndRender(tile: Tile, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
await animateFlip(tile, canvas, tileSize.value, { reverse: true });
tile.flipped = true;
flippedTiles.value.push(tile);
drawBoard(ctx, canvas, tiles.value, { x: mouseX.value, y: mouseY.value }, hoveredTileId.value, tileSize.value);
await playFlip();
}
async function handleClick(e: MouseEvent | TouchEvent) {
if (isLocked() || gameOver.value) return;
const canvas = canvasRef.value;
const ctx = canvas?.getContext('2d');
if (!canvas || !ctx) return;
const rect = canvas.getBoundingClientRect();
const isTouch = 'changedTouches' in e;
const clientX = isTouch ? e.changedTouches[0].clientX : (e as MouseEvent).clientX;
const clientY = isTouch ? e.changedTouches[0].clientY : (e as MouseEvent).clientY;
const x = clientX - rect.left;
const y = clientY - rect.top;
const tile = engine.getTileAt(x, y);
if (!tile || tile.flipped || tile.matched || flippedTiles.value.length >= 2) return;
lock();
await flipAndRender(tile, canvas, ctx);
if (flippedTiles.value.length === 2) {
gameStore.incrementMoves();
const [a, b] = flippedTiles.value;
if (engine.isMatchByName(a, b)) {
await handleMatchPair(a, b, {
playMatch,
checkIfGameOver,
playWin,
onGameOver,
onMatch,
setGameOver: (v) => (gameOver.value = v),
unlock,
});
flippedTiles.value = [];
} else {
await handleMismatchPair(a, b, {
playFail,
drawBoard: () => {
drawBoard(ctx, canvas, tiles.value, { x: mouseX.value, y: mouseY.value }, hoveredTileId.value, tileSize.value);
},
onMismatch,
unlock,
});
flippedTiles.value = [];
}
} else {
unlock();
}
}
function resetInteractions() {
flippedTiles.value = [];
unlock();
gameOver.value = false;
gameStore.setGameOver(false);
}
function isGameOver() {
return gameOver.value;
}
return {
handleClick,
resetInteractions,
isGameOver,
};
}
|