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 { TSocket } from '../../utilities/Types'
import { Server } from 'socket.io'
import { TSocket, ExtendedCharacter } from '../../utilities/Types'
import CharacterRepository from '../../repositories/CharacterRepository'
import { Character, User } from '@prisma/client'
type SocketResponseT = {
character_id: number
@ -11,7 +10,7 @@ export default function (socket: TSocket, io: Server) {
socket.on('character:connect', async (data: SocketResponseT) => {
console.log('character:connect requested', data)
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)
} catch (error: any) {
console.log('character:connect error', error)

View File

@ -2,22 +2,15 @@ import { Server } from 'socket.io'
import { TSocket } from '../../utilities/Types'
import ZoneManager from '../../managers/ZoneManager'
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 { ExtendedCharacter as Character } from '../../utilities/Types'
interface SocketResponse {
position_x: 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) {
socket.on('character:move', async (data: SocketResponse) => {
try {
@ -41,12 +34,23 @@ export default function setupCharacterMove(socket: TSocket, io: Server) {
const path = AStar.findPath(start, end, grid)
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 {
console.log('character:move error', 'No valid path found')
}
} catch (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)
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
await new Promise((resolve) => setTimeout(resolve, 100))
}
@ -93,4 +95,4 @@ async function updateCharacterPosition(character: Character, x: number, y: numbe
where: { id: character.id },
data: { position_x: x, position_y: y, rotation }
})
}
}

View File

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

View File

@ -13,19 +13,13 @@ export interface Node extends Position {
/**
* A* pathfinding algorithm.
*/
class AStar {
export class AStar {
private static readonly ORTHOGONAL_DIRECTIONS: Position[] = [
{ x: 0, y: -1 }, // up
{ x: 0, y: 1 }, // down
{ x: -1, y: 0 }, // left
{ x: 1, y: 0 } // right
{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 }
]
private static readonly DIAGONAL_DIRECTIONS: Position[] = [
{ x: -1, y: -1 }, // up-left
{ x: -1, y: 1 }, // down-left
{ x: 1, y: -1 }, // up-right
{ x: 1, y: 1 } // down-right
{ x: -1, y: -1 }, { x: -1, y: 1 }, { x: 1, y: -1 }, { x: 1, y: 1 }
]
/**
@ -74,40 +68,27 @@ class AStar {
return [] // No path found
}
/**
* Gets the node with the lowest F score from a list.
*/
private static getLowestFScoreNode(nodes: Node[]): Node {
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 {
return node.x === end.x && node.y === end.y
}
/**
* Removes a node from the open list.
*/
private static removeNodeFromOpenList(openList: Node[], node: Node): void {
const index = openList.findIndex((n) => n.x === node.x && n.y === node.y)
if (index !== -1) openList.splice(index, 1)
}
/**
* Converts a node to a string representation.
*/
private static nodeToString(node: Position): string {
return `${node.x},${node.y}`
}
/**
* Gets valid neighbors of the given 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
.map((dir) => ({
@ -117,50 +98,29 @@ class AStar {
h: 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 {
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 {
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 {
const dx = Math.abs(a.x - b.x)
const dy = Math.abs(a.y - b.y)
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)
}
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 {
return this.getDistance(node, goal)
}
/**
* Reconstructs the path from the end node.
*/
private static reconstructPath(endNode: Node): Node[] {
const path: Node[] = []
let currentNode: Node | undefined = endNode
@ -172,6 +132,4 @@ class AStar {
return path
}
}
export default AStar
}

View File

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