Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb40c8de50 | |||
| 6da3e65301 | |||
| a7d06eaed3 | |||
| 18cf63895c | |||
| 0e2ab35db7 | |||
| 0808f17ee3 | |||
| b649dacdb5 | |||
| c490550ed9 | |||
| f12dbf0e61 | |||
| f1fcc52867 | |||
| af03341638 | |||
| 79db620fc1 | |||
| 8bf075c2ce | |||
| f0c6ce1bcc | |||
| 7b392e5c9d | |||
| efe6eac5aa | |||
| 52b64c3016 | |||
| 48d0d17628 | |||
| 8101381cc4 | |||
| 844df150a6 | |||
| 47715e95f6 | |||
| 1dccbcb081 | |||
| 4c3d7ab42a | |||
| 61fc795f7b | |||
| f10fa049b9 | |||
| c844318836 | |||
| 9af692566c | |||
| d08416777b | |||
| 56f506bad2 | |||
| e1d54ed55d | |||
| 504c242e86 | |||
| a48c199863 |
@@ -0,0 +1,4 @@
|
||||
*.md
|
||||
*.html
|
||||
*.json
|
||||
foundry.js
|
||||
@@ -1,3 +1,42 @@
|
||||
## 1.14.0
|
||||
### New features
|
||||
- Drag Ruler now supports hex tokens of size 5 (thanks KitCat!)
|
||||
|
||||
### Compatibility
|
||||
- Drag Ruler has been updated to work with Foundry v12 (thanks TPNils and N0q!)
|
||||
|
||||
|
||||
## 1.13.8
|
||||
### Bugfixes
|
||||
- `getMovedDistanceFromToken` no longer returns incorrect values on gridless maps
|
||||
|
||||
|
||||
## 1.13.7
|
||||
### Compatibility
|
||||
- Drag Ruler's generic speed provider is now aware of good defaults for the Crucible game system
|
||||
|
||||
|
||||
## 1.13.6
|
||||
### Bugfixes
|
||||
- Fixed a bug that caused no measurements to be shown next to the ruler
|
||||
- Fixed a bug where diagonal paths would sometimes highlight squares that don't blong to the path on square maps
|
||||
|
||||
|
||||
## 1.13.5
|
||||
### Compatibility
|
||||
- Drag Ruler is now compatible with Foundry VTT v11 (thanks to pkonshik for doing much of the porting work!)
|
||||
- Drag Ruler's generic speed provider is now aware of good defaults for Warhammer Fantasy Roleplay 4th Edition
|
||||
|
||||
### Translations
|
||||
- Updated Portugese (Brazil) translation (thanks eunaumtenhoid!)
|
||||
|
||||
|
||||
## 1.13.4
|
||||
### Translations
|
||||
- New translation: Portuguese (Brazil) (thanks eunaumtenhoid!)
|
||||
- Foundry should now be able to detect the Simplified Chenese translation properly
|
||||
|
||||
|
||||
## 1.13.3
|
||||
### Translations
|
||||
- New translation: Chinese (Simplified) (thanks bnp800!)
|
||||
|
||||
@@ -66,6 +66,7 @@ The game systems that offer Drag Ruler integration are:
|
||||
- The Dark Eye 5 / Das Schwarze Auge 5 (via the module [TDE5/DSA5 Drag Ruler Integration](https://foundryvtt.com/packages/dsa5-drag-ruler))
|
||||
- Shadow of the Demon Lord (starting with version 1.7.15)
|
||||
- Wasteland Ventures (starting with version 0.1.0)
|
||||
- World of Darkness 20th ed (starting with version 3.3.0)
|
||||
- WWII:OWB (starting with version 1.0.4)
|
||||
|
||||
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"drag-ruler": {
|
||||
"dependencies": {
|
||||
"ok": "Ok",
|
||||
"terrain-ruler": {
|
||||
"neverShowAgain": "Nunca mostre isso de novo",
|
||||
"title": "Como medir terrenos difíceis com o Drag Ruler",
|
||||
"text": "Você tem o módulo {moduleName} habilitado. Drag Ruler é capaz de medir terreno difícil que foi colocado para baixo usando esse módulo. Para tornar isto possível, Drag Ruler utiliza o módulo Terrain Ruler. Se o Terrain Ruler for instalado e ativado, o Drag Ruler começará automaticamente a respeitar o terreno difícil em suas medições."
|
||||
},
|
||||
"socketlib": {
|
||||
"title": "Módulo Socketlib ausente",
|
||||
"text": "Drag Ruler requer que o módulo socketlib funcione corretamente. Por favor, ative o módulo socketlib neste mundo."
|
||||
}
|
||||
},
|
||||
"resetMovementHistory": "Redefinir histórico de movimento",
|
||||
"genericSpeedProvider": {
|
||||
"settings": {
|
||||
"dashMultiplier": {
|
||||
"name": "Traço Multiplicador",
|
||||
"hint": "Isso pode ser usado para dar aos tokens uma velocidade secundária durante a coloração do caminho medido. Defina-o como 0 para desativar a velocidade secundária."
|
||||
},
|
||||
"speedAttribute": {
|
||||
"name": "Atributo de velocidade",
|
||||
"hint": "O atributo que define a velocidade de caminhada de um token. Isso é usado durante a coloração do caminho medido."
|
||||
}
|
||||
},
|
||||
"speeds": {
|
||||
"walk": "andar",
|
||||
"dash": "traço"
|
||||
}
|
||||
},
|
||||
"keybindings": {
|
||||
"cancelDrag": "Cancelar o arrasto",
|
||||
"disableSnap": {
|
||||
"name": "Desativar encaixe",
|
||||
"hint": "O encaixe será desativado temporariamente enquanto esta tecla estiver pressionada."
|
||||
},
|
||||
"createWaypoint": "Criar parada",
|
||||
"deleteWaypoint": "Apagar parada",
|
||||
"togglePathfinding": {
|
||||
"name": "Alternar localização de caminhos",
|
||||
"hint": "Ao ser segurado enquanto arrasta um token, a funcionalidade de localização de caminhos será temporariamente habilitada/desabilitada."
|
||||
},
|
||||
"moveWithoutAnimation": {
|
||||
"name": "Desativar animação de token",
|
||||
"hint": "Ao ser segurado ao soltar um token, o token se moverá para o local de destino sem animação."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"allowPathfinding": {
|
||||
"name": "Permitir localização de caminhos para jogadores",
|
||||
"hint": "Permite que os jogadores usem a funcionalidade de localização de caminhos do Drag Ruler neste mundo. Esteja ciente de que a localização de caminhos pode passar por uma névoa de guerra inexplorada e paredes etéreas, que podem revelar segredos para seus jogadores antes do tempo."
|
||||
},
|
||||
"alwaysShowSpeedForPCs": {
|
||||
"name": "Mostre a velocidade do PJ para todos",
|
||||
"hint": "Se ativado, a coloração baseada na velocidade do ator para os personagens dos jogadores será mostrada a todos, mesmo que eles não tenham permissão de observador para a ficha de personagem."
|
||||
},
|
||||
"autoStartMeasurement": {
|
||||
"name": "Iniciar a medição automaticamente",
|
||||
"hint": "Se ativado, Drag Ruler começará a medir assim que o token estiver sendo arrastado. Se desativado, o Drag Ruler permanecerá inativo e só começará a medir quando o botão para adicionar uma parada for pressionado."
|
||||
},
|
||||
"autoPathfinding": {
|
||||
"hint": "Se ativado, arrastar um token usará automaticamente uma régua de localização de caminho.",
|
||||
"name": "localização de caminho por padrão"
|
||||
},
|
||||
"enableMovementHistory": {
|
||||
"name": "Habilitar histórico de movimento durante o combate",
|
||||
"hint": "Se ativado, o Drag Ruler lembrará o caminho que um token percorreu durante seu turno em combate e o exibirá quando você pegar o token de volta."
|
||||
},
|
||||
"rightClickAction": {
|
||||
"name": "Ação do botão direito",
|
||||
"hint": "Que ação deve ser executada ao clicar com o botão direito do mouse enquanto arrasta um token?",
|
||||
"choices": {
|
||||
"create": "Criar parada",
|
||||
"delete": "Apagar parada",
|
||||
"cancel": "Cancelar arrasto"
|
||||
}
|
||||
},
|
||||
"showGMRulerToPlayers": {
|
||||
"name": "Mostrar régua do GM aos jogadores",
|
||||
"hint": "Se desativado, a régua dos GMs não será exibido para jogadores não-GM."
|
||||
},
|
||||
"speedProviderSettings": {
|
||||
"name": "Configurações do provedor de velocidade",
|
||||
"button": "Configurações do provedor de velocidade",
|
||||
"windowTitle": "Configurações do provedor de velocidade",
|
||||
"headers": {
|
||||
"speedProvider": "Provedor de velocidade",
|
||||
"speedProviderSettings": "Configurações específicas do provedor de velocidade"
|
||||
},
|
||||
"activeProvider": {
|
||||
"name": "Provedor de velocidade ativo",
|
||||
"hint": "As configurações mostradas abaixo dependem do provedor de velocidade ativo. Se o GM selecionar um provedor de velocidade diferente, as configurações disponíveis podem mudar."
|
||||
},
|
||||
"noSettings": "Este provedor de velocidade não oferece nenhuma opção de configuração.",
|
||||
"color": {
|
||||
"name": "Cor para {colorName}",
|
||||
"hint": "A cor que será usada para colorir os quadrados que estão dentro do intervalo {colorName}.",
|
||||
"unreachable": {
|
||||
"name": "inacessível",
|
||||
"hint": "A cor dos espaços que não podem ser acessados pelo token arrastado."
|
||||
}
|
||||
},
|
||||
"speedProvider": {
|
||||
"name": "Provedor de configurações de velocidade",
|
||||
"hint": "Selecione quem fornece informações de velocidade para tokens durante a coloração. Drag Ruler oferece um provedor de velocidade genérico que fornece funcionalidade básica e deve funcionar para todos os sistemas de jogo se configurado corretamente. Mais provedores de velocidade podem ser disponibilizados por meio de sistemas de jogos e módulos instalados. Selecionar um provedor de velocidade diferente do provedor de velocidade genérico pode oferecer uma melhor integração com as regras do sistema de jogo que você está usando. As opções abaixo dependem do provedor de velocidade selecionado aqui.",
|
||||
"choices": {
|
||||
"module": "Módulo {name}",
|
||||
"native": "Genérico",
|
||||
"system": "Sistema {name}"
|
||||
}
|
||||
},
|
||||
"hint": "As configurações do provedor de velocidade contêm todas as configurações específicas do sistema de jogo."
|
||||
},
|
||||
"useGridlessRaster": {
|
||||
"name": "Use o ajuste baseado em velocidade",
|
||||
"hint": "Em cenas sem grid, isso faz com que os tokens se encaixem nas faixas de velocidade do token."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-6
@@ -2,16 +2,17 @@
|
||||
"id": "drag-ruler",
|
||||
"title": "Drag Ruler",
|
||||
"description": "When dragging a token displays a ruler showing how far you've moved that token.",
|
||||
"version": "1.13.3",
|
||||
"version": "1.14.0",
|
||||
"compatibility": {
|
||||
"minimum": "10",
|
||||
"verified": "10"
|
||||
"minimum": "12",
|
||||
"verified": "12",
|
||||
"maximum": "12"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Manuel Vögele",
|
||||
"email": "develop@manuel-voegele.de",
|
||||
"discord": "Stäbchenfisch#5107"
|
||||
"discord": "stabchenfisch"
|
||||
}
|
||||
],
|
||||
"esmodules": [
|
||||
@@ -59,9 +60,14 @@
|
||||
"path": "lang/zh-tw.json"
|
||||
},
|
||||
{
|
||||
"lang": "zh-Hans",
|
||||
"lang": "cn",
|
||||
"name": "中文(简体)",
|
||||
"path": "lang/zh_Hans.json"
|
||||
},
|
||||
{
|
||||
"lang": "pt-BR",
|
||||
"name": "Português (Brasil)",
|
||||
"path": "lang/pt_BR.json"
|
||||
}
|
||||
],
|
||||
"relationships": {
|
||||
@@ -75,7 +81,7 @@
|
||||
},
|
||||
"socket": true,
|
||||
"url": "https://github.com/manuelVo/foundryvtt-drag-ruler",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.13.3.zip",
|
||||
"download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.14.0.zip",
|
||||
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json",
|
||||
"readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md",
|
||||
"changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",
|
||||
|
||||
+5
-1
@@ -133,8 +133,12 @@ export function getColorForDistanceAndToken(distance, token, ranges = null) {
|
||||
export function getMovedDistanceFromToken(token) {
|
||||
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
|
||||
const history = getMovementHistory(token);
|
||||
let tokenPos = {x: token.x, y: token.y};
|
||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||
tokenPos = token.center;
|
||||
}
|
||||
const segments = CONFIG.Canvas.rulerClass
|
||||
.dragRulerGetRaysFromWaypoints(history, {x: token.x, y: token.y})
|
||||
.dragRulerGetRaysFromWaypoints(history, tokenPos)
|
||||
.map(ray => {
|
||||
return {ray};
|
||||
});
|
||||
|
||||
@@ -15,10 +15,10 @@ export function highlightMeasurementTerrainRuler(
|
||||
}
|
||||
|
||||
export function measureDistances(segments, entity, shape, options = {}) {
|
||||
const opts = duplicate(options);
|
||||
const opts = foundry.utils.duplicate(options);
|
||||
if (canvas.grid.diagonalRule === "EUCL") {
|
||||
opts.ignoreGrid = true;
|
||||
opts.gridSpaes = false;
|
||||
opts.gridSpaces = false;
|
||||
}
|
||||
if (opts.enableTerrainRuler) {
|
||||
opts.gridSpaces = true;
|
||||
@@ -31,7 +31,7 @@ export function measureDistances(segments, entity, shape, options = {}) {
|
||||
);
|
||||
previousSegments.forEach(
|
||||
segment =>
|
||||
(segment.ray.terrainRulerVisitedSpaces = duplicate(segment.ray.dragRulerVisitedSpaces)),
|
||||
(segment.ray.terrainRulerVisitedSpaces = foundry.utils.duplicate(segment.ray.dragRulerVisitedSpaces)),
|
||||
);
|
||||
opts.costFunction = buildCostFunction(entity, shape);
|
||||
if (previousSegments.length > 0)
|
||||
|
||||
+11
-16
@@ -1,31 +1,26 @@
|
||||
// Wrapper to fix a FoundryVTT bug that causes the return values of canvas.grid.grid.getPixelsFromGridPosition to be ordered inconsistently
|
||||
|
||||
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
|
||||
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
|
||||
export function getPixelsFromGridPosition(xGrid, yGrid) {
|
||||
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
|
||||
return canvas.grid.grid.getPixelsFromGridPosition(yGrid, xGrid);
|
||||
}
|
||||
return canvas.grid.grid.getPixelsFromGridPosition(xGrid, yGrid);
|
||||
let coord = getPixelsFromGridPositionObj({x: xGrid, y: yGrid});
|
||||
return [coord.x, coord.y];
|
||||
}
|
||||
|
||||
// Wrapper to fix a FoundryVTT bug that causes the return values of canvas.grid.grid.getPixelsFromGridPosition to be ordered inconsistently
|
||||
// https://gitlab.com/foundrynet/foundryvtt/-/issues/4705
|
||||
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
|
||||
export function getGridPositionFromPixels(xPixel, yPixel) {
|
||||
const [x, y] = canvas.grid.grid.getGridPositionFromPixels(xPixel, yPixel);
|
||||
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) return [y, x];
|
||||
return [x, y];
|
||||
let coord = getGridPositionFromPixelsObj({x: xPixel, y: yPixel});
|
||||
return [coord.x, coord.y];
|
||||
}
|
||||
|
||||
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
|
||||
export function getGridPositionFromPixelsObj(o) {
|
||||
const r = {};
|
||||
[r.x, r.y] = getGridPositionFromPixels(o.x, o.y);
|
||||
return r;
|
||||
const coord = canvas.grid.getOffset(o);
|
||||
return {x: coord.j, y: coord.i};
|
||||
}
|
||||
|
||||
// This code could be phased out. The bug that caused the creation of these functions is now fixed, so this is only a wrapper function now
|
||||
export function getPixelsFromGridPositionObj(o) {
|
||||
const r = {};
|
||||
[r.x, r.y] = getPixelsFromGridPosition(o.x, o.y);
|
||||
return r;
|
||||
return canvas.grid.getTopLeftPoint({j: o.x, i: o.y});
|
||||
}
|
||||
|
||||
export function getCenterFromGridPositionObj(o) {
|
||||
|
||||
+14
-47
@@ -35,7 +35,8 @@ export async function moveEntities(draggedEntity, selectedEntities) {
|
||||
);
|
||||
});
|
||||
if (hasCollision) {
|
||||
ui.notifications.error(game.i18n.localize("ERROR.TokenCollide"));
|
||||
ui.notifications.error(game.i18n.localize("RULER.MovementCollision"));
|
||||
this._state = Ruler.STATES.MEASURING;
|
||||
this._endMeasurement();
|
||||
return true;
|
||||
}
|
||||
@@ -47,7 +48,10 @@ export async function moveEntities(draggedEntity, selectedEntities) {
|
||||
await animateEntities.call(this, selectedEntities, draggedEntity, rays, wasPaused);
|
||||
|
||||
// Once all animations are complete we can clear the ruler
|
||||
if (this.draggedEntity?.id === draggedEntity.id) this._endMeasurement();
|
||||
if (this.draggedEntity?.id === draggedEntity.id) {
|
||||
this._state = Ruler.STATES.MEASURING;
|
||||
this._endMeasurement();
|
||||
}
|
||||
}
|
||||
|
||||
// This is a modified version code extracted from Ruler.moveToken from foundry 0.7.9
|
||||
@@ -133,8 +137,8 @@ export function onMouseMove(event) {
|
||||
|
||||
// Extract event data
|
||||
const destination = {
|
||||
x: event.data.destination.x + this.rulerOffset.x,
|
||||
y: event.data.destination.y + this.rulerOffset.y,
|
||||
x: event.interactionData.destination.x,
|
||||
y: event.interactionData.destination.y,
|
||||
};
|
||||
|
||||
// Hide any existing Token HUD
|
||||
@@ -148,7 +152,7 @@ export function onMouseMove(event) {
|
||||
function scheduleMeasurement(destination, event) {
|
||||
const measurementInterval = 50;
|
||||
const mt = event._measureTime || 0;
|
||||
const originalEvent = event.data.originalEvent;
|
||||
const originalEvent = event.interactionData.originalEvent;
|
||||
if (Date.now() - mt > measurementInterval) {
|
||||
this.measure(destination, {snap: !disableSnap});
|
||||
event._measureTime = Date.now();
|
||||
@@ -185,51 +189,14 @@ export function highlightMeasurementNative(
|
||||
tokenShape = [{x: 0, y: 0}],
|
||||
alpha = 1,
|
||||
) {
|
||||
const spacer = canvas.scene.gridType === CONST.GRID_TYPES.SQUARE ? 1.41 : 1;
|
||||
const nMax = Math.max(
|
||||
Math.floor(ray.distance / (spacer * Math.min(canvas.grid.w, canvas.grid.h))),
|
||||
1,
|
||||
);
|
||||
const tMax = Array.fromRange(nMax + 1).map(t => t / nMax);
|
||||
|
||||
// Track prior position
|
||||
let prior = null;
|
||||
|
||||
// Iterate over ray portions
|
||||
for (let [i, t] of tMax.reverse().entries()) {
|
||||
let {x, y} = ray.project(t);
|
||||
|
||||
// Get grid position
|
||||
let [x0, y0] = i === 0 ? [null, null] : prior;
|
||||
let [x1, y1] = canvas.grid.grid.getGridPositionFromPixels(x, y);
|
||||
if (x0 === x1 && y0 === y1) continue;
|
||||
|
||||
// Highlight the grid position
|
||||
let [xgtl, ygtl] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1);
|
||||
let [xg, yg] = canvas.grid.grid.getCenter(xgtl, ygtl);
|
||||
const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, {x: xg, y: yg})}]);
|
||||
for (const offset of canvas.grid.getDirectPath([ray.A, ray.B]).reverse()) {
|
||||
const point = canvas.grid.getTopLeftPoint(offset);
|
||||
const center = canvas.grid.getCenterPoint(offset);
|
||||
const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, center)}]);
|
||||
const distance = sum(canvas.grid.measureDistances(pathUntilSpace, {gridSpaces: true}));
|
||||
const color = this.dragRulerGetColorForDistance(distance);
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity);
|
||||
const snapPoint = getSnapPointForToken(point.x, point.y, this.draggedEntity);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
|
||||
prior = [x1, y1];
|
||||
|
||||
// If the positions are not neighbors, also highlight their halfway point
|
||||
if (i > 0 && !canvas.grid.isNeighbor(x0, y0, x1, y1)) {
|
||||
let th = tMax[i - 1] - 0.5 / nMax;
|
||||
let {x, y} = ray.project(th);
|
||||
let [x1h, y1h] = canvas.grid.grid.getGridPositionFromPixels(x, y);
|
||||
let [xghtl, yghtl] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h);
|
||||
let [xgh, ygh] = canvas.grid.grid.getCenter(xghtl, yghtl);
|
||||
const pathUntilSpace = previousSegments.concat([{ray: new Ray(ray.A, {x: xgh, y: ygh})}]);
|
||||
const distance = sum(canvas.grid.measureDistances(pathUntilSpace, {gridSpaces: true}));
|
||||
const color = this.dragRulerGetColorForDistance(distance);
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedEntity);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color, alpha);
|
||||
}
|
||||
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -9,11 +9,11 @@
|
||||
* - Instead of taking a grid parameter, get the grid value from the globas canvas
|
||||
*/
|
||||
export function findVertexSnapPoint(x, y, altOrientationFlag) {
|
||||
const grid = canvas.grid.grid;
|
||||
const grid = canvas.grid;
|
||||
if (grid.columnar) {
|
||||
return findSnapPointCols(x, y, grid.h, grid.w, altOrientationFlag);
|
||||
return findSnapPointCols(x, y, grid.sizeY, grid.sizeX, altOrientationFlag);
|
||||
} else {
|
||||
return findSnapPointRows(x, y, grid.h, grid.w, altOrientationFlag);
|
||||
return findSnapPointRows(x, y, grid.sizeY, grid.sizeX, altOrientationFlag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-14
@@ -137,10 +137,6 @@ function onEntityLeftDragStart(wrapped, event) {
|
||||
const ruler = canvas.controls.ruler;
|
||||
ruler.draggedEntity = this;
|
||||
const entityCenter = getEntityCenter(this);
|
||||
ruler.rulerOffset = {
|
||||
x: entityCenter.x - event.data.origin.x,
|
||||
y: entityCenter.y - event.data.origin.y,
|
||||
};
|
||||
if (game.settings.get(settingsKey, "autoStartMeasurement")) {
|
||||
let options = {};
|
||||
setSnapParameterOnOptions(ruler, options);
|
||||
@@ -215,9 +211,9 @@ function applyGridlessSnapping(event) {
|
||||
if (canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS) return;
|
||||
|
||||
const rasterWidth = 35 / canvas.stage.scale.x;
|
||||
const tokenX = event.data.destination.x;
|
||||
const tokenY = event.data.destination.y;
|
||||
const destination = {x: tokenX + ruler.rulerOffset.x, y: tokenY + ruler.rulerOffset.y};
|
||||
const tokenX = event.interactionData.destination.x;
|
||||
const tokenY = event.interactionData.destination.y;
|
||||
const destination = {x: tokenX, y: tokenY};
|
||||
const ranges = getRangesFromSpeedProvider(ruler.draggedEntity);
|
||||
|
||||
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
|
||||
@@ -241,13 +237,13 @@ function applyGridlessSnapping(event) {
|
||||
const deltaY = destination.y - rasterLocation.y;
|
||||
const rasterDistance = Math.hypot(deltaX, deltaY);
|
||||
if (rasterDistance < rasterWidth) {
|
||||
event.data.destination.x = rasterLocation.x - ruler.rulerOffset.x;
|
||||
event.data.destination.y = rasterLocation.y - ruler.rulerOffset.y;
|
||||
event.interactionData.destination.x = rasterLocation.x;
|
||||
event.interactionData.destination.y = rasterLocation.y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let waypointDistance = 0;
|
||||
let origin = event.data.origin;
|
||||
let origin = event.interactionData.origin;
|
||||
if (ruler.waypoints.length > 1) {
|
||||
const segments = ruler.constructor
|
||||
.dragRulerGetRaysFromWaypoints(ruler.waypoints, destination)
|
||||
@@ -255,8 +251,8 @@ function applyGridlessSnapping(event) {
|
||||
return {ray};
|
||||
});
|
||||
origin = segments.pop().ray.A;
|
||||
waypointDistance = canvas.grid.measureDistances(segments).reduce((a, b) => a + b);
|
||||
origin = {x: origin.x - ruler.rulerOffset.x, y: origin.y - ruler.rulerOffset.y};
|
||||
waypointDistance = canvas.grid.measurePath(segments).reduce((a, b) => a + b);
|
||||
origin = {x: origin.x, y: origin.y};
|
||||
}
|
||||
|
||||
const deltaX = tokenX - origin.x;
|
||||
@@ -271,8 +267,8 @@ function applyGridlessSnapping(event) {
|
||||
.reduce((a, b) => Math.max(a, b), 0);
|
||||
if (targetDistance) {
|
||||
if (distance < targetDistance + rasterWidth) {
|
||||
event.data.destination.x = origin.x + (deltaX * targetDistance) / distance;
|
||||
event.data.destination.y = origin.y + (deltaY * targetDistance) / distance;
|
||||
event.interactionData.destination.x = origin.x + (deltaX * targetDistance) / distance;
|
||||
event.interactionData.destination.y = origin.y + (deltaY * targetDistance) / distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-33
@@ -17,6 +17,7 @@ import {
|
||||
getTokenShape,
|
||||
isPathfindingEnabled,
|
||||
} from "./util.js";
|
||||
import {getPointer} from "./util.js";
|
||||
|
||||
export function extendRuler() {
|
||||
class DragRulerRuler extends CONFIG.Canvas.rulerClass {
|
||||
@@ -40,14 +41,17 @@ export function extendRuler() {
|
||||
if (!this.isDragRuler) return await super.moveToken(event);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const json = super.toJSON();
|
||||
_getMeasurementData() {
|
||||
const json =
|
||||
typeof super._getMeasurementData === "function"
|
||||
? super._getMeasurementData()
|
||||
: super.toJSON();
|
||||
if (this.draggedEntity) {
|
||||
const isToken = this.draggedEntity instanceof Token;
|
||||
json.draggedEntityIsToken = isToken;
|
||||
json.draggedEntity = this.draggedEntity.id;
|
||||
json.waypoints = json.waypoints.map(old => {
|
||||
let w = duplicate(old);
|
||||
let w = foundry.utils.duplicate(old);
|
||||
w.isPathfinding = undefined;
|
||||
return w;
|
||||
});
|
||||
@@ -55,7 +59,13 @@ export function extendRuler() {
|
||||
return json;
|
||||
}
|
||||
|
||||
/** @deprecated since V12 */
|
||||
toJSON() {
|
||||
return this._getMeasurementData();
|
||||
}
|
||||
|
||||
update(data) {
|
||||
if (!data || data.state === Ruler.STATES.INACTIVE) return this.clear();
|
||||
// Don't show a GMs drag ruler to non GM players
|
||||
if (
|
||||
data.draggedEntity &&
|
||||
@@ -104,7 +114,10 @@ export function extendRuler() {
|
||||
|
||||
// Compute the measurement destination, segments, and distance
|
||||
const d = this._getMeasurementDestination(destination);
|
||||
if (d.x === this.destination.x && d.y === this.destination.y) return;
|
||||
if (this.destination && d.x === this.destination.x && d.y === this.destination.y) {
|
||||
this.performPostPathfindingActions(options);
|
||||
return;
|
||||
}
|
||||
this.destination = d;
|
||||
|
||||
// TODO Check if we can reuse the old path
|
||||
@@ -205,6 +218,7 @@ export function extendRuler() {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dragRulerSendState();
|
||||
return this.segments;
|
||||
}
|
||||
|
||||
@@ -226,7 +240,7 @@ export function extendRuler() {
|
||||
const waypoints =
|
||||
this.draggedEntity instanceof Token
|
||||
? applyTokenSizeOffset(unsnappedWaypoints, this.draggedEntity)
|
||||
: duplicate(unsnappedWaypoints);
|
||||
: foundry.utils.duplicate(unsnappedWaypoints);
|
||||
const unsnappedSegments = [];
|
||||
const segments = [];
|
||||
for (const [i, p1] of waypoints.entries()) {
|
||||
@@ -251,6 +265,9 @@ export function extendRuler() {
|
||||
unsnappedSegments.push({ray: unsnappedRay, label});
|
||||
}
|
||||
this.dragRulerUnsnappedSegments = unsnappedSegments;
|
||||
if (this.labels.children.length > segments.length) {
|
||||
this.labels.removeChildren(segments.length).forEach(c => c.destroy());
|
||||
}
|
||||
return segments;
|
||||
} else {
|
||||
return super._getMeasurementSegments();
|
||||
@@ -261,12 +278,6 @@ export function extendRuler() {
|
||||
if (!this.isDragRuler) {
|
||||
return super._computeDistance(gridSpaces);
|
||||
}
|
||||
if (!this.dragRulerEnableTerrainRuler) {
|
||||
if (!this.dragRulerIgnoreGrid) {
|
||||
gridSpaces = true;
|
||||
}
|
||||
super._computeDistance(gridSpaces);
|
||||
} else {
|
||||
const shape = this.draggedEntity ? getTokenShape(this.draggedEntity) : null;
|
||||
const options = {
|
||||
ignoreGrid: this.dragRulerIgnoreGrid,
|
||||
@@ -274,16 +285,16 @@ export function extendRuler() {
|
||||
enableTerrainRuler: this.dragRulerEnableTerrainRuler,
|
||||
};
|
||||
const distances = measureDistances(this.segments, this.draggedEntity, shape, options);
|
||||
let totalDistance = 0;
|
||||
this.totalDistance = 0;
|
||||
for (const [i, d] of distances.entries()) {
|
||||
let s = this.segments[i];
|
||||
s.startDistance = totalDistance;
|
||||
totalDistance += d;
|
||||
s.startDistance = this.totalDistance;
|
||||
this.totalDistance += d;
|
||||
s.last = i === this.segments.length - 1;
|
||||
s.distance = d;
|
||||
s.text = this._getSegmentLabel(s, totalDistance);
|
||||
}
|
||||
s.text = this._getSegmentLabel(s);
|
||||
}
|
||||
|
||||
for (const [i, segment] of this.segments.entries()) {
|
||||
const unsnappedSegment = this.dragRulerUnsnappedSegments[i];
|
||||
unsnappedSegment.startDistance = segment.startDistance;
|
||||
@@ -379,18 +390,12 @@ export function extendRuler() {
|
||||
options.snap = options.snap ?? true;
|
||||
if (this.waypoints.filter(w => !w.isPrevious).length > 1) {
|
||||
event.preventDefault();
|
||||
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(
|
||||
canvas.tokens,
|
||||
);
|
||||
const rulerOffset = this.rulerOffset;
|
||||
const mousePosition = getPointer().getLocalPosition(canvas.tokens);
|
||||
|
||||
// Options are not passed to _removeWaypoint in vanilla Foundry.
|
||||
// Send them in case other modules have overriden that behavior and accept an options parameter (Toggle Snap to Grid)
|
||||
this._removeWaypoint(
|
||||
{x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y},
|
||||
options,
|
||||
);
|
||||
game.user.broadcastActivity({ruler: this});
|
||||
this._removeWaypoint({x: mousePosition.x, y: mousePosition.y}, options);
|
||||
this.performPostPathfindingActions(options);
|
||||
} else {
|
||||
this.dragRulerAbortDrag(event);
|
||||
}
|
||||
@@ -416,7 +421,7 @@ export function extendRuler() {
|
||||
this._endMeasurement();
|
||||
|
||||
// Deactivate the drag workflow in mouse
|
||||
token.mouseInteractionManager._deactivateDragEvents();
|
||||
token.mouseInteractionManager.cancel(event);
|
||||
token.mouseInteractionManager.state = token.mouseInteractionManager.states.HOVER;
|
||||
|
||||
// This will cancel the current drag operation
|
||||
@@ -435,7 +440,7 @@ export function extendRuler() {
|
||||
this.dragRulerAddWaypoint(waypoint, {snap: false});
|
||||
}
|
||||
this.measure(this.destination);
|
||||
game.user.broadcastActivity({ruler: this});
|
||||
this.dragRulerSendState();
|
||||
}
|
||||
|
||||
static dragRulerGetRaysFromWaypoints(waypoints, destination) {
|
||||
@@ -483,19 +488,20 @@ export function extendRuler() {
|
||||
if (isToken && game.settings.get(settingsKey, "enableMovementHistory"))
|
||||
ruler.dragRulerAddWaypointHistory(getMovementHistory(entity));
|
||||
ruler.dragRulerAddWaypoint(entityCenter, {snap: false});
|
||||
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(
|
||||
canvas.tokens,
|
||||
);
|
||||
const mousePosition = getPointer().getLocalPosition(canvas.tokens);
|
||||
const destination = {
|
||||
x: mousePosition.x + ruler.rulerOffset.x,
|
||||
y: mousePosition.y + ruler.rulerOffset.y,
|
||||
x: mousePosition.x,
|
||||
y: mousePosition.y,
|
||||
};
|
||||
if (measureImmediately) ruler.measure(destination, options);
|
||||
}
|
||||
|
||||
dragRulerSendState() {
|
||||
if (this.user !== game.user) {
|
||||
return;
|
||||
}
|
||||
game.user.broadcastActivity({
|
||||
ruler: this.toJSON(),
|
||||
ruler: this._getMeasurementData(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -298,7 +298,7 @@ function enumerateProviderSettings(provider) {
|
||||
for (const setting of provider.settings) {
|
||||
try {
|
||||
if (setting.scope === "world" && !game.user.isGM) continue;
|
||||
const s = duplicate(setting);
|
||||
const s = foundry.utils.duplicate(setting);
|
||||
s.id = `${provider.id}.setting.${s.id}`;
|
||||
s.name = game.i18n.localize(s.name);
|
||||
s.hint = game.i18n.localize(s.hint);
|
||||
|
||||
@@ -115,7 +115,7 @@ export class GenericSpeedProvider extends SpeedProvider {
|
||||
getRanges(token) {
|
||||
const speedAttribute = this.getSetting("speedAttribute");
|
||||
if (!speedAttribute) return [];
|
||||
const tokenSpeed = parseFloat(getProperty(token, speedAttribute));
|
||||
const tokenSpeed = parseFloat(foundry.utils.getProperty(token, speedAttribute));
|
||||
if (tokenSpeed === undefined) {
|
||||
console.warn(
|
||||
`Drag Ruler (Generic Speed Provider) | The configured token speed attribute "${speedAttribute}" didn't return a speed value. To use colors based on drag distance set the setting to the correct value (or clear the box to disable this feature).`,
|
||||
|
||||
@@ -23,6 +23,10 @@ export function getDefaultSpeedAttribute() {
|
||||
return "actor.system.combatValues.movement.total";
|
||||
case "splittermond":
|
||||
return "actor.derivedValues.speed.value";
|
||||
case "wfrp4e":
|
||||
return "actor.system.details.move.walk";
|
||||
case "crucible":
|
||||
return "actor.system.movement.stride";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -45,6 +49,10 @@ export function getDefaultDashMultiplier() {
|
||||
return 5;
|
||||
case "splittermond":
|
||||
return 3;
|
||||
case "wfrp4e":
|
||||
return 2;
|
||||
case "crucible":
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+46
-31
@@ -37,20 +37,20 @@ export function getHexTokenSize(token) {
|
||||
}
|
||||
|
||||
export function getEntityCenter(token) {
|
||||
if (token instanceof Token && canvas.grid.isHex) {
|
||||
if (token instanceof Token && isCanvasHex()) {
|
||||
const center = token.center;
|
||||
const size = getHexTokenSize(token);
|
||||
if (size % 2 === 0) {
|
||||
let offset;
|
||||
if (canvas.grid.grid.columnar) {
|
||||
offset = canvas.grid.grid.w - canvas.grid.grid.h;
|
||||
if (canvas.grid.columnar) {
|
||||
offset = canvas.grid.sizeX - canvas.grid.sizeY;
|
||||
} else {
|
||||
offset = canvas.grid.grid.h - canvas.grid.grid.w;
|
||||
offset = canvas.grid.sizeY - canvas.grid.sizeX;
|
||||
}
|
||||
if (getAltOrientationFlagForToken(token, size)) {
|
||||
offset *= -1;
|
||||
}
|
||||
if (canvas.grid.grid.columnar) {
|
||||
if (canvas.grid.columnar) {
|
||||
center.x -= offset;
|
||||
return center;
|
||||
} else {
|
||||
@@ -79,7 +79,7 @@ export function getSnapPointForToken(x, y, token) {
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
if (canvas.grid.isHex) {
|
||||
if (isCanvasHex()) {
|
||||
const size = getHexTokenSize(token);
|
||||
if (size % 2 === 0) {
|
||||
return findVertexSnapPoint(x, y, getAltOrientationFlagForToken(token, size));
|
||||
@@ -89,27 +89,27 @@ export function getSnapPointForToken(x, y, token) {
|
||||
}
|
||||
|
||||
const [topLeftX, topLeftY] = canvas.grid.getTopLeft(x, y);
|
||||
let cellX, cellY;
|
||||
if (token.document.width % 2 === 0) cellX = x - canvas.grid.h / 2;
|
||||
else cellX = x;
|
||||
if (token.document.height % 2 === 0) cellY = y - canvas.grid.h / 2;
|
||||
else cellY = y;
|
||||
const [centerX, centerY] = canvas.grid.getCenter(cellX, cellY);
|
||||
let cell = {};
|
||||
if (token.document.width % 2 === 0) cell.x = x - canvas.grid.sizeY / 2;
|
||||
else cell.x = x;
|
||||
if (token.document.height % 2 === 0) cell.y = y - canvas.grid.sizeY / 2;
|
||||
else cell.y = y;
|
||||
const center = canvas.grid.getCenterPoint(cell);
|
||||
let snapX, snapY;
|
||||
// Tiny tokens can snap to the cells corners
|
||||
if (token.document.width <= 0.5) {
|
||||
const offsetX = x - topLeftX;
|
||||
const subGridWidth = Math.floor(canvas.grid.w / 2);
|
||||
const subGridWidth = Math.floor(canvas.grid.sizeX / 2);
|
||||
const subGridPosX = Math.floor(offsetX / subGridWidth);
|
||||
snapX = topLeftX + (subGridPosX + 0.5) * subGridWidth;
|
||||
}
|
||||
// Tokens with odd multipliers (1x1, 3x3, ...) and tokens smaller than 1x1 but bigger than 0.5x0.5 snap to the center of the grid cell
|
||||
else if (Math.round(token.document.width) % 2 === 1 || token.document.width < 1) {
|
||||
snapX = centerX;
|
||||
snapX = center.x;
|
||||
}
|
||||
// All remaining tokens (those with even or fractional multipliers on square grids) snap to the intersection points of the grid
|
||||
else {
|
||||
snapX = centerX + canvas.grid.w / 2;
|
||||
snapX = center.x + canvas.grid.sizeX / 2;
|
||||
}
|
||||
if (token.document.height <= 0.5) {
|
||||
const offsetY = y - topLeftY;
|
||||
@@ -117,9 +117,9 @@ export function getSnapPointForToken(x, y, token) {
|
||||
const subGridPosY = Math.floor(offsetY / subGridHeight);
|
||||
snapY = topLeftY + (subGridPosY + 0.5) * subGridHeight;
|
||||
} else if (Math.round(token.document.height) % 2 === 1 || token.document.height < 1) {
|
||||
snapY = centerY;
|
||||
snapY = center.y;
|
||||
} else {
|
||||
snapY = centerY + canvas.grid.h / 2;
|
||||
snapY = center.y + canvas.grid.sizeY / 2;
|
||||
}
|
||||
return {x: snapX, y: snapY};
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export function highlightTokenShape(position, shape, color, alpha) {
|
||||
const area = getAreaFromPositionAndShape(position, shape);
|
||||
for (const space of area) {
|
||||
const [x, y] = getPixelsFromGridPosition(space.x, space.y);
|
||||
canvas.grid.grid.highlightGridPosition(layer, {x, y, color, alpha: 0.25 * alpha});
|
||||
canvas.grid.highlightGridPosition(layer, {x, y, color, alpha: 0.25 * alpha});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,9 @@ export function getAreaFromPositionAndShape(position, shape) {
|
||||
return shape.map(space => {
|
||||
let x = position.x + space.x;
|
||||
let y = position.y + space.y;
|
||||
if (canvas.grid.isHex) {
|
||||
if (isCanvasHex()) {
|
||||
let shiftedRow;
|
||||
if (canvas.grid.grid.options.even) shiftedRow = 1;
|
||||
if (canvas.grid?.even) shiftedRow = 1;
|
||||
else shiftedRow = 0;
|
||||
if (canvas.grid.grid.columnar) {
|
||||
if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) {
|
||||
@@ -212,6 +212,16 @@ export function getTokenShape(token) {
|
||||
{x: 0, y: -2},
|
||||
{x: 1, y: -2},
|
||||
]);
|
||||
if (size >= 5)
|
||||
shape = shape.concat([
|
||||
{x: -2, y: 0},
|
||||
{x: 1, y: 1},
|
||||
{x: -1, y: 2},
|
||||
{x: 0, y: 2},
|
||||
{x: 1, y: 2},
|
||||
{x: -2, y: 1},
|
||||
{x: 2, y: 0},
|
||||
]);
|
||||
|
||||
if (getAltOrientationFlagForToken(token, size)) {
|
||||
shape.forEach(space => (space.y *= -1));
|
||||
@@ -226,7 +236,7 @@ export function getTokenShape(token) {
|
||||
|
||||
export function getTokenSize(token) {
|
||||
let w, h;
|
||||
if (canvas.grid.isHex) {
|
||||
if (isCanvasHex()) {
|
||||
w = h = getHexTokenSize(token);
|
||||
} else {
|
||||
w = token.document.width;
|
||||
@@ -244,26 +254,26 @@ export function applyTokenSizeOffset(waypoints, token) {
|
||||
|
||||
const tokenSize = getTokenSize(token);
|
||||
const waypointOffset = {x: 0, y: 0};
|
||||
if (canvas.grid.isHex) {
|
||||
if (isCanvasHex()) {
|
||||
const isAltOrientation = getAltOrientationFlagForToken(token, getHexTokenSize(token));
|
||||
if (canvas.grid.grid.columnar) {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
waypointOffset.x = canvas.grid.sizeX / 2;
|
||||
if (isAltOrientation) waypointOffset.x *= -1;
|
||||
}
|
||||
} else {
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
waypointOffset.y = canvas.grid.sizeY / 2;
|
||||
if (isAltOrientation) waypointOffset.y *= -1;
|
||||
}
|
||||
}
|
||||
// If hex size support isn't active leave the waypoints like they are
|
||||
} else {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
waypointOffset.x = canvas.grid.sizeX / 2;
|
||||
}
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
waypointOffset.y = canvas.grid.sizeY / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,12 +295,13 @@ export function isClose(a, b, delta) {
|
||||
return Math.abs(a - b) <= delta;
|
||||
}
|
||||
|
||||
export function getPointer() {
|
||||
return canvas.app.renderer.events.pointer;
|
||||
}
|
||||
|
||||
export function getMeasurePosition() {
|
||||
const mousePosition = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(
|
||||
canvas.tokens,
|
||||
);
|
||||
const rulerOffset = canvas.controls.ruler.rulerOffset;
|
||||
const measurePosition = {x: mousePosition.x + rulerOffset.x, y: mousePosition.y + rulerOffset.y};
|
||||
const mousePosition = getPointer().getLocalPosition(canvas.tokens);
|
||||
const measurePosition = {x: mousePosition.x, y: mousePosition.y};
|
||||
return measurePosition;
|
||||
}
|
||||
|
||||
@@ -312,3 +323,7 @@ export function isPathfindingEnabled() {
|
||||
if (moveWithoutAnimation) return false;
|
||||
return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding;
|
||||
}
|
||||
|
||||
function isCanvasHex() {
|
||||
return canvas.grid.isHexagonal;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user