Commit ec565c58 authored by Ategon's avatar Ategon
Browse files

Add a sound for when your pixel bucket is full

parent efb57d2c
Loading
Loading
Loading
Loading
Loading
+152 −2
Original line number Diff line number Diff line
@@ -14,20 +14,23 @@ import African3 from "../../sounds/African3.mp3";
import African4 from "../../sounds/African4.mp3";
import Coffee1 from "../../sounds/Coffee1.mp3";
import Coffee2 from "../../sounds/Coffee2.mp3";
import Retro6 from "../../sounds/Retro6.mp3";
import WoodBlock1 from "../../sounds/Wood Block1.mp3";
import WoodBlock2 from "../../sounds/Wood Block2.mp3";
import WoodBlock3 from "../../sounds/Wood Block3.mp3";
import { Switch } from "../core/Switch";

const _ = (v: boolean) => (v ? "enable" : "disable");
const DEFAULT_PIXEL_BUCKET_THRESHOLD = 6;

export const AudioSettings = () => {
  const { state, dispatch } = useAudio();
  const { config } = useAppContext<true>();
  const { config } = useAppContext();

  const coffee2Ref = useRef<(() => void) | null>(null);
  const delayedAvailableSoundRef = useRef<number | undefined>(undefined);
  const earlyAvailableSoundPlayedRef = useRef(false);
  const earlyBucketSoundPlayedRef = useRef(false);

  const [Coffee1Sound] = useSound(Coffee1, { volume: 0.5 });

@@ -43,6 +46,8 @@ export const AudioSettings = () => {

  const [African4Sound] = useSound(African4, { volume: 0.5 });

  const [Retro6Sound] = useSound(Retro6, { volume: 0.3 });

  const [WoodBlock1Sound] = useSound(WoodBlock1, { volume: 0.1 });

  const [WoodBlock2Sound] = useSound(WoodBlock2, { volume: 0.1 });
@@ -51,6 +56,14 @@ export const AudioSettings = () => {

  const [pixelsAvailable, setPixelsAvailable] = useState(0);
  const [pixelAvailableOffset, setPixelAvailableOffset] = useState(0);
  const [pixelBucketThreshold, setPixelBucketThreshold] = useState(6);
  const [pixelBucketOffset, setPixelBucketOffset] = useState(0);
  const [pixelGainStartedAt, setPixelGainStartedAt] = useState<
    number | undefined
  >(() => network.getState("pixelLastPlaced")?.[0]);
  const [bucketReachedAt, setBucketReachedAt] = useState<number | undefined>(
    undefined,
  );

  const WoodSounds = [WoodBlock1Sound, WoodBlock2Sound, WoodBlock3Sound];

@@ -67,6 +80,18 @@ export const AudioSettings = () => {
    };
  }, [pixelAvailableOffset, state.pixelAvailable]);

  useEffect(() => {
    const handlePixelGainStarted = (time: number) => {
      setPixelGainStartedAt(time);
    };

    network.on("pixelLastPlaced", handlePixelGainStarted);

    return () => {
      network.off("pixelLastPlaced", handlePixelGainStarted);
    };
  }, []);

  const handleAvailable = useCallback(
    (available: number, isUndo: boolean) => {
      if (available > pixelsAvailable && state.pixelAvailable && !isUndo) {
@@ -85,13 +110,33 @@ export const AudioSettings = () => {
        earlyAvailableSoundPlayedRef.current = false;
      }

      if (
        state.pixelBucketFull &&
        !isUndo &&
        available >= pixelBucketThreshold &&
        pixelsAvailable < pixelBucketThreshold
      ) {
        if (pixelBucketOffset < 0) {
          if (!earlyBucketSoundPlayedRef.current) Retro6Sound();
          earlyBucketSoundPlayedRef.current = false;
        } else {
          setBucketReachedAt(Date.now());
        }
      } else if (isUndo || available < pixelsAvailable) {
        earlyBucketSoundPlayedRef.current = false;
      }

      setPixelsAvailable(available);
    },
    [
      Retro6Sound,
      Coffee2Sound,
      pixelAvailableOffset,
      pixelBucketOffset,
      pixelBucketThreshold,
      pixelsAvailable,
      state.pixelAvailable,
      state.pixelBucketFull,
    ],
  );

@@ -109,6 +154,7 @@ export const AudioSettings = () => {
    if (
      !state.pixelAvailable ||
      pixelAvailableOffset >= 0 ||
      !config ||
      pixelsAvailable >= config.canvas.pixel.maxStack
    ) {
      return;
@@ -129,7 +175,6 @@ export const AudioSettings = () => {
      earlyAvailableSoundPlayedRef.current = true;
      Coffee2Sound();
    }, delay);

    return () => {
      window.clearTimeout(timeout);
    };
@@ -141,6 +186,63 @@ export const AudioSettings = () => {
    state.pixelAvailable,
  ]);

  useEffect(() => {
    earlyBucketSoundPlayedRef.current = false;

    if (!state.pixelBucketFull || !config) return;

    let soundAt: number;
    let clearReachedAt = false;

    if (pixelBucketOffset >= 0) {
      if (bucketReachedAt === undefined) return;

      soundAt = bucketReachedAt + pixelBucketOffset * 1000;
      clearReachedAt = true;
    } else {
      if (
        pixelGainStartedAt === undefined ||
        pixelsAvailable >= pixelBucketThreshold
      ) {
        return;
      }

      let cooldownSeconds = 0;
      for (
        let pixel = pixelsAvailable + 1;
        pixel <= pixelBucketThreshold;
        pixel += 1
      ) {
        cooldownSeconds += CanvasLib.getPixelCooldown(pixel, config);
      }

      soundAt =
        pixelGainStartedAt +
        cooldownSeconds * 1000 +
        pixelBucketOffset * 1000;
      if (soundAt <= Date.now()) return;
    }

    const timeout = window.setTimeout(() => {
      Retro6Sound();
      if (pixelBucketOffset < 0) earlyBucketSoundPlayedRef.current = true;
      if (clearReachedAt) setBucketReachedAt(undefined);
    }, Math.max(0, soundAt - Date.now()));

    return () => {
      window.clearTimeout(timeout);
    };
  }, [
    Retro6Sound,
    bucketReachedAt,
    config,
    pixelBucketOffset,
    pixelBucketThreshold,
    pixelGainStartedAt,
    pixelsAvailable,
    state.pixelBucketFull,
  ]);

  const handlePlace = useCallback(() => {
    if (!state.place) return;

@@ -254,6 +356,54 @@ export const AudioSettings = () => {
            }}
          />
        )}
        <Switch
          silent
          isSelected={state.pixelBucketFull}
          onValueChange={(v) => {
            dispatch([_(v), "pixelBucketFull"]);
            setBucketReachedAt(undefined);
            if (v) {
              Retro6Sound();
            }
          }}
        >
          Pixel Bucket Full
        </Switch>
        {state.pixelBucketFull && (
          <div className="flex flex-col gap-4 px-2 pb-2">
            <Slider
              label="Bucket Alert Level"
              step={1}
              minValue={1}
              maxValue={
                config?.canvas.pixel.maxStack ?? DEFAULT_PIXEL_BUCKET_THRESHOLD
              }
              value={pixelBucketThreshold}
              onChange={(value) => {
                setBucketReachedAt(undefined);
                setPixelBucketThreshold(value as number);
              }}
              getValue={(value) => {
                const pixels = value as number;
                return `${pixels} pixel${pixels === 1 ? "" : "s"}`;
              }}
            />
            <Slider
              label="Chime Offset"
              step={1}
              minValue={-5}
              maxValue={15}
              value={-pixelBucketOffset}
              onChange={(value) =>
                setPixelBucketOffset(-(value as number))
              }
              getValue={(value) => {
                const offset = -(value as number);
                return `${offset > 0 ? "+" : ""}${offset}s`;
              }}
            />
          </div>
        )}
        <Switch
          silent
          isSelected={state.place}
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ interface IAudioState {
  disconnected: boolean;
  reconnected: boolean;
  pixelAvailable: boolean;
  pixelBucketFull: boolean;
  pixelUndo: boolean;
  uiClick: boolean;
}
@@ -24,6 +25,7 @@ const Defaults: IAudioState = {
  disconnected: false,
  reconnected: false,
  pixelAvailable: false,
  pixelBucketFull: false,
  pixelUndo: false,
  uiClick: false,
};
+18.5 KiB

File added.

No diff preview for this file type.