Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe89a871c9 | |||
| 62c965b499 | |||
| 4149f0e351 | |||
| 5c43651925 | |||
| 59f2e67717 | |||
| 33cb9a2ba2 | |||
| c8a7352d5f | |||
| bbd2173e63 | |||
| 633563b672 | |||
| 135a3091b0 | |||
| 6beae157e7 |
@@ -0,0 +1,12 @@
|
||||
## v1.1.0
|
||||
### New features
|
||||
- The drag ruler will now be colored for other players than the dragging player as well (only if they have at least observer permissions for that token)
|
||||
- The drag ruler won't be shown to other players if they cannot see the dragged token
|
||||
|
||||
### Bugfixes
|
||||
- Fixed a bug where Drag Ruler wouldn't work at all on some windows installations (specificially where the `foundry.js` has `CRLF` line endings)
|
||||
|
||||
## v1.0.1
|
||||
### Bugfixes
|
||||
- The GM can now move tokens through walls
|
||||
- It is now possible to move multiple tokens with Drag Ruler enabled
|
||||
@@ -1,3 +1,5 @@
|
||||
[](https://ko-fi.com/staebchenfisch)
|
||||
|
||||
# Drag Ruler
|
||||
This module shows a ruler when you drag a token to infrom you how far you've dragged the token from it's start point. Additionally, if you're using a grid, the spaces the token will travel though will be colored depending on your tokens speed.
|
||||
|
||||
@@ -49,7 +51,7 @@ Here is an example of how a Speed Provider might look like:
|
||||
```javascript
|
||||
function mySpeedProvider(token, playerColor) {
|
||||
const baseSpeed = token.actor.data.speed
|
||||
const ranges = [{range: baseSpeed, color: playerColor, range: baseSpeed * 2, color: 0xFFFF00}]
|
||||
const ranges = [{range: baseSpeed, color: playerColor}, {range: baseSpeed * 2, color: 0xFFFF00}]
|
||||
if (!token.actor.data.isWearingArmor) {
|
||||
ranges.push({range: baseSpeed * 3, color: 0xFF8000})
|
||||
}
|
||||
@@ -69,7 +71,7 @@ Hooks.once("dragRuler.ready", () => {
|
||||
|
||||
function mySpeedProvider(token, playerColor) {
|
||||
const baseSpeed = token.actor.data.speed
|
||||
const ranges = [{range: baseSpeed, color: playerColor, range: baseSpeed * 2, color: 0xFFFF00}]
|
||||
const ranges = [{range: baseSpeed, color: playerColor}, {range: baseSpeed * 2, color: 0xFFFF00}]
|
||||
if (!token.actor.data.isWearingArmor) {
|
||||
ranges.push({range: baseSpeed * 3, color: 0xFF8000})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"drag-ruler": {
|
||||
"settings": {
|
||||
"alwaysShowSpeedForPCs": {
|
||||
"name": "Show PC speed to everyone",
|
||||
"hint": "If enabled the coloring based on actor speed for player characters will shown to everyone, even if they don't have observer permission for the character sheet."
|
||||
},
|
||||
"dashMultiplier": {
|
||||
"name": "Dash Multiplier",
|
||||
"hint": "This can be used to give tokens a secondary speed during coloring of the measured path. Set it to 0 to disable the secondary speed."
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
"name": "drag-ruler",
|
||||
"title": "Drag Ruler",
|
||||
"description": "When dragging a token displays a ruler showing how far you've moved that token.",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"minimumCoreVersion" : "0.7.9",
|
||||
"compatibleCoreVersion" : "0.7.9",
|
||||
"authors": [
|
||||
@@ -23,8 +23,9 @@
|
||||
}
|
||||
],
|
||||
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.0.0.zip",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.1.0.zip",
|
||||
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json",
|
||||
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md",
|
||||
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
|
||||
"bugs": "https://github.com/manuelVo/foundryvtt-drag-ruler/issues"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||
export async function moveTokens(selectedTokens) {
|
||||
let wasPaused = game.paused;
|
||||
if (wasPaused && !game.user.isGM) {
|
||||
ui.notifications.warn(game.i18n.localize("GAME.PausedWarning"));
|
||||
return false;
|
||||
}
|
||||
if (!this.visible || !this.destination) return false;
|
||||
const draggedToken = this._getMovementToken();
|
||||
if (!draggedToken) return;
|
||||
|
||||
// Get the movement rays and check collision along each Ray
|
||||
// These rays are center-to-center for the purposes of collision checking
|
||||
const rays = this._getRaysFromWaypoints(this.waypoints, this.destination);
|
||||
if (!game.user.isGM) {
|
||||
const hasCollision = selectedTokens.some(token => {
|
||||
const offset = calculateTokenOffset(token, draggedToken)
|
||||
const offsetRays = rays.map(ray => applyOffsetToRay(ray, offset))
|
||||
return offsetRays.some(r => canvas.walls.checkCollision(r));
|
||||
})
|
||||
if (hasCollision) {
|
||||
ui.notifications.error(game.i18n.localize("ERROR.TokenCollide"));
|
||||
this._endMeasurement();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the movement path.
|
||||
// Transform each center-to-center ray into a top-left to top-left ray using the prior token offsets.
|
||||
this._state = Ruler.STATES.MOVING;
|
||||
await Promise.all(selectedTokens.map(token => {
|
||||
// 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
|
||||
this._endMeasurement();
|
||||
}
|
||||
|
||||
// This is a modified version code extracted from Ruler.moveToken from foundry 0.7.9
|
||||
async function animateToken(token, rays, tokenOffset, wasPaused) {
|
||||
const offsetRays = rays.map(ray => applyOffsetToRay(ray, tokenOffset))
|
||||
|
||||
// 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.
|
||||
const origin = canvas.grid.getTopLeft(this.waypoints[0].x + tokenOffset.x, this.waypoints[0].y + tokenOffset.y);
|
||||
const s2 = canvas.dimensions.size / 2;
|
||||
const dx = Math.round((token.data.x - origin[0]) / s2) * s2;
|
||||
const dy = Math.round((token.data.y - origin[1]) / s2) * s2;
|
||||
|
||||
token._noAnimate = true;
|
||||
for (let r of offsetRays) {
|
||||
if (!wasPaused && game.paused) break;
|
||||
const dest = canvas.grid.getTopLeft(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);
|
||||
}
|
||||
token._noAnimate = false;
|
||||
}
|
||||
|
||||
function calculateTokenOffset(tokenA, tokenB) {
|
||||
return {x: tokenA.data.x - tokenB.data.x, y: tokenA.data.y - tokenB.data.y}
|
||||
}
|
||||
|
||||
function applyOffsetToRay(ray, offset) {
|
||||
return new Ray({x: ray.A.x + offset.x, y: ray.A.y + offset.y}, {x: ray.B.x + offset.x, y: ray.B.y + offset.y})
|
||||
}
|
||||
+39
-5
@@ -1,12 +1,13 @@
|
||||
"use strict"
|
||||
|
||||
import {availableSpeedProviders, currentSpeedProvider, registerModule, registerSystem, setCurrentSpeedProvider} from "./api.js"
|
||||
import {moveTokens} from "./foundry_imports.js"
|
||||
import {registerSettings, settingsKey} from "./settings.js"
|
||||
|
||||
Hooks.once("init", () => {
|
||||
registerSettings()
|
||||
hookTokenDragHandlers()
|
||||
hookRulerHandlers()
|
||||
hookRulerFunctions()
|
||||
patchRulerMeasure()
|
||||
patchRulerHighlightMeasurement()
|
||||
|
||||
@@ -26,13 +27,15 @@ Hooks.once("ready", () => {
|
||||
})
|
||||
|
||||
Hooks.on("canvasReady", () => {
|
||||
canvas.controls.ruler.draggedToken = null
|
||||
Object.defineProperty(canvas.controls.ruler, "isDragRuler", {
|
||||
canvas.controls.rulers.children.forEach(ruler => {
|
||||
ruler.draggedToken = null
|
||||
Object.defineProperty(ruler, "isDragRuler", {
|
||||
get: function isDragRuler() {
|
||||
return Boolean(this.draggedToken) // If draggedToken is set this is a drag ruler
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function hookTokenDragHandlers() {
|
||||
const originalDragLeftStartHandler = Token.prototype._onDragLeftStart
|
||||
@@ -62,7 +65,7 @@ function hookTokenDragHandlers() {
|
||||
}
|
||||
}
|
||||
|
||||
function hookRulerHandlers() {
|
||||
function hookRulerFunctions() {
|
||||
const originalMoveTokenHandler = Ruler.prototype.moveToken
|
||||
Ruler.prototype.moveToken = function (event) {
|
||||
const eventHandled = onRulerMoveToken.call(this, event)
|
||||
@@ -70,6 +73,22 @@ function hookRulerHandlers() {
|
||||
return originalMoveTokenHandler.call(this, event)
|
||||
return true
|
||||
}
|
||||
|
||||
const originalToJSON = Ruler.prototype.toJSON
|
||||
Ruler.prototype.toJSON = function () {
|
||||
const json = originalToJSON.call(this)
|
||||
if (this.draggedToken)
|
||||
json["draggedToken"] = this.draggedToken.data._id
|
||||
return json
|
||||
}
|
||||
|
||||
const originalUpdate = Ruler.prototype.update
|
||||
Ruler.prototype.update = function (data) {
|
||||
if (data.draggedToken) {
|
||||
this.draggedToken = canvas.tokens.get(data.draggedToken)
|
||||
}
|
||||
originalUpdate.call(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
function onTokenLeftDragStart(event) {
|
||||
@@ -86,7 +105,8 @@ function onTokenDragLeftDrop(event) {
|
||||
if (!canvas.controls.ruler.isDragRuler)
|
||||
return false
|
||||
canvas.controls.ruler.draggedToken = null
|
||||
canvas.controls.ruler.moveToken(event)
|
||||
const selectedTokens = canvas.tokens.placeables.filter(token => token._controlled)
|
||||
moveTokens.call(canvas.controls.ruler, selectedTokens)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -123,10 +143,16 @@ function strInsertAfter(haystack, needle, strToInsert) {
|
||||
// These patches were written with foundry-0.7.9.js as reference
|
||||
function patchRulerMeasure() {
|
||||
let code = Ruler.prototype.measure.toString()
|
||||
// Replace CRLF with LF in case foundry.js has CRLF for some reason
|
||||
code = code.replace(/\r\n/g, "\n")
|
||||
// Remove function signature and closing curly bracket (those are on the first and last line)
|
||||
code = code.slice(code.indexOf("\n"), code.lastIndexOf("\n"))
|
||||
code = strInsertAfter(code, "for ( let [i, d] of distances.entries() ) {\n", "segments[i].startDistance = totalDistance\n")
|
||||
code = strInsertAfter(code, "this._highlightMeasurement(ray", ", s.startDistance")
|
||||
|
||||
// Don't show ruler if the measured token is invisible
|
||||
code = "if (this.isDragRuler && !this.draggedToken.isVisible) return [];" + code
|
||||
|
||||
Ruler.prototype.measure = new Function("destination", "{gridSpaces=true}={}", code)
|
||||
}
|
||||
|
||||
@@ -148,6 +174,12 @@ function nativeSpeedProvider(token, playercolor) {
|
||||
function getColorForDistance(startDistance, subDistance) {
|
||||
if (!this.isDragRuler)
|
||||
return this.color
|
||||
// Don't apply colors if the current user doesn't have at least observer permissions
|
||||
if (this.draggedToken.actor.permission < 2) {
|
||||
// If this is a pc and alwaysShowSpeedForPCs is enabled we show the color anyway
|
||||
if (!(this.draggedToken.actor.data.type === "character" && game.settings.get(settingsKey, "alwaysShowSpeedForPCs")))
|
||||
return this.color
|
||||
}
|
||||
const distance = startDistance + subDistance
|
||||
const ranges = currentSpeedProvider(this.draggedToken, this.color)
|
||||
if (ranges.length === 0)
|
||||
@@ -163,6 +195,8 @@ function getColorForDistance(startDistance, subDistance) {
|
||||
// These patches were written with foundry-0.7.9.js as reference
|
||||
function patchRulerHighlightMeasurement() {
|
||||
let code = Ruler.prototype._highlightMeasurement.toString()
|
||||
// Replace CRLF with LF in case foundry.js has CRLF for some reason
|
||||
code = code.replace(/\r\n/g, "\n")
|
||||
// Remove function signature and closing curly bracket (those are on the first and last line)
|
||||
code = code.slice(code.indexOf("\n"), code.lastIndexOf("\n"))
|
||||
|
||||
|
||||
@@ -4,6 +4,15 @@ import {getDefaultDashMultiplier, getDefaultSpeedAttribute} from "./systems.js"
|
||||
export const settingsKey = "drag-ruler";
|
||||
|
||||
export function registerSettings() {
|
||||
game.settings.register(settingsKey, "alwaysShowSpeedForPCs", {
|
||||
name: "drag-ruler.settings.alwaysShowSpeedForPCs.name",
|
||||
hint: "drag-ruler.settings.alwaysShowSpeedForPCs.hint",
|
||||
scope: "world",
|
||||
config: true,
|
||||
type: Boolean,
|
||||
default: true,
|
||||
})
|
||||
|
||||
// This setting will be modified by the api if modules register to it
|
||||
game.settings.register(settingsKey, "speedProvider", {
|
||||
name: "drag-ruler.settings.speedProvider.name",
|
||||
|
||||
Reference in New Issue
Block a user