Compare commits

...

11 Commits

Author SHA1 Message Date
Manuel Vögele aa2b8b3928 Release v1.9.0 2021-11-24 09:53:02 +01:00
Manuel Vögele a164811bb6 Add hint that shift can disable gridless snapping to the setting 2021-11-24 09:52:31 +01:00
Manuel Vögele 752b8375ab Non-square tokens are now fully supported on square grids (fixes #116) 2021-11-24 09:14:27 +01:00
Manuel Vögele 32b38d8efe Make tokens snap to their speed ranges on gridless scenes (resolves #71) 2021-11-23 11:10:21 +01:00
Manuel Vögele c3a62e3646 Only switch to the next color once the distance is 0.01 above the allowed distance
The goal of this is to reduce color flickering noise due to floating number errors
2021-11-21 22:55:02 +01:00
Manuel Vögele 39f7bab4b6 Pass enableTerrainRuler option to all measureDistances calls
This fixes a regression introduced with e1265ad6fb
2021-11-21 20:58:18 +01:00
Manuel Vögele a3be4ceb03 Remove support for TerrainLayer (Enhanced Terrain Layer stays supported) 2021-11-21 16:52:55 +01:00
Manuel Vögele b275e777db Ignore space key presses if there is no active canvas (fixes #123) 2021-11-21 15:13:45 +01:00
Manuel Vögele 035cfb8969 Add API function to receive the color for a given distance and token 2021-11-17 12:16:36 +01:00
Manuel Vögele 90a25f467b Add "TheWitcherTRPG" to the game system integration list 2021-11-17 11:48:18 +01:00
Manuel Vögele ab85b98a5c Add several new game system integrations to the list 2021-10-17 20:38:01 +02:00
14 changed files with 198 additions and 61 deletions
+13
View File
@@ -1,3 +1,16 @@
## 1.9.0
### New features
- On Gridless scenes, tokens can now snap to their speed limits, to make full usage of a token's movement speed easier. This feature can be temporarily disabled by pressing Shift during drag and can be disabled completely in the settings.
### Bugfixes
- Non-square tokens (e.g. 2x1) now work correctly on square grids
- When modifying difficult terrain that a token has already moved over, this the movement history of the token won't change anymore (this was a regression introduced in 1.8.0)
- Fixed a bug that prevented pausing/unpausing the game when no scene was active
### API
- Added `dragRuler.getColorForDistanceAndToken` API endpoint that allows other modules to receive the highlight color for a specified distance with a given token.
## 1.8.2 ## 1.8.2
### Compatibility ### Compatibility
- The generic speed provider defaults have been updated for lance 1.0 (thanks to BoltsJ!) - The generic speed provider defaults have been updated for lance 1.0 (thanks to BoltsJ!)
+5
View File
@@ -37,11 +37,16 @@ Drag Ruler will work with all Foundry VTT game systems. However some game system
The game systems that offer Drag Ruler integration are: The game systems that offer Drag Ruler integration are:
- Cypher System (starting with version 1.13.0) - Cypher System (starting with version 1.13.0)
- GURPS 4th Edition Game Aid (Unofficial) (starting with version 0.9.1)
- Ironclaw Second Edition (starting with version 0.2.2) - Ironclaw Second Edition (starting with version 0.2.2)
- Lancer (via the module [Lancer Speed Provider](https://foundryvtt.com/packages/lancer-speed-provider)) - Lancer (via the module [Lancer Speed Provider](https://foundryvtt.com/packages/lancer-speed-provider))
- Pathfinder 1 (starting with version 0.77.3) - Pathfinder 1 (starting with version 0.77.3)
- Pathfinder 2e (via the module [PF2E Drag Ruler Integration](https://foundryvtt.com/packages/pf2e-dragruler/)) - Pathfinder 2e (via the module [PF2E Drag Ruler Integration](https://foundryvtt.com/packages/pf2e-dragruler/))
- Shadowrun 5th Edition (via the module [Drag Ruler Integration for Shadowrun 5E](https://foundryvtt.com/packages/drag-ruler-integration-for-shadowrun-5e))
- Starfinder (via the module [Starfinder Drag Ruler Integration](https://foundryvtt.com/packages/starfinder-drag-ruler))
- Stargate RPG (starting with version 1.6.0)
- Tagmar RPG (starting with version 1.1.4) - Tagmar RPG (starting with version 1.1.4)
- TheWitcherTRPG (starting with version 0.0.62)
- Tormenta20 (starting with version 1.1.37) - Tormenta20 (starting with version 1.1.37)
- Shadow of the Demon Lord (starting with version 1.7.15) - Shadow of the Demon Lord (starting with version 1.7.15)
- Wasteland Ventures (starting with version 0.1.0) - Wasteland Ventures (starting with version 0.1.0)
+4
View File
@@ -81,6 +81,10 @@
"swapSpacebarRightClick": { "swapSpacebarRightClick": {
"name": "Leertaste und Rechtsklick tauschen", "name": "Leertaste und Rechtsklick tauschen",
"hint": "Die Funktionen der Leertaste und des Rechtsklicks sind, während eine Spielfigur bewegt wird, vertauscht. Wenn diese Option aktiviert wird können mit Rechtsklick Wegpunkte gesetzt werden und mit der Leertaste werden sie wieder gelöscht." "hint": "Die Funktionen der Leertaste und des Rechtsklicks sind, während eine Spielfigur bewegt wird, vertauscht. Wenn diese Option aktiviert wird können mit Rechtsklick Wegpunkte gesetzt werden und mit der Leertaste werden sie wieder gelöscht."
},
"useGridlessraster": {
"name": "Geschwindigkeitsraster aktivieren",
"hint": "Lässt Spielfiguren auf gitterlosen Szenen an deren Geschwindigkeitsgrenzen einrasten. Dies kann vorübergehend deaktiviert werden, indem während des ziehens die Umschalttaste gedrückt wird."
} }
} }
} }
+4
View File
@@ -81,6 +81,10 @@
"swapSpacebarRightClick": { "swapSpacebarRightClick": {
"name": "Swap spacebar and right click", "name": "Swap spacebar and right click",
"hint": "Swaps the functions of spacebar and right click during dragging. If enabled right click will place waypoints and spacebar will delete them" "hint": "Swaps the functions of spacebar and right click during dragging. If enabled right click will place waypoints and spacebar will delete them"
},
"useGridlessRaster": {
"name": "Use speed based snapping",
"hint": "On Gridless scenes, this makes tokens snap to the token's speed ranges. This can be temporarily disabled by pressing Shift during drag."
} }
} }
} }
+2 -2
View File
@@ -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.8.2", "version": "1.9.0",
"minimumCoreVersion" : "0.8.5", "minimumCoreVersion" : "0.8.5",
"compatibleCoreVersion" : "0.8.9", "compatibleCoreVersion" : "0.8.9",
"authors": [ "authors": [
@@ -59,7 +59,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.8.2.zip", "download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.9.0.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",
+16 -1
View File
@@ -116,11 +116,26 @@ export function getCostFromSpeedProvider(token, area, options) {
} }
} }
export function getColorForDistanceAndToken(distance, token, ranges=null) {
if (!ranges) {
ranges = getRangesFromSpeedProvider(token);
}
if (ranges.length === 0)
return this.color;
const currentRange = ranges.reduce((minRange, currentRange) => {
if (distance <= currentRange.range && currentRange.range < minRange.range)
return currentRange;
return minRange;
}, {range: Infinity, color: getUnreachableColorFromSpeedProvider()});
return currentRange.color;
}
export function getMovedDistanceFromToken(token) { export function getMovedDistanceFromToken(token) {
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
const history = getMovementHistory(token); const history = getMovementHistory(token);
const segments = Ruler.dragRulerGetRaysFromWaypoints(history, {x: token.x, y: token.y}).map(ray => {return {ray}}); const segments = Ruler.dragRulerGetRaysFromWaypoints(history, {x: token.x, y: token.y}).map(ray => {return {ray}});
const shape = getTokenShape(token); const shape = getTokenShape(token);
const distances = measureDistances(segments, token, shape); const distances = measureDistances(segments, token, shape, {enableTerrainRuler: terrainRulerAvailable});
// Sum up the distances // Sum up the distances
return distances.reduce((acc, val) => acc + val, 0); return distances.reduce((acc, val) => acc + val, 0);
} }
+1 -5
View File
@@ -1,5 +1,4 @@
import {getCostFromSpeedProvider} from "./api.js"; import {getCostFromSpeedProvider} from "./api.js";
import {getColorForDistance} from "./main.js"
import {settingsKey} from "./settings.js"; import {settingsKey} from "./settings.js";
import {getAreaFromPositionAndShape, highlightTokenShape} from "./util.js"; import {getAreaFromPositionAndShape, highlightTokenShape} from "./util.js";
@@ -10,7 +9,7 @@ export function getHexSizeSupportTokenGridCenter(token) {
export function highlightMeasurementTerrainRuler(ray, startDistance, tokenShape=[{x: 0, y: 0}], alpha=1) { export function highlightMeasurementTerrainRuler(ray, startDistance, tokenShape=[{x: 0, y: 0}], alpha=1) {
for (const space of ray.terrainRulerVisitedSpaces.reverse()) { for (const space of ray.terrainRulerVisitedSpaces.reverse()) {
const color = getColorForDistance.call(this, startDistance, space.distance) const color = this.dragRulerGetColorForDistance(startDistance + space.distance);
highlightTokenShape.call(this, space, tokenShape, color, alpha) highlightTokenShape.call(this, space, tokenShape, color, alpha)
} }
} }
@@ -63,9 +62,6 @@ export function checkDependencies() {
if (game.modules.get("enhanced-terrain-layer")?.active) { if (game.modules.get("enhanced-terrain-layer")?.active) {
enabledTerrainModule = game.modules.get("enhanced-terrain-layer").data.title; enabledTerrainModule = game.modules.get("enhanced-terrain-layer").data.title;
} }
else if (game.modules.get("TerrainLayer")?.active) {
enabledTerrainModule = game.modules.get("TerrainLayer").data.title;
}
if (enabledTerrainModule) { if (enabledTerrainModule) {
new Dialog({ new Dialog({
title: game.i18n.localize("drag-ruler.dependencies.terrain-ruler.title"), title: game.i18n.localize("drag-ruler.dependencies.terrain-ruler.title"),
+4 -5
View File
@@ -1,7 +1,6 @@
import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js"; import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js";
import {getGridPositionFromPixels} from "./foundry_fixes.js"; import {getGridPositionFromPixels} from "./foundry_fixes.js";
import {Line} from "./geometry.js"; import {Line} from "./geometry.js";
import {getColorForDistance} from "./main.js"
import {trackRays} from "./movement_tracking.js" import {trackRays} from "./movement_tracking.js"
import {recalculate} from "./socket.js"; import {recalculate} from "./socket.js";
import {applyTokenSizeOffset, getSnapPointForEntity, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js"; import {applyTokenSizeOffset, getSnapPointForEntity, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
@@ -169,7 +168,7 @@ export function measure(destination, options={}) {
options.ignoreGrid = false; options.ignoreGrid = false;
} }
options.enableTerrainRuler = isToken && game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS); options.enableTerrainRuler = isToken && game.modules.get("terrain-ruler")?.active;
const waypoints = this.waypoints.concat([destination]); const waypoints = this.waypoints.concat([destination]);
// Move the waypoints to the center of the grid if a size is used that measures from edge to edge // Move the waypoints to the center of the grid if a size is used that measures from edge to edge
@@ -229,7 +228,7 @@ export function measure(destination, options={}) {
r.clear(); r.clear();
let rulerColor let rulerColor
if (!options.gridSpaces || canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) if (!options.gridSpaces || canvas.grid.type === CONST.GRID_TYPES.GRIDLESS)
rulerColor = getColorForDistance.call(this, totalDistance) rulerColor = this.dragRulerGetColorForDistance(totalDistance);
else else
rulerColor = this.color rulerColor = this.color
for (const [s, cs] of zip(segments.reverse(), centeredSegments.reverse())) { for (const [s, cs] of zip(segments.reverse(), centeredSegments.reverse())) {
@@ -301,7 +300,7 @@ export function highlightMeasurementNative(ray, startDistance, tokenShape=[{x: 0
// Highlight the grid position // Highlight the grid position
let [xg, yg] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1); let [xg, yg] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1);
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0] const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0]
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance) const color = this.dragRulerGetColorForDistance(startDistance + subDistance);
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity); const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity);
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1); const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
@@ -314,7 +313,7 @@ export function highlightMeasurementNative(ray, startDistance, tokenShape=[{x: 0
let [x1h, y1h] = canvas.grid.grid.getGridPositionFromPixels(x, y); let [x1h, y1h] = canvas.grid.grid.getGridPositionFromPixels(x, y);
let [xgh, ygh] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h); let [xgh, ygh] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h);
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0] const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0]
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance) const color = this.dragRulerGetColorForDistance(startDistance + subDistance);
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity); const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity);
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1); const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color, alpha); highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color, alpha);
+71 -26
View File
@@ -1,7 +1,7 @@
"use strict" "use strict"
import {currentSpeedProvider, getMovedDistanceFromToken, getRangesFromSpeedProvider, getUnreachableColorFromSpeedProvider, initApi, registerModule, registerSystem} from "./api.js" import {currentSpeedProvider, getColorForDistanceAndToken, getMovedDistanceFromToken, getRangesFromSpeedProvider, initApi, registerModule, registerSystem} from "./api.js";
import {checkDependencies, getHexSizeSupportTokenGridCenter} from "./compatibility.js"; import {checkDependencies, getHexSizeSupportTokenGridCenter, highlightMeasurementTerrainRuler} from "./compatibility.js";
import {moveEntities, onMouseMove} from "./foundry_imports.js" import {moveEntities, onMouseMove} from "./foundry_imports.js"
import {performMigrations} from "./migration.js" import {performMigrations} from "./migration.js"
import {DragRulerRuler} from "./ruler.js"; import {DragRulerRuler} from "./ruler.js";
@@ -22,7 +22,7 @@ Hooks.once("init", () => {
Ruler = DragRulerRuler; Ruler = DragRulerRuler;
window.dragRuler = { window.dragRuler = {
getColorForDistance, getColorForDistanceAndToken,
getMovedDistanceFromToken, getMovedDistanceFromToken,
registerModule, registerModule,
registerSystem, registerSystem,
@@ -66,6 +66,8 @@ function hookDragHandlers(entityType) {
const originalDragLeftMoveHandler = entityType.prototype._onDragLeftMove const originalDragLeftMoveHandler = entityType.prototype._onDragLeftMove
entityType.prototype._onDragLeftMove = function (event) { entityType.prototype._onDragLeftMove = function (event) {
if (entityType === Token)
applyGridlessSnapping.call(this, event);
originalDragLeftMoveHandler.call(this, event) originalDragLeftMoveHandler.call(this, event)
onEntityLeftDragMove.call(this, event) onEntityLeftDragMove.call(this, event)
} }
@@ -149,7 +151,8 @@ function onKeyShift(up) {
function onKeySpace(up) { function onKeySpace(up) {
const ruler = canvas.controls.ruler; const ruler = canvas.controls.ruler;
if (!ruler.draggedEntity) // Ruler can end up being undefined here if no canvas is active
if (!ruler?.draggedEntity)
return false; return false;
if (ruler._state !== Ruler.STATES.INACTIVE) if (ruler._state !== Ruler.STATES.INACTIVE)
@@ -266,28 +269,70 @@ function onEntityDragLeftCancel(event) {
return true return true
} }
export function getColorForDistance(startDistance, subDistance=0) { function applyGridlessSnapping(event) {
if (!this.isDragRuler) const ruler = canvas.controls.ruler;
return this.color if (!game.settings.get(settingsKey, "useGridlessRaster"))
if (!this.draggedEntity.actor) { return;
return this.color; if (!ruler.isDragRuler)
return;
if (game.keyboard.isDown("Shift"))
return;
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS)
return;
const rasterWidth = 35 / canvas.stage.scale.x;
const tokenX = event.data.destination.x;
const tokenY = event.data.destination.y;
const destination = {x: tokenX + ruler.rulerOffset.x, y: tokenY + ruler.rulerOffset.y};
const ranges = getRangesFromSpeedProvider(ruler.draggedEntity);
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
if (terrainRulerAvailable) {
const segments = Ruler.dragRulerGetRaysFromWaypoints(ruler.waypoints, destination).map(ray => {return {ray}});
const pinpointDistances = new Map();
for (const range of ranges) {
pinpointDistances.set(range.range, null);
}
terrainRuler.measureDistances(segments, {pinpointDistances});
const targetDistance = Array.from(pinpointDistances.entries())
.filter(([_key, val]) => val)
.reduce((value, current) => value[0] > current[0] ? value : current, [0, null]);
const rasterLocation = targetDistance[1];
if (rasterLocation) {
const deltaX = destination.x - rasterLocation.x;
const deltaY = destination.y - rasterLocation.y;
const rasterDistance = Math.hypot(deltaX, deltaY);
if (rasterDistance < rasterWidth) {
event.data.destination.x = rasterLocation.x - ruler.rulerOffset.x;
event.data.destination.y = rasterLocation.y - ruler.rulerOffset.y;
}
}
} }
// Don't apply colors if the current user doesn't have at least observer permissions else {
if (this.draggedEntity.actor.permission < 2) { let waypointDistance = 0;
// If this is a pc and alwaysShowSpeedForPCs is enabled we show the color anyway let origin = event.data.origin;
if (!(this.draggedEntity.actor.data.type === "character" && game.settings.get(settingsKey, "alwaysShowSpeedForPCs"))) if (ruler.waypoints.length > 1) {
return this.color const segments = Ruler.dragRulerGetRaysFromWaypoints(ruler.waypoints, destination).map(ray => {return {ray}});
origin = segments.pop().ray.A;
waypointDistance = canvas.grid.measureDistances(segments).reduce((a, b) => a + b);
origin = {x: origin.x - ruler.rulerOffset.x, y: origin.y - ruler.rulerOffset.y};
}
const deltaX = tokenX - origin.x;
const deltaY = tokenY - origin.y;
const distance = Math.hypot(deltaX, deltaY);
// targetRange will be the largest range that's still smaller than distance
let targetDistance = ranges
.map(range => range.range)
.map(range => range - waypointDistance)
.map(range => range * canvas.dimensions.size / canvas.dimensions.distance)
.filter(range => range < distance)
.reduce((a, b) => Math.max(a, b), 0);
if (targetDistance) {
if (distance < targetDistance + rasterWidth) {
event.data.destination.x = origin.x + deltaX * targetDistance / distance;
event.data.destination.y = origin.y + deltaY * targetDistance / distance;
}
}
} }
const distance = startDistance + subDistance
if (!this.dragRulerRanges)
this.dragRulerRanges = getRangesFromSpeedProvider(this.draggedEntity);
const ranges = this.dragRulerRanges;
if (ranges.length === 0)
return this.color
const currentRange = ranges.reduce((minRange, currentRange) => {
if (distance <= currentRange.range && currentRange.range < minRange.range)
return currentRange
return minRange
}, {range: Infinity, color: getUnreachableColorFromSpeedProvider()})
return currentRange.color
} }
+2 -2
View File
@@ -49,14 +49,14 @@ function calculateUpdate(combat, token, rays) {
} }
// Add the passed waypoints to the combatant // Add the passed waypoints to the combatant
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS); const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
const dragRulerFlags = combatant.data.flags.dragRuler; const dragRulerFlags = combatant.data.flags.dragRuler;
const waypoints = dragRulerFlags.passedWaypoints; const waypoints = dragRulerFlags.passedWaypoints;
for (const ray of rays) { for (const ray of rays) {
// Ignore rays that have the same start and end coordinates // Ignore rays that have the same start and end coordinates
if (ray.A.x !== ray.B.x || ray.A.y !== ray.B.y) { if (ray.A.x !== ray.B.x || ray.A.y !== ray.B.y) {
if (terrainRulerAvailable) { if (terrainRulerAvailable) {
measureDistances([{ray}], token, getTokenShape(token), {terrainRulerInitialState: waypoints[waypoints.length - 1]?.dragRulerFinalState}); measureDistances([{ray}], token, getTokenShape(token), {terrainRulerInitialState: waypoints[waypoints.length - 1]?.dragRulerFinalState, enableTerrainRuler: terrainRulerAvailable});
ray.A.dragRulerVisitedSpaces = ray.terrainRulerVisitedSpaces; ray.A.dragRulerVisitedSpaces = ray.terrainRulerVisitedSpaces;
ray.A.dragRulerFinalState = ray.terrainRulerFinalState; ray.A.dragRulerFinalState = ray.terrainRulerFinalState;
} }
+19
View File
@@ -1,3 +1,4 @@
import {getColorForDistanceAndToken, getRangesFromSpeedProvider} from "./api.js";
import {cancelScheduledMeasurement, measure} from "./foundry_imports.js" import {cancelScheduledMeasurement, measure} from "./foundry_imports.js"
import {getMovementHistory} from "./movement_tracking.js"; import {getMovementHistory} from "./movement_tracking.js";
import {settingsKey} from "./settings.js"; import {settingsKey} from "./settings.js";
@@ -152,4 +153,22 @@ export class DragRulerRuler extends Ruler {
return ray; return ray;
}); });
} }
dragRulerGetColorForDistance(distance) {
if (!this.isDragRuler)
return this.color;
if (!this.draggedEntity.actor) {
return this.color;
}
// Don't apply colors if the current user doesn't have at least observer permissions
if (this.draggedEntity.actor.permission < 2) {
// If this is a pc and alwaysShowSpeedForPCs is enabled we show the color anyway
if (!(this.draggedEntity.actor.data.type === "character" && game.settings.get(settingsKey, "alwaysShowSpeedForPCs")))
return this.color;
}
distance = Math.round(distance * 100) / 100;
if (!this.dragRulerRanges)
this.dragRulerRanges = getRangesFromSpeedProvider(this.draggedEntity);
return getColorForDistanceAndToken(distance, this.draggedEntity, this.dragRulerRanges);
}
} }
+9
View File
@@ -29,6 +29,15 @@ export function registerSettings() {
default: false, default: false,
}) })
game.settings.register(settingsKey, "useGridlessRaster", {
name: "drag-ruler.settings.useGridlessRaster.name",
hint: "drag-ruler.settings.useGridlessRaster.hint",
scope: "client",
config: true,
type: Boolean,
default: true,
});
game.settings.register(settingsKey, "alwaysShowSpeedForPCs", { game.settings.register(settingsKey, "alwaysShowSpeedForPCs", {
name: "drag-ruler.settings.alwaysShowSpeedForPCs.name", name: "drag-ruler.settings.alwaysShowSpeedForPCs.name",
hint: "drag-ruler.settings.alwaysShowSpeedForPCs.hint", hint: "drag-ruler.settings.alwaysShowSpeedForPCs.hint",
+2 -2
View File
@@ -63,9 +63,9 @@ export class SpeedProvider {
* (1 is regular cost, 2 costs double, 3 costs triple, ...) * (1 is regular cost, 2 costs double, 3 costs triple, ...)
* *
* Parameters: * Parameters:
* - options: An object used to configure TerrainLayer's cost calculation. Ex: If options.ignoreGrid is set to true, then Euclidean measurement can be forced on a gridded map. * - options: An object used to configure Enhanced Terrain Layer's cost calculation. Ex: If options.ignoreGrid is set to true, then Euclidean measurement can be forced on a gridded map.
* *
* This function is only called if the TerrainLayer and TerrainRuler modules are enabled. * This function is only called if the Enhanced Terrain Layer and Terrain Ruler 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) * 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)
*/ */
+44 -16
View File
@@ -10,33 +10,61 @@ export function getSnapPointForToken(x, y, token) {
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);
} }
if (canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) { if (canvas.grid.isHex) {
if (token.document.getFlag("hex-size-support", "borderSize") % 2 === 0) { if (game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid) if (token.document.getFlag("hex-size-support", "borderSize") % 2 === 0) {
return new PIXI.Point(snapPoint.x, snapPoint.y) const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid)
return new PIXI.Point(snapPoint.x, snapPoint.y)
}
else {
return new PIXI.Point(...canvas.grid.getCenter(x, y))
}
} }
else { else {
return new PIXI.Point(...canvas.grid.getCenter(x, y)) return new PIXI.Point(...canvas.grid.getCenter(x, y));
} }
} }
const [topLeftX, topLeftY] = canvas.grid.getTopLeft(x, y);
let cellX, cellY;
if (token.data.width % 2 === 0)
cellX = x - canvas.grid.h / 2;
else
cellX = x;
if (token.data.height % 2 === 0)
cellY = y - canvas.grid.h / 2;
else
cellY = y;
const [centerX, centerY] = canvas.grid.getCenter(cellX, cellY);
let snapX, snapY;
// Tiny tokens can snap to the cells corners // Tiny tokens can snap to the cells corners
if (!canvas.grid.isHex && token.data.width <= 0.5) { if (token.data.width <= 0.5) {
const [topLeftX, topLeftY] = canvas.grid.getTopLeft(x, y);
const offsetX = x - topLeftX; const offsetX = x - topLeftX;
const offsetY = y - topLeftY;
const subGridWidth = Math.floor(canvas.grid.w / 2); const subGridWidth = Math.floor(canvas.grid.w / 2);
const subGridHeight = Math.floor(canvas.grid.h / 2);
const subGridPosX = Math.floor(offsetX / subGridWidth); const subGridPosX = Math.floor(offsetX / subGridWidth);
const subGridPosY = Math.floor(offsetY / subGridHeight); snapX = topLeftX + (subGridPosX + 0.5) * subGridWidth;
return new PIXI.Point(topLeftX + (subGridPosX + 0.5) * subGridWidth, topLeftY + (subGridPosY + 0.5) * subGridHeight);
} }
// Hex tokens, tokens with odd multipliers (1x1, 3x3, ...) and tokens smaller than 1x1 but bigger than 0.5x0.5 snap to the center of the grid cell // Tokens with odd multipliers (1x1, 3x3, ...) and tokens smaller than 1x1 but bigger than 0.5x0.5 snap to the center of the grid cell
if (canvas.grid.isHex || Math.round(token.data.width) % 2 === 1 || token.data.width < 1) { else if (Math.round(token.data.width) % 2 === 1 || token.data.width < 1) {
return new PIXI.Point(...canvas.grid.getCenter(x, y)) snapX = centerX;
} }
// All remaining tokens (those with even or fractional multipliers on square grids) snap to the intersection points of the grid // All remaining tokens (those with even or fractional multipliers on square grids) snap to the intersection points of the grid
const [snappedX, snappedY] = canvas.grid.getCenter(x - canvas.grid.w / 2, y - canvas.grid.h / 2) else {
return new PIXI.Point(snappedX + canvas.grid.w / 2, snappedY + canvas.grid.h / 2) snapX = centerX + canvas.grid.w / 2;
}
if (token.data.height <= 0.5) {
const offsetY = y - topLeftY;
const subGridHeight = Math.floor(canvas.grid.h / 2);
const subGridPosY = Math.floor(offsetY / subGridHeight);
snapY = topLeftY + (subGridPosY + 0.5) * subGridHeight;
}
else if (Math.round(token.data.height) % 2 === 1 || token.data.height < 1) {
snapY = centerY;
}
else {
snapY = centerY + canvas.grid.h / 2;
}
return new PIXI.Point(snapX, snapY);
} }
export function getSnapPointForMeasuredTemplate(x, y) { export function getSnapPointForMeasuredTemplate(x, y) {