Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cf974455a | ||
|
|
e7e9b84227 | ||
|
|
108c955ac2 | ||
|
|
4d320a5f6c | ||
|
|
ccacd3e26e | ||
|
|
d3c2c6465d | ||
|
|
bc0804bc08 | ||
|
|
49bc188dde |
@@ -23,6 +23,8 @@ export var tokenHelper;
|
|||||||
|
|
||||||
export const moduleName = "MaterialDeck";
|
export const moduleName = "MaterialDeck";
|
||||||
|
|
||||||
|
export let gamingSystem = "dnd5e";
|
||||||
|
|
||||||
let ready = false;
|
let ready = false;
|
||||||
|
|
||||||
export let hotbarUses = false;
|
export let hotbarUses = false;
|
||||||
@@ -65,7 +67,7 @@ async function analyzeWSmessage(msg){
|
|||||||
const msg = {
|
const msg = {
|
||||||
target: "SD",
|
target: "SD",
|
||||||
type: "init",
|
type: "init",
|
||||||
system: game.system.id
|
system: gamingSystem
|
||||||
}
|
}
|
||||||
ws.send(JSON.stringify(msg));
|
ws.send(JSON.stringify(msg));
|
||||||
|
|
||||||
@@ -222,7 +224,7 @@ function startWebsocket() {
|
|||||||
const msg2 = {
|
const msg2 = {
|
||||||
target: "SD",
|
target: "SD",
|
||||||
type: "init",
|
type: "init",
|
||||||
system: game.system.id
|
system: gamingSystem
|
||||||
}
|
}
|
||||||
ws.send(JSON.stringify(msg2));
|
ws.send(JSON.stringify(msg2));
|
||||||
clearInterval(wsInterval);
|
clearInterval(wsInterval);
|
||||||
@@ -291,6 +293,9 @@ Hooks.once('ready', async()=>{
|
|||||||
registerSettings();
|
registerSettings();
|
||||||
enableModule = (game.settings.get(moduleName,'Enable')) ? true : false;
|
enableModule = (game.settings.get(moduleName,'Enable')) ? true : false;
|
||||||
|
|
||||||
|
const systemOverride = game.settings.get(moduleName,'systemOverride');
|
||||||
|
gamingSystem = systemOverride != '' ? systemOverride : game.system.id;
|
||||||
|
|
||||||
soundboard = new SoundboardControl();
|
soundboard = new SoundboardControl();
|
||||||
streamDeck = new StreamDeck();
|
streamDeck = new StreamDeck();
|
||||||
tokenControl = new TokenControl();
|
tokenControl = new TokenControl();
|
||||||
@@ -391,6 +396,11 @@ Hooks.once('ready', async()=>{
|
|||||||
if (hotbarUsesTemp != undefined) hotbarUses = true;
|
if (hotbarUsesTemp != undefined) hotbarUses = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateActor(id) {
|
||||||
|
const token = tokenHelper.getTokenFromActorId(id)
|
||||||
|
tokenControl.update(token.id);
|
||||||
|
}
|
||||||
|
|
||||||
Hooks.on('updateToken',(scene,token)=>{
|
Hooks.on('updateToken',(scene,token)=>{
|
||||||
if (enableModule == false || ready == false) return;
|
if (enableModule == false || ready == false) return;
|
||||||
let tokenId = token._id;
|
let tokenId = token._id;
|
||||||
@@ -400,19 +410,28 @@ Hooks.on('updateToken',(scene,token)=>{
|
|||||||
|
|
||||||
Hooks.on('updateActor',(actor)=>{
|
Hooks.on('updateActor',(actor)=>{
|
||||||
if (enableModule == false || ready == false) return;
|
if (enableModule == false || ready == false) return;
|
||||||
let children = canvas.tokens.children[0].children;
|
updateActor(actor.id);
|
||||||
for (let i=0; i<children.length; i++){
|
|
||||||
if (children[i].actor.id == actor._id){
|
|
||||||
let tokenId = children[i].id;
|
|
||||||
if (tokenId == canvas.tokens.controlled[0]?.id) {
|
|
||||||
tokenControl.update(canvas.tokens.controlled[0]?.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (macroControl != undefined) macroControl.updateAll();
|
if (macroControl != undefined) macroControl.updateAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Hooks.on('createActiveEffect',(data)=>{
|
||||||
|
if (enableModule == false || ready == false) return;
|
||||||
|
updateActor(data.parent.id);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
Hooks.on('deleteActiveEffect',(data)=>{
|
||||||
|
if (enableModule == false || ready == false) return;
|
||||||
|
updateActor(data.parent.id);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
Hooks.on('onActorSetCondition',(data)=>{
|
||||||
|
if (enableModule == false || ready == false) return;
|
||||||
|
updateActor(data.actor.id);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
Hooks.on('controlToken',(token,controlled)=>{
|
Hooks.on('controlToken',(token,controlled)=>{
|
||||||
if (enableModule == false || ready == false) return;
|
if (enableModule == false || ready == false) return;
|
||||||
if (controlled) {
|
if (controlled) {
|
||||||
@@ -583,6 +602,11 @@ Hooks.on('about-time.clockRunningStatus', ()=>{
|
|||||||
externalModules.updateAll();
|
externalModules.updateAll();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Hooks.on('updateTile',()=>{
|
||||||
|
if (enableModule == false || ready == false) return;
|
||||||
|
externalModules.updateAll();
|
||||||
|
});
|
||||||
|
|
||||||
Hooks.once('init', ()=>{
|
Hooks.once('init', ()=>{
|
||||||
//CONFIG.debug.hooks = true;
|
//CONFIG.debug.hooks = true;
|
||||||
//registerSettings(); //in ./src/settings.js
|
//registerSettings(); //in ./src/settings.js
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Instructions and more info can be found in the <a href="https://github.com/CDeen
|
|||||||
Module manifest: https://raw.githubusercontent.com/CDeenen/MaterialDeck/Master/module.json
|
Module manifest: https://raw.githubusercontent.com/CDeenen/MaterialDeck/Master/module.json
|
||||||
|
|
||||||
## Software Versions & Module Incompatibilities
|
## Software Versions & Module Incompatibilities
|
||||||
<b>Foundry VTT:</b> Tested on 0.7.9 - 0.9<br>
|
<b>Foundry VTT:</b> Tested on 0.7.9 - 0.8.5<br>
|
||||||
<b>Module Incompatibilities:</b> None known.<br>
|
<b>Module Incompatibilities:</b> None known.<br>
|
||||||
|
|
||||||
## Developer Guide
|
## Developer Guide
|
||||||
|
|||||||
23
changelog.md
23
changelog.md
@@ -1,4 +1,27 @@
|
|||||||
# Changelog Material Deck Module
|
# Changelog Material Deck Module
|
||||||
|
### v1.4.9 - 16-04-2022
|
||||||
|
Fixes:
|
||||||
|
<ul>
|
||||||
|
<li>PF2e: Fixed rolls and conditions for compatibility with latest pf2e version. Thanks to Kyamsil</li>
|
||||||
|
<li>Token Action => Set Vison: Setting light is working again</li>
|
||||||
|
<li>Other Actions => Roll Dice: Roll result would show as 'NaN' in Foundry v9, this is fixed</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Additions:
|
||||||
|
<ul>
|
||||||
|
<li>Added support for the Starfinder system</li>
|
||||||
|
<li>External Modules => Added support for 'Monks Active Tile Triggers'</li>
|
||||||
|
<li>Added 'System Override' module setting. If your system is not supported, MD defaults to 5e. Here you can choose a supported system that is most similar to yours and have Material Deck use that system instead.</li>
|
||||||
|
<li>Token Action => Displayed Stats: A colored border is drawn around certain skills, saves, etc to indicate proficiency (compatible systems: dnd5e, pf2e). Thanks to Kyamsil</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
### v1.4.8 - 22-12-2021
|
||||||
|
Fixes:
|
||||||
|
<ul>
|
||||||
|
<li>'Device Configuration' would not save its settings properly</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
### v1.4.7 - 20-12-2021
|
### v1.4.7 - 20-12-2021
|
||||||
Fixes:
|
Fixes:
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
BIN
img/token/skills/.thumb/lor.png.jpg
Normal file
BIN
img/token/skills/.thumb/lor.png.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -10,6 +10,7 @@ his.png: https://game-icons.net/1x1/delapouite/backward-time.html
|
|||||||
ins.png: https://game-icons.net/1x1/lorc/light-bulb.html
|
ins.png: https://game-icons.net/1x1/lorc/light-bulb.html
|
||||||
itm.png: https://game-icons.net/1x1/lorc/one-eyed.html
|
itm.png: https://game-icons.net/1x1/lorc/one-eyed.html
|
||||||
inv.png: https://game-icons.net/1x1/lorc/magnifying-glass.html
|
inv.png: https://game-icons.net/1x1/lorc/magnifying-glass.html
|
||||||
|
lor.png: https://game-icons.net/1x1/lorc/bookmarklet.html
|
||||||
med.png: https://game-icons.net/1x1/delapouite/first-aid-kit.html
|
med.png: https://game-icons.net/1x1/delapouite/first-aid-kit.html
|
||||||
nat.png: https://game-icons.net/1x1/delapouite/forest.html
|
nat.png: https://game-icons.net/1x1/delapouite/forest.html
|
||||||
occ.png: https://game-icons.net/1x1/skoll/pentacle.html
|
occ.png: https://game-icons.net/1x1/skoll/pentacle.html
|
||||||
|
|||||||
BIN
img/token/skills/lor.png
Normal file
BIN
img/token/skills/lor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -21,6 +21,8 @@
|
|||||||
"MaterialDeck.Sett.ImageBrightnessHint": "Sets the brightness of the default white images. If the Image Cache Size is bigger than 0, perform a refresh for instant results.",
|
"MaterialDeck.Sett.ImageBrightnessHint": "Sets the brightness of the default white images. If the Image Cache Size is bigger than 0, perform a refresh for instant results.",
|
||||||
"MaterialDeck.Sett.NrOfConnMessages": "Number of Connection Warnings",
|
"MaterialDeck.Sett.NrOfConnMessages": "Number of Connection Warnings",
|
||||||
"MaterialDeck.Sett.NrOfConnMessagesHint": "Sets the number of times a connection warning is displayed if Material Deck cannot connect to Material Server. If set to 0, it will give not be limited.",
|
"MaterialDeck.Sett.NrOfConnMessagesHint": "Sets the number of times a connection warning is displayed if Material Deck cannot connect to Material Server. If set to 0, it will give not be limited.",
|
||||||
|
"MaterialDeck.Sett.SystemOverride": "System Override",
|
||||||
|
"MaterialDeck.Sett.SystemOverrideHint": "Overrides the automatic gaming system detection. Unsupported systems default to dnd5e, but if an unsupported system is similar to a different system you can specify that system here. Leave empty for system autodetection.",
|
||||||
|
|
||||||
"MaterialDeck.PL.Unrestricted": "Unrestricted",
|
"MaterialDeck.PL.Unrestricted": "Unrestricted",
|
||||||
"MaterialDeck.PL.OneTrackPlaylist": "One track per playlist",
|
"MaterialDeck.PL.OneTrackPlaylist": "One track per playlist",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "MaterialDeck",
|
"name": "MaterialDeck",
|
||||||
"title": "Material Deck",
|
"title": "Material Deck",
|
||||||
"description": "Material Deck allows you to control Foundry using an Elgato Stream Deck",
|
"description": "Material Deck allows you to control Foundry using an Elgato Stream Deck",
|
||||||
"version": "1.4.7",
|
"version": "1.4.9",
|
||||||
"author": "CDeenen",
|
"author": "CDeenen",
|
||||||
"authors": {
|
"authors": {
|
||||||
"name": "CDeenen",
|
"name": "CDeenen",
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export class ExternalModules{
|
|||||||
else if (module == 'lockView') this.updateLockView(settings,context,device);
|
else if (module == 'lockView') this.updateLockView(settings,context,device);
|
||||||
else if (module == 'aboutTime') this.updateAboutTime(settings,context,device);
|
else if (module == 'aboutTime') this.updateAboutTime(settings,context,device);
|
||||||
else if (module == 'soundscape') this.updateSoundscape(settings,context,device);
|
else if (module == 'soundscape') this.updateSoundscape(settings,context,device);
|
||||||
|
else if (module == 'monksActiveTiles') this.updateMonksActiveTiles(settings,context,device);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPress(settings,context,device){
|
keyPress(settings,context,device){
|
||||||
@@ -84,6 +85,7 @@ export class ExternalModules{
|
|||||||
else if (module == 'lockView') this.keyPressLockView(settings,context,device);
|
else if (module == 'lockView') this.keyPressLockView(settings,context,device);
|
||||||
else if (module == 'aboutTime') this.keyPressAboutTime(settings,context,device);
|
else if (module == 'aboutTime') this.keyPressAboutTime(settings,context,device);
|
||||||
else if (module == 'soundscape') this.keyPressSoundscape(settings,context,device);
|
else if (module == 'soundscape') this.keyPressSoundscape(settings,context,device);
|
||||||
|
else if (module == 'monksActiveTiles') this.keyPressMonksActiveTiles(settings,context,device);
|
||||||
}
|
}
|
||||||
|
|
||||||
getModuleEnable(moduleId){
|
getModuleEnable(moduleId){
|
||||||
@@ -990,6 +992,42 @@ export class ExternalModules{
|
|||||||
|
|
||||||
this.updateAll();
|
this.updateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//Monks Active Tile Triggers
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
updateMonksActiveTiles(settings,context,device) {
|
||||||
|
const id = settings.monksActiveTilesId;
|
||||||
|
if (id == undefined || id == '') return;
|
||||||
|
let tile = canvas.background.placeables.find(t => t.id == id);
|
||||||
|
if (tile == undefined) return;
|
||||||
|
const tileData = tile.data.flags?.['monks-active-tiles'];
|
||||||
|
if (tileData == undefined) return;
|
||||||
|
|
||||||
|
let ring = 1;
|
||||||
|
let ringColor = '#000000';
|
||||||
|
let background = '#000000';
|
||||||
|
if (tileData.active) {
|
||||||
|
ring = 2;
|
||||||
|
ringColor = '#00ff00'
|
||||||
|
}
|
||||||
|
let src = tile.data.img;
|
||||||
|
|
||||||
|
streamDeck.setTitle('',context);
|
||||||
|
streamDeck.setIcon(context,device,src,{background:background,ring:ring,ringColor:ringColor});
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPressMonksActiveTiles(settings,context,device) {
|
||||||
|
const mode = settings.monksActiveTilesMode ? settings.monksActiveTilesMode : 'toggle';
|
||||||
|
const id = settings.monksActiveTilesId;
|
||||||
|
if (id == undefined || id == '') return;
|
||||||
|
let tile = canvas.background.placeables.find(t => t.id == id);
|
||||||
|
if (tile == undefined) return;
|
||||||
|
const tileData = tile.data.flags?.['monks-active-tiles'];
|
||||||
|
if (tileData == undefined) return;
|
||||||
|
|
||||||
|
if (mode == 'toggle') tile.document.setFlag('monks-active-tiles','active',!tileData.active);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1214,7 +1214,11 @@ export class deviceConfig extends FormApplication {
|
|||||||
*/
|
*/
|
||||||
getData() {
|
getData() {
|
||||||
this.devices = [];
|
this.devices = [];
|
||||||
const dConfig = game.settings.get(moduleName, 'devices');
|
let dConfig = game.settings.get(moduleName, 'devices');
|
||||||
|
if (Object.prototype.toString.call(game.settings.get('MaterialDeck', 'devices')) === "[object String]") {
|
||||||
|
dConfig = {};
|
||||||
|
game.settings.set(moduleName, 'devices', dConfig);
|
||||||
|
}
|
||||||
|
|
||||||
for (let d of streamDeck.buttonContext) {
|
for (let d of streamDeck.buttonContext) {
|
||||||
let type;
|
let type;
|
||||||
@@ -1263,7 +1267,6 @@ export class deviceConfig extends FormApplication {
|
|||||||
let dConfig = game.settings.get(moduleName, 'devices');
|
let dConfig = game.settings.get(moduleName, 'devices');
|
||||||
delete dConfig[id];
|
delete dConfig[id];
|
||||||
dConfig[id] = {enable: event.currentTarget.checked}
|
dConfig[id] = {enable: event.currentTarget.checked}
|
||||||
|
|
||||||
game.settings.set(moduleName, 'devices', dConfig);
|
game.settings.set(moduleName, 'devices', dConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as MODULE from "../MaterialDeck.js";
|
import * as MODULE from "../MaterialDeck.js";
|
||||||
import {streamDeck} from "../MaterialDeck.js";
|
import {streamDeck, gamingSystem} from "../MaterialDeck.js";
|
||||||
import {compatibleCore} from "./misc.js";
|
import {compatibleCore} from "./misc.js";
|
||||||
|
|
||||||
export class OtherControls{
|
export class OtherControls{
|
||||||
@@ -530,7 +530,7 @@ export class OtherControls{
|
|||||||
if (tokenControlled) r = new Roll(settings.rollDiceFormula,actor.getRollData());
|
if (tokenControlled) r = new Roll(settings.rollDiceFormula,actor.getRollData());
|
||||||
else r = new Roll(settings.rollDiceFormula);
|
else r = new Roll(settings.rollDiceFormula);
|
||||||
|
|
||||||
r.evaluate();
|
r.evaluate({async:false});
|
||||||
|
|
||||||
if (rollFunction == 'public') {
|
if (rollFunction == 'public') {
|
||||||
r.toMessage(r,{rollMode:"roll"})
|
r.toMessage(r,{rollMode:"roll"})
|
||||||
@@ -687,7 +687,7 @@ export class OtherControls{
|
|||||||
|
|
||||||
updateCompendiumBrowser(settings,context,device,options={}){
|
updateCompendiumBrowser(settings,context,device,options={}){
|
||||||
let rendered = options.renderCompendiumBrowser;
|
let rendered = options.renderCompendiumBrowser;
|
||||||
if (rendered == undefined && game.system.id == "pf2e") rendered = (document.getElementById("app-1") != null);
|
if (rendered == undefined && gamingSystem == "pf2e") rendered = (document.getElementById("app-1") != null);
|
||||||
else if (rendered == undefined) rendered = (document.getElementById("compendium-popout") != null);
|
else if (rendered == undefined) rendered = (document.getElementById("compendium-popout") != null);
|
||||||
const background = settings.background ? settings.background : '#000000';
|
const background = settings.background ? settings.background : '#000000';
|
||||||
const ringOffColor = settings.offRing ? settings.offRing : '#000000';
|
const ringOffColor = settings.offRing ? settings.offRing : '#000000';
|
||||||
@@ -701,13 +701,13 @@ export class OtherControls{
|
|||||||
|
|
||||||
keyPressCompendiumBrowser(settings){
|
keyPressCompendiumBrowser(settings){
|
||||||
let element = null;
|
let element = null;
|
||||||
if (game.system.id == "pf2e") element = document.getElementById("app-1")
|
if (gamingSystem == "pf2e") element = document.getElementById("app-1")
|
||||||
else element = document.getElementById("compendium-popout");
|
else element = document.getElementById("compendium-popout");
|
||||||
const rendered = (element != null);
|
const rendered = (element != null);
|
||||||
|
|
||||||
if (rendered)
|
if (rendered)
|
||||||
element.getElementsByClassName("close")[0].click();
|
element.getElementsByClassName("close")[0].click();
|
||||||
else if (game.system.id == "pf2e")
|
else if (gamingSystem == "pf2e")
|
||||||
document.getElementsByClassName("compendium-browser-btn")[0].click()
|
document.getElementsByClassName("compendium-browser-btn")[0].click()
|
||||||
else
|
else
|
||||||
ui.compendium.renderPopout();
|
ui.compendium.renderPopout();
|
||||||
|
|||||||
@@ -79,6 +79,19 @@ export const registerSettings = async function() {
|
|||||||
onChange: x => window.location.reload()
|
onChange: x => window.location.reload()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System override
|
||||||
|
*/
|
||||||
|
game.settings.register(MODULE.moduleName,'systemOverride', {
|
||||||
|
name: "MaterialDeck.Sett.SystemOverride",
|
||||||
|
hint: "MaterialDeck.Sett.SystemOverrideHint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
default: "",
|
||||||
|
type: String,
|
||||||
|
onChange: x => window.location.reload()
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the ip address of the server
|
* Sets the ip address of the server
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -134,4 +134,15 @@ export class demonlord{
|
|||||||
rollItem(item) {
|
rollItem(item) {
|
||||||
return item.roll()
|
return item.roll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -199,4 +199,15 @@ export class dnd35e{
|
|||||||
rollItem(item) {
|
rollItem(item) {
|
||||||
return item.roll()
|
return item.roll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
import {compatibleCore} from "../misc.js";
|
import {compatibleCore} from "../misc.js";
|
||||||
|
|
||||||
|
const proficiencyColors = {
|
||||||
|
0: "#000000",
|
||||||
|
0.5: "#804A00",
|
||||||
|
1: "#C0C0C0",
|
||||||
|
2: "#FFD700"
|
||||||
|
}
|
||||||
|
|
||||||
export class dnd5e{
|
export class dnd5e{
|
||||||
constructor(){
|
constructor(){
|
||||||
|
|
||||||
@@ -202,4 +209,19 @@ export class dnd5e{
|
|||||||
rollItem(item) {
|
rollItem(item) {
|
||||||
return item.roll()
|
return item.roll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
const profLevel = token.actor.data.data?.skills[skill]?.proficient;
|
||||||
|
if (profLevel == undefined) return;
|
||||||
|
return proficiencyColors?.[profLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
const profLevel = token.actor.data.data?.abilities[save]?.proficient;
|
||||||
|
if (profLevel == undefined) return;
|
||||||
|
return proficiencyColors?.[profLevel];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -235,4 +235,15 @@ export class forbiddenlands{
|
|||||||
sheet.rollSpecificAttack(item.id);
|
sheet.rollSpecificAttack(item.id);
|
||||||
return item.sendToChat();
|
return item.sendToChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,58 @@
|
|||||||
import {compatibleCore} from "../misc.js";
|
import {compatibleCore} from "../misc.js";
|
||||||
import {otherControls} from "../../MaterialDeck.js";
|
import {otherControls} from "../../MaterialDeck.js";
|
||||||
|
|
||||||
|
const limitedSheets = ['loot', 'vehicle'];
|
||||||
|
const proficiencyColors =
|
||||||
|
{
|
||||||
|
untrained: "#424242",
|
||||||
|
trained: "#171F69",
|
||||||
|
expert: "#3C005E",
|
||||||
|
master: "#664400",
|
||||||
|
legendary: "#5E0000"
|
||||||
|
};
|
||||||
|
|
||||||
export class pf2e{
|
export class pf2e{
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenSpellData = new Map();
|
||||||
|
|
||||||
getHP(token) {
|
getHP(token) {
|
||||||
const hp = token.actor.data.data.attributes.hp;
|
const hp = token.actor.attributes?.hp;
|
||||||
return {
|
return {
|
||||||
value: hp.value,
|
value: (hp?.value == null) ? 0 : hp.value,
|
||||||
max: hp.max
|
max: (hp?.max == null) ? 0 : hp.max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTempHP(token) {
|
getTempHP(token) {
|
||||||
const hp = token.actor.data.data.attributes.hp;
|
const hp = token.actor.attributes?.hp;
|
||||||
return {
|
return {
|
||||||
value: (hp.temp == null) ? 0 : hp.temp,
|
value: (hp?.temp == null) ? 0 : hp.temp,
|
||||||
max: (hp.tempmax == null) ? 0 : hp.tempmax
|
max: (hp?.tempmax == null) ? 0 : hp.tempmax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAC(token) {
|
getAC(token) {
|
||||||
return token.actor.data.data.attributes.ac.value;
|
const ac = token.actor.attributes?.ac;
|
||||||
|
return (ac?.value == null) ? 10 : ac?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getShieldHP(token) {
|
getShieldHP(token) {
|
||||||
return token.actor.data.data.attributes.shield.value;
|
const shieldhp = token.actor.attributes.shield
|
||||||
|
return (shieldhp?.value == null) ? 0 : shieldhp?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpeed(token) {
|
getSpeed(token) {
|
||||||
let speed = `${token.actor.data.data.attributes.speed.total}'`;
|
if (this.isLimitedSheet(token.actor) || token.actor.type == 'hazard') {
|
||||||
const otherSpeeds = token.actor.data.data.attributes.speed.otherSpeeds;
|
if (token.actor.type == 'vehicle') {
|
||||||
|
return token.actor.data.data.details.speed;
|
||||||
|
} else return '';
|
||||||
|
}
|
||||||
|
let speed = `${token.actor.attributes.speed?.total}'`;
|
||||||
|
const otherSpeeds = token.actor.attributes.speed?.otherSpeeds;
|
||||||
if (otherSpeeds.length > 0)
|
if (otherSpeeds.length > 0)
|
||||||
for (let os of otherSpeeds)
|
for (let os of otherSpeeds)
|
||||||
speed += `\n${os.type} ${os.total}'`;
|
speed += `\n${os.type} ${os.total}'`;
|
||||||
@@ -40,14 +60,20 @@ export class pf2e{
|
|||||||
}
|
}
|
||||||
|
|
||||||
getInitiative(token) {
|
getInitiative(token) {
|
||||||
let initiativeModifier = token.actor.data.data.attributes?.initiative.totalModifier;
|
if (this.isLimitedSheet(token.actor) || token.actor.type == 'familiar') return '';
|
||||||
let initiativeAbility = token.actor.data.data.attributes?.initiative.ability;
|
if (token.actor.type == 'hazard') {
|
||||||
|
let initiative = token.actor.attributes?.stealth?.value;
|
||||||
|
return `Init: Stealth (${initiative})`;
|
||||||
|
}
|
||||||
|
let initiative = token.actor.attributes.initiative;
|
||||||
|
let initiativeModifier = initiative?.totalModifier;
|
||||||
|
let initiativeLabel = initiative?.label.replace('iative',''); //Initiative is too long for the button
|
||||||
if (initiativeModifier > 0) {
|
if (initiativeModifier > 0) {
|
||||||
initiativeModifier = `+${initiativeModifier}`;
|
initiativeModifier = `+${initiativeModifier}`;
|
||||||
} else {
|
} else {
|
||||||
initiativeModifier = this.getPerception(token); //NPCs won't have a valid Initiative value, so default to use Perception
|
initiativeModifier = this.getPerception(token); //NPCs won't have a valid Initiative value, so default to use Perception
|
||||||
}
|
}
|
||||||
return (initiativeAbility != '') ? `(${initiativeAbility}): ${initiativeModifier}` : `(perception): ${initiativeModifier}`;
|
return `${initiativeLabel} (${initiativeModifier})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleInitiative(token) {
|
toggleInitiative(token) {
|
||||||
@@ -63,46 +89,73 @@ export class pf2e{
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPerception(token) {
|
getPerception(token) {
|
||||||
let perception = token.actor.data.data.attributes?.perception.totalModifier;
|
if (this.isLimitedSheet(token.actor) || token.actor.type == 'hazard') return '';
|
||||||
|
let perception = token.actor.attributes.perception?.totalModifier;
|
||||||
return (perception >= 0) ? `+${perception}` : perception;
|
return (perception >= 0) ? `+${perception}` : perception;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbility(token, ability) {
|
getAbility(token, ability) {
|
||||||
|
if (this.isLimitedSheet(token.actor) || token.actor.type == 'familiar') return '';
|
||||||
if (ability == undefined) ability = 'str';
|
if (ability == undefined) ability = 'str';
|
||||||
return token.actor.data.data.abilities?.[ability].value;
|
return token.actor.abilities?.[ability]?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbilityModifier(token, ability) {
|
getAbilityModifier(token, ability) {
|
||||||
|
if (this.isLimitedSheet(token.actor) || token.actor.type == 'hazard' || token.actor.type == 'familiar') return '';
|
||||||
if (ability == undefined) ability = 'str';
|
if (ability == undefined) ability = 'str';
|
||||||
let val = token.actor.data.data.abilities?.[ability].mod;
|
let val = token.actor.abilities?.[ability]?.mod;
|
||||||
return (val >= 0) ? `+${val}` : val;
|
return (val >= 0) ? `+${val}` : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbilitySave(token, ability) {
|
getAbilitySave(token, ability) {
|
||||||
if (ability == undefined) ability = 'fortitude';
|
ability = this.fixSave(ability);
|
||||||
else if (ability == 'fort') ability = 'fortitude';
|
const save = this.findSave(token, ability);
|
||||||
else if (ability == 'ref') ability = 'reflex';
|
if (save == undefined) return '';
|
||||||
else if (ability == 'will') ability = 'will';
|
let val = save?.value;
|
||||||
let val = token.actor.data.data.saves?.[ability].value;
|
|
||||||
return (val >= 0) ? `+${val}` : val;
|
return (val >= 0) ? `+${val}` : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findSave(token, ability) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return;
|
||||||
|
return token.actor.data.data.saves?.[ability];
|
||||||
|
}
|
||||||
|
|
||||||
|
fixSave(ability) {
|
||||||
|
if (ability == undefined) return 'fortitude';
|
||||||
|
else if (ability == 'fort') return 'fortitude';
|
||||||
|
else if (ability == 'ref') return 'reflex';
|
||||||
|
else if (ability == 'will') return 'will';
|
||||||
|
}
|
||||||
|
|
||||||
getSkill(token, skill) {
|
getSkill(token, skill) {
|
||||||
|
const tokenSkill = this.findSkill(token, skill);
|
||||||
|
if (tokenSkill == undefined) return '';
|
||||||
|
|
||||||
|
if (skill.startsWith('lor')) {
|
||||||
|
return `${tokenSkill.name}: +${tokenSkill.totalModifier}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = tokenSkill.totalModifier;
|
||||||
|
return (val >= 0) ? `+${val}` : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
findSkill(token, skill) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return;
|
||||||
if (skill == undefined) skill = 'acr';
|
if (skill == undefined) skill = 'acr';
|
||||||
if (skill.startsWith('lor')) {
|
if (skill.startsWith('lor')) {
|
||||||
const index = parseInt(skill.split('_')[1])-1;
|
const index = parseInt(skill.split('_')[1])-1;
|
||||||
const loreSkills = this.getLoreSkills(token);
|
const loreSkills = this.getLoreSkills(token);
|
||||||
if (loreSkills.length > index) {
|
if (loreSkills.length > index) {
|
||||||
return `${loreSkills[index].name}: +${loreSkills[index].totalModifier}`;
|
return loreSkills[index];
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const val = token.actor.data.data.skills?.[skill].totalModifier;
|
return token.actor.data.data.skills?.[skill];
|
||||||
return (val >= 0) ? `+${val}` : val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getLoreSkills(token) {
|
getLoreSkills(token) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return [];
|
||||||
const skills = token.actor.data.data.skills;
|
const skills = token.actor.data.data.skills;
|
||||||
return Object.keys(skills).map(key => skills[key]).filter(s => s.lore == true);
|
return Object.keys(skills).map(key => skills[key]).filter(s => s.lore == true);
|
||||||
}
|
}
|
||||||
@@ -142,9 +195,7 @@ export class pf2e{
|
|||||||
const effect = this.getConditionValue(token,condition);
|
const effect = this.getConditionValue(token,condition);
|
||||||
if (effect == undefined) {
|
if (effect == undefined) {
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
const Condition = this.getConditionName(condition);
|
await game.pf2e.ConditionManager.addConditionToToken(condition, token);
|
||||||
const newCondition = game.pf2e.ConditionManager.getCondition(Condition);
|
|
||||||
await game.pf2e.ConditionManager.addConditionToToken(newCondition, token);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@@ -166,19 +217,16 @@ export class pf2e{
|
|||||||
async toggleCondition(token,condition) {
|
async toggleCondition(token,condition) {
|
||||||
if (condition == undefined) condition = 'removeAll';
|
if (condition == undefined) condition = 'removeAll';
|
||||||
if (condition == 'removeAll'){
|
if (condition == 'removeAll'){
|
||||||
for( let effect of token.actor.items.filter(i => i.type == 'condition'))
|
for( let existing of token.actor.items.filter(i => i.type == 'condition'))
|
||||||
await effect.delete();
|
await game.pf2e.ConditionManager.removeConditionFromToken(existing.data._id, token);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const effect = this.getCondition(token,condition);
|
const effect = this.getCondition(token,condition);
|
||||||
if (effect == undefined) {
|
if (effect == undefined) {
|
||||||
const Condition = this.getConditionName(condition);
|
await game.pf2e.ConditionManager.addConditionToToken(condition, token);
|
||||||
const newCondition = game.pf2e.ConditionManager.getCondition(Condition);
|
|
||||||
newCondition.data.sources.hud = !0,
|
|
||||||
await game.pf2e.ConditionManager.addConditionToToken(newCondition, token);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
effect.delete();
|
await game.pf2e.ConditionManager.removeConditionFromToken(effect.data._id, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -188,19 +236,28 @@ export class pf2e{
|
|||||||
* Roll
|
* Roll
|
||||||
*/
|
*/
|
||||||
roll(token,roll,options,ability,skill,save) {
|
roll(token,roll,options,ability,skill,save) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return;
|
||||||
|
options.skipDialog = true;
|
||||||
if (roll == undefined) roll = 'skill';
|
if (roll == undefined) roll = 'skill';
|
||||||
if (ability == undefined) ability = 'str';
|
if (ability == undefined) ability = 'str';
|
||||||
if (skill == undefined) skill = 'acr';
|
if (skill == undefined) skill = 'acr';
|
||||||
if (save == undefined) save = 'fort';
|
if (save == undefined) save = 'fort';
|
||||||
if (roll == 'perception') token.actor.data.data.attributes.perception.roll(options);
|
if (roll == 'perception') {
|
||||||
if (roll == 'initiative') token.actor.rollInitiative(options);
|
this.checkRoll(`Perception Check`, token.actor.perception, 'perception-check', token.actor);
|
||||||
if (roll == 'ability') token.actor.rollAbility(options, ability);
|
}
|
||||||
|
if (roll == 'initiative') {
|
||||||
|
token.actor.rollInitiative({createCombatants:true, initiativeOptions: {skipDialog: true}});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roll == 'ability') return; //Ability Checks are not supported in pf2e
|
||||||
else if (roll == 'save') {
|
else if (roll == 'save') {
|
||||||
let ability = save;
|
let ability = save;
|
||||||
if (ability == 'fort') ability = 'fortitude';
|
if (ability == 'fort') ability = 'fortitude';
|
||||||
else if (ability == 'ref') ability = 'reflex';
|
else if (ability == 'ref') ability = 'reflex';
|
||||||
else if (ability == 'will') ability = 'will';
|
else if (ability == 'will') ability = 'will';
|
||||||
token.actor.rollSave(options, ability);
|
if (token.actor.type == 'hazard' && ability == 'will') return; //Hazards don't have Will saves
|
||||||
|
let abilityName = ability.charAt(0).toUpperCase() + ability.slice(1);
|
||||||
|
this.checkRoll(`${abilityName} Saving Throw`, token.actor.saves?.[ability], 'saving-throw', token.actor);
|
||||||
}
|
}
|
||||||
else if (roll == 'skill') {
|
else if (roll == 'skill') {
|
||||||
if (skill.startsWith('lor')) {
|
if (skill.startsWith('lor')) {
|
||||||
@@ -212,15 +269,23 @@ export class pf2e{
|
|||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
token.actor.data.data.skills?.[skill].roll(options);
|
let skillName = token.actor.data.data.skills?.[skill].name;
|
||||||
|
skillName = skillName.charAt(0).toUpperCase() + skillName.slice(1);
|
||||||
|
this.checkRoll(`Skill Check: ${skillName}`, token.actor.skills?.[skill], 'skill-check', token.actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkRoll(checkLabel,stat,type,actor) {
|
||||||
|
let checkModifier = new game.pf2e.CheckModifier(checkLabel, stat);
|
||||||
|
game.pf2e.Check.roll(checkModifier, {type:type, actor: actor, skipDialog: true}, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items
|
* Items
|
||||||
*/
|
*/
|
||||||
getItems(token,itemType) {
|
getItems(token,itemType) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return [];
|
||||||
if (itemType == undefined) itemType = 'any';
|
if (itemType == undefined) itemType = 'any';
|
||||||
const allItems = token.actor.items;
|
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');
|
if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container');
|
||||||
@@ -229,13 +294,14 @@ export class pf2e{
|
|||||||
}
|
}
|
||||||
|
|
||||||
getItemUses(item) {
|
getItemUses(item) {
|
||||||
return {available: item.data.data.quantity.value};
|
return {available: item.quantity.value};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Features
|
* Features
|
||||||
*/
|
*/
|
||||||
getFeatures(token,featureType) {
|
getFeatures(token,featureType) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return [];
|
||||||
if (featureType == undefined) featureType = 'any';
|
if (featureType == undefined) featureType = 'any';
|
||||||
const allItems = token.actor.items;
|
const allItems = token.actor.items;
|
||||||
if (featureType == 'any') return allItems.filter(i => i.type == 'ancestry' || i.type == 'background' || i.type == 'class' || i.type == 'feat' || i.type == 'action');
|
if (featureType == 'any') return allItems.filter(i => i.type == 'ancestry' || i.type == 'background' || i.type == 'class' || i.type == 'feat' || i.type == 'action');
|
||||||
@@ -244,7 +310,10 @@ export class pf2e{
|
|||||||
if (featureType == 'action-int') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'interaction');
|
if (featureType == 'action-int') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'interaction');
|
||||||
if (featureType == 'action-off') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'offensive');
|
if (featureType == 'action-off') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'offensive');
|
||||||
if (featureType == 'strike') { //Strikes are not in the actor.items collection
|
if (featureType == 'strike') { //Strikes are not in the actor.items collection
|
||||||
let actions = token.actor.data.data.actions.filter(a=>a.type == 'strike');
|
if (token.actor.type == 'hazard' || token.actor.type == 'familiar') {
|
||||||
|
return allItems.filter(i => i.type == 'melee' || i.type == 'ranged');
|
||||||
|
}
|
||||||
|
let actions = token.actor.data.data.actions?.filter(a=>a.type == 'strike');
|
||||||
for (let a of actions) {
|
for (let a of actions) {
|
||||||
a.img = a.imageUrl;
|
a.img = a.imageUrl;
|
||||||
a.data = {
|
a.data = {
|
||||||
@@ -257,38 +326,188 @@ export class pf2e{
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFeatureUses(item) {
|
getFeatureUses(item) {
|
||||||
if (item.data.type == 'class') return {available: item.actor.data.data.details.level.value};
|
if (item.data.type == 'class') return {available: item.actor.details.level.value};
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spells
|
* Spells
|
||||||
*/
|
*/
|
||||||
|
buildSpellData(token) {
|
||||||
|
let spellData = [[],[],[],[],[],[],[],[],[],[],[],[]];
|
||||||
|
let spellcastingEntries = token.actor.spellcasting;
|
||||||
|
const actorLevel = token.actor.data.data.details.level.value;
|
||||||
|
spellcastingEntries.forEach(spellCastingEntry => {
|
||||||
|
let highestSpellSlot = Math.ceil(actorLevel/2);
|
||||||
|
while (spellCastingEntry.data.data.slots?.[`slot${highestSpellSlot}`]?.max <= 0) highestSpellSlot--;
|
||||||
|
//Prepared (not flexible)
|
||||||
|
if (spellCastingEntry.data.data.prepared?.value == 'prepared' && !spellCastingEntry?.data.data?.prepared?.flexible == true) {
|
||||||
|
for (let slotLevel = 0; slotLevel < 11; slotLevel++) {
|
||||||
|
for (let slot = 0; slot < spellCastingEntry.data.data.slots?.[`slot${slotLevel}`].max; slot++) {
|
||||||
|
let spellId = spellCastingEntry.data.data.slots?.[`slot${slotLevel}`].prepared?.[slot].id;
|
||||||
|
let spell = spellCastingEntry.spells.get(spellId);
|
||||||
|
if (spellId != null) {
|
||||||
|
spellData[slotLevel].push(spell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spellCastingEntry.spells.forEach( ses => {
|
||||||
|
if ((spellCastingEntry.data.data.prepared.value == 'spontaneous' || spellCastingEntry.data.data.prepared?.flexible == true) && ses.data.data.location.signature == true) {
|
||||||
|
let baseLevel = this.getSpellLevel(ses);
|
||||||
|
for (let level = baseLevel; level <= highestSpellSlot; level++) {
|
||||||
|
spellData[level].push(ses);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spellData[this.getSpellLevel(ses)].push(ses);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.tokenSpellData.set(token.id, {spellData: spellData, timeStamp: Date.now()});
|
||||||
|
return spellData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpellData(token) {
|
||||||
|
let existingSpellData = this.tokenSpellData.get(token.id);
|
||||||
|
if (existingSpellData == undefined) return this.buildSpellData(token);
|
||||||
|
let milisSinceCreation = Date.now() - existingSpellData.timeStamp;
|
||||||
|
if (milisSinceCreation > 10000) {
|
||||||
|
this.tokenSpellData.delete(token.id);
|
||||||
|
return this.buildSpellData(token);
|
||||||
|
}
|
||||||
|
return existingSpellData.spellData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpellLevel(spell) {
|
||||||
|
if (spell.isFocusSpell == true) return 11;
|
||||||
|
if (spell.isCantrip == true) return 0;
|
||||||
|
return spell.level;
|
||||||
|
}
|
||||||
|
|
||||||
getSpells(token,level) {
|
getSpells(token,level) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return '';
|
||||||
if (level == undefined) level = 'any';
|
if (level == undefined) level = 'any';
|
||||||
const allItems = token.actor.items;
|
let spellData = this.getSpellData(token);
|
||||||
if (level == 'any') return allItems.filter(i => i.type == 'spell')
|
|
||||||
if (level == '0') return allItems.filter(i => i.type == 'spell' && i.isCantrip == true)
|
if (level == 'f') return this.getUniqueSpells(spellData[11]);
|
||||||
else return allItems.filter(i => i.type == 'spell' && i.level == level && i.isCantrip == false)
|
if (level == 'any') return this.getUniqueSpells(spellData.flat());
|
||||||
|
return this.getUniqueSpells(spellData[level]);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUniqueSpells(spells) {
|
||||||
|
return Array.from(new Set(spells));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpellUses(token,level,item) {
|
getSpellUses(token,level,item) {
|
||||||
|
if (this.isLimitedSheet(token.actor)) return '';
|
||||||
if (level == undefined || level == 'any') level = item.level;
|
if (level == undefined || level == 'any') level = item.level;
|
||||||
if (item.isCantrip == true) return;
|
if (item.isCantrip == true) return;
|
||||||
const spellbook = token.actor.items.filter(i => i.data.type === 'spellcastingEntry')[0];
|
if (item.isFocusSpell == true) return {
|
||||||
|
available: token.actor.data.data.resources.focus.value,
|
||||||
|
maximum: token.actor.data.data.resources.focus.max
|
||||||
|
}
|
||||||
|
const spellbook = this.findSpellcastingEntry(token.actor, item);
|
||||||
if (spellbook == undefined) return;
|
if (spellbook == undefined) return;
|
||||||
|
if (spellbook.data.data.prepared.value == 'innate') {
|
||||||
|
return {
|
||||||
|
available: item.data.data.location.uses.value,
|
||||||
|
maximum: item.data.data.location.uses.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spellbook.data.data.prepared.value == 'prepared') {
|
||||||
|
if (!spellbook.data.data.prepared?.flexible == true) {
|
||||||
|
let slotsExpended = 0;
|
||||||
|
let slotsPrepared = 0;
|
||||||
|
for (let slot = 0; slot < spellbook.data.data.slots?.[`slot${level}`].max; slot++) {
|
||||||
|
let slotEntry = spellbook.data.data.slots?.[`slot${level}`].prepared?.[slot];
|
||||||
|
if (slotEntry.id == item.id) {
|
||||||
|
slotsPrepared++;
|
||||||
|
if (slotEntry?.expended == true) {
|
||||||
|
slotsExpended++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
available: slotsPrepared - slotsExpended,
|
||||||
|
maximum: slotsPrepared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
available: spellbook.data.data.slots?.[`slot${level}`].value,
|
available: spellbook.data.data.slots?.[`slot${level}`].value,
|
||||||
maximum: spellbook.data.data.slots?.[`slot${level}`].max
|
maximum: spellbook.data.data.slots?.[`slot${level}`].max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rollItem(item) {
|
findSpellcastingEntry(actor, spell) {
|
||||||
|
let spellcastingEntries = actor.spellcasting;
|
||||||
|
let result;
|
||||||
|
spellcastingEntries.forEach(spellCastingEntry => {
|
||||||
|
if (spellCastingEntry.spells.get(spell.id) != undefined) {
|
||||||
|
result = spellCastingEntry;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
rollItem(item, settings) {
|
||||||
let variant = 0;
|
let variant = 0;
|
||||||
if (otherControls.rollOption == 'map1') variant = 1;
|
if (otherControls.rollOption == 'map1') variant = 1;
|
||||||
if (otherControls.rollOption == 'map2') variant = 2;
|
if (otherControls.rollOption == 'map2') variant = 2;
|
||||||
|
if (item?.parent?.type == 'hazard' && item.type==='melee') return item.rollNPCAttack({}, variant+1);
|
||||||
if (item.type==='strike') return item.variants[variant].roll({event});
|
if (item.type==='strike') return item.variants[variant].roll({event});
|
||||||
if (item.type==='weapon' || item.type==='melee') return item.parent.data.data.actions.find(a=>a.name===item.name).variants[variant].roll({event});
|
if (item?.parent?.type !== 'hazard' && (item.type==='weapon' || item.type==='melee')) return item.parent.actions.find(a=>a.name===item.name).variants[variant].roll({event});
|
||||||
|
if (item.type === 'spell') {
|
||||||
|
const spellbook = this.findSpellcastingEntry(item.parent, item);
|
||||||
|
if (spellbook != undefined) {
|
||||||
|
let spellLvl = item.level;
|
||||||
|
if (settings.spellType == 'f' || settings.spellType == '0') {
|
||||||
|
const actorLevel = item.parent.data.data.details.level.value;
|
||||||
|
spellLvl = Math.ceil(actorLevel/2);
|
||||||
|
} else if (settings.spellType != 'any') {
|
||||||
|
spellLvl = settings.spellType;
|
||||||
|
}
|
||||||
|
if (spellbook.data.data.prepared.value == 'prepared' && !spellbook.data.data.prepared?.flexible == true) {
|
||||||
|
for (let slotId = 0; slotId < spellbook.data.data.slots?.[`slot${spellLvl}`].max; slotId++) {
|
||||||
|
let slotEntry = spellbook.data.data.slots?.[`slot${spellLvl}`].prepared?.[slotId];
|
||||||
|
if (slotEntry.id == item.id) {
|
||||||
|
if (!slotEntry?.expended == true) {
|
||||||
|
return spellbook.cast(item, {slot: slotId, level: spellLvl});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return spellbook.cast(item, { level: spellLvl});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return game.pf2e.rollItemMacro(item.id);
|
return game.pf2e.rollItemMacro(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLimitedSheet(actor) {
|
||||||
|
return limitedSheets.includes(actor.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
return this.getRingColor(this.findSkill(token, skill));
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
save = this.fixSave(save);
|
||||||
|
return this.getRingColor(this.findSave(token, save));
|
||||||
|
}
|
||||||
|
|
||||||
|
getRingColor(stat) {
|
||||||
|
if (stat == undefined) return;
|
||||||
|
let statModifiers = stat?.modifiers || stat?._modifiers;
|
||||||
|
const profLevel = statModifiers?.find(m => m.type == 'proficiency')?.slug;
|
||||||
|
if (profLevel != undefined) {
|
||||||
|
return proficiencyColors?.[profLevel];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
242
src/systems/starfinder.js
Normal file
242
src/systems/starfinder.js
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import {compatibleCore} from "../misc.js";
|
||||||
|
|
||||||
|
const proficiencyColors = {
|
||||||
|
0: "#000000",
|
||||||
|
0.5: "#804A00",
|
||||||
|
1: "#C0C0C0",
|
||||||
|
2: "#FFD700"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class starfinder{
|
||||||
|
constructor(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getHP(token) {
|
||||||
|
const hp = token.actor.data.data.attributes.hp;
|
||||||
|
return {
|
||||||
|
value: hp.value,
|
||||||
|
max: hp.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTempHP(token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStamina(token) {
|
||||||
|
const stamina = token.actor.data.data.attributes.sp;
|
||||||
|
return {
|
||||||
|
value: stamina.value,
|
||||||
|
max: stamina.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAC(token) {
|
||||||
|
return token.actor.data.data.attributes.eac.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKinAC(token) {
|
||||||
|
return token.actor.data.data.attributes.kac.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShieldHP(token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpeed(token) {
|
||||||
|
const movement = token.actor.data.data.attributes.speed;
|
||||||
|
let speed = "";
|
||||||
|
if (movement.burrowing.value > 0) speed += `Burrow: ${movement.burrowing.value}Ft`;
|
||||||
|
if (movement.climbing.value > 0) {
|
||||||
|
if (speed.length > 0) speed += '\n';
|
||||||
|
speed += `Climb: ${movement.climbing.value}Ft`;
|
||||||
|
}
|
||||||
|
if (movement.flying.value > 0) {
|
||||||
|
if (speed.length > 0) speed += '\n';
|
||||||
|
speed += `Fly: ${movement.flying.value}Ft`;
|
||||||
|
}
|
||||||
|
if (movement.land.value > 0) {
|
||||||
|
if (speed.length > 0) speed += '\n';
|
||||||
|
speed += `Land: ${movement.land.value}Ft`;
|
||||||
|
}
|
||||||
|
if (movement.swimming.value > 0) {
|
||||||
|
if (speed.length > 0) speed += '\n';
|
||||||
|
speed += `Swim: ${movement.swimming.value}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';
|
||||||
|
else if (ability == 'ref') ability = 'reflex';
|
||||||
|
let val = token.actor.data.data.attributes?.[ability].bonus;
|
||||||
|
return (val >= 0) ? `+${val}` : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSkill(token, skill) {
|
||||||
|
if (skill == undefined) skill = 'acr';
|
||||||
|
const val = token.actor.data.data.skills?.[skill].mod;
|
||||||
|
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) {
|
||||||
|
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 == 'shield' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'goods' || i.type == 'container' || i.type == 'technological' || i.type == 'fusion' || i.type == 'upgrade' || i.type == 'weaponAccessory' || i.type == 'augmentation');
|
||||||
|
else if (itemType == 'enhancers') return allItems.filter(i => i.type == 'fusion' || i.type == 'upgrade' || i.type == 'weaponAccessory');
|
||||||
|
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 == 'race' || i.type == 'theme'|| i.type == 'asi' || i.type == 'archetype' || i.type == 'feat' || i.type == 'actorResource')
|
||||||
|
else if (featureType == 'activeFeat' || featureType == 'passiveFeat') {
|
||||||
|
const features = allItems.filter(i => i.type == 'feat');
|
||||||
|
if (featureType == 'activeFeat') return features.filter(i => i.labels.featType == 'Action');
|
||||||
|
else return features.filter(i => i.labels.featType == 'Passive');
|
||||||
|
}
|
||||||
|
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 if (level == 'innate') return allItems.filter(i => i.type == 'spell' && i.data.data.preparation.mode == 'innate');
|
||||||
|
else return allItems.filter(i => i.type == 'spell' && i.data.data.preparation.mode == '' && i.data.data.level == level)
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpellUses(token,level,item) {
|
||||||
|
if (level == undefined) level = 'any';
|
||||||
|
if (item.data.data.level == 0) return;
|
||||||
|
|
||||||
|
const spellSlots = token.actor.data.data.spells;
|
||||||
|
const allowedClasses = item.data.data.allowedClasses;
|
||||||
|
|
||||||
|
let uses = {available: 0, maximum: 0};
|
||||||
|
if (allowedClasses.myst && spellSlots?.[`spell${level}`].perClass?.mystic?.max > uses.maximum)
|
||||||
|
uses = {available: spellSlots?.[`spell${level}`].perClass?.mystic.value, maximum: spellSlots?.[`spell${level}`].perClass?.mystic.max}
|
||||||
|
if (allowedClasses.precog && spellSlots?.[`spell${level}`].perClass?.precog?.max > uses.maximum)
|
||||||
|
uses = {available: spellSlots?.[`spell${level}`].perClass?.precog.value, maximum: spellSlots?.[`spell${level}`].perClass?.precog.max}
|
||||||
|
if (allowedClasses.tech && spellSlots?.[`spell${level}`].perClass?.technomancer?.max > uses.maximum)
|
||||||
|
uses = {available: spellSlots?.[`spell${level}`].perClass?.technomancer.value, maximum: spellSlots?.[`spell${level}`].perClass?.technomancer.max}
|
||||||
|
if (allowedClasses.wysh && spellSlots?.[`spell${level}`].perClass?.witchwarper?.max > uses.maximum)
|
||||||
|
uses = {available: spellSlots?.[`spell${level}`].perClass?.witchwarper.value, maximum: spellSlots?.[`spell${level}`].perClass?.witchwarper.max}
|
||||||
|
|
||||||
|
return uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
rollItem(item) {
|
||||||
|
return item.roll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
const profLevel = token.actor.data.data?.skills[skill]?.proficient;
|
||||||
|
if (profLevel == undefined) return;
|
||||||
|
return proficiencyColors?.[profLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
const profLevel = token.actor.data.data?.abilities[save]?.proficient;
|
||||||
|
if (profLevel == undefined) return;
|
||||||
|
return proficiencyColors?.[profLevel];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import {dnd5e} from "./dnd5e.js"
|
import {dnd5e} from "./dnd5e.js";
|
||||||
import {dnd35e} from "./dnd35e.js"
|
import {dnd35e} from "./dnd35e.js";
|
||||||
import {pf2e} from "./pf2e.js"
|
import {pf2e} from "./pf2e.js";
|
||||||
import {demonlord} from "./demonlord.js";
|
import {demonlord} from "./demonlord.js";
|
||||||
import {wfrp4e} from "./wfrp4e.js"
|
import {wfrp4e} from "./wfrp4e.js";
|
||||||
import {forbiddenlands} from "./forbidden-lands.js"
|
import {forbiddenlands} from "./forbidden-lands.js";
|
||||||
|
import {starfinder} from "./starfinder.js";
|
||||||
import {compatibleCore} from "../misc.js";
|
import {compatibleCore} from "../misc.js";
|
||||||
|
import {gamingSystem} from "../../MaterialDeck.js";
|
||||||
|
|
||||||
|
|
||||||
export class TokenHelper{
|
export class TokenHelper{
|
||||||
@@ -14,11 +16,12 @@ export class TokenHelper{
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSystem() {
|
setSystem() {
|
||||||
if (game.system.id == 'D35E' || game.system.id == 'pf1') this.system = new dnd35e();
|
if (gamingSystem == 'D35E' || gamingSystem == 'pf1') this.system = new dnd35e();
|
||||||
else if (game.system.id == 'pf2e') this.system = new pf2e();
|
else if (gamingSystem == 'pf2e') this.system = new pf2e();
|
||||||
else if (game.system.id == 'demonlord') this.system = new demonlord();
|
else if (gamingSystem == 'demonlord') this.system = new demonlord();
|
||||||
else if (game.system.id == 'wfrp4e') this.system = new wfrp4e();
|
else if (gamingSystem == 'wfrp4e') this.system = new wfrp4e();
|
||||||
else if (game.system.id == 'forbidden-lands') this.system = new forbiddenlands();
|
else if (gamingSystem == 'forbidden-lands') this.system = new forbiddenlands();
|
||||||
|
else if (gamingSystem == 'sfrpg') this.system = new starfinder();
|
||||||
else this.system = new dnd5e(); //default to dnd5e
|
else this.system = new dnd5e(); //default to dnd5e
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +37,7 @@ export class TokenHelper{
|
|||||||
else if (type == 'actorId') return this.getTokenFromActorId(identifier);
|
else if (type == 'actorId') return this.getTokenFromActorId(identifier);
|
||||||
}
|
}
|
||||||
getTokenFromTokenId(id) {
|
getTokenFromTokenId(id) {
|
||||||
return canvas.tokens.children[0].children.find(p => p.id == id);
|
return canvas.tokens.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTokenFromTokenName(name) {
|
getTokenFromTokenName(name) {
|
||||||
@@ -132,7 +135,7 @@ export class TokenHelper{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* System agnostic functions
|
* System specific functions
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
getHP(token) {
|
getHP(token) {
|
||||||
return this.system.getHP(token);
|
return this.system.getHP(token);
|
||||||
@@ -245,9 +248,19 @@ export class TokenHelper{
|
|||||||
return this.system.getEmpathy(token)
|
return this.system.getEmpathy(token)
|
||||||
}
|
}
|
||||||
/* forbidden-lands */
|
/* forbidden-lands */
|
||||||
getWillPower(token) {
|
getWillPower(token) {
|
||||||
return this.system.getWillPower(token)
|
return this.system.getWillPower(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* starfinder */
|
||||||
|
getStamina(token) {
|
||||||
|
return this.system.getStamina(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* starfinder */
|
||||||
|
getKinAC(token) {
|
||||||
|
return this.system.getKinAC(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -315,7 +328,17 @@ export class TokenHelper{
|
|||||||
return this.system.getSpellUses(token,level,item);
|
return this.system.getSpellUses(token,level,item);
|
||||||
}
|
}
|
||||||
|
|
||||||
rollItem(item) {
|
rollItem(item, settings) {
|
||||||
return this.system.rollItem(item);
|
return this.system.rollItem(item, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token,skill) {
|
||||||
|
return this.system.getSkillRingColor(token,skill);
|
||||||
|
}
|
||||||
|
getSaveRingColor(token,save) {
|
||||||
|
return this.system.getSaveRingColor(token,save);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,6 @@ export class wfrp4e {
|
|||||||
}
|
}
|
||||||
|
|
||||||
roll(token,roll,options,ability,skill,save) {
|
roll(token,roll,options,ability,skill,save) {
|
||||||
//console.log("roll(", token, roll, options, ability, skill, save, ")");
|
|
||||||
if (ability == undefined) ability = 'ag';
|
if (ability == undefined) ability = 'ag';
|
||||||
return game.wfrp4e.utility.rollItemMacro(ability, "characteristic", false);
|
return game.wfrp4e.utility.rollItemMacro(ability, "characteristic", false);
|
||||||
}
|
}
|
||||||
@@ -125,7 +124,6 @@ export class wfrp4e {
|
|||||||
|
|
||||||
|
|
||||||
getItemUses(item) {
|
getItemUses(item) {
|
||||||
//console.log("getItemUses(" , item , ")")
|
|
||||||
if ( item.type == 'ammunition') {
|
if ( item.type == 'ammunition') {
|
||||||
return {available: item.data.data.quantity.value};
|
return {available: item.data.data.quantity.value};
|
||||||
}
|
}
|
||||||
@@ -168,5 +166,14 @@ export class wfrp4e {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring Colors
|
||||||
|
*/
|
||||||
|
getSkillRingColor(token, skill) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveRingColor(token, save) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
49
src/token.js
49
src/token.js
@@ -190,6 +190,13 @@ export class TokenControl{
|
|||||||
heart: "#00FF00"
|
heart: "#00FF00"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (stats == 'Stamina') { //starfinder
|
||||||
|
const stamina = tokenHelper.getStamina(token);
|
||||||
|
txt += `${stamina.value}/${stamina.max}`;
|
||||||
|
}
|
||||||
|
else if (stats == 'KinAC') { //starfinder
|
||||||
|
txt += tokenHelper.getKinAC(token);
|
||||||
|
}
|
||||||
else if (stats == 'AC') txt += tokenHelper.getAC(token);
|
else if (stats == 'AC') txt += tokenHelper.getAC(token);
|
||||||
else if (stats == 'ShieldHP') txt += tokenHelper.getShieldHP(token);
|
else if (stats == 'ShieldHP') txt += tokenHelper.getShieldHP(token);
|
||||||
else if (stats == 'Speed') txt += tokenHelper.getSpeed(token);
|
else if (stats == 'Speed') txt += tokenHelper.getSpeed(token);
|
||||||
@@ -198,8 +205,16 @@ export class TokenControl{
|
|||||||
else if (stats == 'PassiveInvestigation') txt += tokenHelper.getPassiveInvestigation(token);
|
else if (stats == 'PassiveInvestigation') txt += tokenHelper.getPassiveInvestigation(token);
|
||||||
else if (stats == 'Ability') txt += tokenHelper.getAbility(token, settings.ability);
|
else if (stats == 'Ability') txt += tokenHelper.getAbility(token, settings.ability);
|
||||||
else if (stats == 'AbilityMod') txt += tokenHelper.getAbilityModifier(token, settings.ability);
|
else if (stats == 'AbilityMod') txt += tokenHelper.getAbilityModifier(token, settings.ability);
|
||||||
else if (stats == 'Save') txt += tokenHelper.getAbilitySave(token, settings.save);
|
else if (stats == 'Save') {
|
||||||
else if (stats == 'Skill') txt += tokenHelper.getSkill(token, settings.skill);
|
txt += tokenHelper.getAbilitySave(token, settings.save);
|
||||||
|
ringColor = tokenHelper.getSaveRingColor(token, settings.save);
|
||||||
|
if (ringColor != undefined) ring = 2;
|
||||||
|
}
|
||||||
|
else if (stats == 'Skill') {
|
||||||
|
txt += tokenHelper.getSkill(token, settings.skill);
|
||||||
|
ringColor = tokenHelper.getSkillRingColor(token, settings.skill);
|
||||||
|
if (ringColor != undefined) ring = 2;
|
||||||
|
}
|
||||||
else if (stats == 'Prof') txt += tokenHelper.getProficiency(token);
|
else if (stats == 'Prof') txt += tokenHelper.getProficiency(token);
|
||||||
else if (stats == 'Fate') txt += tokenHelper.getFate(token) /* WFRP4e */
|
else if (stats == 'Fate') txt += tokenHelper.getFate(token) /* WFRP4e */
|
||||||
else if (stats == 'Fortune') txt += tokenHelper.getFortune(token) /* WFRP4e */
|
else if (stats == 'Fortune') txt += tokenHelper.getFortune(token) /* WFRP4e */
|
||||||
@@ -426,7 +441,7 @@ export class TokenControl{
|
|||||||
iconSrc = "modules/MaterialDeck/img/token/hp_empty.png";
|
iconSrc = "modules/MaterialDeck/img/token/hp_empty.png";
|
||||||
if (stats == 'TempHP') //Temp HP
|
if (stats == 'TempHP') //Temp HP
|
||||||
iconSrc = "modules/MaterialDeck/img/token/temp_hp_empty.png";
|
iconSrc = "modules/MaterialDeck/img/token/temp_hp_empty.png";
|
||||||
else if (stats == 'AC' || stats == 'ShieldHP') //AC
|
else if (stats == 'AC' || stats == 'ShieldHP' || stats == 'KinAC') //AC
|
||||||
iconSrc = "modules/MaterialDeck/img/token/ac.webp";
|
iconSrc = "modules/MaterialDeck/img/token/ac.webp";
|
||||||
else if (stats == 'Speed') //Speed
|
else if (stats == 'Speed') //Speed
|
||||||
iconSrc = "modules/MaterialDeck/img/token/speed.webp";
|
iconSrc = "modules/MaterialDeck/img/token/speed.webp";
|
||||||
@@ -444,7 +459,6 @@ export class TokenControl{
|
|||||||
}
|
}
|
||||||
else if (stats == 'Ability' || stats == 'AbilityMod' || stats == 'Save') {
|
else if (stats == 'Ability' || stats == 'AbilityMod' || stats == 'Save') {
|
||||||
overlay = true;
|
overlay = true;
|
||||||
ring = 1;
|
|
||||||
let ability = (stats == 'Save') ? settings.save : settings.ability;
|
let ability = (stats == 'Save') ? settings.save : settings.ability;
|
||||||
if (ability == undefined) ability = 'str';
|
if (ability == undefined) ability = 'str';
|
||||||
if (ability == 'con') iconSrc = "modules/MaterialDeck/img/token/abilities/cons.png";
|
if (ability == 'con') iconSrc = "modules/MaterialDeck/img/token/abilities/cons.png";
|
||||||
@@ -452,10 +466,9 @@ export class TokenControl{
|
|||||||
}
|
}
|
||||||
else if (stats == 'Skill') {
|
else if (stats == 'Skill') {
|
||||||
overlay = true;
|
overlay = true;
|
||||||
ring = 1;
|
|
||||||
let skill = settings.skill;
|
let skill = settings.skill;
|
||||||
if (skill == undefined) skill = 'acr';
|
if (skill == undefined) skill = 'acr';
|
||||||
else iconSrc = "modules/MaterialDeck/img/token/skills/" + skill + ".png";
|
else iconSrc = "modules/MaterialDeck/img/token/skills/" + (skill.startsWith('lor')? 'lor' : skill) + ".png";
|
||||||
}
|
}
|
||||||
else if (settings.onClick == 'center' || settings.onClick == 'centerSelect') {
|
else if (settings.onClick == 'center' || settings.onClick == 'centerSelect') {
|
||||||
overlay = true;
|
overlay = true;
|
||||||
@@ -610,22 +623,28 @@ export class TokenControl{
|
|||||||
if (isNaN(dimVision)==false) data.dimSight = dimVision;
|
if (isNaN(dimVision)==false) data.dimSight = dimVision;
|
||||||
if (isNaN(brightVision)==false) data.brightSight = brightVision;
|
if (isNaN(brightVision)==false) data.brightSight = brightVision;
|
||||||
if (isNaN(sightAngle)==false) data.sightAngle = sightAngle;
|
if (isNaN(sightAngle)==false) data.sightAngle = sightAngle;
|
||||||
if (isNaN(dimRadius)==false) data.dimLight = dimRadius;
|
|
||||||
if (isNaN(brightRadius)==false) data.brightLight = brightRadius;
|
let light = {};
|
||||||
if (isNaN(emissionAngle)==false) data.lightAngle = emissionAngle;
|
|
||||||
data.lightColor = lightColor;
|
|
||||||
data.lightAlpha = Math.sqrt(colorIntensity).toNearest(0.05)
|
if (isNaN(dimRadius)==false) light.dim = dimRadius;
|
||||||
|
if (isNaN(brightRadius)==false) light.bright = brightRadius;
|
||||||
|
if (isNaN(emissionAngle)==false) light.angle = emissionAngle;
|
||||||
|
light.color = lightColor;
|
||||||
|
light.alpha = Math.sqrt(colorIntensity).toNearest(0.05)
|
||||||
|
|
||||||
let animation = {
|
let animation = {
|
||||||
type: '',
|
type: '',
|
||||||
speed: tokenData.lightAnimation.speed,
|
speed: tokenData.light.animation.speed,
|
||||||
intensity: tokenData.lightAnimation.intensity
|
intensity: tokenData.light.animation.intensity
|
||||||
};
|
};
|
||||||
if (animationType != 'none'){
|
if (animationType != 'none'){
|
||||||
animation.type = animationType;
|
animation.type = animationType;
|
||||||
animation.intensity = animationIntensity;
|
animation.intensity = animationIntensity;
|
||||||
animation.speed = animationSpeed;
|
animation.speed = animationSpeed;
|
||||||
}
|
}
|
||||||
data.lightAnimation = animation;
|
light.animation = animation;
|
||||||
|
data.light = light;
|
||||||
if (compatibleCore('0.8.1')) token.document.update(data);
|
if (compatibleCore('0.8.1')) token.document.update(data);
|
||||||
else token.update(data);
|
else token.update(data);
|
||||||
}
|
}
|
||||||
@@ -835,7 +854,7 @@ export class TokenControl{
|
|||||||
|
|
||||||
const item = items[itemNr];
|
const item = items[itemNr];
|
||||||
if (item != undefined) {
|
if (item != undefined) {
|
||||||
tokenHelper.rollItem(item);
|
tokenHelper.rollItem(item, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user