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
20 changes: 9 additions & 11 deletions src/commons/svgs/GameReadyButton.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import game_ready_active from "assets/commons/game-ready-active.png";
import game_ready_btn from "assets/commons/game-ready-btn.png";
import game_ready_hover_btn from "assets/commons/game-ready-hover-btn.png";
import { useState } from "react";
import "styles/commons/svgs/GameStartButton.scss";
import game_ready_active from 'assets/commons/game-ready-active.png';
import game_ready_btn from 'assets/commons/game-ready-btn.png';
import game_ready_hover_btn from 'assets/commons/game-ready-hover-btn.png';
import { useState } from 'react';
import 'styles/commons/svgs/GameStartButton.scss';

const GameReadyButton = ({ onClick }) => {
const GameReadyButton = ({ onClick, isReady }) => {
const [isHover, setIsHover] = useState(false);
const [isClicked, setClicked] = useState(false);

const handleClick = (e) => {
setClicked(!isClicked);
onClick?.(e); // 기존 클릭 핸들러 호출
};

Expand All @@ -30,21 +28,21 @@ const GameReadyButton = ({ onClick }) => {
<path
className="svg-main-fill"
d="M344.5 0.5V31.5625C344.5 49.2011 330.201 63.4999 312.562 63.5H0.5V0.5H344.5Z"
fill={isClicked ? "#F28110" : isHover ? "#f0a000" : "#FFC466"}
fill={isReady ? '#F28110' : isHover ? '#f0a000' : '#FFC466'}
stroke="black"
/>
<path
className="svg-sub-fill"
d="M344.493 34.5C344.229 50.3954 331.395 63.229 315.5 63.4932V34.5H344.493Z"
fill={isClicked ? "#7E5615" : "#F28110"}
fill={isReady ? '#7E5615' : '#F28110'}
stroke="black"
/>
</svg>

<span className="label">
<img
src={
isClicked
isReady
? game_ready_active
: isHover
? game_ready_hover_btn
Expand Down
23 changes: 14 additions & 9 deletions src/commons/svgs/GameStartButton.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import game_start_active from "assets/commons/game-start-active.png";
import game_start_btn from "assets/commons/game-start-btn.png";
import game_start_hover_btn from "assets/commons/game-start-hover-btn.png";
import { useState } from "react";
import "styles/commons/svgs/GameStartButton.scss";
import game_start_active from 'assets/commons/game-start-active.png';
import game_start_btn from 'assets/commons/game-start-btn.png';
import game_start_hover_btn from 'assets/commons/game-start-hover-btn.png';
import { useState } from 'react';
import { useToastStore } from 'store/toast';
import 'styles/commons/svgs/GameStartButton.scss';

const GameStartButton = ({ onClick }) => {
const { showToast } = useToastStore.getState();
const [isHover, setIsHover] = useState(false);
const [isClicked, setClicked] = useState(false);

const handleClick = (e) => {
setClicked(!isClicked);
onClick?.(e); // 기존 클릭 핸들러 호출
if (onClick(e)) {
setClicked(!isClicked);
return;
}
showToast('alert', '모든 플레이어가 준비되어야 합니다!');
};

return (
Expand All @@ -30,13 +35,13 @@ const GameStartButton = ({ onClick }) => {
<path
className="svg-main-fill"
d="M344.5 0.5V31.5625C344.5 49.2011 330.201 63.4999 312.562 63.5H0.5V0.5H344.5Z"
fill={isClicked ? "#F28110" : isHover ? "#f0a000" : "#FFC466"}
fill={isClicked ? '#F28110' : isHover ? '#f0a000' : '#FFC466'}
stroke="black"
/>
<path
className="svg-sub-fill"
d="M344.493 34.5C344.229 50.3954 331.395 63.229 315.5 63.4932V34.5H344.493Z"
fill={isClicked ? "#7E5615" : "#F28110"}
fill={isClicked ? '#7E5615' : '#F28110'}
stroke="black"
/>
</svg>
Expand Down
64 changes: 43 additions & 21 deletions src/components/lobby/waiting/WaitingRoom.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import CheckBox from "commons/svgs/CheckBox";
import GameReadyButton from "commons/svgs/GameReadyButton";
import GameStartButton from "commons/svgs/GameStartButton";
import PlayerSlot from "components/lobby/waiting/PlayerSlot";
import useWaitingRoomSocket from "hooks/waiting-room/useWaitingRoomSocket";
import { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { getRoomDetailAPI } from "services/lobby/waiting/waitingRoom";
import "styles/components/lobby/waiting/WaitingRoom.scss";
import { getUserUuid } from "utils/user";
import CheckBox from 'commons/svgs/CheckBox';
import GameReadyButton from 'commons/svgs/GameReadyButton';
import GameStartButton from 'commons/svgs/GameStartButton';
import PlayerSlot from 'components/lobby/waiting/PlayerSlot';
import useWaitingRoomSocket from 'hooks/waiting-room/useWaitingRoomSocket';
import { useEffect, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { getRoomDetailAPI } from 'services/lobby/waiting/waitingRoom';
import { useToastStore } from 'store/toast';
import 'styles/components/lobby/waiting/WaitingRoom.scss';
import { getUserUuid } from 'utils/user';

const WaitingRoom = () => {
const { showToast } = useToastStore.getState();
const navigate = useNavigate();
const location = useLocation();
const userUuid = getUserUuid();

const [roomId, setRoomId] = useState(null); // 방번호
const [roomInfo, setRoomInfo] = useState(null); // 방 전체 정보
const [selectedDifficulty, setSelectedDifficulty] = useState("BASIC"); // 난이도
const [selectedDifficulty, setSelectedDifficulty] = useState(null); // 난이도
const [isReady, setIsReady] = useState(false); // 준비 여부
const [isOwner, setIsOwner] = useState(false);
const ownerIsMe = userUuid === roomInfo?.roomInfo.ownerUuid;
const userInfos = roomInfo?.userInfos || [];

const {
startGame,
Expand All @@ -35,7 +39,7 @@ const WaitingRoom = () => {
// 최초 방정보 갱신
useEffect(() => {
const params = new URLSearchParams(location.search);
const roomIdFromQuery = params.get("roomId");
const roomIdFromQuery = params.get('roomId');

if (roomIdFromQuery) {
setRoomId(roomIdFromQuery);
Expand All @@ -54,13 +58,14 @@ const WaitingRoom = () => {
}
};

const isFirstDifficultyInit = useRef(true);
// 방정보 갱신 시 데이터 분배
useEffect(() => {
if (!roomInfo) return;

// 내 정보 조회
const myState = roomInfo.userInfos.find(
(user) => user.userUuid === userUuid
(user) => user.userUuid === userUuid,
);

const isMeOwner = roomInfo.roomInfo.ownerUuid === userUuid;
Expand All @@ -69,8 +74,20 @@ const WaitingRoom = () => {

// 레디 여부 적용
setIsReady(myState?.ready);

// 최초 난이도 세팅이면 토스트 스킵
if (isFirstDifficultyInit.current) {
isFirstDifficultyInit.current = false;
setSelectedDifficulty(roomInfo.roomInfo.difficulty);
return;
}

if (roomInfo.roomInfo.difficulty === selectedDifficulty) {
return;
}
// 난이도 적용
setSelectedDifficulty(roomInfo.roomInfo.difficulty);
showToast('alert', '로봇 성능이 변경되었습니다!');
}, [roomInfo]);

// 게임 시작
Expand All @@ -83,12 +100,14 @@ const WaitingRoom = () => {
// 방 나가기
const quitRoomHandler = () => {
quitRoom(roomId);
navigate("/lobby");
navigate('/lobby');
};

// 게임 시작 요청
const gameStartHandler = () => {
if (!userInfos.every((user) => user.ready)) return false;
startGame(roomId);
return true;
};

// 게임 준비/취소
Expand All @@ -101,7 +120,10 @@ const WaitingRoom = () => {

// 난이도 변경
const updateDifficultyHandler = (difficulty) => {
if (!isOwner) return;
if (!isOwner) {
showToast('alert', '방장만 로봇 성능을 변경할 수 있습니다!');
return;
}
if (difficulty === selectedDifficulty) return;
setSelectedDifficulty(difficulty);

Expand Down Expand Up @@ -150,24 +172,24 @@ const WaitingRoom = () => {
<div className="checkbox-options">
<CheckBox
label="초보"
checked={selectedDifficulty === "BASIC"}
onChange={() => updateDifficultyHandler("BASIC")}
checked={selectedDifficulty === 'BASIC'}
onChange={() => updateDifficultyHandler('BASIC')}
/>
<CheckBox
label="고수"
checked={selectedDifficulty === "ADVANCED"}
onChange={() => updateDifficultyHandler("ADVANCED")}
checked={selectedDifficulty === 'ADVANCED'}
onChange={() => updateDifficultyHandler('ADVANCED')}
/>
</div>
</div>

{userUuid === roomInfo.roomInfo.ownerUuid ? (
{ownerIsMe ? (
<label className="start-btn-wrapper">
<GameStartButton onClick={gameStartHandler} />
</label>
) : (
<label className="ready-btn-wrapper">
<GameReadyButton onClick={gameReadyHandler} />
<GameReadyButton onClick={gameReadyHandler} isReady={isReady} />
</label>
)}
</div>
Expand Down
40 changes: 20 additions & 20 deletions src/components/scenes/game1/BattleScene.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import leftCloudImg from "assets/components/scenes/commons/left-cloud.png";
import rightCloudImg from "assets/components/scenes/commons/right-cloud.png";
import aiImg from "assets/components/scenes/game1/ai.png";
import userImg from "assets/components/scenes/game1/player.png";
import Timer from "commons/Timer";
import { correctSFX, game1BattleBGM, incorrectSFX } from "constants/audio";
import useAudio from "hooks/audio/useAudio";
import useBattle from "hooks/game/game1/useBattle";
import leftCloudImg from 'assets/components/scenes/commons/left-cloud.png';
import rightCloudImg from 'assets/components/scenes/commons/right-cloud.png';
import aiImg from 'assets/components/scenes/game1/ai.png';
import userImg from 'assets/components/scenes/game1/player.png';
import Timer from 'commons/Timer';
import { correctSFX, game1BattleBGM, incorrectSFX } from 'constants/audio';
import useAudio from 'hooks/audio/useAudio';
import useBattle from 'hooks/game/game1/useBattle';
// import useEffectSound from "hooks/game/game1/useEffectSound";
import { useEffect, useRef, useState } from "react";
import { useToastStore } from "store/toast";
import "styles/components/scenes/game1/BattleScene.scss";
import { isPressEnterKey } from "utils/keyDown";
import { isBlank } from "utils/validation";
import { useEffect, useRef, useState } from 'react';
import { useToastStore } from 'store/toast';
import 'styles/components/scenes/game1/BattleScene.scss';
import { isPressEnterKey } from 'utils/keyDown';
import { isBlank } from 'utils/validation';

const BattleScene = ({ roomId, drawings }) => {
// 배틀 씬 guess 상태 관리 훅 호출
Expand All @@ -29,15 +29,15 @@ const BattleScene = ({ roomId, drawings }) => {
const { showToast } = useToastStore.getState();

const inputRef = useRef(null);
const [inputValue, setInputValue] = useState("");
const [inputValue, setInputValue] = useState('');
const [aiSaying, setAiSaying] = useState(true);

useAudio(game1BattleBGM);
const { playTrack } = useAudio();
// const { playEffect } = useEffectSound();

useEffect(() => {
showToast("gamealert", "AI가 맞출 차례입니다!");
showToast('gamealert', 'AI가 맞출 차례입니다!');
}, []);

useEffect(() => {
Expand All @@ -50,7 +50,7 @@ const BattleScene = ({ roomId, drawings }) => {
return () => clearTimeout(timer);
} else {
// ai 턴으로 이동
setInputValue("");
setInputValue('');
setAiSaying(true);
}
}, [isAiguessTurn]);
Expand All @@ -66,23 +66,23 @@ const BattleScene = ({ roomId, drawings }) => {
if (guessResult === null) return;

if (guessResult) {
showToast("gameO");
showToast('gameO');
playTrack(correctSFX);
// playEffect("correct");
} else {
showToast("gameX");
showToast('gameX');
playTrack(incorrectSFX);
// playEffect("incorrect");
}
}, [guessResult]);

const handleKeyDown = (e) => {
if (isSubmit) return;
if (isPressEnterKey(e)) {
if (isPressEnterKey(e) && e.nativeEvent.isComposing === false) {
e.preventDefault();
if (!isBlank(inputValue)) {
inputRef.current?.blur();
sendGuess(inputValue);
inputRef.current?.blur();
}
}
};
Expand Down
31 changes: 16 additions & 15 deletions src/hooks/game/game1/useBattle.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SOCKET_GAME_API } from "constants/api";
import { useCallback, useEffect, useState } from "react";
import { useGameSocketStore } from "store/socket";
import { useToastStore } from "store/toast";
import { getUserUuid } from "utils/user";
import { SOCKET_GAME_API } from 'constants/api';
import { useCallback, useEffect, useState } from 'react';
import { useGameSocketStore } from 'store/socket';
import { useToastStore } from 'store/toast';
import { getUserUuid } from 'utils/user';

const useBattle = ({ roomId }) => {
const userUuid = getUserUuid();
Expand All @@ -25,28 +25,29 @@ const useBattle = ({ roomId }) => {
(message) => {
const payload = JSON.parse(message.body);
const { eventType: type, data } = payload;
const aiSay = JSON.parse(payload.aiSays)?.message;

switch (type) {
case "GUESS_REQUEST":
case 'GUESS_REQUEST':
setEndTime(data.guessEndTime);
setGuessResult(null);
setIsMyguessTurn(data.guesserUuid === userUuid);
setIsAiguessTurn(data.guesserUuid === "AI");
setIsAiguessTurn(data.guesserUuid === 'AI');
setIsSubmit(false);
break;
case "GUESS_SUBMIT":
case 'GUESS_SUBMIT':
setGuessWord(data.guessWord);
setAiSays(payload.aiSays);
setAiSays(aiSay);
break;
case "GUESS_RESULT":
case 'GUESS_RESULT':
setGuessResult(data.correct);
setAiSays(payload.aiSays);
setAiSays(aiSay);
setGuessWord(null);
break;
default:
break;
}
}
},
);

return () => sub.unsubscribe();
Expand All @@ -56,17 +57,17 @@ const useBattle = ({ roomId }) => {
(guess) => {
if (!isConnected) return;
setIsSubmit(true);
showToast("alert", "제출 완료!");
showToast('alert', '제출 완료!');

stompClient.publish({
destination: `/pub${SOCKET_GAME_API}/${roomId}`,
body: JSON.stringify({
eventType: "GUESS_SUBMIT",
eventType: 'GUESS_SUBMIT',
data: { guessWord: guess },
}),
});
},
[isConnected, stompClient, roomId]
[isConnected, stompClient, roomId],
);

return {
Expand Down
Loading