/fvtt-lib-ruler

Library for accessing the Ruler Class in Foundry across modules.

Primary LanguageJavaScriptMIT LicenseMIT

FVTT libRuler

Library for Foundry VTT which provides module developers with a means to modify the Ruler class in core Foundry VTT code, while reducing the likelihood of conflict with other modules. Also aims to make certain overrides of the Foundry ruler easier to accomplish.

Version (latest) Foundry Version License

User Installation

Add this Manifest URL in Foundry to install.

Enable the Module in your World's Module Settings. That's it! As a module library, there are no user-facing settings.

Dependencies

Table of Contents

Developer Module Usage

Your options parallel that of libWrapper. Those options are shamelessly copied and adapted here:

Use a compatibility shim

  1. Adapt the shim provided by libWrapper shim.

Write your own custom shim

  1. Write your own. Please do not make your custom shim available in the global scope.

Add switches based on whether libRuler is available

  1. Trigger a different code path depending on whether libRuler is installed and active or not. For example:

    Hooks.once('libRulerReady', async function() {
     // FLAG ANYTHING YOU NEED GIVEN THAT LIBRULER IS ACTIVE
    }
    
    // or 
    
    if(game.modules.get('libruler')?.active) {
     // DO LIB-RULER VERSION
    }

    or

Require libRuler as a dependency

  1. Require your users to install this library. One simple example that achieves this is provided below. Reference the more complex example in the libWrapper shim if you prefer a dialog (including an option to dismiss it permanently) instead of a simple notification.

    Hooks.once('ready', () => {
        if(!game.modules.get('libruler')?.active && game.user.isGM)
            ui.notifications.error("Module XYZ requires the 'libRuler' module. Please install and activate this dependency.");
    });

    Note that if you choose this option and require the user to install this library, you should make sure to list libRuler as a dependency. This can be done by adding the following to your package's manifest:

    "dependencies": [
        {
            "name": "libruler"
        }
    ]

Developer Usage

libRuler overrides methods of and adds methods to the base Foundry Ruler class. Use libWrapper to wrap functions needed by your module. Additional documentation describes the functions available:

Additional Documentation

  • Ruler class overrides. Ruler methods overriden by libRuler.
  • Ruler class additions. Ruler methods added by libRuler.
  • RulerSegment class. New class added by libRuler, representing the segment between two waypoints (including origin and destination as waypoints). Accessible at window.libRuler.RulerSegment.
  • Ruler Utilities. Ruler utility methods added by libRuler. Accessible at window.libRuler.RulerUtilities.

Examples in the additional documentation sometimes point to branches of other modules. Specifically:

How to use libRuler

The expectation is that modules will use libWrapper to wrap one or more functions in the new RulerSegment class, the new methods added to the Ruler class, or (rarely) the underlying Ruler class methods.

The RulerSegment Class is exposed at window.libRuler.RulerSegment and can be wrapped similarly to wrapping core Foundry methods, for example:

libWrapper.register(MODULE_ID, 'window.libRuler.RulerSegment.prototype.addProperties', myCoolFunction, 'WRAPPER');

Elevation Ruler has several examples of wrapping libRuler functions.

Changes to Ruler.prototype.measure

libRuler's version of Ruler.prototype.measure, at measure.js now creates a RulerSegment representing the path between two waypoints (including origin or destination, as appropriate). The sequence of events remains the same, but events are now mostly handled by the RulerSegment Class. This permits modules to wrap or otherwise modify sub-parts of the Ruler measurement flow without having to re-write the entire measure method.

RulerSegment class and measuring distance

The RulerSegment class class functions to represent segments of the ruler when measuring. The key function of RulerSegment is to break down measurement into three subparts:

  1. RulerSegment.prototype.constructPhysicalPath .
  2. RulerSegment.prototype.measurePhysicalPath .
  3. RulerSegment.prototype.modifyDistanceResult .

Flow diagram of Ruler.prototype.measure

The full flow of Ruler.prototype.measure is as follows:

  1. Ruler.prototype.setDestination allows modules to modify the destination point.

For each segment in turn, from the origin outward:

  1. A new RulerSegment is created.
  2. RulerSegment.prototype.drawLine draws the ruler line on the canvas.
  3. RulerSegment.prototype.drawDistanceLabel draws the text label indicating the ruler distance.

Distance label gets the current text, with the following: 4.1. RulerSegment.prototype.text (getter) 4.1.1. RulerSegment.prototype.totalDistance (getter) 4.1.1.1 RulerSegment.prototype.totalPriorDistance (getter) 4.1.1.2 RulerSegment.prototype.distance (getter) 4.1.2. RulerSegment.prototype.distance (getter)

RulerSegment.prototype.distance is cached, but when calculated for the first time, it calls RulerSegment.prototype.recalculateDistance , which calls RulerSegment.prototype.measureDistance , which in turn calls:

  1. RulerSegment.prototype.highlightMeasurement highlights grid spaces on the canvas. 5.1 RulerSegment.prototype.colorForPosition 5.2. canvas.grid.highlightPosition
  2. RulerSegment.prototype.drawEndpoints handles drawing the origin, destination, and waypoint

Changes to Ruler.prototype.moveToken

Ruler.prototype.moveToken is broken up into parts, namely:

  1. collision test to permit the movement
  2. animating the token movement

The full flow of moveToken:

  1. Return if certain conditions met, such as the token is not available.
  2. Ruler.prototype.doDeferredMeasurements
  3. Ruler.prototype.testForCollision
  4. For each ray (segment) of the ruler:
  1. Ruler.prototype.endMeasurement

Other changes to Ruler class

Ruler.prototype._highlightMeasurement is deprecated, because its functionality is now part of RulerSegment.prototype.highlightMeasurement .

Ruler.prototype._addWaypoint and Ruler.prototype._removeWaypoint are overriden. _addWaypoint now takes an optional center parameter; if true it will apply canvas.grid.getCenter to the point. _removeWaypoint adds a remeasure option to trigger measurement when removing a waypoint (defaults to true).

'Ruler.prototype.toJSON' and 'Ruler.prototype.update' are wrapped to accommodate Ruler flags.

Flags, which let modules store properties to the instantiated object, are added to Ruler and RulerSegment classes:

  • getFlag
  • setFlag
  • unsetFlag

Ruler.prototype._onMouseMove gains several sub-methods to permit modules more control over when ruler measurements occur: