diff --git a/src/managers/mapManager.ts b/src/managers/mapManager.ts index 4853ceb..4f846a8 100644 --- a/src/managers/mapManager.ts +++ b/src/managers/mapManager.ts @@ -15,16 +15,20 @@ class MapManager { await mapRepository.getEntityManager().populate(maps, mapRepository.POPULATE_ALL as never[]) await Promise.all(maps.map((map) => this.loadMap(map))) - this.logger.info(`Map manager loaded with ${Object.keys(this.maps).length} maps`) } - public async loadMap(map: Map): Promise { - this.maps[map.id] = new LoadedMap(map) + public async loadMap(map: Map): Promise { + const loadedMap = new LoadedMap(map) + this.maps[map.id] = loadedMap this.logger.info(`Map ID ${map.id} loaded`) + return loadedMap } public unloadMap(mapId: UUID): void { + const map = this.maps[mapId] + if (!map) return + delete this.maps[mapId] this.logger.info(`Map ID ${mapId} unloaded`) } @@ -38,11 +42,11 @@ class MapManager { } public getCharacterById(characterId: UUID): MapCharacter | null { - return ( - Object.values(this.maps) - .flatMap((map) => map.getCharactersInMap()) - .find((char) => char.character.id === characterId) ?? null - ) + for (const map of Object.values(this.maps)) { + const character = map.getCharacterById(characterId) + if (character) return character + } + return null } } diff --git a/src/models/loadedMap.ts b/src/models/loadedMap.ts index f710eec..a2c0c7a 100644 --- a/src/models/loadedMap.ts +++ b/src/models/loadedMap.ts @@ -16,17 +16,23 @@ class LoadedMap { return this.map } - public addCharacter(character: Character) { + public addCharacter(character: Character): MapCharacter { + const existingCharacter = this.getCharacterById(character.id) + if (existingCharacter) { + return existingCharacter + } + const mapCharacter = new MapCharacter(character) this.characters.push(mapCharacter) + return mapCharacter } - public async removeCharacter(id: UUID) { + public async removeCharacter(id: UUID): Promise { const mapCharacter = this.getCharacterById(id) - if (mapCharacter) { - await mapCharacter.savePosition() - this.characters = this.characters.filter((c) => c.character.id !== id) - } + if (!mapCharacter) return + + await mapCharacter.savePosition() + this.characters = this.characters.filter((c) => c.character.id !== id) } public getCharacterById(id: UUID): MapCharacter | undefined { @@ -38,12 +44,11 @@ class LoadedMap { } public async getGrid(): Promise { - let grid: number[][] = Array.from({ length: this.map.height }, () => Array.from({ length: this.map.width }, () => 0)) + const grid: number[][] = Array.from({ length: this.map.height }, () => Array.from({ length: this.map.width }, () => 0)) const mapEventTileRepository = new MapEventTileRepository() const eventTiles = await mapEventTileRepository.getAll(this.map.id) - // Set the grid values based on the event tiles, these are strings eventTiles.forEach((eventTile) => { if (eventTile.type === 'BLOCK') { grid[eventTile.positionY]![eventTile.positionX] = 1 diff --git a/src/services/characterTeleportService.ts b/src/services/characterTeleportService.ts index e3cf0e2..cf1d0c9 100644 --- a/src/services/characterTeleportService.ts +++ b/src/services/characterTeleportService.ts @@ -21,72 +21,25 @@ class CharacterTeleportService { private readonly logger = Logger.type(LoggerType.GAME) public async teleportCharacter(characterId: UUID, options: TeleportOptions): Promise { - const mapRepository = new MapRepository() - const socket = SocketManager.getSocketByCharacterId(characterId) - const targetMap = MapManager.getMapById(options.targetMapId) - - if (!socket || !targetMap) { - this.logger.error(`Teleport failed - Missing socket or target map for character ${characterId}`) - return false - } - - if (options.isInitialJoin && !options.character) { - this.logger.error('Initial join requires character data') - return false - } - - const existingCharacter = !options.isInitialJoin && MapManager.getCharacterById(characterId) - const mapCharacter = options.isInitialJoin - ? new MapCharacter(options.character!) - : existingCharacter || - (() => { - this.logger.error(`Teleport failed - Character ${characterId} not found in MapManager`) - return null - })() - - if (!mapCharacter) return false - try { + const { socket, targetMap, mapCharacter } = await this.validateTeleportRequest(characterId, options) + if (!socket || !targetMap || !mapCharacter) return false + const currentMapId = mapCharacter.character.map?.id const currentMap = MapManager.getMapById(currentMapId!) const io = SocketManager.getIO() // Update character position and map - await mapCharacter - .getCharacter() - .setPositionX(options.targetX) - .setPositionY(options.targetY) - .setRotation(options.rotation ?? 0) - .setMap(targetMap.getMap()) - .save() + await this.updateCharacterPosition(mapCharacter, options, targetMap) - // If the current map is the target map and we are not joining, send move event + // Handle same map teleport if (currentMapId === options.targetMapId && !options.isInitialJoin) { - // If the current map is the target map, send move event CharacterMoveService.broadcastMovement(mapCharacter.character, false) return true } - // Handle current map cleanup - if (currentMapId && currentMap) { - socket.leave(currentMapId) - await currentMap.removeCharacter(characterId) - io.in(currentMapId).emit(SocketEvent.MAP_CHARACTER_LEAVE, characterId) - } - - // Join new map - socket.join(options.targetMapId) - targetMap.addCharacter(mapCharacter.getCharacter()) - - const map = await mapRepository.getById(options.targetMapId) - await mapRepository.getEntityManager().populate(map!, mapRepository.POPULATE_TELEPORT as any) - - // Notify clients - io.in(options.targetMapId).emit(SocketEvent.MAP_CHARACTER_JOIN, mapCharacter) - socket.emit(SocketEvent.MAP_CHARACTER_TELEPORT, { - mapId: options.targetMapId, - characters: targetMap.getCharactersInMap() - }) + // Handle map transition + await this.handleMapTransition(socket, io, currentMapId, currentMap, options.targetMapId, targetMap, characterId, mapCharacter) return true } catch (error) { @@ -94,6 +47,64 @@ class CharacterTeleportService { return false } } + + private async validateTeleportRequest(characterId: UUID, options: TeleportOptions) { + const socket = SocketManager.getSocketByCharacterId(characterId) + const targetMap = MapManager.getMapById(options.targetMapId) + + if (!socket || !targetMap) { + this.logger.error(`Teleport failed - Missing socket or target map for character ${characterId}`) + return { socket: null, targetMap: null, mapCharacter: null } + } + + if (options.isInitialJoin && !options.character) { + this.logger.error('Initial join requires character data') + return { socket, targetMap, mapCharacter: null } + } + + const existingCharacter = !options.isInitialJoin && MapManager.getCharacterById(characterId) + const mapCharacter = options.isInitialJoin ? new MapCharacter(options.character!) : existingCharacter || null + + if (!mapCharacter) { + this.logger.error(`Teleport failed - Character ${characterId} not found in MapManager`) + } + + return { socket, targetMap, mapCharacter } + } + + private async updateCharacterPosition(mapCharacter: MapCharacter, options: TeleportOptions, targetMap: any) { + await mapCharacter + .getCharacter() + .setPositionX(options.targetX) + .setPositionY(options.targetY) + .setRotation(options.rotation ?? 0) + .setMap(targetMap.getMap()) + .save() + } + + private async handleMapTransition(socket: any, io: any, currentMapId: UUID | undefined, currentMap: any, targetMapId: UUID, targetMap: any, characterId: UUID, mapCharacter: MapCharacter) { + // Clean up current map + if (currentMapId && currentMap) { + socket.leave(currentMapId) + await currentMap.removeCharacter(characterId) + io.in(currentMapId).emit(SocketEvent.MAP_CHARACTER_LEAVE, characterId) + } + + // Join new map + socket.join(targetMapId) + targetMap.addCharacter(mapCharacter.getCharacter()) + + const mapRepository = new MapRepository() + const map = await mapRepository.getById(targetMapId) + await mapRepository.getEntityManager().populate(map!, mapRepository.POPULATE_TELEPORT as any) + + // Notify clients + io.in(targetMapId).emit(SocketEvent.MAP_CHARACTER_JOIN, mapCharacter) + socket.emit(SocketEvent.MAP_CHARACTER_TELEPORT, { + mapId: targetMapId, + characters: targetMap.getCharactersInMap() + }) + } } export default new CharacterTeleportService()