Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1367aa82e | |||
| 9f70e5bc27 | |||
| 334ccbc0f1 | |||
| 1242035744 | |||
| 49542a68e5 | |||
| 27a6235cc1 | |||
| c26b39c984 | |||
| e8ab77a62e | |||
| 7bed5abd0a | |||
| 4a96348659 | |||
| bed16de575 | |||
| 891bb1b4d8 | |||
| 43f26088b5 | |||
| 39a0787c79 | |||
| 3be898e49c | |||
| 817662bf30 | |||
| 56527ccf75 | |||
| 6bf8083f7a | |||
| b5ea1f2284 | |||
| 6d9870bedb | |||
| afbe5f9473 | |||
| bae0e43b7b | |||
| 1acc012d65 | |||
| 4671e6de51 | |||
| f0ef109658 | |||
| f0d1ef9d48 | |||
| d04ea9b0b7 | |||
| ba8ab9d473 | |||
| 2691720090 | |||
| 70b166d844 | |||
| 908600bfa3 | |||
| 6472c1d5bd | |||
| 9151b45874 | |||
| d732f7ca17 | |||
| 4be6730fd5 | |||
| 1faeda6f47 | |||
| 667259d5d6 |
@@ -1,3 +1,62 @@
|
|||||||
|
## 1.6.5
|
||||||
|
### Bugfixes
|
||||||
|
- Drag Ruler no longer get's stuck if the user presses ESC during drag ([#70](https://github.com/manuelVo/foundryvtt-drag-ruler/issues/70))
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.4
|
||||||
|
### Bugfixes
|
||||||
|
- Fixed a bug where a bug in a Speed Provider could lead to the ruler getting stuck, leaving the token immovable
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.3
|
||||||
|
### Bugfixes
|
||||||
|
- If the movement history for a token is being updated (for example by a history reset by the gm) while a player is currently measuring a distance for that token the history change is now being reflected in the active measurement.
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
- Drag Ruler's Generic SpeedProvider is now aware of good default values for the Savage Worlds Adventure Edition game system
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.2
|
||||||
|
### Bugfixes
|
||||||
|
- The reset movement history button now resets the movement history for all players, not just for the GM
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.1
|
||||||
|
### API
|
||||||
|
- Added `onMovementHistoryUpdate` callback to Speed Providers, that allows them to perform game systems specific improvements to the movement history
|
||||||
|
- Added `dragRuler.resetMovementHistory` that clears the stored movement history for a token.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.0
|
||||||
|
### Performance
|
||||||
|
- Greatly increased the performance when playing on huge maps and when moving many tokens at once.
|
||||||
|
- Huge performance improvements for speed providers. (Technical details: `getRanges` is now being called way less frequently)
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- GMs now have an option to reset the movement history for individual tokens in the right click menu of the combat tracker
|
||||||
|
- When releasing a dragged token while pressing Alt the token will be moved to the target location without an animation ([#3](https://github.com/manuelVo/foundryvtt-drag-ruler/issues/3))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- When starting to drag a new token while the previous one is still moving the ruler won't dissappear anymore when the previous token arrives at it's destination.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.5.4
|
||||||
|
### Bugfixes
|
||||||
|
- Fixed a bug that prevented tokens from being moved when their movement history collides with a wall. ([#61](https://github.com/manuelVo/foundryvtt-drag-ruler/issues/61))
|
||||||
|
|
||||||
|
|
||||||
|
## 1.5.3
|
||||||
|
### Compatiblilty
|
||||||
|
- Drag Ruler's Generic Speed Provider is now able to work with game systems that put non-number characters behind the tokens movement speed (like `30ft.`). One example for such a game system is Dungeon Crawl Classics. ([#60](https://github.com/manuelVo/foundryvtt-drag-ruler/issues/60))
|
||||||
|
- The Generic Speed Provider now has good default settings for the Dungeon Crawl Classics (dcc) game system.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.5.2
|
||||||
|
### Bugfixes
|
||||||
|
- Drag Ruler no longer prevents tokens that don't have an actor from being moved. ([#58](https://github.com/manuelVo/foundryvtt-drag-ruler/issues/58))
|
||||||
|
- Grid highlighting now also works for tokens that don't have an actor.
|
||||||
|
|
||||||
|
|
||||||
## 1.5.1
|
## 1.5.1
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
- The hint that tells users how to enable difficult terrain measurement in Drag Ruler is no longer shown if no terrain layer module is installed.
|
- The hint that tells users how to enable difficult terrain measurement in Drag Ruler is no longer shown if no terrain layer module is installed.
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ The game systems that offer Drag Ruler integration are:
|
|||||||
- Cypher System (starting with version 1.13.0)
|
- Cypher System (starting with version 1.13.0)
|
||||||
- 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/))
|
||||||
|
- Tagmar RPG (starting with version 1.1.4)
|
||||||
- Tormenta20 (starting with version 1.1.37)
|
- Tormenta20 (starting with version 1.1.37)
|
||||||
|
- Shadow of the Demon Lord (starting with version 1.7.15)
|
||||||
|
- WWII:OWB (starting with version 1.0.4)
|
||||||
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"text": "Drag Ruler benötigt das socketlib-Modul, um korrekt zu funktionieren. Bitte aktiviere das socketlib-Modul in dieser Welt."
|
"text": "Drag Ruler benötigt das socketlib-Modul, um korrekt zu funktionieren. Bitte aktiviere das socketlib-Modul in dieser Welt."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"resetMovementHistory": "Bewegungsverlauf zurücksetzen",
|
||||||
"genericSpeedProvider": {
|
"genericSpeedProvider": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"dashMultiplier": {
|
"dashMultiplier": {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"text": "Drag Ruler requires the socketlib module to work properly. Please activate the socketlib module in this world."
|
"text": "Drag Ruler requires the socketlib module to work properly. Please activate the socketlib module in this world."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"resetMovementHistory": "Reset Movement History",
|
||||||
"genericSpeedProvider": {
|
"genericSpeedProvider": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"dashMultiplier": {
|
"dashMultiplier": {
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
"name": "drag-ruler",
|
"name": "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.5.1",
|
"version": "1.6.5",
|
||||||
"minimumCoreVersion" : "0.7.9",
|
"minimumCoreVersion" : "0.7.9",
|
||||||
"compatibleCoreVersion" : "0.7.9",
|
"compatibleCoreVersion" : "0.7.9",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -49,7 +49,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.5.1.zip",
|
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.6.5.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",
|
||||||
|
|||||||
@@ -101,11 +101,17 @@ export function getUnreachableColorFromSpeedProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getCostFromSpeedProvider(token, area) {
|
export function getCostFromSpeedProvider(token, area) {
|
||||||
|
try {
|
||||||
if (currentSpeedProvider instanceof Function) {
|
if (currentSpeedProvider instanceof Function) {
|
||||||
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area);
|
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area);
|
||||||
}
|
}
|
||||||
return currentSpeedProvider.getCostForStep(token, area);
|
return currentSpeedProvider.getCostForStep(token, area);
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getMovedDistanceFromToken(token) {
|
export function getMovedDistanceFromToken(token) {
|
||||||
const history = getMovementHistory(token);
|
const history = getMovementHistory(token);
|
||||||
|
|||||||
@@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
|
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
|
||||||
export function getPixelsFromGridPosition(xGrid, yGrid) {
|
export function getPixelsFromGridPosition(xGrid, yGrid) {
|
||||||
if (canvas.grid.isHex) {
|
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
|
||||||
return canvas.grid.grid.getPixelsFromGridPosition(yGrid, xGrid)
|
return canvas.grid.grid.getPixelsFromGridPosition(yGrid, xGrid)
|
||||||
}
|
}
|
||||||
const [x, y] = canvas.grid.grid.getPixelsFromGridPosition(xGrid, yGrid)
|
return canvas.grid.grid.getPixelsFromGridPosition(xGrid, yGrid)
|
||||||
if (canvas.grid.type === CONST.GRID_TYPES.SQUARE)
|
|
||||||
return [y, x]
|
|
||||||
return [x, y]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
+38
-20
@@ -2,6 +2,7 @@ import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibilit
|
|||||||
import {getGridPositionFromPixels} from "./foundry_fixes.js";
|
import {getGridPositionFromPixels} from "./foundry_fixes.js";
|
||||||
import {getColorForDistance} from "./main.js"
|
import {getColorForDistance} from "./main.js"
|
||||||
import {trackRays} from "./movement_tracking.js"
|
import {trackRays} from "./movement_tracking.js"
|
||||||
|
import {recalculate} from "./socket.js";
|
||||||
import {applyTokenSizeOffset, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
|
import {applyTokenSizeOffset, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
|
||||||
|
|
||||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||||
@@ -20,7 +21,7 @@ export async function moveTokens(draggedToken, selectedTokens) {
|
|||||||
if (!game.user.isGM) {
|
if (!game.user.isGM) {
|
||||||
const hasCollision = selectedTokens.some(token => {
|
const hasCollision = selectedTokens.some(token => {
|
||||||
const offset = calculateTokenOffset(token, draggedToken)
|
const offset = calculateTokenOffset(token, draggedToken)
|
||||||
const offsetRays = rays.map(ray => applyOffsetToRay(ray, offset))
|
const offsetRays = rays.filter(ray => !ray.isPrevious).map(ray => applyOffsetToRay(ray, offset))
|
||||||
return offsetRays.some(r => canvas.walls.checkCollision(r));
|
return offsetRays.some(r => canvas.walls.checkCollision(r));
|
||||||
})
|
})
|
||||||
if (hasCollision) {
|
if (hasCollision) {
|
||||||
@@ -33,45 +34,62 @@ export async function moveTokens(draggedToken, selectedTokens) {
|
|||||||
// Execute the movement path.
|
// Execute the movement path.
|
||||||
// Transform each center-to-center ray into a top-left to top-left ray using the prior token offsets.
|
// Transform each center-to-center ray into a top-left to top-left ray using the prior token offsets.
|
||||||
this._state = Ruler.STATES.MOVING;
|
this._state = Ruler.STATES.MOVING;
|
||||||
await Promise.all(selectedTokens.map(token => {
|
await animateTokens.call(this, selectedTokens, draggedToken, rays, wasPaused);
|
||||||
// Return the promise so we can wait for it outside the loop
|
|
||||||
const offset = calculateTokenOffset(token, draggedToken)
|
|
||||||
return animateToken.call(this, token, rays, offset, wasPaused)
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Once all animations are complete we can clear the ruler
|
// Once all animations are complete we can clear the ruler
|
||||||
|
if (this.draggedToken?.id === draggedToken.id)
|
||||||
this._endMeasurement();
|
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
|
||||||
async function animateToken(token, rays, tokenOffset, wasPaused) {
|
async function animateTokens(tokens, draggedToken, draggedRays, wasPaused) {
|
||||||
const offsetRays = rays.filter(r => !r.isPrevious).map(ray => applyOffsetToRay(ray, tokenOffset));
|
const newRays = draggedRays.filter(r => !r.isPrevious);
|
||||||
trackRays(token, offsetRays);
|
const tokenAnimationData = tokens.map(token => {
|
||||||
|
const tokenOffset = calculateTokenOffset(token, draggedToken);
|
||||||
|
const offsetRays = newRays.map(ray => applyOffsetToRay(ray, tokenOffset));
|
||||||
|
|
||||||
// Determine offset relative to the Token top-left.
|
// Determine offset relative to the Token top-left.
|
||||||
// This is important so we can position the token relative to the ruler origin for non-1x1 tokens.
|
// This is important so we can position the token relative to the ruler origin for non-1x1 tokens.
|
||||||
const firstWaypoint = this.waypoints.find(w => !w.isPrevious);
|
const firstWaypoint = this.waypoints.find(w => !w.isPrevious);
|
||||||
const origin = [firstWaypoint.x + tokenOffset.x, firstWaypoint.y + tokenOffset.y];
|
const origin = [firstWaypoint.x + tokenOffset.x, firstWaypoint.y + tokenOffset.y];
|
||||||
let dx, dy
|
let dx, dy;
|
||||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||||
dx = token.data.x - origin[0]
|
dx = token.data.x - origin[0];
|
||||||
dy = token.data.y - origin[1]
|
dy = token.data.y - origin[1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dx = token.data.x - origin[0]
|
dx = token.data.x - origin[0];
|
||||||
dy = token.data.y - origin[1]
|
dy = token.data.y - origin[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {token, rays: offsetRays, dx, dy};
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const {token, rays} of tokenAnimationData) {
|
||||||
token._noAnimate = true;
|
token._noAnimate = true;
|
||||||
for (let r of offsetRays) {
|
|
||||||
if (!wasPaused && game.paused) break;
|
|
||||||
const dest = [r.B.x, r.B.y];
|
|
||||||
const path = new Ray({ x: token.x, y: token.y }, { x: dest[0] + dx, y: dest[1] + dy });
|
|
||||||
await token.update(path.B);
|
|
||||||
await token.animateMovement(path);
|
|
||||||
}
|
}
|
||||||
|
const animate = !game.keyboard.isDown("Alt");
|
||||||
|
const startWaypoint = animate ? 0 : tokenAnimationData[0].rays.length - 1;
|
||||||
|
for (let i = startWaypoint;i < tokenAnimationData[0].rays.length; i++) {
|
||||||
|
if (!wasPaused && game.paused) break;
|
||||||
|
const tokenPaths = tokenAnimationData.map(({token, rays, dx, dy}) => {
|
||||||
|
const ray = rays[i];
|
||||||
|
const dest = [ray.B.x, ray.B.y];
|
||||||
|
const path = new Ray({x: token.x, y: token.y}, {x: dest[0] + dx, y: dest[1] + dy});
|
||||||
|
return {token, path};
|
||||||
|
});
|
||||||
|
const updates = tokenPaths.map(({token, path}) => {
|
||||||
|
return {x: path.B.x, y: path.B.y, _id: token.id};
|
||||||
|
});
|
||||||
|
await draggedToken.scene.updateEmbeddedEntity(draggedToken.constructor.embeddedName, updates, {animate});
|
||||||
|
if (animate)
|
||||||
|
await Promise.all(tokenPaths.map(({token, path}) => token.animateMovement(path)));
|
||||||
|
}
|
||||||
|
for (const {token} of tokenAnimationData) {
|
||||||
token._noAnimate = false;
|
token._noAnimate = false;
|
||||||
}
|
}
|
||||||
|
trackRays(tokens, tokenAnimationData.map(({rays}) => rays)).then(() => recalculate(tokens));
|
||||||
|
}
|
||||||
|
|
||||||
function calculateTokenOffset(tokenA, tokenB) {
|
function calculateTokenOffset(tokenA, tokenB) {
|
||||||
return {x: tokenA.data.x - tokenB.data.x, y: tokenA.data.y - tokenB.data.y}
|
return {x: tokenA.data.x - tokenB.data.x, y: tokenA.data.y - tokenB.data.y}
|
||||||
|
|||||||
+22
-2
@@ -5,8 +5,9 @@ import {checkDependencies, getHexSizeSupportTokenGridCenter} from "./compatibili
|
|||||||
import {moveTokens, onMouseMove} from "./foundry_imports.js"
|
import {moveTokens, onMouseMove} from "./foundry_imports.js"
|
||||||
import {performMigrations} from "./migration.js"
|
import {performMigrations} from "./migration.js"
|
||||||
import {DragRulerRuler} from "./ruler.js";
|
import {DragRulerRuler} from "./ruler.js";
|
||||||
import {getMovementHistory} from "./movement_tracking.js";
|
import {getMovementHistory, resetMovementHistory} from "./movement_tracking.js";
|
||||||
import {registerSettings, settingsKey} from "./settings.js"
|
import {registerSettings, settingsKey} from "./settings.js"
|
||||||
|
import {recalculate} from "./socket.js";
|
||||||
import {SpeedProvider} from "./speed_provider.js"
|
import {SpeedProvider} from "./speed_provider.js"
|
||||||
|
|
||||||
Hooks.once("init", () => {
|
Hooks.once("init", () => {
|
||||||
@@ -22,6 +23,8 @@ Hooks.once("init", () => {
|
|||||||
getMovedDistanceFromToken,
|
getMovedDistanceFromToken,
|
||||||
registerModule,
|
registerModule,
|
||||||
registerSystem,
|
registerSystem,
|
||||||
|
recalculate,
|
||||||
|
resetMovementHistory,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -42,6 +45,15 @@ Hooks.on("canvasReady", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Hooks.on("getCombatTrackerEntryContext", function (html, menu) {
|
||||||
|
const entry = {
|
||||||
|
name: "drag-ruler.resetMovementHistory",
|
||||||
|
icon: '<i class="fas fa-undo-alt"></i>',
|
||||||
|
callback: li => resetMovementHistory(ui.combat.combat, li.data('combatant-id')),
|
||||||
|
};
|
||||||
|
menu.splice(1, 0, entry);
|
||||||
|
});
|
||||||
|
|
||||||
function hookTokenDragHandlers() {
|
function hookTokenDragHandlers() {
|
||||||
const originalDragLeftStartHandler = Token.prototype._onDragLeftStart
|
const originalDragLeftStartHandler = Token.prototype._onDragLeftStart
|
||||||
Token.prototype._onDragLeftStart = function(event) {
|
Token.prototype._onDragLeftStart = function(event) {
|
||||||
@@ -140,6 +152,9 @@ function onTokenDragLeftDrop(event) {
|
|||||||
return false
|
return false
|
||||||
onMouseMove.call(ruler, event);
|
onMouseMove.call(ruler, event);
|
||||||
const selectedTokens = canvas.tokens.controlled
|
const selectedTokens = canvas.tokens.controlled
|
||||||
|
// This can happen if the user presses ESC during drag (maybe there are other ways too)
|
||||||
|
if (selectedTokens.length === 0)
|
||||||
|
selectedTokens.push(ruler.draggedToken);
|
||||||
ruler._state = Ruler.STATES.MOVING
|
ruler._state = Ruler.STATES.MOVING
|
||||||
moveTokens.call(ruler, ruler.draggedToken, selectedTokens)
|
moveTokens.call(ruler, ruler.draggedToken, selectedTokens)
|
||||||
return true
|
return true
|
||||||
@@ -166,6 +181,9 @@ function onTokenDragLeftCancel(event) {
|
|||||||
export function getColorForDistance(startDistance, subDistance=0) {
|
export function getColorForDistance(startDistance, subDistance=0) {
|
||||||
if (!this.isDragRuler)
|
if (!this.isDragRuler)
|
||||||
return this.color
|
return this.color
|
||||||
|
if (!this.draggedToken.actor) {
|
||||||
|
return this.color;
|
||||||
|
}
|
||||||
// Don't apply colors if the current user doesn't have at least observer permissions
|
// Don't apply colors if the current user doesn't have at least observer permissions
|
||||||
if (this.draggedToken.actor.permission < 2) {
|
if (this.draggedToken.actor.permission < 2) {
|
||||||
// If this is a pc and alwaysShowSpeedForPCs is enabled we show the color anyway
|
// If this is a pc and alwaysShowSpeedForPCs is enabled we show the color anyway
|
||||||
@@ -173,7 +191,9 @@ export function getColorForDistance(startDistance, subDistance=0) {
|
|||||||
return this.color
|
return this.color
|
||||||
}
|
}
|
||||||
const distance = startDistance + subDistance
|
const distance = startDistance + subDistance
|
||||||
const ranges = getRangesFromSpeedProvider(this.draggedToken)
|
if (!this.dragRulerRanges)
|
||||||
|
this.dragRulerRanges = getRangesFromSpeedProvider(this.draggedToken)
|
||||||
|
const ranges = this.dragRulerRanges;
|
||||||
if (ranges.length === 0)
|
if (ranges.length === 0)
|
||||||
return this.color
|
return this.color
|
||||||
const currentRange = ranges.reduce((minRange, currentRange) => {
|
const currentRange = ranges.reduce((minRange, currentRange) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {measureDistances} from "./compatibility.js";
|
import {measureDistances} from "./compatibility.js";
|
||||||
import {updateCombatantDragRulerFlags} from "./socket.js";
|
import {recalculate, updateCombatantDragRulerFlags} from "./socket.js";
|
||||||
import {getTokenShape} from "./util.js";
|
import {getTokenShape, zip} from "./util.js";
|
||||||
|
|
||||||
function initTrackingFlag(combatant) {
|
function initTrackingFlag(combatant) {
|
||||||
const initialFlag = {passedWaypoints: [], trackedRound: 0};
|
const initialFlag = {passedWaypoints: [], trackedRound: 0};
|
||||||
@@ -23,13 +23,21 @@ function getInitializedCombatant(token, combat) {
|
|||||||
return combatant;
|
return combatant;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function trackRays(token, rays) {
|
export async function trackRays(tokens, tokenRays) {
|
||||||
// Only track movement if the current token is participating in the active combat
|
|
||||||
const combat = game.combat;
|
const combat = game.combat;
|
||||||
if (!combat)
|
if (!combat)
|
||||||
return;
|
return;
|
||||||
if (!combat.started)
|
if (!combat.started)
|
||||||
return;
|
return;
|
||||||
|
if (!(tokens instanceof Array)) {
|
||||||
|
tokens = [tokens];
|
||||||
|
tokenRays = [tokenRays];
|
||||||
|
}
|
||||||
|
const updates = Array.from(zip(tokens, tokenRays)).map(([token, rays]) => calculateUpdate(combat, token, rays)).filter(Boolean);
|
||||||
|
await updateCombatantDragRulerFlags(combat, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateUpdate(combat, token, rays) {
|
||||||
const combatant = getInitializedCombatant(token, combat);
|
const combatant = getInitializedCombatant(token, combat);
|
||||||
if (!combatant)
|
if (!combatant)
|
||||||
return;
|
return;
|
||||||
@@ -55,7 +63,7 @@ export async function trackRays(token, rays) {
|
|||||||
waypoints.push(ray.A);
|
waypoints.push(ray.A);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await updateCombatantDragRulerFlags(combat, combatant, dragRulerFlags);
|
return {_id: combatant._id, dragRulerFlags};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMovementHistory(token) {
|
export function getMovementHistory(token) {
|
||||||
@@ -72,3 +80,15 @@ export function getMovementHistory(token) {
|
|||||||
return [];
|
return [];
|
||||||
return dragRulerFlags.passedWaypoints ?? [];
|
return dragRulerFlags.passedWaypoints ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resetMovementHistory(combat, combatantId) {
|
||||||
|
const combatant = combat.getCombatant(combatantId);
|
||||||
|
const dragRulerFlags = combatant.flags.dragRuler;
|
||||||
|
if (!dragRulerFlags)
|
||||||
|
return;
|
||||||
|
dragRulerFlags.passedWaypoints = null;
|
||||||
|
dragRulerFlags.trackedRound = null;
|
||||||
|
dragRulerFlags.rulerState = null;
|
||||||
|
await updateCombatantDragRulerFlags(combat, [{_id: combatantId, dragRulerFlags}]);
|
||||||
|
recalculate();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {measure} from "./foundry_imports.js"
|
import {measure} from "./foundry_imports.js"
|
||||||
|
import {getMovementHistory} from "./movement_tracking.js";
|
||||||
import {settingsKey} from "./settings.js";
|
import {settingsKey} from "./settings.js";
|
||||||
import {getSnapPointForToken} from "./util.js";
|
import {getSnapPointForToken} from "./util.js";
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ export class DragRulerRuler extends Ruler {
|
|||||||
super.clear();
|
super.clear();
|
||||||
this.previousWaypoints = [];
|
this.previousWaypoints = [];
|
||||||
this.previousLabels.removeChildren().forEach(c => c.destroy());
|
this.previousLabels.removeChildren().forEach(c => c.destroy());
|
||||||
|
this.dragRulerRanges = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveToken(event) {
|
async moveToken(event) {
|
||||||
@@ -77,6 +79,11 @@ export class DragRulerRuler extends Ruler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragRulerClearWaypoints() {
|
||||||
|
this.waypoints = [];
|
||||||
|
this.labels.removeChildren().forEach(c => c.destroy());
|
||||||
|
}
|
||||||
|
|
||||||
dragRulerDeleteWaypoint(event={preventDefault: () => {return}}) {
|
dragRulerDeleteWaypoint(event={preventDefault: () => {return}}) {
|
||||||
if (this.waypoints.filter(w => !w.isPrevious).length > 1) {
|
if (this.waypoints.filter(w => !w.isPrevious).length > 1) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -99,6 +106,22 @@ export class DragRulerRuler extends Ruler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async dragRulerRecalculate(tokenIds) {
|
||||||
|
if (this._state !== Ruler.STATES.MEASURING)
|
||||||
|
return;
|
||||||
|
if (tokenIds && !tokenIds.includes(this.draggedToken.id))
|
||||||
|
return;
|
||||||
|
const waypoints = this.waypoints.filter(waypoint => !waypoint.isPrevious);
|
||||||
|
this.dragRulerClearWaypoints();
|
||||||
|
if (game.settings.get(settingsKey, "enableMovementHistory"))
|
||||||
|
this.dragRulerAddWaypointHistory(getMovementHistory(this.draggedToken));
|
||||||
|
for (const waypoint of waypoints) {
|
||||||
|
this.dragRulerAddWaypoint(waypoint, false);
|
||||||
|
}
|
||||||
|
this.measure(this.destination);
|
||||||
|
game.user.broadcastActivity({ruler: this});
|
||||||
|
}
|
||||||
|
|
||||||
static dragRulerGetRaysFromWaypoints(waypoints, destination) {
|
static dragRulerGetRaysFromWaypoints(waypoints, destination) {
|
||||||
if ( destination )
|
if ( destination )
|
||||||
waypoints = waypoints.concat([destination]);
|
waypoints = waypoints.concat([destination]);
|
||||||
|
|||||||
+31
-5
@@ -1,17 +1,43 @@
|
|||||||
|
import {currentSpeedProvider} from "./api.js";
|
||||||
|
|
||||||
let socket;
|
let socket;
|
||||||
|
|
||||||
Hooks.once("socketlib.ready", () => {
|
Hooks.once("socketlib.ready", () => {
|
||||||
socket = socketlib.registerModule("drag-ruler");
|
socket = socketlib.registerModule("drag-ruler");
|
||||||
socket.register("updateCombatantDragRulerFlags", _socketUpdateCombatantDragRulerFlags);
|
socket.register("updateCombatantDragRulerFlags", _socketUpdateCombatantDragRulerFlags);
|
||||||
|
socket.register("recalculate", _socketRecalculate);
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function updateCombatantDragRulerFlags(combat, combatant, flags) {
|
export function updateCombatantDragRulerFlags(combat, updates) {
|
||||||
const combatId = combat.id;
|
const combatId = combat.id;
|
||||||
const combatantId = combatant._id;
|
// TODO Check if canvas.tokens.get is still neccessary in future foundry versions
|
||||||
return socket.executeAsGM(_socketUpdateCombatantDragRulerFlags, combatId, combatantId, flags);
|
return socket.executeAsGM(_socketUpdateCombatantDragRulerFlags, combatId, updates)
|
||||||
|
.then(() => currentSpeedProvider.onMovementHistoryUpdate(updates.map(update => canvas.tokens.get(combat.getCombatant(update._id).token._id))));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _socketUpdateCombatantDragRulerFlags(combatId, combatantId, flags) {
|
async function _socketUpdateCombatantDragRulerFlags(combatId, updates) {
|
||||||
|
const user = game.users.get(this.socketdata.userId);
|
||||||
const combat = game.combats.get(combatId);
|
const combat = game.combats.get(combatId);
|
||||||
await combat.updateEmbeddedEntity("Combatant", {_id: combatantId, flags: {dragRuler: flags}}, {diff: false});
|
const requestedUpdates = updates.length;
|
||||||
|
updates = updates.filter(update => {
|
||||||
|
const actor = combat.getCombatant(update._id).actor;
|
||||||
|
if (!actor)
|
||||||
|
return false;
|
||||||
|
return actor.hasPerm(user, "OWNER");
|
||||||
|
});
|
||||||
|
if (updates.length !== requestedUpdates) {
|
||||||
|
console.warn(`Some of the movement history updates requested by user '${game.users.get(this.socketdata.userId).name}' were not performed because the user lacks owner permissions for those tokens`);
|
||||||
|
}
|
||||||
|
updates = updates.map(update => {
|
||||||
|
return {_id: update._id, flags: {dragRuler: update.dragRulerFlags}};
|
||||||
|
});
|
||||||
|
await combat.updateEmbeddedEntity("Combatant", updates, {diff: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recalculate(tokens) {
|
||||||
|
socket.executeForEveryone(_socketRecalculate, tokens ? tokens.map(token => token.id) : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _socketRecalculate(tokenIds) {
|
||||||
|
return canvas.controls.ruler.dragRulerRecalculate(tokenIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ export class SpeedProvider {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This hook is being called after Drag Ruler has updated the movement history for one or more tokens.
|
||||||
|
* It'll receive an array of tokens that have been updated.
|
||||||
|
* If your speed provider is storing any additional values that are relevant for the movement history, this function should
|
||||||
|
* await until those updates have completed inside foundry.
|
||||||
|
*/
|
||||||
|
async onMovementHistoryUpdate(tokens) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value that is currently set for the setting registered with the provided settingId.
|
* Returns the value that is currently set for the setting registered with the provided settingId.
|
||||||
*
|
*
|
||||||
@@ -124,7 +132,7 @@ export class GenericSpeedProvider extends SpeedProvider {
|
|||||||
const speedAttribute = this.getSetting("speedAttribute")
|
const speedAttribute = this.getSetting("speedAttribute")
|
||||||
if (!speedAttribute)
|
if (!speedAttribute)
|
||||||
return []
|
return []
|
||||||
const tokenSpeed = getProperty(token, speedAttribute)
|
const tokenSpeed = parseInt(getProperty(token, speedAttribute));
|
||||||
if (tokenSpeed === undefined) {
|
if (tokenSpeed === undefined) {
|
||||||
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).`)
|
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).`)
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
export function getDefaultSpeedAttribute() {
|
export function getDefaultSpeedAttribute() {
|
||||||
switch (game.system.id) {
|
switch (game.system.id) {
|
||||||
|
case "dcc":
|
||||||
|
return "actor.data.data.attributes.speed.value";
|
||||||
case "dnd5e":
|
case "dnd5e":
|
||||||
return "actor.data.data.attributes.movement.walk"
|
return "actor.data.data.attributes.movement.walk"
|
||||||
case "lancer":
|
case "lancer":
|
||||||
return "actor.data.data.mech.speed"
|
return "actor.data.data.mech.speed"
|
||||||
case "pf1":
|
case "pf1":
|
||||||
return "actor.data.data.attributes.speed.land.total"
|
return "actor.data.data.attributes.speed.land.total"
|
||||||
|
case "shadowrun5e":
|
||||||
|
return "actor.data.data.movement.walk.value";
|
||||||
case "swade":
|
case "swade":
|
||||||
return "actor.data.data.stats.speed.value"
|
return "actor.data.data.stats.speed.value"
|
||||||
}
|
}
|
||||||
@@ -17,9 +21,11 @@ export function getDefaultDashMultiplier() {
|
|||||||
switch (game.system.id) {
|
switch (game.system.id) {
|
||||||
case "swade":
|
case "swade":
|
||||||
return 0
|
return 0
|
||||||
|
case "dcc":
|
||||||
case "dnd5e":
|
case "dnd5e":
|
||||||
case "lancer":
|
case "lancer":
|
||||||
case "pf1":
|
case "pf1":
|
||||||
|
case "shadowrun5e":
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user