From aec11f9f79b289f6e5c3aec7147c87fb96006969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=B6gele?= Date: Wed, 2 Mar 2022 00:23:45 +0100 Subject: [PATCH] Initial difficult terrain support --- js/api.js | 6 +++++- js/compatibility.js | 6 +++--- js/pathfinding.js | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/js/api.js b/js/api.js index 0672964..7a371c4 100644 --- a/js/api.js +++ b/js/api.js @@ -2,7 +2,7 @@ import {measureDistances} from "./compatibility.js"; import {getMovementHistory} from "./movement_tracking.js"; import {GenericSpeedProvider, SpeedProvider} from "./speed_provider.js" import {settingsKey} from "./settings.js" -import {getTokenShape} from "./util.js"; +import {getAreaFromPositionAndShape, getTokenShape} from "./util.js"; export const availableSpeedProviders = {} export let currentSpeedProvider = undefined @@ -140,6 +140,10 @@ export function getMovedDistanceFromToken(token) { return distances.reduce((acc, val) => acc + val, 0); } +export function buildCostFunction(token, shape) { + return (x, y, costOptions={}) => getCostFromSpeedProvider(token, getAreaFromPositionAndShape({x, y}, shape), costOptions); +} + export function registerModule(moduleId, speedProvider) { // Check if a module with the given id exists and is currently enabled const module = game.modules.get(moduleId) diff --git a/js/compatibility.js b/js/compatibility.js index 5217038..b97bcf0 100644 --- a/js/compatibility.js +++ b/js/compatibility.js @@ -1,6 +1,6 @@ -import {getCostFromSpeedProvider} from "./api.js"; +import {buildCostFunction} from "./api.js"; import {settingsKey} from "./settings.js"; -import {getAreaFromPositionAndShape, highlightTokenShape} from "./util.js"; +import {highlightTokenShape} from "./util.js"; export function getHexSizeSupportTokenGridCenter(token) { const tokenCenterOffset = CONFIG.hexSizeSupport.getCenterOffset(token) @@ -27,7 +27,7 @@ export function measureDistances(segments, entity, shape, options={}) { const newSegments = segments.slice(firstNewSegmentIndex); const distances = previousSegments.map(segment => segment.ray.dragRulerVisitedSpaces[segment.ray.dragRulerVisitedSpaces.length - 1].distance); previousSegments.forEach(segment => segment.ray.terrainRulerVisitedSpaces = duplicate(segment.ray.dragRulerVisitedSpaces)); - opts.costFunction = (x, y, costOptions={}) => { return getCostFromSpeedProvider(entity, getAreaFromPositionAndShape({x, y}, shape), costOptions); } + opts.costFunction = buildCostFunction(entity, shape); if (previousSegments.length > 0) opts.terrainRulerInitialState = previousSegments[previousSegments.length - 1].ray.dragRulerFinalState; return distances.concat(terrainRuler.measureDistances(newSegments, opts)); diff --git a/js/pathfinding.js b/js/pathfinding.js index d361c93..3816927 100644 --- a/js/pathfinding.js +++ b/js/pathfinding.js @@ -1,4 +1,4 @@ -import {getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js"; +import {getCenterFromGridPositionObj, getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js"; import {moveWithoutAnimation, togglePathfinding} from "./keybindings.js"; import {debugGraphics} from "./main.js"; import {settingsKey} from "./settings.js"; @@ -6,6 +6,8 @@ import {getSnapPointForTokenObj, iterPairs} from "./util.js"; import * as GridlessPathfinding from "../wasm/gridless_pathfinding.js"; import {PriorityQueueSet} from "./data_structures.js"; +import { buildCostFunction } from "./api.js"; +import { measure } from "./foundry_imports.js"; let cachedNodes = undefined; let cacheElevation; @@ -74,12 +76,22 @@ function getNode(pos, token, initialize=true) { // TODO Work with pixels instead of grid locations if (!stepCollidesWithWall(neighborPos, pos, token)) { - const isDiagonal = node.x !== neighborPos.x && node.y !== neighborPos.y && canvas.grid.type === CONST.GRID_TYPES.SQUARE; - const neighbor = getNode(neighborPos, token, false); + let edgeCost; + if (window.terrainRuler) { + // TODO Additional cache for each token shape + // TODO Use the correct token shape + let ray = new Ray(getCenterFromGridPositionObj(neighborPos), getCenterFromGridPositionObj(pos)); + let measuredDistance = terrainRuler.measureDistances([{ray}], {costFunction: buildCostFunction(token, [{x: 0, y: 0}])})[0]; + edgeCost = Math.round(measuredDistance / canvas.dimensions.distance); + } + else { + const isDiagonal = node.x !== neighborPos.x && node.y !== neighborPos.y && canvas.grid.type === CONST.GRID_TYPES.SQUARE; - // Count 5-10-5 diagonals as 1.5 (so two add up to 3) and 5-5-5 diagonals as 1.0001 (to discourage unnecessary diagonals) - // TODO Account for difficult terrain - let edgeCost = isDiagonal ? (use5105 ? 1.5 : 1.0001) : 1; + // Count 5-10-5 diagonals as 1.5 (so two add up to 3) and 5-5-5 diagonals as 1.0001 (to discourage unnecessary diagonals) + // TODO Account for difficult terrain + edgeCost = isDiagonal ? (use5105 ? 1.5 : 1.0001) : 1; + } + const neighbor = getNode(neighborPos, token, false); node.edges.push({target: neighbor, cost: edgeCost}); } }