Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0d0483722 | |||
| 4f1dec3089 | |||
| c4d089e8ff | |||
| f8fe4ee971 | |||
| 378401b5bb | |||
| 5a7f5531f2 | |||
| 862477c698 | |||
| 384de1a1b5 |
@@ -1,7 +1,20 @@
|
||||
## 1.4.0
|
||||
### New features
|
||||
- If the [Terrain Ruler module](https://foundryvtt.com/packages/terrain-ruler/) is installed and activated, Drag Ruler will now take difficult terrain that was placed with the [TerrainLayer module](https://foundryvtt.com/packages/TerrainLayer/) into account.
|
||||
- The ruler will now always be drawn from the tokens center (even for tokens larger than 1x1) on all grid types (on Hex Grids this requires the Hex Token Size Support module).
|
||||
- For tokens larger than 1x1 the highlighted path will now reflect the tokens size (on Hex Grids this requires the Hex Token Size Support module)
|
||||
- The GM's Drag Ruler can now be hidden from non GM players via a setting.
|
||||
- When multiple different colors apply to a single grid space because the path crosses itself the color representing ranges further away will take priortiy over colors representing closer ranges.
|
||||
|
||||
### API
|
||||
- Speed providers can now selectively ignore difficult terrain and even implement their own cost functions, if the default Drag Ruler behavior doesn't fit the game system. This can be achieved by overriding the new, optional `SpeedProvider` function `getCostForStep`.
|
||||
|
||||
|
||||
## 1.3.5
|
||||
### Bugfixes
|
||||
- Fixed a regression where spaces could suddenly change their color during measurement
|
||||
|
||||
|
||||
## 1.3.4
|
||||
### Module compatibility
|
||||
- Increased compatiblility with other modules (namely Drag Ruler and Terrain Ruler are no longer incompatible)
|
||||
|
||||
@@ -15,6 +15,13 @@ You can add waypoints to the path by pressing spacebar while you drag the token.
|
||||
|
||||

|
||||
|
||||
## Difficult Terrain
|
||||
**To use support for difficult terrain you must install the [Terrain Ruler module](https://foundryvtt.com/packages/terrain-ruler)**
|
||||
|
||||
With the Terrain Ruler module installed, Drag Ruler is able to take difficult terrain that was placed with the [TerrainLayer module](https://foundryvtt.com/packages/TerrainLayer/) into account when measuring distances.
|
||||
|
||||

|
||||
|
||||
|
||||
## Why would I want to use this instead of ShowDragDistance?
|
||||
ShowDragDistance isn't maintained anymore. This means that it is at risk to stop working with every foundry update. In fact this process has already begun. As of Foundry Version 0.7.9 ShowDragDistance doesn't work anymore on gridless maps. Drag Ruler on the other hand is fully compatible with the current Foundry release and I'll continue updating it for future foundry releases for the forseeable future.
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
"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."
|
||||
},
|
||||
"showGMRulerToPlayers": {
|
||||
"name": "Show GM ruler to players",
|
||||
"hint": "If disabled the ruler of GMs will not be shown for non-GM players"
|
||||
},
|
||||
"speedProviderSettings": {
|
||||
"name": "Speed Provider Settings",
|
||||
"hint": "The Speed Provider Settings contain all the game system specific settings.",
|
||||
|
||||
+4
-3
@@ -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.3.5",
|
||||
"version": "1.4.0",
|
||||
"minimumCoreVersion" : "0.7.9",
|
||||
"compatibleCoreVersion" : "0.7.9",
|
||||
"authors": [
|
||||
@@ -31,9 +31,10 @@
|
||||
}
|
||||
],
|
||||
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.3.5.zip",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.4.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"
|
||||
"bugs": "https://github.com/manuelVo/foundryvtt-drag-ruler/issues",
|
||||
"allowBugReporter": true
|
||||
}
|
||||
|
||||
@@ -97,6 +97,13 @@ export function getUnreachableColorFromSpeedProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCostFromSpeedProvider(token, area) {
|
||||
if (currentSpeedProvider instanceof Function) {
|
||||
return SpeedProvider.prototype.getCostForStep.call(undefined, token, area);
|
||||
}
|
||||
return currentSpeedProvider.getCostForStep(token, area);
|
||||
}
|
||||
|
||||
export function registerModule(moduleId, speedProvider) {
|
||||
// Check if a module with the given id exists and is currently enabled
|
||||
const module = game.modules.get(moduleId)
|
||||
|
||||
+11
-16
@@ -1,19 +1,14 @@
|
||||
import {getColorForDistance} from "./main.js"
|
||||
import {highlightTokenShape} from "./util.js"
|
||||
|
||||
export function getHexSizeSupportTokenGridCenter(token) {
|
||||
const tokenCenterOffset = CONFIG.hexSizeSupport.getCenterOffset(token)
|
||||
const tokenCenter = {x: token.x + tokenCenterOffset.x, y: token.y + tokenCenterOffset.y}
|
||||
if (token.getFlag("hex-size-support", "borderSize") % 2 === 1)
|
||||
return tokenCenter
|
||||
if (canvas.grid.grid.columns) {
|
||||
let hexOffset = canvas.grid.w / 2
|
||||
if (!CONFIG.hexSizeSupport.getAltOrientationFlag(token))
|
||||
hexOffset *= -1
|
||||
tokenCenter.x += hexOffset
|
||||
}
|
||||
else {
|
||||
let hexOffset = canvas.grid.h / 2
|
||||
if (CONFIG.hexSizeSupport.getAltOrientationFlag(token))
|
||||
hexOffset *= -1
|
||||
tokenCenter.y += hexOffset
|
||||
}
|
||||
return tokenCenter
|
||||
return {x: token.x + tokenCenterOffset.x, y: token.y + tokenCenterOffset.y}
|
||||
}
|
||||
|
||||
export function highlightMeasurementTerrainRuler(ray, startDistance, tokenShape=[{x: 0, y: 0}]) {
|
||||
for (const space of ray.terrainRulerVisitedSpaces.reverse()) {
|
||||
const color = getColorForDistance.call(this, startDistance, space.distance)
|
||||
highlightTokenShape.call(this, space, tokenShape, color)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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
|
||||
export function getPixelsFromGridPosition(xGrid, yGrid) {
|
||||
if (canvas.grid.isHex) {
|
||||
return canvas.grid.grid.getPixelsFromGridPosition(yGrid, xGrid)
|
||||
}
|
||||
const [x, y] = 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
|
||||
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
|
||||
export function getGridPositionFromPixels(xPixel, yPixel) {
|
||||
const [x, y] = canvas.grid.grid.getGridPositionFromPixels(xPixel, yPixel)
|
||||
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS)
|
||||
return [y, x]
|
||||
return [x, y]
|
||||
}
|
||||
+44
-23
@@ -1,4 +1,8 @@
|
||||
import { getCostFromSpeedProvider } from "./api.js";
|
||||
import {highlightMeasurementTerrainRuler} from "./compatibility.js";
|
||||
import {getGridPositionFromPixels} from "./foundry_fixes.js";
|
||||
import {getColorForDistance} from "./main.js"
|
||||
import {applyTokenSizeOffset, getAreaFromPositionAndShape, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
|
||||
|
||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||
export async function moveTokens(draggedToken, selectedTokens) {
|
||||
@@ -102,9 +106,12 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
return []
|
||||
|
||||
if (snap)
|
||||
destination = new PIXI.Point(...canvas.grid.getCenter(destination.x, destination.y));
|
||||
destination = getSnapPointForToken(destination.x, destination.y, this.draggedToken)
|
||||
|
||||
const waypoints = this.waypoints.concat([destination]);
|
||||
const centeredWaypoints = waypoints.map(w => new PIXI.Point(...canvas.grid.getCenter(w.x, w.y)))
|
||||
// Move the waypoints to the center of the grid if a size is used that measures from edge to edge
|
||||
const centeredWaypoints = applyTokenSizeOffset(waypoints, this.draggedToken)
|
||||
|
||||
const r = this.ruler;
|
||||
this.destination = destination;
|
||||
|
||||
@@ -126,14 +133,22 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
centeredSegments.push({ray: centeredRay, label})
|
||||
}
|
||||
|
||||
const shape = getTokenShape(this.draggedToken)
|
||||
|
||||
// Compute measured distance
|
||||
const distances = canvas.grid.measureDistances(centeredSegments, { gridSpaces });
|
||||
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS
|
||||
let distances
|
||||
if (terrainRulerAvailable)
|
||||
distances = game.terrainRuler.measureDistances(centeredSegments, {costFunction: (x, y) => getCostFromSpeedProvider(this.draggedToken, getAreaFromPositionAndShape({x, y}, shape), {x, y})});
|
||||
else
|
||||
distances = canvas.grid.measureDistances(centeredSegments, { gridSpaces });
|
||||
|
||||
let totalDistance = 0;
|
||||
for (let [i, d] of distances.entries()) {
|
||||
let s = segments[i];
|
||||
let s = centeredSegments[i];
|
||||
s.startDistance = totalDistance
|
||||
totalDistance += d;
|
||||
s.last = i === (segments.length - 1);
|
||||
s.last = i === (centeredSegments.length - 1);
|
||||
s.distance = d;
|
||||
s.text = this._getSegmentLabel(d, totalDistance, s.last);
|
||||
}
|
||||
@@ -149,24 +164,27 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
rulerColor = getColorForDistance.call(this, totalDistance)
|
||||
else
|
||||
rulerColor = this.color
|
||||
for (let s of segments) {
|
||||
const { ray, label, text, last } = s;
|
||||
for (const [s, cs] of zip(segments.reverse(), centeredSegments.reverse())) {
|
||||
const { label, text, last } = cs;
|
||||
|
||||
// Draw line segment
|
||||
r.lineStyle(6, 0x000000, 0.5).moveTo(ray.A.x, ray.A.y).lineTo(ray.B.x, ray.B.y)
|
||||
.lineStyle(4, rulerColor, 0.25).moveTo(ray.A.x, ray.A.y).lineTo(ray.B.x, ray.B.y);
|
||||
r.lineStyle(6, 0x000000, 0.5).moveTo(s.ray.A.x, s.ray.A.y).lineTo(s.ray.B.x, s.ray.B.y)
|
||||
.lineStyle(4, rulerColor, 0.25).moveTo(s.ray.A.x, s.ray.A.y).lineTo(s.ray.B.x, s.ray.B.y);
|
||||
|
||||
// Draw the distance label just after the endpoint of the segment
|
||||
if (label) {
|
||||
label.text = text;
|
||||
label.alpha = last ? 1.0 : 0.5;
|
||||
label.visible = true;
|
||||
let labelPosition = ray.project((ray.distance + 50) / ray.distance);
|
||||
let labelPosition = cs.ray.project((cs.ray.distance + 50) / cs.ray.distance);
|
||||
label.position.set(labelPosition.x, labelPosition.y);
|
||||
}
|
||||
|
||||
// Highlight grid positions
|
||||
highlightMeasurementNative.call(this, ray, s.startDistance);
|
||||
if (terrainRulerAvailable)
|
||||
highlightMeasurementTerrainRuler.call(this, cs.ray, cs.startDistance, shape)
|
||||
else
|
||||
highlightMeasurementNative.call(this, cs.ray, cs.startDistance, shape);
|
||||
}
|
||||
|
||||
// Draw endpoints
|
||||
@@ -178,7 +196,7 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
return segments;
|
||||
}
|
||||
|
||||
export function highlightMeasurementNative(ray, startDistance) {
|
||||
export function highlightMeasurementNative(ray, startDistance, tokenShape=[{x: 0, y: 0}]) {
|
||||
const spacer = canvas.scene.data.gridType === CONST.GRID_TYPES.SQUARE ? 1.41 : 1;
|
||||
const nMax = Math.max(Math.floor(ray.distance / (spacer * Math.min(canvas.grid.w, canvas.grid.h))), 1);
|
||||
const tMax = Array.fromRange(nMax+1).map(t => t / nMax);
|
||||
@@ -187,7 +205,7 @@ export function highlightMeasurementNative(ray, startDistance) {
|
||||
let prior = null;
|
||||
|
||||
// Iterate over ray portions
|
||||
for ( let [i, t] of tMax.entries() ) {
|
||||
for ( let [i, t] of tMax.reverse().entries() ) {
|
||||
let {x, y} = ray.project(t);
|
||||
|
||||
// Get grid position
|
||||
@@ -197,23 +215,26 @@ export function highlightMeasurementNative(ray, startDistance) {
|
||||
|
||||
// Highlight the grid position
|
||||
let [xg, yg] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1);
|
||||
let subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0]
|
||||
let color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
canvas.grid.highlightPosition(this.name, {x: xg, y: yg, color: color});
|
||||
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0]
|
||||
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedToken);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
|
||||
// Skip the first one
|
||||
prior = [x1, y1];
|
||||
if ( i === 0 ) continue;
|
||||
|
||||
// If the positions are not neighbors, also highlight their halfway point
|
||||
if ( !canvas.grid.isNeighbor(x0, y0, x1, y1) ) {
|
||||
let th = tMax[i - 1] + (0.5 / nMax);
|
||||
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 [xgh, ygh] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h);
|
||||
subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0]
|
||||
color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
canvas.grid.highlightPosition(this.name, {x: xgh, y: ygh, color: color});
|
||||
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0]
|
||||
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedToken);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color);
|
||||
}
|
||||
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -6,6 +6,7 @@ import {measure, moveTokens, onMouseMove} from "./foundry_imports.js"
|
||||
import {performMigrations} from "./migration.js"
|
||||
import {registerSettings, settingsKey} from "./settings.js"
|
||||
import {SpeedProvider} from "./speed_provider.js"
|
||||
import { getSnapPointForToken } from "./util.js"
|
||||
|
||||
Hooks.once("init", () => {
|
||||
registerSettings()
|
||||
@@ -84,6 +85,9 @@ function hookRulerFunctions() {
|
||||
|
||||
const originalUpdate = Ruler.prototype.update
|
||||
Ruler.prototype.update = function (data) {
|
||||
// Don't show a GMs drag ruler to non GM players
|
||||
if (data.draggedToken && this.user.isGM && !game.user.isGM && !game.settings.get(settingsKey, "showGMRulerToPlayers"))
|
||||
return
|
||||
if (data.draggedToken) {
|
||||
this.draggedToken = canvas.tokens.get(data.draggedToken)
|
||||
}
|
||||
@@ -99,6 +103,12 @@ function hookRulerFunctions() {
|
||||
return originalMeasure.call(this, destination, options)
|
||||
}
|
||||
}
|
||||
|
||||
const originalEndMeasurement = Ruler.prototype._endMeasurement
|
||||
Ruler.prototype._endMeasurement = function () {
|
||||
originalEndMeasurement.call(this)
|
||||
this.draggedToken = null
|
||||
}
|
||||
}
|
||||
|
||||
function hookKeyboardManagerFunctions() {
|
||||
@@ -149,7 +159,7 @@ function onTokenLeftDragStart(event) {
|
||||
if (canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(this))
|
||||
tokenCenter = getHexSizeSupportTokenGridCenter(this)
|
||||
else
|
||||
tokenCenter = {x: this.x + canvas.grid.grid.w / 2, y: this.y + canvas.grid.grid.h / 2}
|
||||
tokenCenter = this.center
|
||||
ruler.clear();
|
||||
ruler._state = Ruler.STATES.STARTING;
|
||||
ruler.rulerOffset = {x: tokenCenter.x - event.data.origin.x, y: tokenCenter.y - event.data.origin.y}
|
||||
@@ -167,15 +177,15 @@ function onTokenDragLeftDrop(event) {
|
||||
if (!ruler.isDragRuler)
|
||||
return false
|
||||
const selectedTokens = canvas.tokens.controlled
|
||||
ruler._state = Ruler.STATES.MOVING
|
||||
moveTokens.call(ruler, ruler.draggedToken, selectedTokens)
|
||||
ruler.draggedToken = null
|
||||
return true
|
||||
}
|
||||
|
||||
function onTokenDragLeftCancel(event) {
|
||||
// This function is invoked by right clicking
|
||||
const ruler = canvas.controls.ruler
|
||||
if (!ruler.isDragRuler)
|
||||
if (!ruler.isDragRuler || ruler._state === Ruler.STATES.MOVING)
|
||||
return false
|
||||
if (ruler._state === Ruler.STATES.MEASURING) {
|
||||
if (!game.settings.get(settingsKey, "swapSpacebarRightClick")) {
|
||||
@@ -207,10 +217,8 @@ function onRulerMoveToken(event) {
|
||||
|
||||
function addWaypoint(point, snap=true) {
|
||||
if (snap)
|
||||
point = canvas.grid.getCenter(point.x, point.y);
|
||||
else
|
||||
point = [point.x, point.y]
|
||||
this.waypoints.push(new PIXI.Point(point[0], point[1]));
|
||||
point = getSnapPointForToken(point.x, point.y, this.draggedToken)
|
||||
this.waypoints.push(new PIXI.Point(point.x, point.y));
|
||||
this.labels.addChild(new PreciseText("", CONFIG.canvasTextStyle));
|
||||
}
|
||||
|
||||
@@ -225,7 +233,6 @@ function deleteWaypoint() {
|
||||
else {
|
||||
const token = ruler.draggedToken
|
||||
ruler._endMeasurement()
|
||||
ruler.draggedToken = null
|
||||
|
||||
// Deactivate the drag workflow in mouse
|
||||
token.mouseInteractionManager._deactivateDragEvents();
|
||||
|
||||
@@ -29,6 +29,15 @@ export function registerSettings() {
|
||||
default: true,
|
||||
})
|
||||
|
||||
game.settings.register(settingsKey, "showGMRulerToPlayers", {
|
||||
name: "drag-ruler.settings.showGMRulerToPlayers.name",
|
||||
hint: "drag-ruler.settings.showGMRulerToPlayers.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", {
|
||||
scope: "world",
|
||||
|
||||
@@ -56,6 +56,22 @@ export class SpeedProvider {
|
||||
return 0xFF0000
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cost for a token to step into the specificed area.
|
||||
* The area indicates the whole area that the token will occupy (for tokens larger than 1x1) the array will more than one entry.
|
||||
* The return value should be an integer indicating a multiplicator by that the cost of that step should be increased.
|
||||
* (1 is regular cost, 2 costs double, 3 costs triple, ...)
|
||||
* This function is only called if the TerrainLayer and TerrainRuler modules are enabled.
|
||||
*
|
||||
* Implementing this method is optional and only needs to be done if you want to provide a custom cost function (for example to allow tokens to ignore difficult terrain)
|
||||
*/
|
||||
getCostForStep(token, area) {
|
||||
// Lookup the cost for each square occupied by the token
|
||||
const costs = area.map(space => canvas.terrain.costGrid[space.y]?.[space.x]?.multiple ?? 1)
|
||||
// Return the maximum of the costs
|
||||
return costs.reduce((max, current) => Math.max(max, current))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether this token will use a Ruler or not.
|
||||
* If this is returns `false` for a token Drag Ruler will be disabled for that token. Dragging a token for which this function
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import {getPixelsFromGridPosition} from "./foundry_fixes.js"
|
||||
|
||||
export function* zip(it1, it2) {
|
||||
for (let i = 0;i < Math.min(it1.length, it2.length);i++) {
|
||||
yield [it1[i], it2[i]]
|
||||
}
|
||||
}
|
||||
|
||||
export function getSnapPointForToken(x, y, token) {
|
||||
if (canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
|
||||
if (token.getFlag("hex-size-support", "borderSize") % 2 === 0) {
|
||||
const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid)
|
||||
return new PIXI.Point(snapPoint.x, snapPoint.y)
|
||||
}
|
||||
else {
|
||||
return new PIXI.Point(...canvas.grid.getCenter(x, y))
|
||||
}
|
||||
}
|
||||
if (canvas.grid.isHex || token.data.width % 2 === 1) {
|
||||
return new PIXI.Point(...canvas.grid.getCenter(x, y))
|
||||
}
|
||||
const [snappedX, snappedY] = canvas.grid.getCenter(x - canvas.grid.w / 2, y - canvas.grid.h / 2)
|
||||
return new PIXI.Point(snappedX + canvas.grid.w / 2, snappedY + canvas.grid.h / 2)
|
||||
}
|
||||
|
||||
export function highlightTokenShape(position, shape, color) {
|
||||
const area = getAreaFromPositionAndShape(position, shape);
|
||||
for (const space of area) {
|
||||
const [x, y] = getPixelsFromGridPosition(space.x, space.y);
|
||||
canvas.grid.highlightPosition(this.name, {x, y, color})
|
||||
}
|
||||
}
|
||||
|
||||
export function getAreaFromPositionAndShape(position, shape) {
|
||||
return shape.map(space => {
|
||||
let x = position.x + space.x;
|
||||
let y = position.y + space.y;
|
||||
if (canvas.grid.isHex) {
|
||||
let shiftedRow;
|
||||
if (canvas.grid.grid.options.even)
|
||||
shiftedRow = 1
|
||||
else
|
||||
shiftedRow = 0
|
||||
if (canvas.grid.grid.options.columns) {
|
||||
if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) {
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (space.y % 2 !== 0 && position.y % 2 !== shiftedRow) {
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {x, y}
|
||||
});
|
||||
}
|
||||
|
||||
export function getTokenShape(token) {
|
||||
if (token.scene.data.gridType === CONST.GRID_TYPES.GRIDLESS)
|
||||
throw new Error("getTokenShape cannot be called for tokens on gridless maps")
|
||||
if (token.scene.data.gridType === CONST.GRID_TYPES.SQUARE) {
|
||||
const topOffset = -Math.floor(token.data.height / 2)
|
||||
const leftOffset = -Math.floor(token.data.width / 2)
|
||||
const shape = []
|
||||
for (let y = 0;y < token.data.height;y++) {
|
||||
for (let x = 0;x < token.data.width;x++) {
|
||||
shape.push({x: x + leftOffset, y: y + topOffset})
|
||||
}
|
||||
}
|
||||
return shape
|
||||
}
|
||||
else {
|
||||
// Hex grids
|
||||
if (game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
|
||||
const borderSize = token.data.flags["hex-size-support"].borderSize;
|
||||
let shape = [{x: 0, y: 0}];
|
||||
if (borderSize >= 2)
|
||||
shape = shape.concat([{x: 0, y: -1}, {x: -1, y: -1}]);
|
||||
if (borderSize >= 3)
|
||||
shape = shape.concat([{x: 0, y: 1}, {x: -1, y: 1}, {x: -1, y: 0}, {x: 1, y: 0}]);
|
||||
if (borderSize >= 4)
|
||||
shape = shape.concat([{x: -2, y: -1}, {x: 1, y: -1}, {x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}])
|
||||
|
||||
if (Boolean(CONFIG.hexSizeSupport.getAltOrientationFlag(token)) !== canvas.grid.grid.options.columns)
|
||||
shape.forEach(space => space.y *= -1);
|
||||
if (canvas.grid.grid.options.columns)
|
||||
shape = shape.map(space => {return {x: space.y, y: space.x}});
|
||||
return shape;
|
||||
}
|
||||
else {
|
||||
return [{x: 0, y: 0}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getTokenSize(token) {
|
||||
let w, h;
|
||||
const hexSizeSupportBorderSize = token.data.flags["hex-size-support"]?.borderSize;
|
||||
if (hexSizeSupportBorderSize > 0) {
|
||||
w = h = hexSizeSupportBorderSize
|
||||
}
|
||||
else {
|
||||
w = token.data.width
|
||||
h = token.data.height
|
||||
}
|
||||
return {w, h};
|
||||
}
|
||||
|
||||
// Tokens that have a size divisible by two (2x2, 4x4, 2x1) have their ruler at the edge of a cell.
|
||||
// This function applies an offset to to the waypoints that will move the ruler from the edge to the center of the cell
|
||||
export function applyTokenSizeOffset(waypoints, token) {
|
||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||
return waypoints
|
||||
}
|
||||
|
||||
const tokenSize = getTokenSize(token);
|
||||
const waypointOffset = {x: 0, y: 0};
|
||||
if (canvas.grid.isHex) {
|
||||
const isAltOrientation = CONFIG.hexSizeSupport.getAltOrientationFlag(token);
|
||||
if (canvas.grid.grid.options.columns) {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
if (!isAltOrientation)
|
||||
waypointOffset.x *= -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
if (isAltOrientation)
|
||||
waypointOffset.y *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
}
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return waypoints.map(w => new PIXI.Point(w.x + waypointOffset.x, w.y + waypointOffset.y))
|
||||
}
|
||||
Reference in New Issue
Block a user