Skip to content

Commit

Permalink
Merge branch 'release/0.9.2' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Jun 1, 2024
2 parents cc6b212 + 0194bbb commit fde550f
Show file tree
Hide file tree
Showing 17 changed files with 304 additions and 119 deletions.
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.9.2
Fix pathfinding.
Change the keybind for teleport to "F" (fast-forward) to avoid collision with arrow-key usage on the canvas. Switch to requiring "F" to be held when the user triggers the move to get teleport, to avoid weirdness with the drag still being active when using a separate trigger key.
Catch error if waypoint is not added in `_addWaypoint`.
Correct error when sending ruler data from one user to another.
Move Maximum speed category to `CONFIG.elevationruler.SPEED.CATEGORIES`. Should now be possible to define specific colors per user in the CONFIG, so long as category names are same.

# 0.9.1
Fix errors when using the ruler on gridless scenes.
Correct speed highlighting on gridless scenes.
Expand Down
2 changes: 1 addition & 1 deletion languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"elevationruler.keybindings.forceToGround.hint": "When measuring, press this key to get the distance to the ground. Press again to revert.",

"elevationruler.keybindings.teleport.name": "Teleport along ruler",
"elevationruler.keybindings.teleport.hint": "When measuring, press this key to move the token to the ruler destination without animation",
"elevationruler.keybindings.teleport.hint": "When measuring, hold this key when you release the dragged token or hit the spacebar to move the token to the ruler destination without animation.",

"elevationruler.settings.levels-use-floor-label.name": "Levels Floor Label",
"elevationruler.settings.levels-use-floor-label.hint": "If Levels module is active, label the ruler with the current floor, if the Levels UI floors are named.",
Expand Down
2 changes: 1 addition & 1 deletion module.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"manifestPlusVersion": "1.0.0",
"compatibility": {
"minimum": "12",
"verified": "12.324"
"verified": "12.325"
},
"authors": [
{
Expand Down
51 changes: 35 additions & 16 deletions scripts/Ruler.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* globals
canvas,
Color,
CONFIG,
CONST,
foundry,
Expand All @@ -15,7 +16,7 @@ export const PATCHES = {};
PATCHES.BASIC = {};
PATCHES.SPEED_HIGHLIGHTING = {};

import { SPEED, MODULE_ID, MaximumSpeedCategory } from "./const.js";
import { SPEED, MODULE_ID } from "./const.js";
import { Settings } from "./settings.js";
import { Ray3d } from "./geometry/3d/Ray3d.js";
import { Point3d } from "./geometry/3d/Point3d.js";
Expand Down Expand Up @@ -100,9 +101,11 @@ function _getMeasurementData(wrapper) {
B: s.ray.B
};
newObj.label = Boolean(s.label);
newObj.speed ??= newObj.speed.name;
return newObj;
});


