Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/assets/audio/DardDealt.mp3
Binary file not shown.
Binary file added src/assets/audio/RoundLost.mp3
Binary file not shown.
Binary file added src/assets/audio/RoundWon.mp3
Binary file not shown.
Binary file added src/assets/audio/WarningAlert.mp3
Binary file not shown.
7 changes: 6 additions & 1 deletion src/components/Card/hooks/useCardPile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useRef, useState } from "react";
import { CardObject, Facing } from "../card.types";
import { BestHand, calculateBestHand } from "../../../utilities/deckUtilities";
import useAudioPlayer from "../../Table/hooks/useAudioPlayer";

const defaultBestHand: BestHand = { cards: [], score: 0 };
/**
Expand All @@ -13,14 +14,18 @@ export function useCardPile() {
const bust = useRef<boolean>(false);
const fiveCardTrick = useRef<boolean>(false);
const allCardsVisible = useRef<boolean>(false);
const {play} = useAudioPlayer();

/**
* Add cards to the pile
* @param cardsToAdd The cards to be added to the pile
* @param facing The direction the card should be facing when placed in the pile
*/
function addCards(cardsToAdd: CardObject[], facing: Facing | null = null) {
if (facing) cardsToAdd.forEach((c) => (c.facing = facing));
cardsToAdd.forEach(card => {
play('cardDealt');
if (facing) card.facing = facing;
});
setCards([...cards].concat(cardsToAdd));
}

