diff --git a/example/src/useKeyBindings.ts b/example/src/useKeyBindings.ts index 9099d0d..89e9953 100644 --- a/example/src/useKeyBindings.ts +++ b/example/src/useKeyBindings.ts @@ -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(); diff --git a/src/types.ts b/src/types.ts index deadb3e..92f38b2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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; } diff --git a/src/useBoard/MazeGenerator.ts b/src/useBoard/MazeGenerator.ts index 616e69f..fe9a8b4 100644 --- a/src/useBoard/MazeGenerator.ts +++ b/src/useBoard/MazeGenerator.ts @@ -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(false); const prev = this.initArray(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 }); } } diff --git a/src/useCursor/index.ts b/src/useCursor/index.ts index 31ce475..bbd12c9 100644 --- a/src/useCursor/index.ts +++ b/src/useCursor/index.ts @@ -1 +1,3 @@ export * from './useCursor'; +export * from './useHero'; +export * from './useHeroRender'; diff --git a/src/useCursor/useCursor.ts b/src/useCursor/useCursor.ts index 52cce02..6c80603 100644 --- a/src/useCursor/useCursor.ts +++ b/src/useCursor/useCursor.ts @@ -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({ row: 0, col: 0 }) const mode = useRef('normal') const countRef = useRef(""); const lastMotionRef = useRef(null); const lastKeyRef = useRef(""); + const hero = useHero(board, gameStatus); + function findAnchorTarget(dir: "left" | "right") { const m = board.mazeRef.current; const pos = position.current; @@ -189,8 +192,8 @@ export function useCursor(board: BoardManager) { move(last.dc, last.dr, steps); } - function resetCount() { - countRef.current = ""; + function resetCount() { + countRef.current = ""; } function getCount() { @@ -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; } diff --git a/src/useHero/useHero.ts b/src/useCursor/useHero.ts similarity index 98% rename from src/useHero/useHero.ts rename to src/useCursor/useHero.ts index 2677f77..d9a1f15 100644 --- a/src/useHero/useHero.ts +++ b/src/useCursor/useHero.ts @@ -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; diff --git a/src/useHero/useHeroRender.ts b/src/useCursor/useHeroRender.ts similarity index 100% rename from src/useHero/useHeroRender.ts rename to src/useCursor/useHeroRender.ts diff --git a/src/useGame.ts b/src/useGame.ts index 8b5d835..d257815 100644 --- a/src/useGame.ts +++ b/src/useGame.ts @@ -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 = { diff --git a/src/useHero/index.ts b/src/useHero/index.ts deleted file mode 100644 index 87e29b7..0000000 --- a/src/useHero/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './useHero'; -export * from './useHeroRender';