Compare commits

..

46 Commits

Author SHA1 Message Date
Crovaxon 0372b6a3b9 Removed duplicates from merging, fuck VS Code. 2025-05-11 16:43:52 +02:00
Crovaxon 3bd945b6e2 Merge branch 'develop' of https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler into develop 2025-05-11 16:42:37 +02:00
Crovaxon 63b946051a Updated URLs and prepared for test release. 2025-05-11 16:41:13 +02:00
Crovaxon 179cc73fa1 Merge branch 'develop' of https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler into develop 2025-05-11 16:39:19 +02:00
Crovaxon 9eed5d4245 Updated download URL for upcoming test release. 2025-05-11 16:38:27 +02:00
Crovaxon f31c2c0f8a Updated URLs
Updated the download URL
2025-05-11 16:36:44 +02:00
Crovaxon 72a646ab89 Updated URLs 2025-05-11 16:35:28 +02:00
Crovaxon e0c57ce78b Changed socketlib dependency to my own hosted instance and upped version compatibility so Foundry v13 will be able to install this. 2025-05-11 16:22:17 +02:00
Jonas Karlsson a21085ca5b Add dragonbane support (#333) 2024-08-03 21:19:05 +02:00
Joe 21533a0075 Update lancer speed attribute (#301) 2024-08-03 21:18:48 +02:00
Manuel Vögele 8d67ca01c3 Release v1.14.2 2024-07-28 23:26:33 +02:00
Manuel Vögele 80ff8832fd Add a word of warning about bugs to the readme 2024-07-28 23:24:40 +02:00
Manuel Vögele c787b18fcb Add 1 to hex positions before snapping to avoid rounding issues (fixes #334) 2024-07-28 23:13:23 +02:00
Manuel Vögele c03dbc09ff Always use the token's center as origin for gridless snapping (fixes #336) 2024-07-28 23:03:37 +02:00
Manuel Vögele 27a2b46a8a Revert "Restore rulerOffset for all uses except hex grids (fixes #332)"
This reverts commit ab1f5b4c9b.
2024-07-28 22:47:26 +02:00
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 225 additions and 177 deletions
+4
View File
@@ -0,0 +1,4 @@
*.md
*.html
*.json
foundry.js
+44
View File
@@ -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!)
+7 -2
View File
@@ -1,13 +1,17 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/staebchenfisch) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](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.
![Drag Ruler being used on a square grids](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/basic_square.webp) ![Drag Ruler being used on a square grids](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/basic_square.webp)
![Drag Ruler being used on a gridless scene](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/basic_gridless.webp) ![Drag Ruler being used on a gridless scene](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/basic_gridless.webp)
![Drag Ruler while dragging a measurement template](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/measurement_template.webp) ![Drag Ruler while dragging a measurement template](https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/709774b25f7dd818a90591165f74b3e6dbc788cc/media/measurement_template.webp)
## 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.
@@ -51,7 +55,7 @@ The game systems that offer Drag Ruler integration are:
- GURPS 4th Edition Game Aid (Unofficial) (starting with version 0.9.1) - GURPS 4th Edition Game Aid (Unofficial) (starting with version 0.9.1)
- Hackmaster (starting with version 0.2.11) - Hackmaster (starting with version 0.2.11)
- Ironclaw Second Edition (starting with version 0.2.2) - Ironclaw Second Edition (starting with version 0.2.2)
- Lancer (via the module [Lancer Speed Provider](https://foundryvtt.com/packages/lancer-speed-provider)) - Lancer (via the module [Lancer Ruler Integration](https://foundryvtt.com/packages/lancer-speed-provider))
- Level Up: Advanced 5th Edition (Official) (via the module [A5E Drag Ruler Integration](https://foundryvtt.com/packages/a5edragruler)) - Level Up: Advanced 5th Edition (Official) (via the module [A5E Drag Ruler Integration](https://foundryvtt.com/packages/a5edragruler))
- Pathfinder 1 (starting with version 0.77.3) - Pathfinder 1 (starting with version 0.77.3)
- Pathfinder 2e (via the module [PF2E Drag Ruler Integration](https://foundryvtt.com/packages/pf2e-dragruler/)) - Pathfinder 2e (via the module [PF2E Drag Ruler Integration](https://foundryvtt.com/packages/pf2e-dragruler/))
@@ -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
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."
} }
} }
} }
+12 -11
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.2",
"compatibility": { "compatibility": {
"minimum": "10", "minimum": "12",
"verified": "10" "verified": "12",
"maximum": "13"
}, },
"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": [
@@ -74,16 +75,16 @@
{ {
"id": "socketlib", "id": "socketlib",
"type": "module", "type": "module",
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json" "manifest": "https://gitea.crovaxon.de/crovaxon/foundryvtt-socketlib/raw/branch/develop/module.json"
} }
] ]
}, },
"socket": true, "socket": true,
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler", "url": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler",
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.13.4.zip", "download": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler/archive/v1.14.2-test.zip",
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json", "manifest": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler/raw/branch/develop/module.json",
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md", "readme": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler/blob/master/README.md",
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md", "changelog": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
"bugs": "https://github.com/manuelVo/foundryvtt-drag-ruler/issues", "bugs": "https://gitea.crovaxon.de/crovaxon/foundryvtt-drag-ruler/issues",
"allowBugReporter": true "allowBugReporter": true
} }
+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) {
+14 -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,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
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 -14
View File
@@ -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
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,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
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).`,
+12 -1
View File
@@ -9,7 +9,7 @@ export function getDefaultSpeedAttribute() {
case "dnd5e": case "dnd5e":
return "actor.system.attributes.movement.walk"; return "actor.system.attributes.movement.walk";
case "lancer": case "lancer":
return "actor.system.derived.speed"; return "actor.system.speed";
case "pf1": case "pf1":
case "D35E": case "D35E":
return "actor.system.attributes.speed.land.total"; return "actor.system.attributes.speed.land.total";
@@ -23,6 +23,12 @@ 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";
case "dragonbane":
return "actor.system.movement.value";
} }
return ""; return "";
} }
@@ -40,11 +46,16 @@ export function getDefaultDashMultiplier() {
case "sfrpg": case "sfrpg":
case "shadowrun5e": case "shadowrun5e":
case "ds4": case "ds4":
case "dragonbane":
return 2; return 2;
case "CoC7": case "CoC7":
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
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,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;
}