Support for difficult terrain with tokens larger than 1x1

This commit is contained in:
Manuel Vögele
2021-03-15 20:17:38 +01:00
parent c4d089e8ff
commit 4f1dec3089
5 changed files with 46 additions and 11 deletions
+7
View File
@@ -97,6 +97,13 @@ export function getUnreachableColorFromSpeedProvider() {
}
}
export function getCostFromSpeedProvider(token, area) {
if (currentSpeedProvider instanceof Function) {
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area);
}
return currentSpeedProvider.getCostForStep(token, area);
}
export function registerModule(moduleId, speedProvider) {
// Check if a module with the given id exists and is currently enabled
const module = game.modules.get(moduleId)
+5 -3
View File
@@ -1,7 +1,8 @@
import { getCostFromSpeedProvider } from "./api.js";
import {highlightMeasurementTerrainRuler} from "./compatibility.js";
import {getGridPositionFromPixels} from "./foundry_fixes.js";
import {getColorForDistance} from "./main.js"
import {applyTokenSizeOffset, getSnapPointForToken, getTokenShape, getTokenSize, highlightTokenShape, zip} from "./util.js";
import {applyTokenSizeOffset, getAreaFromPositionAndShape, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
// This is a modified version of Ruler.moveToken from foundry 0.7.9
export async function moveTokens(draggedToken, selectedTokens) {
@@ -132,11 +133,13 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
centeredSegments.push({ray: centeredRay, label})
}
const shape = getTokenShape(this.draggedToken)
// Compute measured distance
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS
let distances
if (terrainRulerAvailable)
distances = game.terrainRuler.measureDistances(centeredSegments)
distances = game.terrainRuler.measureDistances(centeredSegments, {costFunction: (x, y) => getCostFromSpeedProvider(this.draggedToken, getAreaFromPositionAndShape({x, y}, shape), {x, y})});
else
distances = canvas.grid.measureDistances(centeredSegments, { gridSpaces });
@@ -161,7 +164,6 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
rulerColor = getColorForDistance.call(this, totalDistance)
else
rulerColor = this.color
const shape = getTokenShape(this.draggedToken)
for (const [s, cs] of zip(segments.reverse(), centeredSegments.reverse())) {
const { label, text, last } = cs;
+16
View File
@@ -56,6 +56,22 @@ export class SpeedProvider {
return 0xFF0000
}
/**
* Returns the cost for a token to step into the specificed area.
* The area indicates the whole area that the token will occupy (for tokens larger than 1x1) the array will more than one entry.
* The return value should be an integer indicating a multiplicator by that the cost of that step should be increased.
* (1 is regular cost, 2 costs double, 3 costs triple, ...)
* This function is only called if the TerrainLayer and TerrainRuler modules are enabled.
*
* Implementing this method is optional and only needs to be done if you want to provide a custom cost function (for example to allow tokens to ignore difficult terrain)
*/
getCostForStep(token, area) {
// Lookup the cost for each square occupied by the token
const costs = area.map(space => canvas.terrain.costGrid[space.y]?.[space.x]?.multiple ?? 1)
// Return the maximum of the costs
return costs.reduce((max, current) => Math.max(max, current))
}
/**
* Returns a boolean indicating whether this token will use a Ruler or not.
* If this is returns `false` for a token Drag Ruler will be disabled for that token. Dragging a token for which this function
+15 -8
View File
@@ -24,9 +24,17 @@ export function getSnapPointForToken(x, y, token) {
}
export function highlightTokenShape(position, shape, color) {
for (const space of shape) {
let gridX = position.x + space.x;
let gridY = position.y + space.y;
const area = getAreaFromPositionAndShape(position, shape);
for (const space of area) {
const [x, y] = getPixelsFromGridPosition(space.x, space.y);
canvas.grid.highlightPosition(this.name, {x, y, color})
}
}
export function getAreaFromPositionAndShape(position, shape) {
return shape.map(space => {
let x = position.x + space.x;
let y = position.y + space.y;
if (canvas.grid.isHex) {
let shiftedRow;
if (canvas.grid.grid.options.even)
@@ -35,18 +43,17 @@ export function highlightTokenShape(position, shape, color) {
shiftedRow = 0
if (canvas.grid.grid.options.columns) {
if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) {
gridY += 1;
y += 1;
}
}
else {
if (space.y % 2 !== 0 && position.y % 2 !== shiftedRow) {
gridX += 1;
x += 1;
}
}
}
const [x, y] = getPixelsFromGridPosition(gridX, gridY);
canvas.grid.highlightPosition(this.name, {x, y, color})
}
return {x, y}
});
}
export function getTokenShape(token) {