Expand Down
16 changes: 16 additions & 0 deletions src/components/GameSettings/GameSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ function GameSettings() {
</div>
</div>

<div className={styles.GameSetting}>
<div className={styles.Info}>
<span className={styles.Header}>Audio Enabled</span>
<span className={styles.Description}>
Whether or not sound effects should play
</span>
</div>
<div className={styles.ControlWrapper}>
<CheckBox
className={styles.Control}
checked={gameSettings.audioEnabled}
onChanged={() => gameSettings.toggleAudioEnabled()}
/>
</div>
</div>

<div className={styles.GameSetting}>
<div className={styles.Info}>
<span className={styles.Header}>Hit Warnings Enabled</span>
Expand Down
4 changes: 4 additions & 0 deletions src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { useGameSettingsStore } from "../../stores/gameSettingsStore";
import styles from "./Table.module.scss";
import SettingsButton from "../SettingsButton";
import { CardEffect } from "../Card/Card";
import useAudioPlayer from "./hooks/useAudioPlayer";

const Table = ({ hide = false }: { hide?: boolean }) => {
const game = useGame();
const {play} = useAudioPlayer();
const { hitWarningsEnabled, stickWarningsEnabled } = useGameSettingsStore();
const [showHitWarning, setShowHitWarning] = useState<boolean>(false);
const [showStickWarning, setShowStickWarning] = useState<boolean>(false);
Expand All @@ -29,6 +31,7 @@ const Table = ({ hide = false }: { hide?: boolean }) => {
game.getParticipantScore("Player") >= 18 &&
!overrideWarning
) {
play('warning');
setShowHitWarning(true);
return;
}
Expand All @@ -44,6 +47,7 @@ const Table = ({ hide = false }: { hide?: boolean }) => {
game.getParticipantScore("Player") <= 10 &&
!overrideWarning
) {
play('warning');
setShowStickWarning(true);
return;
}
Expand Down
25 changes: 25 additions & 0 deletions src/components/Table/hooks/useAudioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import cardDealt from "../../../assets/audio/DardDealt.mp3";
import roundLost from "../../../assets/audio/RoundLost.mp3";
import roundWon from "../../../assets/audio/RoundWon.mp3";
import warning from "../../../assets/audio/WarningAlert.mp3";
import { useGameSettingsStore } from "../../../stores/gameSettingsStore";

type SoundEffect = "cardDealt" | "warning" | "roundWon" | "roundLost";

const sounds: Record<SoundEffect, string> = {
cardDealt,
roundLost,
roundWon,
warning,
};

function useAudioPlayer() {
const { audioEnabled } = useGameSettingsStore();

function play(soundEffect: SoundEffect) {
audioEnabled && new Audio(sounds[soundEffect]).play();
}

return { play };
}
export default useAudioPlayer;
17 changes: 13 additions & 4 deletions src/components/Table/hooks/useGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { useGameStats } from "../../InfoHud";
import { determineWinner } from "../../../utilities/deckUtilities";
import { Facing, useCardPile, useDeck } from "../../Card";
import useAudioPlayer from "./useAudioPlayer";

export type GameState =
| "WaitingForStart"
Expand All @@ -22,6 +23,8 @@ export type WinType =
| "high-card"
| "draw";

const delayBetweenCards = 400;

export function useGame() {
const [state, setState] = useState<GameState>("WaitingForStart");
const [outcome, setOutcome] = useState<string | null>(null);
Expand All @@ -31,12 +34,13 @@ export function useGame() {
const player = useCardPile();
const dealer = useCardPile();
const stats = useGameStats();
const {play} = useAudioPlayer();

const dealPlayerCard = state === "DealPlayerCard";
const dealDealerCard = state === "DealDealerCard";
const resultGame = state === "Result";
const dealerRound = state === "DealerRound";

/**
* Takes card of determining the winner
* when the game state changes to Result
Expand Down Expand Up @@ -85,7 +89,7 @@ export function useGame() {
else if (dealerScore >= 16 && dealerScore > playerScore) {
setState("Result");
} else {
timeout = setTimeout(() => dealCardsToParticipant("Dealer", 1), 500);
timeout = setTimeout(() => dealCardsToParticipant("Dealer", 1), delayBetweenCards);
}

return () => {
Expand All @@ -105,7 +109,7 @@ export function useGame() {
const timeout = setTimeout(() => {
player.addCards(deck.take(1));
setState("DealDealerCard");
}, 500);
}, delayBetweenCards);

return () => {
clearTimeout(timeout);
Expand All @@ -130,7 +134,7 @@ export function useGame() {
} else {
setState("DealPlayerCard");
}
}, 500);
}, delayBetweenCards);

return () => {
clearTimeout(timeout);
Expand Down Expand Up @@ -207,6 +211,11 @@ export function useGame() {
setState("GameOver");
setOutcome(outcomeText);
setWinner(winner ?? "none");
if (winner) {
play(winner === 'Player' ? 'roundWon' : 'roundLost' );
}



winner && stats.updateWinnerStats(winner);
}
Expand Down
5 changes: 5 additions & 0 deletions src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
/// <reference types="react-scripts" />

declare module "*.mp3" {
const src: string;
export default src;
}
6 changes: 6 additions & 0 deletions src/stores/gameSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export interface GameSettingsStoreState {
hitWarningsEnabled: boolean;
stickWarningsEnabled: boolean;
settingsModalOpen: boolean;
audioEnabled: boolean;
setWarningEnabled: (warningType: WarningType, enabled: boolean) => void;
toggleWarningEnabled: (warningType: WarningType) => void;
getWarningEnabled: (warningType: WarningType) => boolean;
setSettingsModelOpen: (open: boolean) => void;
toggleAudioEnabled: () => void;
}

const nonPersistedKeys: Array<keyof GameSettingsStoreState> = [
Expand All @@ -22,6 +24,7 @@ export const useGameSettingsStore = create<GameSettingsStoreState>()(
hitWarningsEnabled: true,
stickWarningsEnabled: true,
settingsModalOpen: false,
audioEnabled: true,
setWarningEnabled(warningType, enabled) {
switch (warningType) {
case "hit-on-high":
Expand Down Expand Up @@ -59,6 +62,9 @@ export const useGameSettingsStore = create<GameSettingsStoreState>()(
setSettingsModelOpen(open) {
set({ settingsModalOpen: open });
},
toggleAudioEnabled() {
set({ audioEnabled: !get().audioEnabled });
},
}),
{
name: "game-settings",
Expand Down