forked from noxious/server
Renamed zone > map
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import ZoneManager from '#managers/zoneManager'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import TeleportService from '#services/teleportService'
|
||||
@ -61,7 +61,7 @@ export default class CharacterConnectEvent extends BaseEvent {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
|
||||
await TeleportService.teleportCharacter(character.id, {
|
||||
targetZoneId: character.zone.id,
|
||||
targetMapId: character.map.id,
|
||||
targetX: character.positionX,
|
||||
targetY: character.positionY,
|
||||
rotation: character.rotation,
|
||||
@ -75,6 +75,6 @@ export default class CharacterConnectEvent extends BaseEvent {
|
||||
|
||||
private async checkForActiveCharacters(): Promise<boolean> {
|
||||
const characters = await CharacterRepository.getByUserId(this.socket.userId!)
|
||||
return characters?.some((char) => ZoneManager.getCharacterById(char.id)) ?? false
|
||||
return characters?.some((char) => MapManager.getCharacterById(char.id)) ?? false
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { ZCharacterCreate } from '#application/zodTypes'
|
||||
import { Character } from '#entities/character'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import UserRepository from '#repositories/userRepository'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
export default class CharacterCreateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
@ -37,10 +37,10 @@ export default class CharacterCreateEvent extends BaseEvent {
|
||||
}
|
||||
|
||||
// @TODO: Change to default location
|
||||
const zone = await ZoneRepository.getFirst()
|
||||
const map = await MapRepository.getFirst()
|
||||
|
||||
const newCharacter = new Character()
|
||||
await newCharacter.setName(data.name).setUser(user).setZone(zone!).save()
|
||||
await newCharacter.setName(data.name).setUser(user).setMap(map!).save()
|
||||
|
||||
if (!newCharacter) {
|
||||
return this.socket.emit('notification', { message: 'Failed to create character. Please try again (later).' })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import { Character } from '#entities/character'
|
||||
import { Zone } from '#entities/zone'
|
||||
import { Map } from '#entities/map'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
|
||||
type TypePayload = {
|
||||
@ -9,7 +9,7 @@ type TypePayload = {
|
||||
}
|
||||
|
||||
type TypeResponse = {
|
||||
zone: Zone
|
||||
map: Map
|
||||
characters: Character[]
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import ZoneManager from '#managers/zoneManager'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
import TeleportService from '#services/teleportService'
|
||||
|
||||
@ -16,13 +16,13 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
|
||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
||||
try {
|
||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||
if (!zoneCharacter) {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.error('chat:message error', 'Character not found')
|
||||
return
|
||||
}
|
||||
|
||||
const character = zoneCharacter.character
|
||||
const character = mapCharacter.character
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||
@ -36,16 +36,16 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
if (!args || args.length === 0 || args.length > 3) {
|
||||
this.socket.emit('notification', {
|
||||
title: 'Server message',
|
||||
message: 'Usage: /teleport <zoneId> [x] [y]'
|
||||
message: 'Usage: /teleport <mapId> [x] [y]'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const zoneId = args[0] as UUID
|
||||
const mapId = args[0] as UUID
|
||||
const targetX = args[1] ? parseInt(args[1], 10) : 0
|
||||
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
||||
|
||||
if (!zoneId || isNaN(targetX) || isNaN(targetY)) {
|
||||
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
|
||||
this.socket.emit('notification', {
|
||||
title: 'Server message',
|
||||
message: 'Invalid parameters. X and Y coordinates must be numbers.'
|
||||
@ -53,16 +53,16 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
return
|
||||
}
|
||||
|
||||
const zone = await ZoneRepository.getById(zoneId)
|
||||
if (!zone) {
|
||||
const map = await MapRepository.getById(mapId)
|
||||
if (!map) {
|
||||
this.socket.emit('notification', {
|
||||
title: 'Server message',
|
||||
message: 'Zone not found'
|
||||
message: 'Map not found'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (character.zone.id === zone.id && targetX === character.positionX && targetY === character.positionY) {
|
||||
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
|
||||
this.socket.emit('notification', {
|
||||
title: 'Server message',
|
||||
message: 'You are already at that location'
|
||||
@ -71,7 +71,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
}
|
||||
|
||||
const success = await TeleportService.teleportCharacter(character.id, {
|
||||
targetZoneId: zone.id,
|
||||
targetMapId: map.id,
|
||||
targetX,
|
||||
targetY,
|
||||
rotation: character.rotation
|
||||
@ -86,9 +86,9 @@ export default class TeleportCommandEvent extends BaseEvent {
|
||||
|
||||
this.socket.emit('notification', {
|
||||
title: 'Server message',
|
||||
message: `Teleported to ${zone.name} (${targetX}, ${targetY})`
|
||||
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
|
||||
})
|
||||
this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id} at position (${targetX}, ${targetY})`)
|
||||
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error in teleport command: ${error.message}`)
|
||||
this.socket.emit('notification', {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import ZoneManager from '#managers/zoneManager'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import ChatService from '#services/chatService'
|
||||
|
||||
type TypePayload = {
|
||||
@ -18,21 +18,21 @@ export default class ChatMessageEvent extends BaseEvent {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||
if (!zoneCharacter) {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.error('chat:message error', 'Character not found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const character = zoneCharacter.character
|
||||
const character = mapCharacter.character
|
||||
|
||||
const zone = await ZoneRepository.getById(character.zone.id)
|
||||
if (!zone) {
|
||||
this.logger.error('chat:message error', 'Zone not found')
|
||||
const map = await MapRepository.getById(character.map.id)
|
||||
if (!map) {
|
||||
this.logger.error('chat:message error', 'Map not found')
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
if (await ChatService.sendZoneMessage(character.getId(), zone.getId(), data.message)) {
|
||||
if (await ChatService.sendMapMessage(character.getId(), map.getId(), data.message)) {
|
||||
return callback(true)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import ZoneManager from '#managers/zoneManager'
|
||||
import MapManager from '#managers/mapManager'
|
||||
|
||||
export default class DisconnectEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
@ -15,13 +15,13 @@ export default class DisconnectEvent extends BaseEvent {
|
||||
|
||||
this.io.emit('user:disconnect', this.socket.userId)
|
||||
|
||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||
if (!zoneCharacter) {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.info('User disconnected but had no character set')
|
||||
return
|
||||
}
|
||||
|
||||
await zoneCharacter.disconnect(this.socket, this.io)
|
||||
await mapCharacter.disconnect(this.socket, this.io)
|
||||
this.logger.info('User disconnected along with their character')
|
||||
} catch (error: any) {
|
||||
this.logger.error('disconnect error: ' + error.message)
|
||||
|
@ -1,32 +1,18 @@
|
||||
import { Object } from '@prisma/client'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
import ObjectRepository from '#repositories/objectRepository'
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class ObjectListEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
export default class ObjectListEvent extends BaseEvent{
|
||||
public listen(): void {
|
||||
this.socket.on('gm:object:list', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Object[]) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback([])
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback([])
|
||||
}
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
// get all objects
|
||||
const objects = await ObjectRepository.getAll()
|
||||
callback(objects)
|
||||
return callback(objects)
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,10 @@
|
||||
import fs from 'fs/promises'
|
||||
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import Storage from '#application/storage'
|
||||
import { Sprite } from '#entities/sprite'
|
||||
|
||||
export default class SpriteCreateEvent extends BaseEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('gm:sprite:create', this.handleEvent.bind(this))
|
||||
}
|
||||
@ -24,21 +18,19 @@ export default class SpriteCreateEvent extends BaseEvent {
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
|
||||
const sprite = await prisma.sprite.create({
|
||||
data: {
|
||||
name: 'New sprite'
|
||||
}
|
||||
})
|
||||
const sprite = new Sprite()
|
||||
await sprite.setName('New sprite').save()
|
||||
|
||||
const uuid = sprite.id
|
||||
|
||||
// Create folder with uuid
|
||||
const sprite_folder = Storage.getPublicPath('sprites', uuid)
|
||||
await fs.mkdir(sprite_folder, { recursive: true })
|
||||
|
||||
callback(true)
|
||||
return callback(true)
|
||||
} catch (error) {
|
||||
console.error('Error creating sprite:', error)
|
||||
callback(false)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { Zone } from '#entities/zone'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
import { Map } from '#entities/map'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
type Payload = {
|
||||
name: string
|
||||
@ -8,30 +8,30 @@ type Payload = {
|
||||
height: number
|
||||
}
|
||||
|
||||
export default class ZoneCreateEvent extends BaseEvent {
|
||||
export default class MapCreateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:zone_editor:zone:create', this.handleEvent.bind(this))
|
||||
this.socket.on('gm:map_editor:map:create', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (response: Zone[]) => void): Promise<void> {
|
||||
private async handleEvent(data: Payload, callback: (response: Map[]) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
|
||||
|
||||
const zone = new Zone()
|
||||
await zone
|
||||
const map = new Map()
|
||||
await map
|
||||
.setName(data.name)
|
||||
.setWidth(data.width)
|
||||
.setHeight(data.height)
|
||||
.setTiles(Array.from({ length: data.height }, () => Array.from({ length: data.width }, () => 'blank_tile')))
|
||||
.save()
|
||||
|
||||
const zoneList = await ZoneRepository.getAll()
|
||||
return callback(zoneList)
|
||||
const mapList = await MapRepository.getAll()
|
||||
return callback(mapList)
|
||||
} catch (error: any) {
|
||||
this.logger.error('gm:zone_editor:zone:create error', error.message)
|
||||
this.socket.emit('notification', { message: 'Failed to create zone.' })
|
||||
this.logger.error('gm:map_editor:map:create error', error.message)
|
||||
this.socket.emit('notification', { message: 'Failed to create map.' })
|
||||
return callback([])
|
||||
}
|
||||
}
|
29
src/events/gameMaster/mapEditor/delete.ts
Normal file
29
src/events/gameMaster/mapEditor/delete.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
type Payload = {
|
||||
mapId: UUID
|
||||
}
|
||||
|
||||
export default class MapDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:map_editor:map:delete', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
try {
|
||||
this.logger.info(`Deleting map ${data.mapId}`)
|
||||
|
||||
await (await MapRepository.getById(data.mapId))?.delete()
|
||||
|
||||
this.logger.info(`Map ${data.mapId} deleted successfully.`)
|
||||
return callback(true)
|
||||
} catch (error: unknown) {
|
||||
this.logger.error('gm:map_editor:map:delete error', error)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
25
src/events/gameMaster/mapEditor/list.ts
Normal file
25
src/events/gameMaster/mapEditor/list.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { Map } from '#entities/map'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class MapListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:map_editor:map:list', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Map[]) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
|
||||
|
||||
const maps = await MapRepository.getAll()
|
||||
return callback(maps)
|
||||
} catch (error: any) {
|
||||
this.logger.error('gm:map_editor:map:list error', error.message)
|
||||
return callback([])
|
||||
}
|
||||
}
|
||||
}
|
39
src/events/gameMaster/mapEditor/request.ts
Normal file
39
src/events/gameMaster/mapEditor/request.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import { Map } from '#entities/map'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
|
||||
interface IPayload {
|
||||
mapId: UUID
|
||||
}
|
||||
|
||||
export default class MapRequestEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:map_editor:map:request', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has requested map via map editor.`)
|
||||
|
||||
if (!data.mapId) {
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map but did not provide a map id.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
const map = await MapRepository.getById(data.mapId)
|
||||
|
||||
if (!map) {
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
return callback(map)
|
||||
} catch (error: any) {
|
||||
this.logger.error('gm:map_editor:map:request error', error.message)
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
}
|
132
src/events/gameMaster/mapEditor/update.ts
Normal file
132
src/events/gameMaster/mapEditor/update.ts
Normal file
@ -0,0 +1,132 @@
|
||||
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 { MapObject } from '#entities/mapObject'
|
||||
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
|
||||
}[]
|
||||
mapObjects: MapObject[]
|
||||
}
|
||||
|
||||
export default class MapUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:map_editor:map:update', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||
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.mapObjects = data.mapObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
|
||||
|
||||
// Clear existing collections
|
||||
map.mapEventTiles.removeAll()
|
||||
map.mapObjects.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.mapObjects) {
|
||||
const mapObject = new MapObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setMap(map)
|
||||
|
||||
map.mapObjects.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()).update()
|
||||
|
||||
// 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:map_editor:map:update error: ${error instanceof Error ? error.message : String(error)}`)
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
|
||||
type Payload = {
|
||||
zoneId: UUID
|
||||
}
|
||||
|
||||
export default class ZoneDeleteEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:zone_editor:zone:delete', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
try {
|
||||
this.logger.info(`Deleting zone ${data.zoneId}`)
|
||||
|
||||
await (await ZoneRepository.getById(data.zoneId))?.delete()
|
||||
|
||||
this.logger.info(`Zone ${data.zoneId} deleted successfully.`)
|
||||
return callback(true)
|
||||
} catch (error: unknown) {
|
||||
this.logger.error('gm:zone_editor:zone:delete error', error)
|
||||
return callback(false)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { Zone } from '#entities/zone'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
export default class ZoneListEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:zone_editor:zone:list', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Zone[]) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
|
||||
|
||||
const zones = await ZoneRepository.getAll()
|
||||
return callback(zones)
|
||||
} catch (error: any) {
|
||||
this.logger.error('gm:zone_editor:zone:list error', error.message)
|
||||
return callback([])
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { UUID } from '#application/types'
|
||||
import { Zone } from '#entities/zone'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
|
||||
interface IPayload {
|
||||
zoneId: UUID
|
||||
}
|
||||
|
||||
export default class ZoneRequestEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:zone_editor:zone:request', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} has requested zone via zone editor.`)
|
||||
|
||||
if (!data.zoneId) {
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone but did not provide a zone id.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
const zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone ${data.zoneId} but it does not exist.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
return callback(zone)
|
||||
} catch (error: any) {
|
||||
this.logger.error('gm:zone_editor:zone:request error', error.message)
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { ZoneEventTileType } from '#application/enums'
|
||||
import { UUID } from '#application/types'
|
||||
import { Zone } from '#entities/zone'
|
||||
import { ZoneEffect } from '#entities/zoneEffect'
|
||||
import { ZoneEventTile } from '#entities/zoneEventTile'
|
||||
import { ZoneEventTileTeleport } from '#entities/zoneEventTileTeleport'
|
||||
import { ZoneObject } from '#entities/zoneObject'
|
||||
import zoneManager from '#managers/zoneManager'
|
||||
import ZoneRepository from '#repositories/zoneRepository'
|
||||
|
||||
interface IPayload {
|
||||
zoneId: UUID
|
||||
name: string
|
||||
width: number
|
||||
height: number
|
||||
tiles: string[][]
|
||||
pvp: boolean
|
||||
zoneEventTiles: {
|
||||
type: ZoneEventTileType
|
||||
positionX: number
|
||||
positionY: number
|
||||
teleport?: {
|
||||
toZoneId: UUID
|
||||
toPositionX: number
|
||||
toPositionY: number
|
||||
toRotation: number
|
||||
}
|
||||
}[]
|
||||
zoneEffects: {
|
||||
effect: string
|
||||
strength: number
|
||||
}[]
|
||||
zoneObjects: ZoneObject[]
|
||||
}
|
||||
|
||||
export default class ZoneUpdateEvent extends BaseEvent {
|
||||
public listen(): void {
|
||||
this.socket.on('gm:zone_editor:zone:update', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
||||
try {
|
||||
if (!(await this.isCharacterGM())) return
|
||||
|
||||
const character = await this.getCharacter()
|
||||
this.logger.info(`User ${character!.getId()} has updated zone via zone editor.`)
|
||||
|
||||
if (!data.zoneId) {
|
||||
this.logger.info(`User ${character!.getId()} tried to update zone but did not provide a zone id.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
let zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} 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.zoneEventTiles = data.zoneEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
|
||||
|
||||
data.zoneObjects = data.zoneObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
|
||||
|
||||
// Clear existing collections
|
||||
zone.zoneEventTiles.removeAll()
|
||||
zone.zoneObjects.removeAll()
|
||||
zone.zoneEffects.removeAll()
|
||||
|
||||
// Create and add new zone event tiles
|
||||
for (const tile of data.zoneEventTiles) {
|
||||
const zoneEventTile = new ZoneEventTile().setType(tile.type).setPositionX(tile.positionX).setPositionY(tile.positionY).setZone(zone)
|
||||
|
||||
if (tile.teleport) {
|
||||
const teleport = new ZoneEventTileTeleport()
|
||||
.setToZone((await ZoneRepository.getById(tile.teleport.toZoneId))!)
|
||||
.setToPositionX(tile.teleport.toPositionX)
|
||||
.setToPositionY(tile.teleport.toPositionY)
|
||||
.setToRotation(tile.teleport.toRotation)
|
||||
|
||||
zoneEventTile.setTeleport(teleport)
|
||||
}
|
||||
|
||||
zone.zoneEventTiles.add(zoneEventTile)
|
||||
}
|
||||
|
||||
// Create and add new zone objects
|
||||
for (const object of data.zoneObjects) {
|
||||
const zoneObject = new ZoneObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setZone(zone)
|
||||
|
||||
zone.zoneObjects.add(zoneObject)
|
||||
}
|
||||
|
||||
// Create and add new zone effects
|
||||
for (const effect of data.zoneEffects) {
|
||||
const zoneEffect = new ZoneEffect().setEffect(effect.effect).setStrength(effect.strength).setZone(zone)
|
||||
|
||||
zone.zoneEffects.add(zoneEffect)
|
||||
}
|
||||
|
||||
// Update zone properties
|
||||
await zone.setName(data.name).setWidth(data.width).setHeight(data.height).setTiles(data.tiles).setPvp(data.pvp).setUpdatedAt(new Date()).update()
|
||||
|
||||
// Reload zone from database to get fresh data
|
||||
zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} but it does not exist after update.`)
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
// Reload zone for players
|
||||
zoneManager.unloadZone(data.zoneId)
|
||||
await zoneManager.loadZone(zone)
|
||||
|
||||
return callback(zone)
|
||||
} catch (error: any) {
|
||||
this.logger.error(`gm:zone_editor:zone:update error: ${error instanceof Error ? error.message : String(error)}`)
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
}
|
104
src/events/map/characterMove.ts
Normal file
104
src/events/map/characterMove.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { MapEventTileWithTeleport } from '#application/types'
|
||||
import MapManager from '#managers/mapManager'
|
||||
import MapCharacter from '#models/mapCharacter'
|
||||
import mapEventTileRepository from '#repositories/mapEventTileRepository'
|
||||
import CharacterService from '#services/characterService'
|
||||
import MapEventTileService from '#services/mapEventTileService'
|
||||
|
||||
export default class CharacterMove extends BaseEvent {
|
||||
private readonly characterService = CharacterService
|
||||
private readonly mapEventTileService = MapEventTileService
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('map:character:move', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter?.character) {
|
||||
this.logger.error('map:character:move error: Character not found or not initialized')
|
||||
return
|
||||
}
|
||||
|
||||
// If already moving, cancel current movement and wait for it to fully stop
|
||||
if (mapCharacter.isMoving) {
|
||||
mapCharacter.isMoving = false
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
|
||||
const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY)
|
||||
if (!path) {
|
||||
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'No valid path found')
|
||||
return
|
||||
}
|
||||
|
||||
// Start new movement
|
||||
mapCharacter.isMoving = true
|
||||
mapCharacter.currentPath = path // Add this property to MapCharacter class
|
||||
await this.moveAlongPath(mapCharacter, path)
|
||||
}
|
||||
|
||||
private async moveAlongPath(mapCharacter: MapCharacter, path: Array<{ x: number; y: number }>): Promise<void> {
|
||||
const { character } = mapCharacter
|
||||
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
if (!mapCharacter.isMoving || mapCharacter.currentPath !== path) {
|
||||
return
|
||||
}
|
||||
|
||||
const [start, end] = [path[i], path[i + 1]]
|
||||
character.rotation = CharacterService.calculateRotation(start.x, start.y, end.x, end.y)
|
||||
|
||||
const mapEventTile = await mapEventTileRepository.getEventTileByMapIdAndPosition(character.map.id, Math.floor(end.x), Math.floor(end.y))
|
||||
|
||||
if (mapEventTile?.type === 'BLOCK') break
|
||||
if (mapEventTile?.type === 'TELEPORT' && mapEventTile.teleport) {
|
||||
await this.handleMapEventTile(mapEventTile as MapEventTileWithTeleport)
|
||||
break
|
||||
}
|
||||
|
||||
// Update position first
|
||||
character.positionX = end.x
|
||||
character.positionY = end.y
|
||||
|
||||
// Then emit with the same properties
|
||||
this.io.in(character.map.id).emit('map:character:move', {
|
||||
characterId: character.id,
|
||||
positionX: character.positionX,
|
||||
positionY: character.positionY,
|
||||
rotation: character.rotation,
|
||||
isMoving: true
|
||||
})
|
||||
|
||||
await this.characterService.applyMovementDelay()
|
||||
}
|
||||
|
||||
if (mapCharacter.isMoving && mapCharacter.currentPath === path) {
|
||||
this.finalizeMovement(mapCharacter)
|
||||
}
|
||||
}
|
||||
|
||||
private async handleMapEventTile(mapEventTile: MapEventTileWithTeleport): Promise<void> {
|
||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||
if (!mapCharacter) {
|
||||
this.logger.error('map:character:move error: Character not found')
|
||||
return
|
||||
}
|
||||
|
||||
if (mapEventTile.teleport) {
|
||||
await this.mapEventTileService.handleTeleport(this.io, this.socket, mapCharacter.character, mapEventTile.teleport)
|
||||
}
|
||||
}
|
||||
|
||||
private finalizeMovement(mapCharacter: MapCharacter): void {
|
||||
mapCharacter.isMoving = false
|
||||
this.io.in(mapCharacter.character.map.id).emit('map:character:move', {
|
||||
characterId: mapCharacter.character.id,
|
||||
positionX: mapCharacter.character.positionX,
|
||||
positionY: mapCharacter.character.positionY,
|
||||
rotation: mapCharacter.character.rotation,
|
||||
isMoving: false
|
||||
})
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
import { BaseEvent } from '#application/base/baseEvent'
|
||||
import { ZoneEventTileWithTeleport } from '#application/types'
|
||||
import ZoneManager from '#managers/zoneManager'
|
||||
import ZoneCharacter from '#models/zoneCharacter'
|
||||
import zoneEventTileRepository from '#repositories/zoneEventTileRepository'
|
||||
import CharacterService from '#services/characterService'
|
||||
import ZoneEventTileService from '#services/zoneEventTileService'
|
||||
|
||||
export default class CharacterMove extends BaseEvent {
|
||||
private readonly characterService = CharacterService
|
||||
private readonly zoneEventTileService = ZoneEventTileService
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('zone:character:move', this.handleEvent.bind(this))
|
||||
}
|
||||
|
||||
private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
|
||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||
if (!zoneCharacter?.character) {
|
||||
this.logger.error('zone:character:move error: Character not found or not initialized')
|
||||
return
|
||||
}
|
||||
|
||||
// If already moving, cancel current movement and wait for it to fully stop
|
||||
if (zoneCharacter.isMoving) {
|
||||
zoneCharacter.isMoving = false
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
|
||||
const path = await this.characterService.calculatePath(zoneCharacter.character, positionX, positionY)
|
||||
if (!path) {
|
||||
this.io.in(zoneCharacter.character.zone.id).emit('zone:character:moveError', 'No valid path found')
|
||||
return
|
||||
}
|
||||
|
||||
// Start new movement
|
||||
zoneCharacter.isMoving = true
|
||||
zoneCharacter.currentPath = path // Add this property to ZoneCharacter class
|
||||
await this.moveAlongPath(zoneCharacter, path)
|
||||
}
|
||||
|
||||
private async moveAlongPath(zoneCharacter: ZoneCharacter, path: Array<{ x: number; y: number }>): Promise<void> {
|
||||
const { character } = zoneCharacter
|
||||
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
if (!zoneCharacter.isMoving || zoneCharacter.currentPath !== path) {
|
||||
return
|
||||
}
|
||||
|
||||
const [start, end] = [path[i], path[i + 1]]
|
||||
character.rotation = CharacterService.calculateRotation(start.x, start.y, end.x, end.y)
|
||||
|
||||
const zoneEventTile = await zoneEventTileRepository.getEventTileByZoneIdAndPosition(character.zone.id, Math.floor(end.x), Math.floor(end.y))
|
||||
|
||||
if (zoneEventTile?.type === 'BLOCK') break
|
||||
if (zoneEventTile?.type === 'TELEPORT' && zoneEventTile.teleport) {
|
||||
await this.handleZoneEventTile(zoneEventTile as ZoneEventTileWithTeleport)
|
||||
break
|
||||
}
|
||||
|
||||
// Update position first
|
||||
character.positionX = end.x
|
||||
character.positionY = end.y
|
||||
|
||||
// Then emit with the same properties
|
||||
this.io.in(character.zone.id).emit('zone:character:move', {
|
||||
characterId: character.id,
|
||||
positionX: character.positionX,
|
||||
positionY: character.positionY,
|
||||
rotation: character.rotation,
|
||||
isMoving: true
|
||||
})
|
||||
|
||||
await this.characterService.applyMovementDelay()
|
||||
}
|
||||
|
||||
if (zoneCharacter.isMoving && zoneCharacter.currentPath === path) {
|
||||
this.finalizeMovement(zoneCharacter)
|
||||
}
|
||||
}
|
||||
|
||||
private async handleZoneEventTile(zoneEventTile: ZoneEventTileWithTeleport): Promise<void> {
|
||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||
if (!zoneCharacter) {
|
||||
this.logger.error('zone:character:move error: Character not found')
|
||||
return
|
||||
}
|
||||
|
||||
if (zoneEventTile.teleport) {
|
||||
await this.zoneEventTileService.handleTeleport(this.io, this.socket, zoneCharacter.character, zoneEventTile.teleport)
|
||||
}
|
||||
}
|
||||
|
||||
private finalizeMovement(zoneCharacter: ZoneCharacter): void {
|
||||
zoneCharacter.isMoving = false
|
||||
this.io.in(zoneCharacter.character.zone.id).emit('zone:character:move', {
|
||||
characterId: zoneCharacter.character.id,
|
||||
positionX: zoneCharacter.character.positionX,
|
||||
positionY: zoneCharacter.character.positionY,
|
||||
rotation: zoneCharacter.character.rotation,
|
||||
isMoving: false
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user