Add presenter role for game flow control
This commit is contained in:
parent
99977bc8e6
commit
9ef8f7343d
10 changed files with 1412 additions and 17 deletions
|
|
@ -124,6 +124,7 @@ export const useGame = () => {
|
|||
const [firstCorrectPlayerId, setFirstCorrectPlayerId] = useState<string | null>(null);
|
||||
const [hostSecret, setHostSecret] = useState<string | null>(null);
|
||||
const [isReconnecting, setIsReconnecting] = useState(false);
|
||||
const [presenterId, setPresenterId] = useState<string | null>(null);
|
||||
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const syncTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
|
@ -141,6 +142,7 @@ export const useGame = () => {
|
|||
const gameStateRef = useRef<GameState>("LANDING");
|
||||
const firstCorrectPlayerIdRef = useRef<string | null>(null);
|
||||
const currentCorrectShapeRef = useRef<string | null>(null);
|
||||
const presenterIdRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => { timeLeftRef.current = timeLeft; }, [timeLeft]);
|
||||
useEffect(() => { playersRef.current = players; }, [players]);
|
||||
|
|
@ -152,6 +154,7 @@ export const useGame = () => {
|
|||
useEffect(() => { gameStateRef.current = gameState; }, [gameState]);
|
||||
useEffect(() => { firstCorrectPlayerIdRef.current = firstCorrectPlayerId; }, [firstCorrectPlayerId]);
|
||||
useEffect(() => { currentCorrectShapeRef.current = currentCorrectShape; }, [currentCorrectShape]);
|
||||
useEffect(() => { presenterIdRef.current = presenterId; }, [presenterId]);
|
||||
|
||||
const isInitializingFromUrl = useRef(false);
|
||||
|
||||
|
|
@ -817,6 +820,9 @@ export const useGame = () => {
|
|||
updatedPlayers = playersRef.current.map(p => p.id === reconnectedPlayer.id ? { ...p, id: conn.peer } : p);
|
||||
setPlayers(updatedPlayers);
|
||||
assignedName = reconnectedPlayer.name;
|
||||
if (presenterIdRef.current === reconnectedPlayer.id) {
|
||||
setPresenterId(conn.peer);
|
||||
}
|
||||
} else if (!playersRef.current.find(p => p.id === conn.peer)) {
|
||||
const colorIndex = playersRef.current.length % PLAYER_COLORS.length;
|
||||
newPlayer = {
|
||||
|
|
@ -834,6 +840,12 @@ export const useGame = () => {
|
|||
};
|
||||
updatedPlayers = [...playersRef.current, newPlayer];
|
||||
setPlayers(updatedPlayers);
|
||||
|
||||
const realPlayers = updatedPlayers.filter(p => p.id !== 'host');
|
||||
if (!gameConfigRef.current.hostParticipates && realPlayers.length === 1 && !presenterIdRef.current) {
|
||||
setPresenterId(conn.peer);
|
||||
broadcast({ type: 'PRESENTER_CHANGED', payload: { presenterId: conn.peer } });
|
||||
}
|
||||
}
|
||||
|
||||
const currentState = gameStateRef.current;
|
||||
|
|
@ -855,6 +867,7 @@ export const useGame = () => {
|
|||
lastAnswerCorrect: null,
|
||||
selectedShape: null,
|
||||
assignedName,
|
||||
presenterId: presenterIdRef.current,
|
||||
};
|
||||
|
||||
if (currentQuestion) {
|
||||
|
|
@ -933,6 +946,26 @@ export const useGame = () => {
|
|||
endQuestion();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.type === 'ADVANCE') {
|
||||
const { action } = data.payload;
|
||||
if (conn.peer !== presenterIdRef.current) {
|
||||
console.log('[HOST] ADVANCE rejected - not from presenter');
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'START' && gameStateRef.current === 'LOBBY') {
|
||||
startHostGame();
|
||||
} else if (action === 'NEXT') {
|
||||
if (gameStateRef.current === 'REVEAL') {
|
||||
showScoreboard();
|
||||
} else if (gameStateRef.current === 'SCOREBOARD') {
|
||||
nextQuestion();
|
||||
}
|
||||
} else if (action === 'SCOREBOARD' && gameStateRef.current === 'REVEAL') {
|
||||
showScoreboard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -1287,6 +1320,9 @@ export const useGame = () => {
|
|||
setSelectedOption(matchedOption);
|
||||
}
|
||||
}
|
||||
if (payload.presenterId !== undefined) {
|
||||
setPresenterId(payload.presenterId);
|
||||
}
|
||||
|
||||
if (payload.questionText && payload.options && payload.totalQuestions !== undefined) {
|
||||
const questions: Question[] = [];
|
||||
|
|
@ -1406,6 +1442,10 @@ export const useGame = () => {
|
|||
setPlayers(data.payload.players);
|
||||
clearStoredSession();
|
||||
}
|
||||
|
||||
if (data.type === 'PRESENTER_CHANGED') {
|
||||
setPresenterId(data.payload.presenterId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAnswer = (arg: boolean | AnswerOption) => {
|
||||
|
|
@ -1489,10 +1529,21 @@ export const useGame = () => {
|
|||
}
|
||||
}, [gameState, players, role]);
|
||||
|
||||
const setPresenterPlayer = (playerId: string | null) => {
|
||||
if (role !== 'HOST') return;
|
||||
setPresenterId(playerId);
|
||||
broadcast({ type: 'PRESENTER_CHANGED', payload: { presenterId: playerId } });
|
||||
};
|
||||
|
||||
const sendAdvance = (action: 'START' | 'NEXT' | 'SCOREBOARD') => {
|
||||
if (role !== 'CLIENT' || !hostConnectionRef.current) return;
|
||||
hostConnectionRef.current.send({ type: 'ADVANCE', payload: { action } });
|
||||
};
|
||||
|
||||
return {
|
||||
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, lastAnswerCorrect, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak, currentPlayerId, gameConfig,
|
||||
pendingQuizToSave, dismissSavePrompt, sourceQuizId, isReconnecting, currentPlayerName,
|
||||
pendingQuizToSave, dismissSavePrompt, sourceQuizId, isReconnecting, currentPlayerName, presenterId,
|
||||
startQuizGen, startManualCreation, cancelCreation, finalizeManualQuiz, loadSavedQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion, showScoreboard,
|
||||
updateQuizFromEditor, startGameFromEditor, backFromEditor, endGame, attemptReconnect, goHomeFromDisconnected, resumeGame
|
||||
updateQuizFromEditor, startGameFromEditor, backFromEditor, endGame, attemptReconnect, goHomeFromDisconnected, resumeGame, setPresenterPlayer, sendAdvance
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue