diff --git a/README.md b/README.md index 714a06b..c7e5462 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pixels Electronic Dice - Foundry VTT Integration -![alt text](https://raw.githubusercontent.com/foundryvtt/pixels/main/ui/pixels-logo.png) +![Pixels Electronic Dice Logo](https://raw.githubusercontent.com/foundryvtt/pixels/main/ui/pixels-logo.png) This module integrates **Pixels - The Electronic Dice** (https://gamewithpixels.com) with **Foundry Virtual Tabletop** (https://foundryvtt.com). @@ -10,7 +10,7 @@ To use this module you must have: 2. One or more Pixels electronic dice. This module is currently designed and tested using Pixels DevKit d20s. ## Installing and Activating the Module -Install the `pixels` module from the Foundry Virtual Tabletop module installation menu. Also install the dependency module `unfulfilled-rolls` which you will be automatically prompted to install. In your game World, activate both the `pixels` and `unfulfilled-rolls` modules. +Install the "Pixels - Electronic Dice" module from the Foundry Virtual Tabletop module installation menu. In your game World, activate the "Pixels - Electronic Dice" module. ## Enabling Pixels Dice Integration Navigate to the **Settings** sidebar, **Configure Settings**, and the **Pixels** tab. From here check the **Enable Pixels Dice** setting. You will be automatically prompted after this to configure your dice. You can return to this menu at any time to disable Pixels dice or change your Pixels configuration. diff --git a/module.json b/module.json index 17a7583..34bd989 100644 --- a/module.json +++ b/module.json @@ -7,8 +7,8 @@ "download": "#{DOWNLOAD}#", "url": "https://gamewithpixels.com/", "compatibility": { - "minimum": 10, - "verified": 11 + "minimum": 12, + "verified": 12 }, "authors": [ { @@ -16,18 +16,6 @@ "website": "https://foundryvtt.com" } ], - "relationships": { - "systems": [], - "requires": [ - { - "id": "unfulfilled-rolls", - "type": "module", - "compatibility": { - "minimum": "0.1.0" - } - } - ] - }, "scripts": [ "node_modules/@systemic-games/pixels-web-connect/dist/umd/index.js" ], diff --git a/scripts/apps/pixels-resolver.mjs b/scripts/apps/pixels-resolver.mjs deleted file mode 100644 index 92b8fac..0000000 --- a/scripts/apps/pixels-resolver.mjs +++ /dev/null @@ -1,111 +0,0 @@ -/** - * A dialog form used to process input rolls for resolution. - */ -export default class PixelsResolver extends FormApplication { - constructor(terms, roll, callback) { - super(); - pixelsDice.RESOLVERS.push(this); - this.terms = terms; - this.roll = roll; - this.callback = callback; - } - - /* -------------------------------------------- */ - - /** @inheritdoc */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["pixels", "resolver"], - title: "Pixels Roll Resolver", - template: "modules/pixels/templates/pixels-resolver.hbs", - width: 600, - height: "auto", - closeOnSubmit: true, - submitOnClose: false - }); - } - - /* -------------------------------------------- */ - - /** @override */ - async getData(options) { - const terms = this.terms.map(t => ({...t, - disabled: t.fulfillmentMethod === "bluetooth", - label: `d${t.faces}` - })); - return {roll: this.roll, terms}; - } - - /* -------------------------------------------- */ - - /** @override */ - async _updateObject(event, formData) { - const result = new Map(this.terms.map(t => [t.id, t.value])); - for ( const [k, v] of Object.entries(formData) ) { - if ( result.has(k) ) result.set(k, v); - } - return this.#resolve(result); - } - - /* -------------------------------------------- */ - - /** - * Record a result from a Pixels die roll event. - * @param {PixelConfiguration} config The configuration of the rolled Pixel - * @param {number} result The result of the roll - * @returns {Promise} - */ - async registerResult(config, result) { - const nextTerm = this.terms.find(t => { - if ( t.value ) return false; // Already rolled - if ( t.fulfillmentMethod !== "bluetooth" ) return false; // Not for Pixels - if ( t.faces !== config.denomination ) return false; // Wrong die - return true; - }); - if ( !nextTerm ) return; - nextTerm.value = result; - this.render(); - - // Complete all rolls - if ( this.#isComplete() ) { - this.#disableButton(); - const result = new Map(this.terms.map(t => [t.id, t.value])); - window.setTimeout(this.#resolve.bind(this, result), 1000); - } - } - - /* -------------------------------------------- */ - - /** - * Has the roll been entirely fulfilled? - * @returns {boolean} - */ - #isComplete() { - return this.terms.every(t => Number.isNumeric(t.value)); - } - - /* -------------------------------------------- */ - - /** - * Disable the submit button if the roll is in the process of being fulfilled. - */ - #disableButton() { - const submit = this.form.querySelector(`button[type="submit"]`); - submit.disabled = true; - const icon = submit.querySelector("i"); - icon.className = "fa-solid fa-spinner fa-spin"; - } - - /* -------------------------------------------- */ - - /** - * Complete fulfillment of the roll by resolving the callback Promise. - * @param {Map} result The fulfilled result - * @returns {Promise} - */ - async #resolve(result) { - pixelsDice.RESOLVERS.findSplice(r => r === this); - this.callback(result); - return this.close(); - } -} diff --git a/scripts/handlers.mjs b/scripts/handlers.mjs index 5bbe406..b09ee27 100644 --- a/scripts/handlers.mjs +++ b/scripts/handlers.mjs @@ -6,9 +6,9 @@ */ export function handleRoll(config, result) { console.debug(`${config.name} rolled an ${result}`); - const resolver = pixelsDice.RESOLVERS[0]; - if ( resolver ) return resolver.registerResult(config, result); - else return manualRoll(config, result); + if ( !Roll.defaultImplementation.registerResult("pixels", `d${config.denomination}`, result) ) { + return manualRoll(config, result); + } } /* -------------------------------------------- */ @@ -49,7 +49,7 @@ let _pendingRoll = {}; export function manualRoll(config, result) { _pendingRoll[config.name] = {denomination: config.denomination, value: result}; - pixelsDice.debounceRoll() + pixelsDice.debounceRoll(); } /* -------------------------------------------- */ diff --git a/scripts/pixels.mjs b/scripts/pixels.mjs index ff14bef..faf0795 100644 --- a/scripts/pixels.mjs +++ b/scripts/pixels.mjs @@ -1,14 +1,7 @@ import PixelsManager from "./manager.mjs"; import PixelsConfiguration from "./apps/pixels-config.mjs"; -import PixelsResolver from "./apps/pixels-resolver.mjs"; import * as api from "./handlers.mjs"; -/** - * A queue of PixelsResolver instances which require resolution - * @type {PixelsResolver[]} - */ -const RESOLVERS = []; - /* -------------------------------------------- */ /* Client Initialization */ /* -------------------------------------------- */ @@ -46,11 +39,13 @@ Hooks.on("init", function() { restricted: false }); + // Core Dice Configuration + CONFIG.Dice.fulfillment.methods.pixels = { label: "Pixels - Electronic Dice", interactive: true }; + // Register module properties const module = globalThis.pixelsDice = game.modules.get("pixels"); module.enabled = false; module.PIXELS = PixelsManager.fromSetting(); - module.RESOLVERS = RESOLVERS; module.api = api; module.debounceRoll = foundry.utils.debounce(api.completeManualRoll, 1000); }); @@ -67,22 +62,6 @@ Hooks.on("ready", function() { /* -------------------------------------------- */ async function _initialize(enabled) { - - // Automatic configuration of dice provider settings - const unfulfilledRollsConfig = game.settings.get("unfulfilled-rolls", "diceSettings"); - if ( enabled ) { - game.settings.set("unfulfilled-rolls", "diceSettings", Object.assign(unfulfilledRollsConfig, { - bluetoothDieProvider: "pixels", - d20: "bluetooth" - })); - } - else if ( unfulfilledRollsConfig.bluetoothDieProvider === "pixels" ) { - game.settings.set("unfulfilled-rolls", "diceSettings", Object.assign(unfulfilledRollsConfig, { - bluetoothDieProvider: "none", - d20: "fvtt" - })); - } - // Automatic connection to available dice if ( !enabled ) return; const reconnectSuccess = await pixelsDice.PIXELS.tryReconnect(); @@ -93,16 +72,3 @@ async function _initialize(enabled) { app.render(true); } } - -/* -------------------------------------------- */ -/* Unfulfilled Rolls Configuration */ -/* -------------------------------------------- */ - -Hooks.once('unfulfilled-rolls-bluetooth', function(providers) { - providers.pixels = { - label: "Pixels - Electronic Dice", - url: "https://gamewithpixels.com", - app: PixelsResolver - } -}); - diff --git a/templates/pixels-config.hbs b/templates/pixels-config.hbs index 049b082..337028a 100644 --- a/templates/pixels-config.hbs +++ b/templates/pixels-config.hbs @@ -16,7 +16,8 @@
- + Pixels Electronic Dice Logo
{{#if hasDevices}} diff --git a/templates/pixels-resolver.hbs b/templates/pixels-resolver.hbs deleted file mode 100644 index 327a4c3..0000000 --- a/templates/pixels-resolver.hbs +++ /dev/null @@ -1,18 +0,0 @@ - -
-

{{roll.formula}}

-
-
- {{#each terms as |term|}} -
-

{{term.label}}

- -
- {{/each}} -
- -