Merge branch 'develop' into pathfinding
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
## 1.11.1
|
||||||
|
### Bugfixes
|
||||||
|
- Fixed a bug that would cause the pathfinding algorithm to make tokens of size 2 and 4 to take an unnecessary step
|
||||||
|
|
||||||
|
|
||||||
## 1.11.0
|
## 1.11.0
|
||||||
### New features
|
### New features
|
||||||
- Drag Ruler now supports pathfinding. Pressing the assigned button will automatically calculate the shortest route to the point you're dragging your token to and add the necessary waypoints to the ruler.
|
- Drag Ruler now supports pathfinding. Pressing the assigned button will automatically calculate the shortest route to the point you're dragging your token to and add the necessary waypoints to the ruler.
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
"name": "drag-ruler",
|
"name": "drag-ruler",
|
||||||
"title": "Drag Ruler",
|
"title": "Drag Ruler",
|
||||||
"description": "When dragging a token displays a ruler showing how far you've moved that token.",
|
"description": "When dragging a token displays a ruler showing how far you've moved that token.",
|
||||||
"version": "1.11.0",
|
"version": "1.11.1",
|
||||||
"minimumCoreVersion" : "9.245",
|
"minimumCoreVersion" : "9.245",
|
||||||
"compatibleCoreVersion" : "9",
|
"compatibleCoreVersion" : "9",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
],
|
],
|
||||||
"socket": true,
|
"socket": true,
|
||||||
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler",
|
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler",
|
||||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.11.0.zip",
|
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.11.1.zip",
|
||||||
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json",
|
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json",
|
||||||
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md",
|
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md",
|
||||||
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
|
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js";
|
import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js";
|
||||||
import {getCenterFromGridPositionObj, getGridPositionFromPixels, getGridPositionFromPixelsObj} from "./foundry_fixes.js";
|
import {getGridPositionFromPixels, getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js";
|
||||||
import {Line} from "./geometry.js";
|
import {Line} from "./geometry.js";
|
||||||
import {disableSnap, moveWithoutAnimation} from "./keybindings.js";
|
import {disableSnap, moveWithoutAnimation} from "./keybindings.js";
|
||||||
import {trackRays} from "./movement_tracking.js"
|
import {trackRays} from "./movement_tracking.js"
|
||||||
import {findPath, isPathfindingEnabled} from "./pathfinding.js";
|
import {findPath, isPathfindingEnabled} from "./pathfinding.js";
|
||||||
import {settingsKey} from "./settings.js";
|
import {settingsKey} from "./settings.js";
|
||||||
import {recalculate} from "./socket.js";
|
import {recalculate} from "./socket.js";
|
||||||
import {applyTokenSizeOffset, enumeratedZip, getSnapPointForEntity, getSnapPointForToken, getTokenShape, highlightTokenShape, sum} from "./util.js";
|
import {applyTokenSizeOffset, enumeratedZip, getSnapPointForEntity, getSnapPointForToken, getSnapPointForTokenObj, getTokenShape, highlightTokenShape, sum} from "./util.js";
|
||||||
|
|
||||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||||
export async function moveEntities(draggedEntity, selectedEntities) {
|
export async function moveEntities(draggedEntity, selectedEntities) {
|
||||||
@@ -169,9 +169,11 @@ export function measure(destination, options={}) {
|
|||||||
this.dragRulerRemovePathfindingWaypoints();
|
this.dragRulerRemovePathfindingWaypoints();
|
||||||
|
|
||||||
if (isToken && isPathfindingEnabled()) {
|
if (isToken && isPathfindingEnabled()) {
|
||||||
let path = findPath(getGridPositionFromPixelsObj(this.waypoints[this.waypoints.length - 1]), getGridPositionFromPixelsObj(destination), this.waypoints);
|
const from = getGridPositionFromPixelsObj(this.waypoints[this.waypoints.length - 1]);
|
||||||
|
const to = getGridPositionFromPixelsObj(destination);
|
||||||
|
let path = findPath(from, to, this.draggedEntity, this.waypoints);
|
||||||
if (path) {
|
if (path) {
|
||||||
path = path.map(point => getCenterFromGridPositionObj(point))
|
path = path.map(point => getSnapPointForTokenObj(getPixelsFromGridPositionObj(point), this.draggedEntity));
|
||||||
|
|
||||||
// If the token is snapped to the grid, the first point of the path is already handled by the ruler
|
// If the token is snapped to the grid, the first point of the path is already handled by the ruler
|
||||||
if (path[0].x === this.waypoints[this.waypoints.length - 1].x && path[0].y === this.waypoints[this.waypoints.length - 1].y)
|
if (path[0].x === this.waypoints[this.waypoints.length - 1].x && path[0].y === this.waypoints[this.waypoints.length - 1].y)
|
||||||
|
|||||||
+21
-15
@@ -1,8 +1,8 @@
|
|||||||
import {getCenterFromGridPositionObj, getGridPositionFromPixelsObj} from "./foundry_fixes.js";
|
import {getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js";
|
||||||
import {togglePathfinding} from "./keybindings.js";
|
import {togglePathfinding} from "./keybindings.js";
|
||||||
import {debugGraphics} from "./main.js";
|
import {debugGraphics} from "./main.js";
|
||||||
import {settingsKey} from "./settings.js";
|
import {settingsKey} from "./settings.js";
|
||||||
import {iterPairs} from "./util.js";
|
import {getSnapPointForTokenObj, iterPairs} from "./util.js";
|
||||||
|
|
||||||
let cachedNodes = undefined;
|
let cachedNodes = undefined;
|
||||||
let use5105 = false;
|
let use5105 = false;
|
||||||
@@ -15,16 +15,16 @@ export function isPathfindingEnabled() {
|
|||||||
return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding;
|
return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findPath(from, to, previousWaypoints) {
|
export function findPath(from, to, token, previousWaypoints) {
|
||||||
const lastNode = calculatePath(from, to, previousWaypoints);
|
const lastNode = calculatePath(from, to, token, previousWaypoints);
|
||||||
if (!lastNode)
|
if (!lastNode)
|
||||||
return null;
|
return null;
|
||||||
paintPathfindingDebug(lastNode);
|
paintPathfindingDebug(lastNode, token);
|
||||||
const path = [];
|
const path = [];
|
||||||
let currentNode = lastNode;
|
let currentNode = lastNode;
|
||||||
while (currentNode) {
|
while (currentNode) {
|
||||||
// TODO Check if the distance doesn't change
|
// TODO Check if the distance doesn't change
|
||||||
if (path.length >= 2 && !canvas.walls.checkCollision(new Ray(getCenterFromGridPositionObj(currentNode.node), getCenterFromGridPositionObj(path[path.length - 2]))))
|
if (path.length >= 2 && !stepCollidesWithWall(currentNode.node, path[path.length - 2], token))
|
||||||
// Replace last waypoint if the current waypoint leads to a valid path
|
// Replace last waypoint if the current waypoint leads to a valid path
|
||||||
path[path.length - 1] = {x: currentNode.node.x, y: currentNode.node.y};
|
path[path.length - 1] = {x: currentNode.node.x, y: currentNode.node.y};
|
||||||
else
|
else
|
||||||
@@ -38,7 +38,7 @@ export function wipePathfindingCache() {
|
|||||||
cachedNodes = undefined;
|
cachedNodes = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNode(pos, initialize=true) {
|
function getNode(pos, token, initialize=true) {
|
||||||
pos = {layer: 0, ...pos}; // Copy pos and set pos.layer to the default value if it's unset
|
pos = {layer: 0, ...pos}; // Copy pos and set pos.layer to the default value if it's unset
|
||||||
if (!cachedNodes)
|
if (!cachedNodes)
|
||||||
cachedNodes = new Array(2);
|
cachedNodes = new Array(2);
|
||||||
@@ -57,12 +57,12 @@ function getNode(pos, initialize=true) {
|
|||||||
if (neighborPos.x < 0 || neighborPos.y < 0)
|
if (neighborPos.x < 0 || neighborPos.y < 0)
|
||||||
continue;
|
continue;
|
||||||
// TODO Work with pixels instead of grid locations
|
// TODO Work with pixels instead of grid locations
|
||||||
if (!canvas.walls.checkCollision(new Ray(getCenterFromGridPositionObj(pos), getCenterFromGridPositionObj(neighborPos)))) {
|
if (!stepCollidesWithWall(pos, neighborPos, token)) {
|
||||||
const isDiagonal = node.x !== neighborPos.x && node.y !== neighborPos.y && canvas.grid.type === CONST.GRID_TYPES.SQUARE;
|
const isDiagonal = node.x !== neighborPos.x && node.y !== neighborPos.y && canvas.grid.type === CONST.GRID_TYPES.SQUARE;
|
||||||
let targetLayer = pos.layer;
|
let targetLayer = pos.layer;
|
||||||
if (use5105 && isDiagonal)
|
if (use5105 && isDiagonal)
|
||||||
targetLayer = 1 - targetLayer;
|
targetLayer = 1 - targetLayer;
|
||||||
const neighbor = getNode({...neighborPos, layer: targetLayer}, false);
|
const neighbor = getNode({...neighborPos, layer: targetLayer}, token, false);
|
||||||
|
|
||||||
// TODO We currently assume a cost of one or two for all transitions. Change this for difficult terrain support
|
// TODO We currently assume a cost of one or two for all transitions. Change this for difficult terrain support
|
||||||
let edgeCost = 1;
|
let edgeCost = 1;
|
||||||
@@ -77,17 +77,17 @@ function getNode(pos, initialize=true) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculatePath(from, to, previousWaypoints) {
|
function calculatePath(from, to, token, previousWaypoints) {
|
||||||
if (game.system.id === "pf2e")
|
if (game.system.id === "pf2e")
|
||||||
use5105 = true;
|
use5105 = true;
|
||||||
if (canvas.grid.diagonalRule === "5105")
|
if (canvas.grid.diagonalRule === "5105")
|
||||||
use5105 = true;
|
use5105 = true;
|
||||||
let startLayer = 0;
|
let startLayer = 0;
|
||||||
if (use5105) {
|
if (use5105 && canvas.grid.type === CONST.GRID_TYPES.SQUARE) {
|
||||||
previousWaypoints = previousWaypoints.map(w => getGridPositionFromPixelsObj(w));
|
previousWaypoints = previousWaypoints.map(w => getGridPositionFromPixelsObj(w));
|
||||||
startLayer = calcNoDiagonals(previousWaypoints) % 2;
|
startLayer = calcNoDiagonals(previousWaypoints) % 2;
|
||||||
}
|
}
|
||||||
const nextNodes = [{node: getNode({...to, layer: startLayer}), cost: 0, estimated: estimateCost(to, from), previous: null}];
|
const nextNodes = [{node: getNode({...to, layer: startLayer}, token), cost: 0, estimated: estimateCost(to, from), previous: null}];
|
||||||
const previousNodes = new Set();
|
const previousNodes = new Set();
|
||||||
while (nextNodes.length > 0) {
|
while (nextNodes.length > 0) {
|
||||||
// Sort by estimated cost, high to low
|
// Sort by estimated cost, high to low
|
||||||
@@ -99,7 +99,7 @@ function calculatePath(from, to, previousWaypoints) {
|
|||||||
return currentNode;
|
return currentNode;
|
||||||
previousNodes.add(currentNode.node);
|
previousNodes.add(currentNode.node);
|
||||||
for (const edge of currentNode.node.edges) {
|
for (const edge of currentNode.node.edges) {
|
||||||
const neighborNode = getNode(edge.target);
|
const neighborNode = getNode(edge.target, token);
|
||||||
if (previousNodes.has(neighborNode))
|
if (previousNodes.has(neighborNode))
|
||||||
continue;
|
continue;
|
||||||
const neighbor = {node: neighborNode, cost: currentNode.cost + edge.cost, estimated: currentNode.cost + edge.cost + estimateCost(neighborNode, from), previous: currentNode};
|
const neighbor = {node: neighborNode, cost: currentNode.cost + edge.cost, estimated: currentNode.cost + edge.cost + estimateCost(neighborNode, from), previous: currentNode};
|
||||||
@@ -129,7 +129,13 @@ function estimateCost(pos, target) {
|
|||||||
return Math.max(Math.abs(pos.x - target.x), Math.abs(pos.y - target.y));
|
return Math.max(Math.abs(pos.x - target.x), Math.abs(pos.y - target.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
function paintPathfindingDebug(lastNode) {
|
function stepCollidesWithWall(from, to, token) {
|
||||||
|
const stepStart = getSnapPointForTokenObj(getPixelsFromGridPositionObj(from), token);
|
||||||
|
const stepEnd = getSnapPointForTokenObj(getPixelsFromGridPositionObj(to), token);
|
||||||
|
return canvas.walls.checkCollision(new Ray(stepStart, stepEnd));
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintPathfindingDebug(lastNode, token) {
|
||||||
if (!CONFIG.debug.dragRuler)
|
if (!CONFIG.debug.dragRuler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -137,7 +143,7 @@ function paintPathfindingDebug(lastNode) {
|
|||||||
let currentNode = lastNode;
|
let currentNode = lastNode;
|
||||||
while (currentNode) {
|
while (currentNode) {
|
||||||
let text = new PIXI.Text(currentNode.cost.toFixed(0));
|
let text = new PIXI.Text(currentNode.cost.toFixed(0));
|
||||||
let pixels = getCenterFromGridPositionObj(currentNode.node);
|
let pixels = getSnapPointForTokenObj(getPixelsFromGridPositionObj(currentNode.node), token);
|
||||||
text.anchor.set(0.5, 1.0);
|
text.anchor.set(0.5, 1.0);
|
||||||
text.x = pixels.x;
|
text.x = pixels.x;
|
||||||
text.y = pixels.y;
|
text.y = pixels.y;
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ export function getSnapPointForToken(x, y, token) {
|
|||||||
return new PIXI.Point(snapX, snapY);
|
return new PIXI.Point(snapX, snapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSnapPointForTokenObj(pos, token) {
|
||||||
|
return getSnapPointForToken(pos.x, pos.y, token);
|
||||||
|
}
|
||||||
|
|
||||||
export function getSnapPointForMeasuredTemplate(x, y) {
|
export function getSnapPointForMeasuredTemplate(x, y) {
|
||||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||||
return new PIXI.Point(x, y);
|
return new PIXI.Point(x, y);
|
||||||
|
|||||||
Reference in New Issue
Block a user