Skip to content

Commit

Permalink
Merge branch 'release/0.7.5' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Jan 8, 2024
2 parents 1d06e34 + 4e03170 commit e8192fc
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 22 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.7.5
Fix for pause/unpause game not working due to conflict with token move using spacebar.
Add handling for unsnapping from the grid. If shift is held, unsnap ruler waypoints and destination. If measuring from a token, set the origin point of the ruler to the (possibly unsnapped) token center.

# 0.7.4
Add option to enable a Token Ruler when dragging tokens.
Add option to use a token speed attribute to highlight in differing colors when using the ruler (from a token) or dragging tokens.
Expand Down
22 changes: 22 additions & 0 deletions scripts/BaseGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* globals
canvas
*/
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */

// Patches for the BaseGrid class
export const PATCHES = {};
PATCHES.BASIC = {};

/**
* Mix BaseGrid.prototype._getRulerDestination
* If the ruler is not snapped, then return the actual token position, adjusted for token dimensions.
* @param {Ray} ray The ray being moved along.
* @param {Point} offset The offset of the ruler's origin relative to the token's position.
* @param {Token} token The token placeable being moved.
*/
function _getRulerDestination(wrapped, ray, offset, token) {
if ( canvas.controls.ruler._unsnap ) return ray.B.add(offset);
return wrapped(ray, offset, token);
}

PATCHES.BASIC.MIXES = { _getRulerDestination };
14 changes: 8 additions & 6 deletions scripts/ClientKeybindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ PATCHES.TOKEN_RULER = {}; // Assume this patch is only present if the token rule
* If the Token Ruler is active, call that instead.
* @param {KeyboardEventContext} context The context data of the event
*/
async function _onMeasuredRulerMovement(wrapped, context) {
console.log("_onMeasuredRulerMovement");
function _onMeasuredRulerMovement(wrapped, context) {
// We only care about when tokens are being dragged
const ruler = canvas.controls.ruler;
if ( ui.controls.tool !== "select" ) return wrapped(context);

// If in token selection, don't use the ruler unless we are already starting a measurement.
if ( !ruler.active
|| !canvas.tokens.active
|| ui.controls.tool !== "select" ) return wrapped(context);
|| !canvas.controls.ruler._state
|| !canvas.tokens.active ) return false;

// For each controlled token, end the drag.
canvas.tokens.clearPreviewContainer();
await ruler.moveToken();
ruler._endMeasurement();
ruler.moveToken().then((response) => ruler._endMeasurement());
return true;
}

PATCHES.TOKEN_RULER.STATIC_WRAPS = { _onMeasuredRulerMovement }
22 changes: 22 additions & 0 deletions scripts/HexagonalGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* globals
canvas
*/
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */

// Patches for the HexagonalGrid class
export const PATCHES = {};
PATCHES.BASIC = {};

/**
* Mix HexagonalGrid.prototype._getRulerDestination
* If the ruler is not snapped, then return the actual token position, adjusted for token dimensions.
* @param {Ray} ray The ray being moved along.
* @param {Point} offset The offset of the ruler's origin relative to the token's position.
* @param {Token} token The token placeable being moved.
*/
function _getRulerDestination(wrapped, ray, offset, token) {
if ( canvas.controls.ruler._unsnap ) return ray.B.add(offset);
return wrapped(ray, offset, token);
}

PATCHES.BASIC.MIXES = { _getRulerDestination };
1 change: 1 addition & 0 deletions scripts/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Hooks.once("init", function() {
MODULES_ACTIVE.ENHANCED_TERRAIN_LAYER = game.modules.get("enhanced-terrain-layer")?.active;
MODULES_ACTIVE.LEVELS = game.modules.get("levels")?.active;
MODULES_ACTIVE.ELEVATED_VISION = game.modules.get("elevatedvision")?.active;
MODULES_ACTIVE.TERRAIN_MAPPER = game.modules.get("terrainmapper")?.active;
});

