117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
import { AStar } from '#application/character/aStar'
|
|
import ZoneManager from '#managers/zoneManager'
|
|
import Rotation from '#application/character/rotation'
|
|
import { appLogger, gameLogger } from '#application/logger'
|
|
import { Database } from '#application/database'
|
|
import { Character } from '#entities/character'
|
|
import UserRepository from '#repositories/userRepository'
|
|
import CharacterRepository from '#repositories/characterRepository'
|
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
|
import ZoneRepository from '#repositories/zoneRepository'
|
|
import { Zone } from '#entities/zone'
|
|
|
|
interface Position {
|
|
x: number
|
|
y: number
|
|
}
|
|
|
|
export class CharacterService {
|
|
private readonly MOVEMENT_DELAY_MS = 250
|
|
|
|
async create(name: string, userId: number) {
|
|
const user = await UserRepository.getById(userId)
|
|
if (!user) return null
|
|
|
|
const character = new Character()
|
|
character.name = name
|
|
character.user = user
|
|
|
|
return await character.save()
|
|
}
|
|
|
|
async updateHair(characterId: number, characterHairId: number | null) {
|
|
const character = await CharacterRepository.getById(characterId)
|
|
if (!character) return null
|
|
|
|
if (characterHairId === null) {
|
|
character.characterHair = undefined
|
|
return await character.save()
|
|
}
|
|
|
|
const characterHair = await CharacterHairRepository.getById(characterHairId)
|
|
character.characterHair = characterHair ?? undefined
|
|
|
|
return await character.save()
|
|
}
|
|
|
|
async deleteByUserIdAndId(userId: number, characterId: number): Promise<Character | null> {
|
|
try {
|
|
const character = await CharacterRepository.getByUserAndId(userId, characterId)
|
|
if (!character) return null
|
|
|
|
return await character.delete()
|
|
} catch (error: any) {
|
|
// Handle error
|
|
appLogger.error(`Failed to delete character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`)
|
|
return null
|
|
}
|
|
}
|
|
|
|
async updateCharacterPosition(id: number, positionX: number, positionY: number, rotation: number, zoneId: number) {
|
|
const character = await CharacterRepository.getById(id)
|
|
if (!character) return null
|
|
|
|
character.positionX = positionX
|
|
character.positionY = positionY
|
|
character.rotation = rotation
|
|
character.zone = await ZoneRepository.getById(zoneId) as Zone
|
|
|
|
await character.save()
|
|
|
|
return character
|
|
}
|
|
|
|
public updatePosition(character: Character, position: Position, newZoneId?: number): void {
|
|
if (!this.isValidPosition(position)) {
|
|
gameLogger.error(`Invalid position coordinates: ${position.x}, ${position.y}`)
|
|
}
|
|
|
|
Object.assign(character, {
|
|
positionX: position.x,
|
|
positionY: position.y,
|
|
rotation: Rotation.calculate(character.positionX, character.positionY, position.x, position.y),
|
|
zoneId: newZoneId ?? character.zone.id
|
|
})
|
|
}
|
|
|
|
public async calculatePath(character: Character, targetX: number, targetY: number): Promise<Position[] | null> {
|
|
const zone = ZoneManager.getZoneById(character.zone.id)
|
|
const grid = await zone?.getGrid()
|
|
|
|
if (!grid?.length) {
|
|
gameLogger.error('character:move error', 'Grid not found or empty')
|
|
return null
|
|
}
|
|
|
|
const start: Position = {
|
|
x: Math.floor(character.positionX),
|
|
y: Math.floor(character.positionY)
|
|
}
|
|
|
|
const end: Position = {
|
|
x: Math.floor(targetX),
|
|
y: Math.floor(targetY)
|
|
}
|
|
|
|
return AStar.findPath(start, end, grid)
|
|
}
|
|
|
|
public async applyMovementDelay(): Promise<void> {
|
|
await new Promise((resolve) => setTimeout(resolve, this.MOVEMENT_DELAY_MS))
|
|
}
|
|
|
|
private isValidPosition(position: Position): boolean {
|
|
return Number.isFinite(position.x) && Number.isFinite(position.y) && position.x >= 0 && position.y >= 0
|
|
}
|
|
}
|