Skip to content

Commit

Permalink
Merge branch 'release/0.10.15' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Nov 2, 2024
2 parents 35d21b5 + b7dff22 commit 87956db
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 100 deletions.
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.10.15
Fix for multiple token dragging resulting in token moving through a wall. Closes #224.
Correct error with Pathfinding when Wall Height 6.1.0 is active. Pathfinding will now assume vaulting is enabled if Wall Height is present. Closes #223. Thanks @drcormier!
Add speed method to handle dnd5e group actor speed (air, land). Closes #221.
Correct ruler measurement in gridless scenes after waypoint is placed. Closes #220.
Fix display of other user's ruler distances. Closes #219.

# 0.10.14
Fix for NaN in the move penalty calculation when moving over regions.

Expand Down
44 changes: 42 additions & 2 deletions scripts/Ruler.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ function _getMeasurementSegments(wrapped) {
this.segments ??= [];
for ( const s of this.segments ) {
if ( !s.label ) continue; // Not every segment has a label.
s.label = this.labels.children[labelIndex++];
s.label = this.labels.children[labelIndex++] ?? this.labels.addChild(new PreciseText("", CONFIG.canvasTextStyle));
}
return this.segments;
}
Expand Down Expand Up @@ -596,7 +596,7 @@ async function _animateMovement(wrapped, token) {
const promises = [wrapped(token)];
for ( const controlledToken of canvas.tokens.controlled ) {
if ( controlledToken === token ) continue;
if ( !(this.user.isGM || this._canMove(controlledToken)) ) {
if ( !(this.user.isGM || testMovement.call(this, controlledToken)) ) {
ui.notifications.error(`${game.i18n.localize("RULER.MovementNotAllowed")} for ${controlledToken.name}`);
continue;
}
Expand All @@ -610,6 +610,23 @@ async function _animateMovement(wrapped, token) {
return Promise.allSettled(promises);
}

/**
* Helper to catch movement errors. Must use "call" or "bind" to bind it to the Ruler instance.
*/
function testMovement(token) {
let error;
try {
if ( !this._canMove(token) ) error = "RULER.MovementNotAllowed";
} catch(err) {
error = err.message;
}
if ( error ) {
ui.notifications.error(error, {localize: true});
return false;
}
return true;
}

/**
* Wrap Ruler.prototype._getMeasurementHistory
* Store the history temporarily in the token.
Expand Down Expand Up @@ -656,6 +673,29 @@ function _createMeasurementHistory(wrapped) {
*/
function _canMove(wrapper, token) {
if ( this.user.isGM ) return true;
if ( !wrapper(token) ) return false;

// Adjust each segment for the difference from the original token position.
// Important when dragging multiple tokens.
// See Ruler#_animateMovement
const origin = this.segments[this.history.length].ray.A;
const dx = token.document.x - origin.x;
const dy = token.document.y - origin.y;
let {x, y} = token.document._source;
for ( const segment of this.segments ) {
if ( segment.history || (segment.ray.distance === 0) ) continue;
const r = segment.ray;
const adjustedDestination = {x: Math.round(r.B.x + dx), y: Math.round(r.B.y + dy)};
const a = token.getCenterPoint({x, y});
const b = token.getCenterPoint(adjustedDestination);
if ( token.checkCollision(b, {origin: a, type: "move", mode: "any"}) ) {
throw new Error("RULER.MovementCollision");
return false;
}
x = adjustedDestination.x;
y = adjustedDestination.y;
}

return wrapper(token);
}

Expand Down
10 changes: 5 additions & 5 deletions scripts/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@ export const FLAGS = {

// Track certain modules that complement features of this module.
export const OTHER_MODULES = {
TERRAIN_MAPPER: { KEY: "terrainmapper" },
TERRAIN_MAPPER: { KEY: "terrainmapper" },
LEVELS: { KEY: "levels" },
WALL_HEIGHT: { KEY: "wall-height", FLAGS: { VAULTING: "blockSightMovement" } }
}
WALL_HEIGHT: { KEY: "wall-height" }
};

// Hook init b/c game.modules is not initialized at start.
Hooks.once("init", function() {
for ( const obj of Object.values(OTHER_MODULES) ) obj.ACTIVE = game.modules.get(obj.KEY)?.active
for ( const obj of Object.values(OTHER_MODULES) ) obj.ACTIVE = game.modules.get(obj.KEY)?.active;
});

// API not necessarily available until ready hook. (Likely added at init.)
Hooks.once("ready", function() {
const tm = OTHER_MODULES.TERRAIN_MAPPER;
if ( tm.ACTIVE ) tm.API = game.modules.get(tm.KEY).api;
})
});


export const MOVEMENT_TYPES = {
Expand Down
12 changes: 7 additions & 5 deletions scripts/measurement/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ PATCHES_HexagonalGrid.BASIC = {};
* Wrap GridlessGrid#getDirectPath
* Returns the sequence of grid offsets of a shortest, direct path passing through the given waypoints.
* @param {RegionMovementWaypoint3d|GridCoordinates3d[]} waypoints The waypoints the path must pass through
* @returns {GridOffset[]} The sequence of grid offsets of a shortest, direct path
* @returns {GridCoordinates|GridCoordinates3d[]} The sequence of grid offsets of a shortest, direct path
* @abstract
*/
function getDirectPathGridless(wrapped, waypoints) {
const GridCoordinates = CONFIG.GeometryLib.GridCoordinates;
const offsets2d = wrapped(waypoints);
if ( !(waypoints[0] instanceof CONFIG.GeometryLib.threeD.Point3d) ) return offsets2d;
if ( !(waypoints[0] instanceof CONFIG.GeometryLib.threeD.Point3d) ) return offsets2d.map(o => GridCoordinates.fromOffset(o));

// 1-to-1 relationship between the waypoints and the offsets2d for gridless.
const GridCoordinates3d = CONFIG.GeometryLib.threeD.GridCoordinates3d;
Expand All @@ -48,13 +49,14 @@ function getDirectPathGridless(wrapped, waypoints) {
* Wrap HexagonalGrid#getDirectPath and SquareGrid#getDirectPath
* Returns the sequence of grid offsets of a shortest, direct path passing through the given waypoints.
* @param {Point3d[]} waypoints The waypoints the path must pass through
* @returns {GridOffset[]} The sequence of grid offsets of a shortest, direct path
* @returns {GridCoordinates|GridCoordinates3d[]} The sequence of grid offsets of a shortest, direct path
* @abstract
*/
function getDirectPathGridded(wrapped, waypoints) {
const { HexGridCoordinates3d, GridCoordinates3d } = CONFIG.GeometryLib.threeD;
const { HexGridCoordinates3d, GridCoordinates3d, Point3d } = CONFIG.GeometryLib.threeD;
const GridCoordinates = CONFIG.GeometryLib.GridCoordinates;
if ( !(waypoints[0] instanceof Point3d) ) return wrapped(waypoints).map(o => GridCoordinates.fromObject(o));

if ( !(waypoints[0] instanceof CONFIG.GeometryLib.threeD.Point3d) ) return wrapped(waypoints);
let prevWaypoint = GridCoordinates3d.fromObject(waypoints[0]);
const path3d = [];
const path3dFn = canvas.grid.isHexagonal ? HexGridCoordinates3d._directPathHex : GridCoordinates3d._directPathSquare;
Expand Down
9 changes: 6 additions & 3 deletions scripts/measurement/MovePenalty.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ export class MovePenalty {
if ( this.#penaltyCache.has(key) ) {
const res = this.#penaltyCache.get(key);
log(`Using key ${key}: ${res}`);
console.groupEnd("movementCostForSegment");
if ( CONFIG[MODULE_ID].debug ) console.groupEnd("movementCostForSegment");
return res;
}

Expand All @@ -294,7 +294,10 @@ export class MovePenalty {
const isOneStep = Math.abs(endCoords.i - startCoords.i) < 2
&& Math.abs(endCoords.j - startCoords.j) < 2
&& Math.abs(endCoords.k - startCoords.k) < 2;
if ( isOneStep ) return this.movementCostForGridSpace(endCoords, costFreeDistance);
if ( isOneStep ) {
if ( CONFIG[MODULE_ID].debug ) console.groupEnd("movementCostForSegment");
return this.movementCostForGridSpace(endCoords, costFreeDistance);
}

// Unlikely scenario where endCoords are more than 1 step away from startCoords.
let totalCost = 0;
Expand All @@ -316,7 +319,7 @@ export class MovePenalty {
this.#penaltyCache.set(key, res);
const t1 = performance.now();
log(`Found cost ${res} in ${Math.round(t1 - t0)} ms`);
console.groupEnd("movementCostForSegment");
if ( CONFIG[MODULE_ID].debug ) console.groupEnd("movementCostForSegment");
return res;
}

Expand Down
4 changes: 2 additions & 2 deletions scripts/pathfinding/WallTracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export class WallTracerEdge extends GraphEdge {

// If Wall Height vaulting is enabled, walls less than token vision height do not block.
const wh = OTHER_MODULES.WALL_HEIGHT;
if ( wh.ACTIVE && game.settings.get(wh.KEY, wh.FLAGS.VAULTING) && moveToken.visionZ >= wall.topZ ) return false;
if ( wh.ACTIVE && moveToken.visionZ >= wall.topZ ) return false;
return true;
}

Expand All @@ -408,7 +408,7 @@ export class WallTracerEdge extends GraphEdge {
// Don't block dead tokens (HP <= 0).
const { tokenHPAttribute, pathfindingIgnoreStatuses } = CONFIG[MODULE_ID];
let tokenHP = Number(foundry.utils.getProperty(token, tokenHPAttribute));

//DemonLord using damage system
if ( game.system.id === 'demonlord')
{
Expand Down
Loading

0 comments on commit 87956db

Please sign in to comment.