Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d67ca01c3 | |||
| 80ff8832fd | |||
| c787b18fcb | |||
| c03dbc09ff | |||
| 27a2b46a8a | |||
| 94a8e6f147 | |||
| ab1f5b4c9b | |||
| 1974e6e4a3 | |||
| cb40c8de50 | |||
| 6da3e65301 | |||
| a7d06eaed3 | |||
| 18cf63895c | |||
| 0e2ab35db7 | |||
| 0808f17ee3 | |||
| b649dacdb5 | |||
| c490550ed9 | |||
| f12dbf0e61 | |||
| f1fcc52867 | |||
| af03341638 | |||
| 79db620fc1 | |||
| 8bf075c2ce | |||
| f0c6ce1bcc | |||
| 7b392e5c9d | |||
| efe6eac5aa | |||
| 52b64c3016 | |||
| 48d0d17628 | |||
| 8101381cc4 | |||
| 844df150a6 | |||
| 47715e95f6 | |||
| 1dccbcb081 | |||
| 4c3d7ab42a | |||
| 61fc795f7b | |||
| f10fa049b9 | |||
| c844318836 | |||
| 9af692566c | |||
| d08416777b |
@@ -0,0 +1,4 @@
|
|||||||
|
*.md
|
||||||
|
*.html
|
||||||
|
*.json
|
||||||
|
foundry.js
|
||||||
@@ -1,3 +1,47 @@
|
|||||||
|
## 1.14.2
|
||||||
|
### Bug fixes
|
||||||
|
- Fixed a bug where tokens would snap to inconsistent distances on gridless scenes
|
||||||
|
- Fixed a bug that caused the highlighted path on hex grids to be incorrect
|
||||||
|
|
||||||
|
|
||||||
|
## 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,13 +1,17 @@
|
|||||||
[](https://ko-fi.com/staebchenfisch)
|
[](https://ko-fi.com/staebchenfisch)
|
||||||
|
|
||||||
|
## Beware of bugs in v12 and onward
|
||||||
|
Foundry v12 has introduced significant changes to how the grid and the ruler are implemented. While Drag Ruler has now been updated to generally work with v12, quite a lot of bugs remain. This is espeically true for Hex grids, but some bugs affect all the grid types. Some of the issues require a large code rewrite to address them properly. Unfortunately this means that Drag Ruler will not provide the same quality experience (snappyness, bugfreeness) you're used to in v12 (and possibly onward). If you're planning to update to v12 and depend on Drag Ruler, proceed with caution.
|
||||||
|
|
||||||
|
|
||||||
# Drag Ruler
|
# Drag Ruler
|
||||||
|
|
||||||
This module shows a ruler when you drag a token or measurement template to inform you how far you've dragged it from its start point. Additionally, if you're using a grid, the spaces the token will travel though will be colored depending on your tokens speed. By default, three colors are being used: green for spaces that your token can reach by walking normally are colored green, spaces that can only be reached by dashing will be colored yellow and spaces that cannot be reached with the token's speed will be colored red. If you're using a gridless map the ruler color will change to convey this information.
|
This module shows a ruler when you drag a token or measurement template to inform you how far you've dragged it from its start point. Additionally, if you're using a grid, the spaces the token will travel though will be colored depending on your tokens speed. By default, three colors are being used: green for spaces that your token can reach by walking normally are colored green, spaces that can only be reached by dashing will be colored yellow and spaces that cannot be reached with the token's speed will be colored red. If you're using a gridless map the ruler color will change to convey this information.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Supports Tokens of all sizes
|
## Supports Tokens of all sizes
|
||||||
Terrain ruler has excellent support for tokens of all sizes. The Ruler will always originate from the token's center and will always highlight all the squares that tokens move over.
|
Terrain ruler has excellent support for tokens of all sizes. The Ruler will always originate from the token's center and will always highlight all the squares that tokens move over.
|
||||||
|
|
||||||
@@ -66,6 +70,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
@@ -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
@@ -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.2",
|
||||||
"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.2.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,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
@@ -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) {
|
||||||
|
|||||||
+14
-47
@@ -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,8 +137,8 @@ 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hide any existing Token HUD
|
// Hide any existing Token HUD
|
||||||
@@ -148,7 +152,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 +189,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 + 1, point.y + 1, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -137,10 +137,6 @@ 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);
|
||||||
ruler.rulerOffset = {
|
|
||||||
x: entityCenter.x - event.data.origin.x,
|
|
||||||
y: entityCenter.y - event.data.origin.y,
|
|
||||||
};
|
|
||||||
if (game.settings.get(settingsKey, "autoStartMeasurement")) {
|
if (game.settings.get(settingsKey, "autoStartMeasurement")) {
|
||||||
let options = {};
|
let options = {};
|
||||||
setSnapParameterOnOptions(ruler, options);
|
setSnapParameterOnOptions(ruler, options);
|
||||||
@@ -149,7 +145,7 @@ function onEntityLeftDragStart(wrapped, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onEntityLeftDragMoveSnap(wrapped, event) {
|
function onEntityLeftDragMoveSnap(wrapped, event) {
|
||||||
applyGridlessSnapping.call(this, event);
|
applyGridlessSnapping.call(canvas.controls.ruler, event);
|
||||||
onEntityLeftDragMove.call(this, wrapped, event);
|
onEntityLeftDragMove.call(this, wrapped, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,9 +211,9 @@ 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, y: tokenY};
|
||||||
const ranges = getRangesFromSpeedProvider(ruler.draggedEntity);
|
const ranges = getRangesFromSpeedProvider(ruler.draggedEntity);
|
||||||
|
|
||||||
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
|
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
|
||||||
@@ -241,13 +237,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;
|
||||||
event.data.destination.y = rasterLocation.y - ruler.rulerOffset.y;
|
event.interactionData.destination.y = rasterLocation.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let waypointDistance = 0;
|
let waypointDistance = 0;
|
||||||
let origin = event.data.origin;
|
let origin = this.draggedEntity.getCenterPoint();
|
||||||
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)
|
||||||
@@ -256,7 +252,7 @@ function applyGridlessSnapping(event) {
|
|||||||
});
|
});
|
||||||
origin = segments.pop().ray.A;
|
origin = segments.pop().ray.A;
|
||||||
waypointDistance = canvas.grid.measureDistances(segments).reduce((a, b) => a + b);
|
waypointDistance = canvas.grid.measureDistances(segments).reduce((a, b) => a + b);
|
||||||
origin = {x: origin.x - ruler.rulerOffset.x, y: origin.y - ruler.rulerOffset.y};
|
origin = {x: origin.x, y: origin.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaX = tokenX - origin.x;
|
const deltaX = tokenX - origin.x;
|
||||||
@@ -271,8 +267,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-33
@@ -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,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,
|
||||||
@@ -274,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;
|
||||||
@@ -379,18 +390,12 @@ 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;
|
|
||||||
|
|
||||||
// Options are not passed to _removeWaypoint in vanilla Foundry.
|
// 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)
|
// Send them in case other modules have overriden that behavior and accept an options parameter (Toggle Snap to Grid)
|
||||||
this._removeWaypoint(
|
this._removeWaypoint({x: mousePosition.x, y: mousePosition.y}, options);
|
||||||
{x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y},
|
this.performPostPathfindingActions(options);
|
||||||
options,
|
|
||||||
);
|
|
||||||
game.user.broadcastActivity({ruler: this});
|
|
||||||
} else {
|
} else {
|
||||||
this.dragRulerAbortDrag(event);
|
this.dragRulerAbortDrag(event);
|
||||||
}
|
}
|
||||||
@@ -416,7 +421,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 +440,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,19 +488,20 @@ 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,
|
||||||
y: mousePosition.y + ruler.rulerOffset.y,
|
y: mousePosition.y,
|
||||||
};
|
};
|
||||||
if (measureImmediately) ruler.measure(destination, options);
|
if (measureImmediately) ruler.measure(destination, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
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).`,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-31
@@ -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,12 +295,13 @@ 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 measurePosition = {x: mousePosition.x, y: mousePosition.y};
|
||||||
);
|
|
||||||
const rulerOffset = canvas.controls.ruler.rulerOffset;
|
|
||||||
const measurePosition = {x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y};
|
|
||||||
return measurePosition;
|
return measurePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,3 +323,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