From 22de35a79c4202878e65946d9f93fbe971c4394d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sim=C3=B5es?= Date: Mon, 2 Dec 2024 15:22:09 -0600 Subject: [PATCH] Remove system drag measurement implementation --- src/module/canvas/index.ts | 4 +- src/module/canvas/ruler.ts | 294 ---------------------------- src/module/canvas/token/object.ts | 49 +---- src/module/system/settings/index.ts | 3 - src/scripts/hooks/canvas-ready.ts | 9 - src/scripts/hooks/init.ts | 4 - src/scripts/register-keybindings.ts | 17 -- 7 files changed, 2 insertions(+), 378 deletions(-) delete mode 100644 src/module/canvas/ruler.ts diff --git a/src/module/canvas/index.ts b/src/module/canvas/index.ts index f7bd206f9d1..2dda7e4c089 100644 --- a/src/module/canvas/index.ts +++ b/src/module/canvas/index.ts @@ -12,7 +12,6 @@ import { TemplateLayerPF2e } from "./layer/template.ts"; import { TokenLayerPF2e } from "./layer/token.ts"; import { MeasuredTemplatePF2e } from "./measured-template.ts"; import { RegionPF2e } from "./region.ts"; -import { RulerPF2e } from "./ruler.ts"; import { TokenPF2e } from "./token/object.ts"; export type CanvasPF2e = Canvas< @@ -22,7 +21,7 @@ export type CanvasPF2e = Canvas< TokenPF2e>, EffectsCanvasGroupPF2e, RegionPF2e>, - RulerPF2e + Ruler >; export * from "./helpers.ts"; @@ -32,7 +31,6 @@ export { LightingLayerPF2e, MeasuredTemplatePF2e, RegionPF2e, - RulerPF2e, TemplateLayerPF2e, TokenLayerPF2e, TokenPF2e, diff --git a/src/module/canvas/ruler.ts b/src/module/canvas/ruler.ts deleted file mode 100644 index ada3a2392ea..00000000000 --- a/src/module/canvas/ruler.ts +++ /dev/null @@ -1,294 +0,0 @@ -import type { UserPF2e } from "@module/user/document.ts"; -import type { RegionDocumentPF2e, ScenePF2e } from "@scene"; -import type { EnvironmentFeatureRegionBehavior } from "@scene/region-behavior/types.ts"; -import * as R from "remeda"; -import type { TokenPF2e } from "./token/object.ts"; - -class RulerPF2e extends Ruler { - static override get canMeasure(): boolean { - return this.#dragMeasurement ? game.activeTool === "ruler" : super.canMeasure; - } - - /** Whether drag measurement is enabled */ - static get #dragMeasurement(): boolean { - const pointer = canvas.app.renderer.events.pointer; - if (pointer.ctrlKey || pointer.metaKey) return false; - const setting = game.pf2e.settings.dragMeasurement; - return setting === "always" || (setting === "encounters" && !!game.combat?.active); - } - - static get hasModuleConflict(): boolean { - return ["drag-ruler", "elevationruler", "pf2e-ruler"].some((id) => game.modules.get(id)?.active); - } - - /** The footprint of the drag-measured token relative to the origin center */ - #footprint: GridOffset[] = []; - - /** Grid spaces highlighted for the current measurement */ - #highlighted = new Set(); - - #exactDestination: Point | null = null; - - /** Whether drag measurement is currently in progress */ - isDragMeasuring = false; - - /** A grid-snapping mode appropriate for the token's dimensions */ - get #snapMode(): GridSnappingMode { - const token = this.token; - const M = CONST.GRID_SNAPPING_MODES; - if (!token || Math.max(token.document.width, 1) % 2 === 1) { - return M.CENTER; - } - - return M.BOTTOM_RIGHT_VERTEX; - } - - /** Whether drag measurement is enabled */ - get dragMeasurement(): boolean { - if (!RulerPF2e.#dragMeasurement) return false; - if (this.isMeasuring && !this.isDragMeasuring) return false; - return game.activeTool === "ruler" || canvas.tokens.controlled.length <= 1; - } - - get isMeasuring(): boolean { - return this.state === RulerPF2e.STATES.MEASURING; - } - - /** Add a waypoint at the currently-drawn destination. */ - saveWaypoint(): void { - if (!this.isDragMeasuring) return; - - const point = this.destination; - if (point) { - const snap = !game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.SHIFT); - this._addWaypoint(point, { snap }); - } - } - - startDragMeasurement(event: TokenPointerEvent>): void { - const token = event.interactionData.object; - if (!this.dragMeasurement || !token || game.activeTool === "ruler") { - return; - } - this.isDragMeasuring = true; - token.document.locked = true; - return this._startMeasurement(token.center, { snap: !event.shiftKey, token }); - } - - /** - * @param exactDestination?: The coordinates of the dragged token preview, if any - */ - async finishDragMeasurement( - event: TokenPointerEvent>, - exactDestination: Point | null = null, - ): Promise { - if (!this.isDragMeasuring) return; - if (!this.token) return this._endMeasurement(); - - this.token.document.locked = this.token.document._source.locked; - canvas.mouseInteractionManager.cancel(); - // If snapping, adjust the token document's current position to prevent upstream from "correcting" it - if (!event.shiftKey) { - const { x, y } = this.token.getSnappedPosition(); - this.token.document.x = x; - this.token.document.y = y; - } - // Special consideration for tiny tokens: allow them to move within a square - this.#exactDestination = exactDestination; - if (exactDestination && this.token.document.width < 1) { - const lastSegment = this.segments.at(-1); - if (lastSegment) { - lastSegment.ray = new Ray(R.pick(this.token, ["x", "y"]), exactDestination); - } - } - return this.moveToken(); - } - - /** Acquire the token's footprint for drag measurement. */ - override measure( - destination: Point, - options?: { snap?: boolean; force?: boolean }, - ): void | RulerMeasurementSegment[] { - if (this.isDragMeasuring && this.token && this.origin) { - const offset = canvas.grid.getOffset(this.origin); - this.#footprint = this.token.footprint.map((o) => ({ i: o.i - offset.i, j: o.j - offset.j })); - } - return super.measure(destination, options); - } - - /** Allow GMs to move tokens through walls when drag-measuring. */ - protected override _canMove(token: TToken): boolean { - if (!game.user.isGM || !this.isDragMeasuring) return super._canMove(token); - try { - return super._canMove(token); - } catch (error) { - if (error instanceof Error && error.message === "RULER.MovementCollision") { - return true; - } else { - throw error; - } - } - } - - /** Prevent inclusion of a token when using the ruler tool. */ - protected override _startMeasurement(origin: Point, options: { snap?: boolean; token?: TToken | null } = {}): void { - if (game.activeTool === "ruler" && this.isDragMeasuring) { - options.token = null; - } - return super._startMeasurement(origin, options); - } - - /** Calculate cost as an addend to distance due to difficult terrain. */ - protected override _getCostFunction(): GridMeasurePathCostFunction | undefined { - const isCreature = !!this.token?.actor?.isOfType("creature"); - if (!this.isDragMeasuring || !isCreature || canvas.regions.placeables.length === 0) { - return; - } - - return (_from: GridOffset, to: GridOffset, distance: number): number => { - const token = canvas.controls.ruler.token; - if (!token) return 0; - - const toPoint = canvas.grid.getTopLeftPoint(to); - const difficultBehaviors = canvas.regions.placeables - .filter( - (r) => - r.document.behaviors.some( - (b) => !b.disabled && b.type === "environmentFeature" && b.system.terrain.difficult > 0, - ) && token.testInsideRegion(r, toPoint), - ) - .flatMap((r) => - r.document.behaviors.filter( - (b): b is EnvironmentFeatureRegionBehavior> => - !b.disabled && b.type === "environmentFeature" && b.system.terrain.difficult > 0, - ), - ); - - return difficultBehaviors.length > 0 - ? difficultBehaviors.some((b) => b.system.terrain.difficult === 2) - ? distance + 10 - : distance + 5 - : distance; - }; - } - - protected override _getMeasurementOrigin(point: Point, options?: { snap?: boolean }): Point { - if (!this.isDragMeasuring || !this.token || options?.snap === false) { - return super._getMeasurementOrigin(point, options); - } - return canvas.grid.getSnappedPoint(point, { mode: this.#snapMode }); - } - - protected override _getMeasurementDestination(point: Point, options?: { snap?: boolean }): Point { - if (!this.isDragMeasuring || !this.token || options?.snap === false) { - return super._getMeasurementDestination(point, options); - } - return canvas.grid.getSnappedPoint(point, { mode: this.#snapMode }); - } - - /** Widen the ruler when measuring with larger tokens. */ - protected override _highlightMeasurementSegment(segment: RulerMeasurementSegment): void { - const token = this.token; - if (segment.teleport || !this.isDragMeasuring || !token) { - return super._highlightMeasurementSegment(segment); - } - - this.#highlighted.clear(); - if (canvas.grid.isHexagonal) { - this.#highlightHexSegment(segment); - } else { - this.#highlightSegment(segment); - } - } - - #highlightSegment(segment: RulerMeasurementSegment): void { - // Keep track of grid spaces set to be highlighed in order to skip repeated highlighting - for (const offset of canvas.grid.getDirectPath([segment.ray.A, segment.ray.B])) { - for (const stomp of this.#footprint) { - const newOffset = { i: offset.i + stomp.i, j: offset.j + stomp.j }; - const packed = (newOffset.i << 16) + newOffset.j; - if (!this.#highlighted.has(packed)) { - this.#highlighted.add(packed); - const point = canvas.grid.getTopLeftPoint(newOffset); - canvas.interface.grid.highlightPosition(this.name, { ...point, color: this.color }); - } - } - } - } - - /** "Fat ruler" highlighting not yet supported for hexagonal grids */ - #highlightHexSegment(segment: RulerMeasurementSegment): void { - // Keep track of grid spaces set to be highlighed in order to skip repeated highlighting - for (const offset of canvas.grid.getDirectPath([segment.ray.A, segment.ray.B])) { - const packed = (offset.i << 16) + offset.j; - if (!this.#highlighted.has(packed)) { - this.#highlighted.add(packed); - const point = canvas.grid.getTopLeftPoint(offset); - canvas.interface.grid.highlightPosition(this.name, { ...point, color: this.color }); - } - } - } - - protected override _animateSegment( - token: TToken, - segment: RulerMeasurementSegment, - destination: Point, - ): Promise { - if (this.isDragMeasuring && this.#exactDestination && segment === this.segments.at(-1)) { - const exactDestination = this.#exactDestination; - this.#exactDestination = null; - return super._animateSegment(token, segment, exactDestination); - } - return super._animateSegment(token, segment, destination); - } - - /** If measuring with a token, broadcast if the token is not hidden and only during encounters. */ - protected override _broadcastMeasurement(): void { - if (this.token?.document.hidden) return; - if (!this.isDragMeasuring || game.activeTool === "ruler" || game.combat?.started) { - return super._broadcastMeasurement(); - } - } - - protected override _endMeasurement(): void { - super._endMeasurement(); - this.isDragMeasuring = false; - } - - /* -------------------------------------------- */ - /* Event Listeners and Handlers */ - /* -------------------------------------------- */ - - /** Prevent behavior from keybind modifiers if token drag measurement is enabled. */ - override _onMouseUp(event: PlaceablesLayerPointerEvent>): void { - if (this.isDragMeasuring) { - event.ctrlKey = false; - event.metaKey = false; - if (game.activeTool === "ruler") return this._endMeasurement(); - } - return super._onMouseUp(event); - } - - /** Prevent behavior from movement keys (typically Space) if token drag measurement is enabled. */ - override _onMoveKeyDown(context: KeyboardEventContext): void { - if (!this.isDragMeasuring) super._onMoveKeyDown(context); - } - - onDragMeasureMove(event: TokenPointerEvent>): void { - if (this.isDragMeasuring) this._onMouseMove(event); - } - - onDragLeftCancel(event?: TokenPointerEvent>): void { - if (!this.isDragMeasuring) return; - - this._removeWaypoint(); - // Prevent additional events from firing for dragged token - if (this.isMeasuring) { - event?.preventDefault(); - } else { - canvas.mouseInteractionManager.cancel(); - } - } -} - -export { RulerPF2e }; diff --git a/src/module/canvas/token/object.ts b/src/module/canvas/token/object.ts index 540ad82fd96..f14ee8065a9 100644 --- a/src/module/canvas/token/object.ts +++ b/src/module/canvas/token/object.ts @@ -3,7 +3,7 @@ import type { UserPF2e } from "@module/user/document.ts"; import type { TokenDocumentPF2e } from "@scene"; import * as R from "remeda"; import type { CanvasPF2e, TokenLayerPF2e } from "../index.ts"; -import { RulerPF2e, measureDistanceCuboid, squareAtPoint } from "../index.ts"; +import { measureDistanceCuboid, squareAtPoint } from "../index.ts"; import { AuraRenderers } from "./aura/index.ts"; import { FlankingHighlightRenderer } from "./flanking-highlight/renderer.ts"; @@ -68,11 +68,6 @@ class TokenPF2e extends return offsets.sort((a, b) => a.j - b.j).sort((a, b) => a.i - b.i); } - get #isDragMeasuring(): boolean { - const ruler = canvas.controls.ruler; - return !!ruler.isDragMeasuring && ruler.token === this; - } - /** Increase center-to-center point tolerance to be more compliant with 2e rules */ override get isVisible(): boolean { // Clear the detection filter @@ -579,48 +574,6 @@ class TokenPF2e extends return super._onRelease(options); } - /** Initiate token drag measurement unless using the ruler tool. */ - protected override _onDragLeftStart(event: TokenPointerEvent): void { - event.interactionData.clones ??= []; - const hasModuleConflict = RulerPF2e.hasModuleConflict; - if (game.activeTool !== "ruler" || hasModuleConflict) { - if (!hasModuleConflict) canvas.controls.ruler.startDragMeasurement(event); - return super._onDragLeftStart(event); - } - } - - protected override _onDragLeftMove(event: TokenPointerEvent): void { - if (this.#isDragMeasuring) { - canvas.controls.ruler.onDragMeasureMove(event); - } - return super._onDragLeftMove(event); - } - - protected override async _onDragLeftDrop(event: TokenPointerEvent): Promise { - if (this.#isDragMeasuring) { - // Pass along exact destination coordinates if this token is tiny - const destination = - this.isTiny && event.interactionData.clones?.length - ? R.pick(event.interactionData.clones[0], ["x", "y"]) - : null; - canvas.controls.ruler.finishDragMeasurement(event, destination); - this.layer.clearPreviewContainer(); - } else { - super._onDragLeftDrop(event); - } - } - - protected override _onDragLeftCancel(event: TokenPointerEvent): void { - if (this.#isDragMeasuring) { - canvas.controls.ruler.onDragLeftCancel(event); - if (!this.#isDragMeasuring) { - super._onDragLeftCancel(event); - } - } else { - super._onDragLeftCancel(event); - } - } - /** Handle system-specific status effects (upstream handles invisible and blinded) */ override _onApplyStatusEffect(statusId: string, active: boolean): void { super._onApplyStatusEffect(statusId, active); diff --git a/src/module/system/settings/index.ts b/src/module/system/settings/index.ts index 076d2b54bb4..fc1b5ad3bfe 100644 --- a/src/module/system/settings/index.ts +++ b/src/module/system/settings/index.ts @@ -1,7 +1,6 @@ import { resetActors } from "@actor/helpers.ts"; import { ActorSheetPF2e } from "@actor/sheet/base.ts"; import { ItemSheetPF2e, type ItemPF2e } from "@item"; -import { RulerPF2e } from "@module/canvas/ruler.ts"; import { StatusEffects } from "@module/canvas/status-effects.ts"; import { MigrationRunner } from "@module/migration/runner/index.ts"; import { isImageOrVideoPath, tupleHasValue } from "@util"; @@ -303,8 +302,6 @@ export function registerSettings(): void { // Called from hook to ensure keybindings are available Hooks.once("canvasInit", () => { - if (RulerPF2e.hasModuleConflict) return; - const placeWaypointKey = ((): string => { const action = game.keybindings.bindings.get("pf2e.placeWaypoint")?.at(0); return action ? KeybindingsConfig._humanizeBinding(action) : ""; diff --git a/src/scripts/hooks/canvas-ready.ts b/src/scripts/hooks/canvas-ready.ts index 87673934a1d..79596019517 100644 --- a/src/scripts/hooks/canvas-ready.ts +++ b/src/scripts/hooks/canvas-ready.ts @@ -11,15 +11,6 @@ export const CanvasReady = { game.pf2e.effectTracker.register(effect); } } - - // Work around `MouseInteractionManager` preventing right-click propagation to ruler - document.body.addEventListener("contextmenu", (event) => { - const ruler = canvas.controls.ruler; - if (canvas.ready && game.activeTool === "ruler" && ruler.dragMeasurement && ruler.isMeasuring) { - event.preventDefault(); - canvas.controls.ruler.onDragLeftCancel(); - } - }); }); Hooks.on("canvasReady", () => { diff --git a/src/scripts/hooks/init.ts b/src/scripts/hooks/init.ts index 070a266c6db..52a99e18fdf 100644 --- a/src/scripts/hooks/init.ts +++ b/src/scripts/hooks/init.ts @@ -7,7 +7,6 @@ import { ItemDirectoryPF2e, } from "@module/apps/sidebar/index.ts"; import { setPerceptionModes } from "@module/canvas/perception/modes.ts"; -import { RulerPF2e } from "@module/canvas/ruler.ts"; import { TokenConfigPF2e } from "@scene/token-document/sheet.ts"; import { PF2ECONFIG } from "@scripts/config/index.ts"; import { registerHandlebarsHelpers } from "@scripts/handlebars.ts"; @@ -47,9 +46,6 @@ export const Init = { CONFIG.Token.prototypeSheetClass = TokenConfigPF2e; } - // Set after load in case of module conflicts - if (!RulerPF2e.hasModuleConflict) CONFIG.Canvas.rulerClass = RulerPF2e; - // The condition in Pathfinder 2e is "blinded" rather than "blind" CONFIG.specialStatusEffects.BLIND = "blinded"; diff --git a/src/scripts/register-keybindings.ts b/src/scripts/register-keybindings.ts index d2c73550a61..847741da196 100644 --- a/src/scripts/register-keybindings.ts +++ b/src/scripts/register-keybindings.ts @@ -1,5 +1,4 @@ import type { PartyPF2e } from "@actor"; -import { RulerPF2e } from "@module/canvas/ruler.ts"; export function registerKeybindings(): void { game.keybindings.register("pf2e", "cycle-token-stack", { @@ -42,22 +41,6 @@ export function registerKeybindings(): void { }, }); - if (!RulerPF2e.hasModuleConflict) { - game.keybindings.register("pf2e", "placeWaypoint", { - name: "PF2E.Keybinding.PlaceWaypoint.Label", - hint: "PF2E.Keybinding.PlaceWaypoint.Hint", - editable: [{ key: "KeyX", modifiers: [] }], - onUp: (): boolean | null => { - if (canvas.ready && canvas.controls.ruler.isMeasuring && game.pf2e.settings.dragMeasurement) { - canvas.controls.ruler.saveWaypoint(); - return true; - } else { - return false; - } - }, - }); - } - // Defer to the GM Vision module if enabled if (!game.modules.get("gm-vision")?.active) { game.keybindings.register("pf2e", "gmVision", {