diff --git a/package-lock.json b/package-lock.json index 5f728a2..629d62a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -706,9 +706,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "license": "MIT", "dependencies": { "acorn": "^8.11.0" diff --git a/src/events/chat/sendMessage.ts b/src/events/chat/sendMessage.ts index 0288439..4979432 100644 --- a/src/events/chat/sendMessage.ts +++ b/src/events/chat/sendMessage.ts @@ -51,4 +51,4 @@ export default class ChatMessageEvent { callback(false) } } -} \ No newline at end of file +} diff --git a/src/events/gameMaster/assetManager/sprite/delete.ts b/src/events/gameMaster/assetManager/sprite/delete.ts index 43981a7..2377686 100644 --- a/src/events/gameMaster/assetManager/sprite/delete.ts +++ b/src/events/gameMaster/assetManager/sprite/delete.ts @@ -56,4 +56,4 @@ export default class GMSpriteDeleteEvent { } }) } -} \ No newline at end of file +} diff --git a/src/events/gameMaster/assetManager/tile/delete.ts b/src/events/gameMaster/assetManager/tile/delete.ts index 8093aa7..5f792c2 100644 --- a/src/events/gameMaster/assetManager/tile/delete.ts +++ b/src/events/gameMaster/assetManager/tile/delete.ts @@ -61,4 +61,4 @@ export default class GMTileDeleteEvent { logger.warn(`File ${finalFilePath} does not exist.`) } } -} \ No newline at end of file +} diff --git a/src/events/zone/characterMoveEvent.ts b/src/events/zone/characterMoveEvent.ts index a86b75f..7292530 100644 --- a/src/events/zone/characterMoveEvent.ts +++ b/src/events/zone/characterMoveEvent.ts @@ -16,7 +16,7 @@ export default class CharacterMoveEvent { private characterMoveService: CharacterMoveService private zoneEventTileService: ZoneEventTileService private movementValidator: MovementValidator - private nextPath: {[index: number]: {x: number, y: number}[]} = []; + private nextPath: { [index: number]: { x: number; y: number }[] } = [] constructor( private readonly io: Server, @@ -38,16 +38,15 @@ export default class CharacterMoveEvent { return } - const path = await this.characterMoveService.calculatePath(character, positionX, positionY) if (!path) { this.io.in(character.zoneId.toString()).emit('character:moveError', 'No valid path found') return } - if(character.isMoving && !character.resetMovement) { - character.resetMovement = true; - this.nextPath[character.id] = path; + if (character.isMoving && !character.resetMovement) { + character.resetMovement = true + this.nextPath[character.id] = path } else { await this.moveAlongPath(character, path) } @@ -55,26 +54,26 @@ export default class CharacterMoveEvent { private async moveAlongPath(character: ExtendedCharacter, path: Array<{ x: number; y: number }>): Promise { for (let i = 0; i < path.length - 1; i++) { - const start = path[i]; - const end = path[i + 1]; + const start = path[i] + const end = path[i + 1] if (!(await this.movementValidator.isValidMove(character, end))) { - break; + break } - if(character.isMoving && character.resetMovement) { - character.isMoving = false; - character.resetMovement = false; - const nextPath = this.nextPath[character.id]; - this.moveAlongPath(character, nextPath); - break; + if (character.isMoving && character.resetMovement) { + character.isMoving = false + character.resetMovement = false + const nextPath = this.nextPath[character.id] + this.moveAlongPath(character, nextPath) + break } - if(!character.isMoving) { - character.isMoving = true; + if (!character.isMoving) { + character.isMoving = true } - character.rotation = Rotation.calculate(start.x, start.y, end.x, end.y); + character.rotation = Rotation.calculate(start.x, start.y, end.x, end.y) const zoneEventTile = await prisma.zoneEventTile.findFirst({ where: { @@ -82,33 +81,33 @@ export default class CharacterMoveEvent { positionX: Math.floor(end.x), positionY: Math.floor(end.y) } - }); + }) if (zoneEventTile) { if (zoneEventTile.type === 'BLOCK') { - break; + break } if (zoneEventTile.type === 'TELEPORT') { - const teleportTile = await prisma.zoneEventTile.findFirst({ + const teleportTile = (await prisma.zoneEventTile.findFirst({ where: { id: zoneEventTile.id }, include: { teleport: true } - }) as ZoneEventTileWithTeleport; + })) as ZoneEventTileWithTeleport if (teleportTile) { - await this.handleZoneEventTile(teleportTile); - break; + await this.handleZoneEventTile(teleportTile) + break } } } - await this.characterMoveService.updatePosition(character, end); - this.io.in(character.zoneId.toString()).emit('character:move', character); + await this.characterMoveService.updatePosition(character, end) + this.io.in(character.zoneId.toString()).emit('character:move', character) - await this.characterMoveService.applyMovementDelay(); + await this.characterMoveService.applyMovementDelay() } - this.finalizeMovement(character); + this.finalizeMovement(character) } private async handleZoneEventTile(zoneEventTile: ZoneEventTileWithTeleport): Promise { diff --git a/src/managers/zoneManager.ts b/src/managers/zoneManager.ts index 3f7bfe2..0d894ab 100644 --- a/src/managers/zoneManager.ts +++ b/src/managers/zoneManager.ts @@ -3,15 +3,10 @@ import ZoneRepository from '../repositories/zoneRepository' import ZoneService from '../services/zoneService' import zoneRepository from '../repositories/zoneRepository' import logger from '../utilities/logger' - -type TLoadedZone = { - zone: Zone - characters: Character[] - grid: number[][] -} +import LoadedZone from '../models/zone/loadedZone' class ZoneManager { - private loadedZones: TLoadedZone[] = [] + private loadedZones: LoadedZone[] = [] // Method to initialize zoneEditor manager public async boot() { @@ -31,149 +26,32 @@ class ZoneManager { // Method to handle individual zoneEditor loading public async loadZone(zone: Zone) { - const grid = await this.getGrid(zone.id) // Create the grid for the zoneEditor - this.loadedZones.push({ - zone, - characters: [], - grid - }) + const loadedZone = new LoadedZone(zone) + this.loadedZones.push(loadedZone) logger.info(`Zone ID ${zone.id} loaded`) } // Method to handle individual zoneEditor unloading public unloadZone(zoneId: number) { - this.loadedZones = this.loadedZones.filter((loadedZone) => { - return loadedZone.zone.id !== zoneId - }) + this.loadedZones = this.loadedZones.filter((loadedZone) => loadedZone.getZone().id !== zoneId) logger.info(`Zone ID ${zoneId} unloaded`) } // Getter for loaded zones - public getLoadedZones(): TLoadedZone[] { + public getLoadedZones(): LoadedZone[] { return this.loadedZones } - // Check if position is walkable - private isPositionWalkable(zoneId: number, x: number, y: number): boolean { - const loadedZone = this.loadedZones.find((lz) => lz.zone.id === zoneId) - if (!loadedZone) { - console.log(`Zone ${zoneId} not found in loadedZones`) - return false - } - if (!loadedZone.grid) { - console.log(`Grid for zone ${zoneId} is undefined`) - return false - } - if (!loadedZone.grid[y]) { - console.log(`Row ${y} in grid for zone ${zoneId} is undefined`) - return false - } - return loadedZone.grid[y][x] === 0 - } - public addCharacterToZone(zoneId: number, character: Character) { - console.log(`Adding character ${character.id} to zone ${zoneId}`) - console.log(`Character position: x=${character.positionX}, y=${character.positionY}`) - const loadedZone = this.loadedZones.find((loadedZone) => { - return loadedZone.zone.id === zoneId - }) - - if (!loadedZone) { - console.log(`Zone ${zoneId} not found in loadedZones`) - return - } - - if (this.isPositionWalkable(zoneId, character.positionX, character.positionY)) { - loadedZone.characters.push(character) - console.log(`Character ${character.id} added to zone ${zoneId}`) - } else { - // set position to 0,0 if not walkable - console.log(`Position (${character.positionX}, ${character.positionY}) is not walkable in zone ${zoneId}`) - character.positionX = 0 - character.positionY = 0 - loadedZone.characters.push(character) - } } public removeCharacterFromZone(zoneId: number, character: Character) { - const loadedZone = this.loadedZones.find((loadedZone) => { - return loadedZone.zone.id === zoneId - }) - if (loadedZone) { - loadedZone.characters = loadedZone.characters.filter((loadedCharacter) => { - return loadedCharacter.id !== character.id - }) - } - } - public updateCharacterInZone(zoneId: number, character: Character) { - const loadedZone = this.loadedZones.find((loadedZone) => { - return loadedZone.zone.id === zoneId - }) - if (loadedZone) { - const characterIndex = loadedZone.characters.findIndex((loadedCharacter) => { - return loadedCharacter.id === character.id - }) - if (characterIndex !== -1) { - loadedZone.characters[characterIndex] = character - } - } - } - - public getCharactersInZone(zoneId: number): Character[] { - const loadedZone = this.loadedZones.find((loadedZone) => { - return loadedZone.zone.id === zoneId - }) - return loadedZone ? loadedZone.characters : [] - } - - public async getGrid(zoneId: number): Promise { - const zone = this.loadedZones.find((z) => z.zone.id === zoneId) - if (zone) return zone.grid - - const loadedZone = await ZoneRepository.getById(zoneId) - if (!loadedZone) return [] - - let grid: number[][] = Array.from({ length: loadedZone.height }, () => Array.from({ length: loadedZone.width }, () => 0)) - - const eventTiles = await zoneRepository.getEventTiles(zoneId) - - // 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 - } - }) - - return grid } public async moveCharacterBetweenZones(oldZoneId: number, newZoneId: number, character: Character): Promise { - // Find the old and new zones - const oldZone = this.loadedZones.find(zone => zone.zone.id === oldZoneId); - const newZone = this.loadedZones.find(zone => zone.zone.id === newZoneId); - if (!oldZone || !newZone) { - logger.error(`Unable to move character ${character.id}: zones not found`); - return; - } - - // Remove character from old zone - oldZone.characters = oldZone.characters.filter(c => c.id !== character.id); - - // Check if the new position is walkable - if (this.isPositionWalkable(newZoneId, character.positionX, character.positionY)) { - newZone.characters.push(character); - } else { - // Set position to 0,0 if not walkable - logger.warn(`Position (${character.positionX}, ${character.positionY}) is not walkable in zone ${newZoneId}. Moving character to (0,0).`); - character.positionX = 0; - character.positionY = 0; - newZone.characters.push(character); - } - - logger.info(`Character ${character.id} moved from zone ${oldZoneId} to zone ${newZoneId}`); } } diff --git a/src/models/zone/loadedZone.ts b/src/models/zone/loadedZone.ts new file mode 100644 index 0000000..63f896d --- /dev/null +++ b/src/models/zone/loadedZone.ts @@ -0,0 +1,60 @@ +import { Character, Tile, Zone } from '@prisma/client' +import ZoneCharacter from './zoneCharacter' +import zoneRepository from '../../repositories/zoneRepository' +import { ExtendedCharacter } from '../../utilities/types' +import ZoneManager from '../../managers/zoneManager' + +class LoadedZone { + private readonly zone: Zone + private characters: ZoneCharacter[] = [] + // private readonly npcs: ZoneNPC[] = [] + private readonly grid: number[][] = [] + + constructor(zone: Zone) { + this.zone = zone + } + + public getZone(): Zone { + return this.zone + } + + public getCharacters(): ZoneCharacter[] { + return this.characters + } + + public addCharacter(character: Character): void { + const zoneCharacter = new ZoneCharacter(character) + this.characters.push(zoneCharacter) + } + + public removeCharacter(character: Character): void { + this.characters = this.characters.filter((zoneCharacter) => zoneCharacter.getCharacter().id !== character.id) + } + + public async getGrid(): Promise { + let grid: number[][] = Array.from({ length: this.zone..height }, () => Array.from({ length: this.zone.width }, () => 0)) + + const eventTiles = await zoneRepository.getEventTiles(this.zone.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 + } + }) + + return grid + } + + public async isPositionWalkable(position: { x: number; y: number }): Promise { + const grid = await this.getGrid() + if (!grid?.length) return false + + const gridX = Math.floor(position.x) + const gridY = Math.floor(position.y) + + return grid[gridY]?.[gridX] === 1 || grid[gridY]?.[Math.ceil(position.x)] === 1 || grid[Math.ceil(position.y)]?.[gridX] === 1 || grid[Math.ceil(position.y)]?.[Math.ceil(position.x)] === 1 + } +} + +export default LoadedZone \ No newline at end of file diff --git a/src/game/zone/zoneCharacter.ts b/src/models/zone/zoneCharacter.ts similarity index 87% rename from src/game/zone/zoneCharacter.ts rename to src/models/zone/zoneCharacter.ts index ef573e9..128de8e 100644 --- a/src/game/zone/zoneCharacter.ts +++ b/src/models/zone/zoneCharacter.ts @@ -1,6 +1,6 @@ import { Character } from '@prisma/client' -class ZoneCharacter { +export default class ZoneCharacter { private readonly character: Character private isMoving: boolean = false @@ -11,4 +11,4 @@ class ZoneCharacter { public getCharacter(): Character { return this.character } -} \ No newline at end of file +} diff --git a/src/services/zoneEventTileService.ts b/src/services/zoneEventTileService.ts index 2afefb9..ce99751 100644 --- a/src/services/zoneEventTileService.ts +++ b/src/services/zoneEventTileService.ts @@ -7,13 +7,13 @@ import { Server } from 'socket.io' export class ZoneEventTileService { public async handleTeleport(io: Server, socket: TSocket, character: ExtendedCharacter, teleport: ZoneEventTileTeleport): Promise { - if (teleport.toZoneId === character.zoneId) return; + if (teleport.toZoneId === character.zoneId) return - const zone = await ZoneRepository.getById(teleport.toZoneId); - if (!zone) return; + const zone = await ZoneRepository.getById(teleport.toZoneId) + if (!zone) return - const oldZoneId = character.zoneId; - const newZoneId = teleport.toZoneId; + const oldZoneId = character.zoneId + const newZoneId = teleport.toZoneId // Update character in database await prisma.character.update({ @@ -23,28 +23,28 @@ export class ZoneEventTileService { positionX: teleport.toPositionX, positionY: teleport.toPositionY } - }); + }) // Update local character object - character.zoneId = newZoneId; - character.positionX = teleport.toPositionX; - character.positionY = teleport.toPositionY; + character.zoneId = newZoneId + character.positionX = teleport.toPositionX + character.positionY = teleport.toPositionY // Atomic operation in ZoneManager - await ZoneManager.moveCharacterBetweenZones(oldZoneId, newZoneId, character as Character); + await ZoneManager.moveCharacterBetweenZones(oldZoneId, newZoneId, character as Character) // Emit events - io.to(oldZoneId.toString()).emit('zone:character:leave', character.id); - io.to(newZoneId.toString()).emit('zone:character:join', character); + io.to(oldZoneId.toString()).emit('zone:character:leave', character.id) + io.to(newZoneId.toString()).emit('zone:character:join', character) // Update socket rooms - socket.leave(oldZoneId.toString()); - socket.join(newZoneId.toString()); + socket.leave(oldZoneId.toString()) + socket.join(newZoneId.toString()) // Send teleport information to the client socket.emit('zone:teleport', { zone, characters: ZoneManager.getCharactersInZone(zone.id) - }); + }) } } diff --git a/src/utilities/character/movementValidator.ts b/src/utilities/character/movementValidator.ts index 66c930b..c20a41f 100644 --- a/src/utilities/character/movementValidator.ts +++ b/src/utilities/character/movementValidator.ts @@ -2,16 +2,5 @@ import { ExtendedCharacter } from '../types' import ZoneManager from '../../managers/zoneManager' export class MovementValidator { - public async isValidMove(character: ExtendedCharacter, position: { x: number; y: number }): Promise { - const grid = await ZoneManager.getGrid(character.zoneId) - if (!grid?.length) return false - return !this.isObstacle(position, grid) - } - - private isObstacle({ x, y }: { x: number; y: number }, grid: number[][]): boolean { - const gridX = Math.floor(x) - const gridY = Math.floor(y) - return grid[gridY]?.[gridX] === 1 || grid[gridY]?.[Math.ceil(x)] === 1 || grid[Math.ceil(y)]?.[gridX] === 1 || grid[Math.ceil(y)]?.[Math.ceil(x)] === 1 - } } diff --git a/src/utilities/character/rotation.ts b/src/utilities/character/rotation.ts index 43c8f15..2ff2fba 100644 --- a/src/utilities/character/rotation.ts +++ b/src/utilities/character/rotation.ts @@ -32,4 +32,4 @@ class Rotation { } } -export default Rotation \ No newline at end of file +export default Rotation