Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94a8e6f147 | |||
| ab1f5b4c9b | |||
| 1974e6e4a3 | |||
| cb40c8de50 | |||
| 6da3e65301 | |||
| a7d06eaed3 | |||
| 18cf63895c | |||
| 0e2ab35db7 | |||
| 0808f17ee3 | |||
| b649dacdb5 | |||
| c490550ed9 | |||
| f12dbf0e61 | |||
| f1fcc52867 | |||
| af03341638 | |||
| 79db620fc1 | |||
| 8bf075c2ce | |||
| f0c6ce1bcc | |||
| 7b392e5c9d | |||
| efe6eac5aa | |||
| 52b64c3016 | |||
| 48d0d17628 | |||
| 8101381cc4 |
@@ -1,3 +1,32 @@
|
|||||||
|
## 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
|
## 1.13.5
|
||||||
### Compatibility
|
### Compatibility
|
||||||
- Drag Ruler is now compatible with Foundry VTT v11 (thanks to pkonshik for doing much of the porting work!)
|
- Drag Ruler is now compatible with Foundry VTT v11 (thanks to pkonshik for doing much of the porting work!)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+6
-5
@@ -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.5",
|
"version": "1.14.1",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "11",
|
"minimum": "12",
|
||||||
"verified": "11"
|
"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.5.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
@@ -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};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ 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.gridSpaces = false;
|
opts.gridSpaces = false;
|
||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
+17
-46
@@ -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.interactionData.destination.x + this.rulerOffset.x,
|
x: event.interactionData.destination.x,
|
||||||
y: event.interactionData.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();
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
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);
|
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
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -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.interactionData.origin.x,
|
x: isV11 ? entityCenter.x - event.interactionData.origin.x : 0,
|
||||||
y: entityCenter.y - event.interactionData.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 = {};
|
||||||
|
|||||||
+32
-19
@@ -41,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;
|
||||||
});
|
});
|
||||||
@@ -56,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 &&
|
||||||
@@ -105,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
|
||||||
@@ -206,6 +218,7 @@ export function extendRuler() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.dragRulerSendState();
|
||||||
return this.segments;
|
return this.segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,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()) {
|
||||||
@@ -252,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();
|
||||||
@@ -262,12 +278,6 @@ export function extendRuler() {
|
|||||||
if (!this.isDragRuler) {
|
if (!this.isDragRuler) {
|
||||||
return super._computeDistance(gridSpaces);
|
return super._computeDistance(gridSpaces);
|
||||||
}
|
}
|
||||||
if (!this.dragRulerEnableTerrainRuler) {
|
|
||||||
if (!this.dragRulerIgnoreGrid) {
|
|
||||||
gridSpaces = true;
|
|
||||||
}
|
|
||||||
super._computeDistance(gridSpaces);
|
|
||||||
} else {
|
|
||||||
const shape = this.draggedEntity ? getTokenShape(this.draggedEntity) : null;
|
const shape = this.draggedEntity ? getTokenShape(this.draggedEntity) : null;
|
||||||
const options = {
|
const options = {
|
||||||
ignoreGrid: this.dragRulerIgnoreGrid,
|
ignoreGrid: this.dragRulerIgnoreGrid,
|
||||||
@@ -275,16 +285,16 @@ export function extendRuler() {
|
|||||||
enableTerrainRuler: this.dragRulerEnableTerrainRuler,
|
enableTerrainRuler: this.dragRulerEnableTerrainRuler,
|
||||||
};
|
};
|
||||||
const distances = measureDistances(this.segments, this.draggedEntity, shape, options);
|
const distances = measureDistances(this.segments, this.draggedEntity, shape, options);
|
||||||
let totalDistance = 0;
|
this.totalDistance = 0;
|
||||||
for (const [i, d] of distances.entries()) {
|
for (const [i, d] of distances.entries()) {
|
||||||
let s = this.segments[i];
|
let s = this.segments[i];
|
||||||
s.startDistance = totalDistance;
|
s.startDistance = this.totalDistance;
|
||||||
totalDistance += d;
|
this.totalDistance += d;
|
||||||
s.last = i === this.segments.length - 1;
|
s.last = i === this.segments.length - 1;
|
||||||
s.distance = d;
|
s.distance = d;
|
||||||
s.text = this._getSegmentLabel(s, totalDistance);
|
s.text = this._getSegmentLabel(s);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -389,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);
|
||||||
}
|
}
|
||||||
@@ -434,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) {
|
||||||
@@ -491,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
@@ -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);
|
||||||
|
|||||||
@@ -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).`,
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export function getDefaultSpeedAttribute() {
|
|||||||
return "actor.derivedValues.speed.value";
|
return "actor.derivedValues.speed.value";
|
||||||
case "wfrp4e":
|
case "wfrp4e":
|
||||||
return "actor.system.details.move.walk";
|
return "actor.system.details.move.walk";
|
||||||
|
case "crucible":
|
||||||
|
return "actor.system.movement.stride";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -49,6 +51,8 @@ export function getDefaultDashMultiplier() {
|
|||||||
return 3;
|
return 3;
|
||||||
case "wfrp4e":
|
case "wfrp4e":
|
||||||
return 2;
|
return 2;
|
||||||
|
case "crucible":
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-27
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +296,7 @@ export function isClose(a, b, delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPointer() {
|
export function getPointer() {
|
||||||
return canvas.app.renderer.plugins.interaction?.mouse ?? canvas.app.renderer.events.pointer;
|
return canvas.app.renderer.events.pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMeasurePosition() {
|
export function getMeasurePosition() {
|
||||||
@@ -314,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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user