diff --git a/MaterialDeck.js b/MaterialDeck.js
index 527126e..76c9856 100644
--- a/MaterialDeck.js
+++ b/MaterialDeck.js
@@ -118,7 +118,7 @@ async function analyzeWSmessage(msg){
}
else if (event == 'willDisappear'){
- streamDeck.clearContext(action,coordinates);
+ streamDeck.clearContext(action,coordinates,context);
}
else if (event == 'keyDown'){
diff --git a/README.md b/README.md
index 9f07c41..80219ef 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-Note:
-At the moment Windows and OSX are supported. Linux has been reported to work, there is no official Linux support for the Stream Deck, but there exist a 3rd party Stream Deck UI that appears be compatible. To get it working on Linux requires more work, and help I can personally offer is limited.
-The module works on the native Foundry application, Chrome and Firefox. Safari (you'll need the latest dev version to get Foundry to work on it) doesn't work if your Foundry server is secured, unless you use something like Nginx with which I cannot help you.
+Note: At the moment Windows and OSX support has been confirmed. Linux support is unknown, there is no official Linux support for the Stream Deck, but there exist a 3rd party Stream Deck UI that might be compatible.
In any case: Proceed at your own risk, I will not take any responsibility if you spent money and the module doesn't work!
Please read the documentation carefully, especially if you want to modify the default profile!
@@ -74,7 +72,7 @@ Module manifest: https://raw.githubusercontent.com/CDeenen/MaterialDeck/Master/m
## Software Versions & Module Incompatibilities
Foundry VTT: Tested on 0.7.9
-Module Incompatibilities: Combat Utility Belt conditions do not work with the Token Action.
+Module Incompatibilities: None known.
## Feedback
If you have any suggestions or bugs to report, feel free to create an issue, contact me on Discord (Cris#6864), or send me an email: cdeenen@outlook.com.
diff --git a/changelog.md b/changelog.md
index 0ecfb77..149eea1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,25 @@
# Changelog Material Deck Module
+### v1.3.1 - 27-02-2021
+Additions:
+
+- Token Action & Move Action: You can now choose what token should be targeted with the action using: 'Selected Token', 'Token Name', 'Actor Name', 'Token Id' or 'Actor Id'. Added relevant user permissions to the permission configuration
+- Token Action => On Click: Added options 'Select Token' and 'Center on Token and Select Token'
+- Playlist Action: Added relative offset mode, with the option to display the offset target name for playlists
+- Playlist Action => Stop All: Added option to display the name of the playlist at the current offset
+
+
+Fixes:
+
+- Default user permissions would not be loaded if no previously saved permissions were present, resulting in MD assuming nobody has any permissions
+- Other Actions => Control Buttons => Lighting Controls: Would create a button for ambient sound instead of lighting
+- Token Action => Display Token Icon: It used to show the icon, even if unchecked, if no stat with default icon was selected
+
+
+
+Compatible server app and SD plugin:
+Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases
+SD plugin v1.3.1 (must be updated!): https://github.com/CDeenen/MaterialDeck_SD/releases
+
### v1.3.0 - 25-02-2021
Additions:
diff --git a/lang/en.json b/lang/en.json
index 799e810..9eb6784 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -164,6 +164,10 @@
"MaterialDeck.Perm.TOKEN.CONDITIONS.label": "Set Conditions",
"MaterialDeck.Perm.TOKEN.CONDITIONS.hint": "Allow the users to set conditions for the controlled token",
"MaterialDeck.Perm.TOKEN.CUSTOM.label": "Custom On-Click",
- "MaterialDeck.Perm.TOKEN.CUSTOM.hint": "Allow the users to set custom on-click functions"
+ "MaterialDeck.Perm.TOKEN.CUSTOM.hint": "Allow the users to set custom on-click functions",
+ "MaterialDeck.Perm.TOKEN.NON_OWNED.label": "Non-Owned and Non-Observer Tokens",
+ "MaterialDeck.Perm.TOKEN.NON_OWNED.hint": "Allow users access to tokens with non-owned or limited permission",
+ "MaterialDeck.Perm.TOKEN.OBSERVER.label": "Observer",
+ "MaterialDeck.Perm.TOKEN.OBSERVER.hint": "Allow users access to tokens with observer permission"
}
diff --git a/module.json b/module.json
index df16afd..76dc7b5 100644
--- a/module.json
+++ b/module.json
@@ -2,8 +2,8 @@
"name": "MaterialDeck",
"title": "Material Deck",
"description": "Material Deck allows you to control Foundry using an Elgato Stream Deck",
- "version": "1.3.0",
- "minimumSDversion": "1.3.0",
+ "version": "1.3.1",
+ "minimumSDversion": "1.3.1",
"minimumMSversion": "1.0.2",
"author": "CDeenen",
"esmodules": [
diff --git a/src/playlist.js b/src/playlist.js
index a8033c1..44b4056 100644
--- a/src/playlist.js
+++ b/src/playlist.js
@@ -23,38 +23,30 @@ export class PlaylistControl{
return;
}
this.active = true;
- if (settings.playlistMode == undefined) settings.playlistMode = 'playlist';
- if (settings.playlistMode == 'playlist'){
+ const mode = settings.playlistMode ? settings.playlistMode : 'playlist';
+ if (mode == 'playlist'){
this.updatePlaylist(settings,context);
}
- else if (settings.playlistMode == 'track'){
+ else if (mode == 'track'){
this.updateTrack(settings,context);
}
else {
- let src = 'modules/MaterialDeck/img/playlist/stop.png';
- if (game.playlists.playing.length > 0)
- streamDeck.setIcon(context,src,settings.background,2,'#00FF00',true);
- else
- streamDeck.setIcon(context,src,settings.background,1,'#000000',true);
+ const src = 'modules/MaterialDeck/img/playlist/stop.png';
+ const ringColor = (game.playlists.playing.length > 0) ? '#00FF00' : '#000000';
+ const ring = (game.playlists.playing.length > 0) ? 2 : 1;
+ const txt = settings.displayPlaylistName ? this.getPlaylist(this.playlistOffset).name : '';
+ streamDeck.setIcon(context,src,settings.background,ring,ringColor,true);
+ streamDeck.setTitle(txt,context);
}
}
updatePlaylist(settings,context){
let name = "";
-
- let background = settings.background;
- if(background == undefined) background = '#000000';
-
let ringColor = "#000000"
-
- let ringOffColor = settings.offRing;
- if (ringOffColor == undefined) ringOffColor = '#FF0000';
-
- let ringOnColor = settings.onRing;
- if (ringOnColor == undefined) ringOnColor = '#00FF00';
-
- let playlistType = settings.playlistType;
- if (playlistType == undefined) playlistType = 'playStop';
+ const background = settings.background ? settings.background : '#000000';
+ const ringOffColor = settings.offRing ? settings.offRing : '#FF0000';
+ const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
+ const playlistType = settings.playlistType ? settings.playlistType : 'playStop';
//Play/Stop
if (playlistType == 'playStop'){
@@ -79,26 +71,28 @@ export class PlaylistControl{
if (isNaN(playlistOffset)) playlistOffset = 0;
if (playlistOffset == this.playlistOffset) ringColor = ringOnColor;
}
+ //Relative Offset
+ else if (playlistType == 'relativeOffset') {
+ let playlistOffset = parseInt(settings.offset);
+ if (isNaN(playlistOffset)) playlistOffset = 0;
+ let number = parseInt(this.playlistOffset + playlistOffset);
+ const nrOfPlaylists = parseInt(game.settings.get(MODULE.moduleName,'playlists').playlistNumber);
+ if (number < 0) number += nrOfPlaylists;
+ else if (number > nrOfPlaylists) number -= nrOfPlaylists;
+ const targetPlaylist = this.getPlaylist(number);
+ if (targetPlaylist != undefined) name = targetPlaylist.name;
+ }
streamDeck.setIcon(context,"",background,2,ringColor);
streamDeck.setTitle(name,context);
}
updateTrack(settings,context){
let name = "";
-
- let background = settings.background;
- if(background == undefined) background = '#000000';
-
let ringColor = "#000000"
-
- let ringOffColor = settings.offRing;
- if (ringOffColor == undefined) ringOffColor = '#FF0000';
-
- let ringOnColor = settings.onRing;
- if (ringOnColor == undefined) ringOnColor = '#00FF00';
-
- let playlistType = settings.playlistType;
- if (playlistType == undefined) playlistType = 'playStop';
+ const background = settings.background ? settings.background : '#000000';
+ const ringOffColor = settings.offRing ? settings.offRing : '#FF0000';
+ const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
+ const playlistType = settings.playlistType ? settings.playlistType : 'playStop';
//Play/Stop
if (playlistType == 'playStop'){
@@ -125,11 +119,14 @@ export class PlaylistControl{
}
}
//Offset
- else {
+ else if (playlistType == 'offset') {
let trackOffset = parseInt(settings.offset);
if (isNaN(trackOffset)) trackOffset = 0;
if (trackOffset == this.trackOffset) ringColor = ringOnColor;
}
+ //Relative Offset
+ else if (playlistType == 'relativeOffset') {
+ }
streamDeck.setIcon(context,"",background,2,ringColor);
streamDeck.setTitle(name,context);
}
@@ -179,16 +176,17 @@ export class PlaylistControl{
trackNr--;
trackNr += this.trackOffset;
- if (settings.playlistMode == undefined) settings.playlistMode = 'playlist';
- if (settings.playlistType == undefined) settings.playlistType = 'playStop';
- if (settings.playlistMode == 'stopAll') {
+ const playlistMode = settings.playlistMode ? settings.playlistMode : 'playlist';
+ const playlistType = settings.playlistType ? settings.playlistType : 'playStop';
+
+ if (playlistMode == 'stopAll') {
this.stopAll(true);
}
else {
- if (settings.playlistType == 'playStop') {
+ if (playlistType == 'playStop') {
let playlist = this.getPlaylist(playlistNr);
if (playlist != undefined){
- if (settings.playlistMode == 'playlist')
+ if (playlistMode == 'playlist')
this.playPlaylist(playlist,playlistNr);
else {
let track = playlist.data.sounds[trackNr];
@@ -198,17 +196,34 @@ export class PlaylistControl{
}
}
}
- else {
- if (settings.playlistMode == 'playlist') {
+ else if (playlistType == 'offset'){
+ if (playlistMode == 'playlist') {
this.playlistOffset = parseInt(settings.offset);
if (isNaN(this.playlistOffset)) this.playlistOffset = 0;
}
- else {
+ else {
this.trackOffset = parseInt(settings.offset);
if (isNaN(this.trackOffset)) this.trackOffset = 0;
}
this.updateAll();
}
+ else if (playlistType == 'relativeOffset'){
+ if (playlistMode == 'playlist') {
+ let playlistOffset = parseInt(settings.offset);
+ if (isNaN(playlistOffset)) playlistOffset = 0;
+ let number = parseInt(this.playlistOffset + playlistOffset);
+ const nrOfPlaylists = parseInt(game.settings.get(MODULE.moduleName,'playlists').playlistNumber);
+ if (number < 0) number += nrOfPlaylists;
+ else if (number > nrOfPlaylists) number -= nrOfPlaylists;
+ this.playlistOffset = number;
+ }
+ else {
+ let value = parseInt(settings.offset);
+ if (isNaN(value)) return;
+ this.trackOffset += value;
+ }
+ this.updateAll();
+ }
}
}
diff --git a/src/settings.js b/src/settings.js
index b85a74c..2a79e0a 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -60,11 +60,13 @@ const defaultUserPermissions = {
VISION: [false,true,true,true],
WILDCARD: [false,true,true,true],
CONDITIONS: [false,true,true,true],
- CUSTOM: [false,false,true,true]
+ CUSTOM: [false,false,true,true],
+ NON_OWNED: [false,false,true,true],
+ OBSERVER: [false,true,true,true]
}
}
-export const registerSettings = function() {
+export const registerSettings = async function() {
/**
* Main settings
*/
@@ -75,7 +77,7 @@ export const registerSettings = function() {
name: "MaterialDeck.Sett.Enable",
scope: "client",
config: true,
- default: true,
+ default: false,
type: Boolean,
onChange: x => window.location.reload()
});
@@ -195,6 +197,21 @@ export const registerSettings = function() {
type: soundboardConfigForm,
restricted: false
});
+
+ let permissionSettings = game.settings.get(MODULE.moduleName,'userPermission');
+ if (permissionSettings == undefined || permissionSettings == null || MODULE.isEmpty(permissionSettings)) {
+ permissionSettings = {
+ enable: defaultEnable,
+ permissions: defaultUserPermissions
+ }
+ }
+ else {
+ if (permissionSettings.permissions.TOKEN.NON_OWNED == undefined) permissionSettings.permissions.TOKEN.NON_OWNED = [false,false,true,true];
+ if (permissionSettings.permissions.TOKEN.OBSERVER == undefined) permissionSettings.permissions.TOKEN.OBSERVER = [false,true,true,true];
+ }
+ game.settings.set(MODULE.moduleName,'userPermission',permissionSettings);
+
+
}
export class helpMenu extends FormApplication {
diff --git a/src/token.js b/src/token.js
index c122d94..80dc3ce 100644
--- a/src/token.js
+++ b/src/token.js
@@ -7,7 +7,7 @@ export class TokenControl{
this.wildcardOffset = 0;
}
- async update(tokenId){
+ async update(tokenId=null){
if (this.active == false) return;
for (let i=0; i<32; i++){
const data = streamDeck.buttonContext[i];
@@ -21,14 +21,35 @@ export class TokenControl{
const icon = settings.displayIcon ? settings.displayIcon : false;
const background = settings.background ? settings.background : "#000000";
let stats = settings.stats ? settings.stats : 'none';
-
+ const selection = settings.selection ? settings.selection : 'selected';
+ const tokenIdentifier = settings.tokenName ? settings.tokenName : '';
+
+ let validToken = false;
+ let token;
+ if (selection == 'selected') token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
+ else if (selection != 'selected' && tokenIdentifier == '') {}
+ else if (selection == 'tokenName') token = canvas.tokens.children[0].children.find(p => p.name == tokenIdentifier);
+ else if (selection == 'actorName') token = canvas.tokens.children[0].children.find(p => p.actor.name == tokenIdentifier);
+ else if (selection == 'tokenId') token = canvas.tokens.children[0].children.find(p => p.id == tokenIdentifier);
+ else if (selection == 'actorId') token = canvas.tokens.children[0].children.find(p => p.actor.id == tokenIdentifier);
+
+ if (token != undefined) validToken = true;
+
let tokenName = "";
let txt = "";
let iconSrc = "";
let overlay = false;
let statsOld;
- if (tokenId != undefined) {
- const token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
+ if (validToken) {
+ if (token.owner == false && token.observer == true && MODULE.getPermission('TOKEN','OBSERVER') == false ) {
+ streamDeck.noPermission(context);
+ return;
+ }
+ if (token.owner == false && token.observer == false && MODULE.getPermission('TOKEN','NON_OWNED') == false ) {
+ streamDeck.noPermission(context);
+ return;
+ }
+
tokenName = token.data.name;
if (name) txt += tokenName;
if (name && stats != 'none') txt += "\n";
@@ -49,7 +70,7 @@ export class TokenControl{
stats = 'none';
}
- iconSrc = token.data.img;
+ if (icon) iconSrc = token.data.img;
if (stats == 'custom'){
const custom = settings.custom ? settings.custom : '';
@@ -432,20 +453,39 @@ export class TokenControl{
}
async keyPress(settings){
- if (MODULE.selectedTokenId == undefined) return;
const tokenId = MODULE.selectedTokenId;
- const token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
+ const selection = settings.selection ? settings.selection : 'selected';
+ const tokenIdentifier = settings.tokenName ? settings.tokenName : '';
+
+ let token;
+ if (selection == 'selected') token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
+ else if (selection != 'selected' && tokenIdentifier == '') {}
+ else if (selection == 'tokenName') token = canvas.tokens.children[0].children.find(p => p.name == tokenIdentifier);
+ else if (selection == 'actorName') token = canvas.tokens.children[0].children.find(p => p.actor.name == tokenIdentifier);
+ else if (selection == 'tokenId') token = canvas.tokens.children[0].children.find(p => p.id == tokenIdentifier);
+ else if (selection == 'actorId') token = canvas.tokens.children[0].children.find(p => p.actor.id == tokenIdentifier);
+
if (token == undefined) return;
+ if (token.owner == false && token.observer == true && MODULE.getPermission('TOKEN','OBSERVER') == false ) return;
+ if (token.owner == false && token.observer == false && MODULE.getPermission('TOKEN','NON_OWNED') == false ) return;
const onClick = settings.onClick ? settings.onClick : 'doNothing';
if (onClick == 'doNothing') //Do nothing
return;
+ else if (onClick == 'select'){ //select token
+ token.control();
+ }
else if (onClick == 'center'){ //center on token
let location = token.getCenter(token.x,token.y);
canvas.animatePan(location);
}
+ else if (onClick == 'centerSelect'){ //center on token and select
+ const location = token.getCenter(token.x,token.y);
+ canvas.animatePan(location);
+ token.control();
+ }
else if (onClick == 'charSheet'){ //Open character sheet
const element = document.getElementById(token.actor.sheet.id);
if (element == null) token.actor.sheet.render(true);