This commit is contained in:
CDeenen
2020-11-10 01:52:58 +01:00
commit 0112e029d9
22 changed files with 2874 additions and 0 deletions

347
MaterialDeck.js Normal file
View File

@@ -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;
}

0
README.md Normal file
View File

0
changelog.md Normal file
View File

BIN
img/.thumb/Black.png.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
img/Black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
img/transparant.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

39
lang/en.json Normal file
View File

@@ -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"
}

23
module.json Normal file
View File

@@ -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": ""
}

194
src/combattracker.js Normal file
View File

@@ -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();
}
}
}

215
src/macro.js Normal file
View File

@@ -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, {});
}
}
}
}
}

409
src/misc.js Normal file
View File

@@ -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; j<jMax; j++){
let macroThis = {};
for (let i=0; i<iMax; i++){
let colorThis = color[iteration];
if (colorThis != undefined){
let colorCorrect = true;
if (colorThis[0] != '#') colorCorrect = false;
for (let k=0; k<6; k++){
if (parseInt(colorThis[k+1],16)>15)
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<jMax; j++){
let soundsThis = {};
for (let i=0; i<iMax; i++){
if (volume == undefined) volume = 50;
let dataThis = {
iteration: iteration+1,
sound: selectedSounds[iteration],
sounds: sounds,
colorOn: colorOn[iteration],
colorOff: colorOff[iteration],
mode: mode[iteration],
volume: volume[iteration],
imgPath: img[iteration],
name: name[iteration]
}
MODULE.setToJSONArray(soundsThis,i,dataThis);
iteration++;
}
let data = {
dataThis: soundsThis,
};
MODULE.setToJSONArray(soundData,j,data);
}
return {
playlists: game.playlists.entities,
playlist: playlistId,
sounds: sounds,
selectedSound81: selectedSounds.a,
soundData: soundData,
}
}
/**
* Update on form submit
* @param {*} event
* @param {*} formData
*/
async _updateObject(event, formData) {
let length = formData["sounds"].length;
let img = [];
for (let i=0; i<length; i++){
let name = "img"+(i+1);
let src = formData[name];
img[i] = src;
}
await game.settings.set(MODULE.moduleName,'soundboardSettings',{
playlist: formData["playlist"],
sounds: formData["sounds"],
colorOn: formData["colorOn"],
colorOff: formData["colorOff"],
mode: formData["mode"],
img: img,
volume: formData["volume"],
name: formData["name"]
});
//MODULE.launchpad.audioSoundboardUpdate();
}
async activateListeners(html) {
super.activateListeners(html);
const colorPickerOn = html.find("button[name='colorPickerOn']");
const colorPickerOff = html.find("button[name='colorPickerOff']");
const playlistSelect = html.find("select[name='playlist']");
const volumeSlider = html.find("input[name='volume']");
const soundSelect = html.find("select[name='sounds']");
/*
colorPickerOn.on('click',(event) => {
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);
}
});
}
*/
}
}

85
src/move.js Normal file
View File

@@ -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);
}
}

501
src/othercontrols.js Normal file
View File

@@ -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"});
}
}
}
}

224
src/playlist.js Normal file
View File

@@ -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<playing.length; i++){
playing[i].stopAll();
}
}
getPlaylist(num){
let playlistId = game.settings.get(MODULE.moduleName,'selectedPlaylists')[num];
return game.playlists.entities.find(p => 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});
}
}

97
src/settings.js Normal file
View File

@@ -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
});
}

161
src/soundboard.js Normal file
View File

@@ -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();
}
}

297
src/streamDeck.js Normal file
View File

@@ -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<txtArray.length; i++){
let txtNewPart = txtArray[i];
if (i<txtArray.length-1 && txtArray[i].length + txtArray[i+1].length < 8) {
txtNewPart = txtArray[i] + " " + txtArray[i+1];
i++;
}
if (txtNew.length > 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,<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --><svg version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 288 288\" style=\"enable-background:new 0 0 288 288;\" xml:space=\"preserve\"> <style> .text { font-family: sans-serif; fill: red; font-size: 10em; } </style><g> <text x=\"0px\" y=\"100px\" class=\"text\">GPUUTILIZATION</text></g></svg>";
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<background.length; i++)
if(isNaN(parseInt(background[i],16)))
BGvalid = false;
if (BGvalid == false) background = '#000000';
let canvas;
let canvasId = 'sdCanvas' + this.counter;
canvas = document.getElementById(canvasId);
if (canvas == null){
canvas = document.createElement('canvas');
canvas.id = canvasId;
canvas.width="144";
canvas.height="144";
canvas.style="background-color:transparent";
document.getElementById('sdCanvasBox').appendChild(canvas); // adds the canvas to #someBox
}
this.counter++;
if (this.counter > 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;
}
}

