浏览代码

Implement kill feed + early end of game routing

Kirk Trombley 3 年之前
父节点
当前提交
83478b4066

+ 3 - 1
client/src/components/screens/GamePanel/GamePanel.jsx

@@ -1,4 +1,4 @@
-import { FROZEN, RACE } from "../../../domain/constants";
+import { FROZEN, GUN_GAME, RACE } from "../../../domain/constants";
 import { useGameConfig } from "../../../hooks/useGameInfo";
 import usePreventNavigation from "../../../hooks/usePreventNavigation";
 import Loading from "../../util/Loading";
@@ -7,6 +7,7 @@ import GuessPane from "./GuessPane";
 import PositionedStreetView from "./PositionedStreetView";
 import RaceMode from "./RaceMode";
 import { useIsFinished } from "./hooks";
+import KillFeed from "./KillFeed";
 
 const GamePanel = () => {
   // warn the user if they navigate away
@@ -21,6 +22,7 @@ const GamePanel = () => {
       {clockMode === RACE && <RaceMode rate={1000} cutoffTime={10} />}
       <div className={styles.streetview}>
         <PositionedStreetView />
+        {gameMode === GUN_GAME && <KillFeed />}
         {gameMode === FROZEN && <div className={styles.freeze} />}
       </div>
       <GuessPane />

+ 29 - 0
client/src/components/screens/GamePanel/GamePanel.module.css

@@ -53,6 +53,35 @@
   z-index: 1;
 }
 
+.killFeed {
+  position: absolute;
+  z-index: 4;
+  right: 5vw;
+  top: 5vh;
+  display: flex;
+  flex-flow: column nowrap;
+  justify-content: flex-end;
+  align-items: flex-start;
+}
+
+.killFeedItem {
+  display: inline-flex;
+  background: #333;
+  padding: 0.5em;
+  justify-content: flex-end;
+  align-items: center;
+}
+
+.killFeedName {
+  display: inline-block;
+  width: 10em;
+  margin-right: 0.5em;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  text-align: right;
+}
+
 .cutoff {
   position: absolute;
   top: 20%;

+ 62 - 0
client/src/components/screens/GamePanel/KillFeed.jsx

@@ -0,0 +1,62 @@
+import { useEffect, useState } from "react";
+import { dispatch, useGameId } from "../../../domain/gameStore";
+import { usePlayers } from "../../../hooks/useGameInfo";
+import styles from "./GamePanel.module.css";
+
+// okay, in an ideal world this would be part of the game store or something
+// and it would get properly managed by reactive state
+// but also, this totally works as is, and the only downside is it might potentially grow too big
+// but that only happens if someone plays that many gun games without ever leaving the window
+const shownItems = new Set();
+
+const KillFeed = () => {
+  const gameId = useGameId();
+  const players = usePlayers();
+  useEffect(() => {
+    if (players?.find(({ currentRound }) => currentRound === null)) {
+      dispatch.goToSummary();
+    }
+  }, [players]);
+  const [shownItemsState, setShownItemsState] = useState(shownItems);
+  const [display, setDisplay] = useState([]);
+  useEffect(() => {
+    const toDisplay =
+      players
+        ?.flatMap(({ name, guesses }) =>
+          Object.entries(guesses).map(([round, { score }]) => ({
+            name,
+            round,
+            score,
+          }))
+        )
+        ?.filter(
+          ({ name, round }) =>
+            !shownItemsState.has(`${gameId}-${name}-${round}`)
+        ) ?? [];
+    setDisplay(toDisplay);
+    const timeouts = [];
+    toDisplay.forEach(({ name, round }) => {
+      timeouts.push(
+        setTimeout(() => {
+          shownItems.add(`${gameId}-${name}-${round}`);
+          setShownItemsState(new Set(shownItems));
+        }, 5000)
+      );
+    });
+    return () => {
+      timeouts.forEach(t => clearTimeout(t));
+    };
+  }, [shownItemsState, gameId, players]);
+  return (
+    <div className={styles.killFeed}>
+      {display.map(({ name, round, score }) => (
+        <span key={`${name}-${round}`} className={styles.killFeedItem}>
+          <span className={styles.killFeedName}>{name}</span>{" "}
+          {score >= 4950 ? "🎯" : "🖱️"} 🗺️ {round}
+        </span>
+      ))}
+    </div>
+  );
+};
+
+export default KillFeed;