diff --git a/df-templates/CHANGELOG.md b/df-templates/CHANGELOG.md index d7ddae0..71295b4 100644 --- a/df-templates/CHANGELOG.md +++ b/df-templates/CHANGELOG.md @@ -1,5 +1,9 @@ # DragonFlagon Template Enhancements +## Release 1.3.0 (2022-10-23) +- **UPDATE:** Migrated to v10. +- **NEW #339:** Brazillian Portuguese Localization: Courtesy of [MagelaCE](https://github.com/MagelaCE). + ## Release 1.2.1 (2022-04-05) - **FIX #350:** Error when opening config while on a language other than English. diff --git a/df-templates/README.md b/df-templates/README.md index fcf0d88..c4b12f9 100644 --- a/df-templates/README.md +++ b/df-templates/README.md @@ -7,6 +7,7 @@ Various enhancements to the FoundryVTT Templates layer. This brings different fo ## Contributers - Touge & [BrotherSharper](https://github.com/BrotherSharper): Japanese Localization +- [MagelaCE](https://github.com/MagelaCE): Brazillian Portuguese Localization ## Auto-Target Tokens with Template diff --git a/df-templates/lang/pt-BR.json b/df-templates/lang/pt-BR.json new file mode 100644 index 0000000..4b97026 --- /dev/null +++ b/df-templates/lang/pt-BR.json @@ -0,0 +1,28 @@ +{ + "DF_TEMPLATES": { + "errorLibWrapperMissing": "'DF Template Enhancements' requer o módulo 'libWrapper'. Instale e ative 'libWrapper'.", + "ToggleTitle": "Selecionar Tokens Automáticamente", + "AutoTargetName": "Modelos Selecionam Tokens Automáticamente", + "AutoTargetHint": "Se ativado, irá automaticamente selecionar tokens que são atingidos por um Modelo de Medida", + "SnapIntersectName": "Encaixe os modelos nas interseções da grade", + "SnapIntersectHint": "Alguns sistemas de regras (como D&D 5e) afirmam que os modelos de magia devem se originar de uma interseção do grid.", + "GridlessPointResolutionName": "Resolução para Seleção Automática Sem Grid (Padrão: 3)", + "GridlessPointResolutionHint": "Quantos pontos ao longo de cada eixo devem ser gerados para seleção automática em cenas sem grid (Exemplo: para 3, haverá 3 x 3 = 9 pontos sobre cada quadrado). Um número mais alto pode causar desempenho mais lento com muitos tokens em uma cena.", + "PreviewName": "Pré-visualizar Realce/Seleção do Modelo", + "PreviewHint": "Mostra o realce da grade e executa a seleção automática ao mover ou criar um modelo.", + "SquareRotateName": "Corrigir a Rotação do Modelo Quadrado", + "SquareRotateHint": "Os modelos quadrados não mantêm sua forma ao girar. Isso corrigirá a rotação para que um modelo quadrado mantenha sua forma enquanto gira em torno do ponto de origem do modelo.", + "DebugName": "[DEBUG Somente] Exibir Pontos do Grid de Segleção Automática e Testa Grids", + "DebugHint": "Para fins de depuração do recurso de selecão automática, esta opção exibirá os pontos usados para detectar a seleção de token em uma cena sem grid. Ou exibirá toda a zona de teste para realce do grid.", + "Patch5e_Name": "Use Modelos no Estilo D&D 5e", + "Patch5e_Hint": "O padrão do Foundry requer que o centro de um quadrado do grid esteja dentro de um modelo para ser afetado. Isso ajusta os modelos para seguir as regras D&D5e para selecionar quadrados do grid onde qualquer quadrado TOCADO por um modelo é atingido (com exceção de círculos).", + "Patch5e_Circle_Name": "Tornar os Modelos de Círculo Gananciosos (Requer os Modelos no Estilo D&D 5e)", + "Patch5e_Circle_Hint": "Se você quiser que os modelos de círculo não sejam uma exceção às regras do estilo D&D 5e acima, habilite isso e qualquer quadrado tocado por um círculo será atingido.", + "AngleSnap": { + "MacroName": "Contagem de Pontos de Encaixe do Ângulo do Modelo (Padrão do FoundryVTT: 24)", + "MacroHint": "Quantos pontos de encaixe estão disponíveis para girar um modelo. Isso pode ser usado para reduzir o número de ângulos que um jogador tem que escolher para acelerar o combate", + "MicroName": "Multiplicador de Rotação Pequeno (Padrão do FoundryVTT: 3)", + "MicroHint": "Ao segurar Ctrl, você pode realizar encaixes de rotação menores. Isso basicamente multiplica a configuração acima por esse valor para gerar um subconjunto de encaixes de ângulo. Definir isso para 1 desativa efetivamente as etapas de micro ângulo." + } + } +} \ No newline at end of file diff --git a/df-templates/module.json b/df-templates/module.json index b77013d..d090089 100644 --- a/df-templates/module.json +++ b/df-templates/module.json @@ -1,17 +1,30 @@ { - "name": "df-templates", - "version": "1.2.1", + "id": "df-templates", + "version": "1.3.0", "title": "DF Template Enhancements", "description": "Enhanced templates for different types of grid targetting.", "author": "flamewave000#0001", - "minimumCoreVersion": "9", - "compatibleCoreVersion": "9", - "dependencies": [ { "name": "lib-wrapper" } ], + "compatibility": { + "minimum": 10, + "verified": 10.288 + }, + "relationships": { + "requires": [ + { + "id": "lib-wrapper", + "type": "module", + "compatibility": { + "verified": "1.12.10.0" + } + } + ] + }, "esmodules": "{{sources}}", "styles": "{{css}}", "languages": [ { "lang": "en", "path": "lang/en.json", "name": "English" }, - { "lang": "ja", "path": "lang/ja.json", "name": "日本語" } + { "lang": "ja", "path": "lang/ja.json", "name": "日本語" }, + { "lang": "pt-BR", "path": "lang/pt-BR.json", "name": "Português (Brasil)" } ], "url": "https://github.com/flamewave000/dragonflagon-fvtt/tree/master/df-templates", "license": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/LICENSE", @@ -23,6 +36,13 @@ "bugs": "https://github.com/flamewave000/dragonflagon-fvtt/issues", "authors": [ { "name": "flamewave000", "discord": "flamewave000#0001", "url": "https://github.com/flamewave000" } ], "media": [ - { "type": "icon", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/logo.png" } + { "type": "icon", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/logo.png" }, + { "type": "cover", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/templates-dnd5e.png" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/auto-target.gif" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/grid-intersect-snapping.gif" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/square-rotate-core.gif" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/square-rotate-corrected.gif" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/square-rotate-corrected-5e.gif" }, + { "type": "screenshot", "url": "https://raw.githubusercontent.com/flamewave000/dragonflagon-fvtt/master/.assets/df-templates/template-preview.gif" } ] } diff --git a/df-templates/src/AngleSnaps.ts b/df-templates/src/AngleSnaps.ts index f35dbdf..afb6d78 100644 --- a/df-templates/src/AngleSnaps.ts +++ b/df-templates/src/AngleSnaps.ts @@ -1,5 +1,4 @@ import SETTINGS from "../../common/Settings"; -import DnD5eAbilityTemplateHandlers from "./DnD5eAbilityTemplateHandlers"; export default class AngleSnaps { static init() { @@ -34,7 +33,7 @@ export default class AngleSnaps { static ready() { libWrapper.register(SETTINGS.MOD_NAME, 'canvas.templates._onMouseWheel', function (this: TemplateLayer, event: MouseEvent): any { // Determine whether we have a hovered template? - const template = this._hover; + const template = this.hover; if (!template) return; // Determine the incremental angle of rotation from event data const snapCount = SETTINGS.get('angle-snap-macro'); @@ -44,31 +43,34 @@ export default class AngleSnaps { const sign = Math.sign((event as any).deltaY); const delta = snap * sign; - let direction = template.data.direction - (template.data.direction % snap); - if (template.data.direction % snap !== 0 && sign < 0) + let direction = template.document.direction - (template.document.direction % snap); + if (template.document.direction % snap !== 0 && sign < 0) direction += snap; return template.rotate(direction + delta, snap); }, 'OVERRIDE'); } - static handleDnD5eAbilityTemplate(abilityTemplate: any, handlers: DnD5eAbilityTemplateHandlers) { - handlers.mw = event => { - if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window - event.stopPropagation(); - const snapCount = SETTINGS.get('angle-snap-macro'); - let snap = 360 / snapCount; - if (event.shiftKey) - snap /= SETTINGS.get('angle-snap-micro'); + static handleDnD5eAbilityTemplate(this: any, event: any) { + /***************** THIS IS COPIED FROM THE DnD 5e CODE BASE `AbilityTemplate.prototype._onRotatePlacement `module/canvas/ability-template.mjs`` ***************/ + if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window + event.stopPropagation(); - const sign = Math.sign(event.deltaY); - let direction = abilityTemplate.data.direction; - if (direction < 0) direction += 360; - direction = direction - (direction % snap); - if (abilityTemplate.data.direction % snap !== 0 && sign < 0) - direction += snap; + /**** MODIFIED THIS REGION ****/ + const snapCount = SETTINGS.get('angle-snap-macro'); + let snap = 360 / snapCount; + if (event.shiftKey) + snap /= SETTINGS.get('angle-snap-micro'); + + const sign = Math.sign(event.deltaY); + let direction = this.document.direction; + if (direction < 0) direction += 360; + direction = direction - (direction % snap); + if (this.document.direction % snap !== 0 && sign < 0) + direction += snap; + this.document.updateSource({ direction: direction + (snap * sign) }); + /**** END OF MODIFICATION ****/ - abilityTemplate.data.update({ direction: direction + (snap * sign) }); - abilityTemplate.refresh(); - }; + this.refresh(); + /***************** END OF COPY ***************/ } } \ No newline at end of file diff --git a/df-templates/src/DnD5eAbilityTemplateHandlers.ts b/df-templates/src/DnD5eAbilityTemplateHandlers.ts deleted file mode 100644 index b8d5ae7..0000000 --- a/df-templates/src/DnD5eAbilityTemplateHandlers.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable semi */ -export default interface DnD5eAbilityTemplateHandlers { - mm?: (a: any) => void, - lc?: (a: any) => void, - rc?: (a: any) => void, - mw?: (a: any) => void -} diff --git a/df-templates/src/SnapIntersect.ts b/df-templates/src/SnapIntersect.ts index 74511ba..462bb26 100644 --- a/df-templates/src/SnapIntersect.ts +++ b/df-templates/src/SnapIntersect.ts @@ -1,5 +1,4 @@ import SETTINGS from "../../common/Settings"; -import DnD5eAbilityTemplateHandlers from "./DnD5eAbilityTemplateHandlers"; export default class SnapIntersect { static init() { @@ -30,21 +29,16 @@ export default class SnapIntersect { return canvas.grid.type === CONST.GRID_TYPES.GRIDLESS ? 0 : 1; } - static handleDnD5eAbilityTemplate(abilityTemplate: any, handlers: DnD5eAbilityTemplateHandlers) { - /***************** THIS IS COPIED FROM THE DnD 5e CODE BASE `AbilityTemplate.prototype.activatePreviewListeners` ***************/ - let moveTime = 0; - // Update placement (mouse-move) - handlers.mm = event => { - event.stopPropagation(); - const now = Date.now(); // Apply a 20ms throttle - if (now - moveTime <= 20) return; - const center = event.data.getLocalPosition(abilityTemplate.layer); - /**** MODIFIED THIS `getSnappedPosition` TO HAVE INTERVAL 1 INSTEAD OF 2 IF ENABLED ****/ - const snapped = canvas.grid.getSnappedPosition(center.x, center.y, SETTINGS.get('SnapIntersect') ? 1 : 2); - abilityTemplate.data.update({ x: snapped.x, y: snapped.y }); - abilityTemplate.refresh(); - moveTime = now; - }; + static handleDnD5eAbilityTemplate(this: any, event: any) { + /***************** THIS IS COPIED FROM THE DnD 5e CODE BASE `AbilityTemplate.prototype._onMovePlacement` `module/canvas/ability-template.mjs` ***************/ + const now = Date.now(); // Apply a 20ms throttle + if ( now - this._moveTime <= 20 ) return; + const center = event.data.getLocalPosition(this.layer); + /**** MODIFIED THIS `getSnappedPosition` TO HAVE INTERVAL 1 INSTEAD OF 2 IF ENABLED ****/ + const snapped = canvas.grid.getSnappedPosition(center.x, center.y, SETTINGS.get('SnapIntersect') ? 1 : 2); + this.document.updateSource({x: snapped.x, y: snapped.y}); + this.refresh(); + this._moveTime = now; /***************** END OF COPY ***************/ } } \ No newline at end of file diff --git a/df-templates/src/SquareTemplate.ts b/df-templates/src/SquareTemplate.ts index 35700f7..f205522 100644 --- a/df-templates/src/SquareTemplate.ts +++ b/df-templates/src/SquareTemplate.ts @@ -53,11 +53,11 @@ export default class SquareTemplate { private static MeasuredTemplate_refreshRulerText(this: MeasuredTemplate, wrapped: () => void): void { wrapped(); // Overwrite the text for the "rect" type - if (this.data.t === "rect") { + if (this.document.t === "rect") { // Use simple Pythagoras to calculate the square's size from the diagonal "distance". - const size = Math.sqrt((this.data.distance * this.data.distance) / 2).toFixed(1); - const text = `${size}${canvas.scene.data.gridUnits}`; - (this).hud.ruler.text = text; + const size = Math.sqrt((this.document.distance * this.document.distance) / 2).toFixed(1); + const text = `${size}${canvas.scene.grid.units}`; + (this).ruler.text = text; } } } \ No newline at end of file diff --git a/df-templates/src/TemplateConfig.ts b/df-templates/src/TemplateConfig.ts index 4f7f87e..246b810 100644 --- a/df-templates/src/TemplateConfig.ts +++ b/df-templates/src/TemplateConfig.ts @@ -41,7 +41,7 @@ export class TemplateConfig extends FormApplication { return this._options; } - static get defaultOptions(): FormApplication.Options { + static get defaultOptions(): FormApplicationOptions { const options = mergeObject(super.defaultOptions, { resizable: false, submitOnChange: false, @@ -93,13 +93,13 @@ export class TemplateConfig extends FormApplication { rect: old5ePatch ? HighlightMode.TOUCH : HighlightMode.CENTER, ray: old5ePatch ? HighlightMode.TOUCH : HighlightMode.CENTER }, - onChange: () => canvas.templates?.placeables.filter((t: MeasuredTemplate) => t.data.t === "circle") + onChange: () => canvas.templates?.placeables.filter((t: MeasuredTemplate) => t.document.t === "circle") .forEach((t: MeasuredTemplate) => t.draw()) }); SETTINGS.registerMenu('template-config', { restricted: true, - type: TemplateConfig, + type: TemplateConfig, label: "DF_TEMPLATES.TemplateConfig.Title" }); } diff --git a/df-templates/src/TemplateTargeting.ts b/df-templates/src/TemplateTargeting.ts index f9b4867..2d9358c 100644 --- a/df-templates/src/TemplateTargeting.ts +++ b/df-templates/src/TemplateTargeting.ts @@ -58,7 +58,7 @@ export default class TemplateTargeting { default: true, onChange: () => { if (SETTINGS.get(TemplateTargeting.TARGETING_MODE_PREF) !== 'toggle') return; } }); - SETTINGS.register(TemplateTargeting.TARGETING_MODE_PREF, { + SETTINGS.register(TemplateTargeting.TARGETING_MODE_PREF, { config: true, scope: 'world', name: 'DF_TEMPLATES.AutoTargetName', @@ -173,9 +173,9 @@ export default class TemplateTargeting { private static _MeasuredTemplate_highlightGrid(this: MeasuredTemplate) { const mode = SETTINGS.get(TemplateTargeting.TARGETING_MODE_PREF); const shouldAutoSelect = mode === 'always' || (mode === 'toggle' && SETTINGS.get(TemplateTargeting.TARGETING_TOGGLE_PREF)); - const isOwner = this.document.author.id === game.userId; + const isOwner = this.document.user.id === game.userId; // Release all previously targeted tokens - if (isOwner && shouldAutoSelect && canvas.tokens.objects) { + if ((this.hover || !this.id) && isOwner && shouldAutoSelect && canvas.tokens.objects) { for (const t of game.user.targets) { t.setTarget(false, { releaseOthers: false, groupSelection: true }); } @@ -224,11 +224,11 @@ export default class TemplateTargeting { const DEBUG = SETTINGS.get('template-debug'); // Only highlight for objects which have a defined shape - const id: string = this.id ?? (this)['_original']?.id; + const id: string = this.highlightId ?? (this)['_original']?.highlightId; if ((!this.id && !SETTINGS.get(TemplateTargeting.PREVIEW_PREF)) || !this.shape) return; // Clear existing highlight - const hl = grid.getHighlightLayer(`Template.${id ?? null}`); + const hl = grid.getHighlightLayer(id); hl?.clear(); // If we are in gridless mode, highlight the shape directly @@ -249,7 +249,7 @@ export default class TemplateTargeting { }// eslint-disable-next-line no-empty catch (error) { } grid.grid.highlightGridPosition(hl, { border, color: color, shape: shape }); - TemplateTargeting._selectTokensByPointContainment.bind(this)(isOwner, shouldAutoSelect, this.data, this.shape, true); + TemplateTargeting._selectTokensByPointContainment.bind(this)(isOwner, shouldAutoSelect, this, this.shape, true); return; } @@ -259,14 +259,14 @@ export default class TemplateTargeting { const rowCount = Math.ceil(shapeBounds.height() / grid.h) + 2; //? Add a padding ring around for any outlier cases // Get the offset of the template origin relative to the top-left grid space - const [tx, ty] = canvas.grid.getTopLeft(this.data.x, this.data.y); + const [tx, ty] = canvas.grid.getTopLeft(this.document.x, this.document.y); const [row0, col0] = grid.grid.getGridPositionFromPixels(shapeBounds.left + tx, shapeBounds.top + ty); const hx = canvas.grid.w / 2; const hy = canvas.grid.h / 2; /***** START OF CODE EDIT *****/ // Extract and prepare data - let { direction, distance, angle, width } = this.data; + let { direction, distance, angle, width } = this.document; distance *= (d.size / d.distance); width *= (d.size / d.distance); angle = Math.toRadians(angle); @@ -282,31 +282,31 @@ export default class TemplateTargeting { if (coneInitialized) return; coneInitialized = true; [ax1, ay1, bx1, by1] = [ - this.data.x, - this.data.y, - this.data.x + (Math.cos(direction - (angle / 2)) * rayLength), - this.data.y + (Math.sin(direction - (angle / 2)) * rayLength) + this.document.x, + this.document.y, + this.document.x + (Math.cos(direction - (angle / 2)) * rayLength), + this.document.y + (Math.sin(direction - (angle / 2)) * rayLength) ]; [ax2, ay2, bx2, by2] = [ - this.data.x, - this.data.y, - this.data.x + (Math.cos(direction + (angle / 2)) * rayLength), - this.data.y + (Math.sin(direction + (angle / 2)) * rayLength) + this.document.x, + this.document.y, + this.document.x + (Math.cos(direction + (angle / 2)) * rayLength), + this.document.y + (Math.sin(direction + (angle / 2)) * rayLength) ]; }; const generateRayData = () => { if (coneInitialized) return; [ax1, ay1] = [ - this.data.x + (Math.cos(direction - (Math.PI / 2)) * (width / 2)), - this.data.y + (Math.sin(direction - (Math.PI / 2)) * (width / 2)) + this.document.x + (Math.cos(direction - (Math.PI / 2)) * (width / 2)), + this.document.y + (Math.sin(direction - (Math.PI / 2)) * (width / 2)) ]; [bx1, by1] = [ ax1 + (Math.cos(direction) * distance), ay1 + (Math.sin(direction) * distance) ]; [ax2, ay2] = [ - this.data.x + (Math.cos(direction + (Math.PI / 2)) * (width / 2)), - this.data.y + (Math.sin(direction + (Math.PI / 2)) * (width / 2)) + this.document.x + (Math.cos(direction + (Math.PI / 2)) * (width / 2)), + this.document.y + (Math.sin(direction + (Math.PI / 2)) * (width / 2)) ]; [bx2, by2] = [ ax2 + (Math.cos(direction) * distance), @@ -321,12 +321,12 @@ export default class TemplateTargeting { const [gx, gy] = canvas.grid.grid.getPixelsFromGridPosition(row0 + r, col0 + c); const testX = gx + hx; const testY = gy + hy; - const testRect = new NormalizedRectangle(gx, gy, canvas.grid.w, canvas.grid.h); + const testRect = new PIXI.Rectangle(gx, gy, canvas.grid.w, canvas.grid.h).normalize(); let contains = false; - switch (this.data.t) { + switch (this.document.t) { case "circle": { // Calculate the vector from the PoI to the grid square center - const [rcx, rcy] = [testX - this.data.x, testY - this.data.y]; + const [rcx, rcy] = [testX - this.document.x, testY - this.document.y]; // If the distance between the centres is <= the circle's radius contains = ((rcx * rcx) + (rcy * rcy)) <= (distance * distance); if (contains || TemplateConfig.config.circle === HighlightMode.CENTER) break; @@ -334,7 +334,7 @@ export default class TemplateTargeting { const sqrDistance = distance * distance; let [vx, vy] = [0, 0]; const testPoint = (x: number, y: number) => { - [vx, vy] = [x - this.data.x, y - this.data.y]; + [vx, vy] = [x - this.document.x, y - this.document.y]; return (vx * vx + vy * vy) < sqrDistance; }; @@ -347,7 +347,7 @@ export default class TemplateTargeting { case "rect": { const rect = (this as any)._getRectShape(direction, distance, true); if (rect instanceof PIXI.Polygon) { - contains = this.shape.contains(testX - this.data.x, testY - this.data.y); + contains = this.shape.contains(testX - this.document.x, testY - this.document.y); if (contains || TemplateConfig.config.rect === HighlightMode.CENTER) break; /* Rectangle vertex data order A1───▶B1 @@ -356,7 +356,7 @@ export default class TemplateTargeting { A2◀───B2 */ // Translate points to the position of the MeasuredTemplate and map the points to the dataset - [ax1, ay1, bx1, by1, bx2, by2, ax2, ay2] = rect.points.map((e, i) => e + (i % 2 ? this.data.y : this.data.x)); + [ax1, ay1, bx1, by1, bx2, by2, ax2, ay2] = rect.points.map((e, i) => e + (i % 2 ? this.document.y : this.document.x)); // check the top line contains = LineToBoxCollision.cohenSutherlandLineClipAndDraw(ax1, ay1, bx1, by1, testRect) // check the right line @@ -366,8 +366,8 @@ export default class TemplateTargeting { // check the left line || LineToBoxCollision.cohenSutherlandLineClipAndDraw(ax2, ay2, ax1, ay1, testRect); } else { - rect.x += this.data.x; - rect.y += this.data.y; + rect.x += this.document.x; + rect.y += this.document.y; // The normalized rectangle always adds 1 to the width and height rect.width -= 1; rect.height -= 1; @@ -378,7 +378,7 @@ export default class TemplateTargeting { break; } case "cone": { - contains = this.shape.contains(testX - this.data.x, testY - this.data.y); + contains = this.shape.contains(testX - this.document.x, testY - this.document.y); if (contains || TemplateConfig.config.cone === HighlightMode.CENTER) break; generateConeData(); // check the top line @@ -394,7 +394,7 @@ export default class TemplateTargeting { let mag = 0; let vecAngle = 0; const testPoint = (x: number, y: number) => { - [vx, vy] = [x - this.data.x, y - this.data.y]; + [vx, vy] = [x - this.document.x, y - this.document.y]; return (vx * vx + vy * vy) < sqrDistance; }; const testAngle: () => boolean = () => { @@ -433,7 +433,7 @@ export default class TemplateTargeting { break; } case "ray": { - contains = this.shape.contains(testX - this.data.x, testY - this.data.y); + contains = this.shape.contains(testX - this.document.x, testY - this.document.y); if (contains || TemplateConfig.config.ray === HighlightMode.CENTER) break; generateRayData(); // check the top line @@ -457,16 +457,16 @@ export default class TemplateTargeting { if (!contains) continue; // Ignore changing the target selection if we don't own the template, or `shouldAutoSelect` is false - if (!isOwner || !shouldAutoSelect) continue; + if ((!this.hover && this.id) || !isOwner || !shouldAutoSelect) continue; // If we are using Point based targetting for this template - if (TemplateConfig.config[this.data.t] === HighlightMode.POINTS) { - TemplateTargeting._selectTokensByPointContainment.bind(this)(isOwner, shouldAutoSelect, this.data, this.shape, true); + if (TemplateConfig.config[this.document.t] === HighlightMode.POINTS) { + TemplateTargeting._selectTokensByPointContainment.bind(this)(isOwner, shouldAutoSelect, this.document, this.shape, true); continue; } // Iterate over all existing tokens and target the ones within the template area for (const token of canvas.tokens.placeables) { - const tokenRect = new NormalizedRectangle(token.x, token.y, token.w, token.h); + const tokenRect = new PIXI.Rectangle(token.x, token.y, token.w, token.h).normalize(); if (testRect.left >= tokenRect.right || testRect.right <= tokenRect.left || testRect.top >= tokenRect.bottom || testRect.bottom <= tokenRect.top) continue; token.setTarget(true, { user: game.user, releaseOthers: false, groupSelection: true }); diff --git a/df-templates/src/df-templates.ts b/df-templates/src/df-templates.ts index 1c75a73..0ab8961 100644 --- a/df-templates/src/df-templates.ts +++ b/df-templates/src/df-templates.ts @@ -1,6 +1,5 @@ import SETTINGS from "../../common/Settings"; import AngleSnaps from "./AngleSnaps"; -import DnD5eAbilityTemplateHandlers from "./DnD5eAbilityTemplateHandlers"; import SnapIntersect from "./SnapIntersect"; import SquareTemplate from "./SquareTemplate"; import TemplateTargeting from "./TemplateTargeting"; @@ -37,38 +36,12 @@ Hooks.once('ready', function () { if ((game as any).dnd5e) { libWrapper.register(SETTINGS.MOD_NAME, 'game.dnd5e.canvas.AbilityTemplate.prototype.activatePreviewListeners', - function (this: any, initialLayer: CanvasLayer) { - const handlers: DnD5eAbilityTemplateHandlers = {}; - - /***************** THIS IS COPIED FROM THE DnD 5e CODE BASE `AbilityTemplate.prototype.activatePreviewListeners` ***************/ - // Cancel the workflow (right-click) - handlers.rc = event => { - this.layer._onDragLeftCancel(event); - canvas.stage.off("mousemove", handlers.mm); - canvas.stage.off("mousedown", handlers.lc); - canvas.app.view.oncontextmenu = null; - canvas.app.view.onwheel = null; - initialLayer.activate(); - this.actorSheet?.maximize(); - }; - // Confirm the workflow (left-click) - handlers.lc = event => { - handlers.rc(event); - const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2); - this.data.update(destination); - canvas.scene.createEmbeddedDocuments("MeasuredTemplate", [this.data]); - }; - /***************** END OF COPY ***************/ - - SnapIntersect.handleDnD5eAbilityTemplate(this, handlers); - AngleSnaps.handleDnD5eAbilityTemplate(this, handlers); - - /***************** THIS IS COPIED FROM THE DnD 5e CODE BASE `AbilityTemplate.prototype.activatePreviewListeners` ***************/ - canvas.stage.on("mousemove", handlers.mm); - canvas.stage.on("mousedown", handlers.lc); - canvas.app.view.oncontextmenu = handlers.rc; - canvas.app.view.onwheel = handlers.mw; - /***************** END OF COPY ***************/ - }, 'OVERRIDE'); + function (this: any, wrapper: (il: any) => any, initialLayer: CanvasLayer) { + this._onMovePlacement_ORIG = this._onMovePlacement; + this._onMovePlacement = SnapIntersect.handleDnD5eAbilityTemplate.bind(this); + this._onRotatePlacement_ORIG = this._onRotatePlacement; + this._onRotatePlacement = AngleSnaps.handleDnD5eAbilityTemplate.bind(this); + return wrapper(initialLayer); + }, 'WRAPPER'); } });