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, }; } |