Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement game logic and UI #203

Merged
merged 5 commits into from
Dec 4, 2024
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
7 changes: 6 additions & 1 deletion client/src/components/modal/RoundEndModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import gameWin from '@/assets/sounds/game-win.mp3';
import { Modal } from '@/components/ui/Modal';
import { useModal } from '@/hooks/useModal';
import { useGameSocketStore } from '@/stores/socket/gameSocket.store';
import { useTimerStore } from '@/stores/timer.store';
import { cn } from '@/utils/cn';
import { SOUND_IDS, SoundManager } from '@/utils/soundManager';

Expand All @@ -16,6 +17,7 @@ const RoundEndModal = () => {
const roundWinners = useGameSocketStore((state) => state.roundWinners);
const players = useGameSocketStore((state) => state.players);
const currentPlayerId = useGameSocketStore((state) => state.currentPlayerId);
const timer = useTimerStore((state) => state.timers.ENDING);

const { isModalOpened, openModal, closeModal } = useModal();
const [showAnimation, setShowAnimation] = useState(false);
Expand Down Expand Up @@ -100,7 +102,10 @@ const RoundEndModal = () => {
isModalOpened={isModalOpened}
className="max-w-[26.875rem] sm:max-w-[61.75rem]"
>
<div className="flex min-h-[12rem] items-center justify-center sm:min-h-[15.75rem]">
<div className="relative flex min-h-[12rem] items-center justify-center sm:min-h-[15.75rem]">
<span className="absolute right-2 top-2 flex h-8 w-8 items-center justify-center rounded-full border-2 border-violet-300 text-base text-violet-300">
{timer}
</span>
<p className="text-center text-2xl sm:m-2 sm:text-3xl">
{isDevilWin ? (
<> 정닡을 맞좘 ꡬ경꾼이 μ—†μŠ΅λ‹ˆλ‹€</>
Expand Down
29 changes: 22 additions & 7 deletions client/src/components/quiz/QuizStage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { PlayerRole } from '@troublepainter/core';
import { PlayerRole, RoomStatus } from '@troublepainter/core';
import { GameCanvas } from '../canvas/GameCanvas';
import { QuizTitle } from '../ui/QuizTitle';
import sizzlingTimer from '@/assets/big-timer.gif';
Expand Down Expand Up @@ -46,20 +46,35 @@ const QuizStageContainer = () => {
return 0;
}
}, [room?.status, timers, roomSettings?.drawTime]);

const quizTitleText = useMemo(() => {
if (room.status === RoomStatus.DRAWING) {
return roundAssignedRole !== PlayerRole.GUESSER ? `${room.currentWord}` : '';
}
if (room.status === RoomStatus.GUESSING) {
return roundAssignedRole !== PlayerRole.GUESSER ? `${room.currentWord} (λ§žνžˆλŠ”μ€‘...)` : 'λ§žν˜€λ³΄μ„Έμš”-!';
}
}, [room.status, room.currentWord, roundAssignedRole]);

return (
<>
{/* ꡬ경꾼 μ „μš© 타이머 */}
<div className={cn('relative', shouldHideSizzlingTimer && 'hidden')}>
<img src={sizzlingTimer} alt="ꡬ경꾼 μ „μš© 타이머" width={450} />
<span className="absolute left-[42%] top-[45%] text-6xl text-stroke-md lg:text-7xl">
{timers.DRAWING ?? roomSettings.drawTime - 5}
</span>
<div className={cn(shouldHideSizzlingTimer && 'hidden')}>
<p className="mb-3 text-center text-xl text-eastbay-50 text-stroke-md sm:mb-0 sm:text-2xl lg:text-3xl">
화가듀이 μ‹€λ ₯을 λ½λ‚΄λŠ”μ€‘...
</p>
<div className="relative">
<img src={sizzlingTimer} alt="ꡬ경꾼 μ „μš© 타이머" width={450} />
<span className="absolute left-[42%] top-[45%] text-6xl text-stroke-md lg:text-7xl">
{timers.DRAWING ?? roomSettings.drawTime - 5}
</span>
</div>
</div>

<QuizTitle
currentRound={room.currentRound}
totalRound={roomSettings.totalRounds}
title={room?.currentWord || (roundAssignedRole === PlayerRole.GUESSER ? 'λ§žμΆ°λ³΄μ„Έμš©-!' : '')}
title={quizTitleText || 'μ œμ‹œμ–΄κ°€ μ—†μŠ΅λ‹ˆλ‹€.'}
remainingTime={remainingTime || 0}
isHidden={shouldHideQuizTitle}
/>
Expand Down
31 changes: 15 additions & 16 deletions client/src/components/setting/Setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface RoomSettingItem {
}

export const ROOM_SETTINGS: RoomSettingItem[] = [
{ label: 'λΌμš΄λ“œ 수', key: 'totalRounds', options: [3, 5], shortcutKey: 'DROPDOWN_TOTAL_ROUNDS' },
{ label: 'λΌμš΄λ“œ 수', key: 'totalRounds', options: [3, 5, 7, 9, 11], shortcutKey: 'DROPDOWN_TOTAL_ROUNDS' },
{ label: 'μ΅œλŒ€ ν”Œλ ˆμ΄μ–΄ 수', key: 'maxPlayers', options: [4, 5], shortcutKey: 'DROPDOWN_MAX_PLAYERS' },
{ label: 'μ œν•œ μ‹œκ°„', key: 'drawTime', options: [15, 20, 25, 30], shortcutKey: 'DROPDOWN_DRAW_TIME' },
//{ label: 'ν”½μ…€ 수', key: 'maxPixels', options: [300, 500] },
Expand All @@ -41,21 +41,20 @@ const Setting = memo(({ className, ...props }: HTMLAttributes<HTMLDivElement>) =
setSelectedValues(roomSettings);
}, [roomSettings]);

useEffect(() => {
if (!isHost || !selectedValues || !selectedValues.drawTime) return;
// λ°©μž₯일 λ•Œλ§Œ μ‹€ν–‰λ˜λŠ” μ„€μ • μ—…λ°μ΄νŠΈ
void gameSocketHandlers.updateSettings({
settings: { ...selectedValues, drawTime: selectedValues.drawTime + 5 },
});
actions.updateRoomSettings(selectedValues);
}, [selectedValues, isHost]);

const handleSettingChange = useCallback((key: keyof RoomSettings, value: string) => {
setSelectedValues((prev) => ({
...prev,
[key]: Number(value),
}));
}, []);
const handleSettingChange = useCallback(
(key: keyof RoomSettings, value: string) => {
const newSettings = {
...selectedValues,
[key]: Number(value),
};
setSelectedValues(newSettings);
void gameSocketHandlers.updateSettings({
settings: { ...newSettings, drawTime: newSettings.drawTime + 5 },
});
actions.updateRoomSettings(newSettings);
},
[selectedValues, actions],
);

return (
<section
Expand Down
2 changes: 1 addition & 1 deletion client/src/layouts/GameLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const GameLayout = () => {
<BrowserNavigationGuard />
<NavigationModal />
<div
className={`before:bg-patternImg relative flex min-h-screen flex-col justify-start bg-gradient-to-b from-violet-950 via-violet-800 to-fuchsia-800 before:absolute before:left-0 before:top-0 before:h-full before:w-full before:bg-cover before:bg-center lg:py-5`}
className={`relative flex min-h-screen flex-col justify-start bg-gradient-to-b from-violet-950 via-violet-800 to-fuchsia-800 before:absolute before:left-0 before:top-0 before:h-full before:w-full before:bg-patternImg before:bg-cover before:bg-center lg:py-5`}
>
{/* 상단 헀더 */}
<GameHeader />
Expand Down
3 changes: 2 additions & 1 deletion client/src/pages/ResultPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ const ResultPage = () => {
terminateType === TerminationType.PLAYER_DISCONNECT
? 'λ‚˜κ°„ ν”Œλ ˆμ΄μ–΄κ°€ μžˆμ–΄μš”. 20초 ν›„ λŒ€κΈ°μ‹€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€!'
: '20초 ν›„ λŒ€κΈ°μ‹€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€!';
const variant = terminateType === TerminationType.PLAYER_DISCONNECT ? 'warning' : 'success';

toastActions.addToast({
title: 'κ²Œμž„ μ’…λ£Œ',
description,
variant: 'success',
variant,
duration: 20000,
});
}, [terminateType, toastActions]);
Expand Down
Loading