import { BaseEvent } from '#application/base/baseEvent' import { MapEventTileType } from '#application/enums' import { UUID } from '#application/types' import { Map } from '#entities/map' import { MapEffect } from '#entities/mapEffect' import { MapEventTile } from '#entities/mapEventTile' import { MapEventTileTeleport } from '#entities/mapEventTileTeleport' import { PlacedMapObject } from '#entities/placedMapObject' import mapManager from '#managers/mapManager' import MapRepository from '#repositories/mapRepository' interface IPayload { mapId: UUID name: string width: number height: number tiles: string[][] pvp: boolean mapEventTiles: { type: MapEventTileType positionX: number positionY: number teleport?: { toMapId: UUID toPositionX: number toPositionY: number toRotation: number } }[] mapEffects: { effect: string strength: number }[] placedMapObjects: PlacedMapObject[] } export default class MapUpdateEvent extends BaseEvent { public listen(): void { this.socket.on('gm:map:update', this.handleEvent.bind(this)) } private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise { try { if (!(await this.isCharacterGM())) return const character = await this.getCharacter() this.logger.info(`User ${character!.getId()} has updated map via map editor.`) if (!data.mapId) { this.logger.info(`User ${character!.getId()} tried to update map but did not provide a map id.`) return callback(null) } let map = await MapRepository.getById(data.mapId) if (!map) { this.logger.info(`User ${character!.getId()} tried to update map ${data.mapId} but it does not exist.`) return callback(null) } // Validation logic remains the same if (data.tiles.length > data.height) { data.tiles = data.tiles.slice(0, data.height) } for (let i = 0; i < data.tiles.length; i++) { if (data.tiles[i].length > data.width) { data.tiles[i] = data.tiles[i].slice(0, data.width) } } data.mapEventTiles = data.mapEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height) data.placedMapObjects = data.placedMapObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height) // Clear existing collections map.mapEventTiles.removeAll() map.placedMapObjects.removeAll() map.mapEffects.removeAll() // Create and add new map event tiles for (const tile of data.mapEventTiles) { const mapEventTile = new MapEventTile().setType(tile.type).setPositionX(tile.positionX).setPositionY(tile.positionY).setMap(map) if (tile.teleport) { const teleport = new MapEventTileTeleport() .setToMap(await MapRepository.getById(tile.teleport.toMapId)) .setToPositionX(tile.teleport.toPositionX) .setToPositionY(tile.teleport.toPositionY) .setToRotation(tile.teleport.toRotation) mapEventTile.setTeleport(teleport) } map.mapEventTiles.add(mapEventTile) } // Create and add new map objects for (const object of data.placedMapObjects) { const mapObject = new PlacedMapObject() .setMapObject(object.mapObject) .setDepth(object.depth) .setIsRotated(object.isRotated) .setPositionX(object.positionX) .setPositionY(object.positionY) .setMap(map) map.placedMapObjects.add(mapObject) } // Create and add new map effects for (const effect of data.mapEffects) { const mapEffect = new MapEffect().setEffect(effect.effect).setStrength(effect.strength).setMap(map) map.mapEffects.add(mapEffect) } // Update map properties await map.setName(data.name).setWidth(data.width).setHeight(data.height).setTiles(data.tiles).setPvp(data.pvp).setUpdatedAt(new Date()).save() // Reload map from database to get fresh data map = await MapRepository.getById(data.mapId) if (!map) { this.logger.info(`User ${character!.getId()} tried to update map ${data.mapId} but it does not exist after update.`) return callback(null) } // Reload map for players mapManager.unloadMap(data.mapId) await mapManager.loadMap(map) return callback(map) } catch (error: any) { this.logger.error(`gm:mapObject:update error: ${error instanceof Error ? error.message : String(error)}`) return callback(null) } } }