Skip to content

Commit

Permalink
Che bato/end game screen (#61)
Browse files Browse the repository at this point in the history
## End game screen & Homepage preferences
### GameBoard/page.tsx
- Added check that if lobbyState exists and a game doesn't exist that we
send the user to the lobby.
- Added check if we have winners in the gameState. If we have winners we
permanently open the preference page with the end game screen.

### Preferences/page.tsx
- Added Preferences screen for the homePage it is similar to the
preferences currently it adds the block list and logout.

### Controlhub.tsx
- Added Preferences link

### OpponentCardTray.tsx
- Changed sendManualDisconnectMessage to sendMessage since they do the
same.

### SetUp.tsx (lobby)
- Added the manualDisconnect to the handleExit function since we know a
user wishes to leave the lobby if they click exit.

### PreferencesComponent.tsx
- Redid the preference page so we receive the tabs, openState, title and
subtitle from a parent component.
- Changed the background color so its more in line with the design
- minor design fixes for differentiating between homePage and gameBoard

### BlockListTab.tsx
- Added the BlockListTab to list of components for handling blocking
users.

### Preferences.types.ts
- Added buttonFnc to preference buttons this way we send what the
buttons does from a parent component
- remade the PreferenceProps to support receiving properties from a
parent component

### CardSleevesTab.tsx
- Removed the scroll from cardSleevesTab and moved it to a parent
component (this will make scrolls available in other screens).

### CurrentGameTab.tsx
- Added Concede functionality
- Minor design fix

### EndGameTab.tsx
- Added a new endGameTab which checks what type of game we are in and
returns the appropriate component.

### GameOptionsTab.tsx & KeyboardShortcutsTab.tsx
- Minor design fix

### BlockedUser.tsx
- Added a new component that represents a row in blocked users accepts
username for now.

### EndGameOptionsCustom.tsx
- New EndGameTab component for custom/private games

### EndGameOptionsQuickMatch
- New EndGameTab component for quickMatches
- I used the API call enter-queue to reenter the user into a queue.
After multiple testing scenarios this was by far the most elegant and
stable option.

### PreferenceButton.tsx
- Added buttonFunctionalities
- Added disabled check.

### VerticalTabs.tsx
- Added the scroll for overflow here so its accessible to all subTabs.
- Added the endGame screen tab, BlockedUserTab and Logout tab.

### Game.context.tsx
- Added resetStates which resets the states of GameState and LobbyState.
I found that we need to do this to requeue a user into the queue
otherwise the user will just be sent back into the game.
- Added socketKey as a userEffect dependable since when a user wanted to
requeue we closed the connection with the socket but the user wasn't
able to reconnect to a new socket since there wasn't a dependable
triggered. This way when we change the socketKey for the user in
resetStates we can make sure the user can requeue with a new socket.
- Removed sendManualDisconnectMessage since it was redundant (we can
send the message via sendMessage()).

### User.context.tsx
- Added callbackUrl to logout function. Since if we logout in the
preference page it redirects to the preference page which it shouldn't.
This way we always redirect to the homepage.

### Lobby/page.tsx
- Changed how we check if a game is onGoing we have a new parameter in
the lobbyState that gives us that information. We do this since
otherwise when users want to rejoin the lobby from the match we can't
send gameState as null from the BE hence why we set this parameter to
allow users back into the lobby.

### quickGame/page.tsx
- minor design fixes.

-------------------------------------------------
### new changes
- Refactored the way time was count down when two users matched in the
quickMatch
- Refactored both endgame screens to allow for users to send and receive
rematch requests
- Refactored states so all rematches, requeues work correctly.
- Refactored requeue to use the same socket instead of an API call
- Fixes to certain hooks so we don't get warnings or errors when
requeueing or rematching.

---------

Co-authored-by: Dan Bastin <[email protected]>
  • Loading branch information
CheBato and danbastin authored Jan 19, 2025
1 parent c426582 commit b7ee1e5
Show file tree
Hide file tree
Showing 25 changed files with 809 additions and 128 deletions.
47 changes: 43 additions & 4 deletions src/app/GameBoard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ import BasicPrompt from '../_components/Gameboard/_subcomponents/Overlays/Prompt
import { useGame } from '../_contexts/Game.context';
import { useSidebar } from '../_contexts/Sidebar.context';
import PopupShell from '../_components/_sharedcomponents/Popup/Popup';
import Preferences from '@/app/_components/_sharedcomponents/Preferences/Preferences';
import PreferencesComponent from '@/app/_components/_sharedcomponents/Preferences/PreferencesComponent';
import { useRouter } from 'next/navigation';
export enum MatchType {
Custom = 'Custom',
Private = 'Private',
Quick = 'Quick',
}


const GameBoard = () => {
const { getOpponent, connectedPlayer, gameState } = useGame();
const { getOpponent, connectedPlayer, gameState, lobbyState } = useGame();
const router = useRouter();
const { sidebarOpen, toggleSidebar } = useSidebar();
const [chatMessage, setChatMessage] = useState('');
const [chatHistory, setChatHistory] = useState<string[]>([]);
Expand All @@ -40,7 +48,18 @@ const GameBoard = () => {
if (drawerRef.current) {
setDrawerWidth(drawerRef.current.offsetWidth);
}
}, [sidebarOpen]);
if(lobbyState && !lobbyState.gameOngoing && lobbyState.gameType !== MatchType.Quick) {
router.push('/lobby');
}
}, [sidebarOpen, gameState, lobbyState, router]);

