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 // ignore other keys while waiting
if (gameStatus === "waiting") return; if (gameStatus === "waiting") return;
// cancel count or stop game
if (ev.key === "Escape") {
quitGame();
return;
}
// quit // quit
if (ev.key === "q" || ev.key === "Q") { if (ev.key === "q" || ev.key === "Q") {
quitGame(); quitGame();
+7
View File
@@ -69,6 +69,12 @@ export type GameStatusManager = {
//Cursor Stuff //Cursor Stuff
export type CursorMode = 'normal' | 'insert' | 'replace' | 'visual' | 'visual-line'; 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) export type Cursor = { //return type of useCursor (currently useHero)
position: () => Coord; position: () => Coord;
mode: () => CursorMode; mode: () => CursorMode;
@@ -87,4 +93,5 @@ export type Cursor = { //return type of useCursor (currently useHero)
setCount: (digit: string) => void; setCount: (digit: string) => void;
setLastKey: (key: string) => void; setLastKey: (key: string) => void;
getLastKey: () => string; 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 { placeKey(): void {
const [fr, fc] = this.getKeyLocation(); const keyLocation = this.getKeyLocation();
this.maze[fr][fc] = ["key"]; this.maze[keyLocation.row][keyLocation.col] = ["key"];
} }
shortestPath( shortestPath(
@@ -250,14 +250,14 @@ export class MazeGenerator {
} else { } else {
for (let r = 0; r < this.rows; r++) { for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) { 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 r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) { 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 visited = this.initArray<boolean>(false);
const prev = this.initArray<Coord | null>(null); const prev = this.initArray<Coord | null>(null);
const queue: Coord[] = [[...start]]; const queue: Coord[] = [start];
visited[start[0]][start[1]] = true; visited[start.row][start.col] = true;
const dirs: Coord[] = [ const dirs: Coord[] = [
[-1, 0], { row: -1, col: 0 },
[1, 0], { row: 1, col: 0 },
[0, -1], { row: 0, col: -1 },
[0, 1], { row: 0, col: 1 },
]; ];
while (queue.length > 0) { 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[] = []; const path: Coord[] = [];
let curr: Coord | null = end; let curr: Coord | null = end;
while (curr) { while (curr) {
path.unshift(curr); path.unshift(curr);
curr = prev[curr[0]][curr[1]]; curr = prev[curr.row][curr.col];
} }
return path; return path;
} }
for (const [dr, dc] of dirs) { for (const dir of dirs) {
const nr = r + dr, const nr = r + dir.row,
nc = c + dc; nc = c + dir.col;
if (!this.inBounds(nr, nc)) continue; if (!this.inBounds(nr, nc)) continue;
if (visited[nr][nc]) continue; if (visited[nr][nc]) continue;
if (this.maze[nr][nc].includes("wall")) continue; if (this.maze[nr][nc].includes("wall")) continue;
visited[nr][nc] = true; visited[nr][nc] = true;
prev[nr][nc] = [r, c]; prev[nr][nc] = { row: r, col: c };
queue.push([nr, nc]); queue.push({ row: nr, col: nc });
} }
} }
+2
View File
@@ -1 +1,3 @@
export * from './useCursor'; export * from './useCursor';
export * from './useHero';
export * from './useHeroRender';
+8 -3
View File
@@ -1,15 +1,18 @@
import { useRef } from 'react' 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 }; 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 position = useRef<Coord>({ row: 0, col: 0 })
const mode = useRef<CursorMode>('normal') const mode = useRef<CursorMode>('normal')
const countRef = useRef<string>(""); const countRef = useRef<string>("");
const lastMotionRef = useRef<Motion | null>(null); const lastMotionRef = useRef<Motion | null>(null);
const lastKeyRef = useRef<string>(""); const lastKeyRef = useRef<string>("");
const hero = useHero(board, gameStatus);
function findAnchorTarget(dir: "left" | "right") { function findAnchorTarget(dir: "left" | "right") {
const m = board.mazeRef.current; const m = board.mazeRef.current;
const pos = position.current; const pos = position.current;
@@ -209,7 +212,7 @@ export function useCursor(board: BoardManager) {
return lastKeyRef.current; return lastKeyRef.current;
} }
return { const cursorAPI = {
position: () => position.current, position: () => position.current,
mode: () => mode.current, mode: () => mode.current,
move, move,
@@ -228,5 +231,7 @@ export function useCursor(board: BoardManager) {
setLastKey, setLastKey,
getLastKey, getLastKey,
} }
return cursorAPI;
} }
@@ -8,29 +8,6 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
const animatingRef = useRef(false); 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 getCellEl = useCallback((r: number, c: number): HTMLElement | null => {
const container = containerRef.current; const container = containerRef.current;
if (!container) return null; if (!container) return null;
@@ -61,6 +38,29 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
return () => window.removeEventListener("maze-invalid", onInvalid); return () => window.removeEventListener("maze-invalid", onInvalid);
}, [flashInvalid]); }, [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( const moveHero = useCallback(
( (
dr: number, dr: number,
@@ -204,7 +204,6 @@ export function useHero(board: BoardManager, gameStatus: GameStatus) {
moveHero, moveHero,
}; };
console.log('calling useHeroRender with', { gameStatus, board, hero });
useHeroRender({ gameStatus, board, hero }); useHeroRender({ gameStatus, board, hero });
return hero; return hero;
+1 -3
View File
@@ -1,7 +1,6 @@
import type { GameManager, GameOptions } from './types'; import type { GameManager, GameOptions } from './types';
import { useBoard } from './useBoard'; import { useBoard } from './useBoard';
import { useCursor } from './useCursor'; import { useCursor } from './useCursor';
import { useHero } from './useHero';
import { useKeyBindings } from './useKeyBindings'; import { useKeyBindings } from './useKeyBindings';
import { useGameStatus } from './useGameStatus'; import { useGameStatus } from './useGameStatus';
@@ -9,12 +8,11 @@ export function useGame(options: GameOptions, platformHook?: unknown): GameManag
const { cols, rows } = options; const { cols, rows } = options;
const board = useBoard(cols, rows); const board = useBoard(cols, rows);
const { containerRef, renderBoard } = board; const { containerRef, renderBoard } = board;
const cursor = useCursor(board);
const gameStatusManager = useGameStatus(); const gameStatusManager = useGameStatus();
const { gameStatus, startGame, togglePause, quitGame } = gameStatusManager; const { gameStatus, startGame, togglePause, quitGame } = gameStatusManager;
const hero = useHero(board, gameStatus); const cursor = useCursor(board, gameStatus);
const keyManager = useKeyBindings({ cursor, gameStatus }); const keyManager = useKeyBindings({ cursor, gameStatus });
const gameManager = { const gameManager = {
-2
View File
@@ -1,2 +0,0 @@
export * from './useHero';
export * from './useHeroRender';