diff --git a/src/events/character/Move.ts b/src/events/character/Move.ts index 86f865f..e43f36f 100644 --- a/src/events/character/Move.ts +++ b/src/events/character/Move.ts @@ -5,14 +5,12 @@ import prisma from '../../utilities/Prisma' import { AStar, type Node } from '../../utilities/Player/AStar' import Rotation from '../../utilities/Player/Rotation' import { ExtendedCharacter as Character } from '../../utilities/Types' -import { start } from 'node:repl' interface SocketResponse { position_x: number position_y: number } -// Map to store movement tokens for each character const characterMoveTokens = new Map() export default function setupCharacterMove(socket: TSocket, io: Server) { @@ -25,19 +23,26 @@ export default function setupCharacterMove(socket: TSocket, io: Server) { return } - // Cancel any ongoing movement for this character + const oldMoveToken = characterMoveTokens.get(socket.character.id) + if (oldMoveToken) { + characterMoveTokens.delete(socket.character.id) + } + const moveToken = Symbol('moveToken') characterMoveTokens.set(socket.character.id, moveToken) const grid = await ZoneManager.getGrid(socket.character.zoneId) - if (grid.length === 0) { - console.error('character:move error', 'Grid not found') + if (!grid || grid.length === 0) { + console.error('character:move error', 'Grid not found or empty') return } - const start = { x: socket.character.position_x, y: socket.character.position_y } - const end = { x: data.position_x, y: data.position_y } + const start = { x: Math.floor(socket.character.position_x), y: Math.floor(socket.character.position_y) } + const end = { x: Math.floor(data.position_x), y: Math.floor(data.position_y) } + + console.log('Pathfinding from', start, 'to', end) + console.log('Grid dimensions:', grid.length, 'x', grid[0].length) const path = AStar.findPath(start, end, grid) @@ -45,10 +50,10 @@ export default function setupCharacterMove(socket: TSocket, io: Server) { socket.character.isMoving = true io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) - // Start movement in a non-blocking manner moveAlongPath(socket, io, path, grid, moveToken).catch(console.error) } else { console.log('character:move error', 'No valid path found') + socket.emit('character:moveError', 'No valid path found') } } catch (error) { console.error('character:move error', error) @@ -65,18 +70,23 @@ async function moveAlongPath(socket: TSocket, io: Server, path: Node[], grid: nu const totalSteps = path.length const updateInterval = 50 // milliseconds between updates - const totalDuration = totalSteps * 250 // total duration of movement for (let step = 0; step < totalSteps; step++) { - const startTime = Date.now() + if (characterMoveTokens.get(socket.character.id) !== moveToken) { + console.log('Movement cancelled for character', socket.character.id) + return + } - while (Date.now() - startTime < 250) { // 250ms per tile + const startTime = Date.now() + const stepDuration = 250 // 250ms per tile + + while (Date.now() - startTime < stepDuration) { if (characterMoveTokens.get(socket.character.id) !== moveToken) { console.log('Movement cancelled for character', socket.character.id) return } - const progress = (Date.now() - startTime) / 250 + const progress = (Date.now() - startTime) / stepDuration const currentPosition = interpolatePosition(path[step], path[step + 1] || path[step], progress) if (isObstacle(currentPosition, grid)) { @@ -97,8 +107,9 @@ async function moveAlongPath(socket: TSocket, io: Server, path: Node[], grid: nu } } - if (socket.character) { + if (socket.character && characterMoveTokens.get(socket.character.id) === moveToken) { socket.character.isMoving = false + characterMoveTokens.delete(socket.character.id) io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) } } diff --git a/src/utilities/Player/AStar.ts b/src/utilities/Player/AStar.ts index 4df0e92..afb9687 100644 --- a/src/utilities/Player/AStar.ts +++ b/src/utilities/Player/AStar.ts @@ -10,9 +10,6 @@ export interface Node extends Position { f: number } -/** - * A* pathfinding algorithm. - */ export class AStar { private static readonly ORTHOGONAL_DIRECTIONS: Position[] = [ { x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 } @@ -22,14 +19,6 @@ export class AStar { { x: -1, y: -1 }, { x: -1, y: 1 }, { x: 1, y: -1 }, { x: 1, y: 1 } ] - /** - * Finds the shortest path from start to end on the given grid. - * @param start - Start position. - * @param end - End position. - * @param grid - The grid representing the map (0 = open space, 1 = obstacle). - * @param allowDiagonal - Whether diagonal movements are allowed. - * @returns Array of `Node` representing the path from start to end. - */ static findPath(start: Position, end: Position, grid: number[][], allowDiagonal: boolean = false): Node[] { const openList: Node[] = [] const closedSet = new Set() @@ -103,8 +92,16 @@ export class AStar { private static isValidPosition(pos: Position, grid: number[][], end: Position): boolean { const { x, y } = pos - return x >= 0 && y >= 0 && x < grid.length && y < grid[0].length && - (grid[y][x] === 0 || (x === end.x && y === end.y)) + + if (!grid || grid.length === 0 || !Array.isArray(grid[0])) { + return false; + } + + const height = grid.length; + const width = grid[0].length; + + return x >= 0 && x < width && y >= 0 && y < height && + (grid[y][x] === 0 || (x === end.x && y === end.y)); } private static isInOpenList(openList: Node[], node: Position): boolean {