117
src/token.js Normal file
View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,45 @@
<form autocomplete="off" onsubmit="event.preventDefault()">
<style>
.boxed {
border: 1px solid black ;
border-radius: 5px ;
width: 100px;
height: {{height}}px;
}
</style>
{{#each macroData}}
<div class="form-group">
{{#each this.dataThis}}
<div class="boxed" style="padding: 5px; margin:2px">
<div style="text-align:center;">
{{localize "MaterialDeck.Macro.Macro"}} {{this.iteration}}
</div>
<div>
<select name="macros" class="macros-select" default="" style="max-width:140px;">
{{#select this.macro}}
<option value="">{{localize "MaterialDeck.Playlist.None"}}</option>
{{#each macros}}
<option value="{{this._id}}">{{this.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
{{#if this.furnace}}
<label>{{localize "MaterialDeck.Macro.FurnaceArgs"}}</label>
<input type="text" name="args" value="{{this.args}}">
{{/if}}
<div class="flex-container" style="display:flex;flex-direction:row;padding-top:10px">
<label style="flex:1">{{localize "MaterialDeck.Macro.Background"}}</label>
<input style="flex:1" type="color" name="colorPicker" data-dtype="String" value="{{this.color}}">
</div>
</div>
{{/each}}
</div>
{{/each}}
<button type="submit" name="submit">
<i class="far fa-save"></i> {{localize "MaterialDeck.Playlist.Save"}}
</button>
</form>

View File

@@ -0,0 +1,36 @@
<form autocomplete="off" onsubmit="event.preventDefault()">
<div >
<h2>{{localize "MaterialDeck.Playlist.Playmethod.Header"}}</h2>
</div>
<div class="form-group">
<label>{{localize "MaterialDeck.Playlist.Playmethod.Label"}}</label>
<select name="playMethod" class="playMethod" default="">
{{#select playMethod}}
<option value="0">{{localize "MaterialDeck.Playlist.Playmethod.Unrestricted"}}</option>
<option value="1">{{localize "MaterialDeck.Playlist.Playmethod.OneTrackPlaylist"}}</option>
<option value="2">{{localize "MaterialDeck.Playlist.Playmethod.OneTrackTotal"}}</option>
{{/select}}
</select>
</div>
<div>
<h2>{{localize "MaterialDeck.Playlist.Playlists"}}</h2>
</div>
{{#each playlistData}}
<div class="form-group">
<label>{{localize "MaterialDeck.Playlist.Playlist"}} {{this.iteration}}</label>
<select name="selectedPlaylist" class="playlist-select" default="">
{{#select this.playlist}}
<option value="">{{localize "MaterialDeck.Playlist.None"}}</option>
{{#each this.playlists}}
<option value="{{this._id}}">{{this.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
{{/each}}
<button type="submit" name="submit">
<i class="far fa-save"></i> {{localize "MaterialDeck.Playlist.Save"}}
</button>
</form>

View File

@@ -0,0 +1,84 @@
<form autocomplete="off" onsubmit="event.preventDefault()">
<style>
.boxed {
border: 1px solid black ;
border-radius: 5px ;
width: 100px;
height: 255px;
}
</style>
<div class="form-group">
<label>{{localize "MaterialDeck.Playlist.Playlist"}}&nbsp;&nbsp;&nbsp;</label>
<select name="playlist" class="playlist-select" default="" style="max-width:200px;">
{{#select playlist}}
<option value="">{{localize "MaterialDeck.Playlist.None"}}</option>
{{#each playlists}}
<option value="{{this._id}}">{{this.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
{{#each soundData}}
<div class="form-group">
{{#each this.dataThis}}
<div class="boxed" style="padding: 5px; margin:2px">
<div style="text-align:center;">
{{localize "MaterialDeck.Soundboard.Sound"}} {{this.iteration}}
</div>
<div style="text-align:center;">
{{localize "MaterialDeck.Name"}}
</div>
<input type="text" name="name" value="{{this.name}}">
<div style="text-align:center;">
{{localize "MaterialDeck.Soundboard.Sound"}}
</div>
<div>
<select name="sounds" class="sounds-select" default="" style="width:132px;" id="soundSelect{{this.iteration}}">
{{#select this.sound}}
<option value="">{{localize "MaterialDeck.Playlist.None"}}</option>
{{#each sounds}}
<option value="{{this._id}}">{{this.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div style="text-align:center;">
{{localize "MaterialDeck.Soundboard.Icon"}}
</div>
<div class="form-fields">
<button type="button" class="file-picker" data-type="image" data-target="img{{this.iteration}}" title="Browse Files" tabindex="-1">
<i class="fas fa-file-import fa-fw"></i>
</button>
<input class="image" type="text" name="img{{this.iteration}}" id="imgPath{{this.iteration}}" placeholder="path/image.png" value={{this.imgPath}}>
</div>
<div class="flex-container" style="display:flex;flex-direction:row;padding-top:5px">
<label style="flex:1">{{localize "MaterialDeck.Soundboard.On"}}&nbsp;&nbsp;&nbsp;</label>
<input style="flex:1" type="color" id="colorOn{{this.iteration}}" name="colorOn" style="flex:4" data-dtype="String" value="{{this.colorOn}}">
<label style="flex:1">&nbsp;{{localize "MaterialDeck.Soundboard.Off"}}&nbsp;&nbsp;</label>
<input style="flex:1" type="color" id="colorOff{{this.iteration}}" name="colorOff" style="flex:4" data-dtype="String" value="{{this.colorOff}}">
</div>
<div class="form-group options">
<label>{{localize "MaterialDeck.Soundboard.Playback"}}</label>
<select name="mode" style="flex:1">
{{#select this.mode}}
<option value="0">{{localize "MaterialDeck.Soundboard.Once"}}</option>
<option value="1">{{localize "MaterialDeck.Soundboard.Repeat"}}</option>
<option value="2">{{localize "MaterialDeck.Soundboard.Hold"}}</option>
{{/select}}
</select>
</div>
<div class="form-group options">
<label>{{localize "MaterialDeck.Soundboard.Volume"}}</label>
<input type="range" min="0" max="100" value={{this.volume}} class="slider" name="volume" id="volume{{this.iteration}}">
</div>
</div>
{{/each}}
</div>
{{/each}}
<button type="submit" name="submit">
<i class="far fa-save"></i> {{localize "MaterialDeck.Playlist.Save"}}
</button>
</form>