Browse Source

Implement hit marker and hit sound

Kirk Trombley 3 years ago
parent
commit
333db30f6b

+ 6 - 0
client/src/assets/hitmarker.svg

@@ -0,0 +1,6 @@
+<svg overflow="visible" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" fill="#ffffff" stroke="#000000" stroke-width="5">
+    <rect x="95" y="0" width="150" height="40" transform="rotate(45)"></rect>
+    <rect x="305" y="0" width="150" height="40" transform="rotate(45)"></rect>
+    <rect x="10" y="255" width="150" height="40" transform="rotate(-45)"></rect>
+    <rect x="-200" y="255" width="150" height="40" transform="rotate(-45)"></rect>
+</svg>

BIN
client/src/assets/hitsound.wav


+ 40 - 17
client/src/components/util/KillFeed/KillFeed.jsx

@@ -2,6 +2,8 @@ import { useEffect, useState } from "react";
 import { dispatch, useGameId, usePlayerName } from "../../../domain/gameStore";
 import { usePlayers } from "../../../hooks/useGameInfo";
 import styles from "./KillFeed.module.css";
+import hitmarker from "../../../assets/hitmarker.svg";
+import hitsound from "../../../assets/hitsound.wav";
 
 // okay, in an ideal world this would be part of the game store or something
 // and it would get properly managed by reactive state
@@ -36,28 +38,49 @@ const KillFeed = () => {
             !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)
-      );
-    });
+    const timeout = setTimeout(() => {
+      toDisplay.forEach(({ name, round }) => {
+        shownItems.add(`${gameId}-${name}-${round}`);
+      });
+      setShownItemsState(new Set(shownItems));
+    }, 5000);
     return () => {
-      timeouts.forEach(t => clearTimeout(t));
+      clearTimeout(timeout);
     };
   }, [shownItemsState, gameId, players, playerName]);
+  useEffect(() => {
+    display.forEach(() => {
+      const audio = new Audio(hitsound);
+      audio.volume = 0.5;
+      const delay = Math.random() * 500; // delay up to half a second so overlapping sounds better
+      audio.addEventListener("canplaythrough", () =>
+        setTimeout(() => audio.play(), delay)
+      );
+      audio.addEventListener("ended", () => audio.remove()); // clean up after ourselves
+    });
+  }, [display]);
   return (
-    <div className={styles.feed}>
-      {display.map(({ name, round, score }) => (
-        <span key={`${name}-${round}`} className={styles.item}>
-          <span className={styles.name}>{name}</span>{" "}
-          {score >= 4950 ? "🎯" : "🖱️"} 🗺️ {round}
-        </span>
+    <>
+      {display.map(() => (
+        <img
+          alt="hitmarker"
+          className={styles.hitmarker}
+          style={{
+            top: `${10 + Math.random() * 80}vh`,
+            left: `${10 + Math.random() * 80}vw`,
+          }}
+          src={hitmarker}
+        />
       ))}
-    </div>
+      <div className={styles.feed}>
+        {display.map(({ name, round, score }) => (
+          <span className={styles.item}>
+            <span className={styles.name}>{name}</span>{" "}
+            {score >= 4950 ? "🎯" : "🖱️"} 🗺️ {round}
+          </span>
+        ))}
+      </div>
+    </>
   );
 };
 

+ 8 - 0
client/src/components/util/KillFeed/KillFeed.module.css

@@ -7,6 +7,7 @@
   flex-flow: column nowrap;
   justify-content: flex-end;
   align-items: flex-start;
+  pointer-events: none;
 }
 
 .item {
@@ -26,3 +27,10 @@
   text-overflow: ellipsis;
   text-align: right;
 }
+
+.hitmarker {
+  position: absolute;
+  z-index: 5;
+  width: 100px;
+  pointer-events: none;
+}