useGame=>useCursor=>useHero=>useHeroRender

This commit is contained in:
2025-10-19 03:01:22 +01:00
parent 7e9058b2c9
commit 6108640267
9 changed files with 64 additions and 59 deletions
-6
View File
@@ -25,12 +25,6 @@ export const useKeyBindings = (gameManager: GameManager) => {
// ignore other keys while waiting
if (gameStatus === "waiting") return;
// cancel count or stop game
if (ev.key === "Escape") {
quitGame();
return;
}
// quit
if (ev.key === "q" || ev.key === "Q") {
quitGame();
+7
View File
@@ -69,6 +69,12 @@ export type GameStatusManager = {
//Cursor Stuff
export type CursorMode = 'normal' | 'insert' | 'replace' | 'visual' | 'visual-line';
export type UseHeroType = {
heroPos: Coord | null;
setHeroPos: (pos: Coord | null) => void;
moveHero: (dr: number, dc: number, steps: number, gameStatus: GameStatus, setGameStatus: (s: GameStatus) => void) => void;
};
export type Cursor = { //return type of useCursor (currently useHero)
position: () => Coord;
mode: () => CursorMode;
@@ -87,4 +93,5 @@ export type Cursor = { //return type of useCursor (currently useHero)
setCount: (digit: string) => void;
setLastKey: (key: string) => void;
getLastKey: () => string;
hero?: UseHeroType;
}
+21 -19
View File
@@ -229,12 +229,12 @@ export class MazeGenerator {
});
});
return [fr, fc];
return { row: fr, col: fc };
}
placeKey(): void {
const [fr, fc] = this.getKeyLocation();
this.maze[fr][fc] = ["key"];
const keyLocation = this.getKeyLocation();
this.maze[keyLocation.row][keyLocation.col] = ["key"];
}
shortestPath(
@@ -250,14 +250,14 @@ export class MazeGenerator {
} else {
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
if (this.maze[r][c].includes(fromTag)) start = [r, c];
if (this.maze[r][c].includes(fromTag)) start = { row: r, col: c };
}
}
}
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
if (this.maze[r][c].includes(toTag)) end = [r, c];
if (this.maze[r][c].includes(toTag)) end = { row: r, col: c };
}
}
@@ -265,39 +265,41 @@ export class MazeGenerator {
const visited = this.initArray<boolean>(false);
const prev = this.initArray<Coord | null>(null);
const queue: Coord[] = [[...start]];
visited[start[0]][start[1]] = true;
const queue: Coord[] = [start];
visited[start.row][start.col] = true;
const dirs: Coord[] = [
[-1, 0],
[1, 0],
[0, -1],
[0, 1],
{ row: -1, col: 0 },
{ row: 1, col: 0 },
{ row: 0, col: -1 },
{ row: 0, col: 1 },
];
while (queue.length > 0) {
const [r, c] = queue.shift() as Coord;
const current = queue.shift()!;
const r = current.row;
const c = current.col;
if (r === end[0] && c === end[1]) {
if (r === end.row && c === end.col) {
const path: Coord[] = [];
let curr: Coord | null = end;
while (curr) {
path.unshift(curr);
curr = prev[curr[0]][curr[1]];
curr = prev[curr.row][curr.col];
}
return path;
}
for (const [dr, dc] of dirs) {
const nr = r + dr,
nc = c + dc;
for (const dir of dirs) {
const nr = r + dir.row,
nc = c + dir.col;
if (!this.inBounds(nr, nc)) continue;
if (visited[nr][nc]) continue;
if (this.maze[nr][nc].includes("wall")) continue;
visited[nr][nc] = true;
prev[nr][nc] = [r, c];
queue.push([nr, nc]);
prev[nr][nc] = { row: r, col: c };
queue.push({ row: nr, col: nc });
}
}
+2
View File
@@ -1 +1,3 @@
export * from './useCursor';
export * from './useHero';
export * from './useHeroRender';
+8 -3
View File
@@ -1,15 +1,18 @@
import { useRef } from 'react'
import type { CursorMode, Coord, BoardManager } from '../types'
import type { CursorMode, Coord, BoardManager, GameStatus } from '../types'
import { useHero } from './useHero'
type Motion = { dr: number; dc: number; steps: number };
export function useCursor(board: BoardManager) {
export function useCursor(board: BoardManager, gameStatus: GameStatus) {
const position = useRef<Coord>({ row: 0, col: 0 })
const mode = useRef<CursorMode>('normal')
const countRef = useRef<string>("");
const lastMotionRef = useRef<Motion | null>(null);
const lastKeyRef = useRef<string>("");
const hero = useHero(board, gameStatus);
function findAnchorTarget(dir: "left" | "right") {
const m = board.mazeRef.current;
const pos = position.current;
@@ -209,7 +212,7 @@ export function useCursor(board: BoardManager) {
return lastKeyRef.current;
}
return {
const cursorAPI = {
position: () => position.current,
mode: () => mode.current,
move,
@@ -228,5 +231,7 @@ export function useCursor(board: BoardManager) {
setLastKey,
getLastKey,
}
return cursorAPI;
}
@@ -8,29 +8,6 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
const animatingRef = useRef(false);
useEffect(() => {
if (gameStatus === 'started' && !heroPos) {
const m = mazeRef.current;
if (!m.length) return;
for (let c = 0; c < m[0].length; c++) {
if (m[m.length - 1][c].includes("entrance")) {
setHeroPos({ row: m.length - 1, col: c });
break;
}
}
}
}, [gameStatus, heroPos, mazeRef]);
useEffect(() => {
if (gameStatus === 'waiting' || gameStatus === 'game-over' || gameStatus === 'game-won') {
setHeroPos(null);
const container = containerRef.current;
container?.querySelector("#maze")?.classList.remove("finished");
container?.querySelectorAll(".hero").forEach((el) => el.classList.remove("hero"));
}
}, [gameStatus, containerRef]);
const getCellEl = useCallback((r: number, c: number): HTMLElement | null => {
const container = containerRef.current;
if (!container) return null;
@@ -61,6 +38,29 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
return () => window.removeEventListener("maze-invalid", onInvalid);
}, [flashInvalid]);
useEffect(() => {
if (gameStatus === 'started' && !heroPos) {
const m = mazeRef.current;
if (!m.length) return;
for (let c = 0; c < m[0].length; c++) {
if (m[m.length - 1][c].includes("entrance")) {
setHeroPos({ row: m.length - 1, col: c });
break;
}
}
}
}, [gameStatus, heroPos, mazeRef]);
useEffect(() => {
if (gameStatus === 'waiting' || gameStatus === 'game-over' || gameStatus === 'game-won') {
setHeroPos(null);
const container = containerRef.current;
container?.querySelector("#maze")?.classList.remove("finished");
container?.querySelectorAll(".hero").forEach((el) => el.classList.remove("hero"));
}
}, [gameStatus, containerRef]);
const moveHero = useCallback(
(
dr: number,
@@ -204,7 +204,6 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
moveHero,
};
console.log('calling useHeroRender with', { gameStatus, board, hero });
useHeroRender({ gameStatus, board, hero });
return hero;
+1 -3
View File
@@ -1,7 +1,6 @@
import type { GameManager, GameOptions } from './types';
import { useBoard } from './useBoard';
import { useCursor } from './useCursor';
import { useHero } from './useHero';
import { useKeyBindings } from './useKeyBindings';
import { useGameStatus } from './useGameStatus';
@@ -9,12 +8,11 @@ export function useGame(options: GameOptions, platformHook?: unknown): GameManag
const { cols, rows } = options;
const board = useBoard(cols, rows);
const { containerRef, renderBoard } = board;
const cursor = useCursor(board);
const gameStatusManager = useGameStatus();
const { gameStatus, startGame, togglePause, quitGame } = gameStatusManager;
const hero = useHero(board, gameStatus);
const cursor = useCursor(board, gameStatus);
const keyManager = useKeyBindings({ cursor, gameStatus });
const gameManager = {
-2
View File
@@ -1,2 +0,0 @@
export * from './useHero';
export * from './useHeroRender';