Worked on character animations

This commit is contained in:
Dennis Postma 2024-07-30 23:31:11 +02:00
parent bbeac95512
commit 7f9ca66c0e
5 changed files with 46 additions and 76 deletions

View File

@ -1,7 +1,6 @@
import { Socket, Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../utilities/Types' import { TSocket, ExtendedCharacter } from '../../utilities/Types'
import CharacterRepository from '../../repositories/CharacterRepository' import CharacterRepository from '../../repositories/CharacterRepository'
import { Character, User } from '@prisma/client'
type SocketResponseT = { type SocketResponseT = {
character_id: number character_id: number
@ -11,7 +10,7 @@ export default function (socket: TSocket, io: Server) {
socket.on('character:connect', async (data: SocketResponseT) => { socket.on('character:connect', async (data: SocketResponseT) => {
console.log('character:connect requested', data) console.log('character:connect requested', data)
try { try {
socket.character = (await CharacterRepository.getByUserAndId(socket.user?.id as number, data.character_id)) as Character socket.character = (await CharacterRepository.getByUserAndId(socket.user?.id as number, data.character_id)) as ExtendedCharacter
socket.emit('character:connect', socket.character) socket.emit('character:connect', socket.character)
} catch (error: any) { } catch (error: any) {
console.log('character:connect error', error) console.log('character:connect error', error)

View File

@ -2,22 +2,15 @@ import { Server } from 'socket.io'
import { TSocket } from '../../utilities/Types' import { TSocket } from '../../utilities/Types'
import ZoneManager from '../../managers/ZoneManager' import ZoneManager from '../../managers/ZoneManager'
import prisma from '../../utilities/Prisma' import prisma from '../../utilities/Prisma'
import AStar, { type Node } from '../../utilities/Player/AStar' import { AStar, type Node } from '../../utilities/Player/AStar'
import Rotation from '../../utilities/Player/Rotation' import Rotation from '../../utilities/Player/Rotation'
import { ExtendedCharacter as Character } from '../../utilities/Types'
interface SocketResponse { interface SocketResponse {
position_x: number position_x: number
position_y: number position_y: number
} }
interface Character {
id: number
position_x: number
position_y: number
rotation: number
zoneId: number
}
export default function setupCharacterMove(socket: TSocket, io: Server) { export default function setupCharacterMove(socket: TSocket, io: Server) {
socket.on('character:move', async (data: SocketResponse) => { socket.on('character:move', async (data: SocketResponse) => {
try { try {
@ -41,12 +34,23 @@ export default function setupCharacterMove(socket: TSocket, io: Server) {
const path = AStar.findPath(start, end, grid) const path = AStar.findPath(start, end, grid)
if (path.length > 0) { if (path.length > 0) {
await moveAlongPath(socket, io, path, grid) socket.character.isMoving = true
io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character)
try {
await moveAlongPath(socket, io, path, grid)
} finally {
socket.character.isMoving = false
io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character)
}
} else { } else {
console.log('character:move error', 'No valid path found') console.log('character:move error', 'No valid path found')
} }
} catch (error) { } catch (error) {
console.error('character:move error', error) console.error('character:move error', error)
if (socket.character) {
socket.character.isMoving = false
io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character)
}
} }
}) })
} }
@ -73,8 +77,6 @@ async function moveAlongPath(socket: TSocket, io: Server, path: Node[], grid: nu
ZoneManager.updateCharacterInZone(socket.character.zoneId, socket.character) ZoneManager.updateCharacterInZone(socket.character.zoneId, socket.character)
io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character) io.in(socket.character.zoneId.toString()).emit('character:moved', socket.character)
console.log('Character moved to', position)
// Add a small delay between moves to avoid overwhelming the server // Add a small delay between moves to avoid overwhelming the server
await new Promise((resolve) => setTimeout(resolve, 100)) await new Promise((resolve) => setTimeout(resolve, 100))
} }

View File

@ -3,6 +3,7 @@ import { TSocket } from '../../../utilities/Types'
import ZoneRepository from '../../../repositories/ZoneRepository' import ZoneRepository from '../../../repositories/ZoneRepository'
import { ZoneEventTile, ZoneObject } from '@prisma/client' import { ZoneEventTile, ZoneObject } from '@prisma/client'
import prisma from '../../../utilities/Prisma' import prisma from '../../../utilities/Prisma'
import zoneManager from '../../../managers/ZoneManager'
interface IPayload { interface IPayload {
zoneId: number zoneId: number
@ -82,8 +83,16 @@ export default function (socket: TSocket, io: Server) {
zone = await ZoneRepository.getById(data.zoneId) zone = await ZoneRepository.getById(data.zoneId)
if (!zone) {
console.log(`---Zone not found.`)
return
}
// send over zone and characters to socket // send over zone and characters to socket
socket.emit('gm:zone_editor:zone:load', zone) socket.emit('gm:zone_editor:zone:load', zone)
zoneManager.unloadZone(data.zoneId)
await zoneManager.loadZone(zone)
} catch (error: any) { } catch (error: any) {
console.log(`---Error updating zone: ${error.message}`) console.log(`---Error updating zone: ${error.message}`)
} }

View File

@ -13,19 +13,13 @@ export interface Node extends Position {
/** /**
* A* pathfinding algorithm. * A* pathfinding algorithm.
*/ */
class AStar { export class AStar {
private static readonly ORTHOGONAL_DIRECTIONS: Position[] = [ private static readonly ORTHOGONAL_DIRECTIONS: Position[] = [
{ x: 0, y: -1 }, // up { x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 }
{ x: 0, y: 1 }, // down
{ x: -1, y: 0 }, // left
{ x: 1, y: 0 } // right
] ]
private static readonly DIAGONAL_DIRECTIONS: Position[] = [ private static readonly DIAGONAL_DIRECTIONS: Position[] = [
{ x: -1, y: -1 }, // up-left { x: -1, y: -1 }, { x: -1, y: 1 }, { x: 1, y: -1 }, { x: 1, y: 1 }
{ x: -1, y: 1 }, // down-left
{ x: 1, y: -1 }, // up-right
{ x: 1, y: 1 } // down-right
] ]
/** /**
@ -74,40 +68,27 @@ class AStar {
return [] // No path found return [] // No path found
} }
/**
* Gets the node with the lowest F score from a list.
*/
private static getLowestFScoreNode(nodes: Node[]): Node { private static getLowestFScoreNode(nodes: Node[]): Node {
return nodes.reduce((min, node) => (node.f < min.f ? node : min)) return nodes.reduce((min, node) => (node.f < min.f ? node : min))
} }
/**
* Checks if the given node is the end node.
*/
private static isEndNode(node: Node, end: Position): boolean { private static isEndNode(node: Node, end: Position): boolean {
return node.x === end.x && node.y === end.y return node.x === end.x && node.y === end.y
} }
/**
* Removes a node from the open list.
*/
private static removeNodeFromOpenList(openList: Node[], node: Node): void { private static removeNodeFromOpenList(openList: Node[], node: Node): void {
const index = openList.findIndex((n) => n.x === node.x && n.y === node.y) const index = openList.findIndex((n) => n.x === node.x && n.y === node.y)
if (index !== -1) openList.splice(index, 1) if (index !== -1) openList.splice(index, 1)
} }
/**
* Converts a node to a string representation.
*/
private static nodeToString(node: Position): string { private static nodeToString(node: Position): string {
return `${node.x},${node.y}` return `${node.x},${node.y}`
} }
/**
* Gets valid neighbors of the given node.
*/
private static getValidNeighbors(node: Node, grid: number[][], end: Position, allowDiagonal: boolean): Node[] { private static getValidNeighbors(node: Node, grid: number[][], end: Position, allowDiagonal: boolean): Node[] {
const directions = allowDiagonal ? [...this.ORTHOGONAL_DIRECTIONS, ...this.DIAGONAL_DIRECTIONS] : this.ORTHOGONAL_DIRECTIONS const directions = allowDiagonal
? [...this.ORTHOGONAL_DIRECTIONS, ...this.DIAGONAL_DIRECTIONS]
: this.ORTHOGONAL_DIRECTIONS
return directions return directions
.map((dir) => ({ .map((dir) => ({
@ -117,50 +98,29 @@ class AStar {
h: 0, h: 0,
f: 0 f: 0
})) }))
.filter((pos) => this.isValidPosition(pos, grid, end)) as Node[] .filter((pos) => this.isValidPosition(pos, grid, end))
} }
/**
* Checks if the given position is valid.
*/
private static isValidPosition(pos: Position, grid: number[][], end: Position): boolean { private static isValidPosition(pos: Position, grid: number[][], end: Position): boolean {
const { x, y } = pos const { x, y } = pos
return x >= 0 && y >= 0 && x < grid.length && y < grid[0].length && (grid[y][x] === 0 || (x === end.x && y === end.y)) return x >= 0 && y >= 0 && x < grid.length && y < grid[0].length &&
(grid[y][x] === 0 || (x === end.x && y === end.y))
} }
/**
* Checks if the given node is in the open list.
*/
private static isInOpenList(openList: Node[], node: Position): boolean { private static isInOpenList(openList: Node[], node: Position): boolean {
return openList.some((n) => n.x === node.x && n.y === node.y) return openList.some((n) => n.x === node.x && n.y === node.y)
} }
/**
* Gets the distance between two positions.
*/
private static getDistance(a: Position, b: Position): number { private static getDistance(a: Position, b: Position): number {
const dx = Math.abs(a.x - b.x) const dx = Math.abs(a.x - b.x)
const dy = Math.abs(a.y - b.y) const dy = Math.abs(a.y - b.y)
return Math.sqrt(dx * dx + dy * dy)
if (a.x === b.x || a.y === b.y) {
// Orthogonal movement (horizontal/vertical)
return Math.sqrt(dx * dx + dy * dy)
} else {
// Diagonal movement with cost sqrt(2)
return Math.sqrt(dx * dx + dy * dy)
}
} }
/**
* Heuristic function estimating the distance from a node to the goal.
*/
private static heuristic(node: Position, goal: Position): number { private static heuristic(node: Position, goal: Position): number {
return this.getDistance(node, goal) return this.getDistance(node, goal)
} }
/**
* Reconstructs the path from the end node.
*/
private static reconstructPath(endNode: Node): Node[] { private static reconstructPath(endNode: Node): Node[] {
const path: Node[] = [] const path: Node[] = []
let currentNode: Node | undefined = endNode let currentNode: Node | undefined = endNode
@ -173,5 +133,3 @@ class AStar {
return path return path
} }
} }
export default AStar

View File

@ -3,7 +3,7 @@ import { Character, User } from '@prisma/client'
export type TSocket = Socket & { export type TSocket = Socket & {
user?: User user?: User
character?: Character character?: ExtendedCharacter
handshake?: { handshake?: {
query?: { query?: {
token?: any token?: any
@ -16,13 +16,10 @@ export type TSocket = Socket & {
} }
} }
export type TCharacter = Socket & { export type ExtendedCharacter = Character & {
user?: User isMoving?: boolean
character?: Character
} }
export type TZoneCharacter = Character & {}
export type TAsset = { export type TAsset = {
key: string key: string
url: string url: string
@ -30,3 +27,8 @@ export type TAsset = {
frameWidth?: number frameWidth?: number
frameHeight?: number frameHeight?: number
} }
// export type TCharacter = Socket & {
// user?: User
// character?: Character
// }