myObj._userElevationIncrements = this._userElevationIncrements;
myObj._unsnap = this._unsnap;
myObj._unsnappedOrigin = this._unsnappedOrigin;
Expand All @@ -117,6 +120,7 @@ function _getMeasurementData(wrapper) {
* Retrieve the current snap status.
*/
function update(wrapper, data) {
if ( !data || (data.state === Ruler.STATES.INACTIVE) ) return wrapper(data);
const myData = data[MODULE_ID];
if ( !myData ) return wrapper(data); // Just in case.

Expand All @@ -129,6 +133,7 @@ function update(wrapper, data) {
// Reconstruct segments.
if ( myData._segments ) this.segments = myData._segments.map(s => {
s.ray = new Ray3d(s.ray.A, s.ray.B);
s.speed ??= SPEED.CATEGORIES.find(s => s.name === s.speed);
return s;
});

Expand All @@ -152,6 +157,10 @@ function update(wrapper, data) {
function _addWaypoint(wrapper, point) {
wrapper(point);

// In case the waypoint was never added.
if ( (this.state !== Ruler.STATES.STARTING) && (this.state !== Ruler.STATES.MEASURING ) ) return;
if ( !this.waypoints.length ) return;

// If shift was held, use the precise point.
if ( this._unsnap ) {
const lastWaypoint = this.waypoints.at(-1);
Expand Down Expand Up @@ -397,20 +406,17 @@ function _computeTokenSpeed() {
let segment;

// Debugging
// if ( this.segments[0].moveDistance > 25 ) log(`${this.segments[0].moveDistance}`);
// if ( this.segments[0].moveDistance > 30 ) log(`${this.segments[0].moveDistance}`);
// if ( this.segments[0].moveDistance > 50 ) log(`${this.segments[0].moveDistance}`);
// if ( this.segments[0].moveDistance > 60 ) log(`${this.segments[0].moveDistance}`);
if ( CONFIG[MODULE_ID].debug ) {
if ( this.segments[0].moveDistance > 25 ) log(`${this.segments[0].moveDistance}`);
if ( this.segments[0].moveDistance > 30 ) log(`${this.segments[0].moveDistance}`);
if ( this.segments[0].moveDistance > 50 ) log(`${this.segments[0].moveDistance}`);
if ( this.segments[0].moveDistance > 60 ) log(`${this.segments[0].moveDistance}`);
}

// Progress through each speed attribute in turn.
const categoryIter = [...SPEED.CATEGORIES, MaximumSpeedCategory].values();
const maxDistFn = (token, speedCategory, tokenSpeed) => {
if ( speedCategory.name === "Maximum" ) return Number.POSITIVE_INFINITY;
return SPEED.maximumCategoryDistance(token, speedCategory, tokenSpeed);
};

const categoryIter = [...SPEED.CATEGORIES].values();
let speedCategory = categoryIter.next().value;
let maxDistance = maxDistFn(token, speedCategory, tokenSpeed);
let maxDistance = SPEED.maximumCategoryDistance(token, speedCategory, tokenSpeed);

// Determine which speed category we are starting with
// Add in already moved combat distance and determine the starting category
Expand All @@ -424,10 +430,11 @@ function _computeTokenSpeed() {

while ( (segment = this.segments[s]) ) {
// Skip speed categories that do not provide a distance larger than the last.
while ( speedCategory.name !== "Maximum" && maxDistance <= minDistance ) {
while ( speedCategory && maxDistance <= minDistance ) {
speedCategory = categoryIter.next().value;
maxDistance = maxDistFn(token, speedCategory, tokenSpeed);
maxDistance = SPEED.maximumCategoryDistance(token, speedCategory, tokenSpeed);
}
if ( !speedCategory ) speedCategory = SPEED.CATEGORIES.at(-1);

segment.speed = speedCategory;
let newPrevDiagonal = _measureSegment(segment, token, numPrevDiagonal);
Expand Down Expand Up @@ -608,6 +615,17 @@ function _onMouseUp(wrapped, event) {
return wrapped(event);
}

/**
* Wrap Ruler.prototype._onMoveKeyDown
* If the teleport key is held, teleport the token.
* @param {KeyboardEventContext} context
*/
function _onMoveKeyDown(wrapped, context) {
const teleportKeys = new Set(game.keybindings.get(MODULE_ID, Settings.KEYBINDINGS.TELEPORT).map(binding => binding.key));
if ( teleportKeys.intersects(game.keyboard.downKeys) ) this.segments.forEach(s => s.teleport = true);
wrapped(context);
}

PATCHES.BASIC.WRAPS = {
_getMeasurementData,
update,
Expand All @@ -623,7 +641,8 @@ PATCHES.BASIC.WRAPS = {
_onClickLeft,
_onClickRight,
_onMouseMove,
_canMove
_canMove,
_onMoveKeyDown
};

PATCHES.BASIC.MIXES = { _animateMovement, _getMeasurementSegments, _onMouseUp };
Expand Down Expand Up @@ -677,7 +696,7 @@ function decrementElevation() {
* Move the token and stop the ruler measurement
* @returns {boolean} False if the movement did not occur
*/
async function teleport(context) {
async function teleport(_context) {
if ( this._state !== this.constructor.STATES.MEASURING ) return false;
if ( !this._canMove(this.token) ) return false;

Expand Down
3 changes: 1 addition & 2 deletions scripts/Token.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ async function _onDragLeftDrop(wrapped, event) {
// }

// ruler._state = Ruler.STATES.MOVING; // Do NOT set state to MOVING here in v12, as it will break the canvas.
await ruler.moveToken();
ruler._onMouseUp(event);
ruler._onMoveKeyDown(event); // Movement is async here but not awaited in _onMoveKeyDown.
}

/**
Expand Down
24 changes: 7 additions & 17 deletions scripts/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ const DashSpeedCategory = {
multiplier: 2
};

const MaximumSpeedCategory = {
Name: "Maximum",
color: Color.from(0xff0000),
multiplier: Number.POSITIVE_INFINITY
}

export const SPEED = {
/**
* Object of strings indicating where on the actor to locate the given attribute.
Expand All @@ -110,13 +116,7 @@ export const SPEED = {
* in the first category is the next category considered.
* @type {SpeedCategory[]}
*/
CATEGORIES: [WalkSpeedCategory, DashSpeedCategory],

/**
* Color to use once all SpeedCategory distances have been exceeded.
* @type {Color}
*/
MAXIMUM_COLOR: Color.from(0xff0000),
CATEGORIES: [WalkSpeedCategory, DashSpeedCategory, MaximumSpeedCategory],

// Use Font Awesome font unicode instead of basic unicode for displaying terrain symbol.

Expand All @@ -134,14 +134,6 @@ export const SPEED = {
terrainSymbol: "🥾"
};

export const MaximumSpeedCategory = {
name: "Maximum",
multiplier: Number.POSITIVE_INFINITY
};

Object.defineProperty(MaximumSpeedCategory, "color", {
get: () => SPEED.MAXIMUM_COLOR
});

/**
* Given a token, get the maximum distance the token can travel for a given type.
Expand Down Expand Up @@ -179,8 +171,6 @@ Hooks.once("init", function() {
DashSpeedCategory.multiplier = defaultDashMultiplier();
});


/* eslint-disable no-multi-spaces */
export function defaultHPAttribute() {
switch ( game.system.id ) {
case "dnd5e": return "actor.system.attributes.hp.value";
Expand Down
2 changes: 1 addition & 1 deletion scripts/geometry
Submodule geometry updated 4 files
+5 −0 Changelog.md
+11 −11 Shadow.js
+1 −1 Token.js
+1 −1 registration.js
5 changes: 3 additions & 2 deletions scripts/patching.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { Patcher } from "./Patcher.js";
import { PATCHES as PATCHES_Ruler } from "./Ruler.js";
import { PATCHES as PATCHES_Token } from "./Token.js";
import { PATCHES as PATCHES_ClientKeybindings } from "./ClientKeybindings.js";
import { PATCHES as PATCHES_TokenPF } from "./pathfinding/Token.js";
import { PATCHES as PATCHES_DrawingConfig } from "./DrawingConfig.js";


// Pathfinding
import { PATCHES as PATCHES_Wall } from "./pathfinding/Wall.js";
import { PATCHES as PATCHES_CanvasEdges } from "./pathfinding/CanvasEdges.js";
import { PATCHES as PATCHES_TokenPF } from "./pathfinding/Token.js";

// Movement tracking
import { PATCHES as PATCHES_TokenHUD } from "./token_hud.js";
Expand All @@ -26,6 +26,7 @@ const mergeObject = foundry.utils.mergeObject;
const PATCHES = {
ClientKeybindings: PATCHES_ClientKeybindings,
ClientSettings: PATCHES_ClientSettings,
["foundry.canvas.edges.CanvasEdges"]: PATCHES_CanvasEdges,
DrawingConfig: PATCHES_DrawingConfig,
Ruler: PATCHES_Ruler,
Token: mergeObject(mergeObject(PATCHES_Token, PATCHES_TokenPF), PATCHES_TokenHUD),
Expand Down
64 changes: 54 additions & 10 deletions scripts/pathfinding/BorderTriangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ import { PhysicalDistance } from "../PhysicalDistance.js";
import { Draw } from "../geometry/Draw.js";
import { WallTracerEdge } from "./WallTracer.js";

const OTHER_DIRECTION = {
ccw: "cw",
cw: "ccw"
};

const OTHER_TRIANGLE = {
cwTriangle: "ccwTriangle",
ccwTriangle: "cwTriangle"
};

/**
* An edge that makes up the triangle-shaped polygon
*/
Expand Down Expand Up @@ -86,8 +96,14 @@ export class BorderEdge {
*/
findTriangleFromVertexKey(vertexKey, dir = "ccw") {
const [a, b] = this.a.key === vertexKey ? [this.a, this.b] : [this.b, this.a];
const cCCW = this._nonSharedVertex(this.ccwTriangle);
return (foundry.utils.orient2dFast(a, b, cCCW) > 0) ^ (dir !== "ccw") ? this.ccwTriangle : this.cwTriangle;

if ( this.ccwTriangle ) {
const cCCW = this._nonSharedVertex(this.ccwTriangle);
return (foundry.utils.orient2dFast(a, b, cCCW) > 0) ^ (dir !== "ccw") ? this.ccwTriangle : this.cwTriangle;
} else {
const cCW = this._nonSharedVertex(this.cwTriangle);
return (foundry.utils.orient2dFast(a, b, cCW) < 0) ^ (dir !== "cw") ? this.cwTriangle : this.ccwTriangle;
}
}

/**
Expand Down Expand Up @@ -171,12 +187,13 @@ export class BorderEdge {
*/
edgeBlocks(origin, elevation = 0) {
if ( !origin ) {
if ( !this.ccwTriangle.center || !this.cwTriangle.center) {
console.warn("edgeBlocks|Triangle centers not defined.");
return false;
}
return this.edgeBlocks(this.ccwTriangle.center, elevation)
|| this.edgeBlocks(this.cwTriangle.center, elevation);
// if ( !this.ccwTriangle || !this.cwTriangle || !this.ccwTriangle.center || !this.cwTriangle.center) {
// console.warn("edgeBlocks|Triangle centers not defined.");
// return false;
// }
const ccwBlocks = this.ccwTriangle ? this.edgeBlocks(this.ccwTriangle.center, elevation) : false;
const cwBlocks = this.cwTriangle ? this.edgeBlocks(this.cwTriangle.center, elevation) : false;
return ccwBlocks || cwBlocks;
}

const { moveToken, tokenBlockType } = this.constructor;
Expand All @@ -199,7 +216,18 @@ export class BorderEdge {
const otherEndpoint = !this.endpointKeys.has(aTri.key) ? aTri
: !this.endpointKeys.has(bTri.key) ? bTri
: cTri;

// Debugging
if ( !this.endpointKeys.has(aTri.key)
&& !this.endpointKeys.has(bTri.key)
&& !this.endpointKeys.has(cTri.key) ) console.error(`Triangle ${triangle.id} keys not found ${aTri.key}, ${bTri.key}, ${cTri.key}`, this);

const orient2d = foundry.utils.orient2dFast;
const oABE = orient2d(a, b, otherEndpoint);

// Debugging
if ( oABE === 0 ) console.error(`Triangle ${triangle.id} collinear to this edge at ${otherEndpoint.x},${otherEndpoint.y}`, this);

if ( orient2d(a, b, otherEndpoint) > 0 ) this.ccwTriangle = triangle;
else this.cwTriangle = triangle;
}
Expand All @@ -215,6 +243,7 @@ export class BorderEdge {
vertexBlocks(vertexKey, elevation = 0) {
const iter = this.sharedVertexEdges(vertexKey);
for ( const edge of iter ) {
// if ( !edge.ccwTriangle || !edge.cwTriangle ) console.warn("vertexBlocks|Edge triangles not defined."); // Debugging.
if ( edge === this ) continue; // Could break here b/c this edge implicitly is always last.
if ( edge.edgeBlocks(undefined, elevation) ) return true;
}
Expand Down Expand Up @@ -247,9 +276,24 @@ export class BorderEdge {
* @param {string} [direction] Either ccw or c.
* @returns {BorderEdge}
*/
_nextEdge(vertexKey, dir = "ccw") {
_nextEdge(vertexKey, dir = "ccw", _recurse = true) {
const tri = this.findTriangleFromVertexKey(vertexKey, dir);
return Object.values(tri.edges).find(e => e !== this && e.endpointKeys.has(vertexKey));
if ( tri ) return Object.values(tri.edges).find(e => e !== this && e.endpointKeys.has(vertexKey));

// Edge is at a border, vertex at the corner of the border.
// Need to run the opposite direction until we get undefined in that direction.
if ( !_recurse ) return null;
const maxIter = 100;
let iter = 0;
let edge = this;
let prevEdge;
const otherDir = OTHER_DIRECTION[dir];
do {
prevEdge = edge;
iter += 1;
edge = prevEdge._nextEdge(vertexKey, otherDir, false);
} while ( iter < maxIter && edge && edge !== this );
return prevEdge;
}

/**
Expand Down
Loading

0 comments on commit fde550f

Please sign in to comment.