diff --git a/public/audio/mixkit-modern-technology-select-3124.wav b/public/audio/accept-mission-click-2.wav similarity index 100% rename from public/audio/mixkit-modern-technology-select-3124.wav rename to public/audio/accept-mission-click-2.wav diff --git a/public/audio/accept-mission-click.mp3 b/public/audio/accept-mission-click.mp3 new file mode 100644 index 0000000..ab731c3 Binary files /dev/null and b/public/audio/accept-mission-click.mp3 differ diff --git a/public/audio/click1.mp3 b/public/audio/click1.mp3 deleted file mode 100644 index 592613b..0000000 Binary files a/public/audio/click1.mp3 and /dev/null differ diff --git a/public/audio/mixkit-sci-fi-click-900.wav b/public/audio/deploy-stratagem-click.wav similarity index 100% rename from public/audio/mixkit-sci-fi-click-900.wav rename to public/audio/deploy-stratagem-click.wav diff --git a/public/audio/click2.mp3 b/public/audio/play-recording-click.mp3 similarity index 100% rename from public/audio/click2.mp3 rename to public/audio/play-recording-click.mp3 diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index c23630e..04f4350 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,3 +1,5 @@ +"use client" + import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { X } from "lucide-react" @@ -19,7 +21,7 @@ const DialogOverlay = React.forwardRef< void }) => { const [displayedText, setDisplayedText] = useState(''); + const intervalRef = useRef(); useEffect(() => { - let index = 0; - const timer = setInterval(() => { - if (index < text.length) { - setDisplayedText((prev) => prev + text[index]); - index++; + // Reset displayed text when text prop changes + setDisplayedText(''); + + const characters = text.split(''); + let currentIndex = 0; + + // Clear any existing interval + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + intervalRef.current = setInterval(() => { + if (currentIndex < characters.length) { + setDisplayedText(prev => prev + characters[currentIndex]); + currentIndex++; } else { - clearInterval(timer); + if (intervalRef.current) { + clearInterval(intervalRef.current); + } onComplete?.(); } }, 30); - return () => clearInterval(timer); + // Cleanup function + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }; }, [text, onComplete]); - return {displayedText}; + // Only render the text, nothing else + return displayedText; }; const BriefingAudio = ({ @@ -523,7 +549,7 @@ const BriefingAudio = ({ if (!expertAudio) return null; const togglePlay = () => { - playClickSound(); + playRecordingSound(); if (audioRef.current) { if (isPlaying) { audioRef.current.pause(); @@ -535,7 +561,7 @@ const BriefingAudio = ({ }; const toggleMute = () => { - playClickSound(); + playRecordingSound(); if (isMuted) { setVolume(prevVolume.current); setIsMuted(false); @@ -624,9 +650,10 @@ const Index = () => { const [loadingProgress, setLoadingProgress] = useState(0); const audioRef = useRef(null); const [showingInitialTransition, setShowingInitialTransition] = useState(false); + const [showIntroDialog, setShowIntroDialog] = useState(true); const handleStartGame = () => { - playClickSound(); + playAcceptMissionSound(); setShowingInitialTransition(true); }; @@ -640,7 +667,7 @@ const Index = () => { }; const handleChoice = async (choice: GameStage["choices"][0]) => { - playClickSound(); + playDeployStratagemSound(); if (audioRef.current) { audioRef.current.pause(); audioRef.current = null; @@ -687,8 +714,17 @@ const Index = () => { }; const handleContinue = () => { - playClickSound(); + playDeployStratagemSound(); setShowingResult(false); + + // Check if this was the last stage + if (currentStage >= stages.length - 1) { + // Move to completion screen + setCurrentStage(stages.length); + return; + } + + // Otherwise, continue to next stage setShowingMonthTransition(true); setNextStage(currentStage + 1); }; @@ -707,7 +743,6 @@ const Index = () => { + + + + ); + if (!gameStarted) { if (showingInitialTransition) { return ( @@ -779,12 +891,6 @@ const Index = () => {
- {
+ diff --git a/src/utils/audio.ts b/src/utils/audio.ts index b64eb3c..82bf309 100644 --- a/src/utils/audio.ts +++ b/src/utils/audio.ts @@ -1,37 +1,35 @@ -const CLICK_SOUNDS = [ - "/audio/click1.mp3", - "/audio/click2.mp3" -]; +const audioCache: { [key: string]: HTMLAudioElement } = {}; -class AudioPlayer { - private static instance: AudioPlayer; - private audioElements: { [key: string]: HTMLAudioElement } = {}; - - private constructor() { - // Pre-load click sounds - CLICK_SOUNDS.forEach((sound, index) => { - const audio = new Audio(sound); - audio.volume = 0.3; // Lower volume for UI sounds - this.audioElements[`click${index + 1}`] = audio; - }); - } - - public static getInstance(): AudioPlayer { - if (!AudioPlayer.instance) { - AudioPlayer.instance = new AudioPlayer(); - } - return AudioPlayer.instance; - } - - public playClickSound() { - const randomIndex = Math.floor(Math.random() * CLICK_SOUNDS.length); - const sound = this.audioElements[`click${randomIndex + 1}`]; - if (sound) { - // Create a clone to allow overlapping sounds - const clone = sound.cloneNode() as HTMLAudioElement; - clone.play(); - } +function createAudio(src: string): HTMLAudioElement { + if (audioCache[src]) { + return audioCache[src]; } + const audio = new Audio(src); + audioCache[src] = audio; + return audio; } -export const playClickSound = () => AudioPlayer.getInstance().playClickSound(); \ No newline at end of file +export function playSound(src: string, volume = 0.75) { + const audio = createAudio(src); + audio.volume = volume; + audio.currentTime = 0; + audio.play().catch(err => console.error('Audio playback failed:', err)); +} + +// Dedicated sound functions +export function playAcceptMissionSound() { + playSound('/audio/accept-mission-click.mp3'); +} + +export function playDeployStratagemSound() { + playSound('/audio/deploy-stratagem-click.wav'); +} + +export function playRecordingSound() { + playSound('/audio/play-recording-click.mp3'); +} + +// Generic click sound (keep existing functionality) +export function playClickSound() { + playDeployStratagemSound(); +} \ No newline at end of file