Skip to content

Commit

Permalink
Merge branch 'release/0.9.6' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Jun 30, 2024
2 parents b724f17 + 191b985 commit b22dcd7
Show file tree
Hide file tree
Showing 16 changed files with 697 additions and 465 deletions.
13 changes: 13 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# 0.9.6
Added `CONFIG.elevationruler.tokenPathfindingBuffer`. This defaults to -1, allowing movement diagonally when at the corner of a large token and "Tokens Block" is set. For pathfinding, more negative numbers shrink the token border; positive numbers increase it. Note that if you change this, you will need to reload the scene or at least move the tokens before their pathfinding borders will be changed. Closes #88.
Move calculation of speed colors to `Ruler#_highlightMeasurementSegment`. As a result, Ruler segments are not split at movement speed category changes, so there are no longer extra waypoints added.
Remove settings related to determining when a terrain affects a token. Currently, Foundry regions only checks the center point.
For dnd5e, remove the Token HUD control to select movement, because dnd5e now uses status effects to signify when tokens are flying or burrowing. Added a setting to automatically determine token movement for dnd5e.
Updated Polish translation. Thanks @Lioheart! Closes #105.
Added `SCENE.BACKGROUND` to flags (imported from Terrain Mapper). Closes #107.
Don't broadcast the Token Ruler if the token being dragged is secret, invisible, or hidden. Closes #112.
Consolidate speed attributes to a single file.
Add ARS speed definitions. Closes #111.
Add sfrpg speed definitions.
Add a5e speed definitions.

# 0.9.5
Added Brazilian translation. Thanks @Kharmans!
Added Russian translation. Thanks @VirusNik21! Closes #96.
Expand Down
3 changes: 3 additions & 0 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
"elevationruler.settings.grid-terrain-area-threshold.name": "Percent Area Threshold",
"elevationruler.settings.grid-terrain-area-threshold.hint": "If 'Percent Area' is selected for Terrain Grid Measurement, this determines the percent overlap required for the terrain/token to count when measuring on grids.",

"elevationruler.settings.automatic-movement-type.name": "Autodetect Movement Type",
"elevationruler.settings.automatic-movement-type.hint": "Automatically detect the movement type based on token position. Set the token status effect to 'fly' or 'burrowing' to override.",

"elevationruler.controls.prefer-token-elevation.name": "Prefer Token Elevation",
"elevationruler.controls.pathfinding-control.name": "Use Pathfinding",

Expand Down
13 changes: 11 additions & 2 deletions languages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
"elevationruler.keybindings.incrementElevation.name": "Zwiększenie wysokości linijki",
"elevationruler.keybindings.incrementElevation.hint": "Zwiększa wysokość w odstępach siatki podczas korzystania z linijki.",

"elevationruler.keybindings.addWaypoint.name": "Dodanie punktu trasy zwykłej linijki",
"elevationruler.keybindings.addWaypoint.hint": "Podczas korzystania z linijki na płótnie dodaj punkt trasy.",

"elevationruler.keybindings.removeWaypoint.name": "Usunięcie punktu trasy zwykłej linijki",
"elevationruler.keybindings.removeWaypoint.hint": "Podczas korzystania z linijki na płótnie usuń punkt trasy.",

"elevationruler.keybindings.addWaypointTokenRuler.name": "Dodanie punktu trasy dla tokena",
"elevationruler.keybindings.addWaypointTokenRuler.hint": "Gdy linijka tokena jest włączona, dodaje punkt trasy podczas przeciągania tokena.",
"elevationruler.keybindings.addWaypointTokenRuler.hint": "Gdy linijka tokenów jest włączona i przeciągasz token, dodaj punkt trasy.",

"elevationruler.keybindings.removeWaypointTokenRuler.name": "Usunięcie punktu trasy tokena",
"elevationruler.keybindings.removeWaypointTokenRuler.hint": "Gdy linijka tokena jest włączona, usuwa punkt trasy podczas przeciągania tokena.",
"elevationruler.keybindings.removeWaypointTokenRuler.hint": "Gdy linijka tokenów jest włączona i przeciągasz token, usuń punkt trasy.",

