server/src/socketEvents/zone/characterMove.ts

99 lines
3.6 KiB
TypeScript

import { Server } from 'socket.io'
import { TSocket, ZoneEventTileWithTeleport } from '../../utilities/types'
import { CharacterMoveService } from '../../services/character/characterMoveService'
import { ZoneEventTileService } from '../../services/zoneEventTileService'
import prisma from '../../utilities/prisma'
import Rotation from '../../utilities/character/rotation'
import { gameLogger } from '../../utilities/logger'
import ZoneManager from '../../managers/zoneManager'
import ZoneCharacter from '../../models/zoneCharacter'
export default class CharacterMove {
private readonly characterMoveService = new CharacterMoveService()
private readonly zoneEventTileService = new ZoneEventTileService()
private nextPath = new Map<number, { x: number; y: number }[]>()
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
public listen(): void {
this.socket.on('character:move', this.handleCharacterMove.bind(this))
}
private async handleCharacterMove({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
const zoneCharacter = ZoneManager.getCharacter(this.socket.characterId!)
if (!zoneCharacter?.character) {
gameLogger.error('character:move error', 'Character not found or not initialized')
return
}
const path = await this.characterMoveService.calculatePath(zoneCharacter.character, positionX, positionY)
if (!path) {
this.io.in(zoneCharacter.character.zoneId.toString()).emit('character:moveError', 'No valid path found')
return
}
if (!zoneCharacter.isMoving) {
zoneCharacter.isMoving = true
await this.moveAlongPath(zoneCharacter, path)
} else {
this.nextPath.set(zoneCharacter.character.id, path)
}
}
private async moveAlongPath(zoneCharacter: ZoneCharacter, path: Array<{ x: number; y: number }>): Promise<void> {
const { character } = zoneCharacter
for (let i = 0; i < path.length - 1; i++) {
const [start, end] = [path[i], path[i + 1]]
character.rotation = Rotation.calculate(start.x, start.y, end.x, end.y)
const zoneEventTile = await prisma.zoneEventTile.findFirst({
where: {
zoneId: character.zoneId,
positionX: Math.floor(end.x),
positionY: Math.floor(end.y)
},
include: { teleport: true }
})
if (zoneEventTile?.type === 'BLOCK') break
if (zoneEventTile?.type === 'TELEPORT' && zoneEventTile.teleport) {
await this.handleZoneEventTile(zoneEventTile as ZoneEventTileWithTeleport)
break
}
this.characterMoveService.updatePosition(character, end)
this.io.in(character.zoneId.toString()).emit('character:move', zoneCharacter)
await this.characterMoveService.applyMovementDelay()
}
const nextPath = this.nextPath.get(character.id)
if (nextPath) {
this.nextPath.delete(character.id)
await this.moveAlongPath(zoneCharacter, nextPath)
} else {
this.finalizeMovement(zoneCharacter)
}
}
private async handleZoneEventTile(zoneEventTile: ZoneEventTileWithTeleport): Promise<void> {
const zoneCharacter = ZoneManager.getCharacter(this.socket.characterId!)
if (!zoneCharacter) {
gameLogger.error('character:move error', 'Character not found')
return
}
if (zoneEventTile.teleport) {
await this.zoneEventTileService.handleTeleport(this.io, this.socket, zoneCharacter.character, zoneEventTile.teleport)
}
}
private finalizeMovement(zoneCharacter: ZoneCharacter): void {
zoneCharacter.isMoving = false
this.io.in(zoneCharacter.character.zoneId.toString()).emit('character:move', zoneCharacter)
}
}