From 0112e029d998e62f3e043d0699c9529265061fbf Mon Sep 17 00:00:00 2001 From: CDeenen Date: Tue, 10 Nov 2020 01:52:58 +0100 Subject: [PATCH] v0.8.2 --- MaterialDeck.js | 347 ++++++++++++++++++++++ README.md | 0 changelog.md | 0 img/.thumb/Black.png.jpg | Bin 0 -> 3346 bytes img/.thumb/transparant.png.jpg | Bin 0 -> 3364 bytes img/Black.png | Bin 0 -> 1090 bytes img/transparant.png | Bin 0 -> 1573 bytes lang/en.json | 39 +++ module.json | 23 ++ src/combattracker.js | 194 +++++++++++++ src/macro.js | 215 ++++++++++++++ src/misc.js | 409 ++++++++++++++++++++++++++ src/move.js | 85 ++++++ src/othercontrols.js | 501 ++++++++++++++++++++++++++++++++ src/playlist.js | 224 ++++++++++++++ src/settings.js | 97 +++++++ src/soundboard.js | 161 ++++++++++ src/streamDeck.js | 297 +++++++++++++++++++ src/token.js | 117 ++++++++ templates/macroConfig.html | 45 +++ templates/playlistConfig.html | 36 +++ templates/soundboardConfig.html | 84 ++++++ 22 files changed, 2874 insertions(+) create mode 100644 MaterialDeck.js create mode 100644 README.md create mode 100644 changelog.md create mode 100644 img/.thumb/Black.png.jpg create mode 100644 img/.thumb/transparant.png.jpg create mode 100644 img/Black.png create mode 100644 img/transparant.png create mode 100644 lang/en.json create mode 100644 module.json create mode 100644 src/combattracker.js create mode 100644 src/macro.js create mode 100644 src/misc.js create mode 100644 src/move.js create mode 100644 src/othercontrols.js create mode 100644 src/playlist.js create mode 100644 src/settings.js create mode 100644 src/soundboard.js create mode 100644 src/streamDeck.js create mode 100644 src/token.js create mode 100644 templates/macroConfig.html create mode 100644 templates/playlistConfig.html create mode 100644 templates/soundboardConfig.html diff --git a/MaterialDeck.js b/MaterialDeck.js new file mode 100644 index 0000000..f5c063f --- /dev/null +++ b/MaterialDeck.js @@ -0,0 +1,347 @@ +import {registerSettings} from "./src/settings.js"; +import {StreamDeck} from "./src/streamDeck.js"; +import {TokenControl} from "./src/token.js"; +import {Move} from "./src/move.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"; +export var streamDeck; +export var tokenControl; +var move; +export var macroControl; +var combatTracker; +var playlistControl; +var soundboard; +var otherControls; + +export const moduleName = "MaterialDeck"; +export var selectedTokenId; + +let ready = false; +//CONFIG.debug.hooks = true; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Global variables +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +var enableModule; + +//Websocket variables +let ip = "localhost"; //Ip address of the websocket server +let port = "3003"; //Port of the websocket server +var ws; //Websocket variable +let wsOpen = false; //Bool for checking if websocket has ever been opened => changes the warning message if there's no connection +let wsInterval; //Interval timer to detect disconnections +let WSconnected = false; + +/* + * Analyzes the message received + * + * @param {*} msg Message received + */ +async function analyzeWSmessage(msg,passthrough = false){ + if (enableModule == false) return; + const data = JSON.parse(msg); + if (data == undefined || data.payload == undefined) return; + console.log(data); + const action = data.action; + const event = data.event; + const context = data.context; + const coordinates = data.payload.coordinates; + if (coordinates == undefined) coordinates = 0; + const settings = data.payload.settings; + + + if (data.data == 'init'){ + + } + if (event == 'willAppear' || event == 'didReceiveSettings'){ + streamDeck.setScreen(action); + streamDeck.setContext(action,context,coordinates,settings); + + if (action == 'token'){ + if (selectedTokenId != undefined) + tokenControl.update(selectedTokenId); + } + else if (action == 'macro') + macroControl.update(settings,context); + else if (action == 'combattracker') + combatTracker.update(settings,context); + else if (action == 'playlist') + playlistControl.update(settings,context); + else if (action == 'soundboard') + soundboard.update(settings,context); + else if (action == 'other') + otherControls.update(settings,context); + } + + else if (event == 'willDisappear'){ + streamDeck.clearContext(action,coordinates); + } + + else if (event == 'keyDown'){ + if (action == 'token') + tokenControl.keyPress(settings); + else if (action == 'move') + move.keyPress(settings); + else if (action == 'macro') + macroControl.keyPress(settings); + else if (action == 'combattracker') + combatTracker.keyPress(settings,context); + else if (action == 'playlist') + playlistControl.keyPress(settings,context); + else if (action == 'soundboard'){ + soundboard.keyPressDown(settings); + } + } + + else if (event == 'keyUp'){ + if (action == 'soundboard'){ + soundboard.keyPressUp(settings); + } + else if (action == 'other') + otherControls.keyPress(settings); + } +}; + +/** + * Start a new websocket + * Start a 10s interval, if no connection is made, run resetWS() + * If connection is made, set interval to 1.5s to check for disconnects + * If message is received, reset the interval, and send the message to analyzeWSmessage() + */ +function startWebsocket() { + //ip = localhost; + ws = new WebSocket('ws://'+ip+':'+port+'/1'); + + ws.onmessage = function(msg){ + //console.log(msg); + + analyzeWSmessage(msg.data); + clearInterval(wsInterval); + wsInterval = setInterval(resetWS, 5000); + } + + ws.onopen = function() { + WSconnected = true; + ui.notifications.info("Material Deck Connected: "+ip+':'+port); + wsOpen = true; + let msg = { + type: "Foundry" + } + ws.send(JSON.stringify(msg)); + clearInterval(wsInterval); + wsInterval = setInterval(resetWS, 5000); + } + + clearInterval(wsInterval); + wsInterval = setInterval(resetWS, 10000); +} + +/** + * Try to reset the websocket if a connection is lost + */ +function resetWS(){ + if (wsOpen) ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.Disconnected")); + else ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.ConnectFail")); + WSconnected = false; + startWebsocket(); +} + +export function sendWS(txt){ + if (WSconnected) + ws.send(txt); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Hooks +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Ready hook + * Attempt to open the websocket + */ +Hooks.once('ready', ()=>{ + enableModule = (game.settings.get(moduleName,'Enable')) ? true : false; + if (enableModule == false) return; + + game.socket.on(`module.MaterialDeck`, (payload) =>{ + //console.log(payload); + if (payload.msgType != "playSound") return; + playTrack(payload.trackNr,payload.play,payload.repeat,payload.volume); + }); + + if (game.user.isGM == false) { + ready = true; + return; + } + + startWebsocket(); + + streamDeck = new StreamDeck(); + tokenControl = new TokenControl(); + move = new Move(); + macroControl = new MacroControl(); + combatTracker = new CombatTracker(); + playlistControl = new PlaylistControl(); + soundboard = new SoundboardControl(); + otherControls = new OtherControls(); + + + let soundBoardSettings = game.settings.get(moduleName,'soundboardSettings'); + let macroSettings = game.settings.get(moduleName, 'macroSettings'); + let array = []; + for (let i=0; i<64; i++) array[i] = ""; + let arrayVolume = []; + for (let i=0; i<64; i++) arrayVolume[i] = "50"; + let arrayZero = []; + for (let i=0; i<64; i++) arrayZero[i] = "0"; + + if (macroSettings.color == undefined){ + game.settings.set(moduleName,'macroSettings',{ + macros: array, + color: arrayZero + }); + } + if (soundBoardSettings.colorOff == undefined){ + game.settings.set(moduleName,'soundboardSettings',{ + playlist: "", + sounds: array, + colorOn: arrayZero, + colorOff: arrayZero, + mode: arrayZero, + toggle: arrayZero, + volume: arrayVolume + }); + } + +}); + +export function playTrack(soundNr,play,repeat,volume){ + if (play){ + let trackId = game.settings.get(moduleName,'soundboardSettings').sounds[soundNr]; + let playlistId = game.settings.get(moduleName,'soundboardSettings').playlist; + let sounds = game.playlists.entities.find(p => p._id == playlistId).data.sounds; + let sound = sounds.find(p => p._id == trackId); + if (sound == undefined){ + activeSounds[soundNr] = false; + return; + } + volume *= game.settings.get("core", "globalInterfaceVolume"); + let src = sound.path; + + let howl = new Howl({src, volume, loop: repeat, onend: (id)=>{ + if (repeat == false){ + activeSounds[soundNr] = false; + } + }, + onstop: (id)=>{ + activeSounds[soundNr] = false; + }}); + howl.play(); + activeSounds[soundNr] = howl; + } + else { + activeSounds[soundNr].stop(); + } +} + +Hooks.on('updateToken',(scene,token)=>{ + if (enableModule == false || ready == false) return; + let tokenId = token._id; + if (tokenId == selectedTokenId) + tokenControl.update(selectedTokenId); +}); + +Hooks.on('controlToken',(token,controlled)=>{ + if (enableModule == false || ready == false) return; + if (controlled) { + selectedTokenId = token.data._id; + } + else { + selectedTokenId = undefined; + } + tokenControl.update(selectedTokenId); +}); + +Hooks.on('renderHotbar', (hotbar)=>{ + if (enableModule == false || ready == false) return; + macroControl.hotbar(hotbar.macros); +}); + +Hooks.on('renderCombatTracker',()=>{ + if (enableModule == false || ready == false) return; + combatTracker.updateAll(); +}); + +Hooks.on('renderPlaylistDirectory', (playlistDirectory)=>{ + if (enableModule == false || ready == false) return; + playlistControl.updateAll(); +}); + +Hooks.on('closeplaylistConfigForm', (form)=>{ + if (enableModule == false || ready == false) return; + if (form.template == "./modules/MaterialDeck/templates/playlistConfig.html") + playlistControl.updateAll(); +}); + +Hooks.on('pauseGame',()=>{ + if (enableModule == false || ready == false) return; + otherControls.updateAll(); +}); + +Hooks.on('renderSidebarTab',()=>{ + if (enableModule == false || ready == false) return; + otherControls.updateAll(); +}); + +Hooks.on('updateScene',()=>{ + if (enableModule == false || ready == false) return; + otherControls.updateAll(); +}); + +Hooks.on('renderSceneControls',()=>{ + if (enableModule == false || ready == false) return; + otherControls.updateAll(); +}); + + Hooks.once('init', ()=>{ + //CONFIG.debug.hooks = true; + registerSettings(); //in ./src/settings.js +}); + +Hooks.once('canvasReady',()=>{ + ready = true; +}); + +export function getFromJSONArray(data,i){ + if (i>9) return 'nul'; + let val; + if (i == 0) val = data.a; + else if (i == 1) val = data.a; + else if (i == 2) val = data.c; + else if (i == 3) val = data.d; + else if (i == 4) val = data.e; + else if (i == 5) val = data.f; + else if (i == 6) val = data.g; + else if (i == 7) val = data.h; + else if (i == 8) val = data.i; + return val; +} + +export function setToJSONArray(data,i,val){ + if (i>9) return 'nul'; + if (i == 0) data.a = val; + else if (i == 1) data.b = val; + else if (i == 2) data.c = val; + else if (i == 3) data.d = val; + else if (i == 4) data.e = val; + else if (i == 5) data.f = val; + else if (i == 6) data.g = val; + else if (i == 7) data.h = val; + else if (i == 8) data.i = val; +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..e69de29 diff --git a/img/.thumb/Black.png.jpg b/img/.thumb/Black.png.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a45f2e5ad5fa8ce3db1a27a8a93ce095497a37e GIT binary patch literal 3346 zcmex=>Qk2+>8v&5IGalw)8KWEE00bYv3_Ok`Io6ftU?xR68HY2!iBpoX!XqN1l2cOC(lau%ic3n%$}1|X znp;}i+B-VCCQY6)b=ve9GiNPYykzOJeA&a zSFc^aar4&0M~|O8efIpt%U2&ieg5+G+xH(oe+dGe%*4XN%)$7U4R$nfZf+vW(Qr4zo&5g* zgCGZ^1ET{oqYwj=5CgLi!~a_hJj{#?OoGgU4E78^|8+4iXe?l08l^^qU^EqsW`xnQ ZV6-?Ktr13R!_nGsv^E@$wIToin*iEO%8&p6 literal 0 HcmV?d00001 diff --git a/img/.thumb/transparant.png.jpg b/img/.thumb/transparant.png.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8972afeb1edab915aa374b89b0ce33bda5bfaae1 GIT binary patch literal 3364 zcmex=#U-U>$dGX zcJ4ZK_{h;?$4{I*b?NeztJkjIxOwa0qsLF4K70P+<*SdMK7aZ8?fZ|PzXX9!W@2Gs zW?=_8nUSd+h?xahSQQP~gd79e6AOivj2byaoF*>Zc#u=sIOv0DQqe^&F%^@CsvkkF z20NNKH#d>xXt+K~VMO#oH;&7%MS literal 0 HcmV?d00001 diff --git a/img/Black.png b/img/Black.png new file mode 100644 index 0000000000000000000000000000000000000000..b26e6886f49db732a725d5f298b739c955cb6bcc GIT binary patch literal 1090 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{oSrU@Ar*7po>OFGFyLX? z_{)6nNsAAR4|ndaW|(Nh@a7crfpw$QXb_C1g5jMJdVbyeeeQnqL}oQ$*}&lG>gTe~ HDWM4fo_hZn literal 0 HcmV?d00001 diff --git a/img/transparant.png b/img/transparant.png new file mode 100644 index 0000000000000000000000000000000000000000..b2bd0dea9751c7dd3607c4eccbcf647e9ea85955 GIT binary patch literal 1573 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe)8TQZ%U13aCb6$*;-(=u~X z85lGs)=sqbIP4&EG(Py~qN!YE0);06=IV5GS$K(dCoJ3|8dYkr^9%PZ6BSLpzNYMh z2m6n%YVO{=j&EI)!VktrPhKo}p``jxUvo>x#|Jy)-@mhccaPC&@u85-*-QbZr#+4) zCz);6`nG2gKO>Jq(}W{FwiDyd94M~2d-(YE-5TdV*?oWhBK(sYbB)qRnajN070nfC zmoHhq7jBRA4C*-Fd7O8b!bj!nsnhRUh|7vqq}@2N(f}} z_S5NFYU^jQ^sPb3YpXwrvwgnhHS4vByTEZFArGgP9;s6+4meE->lG2!ywzX%qg}Rs zQjF2gQwdKZ97MHLj`ROx-@n_GJ9u(lkO5oC{u|$pYh2jOZSs2C-`bqpXN3>^=ZVS8 zt>4OcrfyPPe)dFVg)O`>G22Zm`FGr5Y@1hda)QiB|6mP~hVRC|mdO}YaDQ31bMMw~ zg})e5R;+$1{osLsgm%!kUB`Db&)xgCdfCZ0v%n*=n1Ml08H5=t zmfDvA1=&kHeO=j~aB~Q#S+LA60*Ww5mbgZgIOpf)rskC}I2WZRmZYXAlxLP?D7bt2 z281{Ai8C;;@O!#AhE&{od(e;(sO8Xtul~90`3peWN5NU P2Kn35)z4*}Q$iB}5(DRs literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..2d545a4 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,39 @@ +{ + "MaterialDeck.Notifications.Disconnected": "Disconnected from mdServer, attempting to reconnect", + "MaterialDeck.Notifications.ConnectFail": "Can't connect to mdServer, retrying", + + "MaterialDeck.Sett.PlaylistConfig": "Playlist Configuration", + "MaterialDeck.Sett.SoundboardConfig": "Soundboard Configuration", + "MaterialDeck.Sett.MacroConfig": "Macro Configuration", + "MaterialDeck.Sett.Brightness": "Brightness", + "MaterialDeck.Sett.Brightness_Hint": "LED brightness level", + "MaterialDeck.Sett.Enable": "Enable module", + + "MaterialDeck.Playlist.Clear": "Clear data", + "MaterialDeck.Playlist.Playmethod.Header": "Play Method", + "MaterialDeck.Playlist.Playmethod.Label": "Method", + "MaterialDeck.Playlist.Playmethod.Unrestricted": "Unrestricted", + "MaterialDeck.Playlist.Playmethod.OneTrackPlaylist": "One track per playlist", + "MaterialDeck.Playlist.Playmethod.OneTrackTotal": "One track in total", + "MaterialDeck.Playlist.Playlists": "Playlists", + "MaterialDeck.Playlist.Playlist": "Playlist", + "MaterialDeck.Playlist.None": "None", + "MaterialDeck.Playlist.Save": "Save", + "MaterialDeck.Soundboard.Volume": "Volume", + + "MaterialDeck.Soundboard.Sound": "Sound", + "MaterialDeck.Soundboard.Icon": "Icon", + "MaterialDeck.Soundboard.Playback": "Playback", + "MaterialDeck.Soundboard.Once": "Once", + "MaterialDeck.Soundboard.Repeat": "Repeat", + "MaterialDeck.Soundboard.Hold": "Hold", + "MaterialDeck.Soundboard.On": "On", + "MaterialDeck.Soundboard.Off": "Off", + + "MaterialDeck.Macro.Macro": "Macro", + "MaterialDeck.Macro.Background": "Background", + "MaterialDeck.Macro.FurnaceArgs": "Furnace arguments", + + "MaterialDeck.Name": "Name" +} + diff --git a/module.json b/module.json new file mode 100644 index 0000000..e045d0c --- /dev/null +++ b/module.json @@ -0,0 +1,23 @@ +{ + "name": "MaterialDeck", + "title": "Material Deck", + "description": "", + "version": "0.8.2", + "author": "CDeenen", + "esmodules": [ + "./MaterialDeck.js" + ], + "socket": true, + "minimumCoreVersion": "0.7.5", + "compatibleCoreVersion": "0.7.6", + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "url": "https://github.com/CDeenen/MaterialDeck", + "manifest": "", + "download": "" + } \ No newline at end of file diff --git a/src/combattracker.js b/src/combattracker.js new file mode 100644 index 0000000..2667a0b --- /dev/null +++ b/src/combattracker.js @@ -0,0 +1,194 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck, tokenControl} from "../MaterialDeck.js"; + +export class CombatTracker{ + constructor(){ + this.combatantLength = 0; + } + + async updateAll(){ + + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'combattracker') continue; + await this.update(data.settings,data.context); + } + } + + update(settings,context){ + let ctFunction = settings.combatTrackerFunction; + if (ctFunction == undefined) ctFunction == 0; + + + let combat = game.combat; + let src = "action/images/black.png"; + let txt = ""; + let background = "#000000"; + let mode = settings.combatTrackerMode; + if (mode == undefined) mode = 0; + + if (mode == 0){ + if (combat != null && combat != undefined && combat.turns.length != 0){ + let initiativeOrder = combat.turns; + let nr = settings.combatantNr - 1; + if (nr == undefined || nr < 1) nr = 0; + let combatantState = 1; + if (nr == combat.turn) combatantState = 2; + let combatant = initiativeOrder[nr] + + if (combatant != undefined){ + let tokenId = combatant.tokenId; + tokenControl.pushData(tokenId,settings,context,combatantState,'#cccc00'); + return; + } + else { + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(txt,context); + } + + } + else { + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(txt,context); + } + } + else if (mode == 1){ + if (combat != null && combat != undefined && combat.started){ + let tokenId = combat.combatant.tokenId; + tokenControl.pushData(tokenId,settings,context); + } + else { + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(txt,context); + } + + + } + else if (mode == 2){ + + if (ctFunction == 0) { + if (combat == null || combat == undefined || combat.combatants.length == 0) { + src = "action/images/combattracker/startcombat.png"; + background = "#000000"; + } + else { + if (combat.started == false) { + src = "action/images/combattracker/startcombat.png"; + background = "#008000"; + } + else { + src = "action/images/combattracker/stopcombat.png"; + background = "#FF0000"; + } + } + } + else if (ctFunction == 1) { + src = "action/images/combattracker/nextturn.png"; + } + else if (ctFunction == 2) { + src = "action/images/combattracker/previousturn.png"; + } + else if (ctFunction == 3) { + src = "action/images/combattracker/nextround.png"; + } + else if (ctFunction == 4) { + src = "action/images/combattracker/previousround.png"; + } + else if (ctFunction == 5){ + src = "action/images/black.png"; + let round = 0; + let turn = 0; + if (combat != null && combat != undefined && combat.started != false){ + round = combat.round; + turn = combat.turn+1; + } + if (settings.displayRound) txt += "Round\n"+round; + if (txt != "") txt += "\n"; + if (settings.displayTurn) txt += "Turn\n"+turn; + } + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(txt,context); + } + } + + keyPress(settings,context){ + let mode = parseInt(settings.combatTrackerMode); + if (isNaN(mode)) mode = 0; + + if (mode < 2) { + let onClick = settings.onClick; + if (onClick == undefined) onClick = 0; + let tokenId; + let combat = game.combat; + if (mode == 0) { + if (combat != null && combat != undefined && combat.turns.length != 0){ + let initiativeOrder = combat.turns; + let nr = settings.combatantNr - 1; + if (nr == undefined || nr < 1) nr = 0; + let combatantState = 1; + if (nr == combat.turn) combatantState = 2; + let combatant = initiativeOrder[nr] + + if (combatant == undefined) return; + tokenId = combatant.tokenId; + } + } + else if (mode == 1) + if (combat != null && combat != undefined && combat.started) + tokenId = combat.combatant.tokenId; + + let token = canvas.tokens.children[0].children.find(p => p.id == tokenId); + if (token == undefined) return; + if (onClick == 0) //Do nothing + return; + else if (onClick == 1){ //select token + token.control(); + } + else if (onClick == 2){ //center on token + let location = token.getCenter(token.x,token.y); + canvas.animatePan(location); + } + else if (onClick == 3){ //center on token and select + let location = token.getCenter(token.x,token.y); + canvas.animatePan(location); + token.control(); + } + else if (onClick == 4){ //Open character sheet + token.actor.sheet.render(true); + } + else { //Open token config + token.sheet._render(true); + } + } + else if (mode == 2){ + let combat = game.combat; + if (combat == null || combat == undefined) return; + + let ctFunction = settings.combatTrackerFunction; + if (ctFunction == undefined) ctFunction == 0; + + if (ctFunction == 0){ + let src; + let background; + if (game.combat.started){ + game.combat.endCombat(); + src = "action/images/combattracker/startcombat.png"; + background = "#000000"; + } + else { + game.combat.startCombat(); + src = "action/images/combattracker/stopcombat.png"; + background = "#FF0000"; + } + streamDeck.setIcon(context,src,background); + return; + } + if (game.combat.started == false) return; + + if (ctFunction == 1) game.combat.nextTurn(); + else if (ctFunction == 2) game.combat.previousTurn(); + else if (ctFunction == 3) game.combat.nextRound(); + else if (ctFunction == 4) game.combat.previousRound(); + } + } +} \ No newline at end of file diff --git a/src/macro.js b/src/macro.js new file mode 100644 index 0000000..e877e35 --- /dev/null +++ b/src/macro.js @@ -0,0 +1,215 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck} from "../MaterialDeck.js"; + +export class MacroControl{ + constructor(){ + this.offset = 0; + } + + async updateAll(){ + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'macro') continue; + await this.update(data.settings,data.context); + } + } + + update(settings,context){ + let mode = settings.macroMode; + let displayName = settings.displayName; + let macroNumber = settings.macroNumber; + let background = settings.background; + if(macroNumber == undefined || isNaN(parseInt(macroNumber))){ + macroNumber = 0; + } + if (mode == undefined) mode = 0; + if (displayName == undefined) displayName = false; + if (background == undefined) background = '#000000'; + + macroNumber = parseInt(macroNumber); + + //Macro Hotbar + if (mode < 2){ + let macroId + if (mode == 0) macroId = game.user.data.hotbar[macroNumber]; + else { + let macros = game.macros.apps[0].macros; + for (let j=0; j<10; j++){ + if (macros[j].key == macroNumber){ + if (macros[j].macro == null) macroId == undefined; + else macroId = macros[j].macro._id; + } + } + } + let src = ""; + let name = ""; + + if (macroId != undefined){ + let macro = game.macros._source.find(p => p._id == macroId); + if (macro != undefined) { + name += macro.name; + src += macro.img; + } + } + + streamDeck.setIcon(1,context,src,background); + if (displayName == 0) name = ""; + streamDeck.setTitle(name,context); + } + else { + let name = ""; + let src = ''; + if (settings.macroBoardMode == 0) { + macroNumber += this.offset - 1; + if (macroNumber < 0) macroNumber = 0; + var macroId = game.settings.get(MODULE.moduleName,'macroSettings').macros[macroNumber]; + background = game.settings.get(MODULE.moduleName,'macroSettings').color[macroNumber]; + + if (background == undefined) background = '#000000'; + src = ""; + if (macroId != undefined){ + let macro = game.macros._source.find(p => p._id == macroId); + if (macro != undefined) { + name += macro.name; + src += macro.img; + } + } + } + else { + src = ""; + let onBackground = settings.onBackground; + if (onBackground == undefined) onBackground = '#00FF00'; + let offBackground = settings.offBackground; + if (offBackground == undefined) offBackground = '#000000'; + let macroOffset = parseInt(settings.macroOffset); + if (macroOffset == undefined || isNaN(macroOffset)) macroOffset = 0; + + if (macroOffset == parseInt(this.offset)) background = onBackground; + else background = offBackground; + } + streamDeck.setIcon(1,context,src,background); + if (displayName == 0) name = ""; + streamDeck.setTitle(name,context); + } + } + + hotbar(macros){ + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'macro') continue; + let context = data.context; + let mode = data.settings.macroMode; + let displayName = data.settings.displayName; + let macroNumber = data.settings.macroNumber; + let background = data.settings.background; + let src = ""; + let name = ""; + if(macroNumber == undefined || isNaN(parseInt(macroNumber))){ + macroNumber = 1; + } + if (mode == undefined) mode = 0; + if (mode == 2) continue; + if (displayName == undefined) displayName = false; + if (background == undefined) background = '#000000'; + + let macroId; + if (mode == 0){ + macroId = game.user.data.hotbar[macroNumber]; + } + else { + for (let j=0; j<10; j++){ + if (macros[j].key == macroNumber){ + if (macros[j].macro == null) macroId == undefined; + else macroId = macros[j].macro._id; + } + } + } + let macro = undefined; + if (macroId != undefined) macro = game.macros._source.find(p => p._id == macroId); + if (macro != undefined && macro != null) { + name += macro.name; + src += macro.img; + } + streamDeck.setIcon(1,context,src,background); + if (displayName == 0) name = ""; + streamDeck.setTitle(name,context); + } + } + + keyPress(settings){ + let mode = settings.macroMode; + if (mode == undefined) mode = 0; + + if (mode == 0 || mode == 1){ + let macroNumber = settings.macroNumber; + if(macroNumber == undefined || isNaN(parseInt(macroNumber))){ + macroNumber = 0; + } + this.executeHotbar(macroNumber,mode); + } + else { + if (settings.macroBoardMode == 0) + this.executeBoard(macroNumber); + else { + let macroOffset = settings.macroOffset; + if (macroOffset == undefined) macroOffset = 0; + this.offset = macroOffset; + this.updateAll(); + } + } + } + + executeHotbar(macroNumber,mode){ + let macroId + if (mode == 0) macroId = game.user.data.hotbar[macroNumber]; + else { + let macros = game.macros.apps[0].macros; + for (let j=0; j<10; j++){ + if (macros[j].key == macroNumber){ + if (macros[j].macro == null) macroId == undefined; + else macroId = macros[j].macro._id; + } + } + } + if (macroId == undefined) return; + let macro = game.macros.get(macroId); + macro.execute(); + } + + executeBoard(macroNumber){ + macroNumber = parseInt(macroNumber); + macroNumber += this.offset - 1; + if (macroNumber < 0) macroNumber = 0; + var macroId = game.settings.get(MODULE.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,'macroArgs')[macroNumber]; + let furnaceEnabled = false; + let furnace = game.modules.get("furnace"); + if (furnace != undefined && furnace.active) furnaceEnabled = true; + if (args == "") furnaceEnabled = false; + if (furnaceEnabled == false) macro.execute(); + else { + let chatData = { + user: game.user._id, + speaker: ChatMessage.getSpeaker(), + content: "/'" + macro.name + "' " + args + }; + ChatMessage.create(chatData, {}); + } + } + } + } +} + + + + + + + + + + diff --git a/src/misc.js b/src/misc.js new file mode 100644 index 0000000..11261b5 --- /dev/null +++ b/src/misc.js @@ -0,0 +1,409 @@ +import * as MODULE from "../MaterialDeck.js"; +import {macroControl} from "../MaterialDeck.js"; + +export class playlistConfigForm extends FormApplication { + constructor(data, options) { + super(data, options); + this.data = data; + } + + /** + * Default Options for this FormApplication + */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + id: "playlist-config", + title: "Material Deck: Playlist Config", + template: "./modules/MaterialDeck/templates/playlistConfig.html", + classes: ["sheet"], + width: 500 + }); + } + + /** + * Provide data to the template + */ + getData() { + const selectedPlaylists = game.settings.get(MODULE.moduleName,'selectedPlaylists'); + let playlistData = {}; + + for (let i=0; i<9; i++){ + let playlist; + playlist = MODULE.getFromJSONArray(selectedPlaylists,i); + + let dataThis = { + iteration: i+1, + playlist: selectedPlaylists[i], + playlists: game.playlists.entities + } + MODULE.setToJSONArray(playlistData,i,dataThis); + } + + if (!this.data && selectedPlaylists) { + this.data = selectedPlaylists; + } + return { + playlists: game.playlists.entities, + playlistData: playlistData, + playMethod: game.settings.get(MODULE.moduleName,'playlistMethod') + } + } + + /** + * Update on form submit + * @param {*} event + * @param {*} formData + */ + async _updateObject(event, formData) { + await game.settings.set(MODULE.moduleName,'selectedPlaylists', formData["selectedPlaylist"]); + await game.settings.set(MODULE.moduleName,'playlistMethod',formData["playMethod"]); + + + } + + activateListeners(html) { + super.activateListeners(html); + + + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export class macroConfigForm extends FormApplication { + constructor(data, options) { + super(data, options); + this.data = data; + } + + /** + * Default Options for this FormApplication + */ + static get defaultOptions() { + let streamDeckModel = game.settings.get(MODULE.moduleName,'streamDeckModel'); + let width; + if (streamDeckModel == 0) + width = 500; + else if (streamDeckModel == 1) + width= 800; + else + width = 1400; + + return mergeObject(super.defaultOptions, { + id: "macro-config", + title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.MacroConfig"), + template: "./modules/MaterialDeck/templates/macroConfig.html", + classes: ["sheet"], + width: width + }); + } + + /** + * Provide data to the template + */ + getData() { + var selectedMacros = game.settings.get(MODULE.moduleName,'macroSettings').macros; + var color = game.settings.get(MODULE.moduleName,'macroSettings').color; + var args = game.settings.get(MODULE.moduleName,'macroArgs'); + if (selectedMacros == undefined) selectedMacros = []; + if (color == undefined) color = []; + if (args == undefined) args = []; + let macroData = {}; + let furnaceEnabled = false; + let furnace = game.modules.get("furnace"); + if (furnace != undefined && furnace.active) furnaceEnabled = true; + let height = 95; + if (furnaceEnabled) height += 50; + + let streamDeckModel = game.settings.get(MODULE.moduleName,'streamDeckModel'); + let iMax,jMax; + if (streamDeckModel == 0){ + jMax = 6; + iMax = 3; + } + else if (streamDeckModel == 1){ + jMax = 6; + iMax = 5; + } + else { + jMax = 8; + iMax = 8; + } + + let iteration = 0; + for (let j=0; j15) + colorCorrect = false; + } + if (colorCorrect == false) colorThis = '#000000'; + } + else + colorThis = '#000000'; + + let dataThis = { + iteration: iteration+1, + macro: selectedMacros[iteration], + color: colorThis, + macros:game.macros, + args: args[iteration], + furnace: furnaceEnabled + } + MODULE.setToJSONArray(macroThis,i,dataThis); + iteration++; + } + let data = { + dataThis: macroThis, + }; + MODULE.setToJSONArray(macroData,j,data); + } + + return { + height: height, + macros: game.macros, + selectedMacros: selectedMacros, + macroData: macroData, + } + } + + /** + * Update on form submit + * @param {*} event + * @param {*} formData + */ + async _updateObject(event, formData) { + await game.settings.set(MODULE.moduleName,'macroSettings',{ + macros: formData["macros"], + color: formData["colorPicker"] + }); + + let furnace = game.modules.get("furnace"); + if (furnace != undefined && furnace.active) + await game.settings.set(MODULE.moduleName,'macroArgs', formData["args"]); + macroControl.updateAll(); + } + + activateListeners(html) { + super.activateListeners(html); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export class soundboardConfigForm extends FormApplication { + constructor(data, options) { + super(data, options); + this.data = data; + //this.soundData = {}; + this.playlist; + this.updatePlaylist = false; + } + + /** + * Default Options for this FormApplication + */ + static get defaultOptions() { + let streamDeckModel = game.settings.get(MODULE.moduleName,'streamDeckModel'); + let width; + if (streamDeckModel == 0) + width = 500; + else if (streamDeckModel == 1) + width= 800; + else + width = 1400; + + return mergeObject(super.defaultOptions, { + id: "soundboard-config", + title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.SoundboardConfig"), + template: "./modules/MaterialDeck/templates/soundboardConfig.html", + classes: ["sheet"], + width: width, + height: 720 + }); + } + + getArray(data){ + let array = [data.a,data.b,data.c,data.d,data.e,data.f,data.g,data.h]; + return array; + } + + /** + * Provide data to the template + */ + getData() { + let playlistId = game.settings.get(MODULE.moduleName,'soundboardSettings').playlist; + if (this.updatePlaylist) playlistId = this.playlist; + this.updatePlaylist = false; + let playlist = 'none'; + let sounds = []; + if (playlistId != undefined){ + playlist = game.playlists.entities.find(p => p._id == playlistId); + if (playlist != undefined) sounds = playlist.sounds; + else playlist = 'none'; + } + let selectedSounds = game.settings.get(MODULE.moduleName,'soundboardSettings').sounds; + let colorOn = game.settings.get(MODULE.moduleName,'soundboardSettings').colorOn; + let colorOff = game.settings.get(MODULE.moduleName,'soundboardSettings').colorOff; + let mode = game.settings.get(MODULE.moduleName,'soundboardSettings').mode; + let volume = game.settings.get(MODULE.moduleName,'soundboardSettings').volume; + let img = game.settings.get(MODULE.moduleName,'soundboardSettings').img; + let name = game.settings.get(MODULE.moduleName,'soundboardSettings').name; + + if (selectedSounds == undefined) selectedSounds = []; + if (colorOn == undefined) colorOn = []; + if (colorOff == undefined) colorOff = []; + if (mode == undefined) mode = []; + if (img == undefined) img = []; + if (name == undefined) name = []; + let soundData = {}; + + let streamDeckModel = game.settings.get(MODULE.moduleName,'streamDeckModel'); + let iMax,jMax; + if (streamDeckModel == 0){ + jMax = 6; + iMax = 3; + } + else if (streamDeckModel == 1){ + jMax = 6; + iMax = 5; + } + else { + jMax = 8; + iMax = 8; + } + + let iteration = 0; + + for (let j=0; j { + let target = event.currentTarget.value; + let color = document.getElementById("colorOn"+target).value; + if ((color < 0 && color > 127) || color == "") color = 0; + MODULE.launchpad.colorPicker(target,1,color); + + }); + colorPickerOff.on('click',(event) => { + let target = event.currentTarget.value; + let color = document.getElementById("colorOff"+target).value; + if ((color < 0 && color > 127) || color == "") color = 0; + MODULE.launchpad.colorPicker(target,0,color); + + }); + if (playlistSelect.length > 0) { + playlistSelect.on("change", event => { + this.playlist = event.target.value; + this.updatePlaylist = true; + this.render(); + }); + } + volumeSlider.on('change', event => { + let id = event.target.id.replace('volume',''); + let column = id%10-1; + let row = 8-Math.floor(id/10); + id = row*8+column; + let settings = game.settings.get(MODULE.moduleName,'soundboardSettings'); + settings.volume[id] = event.target.value; + game.settings.set(MODULE.moduleName,'soundboardSettings',settings); + if (MODULE.launchpad.activeSounds[id] != false){ + let volume = AudioHelper.inputToVolume(event.target.value/100) * game.settings.get("core", "globalInterfaceVolume"); + MODULE.launchpad.activeSounds[id].volume(volume); + } + }); + if (soundSelect.length > 0) { + soundSelect.on("change",event => { + let id = event.target.id.replace('soundSelect',''); + let column = id%10-1; + let row = 8-Math.floor(id/10); + id = row*8+column; + let settings = game.settings.get(MODULE.moduleName,'soundboardSettings'); + settings.sounds[id] = event.target.value; + game.settings.set(MODULE.moduleName,'soundboardSettings',settings); + if (MODULE.launchpad.activeSounds[id] != false){ + let mode = settings.mode[id]; + let repeat = false; + if (mode == 1) repeat = true; + MODULE.launchpad.playSound(id,repeat,false); + } + }); + } + */ + } + +} \ No newline at end of file diff --git a/src/move.js b/src/move.js new file mode 100644 index 0000000..516837b --- /dev/null +++ b/src/move.js @@ -0,0 +1,85 @@ +import * as MODULE from "../MaterialDeck.js"; + +export class Move{ + constructor(){ + } + + keyPress(settings){ + let dir = settings.dir; + let mode = settings.mode; + if (mode == undefined) mode = 0; + if (dir == undefined) dir = 0; + if (settings.mode == '1') + this.moveToken(MODULE.selectedTokenId,dir); + else + this.moveCanvas(dir); + } + + async moveToken(tokenId,dir){ + if (tokenId == undefined) return; + + const token = canvas.tokens.children[0].children.find(p => p.id == tokenId); + const gridSize = canvas.scene.data.grid; + let x = token.x; + let y = token.y; + + if (dir == '1') y -= gridSize; + else if (dir == '2') y += gridSize; + else if (dir == '3') x += gridSize; + else if (dir == '4') x -= gridSize; + else if (dir == '5') { + x += gridSize; + y -= gridSize; + } + else if (dir == '6') { + x -= gridSize; + y -= gridSize; + } + else if (dir == '7') { + x += gridSize; + y += gridSize; + } + else if (dir == '8') { + x -= gridSize; + y += gridSize; + } + else if (dir == '0') { + let location = token.getCenter(x,y); + canvas.animatePan(location); + } + if (game.user.isGM == false && (token.can(game.user,"control") == false || token.checkCollision(token.getCenter(x, y)))) return; + token.update({x:x,y:y}); + }; + + moveCanvas(dir){ + let viewPosition = canvas.scene._viewPosition; + const gridSize = canvas.scene.data.grid; + viewPosition.duration = 100; + + if (dir == '1') viewPosition.y -= gridSize; + else if (dir == '2') viewPosition.y += gridSize; + else if (dir == '3') viewPosition.x += gridSize; + else if (dir == '4') viewPosition.x -= gridSize; + else if (dir == '5') { + viewPosition.x += gridSize; + viewPosition.y -= gridSize; + } + else if (dir == '6') { + viewPosition.x -= gridSize; + viewPosition.y -= gridSize; + } + else if (dir == '7') { + viewPosition.x += gridSize; + viewPosition.y += gridSize; + } + else if (dir == '8') { + viewPosition.x -= gridSize; + viewPosition.y += gridSize; + } + else if (dir == '0') { + viewPosition.x = (canvas.dimensions.sceneWidth+window.innerWidth)/2; + viewPosition.y = (canvas.dimensions.sceneHeight+window.innerHeight)/2; + } + canvas.animatePan(viewPosition); + } +} \ No newline at end of file diff --git a/src/othercontrols.js b/src/othercontrols.js new file mode 100644 index 0000000..6ec8555 --- /dev/null +++ b/src/othercontrols.js @@ -0,0 +1,501 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck} from "../MaterialDeck.js"; + +export class OtherControls{ + constructor(){ + this.offset = 0; + } + + async updateAll(){ + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'other') continue; + await this.update(data.settings,data.context); + } + } + + update(settings,context){ + let mode = settings.otherMode; + if (mode == undefined) mode = 0; + + if (mode == 0) { //pause + this.updatePause(settings.pauseFunction,context); + } + else if (mode == 1) { //scene selection + this.updateScene(settings,context); + } + else if (mode == 2){ //control buttons + this.updateControl(settings,context); + } + else if (mode == 3){ //darkness + this.updateDarkness(settings,context); + } + else if (mode == 4){ //roll tables + this.updateRollTable(settings,context); + } + } + + keyPress(settings){ + let mode = settings.otherMode; + if (mode == undefined) mode = 0; + + if (mode == 0) { //pause + this.keyPressPause(settings.pauseFunction); + } + else if (mode == 1) { //scene + this.keyPressScene(settings); + } + else if (mode == 2) { //control buttons + this.keyPressControl(settings); + } + else if (mode == 3) { //darkness controll + this.keyPressDarkness(settings); + } + else if (mode == 4) { //roll tables + this.keyPressRollTable(settings); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + updatePause(pauseFunction,context){ + + if (pauseFunction == undefined) pauseFunction = 0; + let src = ""; + let name = ""; + let background = "#000000"; + if (pauseFunction == 0){ //Pause game + if (game.paused) background = "#00FF00" + src = 'action/images/other/pause/pause.png'; + } + else if (pauseFunction == 1){ //Resume game + if (game.paused == false) background = "#00FF00" + src = 'action/images/other/pause/resume.png'; + } + else if (pauseFunction == 2) { //toggle + if (game.paused == false) background = "#00FF00" + src = 'action/images/other/pause/playpause.png'; + } + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(name,context); + } + + keyPressPause(pauseFunction){ + if (pauseFunction == undefined) pauseFunction = 0; + if (pauseFunction == 0){ //Pause game + if (game.paused) return; + game.togglePause(); + } + else if (pauseFunction == 1){ //Resume game + if (game.paused == false) return; + game.togglePause(); + } + else if (pauseFunction == 2) { //toggle + game.togglePause(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + updateScene(settings,context){ + let func = settings.sceneFunction; + if (func == undefined) func = 0; + + if (func == 0){ //visible scenes + let nr = parseInt(settings.sceneNr); + if (isNaN(nr)) nr = 1; + nr--; + + let background = settings.background; + if (background == undefined) background = "#000000"; + + let scene = game.scenes.apps[0].scenes[nr]; + let name = ""; + let src = ""; + let ringColor = "#000000"; + let ring = 1; + + if (scene != undefined){ + if (scene.isView) { + ringColor = "#00FF00"; + ring = 2; + } + if (settings.displaySceneName) name = scene.name; + if (settings.displaySceneIcon) src = scene.img; + if (scene.active) name += "\n(Active)"; + } + streamDeck.setTitle(name,context); + streamDeck.setIcon(1, context,src,background,ring,ringColor); + } + else if (func == 1) { //all scenes + let name = settings.sceneName; + if (name == undefined) return; + + let background = settings.background; + if (background == undefined) background = "#000000"; + + let src = ""; + let ringColor = "#000000"; + let ring = 1; + let scene = game.scenes.apps[1].entities.find(p=>p.data.name == name); + name = ""; + if (scene != undefined){ + if (scene.isView) { + ringColor = "#00FF00"; + ring = 2; + } + if (settings.displaySceneName) name = scene.name; + if (settings.displaySceneIcon) src = scene.img; + if (scene.active) name += "\n(Active)"; + } + streamDeck.setTitle(name,context); + streamDeck.setIcon(1, context,src,background,ring,ringColor); + } + } + + keyPressScene(settings){ + let func = settings.sceneFunction; + if (func == undefined) func = 0; + if (func == 0){ //visible scenes + let viewFunc = settings.sceneViewFunction; + if (viewFunc == undefined) viewFunc = 0; + + let nr = parseInt(settings.sceneNr); + if (isNaN(nr)) nr = 1; + nr--; + let scene = game.scenes.apps[0].scenes[nr]; + + if (scene != undefined){ + if (viewFunc == 0){ + scene.view(); + } + else if (viewFunc == 1){ + scene.activate(); + } + else { + if (scene.isView) scene.activate(); + scene.view(); + } + } + + } + } + ////////////////////////////////////////////////////////////////////////////////////////// + + updateControl(settings,context){ + let control = settings.control; + if (control == undefined) control = 0; + + let tool = settings.tool; + if (tool == undefined) tool = 0; + + const controlName = this.getControlName(control); + const toolName = this.getToolName(control,tool); + + let txt = ""; + let src = ""; + let background = "#000000"; + let ringColor = "#FF7B00" + let selectedControl; + let selectedTool; + let ring = 1; + let activeControl = ui.controls.activeControl; + let activeTool = ui.controls.activeTool; + + if (control == 0){ + let controlNr = parseInt(settings.controlNr); + if (isNaN(controlNr)) controlNr = 1; + controlNr--; + + selectedControl = ui.controls.controls.find(c => c.name == ui.controls.activeControl); + if (selectedControl != undefined){ + selectedTool = selectedControl.tools[controlNr]; + if (selectedTool != undefined){ + txt = game.i18n.localize(selectedTool.title); + src = selectedTool.icon; + if (selectedTool.toggle){ + background = "#340057" + if (selectedTool.active) + ringColor = "#A600FF" + else + ringColor = "#340057" + ring = 2; + } + else if (activeTool == selectedTool.name) + ring = 2; + } + } + } + else { + selectedControl = ui.controls.controls.find(c => c.name == controlName); + if (selectedControl != undefined){ + if (tool == 0){ //open category + txt = game.i18n.localize(selectedControl.title); + src = selectedControl.icon; + if (activeControl == selectedControl.name) + ring = 2; + } + else { + selectedTool = selectedControl.tools.find(t => t.name == toolName); + if (selectedTool != undefined){ + txt = game.i18n.localize(selectedTool.title); + src = selectedTool.icon; + if (selectedTool.toggle){ + background = "#340057" + if (selectedTool.active) + ringColor = "#A600FF" + else + ringColor = "#340057" + ring = 2; + } + else if (activeTool == selectedTool.name && activeControl == selectedControl.name) + ring = 2; + } + } + } + } + streamDeck.setIcon(1,context,src,background,ring,ringColor); + streamDeck.setTitle(txt,context); + } + + keyPressControl(settings){ + let control = settings.control; + if (control == undefined) control = 0; + + let tool = settings.tool; + if (tool == undefined) tool = 0; + + const controlName = this.getControlName(control); + const toolName = this.getToolName(control,tool); + + if (control == 0){ //displayed tools + let controlNr = parseInt(settings.controlNr); + if (isNaN(controlNr)) controlNr = 1; + controlNr--; + let selectedControl = ui.controls.controls.find(c => c.name == ui.controls.activeControl); + if (selectedControl != undefined){ + let selectedTool = selectedControl.tools[controlNr]; + if (selectedTool != undefined){ + if (selectedTool.toggle){ + let newValue = !selectedTool.active; + selectedTool.active = newValue; + } + else if (selectedTool.button){ + selectedTool.onClick(); + } + else + selectedControl.activeTool = selectedTool.name; + ui.controls.render(); + } + } + } + else { //select control + let selectedControl = ui.controls.controls.find(c => c.name == controlName); + if (selectedControl != undefined){ + if (tool == 0){ //open category + ui.controls.activeControl = controlName; + selectedControl.activeTool = selectedControl.activeTool; + canvas.getLayer(selectedControl.layer).activate(); + } + else { + let selectedTool = selectedControl.tools.find(t => t.name == toolName); + if (selectedTool != undefined){ + ui.controls.activeControl = controlName; + canvas.getLayer(selectedControl.layer).activate(); + if (selectedTool.toggle){ + let newValue = !selectedTool.active; + selectedTool.active = newValue; + } + else if (selectedTool.button){ + selectedTool.onClick(); + } + else + selectedControl.activeTool = toolName; + } + } + ui.controls.render(); + } + } + } + + + + + +//ui.controls.controls.find(c => c.name == 'token') +//ui.controls.controls.find(c => c.name == 'walls').tools.find(t => t.name == 'snap').active=false +//ui.controls.render() + + getControlName(control){ + control--; + let name; + if (control == 0) name = 'token'; + else if (control == 1) name = 'measure'; + else if (control == 2) name = 'tiles'; + else if (control == 3) name = 'drawings'; + else if (control == 4) name = 'walls'; + else if (control == 5) name = 'lighting'; + else if (control == 6) name = 'sounds'; + else if (control == 7) name = 'notes'; + return name; + } + + getToolName(control,tool){ + control--; + tool--; + let name; + if (control == 0){ //basic controls + if (tool == 0) name = 'select'; + else if (tool == 1) name = 'target'; + else if (tool == 2) name = 'ruler'; + } + else if (control == 1){ //measurement controls + if (tool == 0) name = 'circle'; + else if (tool == 1) name = 'cone'; + else if (tool == 2) name = 'rect'; + else if (tool == 3) name = 'ray'; + else if (tool == 4) name = 'clear'; + } + else if (control == 2){ //tile controls + if (tool == 0) name = 'select'; + else if (tool == 1) name = 'tile'; + else if (tool == 2) name = 'browse'; + } + else if (control == 3){ //drawing tools + if (tool == 0) name = 'select'; + else if (tool == 1) name = 'rect'; + else if (tool == 2) name = 'ellipse'; + else if (tool == 3) name = 'polygon'; + else if (tool == 4) name = 'freehand'; + else if (tool == 5) name = 'text'; + else if (tool == 6) name = 'configure'; + else if (tool == 7) name = 'clear'; + } + else if (control == 4){ //wall controls + if (tool == 0) name = 'select'; + else if (tool == 1) name = 'walls'; + else if (tool == 2) name = 'terrain'; + else if (tool == 3) name = 'invisible'; + else if (tool == 4) name = 'ethereal'; + else if (tool == 5) name = 'doors'; + else if (tool == 6) name = 'secret'; + else if (tool == 7) name = 'clone'; + else if (tool == 8) name = 'snap'; + else if (tool == 9) name = 'clear'; + } + else if (control == 5){ //lighting controls + if (tool == 0) name = 'light'; + else if (tool == 1) name = 'day'; + else if (tool == 2) name = 'night'; + else if (tool == 3) name = 'reset'; + else if (tool == 4) name = 'clear'; + } + else if (control == 6){ //ambient sound controls + if (tool == 0) name = 'sound'; + else if (tool == 1) name = 'clear'; + } + else if (control == 7){ //journal notes + if (tool == 0) name = 'select'; + else if (tool == 1) name = 'toggle'; + else if (tool == 2) name = 'clear'; + } + return name; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + updateDarkness(settings,context){ + let func = settings.darknessFunction; + if (func == undefined) func = 0; + + let value = settings.darknessValue; + if (value == undefined) value = 0; + + let background = settings.background; + if (background == undefined) background = "#000000"; + + let src = ""; + let txt = ""; + + if (func == 0){ //value + src = 'action/images/other/darkness/darkness.png'; + } + else if (func == 1){ //increase/decrease + if (value < 0) src = 'action/images/other/darkness/decreasedarkness.png'; + else src = 'action/images/other/darkness/increasedarkness.png'; + } + else if (func == 2){ //display darkness + src = 'action/images/other/darkness/darkness.png'; + let darkness = Math.floor(canvas.scene.data.darkness*100)/100; + txt += darkness; + } + streamDeck.setTitle(txt,context); + streamDeck.setIcon(0, context,src,background); + } + + keyPressDarkness(settings) { + let func = settings.darknessFunction; + if (func == undefined) func = 0; + + let value = parseFloat(settings.darknessValue); + if (value == undefined) value = 0; + + if (func == 0){ //value + canvas.scene.update({darkness: value}); + } + else if (func == 1){ //increase/decrease + let darkness = canvas.scene.data.darkness; + darkness += -1*value; + if (darkness > 1) darkness = 1; + if (darkness < 0) darkness = 0; + canvas.scene.update({darkness: darkness}); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + updateRollTable(settings,context){ + let name = settings.rollTableName; + if (name == undefined) return; + + let background = settings.background; + if (background == undefined) background = "#000000"; + + let table = game.tables.entities.find(p=>p.name == name); + + let txt = ""; + let src = ""; + + if (table != undefined) { + if (settings.displayRollIcon) src = table.data.img; + if (settings.displayRollName) txt = table.name; + } + streamDeck.setTitle(txt,context); + streamDeck.setIcon(1, context,src,background); + } + + keyPressRollTable(settings){ + let func = settings.rolltableFunction; + if (func == undefined) func = 0; + + let name = settings.rollTableName; + if (name == undefined) return; + + let background = settings.background; + if (background == undefined) background = "#000000"; + + let table = game.tables.entities.find(p=>p.name == name); + + if (table != undefined) { + if (func == 0){ //open + table.sheet.render(true); + } + else if (func == 1) {//Public roll + table.draw({rollMode:"roll"}); + } + else if (func == 2) {//private roll + table.draw({rollMode:"selfroll"}); + } + } + } +} \ No newline at end of file diff --git a/src/playlist.js b/src/playlist.js new file mode 100644 index 0000000..84fc98a --- /dev/null +++ b/src/playlist.js @@ -0,0 +1,224 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck} from "../MaterialDeck.js"; + +export class PlaylistControl{ + constructor(){ + this.playlistOffset = 0; + this.trackOffset = 0; + } + + async updateAll(){ + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'playlist') continue; + await this.update(data.settings,data.context); + } + } + + update(settings,context){ + if (settings.playlistMode == undefined) settings.playlistMode = 0; + if (settings.playlistMode == 0){ + this.updatePlaylist(settings,context); + } + else if (settings.playlistMode == 1){ + this.updateTrack(settings,context); + } + else { + let src = 'action/images/playlist/stop.png'; + streamDeck.setIcon(0,context,src,settings.background); + } + } + + updatePlaylist(settings,context){ + let name = ""; + let src = 'action/images/Black.png'; + let background = '#000000'; + + let playlistType = settings.playlistType; + if (playlistType == undefined) playlistType = 0; + + //Play/Stop + if (playlistType == 0){ + let playlistNr = parseInt(settings.playlistNr); + if (isNaN(playlistNr) || playlistNr < 1) playlistNr = 1; + playlistNr--; + playlistNr += this.playlistOffset; + + let playBackground = settings.playBackground; + if (playBackground == undefined) playBackground == '#00FF00'; + let stopBackground = settings.stopBackground; + if (stopBackground == undefined) stopBackground == '#FF0000'; + + let playlist = this.getPlaylist(playlistNr); + if (playlist != undefined){ + if (playlist.playing) { + background = playBackground; + src = 'action/images/playlist/stop.png'; + } + else { + background = stopBackground; + src = 'action/images/playlist/play.png'; + } + if (settings.displayName) + name = playlist.name; + } + + } + //Offset + else { + src = ""; + let onBackground = settings.onBackground; + if (onBackground == undefined) onBackground = '#00FF00'; + let offBackground = settings.offBackground; + if (offBackground == undefined) offBackground = '#000000'; + + let playlistOffset = parseInt(settings.offset); + if (isNaN(playlistOffset)) playlistOffset = 0; + if (playlistOffset == this.playlistOffset) background = onBackground; + else background = offBackground; + } + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(name,context); + } + + updateTrack(settings,context){ + let name = ""; + let src = 'action/images/Black.png'; + let background = '#000000'; + + let playlistType = settings.playlistType; + if (playlistType == undefined) playlistType = 0; + + //Play/Stop + if (playlistType == 0){ + let playlistNr = parseInt(settings.playlistNr); + if (isNaN(playlistNr) || playlistNr < 1) playlistNr = 1; + playlistNr--; + playlistNr += this.playlistOffset; + let trackNr = parseInt(settings.trackNr); + if (isNaN(trackNr) || trackNr < 1) trackNr = 1; + trackNr--; + trackNr += this.trackOffset; + + let playBackground = settings.playBackground; + if (playBackground == undefined) playBackground == '#00FF00'; + let stopBackground = settings.stopBackground; + if (stopBackground == undefined) stopBackground == '#FF0000'; + + let playlist = this.getPlaylist(playlistNr); + if (playlist != undefined){ + let track = playlist.data.sounds[trackNr]; + if (track != undefined){ + if (track.playing) { + background = playBackground; + src = 'action/images/playlist/stop.png'; + } + else { + background = stopBackground; + src = 'action/images/playlist/play.png'; + } + if (settings.displayName) + name = track.name; + } + } + + } + //Offset + else { + src = ""; + let onBackground = settings.onBackground; + if (onBackground == undefined) onBackground = '#00FF00'; + let offBackground = settings.offBackground; + if (offBackground == undefined) offBackground = '#000000'; + + let trackOffset = parseInt(settings.offset); + if (isNaN(trackOffset)) trackOffset = 0; + if (trackOffset == this.trackOffset) background = onBackground; + else background = offBackground; + } + streamDeck.setIcon(0,context,src,background); + streamDeck.setTitle(name,context); + } + + stopAll(){ + let playing = game.playlists.playing; + for (let i=0; i p._id == playlistId); + } + + keyPress(settings,context){ + let playlistNr = settings.playlistNr; + if (playlistNr == undefined || playlistNr < 1) playlistNr = 1; + playlistNr--; + playlistNr += this.playlistOffset; + let trackNr = settings.trackNr; + if (trackNr == undefined || trackNr < 1) trackNr = 1; + trackNr--; + trackNr += this.trackOffset; + + if (settings.playlistMode == undefined) settings.playlistMode = 0; + if (settings.playlistType == undefined) settings.playlistType = 0; + if (settings.playlistMode < 2){ + if (settings.playlistType == 0) { + let playlist = this.getPlaylist(playlistNr); + if (playlist != undefined){ + if (settings.playlistMode == 0) + this.playPlaylist(playlist); + else { + let track = playlist.data.sounds[trackNr]; + if (track != undefined){ + this.playTrack(track,playlist); + } + } + } + } + else { + if (settings.playlistMode == 0) { + this.playlistOffset = parseInt(settings.offset); + if (isNaN(this.playlistOffset)) this.playlistOffset = 0; + } + else { + this.trackOffset = parseInt(settings.offset); + if (isNaN(this.trackOffset)) this.trackOffset = 0; + } + this.updateAll(); + } + } + else { + this.stopAll(); + } + } + + async playPlaylist(playlist){ + if (playlist.playing) { + playlist.stopAll(); + return; + } + let mode = game.settings.get(MODULE.moduleName,'playlistMethod'); + if (mode == 2) await this.stopAll(); + playlist.playAll(); + } + + async playTrack(track,playlist){ + let play; + if (track.playing) + play = false; + else { + play = true; + let mode = game.settings.get(MODULE.moduleName,'playlistMethod'); + if (mode == 1) await playlist.stopAll(); + else if (mode == 2) await this.stopAll(); + } + await playlist.updateEmbeddedEntity("PlaylistSound", {_id: track._id, playing: play}); + playlist.update({playing: play}); + } + + + +} \ No newline at end of file diff --git a/src/settings.js b/src/settings.js new file mode 100644 index 0000000..b6c17df --- /dev/null +++ b/src/settings.js @@ -0,0 +1,97 @@ +import * as MODULE from "../MaterialDeck.js"; +import { playlistConfigForm, macroConfigForm, soundboardConfigForm } from "./misc.js"; + +export const registerSettings = function() { + /** + * Main settings + */ + + //Enabled the module + game.settings.register(MODULE.moduleName,'Enable', { + name: "MaterialDeck.Sett.Enable", + scope: "world", + config: true, + default: true, + type: Boolean, + onChange: x => window.location.reload() + }); + + game.settings.register(MODULE.moduleName,'streamDeckModel', { + name: "Stream Deck Model", + hint: "Reduces the amount of macros and sounds in the macro and soundboard configuration screens. Gives a better overview, but if desired it can be set to XL to get the maximum number. This doesn't influence the operation of the module.", + scope: "world", + config: true, + type:Number, + default:1, + choices:["Mini","Normal or Mobile","XL"], + }); + + /** + * Playlist soundboard + */ + game.settings.register(MODULE.moduleName,'playlistMethod', { + name: "Playlist play method", + scope: "world", + config: false, + type:Number, + default:0, + choices:["Default","One track per playlist","One track in total"], + }); + + game.settings.registerMenu(MODULE.moduleName, 'playlistConfigMenu',{ + name: "Playlist Config", + label: "Playlist Config", + type: playlistConfigForm, + restricted: true + }); + + game.settings.register(MODULE.moduleName, 'selectedPlaylists', { + name: "selectedPlaylists", + scope: "world", + type: Object, + default: {a: "None",b: "None",c: "none",d: "none",e: "none",f: "none",g: "none",h: "none",i: "none"}, + config: false + }); + + /** + * Macro Board + */ + game.settings.registerMenu(MODULE.moduleName, 'macroConfigMenu',{ + name: "MaterialDeck.Sett.MacroConfig", + label: "MaterialDeck.Sett.MacroConfig", + type: macroConfigForm, + restricted: true + }); + + game.settings.register(MODULE.moduleName, 'macroSettings', { + name: "macroSettings", + scope: "world", + type: Object, + config: false + }); + + game.settings.register(MODULE.moduleName, 'macroArgs', { + name: "macroArgs", + scope: "world", + type: Object, + config: false + }); + + /** + * Soundboard + */ + game.settings.register(MODULE.moduleName, 'soundboardSettings', { + name: "soundboardSettings", + scope: "world", + type: Object, + default: "None", + config: false + }); + + game.settings.registerMenu(MODULE.moduleName, 'soundboardConfigMenu',{ + name: "MaterialDeck.Sett.SoundboardConfig", + label: "MaterialDeck.Sett.SoundboardConfig", + type: soundboardConfigForm, + restricted: true + }); +} diff --git a/src/soundboard.js b/src/soundboard.js new file mode 100644 index 0000000..c1e3249 --- /dev/null +++ b/src/soundboard.js @@ -0,0 +1,161 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck} from "../MaterialDeck.js"; + +export class SoundboardControl{ + constructor(){ + this.offset = 0; + this.activeSounds = []; + for (let i=0; i<64; i++) + this.activeSounds[i] = false; + } + + async updateAll(){ + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'soundboard') continue; + await this.update(data.settings,data.context); + } + } + + update(settings,context){ + let mode = settings.soundboardMode; + if (mode == undefined) mode = 0; + + let txt = ""; + let src = ""; + let background = "#000000"; + + if (mode == 0){ //play sound + let soundNr = parseInt(settings.soundNr); + if (isNaN(soundNr)) soundNr = 1; + soundNr--; + soundNr += this.offset; + + let soundboardSettings = game.settings.get(MODULE.moduleName, 'soundboardSettings'); + + let onColor = soundboardSettings.colorOn[soundNr]; + let offColor = soundboardSettings.colorOff[soundNr]; + + background = onColor; + let ring = 2; + if (this.activeSounds[soundNr]==false) { + background = offColor; + ring = 1; + } + if (settings.displayName) txt = soundboardSettings.name[soundNr]; + if (settings.displayIcon) src = soundboardSettings.img[soundNr]; + streamDeck.setTitle(txt,context); + streamDeck.setIcon(1,context,src,background,ring,background); + } + else if (mode == 1) { //Offset + src = ""; + let onBackground = settings.onBackground; + if (onBackground == undefined) onBackground = '#00FF00'; + let offBackground = settings.offBackground; + if (offBackground == undefined) offBackground = '#000000'; + + let offset = parseInt(settings.offset); + if (isNaN(offset)) offset = 0; + if (offset == this.offset) background = onBackground; + else background = offBackground; + streamDeck.setTitle(txt,context); + streamDeck.setIcon(1,context,src,background); + } + else if (mode == 2) { //Stop all sounds + let src = 'action/images/soundboard/stop.png'; + streamDeck.setIcon(0,context,src,settings.background); + } + } + + keyPressDown(settings){ + let mode = settings.soundboardMode; + if (mode == undefined) mode = 0; + if (mode == 0) { //Play sound + let soundNr = parseInt(settings.soundNr); + if (isNaN(soundNr)) soundNr = 1; + soundNr--; + soundNr += this.offset; + + const playMode = game.settings.get(MODULE.moduleName,'soundboardSettings').mode[soundNr]; + + let repeat = false; + if (playMode > 0) repeat = true; + let play = false; + if (this.activeSounds[soundNr] == false) play = true; + this.playSound(soundNr,repeat,play); + } + else if (mode == 1) { //Offset + let offset = parseInt(settings.offset); + if (isNaN(offset)) offset = 0; + this.offset = offset; + this.updateAll(); + } + else { //Stop All Sounds + for (let i=0; i<64; i++) { + if (this.activeSounds[i] != false){ + this.playSound(i,false,false); + } + } + } + } + + keyPressUp(settings){ + let mode = settings.soundboardMode; + if (mode == undefined) mode = 0; + if (mode != 0) return; + let soundNr = parseInt(settings.soundNr); + if (isNaN(soundNr)) soundNr = 1; + soundNr--; + soundNr += this.offset; + + const playMode = game.settings.get(MODULE.moduleName,'soundboardSettings').mode[soundNr]; + + if (playMode == 2) + this.playSound(soundNr,false,false); + } + + playSound(soundNr,repeat,play){ + let trackId = game.settings.get(MODULE.moduleName,'soundboardSettings').sounds[soundNr]; + let volume = game.settings.get(MODULE.moduleName,'soundboardSettings').volume[soundNr]/100; + volume = AudioHelper.inputToVolume(volume); + if (trackId == "" || trackId == undefined) return; + let payload = { + "msgType": "playSound", + "trackNr": soundNr, + "repeat": repeat, + "play": play, + "volume": volume + }; + game.socket.emit(`module.MaterialDeck`, payload); + if (play){ + let trackId = game.settings.get(MODULE.moduleName,'soundboardSettings').sounds[soundNr]; + let playlistId = game.settings.get(MODULE.moduleName,'soundboardSettings').playlist; + let sounds = game.playlists.entities.find(p => p._id == playlistId).data.sounds; + let sound = sounds.find(p => p._id == trackId); + if (sound == undefined){ + this.activeSounds[soundNr] = false; + return; + } + volume *= game.settings.get("core", "globalInterfaceVolume"); + let src = sound.path; + + let howl = new Howl({src, volume, loop: repeat, onend: (id)=>{ + if (repeat == false){ + this.activeSounds[soundNr] = false; + this.updateAll(); + } + }, + onstop: (id)=>{ + this.activeSounds[soundNr] = false; + this.updateAll(); + }}); + howl.play(); + this.activeSounds[soundNr] = howl; + } + else { + this.activeSounds[soundNr].stop(); + this.activeSounds[soundNr] = false; + } + this.updateAll(); + } +} \ No newline at end of file diff --git a/src/streamDeck.js b/src/streamDeck.js new file mode 100644 index 0000000..d82526f --- /dev/null +++ b/src/streamDeck.js @@ -0,0 +1,297 @@ +import * as MODULE from "../MaterialDeck.js"; + +export class StreamDeck{ + constructor() { + this.pluginId; + this.tokenHealthContext; + this.tokenNameContext; + this.tokenACContext; + this.buttonContext = []; + for (let i=0; i<23; i++){ + this.buttonContext[i] = undefined; + } + this.playlistTrackBuffer = []; + this.playlistSelector = 0; + this.trackSelector = 0; + for (let i=0; i<23; i++) + this.playlistTrackBuffer[i] = {state: 3, name: ""}; + this.playlistBuffer = []; + for (let i=0; i<3; i++) + this.playlistBuffer[i] = {state: 3, name: ""}; + this.counter = 0; + + let canvasBox = document.createElement('div'); + canvasBox.id = 'sdCanvasBox'; + document.body.appendChild(canvasBox); // adds the canvas to the body element + } + + setScreen(action){ + + } + + setContext(action,context,coordinates = {column:0,row:0},settings){ + const data = { + context: context, + action: action, + settings: settings + } + let num = coordinates.column + coordinates.row*8; + this.buttonContext[num] = data; + } + + clearContext(action,coordinates = {column:0,row:0}){ + let num = coordinates.column + coordinates.row*8; + this.buttonContext[num] = undefined; + } + + formatTitle(txt){ + let txtArray = txt.split(" "); + let txtNew = ""; + for (let i=0; i 0) + txtNew += "\n"; + txtNew += txtNewPart; + } + return txtNew; + } + + setTitle(txt,context){ + txt = this.formatTitle(txt); + for (let i=0; i<32; i++){ + if (this.buttonContext[i] == undefined) continue; + if (this.buttonContext[i].context == context) { + if (this.buttonContext[i].txt != undefined) + if (this.buttonContext[i].txt == txt) + return; + this.buttonContext[i].txt = txt; + } + } + let msg = { + event: 'setTitle', + context: context, + payload: { + title: txt, + target: 0 + } + }; + MODULE.sendWS(JSON.stringify(msg)); + } + + setColor(context,color = '#000000'){ + let msg = { + event: 'setIcon', + context: context, + url: '', + format: 'color', + background: color + }; + MODULE.sendWS(JSON.stringify(msg)); + } + + setImage(image,context){ + //var image = "data:image/svg+xml;charset=utf8, GPUUTILIZATION"; + + var json = { + event: "setImage", + context: context, + payload: { + image: "" + image, + target: 0 + } + }; + MODULE.sendWS(JSON.stringify(json)); + } + + setIcon(iconLocation, context,src,background = '#000000',ring=0,ringColor = "#000000"){ + for (let i=0; i<32; i++){ + if (this.buttonContext[i] == undefined) continue; + if (this.buttonContext[i].context == context) { + if (this.buttonContext[i].icon == src && this.buttonContext[i].ring == ring && this.buttonContext[i].ringColor == ringColor && this.buttonContext[i].background == background && this.buttonContext[i].iconLocation == iconLocation) + return; + this.buttonContext[i].icon = src; + this.buttonContext[i].ring = ring; + this.buttonContext[i].ringColor = ringColor; + this.buttonContext[i].background = background; + this.buttonContext[i].iconLocation = iconLocation; + } + } + + let split = src.split('.'); + let format = split[1]; + split = src.split(' '); + if (split[0] == 'fas' || split[0] == 'far' || split[0] == 'fal' || split[0] == 'fad') format = 'icon'; + let msg = { + event: 'setIcon', + context: context, + url: src, + format: format, + background: background, + ring: ring, + ringColor: ringColor + }; + if (iconLocation == 0){ + MODULE.sendWS(JSON.stringify(msg)); + } + else + this.getImage(msg); + } + + setState(state,context,action){ + let msg = { + event: 'setStateCustom', + context: context, + action: action, + state: state + }; + MODULE.sendWS(JSON.stringify(msg)); + } + + setProfile(action,device){ + let profile; + if (action == 'playlistcontrol') + profile = 'MaterialDeck-Playlist' + var json = { + "source": 1, + "event": "switchToProfile", + "context": this.pluginId, + "device": device, + "payload": { + "profile": profile + } + }; + MODULE.sendWS(JSON.stringify(json)); + } + + setPluginId(id){ + this.pluginId = id; + } + + getFAChar = function (name) { + var elm = document.createElement('i'); + elm.className = name; + elm.style.display = 'none'; + document.body.appendChild(elm); + var content = window.getComputedStyle( + elm, ':before' + ).getPropertyValue('content') + //console.log(elm); + document.body.removeChild(elm); + return content; + }; + + getImage(data){ + if (data == undefined) + return; + console.log('image',data) + const context = data.context; + var url = data.url; + const format = data.format; + var background = data.background; + + let BGvalid = true; + if (background.length != 7) BGvalid = false; + if (background[0] != '#') BGvalid = false; + for (let i=1; i 31) this.counter = 0; + + let ctx = canvas.getContext("2d"); + ctx.filter = "none"; + + let margin = 0; + + if (data.ring != undefined && data.ring > 0){ + ctx.fillStyle = background; + ctx.fillRect(0, 0, canvas.width, canvas.height); + margin = 10; + if (data.ring == 2) { + ctx.fillStyle = data.ringColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = background; + ctx.fillRect(margin, margin, canvas.width-2*margin, canvas.height-2*margin); + } + } + else { + ctx.fillStyle = background; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + if (format == 'icon' && url != ""){ + ctx.font = '600 90px "Font Awesome 5 Free"'; + ctx.fillStyle = "gray"; + var elm = document.createElement('i'); + elm.className = url; + elm.style.display = 'none'; + canvas.appendChild(elm); + var content = window.getComputedStyle( + elm, ':before' + ).getPropertyValue('content') + //console.log(content[1]); + canvas.removeChild(elm); + ctx.fillText(content[1], 35, 105); + } + + if (format != 'jpg' && format != 'jpeg' && format != 'png' && format != 'webm' && format != 'webp' && format != 'gif' && format != 'svg') url = "modules/MaterialDeck/img/transparant.png"; + if (url == "") url = "modules/MaterialDeck/img/transparant.png" + //console.log(url); + let resImageURL = url; + + let img = new Image(); + img.onload = () => { + if (format == 'color') ctx.filter = "opacity(0)"; + //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; + var renderableHeight, renderableWidth, xStart, yStart; + + // If image's aspect ratio is less than canvas's we fit on height + // and place the image centrally along width + if(imageAspectRatio < canvasAspectRatio) { + renderableHeight = canvas.height; + renderableWidth = img.width * (renderableHeight / img.height); + xStart = (canvas.width - renderableWidth) / 2; + yStart = 0; + } + + // If image's aspect ratio is greater than canvas's we fit on width + // and place the image centrally along height + else if(imageAspectRatio > canvasAspectRatio) { + renderableWidth = canvas.width + renderableHeight = img.height * (renderableWidth / img.width); + xStart = 0; + yStart = (canvas.height - renderableHeight) / 2; + } + + // Happy path - keep aspect ratio + else { + renderableHeight = canvas.height; + renderableWidth = canvas.width; + xStart = 0; + yStart = 0; + } + ctx.drawImage(img, xStart+margin, yStart+margin, renderableWidth - 2*margin, renderableHeight - 2*margin); + var dataURL = canvas.toDataURL(); + this.setImage(dataURL,data.context); + }; + img.src = resImageURL; + } +} \ No newline at end of file diff --git a/src/token.js b/src/token.js new file mode 100644 index 0000000..931a345 --- /dev/null +++ b/src/token.js @@ -0,0 +1,117 @@ +import * as MODULE from "../MaterialDeck.js"; +import {streamDeck} from "../MaterialDeck.js"; + +export class TokenControl{ + constructor(){ + } + + async update(tokenId){ + console.log('tokenId',tokenId) + for (let i=0; i<32; i++){ + let data = streamDeck.buttonContext[i]; + if (data == undefined || data.action != 'token') continue; + await this.pushData(tokenId,data.settings,data.context); + } + } + + pushData(tokenId,settings,context,ring=0,ringColor='#000000'){ + console.log('tk',tokenId,settings) + let name = false; + let icon = false; + let type = 0; + let background = "#000000"; + + if (settings.displayIcon) icon = true; + if (settings.displayName) name = true; + if (settings.stats != undefined) type = settings.stats; + if (settings.background) background = settings.background; + + let tokenName = ""; + let hp = ""; + let AC = ""; + let speed = ""; + let initiative = ""; + let txt = ""; + let iconSrc = ""; + if (tokenId != undefined) { + let token = canvas.tokens.children[0].children.find(p => p.id == tokenId); + tokenName = token.data.name; + iconSrc += token.data.img; + let actor = canvas.tokens.children[0].children.find(p => p.id == tokenId).actor; + let system = game.system.id; + if (system == 'dnd5e'){ + let attributes = actor.data.data.attributes; + let hpCurrent = attributes.hp.value; + let hpMax = attributes.hp.max; + hp = hpCurrent + "/" + hpMax; + AC = attributes.ac.value; + + if (attributes.speed._deprecated){ + if (attributes.movement.burrow > 0) speed += attributes.movement.burrow + attributes.movement.units + " burrow"; + if (attributes.movement.climb > 0) { + if (speed.length > 0) speed += '\n'; + speed += attributes.movement.climb + attributes.movement.units + " climb"; + } + if (attributes.movement.fly > 0) { + if (speed.length > 0) speed += '\n'; + speed += attributes.movement.fly + attributes.movement.units + " fly"; + } + if (attributes.movement.hover > 0) { + if (speed.length > 0) speed += '\n'; + speed += attributes.movement.hover + attributes.movement.units + " hover"; + } + if (attributes.movement.swim > 0) { + if (speed.length > 0) speed += '\n'; + speed += attributes.movement.swim + attributes.movement.units + " swim"; + } + if (attributes.movement.walk > 0) { + if (speed.length > 0) speed += '\n'; + speed += attributes.movement.walk + attributes.movement.units + " walk"; + } + } + else { + speed = attributes.speed.value; + if (attributes.speed.special.length > 0) speed + "\n" + attributes.speed.special; + } + initiative = attributes.init.total; + } + else return; + } + else { + iconSrc += ""; + } + if (icon) streamDeck.setIcon(1,context,iconSrc,background,ring,ringColor); + + if (name) txt += tokenName; + if (name && type > 0) txt += "\n"; + if (type == 1) txt += hp; + else if (type == 2) txt += AC; + else if (type == 3) txt += speed; + else if (type == 4) txt += initiative; + streamDeck.setTitle(txt,context); + } + + keyPress(settings){ + if (MODULE.selectedTokenId == undefined) return; + const tokenId = MODULE.selectedTokenId; + + let onClick = settings.onClick; + if (onClick == undefined) conClick = 0; + + const token = canvas.tokens.children[0].children.find(p => p.id == tokenId); + if (token == undefined) return; + + if (onClick == 0) //Do nothing + return; + else if (onClick == 1){ //center on token + let location = token.getCenter(token.x,token.y); + canvas.animatePan(location); + } + else if (onClick == 2){ //Open character sheet + token.actor.sheet.render(true); + } + else { //Open token config + token.sheet._render(true); + } + } +} \ No newline at end of file diff --git a/templates/macroConfig.html b/templates/macroConfig.html new file mode 100644 index 0000000..f5d15ff --- /dev/null +++ b/templates/macroConfig.html @@ -0,0 +1,45 @@ +
+ + + {{#each macroData}} +
+ {{#each this.dataThis}} +
+
+ {{localize "MaterialDeck.Macro.Macro"}} {{this.iteration}} +
+
+ +
+ {{#if this.furnace}} + + + {{/if}} + +
+ + +
+
+ {{/each}} +
+ {{/each}} + + +
\ No newline at end of file diff --git a/templates/playlistConfig.html b/templates/playlistConfig.html new file mode 100644 index 0000000..106258f --- /dev/null +++ b/templates/playlistConfig.html @@ -0,0 +1,36 @@ +
+
+

{{localize "MaterialDeck.Playlist.Playmethod.Header"}}

+
+
+ + +
+
+

{{localize "MaterialDeck.Playlist.Playlists"}}

+
+ {{#each playlistData}} +
+ + +
+ {{/each}} + + + +
\ No newline at end of file diff --git a/templates/soundboardConfig.html b/templates/soundboardConfig.html new file mode 100644 index 0000000..22c0dfd --- /dev/null +++ b/templates/soundboardConfig.html @@ -0,0 +1,84 @@ +
+ +
+ + +
+ {{#each soundData}} +
+ {{#each this.dataThis}} +
+
+ {{localize "MaterialDeck.Soundboard.Sound"}} {{this.iteration}} +
+
+ {{localize "MaterialDeck.Name"}} +
+ +
+ {{localize "MaterialDeck.Soundboard.Sound"}} +
+
+ +
+
+ {{localize "MaterialDeck.Soundboard.Icon"}} +
+
+ + +
+
+ + + + +
+ +
+ + +
+
+ + +
+ +
+ {{/each}} +
+ {{/each}} + + +
\ No newline at end of file