laureen
Add programmable head dance moves
1757af8
Raw History Blame Contribute Delete
2.6 kB
// App shell: layers in z order - scene canvas (1), halftone (2), CRT (5),
// HUD + touch overlay (9-12), BIOS readout (20), dance controls (25),
// title menu (30). Also owns the pre-game device detection and title -> enter flow.
import { useEffect } from "react";
import GameCanvas from "./scene/GameCanvas.jsx";
import { Halftone, CrtOverlay } from "./ui/Overlays.jsx";
import Hud from "./ui/Hud.jsx";
import TouchOverlay from "./ui/TouchOverlay.jsx";
import BiosOverlay from "./ui/BiosOverlay.jsx";
import TitleMenu from "./ui/TitleMenu.jsx";
import DanceControls from "./ui/DanceControls.jsx";
import { useGame } from "./store.js";
export default function App() {
useEffect(() => {
const params = new URLSearchParams(location.search);
// Gamepad presence for the tutorial variant, before the game controller
// takes over polling (the game loop keeps mirroring it afterwards).
const pollPad = () => {
const gp = [...(navigator.getGamepads?.() ?? [])].find(
(p) => p && p.connected,
);
useGame.setState({ padConnected: !!gp });
};
window.addEventListener("gamepadconnected", pollPad);
window.addEventListener("gamepaddisconnected", pollPad);
pollPad();
// Touch detection, latching.
const coarse = matchMedia("(pointer: coarse)");
const armTouch = () => {
window.__microduckTouched = true;
useGame.setState({ touchMode: true });
};
const onCoarse = () => {
if (coarse.matches) armTouch();
};
if (coarse.matches) armTouch();
else coarse.addEventListener("change", onCoarse);
window.addEventListener("touchstart", armTouch, {
once: true,
passive: true,
});
if (params.get("touch") === "1") armTouch();
// ?boot=1 skips the title entirely - testing hook.
if (params.get("boot") === "1") {
useGame.setState({
prebootDone: true,
entered: true,
menuOpen: false,
});
} else {
useGame.setState({
prebootDone: true,
menuOpen: true,
});
}
return () => {
window.removeEventListener("gamepadconnected", pollPad);
window.removeEventListener("gamepaddisconnected", pollPad);
coarse.removeEventListener("change", onCoarse);
window.removeEventListener("touchstart", armTouch);
};
}, []);
return (
<>
<GameCanvas />
<Halftone />
<CrtOverlay />
<Hud />
<TouchOverlay />
<BiosOverlay />
{/* N = neck isolation, H = circular neck isolation */}
<DanceControls />
<TitleMenu />
</>
);
}