"elevationruler.keybindings.togglePathfinding.name": "Tymczasowe wyłączenie wyszukiwania ścieżek",
"elevationruler.keybindings.togglePathfinding.hint": "Jeśli przycisk wyszukiwania ścieżki jest włączony, przytrzymanie tego przycisku spowoduje tymczasowe wyłączenie wyszukiwania ścieżki. Jeśli przycisk odnajdywania ścieżek nie jest włączony, przytrzymanie tego klawisza tymczasowo włączy odnajdywanie ścieżek.",
Expand All @@ -33,6 +39,9 @@
"elevationruler.settings.enable-token-ruler.name": "Używanie linijki tokenów",
"elevationruler.settings.enable-token-ruler.hint": "Wyświetlanie linijki podczas przeciągania tokenów.",

"elevationruler.settings.hide-gm-ruler.name": "Ukryj linijkę MG",
"elevationruler.settings.hide-gm-ruler.hint": "Nie wyświetlaj linijki tokena MG innym użytkownikom niebędącym MG.",

"elevationruler.settings.speed-highlighting-choice.name": "Używanie podświetlenia prędkości tokenów",
"elevationruler.settings.speed-highlighting-choice.hint": "Podczas korzystania z linijki, używaj różnych kolorów dla tokenów chodu/dystansu/maksymalnego dystansu.",

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.325"
"verified": "12.328"
},
"authors": [
{
Expand Down
4 changes: 2 additions & 2 deletions scripts/MovePenalty.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,10 @@ export class TerrainMovePenaltyGridless extends MovePenaltyGridless {

export class MovePenaltyGridded extends MovePenalty {
/** @type {Settings.KEYS.GRID_TERRAIN.CHOICES} */
static get griddedAlgorithm() { return Settings.get(Settings.KEYS.GRID_TERRAIN.ALGORITHM); }
static get griddedAlgorithm() { return "grid-terrain-choice-center-point"; } // return Settings.get(Settings.KEYS.GRID_TERRAIN.ALGORITHM); }

/** @type {number} */
static get percentAreaThreshold() { return Settings.get(Settings.KEYS.GRID_TERRAIN.AREA_THRESHOLD); }
static get percentAreaThreshold() { return 0.5; } // return Settings.get(Settings.KEYS.GRID_TERRAIN.AREA_THRESHOLD); }

/**
* Returns a penalty function for gridded moves.
Expand Down
194 changes: 15 additions & 179 deletions scripts/Ruler.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* globals
canvas,
CONFIG,
CONST,
game,
PIXI,
Ruler,
Expand All @@ -17,7 +16,6 @@ PATCHES.SPEED_HIGHLIGHTING = {};
import { SPEED, MODULE_ID, FLAGS } from "./const.js";
import { Settings } from "./settings.js";
import { Ray3d } from "./geometry/3d/Ray3d.js";
import { Point3d } from "./geometry/3d/Point3d.js";
import {
elevationFromWaypoint,
elevationAtWaypoint,
Expand All @@ -40,8 +38,6 @@ import { PhysicalDistance } from "./PhysicalDistance.js";

import { MoveDistance } from "./MoveDistance.js";

import { gridShape, pointFromGridCoordinates, canvasElevationFromCoordinates } from "./grid_coordinates.js";

/**
* Modified Ruler
* Measure elevation change at each waypoint and destination.
Expand Down Expand Up @@ -159,7 +155,7 @@ function _addWaypoint(point, {snap=true}={}) {
// Determine the elevation up until this point
if ( !this.waypoints.length ) {
waypoint._prevElevation = this.token?.elevationE ?? canvas.scene.getFlag("terrainmapper", FLAGS.SCENE.BACKGROUND_ELEVATION) ?? 0;
waypoint._forceToGround ||= this.token.movementType === "WALK"
waypoint._forceToGround ||= this.token ? this.token.movementType === "WALK" : false;
} else waypoint._prevElevation = elevationFromWaypoint(this.waypoints.at(-1), waypoint, this.token);

this.waypoints.push(waypoint);
Expand Down Expand Up @@ -274,7 +270,6 @@ function _computeDistance() {

// Determine the distance of each segment.
_computeSegmentDistances.call(this);
_computeTokenSpeed.call(this); // Always compute speed if there is a token b/c other users may get to see the speed.

if ( debug ) {
switch ( this.segments.length ) {
Expand Down Expand Up @@ -334,7 +329,7 @@ function _computeSegmentDistances() {
this.segments.at(-1).last = true;
}
for ( const segment of this.segments ) {
numPrevDiagonal = _measureSegment(segment, token, numPrevDiagonal);
numPrevDiagonal = measureSegment(segment, token, numPrevDiagonal);
totalDistance += segment.distance;
totalMoveDistance += segment.moveDistance;
}
Expand All @@ -351,7 +346,7 @@ function _computeSegmentDistances() {
* @param {number} [numPrevDiagonal=0] Number of previous diagonals for the segment
* @returns {number} numPrevDiagonal
*/
function _measureSegment(segment, token, numPrevDiagonal = 0) {
export function measureSegment(segment, token, numPrevDiagonal = 0) {
segment.numPrevDiagonal = numPrevDiagonal;
const res = MoveDistance.measure(
segment.ray.A,
Expand All @@ -370,181 +365,23 @@ function _measureSegment(segment, token, numPrevDiagonal = 0) {
// Also need to handle array of speed points.
// Need CONFIG function that takes a token and gives array of speeds with colors.

/**
* Incrementally add together all segments. Split segment(s) at SpeedCategory maximum distances.
* Mark each segment with the distance, move distance, and SpeedCategory name.
* Does not assume segments have measurements, and modifies existing measurements.
* Segments modified in place.
*/
function _computeTokenSpeed() {
// Requires a movement token and a defined token speed.
const token = this.token;
if ( !token ) return;

// Precalculate the token speed.
const tokenSpeed = SPEED.tokenSpeed(token);
if ( !tokenSpeed ) return;

// Other constants
const gridless = canvas.grid.type === CONST.GRID_TYPES.GRIDLESS;

// Variables changed in the loop
let totalDistance = 0;
let totalMoveDistance = 0;
let totalCombatMoveDistance = 0;
let minDistance = 0;
let numPrevDiagonal = 0;
let s = 0;
let segment;

// Debugging
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].values();
let speedCategory = categoryIter.next().value;
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
if ( game.combat?.started
&& Settings.get(Settings.KEYS.SPEED_HIGHLIGHTING.COMBAT_HISTORY) ) {

totalCombatMoveDistance = token.lastMoveDistance;
minDistance = totalCombatMoveDistance;
}

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

segment.speed = speedCategory;
let newPrevDiagonal = _measureSegment(segment, token, numPrevDiagonal);

// If we have exceeded maxDistance, determine if a split is required.
const newDistance = totalCombatMoveDistance + segment.moveDistance;

if ( newDistance > maxDistance || newDistance.almostEqual(maxDistance ) ) {
if ( newDistance > maxDistance ) {
// Split the segment, inserting the latter portion in the queue for future iteration.
const splitDistance = maxDistance - totalCombatMoveDistance;
const breakpoint = locateSegmentBreakpoint(segment, splitDistance, token, gridless);
if ( breakpoint ) {
const segments = _splitSegmentAt(segment, breakpoint);
this.segments.splice(s, 1, segments[0]); // Delete the old segment, replace.
this.segments.splice(s + 1, 0, segments[1]); // Add the split.
segment = segments[0];
newPrevDiagonal = _measureSegment(segment, token, numPrevDiagonal);
}
}

// Increment to the next speed category.
// Next category will be selected in the while loop above: first category to exceed minDistance.
minDistance = maxDistance;
}

// Increment totals.
s += 1;
totalDistance += segment.distance;
totalMoveDistance += segment.moveDistance;
totalCombatMoveDistance += segment.moveDistance;
numPrevDiagonal = newPrevDiagonal;
}

this.totalDistance = totalDistance;
this.totalMoveDistance = totalMoveDistance;
}

/**
* Determine the specific point at which to cut a ruler segment such that the first subsegment
* measures a specific incremental move distance.
* @param {RulerMeasurementSegment} segment Segment, with ray property, to split
* @param {number} incrementalMoveDistance Distance, in grid units, of the desired first subsegment move distance
* @param {Token} token Token to use when measuring move distance
* @returns {Point3d|null}
* If the incrementalMoveDistance is less than 0, returns null.
* If the incrementalMoveDistance is greater than segment move distance, returns null
* Otherwise returns the point at which to break the segment.
* Mixed wrap Ruler#_broadcastMeasurement
* For token ruler, don't broadcast the ruler if the token is invisible or disposition secret.
*/
function locateSegmentBreakpoint(segment, splitMoveDistance, token, gridless) {
if ( splitMoveDistance <= 0 ) return null;
if ( !segment.moveDistance || splitMoveDistance > segment.moveDistance ) return null;

// Attempt to move the split distance and determine the split location.
const { A, B } = segment.ray;
const res = Ruler.measureMoveDistance(A, B,
{ token, gridless, useAllElevation: false, stopTarget: splitMoveDistance });

let breakpoint = pointFromGridCoordinates(res.endGridCoords); // We can get the exact split point.
if ( !gridless ) {
// We can get the end grid.
// Use halfway between the intersection points for this grid shape.
breakpoint = Point3d.fromObject(segmentGridHalfIntersection(breakpoint, A, B) ?? A);
if ( breakpoint === A ) breakpoint.z = A.z;
else breakpoint.z = canvasElevationFromCoordinates(res.endGridCoords);
}

if ( breakpoint.almostEqual(B) || breakpoint.almostEqual(A) ) return null;
return breakpoint;
function _broadcastMeasurement(wrapped) {
// Don't broadcast invisible, hidden, or secret token movement when dragging.
if ( this._isTokenRuler && !this.token ) return;
if ( this._isTokenRuler
&& (this.token.document.disposition === CONST.TOKEN_DISPOSITIONS.SECRET
|| this.token.document.hasStatusEffect(CONFIG.specialStatusEffects.INVISIBLE)
|| this.token.document.isHidden) ) return;

wrapped();
}

/**
* Cut a ruler segment at a specified point. Does not remeasure the resulting segments.
* Assumes without testing that the breakpoint lies on the segment between A and B.
* @param {RulerMeasurementSegment} segment Segment, with ray property, to split
* @param {Point3d} breakpoint Point to use when splitting the segments
* @returns [RulerMeasurementSegment, RulerMeasurementSegment]
*/
function _splitSegmentAt(segment, breakpoint) {
const { A, B } = segment.ray;

// Split the segment into two at the break point.
const s0 = {...segment};
s0.ray = new Ray3d(A, breakpoint);
s0.distance = null;
s0.moveDistance = null;
s0.numDiagonal = null;

const s1 = {...segment};
s1.ray = new Ray3d(breakpoint, B);
s1.distance = null;
s1.moveDistance = null;
s1.numPrevDiagonal = null;
s1.numDiagonal = null;
s1.speed = null;

if ( segment.first ) { s1.first = false; }
if ( segment.last ) { s0.last = false; }
return [s0, s1];
}

/**
* For a given segment, locate its intersection at a grid shape.
* The intersection point is on the segment, halfway between the two intersections for the shape.
* @param {number[]} gridCoords
* @param {PIXI.Point} a
* @param {PIXI.Point} b
* @returns {PIXI.Point|undefined} Undefined if no intersection. If only one intersection, the
* endpoint contained within the shape.
*/
function segmentGridHalfIntersection(gridCoords, a, b) {
const shape = gridShape(gridCoords);
const ixs = shape.segmentIntersections(a, b);
if ( !ixs || ixs.length === 0 ) return null;
if ( ixs.length === 1 ) return shape.contains(a.x, a.y) ? a : b;
return PIXI.Point.midPoint(ixs[0], ixs[1]);
}


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

/**
Expand Down Expand Up @@ -588,7 +425,7 @@ PATCHES.BASIC.WRAPS = {
_onMoveKeyDown
};

PATCHES.BASIC.MIXES = { _animateMovement, _getMeasurementSegments };
PATCHES.BASIC.MIXES = { _animateMovement, _getMeasurementSegments, _broadcastMeasurement };

PATCHES.BASIC.OVERRIDES = { _computeDistance, _animateSegment, _addWaypoint };

Expand Down Expand Up @@ -656,7 +493,6 @@ async function teleport(_context) {
PATCHES.BASIC.METHODS = {
incrementElevation,
decrementElevation,
_computeTokenSpeed,
teleport
};

Expand Down
Loading

0 comments on commit b22dcd7

Please sign in to comment.