import { Server } from 'socket.io' import { TSocket, ExtendedCharacter } from '../../utilities/types' import { CharacterMoveService } from '../../services/character/characterMoveService' import { ZoneEventTileService } from '../../services/zoneEventTileService' import { MovementValidator } from '../../utilities/movementValidator' import { SocketEmitter } from '../../utilities/socketEmitter' import prisma from '../../utilities/prisma' import { ZoneEventTile, ZoneEventTileTeleport } from '@prisma/client' import Rotation from '../../utilities/character/rotation' type ZoneEventTileWithTeleport = ZoneEventTile & { teleport: ZoneEventTileTeleport | null } export default class CharacterMoveEvent { private characterMoveService: CharacterMoveService private zoneEventTileService: ZoneEventTileService private movementValidator: MovementValidator private socketEmitter: SocketEmitter constructor( private readonly io: Server, private readonly socket: TSocket ) { this.characterMoveService = new CharacterMoveService() this.zoneEventTileService = new ZoneEventTileService() this.movementValidator = new MovementValidator() this.socketEmitter = new SocketEmitter(io, socket) } public listen(): void { this.socket.on('character:initMove', this.handleCharacterMove.bind(this)) } private async handleCharacterMove({ positionX, positionY }: { positionX: number; positionY: number }): Promise { const { character } = this.socket if (!character) { console.error('character:move error', 'Character not found') return } const path = await this.characterMoveService.calculatePath(character, positionX, positionY) if (!path) { this.socketEmitter.emitMoveError('No valid path found') return } await this.moveAlongPath(character, path) } private async moveAlongPath(character: ExtendedCharacter, path: Array<{ x: number; y: number }>): Promise { for (let i = 0; i < path.length - 1; i++) { const start = path[i]; const end = path[i + 1]; if (!(await this.movementValidator.isValidMove(character, end))) { break; } character.isMoving = true; character.rotation = Rotation.calculate(start.x, start.y, end.x, end.y); const zoneEventTile = await prisma.zoneEventTile.findFirst({ where: { zoneId: character.zoneId, type: 'TELEPORT', positionX: Math.floor(end.x), positionY: Math.floor(end.y) }, include: { teleport: true } }) as ZoneEventTileWithTeleport | null if (zoneEventTile) { await this.handleZoneEventTile(zoneEventTile) break } await this.characterMoveService.updatePosition(character, end) this.socketEmitter.emitCharacterMove(character) await this.characterMoveService.applyMovementDelay() } this.finalizeMovement(character) } private async handleZoneEventTile(zoneEventTile: ZoneEventTileWithTeleport): Promise { const { character } = this.socket if (!character) { console.error('character:move error', 'Character not found') return } const teleport = zoneEventTile.teleport if (teleport) { await this.zoneEventTileService.handleTeleport(this.io, this.socket, character, teleport) return } } private finalizeMovement(character: ExtendedCharacter): void { character.isMoving = false this.socketEmitter.emitCharacterMove(character) } }