/**
Expand Down
4 changes: 4 additions & 0 deletions scripts/patching.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import { PATCHES as PATCHES_Token } from "./Token.js";
import { PATCHES as PATCHES_GridLayer } from "./GridLayer.js";
import { PATCHES as PATCHES_PlaceableObject } from "./PlaceableObject.js";
import { PATCHES as PATCHES_ClientKeybindings } from "./ClientKeybindings.js";
import { PATCHES as PATCHES_BaseGrid } from "./BaseGrid.js";
import { PATCHES as PATCHES_HexagonalGrid } from "./HexagonalGrid.js";

// Settings
import { PATCHES as PATCHES_Settings } from "./ModuleSettingsAbstract.js";

const PATCHES = {
BaseGrid: PATCHES_BaseGrid,
ClientKeybindings: PATCHES_ClientKeybindings,
GridLayer: PATCHES_GridLayer,
HexagonalGrid: PATCHES_HexagonalGrid,
Ruler: PATCHES_Ruler,
PlaceableObject: PATCHES_PlaceableObject,
Token: PATCHES_Token,
Expand Down
163 changes: 147 additions & 16 deletions scripts/ruler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
canvas,
Color,
CONST,
duplicate,
game,
getProperty,
PIXI,
Expand All @@ -28,9 +29,8 @@ import {
_animateSegment
} from "./segments.js";

import { SPEED } from "./const.js";
import { SPEED, MODULES_ACTIVE } from "./const.js";
import { Ray3d } from "./geometry/3d/Ray3d.js";
import { Point3d } from "./geometry/3d/Point3d.js";

/**
* Modified Ruler
Expand Down Expand Up @@ -88,17 +88,20 @@ function toJSON(wrapper) {
// console.log("constructing ruler json!")
const obj = wrapper();
obj._userElevationIncrements = this._userElevationIncrements;
obj._unsnap = this._unsnap;
return obj;
}

/**
* Wrap Ruler.prototype.update
* Retrieve the current _userElevationIncrements
* Retrieve the current _userElevationIncrements.
* Retrieve the current snap status.
*/
function update(wrapper, data) {
// Fix for displaying user elevation increments as they happen.
const triggerMeasure = this._userElevationIncrements !== data._userElevationIncrements;
this._userElevationIncrements = data._userElevationIncrements;
this._unsnap = data._unsnap;
wrapper(data);

if ( triggerMeasure ) {
Expand All @@ -114,6 +117,21 @@ function update(wrapper, data) {
*/
function _addWaypoint(wrapper, point) {
wrapper(point);

// If moving a token, start the origin at the token center.
if ( this.waypoints.length === 1 ) {
// Temporarily replace the waypoint with the point so we can detect the token properly.
const snappedWaypoint = duplicate(this.waypoints[0]);
this.waypoints[0].copyFrom(point);
const token = this._getMovementToken();
if ( token ) this.waypoints[0].copyFrom(token.center);
else this.waypoints[0].copyFrom(snappedWaypoint);
}

// Otherwise if shift was held, use the precise point.
else if ( this._unsnap ) this.waypoints.at(-1).copyFrom(point);

// Elevate the waypoint.
addWaypointElevationIncrements(this, point);
}

Expand All @@ -127,6 +145,18 @@ function _removeWaypoint(wrapper, point, { snap = true } = {}) {
wrapper(point, { snap });
}

/**
* Wrap Ruler.prototype._getMeasurementDestination
* If shift was held, use the precise destination instead of snapping.
* @param {Point} destination The current pixel coordinates of the mouse movement
* @returns {Point} The destination point, a center of a grid space
*/
function _getMeasurementDestination(wrapped, destination) {
const pt = wrapped(destination);
if ( this._unsnap ) pt.copyFrom(destination);
return pt;
}

/**
* Wrap Ruler.prototype._animateMovement
* Add additional controlled tokens to the move, if permitted.
Expand Down Expand Up @@ -162,6 +192,42 @@ function dragRulerClearWaypoints(wrapper) {
this._userElevationIncrements = 0;
}

/**
* Wrap Ruler.prototype._computeDistance
* Add moveDistance property to each segment; track the total.
* If token not present or Terrain Mapper not active, this will be the same as segment distance.
* @param {boolean} gridSpaces Base distance on the number of grid spaces moved?
*/
function _computeDistance(wrapped, gridSpaces) {
wrapped(gridSpaces);

// Add a movement distance based on token and terrain for the segment.
// Default to segment distance.
const token = this._getMovementToken();
let totalMoveDistance = 0;
for ( const segment of this.segments ) {
segment.moveDistance = modifiedMoveDistance(segment.distance, segment.ray, token);
totalMoveDistance += segment.moveDistance;
}
this.totalMoveDistance = totalMoveDistance;
}


/**
* Modify distance by terrain mapper adjustment for token speed.
* @param {number} distance Distance of the ray
* @param {Ray|Ray3d} ray Ray to measure
* @param {Token} token Token to use
* @returns {number} Modified distance
*/
function modifiedMoveDistance(distance, ray, token) {
if ( !MODULES_ACTIVE.TERRAIN_MAPPER || !token ) return distance;
const terrainAPI = game.modules.get("terrainmapper").api;
const moveMult = terrainAPI.Terrain.percentMovementForTokenAlongPath(token, ray.A, ray.B);
if ( !moveMult ) return distance;
return distance * (1 / moveMult); // Invert because moveMult is < 1 if speed is penalized.
}


// ----- NOTE: Segment highlighting ----- //
/**
Expand All @@ -179,7 +245,7 @@ function _highlightMeasurementSegment(wrapped, segment) {
let pastDistance = 0;
for ( const s of this.segments ) {
if ( s === segment ) break;
pastDistance += s.distance;
pastDistance += s.moveDistance;
}

// Constants
Expand All @@ -189,28 +255,24 @@ function _highlightMeasurementSegment(wrapped, segment) {
const dashColor = Color.from(0xffff00);
const maxColor = Color.from(0xff0000);

if ( segment.distance > walkDist ) {
console.debug(`${segment.distance}`);
}

// Track the splits.
let remainingSegment = segment;
const splitSegments = [];

// Walk
remainingSegment.color = walkColor;
const walkSegments = splitSegment(remainingSegment, pastDistance, walkDist);
const walkSegments = splitSegment(remainingSegment, pastDistance, walkDist, token);
if ( walkSegments.length ) {
const segment0 = walkSegments[0];
splitSegments.push(segment0);
pastDistance += segment0.distance;
pastDistance += segment0.moveDistance;
remainingSegment = walkSegments[1]; // May be undefined.
}

// Dash
if ( remainingSegment ) {
remainingSegment.color = dashColor;
const dashSegments = splitSegment(remainingSegment, pastDistance, dashDist);
const dashSegments = splitSegment(remainingSegment, pastDistance, dashDist, token);
if ( dashSegments.length ) {
const segment0 = dashSegments[0];
splitSegments.push(segment0);
Expand Down Expand Up @@ -256,10 +318,10 @@ function _highlightMeasurementSegment(wrapped, segment) {
* - If cutoffDistance is after the segment end, return [segment].
* - If cutoffDistance is within the segment, return [segment0, segment1]
*/
function splitSegment(segment, pastDistance, cutoffDistance) {
function splitSegment(segment, pastDistance, cutoffDistance, token) {
cutoffDistance -= pastDistance;
if ( cutoffDistance <= 0 ) return [];
if ( cutoffDistance >= segment.distance ) return [segment];
if ( cutoffDistance >= segment.moveDistance ) return [segment];

// Determine where on the segment ray the cutoff occurs.
// Use canvas grid distance measurements to handle 5-5-5, 5-10-5, other measurement configs.
Expand Down Expand Up @@ -302,11 +364,12 @@ function splitSegment(segment, pastDistance, cutoffDistance) {
breakPoint.z = z;
const shorterSegment = { ray: new Ray3d(A, breakPoint) };
shorterSegment.distance = canvas.grid.measureDistances([shorterSegment], { gridSpaces: true })[0];
if ( shorterSegment.distance <= cutoffDistance ) break;
shorterSegment.moveDistance = modifiedMoveDistance(shorterSegment.distance, shorterSegment.ray, token);
if ( shorterSegment.moveDistance <= cutoffDistance ) break;
}
} else {
// Use t values.
const t = cutoffDistance / segment.distance;
const t = cutoffDistance / segment.moveDistance;
breakPoint = A.projectToward(B, t);
}
if ( breakPoint === B ) return [segment];
Expand All @@ -318,6 +381,8 @@ function splitSegment(segment, pastDistance, cutoffDistance) {
const distances = canvas.grid.measureDistances(segments, { gridSpaces: false });
segment0.distance = distances[0];
segment1.distance = distances[1];
segment0.moveDistance = modifiedMoveDistance(segment0.distance, segment0.ray, token);
segment1.moveDistance = modifiedMoveDistance(segment1.distance, segment1.ray, token);
return segments;
}

Expand Down Expand Up @@ -396,19 +461,85 @@ export function * iterateGridUnderLine(origin, destination, { reverse = false }
// await t0.scene.updateEmbeddedDocuments(t0.constructor.embeddedName, updates);
// return true;

// ----- NOTE: Event handling ----- //

/**
* Wrap Ruler.prototype._onDragStart
* Record whether shift is held.
* @param {PIXI.FederatedEvent} event The drag start event
* @see {Canvas._onDragLeftStart}
*/
function _onDragStart(wrapped, event) {
this._unsnap = event.shiftKey;
return wrapped(event);
}

/**
* Wrap Ruler.prototype._onClickLeft.
* Record whether shift is held.
* @param {PIXI.FederatedEvent} event The pointer-down event
* @see {Canvas._onDragLeftStart}
*/
function _onClickLeft(wrapped, event) {
this._unsnap = event.shiftKey;
return wrapped(event);
}

/**
* Wrap Ruler.prototype._onClickRight
* Record whether shift is held.
* @param {PIXI.FederatedEvent} event The pointer-down event
* @see {Canvas._onClickRight}
*/
function _onClickRight(wrapped, event) {
this._unsnap = event.shiftKey;
return wrapped(event);
}

/**
* Wrap Ruler.prototype._onMouseMove
* Record whether shift is held.
* @param {PIXI.FederatedEvent} event The mouse move event
* @see {Canvas._onDragLeftMove}
*/
function _onMouseMove(wrapped, event) {
this._unsnap = event.shiftKey;
return wrapped(event);
}

/**
* Wrap Ruler.prototype._onMouseUp
* Record whether shift is held
* @param {PIXI.FederatedEvent} event The pointer-up event
* @see {Canvas._onDragLeftDrop}
*/
function _onMouseUp(wrapped, event) {
this._unsnap = event.shiftKey;
return wrapped(event);
}

PATCHES.BASIC.WRAPS = {
clear,
toJSON,
update,
_addWaypoint,
_removeWaypoint,
_getMeasurementDestination,

// Wraps related to segments
_getMeasurementSegments,
_getSegmentLabel,
_computeDistance,

// Move token methods
_animateMovement
_animateMovement,

// Events
_onDragStart,
_onClickLeft,
_onClickRight,
_onMouseMove,
_onMouseUp
};

PATCHES.SPEED_HIGHLIGHTING.WRAPS = { _highlightMeasurementSegment };
Expand Down

0 comments on commit e8192fc

Please sign in to comment.