feat : add sounds
This commit is contained in:
BIN
public/sounds/capture.webm
Normal file
BIN
public/sounds/capture.webm
Normal file
Binary file not shown.
BIN
public/sounds/castle.webm
Normal file
BIN
public/sounds/castle.webm
Normal file
Binary file not shown.
BIN
public/sounds/game-end.webm
Normal file
BIN
public/sounds/game-end.webm
Normal file
Binary file not shown.
BIN
public/sounds/game-start.webm
Normal file
BIN
public/sounds/game-start.webm
Normal file
Binary file not shown.
BIN
public/sounds/illegal-move.webm
Normal file
BIN
public/sounds/illegal-move.webm
Normal file
Binary file not shown.
BIN
public/sounds/move-check.webm
Normal file
BIN
public/sounds/move-check.webm
Normal file
Binary file not shown.
BIN
public/sounds/move.webm
Normal file
BIN
public/sounds/move.webm
Normal file
Binary file not shown.
BIN
public/sounds/promote.webm
Normal file
BIN
public/sounds/promote.webm
Normal file
Binary file not shown.
@@ -1,4 +1,5 @@
|
|||||||
import { setGameHeaders } from "@/lib/chess";
|
import { setGameHeaders } from "@/lib/chess";
|
||||||
|
import { playIllegalMoveSound, playSoundFromMove } from "@/lib/sounds";
|
||||||
import { Chess, Move } from "chess.js";
|
import { Chess, Move } from "chess.js";
|
||||||
import { PrimitiveAtom, useAtom } from "jotai";
|
import { PrimitiveAtom, useAtom } from "jotai";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
@@ -39,17 +40,23 @@ export const useChessActions = (chessAtom: PrimitiveAtom<Chess>) => {
|
|||||||
const makeMove = useCallback(
|
const makeMove = useCallback(
|
||||||
(move: { from: string; to: string; promotion?: string }): Move | null => {
|
(move: { from: string; to: string; promotion?: string }): Move | null => {
|
||||||
const newGame = copyGame();
|
const newGame = copyGame();
|
||||||
const result = newGame.move(move);
|
try {
|
||||||
setGame(newGame);
|
const result = newGame.move(move);
|
||||||
|
setGame(newGame);
|
||||||
return result;
|
playSoundFromMove(result);
|
||||||
|
return result;
|
||||||
|
} catch {
|
||||||
|
playIllegalMoveSound();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[copyGame, setGame]
|
[copyGame, setGame]
|
||||||
);
|
);
|
||||||
|
|
||||||
const undoMove = useCallback(() => {
|
const undoMove = useCallback(() => {
|
||||||
const newGame = copyGame();
|
const newGame = copyGame();
|
||||||
newGame.undo();
|
const move = newGame.undo();
|
||||||
|
if (move) playSoundFromMove(move);
|
||||||
setGame(newGame);
|
setGame(newGame);
|
||||||
}, [copyGame, setGame]);
|
}, [copyGame, setGame]);
|
||||||
|
|
||||||
|
|||||||
@@ -247,3 +247,8 @@ export const getStartingFen = (
|
|||||||
|
|
||||||
return history[0].before;
|
return history[0].before;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isCheck = (fen: string): boolean => {
|
||||||
|
const game = new Chess(fen);
|
||||||
|
return game.inCheck();
|
||||||
|
};
|
||||||
|
|||||||
35
src/lib/sounds.ts
Normal file
35
src/lib/sounds.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Move } from "chess.js";
|
||||||
|
import { getWhoIsCheckmated, isCheck } from "./chess";
|
||||||
|
|
||||||
|
const playSound = async (url: string) => {
|
||||||
|
const audio = new Audio(url);
|
||||||
|
await audio.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const playCaptureSound = () => playSound("/sounds/capture.webm");
|
||||||
|
export const playCastleSound = () => playSound("/sounds/castle.webm");
|
||||||
|
export const playGameEndSound = () => playSound("/sounds/game-end.webm");
|
||||||
|
export const playGameStartSound = () => playSound("/sounds/game-start.webm");
|
||||||
|
export const playIllegalMoveSound = () =>
|
||||||
|
playSound("/sounds/illegal-move.webm");
|
||||||
|
export const playMoveCheckSound = () => playSound("/sounds/move-check.webm");
|
||||||
|
export const playMoveSound = () => playSound("/sounds/move.webm");
|
||||||
|
export const playPromoteSound = () => playSound("/sounds/promote.webm");
|
||||||
|
|
||||||
|
export const playSoundFromMove = async (move: Move | null) => {
|
||||||
|
if (!move) {
|
||||||
|
playIllegalMoveSound();
|
||||||
|
} else if (getWhoIsCheckmated(move.after)) {
|
||||||
|
playGameEndSound();
|
||||||
|
} else if (isCheck(move.after)) {
|
||||||
|
playMoveCheckSound();
|
||||||
|
} else if (move.promotion) {
|
||||||
|
playPromoteSound();
|
||||||
|
} else if (move.captured) {
|
||||||
|
playCaptureSound();
|
||||||
|
} else if (move.flags.includes("k") || move.flags.includes("q")) {
|
||||||
|
playCastleSound();
|
||||||
|
} else {
|
||||||
|
playMoveSound();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -40,17 +40,13 @@ export default function Board() {
|
|||||||
target: Square,
|
target: Square,
|
||||||
piece: string
|
piece: string
|
||||||
): boolean => {
|
): boolean => {
|
||||||
try {
|
const result = makeBoardMove({
|
||||||
const result = makeBoardMove({
|
from: source,
|
||||||
from: source,
|
to: target,
|
||||||
to: target,
|
promotion: piece[1]?.toLowerCase() ?? "q",
|
||||||
promotion: piece[1]?.toLowerCase() ?? "q",
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return !!result;
|
return !!result;
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSquareLeftClick = () => {
|
const handleSquareLeftClick = () => {
|
||||||
|
|||||||
@@ -70,17 +70,14 @@ export default function Board() {
|
|||||||
piece: string
|
piece: string
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (!piece || piece[0] !== playerColor || !isGameInProgress) return false;
|
if (!piece || piece[0] !== playerColor || !isGameInProgress) return false;
|
||||||
try {
|
|
||||||
const result = makeGameMove({
|
|
||||||
from: source,
|
|
||||||
to: target,
|
|
||||||
promotion: piece[1]?.toLowerCase() ?? "q",
|
|
||||||
});
|
|
||||||
|
|
||||||
return !!result;
|
const result = makeGameMove({
|
||||||
} catch {
|
from: source,
|
||||||
return false;
|
to: target,
|
||||||
}
|
promotion: piece[1]?.toLowerCase() ?? "q",
|
||||||
|
});
|
||||||
|
|
||||||
|
return !!result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isPieceDraggable = ({ piece }: { piece: string }): boolean => {
|
const isPieceDraggable = ({ piece }: { piece: string }): boolean => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Button, CircularProgress, Grid, Typography } from "@mui/material";
|
|||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { gameAtom, isGameInProgressAtom } from "./states";
|
import { gameAtom, isGameInProgressAtom } from "./states";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { playGameEndSound } from "@/lib/sounds";
|
||||||
|
|
||||||
export default function GameInProgress() {
|
export default function GameInProgress() {
|
||||||
const game = useAtomValue(gameAtom);
|
const game = useAtomValue(gameAtom);
|
||||||
@@ -11,6 +12,11 @@ export default function GameInProgress() {
|
|||||||
if (game.isGameOver()) setIsGameInProgress(false);
|
if (game.isGameOver()) setIsGameInProgress(false);
|
||||||
}, [game, setIsGameInProgress]);
|
}, [game, setIsGameInProgress]);
|
||||||
|
|
||||||
|
const handleResign = () => {
|
||||||
|
playGameEndSound();
|
||||||
|
setIsGameInProgress(false);
|
||||||
|
};
|
||||||
|
|
||||||
if (!isGameInProgress) return null;
|
if (!isGameInProgress) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -42,7 +48,7 @@ export default function GameInProgress() {
|
|||||||
xs={12}
|
xs={12}
|
||||||
gap={2}
|
gap={2}
|
||||||
>
|
>
|
||||||
<Button variant="outlined" onClick={() => setIsGameInProgress(false)}>
|
<Button variant="outlined" onClick={handleResign}>
|
||||||
Resign
|
Resign
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
gameAtom,
|
gameAtom,
|
||||||
} from "../states";
|
} from "../states";
|
||||||
import { useChessActions } from "@/hooks/useChessActions";
|
import { useChessActions } from "@/hooks/useChessActions";
|
||||||
|
import { playGameStartSound } from "@/lib/sounds";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -49,6 +50,7 @@ export default function GameSettingsDialog({ open, onClose }: Props) {
|
|||||||
blackName:
|
blackName:
|
||||||
playerColor === Color.Black ? "You" : `Stockfish level ${skillLevel}`,
|
playerColor === Color.Black ? "You" : `Stockfish level ${skillLevel}`,
|
||||||
});
|
});
|
||||||
|
playGameStartSound();
|
||||||
setIsGameInProgress(true);
|
setIsGameInProgress(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user