Skip to content

Commit

Permalink
Get rid of weird stateType-specific functions on gameManager
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Papel committed Jan 8, 2025
1 parent 9f2e033 commit 76f5055
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 154 deletions.
85 changes: 49 additions & 36 deletions client/src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import DOMPurify from "dompurify";
import GraveComponent from "./grave";
import { RoleOutline, translateRoleOutline } from "../game/roleListState.d";
import { CopyButton } from "./ClipboardButtons";
import { useGameState, useLobbyOrGameState, usePlayerState } from "./useHooks";
import { useGameState, useLobbyOrGameState, usePlayerNames, usePlayerState, useSpectator } from "./useHooks";
import { KiraResult, KiraResultDisplay } from "../menu/game/gameScreenContent/AbilityMenu/AbilitySelectionTypes/KiraSelectionMenu";
import { AuditorResult } from "../menu/game/gameScreenContent/AbilityMenu/RoleSpecificMenus/AuditorMenu";
import { ControllerID, AbilitySelection, translateControllerID, controllerIdToLink } from "../game/abilityInput";
Expand All @@ -37,7 +37,8 @@ const ChatElement = React.memo((
const [mouseHovering, setMouseHovering] = React.useState(false);

const message = props.message;
const playerNames = props.playerNames ?? GAME_MANAGER.getPlayerNames();
const realPlayerNames = usePlayerNames();
const playerNames = props.playerNames ?? realPlayerNames;
const chatMessageStyles = require("../resources/styling/chatMessage.json");
if(message.variant === undefined){
console.error("ChatElement message with undefined variant:");
Expand Down Expand Up @@ -132,33 +133,13 @@ const ChatElement = React.memo((
/>
</div>
case "playerDied":

let graveRoleString: string;
switch (message.variant.grave.information.type) {
case "obscured":
graveRoleString = translate("obscured");
break;
case "normal":
graveRoleString = translate("role."+message.variant.grave.information.role+".name");
break;
}

return <div className={"chat-message-div"}>
<DetailsSummary
summary={
<StyledText className={"chat-message " + style}
playerKeywordData={props.playerKeywordData}
>
{(chatGroupIcon??"")} {translate("chatMessage.playerDied",
playerNames[message.variant.grave.player], graveRoleString
)}
</StyledText>
}
defaultOpen={GAME_MANAGER.getMySpectator()}
>
<GraveComponent grave={message.variant.grave} playerNames={playerNames}/>
</DetailsSummary>
</div>;
return <PlayerDiedChatMessage
playerKeywordData={props.playerKeywordData}
style={style}
chatGroupIcon={chatGroupIcon}
playerNames={playerNames}
message={message as any}
/>
}

return <div
Expand All @@ -179,6 +160,43 @@ const ChatElement = React.memo((
</div>;
});

function PlayerDiedChatMessage(props: Readonly<{
playerKeywordData?: KeywordDataMap,
style: string,
chatGroupIcon: string | null,
playerNames: string[],
message: ChatMessage & { variant: { type: "playerDied" } }
}>): ReactElement {
let graveRoleString: string;
switch (props.message.variant.grave.information.type) {
case "obscured":
graveRoleString = translate("obscured");
break;
case "normal":
graveRoleString = translate("role."+props.message.variant.grave.information.role+".name");
break;
}

const spectator = useSpectator();

return <div className={"chat-message-div"}>
<DetailsSummary
summary={
<StyledText className={"chat-message " + props.style}
playerKeywordData={props.playerKeywordData}
>
{(props.chatGroupIcon ?? "")} {translate("chatMessage.playerDied",
props.playerNames[props.message.variant.grave.player], graveRoleString
)}
</StyledText>
}
defaultOpen={spectator}
>
<GraveComponent grave={props.message.variant.grave} playerNames={props.playerNames}/>
</DetailsSummary>
</div>;
}

function LobbyChatMessage(props: Readonly<{
message: ChatMessage & { variant: { type: "lobbyMessage" } }
playerNames: string[],
Expand Down Expand Up @@ -326,14 +344,9 @@ export function sanitizePlayerMessage(text: string): string {

export function translateChatMessage(
message: ChatMessageVariant,
playerNames?: string[],
playerNames: string[],
roleList?: RoleOutline[]
): string {

if (playerNames === undefined) {
playerNames = GAME_MANAGER.getPlayerNames();
}

switch (message.type) {
case "lobbyMessage":
return sanitizePlayerMessage(replaceMentions(message.text, playerNames));
Expand Down Expand Up @@ -522,7 +535,7 @@ export function translateChatMessage(
out = translate("chatMessage.abilityUsed.selection.twoRoleOutlineOption", first, second);
break;
case "string":
out = translate("chatMessage.abilityUsed.selection.string", sanitizePlayerMessage(replaceMentions(message.selection.selection)));
out = translate("chatMessage.abilityUsed.selection.string", sanitizePlayerMessage(replaceMentions(message.selection.selection, playerNames)));
break;
case "integer":
let text = translateChecked("controllerId."+controllerIdToLink(message.abilityId).replace(/\//g, ".") + ".integer." + message.selection.selection);
Expand Down
8 changes: 6 additions & 2 deletions client/src/components/TextAreaDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Icon from "./Icon";
import translate from "../game/lang";
import "./textAreaDropdown.css";
import DetailsSummary from "./DetailsSummary";
import { usePlayerNames } from "./useHooks";

export function TextDropdownArea(props: Readonly<{
titleString: string,
Expand Down Expand Up @@ -82,6 +83,8 @@ function TextDropdownLabel(
return props.savedText !== props.field
}, [props.field, props.savedText]);

const playerNames = usePlayerNames();

function save(field: string) {
props.onSave(field);
}
Expand All @@ -92,7 +95,7 @@ function TextDropdownLabel(
}

return <div>
<StyledText>{replaceMentions(props.titleString)}</StyledText>
<StyledText>{replaceMentions(props.titleString, playerNames)}</StyledText>
<span>
{props.onSubtract ? <Button
onClick={(e) => {
Expand Down Expand Up @@ -152,6 +155,7 @@ function PrettyTextArea(props: Readonly<{
}>): ReactElement {
const [writing, setWriting] = useState<boolean>(false);
const [hover, setHover] = useState<boolean>(false);
const playerNames = usePlayerNames();

return <div className="pretty-text-area"
onMouseEnter={() => setHover(true)}
Expand All @@ -162,7 +166,7 @@ function PrettyTextArea(props: Readonly<{
>
{(!writing && !hover)
? <div className="textarea">
<StyledText noLinks={true}>{sanitizePlayerMessage(replaceMentions(props.field))}</StyledText>
<StyledText noLinks={true}>{sanitizePlayerMessage(replaceMentions(props.field, playerNames))}</StyledText>
</div>
: <textarea
value={props.field}
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/WikiArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default function WikiArticle(props: {
return <section className="wiki-article">
<WikiStyledText className="wiki-article-standard">
{"# "+translate(`wiki.article.${articleType}.${props.article.split("/")[1]}.title`)+"\n"}
{replaceMentions(translate(`wiki.article.${articleType}.${props.article.split("/")[1]}.text`))}
{replaceMentions(translate(`wiki.article.${articleType}.${props.article.split("/")[1]}.text`), DUMMY_NAMES)}
</WikiStyledText>
</section>
}
Expand Down Expand Up @@ -139,7 +139,7 @@ function CategoryArticle(props: Readonly<{ category: WikiCategory }>): ReactElem
return <section className="wiki-article">
<WikiStyledText className="wiki-article-standard">
{"# "+title+"\n"}
{description ? replaceMentions(description) : ""}
{description ? replaceMentions(description, DUMMY_NAMES) : ""}
</WikiStyledText>
<PageCollection
title={title}
Expand Down
28 changes: 28 additions & 0 deletions client/src/components/useHooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import GAME_MANAGER from "..";
import { StateEventType } from "../game/gameManager.d";
import GameState, { LobbyState, PlayerGameState } from "../game/gameState.d";
import DUMMY_NAMES from "../resources/dummyNames.json";

function usePacketListener(listener: (type?: StateEventType) => void) {
// Catch all the packets we miss between setState and useEffect
Expand Down Expand Up @@ -132,4 +133,31 @@ export function usePlayerState<T>(
},
events, fallback
);
}

export function useSpectator(): boolean | undefined {
return useLobbyOrGameState(
state => {
if (state.stateType === "lobby")
return state.players.get(state.myId!)?.clientType.type === "spectator";
if (state.stateType === "game")
return state.clientState.type === "spectator";
return false;
},
["acceptJoin", "rejectStart", "startGame", "lobbyClients", "gamePlayers", "yourId"]
)
}

export function usePlayerNames(): string[] {
return useLobbyOrGameState(
state => {
if (state.stateType === "game") {
return state.players.map(player => player.toString())
} else {
return []
}
},
[],
DUMMY_NAMES
)!;
}
8 changes: 1 addition & 7 deletions client/src/game/gameManager.d.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WikiArticleLink } from "../components/WikiArticleLink";
import { DoomsayerGuess } from "../menu/game/gameScreenContent/AbilityMenu/RoleSpecificMenus/LargeDoomsayerMenu";
import { AbilityInput } from "./abilityInput";
import { PhaseType, PhaseTimes, PlayerIndex, State, Verdict, Player, ModifierType } from "./gameState.d";
import { PhaseType, PhaseTimes, PlayerIndex, State, Verdict, ModifierType } from "./gameState.d";
import { ToClientPacket, ToServerPacket } from "./packet";
import { RoleList, RoleOutline } from "./roleListState.d";
import { Role } from "./roleState.d";
Expand All @@ -27,12 +27,6 @@ export type GameManager = {


state: State,
getMyName(): string | undefined,
getMyHost(): boolean | undefined,
getMySpectator(): boolean,
getPlayerNames(): string[],
getLivingPlayers(): Player[] | null,
getVotesRequired(): number | null,
updateChatFilter(filter: PlayerIndex | null): void,

server: Server,
Expand Down
53 changes: 0 additions & 53 deletions client/src/game/gameManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { RoleOutline } from "./roleListState.d";
import translate from "./lang";
import PlayMenu from "../menu/main/PlayMenu";
import { createGameState, createLobbyState } from "./gameState";
import DUMMY_NAMES from "../resources/dummyNames.json";
import { deleteReconnectData } from "./localStorage";
import AudioController from "../menu/AudioController";

Expand Down Expand Up @@ -108,58 +107,6 @@ export function createGameManager(): GameManager {
stateType: "disconnected"
},

getMyName() {
if (gameManager.state.stateType === "lobby"){
let client = gameManager.state.players.get(gameManager.state.myId!);
if(client === undefined || client === null) return undefined;
if(client.clientType.type === "spectator") return undefined;
return client.clientType.name;
}
if (gameManager.state.stateType === "game" && gameManager.state.clientState.type === "player")
return gameManager.state.players[gameManager.state.clientState.myIndex!]?.name;
return undefined;
},
getMyHost() {
if (gameManager.state.stateType === "lobby")
return gameManager.state.players.get(gameManager.state.myId!)?.ready === "host";
if (gameManager.state.stateType === "game")
return gameManager.state.host;
return undefined;
},
getMySpectator() {
if (gameManager.state.stateType === "lobby")
return gameManager.state.players.get(gameManager.state.myId!)?.clientType.type === "spectator";
if (gameManager.state.stateType === "game")
return gameManager.state.clientState.type === "spectator";
return false;
},
getPlayerNames(): string[] {
switch (GAME_MANAGER.state.stateType) {
case "game":
return GAME_MANAGER.state.players.map((player) => player.toString());
case "lobby":
return [];
default:
return DUMMY_NAMES;
}
},
getLivingPlayers(): Player[] | null{
if(GAME_MANAGER.state.stateType !== "game") return null;
return GAME_MANAGER.state.players.filter(player => player.alive)
},
getVotesRequired(): number | null{
let count = 1;
let livingPlayers = GAME_MANAGER.getLivingPlayers();
if(livingPlayers === null) return null;
for (let player of livingPlayers) {
if (player.alive && !player.playerTags.includes("forfeitVote")) {
count += 1;
}
}


return Math.ceil(count / 2);
},
updateChatFilter(filter: PlayerIndex | null) {
if(GAME_MANAGER.state.stateType === "game" && GAME_MANAGER.state.clientState.type === "player"){
GAME_MANAGER.state.clientState.chatFilter = filter===null?null:{
Expand Down
48 changes: 25 additions & 23 deletions client/src/game/messageListener.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export default function messageListener(packet: ToClientPacket){
}
GAME_MANAGER.state.players = new ListMap(GAME_MANAGER.state.players.entries());
}else if(GAME_MANAGER.state.stateType === "game"){
GAME_MANAGER.state.host = packet.hosts.includes(GAME_MANAGER.state.myId===null?-1:GAME_MANAGER.state.myId)
GAME_MANAGER.state.host = packet.hosts.includes(GAME_MANAGER.state.myId ?? -1)
}
break;
case "playersReady":
Expand Down Expand Up @@ -194,13 +194,13 @@ export default function messageListener(packet: ToClientPacket){
case "lobbyClients":
if(GAME_MANAGER.state.stateType === "lobby"){

let oldMySpectator = GAME_MANAGER.getMySpectator();
const oldMySpectator = GAME_MANAGER.state.players.get(GAME_MANAGER.state.myId!)?.clientType.type === "spectator";

GAME_MANAGER.state.players = new ListMap();
for(let [clientId, lobbyClient] of packet.clients){
GAME_MANAGER.state.players.insert(clientId, lobbyClient);
}
let newMySpectator = GAME_MANAGER.getMySpectator();
const newMySpectator = GAME_MANAGER.state.players.get(GAME_MANAGER.state.myId!)?.clientType.type === "spectator";


if (oldMySpectator && !newMySpectator){
Expand All @@ -220,28 +220,30 @@ export default function messageListener(packet: ToClientPacket){
GAME_MANAGER.state.lobbyName = packet.name;
}
break;
case "startGame": {
const isSpectator = GAME_MANAGER.getMySpectator();
if(isSpectator){
GAME_MANAGER.setSpectatorGameState();
ANCHOR_CONTROLLER?.setContent(<LoadingScreen type="join" />)
}else{
GAME_MANAGER.setGameState();
ANCHOR_CONTROLLER?.setContent(<LoadingScreen type="join" />)
case "startGame":
if (GAME_MANAGER.state.stateType === "lobby") {
const isSpectator = GAME_MANAGER.state.players.get(GAME_MANAGER.state.myId!)?.clientType.type === "spectator";
if(isSpectator){
GAME_MANAGER.setSpectatorGameState();
ANCHOR_CONTROLLER?.setContent(<LoadingScreen type="join" />)
}else{
GAME_MANAGER.setGameState();
ANCHOR_CONTROLLER?.setContent(<LoadingScreen type="join" />)
}

AudioController.queueFile("audio/start_game.mp3");
}

AudioController.queueFile("audio/start_game.mp3");
}
break;
case "gameInitializationComplete": {
const isSpectator = GAME_MANAGER.getMySpectator();
if(isSpectator){
ANCHOR_CONTROLLER?.setContent(<SpectatorGameScreen/>);
}else{
ANCHOR_CONTROLLER?.setContent(<GameScreen/>);
break;
case "gameInitializationComplete":
if (GAME_MANAGER.state.stateType === "game") {
const isSpectator = GAME_MANAGER.state.clientState.type === "spectator";
if(isSpectator){
ANCHOR_CONTROLLER?.setContent(<SpectatorGameScreen/>);
}else{
ANCHOR_CONTROLLER?.setContent(<GameScreen/>);
}
}
}
break;
break;
case "backToLobby":
GAME_MANAGER.setLobbyState();
ANCHOR_CONTROLLER?.setContent(<LobbyMenu/>);
Expand Down
Loading

0 comments on commit 76f5055

Please sign in to comment.