From 9a7088a31070f964de85beec2a228e889d2acf90 Mon Sep 17 00:00:00 2001 From: Cristian Deenen Date: Mon, 5 Sep 2022 01:42:15 +0200 Subject: [PATCH] v1.4.11 --- .github/workflows/main.yml | 45 +++ DEVGUIDE.md | 2 +- MaterialDeck.js | 118 ++++--- changelog.md | 421 ++++++++++++---------- css/style.css | 175 ++++++++++ img/.thumb/down.png.jpg | Bin 0 -> 5169 bytes img/.thumb/right.png.jpg | Bin 0 -> 5077 bytes img/down.png | Bin 0 -> 8199 bytes img/right.png | Bin 0 -> 8356 bytes lang/en.json | 8 +- module.json | 21 +- src/{ => actions}/combattracker.js | 44 ++- src/{ => actions}/external.js | 11 +- src/{ => actions}/macro.js | 61 ++-- src/{ => actions}/othercontrols.js | 211 +++++++----- src/{ => actions}/playlist.js | 47 +-- src/{ => actions}/scene.js | 70 ++-- src/{ => actions}/soundboard.js | 54 +-- src/{ => actions}/token.js | 279 ++++++++++----- src/misc.js | 214 +++++------- src/settings.js | 115 +++++-- src/streamDeck.js | 38 +- src/systems/demonlord.js | 30 +- src/systems/dnd35e.js | 53 +-- src/systems/dnd5e.js | 153 +++++--- src/systems/forbidden-lands.js | 58 ++-- src/systems/pf2e.js | 42 ++- src/systems/starfinder.js | 59 ++-- src/systems/template.js | 325 +++++++++++++++++ src/systems/tokenHelper.js | 39 ++- src/systems/wfrp4e.js | 37 +- templates/deviceConfig.html | 39 +-- templates/downloadUtility.html | 70 ++-- templates/exportDialog.html | 2 +- templates/helpMenu.html | 517 +++++++++++++++------------- templates/importDialog.html | 2 +- templates/macroConfig.html | 41 +-- templates/playlistConfig.html | 8 +- templates/soundboardConfig.html | 54 ++- templates/userPermissionConfig.html | 85 +---- 40 files changed, 2161 insertions(+), 1387 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 css/style.css create mode 100644 img/.thumb/down.png.jpg create mode 100644 img/.thumb/right.png.jpg create mode 100644 img/down.png create mode 100644 img/right.png rename src/{ => actions}/combattracker.js (84%) rename src/{ => actions}/external.js (98%) rename src/{ => actions}/macro.js (80%) rename src/{ => actions}/othercontrols.js (81%) rename src/{ => actions}/playlist.js (87%) rename src/{ => actions}/scene.js (72%) rename src/{ => actions}/soundboard.js (76%) rename src/{ => actions}/token.js (74%) create mode 100644 src/systems/template.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..08b9bcd --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,45 @@ +name: Release Creation + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + # get part of the tag after the `v` + - name: Extract tag version number + id: get_version + uses: battila7/get-version-action@v2 + + # 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: ${{steps.get_version.outputs.version-without-v}} + 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 MaterialDeck.js module.json README.md DEVGUIDE.md changelog.md css/ wiki/ img/ lang/ src/ templates/ + + # 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: ${{ 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 }} diff --git a/DEVGUIDE.md b/DEVGUIDE.md index cc1f038..f1047f1 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -7,7 +7,7 @@ In addition to this repo, you will also need to check out the [MaterialDeck_SD g ### Make a new system.js file -In the [src/systems](src/systems) directory, create a new system file by copying and pasting a similar system to it; for example, `cp demonlord.js wfrp4.js` +In the [src/systems](src/systems) directory, create a new system file by copying and pasting a similar system to it, or use template.js; for example, `cp demonlord.js wfrp4.js` You then need to go through all the functions in there and make sure that the correct data is set. ### Update TokenHelper diff --git a/MaterialDeck.js b/MaterialDeck.js index 0a15ed0..c9fd6de 100644 --- a/MaterialDeck.js +++ b/MaterialDeck.js @@ -1,15 +1,19 @@ -import {registerSettings} from "./src/settings.js"; -import {StreamDeck} from "./src/streamDeck.js"; -import {TokenControl} from "./src/token.js"; -import {MacroControl} from "./src/macro.js"; -import {CombatTracker} from "./src/combattracker.js"; -import {PlaylistControl} from "./src/playlist.js"; -import {SoundboardControl} from "./src/soundboard.js"; -import {OtherControls} from "./src/othercontrols.js"; -import {ExternalModules} from "./src/external.js"; -import {SceneControl} from "./src/scene.js"; -import {downloadUtility, compatibleCore, compareVersions} from "./src/misc.js"; -import {TokenHelper} from "./src/systems/tokenHelper.js"; +import { registerSettings } from "./src/settings.js"; +import { StreamDeck } from "./src/streamDeck.js"; +import { TokenControl } from "./src/actions/token.js"; +import { MacroControl } from "./src/actions/macro.js"; +import { CombatTracker } from "./src/actions/combattracker.js"; +import { PlaylistControl } from "./src/actions/playlist.js"; +import { SoundboardControl } from "./src/actions/soundboard.js"; +import { OtherControls } from "./src/actions/othercontrols.js"; +import { ExternalModules } from "./src/actions/external.js"; +import { SceneControl } from "./src/actions/scene.js"; +import { downloadUtility, compareVersions, compatibleCore } from "./src/misc.js"; +import { TokenHelper } from "./src/systems/tokenHelper.js"; + +export const minimumSDversion = "1.4.11"; +export const minimumMSversion = "1.0.2"; + export var streamDeck; export var tokenControl; export var macroControl; @@ -20,21 +24,15 @@ export var otherControls; export var externalModules; export var sceneControl; export var tokenHelper; - export const moduleName = "MaterialDeck"; - export let gamingSystem = "dnd5e"; - -let ready = false; - export let hotbarUses = false; export let calculateHotbarUses; - -let controlTokenTimer; - export let sdVersion; export let msVersion; +let ready = false; +let controlTokenTimer; let updateDialog; //CONFIG.debug.hooks = true; @@ -67,33 +65,58 @@ async function analyzeWSmessage(msg){ const msg = { target: "SD", type: "init", - system: gamingSystem + system: getGamingSystem(), + coreVersion: game.version.split('.')[0] } ws.send(JSON.stringify(msg)); if (data.MSversion) msVersion = data.MSversion; + if (data.SDversion) sdVersion = data.SDversion; console.log("streamdeck connected to server", msVersion); streamDeck.resetImageBuffer(); } if (data.type == "version" && data.source == "SD") { - let minimumSDversion; - let minimumMSversion; - if (compatibleCore("0.8.5")) { - minimumSDversion = game.modules.get("MaterialDeck").data.flags.minimumSDversion.replace('v',''); - minimumMSversion = game.modules.get("MaterialDeck").data.flags.minimumMSversion; - } - else { - minimumSDversion = game.modules.get("MaterialDeck").data.minimumSDversion.replace('v',''); - minimumMSversion = game.modules.get("MaterialDeck").data.minimumMSversion; - } - sdVersion = data.version; - if (!compareVersions(minimumSDversion,data.version) && updateDialog == undefined) { + const sdCompatible = compareVersions(minimumSDversion,sdVersion); + const msCompatible = compareVersions(minimumMSversion,msVersion); + + if ((!sdCompatible || !msCompatible) && updateDialog == undefined) { + let content = ""; + + if (!sdCompatible && !msCompatible) + content = game.i18n.localize("MaterialDeck.SdMsUpdateRequired") + else if (!sdCompatible) + content = game.i18n.localize("MaterialDeck.SdUpdateRequired") + else + content = game.i18n.localize("MaterialDeck.MsUpdateRequired") + const sd = sdCompatible ? 'display:none' : '' + const ms = msCompatible ? 'display:none' : '' + content += ` + + + + + + + + + + + + + + +
+ ${game.i18n.localize("MaterialDeck.DownloadUtility.Current")}${game.i18n.localize("MaterialDeck.DownloadUtility.Minimum")}
Stream Deck Plugin
${sdVersion}
${minimumSDversion}
Material Server +
${msVersion}
${minimumMSversion}
+ ` + //else if (!sdCompatible) contents += `The Stream Deck plugin version you're using is v${data.version}, which is incompatible with this version of the module.
Update to v${minimumSDversion} or newer.`; + updateDialog = new Dialog({ title: "Material Deck: Update Needed", - content: "

The Stream Deck plugin version you're using is v" + data.version + ", which is incompatible with this version of the module.
Update to v" + minimumSDversion + " or newer.

", + content, buttons: { download: { icon: '', @@ -224,7 +247,8 @@ function startWebsocket() { const msg2 = { target: "SD", type: "init", - system: gamingSystem + system: getGamingSystem(), + coreVersion: game.version.split('.')[0] } ws.send(JSON.stringify(msg2)); clearInterval(wsInterval); @@ -278,6 +302,11 @@ export function getPermission(action,func) { else return settings.permissions?.[action]?.[func]?.[role]; } +function getGamingSystem() { + const systemOverride = game.settings.get(moduleName,'systemOverride'); + gamingSystem = (systemOverride == undefined || systemOverride == null || systemOverride == '') ? game.system.id : systemOverride; + return gamingSystem; +} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -290,11 +319,10 @@ export function getPermission(action,func) { * Attempt to open the websocket */ Hooks.once('ready', async()=>{ - registerSettings(); - enableModule = (game.settings.get(moduleName,'Enable')) ? true : false; + await registerSettings(); + enableModule = (game.settings.get(moduleName,'Enable')) ? true : false; - const systemOverride = game.settings.get(moduleName,'systemOverride'); - gamingSystem = systemOverride != '' ? systemOverride : game.system.id; + getGamingSystem(); soundboard = new SoundboardControl(); streamDeck = new StreamDeck(); @@ -435,7 +463,7 @@ Hooks.on('onActorSetCondition',(data)=>{ Hooks.on('controlToken',(token,controlled)=>{ if (enableModule == false || ready == false) return; if (controlled) { - tokenControl.update(token.data._id); + tokenControl.update(compatibleCore('10.0') ? token.id : token.data._id); if (controlTokenTimer != undefined) { clearTimeout(controlTokenTimer); controlTokenTimer = undefined; @@ -459,9 +487,7 @@ Hooks.on('renderHotbar', (hotbar)=>{ Hooks.on('render', (app)=>{ if (enableModule == false || ready == false) return; - if (compatibleCore("0.8.1") == false) return; if (app.id == "hotbar" && macroControl != undefined) macroControl.hotbar(app.macros); - }); Hooks.on('renderCombatTracker',()=>{ @@ -486,6 +512,11 @@ Hooks.on('closeplaylistConfigForm', (form)=>{ playlistControl.updateAll(); }); +Hooks.on('lightingRefresh',()=>{ + if (enableModule == false || ready == false) return; + if (tokenControl != undefined) tokenControl.update(); +}); + Hooks.on('pauseGame',()=>{ if (enableModule == false || ready == false) return; otherControls.updateAll(); @@ -610,6 +641,7 @@ Hooks.on('updateTile',()=>{ Hooks.once('init', ()=>{ //CONFIG.debug.hooks = true; //registerSettings(); //in ./src/settings.js + }); Hooks.once('canvasReady',()=>{ @@ -618,4 +650,4 @@ Hooks.once('canvasReady',()=>{ Hooks.on("soundscape", (data) => { externalModules.newSoundscapeData(data); -}); \ No newline at end of file +}); diff --git a/changelog.md b/changelog.md index c64ac31..c77c1ff 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,56 @@ # Changelog Material Deck Module +### v1.4.11 - 05-09-2022 + +Fixes: + + +Additions: + + +Other: + + +V10 Compatibility Notes: + + +Compatible server app and SD plugin:
+Material Server v1.0.2+ (unchanged): https://github.com/CDeenen/MaterialServer/releases
+SD plugin v1.4.11 (must be updated!): https://github.com/CDeenen/MaterialDeck_SD/releases
+ ### v1.4.10 - 30-05-2022 Fixes: -Additions; +Additions: @@ -14,77 +59,77 @@ Additions; ### v1.4.9 - 16-04-2022 Fixes: Additions: ### v1.4.8 - 22-12-2021 Fixes: ### v1.4.7 - 20-12-2021 Fixes: Additions: ### v1.4.6 - 07-09-2021 Fixes: Additions: Other: ### v1.4.5 - 27-07-2021 Fixes: Additions:
@@ -95,28 +140,28 @@ SD plugin v1.4.5 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.4.4 - 26-05-2021 Fixes: Additions: Other:
@@ -127,15 +172,15 @@ SD plugin v1.4.4 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.4.3 - 05-05-2021 Fixes: Other
@@ -146,26 +191,26 @@ SD plugin v1.4.2 (unchanged): https://github.com/CDeenen/MaterialDeck_SD/release ### v1.4.2 - 23-04-2021 Fixes: Additions: Other:
@@ -176,25 +221,25 @@ SD plugin v1.4.2 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.4.1 - 21-04-2021 Fixes: ### v1.4.0 - 21-04-2021 Additions: Fixes:
@@ -206,27 +251,27 @@ SD plugin v1.4.0 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.3.3 - 12-04-2021 Additions: Fixes: Other:
@@ -237,23 +282,23 @@ SD plugin v1.3.4 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.3.2 - 11-03-2021 Additions: Fixes: Other:
@@ -264,17 +309,17 @@ SD plugin v1.3.2 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.3.1 - 27-02-2021 Additions: Fixes:
@@ -285,24 +330,24 @@ SD plugin v1.3.1 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.3.0 - 25-02-2021 Additions: Fixes: Other Changes: Note 1: Because the module can now be used by players, some settings have been moved from 'world' settings to 'client' settings. This means that previous settings have been deleted, and they have to be set up again in the module settings.
@@ -316,12 +361,12 @@ SD plugin v1.3.0 (must be updated!): https://github.com/CDeenen/MaterialD ### v1.2.3 - 03-02-2021 Fixes: Other Changes: Compatible server app and SD plugin:
Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases
@@ -330,13 +375,13 @@ SD plugin v1.2.2 (unchanged): https://github.com/CDeenen/MaterialDeck_SD/release ### v1.2.2 - 02-02-2021 Additions: Other Changes: Compatible server app and SD plugin:
Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases
@@ -347,23 +392,23 @@ SD plugin v1.2.2: https://github.com/CDeenen/MaterialDeck_SD/releases

Additions: Other Changes: Compatible server app and SD plugin:
Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases
@@ -372,24 +417,24 @@ SD plugin v1.2.1: https://github.com/CDeenen/MaterialDeck_SD/releases
### v1.2.0 - 28-12-2020 Fixes Additions: ### v1.1.1 - 12-12-2020 Fixes Compatible server app and SD plugin:
@@ -399,16 +444,16 @@ SD plugin v1.1.0 (unchanged): https://github.com/CDeenen/MaterialDeck_SD/release ### v1.1.0 - 09-12-2020 Fixes Additions: Compatible server app and SD plugin:
@@ -417,8 +462,8 @@ SD plugin v1.1.0: https://github.com/CDeenen/MaterialDeck_SD/releases
### V1.0.1 - 26-11-2020 Compatible server app and SD plugin:
@@ -428,7 +473,7 @@ SD plugin v1.0.0 (unchanged): https://github.com/CDeenen/MaterialDeck_SD/release ### v1.0.0 - 24-11-2020 Release Compatible server app and SD plugin:
@@ -437,17 +482,17 @@ SD plugin v1.0.0: https://github.com/CDeenen/MaterialDeck_SD/releases
### v0.9.2 - 24-11-2020 ### v0.9.1 - 23-11-2020 Note1: In 'Macro Configuration', previously saved Furnace arguments have to be filled in again.
@@ -459,12 +504,12 @@ SD plugin v0.9.1: https://github.com/CDeenen/MaterialDeck_SD/releases
### v0.9.0 - 19-11-2020 Compatible server app and SD plugin:
@@ -473,7 +518,7 @@ SD plugin v0.9.0: https://github.com/CDeenen/MaterialDeck_SD/releases
### v0.8.6 - 18-11-2020 Compatible server app and SD plugin:
@@ -482,18 +527,18 @@ SD plugin v0.7.3: https://github.com/CDeenen/MaterialDeck_SD/releases
### v0.8.5 - 17-11-2020 Compatible server app and SD plugin:
Server v0.2.4 (no change)
@@ -501,12 +546,12 @@ SD plugin v0.7.2
### v0.8.4 - 11-11-2020 Compatible server app and SD plugin:
Server v0.2.4 (no change)
@@ -514,7 +559,7 @@ SD plugin v0.7.1
### v0.8.3 - 10-11-2020 Compatible server app and SD plugin:
Server v0.2.4
@@ -522,7 +567,7 @@ SD plugin v0.7.0
### v0.8.2 - 10-11-2020 Compatible server app and SD plugin:
Server v0.2.4
diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..2986021 --- /dev/null +++ b/css/style.css @@ -0,0 +1,175 @@ +/* + * Device Config + */ +.materialDeck_devConf_columnLabel { + max-width:30%; + min-width:30%; + text-align: left; +} +.materialDeck_devConf_columnId { + max-width:25%; + min-width:25%; + text-align: left; + text-overflow: hidden; +} +.materialDeck_devConf_columnCB { + width:10%; + text-align: left; +} + +/* + * Download Utility + */ +.materialDeck_dlUtil_columnLabel { + width:25%; + text-align: left; +} +.materialDeck_dlUtil_columnVersion { + width:15%; + text-align: left; +} +.materialDeck_dlUtil_columnOS { + width:15%; +} +.materialDeck_dlUtil_columnButton { + width:15%; +} +.materialDeck_dlUtil_button { + width:100%; +} + +/* + * Macro Config + */ +.materialDeck_macroConfig_boxed { + border: 1px solid black ; + border-radius: 5px ; + max-width: 166px; + padding: 5px; + margin:2px; + width:10%; +} + +.materialDeck_macroConfig_navigationDiv { + width:100%; + display:flex; + flex-direction:row; +} + +.materialDeck_macroConfig_navigation { + flex:1; + max-width: 8%; +} + +.materialDeck_macroConfig_p { + width:84%; + text-align:center; + padding: 0px 0; + font-size: 20px; +} + +.materialDeck_macroConfig_background { + display:flex; + flex-direction:row; + padding-top:10px; +} + +/* + * Playlist Config + */ +.materialDeck_plConfig_select { + width: 35%; + max-width: 35%; +} + +/* + * Soundboard Config + */ +.materialDeck_sbConfig_boxed { + border: 1px solid black ; + border-radius: 5px ; + max-width: 166px; + height: 300px; + padding: 5px; + margin:2px; + width:10% +} + +.materialDeck_sbConfig_navigationDiv { + flex:1; + width:100%; + display:flex; + flex-direction:row; +} + +.materialDeck_sbConfig_navigation { + flex:1; +} + +.materialDeck_sbConfig_p { + flex:15; + text-align:center; + padding: 0px 0; + font-size: 20px +} + +/* + * User Permission Config + */ + header.materialDeck_uPerm_table-header { + background: rgba(0, 0, 0, 0.5); + padding: 5px; + border: 1px solid #191813; + text-align: center; + color: #f0f0e0; + font-weight: bold; + text-shadow: 1px 1px #000; +} + +ul.materialDeck_uPerm_permissions-list { + list-style: none; + margin: 0; + padding: 0; + + overflow: hidden auto; + scrollbar-width: thin; +} + +li.materialDeck_uPerm_permission { + padding: 5px; + border-bottom: 1px solid #7a7971; +} + +li.materialDeck_uPerm_permission .form-fields { + justify-content: space-around; +} + +li.materialDeck_uPerm_permission input[type="checkbox"] { + margin: 0; +} + +.materialDeck_uPerm_index { + flex: 0 0 200px; + text-align: left; + font-weight: bold; +} + +.materialDeck_uPerm_hint { + flex: 0 0 100%; + color: #4b4a44; + font-size: 13px; + margin: 5px 0 0; +} + +/* + * Help Menu + */ +.materialDeck_helpMenu_expandable { + cursor: pointer; +} +.materialDeck_helpMenu_collapsed { + display:none; +} +.materialDeck_helpMenu_expandableIcon { + border: none; +} \ No newline at end of file diff --git a/img/.thumb/down.png.jpg b/img/.thumb/down.png.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0dc52a430eeb060fa1ebd9a315113a1ea7c03b1a GIT binary patch literal 5169 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 W{*jjY{eK&tM!uu_S9EC3+rI<&!X-)o literal 0 HcmV?d00001 diff --git a/img/.thumb/right.png.jpg b/img/.thumb/right.png.jpg new file mode 100644 index 0000000000000000000000000000000000000000..628f09e1eb99addc3513be232d3e28c39acb9899 GIT binary patch literal 5077 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 literal 0 HcmV?d00001 diff --git a/img/down.png b/img/down.png new file mode 100644 index 0000000000000000000000000000000000000000..5f4abad41274db48eee7ce63945b171634cc6c7a GIT binary patch literal 8199 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 literal 0 HcmV?d00001 diff --git a/img/right.png b/img/right.png new file mode 100644 index 0000000000000000000000000000000000000000..9779581c8de3907ccbe983c83acae1ecab4026ad GIT binary patch literal 8356 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 z zGIv`SWyARc4!-EPu?$!4n8Ze;WL$Bc*#eQezrKdA*R1Vh#(D;oJw#JruRn{*9-%2) zlqG{nt_(wj6&tc;DdA%K}rhrKGDSq$H^f!)t^9Lv{#7&uPsStn?}?verwOVsdM zZ(|g)*IN!{GuyHiIui!1lIP8r=tLh@y%f}t!5yb8GpkB!v&eaRJ>xVe;=u7mMws@0 z;-b)&SXtN2Qb+g3=MIf}^SMlHfmELtqauiNPuMzc!&|Gu3)p1w$2SkzIkCM4wIaeY z^-3C&lc$r~jG_fCZw6!5loCqNJCwlcVyoZbaus!y?PcRzpt5YN`O+k@l-YM31no`y zFHxSGf&58r?n4P;L*wMf#X<0_d^QfX)q8J%_KmC6Xp6=+u;vAc4e48vBMY9)M#-e3 z3D*97;B%zIx+W)2k#mp?&YM!jn)qTtO&+A}V>Q$X1{@bc=`e+9k#|B4#nq|+&+4_S z5G3n&952VLCO`|4ZjQ$cjXLtJP6KchYk=b~<~*65JGFU^8L|7UYF;RRW6E20M6i^I&w6iJ%55`13qrH4MZ11=kFW%jr(`S? zz58n;9DRu$@&r`&C~XH@^G#xpXHDDoG9rM}E71&P?y)Y*dgB`a2$s#7-y}*pCpU%b zV)(4=0dMEq#^8ekYzP~S{ZE2Y$pU8pjx3+UvO^9IaJCdM{;O{;@)3k_p!`qZg@>{o zS%8+H{evm|Y|EX`*LE%VTaausVNtm7gG{2csHEOzK^bLJDMAg5e+vh=Iy=B=p>-Or zf?Yq%I#Kz00w&ZCFi$6@G-$l@HCwf%8+ua29n9JLbha;YfHJgDG|^TarTgs(y}%8O%$?6>`AV z{K;fG(1xp3Ym4++cV=7Rfp;a=jcg~~_T`59lYb>51WtJqG%%R0o3mCeet^eT*1clc zYMb6dp_iy~fkPrEPsdy>v;1rV$Gor8pyGo}1UVBB?2ONG3$2omx(8BGF{KM-H%$A^ zJZjC_2EnU){)o6>Zw6?&5*inEZ`u;-{P;qKo?i0X)ep8rC;Ofj6wN&}E`=MMa{ZG( zNxjASFgZ%@-~itP&bz9#4Sao}xN*6m6o-nsfUT(H2mnXiFcW-_9P0?qO4<*7kTxvp z!sQigmG}CmQ1#(^)Ik7&98`7~4UOw`}L^tlF z$PM;%Nim->z@y!%FpUdFHZ}qbPGV`YsbGj|tccQXqr?&#bN_hTq zqzFeTSdK?BD5%?Du+3wBU=W2_!Uy%V7NmHFGO5R~kr5OvL4by$JEtvyN>bFlzLK%V zGunU#Y#cZ*V&x(TYtmzwfWsNeE}J@tqP_2C%u1C9QyC|)8aCSiB7p|uFqELl(mQyf z$x<17rDt**<2%Lshd3c~Ffg|T!~jxMcZI;H{@=dffT1G_EaHS9X>bAicv!ps=^6B| zK6?E}#J^kqx8i>#{qN;$JDE!U>Dm9qt^dO&ec<#zDOOBm@aI(Uzpbr!O#w?F)G>3z JlA{h+{s)7RCe8o= literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json index c68262e..ec2b323 100644 --- a/lang/en.json +++ b/lang/en.json @@ -22,7 +22,7 @@ "MaterialDeck.Sett.NrOfConnMessages": "Number of Connection Warnings", "MaterialDeck.Sett.NrOfConnMessagesHint": "Sets the number of times a connection warning is displayed if Material Deck cannot connect to Material Server. If set to 0, it will give not be limited.", "MaterialDeck.Sett.SystemOverride": "System Override", - "MaterialDeck.Sett.SystemOverrideHint": "Overrides the automatic gaming system detection. Unsupported systems default to dnd5e, but if an unsupported system is similar to a different system you can specify that system here. Leave empty for system autodetection.", + "MaterialDeck.Sett.SystemOverrideHint": "Overrides the automatic gaming system detection. Unsupported systems default to dnd5e, but if an unsupported system is similar to a different system you can specify that system here.", "MaterialDeck.PL.Unrestricted": "Unrestricted", "MaterialDeck.PL.OneTrackPlaylist": "One track per playlist", @@ -211,5 +211,9 @@ "MaterialDeck.DownloadUtility.Source": "Source", "MaterialDeck.DownloadUtility.Profiles": "Profiles", "MaterialDeck.DownloadUtility.Name": "Name", - "MaterialDeck.DownloadUtility.Refresh": "Refresh" + "MaterialDeck.DownloadUtility.Refresh": "Refresh", + + "MaterialDeck.SdMsUpdateRequired": "The Stream Deck plugin and Material Server version you are using are incompatible with this version of the module.
Please update them.", + "MaterialDeck.SdUpdateRequired": "The Stream Deck plugin you are using is incompatible with this version of the module.
Please update it.", + "MaterialDeck.MsUpdateRequired": "The Material Server version you are using is incompatible with this version of the module.
Please update it." } \ No newline at end of file diff --git a/module.json b/module.json index 282fabc..480bf2a 100644 --- a/module.json +++ b/module.json @@ -2,7 +2,7 @@ "name": "MaterialDeck", "title": "Material Deck", "description": "Material Deck allows you to control Foundry using an Elgato Stream Deck", - "version": "1.4.10", + "version": "1.4.11", "author": "CDeenen", "authors": { "name": "CDeenen", @@ -11,18 +11,15 @@ "patreon": "MaterialFoundry", "reddit": "CDeenen123" }, - "flags": { - "minimumSDversion": "1.4.7", - "minimumMSversion": "1.0.2" - }, - "minimumSDversion": "1.4.7", - "minimumMSversion": "1.0.2", - "esmodules": [ - "./MaterialDeck.js" - ], + "esmodules": ["./MaterialDeck.js"], + "styles": ["./css/style.css"], "socket": true, - "minimumCoreVersion": "0.7.5", - "compatibleCoreVersion": "9", + "minimumCoreVersion": "9", + "compatibleCoreVersion": "10", + "compatibility": { + "minimum": "9", + "verified": "10" + }, "languages": [ { "lang": "en", diff --git a/src/combattracker.js b/src/actions/combattracker.js similarity index 84% rename from src/combattracker.js rename to src/actions/combattracker.js index c478603..224353b 100644 --- a/src/combattracker.js +++ b/src/actions/combattracker.js @@ -1,6 +1,4 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck, tokenControl} from "../MaterialDeck.js"; -import {compatibleCore} from "./misc.js"; +import { streamDeck, tokenControl, getPermission } from "../../MaterialDeck.js"; export class CombatTracker{ constructor(){ @@ -32,7 +30,7 @@ export class CombatTracker{ settings.icon = settings.displayIcon ? 'tokenIcon' : 'none'; if (mode == 'combatants'){ - if (MODULE.getPermission('COMBAT','DISPLAY_COMBATANTS') == false) { + if (getPermission('COMBAT','DISPLAY_COMBATANTS') == false) { streamDeck.noPermission(context,device,device,false,"combat tracker"); return; } @@ -44,7 +42,7 @@ export class CombatTracker{ const combatant = initiativeOrder[nr] if (combatant != undefined){ - const tokenId = compatibleCore("0.8.1") ? combatant.data.tokenId : combatant.tokenId; + const tokenId = combatant.data.tokenId; tokenControl.pushData(tokenId,settings,context,device,combatantState,'#cccc00'); return; } @@ -59,12 +57,12 @@ export class CombatTracker{ } } else if (mode == 'currentCombatant'){ - if (MODULE.getPermission('COMBAT','DISPLAY_COMBATANTS') == false) { + if (getPermission('COMBAT','DISPLAY_COMBATANTS') == false) { streamDeck.noPermission(context,device,device); return; } if (combat != null && combat != undefined && combat.started){ - const tokenId = compatibleCore("0.8.1") ? combat.combatant.data.tokenId : combat.combatant.tokenId; + const tokenId = combat.combatant.data.tokenId; tokenControl.pushData(tokenId,settings,context,device); } else { @@ -74,15 +72,15 @@ export class CombatTracker{ } else if (mode == 'function'){ - if (ctFunction == 'turnDisplay' && MODULE.getPermission('COMBAT','TURN_DISPLAY') == false) { + if (ctFunction == 'turnDisplay' && getPermission('COMBAT','TURN_DISPLAY') == false) { streamDeck.noPermission(context,device); return; } - else if (ctFunction == 'endTurn' && MODULE.getPermission('COMBAT','END_TURN') == false) { + else if (ctFunction == 'endTurn' && getPermission('COMBAT','END_TURN') == false) { streamDeck.noPermission(context,device); return; } - else if (ctFunction != 'turnDisplay' && ctFunction != 'endTurn' && MODULE.getPermission('COMBAT','OTHER_FUNCTIONS') == false) { + else if (ctFunction != 'turnDisplay' && ctFunction != 'endTurn' && getPermission('COMBAT','OTHER_FUNCTIONS') == false) { streamDeck.noPermission(context,device); return; } @@ -130,6 +128,8 @@ export class CombatTracker{ if (settings.displayRound && settings.displayTurn) txt += "\n"; if (settings.displayTurn) txt += "Turn\n"+turn; } + else if (ctFunction == 'rollInitiative' || ctFunction == 'rollInitiativeNPC') + src = "modules/MaterialDeck/img/token/init.png"; streamDeck.setIcon(context,device,src,{background:background}); streamDeck.setTitle(txt,context); @@ -145,20 +145,19 @@ export class CombatTracker{ if (combat == null || combat == undefined) return; const ctFunction = settings.combatTrackerFunction ? settings.combatTrackerFunction : 'startStop'; - if (ctFunction == 'turnDisplay' && MODULE.getPermission('COMBAT','TURN_DISPLAY') == false) { + if (ctFunction == 'turnDisplay' && getPermission('COMBAT','TURN_DISPLAY') == false) { streamDeck.noPermission(context,device); return; } - else if (ctFunction == 'endTurn' && MODULE.getPermission('COMBAT','END_TURN') == false) { + else if (ctFunction == 'endTurn' && getPermission('COMBAT','END_TURN') == false) { streamDeck.noPermission(context,device); return; } - else if (ctFunction != 'turnDisplay' && ctFunction != 'endTurn' && MODULE.getPermission('COMBAT','OTHER_FUNCTIONS') == false) { + else if (ctFunction != 'turnDisplay' && ctFunction != 'endTurn' && getPermission('COMBAT','OTHER_FUNCTIONS') == false) { streamDeck.noPermission(context,device); return; } - - if (ctFunction == 'startStop'){ + else if (ctFunction == 'startStop'){ let src; let background; if (game.combat.started){ @@ -169,8 +168,10 @@ export class CombatTracker{ } return; } - if (game.combat.started == false) return; + else if (ctFunction == 'rollInitiative' && getPermission('COMBAT','OTHER_FUNCTIONS')) game.combat.rollAll(); + else if (ctFunction == 'rollInitiativeNPC' && getPermission('COMBAT','OTHER_FUNCTIONS')) game.combat.rollNPC(); + if (game.combat.started == false) return; if (ctFunction == 'nextTurn') await game.combat.nextTurn(); else if (ctFunction == 'prevTurn') await game.combat.previousTurn(); else if (ctFunction == 'nextRound') await game.combat.nextRound(); @@ -181,7 +182,6 @@ export class CombatTracker{ const token = canvas.tokens.placeables.filter(token => token.id == game.combat.combatant.token.id)[0]; if (token.can(game.userId,"control")) token.control(); } - } else { const onClick = settings.onClick ? settings.onClick : 'doNothing'; @@ -193,12 +193,12 @@ export class CombatTracker{ if (nr == undefined || nr < 1) nr = 0; const combatant = initiativeOrder[nr] if (combatant == undefined) return; - tokenId = compatibleCore("0.8.1") ? combatant.data.tokenId : combatant.tokenId; + tokenId = combatant.data.tokenId; } } else if (mode == 'currentCombatant') if (combat != null && combat != undefined && combat.started) - tokenId = compatibleCore("0.8.1") ? combat.combatant.data.tokenId : combat.combatant.tokenId; + tokenId = combat.combatant.data.tokenId; let token = (canvas.tokens.children[0] != undefined) ? canvas.tokens.children[0].children.find(p => p.id == tokenId) : undefined; if (token == undefined) return; @@ -226,6 +226,12 @@ export class CombatTracker{ if (element == null) token.sheet.render(true); else token.sheet.close(); } + else if (onClick == 'rollInitiative') { + token.actor.rollInitiative({rerollInitiative:true}); + } + else if (onClick == 'target') { + token.setTarget(!token.isTargeted,{releaseOthers:false}) + } } } diff --git a/src/external.js b/src/actions/external.js similarity index 98% rename from src/external.js rename to src/actions/external.js index 5fcb263..c800a34 100644 --- a/src/external.js +++ b/src/actions/external.js @@ -1,4 +1,5 @@ -import {streamDeck} from "../MaterialDeck.js"; +import { streamDeck } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; export class ExternalModules{ soundscapeSettings = { @@ -113,14 +114,14 @@ export class ExternalModules{ let name = ''; if (type == 'weatherControls') { const effect = (settings.weatherEffect == undefined) ? 'leaves' : settings.weatherEffect; - name = CONFIG.fxmaster.weather[effect].label; - icon = CONFIG.fxmaster.weather[effect].icon; + name = compatibleCore('10.0') ? game.i18n.localize(CONFIG.fxmaster.particleEffects[effect].label) : CONFIG.fxmaster.weather[effect].label; + icon = compatibleCore('10.0') ? CONFIG.fxmaster.particleEffects[effect].icon : CONFIG.fxmaster.weather[effect].icon; ring = canvas.scene.getFlag("fxmaster", "effects")?.[`core_${effect}`] ? 2 : 1; ringColor = ring < 2 ? '#000000' : "#00ff00"; } else if (type == 'filters') { const filter = (settings.fxMasterFilter == undefined) ? 'underwater' : settings.fxMasterFilter; - name = CONFIG.fxmaster.filters[filter].label; + name = compatibleCore('10.0') ? game.i18n.localize(CONFIG.fxmaster.filterEffects[filter].label) : CONFIG.fxmaster.filters[filter].label; background = "#340057"; if (displayIcon){ if (filter == 'lightning') icon = "fas fa-bolt"; @@ -174,7 +175,7 @@ export class ExternalModules{ applyColor: (settings.fxWeatherEnColor == undefined) ? false : settings.fxWeatherEnColor } - Hooks.call("fxmaster.switchWeather", { + Hooks.call(compatibleCore('10.0') ? "fxmaster.switchParticleEffect" : "fxmaster.switchWeather", { name: `core_${effect}`, type: effect, options, diff --git a/src/macro.js b/src/actions/macro.js similarity index 80% rename from src/macro.js rename to src/actions/macro.js index 3e12fd5..c3b75c6 100644 --- a/src/macro.js +++ b/src/actions/macro.js @@ -1,6 +1,5 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck} from "../MaterialDeck.js"; -import {compatibleCore} from "./misc.js"; +import { moduleName, streamDeck, getPermission, hotbarUses } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; export class MacroControl{ constructor(){ @@ -39,7 +38,7 @@ export class MacroControl{ let uses = undefined; if (mode == 'macroBoard') { //Macro board - if ((MODULE.getPermission('MACRO','MACROBOARD') == false )) { + if ((getPermission('MACRO','MACROBOARD') == false )) { streamDeck.noPermission(context,device); return; } @@ -57,8 +56,8 @@ export class MacroControl{ else { //Execute macro macroNumber += this.offset - 1; if (macroNumber < 0) macroNumber = 0; - macroId = game.settings.get(MODULE.moduleName,'macroSettings').macros[macroNumber]; - background = game.settings.get(MODULE.moduleName,'macroSettings').color[macroNumber]; + macroId = game.settings.get(moduleName,'macroSettings').macros[macroNumber]; + background = game.settings.get(moduleName,'macroSettings').color[macroNumber]; if (background == undefined) background = '#000000'; ring = 0; } @@ -69,11 +68,11 @@ export class MacroControl{ macroId = macro?.id; } else { //Macro Hotbar - if ((MODULE.getPermission('MACRO','HOTBAR') == false )) { + if ((getPermission('MACRO','HOTBAR') == false )) { streamDeck.noPermission(context,device); return; } - if (mode == 'hotbar') macroId = game.user.data.hotbar[macroNumber]; + if (mode == 'hotbar') macroId = compatibleCore('10.0') ? game.user.hotbar[macroNumber] : game.user.data.hotbar[macroNumber]; else { let macros; if (mode == 'customHotbar' && game.modules.get('custom-hotbar') != undefined) @@ -91,7 +90,7 @@ export class MacroControl{ if (macro != undefined) { if (displayName) name = macro.name; if (displayIcon) src = macro.img; - if (MODULE.hotbarUses && displayUses) uses = await this.getUses(macro); + if (hotbarUses && displayUses) uses = await this.getUses(macro); } } else { @@ -126,7 +125,7 @@ export class MacroControl{ let macroNumber = data.settings.macroNumber; if(macroNumber == undefined || isNaN(parseInt(macroNumber))) macroNumber = 1; - if ((MODULE.getPermission('MACRO','HOTBAR') == false )) { + if ((getPermission('MACRO','HOTBAR') == false )) { streamDeck.noPermission(context,device); return; } @@ -150,7 +149,7 @@ export class MacroControl{ if (macro != undefined && macro != null) { if (displayName) name += macro.name; if (displayIcon) src += macro.img; - if (MODULE.hotbarUses && displayUses) uses = await this.getUses(macro); + if (hotbarUses && displayUses) uses = await this.getUses(macro); } streamDeck.setIcon(context,device,src,{background:background,uses:uses}); streamDeck.setTitle(name,context); @@ -165,11 +164,11 @@ export class MacroControl{ let target = settings.target ? settings.target : undefined; if (mode == 'hotbar' || mode == 'visibleHotbar' || mode == 'customHotbar'){ - if ((MODULE.getPermission('MACRO','HOTBAR') == false )) return; - this.executeHotbar(macroNumber,mode); + if ((getPermission('MACRO','HOTBAR') == false )) return; + this.executeHotbar(macroNumber,mode,target); } else if (mode == 'name') { - if ((MODULE.getPermission('MACRO','BY_NAME') == false )) return; + if ((getPermission('MACRO','BY_NAME') == false )) return; const macroName = settings.macroNumber; const macro = game.macros.getName(macroName); @@ -177,16 +176,13 @@ export class MacroControl{ const args = settings.macroArgs ? settings.macroArgs : ""; - let furnaceEnabled = false; - let furnace = game.modules.get("furnace"); - if (furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) furnaceEnabled = true; - + let advancedMacrosEnabled = false; let advancedMacros = game.modules.get("advanced-macros"); - if (advancedMacros != undefined && advancedMacros.active) furnaceEnabled = true; + if (advancedMacros != undefined && advancedMacros.active) advancedMacrosEnabled = true; - if (args == "" || args == " ") furnaceEnabled = false; + if (args == "" || args == " ") advancedMacrosEnabled = false; - if (furnaceEnabled == false) macro.execute({token:target}); + if (advancedMacrosEnabled == false) macro.execute({token:target}); else { let chatData = { user: game.user._id, @@ -198,7 +194,7 @@ export class MacroControl{ } else { - if ((MODULE.getPermission('MACRO','MACROBOARD') == false )) return; + if ((getPermission('MACRO','MACROBOARD') == false )) return; if (settings.macroBoardMode == 'offset') { let macroOffset = settings.macroOffset; if (macroOffset == undefined) macroOffset = 0; @@ -210,9 +206,9 @@ export class MacroControl{ } } - executeHotbar(macroNumber,mode){ + executeHotbar(macroNumber,mode,target){ let macroId - if (mode == 'hotbar') macroId = game.user.data.hotbar[macroNumber]; + if (mode == 'hotbar') macroId = compatibleCore('10.0') ? game.user.hotbar[macroNumber] : game.user.data.hotbar[macroNumber]; else { let macros; if (mode == 'customHotbar' && game.modules.get('custom-hotbar') != undefined) { @@ -224,29 +220,26 @@ export class MacroControl{ } if (macroId == undefined) return; let macro = game.macros.get(macroId); - macro.execute(); + macro.execute({token:target}); } executeBoard(macroNumber){ macroNumber = parseInt(macroNumber); macroNumber += this.offset - 1; if (macroNumber < 0) macroNumber = 0; - var macroId = game.settings.get(MODULE.moduleName,'macroSettings').macros[macroNumber]; + var macroId = game.settings.get(moduleName,'macroSettings').macros[macroNumber]; if (macroId != undefined){ let macro = game.macros.get(macroId); if (macro != undefined && macro != null) { - const args = game.settings.get(MODULE.moduleName,'macroSettings').args; - let furnaceEnabled = false; - let furnace = game.modules.get("furnace"); - if (furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) furnaceEnabled = true; - + const args = game.settings.get(moduleName,'macroSettings').args; + let advancedMacrosEnabled = false; let advancedMacros = game.modules.get("advanced-macros"); - if (advancedMacros != undefined && advancedMacros.active) furnaceEnabled = true; + if (advancedMacros != undefined && advancedMacros.active) advancedMacrosEnabled = true; - if (args == undefined || args[macroNumber] == undefined || args[macroNumber] == "") furnaceEnabled = false; + if (args == undefined || args[macroNumber] == undefined || args[macroNumber] == "") advancedMacrosEnabled = false; - if (furnaceEnabled == false) macro.execute(); + if (advancedMacrosEnabled == false) macro.execute(); else { let chatData = { user: game.user._id, diff --git a/src/othercontrols.js b/src/actions/othercontrols.js similarity index 81% rename from src/othercontrols.js rename to src/actions/othercontrols.js index 3e693ad..237ccb3 100644 --- a/src/othercontrols.js +++ b/src/actions/othercontrols.js @@ -1,6 +1,5 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck, gamingSystem} from "../MaterialDeck.js"; -import {compatibleCore} from "./misc.js"; +import { streamDeck, gamingSystem, getPermission } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; export class OtherControls{ constructor(){ @@ -94,7 +93,7 @@ export class OtherControls{ ////////////////////////////////////////////////////////////////////////////////////////////////// updatePause(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','PAUSE') == false ) { + if (getPermission('OTHER','PAUSE') == false ) { streamDeck.noPermission(context,device); return; } @@ -119,7 +118,7 @@ export class OtherControls{ } keyPressPause(settings){ - if (MODULE.getPermission('OTHER','PAUSE') == false ) return; + if (getPermission('OTHER','PAUSE') == false ) return; const pauseFunction = settings.pauseFunction ? settings.pauseFunction : 'pause'; @@ -186,7 +185,7 @@ export class OtherControls{ } else { let viewPosition = canvas.scene._viewPosition; - const gridSize = canvas.scene.data.grid; + const gridSize = compatibleCore('10.0') ? canvas.scene.grid.size : canvas.scene.data.grid; viewPosition.duration = 100; if (dir == 'up') viewPosition.y -= gridSize; @@ -220,7 +219,7 @@ export class OtherControls{ ////////////////////////////////////////////////////////////////////////////////////////// updateControl(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','CONTROL') == false ) { + if (getPermission('OTHER','CONTROL') == false ) { streamDeck.noPermission(context,device); return; } @@ -241,19 +240,19 @@ export class OtherControls{ controlNr--; controlNr += this.controlsOffset; - const selectedControl = ui.controls.controls[controlNr]; + const selectedControl = ui.controls.controls[controlNr]; if (selectedControl != undefined){ if (selectedControl.visible == false) { streamDeck.noPermission(context,device,false); return; } - if (tool == 'open'){ //open category + //if (tool == 'open'){ //open category txt = game.i18n.localize(selectedControl.title); src = selectedControl.icon; if (activeControl == selectedControl.name) ringColor = "#FF7B00"; - } + //} } } else if (control == 'dispTools'){ //displayed tools @@ -330,7 +329,7 @@ export class OtherControls{ } keyPressControl(settings){ - if (MODULE.getPermission('OTHER','CONTROL') == false ) return; + if (getPermission('OTHER','CONTROL') == false ) return; if (canvas.scene == null) return; const control = settings.control ? settings.control : 'dispControls'; const tool = settings.tool ? settings.tool : 'open'; @@ -346,9 +345,12 @@ export class OtherControls{ streamDeck.noPermission(context,device,false); return; } - ui.controls.activeControl = selectedControl.name; - selectedControl.activeTool = selectedControl.activeTool; - if (compatibleCore("0.8.2")) { + if (compatibleCore('10.0')) { + ui.controls.initialize({layer: selectedControl.layer}); + } + else { + ui.controls.activeControl = selectedControl.name; + selectedControl.activeTool = selectedControl.activeTool; for (let layer of canvas.layers) { if (layer.options == undefined) continue; if (layer.options.name == selectedControl.layer) { @@ -357,7 +359,6 @@ export class OtherControls{ } } } - else canvas.getLayer(selectedControl.layer).activate(); } } else if (control == 'dispTools'){ //displayed tools @@ -383,8 +384,15 @@ export class OtherControls{ else if (selectedTool.button){ selectedTool.onClick(); } - else - selectedControl.activeTool = selectedTool.name; + else { + if (compatibleCore('10.0')) { + ui.controls.initialize({layer: selectedControl.layer, tool: selectedTool.name}); + } + else { + selectedControl.activeTool = selectedTool.name; + } + } + } } } @@ -406,9 +414,12 @@ export class OtherControls{ return; } if (tool == 'open'){ //open category - ui.controls.activeControl = control; - selectedControl.activeTool = selectedControl.activeTool; - if (compatibleCore("0.8.2")) { + if (compatibleCore('10.0')) { + ui.controls.initialize({layer: selectedControl.layer}); + } + else { + ui.controls.activeControl = control; + selectedControl.activeTool = selectedControl.activeTool; for (let layer of canvas.layers) { if (layer.options == undefined) continue; if (layer.options.name == selectedControl.layer) { @@ -417,7 +428,7 @@ export class OtherControls{ } } } - else canvas.getLayer(selectedControl.layer).activate(); + } else { const selectedTool = selectedControl.tools.find(t => t.name == tool); @@ -426,8 +437,21 @@ export class OtherControls{ streamDeck.noPermission(context,device,false); return; } - ui.controls.activeControl = control; - if (compatibleCore("0.8.2")) { + if (compatibleCore('10.0')) { + if (selectedTool.toggle) { + ui.controls.initialize({layer: selectedControl.layer}); + selectedTool.active = !selectedTool.active; + selectedTool.onClick(selectedTool.active); + } + else if (selectedTool.button){ + ui.controls.initialize({layer: selectedControl.layer}); + selectedTool.onClick(); + } + else + ui.controls.initialize({layer: selectedControl.layer, tool: selectedTool.name}); + } + else { + ui.controls.activeControl = control; for (let layer of canvas.layers) { if (layer.options == undefined) continue; if (layer.options.name == selectedControl.layer) { @@ -435,17 +459,17 @@ export class OtherControls{ break; } } + if (selectedTool.toggle) { + selectedTool.active = !selectedTool.active; + selectedTool.onClick(selectedTool.active); + } + else if (selectedTool.button){ + selectedTool.onClick(); + } + else + selectedControl.activeTool = tool; } - else canvas.getLayer(selectedControl.layer).activate(); - if (selectedTool.toggle) { - selectedTool.active = !selectedTool.active; - selectedTool.onClick(selectedTool.active); - } - else if (selectedTool.button){ - selectedTool.onClick(); - } - else - selectedControl.activeTool = tool; + } } } @@ -456,7 +480,7 @@ export class OtherControls{ ////////////////////////////////////////////////////////////////////////////////////////// updateDarkness(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','DARKNESS') == false ) { + if (getPermission('OTHER','DARKNESS') == false ) { streamDeck.noPermission(context,device); return; } @@ -476,7 +500,7 @@ export class OtherControls{ } else if (func == 'disp'){ //display darkness src = 'modules/MaterialDeck/img/other/darkness/darkness.png'; - const darkness = canvas.scene != null ? Math.floor(canvas.scene.data.darkness*100)/100 : ''; + const darkness = canvas.scene != null ? compatibleCore('10.0') ? Math.floor(canvas.scene.darkness*100)/100 : Math.floor(canvas.scene.data.darkness*100)/100 : ''; txt += darkness; } streamDeck.setTitle(txt,context); @@ -485,39 +509,48 @@ export class OtherControls{ keyPressDarkness(settings) { if (canvas.scene == null) return; - if (MODULE.getPermission('OTHER','DARKNESS') == false ) return; + if (getPermission('OTHER','DARKNESS') == false ) return; const func = settings.darknessFunction ? settings.darknessFunction : 'value'; const value = parseFloat(settings.darknessValue) ? parseFloat(settings.darknessValue) : 0; + const animateDarkness = parseInt(settings.darknessAnimation) ? parseInt(settings.darknessAnimation) : 500; if (func == 'value') //value - canvas.scene.update({darkness: value}); + canvas.scene.update({darkness: value}, {animateDarkness}); else if (func == 'incDec'){ //increase/decrease - let darkness = canvas.scene.data.darkness - value; + let darkness = compatibleCore('10.0') ? canvas.scene.darkness - value : canvas.scene.data.darkness - value; if (darkness > 1) darkness = 1; if (darkness < 0) darkness = 0; - canvas.scene.update({darkness: darkness}); + canvas.scene.update({darkness: darkness}, {animateDarkness}); + } + else if (func == 'transitionDay') { + canvas.scene.update({darkness: 0}, {animateDarkness: 10000}) + } + else if (func == 'transitionNight') { + canvas.scene.update({darkness: 1}, {animateDarkness: 10000}) } } ////////////////////////////////////////////////////////////////////////////////////////// updateRollDice(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','DICE') == false ) { + if (getPermission('OTHER','DICE') == false ) { streamDeck.noPermission(context,device); return; } const background = settings.background ? settings.background : '#000000'; let txt = ''; + const formula = settings.rollDiceFormula ? settings.rollDiceFormula : '1d20 + 7'; - if (settings.displayDiceName) txt = 'Roll: ' + settings.rollDiceFormula; + if (settings.displayDiceName) txt = 'Roll: ' + formula; streamDeck.setTitle(txt,context); streamDeck.setIcon(context,device,'',{background:background}); } keyPressRollDice(settings,context,device){ - if (MODULE.getPermission('OTHER','DICE') == false ) return; - if (settings.rollDiceFormula == undefined || settings.rollDiceFormula == '') return; + if (getPermission('OTHER','DICE') == false ) return; + const formula = settings.rollDiceFormula ? settings.rollDiceFormula : '1d20 + 7'; + if (formula == '') return; const rollFunction = settings.rollDiceFunction ? settings.rollDiceFunction : 'public'; let actor; @@ -527,8 +560,8 @@ export class OtherControls{ if (actor != undefined) tokenControlled = true; let r; - if (tokenControlled) r = new Roll(settings.rollDiceFormula,actor.getRollData()); - else r = new Roll(settings.rollDiceFormula); + if (tokenControlled) r = new Roll(formula,actor.getRollData()); + else r = new Roll(formula); r.evaluate({async:false}); @@ -539,12 +572,12 @@ export class OtherControls{ r.toMessage(r,{rollMode:"selfroll"}) } else if (rollFunction == 'sd'){ - let txt = settings.displayDiceName ? 'Roll: '+settings.rollDiceFormula + '\nResult: ' : ''; + let txt = settings.displayDiceName ? 'Roll: '+formula + '\nResult: ' : ''; txt += r.total; streamDeck.setTitle(txt,context); let data = this.rollData data[context] = { - formula: settings.rollDiceFormula, + formula: formula, result: txt } this.rollData = data; @@ -556,7 +589,7 @@ export class OtherControls{ updateRollTable(settings,context,device,options={}){ const name = settings.rollTableName; if (name == undefined) return; - if (MODULE.getPermission('OTHER','TABLES') == false ) { + if (getPermission('OTHER','TABLES') == false ) { streamDeck.noPermission(context,device); return; } @@ -566,14 +599,14 @@ export class OtherControls{ if (table == undefined) return; let txt = settings.displayRollName ? table.name : ''; - let src = settings.displayRollIcon ? table.data.img : ''; + let src = settings.displayRollIcon ? (compatibleCore('10.0') ? table.img : table.data.img) : ''; if (table == undefined) { src = ''; txt = ''; } else { - if (table.permission < 2 && MODULE.getPermission('OTHER','TABLES_ALL') == false ) { + if (table.permission < 2 && getPermission('OTHER','TABLES_ALL') == false ) { streamDeck.noPermission(context,device); return; } @@ -584,7 +617,7 @@ export class OtherControls{ } keyPressRollTable(settings){ - if (MODULE.getPermission('OTHER','TABLES') == false ) return; + if (getPermission('OTHER','TABLES') == false ) return; const name = settings.rollTableName; if (name == undefined) return; @@ -592,7 +625,7 @@ export class OtherControls{ const table = game.tables.getName(name); if (table != undefined) { - if (table.permission < 2 && MODULE.getPermission('OTHER','TABLES_ALL') == false ) return; + if (table.permission < 2 && getPermission('OTHER','TABLES_ALL') == false ) return; if (func == 'open'){ //open const element = document.getElementById(table.sheet.id); if (element == null) table.sheet.render(true); @@ -609,18 +642,34 @@ export class OtherControls{ getSidebarName(nr){ let name; - if (nr == 'chat') name = game.i18n.localize("SIDEBAR.TabChat"); - else if (nr == 'combat') name = game.i18n.localize("SIDEBAR.TabCombat"); - else if (nr == 'scenes') name = game.i18n.localize("SIDEBAR.TabScenes"); - else if (nr == 'actors') name = game.i18n.localize("SIDEBAR.TabActors"); - else if (nr == 'items') name = game.i18n.localize("SIDEBAR.TabItems"); - else if (nr == 'journal') name = game.i18n.localize("SIDEBAR.TabJournal"); - else if (nr == 'tables') name = game.i18n.localize("SIDEBAR.TabTables"); - else if (nr == 'cards') name = game.i18n.localize("SIDEBAR.TabCards"); - else if (nr == 'playlists') name = game.i18n.localize("SIDEBAR.TabPlaylists"); - else if (nr == 'compendium') name = game.i18n.localize("SIDEBAR.TabCompendium"); - else if (nr == 'settings') name = game.i18n.localize("SIDEBAR.TabSettings"); - else if (nr == 'collapse') name = game.i18n.localize("SIDEBAR.CollapseToggle"); + if (compatibleCore('10.0')) { + if (nr == 'chat') name = game.i18n.localize("DOCUMENT.ChatMessages"); + else if (nr == 'combat') name = game.i18n.localize("DOCUMENT.Combats"); + else if (nr == 'scenes') name = game.i18n.localize("DOCUMENT.Scenes"); + else if (nr == 'actors') name = game.i18n.localize("DOCUMENT.Actors"); + else if (nr == 'items') name = game.i18n.localize("DOCUMENT.Items"); + else if (nr == 'journal') name = game.i18n.localize("DOCUMENT.JournalEntries"); + else if (nr == 'tables') name = game.i18n.localize("DOCUMENT.RollTables"); + else if (nr == 'cards') name = game.i18n.localize("DOCUMENT.Cards"); + else if (nr == 'playlists') name = game.i18n.localize("DOCUMENT.Playlists"); + else if (nr == 'compendium') name = game.i18n.localize("SIDEBAR.TabCompendium"); + else if (nr == 'settings') name = game.i18n.localize("SIDEBAR.TabSettings"); + else if (nr == 'collapse') name = game.i18n.localize("SIDEBAR.CollapseToggle"); + } + else { + if (nr == 'chat') name = game.i18n.localize("SIDEBAR.TabChat"); + else if (nr == 'combat') name = game.i18n.localize("SIDEBAR.TabCombat"); + else if (nr == 'scenes') name = game.i18n.localize("SIDEBAR.TabScenes"); + else if (nr == 'actors') name = game.i18n.localize("SIDEBAR.TabActors"); + else if (nr == 'items') name = game.i18n.localize("SIDEBAR.TabItems"); + else if (nr == 'journal') name = game.i18n.localize("SIDEBAR.TabJournal"); + else if (nr == 'tables') name = game.i18n.localize("SIDEBAR.TabTables"); + else if (nr == 'cards') name = game.i18n.localize("SIDEBAR.TabCards"); + else if (nr == 'playlists') name = game.i18n.localize("SIDEBAR.TabPlaylists"); + else if (nr == 'compendium') name = game.i18n.localize("SIDEBAR.TabCompendium"); + else if (nr == 'settings') name = game.i18n.localize("SIDEBAR.TabSettings"); + else if (nr == 'collapse') name = game.i18n.localize("SIDEBAR.CollapseToggle"); + } return name; } @@ -629,11 +678,11 @@ export class OtherControls{ if (nr == 'chat') icon = window.CONFIG.ChatMessage.sidebarIcon; else if (nr == 'combat') icon = window.CONFIG.Combat.sidebarIcon; else if (nr == 'scenes') icon = window.CONFIG.Scene.sidebarIcon; - else if (nr == 'actors') icon = "fas fa-users"; + else if (nr == 'actors') icon = window.CONFIG.Actor.sidebarIcon; else if (nr == 'items') icon = window.CONFIG.Item.sidebarIcon; else if (nr == 'journal') icon = window.CONFIG.JournalEntry.sidebarIcon; else if (nr == 'tables') icon = window.CONFIG.RollTable.sidebarIcon; - else if (nr == 'cards') icon = "fas fa-id-badge"; + else if (nr == 'cards') icon = window.CONFIG.Cards.sidebarIcon; else if (nr == 'playlists') icon = window.CONFIG.Playlist.sidebarIcon; else if (nr == 'compendium') icon = "fas fa-atlas"; else if (nr == 'settings') icon = "fas fa-cogs"; @@ -642,7 +691,7 @@ export class OtherControls{ } updateSidebar(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','SIDEBAR') == false ) { + if (getPermission('OTHER','SIDEBAR') == false ) { streamDeck.noPermission(context,device); return; } @@ -666,7 +715,7 @@ export class OtherControls{ } keyPressSidebar(settings){ - if (MODULE.getPermission('OTHER','SIDEBAR') == false ) return; + if (getPermission('OTHER','SIDEBAR') == false ) return; const sidebarTab = settings.sidebarTab ? settings.sidebarTab : 'chat'; const popOut = settings.sidebarPopOut ? settings.sidebarPopOut : false; @@ -718,17 +767,17 @@ export class OtherControls{ updateCompendium(settings,context,device,options={}){ const name = settings.compendiumName; if (name == undefined) return; - if (MODULE.getPermission('OTHER','COMPENDIUM') == false ) { + if (getPermission('OTHER','COMPENDIUM') == false ) { streamDeck.noPermission(context,device); return; } const compendium = game.packs.find(p=>p.metadata.label == name); if (compendium == undefined) return; - if (compendium.private && MODULE.getPermission('OTHER','COMPENDIUM_ALL') == false) { + if (compendium.private && getPermission('OTHER','COMPENDIUM_ALL') == false) { streamDeck.noPermission(context,device); return; } - const rendered = compatibleCore("0.8.5") ? compendium.apps[0].rendered : compendium.rendered; + const rendered = compendium.apps[0].rendered; const background = settings.background ? settings.background : '#000000'; const ringOffColor = settings.offRing ? settings.offRing : '#000000'; const ringOnColor = settings.onRing ? settings.onRing : '#00FF00'; @@ -742,13 +791,13 @@ export class OtherControls{ keyPressCompendium(settings){ let name = settings.compendiumName; if (name == undefined) return; - if (MODULE.getPermission('OTHER','COMPENDIUM') == false ) return; + if (getPermission('OTHER','COMPENDIUM') == false ) return; const compendium = game.packs.find(p=>p.metadata.label == name); - const rendered = compatibleCore("0.8.5") ? compendium.apps[0].rendered : compendium.rendered; + const rendered = compendium.apps[0].rendered; if (compendium == undefined) return; - if (compendium.private && MODULE.getPermission('OTHER','COMPENDIUM_ALL') == false) return; - else if (rendered) compatibleCore("0.8.5") ? compendium.apps[0].close() : compendium.close(); + if (compendium.private && getPermission('OTHER','COMPENDIUM_ALL') == false) return; + else if (rendered) compendium.apps[0].close(); else compendium.render(true); } @@ -761,11 +810,11 @@ export class OtherControls{ const journal = game.journal.getName(name); if (journal == undefined) return; - if (MODULE.getPermission('OTHER','JOURNAL') == false ) { + if (getPermission('OTHER','JOURNAL') == false ) { streamDeck.noPermission(context,device); return; } - if (journal.permission < 2 && MODULE.getPermission('OTHER','JOURNAL_ALL') == false ) { + if (journal.permission < 2 && getPermission('OTHER','JOURNAL_ALL') == false ) { streamDeck.noPermission(context,device); return; } @@ -795,8 +844,8 @@ export class OtherControls{ const journal = game.journal.getName(name); if (journal == undefined) return; - if (MODULE.getPermission('OTHER','JOURNAL') == false ) return; - if (journal.permission < 2 && MODULE.getPermission('OTHER','JOURNAL_ALL') == false ) return; + if (getPermission('OTHER','JOURNAL') == false ) return; + if (journal.permission < 2 && getPermission('OTHER','JOURNAL_ALL') == false ) return; if (journal.sheet.rendered == false) journal.sheet.render(true); else journal.sheet.close(); @@ -805,7 +854,7 @@ export class OtherControls{ ////////////////////////////////////////////////////////////////////////////////////////// updateChatMessage(settings,context,device,options={}){ - if (MODULE.getPermission('OTHER','CHAT') == false ) { + if (getPermission('OTHER','CHAT') == false ) { streamDeck.noPermission(context,device); return; } @@ -815,7 +864,7 @@ export class OtherControls{ } keyPressChatMessage(settings){ - if (MODULE.getPermission('OTHER','CHAT') == false ) return; + if (getPermission('OTHER','CHAT') == false ) return; const message = settings.chatMessage ? settings.chatMessage : ''; let chatData = { diff --git a/src/playlist.js b/src/actions/playlist.js similarity index 87% rename from src/playlist.js rename to src/actions/playlist.js index f786381..f28fe3a 100644 --- a/src/playlist.js +++ b/src/actions/playlist.js @@ -1,6 +1,4 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck} from "../MaterialDeck.js"; -import {compatibleCore} from "./misc.js"; +import { moduleName, streamDeck, getPermission } from "../../MaterialDeck.js"; export class PlaylistControl{ constructor(){ @@ -22,7 +20,7 @@ export class PlaylistControl{ } update(settings,context,device){ - if (MODULE.getPermission('PLAYLIST','PLAY') == false ) { + if (getPermission('PLAYLIST','PLAY') == false ) { streamDeck.noPermission(context,device); return; } @@ -84,7 +82,7 @@ export class PlaylistControl{ let playlistOffset = parseInt(settings.offset); if (isNaN(playlistOffset)) playlistOffset = 0; let number = parseInt(this.playlistOffset + playlistOffset); - const nrOfPlaylists = parseInt(game.settings.get(MODULE.moduleName,'playlists').playlistNumber); + const nrOfPlaylists = parseInt(game.settings.get(moduleName,'playlists').playlistNumber); if (number < 0) number += nrOfPlaylists; else if (number >= nrOfPlaylists) number -= nrOfPlaylists; const targetPlaylist = this.getPlaylist(number); @@ -116,7 +114,7 @@ export class PlaylistControl{ let playlist = this.getPlaylist(playlistNr); if (playlist != undefined){ - const track = compatibleCore("0.8.1") ? playlist.sounds.contents[trackNr] : playlist.sounds[trackNr]; + const track = playlist.sounds.contents[trackNr]; if (track != undefined){ if (track.playing) ringColor = ringOnColor; @@ -157,7 +155,7 @@ export class PlaylistControl{ } else { let playing = game.playlists.playing; - let settings = game.settings.get(MODULE.moduleName,'playlists'); + let settings = game.settings.get(moduleName,'playlists'); let selectedPlaylists = settings.selectedPlaylist; for (let i=0; i p == playing[i]._id); @@ -189,14 +187,14 @@ export class PlaylistControl{ } getPlaylist(num){ - let selectedPlaylists = game.settings.get(MODULE.moduleName,'playlists').selectedPlaylist; + let selectedPlaylists = game.settings.get(moduleName,'playlists').selectedPlaylist; if (selectedPlaylists != undefined) return game.playlists.get(selectedPlaylists[num]); else return undefined; } keyPress(settings,context,device){ - if (MODULE.getPermission('PLAYLIST','PLAY') == false ) return; + if (getPermission('PLAYLIST','PLAY') == false ) return; let playlistNr = settings.playlistNr; if (playlistNr == undefined || playlistNr < 1) playlistNr = 1; playlistNr--; @@ -222,7 +220,7 @@ export class PlaylistControl{ if (playlistMode == 'playlist') this.playPlaylist(playlist,playlistNr); else { - const track = compatibleCore("0.8.1") ? playlist.sounds.contents[trackNr] : playlist.sounds[trackNr]; + const track = playlist.sounds.contents[trackNr]; if (track != undefined){ this.playTrack(track,playlist,playlistNr); } @@ -251,7 +249,7 @@ export class PlaylistControl{ let playlistOffset = parseInt(settings.offset); if (isNaN(playlistOffset)) playlistOffset = 0; let number = parseInt(this.playlistOffset + playlistOffset); - const nrOfPlaylists = parseInt(game.settings.get(MODULE.moduleName,'playlists').playlistNumber); + const nrOfPlaylists = parseInt(game.settings.get(moduleName,'playlists').playlistNumber); if (number < 0) number += nrOfPlaylists; else if (number >= nrOfPlaylists) number -= nrOfPlaylists; this.playlistOffset = number; @@ -281,12 +279,15 @@ export class PlaylistControl{ playlist.stopAll(); return; } - let mode = game.settings.get(MODULE.moduleName,'playlists').playlistMode[playlistNr]; + let mode = game.settings.get(moduleName,'playlists').playlistMode[playlistNr]; + const originalPlayMode = playlist.mode; + await playlist.update({mode: CONST.PLAYLIST_MODES.SEQUENTIAL}); if (mode == 0) { - mode = game.settings.get(MODULE.moduleName,'playlists').playMode; - if (mode == 2) await this.stopAll(); + mode = game.settings.get(moduleName,'playlists').playMode; + if (mode == 2) await this.stopAll(true); } playlist.playAll(); + await playlist.update({mode: originalPlayMode}); } async playTrack(track,playlist,playlistNr){ @@ -301,23 +302,25 @@ export class PlaylistControl{ return; } let play; + const originalPlayMode = playlist.mode; if (track.playing) play = false; else { play = true; - let mode = game.settings.get(MODULE.moduleName,'playlists').playlistMode[playlistNr]; + let mode = game.settings.get(moduleName,'playlists').playlistMode[playlistNr]; if (mode == 0) { - mode = game.settings.get(MODULE.moduleName,'playlists').playMode; - if (mode == 1) await playlist.stopAll(); - else if (mode == 2) await this.stopAll(); + mode = game.settings.get(moduleName,'playlists').playMode; + if (mode == 0) await playlist.update({mode: CONST.PLAYLIST_MODES.SIMULTANEOUS}); + else if (mode == 1) await playlist.stopAll(); + else if (mode == 2) await this.stopAll(true); } - else if (mode == 2) await playlist.stopAll(); + else if (mode == 2) await playlist.stopAll(true); } - if (compatibleCore("0.8.1") && play) await playlist.playSound(track); - else if (compatibleCore("0.8.1")) await playlist.stopSound(track); - else await playlist.updateEmbeddedEntity("PlaylistSound", {_id: track._id, playing: play}); + if (play) await playlist.playSound(track); + else await playlist.stopSound(track); playlist.update({playing: play}); + await playlist.update({mode: originalPlayMode}); } } \ No newline at end of file diff --git a/src/scene.js b/src/actions/scene.js similarity index 72% rename from src/scene.js rename to src/actions/scene.js index 2eee566..abed080 100644 --- a/src/scene.js +++ b/src/actions/scene.js @@ -1,6 +1,5 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck} from "../MaterialDeck.js"; -import {compatibleCore} from "./misc.js"; +import { streamDeck, getPermission } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; export class SceneControl{ constructor(){ @@ -34,7 +33,7 @@ export class SceneControl{ let src = ""; let name = ""; if (func == 'visible') { //visible scenes - if (MODULE.getPermission('SCENE','VISIBLE') == false ) { + if (getPermission('SCENE','VISIBLE') == false ) { streamDeck.noPermission(context,device); return; } @@ -47,12 +46,12 @@ export class SceneControl{ if (scene != undefined){ ringColor = scene.isView ? ringOnColor : ringOffColor; if (settings.displaySceneName) name = scene.name; - if (settings.displaySceneIcon) src = scene.img; + if (settings.displaySceneIcon) src = compatibleCore('10.0') ? scene.background.src : scene.img; if (scene.active) name += "\n(Active)"; } } else if (func == 'dir') { //from directory - if (MODULE.getPermission('SCENE','DIRECTORY') == false ) { + if (getPermission('SCENE','DIRECTORY') == false ) { streamDeck.noPermission(context,device); return; } @@ -61,33 +60,39 @@ export class SceneControl{ nr--; let sceneList = []; - for (let i=0; i 0) ? true : false; const play = (this.activeSounds[soundNr] == undefined) ? true : false; @@ -107,7 +105,7 @@ export class SoundboardControl{ } keyPressUp(settings){ - if (MODULE.getPermission('SOUNDBOARD','PLAY') == false ) return; + if (getPermission('SOUNDBOARD','PLAY') == false ) return; const mode = settings.soundboardMode ? settings.soundboardMode : 'playSound'; if (mode != 'playSound') return; @@ -117,14 +115,14 @@ export class SoundboardControl{ soundNr--; soundNr += this.offset; - const playMode = game.settings.get(MODULE.moduleName,'soundboardSettings').mode[soundNr]; + const playMode = game.settings.get(moduleName,'soundboardSettings').mode[soundNr]; if (playMode == 2) this.prePlaySound(soundNr,false,false); } async prePlaySound(soundNr,repeat,play){ - const soundBoardSettings = game.settings.get(MODULE.moduleName,'soundboardSettings'); + const soundBoardSettings = game.settings.get(moduleName,'soundboardSettings'); const playlistId = (soundBoardSettings.selectedPlaylists != undefined) ? soundBoardSettings.selectedPlaylists[soundNr] : undefined; let src; if (playlistId == "" || playlistId == undefined) return; @@ -144,12 +142,12 @@ export class SoundboardControl{ const soundId = soundBoardSettings.sounds[soundNr]; const sounds = game.playlists.get(playlistId).sounds; if (sounds == undefined) return; - const sound = compatibleCore("0.8.1") ? sounds.find(p => p.id == soundId) : sounds.find(p => p._id == soundId); + const sound = sounds.find(p => p.id == soundId); if (sound == undefined) return; src = sound.path; } - let volume = game.settings.get(MODULE.moduleName,'soundboardSettings').volume[soundNr]/100; + let volume = game.settings.get(moduleName,'soundboardSettings').volume[soundNr]/100; volume = AudioHelper.inputToVolume(volume); let payload = { @@ -169,32 +167,16 @@ export class SoundboardControl{ if (play){ volume *= game.settings.get("core", "globalAmbientVolume"); - if (compatibleCore("0.8.1")) { - let newSound = new Sound(src); - if(newSound.loaded == false) await newSound.load({autoplay:true}); - newSound.on('end', ()=>{ - if (repeat == false) { - this.activeSounds[soundNr] = undefined; - this.updateAll(); - } - }); - newSound.play({loop:repeat,volume:volume}); - this.activeSounds[soundNr] = newSound; - } - else { - let howl = new Howl({src, volume, loop: repeat, onend: (id)=>{ - if (repeat == false){ - this.activeSounds[soundNr] = undefined; - this.updateAll(); - } - }, - onstop: ()=>{ + let newSound = new Sound(src); + if(newSound.loaded == false) await newSound.load({autoplay:true}); + newSound.on('end', ()=>{ + if (repeat == false) { this.activeSounds[soundNr] = undefined; this.updateAll(); - }}); - howl.play(); - this.activeSounds[soundNr] = howl; - } + } + }); + newSound.play({loop:repeat,volume:volume}); + this.activeSounds[soundNr] = newSound; } else { if (this.activeSounds[soundNr] != undefined) this.activeSounds[soundNr].stop(); diff --git a/src/token.js b/src/actions/token.js similarity index 74% rename from src/token.js rename to src/actions/token.js index fc5a542..e90e80b 100644 --- a/src/token.js +++ b/src/actions/token.js @@ -1,11 +1,11 @@ -import * as MODULE from "../MaterialDeck.js"; -import {streamDeck, macroControl, otherControls, tokenHelper} from "../MaterialDeck.js"; -import { compatibleCore } from "./misc.js"; +import { streamDeck, macroControl, otherControls, tokenHelper, getPermission } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; export class TokenControl{ constructor(){ this.active = false; this.wildcardOffset = 0; + this.itemOffset = 0; } async update(tokenId=null){ @@ -29,7 +29,7 @@ export class TokenControl{ const tokenIdentifier = settings.tokenName ? settings.tokenName : ''; const prependTitle = settings.prependTitle ? settings.prependTitle : ''; const mode = settings.tokenMode ? settings.tokenMode : 'token'; - + let validToken = false; let token; if (settings.combatTrackerMode) token = tokenHelper.getTokenFromTokenId(tokenId); @@ -43,11 +43,11 @@ export class TokenControl{ let uses = undefined; let hp = undefined; if (validToken) { - if (token.owner == false && token.observer == true && MODULE.getPermission('TOKEN','OBSERVER') == false ) { + if (token.owner == false && token.observer == true && getPermission('TOKEN','OBSERVER') == false ) { streamDeck.noPermission(context,device); return; } - if (token.owner == false && token.observer == false && MODULE.getPermission('TOKEN','NON_OWNED') == false ) { + if (token.owner == false && token.observer == false && getPermission('TOKEN','NON_OWNED') == false ) { streamDeck.noPermission(context,device); return; } @@ -58,16 +58,16 @@ export class TokenControl{ const permission = token.actor?.permission; if (settings.combat){ - if (permission == 0 && MODULE.getPermission('COMBAT','DISPLAY_ALL_NAMES') == false) txt = ""; - else if (permission == 1 && MODULE.getPermission('COMBAT','DISPLAY_LIMITED_NAME') == false) txt = ""; - else if (permission == 2 && MODULE.getPermission('COMBAT','DISPLAY_OBSERVER_NAME') == false) txt = ""; + if (permission == 0 && getPermission('COMBAT','DISPLAY_ALL_NAMES') == false) txt = ""; + else if (permission == 1 && getPermission('COMBAT','DISPLAY_LIMITED_NAME') == false) txt = ""; + else if (permission == 2 && getPermission('COMBAT','DISPLAY_OBSERVER_NAME') == false) txt = ""; if (permission == 0 && stats == 'HP') stats = 'none'; - else if (stats == 'HP' && permission == 1 && MODULE.getPermission('COMBAT','DISPLAY_LIMITED_HP') == false) stats = 'none'; - else if (stats == 'HP' && permission == 2 && MODULE.getPermission('COMBAT','DISPLAY_OBSERVER_HP') == false) stats = 'none'; - else if (stats != 'HP' && permission < 3 && MODULE.getPermission('COMBAT','DISPLAY_NON_OWNED_STATS') == false) stats = 'none'; + else if (stats == 'HP' && permission == 1 && getPermission('COMBAT','DISPLAY_LIMITED_HP') == false) stats = 'none'; + else if (stats == 'HP' && permission == 2 && getPermission('COMBAT','DISPLAY_OBSERVER_HP') == false) stats = 'none'; + else if (stats != 'HP' && permission < 3 && getPermission('COMBAT','DISPLAY_NON_OWNED_STATS') == false) stats = 'none'; } - else if (MODULE.getPermission('TOKEN','STATS') == false) { + else if (getPermission('TOKEN','STATS') == false) { statsOld = stats; stats = 'none'; } @@ -231,12 +231,12 @@ export class TokenControl{ } if (settings.onClick == 'visibility') { //toggle visibility - if (MODULE.getPermission('TOKEN','VISIBILITY') == false ) { + if (getPermission('TOKEN','VISIBILITY') == false ) { streamDeck.noPermission(context,device); return; } ring = 1; - if (token.data.hidden){ + if (compatibleCore('10.0') ? token.document.hidden : token.data.hidden){ ring = 2; ringColor = "#FF7B00"; } @@ -246,7 +246,7 @@ export class TokenControl{ } } else if (settings.onClick == 'combatState') { //toggle combat state - if (MODULE.getPermission('TOKEN','COMBAT') == false ) { + if (getPermission('TOKEN','COMBAT') == false ) { streamDeck.noPermission(context,device); return; } @@ -271,7 +271,7 @@ export class TokenControl{ } } else if (settings.onClick == 'condition') { //handle condition - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { + if (getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } @@ -286,7 +286,7 @@ export class TokenControl{ } } else if (settings.onClick == 'cubCondition') { //Combat Utility Belt conditions - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { + if (getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } @@ -303,7 +303,7 @@ export class TokenControl{ } } else if (settings.onClick == 'wildcard') { //wildcard images - if (MODULE.getPermission('TOKEN','WILDCARD') == false ) { + if (getPermission('TOKEN','WILDCARD') == false ) { streamDeck.noPermission(context,device); return; } @@ -316,7 +316,7 @@ export class TokenControl{ let currentImgNr = 0 let imgNr; for (let i=0; i i.name == settings.itemName)[0]; + else if (selectionMode == 'id') item = items.filter(i => i.id == settings.itemName)[0]; + if (item != undefined && displayUses) uses = tokenHelper.getItemUses(item); } else if (mode == 'features') { items = tokenHelper.getFeatures(token,settings.featureType); items = this.sortItems(items); - item = items[itemNr]; + if (selectionMode == 'order') item = items[itemNr]; + else if (selectionMode == 'name') item = items.filter(i => i.name == settings.itemName)[0]; + else if (selectionMode == 'id') item = items.filter(i => i.id == settings.itemName)[0]; if (item != undefined && displayUses) uses = tokenHelper.getFeatureUses(item); } else if (mode == 'spellbook') { items = tokenHelper.getSpells(token,settings.spellType); items = this.sortItems(items); - item = items[itemNr]; + if (selectionMode == 'order') item = items[itemNr]; + else if (selectionMode == 'name') item = items.filter(i => i.name == settings.itemName)[0]; + else if (selectionMode == 'id') item = items.filter(i => i.id == settings.itemName)[0]; if (displayUses && item != undefined) uses = tokenHelper.getSpellUses(token,settings.spellType,item); } if (item != undefined) { @@ -382,7 +411,7 @@ export class TokenControl{ if (mode == 'token') { iconSrc += ""; if (settings.onClick == 'visibility') { //toggle visibility - if (MODULE.getPermission('TOKEN','VISIBILITY') == false ) { + if (getPermission('TOKEN','VISIBILITY') == false ) { streamDeck.noPermission(context,device); return; } @@ -393,7 +422,7 @@ export class TokenControl{ } } else if (settings.onClick == 'combatState') { //toggle combat state - if (MODULE.getPermission('TOKEN','COMBAT') == false ) { + if (getPermission('TOKEN','COMBAT') == false ) { streamDeck.noPermission(context,device); return; } @@ -411,7 +440,7 @@ export class TokenControl{ } } else if (settings.onClick == 'condition') { //toggle condition - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { + if (getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } @@ -420,7 +449,7 @@ export class TokenControl{ if (icon == 'stats') iconSrc = tokenHelper.getConditionIcon(settings.condition); } else if (settings.onClick == 'cubCondition') { //Combat Utility Belt conditions - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { + if (getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } @@ -434,9 +463,9 @@ export class TokenControl{ } } } - - if (icon == 'stats'){ - if (MODULE.getPermission('TOKEN','STATS') == false) stats = statsOld; + + if (icon == 'stats' && mode != 'offset' && mode != 'offsetRel'){ + if (getPermission('TOKEN','STATS') == false) stats = statsOld; if (stats == 'HP') //HP iconSrc = "modules/MaterialDeck/img/token/hp_empty.png"; if (stats == 'TempHP') //Temp HP @@ -503,13 +532,15 @@ export class TokenControl{ iconSrc = "modules/MaterialDeck/img/move/rotateccw.png"; } } + streamDeck.setIcon(context,device,iconSrc,{background:background,ring:ring,ringColor:ringColor,overlay:overlay,uses:uses,hp:hp}); streamDeck.setTitle(txt,context); } sortItems(items) { let sorted = Object.values(items); - sorted.sort((a,b) => a.data.sort - b.data.sort); + if (compatibleCore('10.0')) sorted.sort((a,b) => a.sort - b.sort); + else sorted.sort((a,b) => a.data.sort - b.data.sort); return sorted; } @@ -522,13 +553,11 @@ export class TokenControl{ const tokenIdentifier = settings.tokenName ? settings.tokenName : ''; const mode = settings.tokenMode ? settings.tokenMode : 'token'; - let token; - if (selection == 'selected') token = canvas.tokens.controlled[0]; - else token = tokenHelper.getToken(selection,tokenIdentifier); + let token = tokenHelper.getToken(selection,tokenIdentifier); if (token == undefined) return; - if (token.owner == false && token.observer == true && MODULE.getPermission('TOKEN','OBSERVER') == false ) return; - if (token.owner == false && token.observer == false && MODULE.getPermission('TOKEN','NON_OWNED') == false ) return; + if (token.owner == false && token.observer == true && getPermission('TOKEN','OBSERVER') == false ) return; + if (token.owner == false && token.observer == false && getPermission('TOKEN','NON_OWNED') == false ) return; if (mode == 'token') { @@ -565,18 +594,18 @@ export class TokenControl{ else token.sheet.close(); } else if (onClick == 'visibility') { //Toggle visibility - if (MODULE.getPermission('TOKEN','VISIBILITY') == false ) return; + if (getPermission('TOKEN','VISIBILITY') == false ) return; token.toggleVisibility(); } else if (onClick == 'combatState') { //Toggle combat state - if (MODULE.getPermission('TOKEN','COMBAT') == false ) return; + if (getPermission('TOKEN','COMBAT') == false ) return; token.toggleCombat(); } else if (onClick == 'target') { //Target token token.setTarget(!token.isTargeted,{releaseOthers:false}); } else if (onClick == 'condition') { //Handle condition - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return; + if (getPermission('TOKEN','CONDITIONS') == false ) return; const func = settings.conditionFunction ? settings.conditionFunction : 'toggle'; if (func == 'toggle'){ //toggle @@ -594,7 +623,7 @@ export class TokenControl{ } else if (onClick == 'cubCondition') { //Combat Utility Belt conditions - if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return; + if (getPermission('TOKEN','CONDITIONS') == false ) return; const condition = settings.cubConditionName; if (condition == undefined || condition == '') return; const effect = CONFIG.statusEffects.find(e => e.label === condition); @@ -602,57 +631,108 @@ export class TokenControl{ this.update(tokenId); } else if (onClick == 'vision'){ - if (MODULE.getPermission('TOKEN','VISION') == false ) return; - const token = canvas.tokens.children[0].children.find(p => p.id == tokenId); - if (token == undefined) return; - let tokenData = token.data; + if (getPermission('TOKEN','VISION') == false ) return; - const dimVision = parseInt(settings.dimVision); - const brightVision = parseInt(settings.brightVision); - const sightAngle = parseInt(settings.sightAngle); - const dimRadius = parseInt(settings.dimRadius); - const brightRadius = parseInt(settings.brightRadius); - const emissionAngle = parseInt(settings.emissionAngle); - const lightColor = settings.lightColor ? settings.lightColor : '#000000'; - const colorIntensity = isNaN(parseInt(settings.colorIntensity)) ? 0 : parseInt(settings.colorIntensity)/100; - const animationType = settings.animationType ? settings.animationType : 'none'; - const animationSpeed = isNaN(parseInt(settings.animationSpeed)) ? 1 : parseInt(settings.animationSpeed); - const animationIntensity = isNaN(parseInt(settings.animationIntensity)) ? 1 : parseInt(settings.animationIntensity); - - let data = {}; - if (isNaN(dimVision)==false) data.dimSight = dimVision; - if (isNaN(brightVision)==false) data.brightSight = brightVision; - if (isNaN(sightAngle)==false) data.sightAngle = sightAngle; - - let light = {}; - - - if (isNaN(dimRadius)==false) light.dim = dimRadius; - if (isNaN(brightRadius)==false) light.bright = brightRadius; - if (isNaN(emissionAngle)==false) light.angle = emissionAngle; - light.color = lightColor; - light.alpha = Math.sqrt(colorIntensity).toNearest(0.05) - - let animation = { - type: '', - speed: tokenData.light.animation.speed, - intensity: tokenData.light.animation.intensity - }; - if (animationType != 'none'){ - animation.type = animationType; - animation.intensity = animationIntensity; - animation.speed = animationSpeed; + let sight = {}; + let light = { + animation: {} } - light.animation = animation; - data.light = light; - if (compatibleCore('0.8.1')) token.document.update(data); - else token.update(data); + + //Vision basic config + if (settings.visionEnabled && settings.visionEnabled != 'noChange') { + if (compatibleCore('10.0')) { + if (settings.visionEnabled == 'toggle') + sight.enabled = !token.document.sight.enabled; + else + sight.enabled = settings.visionEnabled == 'enable'; + } + else { + if (settings.visionEnabled == 'toggle') + sight.vision = !token.data.vision; + else + sight.vision = settings.visionEnabled == 'enable'; + } + } + if (settings.visionRange && isNaN(settings.visionRange) == false) sight.range = parseInt(settings.visionRange); + if (settings.visionDimRange && isNaN(settings.visionDimRange) == false) sight.dimSight = parseInt(settings.visionDimRange); + if (settings.visionBrightRange && isNaN(settings.visionBrightRange) == false) sight.brightSight = parseInt(settings.visionBrightRange); + if (compatibleCore('10.0') && settings.visionAngle && isNaN(settings.visionAngle) == false) sight.angle = parseInt(settings.visionAngle); + else if (!compatibleCore('10.0') && settings.visionAngle && isNaN(settings.visionAngle) == false) sight.sightAngle = parseInt(settings.visionAngle); + if (settings.visionMode && settings.visionMode != 'noChange') sight.visionMode = settings.visionMode; + + //Vision detection modes + let detectionModes = token.document.detectionModes; + if (settings.visionDetectionModeEnable && settings.visionDetectionModeEnable != 'noChange' && settings.visionDetectionModeNumber && detectionModes[settings.visionDetectionModeNumber-1] != undefined) { + if (settings.visionDetectionModeEnable == 'toggle') + detectionModes[settings.visionDetectionModeNumber-1].enabled = !detectionModes[settings.visionDetectionModeNumber-1].enabled; + else if (settings.visionDetectionModeEnable == 'enable') + detectionModes[settings.visionDetectionModeNumber-1].enabled = true; + else if (settings.visionDetectionModeEnable == 'disable') + detectionModes[settings.visionDetectionModeNumber-1].enabled = false; + detectionModes = detectionModes; + } + + //Vision advanced options + if (settings.visionColorEnable) sight.color = settings.visionColor ? (settings.visionColor == '#000000' ? null : settings.visionColor) : null; + if (settings.visionAttenuationEnable) sight.attenuation = settings.visionAttenuation ? parseFloat(settings.visionAttenuation) : 0; + if (settings.visionBrightnessEnable) sight.brightness = settings.visionBrightness ? parseFloat(settings.visionBrightness) : 0; + if (settings.visionSaturationEnable) sight.saturation = settings.visionSaturation ? parseFloat(settings.visionSaturation) : 0; + if (settings.visionContrastEnable) sight.contrast = settings.visionContrast ? parseFloat(settings.visionContrast) : 0; + + //Light basic config + if (settings.lightDimRadius && isNaN(settings.lightDimRadius) == false) light.dim = parseInt(settings.lightDimRadius); + if (settings.lightBrightRadius && isNaN(settings.lightBrightRadius) == false) light.bright = parseInt(settings.lightBrightRadius); + if (settings.lightEmissionAngle && isNaN(settings.lightEmissionAngle) == false) light.angle = parseInt(settings.lightEmissionAngle); + if (settings.lightColorEnable) light.color = settings.lightColor ? (settings.lightColor == '#000000' ? null : settings.lightColor) : null; + if (settings.lightColorIntensityEnable) light.alpha = settings.lightColorIntensity ? parseFloat(settings.lightColorIntensity) : 0; + + //Light animation + if (settings.lightAnimationType && settings.lightAnimationType != 'noChange') light.animation.type = settings.lightAnimationType == 'none' ? null : settings.lightAnimationType; + if (settings.lightAnimationSpeedEnable) light.animation.speed = settings.lightAnimationSpeed ? parseFloat(settings.lightAnimationSpeed) : 5; + if (settings.lightAnimationReverseDirection && settings.lightAnimationReverseDirection != 'noChange') { + if (settings.lightAnimationReverseDirection == 'toggle') + light.animation.reverse = compatibleCore('10.0') ? !token.document.light.animation.reverse : !token.data.light.animation.reverse; + else if (settings.lightAnimationReverseDirection == 'enable') + light.animation.reverse = true; + else if (settings.lightAnimationReverseDirection == 'disable') + light.animation.reverse = false; + } + if (settings.lightAnimationIntensityEnable) light.animation.intensity = settings.lightAnimationIntensity ? parseFloat(settings.lightAnimationIntensity) : 5; + + //Light advanced options + if (settings.lightColorationTechnique && settings.lightColorationTechnique != 'noChange') light.coloration = parseInt(settings.lightColorationTechnique); + if (settings.lightLuminosityEnable) light.luminosity = settings.lightLuminosity ? parseFloat(settings.lightLuminosity) : 0.5; + if (settings.lightGradualIllumination && settings.lightGradualIllumination != 'noChange') { + if (settings.lightGradualIllumination == 'toggle') + light.gradual = !token.data.light.gradual; + else if (settings.lightGradualIllumination == 'enable') + light.gradual = true; + else if (settings.lightGradualIllumination == 'disable') + light.gradual = false; + } + if (settings.lightAttenuationEnable) light.attenuation = settings.lightAttenuation ? parseFloat(settings.lightAttenuation) : 0.5; + if (settings.lightSaturationEnable) light.saturation = settings.lightSaturation ? parseFloat(settings.lightSaturation) : 0; + if (settings.lightContrastEnable) light.contrast = settings.lightContrast ? parseFloat(settings.lightContrast) : 0; + if (settings.lightShadowsEnable) light.shadows = settings.lightShadows ? parseFloat(settings.lightShadows) : 0; + + let data; + if (compatibleCore('10.0')) { + data = { + sight, + light + } + } + else { + data = sight; + data.light = light; + } + token.document.update(data); } else if (onClick == 'initiative'){ tokenHelper.toggleInitiative(token); } else if (onClick == 'wildcard') { //wildcard images - if (MODULE.getPermission('TOKEN','WILDCARD') == false ) return; + if (getPermission('TOKEN','WILDCARD') == false ) return; const method = settings.wildcardMethod ? settings.wildcardMethod : 'iterate'; let value = parseInt(settings.wildcardValue); if (isNaN(value)) value = 1; @@ -663,7 +743,7 @@ export class TokenControl{ if (method == 'iterate'){ let currentImgNr = 0 for (let i=0; i i.name == settings.itemName)[0]; + else if (selectionMode == 'id') item = items.filter(i => i.id == settings.itemName)[0]; if (item != undefined) { - tokenHelper.rollItem(item, settings); + tokenHelper.rollItem(item, settings, otherControls.rollOption); } } diff --git a/src/misc.js b/src/misc.js index 6e7d825..c739c4d 100644 --- a/src/misc.js +++ b/src/misc.js @@ -1,36 +1,35 @@ -import {sdVersion, msVersion, moduleName, getPermission, enableModule, streamDeck} from "../MaterialDeck.js"; -import {macroControl,soundboard,playlistControl} from "../MaterialDeck.js"; +import { sdVersion, msVersion, moduleName, getPermission, enableModule, streamDeck, macroControl,soundboard,playlistControl, minimumMSversion, minimumSDversion } from "../MaterialDeck.js"; export function compareVersions(checkedVersion, requiredVersion) { requiredVersion = requiredVersion.split("."); checkedVersion = checkedVersion.split("."); + for (let i=0; i<3; i++) { requiredVersion[i] = isNaN(parseInt(requiredVersion[i])) ? 0 : parseInt(requiredVersion[i]); checkedVersion[i] = isNaN(parseInt(checkedVersion[i])) ? 0 : parseInt(checkedVersion[i]); } - + if (checkedVersion[0] > requiredVersion[0]) return false; if (checkedVersion[0] < requiredVersion[0]) return true; if (checkedVersion[1] > requiredVersion[1]) return false; if (checkedVersion[1] < requiredVersion[1]) return true; if (checkedVersion[2] > requiredVersion[2]) return false; return true; -} - + } + export function compatibleCore(compatibleVersion){ + const split = compatibleVersion.split("."); + if (split.length == 2) compatibleVersion = `0.${compatibleVersion}`; let coreVersion = game.version == undefined ? game.data.version : `0.${game.version}`; return compareVersions(compatibleVersion, coreVersion); - /* - coreVersion = coreVersion.split("."); - compatibleVersion = compatibleVersion.split("."); - if (compatibleVersion[0] > coreVersion[0]) return false; - if (compatibleVersion[0] < coreVersion[0]) return true; - if (compatibleVersion[1] > coreVersion[1]) return false; - if (compatibleVersion[1] < coreVersion[1]) return true; - if (compatibleVersion[2] > coreVersion[2]) return false; - return true; - */ - } +} + +export function compatibleSystem(compatibleVersion){ + const split = compatibleVersion.split("."); + if (split.length == 2) compatibleVersion = `0.${compatibleVersion}`; + let coreVersion = game.system.data.version; + return compareVersions(compatibleVersion, coreVersion); +} export class playlistConfigForm extends FormApplication { constructor(data, options) { @@ -97,7 +96,7 @@ export class playlistConfigForm extends FormApplication { } return { - playlists: compatibleCore("0.8.1") ? game.playlists.contents : game.playlists.entities, + playlists: game.playlists.contents, numberOfPlaylists: numberOfPlaylists, playlistData: playlistData, playMode: playMode @@ -175,7 +174,7 @@ export class macroConfigForm extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "macro-config", + id: "materialDeck_macroConfig", title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.MacroConfig"), template: "./modules/MaterialDeck/templates/macroConfig.html", classes: ["sheet"] @@ -201,12 +200,12 @@ export class macroConfigForm extends FormApplication { if (args == undefined) args = []; //Check if the Furnace is installed and enabled - let furnaceEnabled = false; let height = 95; - let furnace = game.modules.get("furnace"); + let advancedMacrosEnabled = false; let advancedMacros = game.modules.get("advanced-macros"); - if ((furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) || (advancedMacros != undefined && advancedMacros.active)) { - furnaceEnabled = true; + if (advancedMacros != undefined && advancedMacros.active) advancedMacrosEnabled = true; + if (advancedMacrosEnabled) { + advancedMacrosEnabled = true; height += 50; } @@ -245,7 +244,7 @@ export class macroConfigForm extends FormApplication { macros: game.macros, selectedMacros: selectedMacros, macroData: macroData, - furnace: furnaceEnabled, + furnace: advancedMacrosEnabled, macroRange: `${this.page*32 + 1} - ${this.page*32 + 32}`, prevDisabled: this.page == 0 ? 'disabled' : '', totalMacros: Math.max(Math.ceil(selectedMacros.length/32)*32, this.page*32 + 32) @@ -263,12 +262,12 @@ export class macroConfigForm extends FormApplication { activateListeners(html) { super.activateListeners(html); - const navNext = html.find("button[id='navNext']"); - const navPrev = html.find("button[id='navPrev']"); - const clearAll = html.find("button[id='clearAll']"); - const clearPage = html.find("button[id='clearPage']"); - const importBtn = html.find("button[id='import']"); - const exportBtn = html.find("button[id='export']"); + const navNext = html.find("button[name='navNext']"); + const navPrev = html.find("button[name='navPrev']"); + const clearAll = html.find("button[name='clearAll']"); + const clearPage = html.find("button[name='clearPage']"); + const importBtn = html.find("button[name='import']"); + const exportBtn = html.find("button[name='export']"); const macro = html.find("select[name='macros']"); const args = html.find("input[name='args']"); const color = html.find("input[name='colorPicker']"); @@ -359,21 +358,21 @@ export class macroConfigForm extends FormApplication { }) macro.on("change", event => { - let id = event.target.id.replace('macros',''); + let id = event.target.id.replace('materialDeck_macroConfig_macros',''); let settings = game.settings.get(moduleName,'macroSettings'); settings.macros[id-1]=event.target.value; this.updateSettings(settings); }); args.on("change", event => { - let id = event.target.id.replace('args',''); + let id = event.target.id.replace('materialDeck_macroConfig_args',''); let settings = game.settings.get(moduleName,'macroSettings'); settings.args[id-1]=event.target.value; this.updateSettings(settings); }); color.on("change", event => { - let id = event.target.id.replace('colorpicker',''); + let id = event.target.id.replace('materialDeck_macroConfig_colorpicker',''); let settings = game.settings.get(moduleName,'macroSettings'); settings.color[id-1]=event.target.value; this.updateSettings(settings); @@ -453,7 +452,7 @@ export class soundboardConfigForm extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "soundboard-config", + id: "materialDeck_soundboardConfig", title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.SoundboardConfig"), template: "./modules/MaterialDeck/templates/soundboardConfig.html", classes: ["sheet"], @@ -489,7 +488,7 @@ export class soundboardConfigForm extends FormApplication { playlists.push({id:"none",name:game.i18n.localize("MaterialDeck.None")}); playlists.push({id:"FP",name:game.i18n.localize("MaterialDeck.FilePicker")}) - const playlistArray = compatibleCore("0.8.1") ? game.playlists.contents : game.playlists.entities; + const playlistArray = game.playlists.contents; for (let playlist of playlistArray) playlists.push({id: playlist.id, name: playlist.name}) @@ -515,7 +514,7 @@ export class soundboardConfigForm extends FormApplication { else if (this.settings.selectedPlaylists[iteration] == 'FP') selectedPlaylist = 'FP'; else { //Get the playlist - const playlistArray = compatibleCore("0.8.1") ? game.playlists.contents : game.playlists.entities; + const playlistArray = game.playlists.contents; let pl = playlistArray.find(p => p.id == this.settings.selectedPlaylists[iteration]) if (pl == undefined){ @@ -524,19 +523,12 @@ export class soundboardConfigForm extends FormApplication { } else { //Add the sound name and id to the sounds array - if (compatibleCore("0.8.1")) - for (let sound of pl.sounds.contents) - sounds.push({ - name: sound.name, - id: sound.id - }); - else { - for (let sound of pl.sounds) - sounds.push({ - name: sound.name, - id: sound._id - }); - } + for (let sound of pl.sounds.contents) + sounds.push({ + name: sound.name, + id: sound.id + }); + //Get the playlist id selectedPlaylist = pl.id; } @@ -557,8 +549,8 @@ export class soundboardConfigForm extends FormApplication { sound: this.settings.sounds[iteration], sounds: sounds, srcPath: this.settings.src[iteration], - colorOn: this.settings.colorOn[iteration], - colorOff: this.settings.colorOff[iteration], + colorOn: this.settings.colorOn[iteration] == 0 ? '#000000' : this.settings.colorOn[iteration], + colorOff: this.settings.colorOff[iteration] == 0 ? '#000000' : this.settings.colorOff[iteration], mode: this.settings.mode[iteration], volume: this.settings.volume[iteration], imgPath: this.settings.img[iteration], @@ -576,7 +568,7 @@ export class soundboardConfigForm extends FormApplication { //Push soundsThis (row array) to soundData (full data array) soundData.push({dataThis: soundsThis}); } - + return { soundData: soundData, playlists, @@ -597,12 +589,12 @@ export class soundboardConfigForm extends FormApplication { async activateListeners(html) { super.activateListeners(html); - const navNext = html.find("button[id='navNext']"); - const navPrev = html.find("button[id='navPrev']"); - const clearAll = html.find("button[id='clearAll']"); - const clearPage = html.find("button[id='clearPage']"); - const importBtn = html.find("button[id='import']"); - const exportBtn = html.find("button[id='export']"); + const navNext = html.find("button[name='navNext']"); + const navPrev = html.find("button[name='navPrev']"); + const clearAll = html.find("button[name='clearAll']"); + const clearPage = html.find("button[name='clearPage']"); + const importBtn = html.find("button[name='import']"); + const exportBtn = html.find("button[name='export']"); const nameField = html.find("input[name='namebox']"); const playlistSelect = html.find("select[name='playlist']"); const soundSelect = html.find("select[name='sounds']"); @@ -697,7 +689,7 @@ export class soundboardConfigForm extends FormApplication { }) nameField.on("change",event => { - let id = event.target.id.replace('name','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_name','')-1; this.settings.name[id]=event.target.value; this.updateSettings(this.settings); }); @@ -707,7 +699,7 @@ export class soundboardConfigForm extends FormApplication { //Listener for when the playlist is changed playlistSelect.on("change", event => { //Get the sound number - const iteration = event.target.id.replace('playlists',''); + const iteration = event.target.id.replace('materialDeck_sbConfig_playlists',''); //Get the selected playlist and the sounds of that playlist let selectedPlaylist; @@ -718,24 +710,24 @@ export class soundboardConfigForm extends FormApplication { selectedPlaylist = 'FP'; //Show the file picker - document.querySelector(`#fp${iteration}`).style=''; + document.querySelector(`#materialDeck_sbConfig_fp${iteration}`).style=''; //Hide the sound selector - document.querySelector(`#ss${iteration}`).style='display:none'; + document.querySelector(`#materialDeck_sbConfig_ss${iteration}`).style='display:none'; } else { //Hide the file picker - document.querySelector(`#fp${iteration}`).style='display:none'; + document.querySelector(`#materialDeck_sbConfig_fp${iteration}`).style='display:none'; //Show the sound selector - document.querySelector(`#ss${iteration}`).style=''; + document.querySelector(`#materialDeck_sbConfig_ss${iteration}`).style=''; - const playlistArray = compatibleCore("0.8.1") ? game.playlists.contents : game.playlists.entities; + const playlistArray = game.playlists.contents; const pl = playlistArray.find(p => p.id == event.target.value) selectedPlaylist = pl.id; //Get the sound select element - let SSpicker = document.getElementById(`soundSelect${iteration}`); + let SSpicker = document.getElementById(`materialDeck_sbConfig_soundSelect${iteration}`); //Empty ss element SSpicker.options.length=0; @@ -746,20 +738,12 @@ export class soundboardConfigForm extends FormApplication { optionNone.innerHTML = game.i18n.localize("MaterialDeck.None"); SSpicker.appendChild(optionNone); - if (compatibleCore("0.8.1")) - for (let sound of pl.sounds.contents) { - let newOption = document.createElement('option'); - newOption.value = sound.id; - newOption.innerHTML = sound.name; - SSpicker.appendChild(newOption); - } - else - for (let sound of pl.sounds) { - let newOption = document.createElement('option'); - newOption.value = sound._id; - newOption.innerHTML = sound.name; - SSpicker.appendChild(newOption); - } + for (let sound of pl.sounds.contents) { + let newOption = document.createElement('option'); + newOption.value = sound.id; + newOption.innerHTML = sound.name; + SSpicker.appendChild(newOption); + } } //Save the new playlist to this.settings, and update the settings @@ -769,43 +753,43 @@ export class soundboardConfigForm extends FormApplication { } soundSelect.on("change", event => { - let id = event.target.id.replace('soundSelect','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_soundSelect','')-1; this.settings.sounds[id]=event.target.value; this.updateSettings(this.settings); }); soundFP.on("change",event => { - let id = event.target.id.replace('srcPath','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_srcPath','')-1; this.settings.src[id]=event.target.value; this.updateSettings(this.settings); }); imgFP.on("change",event => { - let id = event.target.id.replace('imgPath','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_imgPath','')-1; this.settings.img[id]=event.target.value; this.updateSettings(this.settings); }); onCP.on("change",event => { - let id = event.target.id.replace('colorOn','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_colorOn','')-1; this.settings.colorOn[id]=event.target.value; this.updateSettings(this.settings); }); offCP.on("change",event => { - let id = event.target.id.replace('colorOff','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_colorOff','')-1; this.settings.colorOff[id]=event.target.value; this.updateSettings(this.settings); }); playMode.on("change",event => { - let id = event.target.id.replace('playmode','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_playmode','')-1; this.settings.mode[id]=event.target.value; this.updateSettings(this.settings); }); volume.on("change",event => { - let id = event.target.id.replace('volume','')-1; + let id = event.target.id.replace('materialDeck_sbConfig_volume','')-1; this.settings.volume[id]=event.target.value; this.updateSettings(this.settings); }); @@ -904,7 +888,7 @@ export class exportConfigForm extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "MD_Export", + id: "materialDeck_Export", title: "Material Deck: " + game.i18n.localize("MaterialDeck.ExportDialog.Title"), template: "./modules/MaterialDeck/templates/exportDialog.html", width: 500, @@ -967,7 +951,7 @@ export class importConfigForm extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "MD_Import", + id: "materialDeck_Import", title: "Material Deck: " + game.i18n.localize("MaterialDeck.ImportDialog.Title"), template: "./modules/MaterialDeck/templates/importDialog.html", width: 500, @@ -1005,7 +989,7 @@ export class importConfigForm extends FormApplication { activateListeners(html) { super.activateListeners(html); - const upload = html.find("input[id='uploadJson']"); + const upload = html.find("input[id='materialDeck_import_uploadJson']"); upload.on('change',(event) => { event.preventDefault(); @@ -1052,7 +1036,7 @@ export class downloadUtility extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "MD_DownloadUtility", + id: "materialDeck_downloadUtility", title: "Material Deck: " + game.i18n.localize("MaterialDeck.DownloadUtility.Title"), template: "./modules/MaterialDeck/templates/downloadUtility.html", width: 500, @@ -1077,24 +1061,13 @@ export class downloadUtility extends FormApplication { } } if (this.localMSversion == undefined) this.localMSversion = 'unknown'; - - let minimumSdVersion; - let minimumMsVersion; - if (compatibleCore("0.8.5")) { - minimumSdVersion = game.modules.get("MaterialDeck").data.flags.minimumSDversion.replace('v',''); - minimumMsVersion = game.modules.get("MaterialDeck").data.flags.minimumMSversion; - } - else { - minimumSdVersion = game.modules.get("MaterialDeck").data.minimumSDversion.replace('v',''); - minimumMsVersion = game.modules.get("MaterialDeck").data.minimumMSversion; - } - + return { - minimumSdVersion, + minimumSdVersion: minimumSDversion, localSdVersion: this.localSDversion, masterSdVersion: this.masterSDversion, sdDlDisable: this.masterSDversion == undefined, - minimumMsVersion, + minimumMsVersion: minimumMSversion, localMsVersion: this.localMSversion, masterMsVersion: this.masterMSversion, msDlDisable: this.masterMSversion == undefined, @@ -1115,20 +1088,20 @@ export class downloadUtility extends FormApplication { activateListeners(html) { super.activateListeners(html); - const downloadSd = html.find("button[id='downloadSd']"); - const downloadMs = html.find("button[id='downloadMs']"); + const downloadSd = html.find("button[id='materialDeck_dlUtil_downloadSd']"); + const downloadMs = html.find("button[id='materialDeck_dlUtil_downloadMs']"); const downloadProfile = html.find("button[name='downloadProfile']") - const refresh = html.find("button[id='refresh']"); + const refresh = html.find("button[id='materialDeck_dlUtil_refresh']"); downloadSd.on('click', () => { - const version = document.getElementById('masterSdVersion').innerHTML; + const version = document.getElementById('materialDeck_dlUtil_masterSdVersion').innerHTML; if (version == '' || version == undefined || version == 'Error') return; const url = `https://github.com/CDeenen/MaterialDeck_SD/releases/download/v${version}/com.cdeenen.materialdeck.streamDeckPlugin`; this.downloadURI(url,'com.cdeenen.materialdeck.streamDeckPlugin') }) downloadMs.on('click', () => { - const version = document.getElementById('masterMsVersion').innerHTML; - const os = document.getElementById('os').value; + const version = document.getElementById('materialDeck_dlUtil_masterMsVersion').innerHTML; + const os = document.getElementById('materialDeck_dlUtil_os').value; if (version == '' || version == undefined || version == 'Error') return; let name = `MaterialServer-${os}.zip`; let url; @@ -1137,13 +1110,13 @@ export class downloadUtility extends FormApplication { this.downloadURI(url,name) }) downloadProfile.on('click',(event) => { - let id = event.currentTarget.id.replace('dlProfile-',''); + let id = event.currentTarget.id.replace('materialDeck_dlUtil_dlProfile-',''); this.downloadURI(this.profiles[id].url,`${this.profiles[id].label}.streamDeckProfile`); }) refresh.on('click', () => { - document.getElementById('masterSdVersion').value = 'Getting data'; + document.getElementById('materialDeck_dlUtil_masterSdVersion').value = 'Getting data'; this.checkForUpdate('SD'); - document.getElementById('masterMsVersion').value = 'Getting data'; + document.getElementById('materialDeck_dlUtil_masterMsVersion').value = 'Getting data'; this.checkForUpdate('MS'); this.getReleaseData(); }) @@ -1170,14 +1143,10 @@ export class downloadUtility extends FormApplication { const data = JSON.parse(request.responseText); parent.releaseAssets = data.assets; parent.render(true); - if (type.indexOf("text") !== 1) { - - return; - } + if (type.indexOf("text") !== 1) return; } } - request.onerror = function () { - } + request.onerror = function () {} } checkForUpdate(reqType) { @@ -1185,7 +1154,7 @@ export class downloadUtility extends FormApplication { let url; if (reqType == 'SD') url = 'https://raw.githubusercontent.com/CDeenen/MaterialDeck_SD/master/Plugin/com.cdeenen.materialdeck.sdPlugin/manifest.json'; else if (reqType == 'MS') url = 'https://raw.githubusercontent.com/CDeenen/MaterialServer/master/package.json'; - const elementId = reqType == 'SD' ? 'masterSdVersion' : 'masterMsVersion'; + const elementId = reqType == 'SD' ? 'materialDeck_dlUtil_masterSdVersion' : 'materialDeck_dlUtil_masterMsVersion'; var request = new XMLHttpRequest(); request.open('GET', url, true); @@ -1199,7 +1168,6 @@ export class downloadUtility extends FormApplication { parent.render(true); return; } - } } request.onerror = function () { @@ -1220,7 +1188,7 @@ export class deviceConfig extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "MD_DeviceConfig", + id: "materialDeck_deviceConfig", title: "Material Deck: " + game.i18n.localize("MaterialDeck.DeviceConfig.Title"), template: "./modules/MaterialDeck/templates/deviceConfig.html", width: 500, @@ -1282,7 +1250,7 @@ export class deviceConfig extends FormApplication { super.activateListeners(html); html.find("input[name='enable']").on('change', (event) => { - const id = event.currentTarget.id; + const id = event.currentTarget.id.replace('materialDeck_devConf_','');; for (let d of this.devices) { if (d.id == id) { let dConfig = game.settings.get(moduleName, 'devices'); diff --git a/src/settings.js b/src/settings.js index 7ca7ec2..731ab06 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,4 +1,4 @@ -import * as MODULE from "../MaterialDeck.js"; +import { moduleName, isEmpty } from "../MaterialDeck.js"; import { playlistConfigForm, macroConfigForm, soundboardConfigForm, downloadUtility, deviceConfig } from "./misc.js"; let userPermissions = {}; @@ -70,7 +70,7 @@ export const registerSettings = async function() { //world,global,client //Enabled the module - game.settings.register(MODULE.moduleName,'Enable', { + game.settings.register(moduleName,'Enable', { name: "MaterialDeck.Sett.Enable", scope: "client", config: true, @@ -82,20 +82,31 @@ export const registerSettings = async function() { /** * System override */ - game.settings.register(MODULE.moduleName,'systemOverride', { + game.settings.register(moduleName,'systemOverride', { name: "MaterialDeck.Sett.SystemOverride", hint: "MaterialDeck.Sett.SystemOverrideHint", scope: "client", config: true, default: "", type: String, + choices: { + "": "Autodetect", + "D35E": "Dungeons & Dragons 3.5e", + "dnd5e": "Dungeons & Dragons 5e", + "forbidden-lands": "Forbidden Lands", + "pf1": "Pathfinder 1e", + "pf2e": "Pathfinder 2e", + "demonlord": "Shadow of the Demon Lord", + "sfrpg": "Starfinder", + "wfrp4e": "Warhammer Fantasy Roleplay 4e", + }, onChange: x => window.location.reload() }); /** * Sets the ip address of the server */ - game.settings.register(MODULE.moduleName,'address', { + game.settings.register(moduleName,'address', { name: "MaterialDeck.Sett.ServerAddr", hint: "MaterialDeck.Sett.ServerAddrHint", scope: "client", @@ -105,7 +116,7 @@ export const registerSettings = async function() { onChange: x => window.location.reload() }); - game.settings.register(MODULE.moduleName, 'imageBuffer', { + game.settings.register(moduleName, 'imageBuffer', { name: "MaterialDeck.Sett.ImageBuffer", hint: "MaterialDeck.Sett.ImageBufferHint", default: 100, @@ -116,7 +127,7 @@ export const registerSettings = async function() { }); - game.settings.register(MODULE.moduleName, 'imageBrightness', { + game.settings.register(moduleName, 'imageBrightness', { name: "MaterialDeck.Sett.ImageBrightness", hint: "MaterialDeck.Sett.ImageBrightnessHint", default: 50, @@ -127,7 +138,7 @@ export const registerSettings = async function() { }); - game.settings.register(MODULE.moduleName, 'nrOfConnMessages', { + game.settings.register(moduleName, 'nrOfConnMessages', { name: "MaterialDeck.Sett.NrOfConnMessages", hint: "MaterialDeck.Sett.NrOfConnMessagesHint", default: 5, @@ -139,59 +150,61 @@ export const registerSettings = async function() { }); //Create the Help button - game.settings.registerMenu(MODULE.moduleName, 'helpMenu',{ + game.settings.registerMenu(moduleName, 'helpMenu',{ name: "MaterialDeck.Sett.Help", label: "MaterialDeck.Sett.Help", type: helpMenu, restricted: false }); - game.settings.registerMenu(MODULE.moduleName, 'downloadUtility',{ + game.settings.registerMenu(moduleName, 'downloadUtility',{ name: "MaterialDeck.DownloadUtility.Title", label: "MaterialDeck.DownloadUtility.Title", type: downloadUtility, restricted: false }); - game.settings.registerMenu(MODULE.moduleName, 'deviceConfig',{ + game.settings.registerMenu(moduleName, 'deviceConfig',{ name: "MaterialDeck.DeviceConfig.Title", label: "MaterialDeck.DeviceConfig.Title", type: deviceConfig, restricted: false }); - game.settings.register(MODULE.moduleName, 'devices', { + game.settings.register(moduleName, 'devices', { name: "devices", scope: "client", type: Object, config: false }); - game.settings.registerMenu(MODULE.moduleName, 'permissionConfig',{ + game.settings.registerMenu(moduleName, 'permissionConfig',{ name: "MaterialDeck.Sett.Permission", label: "MaterialDeck.Sett.Permission", type: userPermission, restricted: true }); - game.settings.register(MODULE.moduleName, 'userPermission', { + game.settings.register(moduleName, 'userPermission', { name: "userPermission", + label: "", scope: "world", type: Object, - config: false + config: false, + default: {} }); /** * Playlist soundboard */ - game.settings.registerMenu(MODULE.moduleName, 'playlistConfigMenu',{ + game.settings.registerMenu(moduleName, 'playlistConfigMenu',{ name: "MaterialDeck.Sett.PlaylistConfig", label: "MaterialDeck.Sett.PlaylistConfig", type: playlistConfigForm, restricted: false }); - game.settings.register(MODULE.moduleName, 'playlists', { + game.settings.register(moduleName, 'playlists', { name: "selectedPlaylists", scope: "world", type: Object, @@ -202,21 +215,22 @@ export const registerSettings = async function() { /** * Macro Board */ - game.settings.registerMenu(MODULE.moduleName, 'macroConfigMenu',{ + game.settings.registerMenu(moduleName, 'macroConfigMenu',{ name: "MaterialDeck.Sett.MacroConfig", label: "MaterialDeck.Sett.MacroConfig", type: macroConfigForm, restricted: false }); - game.settings.register(MODULE.moduleName, 'macroSettings', { + game.settings.register(moduleName, 'macroSettings', { name: "macroSettings", scope: "world", type: Object, - config: false + config: false, + default: {} }); - game.settings.register(MODULE.moduleName, 'macroArgs', { + game.settings.register(moduleName, 'macroArgs', { name: "macroArgs", scope: "world", type: Object, @@ -226,7 +240,7 @@ export const registerSettings = async function() { /** * Soundboard */ - game.settings.register(MODULE.moduleName, 'soundboardSettings', { + game.settings.register(moduleName, 'soundboardSettings', { name: "soundboardSettings", scope: "world", type: Object, @@ -234,15 +248,16 @@ export const registerSettings = async function() { config: false }); - game.settings.registerMenu(MODULE.moduleName, 'soundboardConfigMenu',{ + game.settings.registerMenu(moduleName, 'soundboardConfigMenu',{ name: "MaterialDeck.Sett.SoundboardConfig", label: "MaterialDeck.Sett.SoundboardConfig", type: soundboardConfigForm, restricted: false }); - let permissionSettings = game.settings.get(MODULE.moduleName,'userPermission'); - if (permissionSettings == undefined || permissionSettings == null || MODULE.isEmpty(permissionSettings)) { + let permissionSettings = game.settings.get(moduleName,'userPermission'); + + if (permissionSettings == undefined || permissionSettings == null || isEmpty(permissionSettings)) { permissionSettings = { enable: defaultEnable, permissions: defaultUserPermissions @@ -254,7 +269,7 @@ export const registerSettings = async function() { if (permissionSettings.permissions.MACRO.BY_NAME == undefined) permissionSettings.permissions.MACRO.BY_NAME = [false,false,true,true]; } if (game.user.isGM) - game.settings.set(MODULE.moduleName,'userPermission',permissionSettings); + game.settings.set(moduleName,'userPermission',permissionSettings); } @@ -311,7 +326,7 @@ export class helpMenu extends FormApplication { */ static get defaultOptions() { return mergeObject(super.defaultOptions, { - id: "userPermissionConfig", + id: "materialDeck_userPermissionConfig", title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.Permission"), template: "./modules/MaterialDeck/templates/userPermissionConfig.html", width: 660, @@ -324,18 +339,23 @@ export class helpMenu extends FormApplication { * Provide data to the template */ async getData() { - let settings = game.settings.get(MODULE.moduleName,'userPermission'); - if (settings == undefined || settings == null || MODULE.isEmpty(settings)) { + let settings = game.settings.get(moduleName,'userPermission'); + if (settings == undefined || settings == null || isEmpty(settings)) { settings = { enable: defaultEnable, permissions: defaultUserPermissions } } - + const actions = Object.entries(duplicate(settings.permissions)).reduce((arr, e) => { const perms = Object.entries(duplicate(e[1])).reduce((arr, p) => { let perm = {}; - perm.roles = [p[1][0],p[1][1],p[1][2],p[1][3]] + perm.roles = [ + {role:'player',en:p[1][0]}, + {role:'trusted',en:p[1][1]}, + {role:'assistent',en:p[1][2]}, + {role:'gm',en:p[1][3]} + ] perm.id = p[0]; perm.label = game.i18n.localize("MaterialDeck.Perm."+e[0]+"."+p[0]+".label"); perm.hint = game.i18n.localize("MaterialDeck.Perm."+e[0]+"."+p[0]+".hint"); @@ -355,14 +375,21 @@ export class helpMenu extends FormApplication { if (actions[i].id == 'MOVE') actions.splice(i,1); } + + const enable = [ + {role:'player',en:settings.enable[0]}, + {role:'trusted',en:settings.enable[1]}, + {role:'assistent',en:settings.enable[2]}, + {role:'gm',en:settings.enable[3]} + ] return { roles: Object.keys(CONST.USER_ROLES).reduce((obj, r) => { if ( r === "NONE" ) return obj; obj[r] = `USER.Role${r.titleCase()}`; return obj; }, {}), - actions: actions, - enable: settings.enable + actions, + enable } } @@ -372,12 +399,28 @@ export class helpMenu extends FormApplication { * @param {*} formData */ async _updateObject(event, formData) { - let permissions = expandObject(formData); let settings = {}; - settings.enable = permissions.ENABLE; + let permissions = expandObject(formData); + + for (const [key, value] of Object.entries(permissions)) { + const val = value; + let conf = {}; + if (key == 'ENABLE') { + settings.enable = [value.player, value.trusted, value.assistent, value.gm]; + } + else { + conf = {}; + for (const [key, value] of Object.entries(val)) { + const arr = [value.player, value.trusted, value.assistent, value.gm]; + conf[key] = arr; + } + } + permissions[key] = conf; + } + delete permissions.ENABLE; settings.permissions = permissions; - // game.settings.set(MODULE.moduleName,'userPermission',settings); + game.settings.set(moduleName,'userPermission',settings); } async activateListeners(html) { @@ -396,7 +439,7 @@ export class helpMenu extends FormApplication { enable: defaultEnable, permissions: defaultUserPermissions } - await game.settings.set(MODULE.moduleName,'userPermission',settings); + await game.settings.set(moduleName,'userPermission',settings); this.render(); ui.notifications.info(game.i18n.localize("MaterialDeck.Perm.DefaultNotification")); } diff --git a/src/streamDeck.js b/src/streamDeck.js index bf2eb67..1d2acff 100644 --- a/src/streamDeck.js +++ b/src/streamDeck.js @@ -1,4 +1,4 @@ -import * as MODULE from "../MaterialDeck.js"; +import { moduleName, sendWS, tokenControl, macroControl, combatTracker, playlistControl, soundboard, otherControls, externalModules, sceneControl } from "../MaterialDeck.js"; export class StreamDeck{ constructor() { @@ -90,14 +90,14 @@ export class StreamDeck{ } if (this.getActive(action) == false){ - if (action == 'token') MODULE.tokenControl.active = false; - else if (action == 'macro') MODULE.macroControl.active = false; - else if (action == 'combattracker') MODULE.combatTracker.active = false; - else if (action == 'playlist') MODULE.playlistControl.active = false; - else if (action == 'soundboard') MODULE.soundboard.active = false; - else if (action == 'other') MODULE.otherControls.active = false; - else if (action == 'external') MODULE.externalModules.active = false; - else if (action == 'scene') MODULE.sceneControl.active = false; + if (action == 'token') tokenControl.active = false; + else if (action == 'macro') macroControl.active = false; + else if (action == 'combattracker') combatTracker.active = false; + else if (action == 'playlist') playlistControl.active = false; + else if (action == 'soundboard') soundboard.active = false; + else if (action == 'other') otherControls.active = false; + else if (action == 'external') externalModules.active = false; + else if (action == 'scene') sceneControl.active = false; } } @@ -204,7 +204,7 @@ export class StreamDeck{ target: 0 } }; - MODULE.sendWS(JSON.stringify(msg)); + sendWS(JSON.stringify(msg)); } setColor(context,color = '#000000'){ @@ -216,7 +216,7 @@ export class StreamDeck{ format: 'color', background: color }; - MODULE.sendWS(JSON.stringify(msg)); + sendWS(JSON.stringify(msg)); } setImage(image,context,device,nr,id){ @@ -232,7 +232,7 @@ export class StreamDeck{ target: 0 } }; - MODULE.sendWS(JSON.stringify(json)); + sendWS(JSON.stringify(json)); } setBufferImage(context,device,nr,id){ @@ -247,7 +247,7 @@ export class StreamDeck{ target: 0 } }; - MODULE.sendWS(JSON.stringify(json)); + sendWS(JSON.stringify(json)); } setIcon(context,device,src='',options = {}){ @@ -301,6 +301,8 @@ export class StreamDeck{ let format = split[split.length-1].split('?')[0]; split = split[0].split(' '); if (split[0] == 'fas' || split[0] == 'far' || split[0] == 'fal' || split[0] == 'fad') format = 'icon'; + let split2 = split[0].split('-'); + if (split2[0] == 'fa') format = 'icon'; let msg = { target: "SD", event: 'setIcon', @@ -326,7 +328,7 @@ export class StreamDeck{ action: action, state: state }; - MODULE.sendWS(JSON.stringify(msg)); + sendWS(JSON.stringify(msg)); } setProfile(action,device){ @@ -343,7 +345,7 @@ export class StreamDeck{ profile: profile } }; - MODULE.sendWS(JSON.stringify(json)); + sendWS(JSON.stringify(json)); } setPluginId(id){ @@ -439,7 +441,7 @@ export class StreamDeck{ img.onload = () => { if (format == 'color') ctx.filter = "opacity(0)"; - if (data.overlay == true) ctx.filter = "brightness(" + game.settings.get(MODULE.moduleName,'imageBrightness') + "%)"; + if (data.overlay == true) ctx.filter = "brightness(" + game.settings.get(moduleName,'imageBrightness') + "%)"; //ctx.filter = "brightness(0) saturate(100%) invert(38%) sepia(62%) saturate(2063%) hue-rotate(209deg) brightness(90%) contrast(95%)"; var imageAspectRatio = img.width / img.height; var canvasAspectRatio = canvas.width / canvas.height; @@ -564,7 +566,7 @@ export class StreamDeck{ addToImageBuffer(img,data){ const id = this.getImageBufferId(data); - const maxBufferSize = game.settings.get(MODULE.moduleName,'imageBuffer'); + const maxBufferSize = game.settings.get(moduleName,'imageBuffer'); if (maxBufferSize == 0) return false; if (this.imageBufferCounter > maxBufferSize) this.imageBufferCounter = 0; @@ -581,7 +583,7 @@ export class StreamDeck{ } checkImageBuffer(data){ - if (game.settings.get(MODULE.moduleName,'imageBuffer') == 0) return false; + if (game.settings.get(moduleName,'imageBuffer') == 0) return false; const id = this.getImageBufferId(data); for (let i=0; i= 0) ? `+${val}` : val; } @@ -65,7 +73,7 @@ export class demonlord{ getSkill(token, skill) { if (skill == undefined) skill = 'acr'; - const val = token.actor.data.data.skills?.[skill].total; + const val = this.getActorData(token).skills?.[skill].total; return (val >= 0) ? `+${val}` : val; } @@ -114,7 +122,7 @@ export class demonlord{ } getItemUses(item) { - return {available: item.data.data.quantity}; + return {available: getItemData(item).quantity}; } /** @@ -124,7 +132,7 @@ export class demonlord{ if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') - else return allItems.filter(i => i.type == 'spell' && i.data.data.rank == level) + else return allItems.filter(i => i.type == 'spell' && getItemData(i).rank == level) } getSpellUses(token,level,item) { diff --git a/src/systems/dnd35e.js b/src/systems/dnd35e.js index 72bdc75..86e7708 100644 --- a/src/systems/dnd35e.js +++ b/src/systems/dnd35e.js @@ -1,12 +1,20 @@ -import {compatibleCore} from "../misc.js"; +import { compatibleCore } from "../misc.js"; export class dnd35e{ constructor(){ - + console.log("Material Deck: Using system 'Dungeons & Dragons 3.5e'/'Pathfinder 1e'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; } getHP(token) { - const hp = token.actor.data.data.attributes.hp; + const hp = this.getActorData(token).attributes.hp; return { value: hp.value, max: hp.max @@ -14,7 +22,7 @@ export class dnd35e{ } getTempHP(token) { - const hp = token.actor.data.data.attributes.hp; + const hp = this.getActorData(token).attributes.hp; return { value: (hp.temp == null) ? 0 : hp.temp, max: (hp.tempmax == null) ? 0 : hp.tempmax @@ -22,7 +30,7 @@ export class dnd35e{ } getAC(token) { - return token.actor.data.data.attributes.ac.normal.total; + return this.getActorData(token).attributes.ac.normal.total; } getShieldHP(token) { @@ -30,7 +38,7 @@ export class dnd35e{ } getSpeed(token) { - const movement = token.actor.data.data.attributes.speed; + const movement = this.getActorData(token).attributes.speed; let speed = ""; if (movement.burrow.total > 0) speed += `Burrow: ${movement.burrow.total}Ft`; if (movement.climb.total > 0) { @@ -53,7 +61,7 @@ export class dnd35e{ } getInitiative(token) { - let initiative = token.actor.data.data.attributes.init.total; + let initiative = this.getActorData(token).attributes.init.total; return (initiative >= 0) ? `+${initiative}` : initiative; } @@ -71,29 +79,29 @@ export class dnd35e{ getAbility(token, ability) { if (ability == undefined) ability = 'str'; - return token.actor.data.data.abilities?.[ability].value; + return this.getActorData(token).abilities?.[ability].value; } getAbilityModifier(token, ability) { if (ability == undefined) ability = 'str'; - let val = token.actor.data.data.abilities?.[ability].mod; + let val = this.getActorData(token).abilities?.[ability].mod; return (val >= 0) ? `+${val}` : val; } getAbilitySave(token, ability) { if (ability == undefined) ability = 'fort'; - let val = token.actor.data.data.attributes.savingThrows?.[ability].total; + let val = this.getActorData(token).attributes.savingThrows?.[ability].total; return (val >= 0) ? `+${val}` : val; } getSkill(token, skill) { if (skill == undefined) skill = 'apr'; - const val = token.actor.data.data.skills?.[skill].mod; + const val = this.getActorData(token).skills?.[skill].mod; return (val >= 0) ? `+${val}` : val; } getProficiency(token) { - const val = token.actor.data.data.attributes.prof; + const val = this.getActorData(token).attributes.prof; return (val >= 0) ? `+${val}` : val; } @@ -133,7 +141,10 @@ export class dnd35e{ if (roll == 'ability') token.actor.rollAbilityTest(ability,options); else if (roll == 'save') token.actor.rollSavingThrow(save, null, null,options); else if (roll == 'skill') token.actor.rollSkill(skill,options); - else if (roll == 'initiative') token.actor.rollInitiative(options); + else if (roll == 'initiative') { + options.rerollInitiative = true; + token.actor.rollInitiative(options); + } else if (roll == 'grapple') token.actor.rollGrapple(options); else if (roll == 'bab') token.actor.rollBAB(options); else if (roll == 'melee') token.actor.rollMelee(options); @@ -147,16 +158,16 @@ export class dnd35e{ if (itemType == undefined) itemType = 'any'; const allItems = token.actor.items; if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container'); - else if (game.system.id == 'D35E' && itemType == 'container') return allItems.filter(i => i.type == 'loot' && i.data.data.subType == itemType); + else if (game.system.id == 'D35E' && itemType == 'container') return allItems.filter(i => i.type == 'loot' && this.getItemData(i).subType == itemType); else { if (itemType == 'gear' || itemType == 'ammo' || itemType == 'misc' || itemType == 'tradeGoods') - return allItems.filter(i => i.type == 'loot' && i.data.data.subType == itemType); + return allItems.filter(i => i.type == 'loot' && this.getItemData(i).subType == itemType); else return allItems.filter(i => i.type == itemType); } } getItemUses(item) { - return {available: item.data.data.quantity}; + return {available: this.getItemData(item).quantity}; } /** @@ -170,10 +181,10 @@ export class dnd35e{ } getFeatureUses(item) { - if (item.data.type == 'class') return {available: item.data.data.levels}; + if (item.data.type == 'class') return {available: this.getItemData(item).levels}; else return { - available: item.data.data.uses.value, - maximum: item.data.data.uses.max + available: this.getItemData(item).uses.value, + maximum: this.getItemData(item).uses.max }; } @@ -184,12 +195,12 @@ export class dnd35e{ if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') - else return allItems.filter(i => i.type == 'spell' && i.data.data.level == level) + else return allItems.filter(i => i.type == 'spell' && this.getItemData(i).level == level) } getSpellUses(token,level,item) { if (level == undefined) level = 'any'; - if (item.data.data.level == 0) return; + if (this.getItemData(item).level == 0) return; return { available: item.charges, maximum: item.maxCharges diff --git a/src/systems/dnd5e.js b/src/systems/dnd5e.js index f0c27b4..0e1b935 100644 --- a/src/systems/dnd5e.js +++ b/src/systems/dnd5e.js @@ -1,4 +1,4 @@ -import {compatibleCore} from "../misc.js"; +import { compatibleCore } from "../misc.js"; const proficiencyColors = { 0: "#000000", @@ -9,11 +9,19 @@ const proficiencyColors = { export class dnd5e{ constructor(){ - + console.log("Material Deck: Using system 'Dungeons & Dragons 5e'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; } getHP(token) { - const hp = token.actor.data.data.attributes.hp; + const hp = this.getActorData(token).attributes.hp; return { value: hp.value, max: hp.max @@ -21,7 +29,7 @@ export class dnd5e{ } getTempHP(token) { - const hp = token.actor.data.data.attributes.hp; + const hp = this.getActorData(token).attributes.hp; return { value: (hp.temp == null) ? 0 : hp.temp, max: (hp.tempmax == null) ? 0 : hp.tempmax @@ -29,7 +37,7 @@ export class dnd5e{ } getAC(token) { - return token.actor.data.data.attributes.ac.value; + return this.getActorData(token).attributes.ac.value; } getShieldHP(token) { @@ -37,41 +45,36 @@ export class dnd5e{ } getSpeed(token) { - const movement = token.actor.data.data.attributes.movement; + const movement = this.getActorData(token).attributes.movement; let speed = ""; - if (movement != undefined){ - if (movement.burrow > 0) speed += `${game.i18n.localize("DND5E.MovementBurrow")}: ${movement.burrow + movement.units}`; - if (movement.climb > 0) { - if (speed.length > 0) speed += '\n'; - speed += `${game.i18n.localize("DND5E.MovementClimb")}: ${movement.climb + movement.units}`; - } - if (movement.fly > 0) { - if (speed.length > 0) speed += '\n'; - speed += `${game.i18n.localize("DND5E.MovementFly")}: ${movement.fly + movement.units}`; - } - if (movement.hover > 0) { - if (speed.length > 0) speed += '\n'; - speed += `${game.i18n.localize("DND5E.MovementHover")}: ${movement.hover + movement.units}`; - } - if (movement.swim > 0) { - if (speed.length > 0) speed += '\n'; - speed += `${game.i18n.localize("DND5E.MovementSwim")}: ${movement.swim + movement.units}`; - } - if (movement.walk > 0) { - if (speed.length > 0) speed += '\n'; - speed += `${game.i18n.localize("DND5E.MovementWalk")}: ${movement.walk + movement.units}`; - } + + if (movement.burrow > 0) speed += `${game.i18n.localize("DND5E.MovementBurrow")}: ${movement.burrow + movement.units}`; + if (movement.climb > 0) { + if (speed.length > 0) speed += '\n'; + speed += `${game.i18n.localize("DND5E.MovementClimb")}: ${movement.climb + movement.units}`; } - else { - const spd = token.actor.data.data.attributes.speed; - speed = spd.value; - if (spd.special.length > 0) speed + "\n" + spd.special; + if (movement.fly > 0) { + if (speed.length > 0) speed += '\n'; + speed += `${game.i18n.localize("DND5E.MovementFly")}: ${movement.fly + movement.units}`; } + if (movement.hover > 0) { + if (speed.length > 0) speed += '\n'; + speed += `${game.i18n.localize("DND5E.MovementHover")}: ${movement.hover + movement.units}`; + } + if (movement.swim > 0) { + if (speed.length > 0) speed += '\n'; + speed += `${game.i18n.localize("DND5E.MovementSwim")}: ${movement.swim + movement.units}`; + } + if (movement.walk > 0) { + if (speed.length > 0) speed += '\n'; + speed += `${game.i18n.localize("DND5E.MovementWalk")}: ${movement.walk + movement.units}`; + } + return speed; } getInitiative(token) { - let initiative = token.actor.data.data.attributes.init.total; + let initiative = this.getActorData(token).attributes.init.total; return (initiative >= 0) ? `+${initiative}` : initiative; } @@ -80,38 +83,38 @@ export class dnd5e{ } getPassivePerception(token) { - return token.actor.data.data.skills.prc.passive; + return this.getActorData(token).skills.prc.passive; } getPassiveInvestigation(token) { - return token.actor.data.data.skills.inv.passive; + return this.getActorData(token).skills.inv.passive; } getAbility(token, ability) { if (ability == undefined) ability = 'str'; - return token.actor.data.data.abilities?.[ability].value; + return this.getActorData(token).abilities?.[ability].value; } getAbilityModifier(token, ability) { if (ability == undefined) ability = 'str'; - let val = token.actor.data.data.abilities?.[ability].mod; + let val = this.getActorData(token).abilities?.[ability].mod; return (val >= 0) ? `+${val}` : val; } getAbilitySave(token, ability) { if (ability == undefined) ability = 'str'; - let val = token.actor.data.data.abilities?.[ability].save; + let val = this.getActorData(token).abilities?.[ability].save; return (val >= 0) ? `+${val}` : val; } getSkill(token, skill) { if (skill == undefined) skill = 'acr'; - const val = token.actor.data.data.skills?.[skill].total; + const val = this.getActorData(token).skills?.[skill].total; return (val >= 0) ? `+${val}` : val; } getProficiency(token) { - const val = token.actor.data.data.attributes.prof; + const val = this.getActorData(token).attributes.prof; return (val >= 0) ? `+${val}` : val; } @@ -151,7 +154,10 @@ export class dnd5e{ if (roll == 'ability') token.actor.rollAbilityTest(ability,options); else if (roll == 'save') token.actor.rollAbilitySave(save,options); else if (roll == 'skill') token.actor.rollSkill(skill,options); - else if (roll == 'initiative') token.actor.rollInitiative(options); + else if (roll == 'initiative') { + options.rerollInitiative = true; + token.actor.rollInitiative(options); + } else if (roll == 'deathSave') token.actor.rollDeathSave(options); } @@ -166,7 +172,7 @@ export class dnd5e{ } getItemUses(item) { - return {available: item.data.data.quantity}; + return {available: this.getItemData(item).quantity}; } /** @@ -175,15 +181,18 @@ export class dnd5e{ getFeatures(token,featureType) { if (featureType == undefined) featureType = 'any'; const allItems = token.actor.items; - if (featureType == 'any') return allItems.filter(i => i.type == 'class' || i.type == 'feat') - else return allItems.filter(i => i.type == featureType) + + if (featureType == 'any') return allItems.filter(i => i.type == 'class' || i.type == 'feat') + else if (featureType == 'activeAbilities') return allItems.filter(i => i.type == 'feat' && i.labels.featType == 'Action') + else if (featureType == 'passiveAbilities') return allItems.filter(i => i.type == 'feat' && i.labels.featType == 'Passive') + else return allItems.filter(i => i.type == featureType) } getFeatureUses(item) { - if (item.data.type == 'class') return {available: item.data.data.levels}; + if (item.type == 'class') return {available: this.getItemData(item).levels}; else return { - available: item.data.data.uses.value, - maximum: item.data.data.uses.max + available: this.getItemData(item).uses.value, + maximum: this.getItemData(item).uses.max }; } @@ -194,33 +203,65 @@ export class dnd5e{ if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') - else return allItems.filter(i => i.type == 'spell' && i.data.data.level == level) + else return allItems.filter(i => i.type == 'spell' && this.getItemData(i).level == level) } getSpellUses(token,level,item) { - if (level == undefined) level = 'any'; - if (item.data.data.level == 0) return; + if (level == undefined || level == 'any') level = this.getItemData(item).level; + if (this.getItemData(item).level == 0) return; return { - available: token.actor.data.data.spells?.[`spell${level}`].value, - maximum: token.actor.data.data.spells?.[`spell${level}`].max + available: this.getActorData(token).spells?.[`spell${level}`].value, + maximum: this.getActorData(token).spells?.[`spell${level}`].max } } - rollItem(item) { - return item.roll() + rollItem(item, settings, rollOption) { + let options = { + fastForward: rollOption != 'dialog', + advantage: rollOption == 'advantage', + disadvantage: rollOption == 'disadvantage' + } + if (settings.inventoryType == 'weapon') { + if (settings.weaponRollMode == 'attack') { + options.fastForward = true; + return item.rollAttack(options); + } + else if (settings.weaponRollMode == 'damage' || settings.weaponRollMode == 'versatile') { + options.fastForward = true; + return item.rollDamage({ + options, + critical:false, + versatile: settings.weaponRollMode == 'versatile' + }); + } + else if (settings.weaponRollMode == 'damageCrit' || settings.weaponRollMode == 'versatileCrit') { + options.fastForward = true; + return item.rollDamage({ + options, + critical:true, + versatile: settings.weaponRollMode == 'versatile' || settings.weaponRollMode == 'versatileCrit' + }); + } + else if (settings.weaponRollMode == 'otherFormula') { + return item.rollFormula(options); + } + } + + if (compatibleCore('10.0')) item.use(options) + else item.roll(options) } /** * Ring Colors */ getSkillRingColor(token, skill) { - const profLevel = token.actor.data.data?.skills[skill]?.proficient; + const profLevel = this.getActorData(token).skills[skill]?.proficient; if (profLevel == undefined) return; return proficiencyColors?.[profLevel]; } getSaveRingColor(token, save) { - const profLevel = token.actor.data.data?.abilities[save]?.proficient; + const profLevel = this.getActorData(token).abilities[save]?.proficient; if (profLevel == undefined) return; return proficiencyColors?.[profLevel]; } diff --git a/src/systems/forbidden-lands.js b/src/systems/forbidden-lands.js index 98025a1..edeed4e 100644 --- a/src/systems/forbidden-lands.js +++ b/src/systems/forbidden-lands.js @@ -1,30 +1,36 @@ -import {compatibleCore} from "../misc.js"; +import { compatibleCore } from "../misc.js"; export class forbiddenlands{ constructor(){ - + console.log("Material Deck: Using system 'Forbidden Lands'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; } getHP(token) { - const hp = token.actor.data.data.attribute.strength; + const hp = this.getActorData(token).attribute.strength; return { value: hp.value, max: hp.max } } - getAgility(token) { - const agility = token.actor.data.data.attribute.agility; + const agility = this.getActorData(token).attribute.agility; return { value: agility.value, max: agility.max } } - getWits(token) { - const wits = token.actor.data.data.attribute.wits; + const wits = this.getActorData(token).attribute.wits; return { value: wits.value, max: wits.max @@ -32,7 +38,7 @@ export class forbiddenlands{ } getEmpathy(token) { - const empathy = token.actor.data.data.attribute.empathy; + const empathy = this.getActorData(token).attribute.empathy; return { value: empathy.value, max: empathy.max @@ -40,7 +46,7 @@ export class forbiddenlands{ } getWillPower(token) { - const wp = token.actor.data.data.bio.willpower; + const wp = this.getActorData(token).bio.willpower; return { value: wp.value, max: wp.max @@ -49,15 +55,9 @@ export class forbiddenlands{ getTempHP(token) { return 0; - const hp = token.actor.data.data.attributes.hp; - return { - value: (hp.temp == null) ? 0 : hp.temp, - max: (hp.tempmax == null) ? 0 : hp.tempmax - } } getAC(token) { - const totalArmor = token.actor.itemTypes.armor.reduce((sum, armor) => { if (armor.itemProperties.part === "shield") return sum; const value = armor.itemProperties.bonus.value; @@ -76,8 +76,6 @@ export class forbiddenlands{ getInitiative(token) { return 0; - let initiative = token.actor.data.data.attributes.init.total; - return (initiative >= 0) ? `+${initiative}` : initiative; } toggleInitiative(token) { @@ -86,17 +84,15 @@ export class forbiddenlands{ getPassivePerception(token) { return 0; - return token.actor.data.data.skills.prc.passive; } getPassiveInvestigation(token) { return; - return token.actor.data.data.skills.inv.passive; } getAbility(token, ability) { if (ability == undefined) ability = 'strength'; - return token.actor.data.data.attribute?.[ability].value; + return this.getActorData(token).attribute?.[ability].value; } getAbilityModifier(token, ability) { @@ -116,8 +112,6 @@ export class forbiddenlands{ getProficiency(token) { return; - const val = token.actor.data.data.attributes.prof; - return (val >= 0) ? `+${val}` : val; } getConditionIcon(condition) { @@ -181,9 +175,9 @@ export class forbiddenlands{ getItemUses(item) { if (item.type == 'monsterAttack') return; - if (item.type == 'rawMaterial') return {available: item.data.data.quantity}; - return {available: item.data.data.bonus.value, - maximum: item.data.data.bonus.max}; + if (item.type == 'rawMaterial') return {available: this.getItemData(item).quantity}; + return {available: this.getItemData(item).bonus.value, + maximum: this.getItemData(item).bonus.max}; } /** @@ -197,10 +191,10 @@ export class forbiddenlands{ } getFeatureUses(item) { - if (item.data.type == 'class') return {available: item.data.data.levels}; + if (item.data.type == 'class') return {available: this.getItemData(item).levels}; else return { - available: item.data.data.uses.value, - maximum: item.data.data.uses.max + available: this.getItemData(item).uses.value, + maximum: this.getItemData(item).uses.max }; } @@ -211,15 +205,15 @@ export class forbiddenlands{ if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') - else return allItems.filter(i => i.type == 'spell' && i.data.data.level == level) + else return allItems.filter(i => i.type == 'spell' && this.getItemData(i).level == level) } getSpellUses(token,level,item) { if (level == undefined) level = 'any'; - if (item.data.data.level == 0) return; + if (this.getItemData(item).level == 0) return; return { - available: token.actor.data.data.spells?.[`spell${level}`].value, - maximum: token.actor.data.data.spells?.[`spell${level}`].max + available: this.getActorData(token).spells?.[`spell${level}`].value, + maximum: this.getActorData(token).spells?.[`spell${level}`].max } } diff --git a/src/systems/pf2e.js b/src/systems/pf2e.js index 9981626..52e0a14 100644 --- a/src/systems/pf2e.js +++ b/src/systems/pf2e.js @@ -1,5 +1,5 @@ -import {compatibleCore} from "../misc.js"; -import {otherControls} from "../../MaterialDeck.js"; +import { otherControls } from "../../MaterialDeck.js"; +import { compatibleCore } from "../misc.js"; const limitedSheets = ['loot', 'vehicle']; const proficiencyColors = @@ -14,11 +14,19 @@ const proficiencyColors = export class pf2e{ constructor(){ - + console.log("Material Deck: Using system 'Pathfinder 2e'"); } tokenSpellData = new Map(); + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; + } + getHP(token) { const hp = token.actor.attributes?.hp; return { @@ -48,7 +56,7 @@ export class pf2e{ getSpeed(token) { if (this.isLimitedSheet(token.actor) || token.actor.type == 'hazard') { if (token.actor.type == 'vehicle') { - return token.actor.data.data.details.speed; + return this.getActorData(token).details.speed; } else return ''; } let speed = `${token.actor.attributes.speed?.total}'`; @@ -117,7 +125,7 @@ export class pf2e{ findSave(token, ability) { if (this.isLimitedSheet(token.actor)) return; - return token.actor.data.data.saves?.[ability]; + return this.getActorData(token).saves?.[ability]; } fixSave(ability) { @@ -151,12 +159,12 @@ export class pf2e{ return; } } - return token.actor.data.data.skills?.[skill]; + return this.getActorData(token).skills?.[skill]; } getLoreSkills(token) { if (this.isLimitedSheet(token.actor)) return []; - const skills = token.actor.data.data.skills; + const skills = this.getActorData(token).skills; return Object.keys(skills).map(key => skills[key]).filter(s => s.lore == true); } @@ -270,7 +278,7 @@ export class pf2e{ return; } } - let skillName = token.actor.data.data.skills?.[skill].name; + let skillName = this.getActorData(token).skills?.[skill].name; skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1); this.checkRoll(`Skill Check: ${skillName}`, token.actor.skills?.[skill], 'skill-check', token.actor); } @@ -315,14 +323,14 @@ export class pf2e{ if (featureType == 'feat-gen') return allItems.filter(i => i.type == 'feat' && i.featType == 'general'); if (featureType == 'feat-ski') return allItems.filter(i => i.type == 'feat' && i.featType == 'skill'); if (featureType == 'action-any') return allItems.filter(i => i.type == 'action'); - if (featureType == 'action-def') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'defensive'); - if (featureType == 'action-int') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'interaction'); - if (featureType == 'action-off') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'offensive'); + if (featureType == 'action-def') return allItems.filter(i => i.type == 'action' && this.getItemData(i).actionCategory?.value == 'defensive'); + if (featureType == 'action-int') return allItems.filter(i => i.type == 'action' && this.getItemData(i).actionCategory?.value == 'interaction'); + if (featureType == 'action-off') return allItems.filter(i => i.type == 'action' && this.getItemData(i).actionCategory?.value == 'offensive'); if (featureType == 'strike') { //Strikes are not in the actor.items collection if (token.actor.type == 'hazard' || token.actor.type == 'familiar') { return allItems.filter(i => i.type == 'melee' || i.type == 'ranged'); } - let actions = token.actor.data.data.actions?.filter(a=>a.type == 'strike'); + let actions = this.getActorData(token).actions?.filter(a=>a.type == 'strike'); for (let a of actions) { a.img = a.imageUrl; a.data = { @@ -345,7 +353,7 @@ export class pf2e{ buildSpellData(token) { let spellData = [[],[],[],[],[],[],[],[],[],[],[],[]]; let spellcastingEntries = token.actor.spellcasting; - const actorLevel = token.actor.data.data.details.level.value; + const actorLevel = this.getActorData(token).details.level.value; spellcastingEntries.forEach(spellCastingEntry => { let highestSpellSlot = Math.ceil(actorLevel/2); while (spellCastingEntry.data.data.slots?.[`slot${highestSpellSlot}`]?.max <= 0) highestSpellSlot--; @@ -413,15 +421,15 @@ export class pf2e{ if (level == undefined || level == 'any') level = item.level; if (item.isCantrip == true) return; if (item.isFocusSpell == true) return { - available: token.actor.data.data.resources.focus.value, - maximum: token.actor.data.data.resources.focus.max + available: this.getActorData(token).resources.focus.value, + maximum: this.getActorData(token).resources.focus.max } const spellbook = this.findSpellcastingEntry(token.actor, item); if (spellbook == undefined) return; if (spellbook.data.data.prepared.value == 'innate') { return { - available: item.data.data.location.uses.value, - maximum: item.data.data.location.uses.max + available: this.getItemData(item).location.uses.value, + maximum: this.getItemData(item).location.uses.max } } if (spellbook.data.data.prepared.value == 'prepared') { diff --git a/src/systems/starfinder.js b/src/systems/starfinder.js index 6d6c3c9..be2d235 100644 --- a/src/systems/starfinder.js +++ b/src/systems/starfinder.js @@ -1,4 +1,4 @@ -import {compatibleCore} from "../misc.js"; +import { compatibleCore } from "../misc.js"; const proficiencyColors = { 0: "#000000", @@ -9,11 +9,19 @@ const proficiencyColors = { export class starfinder{ constructor(){ - + console.log("Material Deck: Using system 'Starfinder'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; } getHP(token) { - const hp = token.actor.data.data.attributes.hp; + const hp = this.getActorData(token).attributes.hp; return { value: hp.value, max: hp.max @@ -25,7 +33,7 @@ export class starfinder{ } getStamina(token) { - const stamina = token.actor.data.data.attributes.sp; + const stamina = this.getActorData(token).attributes.sp; return { value: stamina.value, max: stamina.max @@ -33,11 +41,11 @@ export class starfinder{ } getAC(token) { - return token.actor.data.data.attributes.eac.value; + return this.getActorData(token).attributes.eac.value; } getKinAC(token) { - return token.actor.data.data.attributes.kac.value; + return this.getActorData(token).attributes.kac.value; } getShieldHP(token) { @@ -45,7 +53,7 @@ export class starfinder{ } getSpeed(token) { - const movement = token.actor.data.data.attributes.speed; + const movement = this.getActorData(token).attributes.speed; let speed = ""; if (movement.burrowing.value > 0) speed += `Burrow: ${movement.burrowing.value}Ft`; if (movement.climbing.value > 0) { @@ -68,7 +76,7 @@ export class starfinder{ } getInitiative(token) { - let initiative = token.actor.data.data.attributes.init.total; + let initiative = this.getActorData(token).attributes.init.total; return (initiative >= 0) ? `+${initiative}` : initiative; } @@ -86,25 +94,25 @@ export class starfinder{ getAbility(token, ability) { if (ability == undefined) ability = 'str'; - return token.actor.data.data.abilities?.[ability].value; + return this.getActorData(token).abilities?.[ability].value; } getAbilityModifier(token, ability) { if (ability == undefined) ability = 'str'; - let val = token.actor.data.data.abilities?.[ability].mod; + let val = this.getActorData(token).abilities?.[ability].mod; return (val >= 0) ? `+${val}` : val; } getAbilitySave(token, ability) { if (ability == undefined) ability = 'fort'; else if (ability == 'ref') ability = 'reflex'; - let val = token.actor.data.data.attributes?.[ability].bonus; + let val = this.getActorData(token).attributes?.[ability].bonus; return (val >= 0) ? `+${val}` : val; } getSkill(token, skill) { if (skill == undefined) skill = 'acr'; - const val = token.actor.data.data.skills?.[skill].mod; + const val = this.getActorData(token).skills?.[skill].mod; return (val >= 0) ? `+${val}` : val; } @@ -148,7 +156,10 @@ export class starfinder{ if (roll == 'ability') token.actor.rollAbilityTest(ability,options); else if (roll == 'save') token.actor.rollAbilitySave(save,options); else if (roll == 'skill') token.actor.rollSkill(skill,options); - else if (roll == 'initiative') token.actor.rollInitiative(options); + else if (roll == 'initiative') { + options.rerollInitiative = true; + token.actor.rollInitiative(options); + } else if (roll == 'deathSave') token.actor.rollDeathSave(options); } @@ -164,7 +175,7 @@ export class starfinder{ } getItemUses(item) { - return {available: item.data.data.quantity}; + return {available: this.getItemData(item).quantity}; } /** @@ -183,10 +194,10 @@ export class starfinder{ } getFeatureUses(item) { - if (item.data.type == 'class') return {available: item.data.data.levels}; + if (item.data.type == 'class') return {available: this.getItemData(item).levels}; else return { - available: item.data.data.uses.value, - maximum: item.data.data.uses.max + available: this.getItemData(item).uses.value, + maximum: this.getItemData(item).uses.max }; } @@ -197,16 +208,16 @@ export class starfinder{ if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') - else if (level == 'innate') return allItems.filter(i => i.type == 'spell' && i.data.data.preparation.mode == 'innate'); - else return allItems.filter(i => i.type == 'spell' && i.data.data.preparation.mode == '' && i.data.data.level == level) + else if (level == 'innate') return allItems.filter(i => i.type == 'spell' && this.getItemData(i).preparation.mode == 'innate'); + else return allItems.filter(i => i.type == 'spell' && this.getItemData(i).preparation.mode == '' && this.getItemData(i).level == level) } getSpellUses(token,level,item) { if (level == undefined) level = 'any'; - if (item.data.data.level == 0) return; + if (this.getItemData(item).level == 0) return; - const spellSlots = token.actor.data.data.spells; - const allowedClasses = item.data.data.allowedClasses; + const spellSlots = this.getActorData(token).spells; + const allowedClasses = this.getItemData(item).allowedClasses; let uses = {available: 0, maximum: 0}; if (allowedClasses.myst && spellSlots?.[`spell${level}`].perClass?.mystic?.max > uses.maximum) @@ -229,13 +240,13 @@ export class starfinder{ * Ring Colors */ getSkillRingColor(token, skill) { - const profLevel = token.actor.data.data?.skills[skill]?.proficient; + const profLevel = this.getActorData(token)?.skills[skill]?.proficient; if (profLevel == undefined) return; return proficiencyColors?.[profLevel]; } getSaveRingColor(token, save) { - const profLevel = token.actor.data.data?.abilities[save]?.proficient; + const profLevel = this.getActorData(token)?.abilities[save]?.proficient; if (profLevel == undefined) return; return proficiencyColors?.[profLevel]; } diff --git a/src/systems/template.js b/src/systems/template.js new file mode 100644 index 0000000..1146067 --- /dev/null +++ b/src/systems/template.js @@ -0,0 +1,325 @@ +/** + * This is a template for adding a new gaming system. + * Edit it to suit your system, for inspiration, look at other system files. + * Functions that are unused in your system can be left empty, but don't delete the function. + * + * Use the compatibleCore function to get the right value for the Foundry core: + * return compatibleCore('10.0') ? [value in v10+] : [value pre v10]; + * + * Use the compatibleSystem function to get the right value for the gaming system version: + * return compatibleSystem('1.6.3') ? [value in v1.6.3+] : [value pre v1.6.3]; + */ + +import { compatibleCore, compatibleSystem } from "../misc"; + +/** + * Proficiency colors to show if a token is proficient in for example a skill + */ +const proficiencyColors = { + 0: "#000000", + 0.5: "#804A00", + 1: "#C0C0C0", + 2: "#FFD700" +} + +//Rename 'template' to the name of your system +export class template{ + constructor(){ + console.log("Material Deck: Using system 'SystemName'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; + } + + /** + * Returns the HP of the token + * @param {Token} token Token instance to get the HP from + * @returns {object} Token hp value and max: {value: ##, max: ##} + */ + getHP(token) { + return; + } + + /** + * Returns the temporary HP of the token + * @param {Token} token Token instance to get the temp HP from + * @returns {object} Token temp hp value and max: {value: ##, max: ##} + */ + getTempHP(token) { + return; + } + + /** + * Returns the armor class of the token + * @param {Token} token Token instance to get the AC from + * @returns {number} AC value + */ + getAC(token) { + return; + } + + /** + * Returns the shield HP of the token + * @param {Token} token Token instance to get the shield HP from + * @returns {number} Shield HP + */ + getShieldHP(token) { + return; + } + + /** + * Returns a string with movement speeds of the token + * @param {Token} token Token instance to get the speed from + * @returns {string} Movement speed string + */ + getSpeed(token) { + return; + } + + /** + * Returns the initiative of the token + * @param {Token} token Token instance to get the initiative from + * @returns {number} Initiative value + */ + getInitiative(token) { + return; + } + + /** + * Toggles the initiative of the token + * @param {Token} token Token instance to toggle the initiative for + */ + toggleInitiative(token) { + + } + + /** + * Returns the passive perception of the token + * @param {Token} token Token instance to get the passive perception from + * @returns {number} Passive perception value + */ + getPassivePerception(token) { + return; + } + + /** + * Returns the passive investigation of the token + * @param {Token} token Token instance to get the passive investigation from + * @returns {number} Passive investigation value + */ + getPassiveInvestigation(token) { + return; + } + + /** + * Returns the ability value of the token + * @param {Token} token Token instance to get the ability value from + * @param {string} ability Ability to get the value from + * @returns {number} Ability value + */ + getAbility(token, ability) { + if (ability == undefined) ability = ''; //default ability + return; + } + + /** + * Returns the ability modifier of the token + * @param {Token} token Token instance to get the ability modifier from + * @param {string} ability Ability to get the value from + * @returns {number} Ability modifier + */ + getAbilityModifier(token, ability) { + if (ability == undefined) ability = ''; //default ability + return; + } + + /** + * Returns the ability save of the token + * @param {Token} token Token instance to get the ability save from + * @param {string} ability Ability to get the value from + * @returns {number} Ability save + */ + getAbilitySave(token, ability) { + if (ability == undefined) ability = ''; //default ability + return; + } + + /** + * Returns the skill value of the token + * @param {Token} token Token instance to get the skill value from + * @param {string} skill Skill to get the value from + * @returns {number} Skill value + */ + getSkill(token, skill) { + if (skill == undefined) skill = ''; + return; + } + + /** + * Returns the proficiency value of the token + * @param {Token} token Token instance to get the proficiency from + * @returns {number} Proficiency value + */ + getProficiency(token) { + return; + } + + /** + * Returns the icon location of a condition + * @param {string} condition Name of the condition + * @returns {string} Icon location + */ + getConditionIcon(condition) { + if (condition == undefined) condition = 'removeAll'; + if (condition == 'removeAll') return; + else return; + } + + /** + * Returns whether a condition is active on the token + * @param {Token} token Token instance to get the value from + * @param {string} condition Name of the condition + * @returns {boolean} Condition is active or not + */ + getConditionActive(token,condition) { + if (condition == undefined) condition = 'removeAll'; + return; + } + + /** + * Toggles a condition on a token + * @param {Token} token Token instance to get the value from + * @param {string} condition Name of the condition + */ + async toggleCondition(token,condition) { + if (condition == undefined) condition = 'removeAll'; + + } + + /** + * Roll for a token + * @param {Token} token Token instance to roll for + * @param {string} roll Roll type (ability/save/skill/initiative/deathSave) + * @param {bbject} options Roll options + * @param {string} ability Name of the ability to roll + * @param {string} skill Name of the skill to roll + * @param {string} save Name of the save to roll + */ + roll(token,roll,options,ability,skill,save) { + if (roll == undefined) roll = 'ability'; + if (ability == undefined) ability = ''; //default ability + if (skill == undefined) skill = ''; //default skill + if (save == undefined) save = ''; //default save + + if (roll == 'ability') + else if (roll == 'save') + else if (roll == 'skill') + else if (roll == 'initiative') + else if (roll == 'deathSave') + } + + /** + * Get array of items + * @param {Token} token Token instance to get the items from + * @param {string} itemType Item type + * @returns {array} Array of items + */ + getItems(token,itemType) { + if (itemType == undefined) itemType = 'any'; + if (itemType == 'any') return ; + else return ; + } + + /** + * Returns uses/quantity of an item + * @param {Item} item Item instance to get the uses/quantity from + * @returns {object} Uses/quantity available {available: ###, maximum: ###} + */ + getItemUses(item) { + return; + } + + /** + * Returns the features of a token + * @param {Token} token Token instance to get the features from + * @param {string} featureType Feature types to return + * @returns {array} Array of features + */ + getFeatures(token,featureType) { + if (featureType == undefined) featureType = 'any'; + if (featureType == 'any') return; + else return; + } + + /** + * Returns uses/quantity of a feature + * @param {Item} item Item/feature instance to get the uses/quantity from + * @returns {object} Uses/quantity available {available: ###, maximum: ###} + */ + getFeatureUses(item) { + return {}; + } + + /** + * Returns the spells of a token + * @param {Token} token Token instance to get the spells from + * @param {string} level Spell level + * @returns {array} Array of spells + */ + getSpells(token,level) { + if (level == undefined) level = 'any'; + if (level == 'any') return; + else return; + } + + /** + * Returns the spell uses of a specific spell for a token + * @param {Token} token Token instance to get the spell uses from + * @param {string} level Spell level + * @param {Item} item Spell instance to get the uses from + * @returns {object} Spell uses left: {available: ###, maximum: ###} + */ + getSpellUses(token,level,item) { + return { + } + } + + /** + * Roll an item + * @param {Token} item Item instance to roll + * @param {object} settings Settings of the action + * @param {object} rollOption Roll options + */ + rollItem(item, settings, rollOption) { + + } + + /** + * Returns a color to display proficiency for skills + * @param {Token} token Token instance to get the proficiency from + * @param {string} skill Skill name + * @returns {string} Hex color string from proficiencyColors array + */ + getSkillRingColor(token, skill) { + const profLevel = ; + if (profLevel == undefined) return; + return proficiencyColors?.[profLevel]; + } + + /** + * Returns a color to display proficiency for saves + * @param {Token} token Token instance to get the proficiency from + * @param {string} save Save name + * @returns {string} Hex color string from proficiencyColors array + */ + getSaveRingColor(token, save) { + const profLevel = ; + if (profLevel == undefined) return; + return proficiencyColors?.[profLevel]; + } +} \ No newline at end of file diff --git a/src/systems/tokenHelper.js b/src/systems/tokenHelper.js index 558375f..e72db30 100644 --- a/src/systems/tokenHelper.js +++ b/src/systems/tokenHelper.js @@ -1,12 +1,12 @@ -import {dnd5e} from "./dnd5e.js"; -import {dnd35e} from "./dnd35e.js"; -import {pf2e} from "./pf2e.js"; -import {demonlord} from "./demonlord.js"; -import {wfrp4e} from "./wfrp4e.js"; -import {forbiddenlands} from "./forbidden-lands.js"; -import {starfinder} from "./starfinder.js"; -import {compatibleCore} from "../misc.js"; -import {gamingSystem} from "../../MaterialDeck.js"; +import { dnd5e } from "./dnd5e.js"; +import { dnd35e } from "./dnd35e.js"; +import { pf2e } from "./pf2e.js"; +import { demonlord } from "./demonlord.js"; +import { wfrp4e } from "./wfrp4e.js"; +import { forbiddenlands } from "./forbidden-lands.js"; +import { starfinder } from "./starfinder.js"; +import { compatibleCore } from "../misc.js"; +import { gamingSystem } from "../../MaterialDeck.js"; export class TokenHelper{ @@ -58,7 +58,7 @@ export class TokenHelper{ moveToken(token,dir){ if (dir == undefined) dir = 'up'; - const gridSize = canvas.scene.data.grid; + const gridSize = compatibleCore('10.0') ? canvas.scene.grid.size : canvas.scene.data.grid; let x = token.x; let y = token.y; @@ -88,8 +88,10 @@ export class TokenHelper{ } if (game.user.isGM == false && game.paused) return; if (game.user.isGM == false && (token.can(game.user,"control") == false || token.checkCollision(token.getCenter(x, y)))) return; - if (compatibleCore("0.8.1")) token.document.update({x:x,y:y}); - else token.update({x:x,y:y}); + let coords = canvas.grid.getCenter(x,y); + coords[0] -= canvas.grid.size/2; + coords[1] -= canvas.grid.size/2; + token.document.update({x:coords[0],y:coords[1]}); }; rotateToken(token,move,value) { @@ -97,11 +99,10 @@ export class TokenHelper{ value = isNaN(parseInt(value)) ? 0 : parseInt(value); let rotationVal; - if (move == 'by') rotationVal = token.data.rotation + value; + if (move == 'by') rotationVal = compatibleCore('10.0') ? token.document.rotation + value : token.data.rotation + value; else rotationVal = value; - if (compatibleCore("0.8.1")) token.document.update({rotation: rotationVal}); - else token.update({rotation: rotationVal}); + token.document.update({rotation: rotationVal}); } /////////////////////////////////////////////// @@ -127,11 +128,11 @@ export class TokenHelper{ //////////////////////////////////////////////////// getTokenIcon(token) { - return token.data.img; + return compatibleCore('10.0') ? token.document.texture.src : token.data.img; } getActorIcon(token) { - return token.actor.data.img; + return compatibleCore('10.0') ? token.actor.img : token.actor.data.img; } /*********************************************************************** @@ -328,8 +329,8 @@ export class TokenHelper{ return this.system.getSpellUses(token,level,item); } - rollItem(item, settings) { - return this.system.rollItem(item, settings); + rollItem(item, settings, rollOption) { + return this.system.rollItem(item, settings, rollOption); } /** diff --git a/src/systems/wfrp4e.js b/src/systems/wfrp4e.js index 2ba7e7e..bd3e2f6 100644 --- a/src/systems/wfrp4e.js +++ b/src/systems/wfrp4e.js @@ -1,21 +1,28 @@ -import {compatibleCore} from "../misc.js"; +import { compatibleCore } from "../misc.js"; export class wfrp4e { constructor(){ - + console.log("Material Deck: Using system 'Warhammer Fantasy Roleplaying 4e'"); + } + + getActorData(token) { + return compatibleCore('10.0') ? token.actor.system : token.actor.data.data; + } + + getItemData(item) { + return compatibleCore('10.0') ? item.system : item.data.data; } getFate(token) { - return token.actor.data.data.status.fate.value + return this.getActorData(token).status.fate.value } getFortune(token) { - return token.actor.data.data.status.fortune.value + return this.getActorData(token).status.fortune.value } - getWounds(token) { - const wounds = token.actor.data.data.status.wounds + const wounds = this.getActorData(token).status.wounds return { value: wounds.value, max: wounds.max @@ -24,7 +31,7 @@ export class wfrp4e { } getCriticalWounds(token) { - const criticalWounds = token.actor.data.data.status.criticalWounds + const criticalWounds = this.getActorData(token).status.criticalWounds return { value: criticalWounds.value, max: criticalWounds.max @@ -32,19 +39,19 @@ export class wfrp4e { } getCorruption(token) { - return token.actor.data.data.status.corruption.value + return this.getActorData(token).status.corruption.value } getAdvantage(token) { - return token.actor.data.data.status.advantage.value + return this.getActorData(token).status.advantage.value } getResolve(token) { - return token.actor.data.data.status.resolve.value + return this.getActorData(token).status.resolve.value } getResilience(token) { - return token.actor.data.data.status.resilience.value + return this.getActorData(token).status.resilience.value } getAbility(token, abilityName) { @@ -53,7 +60,7 @@ export class wfrp4e { getCharacteristics(token, characteristicName) { if (characteristicName == undefined ) characteristicName = `AG`; - const characteristic = token.actor.data.data.characteristics[characteristicName.toLowerCase()] + const characteristic = this.getActorData(token).characteristics[characteristicName.toLowerCase()] const val = characteristic.value; return (val >= 0) ? `+${val}` : val; } @@ -74,7 +81,7 @@ export class wfrp4e { } getFeatureUses(item) { - return {available: `+${item.data.data.total.value}`}; + return {available: `+${this.getItemData(item).total.value}`}; } getHP(token) { @@ -86,7 +93,7 @@ export class wfrp4e { } getSpeed(token) { - return token.actor.data.data.details.move.value; + return this.getActorData(token).details.move.value; } @@ -125,7 +132,7 @@ export class wfrp4e { getItemUses(item) { if ( item.type == 'ammunition') { - return {available: item.data.data.quantity.value}; + return {available: this.getItemData(item).quantity.value}; } else { return; diff --git a/templates/deviceConfig.html b/templates/deviceConfig.html index 4497ac7..da156a9 100644 --- a/templates/deviceConfig.html +++ b/templates/deviceConfig.html @@ -1,41 +1,18 @@
- - - - - - - + + + + {{#each devices as |d|}} - - - - + + + + {{/each}} -
{{localize "MaterialDeck.Name"}}{{localize "MaterialDeck.Type"}}{{localize "MaterialDeck.Id"}}{{localize "MaterialDeck.Perm.ENABLE.ENABLE.label"}}{{localize "MaterialDeck.Name"}}{{localize "MaterialDeck.Type"}}{{localize "MaterialDeck.Id"}}{{localize "MaterialDeck.Perm.ENABLE.ENABLE.label"}}
{{d.name}}{{d.type}}{{d.name}}{{d.type}}
- - -
\ No newline at end of file diff --git a/templates/downloadUtility.html b/templates/downloadUtility.html index 0e82b36..ffea305 100644 --- a/templates/downloadUtility.html +++ b/templates/downloadUtility.html @@ -1,56 +1,36 @@
- -

{{localize "MaterialDeck.DownloadUtility.Plugin"}}

- - - - - - + + + + + + - - - - - - + + + + + - - - - - + + + + - @@ -71,14 +51,14 @@
{{localize "MaterialDeck.DownloadUtility.Current"}}{{localize "MaterialDeck.DownloadUtility.Minimum"}}{{localize "MaterialDeck.DownloadUtility.Latest"}}{{localize "MaterialDeck.DownloadUtility.OS"}}{{localize "MaterialDeck.DownloadUtility.Download"}}{{localize "MaterialDeck.DownloadUtility.Current"}}{{localize "MaterialDeck.DownloadUtility.Minimum"}}{{localize "MaterialDeck.DownloadUtility.Latest"}}{{localize "MaterialDeck.DownloadUtility.OS"}}{{localize "MaterialDeck.DownloadUtility.Download"}}
{{localize "MaterialDeck.DownloadUtility.SDplugin"}}{{localSdVersion}}{{minimumSdVersion}}{{masterSdVersion}} - {{localize "MaterialDeck.DownloadUtility.SDplugin"}}{{localSdVersion}}{{minimumSdVersion}}{{masterSdVersion}} +
{{localize "MaterialDeck.DownloadUtility.MSserver"}}{{localMsVersion}}{{minimumMsVersion}}{{masterMsVersion}} - {{localize "MaterialDeck.DownloadUtility.MSserver"}}{{localMsVersion}}{{minimumMsVersion}}{{masterMsVersion}} + - +
- +
{{#each profiles as |p|}}
- @@ -87,7 +67,7 @@
{{localize "MaterialDeck.DownloadUtility.Name"}}{{localize "MaterialDeck.DownloadUtility.Download"}}{{localize "MaterialDeck.DownloadUtility.Download"}}
{{p.label}} - +
- diff --git a/templates/exportDialog.html b/templates/exportDialog.html index 3f3511b..7e81b08 100644 --- a/templates/exportDialog.html +++ b/templates/exportDialog.html @@ -3,7 +3,7 @@
- +
-

+

{{localize "MaterialDeck.Perm.MACRO.label"}}: {{macroRange}} {{localize "MaterialDeck.Of"}} {{totalMacros}}

- @@ -28,12 +15,12 @@ {{#each macroData}}
{{#each this.dataThis}} -
+
{{localize "MaterialDeck.Macro"}} {{this.iteration}}
- {{#select this.macro}} {{#each ../../macros}} @@ -44,30 +31,30 @@
{{#if ../../furnace}} - + {{/if}} -
+
- +
{{/each}}
{{/each}}
- diff --git a/templates/playlistConfig.html b/templates/playlistConfig.html index 4559173..f5aedf3 100644 --- a/templates/playlistConfig.html +++ b/templates/playlistConfig.html @@ -4,7 +4,7 @@
- {{#select playMode}} @@ -14,7 +14,7 @@
- +

{{localize "MaterialDeck.Playlists"}}

@@ -22,7 +22,7 @@ {{#each playlistData}}
- {{#select this.playlist}} {{#each ../playlists}} @@ -31,7 +31,7 @@ {{/select}}   - {{#select this.playlistMode}} diff --git a/templates/soundboardConfig.html b/templates/soundboardConfig.html index cf52618..dd3eb1a 100644 --- a/templates/soundboardConfig.html +++ b/templates/soundboardConfig.html @@ -1,25 +1,13 @@ - - - @@ -28,20 +16,20 @@ {{#each soundData}}
{{#each this.dataThis}} -
+
{{localize "MaterialDeck.Sound"}} {{this.iteration}}
{{localize "MaterialDeck.Name"}}
- +
{{localize "MaterialDeck.Playlist"}}
- {{#select this.selectedPlaylist}} {{#each ../../playlists}} @@ -53,8 +41,8 @@
{{localize "MaterialDeck.Sound"}}
-
- {{#select this.sound}} {{#each sounds}} @@ -63,11 +51,11 @@ {{/select}}
-
+
- +
{{localize "MaterialDeck.Icon"}} @@ -76,18 +64,18 @@ - +
- + - +
- {{#select this.mode}} @@ -97,7 +85,7 @@
- +
@@ -105,18 +93,18 @@
{{/each}}
- diff --git a/templates/userPermissionConfig.html b/templates/userPermissionConfig.html index db9aef2..a845543 100644 --- a/templates/userPermissionConfig.html +++ b/templates/userPermissionConfig.html @@ -1,98 +1,49 @@ - - - -

{{localize "MaterialDeck.Perm.Instructions"}}


{{ localize "MaterialDeck.Perm.ENABLE.label" }}

-
- +
+ {{#each roles as |rl r|}} {{/each}}
+
  • + +
    + {{#each enable as |r|}} + + {{/each}} +
    +

    {{ localize "MaterialDeck.Perm.ENABLE.ENABLE.hint" }}

    +
  • -
  • - -
    - {{#each enable as |r|}} - - {{/each}} -
    -

    {{ localize "MaterialDeck.Perm.ENABLE.ENABLE.hint" }}

    -
  • - - {{#each actions as |a|}}

    {{ localize a.label }}

    -
    - +
    + {{#each ../roles as |rl r|}} {{/each}}
    -
      +
        {{#each a.permissions as |p|}} -
      • - +
      • +
        {{#each p.roles as |r|}} - + {{/each}}
        -

        {{ localize p.hint }}

        +

        {{ localize p.hint }}

      • {{/each}}