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 { 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 { 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 { 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 } }