Compare commits
21 Commits
prerelease
...
alpha
| Author | SHA1 | Date | |
|---|---|---|---|
| 605f4371e6 | |||
| 0ad03ad5f7 | |||
| 035cfb8969 | |||
| 90a25f467b | |||
| ab85b98a5c | |||
| 1669edf757 | |||
| d1b5f7c39b | |||
| 24f1e78092 | |||
| 854903a314 | |||
| 232eca74de | |||
| 9c7d4ead50 | |||
| a91c3c1b8c | |||
| 99d057bffc | |||
| 792d644803 | |||
| 63b13cbb34 | |||
| f1a153a2eb | |||
| 59c12d017b | |||
| 9552c25e6e | |||
| 461e1d0818 | |||
| e1265ad6fb | |||
| 93f0bf6369 |
@@ -1,3 +1,31 @@
|
|||||||
|
## 1.8.2
|
||||||
|
### Compatibility
|
||||||
|
- The generic speed provider defaults have been updated for lance 1.0 (thanks to BoltsJ!)
|
||||||
|
- Eliminated a deprecation warning when both Drag Ruler and Hex Size Support are enabled (thanks Argonius-Angelus!)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.8.1
|
||||||
|
### Bugfixes
|
||||||
|
- Fixed a bug where the function that was bound to the spacebar key wouldn't work correctly when the "Toggle Snap To Grid" module was enabled
|
||||||
|
|
||||||
|
### Translation
|
||||||
|
- Updated Spanish translation (thanks to Viriato139ac#342)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.8.0
|
||||||
|
### New features
|
||||||
|
- Pressing escape during a drag now cancels the drag
|
||||||
|
- Undoing a movement via Ctrl+Z will now also remove that movement from Drag Ruler's movement history
|
||||||
|
- Drag Ruler can now configured to stay disabled by default when a Token/Template is being dragged. In that case it will activate once the button to place a waypoint is being pressed.
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- Fixed a bug that caused the ruler to snap to grid when a waypoint was deleted while shift was being pressed (thanks to Michael Clavell!)
|
||||||
|
- Fixed a bug that could leave behind a single waypoint on the canvas when canceling a dragging operation while moving the mouse
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
- Drag Ruler is now compatible with the "Toggle Snap To Grid" module (thanks to Michael Clavell!)
|
||||||
|
|
||||||
|
|
||||||
## 1.7.7
|
## 1.7.7
|
||||||
### Compatibility
|
### Compatibility
|
||||||
- Updated the default settings for the swade game system. The new default speed attribute points to a speed value that gets adjusted for wounds.
|
- Updated the default settings for the swade game system. The new default speed attribute points to a speed value that gets adjusted for wounds.
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Manuel Vögele
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -37,10 +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))
|
||||||
- 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)
|
||||||
|
|||||||
@@ -34,6 +34,10 @@
|
|||||||
"name": "Geschwindigkeit von Spielercharakteren für jeden anzeigen",
|
"name": "Geschwindigkeit von Spielercharakteren für jeden anzeigen",
|
||||||
"hint": "Wenn diese Einstellung aktiviert ist wird die Färbung der hervorgehobenen Felder bei Spielercharakteren allen Spielern angezeigt, selbst wenn diese keinen Zugriff auf den Charakterbogen haben."
|
"hint": "Wenn diese Einstellung aktiviert ist wird die Färbung der hervorgehobenen Felder bei Spielercharakteren allen Spielern angezeigt, selbst wenn diese keinen Zugriff auf den Charakterbogen haben."
|
||||||
},
|
},
|
||||||
|
"autoStartMeasurement": {
|
||||||
|
"name": "Automatisch mit Messen beginnen",
|
||||||
|
"hint": "Wenn diese Option aktiviert ist beginnt Drag Ruler mit dem Messen sobald das Token bewegt wird. Wenn die Option deaktiviert ist bleibt Drag Ruler deaktiviert bis der Knopf zum platzieren eines Wegpunkts betätigt wird."
|
||||||
|
},
|
||||||
"enableMovementHistory": {
|
"enableMovementHistory": {
|
||||||
"name": "Bewegungsverlauf während des Kampfes aktivieren",
|
"name": "Bewegungsverlauf während des Kampfes aktivieren",
|
||||||
"hint": "Wenn diesen Option aktiviert ist merkt sich Drag Ruler die Bewegungen, die während einer Runde eines Kampfes gemacht wurden und zeigt diese an, wenn das Token weiterbewegt wird."
|
"hint": "Wenn diesen Option aktiviert ist merkt sich Drag Ruler die Bewegungen, die während einer Runde eines Kampfes gemacht wurden und zeigt diese an, wenn das Token weiterbewegt wird."
|
||||||
|
|||||||
@@ -34,6 +34,10 @@
|
|||||||
"name": "Show PC speed to everyone",
|
"name": "Show PC speed to everyone",
|
||||||
"hint": "If enabled the coloring based on actor speed for player characters will shown to everyone, even if they don't have observer permission for the character sheet."
|
"hint": "If enabled the coloring based on actor speed for player characters will shown to everyone, even if they don't have observer permission for the character sheet."
|
||||||
},
|
},
|
||||||
|
"autoStartMeasurement": {
|
||||||
|
"name": "Automatically start measuring",
|
||||||
|
"hint": "If enabled, Drag Ruler will start measuring as soon as the token is being dragged. If disabled, Drag Ruler will remain inactive and will only start measuring once the button to add a waypoint is being pressed."
|
||||||
|
},
|
||||||
"enableMovementHistory": {
|
"enableMovementHistory": {
|
||||||
"name": "Enable movement history during combat",
|
"name": "Enable movement history during combat",
|
||||||
"hint": "If enabled, Drag Ruler will remember the path a token took during it's turn in combat and will display it when you pick the token back up."
|
"hint": "If enabled, Drag Ruler will remember the path a token took during it's turn in combat and will display it when you pick the token back up."
|
||||||
|
|||||||
@@ -38,6 +38,10 @@
|
|||||||
"name": "Mostrar velocidad de los PJs a todo el mundo",
|
"name": "Mostrar velocidad de los PJs a todo el mundo",
|
||||||
"hint": "Si se habilita, se mostrará a todo el mundo los códigos de colores de las rutas de los PJs, incluso si no tienen permisos de observador para ese personaje"
|
"hint": "Si se habilita, se mostrará a todo el mundo los códigos de colores de las rutas de los PJs, incluso si no tienen permisos de observador para ese personaje"
|
||||||
},
|
},
|
||||||
|
"autoStartMeasurement": {
|
||||||
|
"name": "Comenzar a medir automáticamente",
|
||||||
|
"hint": "Si se habilita, Drag Ruler comenzará a medir en cuanto se comience a arrastrar un icono. Si se deshabilita, Drag Ruler permanecerá inactivo y comenzará a medir únicamente cuando se presione el botón configurado para añadir un nuevo punto de ruta"
|
||||||
|
},
|
||||||
"enableMovementHistory": {
|
"enableMovementHistory": {
|
||||||
"name": "Habilitar historial de movimiento durante el combate",
|
"name": "Habilitar historial de movimiento durante el combate",
|
||||||
"hint": "Si se habilita, Drag Ruler recordará la ruta que ha seguido un icono en su turno y la mostrará al seleccionarlo de nuevo"
|
"hint": "Si se habilita, Drag Ruler recordará la ruta que ha seguido un icono en su turno y la mostrará al seleccionarlo de nuevo"
|
||||||
|
|||||||
+9
-9
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "drag-ruler",
|
"name": "drag-ruler-alpha",
|
||||||
"title": "Drag Ruler",
|
"title": "Drag Ruler (Alpha Branch)",
|
||||||
"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.7.7",
|
"version": "1.8.3-alpha1",
|
||||||
"minimumCoreVersion" : "0.8.5",
|
"minimumCoreVersion" : "0.8.5",
|
||||||
"compatibleCoreVersion" : "0.8.8",
|
"compatibleCoreVersion" : "0.8.9",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Manuel Vögele",
|
"name": "Manuel Vögele",
|
||||||
@@ -59,10 +59,10 @@
|
|||||||
],
|
],
|
||||||
"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.7.7.zip",
|
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/alpha.zip",
|
||||||
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json",
|
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/alpha/module.json",
|
||||||
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md",
|
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/alpha/README.md",
|
||||||
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
|
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/alpha/CHANGELOG.md",
|
||||||
"bugs": "https://github.com/manuelVo/foundryvtt-drag-ruler/issues",
|
"bugs": "https://github.com/manuelVo/foundryvtt-drag-ruler/issues",
|
||||||
"allowBugReporter": true
|
"allowBugReporter": false
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-3
@@ -103,12 +103,12 @@ export function getUnreachableColorFromSpeedProvider() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCostFromSpeedProvider(token, area) {
|
export function getCostFromSpeedProvider(token, area, options) {
|
||||||
try {
|
try {
|
||||||
if (currentSpeedProvider instanceof Function) {
|
if (currentSpeedProvider instanceof Function) {
|
||||||
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area);
|
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area, options);
|
||||||
}
|
}
|
||||||
return currentSpeedProvider.getCostForStep(token, area);
|
return currentSpeedProvider.getCostForStep(token, area, options);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@@ -116,6 +116,20 @@ export function getCostFromSpeedProvider(token, area) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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}});
|
||||||
|
|||||||
@@ -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,30 +9,32 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function measureDistances(segments, entity, shape, options={}) {
|
export function measureDistances(segments, entity, shape, options={}) {
|
||||||
const isToken = entity instanceof Token;
|
|
||||||
const opts = duplicate(options)
|
const opts = duplicate(options)
|
||||||
if (!opts.gridSpaces)
|
|
||||||
opts.gridSpaces = true;
|
|
||||||
const terrainRulerAvailable = isToken && game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS);
|
|
||||||
|
|
||||||
if (terrainRulerAvailable) {
|
if (opts.enableTerrainRuler) {
|
||||||
|
opts.gridSpaces = true;
|
||||||
const firstNewSegmentIndex = segments.findIndex(segment => !segment.ray.dragRulerVisitedSpaces);
|
const firstNewSegmentIndex = segments.findIndex(segment => !segment.ray.dragRulerVisitedSpaces);
|
||||||
const previousSegments = segments.slice(0, firstNewSegmentIndex);
|
const previousSegments = segments.slice(0, firstNewSegmentIndex);
|
||||||
const newSegments = segments.slice(firstNewSegmentIndex);
|
const newSegments = segments.slice(firstNewSegmentIndex);
|
||||||
const distances = previousSegments.map(segment => segment.ray.dragRulerVisitedSpaces[segment.ray.dragRulerVisitedSpaces.length - 1].distance);
|
const distances = previousSegments.map(segment => segment.ray.dragRulerVisitedSpaces[segment.ray.dragRulerVisitedSpaces.length - 1].distance);
|
||||||
previousSegments.forEach(segment => segment.ray.terrainRulerVisitedSpaces = duplicate(segment.ray.dragRulerVisitedSpaces));
|
previousSegments.forEach(segment => segment.ray.terrainRulerVisitedSpaces = duplicate(segment.ray.dragRulerVisitedSpaces));
|
||||||
opts.costFunction = (x, y) => getCostFromSpeedProvider(entity, getAreaFromPositionAndShape({x, y}, shape), {x, y});
|
opts.costFunction = (x, y, costOptions={}) => { return getCostFromSpeedProvider(entity, getAreaFromPositionAndShape({x, y}, shape), costOptions); }
|
||||||
if (previousSegments.length > 0)
|
if (previousSegments.length > 0)
|
||||||
opts.terrainRulerInitialState = previousSegments[previousSegments.length - 1].ray.dragRulerFinalState;
|
opts.terrainRulerInitialState = previousSegments[previousSegments.length - 1].ray.dragRulerFinalState;
|
||||||
return distances.concat(terrainRuler.measureDistances(newSegments, opts));
|
return distances.concat(terrainRuler.measureDistances(newSegments, opts));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// If another module wants to enable grid measurements but disable grid highlighting,
|
||||||
|
// manually set the *duplicate* option's gridSpaces value to true for the Foundry logic to work properly
|
||||||
|
if(!opts.ignoreGrid) {
|
||||||
|
opts.gridSpaces = true;
|
||||||
|
}
|
||||||
return canvas.grid.measureDistances(segments, opts);
|
return canvas.grid.measureDistances(segments, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-15
@@ -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";
|
||||||
@@ -134,9 +133,7 @@ function scheduleMeasurement(destination, event) {
|
|||||||
this.measure(destination, {snap: !originalEvent.shiftKey});
|
this.measure(destination, {snap: !originalEvent.shiftKey});
|
||||||
event._measureTime = Date.now();
|
event._measureTime = Date.now();
|
||||||
this._state = Ruler.STATES.MEASURING;
|
this._state = Ruler.STATES.MEASURING;
|
||||||
window.clearTimeout(this.deferredMeasurementTimeout);
|
cancelScheduledMeasurement.call(this);
|
||||||
this.deferredMeasurementTimeout = undefined;
|
|
||||||
this.deferredMeasurementResolve?.();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.deferredMeasurementData = {destination, event};
|
this.deferredMeasurementData = {destination, event};
|
||||||
@@ -147,23 +144,37 @@ function scheduleMeasurement(destination, event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cancelScheduledMeasurement() {
|
||||||
|
window.clearTimeout(this.deferredMeasurementTimeout);
|
||||||
|
this.deferredMeasurementTimeout = undefined;
|
||||||
|
this.deferredMeasurementResolve?.();
|
||||||
|
}
|
||||||
|
|
||||||
// This is a modified version of Ruler.measure form foundry 0.7.9
|
// This is a modified version of Ruler.measure form foundry 0.7.9
|
||||||
export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
export function measure(destination, options={}) {
|
||||||
const isToken = this.draggedEntity instanceof Token;
|
const isToken = this.draggedEntity instanceof Token;
|
||||||
if (isToken && !this.draggedEntity.isVisible)
|
if (isToken && !this.draggedEntity.isVisible)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if (snap) {
|
if (options.snap) {
|
||||||
destination = getSnapPointForEntity(destination.x, destination.y, this.draggedEntity);
|
destination = getSnapPointForEntity(destination.x, destination.y, this.draggedEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
const terrainRulerAvailable = isToken && game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS);
|
if(options.gridSpaces === undefined) {
|
||||||
|
options.gridSpaces = canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.ignoreGrid === undefined) {
|
||||||
|
options.ignoreGrid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.enableTerrainRuler = isToken && game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS);
|
||||||
|
|
||||||
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
|
||||||
const centeredWaypoints = isToken ? applyTokenSizeOffset(waypoints, this.draggedEntity) : duplicate(waypoints);
|
const centeredWaypoints = isToken ? applyTokenSizeOffset(waypoints, this.draggedEntity) : duplicate(waypoints);
|
||||||
// Foundries native ruler requires the waypoints to sit in the dead center of the square to work properly
|
// Foundries native ruler requires the waypoints to sit in the dead center of the square to work properly
|
||||||
if (!terrainRulerAvailable)
|
if (!options.enableTerrainRuler && !options.ignoreGrid)
|
||||||
centeredWaypoints.forEach(w => [w.x, w.y] = canvas.grid.getCenter(w.x, w.y));
|
centeredWaypoints.forEach(w => [w.x, w.y] = canvas.grid.getCenter(w.x, w.y));
|
||||||
|
|
||||||
const r = this.ruler;
|
const r = this.ruler;
|
||||||
@@ -197,7 +208,7 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
|||||||
const shape = isToken ? getTokenShape(this.draggedEntity) : null;
|
const shape = isToken ? getTokenShape(this.draggedEntity) : null;
|
||||||
|
|
||||||
// Compute measured distance
|
// Compute measured distance
|
||||||
const distances = measureDistances(centeredSegments, this.draggedEntity, shape, {gridSpaces});
|
const distances = measureDistances(centeredSegments, this.draggedEntity, shape, options);
|
||||||
|
|
||||||
let totalDistance = 0;
|
let totalDistance = 0;
|
||||||
for (let [i, d] of distances.entries()) {
|
for (let [i, d] of distances.entries()) {
|
||||||
@@ -216,8 +227,8 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
|||||||
// Draw measured path
|
// Draw measured path
|
||||||
r.clear();
|
r.clear();
|
||||||
let rulerColor
|
let rulerColor
|
||||||
if (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())) {
|
||||||
@@ -252,8 +263,8 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Highlight grid positions
|
// Highlight grid positions
|
||||||
if (isToken && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
|
if (isToken && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS && options.gridSpaces) {
|
||||||
if (terrainRulerAvailable)
|
if (options.enableTerrainRuler)
|
||||||
highlightMeasurementTerrainRuler.call(this, cs.ray, cs.startDistance, shape, opacityMultiplier)
|
highlightMeasurementTerrainRuler.call(this, cs.ray, cs.startDistance, shape, opacityMultiplier)
|
||||||
else
|
else
|
||||||
highlightMeasurementNative.call(this, cs.ray, cs.startDistance, shape, opacityMultiplier);
|
highlightMeasurementNative.call(this, cs.ray, cs.startDistance, shape, opacityMultiplier);
|
||||||
@@ -289,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);
|
||||||
|
|
||||||
@@ -302,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);
|
||||||
|
|||||||
+109
-44
@@ -1,14 +1,15 @@
|
|||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
import {currentSpeedProvider, getMovedDistanceFromToken, getRangesFromSpeedProvider, getUnreachableColorFromSpeedProvider, initApi, registerModule, registerSystem} from "./api.js"
|
import {currentSpeedProvider, getColorForDistanceAndToken, getMovedDistanceFromToken, initApi, registerModule, registerSystem} from "./api.js";
|
||||||
import {checkDependencies, getHexSizeSupportTokenGridCenter} from "./compatibility.js";
|
import {checkDependencies, getHexSizeSupportTokenGridCenter} 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";
|
||||||
import {getMovementHistory, resetMovementHistory} from "./movement_tracking.js";
|
import {getMovementHistory, removeLastHistoryEntryIfAt, resetMovementHistory} from "./movement_tracking.js";
|
||||||
import {registerSettings, settingsKey} from "./settings.js"
|
import {registerSettings, settingsKey} from "./settings.js"
|
||||||
import {recalculate} from "./socket.js";
|
import {recalculate} from "./socket.js";
|
||||||
import {SpeedProvider} from "./speed_provider.js"
|
import {SpeedProvider} from "./speed_provider.js"
|
||||||
|
import {isClose, setSnapParameterOnOptions} from "./util.js";
|
||||||
|
|
||||||
Hooks.once("init", () => {
|
Hooks.once("init", () => {
|
||||||
registerSettings()
|
registerSettings()
|
||||||
@@ -16,11 +17,12 @@ Hooks.once("init", () => {
|
|||||||
hookDragHandlers(Token);
|
hookDragHandlers(Token);
|
||||||
hookDragHandlers(MeasuredTemplate);
|
hookDragHandlers(MeasuredTemplate);
|
||||||
hookKeyboardManagerFunctions()
|
hookKeyboardManagerFunctions()
|
||||||
|
hookLayerFunctions();
|
||||||
|
|
||||||
Ruler = DragRulerRuler;
|
Ruler = DragRulerRuler;
|
||||||
|
|
||||||
window.dragRuler = {
|
window.dragRuler = {
|
||||||
getColorForDistance,
|
getColorForDistanceAndToken,
|
||||||
getMovedDistanceFromToken,
|
getMovedDistanceFromToken,
|
||||||
registerModule,
|
registerModule,
|
||||||
registerSystem,
|
registerSystem,
|
||||||
@@ -30,6 +32,14 @@ Hooks.once("init", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Hooks.once("ready", () => {
|
Hooks.once("ready", () => {
|
||||||
|
{
|
||||||
|
const alphaWarning = "You are using the alpha branch of Drag Ruler. DO NOT USE IT IN PRODUCTION ENVIRONMENTS. This branch is used for pre-release testing. As a result this branch will only be updated sporadically and may be siginificantly newer (or even older) than the main Drag Ruler release branch. In addition this branch is likely to be likely to contain more bugs.";
|
||||||
|
console.warn(alphaWarning)
|
||||||
|
ui.notifications.warn(alphaWarning)
|
||||||
|
if (game.modules.get("drag-ruler")?.active) {
|
||||||
|
ui.notification.error("You have both the release version and the alpha version of Drag Ruler enabled. Doing so prevents the module from working properly. Please disable either the release or the alpha version of Drag Ruler.", {permanent: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
performMigrations()
|
performMigrations()
|
||||||
checkDependencies();
|
checkDependencies();
|
||||||
Hooks.callAll("dragRuler.ready", SpeedProvider)
|
Hooks.callAll("dragRuler.ready", SpeedProvider)
|
||||||
@@ -40,7 +50,7 @@ Hooks.on("canvasReady", () => {
|
|||||||
ruler.draggedEntity = null;
|
ruler.draggedEntity = null;
|
||||||
Object.defineProperty(ruler, "isDragRuler", {
|
Object.defineProperty(ruler, "isDragRuler", {
|
||||||
get: function isDragRuler() {
|
get: function isDragRuler() {
|
||||||
return Boolean(this.draggedEntity); // If draggedEntity is set this is a drag ruler
|
return Boolean(this.draggedEntity) && this._state !== Ruler.STATES.INACTIVE;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -92,12 +102,32 @@ function hookKeyboardManagerFunctions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hookLayerFunctions() {
|
||||||
|
const originalTokenLayerUndoHistory = TokenLayer.prototype.undoHistory;
|
||||||
|
TokenLayer.prototype.undoHistory = function () {
|
||||||
|
const historyEntry = this.history[this.history.length - 1];
|
||||||
|
return originalTokenLayerUndoHistory.call(this).then((returnValue) => {
|
||||||
|
if (historyEntry.type === "update") {
|
||||||
|
for (const entry of historyEntry.data) {
|
||||||
|
const token = canvas.tokens.get(entry._id);
|
||||||
|
removeLastHistoryEntryIfAt(token, entry.x, entry.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleKeys(event, key, up) {
|
function handleKeys(event, key, up) {
|
||||||
if (event.repeat || this.hasFocus)
|
if (event.repeat || this.hasFocus)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (key.toLowerCase() === "x") return onKeyX(up)
|
const lowercaseKey = key.toLowerCase();
|
||||||
if (key.toLowerCase() === "shift") return onKeyShift(up)
|
|
||||||
|
if (lowercaseKey === "x") return onKeyX(up)
|
||||||
|
if (lowercaseKey === "shift") return onKeyShift(up)
|
||||||
|
if (lowercaseKey === "space") return onKeySpace(up);
|
||||||
|
if (lowercaseKey === "escape") return onKeyEscape(up);
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,10 +155,38 @@ function onKeyShift(up) {
|
|||||||
ruler.measure(measurePosition, {snap: up})
|
ruler.measure(measurePosition, {snap: up})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onKeySpace(up) {
|
||||||
|
const ruler = canvas.controls.ruler;
|
||||||
|
if (!ruler.draggedEntity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ruler._state !== Ruler.STATES.INACTIVE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const swapSpacebarRightClick = game.settings.get(settingsKey, "swapSpacebarRightClick");
|
||||||
|
let options = {};
|
||||||
|
setSnapParameterOnOptions(ruler, options);
|
||||||
|
|
||||||
|
if (!up) {
|
||||||
|
if (swapSpacebarRightClick)
|
||||||
|
ruler.dragRulerAbortDrag();
|
||||||
|
else
|
||||||
|
startDragRuler.call(ruler.draggedEntity, options);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyEscape(up) {
|
||||||
|
const ruler = canvas.controls.ruler;
|
||||||
|
if (!ruler.draggedEntity)
|
||||||
|
return false;
|
||||||
|
if (!up)
|
||||||
|
ruler.dragRulerAbortDrag();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function onEntityLeftDragStart(event) {
|
function onEntityLeftDragStart(event) {
|
||||||
const isToken = this instanceof Token;
|
const isToken = this instanceof Token;
|
||||||
if (isToken && !currentSpeedProvider.usesRuler(this))
|
|
||||||
return
|
|
||||||
const ruler = canvas.controls.ruler
|
const ruler = canvas.controls.ruler
|
||||||
ruler.draggedEntity = this;
|
ruler.draggedEntity = this;
|
||||||
let entityCenter;
|
let entityCenter;
|
||||||
@@ -136,12 +194,33 @@ function onEntityLeftDragStart(event) {
|
|||||||
entityCenter = getHexSizeSupportTokenGridCenter(this);
|
entityCenter = getHexSizeSupportTokenGridCenter(this);
|
||||||
else
|
else
|
||||||
entityCenter = this.center;
|
entityCenter = this.center;
|
||||||
|
ruler.rulerOffset = {x: entityCenter.x - event.data.origin.x, y: entityCenter.y - event.data.origin.y};
|
||||||
|
if (game.settings.get(settingsKey, "autoStartMeasurement")) {
|
||||||
|
let options = {};
|
||||||
|
setSnapParameterOnOptions(ruler, options);
|
||||||
|
startDragRuler.call(this, options, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startDragRuler(options, measureImmediately=true) {
|
||||||
|
const isToken = this instanceof Token;
|
||||||
|
if (isToken && !currentSpeedProvider.usesRuler(this))
|
||||||
|
return;
|
||||||
|
const ruler = canvas.controls.ruler;
|
||||||
ruler.clear();
|
ruler.clear();
|
||||||
ruler._state = Ruler.STATES.STARTING;
|
ruler._state = Ruler.STATES.STARTING;
|
||||||
ruler.rulerOffset = {x: entityCenter.x - event.data.origin.x, y: entityCenter.y - event.data.origin.y};
|
let entityCenter;
|
||||||
|
if (isToken && canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(this))
|
||||||
|
entityCenter = getHexSizeSupportTokenGridCenter(this);
|
||||||
|
else
|
||||||
|
entityCenter = this.center;
|
||||||
if (isToken && game.settings.get(settingsKey, "enableMovementHistory"))
|
if (isToken && game.settings.get(settingsKey, "enableMovementHistory"))
|
||||||
ruler.dragRulerAddWaypointHistory(getMovementHistory(this));
|
ruler.dragRulerAddWaypointHistory(getMovementHistory(this));
|
||||||
ruler.dragRulerAddWaypoint(entityCenter, false);
|
ruler.dragRulerAddWaypoint(entityCenter, {snap: false});
|
||||||
|
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(canvas.tokens);
|
||||||
|
const destination = {x: mousePosition.x + ruler.rulerOffset.x, y: mousePosition.y + ruler.rulerOffset.y};
|
||||||
|
if (measureImmediately)
|
||||||
|
ruler.measure(destination, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEntityLeftDragMove(event) {
|
function onEntityLeftDragMove(event) {
|
||||||
@@ -152,8 +231,10 @@ function onEntityLeftDragMove(event) {
|
|||||||
|
|
||||||
function onEntityDragLeftDrop(event) {
|
function onEntityDragLeftDrop(event) {
|
||||||
const ruler = canvas.controls.ruler
|
const ruler = canvas.controls.ruler
|
||||||
if (!ruler.isDragRuler)
|
if (!ruler.isDragRuler) {
|
||||||
|
ruler.draggedEntity = undefined;
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
// When we're dragging a measured template no token will ever be selected,
|
// When we're dragging a measured template no token will ever be selected,
|
||||||
// resulting in only the dragged template to be moved as would be expected
|
// resulting in only the dragged template to be moved as would be expected
|
||||||
const selectedTokens = canvas.tokens.controlled
|
const selectedTokens = canvas.tokens.controlled
|
||||||
@@ -168,43 +249,27 @@ function onEntityDragLeftDrop(event) {
|
|||||||
function onEntityDragLeftCancel(event) {
|
function onEntityDragLeftCancel(event) {
|
||||||
// This function is invoked by right clicking
|
// This function is invoked by right clicking
|
||||||
const ruler = canvas.controls.ruler
|
const ruler = canvas.controls.ruler
|
||||||
if (!ruler.isDragRuler || ruler._state === Ruler.STATES.MOVING)
|
if (!ruler.draggedEntity || ruler._state === Ruler.STATES.MOVING)
|
||||||
return false
|
return false
|
||||||
if (ruler._state === Ruler.STATES.MEASURING) {
|
|
||||||
if (!game.settings.get(settingsKey, "swapSpacebarRightClick")) {
|
const swapSpacebarRightClick = game.settings.get(settingsKey, "swapSpacebarRightClick");
|
||||||
ruler.dragRulerDeleteWaypoint(event);
|
let options = {};
|
||||||
|
setSnapParameterOnOptions(ruler, options);
|
||||||
|
|
||||||
|
if (ruler._state === Ruler.STATES.INACTIVE) {
|
||||||
|
if (!swapSpacebarRightClick)
|
||||||
|
return false;
|
||||||
|
startDragRuler.call(this, options);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
else if (ruler._state === Ruler.STATES.MEASURING) {
|
||||||
|
if (!swapSpacebarRightClick) {
|
||||||
|
ruler.dragRulerDeleteWaypoint(event, options);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
const snap = !event.shiftKey
|
ruler.dragRulerAddWaypoint(ruler.destination, options);
|
||||||
ruler.dragRulerAddWaypoint(ruler.destination, snap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getColorForDistance(startDistance, subDistance=0) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {measureDistances} from "./compatibility.js";
|
import {measureDistances} from "./compatibility.js";
|
||||||
import {recalculate, updateCombatantDragRulerFlags} from "./socket.js";
|
import {recalculate, updateCombatantDragRulerFlags} from "./socket.js";
|
||||||
import {getTokenShape, zip} from "./util.js";
|
import {getTokenShape, isClose, zip} from "./util.js";
|
||||||
|
|
||||||
function initTrackingFlag(combatant) {
|
function initTrackingFlag(combatant) {
|
||||||
const initialFlag = {passedWaypoints: [], trackedRound: 0};
|
const initialFlag = {passedWaypoints: [], trackedRound: 0};
|
||||||
@@ -81,6 +81,20 @@ export function getMovementHistory(token) {
|
|||||||
return dragRulerFlags.passedWaypoints ?? [];
|
return dragRulerFlags.passedWaypoints ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeLastHistoryEntryIfAt(token, x, y) {
|
||||||
|
const history = getMovementHistory(token);
|
||||||
|
if (history.length === 0)
|
||||||
|
return;
|
||||||
|
const entry = history[history.length - 1];
|
||||||
|
if (!isClose(x + token.w / 2, entry.x, 0.1) || !isClose(y + token.h / 2, entry.y, 0.1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
history.pop();
|
||||||
|
const combat = game.combat;
|
||||||
|
const combatant = combat.getCombatantByToken(token.id);
|
||||||
|
await updateCombatantDragRulerFlags(combat, [{_id: combatant.id, dragRulerFlags: combatant.data.flags.dragRuler}]);
|
||||||
|
}
|
||||||
|
|
||||||
export async function resetMovementHistory(combat, combatantId) {
|
export async function resetMovementHistory(combat, combatantId) {
|
||||||
const combatant = combat.combatants.get(combatantId);
|
const combatant = combat.combatants.get(combatantId);
|
||||||
const dragRulerFlags = combatant.data.flags.dragRuler;
|
const dragRulerFlags = combatant.data.flags.dragRuler;
|
||||||
|
|||||||
+52
-20
@@ -1,7 +1,8 @@
|
|||||||
import {measure} from "./foundry_imports.js"
|
import {getColorForDistanceAndToken, getRangesFromSpeedProvider} from "./api.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";
|
||||||
import {getSnapPointForEntity} from "./util.js";
|
import {getSnapPointForEntity, setSnapParameterOnOptions} from "./util.js";
|
||||||
|
|
||||||
export class DragRulerRuler extends Ruler {
|
export class DragRulerRuler extends Ruler {
|
||||||
// Functions below are overridden versions of functions in Ruler
|
// Functions below are overridden versions of functions in Ruler
|
||||||
@@ -16,18 +17,22 @@ export class DragRulerRuler extends Ruler {
|
|||||||
this.previousWaypoints = [];
|
this.previousWaypoints = [];
|
||||||
this.previousLabels.removeChildren().forEach(c => c.destroy());
|
this.previousLabels.removeChildren().forEach(c => c.destroy());
|
||||||
this.dragRulerRanges = undefined;
|
this.dragRulerRanges = undefined;
|
||||||
|
cancelScheduledMeasurement.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveToken(event) {
|
async moveToken(event) {
|
||||||
// This function is invoked by left clicking
|
// This function is invoked by left clicking
|
||||||
if (!this.isDragRuler)
|
if (!this.isDragRuler)
|
||||||
return await super.moveToken(event);
|
return await super.moveToken(event);
|
||||||
|
|
||||||
|
let options = {};
|
||||||
|
setSnapParameterOnOptions(this, options);
|
||||||
|
|
||||||
if (!game.settings.get(settingsKey, "swapSpacebarRightClick")) {
|
if (!game.settings.get(settingsKey, "swapSpacebarRightClick")) {
|
||||||
const snap = !event.shiftKey;
|
this.dragRulerAddWaypoint(this.destination, options);
|
||||||
this.dragRulerAddWaypoint(this.destination, snap);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.dragRulerDeleteWaypoint();
|
this.dragRulerDeleteWaypoint(event, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +57,7 @@ export class DragRulerRuler extends Ruler {
|
|||||||
else
|
else
|
||||||
this.draggedEntity = canvas.templates.get(data.draggedEntity);
|
this.draggedEntity = canvas.templates.get(data.draggedEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.update(data);
|
super.update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +76,9 @@ export class DragRulerRuler extends Ruler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The functions below aren't present in the orignal Ruler class and are added by Drag Ruler
|
// The functions below aren't present in the orignal Ruler class and are added by Drag Ruler
|
||||||
dragRulerAddWaypoint(point, snap=true) {
|
dragRulerAddWaypoint(point, options={}) {
|
||||||
if (snap) {
|
options.snap = options.snap ?? true;
|
||||||
|
if (options.snap) {
|
||||||
point = getSnapPointForEntity(point.x, point.y, this.draggedEntity);
|
point = getSnapPointForEntity(point.x, point.y, this.draggedEntity);
|
||||||
}
|
}
|
||||||
this.waypoints.push(new PIXI.Point(point.x, point.y));
|
this.waypoints.push(new PIXI.Point(point.x, point.y));
|
||||||
@@ -91,28 +98,36 @@ export class DragRulerRuler extends Ruler {
|
|||||||
this.labels.removeChildren().forEach(c => c.destroy());
|
this.labels.removeChildren().forEach(c => c.destroy());
|
||||||
}
|
}
|
||||||
|
|
||||||
dragRulerDeleteWaypoint(event={preventDefault: () => {return}}) {
|
dragRulerDeleteWaypoint(event={preventDefault: () => {return}}, options={}) {
|
||||||
|
options.snap = options.snap ?? true;
|
||||||
if (this.waypoints.filter(w => !w.isPrevious).length > 1) {
|
if (this.waypoints.filter(w => !w.isPrevious).length > 1) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(canvas.tokens);
|
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(canvas.tokens);
|
||||||
const rulerOffset = this.rulerOffset;
|
const rulerOffset = this.rulerOffset;
|
||||||
this._removeWaypoint({x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y});
|
|
||||||
|
// Options are not passed to _removeWaypoint in vanilla Foundry.
|
||||||
|
// Send them in case other modules have overriden that behavior and accept an options parameter (Toggle Snap to Grid)
|
||||||
|
this._removeWaypoint({x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y}, options);
|
||||||
game.user.broadcastActivity({ruler: this});
|
game.user.broadcastActivity({ruler: this});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const token = this.draggedEntity;
|
this.dragRulerAbortDrag(event);
|
||||||
this._endMeasurement();
|
|
||||||
|
|
||||||
// Deactivate the drag workflow in mouse
|
|
||||||
token.mouseInteractionManager._deactivateDragEvents();
|
|
||||||
token.mouseInteractionManager.state = token.mouseInteractionManager.states.HOVER;
|
|
||||||
|
|
||||||
// This will cancel the current drag operation
|
|
||||||
// Pass in a fake event that hopefully is enough to allow other modules to function
|
|
||||||
token._onDragLeftCancel(event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragRulerAbortDrag(event={preventDefault: () => {return}}) {
|
||||||
|
const token = this.draggedEntity;
|
||||||
|
this._endMeasurement();
|
||||||
|
|
||||||
|
// Deactivate the drag workflow in mouse
|
||||||
|
token.mouseInteractionManager._deactivateDragEvents();
|
||||||
|
token.mouseInteractionManager.state = token.mouseInteractionManager.states.HOVER;
|
||||||
|
|
||||||
|
// This will cancel the current drag operation
|
||||||
|
// Pass in a fake event that hopefully is enough to allow other modules to function
|
||||||
|
token._onDragLeftCancel(event);
|
||||||
|
}
|
||||||
|
|
||||||
async dragRulerRecalculate(tokenIds) {
|
async dragRulerRecalculate(tokenIds) {
|
||||||
if (this._state !== Ruler.STATES.MEASURING)
|
if (this._state !== Ruler.STATES.MEASURING)
|
||||||
return;
|
return;
|
||||||
@@ -123,7 +138,7 @@ export class DragRulerRuler extends Ruler {
|
|||||||
if (game.settings.get(settingsKey, "enableMovementHistory"))
|
if (game.settings.get(settingsKey, "enableMovementHistory"))
|
||||||
this.dragRulerAddWaypointHistory(getMovementHistory(this.draggedEntity));
|
this.dragRulerAddWaypointHistory(getMovementHistory(this.draggedEntity));
|
||||||
for (const waypoint of waypoints) {
|
for (const waypoint of waypoints) {
|
||||||
this.dragRulerAddWaypoint(waypoint, false);
|
this.dragRulerAddWaypoint(waypoint, {snap: false});
|
||||||
}
|
}
|
||||||
this.measure(this.destination);
|
this.measure(this.destination);
|
||||||
game.user.broadcastActivity({ruler: this});
|
game.user.broadcastActivity({ruler: this});
|
||||||
@@ -138,4 +153,21 @@ 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;
|
||||||
|
}
|
||||||
|
if (!this.dragRulerRanges)
|
||||||
|
this.dragRulerRanges = getRangesFromSpeedProvider(this.draggedEntity);
|
||||||
|
return getColorForDistanceAndToken(distance, this.draggedEntity, this.dragRulerRanges);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ export function registerSettings() {
|
|||||||
default: "fresh install"
|
default: "fresh install"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
game.settings.register(settingsKey, "autoStartMeasurement", {
|
||||||
|
name: "drag-ruler.settings.autoStartMeasurement.name",
|
||||||
|
hint: "drag-ruler.settings.autoStartMeasurement.hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.register(settingsKey, "swapSpacebarRightClick", {
|
game.settings.register(settingsKey, "swapSpacebarRightClick", {
|
||||||
name: "drag-ruler.settings.swapSpacebarRightClick.name",
|
name: "drag-ruler.settings.swapSpacebarRightClick.name",
|
||||||
hint: "drag-ruler.settings.swapSpacebarRightClick.hint",
|
hint: "drag-ruler.settings.swapSpacebarRightClick.hint",
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import {currentSpeedProvider} from "./api.js";
|
|||||||
let socket;
|
let socket;
|
||||||
|
|
||||||
Hooks.once("socketlib.ready", () => {
|
Hooks.once("socketlib.ready", () => {
|
||||||
socket = socketlib.registerModule("drag-ruler");
|
socket = socketlib.registerModule("drag-ruler-alpha");
|
||||||
socket.register("updateCombatantDragRulerFlags", _socketUpdateCombatantDragRulerFlags);
|
socket.register("updateCombatantDragRulerFlags", _socketUpdateCombatantDragRulerFlags);
|
||||||
socket.register("recalculate", _socketRecalculate);
|
socket.register("recalculate", _socketRecalculate);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,13 +61,18 @@ export class SpeedProvider {
|
|||||||
* The area indicates the whole area that the token will occupy (for tokens larger than 1x1) the array will more than one entry.
|
* 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.
|
* 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, ...)
|
* (1 is regular cost, 2 costs double, 3 costs triple, ...)
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
* This function is only called if the TerrainLayer and TerrainRuler modules are enabled.
|
* 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)
|
* 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) {
|
getCostForStep(token, area, options={}) {
|
||||||
// Lookup the cost for each square occupied by the token
|
// Lookup the cost for each square occupied by the token
|
||||||
const costs = area.map(space => terrainRuler.getCost(space.x, space.y, {token}));
|
options.token = token;
|
||||||
|
const costs = area.map(space => terrainRuler.getCost(space.x, space.y, options));
|
||||||
// Return the maximum of the costs
|
// Return the maximum of the costs
|
||||||
return costs.reduce((max, current) => Math.max(max, current))
|
return costs.reduce((max, current) => Math.max(max, current))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ export function getDefaultSpeedAttribute() {
|
|||||||
case "dnd5e":
|
case "dnd5e":
|
||||||
return "actor.data.data.attributes.movement.walk"
|
return "actor.data.data.attributes.movement.walk"
|
||||||
case "lancer":
|
case "lancer":
|
||||||
return "actor.data.data.mech.speed"
|
return "actor.data.data.derived.speed"
|
||||||
case "pf1":
|
case "pf1":
|
||||||
case "D35E":
|
case "D35E":
|
||||||
return "actor.data.data.attributes.speed.land.total"
|
return "actor.data.data.attributes.speed.land.total"
|
||||||
|
|||||||
+17
-1
@@ -11,7 +11,7 @@ export function getSnapPointForToken(x, y, token) {
|
|||||||
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 && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
|
||||||
if (token.getFlag("hex-size-support", "borderSize") % 2 === 0) {
|
if (token.document.getFlag("hex-size-support", "borderSize") % 2 === 0) {
|
||||||
const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid)
|
const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid)
|
||||||
return new PIXI.Point(snapPoint.x, snapPoint.y)
|
return new PIXI.Point(snapPoint.x, snapPoint.y)
|
||||||
}
|
}
|
||||||
@@ -198,3 +198,19 @@ export function applyTokenSizeOffset(waypoints, token) {
|
|||||||
|
|
||||||
return waypoints.map(w => new PIXI.Point(w.x + waypointOffset.x, w.y + waypointOffset.y))
|
return waypoints.map(w => new PIXI.Point(w.x + waypointOffset.x, w.y + waypointOffset.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setSnapParameterOnOptions(sourceObject, options) {
|
||||||
|
// Allow outside modules to override snapping
|
||||||
|
if (sourceObject.snapOverride?.active) {
|
||||||
|
options.snapOverrideActive = true;
|
||||||
|
options.snap = sourceObject.snapOverride.snap;
|
||||||
|
sourceObject.snapOverride = undefined; // remove it to prevent any lingering data issues
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.snap = !game.keyboard._downKeys.has("Shift");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isClose(a, b, delta) {
|
||||||
|
return Math.abs(a - b) <= delta;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user