Compare commits

...

31 Commits

Author SHA1 Message Date
Manuel Vögele 94a8e6f147 Release v1.14.1 2024-07-28 09:29:39 +02:00
Manuel Vögele ab1f5b4c9b Restore rulerOffset for all uses except hex grids (fixes #332)
This restores the functinality of gridless, but it is not a proper fix. Most likely all the functionality in foundry_imports.js has to be re-imported at this point
2024-07-28 09:28:13 +02:00
Manuel Vögele 1974e6e4a3 Revert to pre-v12 maeasureDistances to restore gridless functionality 2024-07-28 09:04:41 +02:00
Manuel Vögele cb40c8de50 Release v1.14.0 2024-07-27 23:18:55 +02:00
Manuel Vögele 6da3e65301 Fix some deprecation warnings 2024-07-27 23:14:40 +02:00
Manuel Vögele a7d06eaed3 Make use of new foundry functions for highlightMeasurementNative 2024-07-27 23:14:40 +02:00
Manuel Vögele 18cf63895c Remove v11 compatibility code 2024-07-27 22:03:48 +02:00
n0q 0e2ab35db7 fix hex calculations in v12 2024-07-27 21:55:29 +02:00
Nils 0808f17ee3 Update foundry for v12 2024-07-27 21:55:29 +02:00
Manuel Vögele b649dacdb5 Add World of Darkness 20th ed to supported Game System list 2024-03-25 16:38:38 +01:00
KitCat c490550ed9 Add Support for 5x5 Hex Tokens (#315) 2024-03-06 23:28:16 +01:00
Manuel Vögele f12dbf0e61 Update wall colission error string to it's new location (it's a foundry native string that was moved to a new key) 2024-03-06 22:31:57 +01:00
Manuel Vögele f1fcc52867 Remove v10 support code 2023-06-13 19:53:30 +02:00
Manuel Vögele af03341638 Use token.center instead of calculating the center manually 2023-06-13 18:25:42 +02:00
Manuel Vögele 79db620fc1 Release v1.13.8 2023-06-13 18:08:40 +02:00
Manuel Vögele 8bf075c2ce Use the token center as token position in getMovedDistanceFromToken on gridless scenes (fixes #200) 2023-06-13 18:04:14 +02:00
Manuel Vögele f0c6ce1bcc Release v1.13.7 2023-06-13 17:01:02 +02:00
Manuel Vögele 7b392e5c9d Update module.json to reflect the new discord name 2023-06-13 17:00:48 +02:00
YenBenGrey efe6eac5aa Add default values for the Crucible system (#283) 2023-06-13 09:53:18 +02:00
Manuel Vögele 52b64c3016 Release v1.13.6 2023-06-12 22:22:54 +02:00
Manuel Vögele 48d0d17628 Use canvas.scene.grid.type instead of cnavas.scene.gridType (which is undefined since v10) (fixes #272) 2023-06-12 22:21:19 +02:00
Manuel Vögele 8101381cc4 Remove redundant & broken code path in _computeDistance (fixes #280) 2023-06-12 21:40:04 +02:00
Manuel Vögele 844df150a6 Release v1.13.5 2023-06-12 19:52:33 +02:00
Manuel Vögele 47715e95f6 Replace newly private functions with public alternatives 2023-06-12 19:50:55 +02:00
Manuel Vögele 1dccbcb081 Reformat with prettier 2023-06-12 19:36:20 +02:00
Manuel Vögele 4c3d7ab42a Add .prettierignore 2023-06-12 19:26:44 +02:00
Manuel Vögele 61fc795f7b Correct a typo in a variable name (fixes #255) 2023-06-11 13:46:09 +02:00
Foundry Hub f10fa049b9 Translated using Weblate (Portuguese (Brazil)) (#268)
Currently translated at 100.0% (59 of 59 strings)

Translation: Drag Ruler/main
Translate-URL: https://weblate.foundryvtt-hub.com/projects/drag-ruler/main/pt_BR/

Co-authored-by: eunaumtenhoid <eunaumtenhoid@outlook.com>
2023-06-11 13:20:55 +02:00
Manuel Vögele c844318836 Update module.json to reflect v11 compatiblility 2023-06-11 13:20:11 +02:00
pkonshik 9af692566c Apply changes for v11 compatibility (#277) 2023-06-11 13:17:36 +02:00
Txus d08416777b Wfrp4e default values (#276) 2023-06-10 10:27:42 +02:00
16 changed files with 203 additions and 154 deletions
+4
View File
@@ -0,0 +1,4 @@
*.md
*.html
*.json
foundry.js
+38
View File
@@ -1,3 +1,41 @@
## 1.14.1
### Bug fixes
- The functionality Drag Ruler on gridless has been restored
## 1.14.0
### New features
- Drag Ruler now supports hex tokens of size 5 (thanks KitCat!)
### Compatibility
- Drag Ruler has been updated to work with Foundry v12 (thanks TPNils and N0q!)
## 1.13.8
### Bugfixes
- `getMovedDistanceFromToken` no longer returns incorrect values on gridless maps
## 1.13.7
### Compatibility
- Drag Ruler's generic speed provider is now aware of good defaults for the Crucible game system
## 1.13.6
### Bugfixes
- Fixed a bug that caused no measurements to be shown next to the ruler
- Fixed a bug where diagonal paths would sometimes highlight squares that don't blong to the path on square maps
## 1.13.5
### Compatibility
- Drag Ruler is now compatible with Foundry VTT v11 (thanks to pkonshik for doing much of the porting work!)
- Drag Ruler's generic speed provider is now aware of good defaults for Warhammer Fantasy Roleplay 4th Edition
### Translations
- Updated Portugese (Brazil) translation (thanks eunaumtenhoid!)
## 1.13.4 ## 1.13.4
### Translations ### Translations
- New translation: Portuguese (Brazil) (thanks eunaumtenhoid!) - New translation: Portuguese (Brazil) (thanks eunaumtenhoid!)
+1
View File
@@ -66,6 +66,7 @@ The game systems that offer Drag Ruler integration are:
- The Dark Eye 5 / Das Schwarze Auge 5 (via the module [TDE5/DSA5 Drag Ruler Integration](https://foundryvtt.com/packages/dsa5-drag-ruler)) - The Dark Eye 5 / Das Schwarze Auge 5 (via the module [TDE5/DSA5 Drag Ruler Integration](https://foundryvtt.com/packages/dsa5-drag-ruler))
- 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)
- World of Darkness 20th ed (starting with version 3.3.0)
- WWII:OWB (starting with version 1.0.4) - WWII:OWB (starting with version 1.0.4)
+2 -2
View File
@@ -78,7 +78,7 @@
}, },
"showGMRulerToPlayers": { "showGMRulerToPlayers": {
"name": "Mostrar régua do GM aos jogadores", "name": "Mostrar régua do GM aos jogadores",
"hint": "Se desativado, o governante dos GMs não será exibido para jogadores não-GM." "hint": "Se desativado, a régua dos GMs não será exibido para jogadores não-GM."
}, },
"speedProviderSettings": { "speedProviderSettings": {
"name": "Configurações do provedor de velocidade", "name": "Configurações do provedor de velocidade",
@@ -114,7 +114,7 @@
}, },
"useGridlessRaster": { "useGridlessRaster": {
"name": "Use o ajuste baseado em velocidade", "name": "Use o ajuste baseado em velocidade",
"hint": "Em cenas Gridless, isso faz com que os tokens se encaixem nas faixas de velocidade do token." "hint": "Em cenas sem grid, isso faz com que os tokens se encaixem nas faixas de velocidade do token."
} }
} }
} }
+6 -5
View File
@@ -2,16 +2,17 @@
"id": "drag-ruler", "id": "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.13.4", "version": "1.14.1",
"compatibility": { "compatibility": {
"minimum": "10", "minimum": "12",
"verified": "10" "verified": "12",
"maximum": "12"
}, },
"authors": [ "authors": [
{ {
"name": "Manuel Vögele", "name": "Manuel Vögele",
"email": "develop@manuel-voegele.de", "email": "develop@manuel-voegele.de",
"discord": "Stäbchenfisch#5107" "discord": "stabchenfisch"
} }
], ],
"esmodules": [ "esmodules": [
@@ -80,7 +81,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.13.4.zip", "download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.14.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",
+5 -1
View File
@@ -133,8 +133,12 @@ export function getColorForDistanceAndToken(distance, token, ranges = null) {
export function getMovedDistanceFromToken(token) { export function getMovedDistanceFromToken(token) {
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active; const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
const history = getMovementHistory(token); const history = getMovementHistory(token);
let tokenPos = {x: token.x, y: token.y};
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
tokenPos = token.center;
}
const segments = CONFIG.Canvas.rulerClass const segments = CONFIG.Canvas.rulerClass
.dragRulerGetRaysFromWaypoints(history, {x: token.x, y: token.y}) .dragRulerGetRaysFromWaypoints(history, tokenPos)
.map(ray => { .map(ray => {
return {ray}; return {ray};
}); });
+3 -3
View File
@@ -15,10 +15,10 @@ export function highlightMeasurementTerrainRuler(
} }
export function measureDistances(segments, entity, shape, options = {}) { export function measureDistances(segments, entity, shape, options = {}) {
const opts = duplicate(options); const opts = foundry.utils.duplicate(options);
if (canvas.grid.diagonalRule === "EUCL") { if (canvas.grid.diagonalRule === "EUCL") {
opts.ignoreGrid = true; opts.ignoreGrid = true;
opts.gridSpaes = false; opts.gridSpaces = false;
} }
if (opts.enableTerrainRuler) { if (opts.enableTerrainRuler) {
opts.gridSpaces = true; opts.gridSpaces = true;
@@ -31,7 +31,7 @@ export function measureDistances(segments, entity, shape, options = {}) {
); );
previousSegments.forEach( previousSegments.forEach(
segment => segment =>
(segment.ray.terrainRulerVisitedSpaces = duplicate(segment.ray.dragRulerVisitedSpaces)), (segment.ray.terrainRulerVisitedSpaces = foundry.utils.duplicate(segment.ray.dragRulerVisitedSpaces)),
); );
opts.costFunction = buildCostFunction(entity, shape); opts.costFunction = buildCostFunction(entity, shape);
if (previousSegments.length > 0) if (previousSegments.length > 0)
+11 -16
View File
@@ -1,31 +1,26 @@
// Wrapper to fix a FoundryVTT bug that causes the return values of canvas.grid.grid.getPixelsFromGridPosition to be ordered inconsistently // Wrapper to fix a FoundryVTT bug that causes the return values of canvas.grid.grid.getPixelsFromGridPosition to be ordered inconsistently
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705 // This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
export function getPixelsFromGridPosition(xGrid, yGrid) { export function getPixelsFromGridPosition(xGrid, yGrid) {
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) { let coord = getPixelsFromGridPositionObj({x: xGrid, y: yGrid});
return canvas.grid.grid.getPixelsFromGridPosition(yGrid, xGrid); return [coord.x, coord.y];
}
return canvas.grid.grid.getPixelsFromGridPosition(xGrid, yGrid);
} }
// Wrapper to fix a FoundryVTT bug that causes the return values of canvas.grid.grid.getPixelsFromGridPosition to be ordered inconsistently // This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
export function getGridPositionFromPixels(xPixel, yPixel) { export function getGridPositionFromPixels(xPixel, yPixel) {
const [x, y] = canvas.grid.grid.getGridPositionFromPixels(xPixel, yPixel); let coord = getGridPositionFromPixelsObj({x: xPixel, y: yPixel});
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) return [y, x]; return [coord.x, coord.y];
return [x, y];
} }
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
export function getGridPositionFromPixelsObj(o) { export function getGridPositionFromPixelsObj(o) {
const r = {}; const coord = canvas.grid.getOffset(o);
[r.x, r.y] = getGridPositionFromPixels(o.x, o.y); return {x: coord.j, y: coord.i};
return r;
} }
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
export function getPixelsFromGridPositionObj(o) { export function getPixelsFromGridPositionObj(o) {
const r = {}; return canvas.grid.getTopLeftPoint({j: o.x, i: o.y});
[r.x, r.y] = getPixelsFromGridPosition(o.x, o.y);
return r;
} }
export function getCenterFromGridPositionObj(o) { export function getCenterFromGridPositionObj(o) {
+18 -47
View File
@@ -35,7 +35,8 @@ export async function moveEntities(draggedEntity, selectedEntities) {
); );
}); });
if (hasCollision) { if (hasCollision) {
ui.notifications.error(game.i18n.localize("ERROR.TokenCollide")); ui.notifications.error(game.i18n.localize("RULER.MovementCollision"));
this._state = Ruler.STATES.MEASURING;
this._endMeasurement(); this._endMeasurement();
return true; return true;
} }
@@ -47,7 +48,10 @@ export async function moveEntities(draggedEntity, selectedEntities) {
await animateEntities.call(this, selectedEntities, draggedEntity, rays, wasPaused); await animateEntities.call(this, selectedEntities, draggedEntity, rays, wasPaused);
// Once all animations are complete we can clear the ruler // Once all animations are complete we can clear the ruler
if (this.draggedEntity?.id === draggedEntity.id) this._endMeasurement(); if (this.draggedEntity?.id === draggedEntity.id) {
this._state = Ruler.STATES.MEASURING;
this._endMeasurement();
}
} }
// This is a modified version code extracted from Ruler.moveToken from foundry 0.7.9 // This is a modified version code extracted from Ruler.moveToken from foundry 0.7.9
@@ -133,9 +137,13 @@ export function onMouseMove(event) {
// Extract event data // Extract event data
const destination = { const destination = {
x: event.data.destination.x + this.rulerOffset.x, x: event.interactionData.destination.x,
y: event.data.destination.y + this.rulerOffset.y, y: event.interactionData.destination.y,
}; };
if (!canvas.grid.isHex) {
destination.x += this.rulerOffset.x;
destination.y += this.rulerOffset.y;
}
// Hide any existing Token HUD // Hide any existing Token HUD
canvas.hud.token.clear(); canvas.hud.token.clear();
@@ -148,7 +156,7 @@ export function onMouseMove(event) {
function scheduleMeasurement(destination, event) { function scheduleMeasurement(destination, event) {
const measurementInterval = 50; const measurementInterval = 50;
const mt = event._measureTime || 0; const mt = event._measureTime || 0;
const originalEvent = event.data.originalEvent; const originalEvent = event.interactionData.originalEvent;
if (Date.now() - mt > measurementInterval) { if (Date.now() - mt > measurementInterval) {
this.measure(destination, {snap: !disableSnap}); this.measure(destination, {snap: !disableSnap});
event._measureTime = Date.now(); event._measureTime = Date.now();
@@ -185,51 +193,14 @@ export function highlightMeasurementNative(
tokenShape = [{x: 0, y: 0}], tokenShape = [{x: 0, y: 0}],
alpha = 1, alpha = 1,
) { ) {
const spacer = canvas.scene.gridType === CONST.GRID_TYPES.SQUARE ? 1.41 : 1; for (const offset of canvas.grid.getDirectPath([ray.A, ray.B]).reverse()) {
const nMax = Math.max( const point = canvas.grid.getTopLeftPoint(offset);
Math.floor(ray.distance / (spacer * Math.min(canvas.grid.w, canvas.grid.h))), const center = canvas.grid.getCenterPoint(offset);
1, const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, center)}]);
);
const tMax = Array.fromRange(nMax + 1).map(t => t / nMax);
// Track prior position
let prior = null;
// Iterate over ray portions
for (let [i, t] of tMax.reverse().entries()) {
let {x, y} = ray.project(t);
// Get grid position
let [x0, y0] = i === 0 ? [null, null] : prior;
let [x1, y1] = canvas.grid.grid.getGridPositionFromPixels(x, y);
if (x0 === x1 && y0 === y1) continue;
// Highlight the grid position
let [xgtl, ygtl] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1);
let [xg, yg] = canvas.grid.grid.getCenter(xgtl, ygtl);
const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, {x: xg, y: yg})}]);
const distance = sum(canvas.grid.measureDistances(pathUntilSpace, {gridSpaces: true})); const distance = sum(canvas.grid.measureDistances(pathUntilSpace, {gridSpaces: true}));
const color = this.dragRulerGetColorForDistance(distance); const color = this.dragRulerGetColorForDistance(distance);
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity); const snapPoint = getSnapPointForToken(point.x, point.y, this.draggedEntity);
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1); const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
prior = [x1, y1];
// If the positions are not neighbors, also highlight their halfway point
if (i > 0 && !canvas.grid.isNeighbor(x0, y0, x1, y1)) {
let th = tMax[i - 1] - 0.5 / nMax;
let {x, y} = ray.project(th);
let [x1h, y1h] = canvas.grid.grid.getGridPositionFromPixels(x, y);
let [xghtl, yghtl] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h);
let [xgh, ygh] = canvas.grid.grid.getCenter(xghtl, yghtl);
const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, {x: xgh, y: ygh})}]);
const distance = sum(canvas.grid.measureDistances(pathUntilSpace, {gridSpaces: true}));
const color = this.dragRulerGetColorForDistance(distance);
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity);
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); highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color, alpha);
} }
} }
+3 -3
View File
@@ -9,11 +9,11 @@
* - Instead of taking a grid parameter, get the grid value from the globas canvas * - Instead of taking a grid parameter, get the grid value from the globas canvas
*/ */
export function findVertexSnapPoint(x, y, altOrientationFlag) { export function findVertexSnapPoint(x, y, altOrientationFlag) {
const grid = canvas.grid.grid; const grid = canvas.grid;
if (grid.columnar) { if (grid.columnar) {
return findSnapPointCols(x, y, grid.h, grid.w, altOrientationFlag); return findSnapPointCols(x, y, grid.sizeY, grid.sizeX, altOrientationFlag);
} else { } else {
return findSnapPointRows(x, y, grid.h, grid.w, altOrientationFlag); return findSnapPointRows(x, y, grid.sizeY, grid.sizeX, altOrientationFlag);
} }
} }
+10 -9
View File
@@ -137,9 +137,10 @@ function onEntityLeftDragStart(wrapped, event) {
const ruler = canvas.controls.ruler; const ruler = canvas.controls.ruler;
ruler.draggedEntity = this; ruler.draggedEntity = this;
const entityCenter = getEntityCenter(this); const entityCenter = getEntityCenter(this);
const isV11 = game.release.generation === 11;
ruler.rulerOffset = { ruler.rulerOffset = {
x: entityCenter.x - event.data.origin.x, x: isV11 ? entityCenter.x - event.interactionData.origin.x : 0,
y: entityCenter.y - event.data.origin.y, y: isV11 ? entityCenter.y - event.interactionData.origin.y : 0,
}; };
if (game.settings.get(settingsKey, "autoStartMeasurement")) { if (game.settings.get(settingsKey, "autoStartMeasurement")) {
let options = {}; let options = {};
@@ -215,8 +216,8 @@ function applyGridlessSnapping(event) {
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) return; if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) return;
const rasterWidth = 35 / canvas.stage.scale.x; const rasterWidth = 35 / canvas.stage.scale.x;
const tokenX = event.data.destination.x; const tokenX = event.interactionData.destination.x;
const tokenY = event.data.destination.y; const tokenY = event.interactionData.destination.y;
const destination = {x: tokenX + ruler.rulerOffset.x, y: tokenY + ruler.rulerOffset.y}; const destination = {x: tokenX + ruler.rulerOffset.x, y: tokenY + ruler.rulerOffset.y};
const ranges = getRangesFromSpeedProvider(ruler.draggedEntity); const ranges = getRangesFromSpeedProvider(ruler.draggedEntity);
@@ -241,13 +242,13 @@ function applyGridlessSnapping(event) {
const deltaY = destination.y - rasterLocation.y; const deltaY = destination.y - rasterLocation.y;
const rasterDistance = Math.hypot(deltaX, deltaY); const rasterDistance = Math.hypot(deltaX, deltaY);
if (rasterDistance < rasterWidth) { if (rasterDistance < rasterWidth) {
event.data.destination.x = rasterLocation.x - ruler.rulerOffset.x; event.interactionData.destination.x = rasterLocation.x - ruler.rulerOffset.x;
event.data.destination.y = rasterLocation.y - ruler.rulerOffset.y; event.interactionData.destination.y = rasterLocation.y - ruler.rulerOffset.y;
} }
} }
} else { } else {
let waypointDistance = 0; let waypointDistance = 0;
let origin = event.data.origin; let origin = event.interactionData.origin;
if (ruler.waypoints.length > 1) { if (ruler.waypoints.length > 1) {
const segments = ruler.constructor const segments = ruler.constructor
.dragRulerGetRaysFromWaypoints(ruler.waypoints, destination) .dragRulerGetRaysFromWaypoints(ruler.waypoints, destination)
@@ -271,8 +272,8 @@ function applyGridlessSnapping(event) {
.reduce((a, b) => Math.max(a, b), 0); .reduce((a, b) => Math.max(a, b), 0);
if (targetDistance) { if (targetDistance) {
if (distance < targetDistance + rasterWidth) { if (distance < targetDistance + rasterWidth) {
event.data.destination.x = origin.x + (deltaX * targetDistance) / distance; event.interactionData.destination.x = origin.x + (deltaX * targetDistance) / distance;
event.data.destination.y = origin.y + (deltaY * targetDistance) / distance; event.interactionData.destination.y = origin.y + (deltaY * targetDistance) / distance;
} }
} }
} }
+47 -37
View File
@@ -17,6 +17,7 @@ import {
getTokenShape, getTokenShape,
isPathfindingEnabled, isPathfindingEnabled,
} from "./util.js"; } from "./util.js";
import {getPointer} from "./util.js";
export function extendRuler() { export function extendRuler() {
class DragRulerRuler extends CONFIG.Canvas.rulerClass { class DragRulerRuler extends CONFIG.Canvas.rulerClass {
@@ -40,14 +41,17 @@ export function extendRuler() {
if (!this.isDragRuler) return await super.moveToken(event); if (!this.isDragRuler) return await super.moveToken(event);
} }
toJSON() { _getMeasurementData() {
const json = super.toJSON(); const json =
typeof super._getMeasurementData === "function"
? super._getMeasurementData()
: super.toJSON();
if (this.draggedEntity) { if (this.draggedEntity) {
const isToken = this.draggedEntity instanceof Token; const isToken = this.draggedEntity instanceof Token;
json.draggedEntityIsToken = isToken; json.draggedEntityIsToken = isToken;
json.draggedEntity = this.draggedEntity.id; json.draggedEntity = this.draggedEntity.id;
json.waypoints = json.waypoints.map(old => { json.waypoints = json.waypoints.map(old => {
let w = duplicate(old); let w = foundry.utils.duplicate(old);
w.isPathfinding = undefined; w.isPathfinding = undefined;
return w; return w;
}); });
@@ -55,7 +59,13 @@ export function extendRuler() {
return json; return json;
} }
/** @deprecated since V12 */
toJSON() {
return this._getMeasurementData();
}
update(data) { update(data) {
if (!data || data.state === Ruler.STATES.INACTIVE) return this.clear();
// Don't show a GMs drag ruler to non GM players // Don't show a GMs drag ruler to non GM players
if ( if (
data.draggedEntity && data.draggedEntity &&
@@ -104,7 +114,10 @@ export function extendRuler() {
// Compute the measurement destination, segments, and distance // Compute the measurement destination, segments, and distance
const d = this._getMeasurementDestination(destination); const d = this._getMeasurementDestination(destination);
if (d.x === this.destination.x && d.y === this.destination.y) return; if (this.destination && d.x === this.destination.x && d.y === this.destination.y) {
this.performPostPathfindingActions(options);
return;
}
this.destination = d; this.destination = d;
// TODO Check if we can reuse the old path // TODO Check if we can reuse the old path
@@ -205,6 +218,7 @@ export function extendRuler() {
} }
} }
} }
this.dragRulerSendState();
return this.segments; return this.segments;
} }
@@ -226,7 +240,7 @@ export function extendRuler() {
const waypoints = const waypoints =
this.draggedEntity instanceof Token this.draggedEntity instanceof Token
? applyTokenSizeOffset(unsnappedWaypoints, this.draggedEntity) ? applyTokenSizeOffset(unsnappedWaypoints, this.draggedEntity)
: duplicate(unsnappedWaypoints); : foundry.utils.duplicate(unsnappedWaypoints);
const unsnappedSegments = []; const unsnappedSegments = [];
const segments = []; const segments = [];
for (const [i, p1] of waypoints.entries()) { for (const [i, p1] of waypoints.entries()) {
@@ -251,6 +265,9 @@ export function extendRuler() {
unsnappedSegments.push({ray: unsnappedRay, label}); unsnappedSegments.push({ray: unsnappedRay, label});
} }
this.dragRulerUnsnappedSegments = unsnappedSegments; this.dragRulerUnsnappedSegments = unsnappedSegments;
if (this.labels.children.length > segments.length) {
this.labels.removeChildren(segments.length).forEach(c => c.destroy());
}
return segments; return segments;
} else { } else {
return super._getMeasurementSegments(); return super._getMeasurementSegments();
@@ -261,29 +278,23 @@ export function extendRuler() {
if (!this.isDragRuler) { if (!this.isDragRuler) {
return super._computeDistance(gridSpaces); return super._computeDistance(gridSpaces);
} }
if (!this.dragRulerEnableTerrainRuler) { const shape = this.draggedEntity ? getTokenShape(this.draggedEntity) : null;
if (!this.dragRulerIgnoreGrid) { const options = {
gridSpaces = true; ignoreGrid: this.dragRulerIgnoreGrid,
} gridSpaces,
super._computeDistance(gridSpaces); enableTerrainRuler: this.dragRulerEnableTerrainRuler,
} else { };
const shape = this.draggedEntity ? getTokenShape(this.draggedEntity) : null; const distances = measureDistances(this.segments, this.draggedEntity, shape, options);
const options = { this.totalDistance = 0;
ignoreGrid: this.dragRulerIgnoreGrid, for (const [i, d] of distances.entries()) {
gridSpaces, let s = this.segments[i];
enableTerrainRuler: this.dragRulerEnableTerrainRuler, s.startDistance = this.totalDistance;
}; this.totalDistance += d;
const distances = measureDistances(this.segments, this.draggedEntity, shape, options); s.last = i === this.segments.length - 1;
let totalDistance = 0; s.distance = d;
for (const [i, d] of distances.entries()) { s.text = this._getSegmentLabel(s);
let s = this.segments[i];
s.startDistance = totalDistance;
totalDistance += d;
s.last = i === this.segments.length - 1;
s.distance = d;
s.text = this._getSegmentLabel(s, totalDistance);
}
} }
for (const [i, segment] of this.segments.entries()) { for (const [i, segment] of this.segments.entries()) {
const unsnappedSegment = this.dragRulerUnsnappedSegments[i]; const unsnappedSegment = this.dragRulerUnsnappedSegments[i];
unsnappedSegment.startDistance = segment.startDistance; unsnappedSegment.startDistance = segment.startDistance;
@@ -379,9 +390,7 @@ export function extendRuler() {
options.snap = options.snap ?? true; 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( const mousePosition = getPointer().getLocalPosition(canvas.tokens);
canvas.tokens,
);
const rulerOffset = this.rulerOffset; const rulerOffset = this.rulerOffset;
// Options are not passed to _removeWaypoint in vanilla Foundry. // Options are not passed to _removeWaypoint in vanilla Foundry.
@@ -390,7 +399,7 @@ export function extendRuler() {
{x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y}, {x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y},
options, options,
); );
game.user.broadcastActivity({ruler: this}); this.performPostPathfindingActions(options);
} else { } else {
this.dragRulerAbortDrag(event); this.dragRulerAbortDrag(event);
} }
@@ -416,7 +425,7 @@ export function extendRuler() {
this._endMeasurement(); this._endMeasurement();
// Deactivate the drag workflow in mouse // Deactivate the drag workflow in mouse
token.mouseInteractionManager._deactivateDragEvents(); token.mouseInteractionManager.cancel(event);
token.mouseInteractionManager.state = token.mouseInteractionManager.states.HOVER; token.mouseInteractionManager.state = token.mouseInteractionManager.states.HOVER;
// This will cancel the current drag operation // This will cancel the current drag operation
@@ -435,7 +444,7 @@ export function extendRuler() {
this.dragRulerAddWaypoint(waypoint, {snap: false}); this.dragRulerAddWaypoint(waypoint, {snap: false});
} }
this.measure(this.destination); this.measure(this.destination);
game.user.broadcastActivity({ruler: this}); this.dragRulerSendState();
} }
static dragRulerGetRaysFromWaypoints(waypoints, destination) { static dragRulerGetRaysFromWaypoints(waypoints, destination) {
@@ -483,9 +492,7 @@ export function extendRuler() {
if (isToken && game.settings.get(settingsKey, "enableMovementHistory")) if (isToken && game.settings.get(settingsKey, "enableMovementHistory"))
ruler.dragRulerAddWaypointHistory(getMovementHistory(entity)); ruler.dragRulerAddWaypointHistory(getMovementHistory(entity));
ruler.dragRulerAddWaypoint(entityCenter, {snap: false}); ruler.dragRulerAddWaypoint(entityCenter, {snap: false});
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition( const mousePosition = getPointer().getLocalPosition(canvas.tokens);
canvas.tokens,
);
const destination = { const destination = {
x: mousePosition.x + ruler.rulerOffset.x, x: mousePosition.x + ruler.rulerOffset.x,
y: mousePosition.y + ruler.rulerOffset.y, y: mousePosition.y + ruler.rulerOffset.y,
@@ -494,8 +501,11 @@ export function extendRuler() {
} }
dragRulerSendState() { dragRulerSendState() {
if (this.user !== game.user) {
return;
}
game.user.broadcastActivity({ game.user.broadcastActivity({
ruler: this.toJSON(), ruler: this._getMeasurementData(),
}); });
} }
} }
+1 -1
View File
@@ -298,7 +298,7 @@ function enumerateProviderSettings(provider) {
for (const setting of provider.settings) { for (const setting of provider.settings) {
try { try {
if (setting.scope === "world" && !game.user.isGM) continue; if (setting.scope === "world" && !game.user.isGM) continue;
const s = duplicate(setting); const s = foundry.utils.duplicate(setting);
s.id = `${provider.id}.setting.${s.id}`; s.id = `${provider.id}.setting.${s.id}`;
s.name = game.i18n.localize(s.name); s.name = game.i18n.localize(s.name);
s.hint = game.i18n.localize(s.hint); s.hint = game.i18n.localize(s.hint);
+1 -1
View File
@@ -115,7 +115,7 @@ export class GenericSpeedProvider extends SpeedProvider {
getRanges(token) { getRanges(token) {
const speedAttribute = this.getSetting("speedAttribute"); const speedAttribute = this.getSetting("speedAttribute");
if (!speedAttribute) return []; if (!speedAttribute) return [];
const tokenSpeed = parseFloat(getProperty(token, speedAttribute)); const tokenSpeed = parseFloat(foundry.utils.getProperty(token, speedAttribute));
if (tokenSpeed === undefined) { if (tokenSpeed === undefined) {
console.warn( console.warn(
`Drag Ruler (Generic Speed Provider) | The configured token speed attribute "${speedAttribute}" didn't return a speed value. To use colors based on drag distance set the setting to the correct value (or clear the box to disable this feature).`, `Drag Ruler (Generic Speed Provider) | The configured token speed attribute "${speedAttribute}" didn't return a speed value. To use colors based on drag distance set the setting to the correct value (or clear the box to disable this feature).`,
+8
View File
@@ -23,6 +23,10 @@ export function getDefaultSpeedAttribute() {
return "actor.system.combatValues.movement.total"; return "actor.system.combatValues.movement.total";
case "splittermond": case "splittermond":
return "actor.derivedValues.speed.value"; return "actor.derivedValues.speed.value";
case "wfrp4e":
return "actor.system.details.move.walk";
case "crucible":
return "actor.system.movement.stride";
} }
return ""; return "";
} }
@@ -45,6 +49,10 @@ export function getDefaultDashMultiplier() {
return 5; return 5;
case "splittermond": case "splittermond":
return 3; return 3;
case "wfrp4e":
return 2;
case "crucible":
return 0;
} }
return 0; return 0;
} }
+45 -29
View File
@@ -37,20 +37,20 @@ export function getHexTokenSize(token) {
} }
export function getEntityCenter(token) { export function getEntityCenter(token) {
if (token instanceof Token && canvas.grid.isHex) { if (token instanceof Token && isCanvasHex()) {
const center = token.center; const center = token.center;
const size = getHexTokenSize(token); const size = getHexTokenSize(token);
if (size % 2 === 0) { if (size % 2 === 0) {
let offset; let offset;
if (canvas.grid.grid.columnar) { if (canvas.grid.columnar) {
offset = canvas.grid.grid.w - canvas.grid.grid.h; offset = canvas.grid.sizeX - canvas.grid.sizeY;
} else { } else {
offset = canvas.grid.grid.h - canvas.grid.grid.w; offset = canvas.grid.sizeY - canvas.grid.sizeX;
} }
if (getAltOrientationFlagForToken(token, size)) { if (getAltOrientationFlagForToken(token, size)) {
offset *= -1; offset *= -1;
} }
if (canvas.grid.grid.columnar) { if (canvas.grid.columnar) {
center.x -= offset; center.x -= offset;
return center; return center;
} else { } else {
@@ -79,7 +79,7 @@ export function getSnapPointForToken(x, y, token) {
return {x, y}; return {x, y};
} }
if (canvas.grid.isHex) { if (isCanvasHex()) {
const size = getHexTokenSize(token); const size = getHexTokenSize(token);
if (size % 2 === 0) { if (size % 2 === 0) {
return findVertexSnapPoint(x, y, getAltOrientationFlagForToken(token, size)); return findVertexSnapPoint(x, y, getAltOrientationFlagForToken(token, size));
@@ -89,27 +89,27 @@ export function getSnapPointForToken(x, y, token) {
} }
const [topLeftX, topLeftY] = canvas.grid.getTopLeft(x, y); const [topLeftX, topLeftY] = canvas.grid.getTopLeft(x, y);
let cellX, cellY; let cell = {};
if (token.document.width % 2 === 0) cellX = x - canvas.grid.h / 2; if (token.document.width % 2 === 0) cell.x = x - canvas.grid.sizeY / 2;
else cellX = x; else cell.x = x;
if (token.document.height % 2 === 0) cellY = y - canvas.grid.h / 2; if (token.document.height % 2 === 0) cell.y = y - canvas.grid.sizeY / 2;
else cellY = y; else cell.y = y;
const [centerX, centerY] = canvas.grid.getCenter(cellX, cellY); const center = canvas.grid.getCenterPoint(cell);
let snapX, snapY; let snapX, snapY;
// Tiny tokens can snap to the cells corners // Tiny tokens can snap to the cells corners
if (token.document.width <= 0.5) { if (token.document.width <= 0.5) {
const offsetX = x - topLeftX; const offsetX = x - topLeftX;
const subGridWidth = Math.floor(canvas.grid.w / 2); const subGridWidth = Math.floor(canvas.grid.sizeX / 2);
const subGridPosX = Math.floor(offsetX / subGridWidth); const subGridPosX = Math.floor(offsetX / subGridWidth);
snapX = topLeftX + (subGridPosX + 0.5) * subGridWidth; snapX = topLeftX + (subGridPosX + 0.5) * subGridWidth;
} }
// 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
else if (Math.round(token.document.width) % 2 === 1 || token.document.width < 1) { else if (Math.round(token.document.width) % 2 === 1 || token.document.width < 1) {
snapX = centerX; snapX = center.x;
} }
// 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
else { else {
snapX = centerX + canvas.grid.w / 2; snapX = center.x + canvas.grid.sizeX / 2;
} }
if (token.document.height <= 0.5) { if (token.document.height <= 0.5) {
const offsetY = y - topLeftY; const offsetY = y - topLeftY;
@@ -117,9 +117,9 @@ export function getSnapPointForToken(x, y, token) {
const subGridPosY = Math.floor(offsetY / subGridHeight); const subGridPosY = Math.floor(offsetY / subGridHeight);
snapY = topLeftY + (subGridPosY + 0.5) * subGridHeight; snapY = topLeftY + (subGridPosY + 0.5) * subGridHeight;
} else if (Math.round(token.document.height) % 2 === 1 || token.document.height < 1) { } else if (Math.round(token.document.height) % 2 === 1 || token.document.height < 1) {
snapY = centerY; snapY = center.y;
} else { } else {
snapY = centerY + canvas.grid.h / 2; snapY = center.y + canvas.grid.sizeY / 2;
} }
return {x: snapX, y: snapY}; return {x: snapX, y: snapY};
} }
@@ -147,7 +147,7 @@ export function highlightTokenShape(position, shape, color, alpha) {
const area = getAreaFromPositionAndShape(position, shape); const area = getAreaFromPositionAndShape(position, shape);
for (const space of area) { for (const space of area) {
const [x, y] = getPixelsFromGridPosition(space.x, space.y); const [x, y] = getPixelsFromGridPosition(space.x, space.y);
canvas.grid.grid.highlightGridPosition(layer, {x, y, color, alpha: 0.25 * alpha}); canvas.grid.highlightGridPosition(layer, {x, y, color, alpha: 0.25 * alpha});
} }
} }
@@ -155,9 +155,9 @@ export function getAreaFromPositionAndShape(position, shape) {
return shape.map(space => { return shape.map(space => {
let x = position.x + space.x; let x = position.x + space.x;
let y = position.y + space.y; let y = position.y + space.y;
if (canvas.grid.isHex) { if (isCanvasHex()) {
let shiftedRow; let shiftedRow;
if (canvas.grid.grid.options.even) shiftedRow = 1; if (canvas.grid?.even) shiftedRow = 1;
else shiftedRow = 0; else shiftedRow = 0;
if (canvas.grid.grid.columnar) { if (canvas.grid.grid.columnar) {
if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) { if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) {
@@ -212,6 +212,16 @@ export function getTokenShape(token) {
{x: 0, y: -2}, {x: 0, y: -2},
{x: 1, y: -2}, {x: 1, y: -2},
]); ]);
if (size >= 5)
shape = shape.concat([
{x: -2, y: 0},
{x: 1, y: 1},
{x: -1, y: 2},
{x: 0, y: 2},
{x: 1, y: 2},
{x: -2, y: 1},
{x: 2, y: 0},
]);
if (getAltOrientationFlagForToken(token, size)) { if (getAltOrientationFlagForToken(token, size)) {
shape.forEach(space => (space.y *= -1)); shape.forEach(space => (space.y *= -1));
@@ -226,7 +236,7 @@ export function getTokenShape(token) {
export function getTokenSize(token) { export function getTokenSize(token) {
let w, h; let w, h;
if (canvas.grid.isHex) { if (isCanvasHex()) {
w = h = getHexTokenSize(token); w = h = getHexTokenSize(token);
} else { } else {
w = token.document.width; w = token.document.width;
@@ -244,26 +254,26 @@ export function applyTokenSizeOffset(waypoints, token) {
const tokenSize = getTokenSize(token); const tokenSize = getTokenSize(token);
const waypointOffset = {x: 0, y: 0}; const waypointOffset = {x: 0, y: 0};
if (canvas.grid.isHex) { if (isCanvasHex()) {
const isAltOrientation = getAltOrientationFlagForToken(token, getHexTokenSize(token)); const isAltOrientation = getAltOrientationFlagForToken(token, getHexTokenSize(token));
if (canvas.grid.grid.columnar) { if (canvas.grid.grid.columnar) {
if (tokenSize.w % 2 === 0) { if (tokenSize.w % 2 === 0) {
waypointOffset.x = canvas.grid.w / 2; waypointOffset.x = canvas.grid.sizeX / 2;
if (isAltOrientation) waypointOffset.x *= -1; if (isAltOrientation) waypointOffset.x *= -1;
} }
} else { } else {
if (tokenSize.h % 2 === 0) { if (tokenSize.h % 2 === 0) {
waypointOffset.y = canvas.grid.h / 2; waypointOffset.y = canvas.grid.sizeY / 2;
if (isAltOrientation) waypointOffset.y *= -1; if (isAltOrientation) waypointOffset.y *= -1;
} }
} }
// If hex size support isn't active leave the waypoints like they are // If hex size support isn't active leave the waypoints like they are
} else { } else {
if (tokenSize.w % 2 === 0) { if (tokenSize.w % 2 === 0) {
waypointOffset.x = canvas.grid.w / 2; waypointOffset.x = canvas.grid.sizeX / 2;
} }
if (tokenSize.h % 2 === 0) { if (tokenSize.h % 2 === 0) {
waypointOffset.y = canvas.grid.h / 2; waypointOffset.y = canvas.grid.sizeY / 2;
} }
} }
@@ -285,10 +295,12 @@ export function isClose(a, b, delta) {
return Math.abs(a - b) <= delta; return Math.abs(a - b) <= delta;
} }
export function getPointer() {
return canvas.app.renderer.events.pointer;
}
export function getMeasurePosition() { export function getMeasurePosition() {
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition( const mousePosition = getPointer().getLocalPosition(canvas.tokens);
canvas.tokens,
);
const rulerOffset = canvas.controls.ruler.rulerOffset; const rulerOffset = canvas.controls.ruler.rulerOffset;
const measurePosition = {x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y}; const measurePosition = {x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y};
return measurePosition; return measurePosition;
@@ -312,3 +324,7 @@ export function isPathfindingEnabled() {
if (moveWithoutAnimation) return false; if (moveWithoutAnimation) return false;
return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding; return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding;
} }
function isCanvasHex() {
return canvas.grid.isHexagonal;
}