import { Server } from 'socket.io' import { TSocket } from '../../utilities/Types' import ZoneManager from '../../managers/ZoneManager' 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' interface SocketResponse { position_x: number position_y: number } // Add a cancellation token let currentMoveToken: Symbol | null = null export default function setupCharacterMove(socket: TSocket, io: Server) { socket.on('character:move', async (data: SocketResponse) => { try { console.log('character:move requested', data) if (!socket.character) { console.error('character:move error', 'Character not found') return } // Cancel any ongoing movement currentMoveToken = Symbol('moveToken') const moveToken = currentMoveToken const grid = await ZoneManager.getGrid(socket.character.zoneId) if (grid.length === 0) { console.error('character:move error', 'Grid not found') return } const start = { x: socket.character.position_x, y: socket.character.position_y } const end = { x: data.position_x, y: data.position_y } const path = AStar.findPath(start, end, grid) if (path.length > 0) { socket.character.isMoving = true io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) try { await moveAlongPath(socket, io, path, grid, moveToken) } finally { if (currentMoveToken === moveToken) { socket.character.isMoving = false io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) } } } else { console.log('character:move error', 'No valid path found') } } catch (error) { console.error('character:move error', error) if (socket.character) { socket.character.isMoving = false io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) } } }) } async function moveAlongPath(socket: TSocket, io: Server, path: Node[], grid: number[][], moveToken: Symbol) { if (!socket.character) return for (let i = 0; i < path.length; i++) { // Check if this movement has been cancelled if (currentMoveToken !== moveToken) { console.log('Movement cancelled, stopping current path') return } const position = path[i] if (isObstacle(position, grid)) { console.log('Obstacle encountered at', position) break } // Calculate rotation based on the next position in the path let rotation = socket.character.rotation if (i < path.length - 1) { const nextPosition = path[i + 1] rotation = Rotation.calculate(position.x, position.y, nextPosition.x, nextPosition.y) } await updateCharacterPosition(socket.character, position.x, position.y, rotation) ZoneManager.updateCharacterInZone(socket.character.zoneId, socket.character) io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) // Add a small delay between moves to avoid overwhelming the server await new Promise((resolve) => setTimeout(resolve, 250)) } } function isObstacle(position: Node, grid: number[][]): boolean { return grid[position.y][position.x] === 1 } async function updateCharacterPosition(character: Character, x: number, y: number, rotation: number) { character.position_x = x character.position_y = y character.rotation = rotation await prisma.character.update({ where: { id: character.id }, data: { position_x: x, position_y: y, rotation } }) }