From 5bb12ff31f3b7a32a93fed3c90fcc7cc08952166 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 14:07:55 -0800 Subject: [PATCH 01/11] Add Patcher and ModuleSettingsAbstract classes --- scripts/ModuleSettingsAbstract.js | 101 +++++++ scripts/Patcher.js | 453 ++++++++++++++++++++++++++++++ 2 files changed, 554 insertions(+) create mode 100644 scripts/ModuleSettingsAbstract.js create mode 100644 scripts/Patcher.js diff --git a/scripts/ModuleSettingsAbstract.js b/scripts/ModuleSettingsAbstract.js new file mode 100644 index 0000000..0862df7 --- /dev/null +++ b/scripts/ModuleSettingsAbstract.js @@ -0,0 +1,101 @@ +/* globals +game, +Settings +*/ +/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ +"use strict"; + +import { MODULE_ID } from "./const.js"; + +// Patches for the Setting class +export const PATCHES = {}; +PATCHES.BASIC = {}; + +// ----- NOTE: Hooks ----- // + +/** + * Wipe the settings cache on update + */ +function updateSetting(document, change, options, userId) { // eslint-disable-line no-unused-vars + const [module, ...arr] = document.key.split("."); + const key = arr.join("."); // If the key has periods, multiple will be returned by split. + if ( module === MODULE_ID && Settings.cache.has(key) ) Settings.cache.delete(key); +} + +PATCHES.BASIC.HOOKS = { updateSetting }; + +export class ModuleSettingsAbstract { + /** @type {Map} */ + static cache = new Map(); + + /** @type {object} */ + static KEYS = {}; + + // ---- NOTE: Settings static methods ---- // + + /** + * Retrive a specific setting. + * Cache the setting. For caching to work, need to clean the cache whenever a setting below changes. + * @param {string} key + * @returns {*} + */ + static get(key) { + // TODO: Bring back a working cache. + + const cached = this.cache.get(key); + if ( typeof cached !== "undefined" ) { + const origValue = game.settings.get(MODULE_ID, key); + if ( origValue !== cached ) { + console.debug(`Settings cache fail: ${origValue} !== ${cached} for key ${key}`); + return origValue; + } + + return cached; + + } + const value = game.settings.get(MODULE_ID, key); + this.cache.set(key, value); + return value; + } + + /** + * Set a specific setting. + * @param {string} key + * @param {*} value + * @returns {Promise} + */ + static async set(key, value) { + this.cache.delete(key); + return game.settings.set(MODULE_ID, key, value); + } + + static async toggle(key) { + const curr = this.get(key); + return this.set(key, !curr); + } + + /** + * Register a specific setting. + * @param {string} key Passed to registerMenu + * @param {object} options Passed to registerMenu + */ + static register(key, options) { game.settings.register(MODULE_ID, key, options); } + + /** + * Register a submenu. + * @param {string} key Passed to registerMenu + * @param {object} options Passed to registerMenu + */ + static registerMenu(key, options) { game.settings.registerMenu(MODULE_ID, key, options); } + + /** + * Localize a setting key. + * @param {string} key + */ + static localize(key) { return game.i18n.localize(`${MODULE_ID}.settings.${key}`); } + + /** + * Register all settings + */ + static registerAll() {} +} diff --git a/scripts/Patcher.js b/scripts/Patcher.js new file mode 100644 index 0000000..9d0eb9d --- /dev/null +++ b/scripts/Patcher.js @@ -0,0 +1,453 @@ +/* globals +CONFIG, +Hooks, +libWrapper +*/ +/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ +"use strict"; + +import { MODULE_ID } from "./const.js"; + +/** + * Class to control patching: libWrapper, hooks, added methods. + * Patcher is primarily used to register arbitrary groups of patches. + * Patcher can also register/deregister specific patches. + */ +export class Patcher { + + /** @type {Set} */ + registeredGroups = new Set(); + + /** @type {WeakSet} */ + registeredPatches = new WeakSet(); + + /** @type {Map} */ + groupings = new Map(); + + /** @type {Set} */ + patches = new Set(); + + groupIsRegistered(groupName) { return this.registeredGroups.has(groupName); } + + /** @type {Set} */ + groupPatches(groupName) { + if ( !this.groupings.has(groupName) ) this.groupings.set(groupName, new Set()); + return this.groupings.get(groupName); + } + + /** + * Add new patch to track. + * @param {PatchAbstract} patch Patch to add to patch groups tracked by Patcher + * @param {boolean} [register=true] Whether to register the patch if group is registered + */ + addPatch(patch, register = true) { + this.patches.add(patch); + this.groupPatches(patch.group).add(patch); + if ( register && this.groupIsRegistered(patch.group) ) this.registerPatch(patch); + } + + /** + * Remove a patch from the tracker. + * If the patch is registered, deregister. + * @param {PatchAbstract} patch Patch to remove from patch groups tracked by Patcher + * @param {boolean} [deregister=true] Whether to deregister the patch when removing it + */ + removePatch(patch, deregister = true) { + if ( !this.patches.has(patch) ) return; + if ( deregister && this.registeredPatches.has(patch) ) this.deregisterPatch(patch); + this.patches.delete(patch); + const patchGroup = this.groupPatches(patch.group); + patchGroup.delete(patch); + + // If last patch in a group is removed, mark the group as unregistered. + if ( !patchGroup.size ) this.registeredGroups.delete(patch.group); + } + + /** + * Register this patch. + * If the patch is not in Patcher, add it. + * This does not affect group registration. I.e., if the patch group is not registered, + * this patch (but not its group) will be registered. + * @param {PatchAbstract} patch Patch to register + */ + registerPatch(patch) { + if ( !this.patches.has(patch) ) this.addPatch(patch); + if ( this.registeredPatches.has(patch) ) return; + patch.register(); + this.registeredPatches.add(patch); + } + + /** + * Deregister this patch. + * @param {PatchAbstract} patch Patch to deregister + */ + deregisterPatch(patch) { + if ( !this.registeredPatches.has(patch) ) return; + patch.deregister(); + this.registeredPatches.delete(patch); + } + + /** + * Register a grouping of patches. + * @param {string} groupName Name of group to register + */ + registerGroup(groupName) { + if ( this.groupIsRegistered(groupName) || !this.groupings.has(groupName) ) return; + this.groupings.get(groupName).forEach(patch => this.registerPatch(patch)); + this.registeredGroups.add(groupName); + } + + /** + * Deregister a grouping of patches. + * @param {string} groupName Name of group to deregister + */ + deregisterGroup(groupName) { + if ( !this.groupIsRegistered(groupName) ) return; + this.groupings.get(groupName).forEach(patch => this.deregisterPatch(patch)); + this.registeredGroups.delete(groupName); + } + + /** + * Primarily for backward compatibility. + * Given an object of class names, register patches for each. + * - className0 + * - groupNameA + * - WRAPS, METHODS, etc. + * - method/hook + * - function + * @param {registrationObject} regObj + */ + addPatchesFromRegistrationObject(regObj) { + // Cannot use mergeObject because it breaks for names like "PIXI.Circle". + for ( const [clName, patchClass] of Object.entries(regObj) ) { + for ( const [groupName, patchGroup] of Object.entries(patchClass) ) { + for ( const [typeName, patchType] of Object.entries(patchGroup) ) { + for ( const [patchName, patch] of Object.entries(patchType) ) { + let patchCl; + let cfg = { + group: groupName, + perf_mode: libWrapper.PERF_FAST, + className: clName, + isStatic: typeName.includes("STATIC") }; + switch ( typeName ) { + case "HOOKS": patchCl = HookPatch; break; + case "STATIC_OVERRIDES": // eslint-disable-line no-fallthrough + case "OVERRIDES": + case "STATIC_MIXES": + case "MIXES": + case "STATIC_WRAPS": + case "WRAPS": + patchCl = LibWrapperPatch; + cfg.libWrapperType = typeName.includes("OVERRIDES") + ? libWrapper.OVERRIDE : typeName.includes("MIXES") + ? libWrapper.MIXED : libWrapper.WRAPPER; + break; + case "STATIC_GETTERS": // eslint-disable-line no-fallthrough + case "GETTERS": + cfg.isGetter = true; + default: // eslint-disable-line no-fallthrough + patchCl = MethodPatch; + } + const thePatch = patchCl.create(patchName, patch, cfg); + this.addPatch(thePatch); + } + } + } + } + } + + /** + * Add a method or a getter to a class. + * @param {class} cl Either Class.prototype or Class + * @param {string} name Name of the method + * @param {function} fn Function to use for the method + * @param {object} [opts] Optional parameters + * @param {boolean} [opts.getter] True if the property should be made a getter. + * @param {boolean} [opts.optional] True if the getter should not be set if it already exists. + * @returns {undefined|object Date: Sun, 31 Dec 2023 14:18:27 -0800 Subject: [PATCH 02/11] Update lib geometry --- Changelog.md | 3 +++ scripts/geometry | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f3a0dc9..89a6051 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,6 @@ +# 0.7.3 +Updated lib-geometry to 0.2.12. + # 0.7.2 Updated lib-geometry to 0.2.2. diff --git a/scripts/geometry b/scripts/geometry index 3e8b52e..c36f3a3 160000 --- a/scripts/geometry +++ b/scripts/geometry @@ -1 +1 @@ -Subproject commit 3e8b52e975f18205b353be942a2ffee8f4f30268 +Subproject commit c36f3a31e9430a10fd95a53a2214b93872c01748 From 8f732dce6a9c07804879e1a3257489fae981d000 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 14:28:35 -0800 Subject: [PATCH 03/11] Add Ruler class to Patcher --- scripts/Patcher.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/Patcher.js b/scripts/Patcher.js index 9d0eb9d..d59868b 100644 --- a/scripts/Patcher.js +++ b/scripts/Patcher.js @@ -190,9 +190,12 @@ export class Patcher { * @returns {class} */ static lookupByClassName(className, { returnPathString = false } = {}) { + if ( className === "Ruler" ) return returnPathString ? "CONFIG.Canvas.rulerClass" : CONFIG.Canvas.rulerClass; let isDoc = className.endsWith("Document"); let isConfig = className.endsWith("Config"); - let baseClass = isDoc ? className.replace("Document", "") : isConfig ? className.replace("Config", "") : className; + let baseClass = isDoc ? className.replace("Document", "") + : isConfig ? className.replace("Config", "") + : className; const configObj = CONFIG[baseClass]; if ( !configObj || isConfig ) return returnPathString ? className : eval?.(`"use strict";(${className})`); From 0d2573f2998bf5102f2418ca178e3d0e98ce3879 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 15:05:23 -0800 Subject: [PATCH 04/11] Refactor for Patcher --- scripts/GridLayer.js | 36 +++++++++ scripts/Token.js | 43 ++++++++++ scripts/const.js | 17 ++++ scripts/module.js | 9 ++- scripts/patching.js | 95 ++++------------------ scripts/ruler.js | 146 ++++++++++++++++++++++------------ scripts/segments.js | 147 +++++++++-------------------------- scripts/terrain_elevation.js | 11 +-- 8 files changed, 259 insertions(+), 245 deletions(-) create mode 100644 scripts/GridLayer.js create mode 100644 scripts/Token.js diff --git a/scripts/GridLayer.js b/scripts/GridLayer.js new file mode 100644 index 0000000..beed0e1 --- /dev/null +++ b/scripts/GridLayer.js @@ -0,0 +1,36 @@ +/* globals +GridLayer +*/ +/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ + +import { Ray3d } from "./geometry/3d/Ray3d.js"; + +// Patches for the GridLayer class +export const PATCHES = {}; +PATCHES.BASIC = {}; + +/** + * Wrap GridLayer.prototype.measureDistances + * Called by Ruler.prototype._computeDistance + * If a segment ray has a z-dimension, re-do the segment by projecting the hypotenuse + * between the ray A and B endpoints in 3d onto the 2d canvas. Use the projected + * hypotenuse to do the measurement. + */ +export function measureDistancesGridLayer(wrapped, segments, options = {}) { + if ( !segments.length || !(segments[0]?.ray instanceof Ray3d) ) return wrapped(segments, options); + + // Avoid modifying the segment rays. + const ln = segments.length; + const origRays = Array(ln); + for ( let i = 0; i < ln; i += 1 ) { + const s = segments[i]; + origRays[i] = s.ray; + s.ray = s.ray.projectOntoCanvas(); + } + + const out = wrapped(segments, options); + for ( let i = 0; i < ln; i += 1 ) segments[i].ray = origRays[i]; + return out; +} + +PATCHES.BASIC.WRAPS = { GridLayer }; diff --git a/scripts/Token.js b/scripts/Token.js new file mode 100644 index 0000000..38e41dd --- /dev/null +++ b/scripts/Token.js @@ -0,0 +1,43 @@ +/* globals +canvas +*/ +/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ + +import { elevationAtWaypoint } from "./segments.js"; + +// Patches for the Token class +export const PATCHES = {}; +PATCHES.DRAG_RULER = {}; + +/** + * Wrap Token.prototype._onDragLeftDrop + * If Drag Ruler is active, use this to update token(s) after movement has completed. + * Callback actions which occur on a mouse-move operation. + * @see MouseInteractionManager#_handleDragDrop + * @param {PIXI.InteractionEvent} event The triggering canvas interaction event + * @returns {Promise<*>} + */ +async function _onDragLeftDrop(wrapped, event) { + // Assume the destination elevation is the desired elevation if dragging multiple tokens. + // (Likely more useful than having a bunch of tokens move down 10'?) + const ruler = canvas.controls.ruler; + if ( !ruler.isDragRuler ) return wrapped(event); + + // Do before calling wrapper b/c ruler may get cleared. + const elevation = elevationAtWaypoint(ruler.destination); + const selectedTokens = [...canvas.tokens.controlled]; + if ( !selectedTokens.length ) selectedTokens.push(ruler.draggedEntity); + + const result = wrapped(event); + if ( result === false ) return false; // Drag did not happen + + const updates = selectedTokens.map(t => { + return { _id: t.id, elevation }; + }); + + const t0 = selectedTokens[0]; + await t0.scene.updateEmbeddedDocuments(t0.constructor.embeddedName, updates); + return true; +} + +PATCHES.DRAG_RULER.WRAPS = { _onDragLeftDrop }; diff --git a/scripts/const.js b/scripts/const.js index 342a756..da28bb9 100644 --- a/scripts/const.js +++ b/scripts/const.js @@ -5,3 +5,20 @@ export const MODULE_ID = "elevationruler"; export const EPSILON = 1e-08; + +export const MODULES_ACTIVE = { + DRAG_RULER: false, + ELEVATED_VISION: false, + ENHANCED_TERRAIN_LAYER: false, + LEVELS: false +}; + +// Hook init b/c game.modules is not initialized at start. +Hooks.once("init", function() { + MODULES_ACTIVE.DRAG_RULER = game.modules.get("drag-ruler")?.active; + MODULES_ACTIVE.ENHANCED_TERRAIN_LAYER = game.modules.get("enhanced-terrain-layer")?.active; + MODULES_ACTIVE.LEVELS = game.modules.get("levels")?.active; + MODULES_ACTIVE.ELEVATED_VISION = game.modules.get("elevatedvision")?.active; +}); + + diff --git a/scripts/module.js b/scripts/module.js index 81afd8f..0d39d8f 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -8,7 +8,7 @@ ui "use strict"; import { registerSettings, registerKeybindings, SETTINGS, getSetting, setSetting } from "./settings.js"; -import { registerRuler } from "./patching.js"; +import { initializePatching, PATCHER } from "./patching.js"; import { MODULE_ID } from "./const.js"; // For Drag Ruler @@ -45,6 +45,13 @@ const PREFER_TOKEN_CONTROL = { Hooks.once("init", function() { // Cannot access localization until init. PREFER_TOKEN_CONTROL.title = game.i18n.localize(PREFER_TOKEN_CONTROL.title); + game.modules.get(MODULE_ID).api = { + PATCHER + } +}); + +Hooks.once("setup", function() { + initializePatching(); }); // Render the prefer token control if that setting is enabled diff --git a/scripts/patching.js b/scripts/patching.js index 8b923fe..a96461a 100644 --- a/scripts/patching.js +++ b/scripts/patching.js @@ -4,88 +4,23 @@ libWrapper */ "use strict"; -import { MODULE_ID } from "./const.js"; -import { log } from "./util.js"; -import { - clearRuler, - _addWaypointRuler, - dragRulerAddWaypointDragRulerRuler, - dragRulerClearWaypointsDragRuleRuler, - _removeWaypointRuler, - incrementElevation, - decrementElevation, - toJSONRuler, - updateRuler } from "./ruler.js"; +import { Patcher } from "./Patcher.js"; +import { MODULES_ACTIVE } from "./const.js"; -import { - _getMeasurementSegmentsRuler, - _getMeasurementSegmentsDragRulerRuler, - measureDistancesGridLayer, - _getSegmentLabelRuler, - _animateSegmentRuler, - _onDragLeftDropToken } from "./segments.js"; +import { PATCHES as PATCHES.Ruler } from "./Ruler.js"; +import { PATCHES as PATCHES.Token } from "./Token.js"; +import { PATCHES as PATCHES.GridLayer } from "./GridLayer.js"; -import { - terrainElevationAtPoint, - terrainElevationAtDestination, - elevationAtOrigin } from "./terrain_elevation.js"; +const PATCHES = { + GridLayer: PATCHES_GridLayer, + Ruler: PATCHES_Ruler, + Token: PATCHES_Token +}; -/** - * Helper to wrap methods. - * @param {string} method Method to wrap - * @param {function} fn Function to use for the wrap - * @param {object} [options] Options passed to libWrapper.register. E.g., { perf_mode: libWrapper.PERF_FAST} - */ -function wrap(method, fn, options = {}) { libWrapper.register(MODULE_ID, method, fn, libWrapper.WRAPPER, options); } +export const PATCHER = new Patcher(); +PATCHER.addPatchesFromRegistrationObject(PATCHES); -/** - * Helper to add a method to a class. - * @param {class} cl Either Class.prototype or Class - * @param {string} name Name of the method - * @param {function} fn Function to use for the method - */ -function addClassMethod(cl, name, fn) { - Object.defineProperty(cl, name, { - value: fn, - writable: true, - configurable: true - }); -} - -export function registerRuler() { - - // Basic ruler methods - wrap("CONFIG.Canvas.rulerClass.prototype.clear", clearRuler); - wrap("CONFIG.Canvas.rulerClass.prototype._addWaypoint", _addWaypointRuler); - wrap("CONFIG.Canvas.rulerClass.prototype._removeWaypoint", _removeWaypointRuler); - - // Pass needed variables across the sockets - wrap("CONFIG.Canvas.rulerClass.prototype.toJSON", toJSONRuler); - wrap("CONFIG.Canvas.rulerClass.prototype.update", updateRuler); - - // Ruler methods related to ruler segments - wrap("CONFIG.Canvas.rulerClass.prototype._getMeasurementSegments", _getMeasurementSegmentsRuler); - wrap("GridLayer.prototype.measureDistances", measureDistancesGridLayer); - wrap("CONFIG.Canvas.rulerClass.prototype._getSegmentLabel", _getSegmentLabelRuler); - - // Move token methods - wrap("CONFIG.Canvas.rulerClass.prototype._animateSegment", _animateSegmentRuler); - - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "terrainElevationAtPoint", terrainElevationAtPoint); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "terrainElevationAtDestination", terrainElevationAtDestination); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "incrementElevation", incrementElevation); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "decrementElevation", decrementElevation); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "terrainElevationAtPoint", terrainElevationAtPoint); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "terrainElevationAtDestination", terrainElevationAtDestination); - addClassMethod(CONFIG.Canvas.rulerClass.prototype, "elevationAtOrigin", elevationAtOrigin); - - log("registerRuler finished!"); -} - -export function registerDragRuler() { - wrap("CONFIG.Canvas.rulerClass.prototype._getMeasurementSegments", _getMeasurementSegmentsDragRulerRuler); - wrap("CONFIG.Canvas.rulerClass.prototype.dragRulerClearWaypoints", dragRulerClearWaypointsDragRuleRuler); - wrap("CONFIG.Canvas.rulerClass.prototype.dragRulerAddWaypoint", dragRulerAddWaypointDragRulerRuler); - - wrap("CONFIG.Token.objectClass.prototype._onDragLeftDrop", _onDragLeftDropToken); +export function initializePatching() { + PATCHER.registerGroup("BASIC"); + if ( MODULES_ACTIVE.DRAG_RULER ) PATCHER.registerGroup("DRAG_RULER"); } diff --git a/scripts/ruler.js b/scripts/ruler.js index 0fdd8a0..2e8f444 100644 --- a/scripts/ruler.js +++ b/scripts/ruler.js @@ -2,10 +2,24 @@ canvas, game */ -"use strict"; +/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ -import { log } from "./util.js"; +// Patches for the Ruler class +export const PATCHES = {}; +PATCHES.BASIC = {}; +PATCHES.DRAG_RULER = {}; +import { + elevationAtOrigin, + terrainElevationAtPoint, + terrainElevationAtDestination +} from "./terrain_elevation.js"; + +import { + _getMeasurementSegments, + _getSegmentLabel, + _animateSegment +} from "./segments.js"; /** * Modified Ruler @@ -42,13 +56,13 @@ UX goals: would go from 5 to 55. */ +// ----- NOTE: Wrappers ----- // + /** * Wrap Ruler.prototype.clear * Reset properties used to track when the user increments/decrements elevation */ -export function clearRuler(wrapper) { - log("we are clearing!", this); - +function clear(wrapper) { // User increments/decrements to the elevation for the current destination this.destination._userElevationIncrements = 0; return wrapper(); @@ -58,7 +72,7 @@ export function clearRuler(wrapper) { * Wrap Ruler.prototype.toJSON * Store the current userElevationIncrements for the destination. */ -export function toJSONRuler(wrapper) { +function toJSON(wrapper) { // If debugging, log will not display on user's console // console.log("constructing ruler json!") const obj = wrapper(); @@ -70,10 +84,7 @@ export function toJSONRuler(wrapper) { * Wrap Ruler.prototype.update * Retrieve the current _userElevationIncrements */ -export function updateRuler(wrapper, data) { - // If debugging, log will not display on user's console - // console.log("updating ruler!") - +function update(wrapper, data) { // Fix for displaying user elevation increments as they happen. const triggerMeasure = this._userElevationIncrements !== data._userElevationIncrements; this._userElevationIncrements = data._userElevationIncrements; @@ -87,66 +98,71 @@ export function updateRuler(wrapper, data) { } /** - * Wrap Ruler.prototype.addWaypoint + * Wrap Ruler.prototype._addWaypoint * Add elevation increments */ -export function _addWaypointRuler(wrapper, point) { - log("adding waypoint!"); +function _addWaypoint(wrapper, point) { wrapper(point); addWaypointElevationIncrements(this, point); } /** - * Wrap DragRulerRuler.prototype.dragRulerAddWaypoint - * Add elevation increments - */ -export function dragRulerAddWaypointDragRulerRuler(wrapper, point, options = {}) { - log("adding drag ruler waypoint!"); - wrapper(point, options); - addWaypointElevationIncrements(this, point); -} - -/** - * Helper to add elevation increments to waypoint - */ -function addWaypointElevationIncrements(ruler, point) { - const ln = ruler.waypoints.length; - const newWaypoint = ruler.waypoints[ln - 1]; - - if ( ln === 1) { - // Origin waypoint -- cache using elevationAtOrigin - ruler.elevationAtOrigin(); - ruler._userElevationIncrements = 0; - } else { - newWaypoint._terrainElevation = ruler.terrainElevationAtPoint(point); - newWaypoint._userElevationIncrements = ruler._userElevationIncrements; - } -} - -/** - * Wrap Ruler.prototype.removeWaypoint + * Wrap Ruler.prototype._removeWaypoint * Remove elevation increments. * (Note: also called by DragRulerRuler.prototype.dragRulerDeleteWaypoint) */ -export function _removeWaypointRuler(wrapper, point, { snap = true } = {}) { - log("removing waypoint!"); +function _removeWaypoint(wrapper, point, { snap = true } = {}) { this._userElevationIncrements = 0; wrapper(point, { snap }); } +/** + * Wrap DragRulerRuler.prototype.dragRulerAddWaypoint + * Add elevation increments + */ +function dragRulerAddWaypoint(wrapper, point, options = {}) { + wrapper(point, options); + addWaypointElevationIncrements(this, point); +} + /** * Wrap DragRulerRuler.prototype.dragRulerClearWaypoints * Remove elevation increments */ -export function dragRulerClearWaypointsDragRuleRuler(wrapper) { - log("clearing drag ruler waypoints"); +export function dragRulerClearWaypoints(wrapper) { wrapper(); this._userElevationIncrements = 0; } -export function incrementElevation() { + +PATCHES.BASIC.WRAPS = { + clear, + toJSON, + update, + _addWaypoint, + _removeWaypoint, + + // Wraps related to segments + _getMeasurementSegments, + _getSegmentLabel, + + // Move token methods + _animateSegment +}; + +PATCHES.DRAG_RULER.WRAPS = { + dragRulerAddWaypoint, + dragRulerClearWaypoints +}; + +// ----- NOTE: Methods ----- // + +/** + * Add Ruler.prototype.incrementElevation + * Increase the elevation at the current ruler destination by one grid unit. + */ +function incrementElevation() { const ruler = canvas.controls.ruler; - log("Trying to increment...", ruler); if ( !ruler || !ruler.active ) return; ruler._userElevationIncrements += 1; @@ -160,9 +176,12 @@ export function incrementElevation() { game.user.broadcastActivity({ ruler: ruler.toJSON() }); } -export function decrementElevation() { +/** + * Add Ruler.prototype.decrementElevation + * Decrease the elevation at the current ruler destination by one grid unit. + */ +function decrementElevation() { const ruler = canvas.controls.ruler; - log("Trying to decrement...", ruler); if ( !ruler || !ruler.active ) return; ruler._userElevationIncrements -= 1; @@ -175,3 +194,32 @@ export function decrementElevation() { // Broadcast the activity (see ControlsLayer.prototype._onMouseMove) game.user.broadcastActivity({ ruler: ruler.toJSON() }); } + +PATCHES.BASIC.METHODS = { + incrementElevation, + decrementElevation, + + // From terrain_elevation.js + elevationAtOrigin, + terrainElevationAtPoint, + terrainElevationAtDestination +}; + + +// ----- Helper functions ----- // + +/** + * Helper to add elevation increments to waypoint + */ +function addWaypointElevationIncrements(ruler, point) { + const ln = ruler.waypoints.length; + const newWaypoint = ruler.waypoints[ln - 1]; + if ( ln === 1) { + // Origin waypoint -- cache using elevationAtOrigin + ruler.elevationAtOrigin(); + ruler._userElevationIncrements = 0; + } else { + newWaypoint._terrainElevation = ruler.terrainElevationAtPoint(point); + newWaypoint._userElevationIncrements = ruler._userElevationIncrements; + } +} diff --git a/scripts/segments.js b/scripts/segments.js index e5ba12a..ae4365c 100644 --- a/scripts/segments.js +++ b/scripts/segments.js @@ -8,15 +8,23 @@ CONFIG import { MODULE_ID } from "./const.js"; import { SETTINGS } from "./settings.js"; -import { log } from "./util.js"; - import { Ray3d } from "./geometry/3d/Ray3d.js"; +/** + * Calculate the elevation for a given waypoint. + * Terrain elevation + user increment + * @param {object} waypoint + * @returns {number} + */ +export function elevationAtWaypoint(waypoint) { + return waypoint._terrainElevation + (waypoint._userElevationIncrements * canvas.dimensions.distance); +} + /** * Wrap Ruler.prototype._getMeasurementSegments * Add elevation information to the segments */ -export function _getMeasurementSegmentsRuler(wrapped) { +export function _getMeasurementSegments(wrapped) { const segments = wrapped(); // Add destination as the final waypoint @@ -27,21 +35,38 @@ export function _getMeasurementSegmentsRuler(wrapped) { } /** - * Wrap DragRulerRuler.prototype._getMeasurementSegments - * Add elevation information to the segments + * Wrap Ruler.prototype._getSegmentLabel + * Add elevation information to the label */ -export function _getMeasurementSegmentsDragRulerRuler(wrapped) { - const segments = wrapped(); +export function _getSegmentLabel(wrapped, segment, totalDistance) { + const orig_label = wrapped(segment, totalDistance); + let elevation_label = segmentElevationLabel(segment); + const level_name = levelNameAtElevation(CONFIG.GeometryLib.utils.pixelsToGridUnits(segment.ray.B.z)); + if ( level_name ) elevation_label += `\n${level_name}`; + return `${orig_label}\n${elevation_label}`; +} - if ( !this.isDragRuler ) return segments; // Drag Ruler calls super in this situation +/** + * Wrap Ruler.prototype._animateSegment + * When moving the token along the segments, update the token elevation to the destination + increment + * for the given segment. + */ +export async function _animateSegment(wrapped, token, segment, destination) { + const res = await wrapped(token, segment, destination); - // Add destination as the final waypoint - this.destination._terrainElevation = this.terrainElevationAtDestination(); - this.destination._userElevationIncrements = this._userElevationIncrements; + // Update elevation after the token move. + if ( segment.ray.A.z !== segment.ray.B.z ) { + await token.document.update({ elevation: CONFIG.GeometryLib.utils.pixelsToGridUnits(segment.ray.B.z) }); + } - return elevateSegments(this, segments); + return res; } +/** + * Take 2d segments and make 3d. + * @param {Ruler} ruler + * @param {object[]} segments + */ function elevateSegments(ruler, segments) { // Add destination as the final waypoint const waypoints = ruler.waypoints.concat([ruler.destination]); const { distance, size } = canvas.dimensions; @@ -69,40 +94,6 @@ function elevateSegments(ruler, segments) { // Add destination as the final way return segments; } -/** - * Calculate the elevation for a given waypoint. - * Terrain elevation + user increment - * @param {object} waypoint - * @returns {number} - */ -export function elevationAtWaypoint(waypoint) { - return waypoint._terrainElevation + (waypoint._userElevationIncrements * canvas.dimensions.distance); -} - -/** - * Wrap GridLayer.prototype.measureDistances - * Called by Ruler.prototype._computeDistance - * If a segment ray has a z-dimension, re-do the segment by projecting the hypotenuse - * between the ray A and B endpoints in 3d onto the 2d canvas. Use the projected - * hypotenuse to do the measurement. - */ -export function measureDistancesGridLayer(wrapped, segments, options = {}) { - if ( !segments.length || !(segments[0]?.ray instanceof Ray3d) ) return wrapped(segments, options); - - // Avoid modifying the segment rays. - const ln = segments.length; - const origRays = Array(ln); - for ( let i = 0; i < ln; i += 1 ) { - const s = segments[i]; - origRays[i] = s.ray; - s.ray = s.ray.projectOntoCanvas(); - } - - const out = wrapped(segments, options); - - for ( let i = 0; i < ln; i += 1 ) segments[i].ray = origRays[i]; - return out; -} /** * Should Levels floor labels be used? @@ -116,19 +107,6 @@ function useLevelsLabels() { || (labelOpt === SETTINGS.LEVELS_LABELS.UI_ONLY && CONFIG.Levels.UI.rendered); } -/** - * Wrap Ruler.prototype._getSegmentLabel - * Add elevation information to the label - */ -export function _getSegmentLabelRuler(wrapped, segment, totalDistance) { - const orig_label = wrapped(segment, totalDistance); - let elevation_label = segmentElevationLabel(segment); - const level_name = levelNameAtElevation(CONFIG.GeometryLib.utils.pixelsToGridUnits(segment.ray.B.z)); - if ( level_name ) elevation_label += `\n${level_name}`; - - return `${orig_label}\n${elevation_label}`; -} - /** * Find the name of the level, if any, at a given elevation. * @param {number} e Elevation to use. @@ -144,7 +122,6 @@ function levelNameAtElevation(e) { return lvl ? lvl[2] : undefined; } - /* * Construct a label to represent elevation changes in the ruler. * Waypoint version: 10 ft↑ [@10 ft] @@ -167,53 +144,3 @@ function segmentElevationLabel(s) { return label; } - -/** - * Wrap Ruler.prototype._animateSegment - * When moving the token along the segments, update the token elevation to the destination + increment - * for the given segment. - */ -export async function _animateSegmentRuler(wrapped, token, segment, destination) { - log(`Updating token elevation for segment with destination ${destination.x},${destination.y},${destination.z} from elevation ${segment.ray.A.z} --> ${segment.ray.B.z}`, token, segment); - const res = await wrapped(token, segment, destination); - - // Update elevation after the token move. - if ( segment.ray.A.z !== segment.ray.B.z ) { - await token.document.update({ elevation: CONFIG.GeometryLib.utils.pixelsToGridUnits(segment.ray.B.z) }); - } - - return res; -} - -/** - * Wrap Token.prototype._onDragLeftDrop - * If Drag Ruler is active, use this to update token(s) after movement has completed. - * Callback actions which occur on a mouse-move operation. - * @see MouseInteractionManager#_handleDragDrop - * @param {PIXI.InteractionEvent} event The triggering canvas interaction event - * @returns {Promise<*>} - */ -export async function _onDragLeftDropToken(wrapped, event) { - // Assume the destination elevation is the desired elevation if dragging multiple tokens. - // (Likely more useful than having a bunch of tokens move down 10'?) - const ruler = canvas.controls.ruler; - if ( !ruler.isDragRuler ) return wrapped(event); - - log("ending token drag"); - - // Do before calling wrapper b/c ruler may get cleared. - const elevation = elevationAtWaypoint(ruler.destination); - const selectedTokens = [...canvas.tokens.controlled]; - if ( !selectedTokens.length ) selectedTokens.push(ruler.draggedEntity); - - const result = wrapped(event); - if ( result === false ) return false; // Drag did not happen - - const updates = selectedTokens.map(t => { - return { _id: t.id, elevation }; - }); - - const t0 = selectedTokens[0]; - await t0.scene.updateEmbeddedDocuments(t0.constructor.embeddedName, updates); - return true; -} diff --git a/scripts/terrain_elevation.js b/scripts/terrain_elevation.js index 9f5b4db..050679d 100644 --- a/scripts/terrain_elevation.js +++ b/scripts/terrain_elevation.js @@ -10,12 +10,13 @@ PIXI Used by ruler to get elevation at waypoints and at the end of the ruler. */ -import { MODULE_ID } from "./const.js"; +import { MODULE_ID, MODULES_ACTIVE } from "./const.js"; import { log } from "./util.js"; import { SETTINGS, getSetting } from "./settings.js"; import { elevationAtWaypoint } from "./segments.js"; /** + * Add Ruler.prototype.elevationAtOrigin * Retrieve the elevation at the current ruler origin. * This is either the measuring token elevation or terrain elevation or 0. * Cached during a ruler movement @@ -148,7 +149,7 @@ function retrieveVisibleTokens() { * @returns {boolean} */ function useElevatedVision() { - return game.modules.get("elevatedvision")?.active + return MODULES_ACTIVE.ELEVATED_VISION && game.settings.get(MODULE_ID, "enable-elevated-vision-elevation"); } @@ -157,7 +158,7 @@ function useElevatedVision() { * @returns {boolean} */ function useTerrainLayer() { - return game.modules.get("enhanced-terrain-layer")?.active + return MODULES_ACTIVE.ENHANCED_TERRAIN_LAYER && game.settings.get(MODULE_ID, "enable-enhanced-terrain-elevation"); } @@ -166,7 +167,7 @@ function useTerrainLayer() { * @returns {boolean} */ function useLevels() { - return game.modules.get("levels")?.active + return MODULES_ACTIVE.LEVELS && game.settings.get(MODULE_ID, "enable-levels-elevation"); } @@ -190,7 +191,7 @@ function EVElevationAtPoint(location, measuringToken, startingElevation = 0) { EVCalc.elevation = isFinite(startingElevation) ? startingElevation : Number.MAX_SAFE_INTEGER; if ( !measuringToken ) { EVCalc.options.tileStep = Number.POSITIVE_INFINITY; - EVCalc.options.terrainStep = Number.POSITIVE_INFINITY; + EVCalc.options.terrainStep = Number.POSITIVE_INFINITY; } return EVCalc.groundElevation(); From de42aa865fdf6b2a63cf1ec2218d57753a3886f7 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 15:17:04 -0800 Subject: [PATCH 05/11] Refactor for Settings Abstract --- scripts/module.js | 23 ++-- scripts/patching.js | 7 +- scripts/segments.js | 8 +- scripts/settings.js | 203 ++++++++++++++++++----------------- scripts/terrain_elevation.js | 6 +- 5 files changed, 126 insertions(+), 121 deletions(-) diff --git a/scripts/module.js b/scripts/module.js index 0d39d8f..7251759 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -7,7 +7,7 @@ ui /* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ "use strict"; -import { registerSettings, registerKeybindings, SETTINGS, getSetting, setSetting } from "./settings.js"; +import { Settings } from "./settings.js"; import { initializePatching, PATCHER } from "./patching.js"; import { MODULE_ID } from "./const.js"; @@ -21,8 +21,7 @@ import { registerGeometry } from "./geometry/registration.js"; // but before entities, packs, UI, canvas, etc. has been initialized Hooks.once("setup", async function() { registerKeybindings(); // Should go before registering settings, so hotkey group is defined - registerSettings(); - + Settings.registerAll(); registerGeometry(); }); @@ -36,8 +35,8 @@ Hooks.once("libWrapper.Ready", async function() { }); const PREFER_TOKEN_CONTROL = { - name: SETTINGS.PREFER_TOKEN_ELEVATION, - title: `${MODULE_ID}.controls.${SETTINGS.PREFER_TOKEN_ELEVATION}.name`, + name: Settings.KEYS.PREFER_TOKEN_ELEVATION, + title: `${MODULE_ID}.controls.${Settings.KEYS.PREFER_TOKEN_ELEVATION}.name`, icon: "fa-solid fa-user-lock", toggle: true }; @@ -56,7 +55,7 @@ Hooks.once("setup", function() { // Render the prefer token control if that setting is enabled Hooks.on("getSceneControlButtons", controls => { - if ( !canvas.scene || !getSetting(SETTINGS.PREFER_TOKEN_ELEVATION) ) return; + if ( !canvas.scene || !Settings.get(Settings.KEYS.PREFER_TOKEN_ELEVATION) ) return; const tokenTools = controls.find(c => c.name === "token"); tokenTools.tools.push(PREFER_TOKEN_CONTROL); }); @@ -71,19 +70,19 @@ Hooks.on("canvasInit", function(_canvas) { Hooks.on("renderSceneControls", async function(controls, _html, _data) { // Watch for enabling/disabling of the prefer token control - if ( controls.activeControl !== "token" || !getSetting(SETTINGS.PREFER_TOKEN_ELEVATION) ) return; - const toggle = controls.control.tools.find(t => t.name === SETTINGS.PREFER_TOKEN_ELEVATION); + if ( controls.activeControl !== "token" || !Settings.get(Settings.KEYS.PREFER_TOKEN_ELEVATION) ) return; + const toggle = controls.control.tools.find(t => t.name === Settings.KEYS.PREFER_TOKEN_ELEVATION); if ( !toggle ) return; // Shouldn't happen, but... - await setSetting(SETTINGS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE, toggle.active); + await Settings.set(Settings.KEYS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE, toggle.active); }); function updatePreferTokenControl(enable) { - enable ??= getSetting(SETTINGS.PREFER_TOKEN_ELEVATION); + enable ??= Settings.get(Settings.KEYS.PREFER_TOKEN_ELEVATION); const tokenTools = ui.controls.controls.find(c => c.name === "token"); - const index = tokenTools.tools.findIndex(b => b.name === SETTINGS.PREFER_TOKEN_ELEVATION); + const index = tokenTools.tools.findIndex(b => b.name === Settings.KEYS.PREFER_TOKEN_ELEVATION); if ( enable && !~index ) tokenTools.tools.push(PREFER_TOKEN_CONTROL); else if ( ~index ) tokenTools.tools.splice(index, 1); - PREFER_TOKEN_CONTROL.active = getSetting(SETTINGS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE); + PREFER_TOKEN_CONTROL.active = Settings.get(Settings.KEYS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE); ui.controls.render(true); } diff --git a/scripts/patching.js b/scripts/patching.js index a96461a..037fc8a 100644 --- a/scripts/patching.js +++ b/scripts/patching.js @@ -11,10 +11,15 @@ import { PATCHES as PATCHES.Ruler } from "./Ruler.js"; import { PATCHES as PATCHES.Token } from "./Token.js"; import { PATCHES as PATCHES.GridLayer } from "./GridLayer.js"; +// Settings +import { PATCHES as PATCHES_Settings } from "./ModuleSettingsAbstract.js"; + + const PATCHES = { GridLayer: PATCHES_GridLayer, Ruler: PATCHES_Ruler, - Token: PATCHES_Token + Token: PATCHES_Token, + Settings: PATCHES_Settings }; export const PATCHER = new Patcher(); diff --git a/scripts/segments.js b/scripts/segments.js index ae4365c..49bf065 100644 --- a/scripts/segments.js +++ b/scripts/segments.js @@ -7,7 +7,7 @@ CONFIG "use strict"; import { MODULE_ID } from "./const.js"; -import { SETTINGS } from "./settings.js"; +import { Settings } from "./settings.js"; import { Ray3d } from "./geometry/3d/Ray3d.js"; /** @@ -102,9 +102,9 @@ function elevateSegments(ruler, segments) { // Add destination as the final way function useLevelsLabels() { if ( !game.modules.get("levels")?.active ) return false; - const labelOpt = game.settings.get(MODULE_ID, SETTINGS.USE_LEVELS_LABEL); - return labelOpt === SETTINGS.LEVELS_LABELS.ALWAYS - || (labelOpt === SETTINGS.LEVELS_LABELS.UI_ONLY && CONFIG.Levels.UI.rendered); + const labelOpt = Settings.get(MODULE_ID, Settings.KEYS.USE_LEVELS_LABEL); + return labelOpt === Settings.KEYS.LEVELS_LABELS.ALWAYS + || (labelOpt === Settings.KEYS.LEVELS_LABELS.UI_ONLY && CONFIG.Levels.UI.rendered); } /** diff --git a/scripts/settings.js b/scripts/settings.js index 15851e6..629abdd 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -8,7 +8,7 @@ canvas import { MODULE_ID } from "./const.js"; import { log } from "./util.js"; -export const SETTINGS = { +const SETTINGS = { PREFER_TOKEN_ELEVATION: "prefer-token-elevation", USE_EV: "enable-elevated-vision-elevation", USE_TERRAIN: "enable-enhanced-terrain-elevation", @@ -28,116 +28,117 @@ const KEYBINDINGS = { DECREMENT: "decrementElevation" }; -export function getSetting(settingName) { - return game.settings.get(MODULE_ID, settingName); -} -export async function setSetting(settingName, value) { - await game.settings.set(MODULE_ID, settingName, value); -} +export class Settings extends ModuleSettingsAbstract { + /** @type {object} */ + static KEYS = SETTINGS; + + /** @type {object} */ + static KEYBINDINGS = KEYBINDINGS; + + /** + * Register all settings + */ + static registerAll() { + const { KEYS, register,localize } = this; + + if ( !MODULES_ACTIVE.ELEVATED_VISION + && !MODULES_ACTIVE.ENHANCED_TERRAINLAYER + && !MODULES_ACTIVE.LEVELS ) { + register(KEYS.NO_MODS, { + name: localize(`${KEYS.NO_MODS}.name`), + hint: localize(`${KEYS.NO_MODS}.hint`), + scope: "world", + config: true, + enabled: false, + default: true, + type: Boolean + }); + } -export function registerSettings() { - log("Registering settings."); + register(KEYS.USE_EV, { + name: localize(`${KEYS.USE_EV}.name`), + hint: localize(`${KEYS.USE_EV}.hint`), + scope: "world", + config: evActive, + default: evActive, + type: Boolean + }); - const evActive = game.modules.get("elevatedvision")?.active; - const terrainLayerActive = game.modules.get("enhanced-terrain-layer")?.active; - const levelsActive = game.modules.get("levels")?.active; + register(KEYS.USE_TERRAIN, { + name: localize(`${KEYS.USE_TERRAIN}.name`), + hint: localize(`${KEYS.USE_TERRAIN}.hint`), + scope: "world", + config: terrainLayerActive, + default: terrainLayerActive, + type: Boolean + }); - if ( !evActive && !terrainLayerActive && !levelsActive ) { - game.settings.register(MODULE_ID, SETTINGS.NO_MODS, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.NO_MODS}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.NO_MODS}.hint`), + register(KEYS.USE_LEVELS, { + name: localize(`${KEYS.USE_LEVELS}.name`), + hint: localize(`${KEYS.USE_LEVELS}.hint`), scope: "world", - config: true, - enabled: false, - default: true, + config: levelsActive, + default: levelsActive, type: Boolean }); + + register(KEYS.USE_LEVELS_LABEL, { + name: localize(`${KEYS.USE_LEVELS_LABEL}.name`), + hint: localize(`${KEYS.USE_LEVELS_LABEL}.hint`), + scope: "world", + config: levelsActive, + default: KEYS.LEVELS_LABELS.ALWAYS, + type: String, + choices: { + [KEYS.LEVELS_LABELS.NEVER]: game.i18n.localize(`${KEYS.LEVELS_LABELS.NEVER}`), + [KEYS.LEVELS_LABELS.UI_ONLY]: game.i18n.localize(`${KEYS.LEVELS_LABELS.UI_ONLY}`), + [KEYS.LEVELS_LABELS.ALWAYS]: game.i18n.localize(`${KEYS.LEVELS_LABELS.ALWAYS}`) + } + }); + + register(KEYS.PREFER_TOKEN_ELEVATION, { + name: localize(`${KEYS.PREFER_TOKEN_ELEVATION}.name`), + hint: localize(`${KEYS.PREFER_TOKEN_ELEVATION}.hint`), + scope: "user", + config: true, + default: false, + type: Boolean, + requiresReload: false, + onChange: reloadTokenControls + }); + + register(KEYS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE, { + scope: "user", + config: false, + default: false, + type: Boolean, + requiresReload: false + }); } - game.settings.register(MODULE_ID, SETTINGS.USE_EV, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_EV}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_EV}.hint`), - scope: "world", - config: evActive, - default: evActive, - type: Boolean - }); - - game.settings.register(MODULE_ID, SETTINGS.USE_TERRAIN, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_TERRAIN}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_TERRAIN}.hint`), - scope: "world", - config: terrainLayerActive, - default: terrainLayerActive, - type: Boolean - }); - - game.settings.register(MODULE_ID, SETTINGS.USE_LEVELS, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_LEVELS}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_LEVELS}.hint`), - scope: "world", - config: levelsActive, - default: levelsActive, - type: Boolean - }); - - game.settings.register(MODULE_ID, SETTINGS.USE_LEVELS_LABEL, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_LEVELS_LABEL}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.USE_LEVELS_LABEL}.hint`), - scope: "world", - config: levelsActive, - default: SETTINGS.LEVELS_LABELS.ALWAYS, - type: String, - choices: { - [SETTINGS.LEVELS_LABELS.NEVER]: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.LEVELS_LABELS.NEVER}`), - [SETTINGS.LEVELS_LABELS.UI_ONLY]: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.LEVELS_LABELS.UI_ONLY}`), - [SETTINGS.LEVELS_LABELS.ALWAYS]: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.LEVELS_LABELS.ALWAYS}`) - } - }); - - game.settings.register(MODULE_ID, SETTINGS.PREFER_TOKEN_ELEVATION, { - name: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.PREFER_TOKEN_ELEVATION}.name`), - hint: game.i18n.localize(`${MODULE_ID}.settings.${SETTINGS.PREFER_TOKEN_ELEVATION}.hint`), - scope: "user", - config: true, - default: false, - type: Boolean, - requiresReload: false, - onChange: reloadTokenControls - }); - - game.settings.register(MODULE_ID, SETTINGS.PREFER_TOKEN_ELEVATION_CURRENT_VALUE, { - scope: "user", - config: false, - default: false, - type: Boolean, - requiresReload: false - }); - - log("Done registering settings."); -} -export function registerKeybindings() { - game.keybindings.register(MODULE_ID, KEYBINDINGS.DECREMENT, { - name: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.DECREMENT}.name`), - hint: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.DECREMENT}.hint`), - editable: [ - { key: "BracketLeft" } - ], - onDown: () => canvas.controls.ruler.decrementElevation(), - precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL - }); - - game.keybindings.register(MODULE_ID, KEYBINDINGS.INCREMENT, { - name: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.INCREMENT}.name`), - hint: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.INCREMENT}.hint`), - editable: [ - { key: "BracketRight" } - ], - onDown: () => canvas.controls.ruler.incrementElevation(), - precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL - }); + static registerKeybindings() { + game.keybindings.register(MODULE_ID, KEYBINDINGS.DECREMENT, { + name: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.DECREMENT}.name`), + hint: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.DECREMENT}.hint`), + editable: [ + { key: "BracketLeft" } + ], + onDown: () => canvas.controls.ruler.decrementElevation(), + precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL + }); + + game.keybindings.register(MODULE_ID, KEYBINDINGS.INCREMENT, { + name: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.INCREMENT}.name`), + hint: game.i18n.localize(`${MODULE_ID}.keybindings.${KEYBINDINGS.INCREMENT}.hint`), + editable: [ + { key: "BracketRight" } + ], + onDown: () => canvas.controls.ruler.incrementElevation(), + precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL + }); + } } /** diff --git a/scripts/terrain_elevation.js b/scripts/terrain_elevation.js index 050679d..3ebabf2 100644 --- a/scripts/terrain_elevation.js +++ b/scripts/terrain_elevation.js @@ -12,7 +12,7 @@ Used by ruler to get elevation at waypoints and at the end of the ruler. import { MODULE_ID, MODULES_ACTIVE } from "./const.js"; import { log } from "./util.js"; -import { SETTINGS, getSetting } from "./settings.js"; +import { Settings } from "./settings.js"; import { elevationAtWaypoint } from "./segments.js"; /** @@ -52,9 +52,9 @@ function tokenElevation(token) { * @returns {boolean} */ function preferTokenElevation() { - if ( !getSetting(SETTINGS.PREFER_TOKEN_ELEVATION) ) return false; + if ( !Settings.get(Settings.KEYS.PREFER_TOKEN_ELEVATION) ) return false; const token_controls = ui.controls.controls.find(elem => elem.name === "token"); - const prefer_token_control = token_controls.tools.find(elem => elem.name === SETTINGS.PREFER_TOKEN_ELEVATION); + const prefer_token_control = token_controls.tools.find(elem => elem.name === Settings.KEYS.PREFER_TOKEN_ELEVATION); return prefer_token_control.active; } From 5d548c5782cd1980cf2c31a2684ef2bb75b6debe Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 15:35:30 -0800 Subject: [PATCH 06/11] Update geometry lib --- scripts/geometry | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/geometry b/scripts/geometry index 3e8b52e..c36f3a3 160000 --- a/scripts/geometry +++ b/scripts/geometry @@ -1 +1 @@ -Subproject commit 3e8b52e975f18205b353be942a2ffee8f4f30268 +Subproject commit c36f3a31e9430a10fd95a53a2214b93872c01748 From 7ebd282016f23e062d24cf2eb4813a371b82ab33 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 15:49:03 -0800 Subject: [PATCH 07/11] JSlint fixes --- Changelog.md | 1 + scripts/const.js | 5 ++--- scripts/module.js | 25 +++++++++---------------- scripts/patching.js | 11 ++++++----- scripts/settings.js | 20 ++++++++++---------- scripts/util.js | 5 +---- 6 files changed, 29 insertions(+), 38 deletions(-) diff --git a/Changelog.md b/Changelog.md index 89a6051..f75c342 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # 0.7.3 Updated lib-geometry to 0.2.12. +Refactor to use Patcher class. # 0.7.2 Updated lib-geometry to 0.2.2. diff --git a/scripts/const.js b/scripts/const.js index da28bb9..3f82da1 100644 --- a/scripts/const.js +++ b/scripts/const.js @@ -1,5 +1,6 @@ /* globals - +game, +Hooks */ "use strict"; @@ -20,5 +21,3 @@ Hooks.once("init", function() { MODULES_ACTIVE.LEVELS = game.modules.get("levels")?.active; MODULES_ACTIVE.ELEVATED_VISION = game.modules.get("elevatedvision")?.active; }); - - diff --git a/scripts/module.js b/scripts/module.js index 7251759..0996588 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -8,21 +8,22 @@ ui "use strict"; import { Settings } from "./settings.js"; -import { initializePatching, PATCHER } from "./patching.js"; +import { initializePatching, PATCHER, registerDragRuler } from "./patching.js"; import { MODULE_ID } from "./const.js"; -// For Drag Ruler -import { registerDragRuler } from "./patching.js"; // eslint-disable-line no-duplicate-imports - import { registerGeometry } from "./geometry/registration.js"; +Hooks.once("init", function() { + registerGeometry(); +}); + // Setup is after init; before ready. // setup is called after settings and localization have been initialized, // but before entities, packs, UI, canvas, etc. has been initialized -Hooks.once("setup", async function() { - registerKeybindings(); // Should go before registering settings, so hotkey group is defined +Hooks.once("setup", function() { + Settings.registerKeybindings(); // Should go before registering settings, so hotkey group is defined Settings.registerAll(); - registerGeometry(); + initializePatching(); }); // For https://github.com/League-of-Foundry-Developers/foundryvtt-devMode @@ -30,10 +31,6 @@ Hooks.once("devModeReady", ({ registerPackageDebugFlag }) => { registerPackageDebugFlag(MODULE_ID); }); -Hooks.once("libWrapper.Ready", async function() { - registerRuler(); -}); - const PREFER_TOKEN_CONTROL = { name: Settings.KEYS.PREFER_TOKEN_ELEVATION, title: `${MODULE_ID}.controls.${Settings.KEYS.PREFER_TOKEN_ELEVATION}.name`, @@ -46,11 +43,7 @@ Hooks.once("init", function() { PREFER_TOKEN_CONTROL.title = game.i18n.localize(PREFER_TOKEN_CONTROL.title); game.modules.get(MODULE_ID).api = { PATCHER - } -}); - -Hooks.once("setup", function() { - initializePatching(); + }; }); // Render the prefer token control if that setting is enabled diff --git a/scripts/patching.js b/scripts/patching.js index 037fc8a..f546ec8 100644 --- a/scripts/patching.js +++ b/scripts/patching.js @@ -1,15 +1,13 @@ /* globals -CONFIG, -libWrapper */ "use strict"; import { Patcher } from "./Patcher.js"; import { MODULES_ACTIVE } from "./const.js"; -import { PATCHES as PATCHES.Ruler } from "./Ruler.js"; -import { PATCHES as PATCHES.Token } from "./Token.js"; -import { PATCHES as PATCHES.GridLayer } from "./GridLayer.js"; +import { PATCHES as PATCHES_Ruler } from "./Ruler.js"; +import { PATCHES as PATCHES_Token } from "./Token.js"; +import { PATCHES as PATCHES_GridLayer } from "./GridLayer.js"; // Settings import { PATCHES as PATCHES_Settings } from "./ModuleSettingsAbstract.js"; @@ -27,5 +25,8 @@ PATCHER.addPatchesFromRegistrationObject(PATCHES); export function initializePatching() { PATCHER.registerGroup("BASIC"); +} + +export function registerDragRuler() { if ( MODULES_ACTIVE.DRAG_RULER ) PATCHER.registerGroup("DRAG_RULER"); } diff --git a/scripts/settings.js b/scripts/settings.js index 629abdd..bf59198 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -5,8 +5,8 @@ canvas */ "use strict"; -import { MODULE_ID } from "./const.js"; -import { log } from "./util.js"; +import { MODULE_ID, MODULES_ACTIVE } from "./const.js"; +import { ModuleSettingsAbstract } from "./ModuleSettingsAbstract.js"; const SETTINGS = { PREFER_TOKEN_ELEVATION: "prefer-token-elevation", @@ -40,7 +40,7 @@ export class Settings extends ModuleSettingsAbstract { * Register all settings */ static registerAll() { - const { KEYS, register,localize } = this; + const { KEYS, register, localize } = this; if ( !MODULES_ACTIVE.ELEVATED_VISION && !MODULES_ACTIVE.ENHANCED_TERRAINLAYER @@ -60,8 +60,8 @@ export class Settings extends ModuleSettingsAbstract { name: localize(`${KEYS.USE_EV}.name`), hint: localize(`${KEYS.USE_EV}.hint`), scope: "world", - config: evActive, - default: evActive, + config: MODULES_ACTIVE.ELEVATED_VISION, + default: MODULES_ACTIVE.ELEVATED_VISION, type: Boolean }); @@ -69,8 +69,8 @@ export class Settings extends ModuleSettingsAbstract { name: localize(`${KEYS.USE_TERRAIN}.name`), hint: localize(`${KEYS.USE_TERRAIN}.hint`), scope: "world", - config: terrainLayerActive, - default: terrainLayerActive, + config: MODULES_ACTIVE.ENHANCED_TERRAIN_LAYER, + default: MODULES_ACTIVE.ENHANCED_TERRAIN_LAYER, type: Boolean }); @@ -78,8 +78,8 @@ export class Settings extends ModuleSettingsAbstract { name: localize(`${KEYS.USE_LEVELS}.name`), hint: localize(`${KEYS.USE_LEVELS}.hint`), scope: "world", - config: levelsActive, - default: levelsActive, + config: MODULES_ACTIVE.LEVELS, + default: MODULES_ACTIVE.LEVELS, type: Boolean }); @@ -87,7 +87,7 @@ export class Settings extends ModuleSettingsAbstract { name: localize(`${KEYS.USE_LEVELS_LABEL}.name`), hint: localize(`${KEYS.USE_LEVELS_LABEL}.hint`), scope: "world", - config: levelsActive, + config: MODULES_ACTIVE.LEVELS, default: KEYS.LEVELS_LABELS.ALWAYS, type: String, choices: { diff --git a/scripts/util.js b/scripts/util.js index c825c12..e99629a 100644 --- a/scripts/util.js +++ b/scripts/util.js @@ -1,8 +1,5 @@ /* globals -canvas, -CONST, -game, -Ray +game */ "use strict"; From a24c612bbf1459ab14965c00b3aa2b39a9ebc747 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 15:51:00 -0800 Subject: [PATCH 08/11] Fix for GridLayer patch --- scripts/GridLayer.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/GridLayer.js b/scripts/GridLayer.js index beed0e1..ef0804b 100644 --- a/scripts/GridLayer.js +++ b/scripts/GridLayer.js @@ -1,5 +1,4 @@ /* globals -GridLayer */ /* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ @@ -16,7 +15,7 @@ PATCHES.BASIC = {}; * between the ray A and B endpoints in 3d onto the 2d canvas. Use the projected * hypotenuse to do the measurement. */ -export function measureDistancesGridLayer(wrapped, segments, options = {}) { +function measureDistances(wrapped, segments, options = {}) { if ( !segments.length || !(segments[0]?.ray instanceof Ray3d) ) return wrapped(segments, options); // Avoid modifying the segment rays. @@ -33,4 +32,4 @@ export function measureDistancesGridLayer(wrapped, segments, options = {}) { return out; } -PATCHES.BASIC.WRAPS = { GridLayer }; +PATCHES.BASIC.WRAPS = { measureDistances }; From bdf07881da6c06480298997f2f3084addc1b1220 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Sun, 31 Dec 2023 16:31:00 -0800 Subject: [PATCH 09/11] Add test for Drag Ruler compatibility --- scripts/module.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/module.js b/scripts/module.js index 0996588..5c0a2e1 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -53,8 +53,14 @@ Hooks.on("getSceneControlButtons", controls => { tokenTools.tools.push(PREFER_TOKEN_CONTROL); }); +function _onDragLeftDropTest(wrapped, event) { + console.debug("_onDragLeftDropTest"); + return wrapped(); +} + Hooks.on("dragRuler.ready", function() { registerDragRuler(); + libWrapper.register(MODULE_ID, "Token.prototype._onDragLeftDrop", _onDragLeftDropTest, libWrapper.WRAPPED); }); Hooks.on("canvasInit", function(_canvas) { From 9eb36afd1a832431fab3560b66f8b557e2ad0d98 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Mon, 1 Jan 2024 08:29:14 -0800 Subject: [PATCH 10/11] Remove drag ruler test methods --- scripts/module.js | 6 ------ scripts/ruler.js | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/scripts/module.js b/scripts/module.js index 5c0a2e1..0996588 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -53,14 +53,8 @@ Hooks.on("getSceneControlButtons", controls => { tokenTools.tools.push(PREFER_TOKEN_CONTROL); }); -function _onDragLeftDropTest(wrapped, event) { - console.debug("_onDragLeftDropTest"); - return wrapped(); -} - Hooks.on("dragRuler.ready", function() { registerDragRuler(); - libWrapper.register(MODULE_ID, "Token.prototype._onDragLeftDrop", _onDragLeftDropTest, libWrapper.WRAPPED); }); Hooks.on("canvasInit", function(_canvas) { diff --git a/scripts/ruler.js b/scripts/ruler.js index 2e8f444..9469bc7 100644 --- a/scripts/ruler.js +++ b/scripts/ruler.js @@ -129,12 +129,51 @@ function dragRulerAddWaypoint(wrapper, point, options = {}) { * Wrap DragRulerRuler.prototype.dragRulerClearWaypoints * Remove elevation increments */ -export function dragRulerClearWaypoints(wrapper) { +function dragRulerClearWaypoints(wrapper) { wrapper(); this._userElevationIncrements = 0; } +/** + * Wrap DragRulerRuler.prototype._endMeasurement + * If there is a dragged token, apply the elevation to all selected tokens (assumed part of the move). + */ +function _endMeasurement(wrapped) { + console.debug("_endMeasurement"); + return wrapped(); +} + + +function _postMove(wrapped, token) { + console.debug("_animateSegment"); + return wrapped(token); +} + +// // Assume the destination elevation is the desired elevation if dragging multiple tokens. +// // (Likely more useful than having a bunch of tokens move down 10'?) +// const ruler = canvas.controls.ruler; +// if ( !ruler.isDragRuler ) return wrapped(event); +// +// // Do before calling wrapper b/c ruler may get cleared. +// const elevation = elevationAtWaypoint(ruler.destination); +// const selectedTokens = [...canvas.tokens.controlled]; +// if ( !selectedTokens.length ) selectedTokens.push(ruler.draggedEntity); +// +// const result = wrapped(event); +// if ( result === false ) return false; // Drag did not happen +// +// const updates = selectedTokens.map(t => { +// return { _id: t.id, elevation }; +// }); +// +// const t0 = selectedTokens[0]; +// await t0.scene.updateEmbeddedDocuments(t0.constructor.embeddedName, updates); +// return true; + + + + PATCHES.BASIC.WRAPS = { clear, toJSON, @@ -147,12 +186,14 @@ PATCHES.BASIC.WRAPS = { _getSegmentLabel, // Move token methods - _animateSegment + _animateSegment, + // _postMove }; PATCHES.DRAG_RULER.WRAPS = { dragRulerAddWaypoint, - dragRulerClearWaypoints + dragRulerClearWaypoints, + // _endMeasurement }; // ----- NOTE: Methods ----- // From ab4e735849b997fd81bfb51c49679de028efd623 Mon Sep 17 00:00:00 2001 From: Michael Enion Date: Wed, 3 Jan 2024 09:55:18 -0800 Subject: [PATCH 11/11] Update git workflow --- .github/workflows/main.yml | 171 +++++++++++++++++++++++++++++-------- module.json | 11 ++- 2 files changed, 142 insertions(+), 40 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 853437a..e4faba0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,42 +1,145 @@ -name: Release Creation +# GitHub Actions workflow for creating a new FoundryVTT module release. +# +# Useful References: +# - https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# - https://docs.github.com/en/actions/learn-github-actions/contexts +# - https://docs.github.com/en/actions/learn-github-actions/environment-variables +# +# Troubleshooting Checklist: +# - Is the module's manifest file valid JSON? +# You can test your manifest file using https://jsonlint.com/. +# +# - Does the module's manifest have all the required keys? +# See https://foundryvtt.com/article/module-development/#manifest for more +# information. +# +# - Are all the proper files and directories being included in the release's +# module archive ("module.zip")? +# Check that the correct files are being passed to the `zip` command run +# in the "Create Module Archive" step below. +# +# - Is the release tag the proper format? +# See the comments for the "Extract Version From Tag" step below. +# +# - Is a GitHub release being published? +# This workflow will only run when a release is published, not when a +# release is updated. Furthermore, note that while a GitHub release will +# (by default) create a repository tag, a repository tag will not create +# or publish a GitHub release. +# +# - Has the module's entry on FoundryVTT's module administration site +# (https://foundryvtt.com/admin) been updated? +# +name: Create Module Files For GitHub Release -on: + +env: + # The URL used for the module's "Project URL" link on FoundryVTT's website. + project_url: "https://github.com/${{github.repository}}" + + # A URL that will always point to the latest manifest. + # FoundryVTT uses this URL to check whether the current module version that + # is installed is the latest version. This URL should NOT change, + # otherwise FoundryVTT won't be able to perform this check. + latest_manifest_url: "https://github.com/${{github.repository}}/releases/latest/download/module.json" + + # The URL to the module archive associated with the module release being + # processed by this workflow. + release_module_url: "https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.zip" + + +on: + # Only run this workflow when a release is published. + # To modify this workflow when other events occur, see: + # - https://docs.github.com/en/actions/using-workflows/triggering-a-workflow + # - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows + # - https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on + # + # Note that some steps may depend on context variables that are only + # available for release events, so if you add other events, you may need to + # alter other parts of this workflow. release: types: [published] + jobs: build: runs-on: ubuntu-latest + permissions: + contents: write + steps: - - uses: actions/checkout@v2 - with: - submodules: 'true' - - # Substitute the Manifest and Download URLs in the module.json - - name: Substitute Manifest and Download Links For Versioned Ones - id: sub_manifest_link_version - uses: microsoft/variable-substitution@v1 - with: - files: 'module.json' - env: - version: ${{github.event.release.tag_name}} - url: https://github.com/${{github.repository}} - manifest: https://github.com/${{github.repository}}/releases/latest/download/module.json - download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.zip - - # Create a zip file with all files required by the module to add to the release - - run: zip -r ./module.zip module.json LICENSE styles/ scripts/ templates/ languages/ - - # Create a release for this specific version - - name: Update Release with Files - id: create_version_release - uses: ncipollo/release-action@v1 - with: - allowUpdates: true # Set this to false if you want to prevent updating existing releases - name: ${{ github.event.release.name }} - draft: false - prerelease: false - token: ${{ secrets.GITHUB_TOKEN }} - artifacts: './module.json, ./module.zip' - tag: ${{ github.event.release.tag_name }} - body: ${{ github.event.release.body }} + - name: Checkout Repository + uses: actions/checkout@v3 + with: + submodules: 'true' + + # Extract version embedded in the tag. + # This step expects the tag to be one of the following formats: + # - "v.." (e.g., "v1.2.3") + # - ".." (e.g., "1.2.3") + # + # The version will be used by later steps to fill in the value for the + # "version" key required for a valid module manifest. + - name: Extract Version From Tag + id: get_version + uses: battila7/get-version-action@v2 + + + # Modify "module.json" with values specific to the release. + # Since the values for the "version" and "url" keys aren't known ahead of + # time, the manifest file in the repository is updated with these values. + # + # While this does modify the manifest file in-place, the changes are not + # commited to the repository, and only exist in the action's filesystem. + - name: Modify Module Manifest With Release-Specific Values + id: sub_manifest_link_version + uses: cschleiden/replace-tokens@v1 + with: + files: 'module.json' + env: + VERSION: ${{steps.get_version.outputs.version-without-v}} + URL: ${{ env.project_url }} + MANIFEST: ${{ env.latest_manifest_url }} + DOWNLOAD: ${{ env.release_module_url }} + + + # Create a "module.zip" archive containing all the module's required files. + # If you have other directories or files that will need to be added to + # your packaged module, add them here. + - name: Create Module Archive + run: | + # Note that `zip` will only emit warnings when a file or directory + # doesn't exist, it will not fail. + zip \ + `# Options` \ + --recurse-paths \ + `# The name of the output file` \ + ./module.zip \ + `# The files that will be included.` \ + module.json \ + README.md \ + LICENSE \ + templates/ \ + scripts/ \ + styles/ \ + packs/ \ + languages/ \ + assets/ + # Don't forget to add a backslash at the end of the line for any + # additional files or directories! + + + # Update the GitHub release with the manifest and module archive files. + - name: Update Release With Files + id: create_version_release + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + name: ${{ github.event.release.name }} + draft: ${{ github.event.release.unpublished }} + prerelease: ${{ github.event.release.prerelease }} + token: ${{ secrets.GITHUB_TOKEN }} + artifacts: './module.json, ./module.zip' + tag: ${{ github.event.release.tag_name }} + body: ${{ github.event.release.body }} \ No newline at end of file diff --git a/module.json b/module.json index 8f91cf5..aed2d1a 100644 --- a/module.json +++ b/module.json @@ -1,15 +1,14 @@ { "name": "elevationruler", - "minimumCoreVersion": "10", "title": "Elevation Ruler", "id": "elevationruler", "description": "Modify Foundry VTT ruler to adjust for relative elevation of the destination.", - "version": "This is auto replaced", + "version": "#{VERSION}#", "library": false, "manifestPlusVersion": "1.0.0", "compatibility": { "minimum": "11", - "verified": "11.304" + "verified": "11.315" }, "authors": [ { @@ -37,9 +36,9 @@ "path": "languages/en.json" } ], - "url": "This is auto replaced", - "manifest": "This is auto replaced", - "download": "This is auto replaced", + "url": "#{URL}#", + "manifest": "#{MANIFEST}#", + "download": "#{DOWNLOAD}#", "license": "LICENSE", "readme": "README.md" }