From acc9eaae9ece6a116bf79a5935a427065d176c2f Mon Sep 17 00:00:00 2001 From: Dennis Postma Date: Tue, 20 Aug 2024 21:33:57 +0200 Subject: [PATCH] A* and move logic improvements --- src/events/character/Move.ts | 26 ++++++++++++++++++++++---- src/events/chat/gm/AlertCommand.ts | 7 +------ src/utilities/Player/AStar.ts | 11 ++++++----- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/events/character/Move.ts b/src/events/character/Move.ts index 5fa2b04..ecea3cd 100644 --- a/src/events/character/Move.ts +++ b/src/events/character/Move.ts @@ -40,21 +40,32 @@ async function moveAlongPath(socket: TSocket, io: Server, path: Array<{ x: numbe for (let i = 0; i < path.length - 1; i++) { const startTime = Date.now() + const start = path[i] + const end = path[i + 1] + while (Date.now() - startTime < stepDuration) { if (moveTokens.get(character.id) !== moveToken) return const progress = (Date.now() - startTime) / stepDuration - const current = interpolatePosition(path[i], path[i + 1], progress) + const current = interpolatePosition(start, end, progress) - if (isObstacle(current, grid)) break + if (isObstacle(current, grid)) { + // If obstacle encountered, stop at the last valid position + await updateCharacter(character, start, Rotation.calculate(start.x, start.y, end.x, end.y)) + io.in(character.zoneId.toString()).emit('character:moved', character) + return + } - await updateCharacter(character, current, Rotation.calculate(path[i].x, path[i].y, path[i + 1].x, path[i + 1].y)) + await updateCharacter(character, current, Rotation.calculate(start.x, start.y, end.x, end.y)) io.in(character.zoneId.toString()).emit('character:moved', character) await new Promise((resolve) => setTimeout(resolve, updateInterval)) } } + // Ensure the character reaches the exact final position if (moveTokens.get(character.id) === moveToken) { + const finalPosition = path[path.length - 1] + await updateCharacter(character, finalPosition, character.rotation) character.isMoving = false moveTokens.delete(character.id) io.in(character.zoneId.toString()).emit('character:moved', character) @@ -66,7 +77,14 @@ const interpolatePosition = (start: { x: number; y: number }, end: { x: number; y: start.y + (end.y - start.y) * progress }) -const isObstacle = ({ x, y }: { x: number; y: number }, grid: number[][]) => grid[Math.floor(y)]?.[Math.floor(x)] === 1 +const isObstacle = ({ x, y }: { x: number; y: number }, grid: number[][]) => { + 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 +} async function updateCharacter(character: ExtendedCharacter, { x, y }: { x: number; y: number }, rotation: number) { Object.assign(character, { position_x: x, position_y: y, rotation }) diff --git a/src/events/chat/gm/AlertCommand.ts b/src/events/chat/gm/AlertCommand.ts index 70c648e..60be1ef 100644 --- a/src/events/chat/gm/AlertCommand.ts +++ b/src/events/chat/gm/AlertCommand.ts @@ -19,12 +19,7 @@ export default function (socket: TSocket, io: Server) { const character = await CharacterRepository.getByUserAndId(socket.user?.id as number, socket.character?.id as number) if (!character) return - // When 1 arg is provided, only send message. 2 includes a title - if (args.length === 1) { - return io.emit('notification', { message: args[0] }) - } - - io.emit('notification', { title: args[0], message: args.slice(1).join(' ') }) + io.emit('notification', { title: 'Message from GM', message: args.join(' ') }) callback(true) } catch (error: any) { console.log(`---Error sending message: ${error.message}`) diff --git a/src/utilities/Player/AStar.ts b/src/utilities/Player/AStar.ts index f246f42..9d07ab1 100644 --- a/src/utilities/Player/AStar.ts +++ b/src/utilities/Player/AStar.ts @@ -3,10 +3,10 @@ export type Node = Position & { parent?: Node; g: number; h: number; f: number } export class AStar { private static readonly DIRECTIONS = [ - { x: 0, y: -1 }, - { x: 0, y: 1 }, - { x: -1, y: 0 }, - { x: 1, y: 0 }, + { x: 0, y: -1 }, // Up + { x: 0, y: 1 }, // Down + { x: -1, y: 0 }, // Left + { x: 1, y: 0 }, // Right { x: -1, y: -1 }, { x: -1, y: 1 }, { x: 1, y: -1 }, @@ -54,7 +54,8 @@ export class AStar { private static getDistance(a: Position, b: Position): number { const dx = Math.abs(a.x - b.x), dy = Math.abs(a.y - b.y) - return Math.sqrt(dx * dx + dy * dy) + // Manhattan distance for straight paths, then Euclidean for diagonals + return dx + dy + (Math.sqrt(2) - 2) * Math.min(dx, dy) } private static reconstructPath(endNode: Node): Node[] {