This commit is contained in:
CDeenen
2021-05-26 02:02:47 +02:00
parent 07a3bdd837
commit ebdc1b5e5c
30 changed files with 2728 additions and 1257 deletions

View File

@@ -1,4 +1,3 @@
import * as MODULE from "../MaterialDeck.js";
import {streamDeck} from "../MaterialDeck.js";
export class ExternalModules{

View File

@@ -1,4 +1,4 @@
import * as MODULE from "../MaterialDeck.js";
import {sdVersion, msVersion, moduleName, getPermission, enableModule} from "../MaterialDeck.js";
import {macroControl,soundboard,playlistControl} from "../MaterialDeck.js";
export function compatibleCore(compatibleVersion){
@@ -36,12 +36,12 @@ export class playlistConfigForm extends FormApplication {
* Provide data to the template
*/
getData() {
if (MODULE.getPermission('PLAYLIST','CONFIGURE') == false ) {
if (getPermission('PLAYLIST','CONFIGURE') == false ) {
ui.notifications.warn(game.i18n.localize("MaterialDeck.Notifications.Playlist.NoPermission"));
return;
}
//Get the playlist settings
let settings = game.settings.get(MODULE.moduleName,'playlists');
let settings = game.settings.get(moduleName,'playlists');
//Get values from the settings, and check if they are defined
let selectedPlaylists = settings.selectedPlaylist;
@@ -125,8 +125,8 @@ export class playlistConfigForm extends FormApplication {
async updateSettings(settings,render){
if (game.user.isGM) {
await game.settings.set(MODULE.moduleName,'playlists', settings);
if (MODULE.enableModule) playlistControl.updateAll();
await game.settings.set(moduleName,'playlists', settings);
if (enableModule) playlistControl.updateAll();
if (render) this.render();
}
else {
@@ -146,6 +146,7 @@ export class macroConfigForm extends FormApplication {
constructor(data, options) {
super(data, options);
this.data = data;
this.page = 0;
}
/**
@@ -164,14 +165,14 @@ export class macroConfigForm extends FormApplication {
* Provide data to the template
*/
getData() {
if (MODULE.getPermission('MACRO','MACROBOARD_CONFIGURE') == false ) {
if (getPermission('MACRO','MACROBOARD_CONFIGURE') == false ) {
ui.notifications.warn(game.i18n.localize("MaterialDeck.Notifications.Macroboard.NoPermission"));
return;
}
//Get the settings
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,'macroSettings').args;
var selectedMacros = game.settings.get(moduleName,'macroSettings').macros;
var color = game.settings.get(moduleName,'macroSettings').color;
var args = game.settings.get(moduleName,'macroSettings').args;
//Check if the settings are defined
if (selectedMacros == undefined) selectedMacros = [];
@@ -187,28 +188,12 @@ export class macroConfigForm extends FormApplication {
height += 50;
}
//Check what SD model the user is using, and set the number of rows and columns to correspond
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;
let iteration = this.page*32;
let macroData = [];
for (let j=0; j<jMax; j++){
for (let j=0; j<4; j++){
let macroThis = [];
for (let i=0; i<iMax; i++){
for (let i=0; i<8; i++){
let colorData = color[iteration];
if (colorData != undefined){
let colorCorrect = true;
@@ -233,13 +218,15 @@ export class macroConfigForm extends FormApplication {
}
macroData.push({dataThis: macroThis});
}
return {
height: height,
macros: game.macros,
selectedMacros: selectedMacros,
macroData: macroData,
furnace: furnaceEnabled
furnace: furnaceEnabled,
macroRange: `${this.page*32 + 1} - ${this.page*32 + 32}`,
prevDisabled: this.page == 0 ? 'disabled' : '',
totalMacros: Math.max(Math.ceil(selectedMacros.length/32)*32, this.page*32 + 32)
}
}
@@ -254,27 +241,118 @@ export class macroConfigForm extends FormApplication {
activateListeners(html) {
super.activateListeners(html);
const navNext = html.find("button[id='navNext']");
const navPrev = html.find("button[id='navPrev']");
const clearAll = html.find("button[id='clearAll']");
const clearPage = html.find("button[id='clearPage']");
const importBtn = html.find("button[id='import']");
const exportBtn = html.find("button[id='export']");
const macro = html.find("select[name='macros']");
const args = html.find("input[name='args']");
const color = html.find("input[name='colorPicker']");
importBtn.on('click', async(event) => {
let importDialog = new importConfigForm();
importDialog.setData('macroboard',this)
importDialog.render(true);
});
exportBtn.on('click', async(event) => {
const settings = game.settings.get(moduleName,'macroSettings');
let exportDialog = new exportConfigForm();
exportDialog.setData(settings,'macroboard')
exportDialog.render(true);
});
navNext.on('click',async (event) => {
this.page++;
this.render(true);
});
navPrev.on('click',async (event) => {
const settings = game.settings.get(moduleName,'macroSettings');
this.page--;
if (this.page < 0) this.page = 0;
else {
const totalMacros = Math.ceil(settings.macros.length/32)*32;
if ((this.page + 2)*32 == totalMacros) {
let pageEmpty = this.getPageEmpty(totalMacros-32);
if (pageEmpty) {
await this.clearPage(totalMacros-32,true)
}
}
}
this.render(true);
});
clearAll.on('click',async (event) => {
const parent = this;
let d = new Dialog({
title: game.i18n.localize("MaterialDeck.ClearAll"),
content: game.i18n.localize("MaterialDeck.ClearAll_Content"),
buttons: {
continue: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("MaterialDeck.Continue"),
callback: async () => {
this.page = 0;
await parent.clearAllSettings();
parent.render(true);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("MaterialDeck.Cancel")
}
},
default: "cancel"
});
d.render(true);
})
clearPage.on('click',(event) => {
const parent = this;
let d = new Dialog({
title: game.i18n.localize("MaterialDeck.ClearPage"),
content: game.i18n.localize("MaterialDeck.ClearPage_Content"),
buttons: {
continue: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("MaterialDeck.Continue"),
callback: async () => {
await parent.clearPage(parent.page*32)
parent.render(true);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("MaterialDeck.Cancel")
}
},
default: "cancel"
});
d.render(true);
})
macro.on("change", event => {
let id = event.target.id.replace('macros','');
let settings = game.settings.get(MODULE.moduleName,'macroSettings');
let settings = game.settings.get(moduleName,'macroSettings');
settings.macros[id-1]=event.target.value;
this.updateSettings(settings);
});
args.on("change", event => {
let id = event.target.id.replace('args','');
let settings = game.settings.get(MODULE.moduleName,'macroSettings');
let settings = game.settings.get(moduleName,'macroSettings');
settings.args[id-1]=event.target.value;
this.updateSettings(settings);
});
color.on("change", event => {
let id = event.target.id.replace('colorpicker','');
let settings = game.settings.get(MODULE.moduleName,'macroSettings');
let settings = game.settings.get(moduleName,'macroSettings');
settings.color[id-1]=event.target.value;
this.updateSettings(settings);
});
@@ -282,8 +360,8 @@ export class macroConfigForm extends FormApplication {
async updateSettings(settings){
if (game.user.isGM) {
await game.settings.set(MODULE.moduleName,'macroSettings',settings);
if (MODULE.enableModule) macroControl.updateAll();
await game.settings.set(moduleName,'macroSettings',settings);
if (enableModule) macroControl.updateAll();
}
else {
const payload = {
@@ -293,6 +371,49 @@ export class macroConfigForm extends FormApplication {
game.socket.emit(`module.MaterialDeck`, payload);
}
}
getPageEmpty(pageStart) {
const settings = game.settings.get(moduleName,'macroSettings');
let pageEmpty = true;
for (let i=pageStart; i<pageStart+32; i++) {
if (settings.macros[i] != undefined && settings.macros[i] != null && settings.macros[i] != "") {
pageEmpty = false;
break;
}
}
return pageEmpty;
}
async clearPage(pageStart,remove=false) {
const settings = game.settings.get(moduleName,'macroSettings');
if (remove) {
await settings.macros.splice(pageStart,32);
await settings.color.splice(pageStart,32);
if (settings.args != undefined) await settings.args.splice(pageStart,32);
}
else {
for (let i=pageStart; i<pageStart+32; i++) {
settings.macros[i] = null;
settings.color[i] = "0";
if (settings.args != undefined) settings.args[i] = null;
}
}
await this.updateSettings(settings);
}
async clearAllSettings() {
let settings = {
macros: [],
color: [],
args: []
};
for (let i=0; i<32; i++) {
settings.macros[i] = null;
settings.color[i] = "0";
settings.args[i] = null;
}
await this.updateSettings(settings);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -301,9 +422,8 @@ export class soundboardConfigForm extends FormApplication {
constructor(data, options) {
super(data, options);
this.playlists = [];
this.iMax;
this.jMax;
this.settings = {};
this.page = 0;
}
/**
@@ -315,7 +435,7 @@ export class soundboardConfigForm extends FormApplication {
title: "Material Deck: "+game.i18n.localize("MaterialDeck.Sett.SoundboardConfig"),
template: "./modules/MaterialDeck/templates/soundboardConfig.html",
classes: ["sheet"],
height: 720
height: "auto"
});
}
@@ -323,13 +443,13 @@ export class soundboardConfigForm extends FormApplication {
* Provide data to the template
*/
getData() {
if (MODULE.getPermission('SOUNDBOARD','CONFIGURE') == false ) {
if (getPermission('SOUNDBOARD','CONFIGURE') == false ) {
ui.notifications.warn(game.i18n.localize("MaterialDeck.Notifications.Soundboard.NoPermission"));
return;
}
//Get the settings
this.settings = game.settings.get(MODULE.moduleName,'soundboardSettings');
this.settings = game.settings.get(moduleName,'soundboardSettings');
//Check if all settings are defined
if (this.settings.sounds == undefined) this.settings.sounds = [];
@@ -353,29 +473,13 @@ export class soundboardConfigForm extends FormApplication {
this.playlists = playlists;
//Check what SD model the user is using, and set the number of rows and columns to correspond
let streamDeckModel = game.settings.get(MODULE.moduleName,'streamDeckModel');
if (streamDeckModel == 0){
this.jMax = 6;
this.iMax = 3;
}
else if (streamDeckModel == 1){
this.jMax = 6;
this.iMax = 5;
}
else {
this.jMax = 8;
this.iMax = 8;
}
let iteration = 0; //Sound number
let iteration = this.page*16; //Sound number
let soundData = []; //Stores all the data for each sound
//Fill soundData. soundData is an array the size of jMax (nr of rows), with each array element containing an array the size of iMax (nr of columns)
for (let j=0; j<this.jMax; j++){
//Fill soundData
for (let j=0; j<2; j++){
let soundsThis = []; //Stores row data
for (let i=0; i<this.iMax; i++){
for (let i=0; i<8; i++){
//Each iteration gets the data for each sound
//If the volume is undefined for this sound, define it and set it to its default value
@@ -453,7 +557,10 @@ export class soundboardConfigForm extends FormApplication {
return {
soundData: soundData,
playlists
playlists,
soundRange: `${this.page*16 + 1} - ${this.page*16 + 16}`,
prevDisabled: this.page == 0 ? 'disabled' : '',
totalSounds: this.settings.volume.length
}
}
@@ -468,6 +575,12 @@ export class soundboardConfigForm extends FormApplication {
async activateListeners(html) {
super.activateListeners(html);
const navNext = html.find("button[id='navNext']");
const navPrev = html.find("button[id='navPrev']");
const clearAll = html.find("button[id='clearAll']");
const clearPage = html.find("button[id='clearPage']");
const importBtn = html.find("button[id='import']");
const exportBtn = html.find("button[id='export']");
const nameField = html.find("input[name='namebox']");
const playlistSelect = html.find("select[name='playlist']");
const soundSelect = html.find("select[name='sounds']");
@@ -478,6 +591,89 @@ export class soundboardConfigForm extends FormApplication {
const playMode = html.find("select[name='mode']");
const volume = html.find("input[name='volume']");
importBtn.on('click', async(event) => {
let importDialog = new importConfigForm();
importDialog.setData('soundboard',this)
importDialog.render(true);
});
exportBtn.on('click', async(event) => {
const settings = game.settings.get(moduleName,'soundboardSettings');
let exportDialog = new exportConfigForm();
exportDialog.setData(settings,'soundboard')
exportDialog.render(true);
});
navNext.on('click',async (event) => {
this.page++;
this.render(true);
});
navPrev.on('click',async (event) => {
this.page--;
if (this.page < 0) this.page = 0;
else {
const totalSounds = this.settings.volume.length;
if ((this.page + 2)*16 == totalSounds) {
let pageEmpty = this.getPageEmpty(totalSounds-16);
if (pageEmpty) {
await this.clearPage(totalSounds-16,true)
}
}
}
this.render(true);
});
clearAll.on('click',async (event) => {
const parent = this;
let d = new Dialog({
title: game.i18n.localize("MaterialDeck.ClearAll"),
content: game.i18n.localize("MaterialDeck.ClearAll_Content"),
buttons: {
continue: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("MaterialDeck.Continue"),
callback: async () => {
this.page = 0;
await parent.clearAllSettings();
parent.render(true);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("MaterialDeck.Cancel")
}
},
default: "cancel"
});
d.render(true);
})
clearPage.on('click',(event) => {
const parent = this;
let d = new Dialog({
title: game.i18n.localize("MaterialDeck.ClearPage"),
content: game.i18n.localize("MaterialDeck.ClearPage_Content"),
buttons: {
continue: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("MaterialDeck.Continue"),
callback: async () => {
await parent.clearPage(parent.page*16)
parent.render(true);
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("MaterialDeck.Cancel")
}
},
default: "cancel"
});
d.render(true);
})
nameField.on("change",event => {
let id = event.target.id.replace('name','')-1;
this.settings.name[id]=event.target.value;
@@ -595,8 +791,8 @@ export class soundboardConfigForm extends FormApplication {
async updateSettings(settings){
if (game.user.isGM) {
await game.settings.set(MODULE.moduleName,'soundboardSettings',settings);
if (MODULE.enableModule) soundboard.updateAll();
await game.settings.set(moduleName,'soundboardSettings',settings);
if (enableModule) soundboard.updateAll();
}
else {
const payload = {
@@ -606,4 +802,375 @@ export class soundboardConfigForm extends FormApplication {
game.socket.emit(`module.MaterialDeck`, payload);
}
}
getPageEmpty(pageStart) {
let pageEmpty = true;
for (let i=pageStart; i<pageStart+16; i++) {
const name = this.settings.name[i];
const playlist = this.settings.selectedPlaylists[i];
const sound = this.settings.sounds[i];
if ((name != "" && name != null) || playlist != undefined || sound != undefined) {
pageEmpty = false;
break;
}
}
return pageEmpty;
}
async clearPage(pageStart,remove=false) {
if (remove) {
await this.settings.sounds.splice(pageStart,16);
await this.settings.colorOn.splice(pageStart,16);
await this.settings.colorOff.splice(pageStart,16);
await this.settings.mode.splice(pageStart,16);
await this.settings.img.splice(pageStart,16);
await this.settings.volume.splice(pageStart,16);
await this.settings.name.splice(pageStart,16);
await this.settings.selectedPlaylists.splice(pageStart,16);
await this.settings.src.splice(pageStart,16);
await this.settings.sounds.splice(pageStart,16);
}
else {
for (let i=pageStart; i<pageStart+16; i++) {
this.settings.sounds[i] = null;
this.settings.colorOn[i] = null;
this.settings.colorOff[i] = null;
this.settings.mode[i] = null;
this.settings.img[i] = null;
this.settings.volume[i] = null;
this.settings.name[i] = null;
this.settings.selectedPlaylists[i] = null;
this.settings.src[i] = null;
this.settings.sounds[i] = null;
}
}
await this.updateSettings(this.settings);
}
async clearAllSettings() {
let array = [];
for (let i=0; i<16; i++) array[i] = "";
let arrayVolume = [];
for (let i=0; i<16; i++) arrayVolume[i] = "50";
let arrayZero = [];
for (let i=0; i<16; i++) arrayZero[i] = "0";
const settings = {
playlist: "",
sounds: array,
colorOn: arrayZero,
colorOff: arrayZero,
mode: arrayZero,
toggle: arrayZero,
volume: arrayVolume
};
await this.updateSettings(settings);
}
}
export class exportConfigForm extends FormApplication {
constructor(data, options) {
super(data, options);
this.data = {};
this.name = "";
this.source = "";
}
/**
* Default Options for this FormApplication
*/
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
id: "MD_Export",
title: "Material Deck: " + game.i18n.localize("MaterialDeck.ExportDialog.Title"),
template: "./modules/MaterialDeck/templates/exportDialog.html",
width: 500,
height: "auto"
});
}
setData(data,source) {
this.data = data;
this.source = source;
this.name = source;
}
/**
* Provide data to the template
*/
getData() {
return {
source: this.source,
name: this.name,
content: this.source == "soundboard" ? game.i18n.localize("MaterialDeck.ExportDialog.SoundboardContent") : game.i18n.localize("MaterialDeck.ExportDialog.MacroboardContent")
}
}
/**
* Update on form submit
* @param {*} event
* @param {*} formData
*/
async _updateObject(event, formData) {
this.download(this.data,formData.name)
}
activateListeners(html) {
super.activateListeners(html);
}
download(data,name) {
let dataStr = JSON.stringify(data);
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
let exportFileDefaultName = `${name}.json`;
let linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
}
}
export class importConfigForm extends FormApplication {
constructor(data, options) {
super(data, options);
this.data = {};
this.name = "";
this.source = "";
this.parent;
}
/**
* Default Options for this FormApplication
*/
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
id: "MD_Import",
title: "Material Deck: " + game.i18n.localize("MaterialDeck.ImportDialog.Title"),
template: "./modules/MaterialDeck/templates/importDialog.html",
width: 500,
height: "auto"
});
}
setData(source,parent) {
this.source = source;
this.name = source;
this.parent = parent;
}
/**
* Provide data to the template
*/
getData() {
return {
source: this.source,
name: this.name,
content: this.source == "soundboard" ? game.i18n.localize("MaterialDeck.ImportDialog.SoundboardContent") : game.i18n.localize("MaterialDeck.ImportDialog.MacroboardContent")
}
}
/**
* Update on form submit
* @param {*} event
* @param {*} formData
*/
async _updateObject(event, formData) {
await this.parent.updateSettings(this.data);
this.parent.render(true);
}
activateListeners(html) {
super.activateListeners(html);
const upload = html.find("input[id='uploadJson']");
upload.on('change',(event) => {
event.preventDefault();
this.readJsonFile(event.target.files[0]);
})
}
readJsonFile(jsonFile) {
var reader = new FileReader();
reader.addEventListener('load', (loadEvent) => {
try {
let json = JSON.parse(loadEvent.target.result);
this.data = json;
} catch (error) {
console.error(error);
}
});
reader.readAsText(jsonFile);
}
}
export class downloadUtility extends FormApplication {
constructor(data, options) {
super(data, options);
this.localSDversion = sdVersion;
this.masterSDversion;
this.localMSversion = msVersion;
this.masterMSversion;
this.releaseAssets = [];
this.profiles = [];
let parent = this;
setTimeout(function(){
parent.checkForUpdate('SD');
parent.checkForUpdate('MS');
parent.getReleaseData();
},100)
}
/**
* Default Options for this FormApplication
*/
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
id: "MD_DownloadUtility",
title: "Material Deck: " + game.i18n.localize("MaterialDeck.DownloadUtility.Title"),
template: "./modules/MaterialDeck/templates/downloadUtility.html",
width: 500,
height: "auto"
});
}
/**
* Provide data to the template
*/
getData() {
let dlDisabled = true;
this.profiles = [];
let iteration = 0;
for (let asset of this.releaseAssets) {
let split = asset.name.split('.');
if (split[split.length-1] == 'streamDeckProfile') {
this.profiles.push({id: iteration, label:split[0], url:asset.browser_download_url});
iteration++;
dlDisabled = false;
}
}
if (this.localMSversion == undefined) this.localMSversion = 'unknown';
return {
minimumSdVersion: game.modules.get("MaterialDeck").data.minimumSDversion.replace('v',''),
localSdVersion: this.localSDversion,
masterSdVersion: this.masterSDversion,
sdDlDisable: this.masterSDversion == undefined,
minimumMsVersion: game.modules.get("MaterialDeck").data.minimumMSversion.replace('v',''),
localMsVersion: this.localMSversion,
masterMsVersion: this.masterMSversion,
msDlDisable: this.masterMSversion == undefined,
profiles: this.profiles,
profileDlDisable: dlDisabled
}
}
/**
* Update on form submit
* @param {*} event
* @param {*} formData
*/
async _updateObject(event, formData) {
}
activateListeners(html) {
super.activateListeners(html);
const downloadSd = html.find("button[id='downloadSd']");
const downloadMs = html.find("button[id='downloadMs']");
const downloadProfile = html.find("button[name='downloadProfile']")
const refresh = html.find("button[id='refresh']");
downloadSd.on('click', () => {
const version = document.getElementById('masterSdVersion').innerHTML;
if (version == '' || version == undefined || version == 'Error') return;
const url = `https://github.com/CDeenen/MaterialDeck_SD/releases/download/v${version}/com.cdeenen.materialdeck.streamDeckPlugin`;
this.downloadURI(url,'com.cdeenen.materialdeck.streamDeckPlugin')
})
downloadMs.on('click', () => {
const version = document.getElementById('masterMsVersion').innerHTML;
const os = document.getElementById('os').value;
if (version == '' || version == undefined || version == 'Error') return;
let name = `MaterialServer-${os}.zip`;
let url;
if (os == 'source') url = `https://github.com/CDeenen/MaterialServer/archive/refs/tags/v${version}.zip`;
else url = `https://github.com/CDeenen/MaterialServer/releases/download/v${version}/${name}`;
this.downloadURI(url,name)
})
downloadProfile.on('click',(event) => {
let id = event.currentTarget.id.replace('dlProfile-','');
this.downloadURI(this.profiles[id].url,`${this.profiles[id].label}.streamDeckProfile`);
})
refresh.on('click', () => {
document.getElementById('masterSdVersion').value = 'Getting data';
this.checkForUpdate('SD');
document.getElementById('masterMsVersion').value = 'Getting data';
this.checkForUpdate('MS');
this.getReleaseData();
})
}
downloadURI(uri, name) {
var link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
getReleaseData() {
let parent = this;
const url = 'https://api.github.com/repos/CDeenen/MaterialDeck_SD/releases/latest';
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.send(null);
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
var type = request.getResponseHeader('Content-Type');
const data = JSON.parse(request.responseText);
parent.releaseAssets = data.assets;
parent.render(true);
if (type.indexOf("text") !== 1) {
return;
}
}
}
request.onerror = function () {
}
}
checkForUpdate(reqType) {
let parent = this;
let url;
if (reqType == 'SD') url = 'https://raw.githubusercontent.com/CDeenen/MaterialDeck_SD/master/Plugin/com.cdeenen.materialdeck.sdPlugin/manifest.json';
else if (reqType == 'MS') url = 'https://raw.githubusercontent.com/CDeenen/MaterialServer/master/src/Windows/package.json';
const elementId = reqType == 'SD' ? 'masterSdVersion' : 'masterMsVersion';
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.send(null);
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
var type = request.getResponseHeader('Content-Type');
if (type.indexOf("text") !== 1) {
if (reqType == 'SD') parent.masterSDversion = JSON.parse(request.responseText).Version;
else if (reqType == 'MS') parent.masterMSversion = JSON.parse(request.responseText).version;
parent.render(true);
return;
}
}
}
request.onerror = function () {
document.getElementById(elementId).innerHTML = 'Error';
}
}
}

View File

@@ -1,181 +0,0 @@
import * as MODULE from "../MaterialDeck.js";
import {streamDeck} from "../MaterialDeck.js";
import {compatibleCore} from "./misc.js";
export class Move{
constructor(){
this.active = false;
}
update(settings,context,device){
const background = settings.background ? settings.background : '#000000';
const mode = settings.mode ? settings.mode : 'canvas';
const type = settings.type ? settings.type : 'move';
if ((MODULE.getPermission('MOVE','TOKEN') == false && mode == 'selectedToken') || (MODULE.getPermission('MOVE','CANVAS') == false && mode == 'canvas')) {
streamDeck.noPermission(context,device);
return;
}
let url = '';
if (mode == 'canvas' || (mode == 'selectedToken' && type == 'move')){
const dir = settings.dir ? settings.dir : 'center';
if (dir == 'center') //center
url = "modules/MaterialDeck/img/move/center.png";
else if (dir == 'up') //up
url = "modules/MaterialDeck/img/move/up.png";
else if (dir == 'down') //down
url = "modules/MaterialDeck/img/move/down.png";
else if (dir == 'right') //right
url = "modules/MaterialDeck/img/move/right.png";
else if (dir == 'left') //left
url = "modules/MaterialDeck/img/move/left.png";
else if (dir == 'upRight')
url = "modules/MaterialDeck/img/move/upright.png";
else if (dir == 'upLeft')
url = "modules/MaterialDeck/img/move/upleft.png";
else if (dir == 'downRight')
url = "modules/MaterialDeck/img/move/downright.png";
else if (dir == 'downLeft')
url = "modules/MaterialDeck/img/move/downleft.png";
else if (dir == 'zoomIn')
url = "modules/MaterialDeck/img/move/zoomin.png";
else if (dir == 'zoomOut')
url = "modules/MaterialDeck/img/move/zoomout.png";
}
else if (mode == 'selectedToken' && type == 'rotate'){
const value = isNaN(parseInt(settings.rotValue)) ? 0 : parseInt(settings.rotValue);
if (value >= 0)
url = "modules/MaterialDeck/img/move/rotatecw.png";
else
url = "modules/MaterialDeck/img/move/rotateccw.png";
}
streamDeck.setIcon(context,device,url,{background:background,overlay:true});
streamDeck.setTitle('',context);
}
keyPress(settings){
if (canvas.scene == null) return;
if ((MODULE.getPermission('MOVE','TOKEN') == false && mode == 'selectedToken') || (MODULE.getPermission('MOVE','CANVAS') == false && mode == 'canvas')) {
streamDeck.noPermission(context,device);
return;
}
const dir = settings.dir ? settings.dir : 'center';
const mode = settings.mode ? settings.mode : 'canvas';
const type = settings.type ? settings.type : 'move';
let token;
if (mode == 'selectedToken') {
const selection = settings.selection ? settings.selection : 'selected';
const tokenIdentifier = settings.tokenName ? settings.tokenName : '';
if (selection == 'selected') token = canvas.tokens.controlled[0];
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 (type == 'move'){
if (dir == 'zoomIn') {//zoom in
let viewPosition = canvas.scene._viewPosition;
viewPosition.scale = viewPosition.scale*1.05;
viewPosition.duration = 100;
canvas.animatePan(viewPosition);
}
else if (dir == 'zoomOut') {//zoom out
let viewPosition = canvas.scene._viewPosition;
viewPosition.scale = viewPosition.scale*0.95;
viewPosition.duration = 100;
canvas.animatePan(viewPosition);
}
else {
if (settings.mode == 'selectedToken')
this.moveToken(token,dir);
else
this.moveCanvas(dir);
}
}
else if (type == 'rotate' && mode == 'selectedToken'){
const rotType = settings.rot ? settings.rot : 'to';
const value = isNaN(parseInt(settings.rotValue)) ? 0 : parseInt(settings.rotValue);
let rotationVal;
if (rotType == 'by') rotationVal = token.data.rotation + value;
else if (rotType == 'to') rotationVal = value;
if (compatibleCore("0.8.1")) token.document.update({rotation: rotationVal});
else token.update({rotation: rotationVal});
//token.rotate(rotationVal,false)
}
}
async moveToken(token,dir){
const gridSize = canvas.scene.data.grid;
let x = token.x;
let y = token.y;
if (dir == 'up') y -= gridSize;
else if (dir == 'down') y += gridSize;
else if (dir == 'right') x += gridSize;
else if (dir == 'left') x -= gridSize;
else if (dir == 'upRight') {
x += gridSize;
y -= gridSize;
}
else if (dir == 'upLeft') {
x -= gridSize;
y -= gridSize;
}
else if (dir == 'downRight') {
x += gridSize;
y += gridSize;
}
else if (dir == 'downLeft') {
x -= gridSize;
y += gridSize;
}
else if (dir == 'center') {
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;
if (compatibleCore("0.8.1")) token.document.update({x:x,y:y});
else token.update({x:x,y:y});
};
moveCanvas(dir){
let viewPosition = canvas.scene._viewPosition;
const gridSize = canvas.scene.data.grid;
viewPosition.duration = 100;
if (dir == 'up') viewPosition.y -= gridSize;
else if (dir == 'down') viewPosition.y += gridSize;
else if (dir == 'right') viewPosition.x += gridSize;
else if (dir == 'left') viewPosition.x -= gridSize;
else if (dir == 'upRight') {
viewPosition.x += gridSize;
viewPosition.y -= gridSize;
}
else if (dir == 'upLeft') {
viewPosition.x -= gridSize;
viewPosition.y -= gridSize;
}
else if (dir == 'downRight') {
viewPosition.x += gridSize;
viewPosition.y += gridSize;
}
else if (dir == 'downLeft') {
viewPosition.x -= gridSize;
viewPosition.y += gridSize;
}
else if (dir == 'center') {
viewPosition.x = (canvas.dimensions.sceneWidth+window.innerWidth)/2;
viewPosition.y = (canvas.dimensions.sceneHeight+window.innerHeight)/2;
}
canvas.animatePan(viewPosition);
}
}

View File

@@ -32,6 +32,8 @@ export class OtherControls{
if (mode == 'pause') //pause
this.updatePause(settings,context,device,options);
else if (mode == 'move') //move canvas
this.updateMove(settings,context,device,options);
else if (mode == 'controlButtons') //control buttons
this.updateControl(settings,context,device,options);
else if (mode == 'darkness') //darkness
@@ -59,6 +61,8 @@ export class OtherControls{
if (mode == 'pause') //pause
this.keyPressPause(settings);
else if (mode == 'move') //move canvas
this.keyPressMove(settings);
else if (mode == 'controlButtons') //control buttons
this.keyPressControl(settings);
else if (mode == 'darkness') //darkness controll
@@ -126,6 +130,87 @@ export class OtherControls{
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
updateMove(settings,context,device,options={}){
let url = '';
const dir = settings.dir ? settings.dir : 'center';
const background = settings.background ? settings.background : '#000000';
if (dir == 'center') //center
url = "modules/MaterialDeck/img/move/center.png";
else if (dir == 'up') //up
url = "modules/MaterialDeck/img/move/up.png";
else if (dir == 'down') //down
url = "modules/MaterialDeck/img/move/down.png";
else if (dir == 'right') //right
url = "modules/MaterialDeck/img/move/right.png";
else if (dir == 'left') //left
url = "modules/MaterialDeck/img/move/left.png";
else if (dir == 'upRight')
url = "modules/MaterialDeck/img/move/upright.png";
else if (dir == 'upLeft')
url = "modules/MaterialDeck/img/move/upleft.png";
else if (dir == 'downRight')
url = "modules/MaterialDeck/img/move/downright.png";
else if (dir == 'downLeft')
url = "modules/MaterialDeck/img/move/downleft.png";
else if (dir == 'zoomIn')
url = "modules/MaterialDeck/img/move/zoomin.png";
else if (dir == 'zoomOut')
url = "modules/MaterialDeck/img/move/zoomout.png";
streamDeck.setIcon(context,device,url,{background:background,overlay:true});
streamDeck.setTitle('',context);
}
keyPressMove(settings){
if (canvas.scene == null) return;
const dir = settings.dir ? settings.dir : 'center';
if (dir == 'zoomIn') {//zoom in
let viewPosition = canvas.scene._viewPosition;
viewPosition.scale = viewPosition.scale*1.05;
viewPosition.duration = 100;
canvas.animatePan(viewPosition);
}
else if (dir == 'zoomOut') {//zoom out
let viewPosition = canvas.scene._viewPosition;
viewPosition.scale = viewPosition.scale*0.95;
viewPosition.duration = 100;
canvas.animatePan(viewPosition);
}
else {
let viewPosition = canvas.scene._viewPosition;
const gridSize = canvas.scene.data.grid;
viewPosition.duration = 100;
if (dir == 'up') viewPosition.y -= gridSize;
else if (dir == 'down') viewPosition.y += gridSize;
else if (dir == 'right') viewPosition.x += gridSize;
else if (dir == 'left') viewPosition.x -= gridSize;
else if (dir == 'upRight') {
viewPosition.x += gridSize;
viewPosition.y -= gridSize;
}
else if (dir == 'upLeft') {
viewPosition.x -= gridSize;
viewPosition.y -= gridSize;
}
else if (dir == 'downRight') {
viewPosition.x += gridSize;
viewPosition.y += gridSize;
}
else if (dir == 'downLeft') {
viewPosition.x -= gridSize;
viewPosition.y += gridSize;
}
else if (dir == 'center') {
viewPosition.x = (canvas.dimensions.sceneWidth+window.innerWidth)/2;
viewPosition.y = (canvas.dimensions.sceneHeight+window.innerHeight)/2;
}
canvas.animatePan(viewPosition);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
updateControl(settings,context,device,options={}){
@@ -603,18 +688,17 @@ export class OtherControls{
streamDeck.noPermission(context,device);
return;
}
let compendium;
if (compatibleCore("0.8.1")) compendium = game.packs.contents.find(p=>p.metadata.label == name)?.apps[0];
else compendium = game.packs.entries.find(p=>p.metadata.label == name);
const compendium = game.packs.find(p=>p.metadata.label == name);
if (compendium == undefined) return;
if (compendium.private && MODULE.getPermission('OTHER','COMPENDIUM_ALL') == false) {
streamDeck.noPermission(context,device);
return;
}
const rendered = compatibleCore("0.8.5") ? compendium.apps[0].rendered : compendium.rendered;
const background = settings.background ? settings.background : '#000000';
const ringOffColor = settings.offRing ? settings.offRing : '#000000';
const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
const ringColor = compendium.rendered ? ringOnColor : ringOffColor;
const ringColor = rendered ? ringOnColor : ringOffColor;
const txt = settings.displayCompendiumName ? name : '';
streamDeck.setTitle(txt,context);
@@ -626,12 +710,11 @@ export class OtherControls{
if (name == undefined) return;
if (MODULE.getPermission('OTHER','COMPENDIUM') == false ) return;
let compendium;
if (compatibleCore("0.8.1")) compendium = game.packs.contents.find(p=>p.metadata.label == name)?.apps[0];
else compendium = game.packs.entries.find(p=>p.metadata.label == name);
const compendium = game.packs.find(p=>p.metadata.label == name);
const rendered = compatibleCore("0.8.5") ? compendium.apps[0].rendered : compendium.rendered;
if (compendium == undefined) return;
if (compendium.private && MODULE.getPermission('OTHER','COMPENDIUM_ALL') == false) return;
else if (compendium.rendered) compendium.close();
else if (rendered) compatibleCore("0.8.5") ? compendium.apps[0].close() : compendium.close();
else compendium.render(true);
}

View File

@@ -63,6 +63,7 @@ export class PlaylistControl{
playlistNr += this.playlistOffset;
let playlist = this.getPlaylist(playlistNr);
if (playlist != undefined){
if (playlist.playing)
ringColor = ringOnColor;
@@ -115,10 +116,7 @@ export class PlaylistControl{
let playlist = this.getPlaylist(playlistNr);
if (playlist != undefined){
let track;
if (compatibleCore("0.8.1")) track = playlist.sounds.contents[trackNr];
else track = playlist.data.sounds[trackNr];
const track = compatibleCore("0.8.1") ? playlist.sounds.contents[trackNr] : playlist.sounds[trackNr];
if (track != undefined){
if (track.playing)
ringColor = ringOnColor;
@@ -200,9 +198,7 @@ export class PlaylistControl{
if (playlistMode == 'playlist')
this.playPlaylist(playlist,playlistNr);
else {
let track;
if (compatibleCore("0.8.1")) track = playlist.sounds.contents[trackNr];
else track = playlist.data.sounds[trackNr];
const track = compatibleCore("0.8.1") ? playlist.sounds.contents[trackNr] : playlist.sounds[trackNr];
if (track != undefined){
this.playTrack(track,playlist,playlistNr);
}

View File

@@ -1,5 +1,5 @@
import * as MODULE from "../MaterialDeck.js";
import { playlistConfigForm, macroConfigForm, soundboardConfigForm } from "./misc.js";
import { playlistConfigForm, macroConfigForm, soundboardConfigForm, downloadUtility } from "./misc.js";
let userPermissions = {};
const defaultEnable = [true,true,true,true];
@@ -22,10 +22,6 @@ const defaultUserPermissions = {
MACROBOARD: [false,false,true,true],
MACROBOARD_CONFIGURE: [false,false,true,true]
},
MOVE: {
TOKEN: [true,true,true,true],
CANVAS: [true,true,true,true]
},
OTHER: {
PAUSE: [false,false,true,true],
CONTROL: [true,true,true,true],
@@ -83,16 +79,6 @@ export const registerSettings = async function() {
onChange: x => window.location.reload()
});
game.settings.register(MODULE.moduleName,'streamDeckModel', {
name: "MaterialDeck.Sett.Model",
hint: "MaterialDeck.Sett.Model_Hint",
scope: "client",
config: true,
type:Number,
default:1,
choices:["MaterialDeck.Sett.Model_Mini","MaterialDeck.Sett.Model_Normal","MaterialDeck.Sett.Model_XL"],
});
/**
* Sets the ip address of the server
*/
@@ -128,6 +114,17 @@ export const registerSettings = async function() {
});
game.settings.register(MODULE.moduleName, 'nrOfConnMessages', {
name: "MaterialDeck.Sett.NrOfConnMessages",
hint: "MaterialDeck.Sett.NrOfConnMessagesHint",
default: 5,
type: Number,
scope: 'client',
range: { min: 0, max: 100, step: 1 },
config: true
});
//Create the Help button
game.settings.registerMenu(MODULE.moduleName, 'helpMenu',{
name: "MaterialDeck.Sett.Help",
@@ -150,6 +147,13 @@ export const registerSettings = async function() {
config: false
});
game.settings.registerMenu(MODULE.moduleName, 'downloadUtility',{
name: "MaterialDeck.DownloadUtility.Title",
label: "MaterialDeck.DownloadUtility.Title",
type: downloadUtility,
restricted: false
});
/**
* Playlist soundboard
*/
@@ -223,11 +227,11 @@ export const registerSettings = async function() {
if (permissionSettings.permissions.MACRO.BY_NAME == undefined) permissionSettings.permissions.MACRO.BY_NAME = [false,false,true,true];
}
if (game.user.isGM)
game.settings.set(MODULE.moduleName,'userPermission',permissionSettings);
game.settings.set(MODULE.moduleName,'userPermission',permissionSettings);
}
export class helpMenu extends FormApplication {
constructor(data, options) {
super(data, options);
@@ -302,11 +306,7 @@ export class helpMenu extends FormApplication {
}
const actions = Object.entries(duplicate(settings.permissions)).reduce((arr, e) => {
//const perm = e[1];
const perms = Object.entries(duplicate(e[1])).reduce((arr, p) => {
//const perm = e[1];
let perm = {};
perm.roles = [p[1][0],p[1][1],p[1][2],p[1][3]]
perm.id = p[0];
@@ -324,8 +324,10 @@ export class helpMenu extends FormApplication {
arr.push(cat);
return arr;
}, []);
const current = await game.settings.get("core", "permissions");
for (let i=0; i<actions.length; i++) {
if (actions[i].id == 'MOVE')
actions.splice(i,1);
}
return {
roles: Object.keys(CONST.USER_ROLES).reduce((obj, r) => {
if ( r === "NONE" ) return obj;
@@ -348,7 +350,7 @@ export class helpMenu extends FormApplication {
settings.enable = permissions.ENABLE;
delete permissions.ENABLE;
settings.permissions = permissions;
game.settings.set(MODULE.moduleName,'userPermission',settings);
// game.settings.set(MODULE.moduleName,'userPermission',settings);
}
async activateListeners(html) {

View File

@@ -7,8 +7,6 @@ export class SoundboardControl{
this.active = false;
this.offset = 0;
this.activeSounds = [];
for (let i=0; i<64; i++)
this.activeSounds[i] = false;
}
async updateAll(){
@@ -43,7 +41,7 @@ export class SoundboardControl{
soundNr += this.offset;
let soundboardSettings = game.settings.get(MODULE.moduleName, 'soundboardSettings');
ringColor = (this.activeSounds[soundNr]==false) ? soundboardSettings.colorOff[soundNr] : soundboardSettings.colorOn[soundNr];
ringColor = (this.activeSounds[soundNr]==undefined) ? soundboardSettings.colorOff[soundNr] : soundboardSettings.colorOn[soundNr];
if (settings.displayName && soundboardSettings.name != undefined) txt = soundboardSettings.name[soundNr];
if (settings.displayIcon && soundboardSettings.img != undefined) src = soundboardSettings.img[soundNr];
@@ -89,7 +87,7 @@ export class SoundboardControl{
const playMode = game.settings.get(MODULE.moduleName,'soundboardSettings').mode[soundNr];
const repeat = (playMode > 0) ? true : false;
const play = (this.activeSounds[soundNr] == false) ? true : false;
const play = (this.activeSounds[soundNr] == undefined) ? true : false;
this.prePlaySound(soundNr,repeat,play);
}
@@ -100,8 +98,8 @@ export class SoundboardControl{
this.updateAll();
}
else if (mode == 'stopAll') { //Stop All Sounds
for (let i=0; i<64; i++) {
if (this.activeSounds[i] != false){
for (let i=0; i<this.activeSounds.length; i++) {
if (this.activeSounds[i] != undefined){
this.prePlaySound(i,false,false);
}
}
@@ -176,7 +174,7 @@ export class SoundboardControl{
if(newSound.loaded == false) await newSound.load({autoplay:true});
newSound.on('end', ()=>{
if (repeat == false) {
this.activeSounds[soundNr] = false;
this.activeSounds[soundNr] = undefined;
this.updateAll();
}
});
@@ -186,12 +184,12 @@ export class SoundboardControl{
else {
let howl = new Howl({src, volume, loop: repeat, onend: (id)=>{
if (repeat == false){
this.activeSounds[soundNr] = false;
this.activeSounds[soundNr] = undefined;
this.updateAll();
}
},
onstop: ()=>{
this.activeSounds[soundNr] = false;
this.activeSounds[soundNr] = undefined;
this.updateAll();
}});
howl.play();
@@ -199,28 +197,9 @@ export class SoundboardControl{
}
}
else {
this.activeSounds[soundNr].stop();
this.activeSounds[soundNr] = false;
if (this.activeSounds[soundNr] != undefined) this.activeSounds[soundNr].stop();
this.activeSounds[soundNr] = undefined;
}
this.updateAll();
}
/*
volumeChange(soundNr){
let volume = game.settings.get("core", "globalAmbientVolume");
if (soundNr == 'all') {
for (let i=0; this.activeSounds.length; i++) {
volume * game.settings.get(MODULE.moduleName,'soundboardSettings').volume[i]/100;
volume = AudioHelper.inputToVolume(volume);
this.activeSounds[i].volume = volume;
}
}
else {
volume * game.settings.get(MODULE.moduleName,'soundboardSettings').volume[soundNr]/100;
volume = AudioHelper.inputToVolume(volume);
}
}
*/
}

View File

@@ -389,7 +389,7 @@ export class StreamDeck{
else {
}
if (uses != undefined && uses.heart != false && (uses.available > 0 || uses.maximum != undefined)) {
if (uses != undefined && uses.heart != undefined && (uses.available > 0 || uses.maximum != undefined)) {
const percentage = 102*uses.available/uses.maximum;
ctx.fillStyle = uses.heart;
ctx.fillRect(0, 121,144,-percentage);
@@ -411,7 +411,7 @@ export class StreamDeck{
ctx.fillText(content[1], horOffset, vertOffset);
}
if (format != 'jpg' && format != 'jpeg' && format != 'png' && format != 'webm' && format != 'webp' && format != 'gif' && format != 'svg') url = "modules/MaterialDeck/img/transparant.png";
if (format != 'jpg' && format != 'jpeg' && format != 'png' && format != 'PNG' && format != 'webm' && format != 'webp' && format != 'gif' && format != 'svg') url = "modules/MaterialDeck/img/transparant.png";
//if (url == "") url = "modules/MaterialDeck/img/transparant.png"
let resImageURL = url;
let img = new Image();
@@ -451,10 +451,13 @@ export class StreamDeck{
yStart = 0;
}
ctx.drawImage(img, xStart+margin, yStart+margin, renderableWidth - 2*margin, renderableHeight - 2*margin);
if (uses != undefined && uses.heart == false && (uses.available > 0 || uses.maximum != undefined)) {
let txt = uses.available;
if (uses.maximum != undefined) txt = uses.available + '/' + uses.maximum;
if (uses.maximum == undefined ) uses.maximum = 1;
if (uses != undefined && uses.heart == undefined) {
let txt = '';
if (uses.available != undefined) {
txt = uses.available;
if (uses.maximum != undefined) txt = uses.available + '/' + uses.maximum;
if (uses.maximum == undefined ) uses.maximum = 1;
}
ctx.beginPath();
ctx.lineWidth = 4;
let green = Math.ceil(255*(uses.available/uses.maximum));

133
src/systems/demonlord.js Normal file
View File

@@ -0,0 +1,133 @@
import {compatibleCore} from "../misc.js";
export class demonlord{
constructor(){
}
getHP(token) {
const hp = token.actor.data.data.characteristics.health;
return {
value: hp.value,
max: hp.max
}
}
getTempHP(token) {
return;
}
getAC(token) {
return token.actor.data.data.characteristics.defense;
}
getShieldHP(token) {
return;
}
getSpeed(token) {
return token.actor.data.data.characteristics.speed;
}
getInitiative(token) {
return token.actor.data.data.fastturn ? "FAST" : "SLOW";
}
toggleInitiative(token) {
token.actor.update({
'data.fastturn': !token.actor.data?.data?.fastturn
});
return;
}
getPassivePerception(token) {
return;
}
getPassiveInvestigation(token) {
return;
}
getAbility(token, ability) {
if (ability == undefined) ability = 'strength';
return token.actor.data.data.attributes?.[ability].value;
}
getAbilityModifier(token, ability) {
if (ability == undefined) ability = 'str';
let val = token.actor.data.data.attributes?.[ability].modifier;
return (val >= 0) ? `+${val}` : val;
}
getAbilitySave(token, ability) {
return;
}
getSkill(token, skill) {
if (skill == undefined) skill = 'acr';
const val = token.actor.data.data.skills?.[skill].total;
return (val >= 0) ? `+${val}` : val;
}
getProficiency(token) {
return;
}
getConditionIcon(condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll') return window.CONFIG.controlIcons.effects;
else return CONFIG.statusEffects.find(e => e.id === condition).icon;
}
getConditionActive(token,condition) {
if (condition == undefined) condition = 'removeAll';
return token.actor.effects.find(e => e.isTemporary === condition) != undefined;
}
async toggleCondition(token,condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll'){
for( let effect of token.actor.effects)
await effect.delete();
}
else {
const effect = CONFIG.statusEffects.find(e => e.id === condition);
await token.toggleEffect(effect);
}
return true;
}
/**
* Roll
*/
roll(token,roll,options,ability,skill,save) {
return;
}
/**
* Items
*/
getItems(token,itemType) {
if (itemType == undefined) itemType = 'any';
const allItems = token.actor.items;
if (itemType == 'any') return allItems.filter(i => i.type == 'item');
}
getItemUses(item) {
return {available: item.data.data.quantity};
}
/**
* Spells
*/
getSpells(token,level) {
if (level == undefined) level = 'any';
const allItems = token.actor.items;
if (level == 'any') return allItems.filter(i => i.type == 'spell')
else return allItems.filter(i => i.type == 'spell' && i.data.data.rank == level)
}
getSpellUses(token,level,item) {
return;
}
}

198
src/systems/dnd35e.js Normal file
View File

@@ -0,0 +1,198 @@
import {compatibleCore} from "../misc.js";
export class dnd35e{
constructor(){
}
getHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: hp.value,
max: hp.max
}
}
getTempHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: (hp.temp == null) ? 0 : hp.temp,
max: (hp.tempmax == null) ? 0 : hp.tempmax
}
}
getAC(token) {
return token.actor.data.data.attributes.ac.normal.total;
}
getShieldHP(token) {
return;
}
getSpeed(token) {
const movement = token.actor.data.data.attributes.speed;
let speed = "";
if (movement.burrow.total > 0) speed += `Burrow: ${movement.burrow.total}Ft`;
if (movement.climb.total > 0) {
if (speed.length > 0) speed += '\n';
speed += `Climb: ${movement.climb.total}Ft`;
}
if (movement.fly.total > 0) {
if (speed.length > 0) speed += '\n';
speed += `Fly: ${movement.fly.total}Ft`;
}
if (movement.land.total > 0) {
if (speed.length > 0) speed += '\n';
speed += `Land: ${movement.land.total}Ft`;
}
if (movement.swim.total > 0) {
if (speed.length > 0) speed += '\n';
speed += `Swim: ${movement.swim.total}Ft`;
}
return speed;
}
getInitiative(token) {
let initiative = token.actor.data.data.attributes.init.total;
return (initiative >= 0) ? `+${initiative}` : initiative;
}
toggleInitiative(token) {
return;
}
getPassivePerception(token) {
return;
}
getPassiveInvestigation(token) {
return;
}
getAbility(token, ability) {
if (ability == undefined) ability = 'str';
return token.actor.data.data.abilities?.[ability].value;
}
getAbilityModifier(token, ability) {
if (ability == undefined) ability = 'str';
let val = token.actor.data.data.abilities?.[ability].mod;
return (val >= 0) ? `+${val}` : val;
}
getAbilitySave(token, ability) {
if (ability == undefined) ability = 'fort';
let val = token.actor.data.data.attributes.savingThrows?.[ability].total;
return (val >= 0) ? `+${val}` : val;
}
getSkill(token, skill) {
if (skill == undefined) skill = 'apr';
const val = token.actor.data.data.skills?.[skill].mod;
return (val >= 0) ? `+${val}` : val;
}
getProficiency(token) {
const val = token.actor.data.data.attributes.prof;
return (val >= 0) ? `+${val}` : val;
}
getConditionIcon(condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll') return window.CONFIG.controlIcons.effects;
else return CONFIG.statusEffects.find(e => e.id === condition).icon;
}
getConditionActive(token,condition) {
if (condition == undefined) condition = 'removeAll';
return token.actor.effects.find(e => e.isTemporary === condition) != undefined;
}
async toggleCondition(token,condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll'){
for( let effect of token.actor.effects)
await effect.delete();
}
else {
const effect = CONFIG.statusEffects.find(e => e.id === condition);
await token.toggleEffect(effect);
}
return true;
}
/**
* Roll
*/
roll(token,roll,options,ability,skill,save) {
if (roll == undefined) roll = 'ability';
if (ability == undefined) ability = 'str';
if (skill == undefined) skill = 'apr';
if (save == undefined) save = 'fort';
if (roll == 'ability') token.actor.rollAbilityTest(ability,options);
else if (roll == 'save') token.actor.rollSavingThrow(save, null, null,options);
else if (roll == 'skill') token.actor.rollSkill(skill,options);
else if (roll == 'initiative') token.actor.rollInitiative(options);
else if (roll == 'grapple') token.actor.rollGrapple(options);
else if (roll == 'bab') token.actor.rollBAB(options);
else if (roll == 'melee') token.actor.rollMelee(options);
else if (roll == 'ranged') token.actor.rollRanged(options);
}
/**
* Items
*/
getItems(token,itemType) {
if (itemType == undefined) itemType = 'any';
const allItems = token.actor.items;
if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container');
else if (game.system.id == 'D35E' && itemType == 'container') return allItems.filter(i => i.type == 'loot' && i.data.data.subType == itemType);
else {
if (itemType == 'gear' || itemType == 'ammo' || itemType == 'misc' || itemType == 'tradeGoods')
return allItems.filter(i => i.type == 'loot' && i.data.data.subType == itemType);
else return allItems.filter(i => i.type == itemType);
}
}
getItemUses(item) {
return {available: item.data.data.quantity};
}
/**
* Features
*/
getFeatures(token,featureType) {
if (featureType == undefined) featureType = 'any';
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)
}
getFeatureUses(item) {
if (item.data.type == 'class') return {available: item.data.data.levels};
else return {
available: item.data.data.uses.value,
maximum: item.data.data.uses.max
};
}
/**
* Spells
*/
getSpells(token,level) {
if (level == undefined) level = 'any';
const allItems = token.actor.items;
if (level == 'any') return allItems.filter(i => i.type == 'spell')
else return allItems.filter(i => i.type == 'spell' && i.data.data.level == level)
}
getSpellUses(token,level,item) {
if (level == undefined) level = 'any';
if (item.data.data.level == 0) return;
return {
available: item.charges,
maximum: item.maxCharges
}
}
}

201
src/systems/dnd5e.js Normal file
View File

@@ -0,0 +1,201 @@
import {compatibleCore} from "../misc.js";
export class dnd5e{
constructor(){
}
getHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: hp.value,
max: hp.max
}
}
getTempHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: (hp.temp == null) ? 0 : hp.temp,
max: (hp.tempmax == null) ? 0 : hp.tempmax
}
}
getAC(token) {
return token.actor.data.data.attributes.ac.value;
}
getShieldHP(token) {
return;
}
getSpeed(token) {
const movement = token.actor.data.data.attributes.movement;
let speed = "";
if (movement != undefined){
if (movement.burrow > 0) speed += `${game.i18n.localize("DND5E.MovementBurrow")}: ${movement.burrow + movement.units}`;
if (movement.climb > 0) {
if (speed.length > 0) speed += '\n';
speed += `${game.i18n.localize("DND5E.MovementClimb")}: ${movement.climb + movement.units}`;
}
if (movement.fly > 0) {
if (speed.length > 0) speed += '\n';
speed += `${game.i18n.localize("DND5E.MovementFly")}: ${movement.fly + movement.units}`;
}
if (movement.hover > 0) {
if (speed.length > 0) speed += '\n';
speed += `${game.i18n.localize("DND5E.MovementHover")}: ${movement.hover + movement.units}`;
}
if (movement.swim > 0) {
if (speed.length > 0) speed += '\n';
speed += `${game.i18n.localize("DND5E.MovementSwim")}: ${movement.swim + movement.units}`;
}
if (movement.walk > 0) {
if (speed.length > 0) speed += '\n';
speed += `${game.i18n.localize("DND5E.MovementWalk")}: ${movement.walk + movement.units}`;
}
}
else {
const spd = token.actor.data.data.attributes.speed;
speed = spd.value;
if (spd.special.length > 0) speed + "\n" + spd.special;
}
return speed;
}
getInitiative(token) {
let initiative = token.actor.data.data.attributes.init.total;
return (initiative >= 0) ? `+${initiative}` : initiative;
}
toggleInitiative(token) {
return;
}
getPassivePerception(token) {
return token.actor.data.data.skills.prc.passive;
}
getPassiveInvestigation(token) {
return token.actor.data.data.skills.inv.passive;
}
getAbility(token, ability) {
if (ability == undefined) ability = 'str';
return token.actor.data.data.abilities?.[ability].value;
}
getAbilityModifier(token, ability) {
if (ability == undefined) ability = 'str';
let val = token.actor.data.data.abilities?.[ability].mod;
return (val >= 0) ? `+${val}` : val;
}
getAbilitySave(token, ability) {
if (ability == undefined) ability = 'str';
let val = token.actor.data.data.abilities?.[ability].save;
return (val >= 0) ? `+${val}` : val;
}
getSkill(token, skill) {
if (skill == undefined) skill = 'acr';
const val = token.actor.data.data.skills?.[skill].total;
return (val >= 0) ? `+${val}` : val;
}
getProficiency(token) {
const val = token.actor.data.data.attributes.prof;
return (val >= 0) ? `+${val}` : val;
}
getConditionIcon(condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll') return window.CONFIG.controlIcons.effects;
else return CONFIG.statusEffects.find(e => e.id === condition).icon;
}
getConditionActive(token,condition) {
if (condition == undefined) condition = 'removeAll';
return token.actor.effects.find(e => e.isTemporary === condition) != undefined;
}
async toggleCondition(token,condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll'){
for( let effect of token.actor.effects)
await effect.delete();
}
else {
const effect = CONFIG.statusEffects.find(e => e.id === condition);
await token.toggleEffect(effect);
}
return true;
}
/**
* Roll
*/
roll(token,roll,options,ability,skill,save) {
if (roll == undefined) roll = 'ability';
if (ability == undefined) ability = 'str';
if (skill == undefined) skill = 'acr';
if (save == undefined) save = 'str';
if (roll == 'ability') token.actor.rollAbilityTest(ability,options);
else if (roll == 'save') token.actor.rollAbilitySave(save,options);
else if (roll == 'skill') token.actor.rollSkill(skill,options);
else if (roll == 'initiative') token.actor.rollInitiative(options);
else if (roll == 'deathSave') token.actor.rollDeathSave(options);
}
/**
* Items
*/
getItems(token,itemType) {
if (itemType == undefined) itemType = 'any';
const allItems = token.actor.items;
if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container');
else return allItems.filter(i => i.type == itemType);
}
getItemUses(item) {
return {available: item.data.data.quantity};
}
/**
* Features
*/
getFeatures(token,featureType) {
if (featureType == undefined) featureType = 'any';
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)
}
getFeatureUses(item) {
if (item.data.type == 'class') return {available: item.data.data.levels};
else return {
available: item.data.data.uses.value,
maximum: item.data.data.uses.max
};
}
/**
* Spells
*/
getSpells(token,level) {
if (level == undefined) level = 'any';
const allItems = token.actor.items;
if (level == 'any') return allItems.filter(i => i.type == 'spell')
else return allItems.filter(i => i.type == 'spell' && i.data.data.level == level)
}
getSpellUses(token,level,item) {
if (level == undefined) level = 'any';
if (item.data.data.level == 0) return;
return {
available: token.actor.data.data.spells?.[`spell${level}`].value,
maximum: token.actor.data.data.spells?.[`spell${level}`].max
}
}
}

195
src/systems/pf2e.js Normal file
View File

@@ -0,0 +1,195 @@
import {compatibleCore} from "../misc.js";
export class pf2e{
constructor(){
}
getHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: hp.value,
max: hp.max
}
}
getTempHP(token) {
const hp = token.actor.data.data.attributes.hp;
return {
value: (hp.temp == null) ? 0 : hp.temp,
max: (hp.tempmax == null) ? 0 : hp.tempmax
}
}
getAC(token) {
return token.actor.data.data.attributes.ac.value;
}
getShieldHP(token) {
return token.actor.data.data.attributes.shield.value;
}
getSpeed(token) {
let speed = token.actor.data.data.attributes.speed.breakdown;
const otherSpeeds = token.actor.data.data.attributes.speed.otherSpeeds;
if (otherSpeeds.length > 0)
for (let i=0; i<otherSpeeds.length; i++)
speed += `\n${otherSpeeds[i].type}: ${otherSpeeds[i].value}`;
return speed;
}
getInitiative(token) {
let initiative = token.actor.data.data.attributes.init.value;
return (initiative >= 0) ? `+${initiative}` : initiative;
}
toggleInitiative(token) {
return;
}
getPassivePerception(token) {
return;
}
getPassiveInvestigation(token) {
return;
}
getAbility(token, ability) {
if (ability == undefined) ability = 'str';
return token.actor.data.data.abilities?.[ability].value;
}
getAbilityModifier(token, ability) {
if (ability == undefined) ability = 'str';
let val = token.actor.data.data.abilities?.[ability].mod;
return (val >= 0) ? `+${val}` : val;
}
getAbilitySave(token, ability) {
if (ability == undefined) ability = 'fortitude';
else if (ability == 'fort') ability = 'fortitude';
else if (ability == 'ref') ability = 'reflex';
else if (ability == 'will') ability = 'will';
let val = token.actor.data.data.saves?.[ability].value;
return (val >= 0) ? `+${val}` : val;
}
getSkill(token, skill) {
if (skill == undefined) skill = 'acr';
const val = token.actor.data.data.skills?.[skill].totalModifier;
return (val >= 0) ? `+${val}` : val;
}
getProficiency(token) {
return;
}
getCondition(token,condition) {
if (condition == undefined || condition == 'removeAll') return undefined;
const Condition = condition.charAt(0).toUpperCase() + condition.slice(1);
const effects = token.actor.items.filter(i => i.type == 'condition');
return effects.find(e => e.name === Condition);
}
getConditionIcon(condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll') return window.CONFIG.controlIcons.effects;
else return `${CONFIG.PF2E.statusEffects.effectsIconFolder}${condition}.webp`;
}
getConditionActive(token,condition) {
return this.getCondition(token,condition) != undefined;
}
async toggleCondition(token,condition) {
if (condition == undefined) condition = 'removeAll';
if (condition == 'removeAll'){
for( let effect of token.actor.items.filter(i => i.type == 'condition'))
await effect.delete();
}
else {
const effect = this.getCondition(token,condition);
if (effect == undefined) {
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 {
effect.delete();
}
}
return true;
}
/**
* Roll
*/
roll(token,roll,options,ability,skill,save) {
if (roll == undefined) roll = 'ability';
if (ability == undefined) ability = 'str';
if (skill == undefined) skill = 'acr';
if (save == undefined) save = 'fort';
if (roll == 'ability') token.actor.data.data.abilities?.[ability].roll(options);
else if (roll == 'save') {
let ability = save;
if (ability == 'fort') ability = 'fortitude';
else if (ability == 'ref') ability = 'reflex';
else if (ability == 'will') ability = 'will';
token.actor.data.data.saves?.[ability].roll(options);
}
else if (roll == 'skill') token.actor.data.data.skills?.[skill].roll(options);
}
/**
* Items
*/
getItems(token,itemType) {
if (itemType == undefined) itemType = 'any';
const allItems = token.actor.items;
if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container');
else return allItems.filter(i => i.type == itemType);
}
getItemUses(item) {
return {available: item.data.data.quantity.value};
}
/**
* Features
*/
getFeatures(token,featureType) {
if (featureType == undefined) featureType = 'any';
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)
}
getFeatureUses(item) {
if (item.data.type == 'class') return {available: item.actor.data.data.details.level.value};
else return;
}
/**
* Spells
*/
getSpells(token,level) {
if (level == undefined) level = 'any';
const allItems = token.actor.items;
if (level == 'any') return allItems.filter(i => i.type == 'spell')
else return allItems.filter(i => i.type == 'spell' && i.data.data.level.value == level)
}
getSpellUses(token,level,item) {
if (level == undefined) level = 'any';
if (item.data.data.level.value == 0) return;
const spellbook = token.actor.items.filter(i => i.data.type === 'spellcastingEntry')[0];
if (spellbook == undefined) return;
return {
available: spellbook.data.data.slots?.[`slot${level}`].value,
maximum: spellbook.data.data.slots?.[`slot${level}`].max
}
}
}

241
src/systems/tokenHelper.js Normal file
View File

@@ -0,0 +1,241 @@
import {dnd5e} from "./dnd5e.js"
import {dnd35e} from "./dnd35e.js"
import {pf2e} from "./pf2e.js"
import {demonlord} from "./demonlord.js";
import {compatibleCore} from "../misc.js";
export class TokenHelper{
constructor(){
this.system;
this.setSystem();
}
setSystem() {
if (game.system.id == 'D35E' || game.system.id == 'pf1') this.system = new dnd35e();
else if (game.system.id == 'pf2e') this.system = new pf2e();
else if (game.system.id == 'demonlord') this.system = new demonlord();
else this.system = new dnd5e(); //default to dnd5e
}
/***********************************************************************
* System agnostic functions
***********************************************************************/
getToken(type,identifier) {
if (type == 'selected') return this.getSelectedToken();
else if (type != 'selected' && identifier == '') return;
else if (type == 'tokenName') return this.getTokenFromTokenName(identifier);
else if (type == 'actorName') return this.getTokenFromActorName(identifier);
else if (type == 'tokenId') return this.getTokenFromTokenId(identifier);
else if (type == 'actorId') return this.getTokenFromActorId(identifier);
}
getTokenFromTokenId(id) {
return canvas.tokens.children[0].children.find(p => p.id == id);
}
getTokenFromTokenName(name) {
return canvas.tokens.children[0].children.find(p => p.name == name);
}
getTokenFromActorId(id) {
return canvas.tokens.children[0].children.find(p => p.actor.id == id);
}
getTokenFromActorName(name) {
return canvas.tokens.children[0].children.find(p => p.actor.name == name);
}
getSelectedToken() {
return canvas.tokens.controlled[0];
}
moveToken(token,dir){
if (dir == undefined) dir = 'up';
const gridSize = canvas.scene.data.grid;
let x = token.x;
let y = token.y;
if (dir == 'up') y -= gridSize;
else if (dir == 'down') y += gridSize;
else if (dir == 'right') x += gridSize;
else if (dir == 'left') x -= gridSize;
else if (dir == 'upRight') {
x += gridSize;
y -= gridSize;
}
else if (dir == 'upLeft') {
x -= gridSize;
y -= gridSize;
}
else if (dir == 'downRight') {
x += gridSize;
y += gridSize;
}
else if (dir == 'downLeft') {
x -= gridSize;
y += gridSize;
}
else if (dir == 'center') {
let location = token.getCenter(x,y);
canvas.animatePan(location);
}
if (game.user.isGM == false && game.paused == true && (token.can(game.user,"control") == false || token.checkCollision(token.getCenter(x, y)))) return;
if (compatibleCore("0.8.1")) token.document.update({x:x,y:y});
else token.update({x:x,y:y});
};
rotateToken(token,move,value) {
if (move == undefined) move = 'to';
value = isNaN(parseInt(value)) ? 0 : parseInt(value);
let rotationVal;
if (move == 'by') rotationVal = token.data.rotation + value;
else rotationVal = value;
if (compatibleCore("0.8.1")) token.document.update({rotation: rotationVal});
else token.update({rotation: rotationVal});
}
///////////////////////////////////////////////
/**
* Get name/id
*/
getTokenName(token) {
return token.name;
}
getTokenId(token) {
return token.id;
}
getActorName(token) {
return token.actor.name;
}
getActorId(token) {
return token.actor.id;
}
////////////////////////////////////////////////////
getTokenIcon(token) {
return token.data.img;
}
getActorIcon(token) {
return token.actor.data.img;
}
/***********************************************************************
* System agnostic functions
***********************************************************************/
getHP(token) {
return this.system.getHP(token);
}
getTempHP(token) {
return this.system.getTempHP(token);
}
getAC(token) {
return this.system.getAC(token);
}
getShieldHP(token) {
return this.system.getShieldHP(token);
}
getSpeed(token) {
return this.system.getSpeed(token);
}
getInitiative(token) {
return this.system.getInitiative(token);
}
toggleInitiative(token) {
return this.system.toggleInitiative(token);
}
getPassivePerception(token) {
return this.system.getPassivePerception(token);
}
getPassiveInvestigation(token) {
return this.system.getPassiveInvestigation(token);
}
getAbility(token, ability) {
return this.system.getAbility(token, ability);
}
getAbilityModifier(token, ability) {
return this.system.getAbilityModifier(token, ability);
}
getAbilitySave(token, ability) {
return this.system.getAbilitySave(token, ability);
}
getSkill(token, skill) {
return this.system.getSkill(token, skill);
}
getProficiency(token) {
return this.system.getProficiency(token);
}
/**
* Conditions
*/
getConditionIcon(condition) {
return this.system.getConditionIcon(condition);
}
getConditionActive(token,condition) {
return this.system.getConditionActive(token,condition);
}
toggleCondition(token,condition) {
return this.system.toggleCondition(token,condition);
}
/**
* Roll
*/
roll(token,roll,options,ability,skill,save) {
return this.system.roll(token,roll,options,ability,skill,save);
}
/**
* Items
*/
getItems(token,itemType) {
return this.system.getItems(token,itemType);
}
getItemUses(item) {
return this.system.getItemUses(item);
}
/**
* Features
*/
getFeatures(token,featureType) {
return this.system.getFeatures(token,featureType);
}
getFeatureUses(item) {
return this.system.getFeatureUses(item);
}
/**
* Spells
*/
getSpells(token,level) {
return this.system.getSpells(token,level);
}
getSpellUses(token,level,item) {
return this.system.getSpellUses(token,level,item);
}
}

File diff suppressed because it is too large Load Diff