Files
healer-man/src/game/store.test.ts
T

123 lines
4.4 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { DUNGEON_MANIFEST } from "./dungeonManifest";
import { useGameStore } from "./store";
describe("game store", () => {
beforeEach(() => {
useGameStore.getState().resetAtEntrance();
useGameStore.setState({
overlay: null,
paused: false,
mapOpen: false,
companionOpen: false,
assetStatus: "checking",
assetMessage: null,
distanceTravelled: 0,
hasMoved: false,
});
});
it("opens the map without leaving camera control active", () => {
useGameStore.setState({ cameraLookActive: true });
useGameStore.getState().toggleMap();
expect(useGameStore.getState()).toMatchObject({
overlay: "map",
paused: true,
mapOpen: true,
cameraLookActive: false,
});
});
it("keeps all full-screen game panels mutually exclusive", () => {
useGameStore.getState().openOverlay("spellbook");
expect(useGameStore.getState()).toMatchObject({ overlay: "spellbook", paused: true, mapOpen: false });
useGameStore.getState().openOverlay("talents");
expect(useGameStore.getState()).toMatchObject({ overlay: "talents", paused: true, mapOpen: false });
useGameStore.getState().openOverlay("bindings");
expect(useGameStore.getState()).toMatchObject({ overlay: "bindings", paused: true, mapOpen: false });
useGameStore.getState().closeOverlay();
expect(useGameStore.getState()).toMatchObject({ overlay: null, paused: false, mapOpen: false });
});
it("blocks camera-drag play while the one-screen companion is open", () => {
useGameStore.setState({ cameraLookActive: true });
useGameStore.getState().setCompanionOpen(true);
expect(useGameStore.getState()).toMatchObject({ companionOpen: true, cameraLookActive: false });
});
it("records throttled exploration snapshots", () => {
useGameStore.getState().updatePlayerSnapshot({
position: [164, -73.66, 132],
yaw: -2,
distanceDelta: 1.25,
});
const state = useGameStore.getState();
expect(state.distanceTravelled).toBeCloseTo(1.25);
expect(state.hasMoved).toBe(true);
expect(state.areaName).toContain("Entrance");
});
it("keeps a grounded navigation anchor while the physical player is airborne", () => {
useGameStore.getState().updatePlayerSnapshot({
position: [164, -73.66, 132],
yaw: -2,
grounded: true,
});
useGameStore.getState().updatePlayerSnapshot({
position: [166, -72.2, 133],
yaw: -2,
grounded: false,
});
expect(useGameStore.getState()).toMatchObject({
playerPosition: [166, -72.2, 133],
playerNavigationPosition: [164, -73.66, 132],
playerGrounded: false,
});
useGameStore.getState().updatePlayerSnapshot({
position: [167, -73.66, 134],
yaw: -2,
grounded: true,
});
expect(useGameStore.getState()).toMatchObject({
playerNavigationPosition: [167, -73.66, 134],
playerGrounded: true,
});
});
it("reset restores the authoritative transformed entrance", () => {
useGameStore.getState().setActionLayer("tertiary");
useGameStore.getState().updatePlayerSnapshot({ position: [0, 0, 0], yaw: 0, distanceDelta: 8 });
useGameStore.getState().resetAtEntrance();
const state = useGameStore.getState();
expect(state.playerPosition).toEqual(DUNGEON_MANIFEST.entrance.footPosition);
expect(state.playerNavigationPosition).toEqual(DUNGEON_MANIFEST.entrance.footPosition);
expect(state.playerGrounded).toBe(true);
expect(state.cameraYaw).toBe(DUNGEON_MANIFEST.entrance.yaw);
expect(state.distanceTravelled).toBe(0);
expect(state.actionLayer).toBe("primary");
});
it("does not let a late loading effect cover an already-ready scene", () => {
useGameStore.getState().setAssetStatus("ready");
useGameStore.getState().setAssetStatus("loading");
expect(useGameStore.getState().assetStatus).toBe("ready");
});
it("does not notify dual-screen subscribers for repeated input state", () => {
useGameStore.getState().setCameraLookActive(false);
useGameStore.getState().setInputMode("keyboard");
let notifications = 0;
const unsubscribe = useGameStore.subscribe(() => { notifications += 1; });
useGameStore.getState().setCameraLookActive(false);
useGameStore.getState().setInputMode("keyboard");
expect(notifications).toBe(0);
useGameStore.getState().setCameraLookActive(true);
expect(notifications).toBe(1);
unsubscribe();
});
});