This commit is contained in:
CDeenen
2021-07-27 21:56:50 +02:00
committed by kyamsil
parent ce28b2da97
commit 6c840cbf59
13 changed files with 565 additions and 39 deletions

View File

@@ -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);
if (wsOpen) {
ui.notifications.warn("Material Deck: "+game.i18n.localize("MaterialDeck.Notifications.Disconnected"));
wsOpen = false;
messageCount = 0;
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();
}
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);
});

View File

@@ -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.
[![Youtube Video](https://github.com/CDeenen/Soundscape/tree/master/img/Youtube.png)](https://youtu.be/7h5Ew8cJYxg "FoundryVTT Material Deck Demonstration")
[![Youtube Video](https://github.com/CDeenen/MaterialDeck/blob/Master/wiki/img/Youtube.png)](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>

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 = {

View File

@@ -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,

View File

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

View File

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

View File

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

View File

@@ -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
*/

View File

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