diff --git a/Changelog.md b/Changelog.md index 784adcb..e241f00 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +# 0.10.7 +Switch to using Bresenham 3d algorithm to determine 3d grid path for square grids. Use Bresenham 4d algorithm to determine 3d grid path for hex grids (hex-cube dimensions + elevation dimension). Closes #194. +For pathfinding, don't consider walls blocking if the wall top elevation equals the token elevation (but still blocks if wall bottom elevation equals token elevation). +If Wall Height module is active and vaulting is enabled, ignore walls within the vaulting height. +Update Italian localization. Thanks @GregoryWarn! +Update Brazilian Portugese translation. Thanks @Kharmans! +Update libGeometry to v0.3.11. + # 0.10.6 ## New Features Setting to apply movement penalties per-grid-space. Defaults to enabled. For gridded scenes, this will (1) count regions/tokens/drawings as imposing movement penalty only if the region/token/drawing overlaps the center point of the grid space and (2) impose the penalty at a grid-space level. If disabled, this will proportionally apply the penalty based on the precise movement path. Closes #181. diff --git a/languages/it.json b/languages/it.json index 099dd85..f75e4a9 100644 --- a/languages/it.json +++ b/languages/it.json @@ -76,9 +76,12 @@ "elevationruler.settings.round-to-multiple.name": "Arrotonda Distanza ad un multiplo", "elevationruler.settings.round-to-multiple.hint": "Per la misurazione della distanza senza griglia, arrotondare la distanza visualizzata al multiplo più vicino del numero immesso. Impostare su 0 per disabilitare l'arrotondamento.", - "elevationruler.settings.token-terrain-multiplier.name": "Token come moltiplicatore terreno", - "elevationruler.settings.token-terrain-multiplier.hint": "Moltiplicatore da utilizzare per calcolare la velocità di movimento quando ci si sposta attraverso altri token. Impostato su 1 per ignorare. I valori inferiori a 1 trattano gli spazi dei segnalini come più veloci del normale; valori maggiori di 1 penalizzano il movimento attraverso gli spazi dei token.", + "elevationruler.settings.token-terrain-multiplier.name": "Percentuale Penalità Token", + "elevationruler.settings.token-terrain-multiplier.hint": "Penalizza il movimento attraverso altri token. Impostato a 1 per nessuna penalità, inferiore a 1 per garantire un bonus, maggiore di 1 per imporre un moltiplicatore di penalità. Ad esempio, imposta su 2 per raddoppiare il costo di movimento attraverso un token.", + "elevationruler.settings.token-terrain-multiplier-flat.name": "Penalità Token Fissa", + "elevationruler.settings.token-terrain-multiplier-flat.hint": "Se abilitato, considera la penalità di movimento del token come una quantità fissa di distanza aggiuntiva. Ad esempio, imposta su 5 per aggiungere +5 a ciascun quadrato di movimento. I valori negativi forniscono un bonus fisso.", + "elevationruler.settings.pathfinding_enable.name": "Usa Trova Percorso", "elevationruler.settings.pathfinding_enable.hint": "Quando abilitato, aggiunge un tasto Trova percorso al controllo Token che farà sì che il righello mappi un percorso attorno a muri e token a seconda delle impostazioni. Disabilita se Trova Percorso causa problemi di compatibilità. La disabilitazione può anche comportare un piccolo aumento delle prestazioni.", @@ -96,6 +99,7 @@ "elevationruler.settings.grid-terrain-algorithm.name": "Misurazione Griglia Terreno", "elevationruler.settings.grid-terrain-algorithm.hint": "Quando sei su una griglia, come tenere conto delle penalità di movimento o dei bonus derivanti dal terreno e dai token? Centro: si applica se il terreno/token si sovrappone al centro della griglia; Area percentuale: si applica se il terreno/token copre almeno questa parte del quadrato/esagono della griglia; Euclideo: ripartizione proporzionale in base alla percentuale del segmento di linea all'interno del terreno/token tra questo quadrato/esagono della griglia e il precedente.", + "elevationruler.settings.grid-terrain-choice-center-point": "Centro", "elevationruler.settings.grid-terrain-choice-percent-area": "Area Percentuale", "elevationruler.settings.grid-terrain-choice-euclidean": "Euclideo", @@ -112,15 +116,21 @@ "elevationruler.settings.scale-text.name": "Testo Scala Righello", "elevationruler.settings.scale-text.hint": "Se abilitato, il testo del righello verrà ridimensionato in base a 'canvas.dimensions.size'. Regola ulteriormente il ridimensionamento utilizzando `CONFIG.elevationruler.textScale`.", + "elevationruler.settings.force-grid-penalties.name": "Usa Griglia per Penalità Movimento", + "elevationruler.settings.force-grid-penalties.hint": "Se abilitate, le penalità di movimento si applicano solo se il token/disegno/regione copre il centro dell'esagono/quadrato e le penalità vengono applicate per quadrato o per esagono sulle mappe a griglia. Quando disabilitato, le penalità al movimento vengono applicate proporzionalmente al movimento. Ad esempio, se un disegno con una penalità di movimento x2 copre 3/4 di un quadrato della griglia, si applicherà a 3/4 del movimento attraverso quel quadrato (ovvero penalità 1,5x).", + "elevationruler.settings.customized-labels.name": "Testo Righello Personalizzato", "elevationruler.settings.customized-labels.hint": "Personalizza il testo del righello. Modifica gli stili utilizzando `CONFIG.elevationruler.labelStyles` e `CONFIG.elevationruler.labelIcons`.", "elevationruler.controls.prefer-token-elevation.name": "Preferisci elevazione token", "elevationruler.controls.pathfinding-control.name": "Trova Percorso", - "elevationruler.drawingconfig.movementPenalty.name": "Bonus/Penalità Movimento", + "elevationruler.drawingconfig.movementPenalty.name": "Percentuale Penalità Movimento", "elevationruler.drawingconfig.movementPenalty.hint": "Impostato a 1 senza penalità. Valori maggiori di uno penalizzano il movimento di quella percentuale; valori inferiori a uno garantiscono effettivamente un bonus al movimento. Ad esempio, impostalo su 2 per raddoppiare il movimento in quest'area. Il movimento sotto l'elevazione del disegno verrà ignorato.", - + + "elevationruler.drawingconfig.flatMovementPenalty.name": "Usa Penalità Fissa", + "elevationruler.drawingconfig.flatMovementPenalty.hint": "Se abilitato, considera la penalità come un importo fisso di distanza aggiuntiva. Ad esempio, imposta su 5 per aggiungere +5 a ciascun quadrato di movimento. I valori negativi forniscono un bonus fisso.", + "elevationruler.clearMovement": "Cancella Movimento Combattente", "elevationruler.waypoint": "Punto di passaggio", "elevationruler.up": "Su", diff --git a/languages/pt-BR.json b/languages/pt-BR.json index 34be1c2..3684d6b 100644 --- a/languages/pt-BR.json +++ b/languages/pt-BR.json @@ -52,7 +52,7 @@ "elevationruler.settings.speed-highlighting-choice-combat": "Apenas em Combate", "elevationruler.settings.speed-highlighting-choice-always": "Sempre", - "elevationruler.settings.speed-highlighting-no-hostiles.name": "Sem Destaque de Velocidade de Hostis", + "elevationruler.settings.speed-highlighting-no-hostiles.name": "Sem Destaque de Velocidade de Hostis", "elevationruler.settings.speed-highlighting-no-hostiles.hint": "Quando ativado, o usuário não pode ver a velocidade de um token hostil ou secreto, a menos que o usuário tenha permissões de Observador ou Dono.", "elevationruler.settings.token-ruler-combat-history.name": "Rastrear Movimento de Combate", @@ -79,6 +79,9 @@ "elevationruler.settings.token-terrain-multiplier.name": "Token como Multiplicador de Terreno", "elevationruler.settings.token-terrain-multiplier.hint": "Multiplicador a ser usado para calcular a velocidade de movimento ao passar por outros tokens. Defina como 1 para ignorar. Valores menores que 1 tratam os espaços ocupados por tokens como mais rápidos que o normal; valores maiores que 1 penalizam o movimento através de espaços ocupados por tokens.", + "elevationruler.settings.token-terrain-multiplier-flat.name": "Penalidade Fixa do Token", + "elevationruler.settings.token-terrain-multiplier-flat.hint": "Se ativado, trata a penalidade de movimento do token como uma quantidade fixa de distância adicional. Por exemplo, defina 5 para adicionar +5 a cada quadrado de movimento. Valores negativos fornecem um bônus fixo.", + "elevationruler.settings.pathfinding_enable.name": "Usar Descoberta de Rota", "elevationruler.settings.pathfinding_enable.hint": "Quando ativado, adiciona um botão de descoberta de rota aos controles de Token, que fará com que a régua mapeie um caminho ao redor de paredes e tokens, dependendo das configurações. Desative isto se a descoberta de rota estiver causando problemas de compatibilidade. A desativação também pode resultar em um pequeno aumento de desempenho.", @@ -96,6 +99,7 @@ "elevationruler.settings.grid-terrain-algorithm.name": "Medição de Grade de Terreno", "elevationruler.settings.grid-terrain-algorithm.hint": "Quando estiver em uma grade, como contabilizar penalidades de movimento ou bônus de terreno e tokens? Centro: aplica se o terreno/token se sobrepõe ao centro da grade; Percentual de Área: aplica se o terreno/token cobrir pelo menos esta parte do quadrado/hexágono da grade; Euclidiano: rateado com base na porcentagem do segmento de linha dentro do terreno/token entre esta grade quadrada/hexágono e o anterior.", + "elevationruler.settings.grid-terrain-choice-center-point": "Centro", "elevationruler.settings.grid-terrain-choice-percent-area": "Percentual de Área", "elevationruler.settings.grid-terrain-choice-euclidean": "Euclideano", @@ -112,6 +116,9 @@ "elevationruler.settings.scale-text.name": "Texto da Régua de Escala", "elevationruler.settings.scale-text.hint": "Quando ativado, o texto da régua será dimensionado dependendo do `canvas.dimensions.size`. Ajuste ainda mais a escala usando `CONFIG.elevationruler.textScale`.", + "elevationruler.settings.force-grid-penalties.name": "Usar Grade para Penalidades de Movimento", + "elevationruler.settings.force-grid-penalties.hint": "Quando ativado, as penalidades de movimento só se aplicam se o token/desenho/região cobrir o centro do hexágono/quadrado e as penalidades são aplicadas por quadrado ou por hexágono em mapas com grade. Quando desativado, as penalidades de movimento são aplicadas proporcionalmente ao movimento. Por exemplo, se um desenhor com uma penalidade de movimento x2 cobrir 3/4 de um quadrado da grade, ele será aplicado a 3/4 do movimento através desse quadrado (ou seja, penalidade de 1,5x).", + "elevationruler.settings.customized-labels.name": "Texto da Régua Personalizado", "elevationruler.settings.customized-labels.hint": "Personalize o texto da régua. Altere os estilos usando `CONFIG.elevationruler.labelStyles` e `CONFIG.elevationruler.labelIcons`.", @@ -121,6 +128,9 @@ "elevationruler.drawingconfig.movementPenalty.name": "Bônus/Penalidade de Movimento", "elevationruler.drawingconfig.movementPenalty.hint": "Defina como 1 para nenhuma penalidade. Valores superiores a um penalizam o movimento nessa percentagem; valores menores que um concedem efetivamente um bônus ao movimento. Por exemplo, defina como 2 para duplicar o movimento nesta área. Movimento abaixo da elevação do desenho será ignorado.", + "elevationruler.drawingconfig.flatMovementPenalty.name": "Usar Penalidade Fixa", + "elevationruler.drawingconfig.flatMovementPenalty.hint": "Se ativado, trata a penalidade como um valor fixo de distância adicional. Por exemplo, defina 5 para adicionar +5 a cada quadrado de movimento. Valores negativos fornecem um bônus fixo.", + "elevationruler.clearMovement": "Limpar Deslocamento do Combatente", "elevationruler.waypoint": "parada", "elevationruler.up": "acima", diff --git a/scripts/Ruler.js b/scripts/Ruler.js index ac96a5b..66a6002 100644 --- a/scripts/Ruler.js +++ b/scripts/Ruler.js @@ -15,7 +15,7 @@ export const PATCHES = {}; PATCHES.BASIC = {}; PATCHES.SPEED_HIGHLIGHTING = {}; -import { SPEED, MODULE_ID, MODULES_ACTIVE, MOVEMENT_TYPES } from "./const.js"; +import { SPEED, MODULE_ID, OTHER_MODULES, MOVEMENT_TYPES } from "./const.js"; import { Settings } from "./settings.js"; import { Ray3d } from "./geometry/3d/Ray3d.js"; import { @@ -344,7 +344,7 @@ function _getMeasurementSegments(wrapped) { pathPoints = calculatePathPointsForSegment(lastSegment, token); } - if ( MODULES_ACTIVE.TERRAIN_MAPPER ) { + if ( OTHER_MODULES.TERRAIN_MAPPER.ACTIVE ) { const t0 = performance.now(); // For now, determine movement type for each of the path points. PathPoints are {x, y} objects. // If no path points, use the segments. @@ -362,7 +362,7 @@ function _getMeasurementSegments(wrapped) { // Determine the region path. pathPoints.length = 0; - const ElevationHandler = MODULES_ACTIVE.API.TERRAIN_MAPPER.ElevationHandler; + const ElevationHandler = OTHER_MODULES.TERRAIN_MAPPER.API.ElevationHandler; let prevPt = initialPath[0]; pathPoints.push(prevPt); for ( let i = 1, n = initialPath.length; i < n; i += 1 ) { diff --git a/scripts/const.js b/scripts/const.js index 2b3cc5c..6befd5e 100644 --- a/scripts/const.js +++ b/scripts/const.js @@ -23,18 +23,24 @@ export const FLAGS = { MOVEMENT_HISTORY: "movementHistory" }; -export const MODULES_ACTIVE = { API: {} }; +// Track certain modules that complement features of this module. +export const OTHER_MODULES = { + TERRAIN_MAPPER: { KEY: "terrainmapper" }, + LEVELS: { KEY: "levels" }, + WALL_HEIGHT: { KEY: "wall-height", FLAGS: { VAULTING: "blockSightMovement" } } +} // Hook init b/c game.modules is not initialized at start. Hooks.once("init", function() { - MODULES_ACTIVE.LEVELS = game.modules.get("levels")?.active; - MODULES_ACTIVE.TERRAIN_MAPPER = game.modules.get("terrainmapper")?.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() { - if ( MODULES_ACTIVE.TERRAIN_MAPPER ) MODULES_ACTIVE.API.TERRAIN_MAPPER = game.modules.get("terrainmapper").api; -}); + const tm = OTHER_MODULES.TERRAIN_MAPPER; + if ( tm.ACTIVE ) tm.API = game.modules.get(tm.KEY).api; +}) + export const MOVEMENT_TYPES = { AUTO: -1, diff --git a/scripts/geometry b/scripts/geometry index 15bae42..c486651 160000 --- a/scripts/geometry +++ b/scripts/geometry @@ -1 +1 @@ -Subproject commit 15bae42613c94f943c66eb51fe42b70a4a8f3266 +Subproject commit c486651f4e970a8fddf011c274019411e9fab5b5 diff --git a/scripts/measurement/Grid.js b/scripts/measurement/Grid.js index 932d206..fc39507 100644 --- a/scripts/measurement/Grid.js +++ b/scripts/measurement/Grid.js @@ -8,6 +8,7 @@ game "use strict"; import { GridCoordinates3d } from "../geometry/3d/GridCoordinates3d.js"; +import { HexGridCoordinates3d } from "../geometry/3d/HexGridCoordinates3d.js"; import { Point3d } from "../geometry/3d/Point3d.js"; import { Settings } from "../settings.js"; @@ -71,84 +72,17 @@ function getDirectPathGridless(wrapped, waypoints) { * @param {GridOffset[]} [path2d] Optional path2d for the start and end waypoints. * @returns {GridCoordinates3d[]} */ -function directPath3dSquare(start, end, path2d) { - path2d ??= canvas.grid.getDirectPath([start.to2d(), end.to2d()]); - if ( start.z.almostEqual(end.z) ) { - const elev = start.elevation; - return path2d.map(pt => GridCoordinates3d.fromOffset(pt, elev)); - } - - const num2dMoves = path2d.length - 1; - const prevOffset = GridCoordinates3d.fromObject(start); - prevOffset.centerToOffset(); - prevOffset.i = path2d[0].i; - prevOffset.j = path2d[0].j; - - // The currOffset will be modified in the loop; set to end to get elevation steps now. - const currOffset = GridCoordinates3d.fromObject(end); - - // Do 1 elevation move for each 2d diagonal move. Spread out over the diagonal steps. - let num2dDiagonal = 0; - let prev = path2d[0]; - for ( let i = 1, n = path2d.length; i < n; i += 1 ) { - const curr = path2d[i]; - num2dDiagonal += ((prev.i !== curr.i) && (prev.j !== curr.j)); - prev = curr; - } - const elevationStepsRemaining = Math.abs(prevOffset.k - currOffset.k); - let doubleDiagonalElevationStepsRemaining = Math.min(num2dDiagonal, elevationStepsRemaining); - let doubleDiagonalElevationStep = 0; - const doDoubleDiagonalElevationStepMod = Math.ceil(num2dDiagonal / (doubleDiagonalElevationStepsRemaining + 1)); - - // Do 1 elevation move for each 2d non-diagonal move. Spread out over the non-diagonal steps. - const num2dStraight = num2dMoves - num2dDiagonal; - let diagonalElevationStepsRemaining = Math.min(elevationStepsRemaining - - doubleDiagonalElevationStepsRemaining, num2dStraight); - let diagonalElevationStep = 0; - const doDiagonalElevationStepMod = Math.ceil(num2dStraight / (diagonalElevationStepsRemaining + 1)); - - // Rest are all additional elevation-only moves. Spread out evenly. - let additionalElevationStepsRemaining = Math.max(0, - elevationStepsRemaining - diagonalElevationStepsRemaining - diagonalElevationStepsRemaining); - const doAdditionalElevationStepMod = Math.ceil(num2dMoves / (additionalElevationStepsRemaining + 1)); - - currOffset.k = prevOffset.k; // Begin with the starting elevation, incrementing periodically in the loop. - const path3d = [prevOffset.clone()]; - for ( let i = 1, stepsRemaining = num2dMoves, n = num2dMoves + 1; i < n; i += 1, stepsRemaining -= 1 ) { - currOffset.setOffset2d(path2d[i]); - - const is2dDiagonal = (currOffset.i !== prevOffset.i) && (currOffset.j !== prevOffset.j); - const doDoubleDiagonalElevationStep = is2dDiagonal - && doubleDiagonalElevationStepsRemaining > 0 - && ((doubleDiagonalElevationStep + 1) % doDoubleDiagonalElevationStepMod) === 0; - const doDiagonalElevationStep = !is2dDiagonal && diagonalElevationStepsRemaining > 0 - && ((diagonalElevationStep + 1) % doDiagonalElevationStepMod) === 0; - const doAdditionalElevationSteps = additionalElevationStepsRemaining > 0 - && ((i + 1) % doAdditionalElevationStepMod) === 0; - - // Either double or normal diagonals are the same but have separate tracking. - if ( doDoubleDiagonalElevationStep ) { - currOffset.k += 1; - doubleDiagonalElevationStepsRemaining -= 1; - doubleDiagonalElevationStep += 1; - } else if ( doDiagonalElevationStep ) { - currOffset.k += 1; - diagonalElevationStepsRemaining -= 1; - diagonalElevationStep += 1; - } - path3d.push(currOffset.clone()); - - if ( doAdditionalElevationSteps ) { - let elevationSteps = Math.ceil(additionalElevationStepsRemaining / stepsRemaining); - while ( elevationSteps > 0 ) { - currOffset.k += 1; - elevationSteps -= 1; - additionalElevationStepsRemaining -= 1; - path3d.push(currOffset.clone()); - } - } - prevOffset.setOffset(currOffset); - } +function directPath3dSquare(start, end) { + start = GridCoordinates3d.fromObject(start); + end = GridCoordinates3d.fromObject(end); + const points = CONFIG.GeometryLib.utils.bresenhamLine3d(start.i, start.j, start.k, end.i, end.j, end.k); + const path3d = [start]; + // Convert points to GridCoordinates3d. Start and end repeat; skip. + for ( let i = 3, n = points.length - 3; i < n; i += 3 ) path3d.push(GridCoordinates3d.fromOffset({ + i: points[i], + j: points[i + 1], + k: points[i + 2] })); + path3d.push(end); return path3d; } @@ -213,50 +147,22 @@ function singleOffsetSquareDistanceFn(numDiagonals = 0) { * @param {RegionMovementWaypoint3d} start * @param {RegionMovementWaypoint3d} end * @param {GridOffset[]} [path2d] Optional path2d for the start and end waypoints. - * @returns {GridCoordinates3d[]} + * @returns {HexGridCoordinates3d[]} */ -function directPath3dHex(start, end, path2d) { - path2d ??= canvas.grid.getDirectPath([start.to2d(), end.to2d()]); - if ( start.z.almostEqual(end.z) ) { - const elev = start.elevation; - return path2d.map(pt => GridCoordinates3d.fromOffset(pt, elev)); - } - - const num2dMoves = path2d.length - 1; - const startOffset = GridCoordinates3d.fromObject(start); - startOffset.centerToOffset(); - startOffset.i = path2d[0].i; - startOffset.j = path2d[0].j; - - // The currOffset will be modified in the loop; set to end to get elevation steps now. - const currOffset = GridCoordinates3d.fromObject(end); - - const path3d = [startOffset.clone()]; - let elevationStepsRemaining = Math.abs(startOffset.k - currOffset.k); - const doElevationStepMod = Math.ceil((num2dMoves) / (elevationStepsRemaining + 1)); - currOffset.k = startOffset.k; // Begin with the starting elevation, incrementing periodically in the loop. - for ( let i = 1, stepsRemaining = num2dMoves, n = num2dMoves + 1; i < n; i += 1, stepsRemaining -= 1 ) { - currOffset.setOffset2d(path2d[i]); - - const doElevationStep = ((i + 1) % doElevationStepMod) === 0; - let elevationSteps = doElevationStep - && (elevationStepsRemaining > 0) ? Math.ceil(elevationStepsRemaining / stepsRemaining) : 0; - elevationStepsRemaining -= elevationSteps; - - // Apply the first elevation step as a diagonal upwards move in combination with the canvas 2d move. - if ( elevationSteps ) { - currOffset.k += 1; - elevationSteps -= 1; - } - path3d.push(currOffset.clone()); - - // Add additional elevation-only moves as necessary. - while ( elevationSteps > 0 ) { - currOffset.k += 1; - elevationSteps -= 1; - path3d.push(currOffset.clone()); - } - } +function directPath3dHex(start, end) { + start = HexGridCoordinates3d.fromObject(start); + end = HexGridCoordinates3d.fromObject(end); + const points = CONFIG.GeometryLib.utils.bresenhamLine4d( + start.q, start.r, start.s, start.k, + end.q, end.r, end.s, end.k); + const path3d = [start]; + // Convert points to GridCoordinates3d. Start and end repeat; skip. + for ( let i = 4, n = points.length - 4; i < n; i += 4 ) path3d.push(HexGridCoordinates3d.fromHexCube({ + q: points[i], + r: points[i + 1], + s: points[i + 2], + k: points[i + 3] })); + path3d.push(end); return path3d; } @@ -412,14 +318,7 @@ function getDirectPathGridded(wrapped, waypoints) { const path3dFn = canvas.grid.isHexagonal ? directPath3dHex : directPath3dSquare; for ( let i = 1, n = waypoints.length; i < n; i += 1 ) { const currWaypoint = GridCoordinates3d.fromObject(waypoints[i]); - const path2d = wrapped([prevWaypoint, currWaypoint]); - - // Keep the exact start and end points, used by _measure to calculate distance. - const segments3d = path3dFn(prevWaypoint, currWaypoint, path2d); - segments3d[0].x = prevWaypoint.x; - segments3d[0].y = prevWaypoint.y; - segments3d.at(-1).x = currWaypoint.x; - segments3d.at(-1).y = currWaypoint.y; + const segments3d = path3dFn(prevWaypoint, currWaypoint); path3d.push(...segments3d); prevWaypoint = currWaypoint; } diff --git a/scripts/measurement/MovePenalty.js b/scripts/measurement/MovePenalty.js index 4310c6a..4fa58ad 100644 --- a/scripts/measurement/MovePenalty.js +++ b/scripts/measurement/MovePenalty.js @@ -7,7 +7,7 @@ PIXI /* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ "use strict"; -import { MODULE_ID, FLAGS, MODULES_ACTIVE, SPEED, MOVEMENT_TYPES } from "../const.js"; +import { MODULE_ID, FLAGS, OTHER_MODULES, SPEED, MOVEMENT_TYPES } from "../const.js"; import { Settings } from "../settings.js"; import { movementType } from "../token_hud.js"; import { log, keyForValue } from "../util.js"; @@ -234,8 +234,8 @@ export class MovePenalty { // Tokens const tokenMultiplier = this.constructor.tokenMultiplier; const useTokenFlat = this.constructor.useFlatTokenMultiplier; - if ( useTokenFlat ) flatPenalty += (tokenMultiplier * tokens.length); - else currentMultiplier *= (tokenMultiplier * tokens.length); + if ( useTokenFlat ) flatPenalty += (tokenMultiplier * tokens.length); // Default to 0. + else currentMultiplier *= (tokens.length ? (tokenMultiplier * tokens.length) : 1); // Default to 1. // Regions const testRegions = this.constructor.terrainAPI && regions.length; @@ -458,7 +458,7 @@ export class MovePenalty { static get useFlatTokenMultiplier() { return Settings.get(Settings.KEYS.MEASURING.TOKEN_MULTIPLIER_FLAT); } /** @type {object|undefined} */ - static get terrainAPI() { return MODULES_ACTIVE.API?.TERRAIN_MAPPER; } + static get terrainAPI() { return OTHER_MODULES.TERRAIN_MAPPER.API; } // ----- NOTE: Static methods ----- // diff --git a/scripts/pathfinding/BorderTriangle.js b/scripts/pathfinding/BorderTriangle.js index 7313aa8..731ea7b 100644 --- a/scripts/pathfinding/BorderTriangle.js +++ b/scripts/pathfinding/BorderTriangle.js @@ -198,7 +198,7 @@ export class BorderEdge { const { moveToken, tokenBlockType } = this.constructor; return this.objects.some(obj => { - if ( obj instanceof Wall ) return WallTracerEdge.wallBlocks(obj, origin, elevation); + if ( obj instanceof Wall ) return WallTracerEdge.wallBlocks(obj, origin, moveToken, elevation); if ( obj instanceof Token ) return WallTracerEdge.tokenEdgeBlocks(obj, moveToken, tokenBlockType, elevation); return false; }); diff --git a/scripts/pathfinding/WallTracer.js b/scripts/pathfinding/WallTracer.js index 1b67ecd..6053d86 100644 --- a/scripts/pathfinding/WallTracer.js +++ b/scripts/pathfinding/WallTracer.js @@ -19,7 +19,7 @@ import { Draw } from "../geometry/Draw.js"; import { Graph, GraphVertex, GraphEdge } from "../geometry/Graph.js"; import { Settings } from "../settings.js"; import { doSegmentsOverlap, IX_TYPES, segmentCollision } from "../geometry/util.js"; -import { MODULE_ID } from "../const.js"; +import { MODULE_ID, OTHER_MODULES, FLAGS } from "../const.js"; /* WallTracerVertex @@ -350,7 +350,7 @@ export class WallTracerEdge extends GraphEdge { */ edgeBlocks(origin, moveToken, tokenBlockType, elevation = 0) { return this.objects.some(obj => - (obj instanceof Wall) ? this.constructor.wallBlocks(obj, origin, elevation) + (obj instanceof Wall) ? this.constructor.wallBlocks(obj, origin, moveToken, elevation) : (obj instanceof Token) ? this.constructor.tokenEdgeBlocks(obj, moveToken, tokenBlockType, elevation) : false); } @@ -363,7 +363,7 @@ export class WallTracerEdge extends GraphEdge { * @param {number} [elevation=0] Elevation of the point or origin to test, in pixel units. * @returns {boolean} */ - static wallBlocks(wall, origin, elevation = 0) { + static wallBlocks(wall, origin, moveToken, elevation = 0) { if ( !wall.document.move || wall.isOpen ) return false; // Ignore one-directional walls which are facing away from the center @@ -378,9 +378,12 @@ export class WallTracerEdge extends GraphEdge { if ( wall.document.dir && side === wall.document.dir ) return false; - // Test for wall height. - if ( !elevation.between(wall.bottomZ, wall.topZ, false) ) return false; + // Test for wall height. If elevation at the wall bottom, wall blocks; if at wall top it does not. + if ( !elevation.between(wall.bottomZ, wall.topZ, false) && elevation !== wall.bottomZ ) return false; + // 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; return true; } diff --git a/scripts/segment_labels_highlighting.js b/scripts/segment_labels_highlighting.js index a4bf59f..2bf5203 100644 --- a/scripts/segment_labels_highlighting.js +++ b/scripts/segment_labels_highlighting.js @@ -7,7 +7,7 @@ PreciseText */ "use strict"; -import { MODULE_ID, MODULES_ACTIVE } from "./const.js"; +import { MODULE_ID, OTHER_MODULES } from "./const.js"; import { Settings } from "./settings.js"; import { perpendicularPoints, roundMultiple } from "./util.js"; @@ -37,7 +37,7 @@ export function highlightLineRectangle(segment, color, name) { * @returns {boolean} */ function useLevelsLabels() { - if ( !MODULES_ACTIVE.LEVELS ) return false; + if ( !OTHER_MODULES.LEVELS.ACTIVE ) return false; const labelOpt = Settings.get(Settings.KEYS.LABELING.USE_LEVELS_LABEL); return labelOpt === Settings.KEYS.LABELING.LEVELS_LABELS.ALWAYS || (labelOpt === Settings.KEYS.LABELING.LEVELS_LABELS.UI_ONLY && CONFIG.Levels.UI.rendered); diff --git a/scripts/settings.js b/scripts/settings.js index 2076fb7..6b2b2aa 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -7,7 +7,7 @@ ui */ "use strict"; -import { MODULE_ID, MODULES_ACTIVE } from "./const.js"; +import { MODULE_ID, OTHER_MODULES } from "./const.js"; import { ModuleSettingsAbstract } from "./ModuleSettingsAbstract.js"; import { log } from "./util.js"; import { SCENE_GRAPH } from "./pathfinding/WallTracer.js"; @@ -123,7 +123,7 @@ export class Settings extends ModuleSettingsAbstract { name: localize(`${KEYS.LABELING.USE_LEVELS_LABEL}.name`), hint: localize(`${KEYS.LABELING.USE_LEVELS_LABEL}.hint`), scope: "world", - config: MODULES_ACTIVE.LEVELS, + config: OTHER_MODULES.LEVELS.ACTIVE, default: KEYS.LABELING.LEVELS_LABELS.ALWAYS, type: String, choices: { diff --git a/scripts/terrain_elevation.js b/scripts/terrain_elevation.js index 094124d..a6b350f 100644 --- a/scripts/terrain_elevation.js +++ b/scripts/terrain_elevation.js @@ -115,7 +115,7 @@ elevationAtLocation -- all ruler types Used by ruler to get elevation at waypoints and at the end of the ruler. */ -import { MODULES_ACTIVE, MODULE_ID, FLAGS, MOVEMENT_TYPES } from "./const.js"; +import { OTHER_MODULES, MODULE_ID, FLAGS, MOVEMENT_TYPES } from "./const.js"; import { Settings } from "./settings.js"; import { movementTypeForTokenAt } from "./token_hud.js"; @@ -311,7 +311,7 @@ function retrieveVisibleTokens() { * @returns {Number|undefined} Point elevation or null if module not active or no region at location. */ function TMElevationAtPoint(location, startingElevation = Number.POSITIVE_INFINITY) { - const api = MODULES_ACTIVE.API.TERRAIN_MAPPER; + const api = OTHER_MODULES.TERRAIN_MAPPER.API; if ( !api || !api.ElevationHandler ) return undefined; const waypoint = { ...location, elevation: startingElevation }; const res = api.ElevationHandler.nearestGroundElevation(waypoint); @@ -327,7 +327,7 @@ function TMElevationAtPoint(location, startingElevation = Number.POSITIVE_INFINI * @returns {number|undefined} Elevation, in grid units */ function TMElevationForMovement(start, end, opts) { - const api = MODULES_ACTIVE.API.TERRAIN_MAPPER; + const api = OTHER_MODULES.TERRAIN_MAPPER.API; if ( !api || !api.ElevationHandler ) return undefined; return TMPathForMovement(start, end, opts).at(-1)?.elevation; } @@ -343,7 +343,7 @@ function TMElevationForMovement(start, end, opts) { function TMPathForMovement(start, end, opts) { start.elevation ??= CONFIG.GeometryLib.utils.pixelsToGridUnits(start.z); end.elevation ??= CONFIG.GeometryLib.utils.pixelsToGridUnits(end.z); - const api = MODULES_ACTIVE.API.TERRAIN_MAPPER; + const api = OTHER_MODULES.TERRAIN_MAPPER.API; if ( !api || !api.ElevationHandler ) return [start, end]; return api.ElevationHandler.constructPath(start, end, opts); } @@ -365,7 +365,7 @@ function TMPathForMovement(start, end, opts) { * @return {Number|undefined} Levels elevation or undefined if levels is inactive or no levels found. */ export function LevelsElevationAtPoint(p, startingElevation = 0) { - if ( !MODULES_ACTIVE.LEVELS ) return undefined; + if ( !OTHER_MODULES.LEVELS.ACTIVE ) return undefined; let tiles = [...levelsTilesAtPoint(p)]; if ( !tiles.length ) return null;