Implement measurement template support (resolves #13)
This commit is contained in:
+41
-34
@@ -4,10 +4,10 @@ import {Line} from "./geometry.js";
|
||||
import {getColorForDistance} from "./main.js"
|
||||
import {trackRays} from "./movement_tracking.js"
|
||||
import {recalculate} from "./socket.js";
|
||||
import {applyTokenSizeOffset, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
|
||||
import {applyTokenSizeOffset, getSnapPointForMeasuredTemplate, getSnapPointForToken, getTokenShape, highlightTokenShape, zip} from "./util.js";
|
||||
|
||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||
export async function moveTokens(draggedEntity, selectedTokens) {
|
||||
export async function moveEntities(draggedEntity, selectedEntities) {
|
||||
let wasPaused = game.paused;
|
||||
if (wasPaused && !game.user.isGM) {
|
||||
ui.notifications.warn(game.i18n.localize("GAME.PausedWarning"));
|
||||
@@ -19,8 +19,8 @@ export async function moveTokens(draggedEntity, selectedTokens) {
|
||||
// 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.constructor.dragRulerGetRaysFromWaypoints(this.waypoints, this.destination);
|
||||
if (!game.user.isGM) {
|
||||
const hasCollision = selectedTokens.some(token => {
|
||||
if (!game.user.isGM && draggedEntity instanceof Token) {
|
||||
const hasCollision = selectedEntities.some(token => {
|
||||
const offset = calculateTokenOffset(token, draggedEntity);
|
||||
const offsetRays = rays.filter(ray => !ray.isPrevious).map(ray => applyOffsetToRay(ray, offset))
|
||||
return offsetRays.some(r => canvas.walls.checkCollision(r));
|
||||
@@ -35,7 +35,7 @@ export async function moveTokens(draggedEntity, selectedTokens) {
|
||||
// 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 animateTokens.call(this, selectedTokens, draggedEntity, rays, wasPaused);
|
||||
await animateEntities.call(this, selectedEntities, draggedEntity, rays, wasPaused);
|
||||
|
||||
// Once all animations are complete we can clear the ruler
|
||||
if (this.draggedEntity?.id === draggedEntity.id)
|
||||
@@ -43,51 +43,53 @@ export async function moveTokens(draggedEntity, selectedTokens) {
|
||||
}
|
||||
|
||||
// This is a modified version code extracted from Ruler.moveToken from foundry 0.7.9
|
||||
async function animateTokens(tokens, draggedEntity, draggedRays, wasPaused) {
|
||||
async function animateEntities(entities, draggedEntity, draggedRays, wasPaused) {
|
||||
const newRays = draggedRays.filter(r => !r.isPrevious);
|
||||
const tokenAnimationData = tokens.map(token => {
|
||||
const tokenOffset = calculateTokenOffset(token, draggedEntity);
|
||||
const offsetRays = newRays.map(ray => applyOffsetToRay(ray, tokenOffset));
|
||||
const entityAnimationData = entities.map(entity => {
|
||||
const entityOffset = calculateEntityOffset(entity, draggedEntity);
|
||||
const offsetRays = newRays.map(ray => applyOffsetToRay(ray, entityOffset));
|
||||
|
||||
// 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 firstWaypoint = this.waypoints.find(w => !w.isPrevious);
|
||||
const origin = [firstWaypoint.x + tokenOffset.x, firstWaypoint.y + tokenOffset.y];
|
||||
const origin = [firstWaypoint.x + entityOffset.x, firstWaypoint.y + entityOffset.y];
|
||||
let dx, dy;
|
||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||
dx = token.data.x - origin[0];
|
||||
dy = token.data.y - origin[1];
|
||||
dx = entity.data.x - origin[0];
|
||||
dy = entity.data.y - origin[1];
|
||||
}
|
||||
else {
|
||||
dx = token.data.x - origin[0];
|
||||
dy = token.data.y - origin[1];
|
||||
dx = entity.data.x - origin[0];
|
||||
dy = entity.data.y - origin[1];
|
||||
}
|
||||
|
||||
return {token, rays: offsetRays, dx, dy};
|
||||
return {entity, rays: offsetRays, dx, dy};
|
||||
});
|
||||
|
||||
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++) {
|
||||
const isToken = draggedEntity instanceof Token;
|
||||
const animate = isToken && !game.keyboard.isDown("Alt");
|
||||
const startWaypoint = animate ? 0 : entityAnimationData[0].rays.length - 1;
|
||||
for (let i = startWaypoint;i < entityAnimationData[0].rays.length; i++) {
|
||||
if (!wasPaused && game.paused) break;
|
||||
const tokenPaths = tokenAnimationData.map(({token, rays, dx, dy}) => {
|
||||
const entityPaths = entityAnimationData.map(({entity, 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 path = new Ray({x: entity.x, y: entity.y}, {x: dest[0] + dx, y: dest[1] + dy});
|
||||
return {entity, path};
|
||||
});
|
||||
const updates = tokenPaths.map(({token, path}) => {
|
||||
return {x: path.B.x, y: path.B.y, _id: token.id};
|
||||
const updates = entityPaths.map(({entity, path}) => {
|
||||
return {x: path.B.x, y: path.B.y, _id: entity.id};
|
||||
});
|
||||
await draggedEntity.scene.updateEmbeddedEntity(draggedEntity.constructor.embeddedName, updates, {animate});
|
||||
if (animate)
|
||||
await Promise.all(tokenPaths.map(({token, path}) => token.animateMovement(path)));
|
||||
await Promise.all(entityPaths.map(({entity, path}) => entity.animateMovement(path)));
|
||||
}
|
||||
trackRays(tokens, tokenAnimationData.map(({rays}) => rays)).then(() => recalculate(tokens));
|
||||
if (isToken)
|
||||
trackRays(entities, entityAnimationData.map(({rays}) => rays)).then(() => recalculate(entities));
|
||||
}
|
||||
|
||||
function calculateTokenOffset(tokenA, tokenB) {
|
||||
return {x: tokenA.data.x - tokenB.data.x, y: tokenA.data.y - tokenB.data.y}
|
||||
function calculateEntityOffset(entityA, entityB) {
|
||||
return {x: entityA.data.x - entityB.data.x, y: entityA.data.y - entityB.data.y};
|
||||
}
|
||||
|
||||
function applyOffsetToRay(ray, offset) {
|
||||
@@ -119,17 +121,22 @@ export function onMouseMove(event) {
|
||||
|
||||
// This is a modified version of Ruler.measure form foundry 0.7.9
|
||||
export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
if (this.isDragRuler && !this.draggedEntity.isVisible)
|
||||
const isToken = this.draggedEntity instanceof Token;
|
||||
if (isToken && !this.draggedEntity.isVisible)
|
||||
return []
|
||||
|
||||
if (snap)
|
||||
destination = getSnapPointForToken(destination.x, destination.y, this.draggedEntity);
|
||||
if (snap) {
|
||||
if (isToken)
|
||||
destination = getSnapPointForToken(destination.x, destination.y, this.draggedEntity);
|
||||
else
|
||||
destination = getSnapPointForMeasuredTemplate(destination.x, destination.y);
|
||||
}
|
||||
|
||||
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS);
|
||||
const terrainRulerAvailable = isToken && game.modules.get("terrain-ruler")?.active && (!game.modules.get("TerrainLayer")?.active || canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS);
|
||||
|
||||
const waypoints = this.waypoints.concat([destination]);
|
||||
// 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.draggedEntity);
|
||||
const centeredWaypoints = isToken ? applyTokenSizeOffset(waypoints, this.draggedEntity) : duplicate(waypoints);
|
||||
// Foundries native ruler requires the waypoints to sit in the dead center of the square to work properly
|
||||
if (!terrainRulerAvailable)
|
||||
centeredWaypoints.forEach(w => [w.x, w.y] = canvas.grid.getCenter(w.x, w.y));
|
||||
@@ -162,7 +169,7 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
}
|
||||
|
||||
|
||||
const shape = getTokenShape(this.draggedEntity)
|
||||
const shape = isToken ? getTokenShape(this.draggedEntity) : null;
|
||||
|
||||
// Compute measured distance
|
||||
const distances = measureDistances(centeredSegments, this.draggedEntity, shape, {gridSpaces});
|
||||
@@ -220,7 +227,7 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
}
|
||||
|
||||
// Highlight grid positions
|
||||
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
|
||||
if (isToken && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
|
||||
if (terrainRulerAvailable)
|
||||
highlightMeasurementTerrainRuler.call(this, cs.ray, cs.startDistance, shape, opacityMultiplier)
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user