server/src/events/zone/characterMoveEvent.ts

132 lines
4.2 KiB
TypeScript

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/character/movementValidator'
import prisma from '../../utilities/prisma'
import { ZoneEventTile, ZoneEventTileTeleport } from '@prisma/client'
import Rotation from '../../utilities/character/rotation'
import logger from '../../utilities/logger'
type ZoneEventTileWithTeleport = ZoneEventTile & {
teleport: ZoneEventTileTeleport
}
export default class CharacterMoveEvent {
private characterMoveService: CharacterMoveService
private zoneEventTileService: ZoneEventTileService
private movementValidator: MovementValidator
private nextPath: { [index: number]: { x: number; y: number }[] } = []
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {
this.characterMoveService = new CharacterMoveService()
this.zoneEventTileService = new ZoneEventTileService()
this.movementValidator = new MovementValidator()
}
public listen(): void {
this.socket.on('character:initMove', this.handleCharacterMove.bind(this))
}
private async handleCharacterMove({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
const { character } = this.socket
if (!character) {
logger.error('character:move error', 'Character not found')
return
}
const path = await this.characterMoveService.calculatePath(character, positionX, positionY)
if (!path) {
this.io.in(character.zoneId.toString()).emit('character:moveError', 'No valid path found')
return
}
if (character.isMoving && !character.resetMovement) {
character.resetMovement = true
this.nextPath[character.id] = path
} else {
await this.moveAlongPath(character, path)
}
}
private async moveAlongPath(character: ExtendedCharacter, path: Array<{ x: number; y: number }>): Promise<void> {
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
}
if (character.isMoving && character.resetMovement) {
character.isMoving = false
character.resetMovement = false
const nextPath = this.nextPath[character.id]
this.moveAlongPath(character, nextPath)
break
}
if (!character.isMoving) {
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,
positionX: Math.floor(end.x),
positionY: Math.floor(end.y)
}
})
if (zoneEventTile) {
if (zoneEventTile.type === 'BLOCK') {
break
}
if (zoneEventTile.type === 'TELEPORT') {
const teleportTile = (await prisma.zoneEventTile.findFirst({
where: { id: zoneEventTile.id },
include: { teleport: true }
})) as ZoneEventTileWithTeleport
if (teleportTile) {
await this.handleZoneEventTile(teleportTile)
break
}
}
}
await this.characterMoveService.updatePosition(character, end)
this.io.in(character.zoneId.toString()).emit('character:move', character)
await this.characterMoveService.applyMovementDelay()
}
this.finalizeMovement(character)
}
private async handleZoneEventTile(zoneEventTile: ZoneEventTileWithTeleport): Promise<void> {
const { character } = this.socket
if (!character) {
logger.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.io.in(character.zoneId.toString()).emit('character:move', character)
}
}