diff --git a/client/src/components/ChatMessage.tsx b/client/src/components/ChatMessage.tsx index c05f5462b..6583f58f4 100644 --- a/client/src/components/ChatMessage.tsx +++ b/client/src/components/ChatMessage.tsx @@ -399,7 +399,7 @@ export function translateChatMessage( ); } case "playerQuit": - return translate("chatMessage.playerQuit", + return translate(`chatMessage.playerQuit${message.gameOver ? ".gameOver" : ""}`, playerNames[message.playerIndex] ); case "youDied": @@ -834,6 +834,7 @@ export type ChatMessageVariant = { } | { type: "playerQuit", playerIndex: PlayerIndex + gameOver: boolean, } | { type: "phaseChange", phase: PhaseState, diff --git a/client/src/components/Select.tsx b/client/src/components/Select.tsx index 5dac56b29..171989b7a 100644 --- a/client/src/components/Select.tsx +++ b/client/src/components/Select.tsx @@ -176,6 +176,52 @@ export function dropdownPlacementFunction(dropdownElement: HTMLElement, buttonEl dropdownElement.style.top = `${spaceAbove + buttonBounds.height + .25 * oneRem}px`; dropdownElement.style.bottom = `unset`; } + + keepPopoverOnScreen(dropdownElement, buttonElement); +} + +function keepPopoverOnScreen(dropdownElement: HTMLElement, buttonElement?: HTMLElement) { + const dropdownBounds = dropdownElement.getBoundingClientRect(); + + const modifyTop = dropdownElement.style.bottom === 'unset' || dropdownElement.style.bottom === ""; + const modifyLeft = dropdownElement.style.right === 'unset' || dropdownElement.style.right === ""; + + const spaceAbove = dropdownBounds.top; + const spaceBelow = window.innerHeight - dropdownBounds.bottom; + const spaceToTheRight = window.innerWidth - dropdownBounds.right; + const spaceToTheLeft = dropdownBounds.left; + + if (spaceToTheRight < 0) { + if (modifyLeft) { + dropdownElement.style.left = `${window.innerWidth - dropdownBounds.width}px` + } else { + dropdownElement.style.right = "0px" + } + } + + if (spaceToTheLeft < 0) { + if (modifyLeft) { + dropdownElement.style.left = "0px" + } else { + dropdownElement.style.right = `${dropdownBounds.width}px` + } + } + + if (spaceBelow < 0) { + if (modifyTop) { + dropdownElement.style.top = `${window.innerHeight - dropdownBounds.height}px` + } else { + dropdownElement.style.bottom = "0px" + } + } + + if (spaceAbove < 0) { + if (modifyTop) { + dropdownElement.style.top = "0px" + } else { + dropdownElement.style.bottom = `${dropdownBounds.height}px` + } + } } function SelectOptions(props: Readonly<{ diff --git a/client/src/components/gameModeSettings/GameModeSelector.tsx b/client/src/components/gameModeSettings/GameModeSelector.tsx index c86a45cf5..8b5601c34 100644 --- a/client/src/components/gameModeSettings/GameModeSelector.tsx +++ b/client/src/components/gameModeSettings/GameModeSelector.tsx @@ -97,7 +97,7 @@ function GameModeSelectorPanel(props: { useEffect(() => { const listener = (e: KeyboardEvent) => { - if (e.ctrlKey && e.key === 's') { + if (props.canModifySavedGameModes === true && e.ctrlKey && e.key === 's') { e.preventDefault(); const result = saveGameMode(gameModeNameField); @@ -112,7 +112,7 @@ function GameModeSelectorPanel(props: { } document.addEventListener('keydown', listener); return () => document.removeEventListener('keydown', listener); - }, [gameModeNameField, anchorController, saveGameMode]); + }, [gameModeNameField, anchorController, saveGameMode, props.canModifySavedGameModes]); // Caller must ensure location is valid const loadGameMode = (location: GameModeLocation) => { diff --git a/client/src/game/gameState.d.tsx b/client/src/game/gameState.d.tsx index 73e5330d7..131d41d82 100644 --- a/client/src/game/gameState.d.tsx +++ b/client/src/game/gameState.d.tsx @@ -52,9 +52,11 @@ export type PlayerClientType = { } type GameState = { - stateType: "game" + stateType: "game", roomCode: number, lobbyName: string, + + initialized: boolean, myId: number | null, diff --git a/client/src/game/gameState.tsx b/client/src/game/gameState.tsx index 399516938..8513afa41 100644 --- a/client/src/game/gameState.tsx +++ b/client/src/game/gameState.tsx @@ -40,6 +40,8 @@ export function createGameState(): GameState { roomCode: 0, lobbyName: "", + initialized: false, + myId: null, chatMessages : [], diff --git a/client/src/game/messageListener.tsx b/client/src/game/messageListener.tsx index 04070aa3f..4ed0ab425 100644 --- a/client/src/game/messageListener.tsx +++ b/client/src/game/messageListener.tsx @@ -237,6 +237,7 @@ export default function messageListener(packet: ToClientPacket){ case "gameInitializationComplete": if (GAME_MANAGER.state.stateType === "game") { const isSpectator = GAME_MANAGER.state.clientState.type === "spectator"; + GAME_MANAGER.state.initialized = true; if(isSpectator){ ANCHOR_CONTROLLER?.setContent(); }else{ @@ -484,10 +485,12 @@ export default function messageListener(packet: ToClientPacket){ } } - for(let chatMessage of packet.chatMessages){ - let audioSrc = chatMessageToAudio(chatMessage); - if(audioSrc) - AudioController.queueFile(audioSrc); + if (GAME_MANAGER.state.stateType !== "game" || GAME_MANAGER.state.initialized === true) { + for(let chatMessage of packet.chatMessages){ + let audioSrc = chatMessageToAudio(chatMessage); + if(audioSrc) + AudioController.queueFile(audioSrc); + } } } break; diff --git a/client/src/menu/game/gameScreenContent/ChatMenu.tsx b/client/src/menu/game/gameScreenContent/ChatMenu.tsx index bd77c3aec..638d7cd04 100644 --- a/client/src/menu/game/gameScreenContent/ChatMenu.tsx +++ b/client/src/menu/game/gameScreenContent/ChatMenu.tsx @@ -219,7 +219,7 @@ export function ChatTextInput(props: Readonly<{ const playerStrings = useLobbyOrGameState( state => { if (state.stateType === "game") { - return state.players.map(player => player.name) + return state.players.map(player => player.toString()) } else if (state.stateType === "lobby") { return Array.from(state.players.values()) .filter(player => player.clientType.type === "player") diff --git a/client/src/resources/lang/en_us.json b/client/src/resources/lang/en_us.json index 5e35435c0..fa8fd510b 100644 --- a/client/src/resources/lang/en_us.json +++ b/client/src/resources/lang/en_us.json @@ -876,7 +876,8 @@ "chatMessage.gameOver.player.crumb": "\\0 \\1", "chatMessage.playerWon":"\\0 won! They were the \\1.", "chatMessage.playerLost":"\\0 lost! They were the \\1.", - "chatMessage.playerQuit": "\\0 has quit the game and will certainly die at the end of the next night.", + "chatMessage.playerQuit": "\\0 has left the lobby and will certainly die at the end of the next night.", + "chatMessage.playerQuit.gameOver": "\\0 has left the lobby.", "chatMessage.trialInformation":"\\0 votes are needed for a trial. There are \\1 trials left today.", "chatMessage.voted":"\\0 voted for \\1.", diff --git a/server/src/game/chat/chat_message_variant.rs b/server/src/game/chat/chat_message_variant.rs index eff117618..4738d7f4b 100644 --- a/server/src/game/chat/chat_message_variant.rs +++ b/server/src/game/chat/chat_message_variant.rs @@ -56,7 +56,7 @@ pub enum ChatMessageVariant { #[serde(rename_all = "camelCase")] GameOver { synopsis: Synopsis }, #[serde(rename_all = "camelCase")] - PlayerQuit{player_index: PlayerIndex}, + PlayerQuit{player_index: PlayerIndex, game_over: bool}, diff --git a/server/src/game/player/player_send_packet.rs b/server/src/game/player/player_send_packet.rs index b528ad719..240922f1f 100644 --- a/server/src/game/player/player_send_packet.rs +++ b/server/src/game/player/player_send_packet.rs @@ -26,7 +26,7 @@ impl PlayerReference{ if self.alive(game) { game.add_message_to_chat_group( crate::game::chat::ChatGroup::All, - ChatMessageVariant::PlayerQuit{player_index: self.index()} + ChatMessageVariant::PlayerQuit{player_index: self.index(), game_over: game.game_is_over()} ); } }