225 lines
6.5 KiB
TypeScript
225 lines
6.5 KiB
TypeScript
import React, { useState, useEffect, useContext, useRef, useMemo, useCallback } from "react";
|
|
import equal from "fast-deep-equal";
|
|
|
|
import Paper from "@mui/material/Paper";
|
|
import Button from "@mui/material/Button";
|
|
|
|
import "./Winner.css";
|
|
|
|
import { Resource } from "./Resource";
|
|
import { PlayerColor } from "./PlayerColor";
|
|
import { GlobalContext } from "./GlobalContext";
|
|
|
|
interface WinnerProps {
|
|
winnerDismissed: boolean;
|
|
setWinnerDismissed: (dismissed: boolean) => void;
|
|
}
|
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
const Winner: React.FC<WinnerProps> = ({ winnerDismissed, setWinnerDismissed }) => {
|
|
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
|
const [winner, setWinner] = useState<any>(undefined);
|
|
const [state, setState] = useState<string | undefined>(undefined);
|
|
const fields = useMemo(() => ["winner", "state"], []);
|
|
useEffect(() => {
|
|
if (!lastJsonMessage) {
|
|
return;
|
|
}
|
|
|
|
const data = lastJsonMessage;
|
|
switch (data.type) {
|
|
case "game-update":
|
|
console.log(`winner - game update`, data.update);
|
|
if ("winner" in data.update && !equal(data.update.winner, winner)) {
|
|
setWinner(data.update.winner);
|
|
}
|
|
if ("state" in data.update && data.update.state !== state) {
|
|
if (data.update.state !== "winner") {
|
|
setWinner(undefined);
|
|
}
|
|
setWinnerDismissed(false);
|
|
setState(data.update.state);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}, [lastJsonMessage, winner, state, setWinnerDismissed]);
|
|
useEffect(() => {
|
|
if (!sendJsonMessage) {
|
|
return;
|
|
}
|
|
sendJsonMessage({
|
|
type: "get",
|
|
fields,
|
|
});
|
|
}, [sendJsonMessage, fields]);
|
|
|
|
const quitClicked = useCallback(() => {
|
|
if (!winnerDismissed) {
|
|
setWinnerDismissed(true);
|
|
sendJsonMessage({
|
|
type: "goto-lobby",
|
|
});
|
|
}
|
|
}, [sendJsonMessage, winnerDismissed, setWinnerDismissed]);
|
|
|
|
if (!winner || winnerDismissed) {
|
|
return <></>;
|
|
}
|
|
|
|
let losers = [];
|
|
for (const key in winner.players) {
|
|
if (key === winner.color || winner.players[key].status === "Not active") {
|
|
continue;
|
|
}
|
|
losers.push(winner.players[key]);
|
|
}
|
|
|
|
const turnCount = Math.floor(winner.turns / (losers.length + 1));
|
|
|
|
losers = losers.map((player: any) => {
|
|
const averageSeconds = Math.floor(player.totalTime / turnCount / 1000),
|
|
average = `${Math.floor(averageSeconds / 60)}m:${averageSeconds % 60}s`;
|
|
return (
|
|
<div key={player.color}>
|
|
<PlayerColor color={player.color} /> {player.name} finished with {player.points} victory points.
|
|
{Number(player.potential) !== 0 && (
|
|
<>
|
|
They had <b>{player.potential}</b> unplayed Victory Point card(s).
|
|
</>
|
|
)}
|
|
Their average turn time was {average}.
|
|
</div>
|
|
);
|
|
});
|
|
|
|
let robber;
|
|
let max = 0;
|
|
|
|
let playerStolen: any = {};
|
|
const stats = winner.stolen;
|
|
for (const player in stats) {
|
|
if (player === "total" || player === "player") {
|
|
continue;
|
|
}
|
|
if (player === "robber") {
|
|
robber = <></>;
|
|
for (const type in stats.robber.stole) {
|
|
if (type === "total") {
|
|
continue;
|
|
}
|
|
const count = stats.robber.stole[type];
|
|
robber = (
|
|
<>
|
|
{robber}
|
|
<Resource label={true} type={type} count={count} disabled />
|
|
</>
|
|
);
|
|
}
|
|
robber = (
|
|
<div>
|
|
Throughout the game, the robber blocked <b>{stats.robber.stole.total}</b> resources:
|
|
<div className="ThiefStole">{robber}</div>
|
|
</div>
|
|
);
|
|
continue;
|
|
}
|
|
|
|
if (stats[player].stolen.total < max) {
|
|
continue;
|
|
}
|
|
if (stats[player].stolen.total > max) {
|
|
max = stats[player].stolen.total;
|
|
playerStolen = {
|
|
robber: stats[player].stolen.robber,
|
|
player: stats[player].stolen.player,
|
|
element: <></>,
|
|
};
|
|
}
|
|
|
|
let stolen;
|
|
for (const type in stats[player].stolen) {
|
|
if (["total", "robber", "player"].indexOf(type) !== -1) {
|
|
continue;
|
|
}
|
|
if (!stolen) {
|
|
stolen = <></>;
|
|
}
|
|
const count = stats[player].stolen[type];
|
|
stolen = (
|
|
<>
|
|
{stolen}
|
|
<Resource label={true} type={type} count={count} disabled />
|
|
</>
|
|
);
|
|
}
|
|
if (stolen) {
|
|
playerStolen.element = (
|
|
<div key={player}>
|
|
<PlayerColor color={player} /> {winner.players[player].name}
|
|
<div className="PlayerStolen">{stolen}</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!robber) {
|
|
robber = <div>The robber never blocked any resources from anyone!</div>;
|
|
}
|
|
|
|
const averageSeconds = Math.floor(winner.totalTime / turnCount / 1000),
|
|
average = `${Math.floor(averageSeconds / 60)}m:${averageSeconds % 60}s`;
|
|
|
|
const seconds = winner.elapsedTime / 1000,
|
|
h = Math.floor(seconds / (60 * 60)),
|
|
m = Math.floor((seconds % (60 * 60)) / 60),
|
|
s = Math.floor((seconds % (60 * 60)) % 60);
|
|
const totalTime = `${h}h:${m}m:${s}s`;
|
|
|
|
const vpType: string[] = ["market", "university", "library", "palace"];
|
|
const selectedVpType = vpType[Math.floor(vpType.length * Math.random())];
|
|
|
|
return (
|
|
<div className="Winner">
|
|
<Paper>
|
|
<div className="Title">
|
|
{winner.name} has won with {winner.points} victory points!
|
|
</div>
|
|
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
<Resource type={`vp-${selectedVpType}`} disabled count={1} />
|
|
<div className="Description">
|
|
<div>
|
|
Congratulations, <b>{winner.name}</b>!
|
|
</div>
|
|
<div>
|
|
<PlayerColor color={winner.color} /> {winner.name} won the game with <b>{winner.points}</b> Victory Points
|
|
after {turnCount} game turns.
|
|
{Number(winner.potential) !== 0 && (
|
|
<>
|
|
They had <b>{winner.potential}</b> unplayed Victory Point card(s).
|
|
</>
|
|
)}
|
|
Their average turn time was {average}.
|
|
</div>
|
|
{losers}
|
|
<div>The game took {totalTime}.</div>
|
|
{robber}
|
|
{max !== 0 && (
|
|
<>
|
|
<div>
|
|
The robber stole {playerStolen.robber} and other players stole {playerStolen.player} resources from:
|
|
</div>
|
|
<div className="PlayerStolenList">{playerStolen.element}</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Button onClick={quitClicked}>Go back to Lobby</Button>
|
|
</Paper>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export { Winner };
|