v1.4.5
This commit is contained in:
@@ -33,6 +33,8 @@ let controlTokenTimer;
|
||||
export let sdVersion;
|
||||
export let msVersion;
|
||||
|
||||
let updateDialog;
|
||||
|
||||
//CONFIG.debug.hooks = true;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -86,8 +88,8 @@ async function analyzeWSmessage(msg){
|
||||
|
||||
sdVersion = data.version;
|
||||
|
||||
if (data.version < minimumSDversion) {
|
||||
let d = new Dialog({
|
||||
if (data.version < minimumSDversion && updateDialog == undefined) {
|
||||
updateDialog = new Dialog({
|
||||
title: "Material Deck: Update Needed",
|
||||
content: "<p>The Stream Deck plugin version you're using is v" + data.version + ", which is incompatible with this verion of the module.<br>Update to v" + minimumSDversion + " or newer.</p>",
|
||||
buttons: {
|
||||
@@ -103,7 +105,7 @@ async function analyzeWSmessage(msg){
|
||||
},
|
||||
default: "download"
|
||||
});
|
||||
d.render(true);
|
||||
updateDialog.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,19 +227,22 @@ let messageCount = 0;
|
||||
*/
|
||||
function resetWS(){
|
||||
const maxMessages = game.settings.get(moduleName, 'nrOfConnMessages');
|
||||
if (maxMessages == 0 || maxMessages > messageCount) {
|
||||
messageCount++;
|
||||
const countString = maxMessages == 0 ? "" : " (" + messageCount + "/" + maxMessages + ")";
|
||||
if (wsOpen) {
|
||||
ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.Disconnected"));
|
||||
wsOpen = false;
|
||||
messageCount = 0;
|
||||
}
|
||||
else ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.ConnectFail") + countString);
|
||||
}
|
||||
|
||||
WSconnected = false;
|
||||
startWebsocket();
|
||||
}
|
||||
else if (ws.readyState == 3){
|
||||
if (maxMessages == 0 || maxMessages > messageCount) {
|
||||
messageCount++;
|
||||
const countString = maxMessages == 0 ? "" : " (" + messageCount + "/" + maxMessages + ")";
|
||||
ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.ConnectFail") + countString);
|
||||
}
|
||||
WSconnected = false;
|
||||
startWebsocket();
|
||||
}
|
||||
}
|
||||
|
||||
export function sendWS(txt){
|
||||
@@ -275,8 +280,6 @@ Hooks.once('ready', async()=>{
|
||||
registerSettings();
|
||||
enableModule = (game.settings.get(moduleName,'Enable')) ? true : false;
|
||||
|
||||
|
||||
|
||||
soundboard = new SoundboardControl();
|
||||
streamDeck = new StreamDeck();
|
||||
tokenControl = new TokenControl();
|
||||
@@ -365,12 +368,6 @@ Hooks.once('ready', async()=>{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (enableModule == false) return;
|
||||
if (getPermission('ENABLE') == false) {
|
||||
ready = true;
|
||||
@@ -472,6 +469,10 @@ Hooks.on('renderSidebarTab',(app)=>{
|
||||
if (enableModule == false || ready == false) return;
|
||||
if (otherControls != undefined) otherControls.updateAll(options);
|
||||
if (sceneControl != undefined) sceneControl.updateAll();
|
||||
if (document.getElementsByClassName("roll-type-select")[0] != undefined)
|
||||
document.getElementsByClassName("roll-type-select")[0].addEventListener('change',function(){
|
||||
if (otherControls != undefined) otherControls.updateAll(options);
|
||||
})
|
||||
});
|
||||
|
||||
Hooks.on('closeSidebarTab',(app)=>{
|
||||
@@ -579,3 +580,7 @@ Hooks.once('init', ()=>{
|
||||
Hooks.once('canvasReady',()=>{
|
||||
ready = true;
|
||||
});
|
||||
|
||||
Hooks.on("soundscape", (data) => {
|
||||
externalModules.newSoundscapeData(data);
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
I created a <a href="https://discord.gg/3hd4G6TkmA">Discord server</a> to discuss this and other hardware-based Foundry modules. Feel free to join if you'd like to join the discussion and be updated on this module.
|
||||
|
||||
[](https://youtu.be/7h5Ew8cJYxg "FoundryVTT Material Deck Demonstration")
|
||||
[](https://youtu.be/7h5Ew8cJYxg "FoundryVTT Material Deck Demonstration")
|
||||
|
||||
# Material Deck
|
||||
Material Deck is a Foundry VTT module that allows you to control certain Foundry functions using an Elgato Stream Deck. A Stream Deck is a device that has physical buttons with displays behind them. Material Deck uses this to, for example, control playlists, execute macros, display and control the combat tracker.<br><br>
|
||||
|
||||
24
changelog.md
24
changelog.md
@@ -1,4 +1,28 @@
|
||||
# Changelog Material Deck Module
|
||||
### v1.4.5 - 27-07-2021
|
||||
Fixes:
|
||||
<ul>
|
||||
<li>Combat Tracker Action => Turn Display: If only 'Display Round' was enabled, the vertical alignment would be off. This has been fixed.</li>
|
||||
<li>WebSocket client no longer creates duplicate connections</li>
|
||||
<li>Token Action => If 'Display Uses/Quantity' is enabled for an item that has no maximum uses/quantity, the uses/quantity border is now consistently black.</li>
|
||||
<li>Update dialog that appears if the SD plugin needs to be updated now only appears once</li>
|
||||
</ul>
|
||||
|
||||
Additions:
|
||||
<ul>
|
||||
<li>External Modules: Added support for the Soundscape module. Requires Soundscape v1.0.3</li>
|
||||
<li>Macro Action => Advanced Macros is now supported for calling macros with arguments</li>
|
||||
<li>Combat Tracker Action => Function: Added option to select the combatant after changing the turn</li>
|
||||
<li>Other Actions => Added 'Set Roll Mode' which sets the roll mode for all rolls to public, private gm, blind gm or self roll</li>
|
||||
<li>Added support for wfrp4e (thanks to sozin#8622 & eccobold#3541)</li>
|
||||
<li>Added DEVGUIDE.md to help developers add support for new systems (thanks to sozin#8622 & eccobold#3541)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<b>Compatible server app and SD plugin:</b><br>
|
||||
Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases <br>
|
||||
SD plugin v1.4.5 (<b>must be updated!</b>): https://github.com/CDeenen/MaterialDeck_SD/releases<br>
|
||||
|
||||
### v1.4.4 - 26-05-2021
|
||||
Fixes:
|
||||
<ul>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "MaterialDeck",
|
||||
"title": "Material Deck",
|
||||
"description": "Material Deck allows you to control Foundry using an Elgato Stream Deck",
|
||||
"version": "1.4.4",
|
||||
"version": "1.4.5",
|
||||
"author": "CDeenen",
|
||||
"authors": {
|
||||
"name": "CDeenen",
|
||||
@@ -22,7 +22,7 @@
|
||||
],
|
||||
"socket": true,
|
||||
"minimumCoreVersion": "0.7.5",
|
||||
"compatibleCoreVersion": "0.8.5",
|
||||
"compatibleCoreVersion": "0.8.8",
|
||||
"languages": [
|
||||
{
|
||||
"lang": "en",
|
||||
|
||||
@@ -127,16 +127,18 @@ export class CombatTracker{
|
||||
turn = combat.turn+1;
|
||||
}
|
||||
if (settings.displayRound) txt += "Round\n"+round;
|
||||
if (txt != "") txt += "\n";
|
||||
if (settings.displayRound && settings.displayTurn) txt += "\n";
|
||||
if (settings.displayTurn) txt += "Turn\n"+turn;
|
||||
}
|
||||
|
||||
streamDeck.setIcon(context,device,src,{background:background});
|
||||
streamDeck.setTitle(txt,context);
|
||||
}
|
||||
}
|
||||
|
||||
keyPress(settings,context,device){
|
||||
async keyPress(settings,context,device){
|
||||
const mode = settings.combatTrackerMode ? settings.combatTrackerMode : 'combatants';
|
||||
const selectCombatant = settings.selectCombatant ? settings.selectCombatant : false;
|
||||
const combat = game.combat;
|
||||
|
||||
if (mode == 'function'){
|
||||
@@ -169,11 +171,17 @@ export class CombatTracker{
|
||||
}
|
||||
if (game.combat.started == false) return;
|
||||
|
||||
if (ctFunction == 'nextTurn') game.combat.nextTurn();
|
||||
else if (ctFunction == 'prevTurn') game.combat.previousTurn();
|
||||
else if (ctFunction == 'nextRound') game.combat.nextRound();
|
||||
else if (ctFunction == 'prevRound') game.combat.previousRound();
|
||||
else if (ctFunction == 'endTurn' && game.combat.combatant.owner) game.combat.nextTurn();
|
||||
if (ctFunction == 'nextTurn') await game.combat.nextTurn();
|
||||
else if (ctFunction == 'prevTurn') await game.combat.previousTurn();
|
||||
else if (ctFunction == 'nextRound') await game.combat.nextRound();
|
||||
else if (ctFunction == 'prevRound') await game.combat.previousRound();
|
||||
else if (ctFunction == 'endTurn' && game.combat.combatant.owner) await game.combat.nextTurn();
|
||||
|
||||
if (selectCombatant) {
|
||||
const token = canvas.tokens.placeables.filter(token => token.id == game.combat.combatant.token.id)[0];
|
||||
if (token.can(game.userId,"control")) token.control();
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
const onClick = settings.onClick ? settings.onClick : 'doNothing';
|
||||
|
||||
358
src/external.js
358
src/external.js
@@ -1,9 +1,44 @@
|
||||
import {streamDeck} from "../MaterialDeck.js";
|
||||
|
||||
export class ExternalModules{
|
||||
soundscapeSettings = {
|
||||
channels: [],
|
||||
master: {
|
||||
mute: false,
|
||||
volume: 1,
|
||||
playing: false,
|
||||
name: 'Master'
|
||||
},
|
||||
soundboard: [],
|
||||
soundboardVolume: 1,
|
||||
playing: false
|
||||
}
|
||||
constructor(){
|
||||
this.active = false;
|
||||
this.gmScreenOpen = false;
|
||||
|
||||
let channelData = [];
|
||||
let soundboardData = [];
|
||||
for (let i=0; i<8; i++) {
|
||||
channelData.push({
|
||||
volume: 1,
|
||||
mute: false,
|
||||
solo: false,
|
||||
link: false,
|
||||
playing: false,
|
||||
pan: 1,
|
||||
name: ''
|
||||
})
|
||||
}
|
||||
for (let i=0; i<25; i++) {
|
||||
soundboardData.push({
|
||||
active: false,
|
||||
name: '',
|
||||
icon: ''
|
||||
})
|
||||
}
|
||||
this.soundscapeSettings.channels = channelData;
|
||||
this.soundscapeSettings.soundboard = soundboardData;
|
||||
}
|
||||
|
||||
async updateAll(data={}){
|
||||
@@ -33,6 +68,7 @@ export class ExternalModules{
|
||||
else if (module == 'notYourTurn') this.updateNotYourTurn(settings,context,device);
|
||||
else if (module == 'lockView') this.updateLockView(settings,context,device);
|
||||
else if (module == 'aboutTime') this.updateAboutTime(settings,context,device);
|
||||
else if (module == 'soundscape') this.updateSoundscape(settings,context,device);
|
||||
}
|
||||
|
||||
keyPress(settings,context,device){
|
||||
@@ -47,6 +83,7 @@ export class ExternalModules{
|
||||
else if (module == 'notYourTurn') this.keyPressNotYourTurn(settings,context,device);
|
||||
else if (module == 'lockView') this.keyPressLockView(settings,context,device);
|
||||
else if (module == 'aboutTime') this.keyPressAboutTime(settings,context,device);
|
||||
else if (module == 'soundscape') this.keyPressSoundscape(settings,context,device);
|
||||
}
|
||||
|
||||
getModuleEnable(moduleId){
|
||||
@@ -698,6 +735,327 @@ export class ExternalModules{
|
||||
game.Gametime.advanceTime({ hours: -1 });
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//Soundscape
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
updateSoundscape(settings,context,device) {
|
||||
|
||||
if (this.getModuleEnable("soundscape") == false) return;
|
||||
if (game.user.isGM == false) return;
|
||||
|
||||
const target = settings.soundscapeTarget ? settings.soundscapeTarget : 'mixer';
|
||||
let channel = settings.soundscapeChannel ? settings.soundscapeChannel : 1;
|
||||
|
||||
let background = '#000000';
|
||||
let ring = 0;
|
||||
let ringColor = '#000000';
|
||||
|
||||
let txt = '';
|
||||
let src = 'modules/MaterialDeck/img/transparant.png';
|
||||
let name = '';
|
||||
|
||||
if (target == 'mixer') {
|
||||
let mode = settings.soundscapeMixerMode ? settings.soundscapeMixerMode : 'startStopAll';
|
||||
const displayName = settings.soundscapeMixerName;
|
||||
const displayChannel = settings.soundscapeDisplayMixerChannel;
|
||||
|
||||
if (mode == 'startStopAll') {
|
||||
channel = 'master';
|
||||
mode = 'startStop';
|
||||
}
|
||||
|
||||
if (channel == 'Master') channel = 'master';
|
||||
|
||||
let channelSettings;
|
||||
if (channel == 'master') {
|
||||
channelSettings = this.soundscapeSettings.master;
|
||||
channelSettings.playing = this.soundscapeSettings.playing;
|
||||
channelSettings.name = 'Master';
|
||||
}
|
||||
else channelSettings = this.soundscapeSettings.channels[channel-1];
|
||||
if (displayChannel && channel == 'master') txt += 'Master';
|
||||
else if (displayChannel) txt += channel;
|
||||
|
||||
if (mode == 'startStop') {
|
||||
if (displayChannel) txt += '\n';
|
||||
if (channelSettings.playing) {
|
||||
src = "fas fa-stop";
|
||||
ringColor = '#00ff00';
|
||||
}
|
||||
else {
|
||||
src = "fas fa-play";
|
||||
ringColor = '#006600';
|
||||
}
|
||||
ring=2;
|
||||
|
||||
}
|
||||
else if (mode == 'mute') {
|
||||
if (displayChannel) txt += '\n';
|
||||
txt += 'M';
|
||||
if (displayName) txt += '\n';
|
||||
ring=2;
|
||||
ringColor = '#ff0000';
|
||||
background = channelSettings.mute ? '#ff0000' : '#660000'
|
||||
}
|
||||
else if (mode == 'solo') {
|
||||
if (channel == 'master') return;
|
||||
if (displayChannel) txt += '\n';
|
||||
txt += 'S';
|
||||
if (displayName) txt += '\n';
|
||||
ring=2;
|
||||
ringColor = '#ffff00';
|
||||
background = channelSettings.solo ? '#ffff00' : '#666600'
|
||||
}
|
||||
else if (mode == 'link') {
|
||||
if (channel == 'master') return;
|
||||
if (displayChannel) txt += '\n';
|
||||
txt += 'L';
|
||||
if (displayName) txt += '\n';
|
||||
ring=2;
|
||||
ringColor = '#00ffff';
|
||||
background = channelSettings.link ? '#00ffff' : '#000066'
|
||||
}
|
||||
else if (displayName && displayChannel) txt += '\n';
|
||||
|
||||
if (displayName) txt += channelSettings.name;
|
||||
|
||||
if (mode == 'volume') {
|
||||
const displayValue = settings.soundscapeDisplayMixerValue;
|
||||
const volume = Math.floor(channelSettings.volume*100);
|
||||
if (displayValue && (displayName || displayChannel)) txt += '\n';
|
||||
if (displayValue) txt += volume;
|
||||
}
|
||||
|
||||
}
|
||||
else if (target == 'soundboard') {
|
||||
const displayName = settings.soundscapeSoundboardName;
|
||||
const displayIcon = settings.soundscapeSoundboardIcon;
|
||||
const displayChannel = settings.soundscapeDisplayChannel;
|
||||
const displayValue = settings.soundscapeSoundboardValue;
|
||||
const mode = settings.soundscapeSoundboardMode ? settings.soundscapeSoundboardMode : 'play';
|
||||
|
||||
if (mode == 'play') {
|
||||
channel -= 1;
|
||||
let sound = this.soundscapeSettings.soundboard[channel];
|
||||
|
||||
if (displayChannel) txt += channel+1;
|
||||
if (displayChannel && displayName) txt += '\n';
|
||||
if (displayName) txt += sound.name;
|
||||
if (displayIcon) src = sound.icon;
|
||||
}
|
||||
else if (mode == 'volume') {
|
||||
const volume = Math.floor(this.soundscapeSettings.soundboardVolume*100);
|
||||
if (displayValue) txt += volume;
|
||||
}
|
||||
else if (mode == 'stop') {
|
||||
src = 'modules/MaterialDeck/img/playlist/stop.png';
|
||||
}
|
||||
}
|
||||
|
||||
streamDeck.setTitle(txt,context);
|
||||
streamDeck.setIcon(context,device,src,{background:background,ring:ring,ringColor:ringColor});
|
||||
}
|
||||
|
||||
async keyPressSoundscape(settings,context,device) {
|
||||
if (this.getModuleEnable("soundscape") == false) return;
|
||||
if (game.user.isGM == false) return;
|
||||
|
||||
const target = settings.soundscapeTarget ? settings.soundscapeTarget : 'mixer';
|
||||
let channel = settings.soundscapeChannel ? settings.soundscapeChannel : 1;
|
||||
|
||||
if (target == 'mixer') {
|
||||
const mode = settings.soundscapeMixerMode ? settings.soundscapeMixerMode : 'startStopAll';
|
||||
|
||||
if (mode == 'startStopAll') {
|
||||
const playing = !this.soundscapeSettings.playing;
|
||||
if (playing) {
|
||||
Hooks.call('setSoundscape',{"msgType":"start","channelNr":undefined});
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Hooks.call('setSoundscape',{"msgType":"stop","channelNr":undefined});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel == 'Master') channel = 'master';
|
||||
|
||||
let channelSettings;
|
||||
if (channel == 'master') {
|
||||
channelSettings = this.soundscapeSettings.master;
|
||||
channelSettings.playing = this.soundscapeSettings.playing;
|
||||
}
|
||||
else channelSettings = this.soundscapeSettings.channels[channel-1];
|
||||
|
||||
let mute, solo, link, playing;
|
||||
let setChannel = false;
|
||||
|
||||
if (mode == 'startStop') {
|
||||
setChannel = true;
|
||||
playing = !channelSettings.playing;
|
||||
if (channel == 'master' && playing) {
|
||||
Hooks.call('setSoundscape',{"msgType":"stop"});
|
||||
return;
|
||||
}
|
||||
else if (channel == 'master') {
|
||||
Hooks.call('setSoundscape',{"msgType":"start"});
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (mode == 'mute') {
|
||||
setChannel = true;
|
||||
mute = !channelSettings.mute;
|
||||
}
|
||||
else if (mode == 'solo') {
|
||||
if (channel == 'master') return;
|
||||
setChannel = true;
|
||||
solo = !channelSettings.solo;
|
||||
}
|
||||
else if (mode == 'link') {
|
||||
if (channel == 'master') return;
|
||||
setChannel = true;
|
||||
link = !channelSettings.link;
|
||||
}
|
||||
if (setChannel) {
|
||||
const channelNr = channel == 'master' ? 'master' : channel-1;
|
||||
const payload = {
|
||||
"msgType": "setChannel",
|
||||
"channelNr": channelNr,
|
||||
mute,
|
||||
solo,
|
||||
link,
|
||||
playing
|
||||
};
|
||||
Hooks.call('setSoundscape',payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == 'volume') {
|
||||
const volumeMode = settings.soundscapeMixerValueMode ? settings.soundscapeMixerValueMode : 'incrementDecrement';
|
||||
const value = parseInt(settings.soundscapeMixerValue);
|
||||
if (isNaN(value) == false) {
|
||||
let volume = channelSettings.volume*100;
|
||||
|
||||
if (volumeMode == 'set')
|
||||
volume = value;
|
||||
else
|
||||
volume += value;
|
||||
|
||||
volume = Math.floor(volume*100)/10000;
|
||||
const channelNr = channel == 'master' ? 'master' : channel-1;
|
||||
const payload = {
|
||||
"msgType": "setVolume",
|
||||
"channelNr": channelNr,
|
||||
volume
|
||||
};
|
||||
Hooks.call('setSoundscape',payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (target == 'soundboard') {
|
||||
const mode = settings.soundscapeSoundboardMode ? settings.soundscapeSoundboardMode : 'play';
|
||||
|
||||
if (mode == 'play') {
|
||||
channel -= 1;
|
||||
const payload = {
|
||||
"msgType": "playSoundboard",
|
||||
channelNr: channel
|
||||
};
|
||||
Hooks.call('setSoundscape',payload);
|
||||
}
|
||||
else if (mode == 'volume') {
|
||||
const volumeMode = settings.soundscapeSoundboardValueMode ? settings.soundscapeSoundboardValueMode : 'incrementDecrement';
|
||||
const value = parseInt(settings.soundscapeSoundboardValue);
|
||||
if (isNaN(value) == false) {
|
||||
let volume = this.soundscapeSettings.soundboardVolume*100;
|
||||
|
||||
if (volumeMode == 'set')
|
||||
volume = value;
|
||||
else
|
||||
volume += value;
|
||||
|
||||
volume = Math.floor(volume*100)/10000;
|
||||
const payload = {
|
||||
"msgType": "setSoundboardVolume",
|
||||
volume
|
||||
};
|
||||
Hooks.call('setSoundscape',payload);
|
||||
}
|
||||
}
|
||||
else if (mode == 'stop') {
|
||||
const payload = {
|
||||
"msgType": "stopSoundboard"
|
||||
};
|
||||
Hooks.call('setSoundscape',payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newSoundscapeData(data) {
|
||||
let channel;
|
||||
if (data.channel != undefined) channel = data.channel;
|
||||
else if (data.channelNr != undefined) channel = data.channelNr;
|
||||
|
||||
let channelSettings = channel == 'master' ? this.soundscapeSettings.master : this.soundscapeSettings.channels[channel]
|
||||
if (data.msgType == 'soundConfig') {
|
||||
|
||||
let newChannelSettings = {
|
||||
volume: data.data.settings.volume,
|
||||
mute: data.data.settings.mute,
|
||||
solo: data.data.settings.solo,
|
||||
link: data.data.settings.link,
|
||||
playing: false,
|
||||
pan: data.data.settings.pan,
|
||||
name: data.data.settings.name
|
||||
};
|
||||
this.soundscapeSettings.channels[channel] = newChannelSettings;
|
||||
}
|
||||
else if (data.msgType == 'setMute') channelSettings.mute = data.mute;
|
||||
else if (data.msgType == 'setSolo') channelSettings.solo = data.solo;
|
||||
else if (data.msgType == 'setLink') channelSettings.link = data.link;
|
||||
else if (data.msgType == 'setVolume') {
|
||||
if (channel >= 100) return;
|
||||
channelSettings.volume = data.volume;
|
||||
}
|
||||
else if (data.msgType == 'start') {
|
||||
this.soundscapeSettings.playing = true;
|
||||
this.soundscapeSettings.master.playing = true;
|
||||
if (data.channel == undefined) for (let i=0; i<8; i++) this.soundscapeSettings.channels[i].playing = true;
|
||||
else this.soundscapeSettings.channels[data.channel].playing = true;
|
||||
}
|
||||
else if (data.msgType == 'stop') {
|
||||
|
||||
if (data.channel == undefined) {
|
||||
for (let i=0; i<8; i++) this.soundscapeSettings.channels[i].playing = false;
|
||||
this.soundscapeSettings.playing = false;
|
||||
}
|
||||
else {
|
||||
this.soundscapeSettings.channels[data.channel].playing = false;
|
||||
let check = 0;
|
||||
for (let i=0; i<8; i++) if (this.soundscapeSettings.channels[data.channel].playing) check++;
|
||||
if (check == 0) this.soundscapeSettings.playing = false;
|
||||
}
|
||||
}
|
||||
else if (data.msgType == 'sbSoundConfig') {
|
||||
|
||||
const channel = data.channel - 100;
|
||||
let active = true;
|
||||
if (data.data.soundArray == undefined) active = false;
|
||||
this.soundscapeSettings.soundboard[channel] = {
|
||||
active,
|
||||
name: data.data.name,
|
||||
icon: data.data.imageSrc
|
||||
};
|
||||
}
|
||||
else if (data.msgType == 'setSoundboardVolume')
|
||||
this.soundscapeSettings.soundboardVolume = data.volume;
|
||||
|
||||
this.updateAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
10
src/macro.js
10
src/macro.js
@@ -180,7 +180,12 @@ export class MacroControl{
|
||||
let furnaceEnabled = false;
|
||||
let furnace = game.modules.get("furnace");
|
||||
if (furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) furnaceEnabled = true;
|
||||
|
||||
let advancedMacros = game.modules.get("advanced-macros");
|
||||
if (advancedMacros != undefined && advancedMacros.active) furnaceEnabled = true;
|
||||
|
||||
if (args == "" || args == " ") furnaceEnabled = false;
|
||||
|
||||
if (furnaceEnabled == false) macro.execute({token:target});
|
||||
else {
|
||||
let chatData = {
|
||||
@@ -235,7 +240,12 @@ export class MacroControl{
|
||||
let furnaceEnabled = false;
|
||||
let furnace = game.modules.get("furnace");
|
||||
if (furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) furnaceEnabled = true;
|
||||
|
||||
let advancedMacros = game.modules.get("advanced-macros");
|
||||
if (advancedMacros != undefined && advancedMacros.active) furnaceEnabled = true;
|
||||
|
||||
if (args == undefined || args[macroNumber] == undefined || args[macroNumber] == "") furnaceEnabled = false;
|
||||
|
||||
if (furnaceEnabled == false) macro.execute();
|
||||
else {
|
||||
let chatData = {
|
||||
|
||||
18
src/misc.js
18
src/misc.js
@@ -183,7 +183,8 @@ export class macroConfigForm extends FormApplication {
|
||||
let furnaceEnabled = false;
|
||||
let height = 95;
|
||||
let furnace = game.modules.get("furnace");
|
||||
if (furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) {
|
||||
let advancedMacros = game.modules.get("advanced-macros");
|
||||
if ((furnace != undefined && furnace.active && compatibleCore("0.8.1")==false) || (advancedMacros != undefined && advancedMacros.active)) {
|
||||
furnaceEnabled = true;
|
||||
height += 50;
|
||||
}
|
||||
@@ -1056,12 +1057,23 @@ export class downloadUtility extends FormApplication {
|
||||
}
|
||||
if (this.localMSversion == undefined) this.localMSversion = 'unknown';
|
||||
|
||||
let minimumSdVersion;
|
||||
let minimumMsVersion;
|
||||
if (compatibleCore("0.8.5")) {
|
||||
minimumSdVersion = game.modules.get("MaterialDeck").data.flags.minimumSDversion.replace('v','');
|
||||
minimumMsVersion = game.modules.get("MaterialDeck").data.flags.minimumMSversion;
|
||||
}
|
||||
else {
|
||||
minimumSdVersion = game.modules.get("MaterialDeck").data.minimumSDversion.replace('v','');
|
||||
minimumMsVersion = game.modules.get("MaterialDeck").data.minimumMSversion;
|
||||
}
|
||||
|
||||
return {
|
||||
minimumSdVersion: game.modules.get("MaterialDeck").data.minimumSDversion.replace('v',''),
|
||||
minimumSdVersion,
|
||||
localSdVersion: this.localSDversion,
|
||||
masterSdVersion: this.masterSDversion,
|
||||
sdDlDisable: this.masterSDversion == undefined,
|
||||
minimumMsVersion: game.modules.get("MaterialDeck").data.minimumMSversion.replace('v',''),
|
||||
minimumMsVersion,
|
||||
localMsVersion: this.localMSversion,
|
||||
masterMsVersion: this.masterMSversion,
|
||||
msDlDisable: this.masterMSversion == undefined,
|
||||
|
||||
@@ -54,6 +54,8 @@ export class OtherControls{
|
||||
this.updateChatMessage(settings,context,device,options);
|
||||
else if (mode == 'rollOptions')
|
||||
this.updateRollOptions(settings,context,device,options);
|
||||
else if (mode == 'rollMode')
|
||||
this.updateRollMode(settings,context,device,options);
|
||||
}
|
||||
|
||||
keyPress(settings,context,device){
|
||||
@@ -83,6 +85,8 @@ export class OtherControls{
|
||||
this.keyPressChatMessage(settings);
|
||||
else if (mode == 'rollOptions')
|
||||
this.keyPressRollOptions(settings);
|
||||
else if (mode == 'rollMode')
|
||||
this.keyPressRollMode(settings);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -812,4 +816,23 @@ export class OtherControls{
|
||||
this.updateAll();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
updateRollMode(settings,context,device,options={}){
|
||||
const background = settings.background ? settings.background : '#000000';
|
||||
const ringOffColor = settings.offRing ? settings.offRing : '#000000';
|
||||
const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
|
||||
const iconSrc = "modules/MaterialDeck/img/other/d20.png";
|
||||
const rollMode = settings.rollMode ? settings.rollMode : 'roll';
|
||||
const ringColor = (rollMode == game.settings.get('core','rollMode')) ? ringOnColor : ringOffColor;
|
||||
streamDeck.setTitle("",context);
|
||||
streamDeck.setIcon(context,device,iconSrc,{background:background,ring:2,ringColor:ringColor,overlay:true});
|
||||
}
|
||||
|
||||
async keyPressRollMode(settings){
|
||||
const rollMode = settings.rollMode ? settings.rollMode : 'roll';
|
||||
await game.settings.set('core','rollMode',rollMode);
|
||||
this.updateAll();
|
||||
}
|
||||
}
|
||||
@@ -452,11 +452,16 @@ export class StreamDeck{
|
||||
}
|
||||
ctx.drawImage(img, xStart+margin, yStart+margin, renderableWidth - 2*margin, renderableHeight - 2*margin);
|
||||
if (uses != undefined && uses.heart == undefined) {
|
||||
|
||||
let txt = '';
|
||||
let noMaxUses = false;
|
||||
if (uses.available != undefined) {
|
||||
txt = uses.available;
|
||||
if (uses.maximum != undefined) txt = uses.available + '/' + uses.maximum;
|
||||
if (uses.maximum == undefined ) uses.maximum = 1;
|
||||
if (uses.maximum == undefined ) {
|
||||
uses.maximum = 1;
|
||||
noMaxUses = true;
|
||||
}
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 4;
|
||||
@@ -466,7 +471,8 @@ export class StreamDeck{
|
||||
if (green.length == 1) green = "0"+green;
|
||||
red = red.toString(16);
|
||||
if (red.length == 1) red = "0"+red;
|
||||
if (uses.available == 0) ctx.strokeStyle = "#c80000";
|
||||
if (noMaxUses) ctx.strokeStyle = "#c000000";
|
||||
else if (uses.available == 0) ctx.strokeStyle = "#c80000";
|
||||
else ctx.strokeStyle = "#"+red.toString(16)+green.toString(16)+"00";
|
||||
const rect = {height:35, paddingSides:20, paddingBottom: 4}
|
||||
ctx.rect(rect.paddingSides, 144-rect.height-rect.paddingBottom,144-2*rect.paddingSides,rect.height);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {compatibleCore} from "../misc.js";
|
||||
import {otherControls} from "../../MaterialDeck.js";
|
||||
|
||||
export class pf2e{
|
||||
constructor(){
|
||||
@@ -102,6 +103,27 @@ export class pf2e{
|
||||
return this.getCondition(token,condition) != undefined;
|
||||
}
|
||||
|
||||
getValuedCondition(token,condition) {
|
||||
const effect = this.getCondition(token, condition);
|
||||
if (effect != undefined && effect?.data.value != null) return effect;
|
||||
}
|
||||
|
||||
async modifyValuedCondition(token,condition,delta) {
|
||||
const effect = this.getValuedCondition(token,condition);
|
||||
if (effect == undefined) {
|
||||
if (delta > 0) {
|
||||
const Condition = condition.charAt(0).toUpperCase() + condition.slice(1);
|
||||
const newCondition = game.pf2e.ConditionManager.getCondition(Condition);
|
||||
// newCondition.data.sources.hud = !0,
|
||||
await game.pf2e.ConditionManager.addConditionToToken(newCondition, token);
|
||||
}
|
||||
} else {
|
||||
const currentValue = effect.value;
|
||||
console.log(`Current Value: ${currentValue}`);
|
||||
await game.pf2e.ConditionManager.updateConditionValue(effect, token, currentValue+delta);
|
||||
}
|
||||
}
|
||||
|
||||
async toggleCondition(token,condition) {
|
||||
if (condition == undefined) condition = 'removeAll';
|
||||
if (condition == 'removeAll'){
|
||||
@@ -113,7 +135,7 @@ export class pf2e{
|
||||
if (effect == undefined) {
|
||||
const Condition = condition.charAt(0).toUpperCase() + condition.slice(1);
|
||||
const newCondition = game.pf2e.ConditionManager.getCondition(Condition);
|
||||
newCondition.data.sources.hud = !0,
|
||||
// newCondition.data.sources.hud = !0,
|
||||
await game.pf2e.ConditionManager.addConditionToToken(newCondition, token);
|
||||
}
|
||||
else {
|
||||
@@ -162,6 +184,7 @@ export class pf2e{
|
||||
*/
|
||||
getFeatures(token,featureType) {
|
||||
if (featureType == undefined) featureType = 'any';
|
||||
if (featureType == 'action') return this.getActions(token);
|
||||
const allItems = token.actor.items;
|
||||
if (featureType == 'any') return allItems.filter(i => i.type == 'class' || i.type == 'feat')
|
||||
else return allItems.filter(i => i.type == featureType)
|
||||
@@ -193,7 +216,20 @@ export class pf2e{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
getActions(token) {
|
||||
const allActions = token.actor.data.data.actions;
|
||||
return allActions.filter(a=>a.type==='strike');
|
||||
}
|
||||
|
||||
rollItem(item) {
|
||||
let variant = 0;
|
||||
if (otherControls.rollOption == 'map1') variant = 1;
|
||||
if (otherControls.rollOption == 'map2') variant = 2;
|
||||
if(item.type==='strike') return item.variants[variant].roll({event});
|
||||
if(item.type==='weapon') return item.parent.data.data.actions.find(a=>a.name===item.name).variants[variant].roll({event});
|
||||
return item.roll()
|
||||
}
|
||||
}
|
||||
@@ -221,6 +221,11 @@ export class TokenHelper{
|
||||
return this.system.getResilience(token)
|
||||
}
|
||||
|
||||
// /* PF2E */
|
||||
// getStrikes(token) {
|
||||
// return this.system.getStrikes(token);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Conditions
|
||||
*/
|
||||
@@ -236,6 +241,16 @@ export class TokenHelper{
|
||||
return this.system.toggleCondition(token,condition);
|
||||
}
|
||||
|
||||
/* PF2E */
|
||||
getValuedCondition(token,condition) {
|
||||
return this.system.getValuedCondition(token,condition);
|
||||
}
|
||||
|
||||
/* PF2E */
|
||||
modifyValuedCondition(token,condition,delta) {
|
||||
return this.system.modifyValuedCondition(token,condition,delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll
|
||||
*/
|
||||
|
||||
29
src/token.js
29
src/token.js
@@ -210,6 +210,21 @@ export class TokenControl{
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (settings.onClick == 'valuedCondition') { //modify valued condition
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) {
|
||||
streamDeck.noPermission(context,device);
|
||||
return;
|
||||
}
|
||||
ring = 1;
|
||||
overlay = true;
|
||||
if (icon == 'stats') {
|
||||
iconSrc = tokenHelper.getConditionIcon(settings.valuedCondition);
|
||||
if (tokenHelper.getValuedCondition(token,settings.valuedCondition)) {
|
||||
ring = 2;
|
||||
ringColor = "#FF7B00";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (settings.onClick == 'cubCondition') { //Combat Utility Belt conditions
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) {
|
||||
streamDeck.noPermission(context,device);
|
||||
@@ -344,6 +359,15 @@ export class TokenControl{
|
||||
overlay = true;
|
||||
if (icon == 'stats') iconSrc = tokenHelper.getConditionIcon(settings.condition);
|
||||
}
|
||||
else if (settings.onClick == 'valuedCondition') { //modify condition value
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) {
|
||||
streamDeck.noPermission(context,device);
|
||||
return;
|
||||
}
|
||||
ring = 1;
|
||||
overlay = true;
|
||||
if (icon == 'stats') iconSrc = tokenHelper.getConditionIcon(settings.condition);
|
||||
}
|
||||
else if (settings.onClick == 'cubCondition') { //Combat Utility Belt conditions
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) {
|
||||
streamDeck.noPermission(context,device);
|
||||
@@ -507,6 +531,11 @@ export class TokenControl{
|
||||
await tokenHelper.toggleCondition(token,settings.condition);
|
||||
this.update(tokenId);
|
||||
}
|
||||
else if (onClick == 'valuedCondition') { //Modify Valued condition
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return;
|
||||
await tokenHelper.modifyValuedCondition(token,settings.valuedCondition,settings.valuedConditionDelta);
|
||||
this.update(tokenId);
|
||||
}
|
||||
else if (onClick == 'cubCondition') { //Combat Utility Belt conditions
|
||||
if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return;
|
||||
const condition = settings.cubConditionName;
|
||||
|
||||
Reference in New Issue
Block a user