useEffect(() => {
if (gameState?.winner) {
setPreferenceOpen(true);
}else{
setPreferenceOpen(false);
}
}, [gameState?.winner]);

const handleModalToggle = () => {
setIsModalOpen(!isModalOpen);
Expand All @@ -53,6 +72,20 @@ const GameBoard = () => {
setPreferenceOpen(!isPreferenceOpen);
}

// Ensure that essential state values are defined before rendering.
if (!gameState && !connectedPlayer && !lobbyState) {
return null;
}

// check if game ended already.
const winners = gameState?.winner ? gameState.winner : undefined;
// const winners = ['order66']
// we set tabs
const preferenceTabs = winners
? ['endGame','keyboardShortcuts','cardSleeves','gameOptions']
:['currentGame','keyboardShortcuts','cardSleeves','gameOptions']


// ----------------------Styles-----------------------------//

const styles = {
Expand Down Expand Up @@ -129,7 +162,13 @@ const GameBoard = () => {
handleBasicPromptToggle={handleBasicPromptToggle}
/>
<PopupShell/>
<Preferences isPreferenceOpen={isPreferenceOpen} preferenceToggle={handlePreferenceToggle}/>
<PreferencesComponent
isPreferenceOpen={isPreferenceOpen}
preferenceToggle={handlePreferenceToggle}
tabs={preferenceTabs}
title={winners ? 'Game ended' : 'PREFERENCES'}
subtitle={winners ? winners.length > 1 ? 'Game ended in a draw' : `Winner is ${winners[0]}` : undefined}
/>
</Grid>
);
};
Expand Down
60 changes: 60 additions & 0 deletions src/app/Preferences/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client';
import { Box, Typography } from '@mui/material';
import PreferencesComponent from '@/app/_components/_sharedcomponents/Preferences/PreferencesComponent';
import React from 'react';
import { s3ImageURL } from '@/app/_utils/s3Utils';
import { useRouter } from 'next/navigation'

const Preferences: React.FC = () => {
const router = useRouter();
const handleExit = () => {
router.push('/');
}

// ----------------------Styles-----------------------------//
const styles = {
lobbyTextStyle:{
ml:'30px',
fontSize: '3.0em',
fontWeight: '600',
color: 'white',
alignSelf: 'flex-start',
mb: '0px',
cursor: 'pointer',
},
mainContainer:{
height: '100vh',
overflow: 'hidden',
backgroundImage: `url(${s3ImageURL('ui/board-background-1.webp')})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
display:'grid',
},
disclaimer: {
position: 'absolute',
bottom: 0,
width: '100%',
padding: '1rem',
textAlign: 'center',
fontSize: '0.90rem',
}
};

return (
<Box sx={styles.mainContainer}>
<Typography sx={styles.lobbyTextStyle} onClick={handleExit}>KARABAST</Typography>
<PreferencesComponent
isPreferenceOpen={true}
tabs={['keyboardShortcuts','cardSleeves','gameOptions','blockList']}
variant={'homePage'}
/>
<Typography variant="body1" sx={styles.disclaimer}>
Karabast is in no way affiliated with Disney or Fantasy Flight Games.
Star Wars characters, cards, logos, and art are property of Disney
and/or Fantasy Flight Games.
</Typography>
</Box>
);
};

export default Preferences;
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { useRouter } from 'next/navigation';
import { s3CardImageURL } from '@/app/_utils/s3Utils';

const OpponentCardTray: React.FC<IOpponentCardTrayProps> = ({ trayPlayer, preferenceToggle }) => {
const { gameState, connectedPlayer, getOpponent, sendManualDisconnectMessage } = useGame();
const { gameState, connectedPlayer, getOpponent, sendMessage } = useGame();
const router = useRouter();
const handleExitButton = () =>{
sendManualDisconnectMessage();
sendMessage('manualDisconnect');
router.push('/');
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/_components/Lobby/Deck/Deck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const Deck: React.FC = () => {
<Box sx={mainContainerStyle}>
{newDeck.map((card:IServerCardData) => (
<GameCard
key={card.card.id}
key={card.card.uuid}
card={card}
variant={'lobby'}
onClick={() => sendLobbyMessage(['updateDeck','Deck', card.card.id])}
Expand Down
3 changes: 2 additions & 1 deletion src/app/_components/Lobby/SetUp/SetUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { ILobbyUserProps } from '@/app/_components/Lobby/LobbyTypes';
const SetUp: React.FC = ({
}) => {
const router = useRouter();
const { lobbyState, sendLobbyMessage, connectedPlayer } = useGame();
const { lobbyState, sendLobbyMessage, connectedPlayer, sendMessage } = useGame();

// find the user
const connectedUser = lobbyState ? lobbyState.users.find((u: ILobbyUserProps) => u.id === connectedPlayer) : null;
Expand Down Expand Up @@ -77,6 +77,7 @@ const SetUp: React.FC = ({
height: '100%',
};
const handleExit = () => {
sendMessage('manualDisconnect');
router.push('/');
}

Expand Down
17 changes: 12 additions & 5 deletions src/app/_components/QuickGame/FoundGame/FoundGame.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { Box, Card, Typography } from '@mui/material';
import LeaderBaseCard from '@/app/_components/_sharedcomponents/Cards/LeaderBaseCard';
import { useGame } from '@/app/_contexts/Game.context';
Expand All @@ -25,22 +25,29 @@ const FoundGame: React.FC = () => {
// --- Countdown State ---
const [countdown, setCountdown] = useState(5);
const [gameStartSent, setGameStart] = useState(false);
const timerRef = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
const timer = setInterval(() => {
timerRef.current = setInterval(() => {
setCountdown((prev) => Math.max(prev - 1, 0));
}, 1000);

return () => clearInterval(timer);
// Cleanup the timer when the component unmounts
return () => {
if (timerRef.current) {
clearInterval(timerRef.current);
}
};
}, []);

useEffect(() => {
if (countdown !== 0) return;
if(gameState){

if (gameState) {
router.push('/GameBoard');
}

// Only run once
// Only run once—if the connected user is the lobby owner and game start hasn't been sent yet
if (connectedUser?.id === lobbyOwner && !gameStartSent) {
setGameStart(true);
sendLobbyMessage(['onStartGame']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ const ControlHub: React.FC<IControlHubProps> = ({
<NextLinkMui href="/profile" sx={styles.profileLink}>
Profile
</NextLinkMui>
<NextLinkMui href="/Preferences" sx={styles.profileLink}>
Preferences
</NextLinkMui>
<Divider
orientation="vertical"
flexItem
Expand Down
61 changes: 0 additions & 61 deletions src/app/_components/_sharedcomponents/Preferences/Preferences.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export type IButtonType = {
variant: 'concede' | 'standard',
text: string
text: string,
buttonFnc?: () => void
disabled?: boolean
}

export type ISleeve = {
Expand All @@ -15,10 +17,19 @@ export type IPreferenceOptions = {
}

export interface IVerticalTabsProps {
tabs: string[];
tabs: string[]
variant?: 'gameBoard' | 'homePage'
}

export type IBlockedUser = {
username: string,
}

export interface IPreferenceProps {
isPreferenceOpen: boolean,
preferenceToggle: () => void,
tabs: string[],
preferenceToggle?: () => void,
variant?: 'gameBoard' | 'homePage'
title?: string,
subtitle?: string,
}
Loading

0 comments on commit b7ee1e5

Please sign in to comment.