forked from GitHub-Mirrors/foundry-sw5e
commit
2212e3c7d8
43 changed files with 13465 additions and 12707 deletions
14
.prettierrc
Normal file
14
.prettierrc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"quoteProps": "consistent",
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -1,2 +1,3 @@
|
||||||
{
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ import { d20Roll, damageRoll } from "../dice.js";
|
||||||
import SelectItemsPrompt from "../apps/select-items-prompt.js";
|
import SelectItemsPrompt from "../apps/select-items-prompt.js";
|
||||||
import ShortRestDialog from "../apps/short-rest.js";
|
import ShortRestDialog from "../apps/short-rest.js";
|
||||||
import LongRestDialog from "../apps/long-rest.js";
|
import LongRestDialog from "../apps/long-rest.js";
|
||||||
import {SW5E} from '../config.js';
|
import {SW5E} from "../config.js";
|
||||||
import Item5e from "../item/entity.js";
|
import Item5e from "../item/entity.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,6 @@ import Item5e from "../item/entity.js";
|
||||||
* @extends {Actor}
|
* @extends {Actor}
|
||||||
*/
|
*/
|
||||||
export default class Actor5e extends Actor {
|
export default class Actor5e extends Actor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data source for Actor5e.classes allowing it to be lazily computed.
|
* The data source for Actor5e.classes allowing it to be lazily computed.
|
||||||
* @type {Object<string, Item5e>}
|
* @type {Object<string, Item5e>}
|
||||||
|
@ -28,11 +27,13 @@ export default class Actor5e extends Actor {
|
||||||
*/
|
*/
|
||||||
get classes() {
|
get classes() {
|
||||||
if (this._classes !== undefined) return this._classes;
|
if (this._classes !== undefined) return this._classes;
|
||||||
if ( this.data.type !== "character" ) return this._classes = {};
|
if (this.data.type !== "character") return (this._classes = {});
|
||||||
return this._classes = this.items.filter((item) => item.type === "class").reduce((obj, cls) => {
|
return (this._classes = this.items
|
||||||
|
.filter((item) => item.type === "class")
|
||||||
|
.reduce((obj, cls) => {
|
||||||
obj[cls.name.slugify({strict: true})] = cls;
|
obj[cls.name.slugify({strict: true})] = cls;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -54,7 +55,7 @@ export default class Actor5e extends Actor {
|
||||||
super.prepareData();
|
super.prepareData();
|
||||||
|
|
||||||
// iterate over owned items and recompute attributes that depend on prepared actor data
|
// iterate over owned items and recompute attributes that depend on prepared actor data
|
||||||
this.items.forEach(item => item.prepareFinalAttributes());
|
this.items.forEach((item) => item.prepareFinalAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -86,8 +87,8 @@ export default class Actor5e extends Actor {
|
||||||
let originalSaves = null;
|
let originalSaves = null;
|
||||||
let originalSkills = null;
|
let originalSkills = null;
|
||||||
if (this.isPolymorphed) {
|
if (this.isPolymorphed) {
|
||||||
const transformOptions = this.getFlag('sw5e', 'transformOptions');
|
const transformOptions = this.getFlag("sw5e", "transformOptions");
|
||||||
const original = game.actors?.get(this.getFlag('sw5e', 'originalActor'));
|
const original = game.actors?.get(this.getFlag("sw5e", "originalActor"));
|
||||||
if (original) {
|
if (original) {
|
||||||
if (transformOptions.mergeSaves) {
|
if (transformOptions.mergeSaves) {
|
||||||
originalSaves = original.data.data.abilities;
|
originalSaves = original.data.data.abilities;
|
||||||
|
@ -202,13 +203,13 @@ export default class Actor5e extends Actor {
|
||||||
let toCreate = [];
|
let toCreate = [];
|
||||||
if (prompt) {
|
if (prompt) {
|
||||||
const itemIdsToAdd = await SelectItemsPrompt.create(items, {
|
const itemIdsToAdd = await SelectItemsPrompt.create(items, {
|
||||||
hint: game.i18n.localize('SW5E.AddEmbeddedItemPromptHint')
|
hint: game.i18n.localize("SW5E.AddEmbeddedItemPromptHint")
|
||||||
});
|
});
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (itemIdsToAdd.includes(item.id)) toCreate.push(item.toObject());
|
if (itemIdsToAdd.includes(item.id)) toCreate.push(item.toObject());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toCreate = items.map(item => item.toObject());
|
toCreate = items.map((item) => item.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the requested items
|
// Create the requested items
|
||||||
|
@ -223,9 +224,9 @@ export default class Actor5e extends Actor {
|
||||||
* Optionally prompt the user for which they would like to add.
|
* Optionally prompt the user for which they would like to add.
|
||||||
*/
|
*/
|
||||||
async getClassFeatures({className, archetypeName, level} = {}) {
|
async getClassFeatures({className, archetypeName, level} = {}) {
|
||||||
const existing = new Set(this.items.map(i => i.name));
|
const existing = new Set(this.items.map((i) => i.name));
|
||||||
const features = await Actor5e.loadClassFeatures({className, archetypeName, level});
|
const features = await Actor5e.loadClassFeatures({className, archetypeName, level});
|
||||||
return features.filter(f => !existing.has(f.name)) || [];
|
return features.filter((f) => !existing.has(f.name)) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -250,14 +251,14 @@ export default class Actor5e extends Actor {
|
||||||
let ids = [];
|
let ids = [];
|
||||||
for (let [l, f] of Object.entries(clsConfig.features || {})) {
|
for (let [l, f] of Object.entries(clsConfig.features || {})) {
|
||||||
l = parseInt(l);
|
l = parseInt(l);
|
||||||
if ( (l <= level) && (l > priorLevel) ) ids = ids.concat(f);
|
if (l <= level && l > priorLevel) ids = ids.concat(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acquire archetype features
|
// Acquire archetype features
|
||||||
const archConfig = clsConfig.archetypes[archetypeName] || {};
|
const archConfig = clsConfig.archetypes[archetypeName] || {};
|
||||||
for (let [l, f] of Object.entries(archConfig.features || {})) {
|
for (let [l, f] of Object.entries(archConfig.features || {})) {
|
||||||
l = parseInt(l);
|
l = parseInt(l);
|
||||||
if ( (l <= level) && (l > priorLevel) ) ids = ids.concat(f);
|
if (l <= level && l > priorLevel) ids = ids.concat(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load item data for all identified features
|
// Load item data for all identified features
|
||||||
|
@ -288,14 +289,17 @@ export default class Actor5e extends Actor {
|
||||||
const data = actorData.data;
|
const data = actorData.data;
|
||||||
|
|
||||||
// Determine character level and available hit dice based on owned Class items
|
// Determine character level and available hit dice based on owned Class items
|
||||||
const [level, hd] = this.items.reduce((arr, item) => {
|
const [level, hd] = this.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
if (item.type === "class") {
|
if (item.type === "class") {
|
||||||
const classLevels = parseInt(item.data.data.levels) || 1;
|
const classLevels = parseInt(item.data.data.levels) || 1;
|
||||||
arr[0] += classLevels;
|
arr[0] += classLevels;
|
||||||
arr[1] += classLevels - (parseInt(item.data.data.hitDiceUsed) || 0);
|
arr[1] += classLevels - (parseInt(item.data.data.hitDiceUsed) || 0);
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}, [0, 0]);
|
},
|
||||||
|
[0, 0]
|
||||||
|
);
|
||||||
data.details.level = level;
|
data.details.level = level;
|
||||||
data.attributes.hd = hd;
|
data.attributes.hd = hd;
|
||||||
|
|
||||||
|
@ -307,7 +311,7 @@ export default class Actor5e extends Actor {
|
||||||
xp.max = this.getLevelExp(level || 1);
|
xp.max = this.getLevelExp(level || 1);
|
||||||
const prior = this.getLevelExp(level - 1 || 0);
|
const prior = this.getLevelExp(level - 1 || 0);
|
||||||
const required = xp.max - prior;
|
const required = xp.max - prior;
|
||||||
const pct = Math.round((xp.value - prior) * 100 / required);
|
const pct = Math.round(((xp.value - prior) * 100) / required);
|
||||||
xp.pct = Math.clamped(pct, 0, 100);
|
xp.pct = Math.clamped(pct, 0, 100);
|
||||||
|
|
||||||
// Add base Powercasting attributes
|
// Add base Powercasting attributes
|
||||||
|
@ -376,7 +380,7 @@ export default class Actor5e extends Actor {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareSkills(actorData, bonuses, checkBonus, originalSkills) {
|
_prepareSkills(actorData, bonuses, checkBonus, originalSkills) {
|
||||||
if (actorData.type === 'vehicle') return;
|
if (actorData.type === "vehicle") return;
|
||||||
|
|
||||||
const data = actorData.data;
|
const data = actorData.data;
|
||||||
const flags = actorData.flags.sw5e || {};
|
const flags = actorData.flags.sw5e || {};
|
||||||
|
@ -392,13 +396,13 @@ export default class Actor5e extends Actor {
|
||||||
let round = Math.floor;
|
let round = Math.floor;
|
||||||
|
|
||||||
// Remarkable
|
// Remarkable
|
||||||
if ( athlete && (skl.value < 0.5) && feats.remarkableAthlete.abilities.includes(skl.ability) ) {
|
if (athlete && skl.value < 0.5 && feats.remarkableAthlete.abilities.includes(skl.ability)) {
|
||||||
skl.value = 0.5;
|
skl.value = 0.5;
|
||||||
round = Math.ceil;
|
round = Math.ceil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jack of All Trades
|
// Jack of All Trades
|
||||||
if ( joat && (skl.value < 0.5) ) {
|
if (joat && skl.value < 0.5) {
|
||||||
skl.value = 0.5;
|
skl.value = 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +418,7 @@ export default class Actor5e extends Actor {
|
||||||
skl.total = skl.mod + skl.prof + skl.bonus;
|
skl.total = skl.mod + skl.prof + skl.bonus;
|
||||||
|
|
||||||
// Compute passive bonus
|
// Compute passive bonus
|
||||||
const passive = observant && (feats.observantFeat.skills.includes(id)) ? 5 : 0;
|
const passive = observant && feats.observantFeat.skills.includes(id) ? 5 : 0;
|
||||||
skl.passive = 10 + skl.total + passive;
|
skl.passive = 10 + skl.total + passive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,10 +430,10 @@ export default class Actor5e extends Actor {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeBasePowercasting(actorData) {
|
_computeBasePowercasting(actorData) {
|
||||||
if (actorData.type === 'vehicle' || actorData.type === 'starship') return;
|
if (actorData.type === "vehicle" || actorData.type === "starship") return;
|
||||||
const ad = actorData.data;
|
const ad = actorData.data;
|
||||||
const powers = ad.powers;
|
const powers = ad.powers;
|
||||||
const isNPC = actorData.type === 'npc';
|
const isNPC = actorData.type === "npc";
|
||||||
|
|
||||||
// Translate the list of classes into force and tech power-casting progression
|
// Translate the list of classes into force and tech power-casting progression
|
||||||
const forceProgression = {
|
const forceProgression = {
|
||||||
|
@ -456,7 +460,7 @@ export default class Actor5e extends Actor {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tabulate the total power-casting progression
|
// Tabulate the total power-casting progression
|
||||||
const classes = this.data.items.filter(i => i.type === "class");
|
const classes = this.data.items.filter((i) => i.type === "class");
|
||||||
let priority = 0;
|
let priority = 0;
|
||||||
for (let cls of classes) {
|
for (let cls of classes) {
|
||||||
const d = cls.data.data;
|
const d = cls.data.data;
|
||||||
|
@ -465,82 +469,88 @@ export default class Actor5e extends Actor {
|
||||||
const prog = d.powercasting.progression;
|
const prog = d.powercasting.progression;
|
||||||
// TODO: Consider a more dynamic system
|
// TODO: Consider a more dynamic system
|
||||||
switch (prog) {
|
switch (prog) {
|
||||||
case 'consular':
|
case "consular":
|
||||||
priority = 3;
|
priority = 3;
|
||||||
forceProgression.levels += levels;
|
forceProgression.levels += levels;
|
||||||
forceProgression.multi += (SW5E.powerMaxLevel['consular'][19]/9)*levels;
|
forceProgression.multi += (SW5E.powerMaxLevel["consular"][19] / 9) * levels;
|
||||||
forceProgression.classes++;
|
forceProgression.classes++;
|
||||||
// see if class controls high level forcecasting
|
// see if class controls high level forcecasting
|
||||||
if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){
|
if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) {
|
||||||
forceProgression.maxClass = 'consular';
|
forceProgression.maxClass = "consular";
|
||||||
forceProgression.maxClassLevels = levels;
|
forceProgression.maxClassLevels = levels;
|
||||||
forceProgression.maxClassPriority = priority;
|
forceProgression.maxClassPriority = priority;
|
||||||
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel['consular'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel["consular"][Math.clamped(levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
// calculate points and powers known
|
// calculate points and powers known
|
||||||
forceProgression.powersKnown += SW5E.powersKnown['consular'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.powersKnown += SW5E.powersKnown["consular"][Math.clamped(levels - 1, 0, 20)];
|
||||||
forceProgression.points += SW5E.powerPoints['consular'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.points += SW5E.powerPoints["consular"][Math.clamped(levels - 1, 0, 20)];
|
||||||
break;
|
break;
|
||||||
case 'engineer':
|
case "engineer":
|
||||||
priority = 2
|
priority = 2;
|
||||||
techProgression.levels += levels;
|
techProgression.levels += levels;
|
||||||
techProgression.multi += (SW5E.powerMaxLevel['engineer'][19]/9)*levels;
|
techProgression.multi += (SW5E.powerMaxLevel["engineer"][19] / 9) * levels;
|
||||||
techProgression.classes++;
|
techProgression.classes++;
|
||||||
// see if class controls high level techcasting
|
// see if class controls high level techcasting
|
||||||
if ((levels >= techProgression.maxClassLevels) && (priority > techProgression.maxClassPriority)){
|
if (levels >= techProgression.maxClassLevels && priority > techProgression.maxClassPriority) {
|
||||||
techProgression.maxClass = 'engineer';
|
techProgression.maxClass = "engineer";
|
||||||
techProgression.maxClassLevels = levels;
|
techProgression.maxClassLevels = levels;
|
||||||
techProgression.maxClassPriority = priority;
|
techProgression.maxClassPriority = priority;
|
||||||
techProgression.maxClassPowerLevel = SW5E.powerMaxLevel['engineer'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel["engineer"][Math.clamped(levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
techProgression.powersKnown += SW5E.powersKnown['engineer'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.powersKnown += SW5E.powersKnown["engineer"][Math.clamped(levels - 1, 0, 20)];
|
||||||
techProgression.points += SW5E.powerPoints['engineer'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.points += SW5E.powerPoints["engineer"][Math.clamped(levels - 1, 0, 20)];
|
||||||
break;
|
break;
|
||||||
case 'guardian':
|
case "guardian":
|
||||||
priority = 1;
|
priority = 1;
|
||||||
forceProgression.levels += levels;
|
forceProgression.levels += levels;
|
||||||
forceProgression.multi += (SW5E.powerMaxLevel['guardian'][19]/9)*levels;
|
forceProgression.multi += (SW5E.powerMaxLevel["guardian"][19] / 9) * levels;
|
||||||
forceProgression.classes++;
|
forceProgression.classes++;
|
||||||
// see if class controls high level forcecasting
|
// see if class controls high level forcecasting
|
||||||
if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){
|
if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) {
|
||||||
forceProgression.maxClass = 'guardian';
|
forceProgression.maxClass = "guardian";
|
||||||
forceProgression.maxClassLevels = levels;
|
forceProgression.maxClassLevels = levels;
|
||||||
forceProgression.maxClassPriority = priority;
|
forceProgression.maxClassPriority = priority;
|
||||||
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel['guardian'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel["guardian"][Math.clamped(levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
forceProgression.powersKnown += SW5E.powersKnown['guardian'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.powersKnown += SW5E.powersKnown["guardian"][Math.clamped(levels - 1, 0, 20)];
|
||||||
forceProgression.points += SW5E.powerPoints['guardian'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.points += SW5E.powerPoints["guardian"][Math.clamped(levels - 1, 0, 20)];
|
||||||
break;
|
break;
|
||||||
case 'scout':
|
case "scout":
|
||||||
priority = 1;
|
priority = 1;
|
||||||
techProgression.levels += levels;
|
techProgression.levels += levels;
|
||||||
techProgression.multi += (SW5E.powerMaxLevel['scout'][19]/9)*levels;
|
techProgression.multi += (SW5E.powerMaxLevel["scout"][19] / 9) * levels;
|
||||||
techProgression.classes++;
|
techProgression.classes++;
|
||||||
// see if class controls high level techcasting
|
// see if class controls high level techcasting
|
||||||
if ((levels >= techProgression.maxClassLevels) && (priority > techProgression.maxClassPriority)){
|
if (levels >= techProgression.maxClassLevels && priority > techProgression.maxClassPriority) {
|
||||||
techProgression.maxClass = 'scout';
|
techProgression.maxClass = "scout";
|
||||||
techProgression.maxClassLevels = levels;
|
techProgression.maxClassLevels = levels;
|
||||||
techProgression.maxClassPriority = priority;
|
techProgression.maxClassPriority = priority;
|
||||||
techProgression.maxClassPowerLevel = SW5E.powerMaxLevel['scout'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel["scout"][Math.clamped(levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
techProgression.powersKnown += SW5E.powersKnown['scout'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.powersKnown += SW5E.powersKnown["scout"][Math.clamped(levels - 1, 0, 20)];
|
||||||
techProgression.points += SW5E.powerPoints['scout'][Math.clamped((levels - 1), 0, 20)];
|
techProgression.points += SW5E.powerPoints["scout"][Math.clamped(levels - 1, 0, 20)];
|
||||||
break;
|
break;
|
||||||
case 'sentinel':
|
case "sentinel":
|
||||||
priority = 2;
|
priority = 2;
|
||||||
forceProgression.levels += levels;
|
forceProgression.levels += levels;
|
||||||
forceProgression.multi += (SW5E.powerMaxLevel['sentinel'][19]/9)*levels;
|
forceProgression.multi += (SW5E.powerMaxLevel["sentinel"][19] / 9) * levels;
|
||||||
forceProgression.classes++;
|
forceProgression.classes++;
|
||||||
// see if class controls high level forcecasting
|
// see if class controls high level forcecasting
|
||||||
if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){
|
if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) {
|
||||||
forceProgression.maxClass = 'sentinel';
|
forceProgression.maxClass = "sentinel";
|
||||||
forceProgression.maxClassLevels = levels;
|
forceProgression.maxClassLevels = levels;
|
||||||
forceProgression.maxClassPriority = priority;
|
forceProgression.maxClassPriority = priority;
|
||||||
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel['sentinel'][Math.clamped((levels - 1), 0, 20)];
|
forceProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel["sentinel"][Math.clamped(levels - 1, 0, 20)];
|
||||||
|
}
|
||||||
|
forceProgression.powersKnown += SW5E.powersKnown["sentinel"][Math.clamped(levels - 1, 0, 20)];
|
||||||
|
forceProgression.points += SW5E.powerPoints["sentinel"][Math.clamped(levels - 1, 0, 20)];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
forceProgression.powersKnown += SW5E.powersKnown['sentinel'][Math.clamped((levels - 1), 0, 20)];
|
|
||||||
forceProgression.points += SW5E.powerPoints['sentinel'][Math.clamped((levels - 1), 0, 20)];
|
|
||||||
break; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNPC) {
|
if (isNPC) {
|
||||||
|
@ -549,30 +559,31 @@ export default class Actor5e extends Actor {
|
||||||
forceProgression.levels = ad.details.powerForceLevel;
|
forceProgression.levels = ad.details.powerForceLevel;
|
||||||
ad.attributes.force.level = forceProgression.levels;
|
ad.attributes.force.level = forceProgression.levels;
|
||||||
forceProgression.maxClass = ad.attributes.powercasting;
|
forceProgression.maxClass = ad.attributes.powercasting;
|
||||||
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel[forceProgression.maxClass][Math.clamped((forceProgression.levels - 1), 0, 20)];
|
forceProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel[forceProgression.maxClass][Math.clamped(forceProgression.levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
if (ad.details.powerTechLevel) {
|
if (ad.details.powerTechLevel) {
|
||||||
techProgression.levels = ad.details.powerTechLevel;
|
techProgression.levels = ad.details.powerTechLevel;
|
||||||
ad.attributes.tech.level = techProgression.levels;
|
ad.attributes.tech.level = techProgression.levels;
|
||||||
techProgression.maxClass = ad.attributes.powercasting;
|
techProgression.maxClass = ad.attributes.powercasting;
|
||||||
techProgression.maxClassPowerLevel = SW5E.powerMaxLevel[techProgression.maxClass][Math.clamped((techProgression.levels - 1), 0, 20)];
|
techProgression.maxClassPowerLevel =
|
||||||
|
SW5E.powerMaxLevel[techProgression.maxClass][Math.clamped(techProgression.levels - 1, 0, 20)];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// EXCEPTION: multi-classed progression uses multi rounded down rather than levels
|
// EXCEPTION: multi-classed progression uses multi rounded down rather than levels
|
||||||
if (forceProgression.classes > 1) {
|
if (forceProgression.classes > 1) {
|
||||||
forceProgression.levels = Math.floor(forceProgression.multi);
|
forceProgression.levels = Math.floor(forceProgression.multi);
|
||||||
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel['multi'][forceProgression.levels - 1];
|
forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel["multi"][forceProgression.levels - 1];
|
||||||
}
|
}
|
||||||
if (techProgression.classes > 1) {
|
if (techProgression.classes > 1) {
|
||||||
techProgression.levels = Math.floor(techProgression.multi);
|
techProgression.levels = Math.floor(techProgression.multi);
|
||||||
techProgression.maxClassPowerLevel = SW5E.powerMaxLevel['multi'][techProgression.levels - 1];
|
techProgression.maxClassPowerLevel = SW5E.powerMaxLevel["multi"][techProgression.levels - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Look up the number of slots per level from the powerLimit table
|
// Look up the number of slots per level from the powerLimit table
|
||||||
let forcePowerLimit = Array.from(SW5E.powerLimit['none']);
|
let forcePowerLimit = Array.from(SW5E.powerLimit["none"]);
|
||||||
for (let i = 0; i < (forceProgression.maxClassPowerLevel); i++) {
|
for (let i = 0; i < forceProgression.maxClassPowerLevel; i++) {
|
||||||
forcePowerLimit[i] = SW5E.powerLimit[forceProgression.maxClass][i];
|
forcePowerLimit[i] = SW5E.powerLimit[forceProgression.maxClass][i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,8 +599,8 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let techPowerLimit = Array.from(SW5E.powerLimit['none']);
|
let techPowerLimit = Array.from(SW5E.powerLimit["none"]);
|
||||||
for (let i = 0; i < (techProgression.maxClassPowerLevel); i++) {
|
for (let i = 0; i < techProgression.maxClassPowerLevel; i++) {
|
||||||
techPowerLimit[i] = SW5E.powerLimit[techProgression.maxClass][i];
|
techPowerLimit[i] = SW5E.powerLimit[techProgression.maxClass][i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,11 +630,10 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tally Powers Known and check for migration first to avoid errors
|
// Tally Powers Known and check for migration first to avoid errors
|
||||||
let hasKnownPowers = actorData?.data?.attributes?.force?.known?.value !== undefined;
|
let hasKnownPowers = actorData?.data?.attributes?.force?.known?.value !== undefined;
|
||||||
if (hasKnownPowers) {
|
if (hasKnownPowers) {
|
||||||
const knownPowers = this.data.items.filter(i => i.type === "power");
|
const knownPowers = this.data.items.filter((i) => i.type === "power");
|
||||||
let knownForcePowers = 0;
|
let knownForcePowers = 0;
|
||||||
let knownTechPowers = 0;
|
let knownTechPowers = 0;
|
||||||
for (let knownPower of knownPowers) {
|
for (let knownPower of knownPowers) {
|
||||||
|
@ -653,8 +663,7 @@ export default class Actor5e extends Actor {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeDerivedPowercasting(actorData) {
|
_computeDerivedPowercasting(actorData) {
|
||||||
|
if (!(actorData.type === "character" || actorData.type === "npc")) return;
|
||||||
if (!(actorData.type === 'character' || actorData.type === 'npc')) return;
|
|
||||||
|
|
||||||
const ad = actorData.data;
|
const ad = actorData.data;
|
||||||
|
|
||||||
|
@ -662,10 +671,11 @@ export default class Actor5e extends Actor {
|
||||||
// TODO: Consider an option for using the variant rule of all powers use the same value
|
// TODO: Consider an option for using the variant rule of all powers use the same value
|
||||||
ad.attributes.powerForceLightDC = 8 + ad.abilities.wis.mod + ad.attributes.prof ?? 10;
|
ad.attributes.powerForceLightDC = 8 + ad.abilities.wis.mod + ad.attributes.prof ?? 10;
|
||||||
ad.attributes.powerForceDarkDC = 8 + ad.abilities.cha.mod + ad.attributes.prof ?? 10;
|
ad.attributes.powerForceDarkDC = 8 + ad.abilities.cha.mod + ad.attributes.prof ?? 10;
|
||||||
ad.attributes.powerForceUnivDC = Math.max(ad.attributes.powerForceLightDC,ad.attributes.powerForceDarkDC) ?? 10;
|
ad.attributes.powerForceUnivDC =
|
||||||
|
Math.max(ad.attributes.powerForceLightDC, ad.attributes.powerForceDarkDC) ?? 10;
|
||||||
ad.attributes.powerTechDC = 8 + ad.abilities.int.mod + ad.attributes.prof ?? 10;
|
ad.attributes.powerTechDC = 8 + ad.abilities.int.mod + ad.attributes.prof ?? 10;
|
||||||
|
|
||||||
if (actorData.type !== 'character') return;
|
if (actorData.type !== "character") return;
|
||||||
|
|
||||||
// Set Force and tech bonus points for PC Actors
|
// Set Force and tech bonus points for PC Actors
|
||||||
if (!!ad.attributes.force.level) {
|
if (!!ad.attributes.force.level) {
|
||||||
|
@ -674,7 +684,6 @@ export default class Actor5e extends Actor {
|
||||||
if (!!ad.attributes.tech.level) {
|
if (!!ad.attributes.tech.level) {
|
||||||
ad.attributes.tech.points.max += ad.abilities.int.mod;
|
ad.attributes.tech.points.max += ad.abilities.int.mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -696,18 +705,19 @@ export default class Actor5e extends Actor {
|
||||||
if (!physicalItems.includes(i.type)) return weight;
|
if (!physicalItems.includes(i.type)) return weight;
|
||||||
const q = i.data.data.quantity || 0;
|
const q = i.data.data.quantity || 0;
|
||||||
const w = i.data.data.weight || 0;
|
const w = i.data.data.weight || 0;
|
||||||
return weight + (q * w);
|
return weight + q * w;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
// [Optional] add Currency Weight (for non-transformed actors)
|
// [Optional] add Currency Weight (for non-transformed actors)
|
||||||
if (game.settings.get("sw5e", "currencyWeight") && actorData.data.currency) {
|
if (game.settings.get("sw5e", "currencyWeight") && actorData.data.currency) {
|
||||||
const currency = actorData.data.currency;
|
const currency = actorData.data.currency;
|
||||||
const numCoins = Object.values(currency).reduce((val, denom) => val += Math.max(denom, 0), 0);
|
const numCoins = Object.values(currency).reduce((val, denom) => (val += Math.max(denom, 0)), 0);
|
||||||
weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the encumbrance size class
|
// Determine the encumbrance size class
|
||||||
let mod = {
|
let mod =
|
||||||
|
{
|
||||||
tiny: 0.5,
|
tiny: 0.5,
|
||||||
sm: 1,
|
sm: 1,
|
||||||
med: 1,
|
med: 1,
|
||||||
|
@ -721,7 +731,7 @@ export default class Actor5e extends Actor {
|
||||||
weight = weight.toNearest(0.1);
|
weight = weight.toNearest(0.1);
|
||||||
const max = actorData.data.abilities.str.value * CONFIG.SW5E.encumbrance.strMultiplier * mod;
|
const max = actorData.data.abilities.str.value * CONFIG.SW5E.encumbrance.strMultiplier * mod;
|
||||||
const pct = Math.clamped((weight * 100) / max, 0, 100);
|
const pct = Math.clamped((weight * 100) / max, 0, 100);
|
||||||
return { value: weight.toNearest(0.1), max, pct, encumbered: pct > (2/3) };
|
return {value: weight.toNearest(0.1), max, pct, encumbered: pct > 2 / 3};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -750,7 +760,7 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Apply changes in Actor size to Token width/height
|
// Apply changes in Actor size to Token width/height
|
||||||
const newSize = foundry.utils.getProperty(changed, "data.traits.size");
|
const newSize = foundry.utils.getProperty(changed, "data.traits.size");
|
||||||
if ( newSize && (newSize !== foundry.utils.getProperty(this.data, "data.traits.size")) ) {
|
if (newSize && newSize !== foundry.utils.getProperty(this.data, "data.traits.size")) {
|
||||||
let size = CONFIG.SW5E.tokenSizes[newSize];
|
let size = CONFIG.SW5E.tokenSizes[newSize];
|
||||||
if (!foundry.utils.hasProperty(changed, "token.width")) {
|
if (!foundry.utils.hasProperty(changed, "token.width")) {
|
||||||
changed.token = changed.token || {};
|
changed.token = changed.token || {};
|
||||||
|
@ -761,7 +771,7 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Reset death save counters
|
// Reset death save counters
|
||||||
const isDead = this.data.data.attributes.hp.value <= 0;
|
const isDead = this.data.data.attributes.hp.value <= 0;
|
||||||
if ( isDead && (foundry.utils.getProperty(changed, "data.attributes.hp.value") > 0) ) {
|
if (isDead && foundry.utils.getProperty(changed, "data.attributes.hp.value") > 0) {
|
||||||
foundry.utils.setProperty(changed, "data.attributes.death.success", 0);
|
foundry.utils.setProperty(changed, "data.attributes.death.success", 0);
|
||||||
foundry.utils.setProperty(changed, "data.attributes.death.failure", 0);
|
foundry.utils.setProperty(changed, "data.attributes.death.failure", 0);
|
||||||
}
|
}
|
||||||
|
@ -787,7 +797,7 @@ export default class Actor5e extends Actor {
|
||||||
async modifyTokenAttribute(attribute, value, isDelta, isBar) {
|
async modifyTokenAttribute(attribute, value, isDelta, isBar) {
|
||||||
if (attribute === "attributes.hp") {
|
if (attribute === "attributes.hp") {
|
||||||
const hp = getProperty(this.data.data, attribute);
|
const hp = getProperty(this.data.data, attribute);
|
||||||
const delta = isDelta ? (-1 * value) : (hp.value + hp.temp) - value;
|
const delta = isDelta ? -1 * value : hp.value + hp.temp - value;
|
||||||
return this.applyDamage(delta);
|
return this.applyDamage(delta);
|
||||||
}
|
}
|
||||||
return super.modifyTokenAttribute(attribute, value, isDelta, isBar);
|
return super.modifyTokenAttribute(attribute, value, isDelta, isBar);
|
||||||
|
@ -821,12 +831,16 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Delegate damage application to a hook
|
// Delegate damage application to a hook
|
||||||
// TODO replace this in the future with a better modifyTokenAttribute function in the core
|
// TODO replace this in the future with a better modifyTokenAttribute function in the core
|
||||||
const allowed = Hooks.call("modifyTokenAttribute", {
|
const allowed = Hooks.call(
|
||||||
|
"modifyTokenAttribute",
|
||||||
|
{
|
||||||
attribute: "attributes.hp",
|
attribute: "attributes.hp",
|
||||||
value: amount,
|
value: amount,
|
||||||
isDelta: false,
|
isDelta: false,
|
||||||
isBar: true
|
isBar: true
|
||||||
}, updates);
|
},
|
||||||
|
updates
|
||||||
|
);
|
||||||
return allowed !== false ? this.update(updates) : this;
|
return allowed !== false ? this.update(updates) : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,17 +879,19 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reliable Talent applies to any skill check we have full or better proficiency in
|
// Reliable Talent applies to any skill check we have full or better proficiency in
|
||||||
const reliableTalent = (skl.value >= 1 && this.getFlag("sw5e", "reliableTalent"));
|
const reliableTalent = skl.value >= 1 && this.getFlag("sw5e", "reliableTalent");
|
||||||
|
|
||||||
// Roll and return
|
// Roll and return
|
||||||
const rollData = foundry.utils.mergeObject(options, {
|
const rollData = foundry.utils.mergeObject(options, {
|
||||||
parts: parts,
|
parts: parts,
|
||||||
data: data,
|
data: data,
|
||||||
title: game.i18n.format("SW5E.SkillPromptTitle", {skill: CONFIG.SW5E.skills[skillId] || CONFIG.SW5E.starshipSkills[skillId]}),
|
title: game.i18n.format("SW5E.SkillPromptTitle", {
|
||||||
|
skill: CONFIG.SW5E.skills[skillId] || CONFIG.SW5E.starshipSkills[skillId]
|
||||||
|
}),
|
||||||
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
||||||
reliableTalent: reliableTalent,
|
reliableTalent: reliableTalent,
|
||||||
messageData: {
|
messageData: {
|
||||||
speaker: options.speaker || ChatMessage.getSpeaker({actor: this}),
|
"speaker": options.speaker || ChatMessage.getSpeaker({actor: this}),
|
||||||
"flags.sw5e.roll": {type: "skill", skillId}
|
"flags.sw5e.roll": {type: "skill", skillId}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -930,8 +946,7 @@ export default class Actor5e extends Actor {
|
||||||
if (feats.remarkableAthlete && SW5E.characterFlags.remarkableAthlete.abilities.includes(abilityId)) {
|
if (feats.remarkableAthlete && SW5E.characterFlags.remarkableAthlete.abilities.includes(abilityId)) {
|
||||||
parts.push("@proficiency");
|
parts.push("@proficiency");
|
||||||
data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof);
|
data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof);
|
||||||
}
|
} else if (feats.jackOfAllTrades) {
|
||||||
else if ( feats.jackOfAllTrades ) {
|
|
||||||
parts.push("@proficiency");
|
parts.push("@proficiency");
|
||||||
data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof);
|
data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof);
|
||||||
}
|
}
|
||||||
|
@ -955,7 +970,7 @@ export default class Actor5e extends Actor {
|
||||||
title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}),
|
title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}),
|
||||||
halflingLucky: feats.halflingLucky,
|
halflingLucky: feats.halflingLucky,
|
||||||
messageData: {
|
messageData: {
|
||||||
speaker: options.speaker || ChatMessage.getSpeaker({actor: this}),
|
"speaker": options.speaker || ChatMessage.getSpeaker({actor: this}),
|
||||||
"flags.sw5e.roll": {type: "ability", abilityId}
|
"flags.sw5e.roll": {type: "ability", abilityId}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1004,7 +1019,7 @@ export default class Actor5e extends Actor {
|
||||||
title: game.i18n.format("SW5E.SavePromptTitle", {ability: label}),
|
title: game.i18n.format("SW5E.SavePromptTitle", {ability: label}),
|
||||||
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
||||||
messageData: {
|
messageData: {
|
||||||
speaker: options.speaker || ChatMessage.getSpeaker({actor: this}),
|
"speaker": options.speaker || ChatMessage.getSpeaker({actor: this}),
|
||||||
"flags.sw5e.roll": {type: "save", abilityId}
|
"flags.sw5e.roll": {type: "save", abilityId}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1019,10 +1034,9 @@ export default class Actor5e extends Actor {
|
||||||
* @return {Promise<Roll|null>} A Promise which resolves to the Roll instance
|
* @return {Promise<Roll|null>} A Promise which resolves to the Roll instance
|
||||||
*/
|
*/
|
||||||
async rollDeathSave(options = {}) {
|
async rollDeathSave(options = {}) {
|
||||||
|
|
||||||
// Display a warning if we are not at zero HP or if we already have reached 3
|
// Display a warning if we are not at zero HP or if we already have reached 3
|
||||||
const death = this.data.data.attributes.death;
|
const death = this.data.data.attributes.death;
|
||||||
if ( (this.data.data.attributes.hp.value > 0) || (death.failure >= 3) || (death.success >= 3)) {
|
if (this.data.data.attributes.hp.value > 0 || death.failure >= 3 || death.success >= 3) {
|
||||||
ui.notifications.warn(game.i18n.localize("SW5E.DeathSaveUnnecessary"));
|
ui.notifications.warn(game.i18n.localize("SW5E.DeathSaveUnnecessary"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1060,7 @@ export default class Actor5e extends Actor {
|
||||||
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
halflingLucky: this.getFlag("sw5e", "halflingLucky"),
|
||||||
targetValue: 10,
|
targetValue: 10,
|
||||||
messageData: {
|
messageData: {
|
||||||
speaker: options.speaker || ChatMessage.getSpeaker({actor: this}),
|
"speaker": options.speaker || ChatMessage.getSpeaker({actor: this}),
|
||||||
"flags.sw5e.roll": {type: "death"}
|
"flags.sw5e.roll": {type: "death"}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1090,7 +1104,8 @@ export default class Actor5e extends Actor {
|
||||||
else {
|
else {
|
||||||
let failures = (death.failure || 0) + (d20 === 1 ? 2 : 1);
|
let failures = (death.failure || 0) + (d20 === 1 ? 2 : 1);
|
||||||
await this.update({"data.attributes.death.failure": Math.clamped(failures, 0, 3)});
|
await this.update({"data.attributes.death.failure": Math.clamped(failures, 0, 3)});
|
||||||
if ( failures >= 3 ) { // 3 Failures = death
|
if (failures >= 3) {
|
||||||
|
// 3 Failures = death
|
||||||
chatString = "SW5E.DeathSaveFailure";
|
chatString = "SW5E.DeathSaveFailure";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1116,20 +1131,19 @@ export default class Actor5e extends Actor {
|
||||||
* @return {Promise<Roll|null>} The created Roll instance, or null if no hit die was rolled
|
* @return {Promise<Roll|null>} The created Roll instance, or null if no hit die was rolled
|
||||||
*/
|
*/
|
||||||
async rollHitDie(denomination, {dialog = true} = {}) {
|
async rollHitDie(denomination, {dialog = true} = {}) {
|
||||||
|
|
||||||
// If no denomination was provided, choose the first available
|
// If no denomination was provided, choose the first available
|
||||||
let cls = null;
|
let cls = null;
|
||||||
if (!denomination) {
|
if (!denomination) {
|
||||||
cls = this.itemTypes.class.find(c => c.data.data.hitDiceUsed < c.data.data.levels);
|
cls = this.itemTypes.class.find((c) => c.data.data.hitDiceUsed < c.data.data.levels);
|
||||||
if (!cls) return null;
|
if (!cls) return null;
|
||||||
denomination = cls.data.data.hitDice;
|
denomination = cls.data.data.hitDice;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise locate a class (if any) which has an available hit die of the requested denomination
|
// Otherwise locate a class (if any) which has an available hit die of the requested denomination
|
||||||
else {
|
else {
|
||||||
cls = this.items.find(i => {
|
cls = this.items.find((i) => {
|
||||||
const d = i.data.data;
|
const d = i.data.data;
|
||||||
return (d.hitDice === denomination) && ((d.hitDiceUsed || 0) < (d.levels || 1));
|
return d.hitDice === denomination && (d.hitDiceUsed || 0) < (d.levels || 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1154,7 +1168,7 @@ export default class Actor5e extends Actor {
|
||||||
fastForward: !dialog,
|
fastForward: !dialog,
|
||||||
dialogOptions: {width: 350},
|
dialogOptions: {width: 350},
|
||||||
messageData: {
|
messageData: {
|
||||||
speaker: ChatMessage.getSpeaker({actor: this}),
|
"speaker": ChatMessage.getSpeaker({actor: this}),
|
||||||
"flags.sw5e.roll": {type: "hitDie"}
|
"flags.sw5e.roll": {type: "hitDie"}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1195,7 +1209,6 @@ export default class Actor5e extends Actor {
|
||||||
* @return {Promise.<RestResult>} A Promise which resolves once the short rest workflow has completed.
|
* @return {Promise.<RestResult>} A Promise which resolves once the short rest workflow has completed.
|
||||||
*/
|
*/
|
||||||
async shortRest({dialog = true, chat = true, autoHD = false, autoHDThreshold = 3} = {}) {
|
async shortRest({dialog = true, chat = true, autoHD = false, autoHDThreshold = 3} = {}) {
|
||||||
|
|
||||||
// Take note of the initial hit points and number of hit dice the Actor has
|
// Take note of the initial hit points and number of hit dice the Actor has
|
||||||
const hd0 = this.data.data.attributes.hd;
|
const hd0 = this.data.data.attributes.hd;
|
||||||
const hp0 = this.data.data.attributes.hp.value;
|
const hp0 = this.data.data.attributes.hp.value;
|
||||||
|
@ -1215,7 +1228,14 @@ export default class Actor5e extends Actor {
|
||||||
await this.autoSpendHitDice({threshold: autoHDThreshold});
|
await this.autoSpendHitDice({threshold: autoHDThreshold});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._rest(chat, newDay, false, this.data.data.attributes.hd - hd0, this.data.data.attributes.hp.value - hp0, this.data.data.attributes.tech.points.max - this.data.data.attributes.tech.points.value);
|
return this._rest(
|
||||||
|
chat,
|
||||||
|
newDay,
|
||||||
|
false,
|
||||||
|
this.data.data.attributes.hd - hd0,
|
||||||
|
this.data.data.attributes.hp.value - hp0,
|
||||||
|
this.data.data.attributes.tech.points.max - this.data.data.attributes.tech.points.value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -1239,7 +1259,15 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._rest(chat, newDay, true, 0, 0, this.data.data.attributes.tech.points.max - this.data.data.attributes.tech.points.value, this.data.data.attributes.force.points.max - this.data.data.attributes.force.points.value);
|
return this._rest(
|
||||||
|
chat,
|
||||||
|
newDay,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.data.data.attributes.tech.points.max - this.data.data.attributes.tech.points.value,
|
||||||
|
this.data.data.attributes.force.points.max - this.data.data.attributes.force.points.value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -1278,7 +1306,10 @@ export default class Actor5e extends Actor {
|
||||||
dfp: dfp,
|
dfp: dfp,
|
||||||
updateData: {
|
updateData: {
|
||||||
...hitPointUpdates,
|
...hitPointUpdates,
|
||||||
...this._getRestResourceRecovery({ recoverShortRestResources: !longRest, recoverLongRestResources: longRest }),
|
...this._getRestResourceRecovery({
|
||||||
|
recoverShortRestResources: !longRest,
|
||||||
|
recoverLongRestResources: longRest
|
||||||
|
}),
|
||||||
...this._getRestPowerRecovery({recoverForcePowers: longRest})
|
...this._getRestPowerRecovery({recoverForcePowers: longRest})
|
||||||
},
|
},
|
||||||
updateItems: [
|
updateItems: [
|
||||||
|
@ -1286,7 +1317,7 @@ export default class Actor5e extends Actor {
|
||||||
...this._getRestItemUsesRecovery({recoverLongRestUses: longRest, recoverDailyUses: newDay})
|
...this._getRestItemUsesRecovery({recoverLongRestUses: longRest, recoverDailyUses: newDay})
|
||||||
],
|
],
|
||||||
newDay: newDay
|
newDay: newDay
|
||||||
}
|
};
|
||||||
|
|
||||||
// Perform updates
|
// Perform updates
|
||||||
await this.update(result.updateData);
|
await this.update(result.updateData);
|
||||||
|
@ -1319,9 +1350,15 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Summarize the rest duration
|
// Summarize the rest duration
|
||||||
switch (game.settings.get("sw5e", "restVariant")) {
|
switch (game.settings.get("sw5e", "restVariant")) {
|
||||||
case 'normal': restFlavor = (longRest && newDay) ? "SW5E.LongRestOvernight" : `SW5E.${length}RestNormal`; break;
|
case "normal":
|
||||||
case 'gritty': restFlavor = (!longRest && newDay) ? "SW5E.ShortRestOvernight" : `SW5E.${length}RestGritty`; break;
|
restFlavor = longRest && newDay ? "SW5E.LongRestOvernight" : `SW5E.${length}RestNormal`;
|
||||||
case 'epic': restFlavor = `SW5E.${length}RestEpic`; break;
|
break;
|
||||||
|
case "gritty":
|
||||||
|
restFlavor = !longRest && newDay ? "SW5E.ShortRestOvernight" : `SW5E.${length}RestGritty`;
|
||||||
|
break;
|
||||||
|
case "epic":
|
||||||
|
restFlavor = `SW5E.${length}RestEpic`;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the chat message to display
|
// Determine the chat message to display
|
||||||
|
@ -1333,7 +1370,7 @@ export default class Actor5e extends Actor {
|
||||||
if (dhd !== 0) message += "HD";
|
if (dhd !== 0) message += "HD";
|
||||||
} else {
|
} else {
|
||||||
message = "SW5E.ShortRestResultShort";
|
message = "SW5E.ShortRestResultShort";
|
||||||
if ((dhd !== 0) && (dhp !== 0)){
|
if (dhd !== 0 && dhp !== 0) {
|
||||||
if (dtp !== 0) {
|
if (dtp !== 0) {
|
||||||
message = "SW5E.ShortRestResultWithTech";
|
message = "SW5E.ShortRestResultWithTech";
|
||||||
} else {
|
} else {
|
||||||
|
@ -1376,7 +1413,7 @@ export default class Actor5e extends Actor {
|
||||||
const max = this.data.data.attributes.hp.max + this.data.data.attributes.hp.tempmax;
|
const max = this.data.data.attributes.hp.max + this.data.data.attributes.hp.tempmax;
|
||||||
|
|
||||||
let diceRolled = 0;
|
let diceRolled = 0;
|
||||||
while ( (this.data.data.attributes.hp.value + threshold) <= max ) {
|
while (this.data.data.attributes.hp.value + threshold <= max) {
|
||||||
const r = await this.rollHitDie(undefined, {dialog: false});
|
const r = await this.rollHitDie(undefined, {dialog: false});
|
||||||
if (r === null) break;
|
if (r === null) break;
|
||||||
diceRolled += 1;
|
diceRolled += 1;
|
||||||
|
@ -1427,7 +1464,10 @@ export default class Actor5e extends Actor {
|
||||||
_getRestResourceRecovery({recoverShortRestResources = true, recoverLongRestResources = true} = {}) {
|
_getRestResourceRecovery({recoverShortRestResources = true, recoverLongRestResources = true} = {}) {
|
||||||
let updates = {};
|
let updates = {};
|
||||||
for (let [k, r] of Object.entries(this.data.data.resources)) {
|
for (let [k, r] of Object.entries(this.data.data.resources)) {
|
||||||
if ( Number.isNumeric(r.max) && ((recoverShortRestResources && r.sr) || (recoverLongRestResources && r.lr)) ) {
|
if (
|
||||||
|
Number.isNumeric(r.max) &&
|
||||||
|
((recoverShortRestResources && r.sr) || (recoverLongRestResources && r.lr))
|
||||||
|
) {
|
||||||
updates[`data.resources.${k}.value`] = Number(r.max);
|
updates[`data.resources.${k}.value`] = Number(r.max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1452,7 +1492,7 @@ export default class Actor5e extends Actor {
|
||||||
updates["data.attributes.tech.points.tempmax"] = 0;
|
updates["data.attributes.tech.points.tempmax"] = 0;
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(this.data.data.powers)) {
|
for (let [k, v] of Object.entries(this.data.data.powers)) {
|
||||||
updates[`data.powers.${k}.tvalue`] = Number.isNumeric(v.toverride) ? v.toverride : (v.tmax ?? 0);
|
updates[`data.powers.${k}.tvalue`] = Number.isNumeric(v.toverride) ? v.toverride : v.tmax ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1462,7 +1502,7 @@ export default class Actor5e extends Actor {
|
||||||
updates["data.attributes.force.points.tempmax"] = 0;
|
updates["data.attributes.force.points.tempmax"] = 0;
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(this.data.data.powers)) {
|
for (let [k, v] of Object.entries(this.data.data.powers)) {
|
||||||
updates[`data.powers.${k}.fvalue`] = Number.isNumeric(v.foverride) ? v.foverride : (v.fmax ?? 0);
|
updates[`data.powers.${k}.fvalue`] = Number.isNumeric(v.foverride) ? v.foverride : v.fmax ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1494,10 +1534,10 @@ export default class Actor5e extends Actor {
|
||||||
let hitDiceRecovered = 0;
|
let hitDiceRecovered = 0;
|
||||||
for (let item of sortedClasses) {
|
for (let item of sortedClasses) {
|
||||||
const d = item.data.data;
|
const d = item.data.data;
|
||||||
if ( (hitDiceRecovered < maxHitDice) && (d.hitDiceUsed > 0) ) {
|
if (hitDiceRecovered < maxHitDice && d.hitDiceUsed > 0) {
|
||||||
let delta = Math.min(d.hitDiceUsed || 0, maxHitDice - hitDiceRecovered);
|
let delta = Math.min(d.hitDiceUsed || 0, maxHitDice - hitDiceRecovered);
|
||||||
hitDiceRecovered += delta;
|
hitDiceRecovered += delta;
|
||||||
updates.push({_id: item.id, "data.hitDiceUsed": d.hitDiceUsed - delta});
|
updates.push({"_id": item.id, "data.hitDiceUsed": d.hitDiceUsed - delta});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1526,10 +1566,10 @@ export default class Actor5e extends Actor {
|
||||||
for (let item of this.items) {
|
for (let item of this.items) {
|
||||||
const d = item.data.data;
|
const d = item.data.data;
|
||||||
if (d.uses && recovery.includes(d.uses.per)) {
|
if (d.uses && recovery.includes(d.uses.per)) {
|
||||||
updates.push({_id: item.id, "data.uses.value": d.uses.max});
|
updates.push({"_id": item.id, "data.uses.value": d.uses.max});
|
||||||
}
|
}
|
||||||
if (recoverLongRestUses && d.recharge && d.recharge.value) {
|
if (recoverLongRestUses && d.recharge && d.recharge.value) {
|
||||||
updates.push({_id: item.id, "data.recharge.charged": true});
|
updates.push({"_id": item.id, "data.recharge.charged": true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,10 +1596,24 @@ export default class Actor5e extends Actor {
|
||||||
* @param {boolean} [keepVision] Keep vision
|
* @param {boolean} [keepVision] Keep vision
|
||||||
* @param {boolean} [transformTokens] Transform linked tokens too
|
* @param {boolean} [transformTokens] Transform linked tokens too
|
||||||
*/
|
*/
|
||||||
async transformInto(target, { keepPhysical=false, keepMental=false, keepSaves=false, keepSkills=false,
|
async transformInto(
|
||||||
mergeSaves=false, mergeSkills=false, keepClass=false, keepFeats=false, keepPowers=false,
|
target,
|
||||||
keepItems=false, keepBio=false, keepVision=false, transformTokens=true}={}) {
|
{
|
||||||
|
keepPhysical = false,
|
||||||
|
keepMental = false,
|
||||||
|
keepSaves = false,
|
||||||
|
keepSkills = false,
|
||||||
|
mergeSaves = false,
|
||||||
|
mergeSkills = false,
|
||||||
|
keepClass = false,
|
||||||
|
keepFeats = false,
|
||||||
|
keepPowers = false,
|
||||||
|
keepItems = false,
|
||||||
|
keepBio = false,
|
||||||
|
keepVision = false,
|
||||||
|
transformTokens = true
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
// Ensure the player is allowed to polymorph
|
// Ensure the player is allowed to polymorph
|
||||||
const allowed = game.settings.get("sw5e", "allowPolymorphing");
|
const allowed = game.settings.get("sw5e", "allowPolymorphing");
|
||||||
if (!allowed && !game.user.isGM) {
|
if (!allowed && !game.user.isGM) {
|
||||||
|
@ -1602,7 +1656,7 @@ export default class Actor5e extends Actor {
|
||||||
d.token[k] = source.token[k];
|
d.token[k] = source.token[k];
|
||||||
}
|
}
|
||||||
if (!keepVision) {
|
if (!keepVision) {
|
||||||
for ( let k of ['dimSight', 'brightSight', 'dimLight', 'brightLight', 'vision', 'sightAngle'] ) {
|
for (let k of ["dimSight", "brightSight", "dimLight", "brightLight", "vision", "sightAngle"]) {
|
||||||
d.token[k] = source.token[k];
|
d.token[k] = source.token[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1631,18 +1685,20 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep specific items from the original data
|
// Keep specific items from the original data
|
||||||
d.items = d.items.concat(o.items.filter(i => {
|
d.items = d.items.concat(
|
||||||
|
o.items.filter((i) => {
|
||||||
if (i.type === "class") return keepClass;
|
if (i.type === "class") return keepClass;
|
||||||
else if (i.type === "feat") return keepFeats;
|
else if (i.type === "feat") return keepFeats;
|
||||||
else if (i.type === "power") return keepPowers;
|
else if (i.type === "power") return keepPowers;
|
||||||
else return keepItems;
|
else return keepItems;
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Transfer classes for NPCs
|
// Transfer classes for NPCs
|
||||||
if (!keepClass && d.data.details.cr) {
|
if (!keepClass && d.data.details.cr) {
|
||||||
d.items.push({
|
d.items.push({
|
||||||
type: 'class',
|
type: "class",
|
||||||
name: game.i18n.localize('SW5E.PolymorphTmpClass'),
|
name: game.i18n.localize("SW5E.PolymorphTmpClass"),
|
||||||
data: {levels: d.data.details.cr}
|
data: {levels: d.data.details.cr}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1667,16 +1723,27 @@ export default class Actor5e extends Actor {
|
||||||
|
|
||||||
// Update regular Actors by creating a new Actor with the Polymorphed data
|
// Update regular Actors by creating a new Actor with the Polymorphed data
|
||||||
await this.sheet.close();
|
await this.sheet.close();
|
||||||
Hooks.callAll('sw5e.transformActor', this, target, d, {
|
Hooks.callAll("sw5e.transformActor", this, target, d, {
|
||||||
keepPhysical, keepMental, keepSaves, keepSkills, mergeSaves, mergeSkills,
|
keepPhysical,
|
||||||
keepClass, keepFeats, keepPowers, keepItems, keepBio, keepVision, transformTokens
|
keepMental,
|
||||||
|
keepSaves,
|
||||||
|
keepSkills,
|
||||||
|
mergeSaves,
|
||||||
|
mergeSkills,
|
||||||
|
keepClass,
|
||||||
|
keepFeats,
|
||||||
|
keepPowers,
|
||||||
|
keepItems,
|
||||||
|
keepBio,
|
||||||
|
keepVision,
|
||||||
|
transformTokens
|
||||||
});
|
});
|
||||||
const newActor = await this.constructor.create(d, {renderSheet: true});
|
const newActor = await this.constructor.create(d, {renderSheet: true});
|
||||||
|
|
||||||
// Update placed Token instances
|
// Update placed Token instances
|
||||||
if (!transformTokens) return;
|
if (!transformTokens) return;
|
||||||
const tokens = this.getActiveTokens(true);
|
const tokens = this.getActiveTokens(true);
|
||||||
const updates = tokens.map(t => {
|
const updates = tokens.map((t) => {
|
||||||
const newTokenData = foundry.utils.deepClone(d.token);
|
const newTokenData = foundry.utils.deepClone(d.token);
|
||||||
if (!t.data.actorLink) newTokenData.actorData = newActor.data;
|
if (!t.data.actorLink) newTokenData.actorData = newActor.data;
|
||||||
newTokenData._id = t.data._id;
|
newTokenData._id = t.data._id;
|
||||||
|
@ -1711,14 +1778,14 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain a reference to the original actor
|
// Obtain a reference to the original actor
|
||||||
const original = game.actors.get(this.getFlag('sw5e', 'originalActor'));
|
const original = game.actors.get(this.getFlag("sw5e", "originalActor"));
|
||||||
if (!original) return;
|
if (!original) return;
|
||||||
|
|
||||||
// Get the Tokens which represent this actor
|
// Get the Tokens which represent this actor
|
||||||
if (canvas.ready) {
|
if (canvas.ready) {
|
||||||
const tokens = this.getActiveTokens(true);
|
const tokens = this.getActiveTokens(true);
|
||||||
const tokenData = await original.getTokenData();
|
const tokenData = await original.getTokenData();
|
||||||
const tokenUpdates = tokens.map(t => {
|
const tokenUpdates = tokens.map((t) => {
|
||||||
const update = duplicate(tokenData);
|
const update = duplicate(tokenData);
|
||||||
update._id = t.id;
|
update._id = t.id;
|
||||||
delete update.x;
|
delete update.x;
|
||||||
|
@ -1745,16 +1812,16 @@ export default class Actor5e extends Actor {
|
||||||
*/
|
*/
|
||||||
static addDirectoryContextOptions(html, entryOptions) {
|
static addDirectoryContextOptions(html, entryOptions) {
|
||||||
entryOptions.push({
|
entryOptions.push({
|
||||||
name: 'SW5E.PolymorphRestoreTransformation',
|
name: "SW5E.PolymorphRestoreTransformation",
|
||||||
icon: '<i class="fas fa-backward"></i>',
|
icon: '<i class="fas fa-backward"></i>',
|
||||||
callback: li => {
|
callback: (li) => {
|
||||||
const actor = game.actors.get(li.data('entityId'));
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
return actor.revertOriginalForm();
|
return actor.revertOriginalForm();
|
||||||
},
|
},
|
||||||
condition: li => {
|
condition: (li) => {
|
||||||
const allowed = game.settings.get("sw5e", "allowPolymorphing");
|
const allowed = game.settings.get("sw5e", "allowPolymorphing");
|
||||||
if (!allowed && !game.user.isGM) return false;
|
if (!allowed && !game.user.isGM) return false;
|
||||||
const actor = game.actors.get(li.data('entityId'));
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
return actor && actor.isPolymorphed;
|
return actor && actor.isPolymorphed;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1778,7 +1845,7 @@ export default class Actor5e extends Actor {
|
||||||
}
|
}
|
||||||
let type = localizedType;
|
let type = localizedType;
|
||||||
if (!!typeData.swarm) {
|
if (!!typeData.swarm) {
|
||||||
type = game.i18n.format('SW5E.CreatureSwarmPhrase', {
|
type = game.i18n.format("SW5E.CreatureSwarmPhrase", {
|
||||||
size: game.i18n.localize(CONFIG.SW5E.actorSizes[typeData.swarm]),
|
size: game.i18n.localize(CONFIG.SW5E.actorSizes[typeData.swarm]),
|
||||||
type: localizedType
|
type: localizedType
|
||||||
});
|
});
|
||||||
|
@ -1795,7 +1862,9 @@ export default class Actor5e extends Actor {
|
||||||
* @deprecated since sw5e 0.97
|
* @deprecated since sw5e 0.97
|
||||||
*/
|
*/
|
||||||
getPowerDC(ability) {
|
getPowerDC(ability) {
|
||||||
console.warn(`The Actor5e#getPowerDC(ability) method has been deprecated in favor of Actor5e#data.data.abilities[ability].dc`);
|
console.warn(
|
||||||
|
`The Actor5e#getPowerDC(ability) method has been deprecated in favor of Actor5e#data.data.abilities[ability].dc`
|
||||||
|
);
|
||||||
return this.data.data.abilities[ability]?.dc;
|
return this.data.data.abilities[ability]?.dc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ import ActorHitDiceConfig from "../../../apps/hit-dice-config.js";
|
||||||
import ActorMovementConfig from "../../../apps/movement-config.js";
|
import ActorMovementConfig from "../../../apps/movement-config.js";
|
||||||
import ActorSensesConfig from "../../../apps/senses-config.js";
|
import ActorSensesConfig from "../../../apps/senses-config.js";
|
||||||
import ActorTypeConfig from "../../../apps/actor-type.js";
|
import ActorTypeConfig from "../../../apps/actor-type.js";
|
||||||
import {SW5E} from '../../../config.js';
|
import {SW5E} from "../../../config.js";
|
||||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +58,8 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if ( !game.user.isGM && this.actor.limited ) return "systems/sw5e/templates/actors/newActor/expanded-limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited)
|
||||||
|
return "systems/sw5e/templates/actors/newActor/expanded-limited-sheet.html";
|
||||||
return `systems/sw5e/templates/actors/newActor/${this.actor.data.type}-sheet.html`;
|
return `systems/sw5e/templates/actors/newActor/${this.actor.data.type}-sheet.html`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Basic data
|
// Basic data
|
||||||
let isOwner = this.actor.isOwner;
|
let isOwner = this.actor.isOwner;
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -78,7 +78,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
isCharacter: this.actor.type === "character",
|
isCharacter: this.actor.type === "character",
|
||||||
isNPC: this.actor.type === "npc",
|
isNPC: this.actor.type === "npc",
|
||||||
isStarship: this.actor.type === "starship",
|
isStarship: this.actor.type === "starship",
|
||||||
isVehicle: this.actor.type === 'vehicle',
|
isVehicle: this.actor.type === "vehicle",
|
||||||
config: CONFIG.SW5E,
|
config: CONFIG.SW5E,
|
||||||
rollData: this.actor.getRollData.bind(this.actor)
|
rollData: this.actor.getRollData.bind(this.actor)
|
||||||
};
|
};
|
||||||
|
@ -137,7 +137,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
||||||
|
|
||||||
// Return data to the sheet
|
// Return data to the sheet
|
||||||
return data
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -156,31 +156,35 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let speeds = [
|
let speeds = [
|
||||||
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
||||||
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
||||||
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
|
[
|
||||||
|
movement.fly,
|
||||||
|
`${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` +
|
||||||
|
(movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")
|
||||||
|
],
|
||||||
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
||||||
]
|
];
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and sort speeds on their values
|
// Filter and sort speeds on their values
|
||||||
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
|
speeds = speeds.filter((s) => !!s[0]).sort((a, b) => b[0] - a[0]);
|
||||||
|
|
||||||
// Case 1: Largest as primary
|
// Case 1: Largest as primary
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
let primary = speeds.shift();
|
let primary = speeds.shift();
|
||||||
return {
|
return {
|
||||||
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
||||||
special: speeds.map(s => s[1]).join(", ")
|
special: speeds.map((s) => s[1]).join(", ")
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: Walk as primary
|
// Case 2: Walk as primary
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
primary: `${movement.walk || 0} ${movement.units}`,
|
primary: `${movement.walk || 0} ${movement.units}`,
|
||||||
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
|
special: speeds.length ? speeds.map((s) => s[1]).join(", ") : ""
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +194,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const senses = actorData.data.attributes.senses || {};
|
const senses = actorData.data.attributes.senses || {};
|
||||||
const tags = {};
|
const tags = {};
|
||||||
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[k] ?? 0
|
const v = senses[k] ?? 0;
|
||||||
if (v === 0) continue;
|
if (v === 0) continue;
|
||||||
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
||||||
}
|
}
|
||||||
|
@ -207,14 +211,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
*/
|
*/
|
||||||
_prepareTraits(traits) {
|
_prepareTraits(traits) {
|
||||||
const map = {
|
const map = {
|
||||||
"dr": CONFIG.SW5E.damageResistanceTypes,
|
dr: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"di": CONFIG.SW5E.damageResistanceTypes,
|
di: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"dv": CONFIG.SW5E.damageResistanceTypes,
|
dv: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"ci": CONFIG.SW5E.conditionTypes,
|
ci: CONFIG.SW5E.conditionTypes,
|
||||||
"languages": CONFIG.SW5E.languages,
|
languages: CONFIG.SW5E.languages,
|
||||||
"armorProf": CONFIG.SW5E.armorProficiencies,
|
armorProf: CONFIG.SW5E.armorProficiencies,
|
||||||
"weaponProf": CONFIG.SW5E.weaponProficiencies,
|
weaponProf: CONFIG.SW5E.weaponProficiencies,
|
||||||
"toolProf": CONFIG.SW5E.toolProficiencies
|
toolProf: CONFIG.SW5E.toolProficiencies
|
||||||
};
|
};
|
||||||
for (let [t, choices] of Object.entries(map)) {
|
for (let [t, choices] of Object.entries(map)) {
|
||||||
const trait = traits[t];
|
const trait = traits[t];
|
||||||
|
@ -230,7 +234,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Add custom entry
|
// Add custom entry
|
||||||
if (trait.custom) {
|
if (trait.custom) {
|
||||||
trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim());
|
trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim()));
|
||||||
}
|
}
|
||||||
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
||||||
}
|
}
|
||||||
|
@ -252,8 +256,8 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Define some mappings
|
// Define some mappings
|
||||||
const sections = {
|
const sections = {
|
||||||
"atwill": -20,
|
atwill: -20,
|
||||||
"innate": -10,
|
innate: -10
|
||||||
};
|
};
|
||||||
|
|
||||||
// Label power slot uses headers
|
// Label power slot uses headers
|
||||||
|
@ -270,12 +274,17 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
label: label,
|
label: label,
|
||||||
usesSlots: i > 0,
|
usesSlots: i > 0,
|
||||||
canCreate: owner,
|
canCreate: owner,
|
||||||
canPrepare: (data.actor.type === "character") && (i >= 1),
|
canPrepare: data.actor.type === "character" && i >= 1,
|
||||||
powers: [],
|
powers: [],
|
||||||
uses: useLabels[i] || value || 0,
|
uses: useLabels[i] || value || 0,
|
||||||
slots: useLabels[i] || max || 0,
|
slots: useLabels[i] || max || 0,
|
||||||
override: override || 0,
|
override: override || 0,
|
||||||
dataset: {"type": "power", "level": prepMode in sections ? 1 : i, "preparation.mode": prepMode, "school": school},
|
dataset: {
|
||||||
|
"type": "power",
|
||||||
|
"level": prepMode in sections ? 1 : i,
|
||||||
|
"preparation.mode": prepMode,
|
||||||
|
"school": school
|
||||||
|
},
|
||||||
prop: sl
|
prop: sl
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -284,7 +293,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
||||||
if (i === 0) return max;
|
if (i === 0) return max;
|
||||||
const level = levels[`power${i}`];
|
const level = levels[`power${i}`];
|
||||||
if ( (level.max || level.override ) && ( i > max ) ) max = i;
|
if ((level.max || level.override) && i > max) max = i;
|
||||||
return max;
|
return max;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -298,7 +307,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over every power item, adding powers to the powerbook by section
|
// Iterate over every power item, adding powers to the powerbook by section
|
||||||
powers.forEach(power => {
|
powers.forEach((power) => {
|
||||||
const mode = power.data.preparation.mode || "prepared";
|
const mode = power.data.preparation.mode || "prepared";
|
||||||
let s = power.data.level || 0;
|
let s = power.data.level || 0;
|
||||||
const sl = `power${s}`;
|
const sl = `power${s}`;
|
||||||
|
@ -341,13 +350,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_filterItems(items, filters) {
|
_filterItems(items, filters) {
|
||||||
return items.filter(item => {
|
return items.filter((item) => {
|
||||||
const data = item.data;
|
const data = item.data;
|
||||||
|
|
||||||
// Action usage
|
// Action usage
|
||||||
for (let f of ["action", "bonus", "reaction"]) {
|
for (let f of ["action", "bonus", "reaction"]) {
|
||||||
if (filters.has(f)) {
|
if (filters.has(f)) {
|
||||||
if ((data.activation && (data.activation.type !== f))) return false;
|
if (data.activation && data.activation.type !== f) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,64 +404,62 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
|
|
||||||
// Activate Item Filters
|
// Activate Item Filters
|
||||||
const filterLists = html.find(".filter-list");
|
const filterLists = html.find(".filter-list");
|
||||||
filterLists.each(this._initializeFilterItemList.bind(this));
|
filterLists.each(this._initializeFilterItemList.bind(this));
|
||||||
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
||||||
|
|
||||||
// Item summaries
|
// Item summaries
|
||||||
html.find('.item .item-name.rollable h4').click(event => this._onItemSummary(event));
|
html.find(".item .item-name.rollable h4").click((event) => this._onItemSummary(event));
|
||||||
|
|
||||||
// View Item Sheets
|
// View Item Sheets
|
||||||
html.find('.item-edit').click(this._onItemEdit.bind(this));
|
html.find(".item-edit").click(this._onItemEdit.bind(this));
|
||||||
|
|
||||||
// Editable Only Listeners
|
// Editable Only Listeners
|
||||||
if (this.isEditable) {
|
if (this.isEditable) {
|
||||||
|
|
||||||
// Input focus and update
|
// Input focus and update
|
||||||
const inputs = html.find("input");
|
const inputs = html.find("input");
|
||||||
inputs.focus(ev => ev.currentTarget.select());
|
inputs.focus((ev) => ev.currentTarget.select());
|
||||||
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
||||||
|
|
||||||
// Ability Proficiency
|
// Ability Proficiency
|
||||||
html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this));
|
html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this));
|
||||||
|
|
||||||
// Toggle Skill Proficiency
|
// Toggle Skill Proficiency
|
||||||
html.find('.skill-proficiency').on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
html.find(".skill-proficiency").on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
||||||
|
|
||||||
// Trait Selector
|
// Trait Selector
|
||||||
html.find('.trait-selector').click(this._onTraitSelector.bind(this));
|
html.find(".trait-selector").click(this._onTraitSelector.bind(this));
|
||||||
|
|
||||||
// Configure Special Flags
|
// Configure Special Flags
|
||||||
html.find('.config-button').click(this._onConfigMenu.bind(this));
|
html.find(".config-button").click(this._onConfigMenu.bind(this));
|
||||||
|
|
||||||
// Owned Item management
|
// Owned Item management
|
||||||
html.find('.item-create').click(this._onItemCreate.bind(this));
|
html.find(".item-create").click(this._onItemCreate.bind(this));
|
||||||
html.find('.item-delete').click(this._onItemDelete.bind(this));
|
html.find(".item-delete").click(this._onItemDelete.bind(this));
|
||||||
html.find('.item-collapse').click(this._onItemCollapse.bind(this));
|
html.find(".item-collapse").click(this._onItemCollapse.bind(this));
|
||||||
html.find('.item-uses input').click(ev => ev.target.select()).change(this._onUsesChange.bind(this));
|
html.find(".item-uses input")
|
||||||
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
|
.click((ev) => ev.target.select())
|
||||||
html.find('.increment-class-level').click(this._onIncrementClassLevel.bind(this));
|
.change(this._onUsesChange.bind(this));
|
||||||
html.find('.decrement-class-level').click(this._onDecrementClassLevel.bind(this));
|
html.find(".slot-max-override").click(this._onPowerSlotOverride.bind(this));
|
||||||
|
html.find(".increment-class-level").click(this._onIncrementClassLevel.bind(this));
|
||||||
|
html.find(".decrement-class-level").click(this._onDecrementClassLevel.bind(this));
|
||||||
|
|
||||||
// Active Effect management
|
// Active Effect management
|
||||||
html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor));
|
html.find(".effect-control").click((ev) => onManageActiveEffect(ev, this.actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner Only Listeners
|
// Owner Only Listeners
|
||||||
if (this.actor.isOwner) {
|
if (this.actor.isOwner) {
|
||||||
|
|
||||||
// Ability Checks
|
// Ability Checks
|
||||||
html.find('.ability-name').click(this._onRollAbilityTest.bind(this));
|
html.find(".ability-name").click(this._onRollAbilityTest.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// Roll Skill Checks
|
// Roll Skill Checks
|
||||||
html.find('.skill-name').click(this._onRollSkillCheck.bind(this));
|
html.find(".skill-name").click(this._onRollSkillCheck.bind(this));
|
||||||
|
|
||||||
// Item Rolling
|
// Item Rolling
|
||||||
html.find('.item .item-image').click(event => this._onItemRoll(event));
|
html.find(".item .item-image").click((event) => this._onItemRoll(event));
|
||||||
html.find('.item .item-recharge').click(event => this._onItemRecharge(event));
|
html.find(".item .item-recharge").click((event) => this._onItemRecharge(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise remove rollable classes
|
// Otherwise remove rollable classes
|
||||||
|
@ -547,9 +554,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Toggle next level - forward on click, backwards on right
|
// Toggle next level - forward on click, backwards on right
|
||||||
if (event.type === "click") {
|
if (event.type === "click") {
|
||||||
field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]);
|
field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]);
|
||||||
} else if (event.type === "contextmenu") {
|
} else if (event.type === "contextmenu") {
|
||||||
field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]);
|
field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the field value and save the form
|
// Update the field value and save the form
|
||||||
|
@ -560,13 +567,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropActor(event, data) {
|
async _onDropActor(event, data) {
|
||||||
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get('sw5e', 'allowPolymorphing'));
|
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get("sw5e", "allowPolymorphing"));
|
||||||
if (!canPolymorph) return false;
|
if (!canPolymorph) return false;
|
||||||
|
|
||||||
// Get the target actor
|
// Get the target actor
|
||||||
let sourceActor = null;
|
let sourceActor = null;
|
||||||
if (data.pack) {
|
if (data.pack) {
|
||||||
const pack = game.packs.find(p => p.collection === data.pack);
|
const pack = game.packs.find((p) => p.collection === data.pack);
|
||||||
sourceActor = await pack.getEntity(data.id);
|
sourceActor = await pack.getEntity(data.id);
|
||||||
} else {
|
} else {
|
||||||
sourceActor = game.actors.get(data.id);
|
sourceActor = game.actors.get(data.id);
|
||||||
|
@ -574,35 +581,37 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
if (!sourceActor) return;
|
if (!sourceActor) return;
|
||||||
|
|
||||||
// Define a function to record polymorph settings for future use
|
// Define a function to record polymorph settings for future use
|
||||||
const rememberOptions = html => {
|
const rememberOptions = (html) => {
|
||||||
const options = {};
|
const options = {};
|
||||||
html.find('input').each((i, el) => {
|
html.find("input").each((i, el) => {
|
||||||
options[el.name] = el.checked;
|
options[el.name] = el.checked;
|
||||||
});
|
});
|
||||||
const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options);
|
const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options);
|
||||||
game.settings.set('sw5e', 'polymorphSettings', settings);
|
game.settings.set("sw5e", "polymorphSettings", settings);
|
||||||
return settings;
|
return settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and render the Dialog
|
// Create and render the Dialog
|
||||||
return new Dialog({
|
return new Dialog(
|
||||||
title: game.i18n.localize('SW5E.PolymorphPromptTitle'),
|
{
|
||||||
|
title: game.i18n.localize("SW5E.PolymorphPromptTitle"),
|
||||||
content: {
|
content: {
|
||||||
options: game.settings.get('sw5e', 'polymorphSettings'),
|
options: game.settings.get("sw5e", "polymorphSettings"),
|
||||||
i18n: SW5E.polymorphSettings,
|
i18n: SW5E.polymorphSettings,
|
||||||
isToken: this.actor.isToken
|
isToken: this.actor.isToken
|
||||||
},
|
},
|
||||||
default: 'accept',
|
default: "accept",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: {
|
accept: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphAcceptSettings'),
|
label: game.i18n.localize("SW5E.PolymorphAcceptSettings"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, rememberOptions(html))
|
callback: (html) => this.actor.transformInto(sourceActor, rememberOptions(html))
|
||||||
},
|
},
|
||||||
wildshape: {
|
wildshape: {
|
||||||
icon: '<i class="fas fa-paw"></i>',
|
icon: '<i class="fas fa-paw"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphWildShape'),
|
label: game.i18n.localize("SW5E.PolymorphWildShape"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
keepBio: true,
|
keepBio: true,
|
||||||
keepClass: true,
|
keepClass: true,
|
||||||
keepMental: true,
|
keepMental: true,
|
||||||
|
@ -613,45 +622,49 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
},
|
},
|
||||||
polymorph: {
|
polymorph: {
|
||||||
icon: '<i class="fas fa-pastafarianism"></i>',
|
icon: '<i class="fas fa-pastafarianism"></i>',
|
||||||
label: game.i18n.localize('SW5E.Polymorph'),
|
label: game.i18n.localize("SW5E.Polymorph"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
transformTokens: rememberOptions(html).transformTokens
|
transformTokens: rememberOptions(html).transformTokens
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: game.i18n.localize('Cancel')
|
label: game.i18n.localize("Cancel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
classes: ['dialog', 'sw5e'],
|
{
|
||||||
|
classes: ["dialog", "sw5e"],
|
||||||
width: 600,
|
width: 600,
|
||||||
template: 'systems/sw5e/templates/apps/polymorph-prompt.html'
|
template: "systems/sw5e/templates/apps/polymorph-prompt.html"
|
||||||
}).render(true);
|
}
|
||||||
|
).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Check to make sure items of this type are allowed on this actor
|
// Check to make sure items of this type are allowed on this actor
|
||||||
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
||||||
return ui.notifications.warn(game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
return ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
||||||
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
||||||
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a Consumable power scroll on the Inventory tab
|
// Create a Consumable power scroll on the Inventory tab
|
||||||
if ( (itemData.type === "power") && (this._tabs[0].active === "inventory") ) {
|
if (itemData.type === "power" && this._tabs[0].active === "inventory") {
|
||||||
const scroll = await Item5e.createScrollFromPower(itemData);
|
const scroll = await Item5e.createScrollFromPower(itemData);
|
||||||
itemData = scroll.data;
|
itemData = scroll.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemData.data) {
|
if (itemData.data) {
|
||||||
// Ignore certain statuses
|
// Ignore certain statuses
|
||||||
["equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
|
["equipped", "proficient", "prepared"].forEach((k) => delete itemData.data[k]);
|
||||||
|
|
||||||
// Downgrade ATTUNED to REQUIRED
|
// Downgrade ATTUNED to REQUIRED
|
||||||
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
||||||
|
@ -659,14 +672,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Stack identical consumables
|
// Stack identical consumables
|
||||||
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
||||||
const similarItem = this.actor.items.find(i => {
|
const similarItem = this.actor.items.find((i) => {
|
||||||
const sourceId = i.getFlag("core", "sourceId");
|
const sourceId = i.getFlag("core", "sourceId");
|
||||||
return sourceId && (sourceId === itemData.flags.core?.sourceId) &&
|
return sourceId && sourceId === itemData.flags.core?.sourceId && i.type === "consumable";
|
||||||
(i.type === "consumable");
|
|
||||||
});
|
});
|
||||||
if ( similarItem && itemData.name !== "Power Cell" ) { // Always create a new powercell instead of increasing quantity
|
if (similarItem && itemData.name !== "Power Cell") {
|
||||||
|
// Always create a new powercell instead of increasing quantity
|
||||||
return similarItem.update({
|
return similarItem.update({
|
||||||
'data.quantity': similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
"data.quantity": similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,7 +725,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
||||||
event.target.value = uses;
|
event.target.value = uses;
|
||||||
return item.update({ 'data.uses.value': uses });
|
return item.update({"data.uses.value": uses});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -740,7 +753,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
return item.rollRecharge();
|
return item.rollRecharge();
|
||||||
};
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
@ -761,7 +774,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
} else {
|
} else {
|
||||||
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
||||||
let props = $(`<div class="item-properties"></div>`);
|
let props = $(`<div class="item-properties"></div>`);
|
||||||
chatData.properties.forEach(p => props.append(`<span class="tag">${p}</span>`));
|
chatData.properties.forEach((p) => props.append(`<span class="tag">${p}</span>`));
|
||||||
div.append(props);
|
div.append(props);
|
||||||
li.append(div.hide());
|
li.append(div.hide());
|
||||||
div.slideDown(200);
|
div.slideDown(200);
|
||||||
|
@ -847,7 +860,7 @@ _onItemCollapse(event) {
|
||||||
_onIncrementClassLevel(event) {
|
_onIncrementClassLevel(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const div = event.currentTarget.closest(".character")
|
const div = event.currentTarget.closest(".character");
|
||||||
const li = event.currentTarget.closest("li");
|
const li = event.currentTarget.closest("li");
|
||||||
|
|
||||||
const actorId = div.id.split("-")[1];
|
const actorId = div.id.split("-")[1];
|
||||||
|
@ -857,7 +870,7 @@ _onItemCollapse(event) {
|
||||||
const item = actor.items.get(itemId);
|
const item = actor.items.get(itemId);
|
||||||
|
|
||||||
let levels = item.data.data.levels;
|
let levels = item.data.data.levels;
|
||||||
const update = {_id: item.data._id, data: {levels: (levels + 1) }};
|
const update = {_id: item.data._id, data: {levels: levels + 1}};
|
||||||
|
|
||||||
actor.updateEmbeddedDocuments("Item", [update]);
|
actor.updateEmbeddedDocuments("Item", [update]);
|
||||||
}
|
}
|
||||||
|
@ -871,7 +884,7 @@ _onItemCollapse(event) {
|
||||||
_onDecrementClassLevel(event) {
|
_onDecrementClassLevel(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const div = event.currentTarget.closest(".character")
|
const div = event.currentTarget.closest(".character");
|
||||||
const li = event.currentTarget.closest("li");
|
const li = event.currentTarget.closest("li");
|
||||||
|
|
||||||
const actorId = div.id.split("-")[1];
|
const actorId = div.id.split("-")[1];
|
||||||
|
@ -881,7 +894,7 @@ _onItemCollapse(event) {
|
||||||
const item = actor.items.get(itemId);
|
const item = actor.items.get(itemId);
|
||||||
|
|
||||||
let levels = item.data.data.levels;
|
let levels = item.data.data.levels;
|
||||||
const update = {_id: item.data._id, data: {levels: (levels - 1) }};
|
const update = {_id: item.data._id, data: {levels: levels - 1}};
|
||||||
|
|
||||||
actor.updateEmbeddedDocuments("Item", [update]);
|
actor.updateEmbeddedDocuments("Item", [update]);
|
||||||
}
|
}
|
||||||
|
@ -955,7 +968,7 @@ _onItemCollapse(event) {
|
||||||
const label = a.parentElement.querySelector("label");
|
const label = a.parentElement.querySelector("label");
|
||||||
const choices = CONFIG.SW5E[a.dataset.options];
|
const choices = CONFIG.SW5E[a.dataset.options];
|
||||||
const options = {name: a.dataset.target, title: label.innerText, choices};
|
const options = {name: a.dataset.target, title: label.innerText, choices};
|
||||||
return new TraitSelector(this.actor, options).render(true)
|
return new TraitSelector(this.actor, options).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -965,7 +978,7 @@ _onItemCollapse(event) {
|
||||||
let buttons = super._getHeaderButtons();
|
let buttons = super._getHeaderButtons();
|
||||||
if (this.actor.isPolymorphed) {
|
if (this.actor.isPolymorphed) {
|
||||||
buttons.unshift({
|
buttons.unshift({
|
||||||
label: 'SW5E.PolymorphRestoreTransformation',
|
label: "SW5E.PolymorphRestoreTransformation",
|
||||||
class: "restore-transformation",
|
class: "restore-transformation",
|
||||||
icon: "fas fa-backward",
|
icon: "fas fa-backward",
|
||||||
onclick: () => this.actor.revertOriginalForm()
|
onclick: () => this.actor.revertOriginalForm()
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Actor5e from "../../entity.js";
|
||||||
* @type {ActorSheet5e}
|
* @type {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
return "systems/sw5e/templates/actors/newActor/character-sheet.html";
|
return "systems/sw5e/templates/actors/newActor/character-sheet.html";
|
||||||
|
@ -17,17 +16,18 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
|
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["swalt", "sw5e", "sheet", "actor", "character"],
|
classes: ["swalt", "sw5e", "sheet", "actor", "character"],
|
||||||
blockFavTab: true,
|
blockFavTab: true,
|
||||||
subTabs: null,
|
subTabs: null,
|
||||||
width: 800,
|
width: 800,
|
||||||
tabs: [{
|
tabs: [
|
||||||
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,10 +56,12 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
// Experience Tracking
|
// Experience Tracking
|
||||||
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
||||||
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
|
sheetData["classLabels"] = this.actor.itemTypes.class.map((c) => c.name).join(", ");
|
||||||
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
|
sheetData["multiclassLabels"] = this.actor.itemTypes.class
|
||||||
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
|
.map((c) => {
|
||||||
}).join(', ');
|
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" ");
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
return sheetData;
|
return sheetData;
|
||||||
|
@ -72,7 +74,6 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize items as inventory, powerbook, features, and classes
|
// Categorize items as inventory, powerbook, features, and classes
|
||||||
const inventory = {
|
const inventory = {
|
||||||
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
||||||
|
@ -84,11 +85,27 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Partition items by category
|
// Partition items by category
|
||||||
let [items, forcepowers, techpowers, feats, classes, deployments, deploymentfeatures, ventures, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => {
|
let [
|
||||||
|
items,
|
||||||
|
forcepowers,
|
||||||
|
techpowers,
|
||||||
|
feats,
|
||||||
|
classes,
|
||||||
|
deployments,
|
||||||
|
deploymentfeatures,
|
||||||
|
ventures,
|
||||||
|
species,
|
||||||
|
archetypes,
|
||||||
|
classfeatures,
|
||||||
|
backgrounds,
|
||||||
|
fightingstyles,
|
||||||
|
fightingmasteries,
|
||||||
|
lightsaberforms
|
||||||
|
] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
// Item details
|
// Item details
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.attunement = {
|
item.attunement = {
|
||||||
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
||||||
icon: "fa-sun",
|
icon: "fa-sun",
|
||||||
|
@ -103,16 +120,18 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
}[item.data.attunement];
|
}[item.data.attunement];
|
||||||
|
|
||||||
// Item usage
|
// Item usage
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
item.hasUses = item.data.uses && item.data.uses.max > 0;
|
||||||
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
item.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
|
|
||||||
// Item toggle state
|
// Item toggle state
|
||||||
this._prepareItemToggleState(item);
|
this._prepareItemToggleState(item);
|
||||||
|
|
||||||
// Primary Class
|
// Primary Class
|
||||||
if ( item.type === "class" ) item.isOriginalClass = ( item._id === this.actor.data.data.details.originalClass );
|
if (item.type === "class")
|
||||||
|
item.isOriginalClass = item._id === this.actor.data.data.details.originalClass;
|
||||||
|
|
||||||
// Classify items into types
|
// Classify items into types
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[1].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[1].push(item);
|
||||||
|
@ -131,7 +150,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
else if (item.type === "lightsaberform") arr[14].push(item);
|
else if (item.type === "lightsaberform") arr[14].push(item);
|
||||||
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], [], [], [], [], [], [], [], [], [], [], [], [], []]);
|
},
|
||||||
|
[[], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply active item filters
|
// Apply active item filters
|
||||||
items = this._filterItems(items, this._filters.inventory);
|
items = this._filterItems(items, this._filters.inventory);
|
||||||
|
@ -153,18 +174,89 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
// Organize Features
|
// Organize Features
|
||||||
const features = {
|
const features = {
|
||||||
classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true },
|
classes: {
|
||||||
classfeatures: { label: "SW5E.ItemTypeClassFeats", items: [], hasActions: true, dataset: {type: "classfeature"}, isClassfeature: true },
|
label: "SW5E.ItemTypeClassPl",
|
||||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
items: [],
|
||||||
deployments: { label: "SW5E.ItemTypeDeploymentPl", items: [], hasActions: false, dataset: {type: "deployment"}, isDeployment: true },
|
hasActions: false,
|
||||||
deploymentfeatures: { label: "SW5E.ItemTypeDeploymentFeaturePl", items: [], hasActions: true, dataset: {type: "deploymentfeature"}, isDeploymentfeature: true },
|
dataset: {type: "class"},
|
||||||
ventures: { label: "SW5E.ItemTypeVenturePl", items: [], hasActions: false, dataset: {type: "venture"}, isVenture: true },
|
isClass: true
|
||||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
},
|
||||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
classfeatures: {
|
||||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
label: "SW5E.ItemTypeClassFeats",
|
||||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
items: [],
|
||||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
hasActions: true,
|
||||||
active: { label: "SW5E.FeatureActive", items: [], hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
dataset: {type: "classfeature"},
|
||||||
|
isClassfeature: true
|
||||||
|
},
|
||||||
|
archetype: {
|
||||||
|
label: "SW5E.ItemTypeArchetype",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "archetype"},
|
||||||
|
isArchetype: true
|
||||||
|
},
|
||||||
|
deployments: {
|
||||||
|
label: "SW5E.ItemTypeDeploymentPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "deployment"},
|
||||||
|
isDeployment: true
|
||||||
|
},
|
||||||
|
deploymentfeatures: {
|
||||||
|
label: "SW5E.ItemTypeDeploymentFeaturePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "deploymentfeature"},
|
||||||
|
isDeploymentfeature: true
|
||||||
|
},
|
||||||
|
ventures: {
|
||||||
|
label: "SW5E.ItemTypeVenturePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "venture"},
|
||||||
|
isVenture: true
|
||||||
|
},
|
||||||
|
species: {
|
||||||
|
label: "SW5E.ItemTypeSpecies",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "species"},
|
||||||
|
isSpecies: true
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
label: "SW5E.ItemTypeBackground",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "background"},
|
||||||
|
isBackground: true
|
||||||
|
},
|
||||||
|
fightingstyles: {
|
||||||
|
label: "SW5E.ItemTypeFightingStylePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingstyle"},
|
||||||
|
isFightingstyle: true
|
||||||
|
},
|
||||||
|
fightingmasteries: {
|
||||||
|
label: "SW5E.ItemTypeFightingMasteryPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingmastery"},
|
||||||
|
isFightingmastery: true
|
||||||
|
},
|
||||||
|
lightsaberforms: {
|
||||||
|
label: "SW5E.ItemTypeLightsaberFormPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "lightsaberform"},
|
||||||
|
isLightsaberform: true
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
label: "SW5E.FeatureActive",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
||||||
};
|
};
|
||||||
for (let f of feats) {
|
for (let f of feats) {
|
||||||
|
@ -207,8 +299,7 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
||||||
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
||||||
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const isActive = getProperty(item.data, "equipped");
|
const isActive = getProperty(item.data, "equipped");
|
||||||
item.toggleClass = isActive ? "active" : "";
|
item.toggleClass = isActive ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
||||||
|
@ -231,19 +322,21 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
// html.find(".currency-convert").click(this._onConvertCurrency.bind(this));
|
// html.find(".currency-convert").click(this._onConvertCurrency.bind(this));
|
||||||
|
|
||||||
// Item State Toggling
|
// Item State Toggling
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
|
|
||||||
// Short and Long Rest
|
// Short and Long Rest
|
||||||
html.find('.short-rest').click(this._onShortRest.bind(this));
|
html.find(".short-rest").click(this._onShortRest.bind(this));
|
||||||
html.find('.long-rest').click(this._onLongRest.bind(this));
|
html.find(".long-rest").click(this._onLongRest.bind(this));
|
||||||
|
|
||||||
// Rollable sheet actions
|
// Rollable sheet actions
|
||||||
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
||||||
|
|
||||||
// Send Languages to Chat onClick
|
// Send Languages to Chat onClick
|
||||||
html.find('[data-options="share-languages"]').click(event => {
|
html.find('[data-options="share-languages"]').click((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let langs = this.actor.data.data.traits.languages.value.map(l => CONFIG.SW5E.languages[l] || l).join(", ");
|
let langs = this.actor.data.data.traits.languages.value
|
||||||
|
.map((l) => CONFIG.SW5E.languages[l] || l)
|
||||||
|
.join(", ");
|
||||||
let custom = this.actor.data.data.traits.languages.custom;
|
let custom = this.actor.data.data.traits.languages.custom;
|
||||||
if (custom) langs += ", " + custom.replace(/;/g, ",");
|
if (custom) langs += ", " + custom.replace(/;/g, ",");
|
||||||
let content = `
|
let content = `
|
||||||
|
@ -279,9 +372,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Item Delete Confirmation
|
// Item Delete Confirmation
|
||||||
html.find('.item-delete').off("click");
|
html.find(".item-delete").off("click");
|
||||||
html.find('.item-delete').click(event => {
|
html.find(".item-delete").click((event) => {
|
||||||
let li = $(event.currentTarget).parents('.item');
|
let li = $(event.currentTarget).parents(".item");
|
||||||
let itemId = li.attr("data-item-id");
|
let itemId = li.attr("data-item-id");
|
||||||
let item = this.actor.items.get(itemId);
|
let item = this.actor.items.get(itemId);
|
||||||
new Dialog({
|
new Dialog({
|
||||||
|
@ -290,17 +383,17 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
buttons: {
|
buttons: {
|
||||||
Yes: {
|
Yes: {
|
||||||
icon: '<i class="fa fa-check"></i>',
|
icon: '<i class="fa fa-check"></i>',
|
||||||
label: 'Yes',
|
label: "Yes",
|
||||||
callback: dlg => {
|
callback: (dlg) => {
|
||||||
this.actor.deleteOwnedItem(itemId);
|
this.actor.deleteOwnedItem(itemId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: 'No'
|
label: "No"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
default: "cancel"
|
||||||
default: 'cancel'
|
|
||||||
}).render(true);
|
}).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -325,7 +418,6 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle toggling the state of an Owned Item within the Actor
|
* Handle toggling the state of an Owned Item within the Actor
|
||||||
* @param {Event} event The triggering click event
|
* @param {Event} event The triggering click event
|
||||||
|
@ -369,10 +461,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Increment the number of class levels of a character instead of creating a new item
|
// Increment the number of class levels of a character instead of creating a new item
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
const cls = this.actor.itemTypes.class.find(c => c.name === itemData.name);
|
const cls = this.actor.itemTypes.class.find((c) => c.name === itemData.name);
|
||||||
let priorLevel = cls?.data.data.levels ?? 0;
|
let priorLevel = cls?.data.data.levels ?? 0;
|
||||||
if (!!cls) {
|
if (!!cls) {
|
||||||
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
||||||
|
@ -457,9 +548,9 @@ async function addFavorites(app, html, data) {
|
||||||
value: data.actor.data.powers.power9.value,
|
value: data.actor.data.powers.power9.value,
|
||||||
max: data.actor.data.powers.power9.max
|
max: data.actor.data.powers.power9.max
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let powerCount = 0
|
let powerCount = 0;
|
||||||
let items = data.actor.items;
|
let items = data.actor.items;
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.type == "class") continue;
|
if (item.type == "class") continue;
|
||||||
|
@ -470,24 +561,28 @@ async function addFavorites(app, html, data) {
|
||||||
}
|
}
|
||||||
let isFav = item.flags.favtab.isFavourite;
|
let isFav = item.flags.favtab.isFavourite;
|
||||||
if (app.options.editable) {
|
if (app.options.editable) {
|
||||||
let favBtn = $(`<a class="item-control item-toggle item-fav ${isFav ? "active" : ""}" data-fav="${isFav}" title="${isFav ? "Remove from Favourites" : "Add to Favourites"}"><i class="fas fa-star"></i></a>`);
|
let favBtn = $(
|
||||||
favBtn.click(ev => {
|
`<a class="item-control item-toggle item-fav ${isFav ? "active" : ""}" data-fav="${isFav}" title="${
|
||||||
|
isFav ? "Remove from Favourites" : "Add to Favourites"
|
||||||
|
}"><i class="fas fa-star"></i></a>`
|
||||||
|
);
|
||||||
|
favBtn.click((ev) => {
|
||||||
app.actor.items.get(item.data._id).update({
|
app.actor.items.get(item.data._id).update({
|
||||||
"flags.favtab.isFavourite": !item.flags.favtab.isFavourite
|
"flags.favtab.isFavourite": !item.flags.favtab.isFavourite
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
html.find(`.item[data-item-id="${item.data._id}"]`).find('.item-controls').prepend(favBtn);
|
html.find(`.item[data-item-id="${item.data._id}"]`).find(".item-controls").prepend(favBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFav) {
|
if (isFav) {
|
||||||
item.powerComps = "";
|
item.powerComps = "";
|
||||||
if (item.data.components) {
|
if (item.data.components) {
|
||||||
let comps = item.data.components;
|
let comps = item.data.components;
|
||||||
let v = (comps.vocal) ? "V" : "";
|
let v = comps.vocal ? "V" : "";
|
||||||
let s = (comps.somatic) ? "S" : "";
|
let s = comps.somatic ? "S" : "";
|
||||||
let m = (comps.material) ? "M" : "";
|
let m = comps.material ? "M" : "";
|
||||||
let c = !!(comps.concentration);
|
let c = !!comps.concentration;
|
||||||
let r = !!(comps.ritual);
|
let r = !!comps.ritual;
|
||||||
item.powerComps = `${v}${s}${m}`;
|
item.powerComps = `${v}${s}${m}`;
|
||||||
item.powerCon = c;
|
item.powerCon = c;
|
||||||
item.powerRit = r;
|
item.powerRit = r;
|
||||||
|
@ -495,15 +590,15 @@ async function addFavorites(app, html, data) {
|
||||||
|
|
||||||
item.editable = app.options.editable;
|
item.editable = app.options.editable;
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'feat':
|
case "feat":
|
||||||
if (item.flags.favtab.sort === undefined) {
|
if (item.flags.favtab.sort === undefined) {
|
||||||
item.flags.favtab.sort = (favFeats.count + 1) * 100000; // initial sort key if not present
|
item.flags.favtab.sort = (favFeats.count + 1) * 100000; // initial sort key if not present
|
||||||
}
|
}
|
||||||
favFeats.push(item);
|
favFeats.push(item);
|
||||||
break;
|
break;
|
||||||
case 'power':
|
case "power":
|
||||||
if (item.data.preparation.mode) {
|
if (item.data.preparation.mode) {
|
||||||
item.powerPrepMode = ` (${CONFIG.SW5E.powerPreparationModes[item.data.preparation.mode]})`
|
item.powerPrepMode = ` (${CONFIG.SW5E.powerPreparationModes[item.data.preparation.mode]})`;
|
||||||
}
|
}
|
||||||
if (item.data.level) {
|
if (item.data.level) {
|
||||||
favPowers[item.data.level].powers.push(item);
|
favPowers[item.data.level].powers.push(item);
|
||||||
|
@ -529,60 +624,60 @@ async function addFavorites(app, html, data) {
|
||||||
// html.find('.favourite .item-controls').css('flex', '0 0 22px');
|
// html.find('.favourite .item-controls').css('flex', '0 0 22px');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let tabContainer = html.find('.favtabtarget');
|
let tabContainer = html.find(".favtabtarget");
|
||||||
data.favItems = favItems.length > 0 ? favItems.sort((a, b) => (a.flags.favtab.sort) - (b.flags.favtab.sort)) : false;
|
data.favItems = favItems.length > 0 ? favItems.sort((a, b) => a.flags.favtab.sort - b.flags.favtab.sort) : false;
|
||||||
data.favFeats = favFeats.length > 0 ? favFeats.sort((a, b) => (a.flags.favtab.sort) - (b.flags.favtab.sort)) : false;
|
data.favFeats = favFeats.length > 0 ? favFeats.sort((a, b) => a.flags.favtab.sort - b.flags.favtab.sort) : false;
|
||||||
data.favPowers = powerCount > 0 ? favPowers : false;
|
data.favPowers = powerCount > 0 ? favPowers : false;
|
||||||
data.editable = app.options.editable;
|
data.editable = app.options.editable;
|
||||||
|
|
||||||
await loadTemplates(['systems/sw5e/templates/actors/newActor/item.hbs']);
|
await loadTemplates(["systems/sw5e/templates/actors/newActor/item.hbs"]);
|
||||||
let favtabHtml = $(await renderTemplate('systems/sw5e/templates/actors/newActor/template.hbs', data));
|
let favtabHtml = $(await renderTemplate("systems/sw5e/templates/actors/newActor/template.hbs", data));
|
||||||
favtabHtml.find('.item-name h4').click(event => app._onItemSummary(event));
|
favtabHtml.find(".item-name h4").click((event) => app._onItemSummary(event));
|
||||||
|
|
||||||
if (app.options.editable) {
|
if (app.options.editable) {
|
||||||
favtabHtml.find('.item-image').click(ev => app._onItemRoll(ev));
|
favtabHtml.find(".item-image").click((ev) => app._onItemRoll(ev));
|
||||||
let handler = ev => app._onDragStart(ev);
|
let handler = (ev) => app._onDragStart(ev);
|
||||||
favtabHtml.find('.item').each((i, li) => {
|
favtabHtml.find(".item").each((i, li) => {
|
||||||
if (li.classList.contains("inventory-header")) return;
|
if (li.classList.contains("inventory-header")) return;
|
||||||
li.setAttribute("draggable", true);
|
li.setAttribute("draggable", true);
|
||||||
li.addEventListener("dragstart", handler, false);
|
li.addEventListener("dragstart", handler, false);
|
||||||
});
|
});
|
||||||
//favtabHtml.find('.item-toggle').click(event => app._onToggleItem(event));
|
//favtabHtml.find('.item-toggle').click(event => app._onToggleItem(event));
|
||||||
favtabHtml.find('.item-edit').click(ev => {
|
favtabHtml.find(".item-edit").click((ev) => {
|
||||||
let itemId = $(ev.target).parents('.item')[0].dataset.itemId;
|
let itemId = $(ev.target).parents(".item")[0].dataset.itemId;
|
||||||
app.actor.items.get(itemId).sheet.render(true);
|
app.actor.items.get(itemId).sheet.render(true);
|
||||||
});
|
});
|
||||||
favtabHtml.find('.item-fav').click(ev => {
|
favtabHtml.find(".item-fav").click((ev) => {
|
||||||
let itemId = $(ev.target).parents('.item')[0].dataset.itemId;
|
let itemId = $(ev.target).parents(".item")[0].dataset.itemId;
|
||||||
let val = !app.actor.items.get(itemId).data.flags.favtab.isFavourite
|
let val = !app.actor.items.get(itemId).data.flags.favtab.isFavourite;
|
||||||
app.actor.items.get(itemId).update({
|
app.actor.items.get(itemId).update({
|
||||||
"flags.favtab.isFavourite": val
|
"flags.favtab.isFavourite": val
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sorting
|
// Sorting
|
||||||
favtabHtml.find('.item').on('drop', ev => {
|
favtabHtml.find(".item").on("drop", (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
let dropData = JSON.parse(ev.originalEvent.dataTransfer.getData('text/plain'));
|
let dropData = JSON.parse(ev.originalEvent.dataTransfer.getData("text/plain"));
|
||||||
// if (dropData.actorId !== app.actor.id || dropData.data.type === 'power') return;
|
// if (dropData.actorId !== app.actor.id || dropData.data.type === 'power') return;
|
||||||
if (dropData.actorId !== app.actor.id) return;
|
if (dropData.actorId !== app.actor.id) return;
|
||||||
let list = null;
|
let list = null;
|
||||||
if (dropData.data.type === 'feat') list = favFeats;
|
if (dropData.data.type === "feat") list = favFeats;
|
||||||
else list = favItems;
|
else list = favItems;
|
||||||
let dragSource = list.find(i => i.data._id === dropData.data._id);
|
let dragSource = list.find((i) => i.data._id === dropData.data._id);
|
||||||
let siblings = list.filter(i => i.data._id !== dropData.data._id);
|
let siblings = list.filter((i) => i.data._id !== dropData.data._id);
|
||||||
let targetId = ev.target.closest('.item').dataset.itemId;
|
let targetId = ev.target.closest(".item").dataset.itemId;
|
||||||
let dragTarget = siblings.find(s => s.data._id === targetId);
|
let dragTarget = siblings.find((s) => s.data._id === targetId);
|
||||||
|
|
||||||
if (dragTarget === undefined) return;
|
if (dragTarget === undefined) return;
|
||||||
const sortUpdates = SortingHelpers.performIntegerSort(dragSource, {
|
const sortUpdates = SortingHelpers.performIntegerSort(dragSource, {
|
||||||
target: dragTarget,
|
target: dragTarget,
|
||||||
siblings: siblings,
|
siblings: siblings,
|
||||||
sortKey: 'flags.favtab.sort'
|
sortKey: "flags.favtab.sort"
|
||||||
});
|
});
|
||||||
const updateData = sortUpdates.map(u => {
|
const updateData = sortUpdates.map((u) => {
|
||||||
const update = u.update;
|
const update = u.update;
|
||||||
update._id = u.target.data._id;
|
update._id = u.target.data._id;
|
||||||
return update;
|
return update;
|
||||||
|
@ -611,46 +706,44 @@ async function addSubTabs(app, html, data) {
|
||||||
if (data.options.subTabs == null) {
|
if (data.options.subTabs == null) {
|
||||||
//let subTabs = []; //{subgroup: '', target: '', active: false}
|
//let subTabs = []; //{subgroup: '', target: '', active: false}
|
||||||
data.options.subTabs = {};
|
data.options.subTabs = {};
|
||||||
html.find('[data-subgroup-selection] [data-subgroup]').each((idx, el) => {
|
html.find("[data-subgroup-selection] [data-subgroup]").each((idx, el) => {
|
||||||
let subgroup = el.getAttribute('data-subgroup');
|
let subgroup = el.getAttribute("data-subgroup");
|
||||||
let target = el.getAttribute('data-target');
|
let target = el.getAttribute("data-target");
|
||||||
let targetObj = {target: target, active: el.classList.contains("active")}
|
let targetObj = {target: target, active: el.classList.contains("active")};
|
||||||
if (data.options.subTabs.hasOwnProperty(subgroup)) {
|
if (data.options.subTabs.hasOwnProperty(subgroup)) {
|
||||||
data.options.subTabs[subgroup].push(targetObj);
|
data.options.subTabs[subgroup].push(targetObj);
|
||||||
} else {
|
} else {
|
||||||
data.options.subTabs[subgroup] = [];
|
data.options.subTabs[subgroup] = [];
|
||||||
data.options.subTabs[subgroup].push(targetObj);
|
data.options.subTabs[subgroup].push(targetObj);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const group in data.options.subTabs) {
|
for (const group in data.options.subTabs) {
|
||||||
data.options.subTabs[group].forEach(tab => {
|
data.options.subTabs[group].forEach((tab) => {
|
||||||
if (tab.active) {
|
if (tab.active) {
|
||||||
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass('active');
|
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass("active");
|
||||||
} else {
|
} else {
|
||||||
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass('active');
|
html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass("active");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
html.find('[data-subgroup-selection]').children().on('click', event => {
|
html.find("[data-subgroup-selection]")
|
||||||
let subgroup = event.target.closest('[data-subgroup]').getAttribute('data-subgroup');
|
.children()
|
||||||
let target = event.target.closest('[data-target]').getAttribute('data-target');
|
.on("click", (event) => {
|
||||||
html.find(`[data-subgroup=${subgroup}]`).removeClass('active');
|
let subgroup = event.target.closest("[data-subgroup]").getAttribute("data-subgroup");
|
||||||
html.find(`[data-subgroup=${subgroup}][data-target=${target}]`).addClass('active');
|
let target = event.target.closest("[data-target]").getAttribute("data-target");
|
||||||
let tabId = data.options.subTabs[subgroup].find(tab => {
|
html.find(`[data-subgroup=${subgroup}]`).removeClass("active");
|
||||||
return tab.target == target
|
html.find(`[data-subgroup=${subgroup}][data-target=${target}]`).addClass("active");
|
||||||
|
let tabId = data.options.subTabs[subgroup].find((tab) => {
|
||||||
|
return tab.target == target;
|
||||||
});
|
});
|
||||||
data.options.subTabs[subgroup].map(el => {
|
data.options.subTabs[subgroup].map((el) => {
|
||||||
el.active = el.target == target;
|
el.active = el.target == target;
|
||||||
return el;
|
return el;
|
||||||
})
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hooks.on("renderActorSheet5eCharacterNew", (app, html, data) => {
|
Hooks.on("renderActorSheet5eCharacterNew", (app, html, data) => {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
|
@ -17,11 +16,13 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["sw5e", "sheet", "actor", "npc"],
|
classes: ["sw5e", "sheet", "actor", "npc"],
|
||||||
width: 800,
|
width: 800,
|
||||||
tabs: [{
|
tabs: [
|
||||||
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,28 +38,41 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
label: game.i18n.localize("SW5E.AttackPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "weapon", "weapon-type": "natural"}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [forcepowers, techpowers, other] = data.items.reduce((arr, item) => {
|
let [forcepowers, techpowers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
item.hasUses = item.data.uses && item.data.uses.max > 0;
|
||||||
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
item.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
||||||
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
||||||
else arr[2].push(item);
|
else arr[2].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], []]);
|
},
|
||||||
|
[[], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
||||||
|
@ -75,8 +89,7 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -107,7 +120,6 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
@ -145,4 +157,3 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eStarship extends ActorSheet5e {
|
export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
|
@ -17,11 +16,13 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
classes: ["sw5e", "sheet", "actor", "starship"],
|
classes: ["sw5e", "sheet", "actor", "starship"],
|
||||||
width: 800,
|
width: 800,
|
||||||
tabs: [{
|
tabs: [
|
||||||
|
{
|
||||||
navSelector: ".root-tabs",
|
navSelector: ".root-tabs",
|
||||||
contentSelector: ".sheet-body",
|
contentSelector: ".sheet-body",
|
||||||
initial: "attributes"
|
initial: "attributes"
|
||||||
}],
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,29 +33,47 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.ItemTypeWeaponPl"), items: [], hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "weapon", "weapon-type": "natural"}
|
||||||
|
},
|
||||||
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.StarshipEquipment"), items: [], dataset: {type: "equipment"}},
|
equipment: {label: game.i18n.localize("SW5E.StarshipEquipment"), items: [], dataset: {type: "equipment"}},
|
||||||
starshipfeatures: { label: game.i18n.localize("SW5E.StarshipfeaturePl"), items: [], hasActions: true, dataset: {type: "starshipfeature"} },
|
starshipfeatures: {
|
||||||
starshipmods: { label: game.i18n.localize("SW5E.StarshipmodPl"), items: [], hasActions: false, dataset: {type: "starshipmod"} }
|
label: game.i18n.localize("SW5E.StarshipfeaturePl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "starshipfeature"}
|
||||||
|
},
|
||||||
|
starshipmods: {
|
||||||
|
label: game.i18n.localize("SW5E.StarshipmodPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "starshipmod"}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [forcepowers, techpowers, other] = data.items.reduce((arr, item) => {
|
let [forcepowers, techpowers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
item.hasUses = item.data.uses && item.data.uses.max > 0;
|
||||||
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
item.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[0].push(item);
|
||||||
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item);
|
||||||
else arr[2].push(item);
|
else arr[2].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], []]);
|
},
|
||||||
|
[[], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook);
|
||||||
|
@ -71,14 +90,11 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else if (item.type === "starshipfeature") {
|
||||||
else if ( item.type === "starshipfeature" ) {
|
|
||||||
features.starshipfeatures.items.push(item);
|
features.starshipfeatures.items.push(item);
|
||||||
}
|
} else if (item.type === "starshipmod") {
|
||||||
else if ( item.type === "starshipmod" ) {
|
|
||||||
features.starshipmods.items.push(item);
|
features.starshipmods.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -87,7 +103,6 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
// data.techPowerbook = techPowerbook;
|
// data.techPowerbook = techPowerbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
|
@ -115,7 +130,6 @@ export default class ActorSheet5eStarship extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
|
|
@ -25,13 +25,12 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cargo entry for a vehicle Actor.
|
* Creates a new cargo entry for a vehicle Actor.
|
||||||
*/
|
*/
|
||||||
static get newCargo() {
|
static get newCargo() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: "",
|
||||||
quantity: 1
|
quantity: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeEncumbrance(totalWeight, actorData) {
|
_computeEncumbrance(totalWeight, actorData) {
|
||||||
|
|
||||||
// Compute currency weight
|
// Compute currency weight
|
||||||
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
||||||
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||||
|
@ -75,25 +73,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareCrewedItem(item) {
|
_prepareCrewedItem(item) {
|
||||||
|
|
||||||
// Determine crewed status
|
// Determine crewed status
|
||||||
const isCrewed = item.data.crewed;
|
const isCrewed = item.data.crewed;
|
||||||
item.toggleClass = isCrewed ? 'active' : '';
|
item.toggleClass = isCrewed ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`);
|
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`);
|
||||||
|
|
||||||
// Handle crew actions
|
// Handle crew actions
|
||||||
if (item.type === 'feat' && item.data.activation.type === 'crew') {
|
if (item.type === "feat" && item.data.activation.type === "crew") {
|
||||||
item.crew = item.data.activation.cost;
|
item.crew = item.data.activation.cost;
|
||||||
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`);
|
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`);
|
||||||
if (item.data.cover === .5) item.cover = '½';
|
if (item.data.cover === 0.5) item.cover = "½";
|
||||||
else if (item.data.cover === .75) item.cover = '¾';
|
else if (item.data.cover === 0.75) item.cover = "¾";
|
||||||
else if (item.data.cover === null) item.cover = '—';
|
else if (item.data.cover === null) item.cover = "—";
|
||||||
if (item.crew < 1 || item.crew === null) item.crew = '—';
|
if (item.crew < 1 || item.crew === null) item.crew = "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare vehicle weapons
|
// Prepare vehicle weapons
|
||||||
if (item.type === 'equipment' || item.type === 'weapon') {
|
if (item.type === "equipment" || item.type === "weapon") {
|
||||||
item.threshold = item.data.hp.dt ? item.data.hp.dt : '—';
|
item.threshold = item.data.hp.dt ? item.data.hp.dt : "—";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,111 +101,125 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
const cargoColumns = [{
|
const cargoColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "quantity",
|
||||||
}];
|
editable: "Number"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const equipmentColumns = [{
|
const equipmentColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity'
|
css: "item-qty",
|
||||||
}, {
|
property: "data.quantity"
|
||||||
label: game.i18n.localize('SW5E.AC'),
|
},
|
||||||
css: 'item-ac',
|
{
|
||||||
property: 'data.armor.value'
|
label: game.i18n.localize("SW5E.AC"),
|
||||||
}, {
|
css: "item-ac",
|
||||||
label: game.i18n.localize('SW5E.HP'),
|
property: "data.armor.value"
|
||||||
css: 'item-hp',
|
},
|
||||||
property: 'data.hp.value',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.HP"),
|
||||||
}, {
|
css: "item-hp",
|
||||||
label: game.i18n.localize('SW5E.Threshold'),
|
property: "data.hp.value",
|
||||||
css: 'item-threshold',
|
editable: "Number"
|
||||||
property: 'threshold'
|
},
|
||||||
}];
|
{
|
||||||
|
label: game.i18n.localize("SW5E.Threshold"),
|
||||||
|
css: "item-threshold",
|
||||||
|
property: "threshold"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const features = {
|
const features = {
|
||||||
actions: {
|
actions: {
|
||||||
label: game.i18n.localize('SW5E.ActionPl'),
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'feat', 'activation.type': 'crew'},
|
dataset: {"type": "feat", "activation.type": "crew"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
{
|
||||||
css: 'item-crew',
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
property: 'crew'
|
css: "item-crew",
|
||||||
}, {
|
property: "crew"
|
||||||
label: game.i18n.localize('SW5E.Cover'),
|
},
|
||||||
css: 'item-cover',
|
{
|
||||||
property: 'cover'
|
label: game.i18n.localize("SW5E.Cover"),
|
||||||
}]
|
css: "item-cover",
|
||||||
|
property: "cover"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
equipment: {
|
equipment: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeEquipment'),
|
label: game.i18n.localize("SW5E.ItemTypeEquipment"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'equipment', 'armor.type': 'vehicle'},
|
dataset: {"type": "equipment", "armor.type": "vehicle"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
},
|
},
|
||||||
passive: {
|
passive: {
|
||||||
label: game.i18n.localize('SW5E.Features'),
|
label: game.i18n.localize("SW5E.Features"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat'}
|
dataset: {type: "feat"}
|
||||||
},
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
label: game.i18n.localize('SW5E.ReactionPl'),
|
label: game.i18n.localize("SW5E.ReactionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat', 'activation.type': 'reaction'}
|
dataset: {"type": "feat", "activation.type": "reaction"}
|
||||||
},
|
},
|
||||||
weapons: {
|
weapons: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeWeaponPl'),
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'weapon', 'weapon-type': 'siege'},
|
dataset: {"type": "weapon", "weapon-type": "siege"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cargo = {
|
const cargo = {
|
||||||
crew: {
|
crew: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
items: data.data.cargo.crew,
|
items: data.data.cargo.crew,
|
||||||
css: 'cargo-row crew',
|
css: "cargo-row crew",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'crew'},
|
dataset: {type: "crew"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
passengers: {
|
passengers: {
|
||||||
label: game.i18n.localize('SW5E.VehiclePassengers'),
|
label: game.i18n.localize("SW5E.VehiclePassengers"),
|
||||||
items: data.data.cargo.passengers,
|
items: data.data.cargo.passengers,
|
||||||
css: 'cargo-row passengers',
|
css: "cargo-row passengers",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'passengers'},
|
dataset: {type: "passengers"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
cargo: {
|
cargo: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCargo'),
|
label: game.i18n.localize("SW5E.VehicleCargo"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'loot'},
|
dataset: {type: "loot"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "data.quantity",
|
||||||
}, {
|
editable: "Number"
|
||||||
label: game.i18n.localize('SW5E.Price'),
|
},
|
||||||
css: 'item-price',
|
{
|
||||||
property: 'data.price',
|
label: game.i18n.localize("SW5E.Price"),
|
||||||
editable: 'Number'
|
css: "item-price",
|
||||||
}, {
|
property: "data.price",
|
||||||
label: game.i18n.localize('SW5E.Weight'),
|
editable: "Number"
|
||||||
css: 'item-weight',
|
},
|
||||||
property: 'data.weight',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.Weight"),
|
||||||
}]
|
css: "item-weight",
|
||||||
|
property: "data.weight",
|
||||||
|
editable: "Number"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -234,8 +245,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
features.equipment.items.push(item);
|
features.equipment.items.push(item);
|
||||||
break;
|
break;
|
||||||
case "feat":
|
case "feat":
|
||||||
if ( !item.data.activation.type || (item.data.activation.type === "none") ) features.passive.items.push(item);
|
if (!item.data.activation.type || item.data.activation.type === "none")
|
||||||
else if (item.data.activation.type === 'reaction') features.reactions.items.push(item);
|
features.passive.items.push(item);
|
||||||
|
else if (item.data.activation.type === "reaction") features.reactions.items.push(item);
|
||||||
else features.actions.items.push(item);
|
else features.actions.items.push(item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -259,21 +271,21 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
html.find('.item-hp input')
|
html.find(".item-hp input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onHPChange.bind(this));
|
.change(this._onHPChange.bind(this));
|
||||||
|
|
||||||
html.find('.item:not(.cargo-row) input[data-property]')
|
html.find(".item:not(.cargo-row) input[data-property]")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onEditInSheet.bind(this));
|
.change(this._onEditInSheet.bind(this));
|
||||||
|
|
||||||
html.find('.cargo-row input')
|
html.find(".cargo-row input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onCargoRowChange.bind(this));
|
.change(this._onCargoRowChange.bind(this));
|
||||||
|
|
||||||
if (this.actor.data.data.attributes.actions.stations) {
|
if (this.actor.data.data.attributes.actions.stations) {
|
||||||
html.find('.counter.actions, .counter.action-thresholds').hide();
|
html.find(".counter.actions, .counter.action-thresholds").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +300,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
_onCargoRowChange(event) {
|
_onCargoRowChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const row = target.closest('.item');
|
const row = target.closest(".item");
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const property = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const property = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
|
|
||||||
// Get the cargo entry
|
// Get the cargo entry
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
||||||
|
@ -298,10 +310,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
|
|
||||||
// Update the cargo value
|
// Update the cargo value
|
||||||
const key = target.dataset.property || 'name';
|
const key = target.dataset.property || "name";
|
||||||
const type = target.dataset.dtype;
|
const type = target.dataset.dtype;
|
||||||
let value = target.value;
|
let value = target.value;
|
||||||
if (type === 'Number') value = Number(value);
|
if (type === "Number") value = Number(value);
|
||||||
entry[key] = value;
|
entry[key] = value;
|
||||||
|
|
||||||
// Perform the Actor update
|
// Perform the Actor update
|
||||||
|
@ -318,14 +330,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onEditInSheet(event) {
|
_onEditInSheet(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const property = event.currentTarget.dataset.property;
|
const property = event.currentTarget.dataset.property;
|
||||||
const type = event.currentTarget.dataset.dtype;
|
const type = event.currentTarget.dataset.dtype;
|
||||||
let value = event.currentTarget.value;
|
let value = event.currentTarget.value;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Number': value = parseInt(value); break;
|
case "Number":
|
||||||
case 'Boolean': value = value === 'true'; break;
|
value = parseInt(value);
|
||||||
|
break;
|
||||||
|
case "Boolean":
|
||||||
|
value = value === "true";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return item.update({[`${property}`]: value});
|
return item.update({[`${property}`]: value});
|
||||||
}
|
}
|
||||||
|
@ -342,7 +358,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const type = target.dataset.type;
|
const type = target.dataset.type;
|
||||||
if (type === 'crew' || type === 'passengers') {
|
if (type === "crew" || type === "passengers") {
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
||||||
cargo.push(this.constructor.newCargo);
|
cargo.push(this.constructor.newCargo);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
|
@ -360,10 +376,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onItemDelete(event) {
|
_onItemDelete(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const row = event.currentTarget.closest('.item');
|
const row = event.currentTarget.closest(".item");
|
||||||
if (row.classList.contains('cargo-row')) {
|
if (row.classList.contains("cargo-row")) {
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const type = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const type = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
}
|
}
|
||||||
|
@ -376,7 +392,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
||||||
const isCargo = cargoTypes.includes(itemData.type) && (this._tabs[0].active === "cargo");
|
const isCargo = cargoTypes.includes(itemData.type) && this._tabs[0].active === "cargo";
|
||||||
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
||||||
return super._onDropItemCreate(itemData);
|
return super._onDropItemCreate(itemData);
|
||||||
}
|
}
|
||||||
|
@ -391,11 +407,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onHPChange(event) {
|
_onHPChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
||||||
event.currentTarget.value = hp;
|
event.currentTarget.value = hp;
|
||||||
return item.update({'data.hp.value': hp});
|
return item.update({"data.hp.value": hp});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -408,9 +424,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onToggleItem(event) {
|
_onToggleItem(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const crewed = !!item.data.data.crewed;
|
const crewed = !!item.data.data.crewed;
|
||||||
return item.update({'data.crewed': !crewed});
|
return item.update({"data.crewed": !crewed});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import ActorHitDiceConfig from "../../../apps/hit-dice-config.js";
|
||||||
import ActorMovementConfig from "../../../apps/movement-config.js";
|
import ActorMovementConfig from "../../../apps/movement-config.js";
|
||||||
import ActorSensesConfig from "../../../apps/senses-config.js";
|
import ActorSensesConfig from "../../../apps/senses-config.js";
|
||||||
import ActorTypeConfig from "../../../apps/actor-type.js";
|
import ActorTypeConfig from "../../../apps/actor-type.js";
|
||||||
import {SW5E} from '../../../config.js';
|
import {SW5E} from "../../../config.js";
|
||||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
get template() {
|
||||||
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/oldActor/limited-sheet.html";
|
if (!game.user.isGM && this.actor.limited) return "systems/sw5e/templates/actors/oldActor/limited-sheet.html";
|
||||||
|
@ -65,7 +64,6 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Basic data
|
// Basic data
|
||||||
let isOwner = this.actor.isOwner;
|
let isOwner = this.actor.isOwner;
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -77,7 +75,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
isCharacter: this.actor.type === "character",
|
isCharacter: this.actor.type === "character",
|
||||||
isNPC: this.actor.type === "npc",
|
isNPC: this.actor.type === "npc",
|
||||||
isStarship: this.actor.type === "starship",
|
isStarship: this.actor.type === "starship",
|
||||||
isVehicle: this.actor.type === 'vehicle',
|
isVehicle: this.actor.type === "vehicle",
|
||||||
config: CONFIG.SW5E,
|
config: CONFIG.SW5E,
|
||||||
rollData: this.actor.getRollData.bind(this.actor)
|
rollData: this.actor.getRollData.bind(this.actor)
|
||||||
};
|
};
|
||||||
|
@ -132,7 +130,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
data.effects = prepareActiveEffectCategories(this.actor.effects);
|
||||||
|
|
||||||
// Return data to the sheet
|
// Return data to the sheet
|
||||||
return data
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -151,31 +149,35 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let speeds = [
|
let speeds = [
|
||||||
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
|
||||||
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
|
||||||
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
|
[
|
||||||
|
movement.fly,
|
||||||
|
`${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` +
|
||||||
|
(movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")
|
||||||
|
],
|
||||||
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
|
||||||
]
|
];
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and sort speeds on their values
|
// Filter and sort speeds on their values
|
||||||
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
|
speeds = speeds.filter((s) => !!s[0]).sort((a, b) => b[0] - a[0]);
|
||||||
|
|
||||||
// Case 1: Largest as primary
|
// Case 1: Largest as primary
|
||||||
if (largestPrimary) {
|
if (largestPrimary) {
|
||||||
let primary = speeds.shift();
|
let primary = speeds.shift();
|
||||||
return {
|
return {
|
||||||
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
|
||||||
special: speeds.map(s => s[1]).join(", ")
|
special: speeds.map((s) => s[1]).join(", ")
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: Walk as primary
|
// Case 2: Walk as primary
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
primary: `${movement.walk || 0} ${movement.units}`,
|
primary: `${movement.walk || 0} ${movement.units}`,
|
||||||
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
|
special: speeds.length ? speeds.map((s) => s[1]).join(", ") : ""
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +187,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const senses = actorData.data.attributes.senses || {};
|
const senses = actorData.data.attributes.senses || {};
|
||||||
const tags = {};
|
const tags = {};
|
||||||
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[k] ?? 0
|
const v = senses[k] ?? 0;
|
||||||
if (v === 0) continue;
|
if (v === 0) continue;
|
||||||
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`;
|
||||||
}
|
}
|
||||||
|
@ -202,14 +204,14 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
*/
|
*/
|
||||||
_prepareTraits(traits) {
|
_prepareTraits(traits) {
|
||||||
const map = {
|
const map = {
|
||||||
"dr": CONFIG.SW5E.damageResistanceTypes,
|
dr: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"di": CONFIG.SW5E.damageResistanceTypes,
|
di: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"dv": CONFIG.SW5E.damageResistanceTypes,
|
dv: CONFIG.SW5E.damageResistanceTypes,
|
||||||
"ci": CONFIG.SW5E.conditionTypes,
|
ci: CONFIG.SW5E.conditionTypes,
|
||||||
"languages": CONFIG.SW5E.languages,
|
languages: CONFIG.SW5E.languages,
|
||||||
"armorProf": CONFIG.SW5E.armorProficiencies,
|
armorProf: CONFIG.SW5E.armorProficiencies,
|
||||||
"weaponProf": CONFIG.SW5E.weaponProficiencies,
|
weaponProf: CONFIG.SW5E.weaponProficiencies,
|
||||||
"toolProf": CONFIG.SW5E.toolProficiencies
|
toolProf: CONFIG.SW5E.toolProficiencies
|
||||||
};
|
};
|
||||||
for (let [t, choices] of Object.entries(map)) {
|
for (let [t, choices] of Object.entries(map)) {
|
||||||
const trait = traits[t];
|
const trait = traits[t];
|
||||||
|
@ -225,7 +227,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Add custom entry
|
// Add custom entry
|
||||||
if (trait.custom) {
|
if (trait.custom) {
|
||||||
trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim());
|
trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim()));
|
||||||
}
|
}
|
||||||
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
|
||||||
}
|
}
|
||||||
|
@ -246,9 +248,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Define some mappings
|
// Define some mappings
|
||||||
const sections = {
|
const sections = {
|
||||||
"atwill": -20,
|
atwill: -20,
|
||||||
"innate": -10,
|
innate: -10,
|
||||||
"pact": 0.5
|
pact: 0.5
|
||||||
};
|
};
|
||||||
|
|
||||||
// Label power slot uses headers
|
// Label power slot uses headers
|
||||||
|
@ -265,7 +267,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
label: label,
|
label: label,
|
||||||
usesSlots: i > 0,
|
usesSlots: i > 0,
|
||||||
canCreate: owner,
|
canCreate: owner,
|
||||||
canPrepare: (data.actor.type === "character") && (i >= 1),
|
canPrepare: data.actor.type === "character" && i >= 1,
|
||||||
powers: [],
|
powers: [],
|
||||||
uses: useLabels[i] || value || 0,
|
uses: useLabels[i] || value || 0,
|
||||||
slots: useLabels[i] || max || 0,
|
slots: useLabels[i] || max || 0,
|
||||||
|
@ -279,7 +281,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
const maxLevel = Array.fromRange(10).reduce((max, i) => {
|
||||||
if (i === 0) return max;
|
if (i === 0) return max;
|
||||||
const level = levels[`power${i}`];
|
const level = levels[`power${i}`];
|
||||||
if ( (level.max || level.override ) && ( i > max ) ) max = i;
|
if ((level.max || level.override) && i > max) max = i;
|
||||||
return max;
|
return max;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -309,7 +311,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over every power item, adding powers to the powerbook by section
|
// Iterate over every power item, adding powers to the powerbook by section
|
||||||
powers.forEach(power => {
|
powers.forEach((power) => {
|
||||||
const mode = power.data.preparation.mode || "prepared";
|
const mode = power.data.preparation.mode || "prepared";
|
||||||
let s = power.data.level || 0;
|
let s = power.data.level || 0;
|
||||||
const sl = `power${s}`;
|
const sl = `power${s}`;
|
||||||
|
@ -352,13 +354,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_filterItems(items, filters) {
|
_filterItems(items, filters) {
|
||||||
return items.filter(item => {
|
return items.filter((item) => {
|
||||||
const data = item.data;
|
const data = item.data;
|
||||||
|
|
||||||
// Action usage
|
// Action usage
|
||||||
for (let f of ["action", "bonus", "reaction"]) {
|
for (let f of ["action", "bonus", "reaction"]) {
|
||||||
if (filters.has(f)) {
|
if (filters.has(f)) {
|
||||||
if ((data.activation && (data.activation.type !== f))) return false;
|
if (data.activation && data.activation.type !== f) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,61 +407,59 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
|
|
||||||
// Activate Item Filters
|
// Activate Item Filters
|
||||||
const filterLists = html.find(".filter-list");
|
const filterLists = html.find(".filter-list");
|
||||||
filterLists.each(this._initializeFilterItemList.bind(this));
|
filterLists.each(this._initializeFilterItemList.bind(this));
|
||||||
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
||||||
|
|
||||||
// Item summaries
|
// Item summaries
|
||||||
html.find('.item .item-name.rollable h4').click(event => this._onItemSummary(event));
|
html.find(".item .item-name.rollable h4").click((event) => this._onItemSummary(event));
|
||||||
|
|
||||||
// View Item Sheets
|
// View Item Sheets
|
||||||
html.find('.item-edit').click(this._onItemEdit.bind(this));
|
html.find(".item-edit").click(this._onItemEdit.bind(this));
|
||||||
|
|
||||||
// Editable Only Listeners
|
// Editable Only Listeners
|
||||||
if (this.isEditable) {
|
if (this.isEditable) {
|
||||||
|
|
||||||
// Input focus and update
|
// Input focus and update
|
||||||
const inputs = html.find("input");
|
const inputs = html.find("input");
|
||||||
inputs.focus(ev => ev.currentTarget.select());
|
inputs.focus((ev) => ev.currentTarget.select());
|
||||||
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this));
|
||||||
|
|
||||||
// Ability Proficiency
|
// Ability Proficiency
|
||||||
html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this));
|
html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this));
|
||||||
|
|
||||||
// Toggle Skill Proficiency
|
// Toggle Skill Proficiency
|
||||||
html.find('.skill-proficiency').on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
html.find(".skill-proficiency").on("click contextmenu", this._onCycleSkillProficiency.bind(this));
|
||||||
|
|
||||||
// Trait Selector
|
// Trait Selector
|
||||||
html.find('.trait-selector').click(this._onTraitSelector.bind(this));
|
html.find(".trait-selector").click(this._onTraitSelector.bind(this));
|
||||||
|
|
||||||
// Configure Special Flags
|
// Configure Special Flags
|
||||||
html.find('.config-button').click(this._onConfigMenu.bind(this));
|
html.find(".config-button").click(this._onConfigMenu.bind(this));
|
||||||
|
|
||||||
// Owned Item management
|
// Owned Item management
|
||||||
html.find('.item-create').click(this._onItemCreate.bind(this));
|
html.find(".item-create").click(this._onItemCreate.bind(this));
|
||||||
html.find('.item-delete').click(this._onItemDelete.bind(this));
|
html.find(".item-delete").click(this._onItemDelete.bind(this));
|
||||||
html.find('.item-uses input').click(ev => ev.target.select()).change(this._onUsesChange.bind(this));
|
html.find(".item-uses input")
|
||||||
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
|
.click((ev) => ev.target.select())
|
||||||
|
.change(this._onUsesChange.bind(this));
|
||||||
|
html.find(".slot-max-override").click(this._onPowerSlotOverride.bind(this));
|
||||||
|
|
||||||
// Active Effect management
|
// Active Effect management
|
||||||
html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor));
|
html.find(".effect-control").click((ev) => onManageActiveEffect(ev, this.actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner Only Listeners
|
// Owner Only Listeners
|
||||||
if (this.actor.isOwner) {
|
if (this.actor.isOwner) {
|
||||||
|
|
||||||
// Ability Checks
|
// Ability Checks
|
||||||
html.find('.ability-name').click(this._onRollAbilityTest.bind(this));
|
html.find(".ability-name").click(this._onRollAbilityTest.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// Roll Skill Checks
|
// Roll Skill Checks
|
||||||
html.find('.skill-name').click(this._onRollSkillCheck.bind(this));
|
html.find(".skill-name").click(this._onRollSkillCheck.bind(this));
|
||||||
|
|
||||||
// Item Rolling
|
// Item Rolling
|
||||||
html.find('.item .item-image').click(event => this._onItemRoll(event));
|
html.find(".item .item-image").click((event) => this._onItemRoll(event));
|
||||||
html.find('.item .item-recharge').click(event => this._onItemRecharge(event));
|
html.find(".item .item-recharge").click((event) => this._onItemRecharge(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise remove rollable classes
|
// Otherwise remove rollable classes
|
||||||
|
@ -554,9 +554,9 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Toggle next level - forward on click, backwards on right
|
// Toggle next level - forward on click, backwards on right
|
||||||
if (event.type === "click") {
|
if (event.type === "click") {
|
||||||
field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]);
|
field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]);
|
||||||
} else if (event.type === "contextmenu") {
|
} else if (event.type === "contextmenu") {
|
||||||
field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]);
|
field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the field value and save the form
|
// Update the field value and save the form
|
||||||
|
@ -567,13 +567,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropActor(event, data) {
|
async _onDropActor(event, data) {
|
||||||
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get('sw5e', 'allowPolymorphing'));
|
const canPolymorph = game.user.isGM || (this.actor.isOwner && game.settings.get("sw5e", "allowPolymorphing"));
|
||||||
if (!canPolymorph) return false;
|
if (!canPolymorph) return false;
|
||||||
|
|
||||||
// Get the target actor
|
// Get the target actor
|
||||||
let sourceActor = null;
|
let sourceActor = null;
|
||||||
if (data.pack) {
|
if (data.pack) {
|
||||||
const pack = game.packs.find(p => p.collection === data.pack);
|
const pack = game.packs.find((p) => p.collection === data.pack);
|
||||||
sourceActor = await pack.getEntity(data.id);
|
sourceActor = await pack.getEntity(data.id);
|
||||||
} else {
|
} else {
|
||||||
sourceActor = game.actors.get(data.id);
|
sourceActor = game.actors.get(data.id);
|
||||||
|
@ -581,35 +581,37 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
if (!sourceActor) return;
|
if (!sourceActor) return;
|
||||||
|
|
||||||
// Define a function to record polymorph settings for future use
|
// Define a function to record polymorph settings for future use
|
||||||
const rememberOptions = html => {
|
const rememberOptions = (html) => {
|
||||||
const options = {};
|
const options = {};
|
||||||
html.find('input').each((i, el) => {
|
html.find("input").each((i, el) => {
|
||||||
options[el.name] = el.checked;
|
options[el.name] = el.checked;
|
||||||
});
|
});
|
||||||
const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options);
|
const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options);
|
||||||
game.settings.set('sw5e', 'polymorphSettings', settings);
|
game.settings.set("sw5e", "polymorphSettings", settings);
|
||||||
return settings;
|
return settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and render the Dialog
|
// Create and render the Dialog
|
||||||
return new Dialog({
|
return new Dialog(
|
||||||
title: game.i18n.localize('SW5E.PolymorphPromptTitle'),
|
{
|
||||||
|
title: game.i18n.localize("SW5E.PolymorphPromptTitle"),
|
||||||
content: {
|
content: {
|
||||||
options: game.settings.get('sw5e', 'polymorphSettings'),
|
options: game.settings.get("sw5e", "polymorphSettings"),
|
||||||
i18n: SW5E.polymorphSettings,
|
i18n: SW5E.polymorphSettings,
|
||||||
isToken: this.actor.isToken
|
isToken: this.actor.isToken
|
||||||
},
|
},
|
||||||
default: 'accept',
|
default: "accept",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: {
|
accept: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphAcceptSettings'),
|
label: game.i18n.localize("SW5E.PolymorphAcceptSettings"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, rememberOptions(html))
|
callback: (html) => this.actor.transformInto(sourceActor, rememberOptions(html))
|
||||||
},
|
},
|
||||||
wildshape: {
|
wildshape: {
|
||||||
icon: '<i class="fas fa-paw"></i>',
|
icon: '<i class="fas fa-paw"></i>',
|
||||||
label: game.i18n.localize('SW5E.PolymorphWildShape'),
|
label: game.i18n.localize("SW5E.PolymorphWildShape"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
keepBio: true,
|
keepBio: true,
|
||||||
keepClass: true,
|
keepClass: true,
|
||||||
keepMental: true,
|
keepMental: true,
|
||||||
|
@ -620,46 +622,50 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
},
|
},
|
||||||
polymorph: {
|
polymorph: {
|
||||||
icon: '<i class="fas fa-pastafarianism"></i>',
|
icon: '<i class="fas fa-pastafarianism"></i>',
|
||||||
label: game.i18n.localize('SW5E.Polymorph'),
|
label: game.i18n.localize("SW5E.Polymorph"),
|
||||||
callback: html => this.actor.transformInto(sourceActor, {
|
callback: (html) =>
|
||||||
|
this.actor.transformInto(sourceActor, {
|
||||||
transformTokens: rememberOptions(html).transformTokens
|
transformTokens: rememberOptions(html).transformTokens
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: game.i18n.localize('Cancel')
|
label: game.i18n.localize("Cancel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
classes: ['dialog', 'sw5e'],
|
{
|
||||||
|
classes: ["dialog", "sw5e"],
|
||||||
width: 600,
|
width: 600,
|
||||||
template: 'systems/sw5e/templates/apps/polymorph-prompt.html'
|
template: "systems/sw5e/templates/apps/polymorph-prompt.html"
|
||||||
}).render(true);
|
}
|
||||||
|
).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Check to make sure items of this type are allowed on this actor
|
// Check to make sure items of this type are allowed on this actor
|
||||||
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
if (this.constructor.unsupportedItemTypes.has(itemData.type)) {
|
||||||
return ui.notifications.warn(game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
return ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ActorWarningInvalidItem", {
|
||||||
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
itemType: game.i18n.localize(CONFIG.Item.typeLabels[itemData.type]),
|
||||||
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
actorType: game.i18n.localize(CONFIG.Actor.typeLabels[this.actor.type])
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a Consumable power scroll on the Inventory tab
|
// Create a Consumable power scroll on the Inventory tab
|
||||||
// TODO: This is pretty non functional as the base items for the scrolls, and the powers, are not defined, maybe consider using holocrons
|
// TODO: This is pretty non functional as the base items for the scrolls, and the powers, are not defined, maybe consider using holocrons
|
||||||
if ( (itemData.type === "power") && (this._tabs[0].active === "inventory") ) {
|
if (itemData.type === "power" && this._tabs[0].active === "inventory") {
|
||||||
const scroll = await Item5e.createScrollFromPower(itemData);
|
const scroll = await Item5e.createScrollFromPower(itemData);
|
||||||
itemData = scroll.data;
|
itemData = scroll.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemData.data) {
|
if (itemData.data) {
|
||||||
// Ignore certain statuses
|
// Ignore certain statuses
|
||||||
["equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
|
["equipped", "proficient", "prepared"].forEach((k) => delete itemData.data[k]);
|
||||||
|
|
||||||
// Downgrade ATTUNED to REQUIRED
|
// Downgrade ATTUNED to REQUIRED
|
||||||
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
itemData.data.attunement = Math.min(itemData.data.attunement, CONFIG.SW5E.attunementTypes.REQUIRED);
|
||||||
|
@ -667,14 +673,13 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
|
|
||||||
// Stack identical consumables
|
// Stack identical consumables
|
||||||
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
|
||||||
const similarItem = this.actor.items.find(i => {
|
const similarItem = this.actor.items.find((i) => {
|
||||||
const sourceId = i.getFlag("core", "sourceId");
|
const sourceId = i.getFlag("core", "sourceId");
|
||||||
return sourceId && (sourceId === itemData.flags.core?.sourceId) &&
|
return sourceId && sourceId === itemData.flags.core?.sourceId && i.type === "consumable";
|
||||||
(i.type === "consumable");
|
|
||||||
});
|
});
|
||||||
if (similarItem) {
|
if (similarItem) {
|
||||||
return similarItem.update({
|
return similarItem.update({
|
||||||
'data.quantity': similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
"data.quantity": similarItem.data.data.quantity + Math.max(itemData.data.quantity, 1)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,7 +725,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max);
|
||||||
event.target.value = uses;
|
event.target.value = uses;
|
||||||
return item.update({ 'data.uses.value': uses });
|
return item.update({"data.uses.value": uses});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -748,7 +753,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemId);
|
const item = this.actor.items.get(itemId);
|
||||||
return item.rollRecharge();
|
return item.rollRecharge();
|
||||||
};
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
@ -769,7 +774,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
} else {
|
} else {
|
||||||
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
let div = $(`<div class="item-summary">${chatData.description.value}</div>`);
|
||||||
let props = $(`<div class="item-properties"></div>`);
|
let props = $(`<div class="item-properties"></div>`);
|
||||||
chatData.properties.forEach(p => props.append(`<span class="tag">${p}</span>`));
|
chatData.properties.forEach((p) => props.append(`<span class="tag">${p}</span>`));
|
||||||
div.append(props);
|
div.append(props);
|
||||||
li.append(div.hide());
|
li.append(div.hide());
|
||||||
div.slideDown(200);
|
div.slideDown(200);
|
||||||
|
@ -894,7 +899,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
const label = a.parentElement.querySelector("label");
|
const label = a.parentElement.querySelector("label");
|
||||||
const choices = CONFIG.SW5E[a.dataset.options];
|
const choices = CONFIG.SW5E[a.dataset.options];
|
||||||
const options = {name: a.dataset.target, title: label.innerText, choices};
|
const options = {name: a.dataset.target, title: label.innerText, choices};
|
||||||
return new TraitSelector(this.actor, options).render(true)
|
return new TraitSelector(this.actor, options).render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -904,7 +909,7 @@ export default class ActorSheet5e extends ActorSheet {
|
||||||
let buttons = super._getHeaderButtons();
|
let buttons = super._getHeaderButtons();
|
||||||
if (this.actor.isPolymorphed) {
|
if (this.actor.isPolymorphed) {
|
||||||
buttons.unshift({
|
buttons.unshift({
|
||||||
label: 'SW5E.PolymorphRestoreTransformation',
|
label: "SW5E.PolymorphRestoreTransformation",
|
||||||
class: "restore-transformation",
|
class: "restore-transformation",
|
||||||
icon: "fas fa-backward",
|
icon: "fas fa-backward",
|
||||||
onclick: () => this.actor.revertOriginalForm()
|
onclick: () => this.actor.revertOriginalForm()
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Actor5e from "../../entity.js";
|
||||||
* @type {ActorSheet5e}
|
* @type {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eCharacter extends ActorSheet5e {
|
export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define default rendering options for the NPC sheet
|
* Define default rendering options for the NPC sheet
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
@ -45,10 +44,12 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
// Experience Tracking
|
// Experience Tracking
|
||||||
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
|
||||||
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
|
sheetData["classLabels"] = this.actor.itemTypes.class.map((c) => c.name).join(", ");
|
||||||
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
|
sheetData["multiclassLabels"] = this.actor.itemTypes.class
|
||||||
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
|
.map((c) => {
|
||||||
}).join(', ');
|
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" ");
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
return sheetData;
|
return sheetData;
|
||||||
|
@ -61,7 +62,6 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize items as inventory, powerbook, features, and classes
|
// Categorize items as inventory, powerbook, features, and classes
|
||||||
const inventory = {
|
const inventory = {
|
||||||
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}},
|
||||||
|
@ -73,11 +73,23 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Partition items by category
|
// Partition items by category
|
||||||
let [items, powers, feats, classes, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => {
|
let [
|
||||||
|
items,
|
||||||
|
powers,
|
||||||
|
feats,
|
||||||
|
classes,
|
||||||
|
species,
|
||||||
|
archetypes,
|
||||||
|
classfeatures,
|
||||||
|
backgrounds,
|
||||||
|
fightingstyles,
|
||||||
|
fightingmasteries,
|
||||||
|
lightsaberforms
|
||||||
|
] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
// Item details
|
// Item details
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.attunement = {
|
item.attunement = {
|
||||||
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
|
||||||
icon: "fa-sun",
|
icon: "fa-sun",
|
||||||
|
@ -92,16 +104,18 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
}[item.data.attunement];
|
}[item.data.attunement];
|
||||||
|
|
||||||
// Item usage
|
// Item usage
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
item.hasUses = item.data.uses && item.data.uses.max > 0;
|
||||||
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
item.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
|
|
||||||
// Item toggle state
|
// Item toggle state
|
||||||
this._prepareItemToggleState(item);
|
this._prepareItemToggleState(item);
|
||||||
|
|
||||||
// Primary Class
|
// Primary Class
|
||||||
if ( item.type === "class" ) item.isOriginalClass = ( item._id === this.actor.data.data.details.originalClass );
|
if (item.type === "class")
|
||||||
|
item.isOriginalClass = item._id === this.actor.data.data.details.originalClass;
|
||||||
|
|
||||||
// Classify items into types
|
// Classify items into types
|
||||||
if (item.type === "power") arr[1].push(item);
|
if (item.type === "power") arr[1].push(item);
|
||||||
|
@ -116,7 +130,9 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
else if (item.type === "lightsaberform") arr[10].push(item);
|
else if (item.type === "lightsaberform") arr[10].push(item);
|
||||||
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
else if (Object.keys(inventory).includes(item.type)) arr[0].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], [], [], [], [], [], [], [], [], []]);
|
},
|
||||||
|
[[], [], [], [], [], [], [], [], [], [], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply active item filters
|
// Apply active item filters
|
||||||
items = this._filterItems(items, this._filters.inventory);
|
items = this._filterItems(items, this._filters.inventory);
|
||||||
|
@ -133,21 +149,74 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
// Organize Powerbook and count the number of prepared powers (excluding always, at will, etc...)
|
// Organize Powerbook and count the number of prepared powers (excluding always, at will, etc...)
|
||||||
const powerbook = this._preparePowerbook(data, powers);
|
const powerbook = this._preparePowerbook(data, powers);
|
||||||
const nPrepared = powers.filter(s => {
|
const nPrepared = powers.filter((s) => {
|
||||||
return (s.data.level > 0) && (s.data.preparation.mode === "prepared") && s.data.preparation.prepared;
|
return s.data.level > 0 && s.data.preparation.mode === "prepared" && s.data.preparation.prepared;
|
||||||
}).length;
|
}).length;
|
||||||
|
|
||||||
// Organize Features
|
// Organize Features
|
||||||
const features = {
|
const features = {
|
||||||
classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true },
|
classes: {
|
||||||
classfeatures: { label: "SW5E.ItemTypeClassFeats", items: [], hasActions: true, dataset: {type: "classfeature"}, isClassfeature: true },
|
label: "SW5E.ItemTypeClassPl",
|
||||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
items: [],
|
||||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
hasActions: false,
|
||||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
dataset: {type: "class"},
|
||||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
isClass: true
|
||||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
},
|
||||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
classfeatures: {
|
||||||
active: { label: "SW5E.FeatureActive", items: [], hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
label: "SW5E.ItemTypeClassFeats",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {type: "classfeature"},
|
||||||
|
isClassfeature: true
|
||||||
|
},
|
||||||
|
archetype: {
|
||||||
|
label: "SW5E.ItemTypeArchetype",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "archetype"},
|
||||||
|
isArchetype: true
|
||||||
|
},
|
||||||
|
species: {
|
||||||
|
label: "SW5E.ItemTypeSpecies",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "species"},
|
||||||
|
isSpecies: true
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
label: "SW5E.ItemTypeBackground",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "background"},
|
||||||
|
isBackground: true
|
||||||
|
},
|
||||||
|
fightingstyles: {
|
||||||
|
label: "SW5E.ItemTypeFightingStylePl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingstyle"},
|
||||||
|
isFightingstyle: true
|
||||||
|
},
|
||||||
|
fightingmasteries: {
|
||||||
|
label: "SW5E.ItemTypeFightingMasteryPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "fightingmastery"},
|
||||||
|
isFightingmastery: true
|
||||||
|
},
|
||||||
|
lightsaberforms: {
|
||||||
|
label: "SW5E.ItemTypeLightsaberFormPl",
|
||||||
|
items: [],
|
||||||
|
hasActions: false,
|
||||||
|
dataset: {type: "lightsaberform"},
|
||||||
|
isLightsaberform: true
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
label: "SW5E.FeatureActive",
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
passive: {label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"}}
|
||||||
};
|
};
|
||||||
for (let f of feats) {
|
for (let f of feats) {
|
||||||
|
@ -187,8 +256,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always;
|
||||||
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared;
|
||||||
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const isActive = getProperty(item.data, "equipped");
|
const isActive = getProperty(item.data, "equipped");
|
||||||
item.toggleClass = isActive ? "active" : "";
|
item.toggleClass = isActive ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped");
|
||||||
|
@ -208,11 +276,11 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
// Item State Toggling
|
// Item State Toggling
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
|
|
||||||
// Short and Long Rest
|
// Short and Long Rest
|
||||||
html.find('.short-rest').click(this._onShortRest.bind(this));
|
html.find(".short-rest").click(this._onShortRest.bind(this));
|
||||||
html.find('.long-rest').click(this._onLongRest.bind(this));
|
html.find(".long-rest").click(this._onLongRest.bind(this));
|
||||||
|
|
||||||
// Rollable sheet actions
|
// Rollable sheet actions
|
||||||
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
||||||
|
@ -281,10 +349,9 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
|
|
||||||
// Increment the number of class levels a character instead of creating a new item
|
// Increment the number of class levels a character instead of creating a new item
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
const cls = this.actor.itemTypes.class.find(c => c.name === itemData.name);
|
const cls = this.actor.itemTypes.class.find((c) => c.name === itemData.name);
|
||||||
let priorLevel = cls?.data.data.levels ?? 0;
|
let priorLevel = cls?.data.data.levels ?? 0;
|
||||||
if (!!cls) {
|
if (!!cls) {
|
||||||
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ActorSheet5e from "./base.js";
|
||||||
* @extends {ActorSheet5e}
|
* @extends {ActorSheet5e}
|
||||||
*/
|
*/
|
||||||
export default class ActorSheet5eNPC extends ActorSheet5e {
|
export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
|
@ -28,27 +27,40 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
|
|
||||||
// Categorize Items as Features and Powers
|
// Categorize Items as Features and Powers
|
||||||
const features = {
|
const features = {
|
||||||
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
weapons: {
|
||||||
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
label: game.i18n.localize("SW5E.AttackPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "weapon", "weapon-type": "natural"}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
|
items: [],
|
||||||
|
hasActions: true,
|
||||||
|
dataset: {"type": "feat", "activation.type": "action"}
|
||||||
|
},
|
||||||
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
passive: {label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"}},
|
||||||
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start by classifying items into groups for rendering
|
// Start by classifying items into groups for rendering
|
||||||
let [powers, other] = data.items.reduce((arr, item) => {
|
let [powers, other] = data.items.reduce(
|
||||||
|
(arr, item) => {
|
||||||
item.img = item.img || CONST.DEFAULT_TOKEN;
|
item.img = item.img || CONST.DEFAULT_TOKEN;
|
||||||
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1;
|
||||||
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
item.hasUses = item.data.uses && item.data.uses.max > 0;
|
||||||
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
item.isOnCooldown =
|
||||||
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
item.data.recharge && !!item.data.recharge.value && item.data.recharge.charged === false;
|
||||||
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
item.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0;
|
||||||
|
item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type);
|
||||||
if (item.type === "power") arr[0].push(item);
|
if (item.type === "power") arr[0].push(item);
|
||||||
else arr[1].push(item);
|
else arr[1].push(item);
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], []]);
|
},
|
||||||
|
[[], []]
|
||||||
|
);
|
||||||
|
|
||||||
// Apply item filters
|
// Apply item filters
|
||||||
powers = this._filterItems(powers, this._filters.powerbook);
|
powers = this._filterItems(powers, this._filters.powerbook);
|
||||||
|
@ -63,8 +75,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
else if (item.type === "feat") {
|
else if (item.type === "feat") {
|
||||||
if (item.data.activation.type) features.actions.items.push(item);
|
if (item.data.activation.type) features.actions.items.push(item);
|
||||||
else features.passive.items.push(item);
|
else features.passive.items.push(item);
|
||||||
}
|
} else features.equipment.items.push(item);
|
||||||
else features.equipment.items.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign and return
|
// Assign and return
|
||||||
|
@ -72,7 +83,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
data.powerbook = powerbook;
|
data.powerbook = powerbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
@ -95,7 +105,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
|
|
||||||
// Format NPC Challenge Rating
|
// Format NPC Challenge Rating
|
||||||
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
let crv = "data.details.cr";
|
let crv = "data.details.cr";
|
||||||
|
|
|
@ -25,13 +25,12 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cargo entry for a vehicle Actor.
|
* Creates a new cargo entry for a vehicle Actor.
|
||||||
*/
|
*/
|
||||||
static get newCargo() {
|
static get newCargo() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: "",
|
||||||
quantity: 1
|
quantity: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeEncumbrance(totalWeight, actorData) {
|
_computeEncumbrance(totalWeight, actorData) {
|
||||||
|
|
||||||
// Compute currency weight
|
// Compute currency weight
|
||||||
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0);
|
||||||
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||||
|
@ -75,25 +73,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareCrewedItem(item) {
|
_prepareCrewedItem(item) {
|
||||||
|
|
||||||
// Determine crewed status
|
// Determine crewed status
|
||||||
const isCrewed = item.data.crewed;
|
const isCrewed = item.data.crewed;
|
||||||
item.toggleClass = isCrewed ? 'active' : '';
|
item.toggleClass = isCrewed ? "active" : "";
|
||||||
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`);
|
item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`);
|
||||||
|
|
||||||
// Handle crew actions
|
// Handle crew actions
|
||||||
if (item.type === 'feat' && item.data.activation.type === 'crew') {
|
if (item.type === "feat" && item.data.activation.type === "crew") {
|
||||||
item.crew = item.data.activation.cost;
|
item.crew = item.data.activation.cost;
|
||||||
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`);
|
item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`);
|
||||||
if (item.data.cover === .5) item.cover = '½';
|
if (item.data.cover === 0.5) item.cover = "½";
|
||||||
else if (item.data.cover === .75) item.cover = '¾';
|
else if (item.data.cover === 0.75) item.cover = "¾";
|
||||||
else if (item.data.cover === null) item.cover = '—';
|
else if (item.data.cover === null) item.cover = "—";
|
||||||
if (item.crew < 1 || item.crew === null) item.crew = '—';
|
if (item.crew < 1 || item.crew === null) item.crew = "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare vehicle weapons
|
// Prepare vehicle weapons
|
||||||
if (item.type === 'equipment' || item.type === 'weapon') {
|
if (item.type === "equipment" || item.type === "weapon") {
|
||||||
item.threshold = item.data.hp.dt ? item.data.hp.dt : '—';
|
item.threshold = item.data.hp.dt ? item.data.hp.dt : "—";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,111 +101,125 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_prepareItems(data) {
|
_prepareItems(data) {
|
||||||
const cargoColumns = [{
|
const cargoColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "quantity",
|
||||||
}];
|
editable: "Number"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const equipmentColumns = [{
|
const equipmentColumns = [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity'
|
css: "item-qty",
|
||||||
}, {
|
property: "data.quantity"
|
||||||
label: game.i18n.localize('SW5E.AC'),
|
},
|
||||||
css: 'item-ac',
|
{
|
||||||
property: 'data.armor.value'
|
label: game.i18n.localize("SW5E.AC"),
|
||||||
}, {
|
css: "item-ac",
|
||||||
label: game.i18n.localize('SW5E.HP'),
|
property: "data.armor.value"
|
||||||
css: 'item-hp',
|
},
|
||||||
property: 'data.hp.value',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.HP"),
|
||||||
}, {
|
css: "item-hp",
|
||||||
label: game.i18n.localize('SW5E.Threshold'),
|
property: "data.hp.value",
|
||||||
css: 'item-threshold',
|
editable: "Number"
|
||||||
property: 'threshold'
|
},
|
||||||
}];
|
{
|
||||||
|
label: game.i18n.localize("SW5E.Threshold"),
|
||||||
|
css: "item-threshold",
|
||||||
|
property: "threshold"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const features = {
|
const features = {
|
||||||
actions: {
|
actions: {
|
||||||
label: game.i18n.localize('SW5E.ActionPl'),
|
label: game.i18n.localize("SW5E.ActionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'feat', 'activation.type': 'crew'},
|
dataset: {"type": "feat", "activation.type": "crew"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
{
|
||||||
css: 'item-crew',
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
property: 'crew'
|
css: "item-crew",
|
||||||
}, {
|
property: "crew"
|
||||||
label: game.i18n.localize('SW5E.Cover'),
|
},
|
||||||
css: 'item-cover',
|
{
|
||||||
property: 'cover'
|
label: game.i18n.localize("SW5E.Cover"),
|
||||||
}]
|
css: "item-cover",
|
||||||
|
property: "cover"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
equipment: {
|
equipment: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeEquipment'),
|
label: game.i18n.localize("SW5E.ItemTypeEquipment"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'equipment', 'armor.type': 'vehicle'},
|
dataset: {"type": "equipment", "armor.type": "vehicle"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
},
|
},
|
||||||
passive: {
|
passive: {
|
||||||
label: game.i18n.localize('SW5E.Features'),
|
label: game.i18n.localize("SW5E.Features"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat'}
|
dataset: {type: "feat"}
|
||||||
},
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
label: game.i18n.localize('SW5E.ReactionPl'),
|
label: game.i18n.localize("SW5E.ReactionPl"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'feat', 'activation.type': 'reaction'}
|
dataset: {"type": "feat", "activation.type": "reaction"}
|
||||||
},
|
},
|
||||||
weapons: {
|
weapons: {
|
||||||
label: game.i18n.localize('SW5E.ItemTypeWeaponPl'),
|
label: game.i18n.localize("SW5E.ItemTypeWeaponPl"),
|
||||||
items: [],
|
items: [],
|
||||||
crewable: true,
|
crewable: true,
|
||||||
dataset: {type: 'weapon', 'weapon-type': 'siege'},
|
dataset: {"type": "weapon", "weapon-type": "siege"},
|
||||||
columns: equipmentColumns
|
columns: equipmentColumns
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cargo = {
|
const cargo = {
|
||||||
crew: {
|
crew: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCrew'),
|
label: game.i18n.localize("SW5E.VehicleCrew"),
|
||||||
items: data.data.cargo.crew,
|
items: data.data.cargo.crew,
|
||||||
css: 'cargo-row crew',
|
css: "cargo-row crew",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'crew'},
|
dataset: {type: "crew"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
passengers: {
|
passengers: {
|
||||||
label: game.i18n.localize('SW5E.VehiclePassengers'),
|
label: game.i18n.localize("SW5E.VehiclePassengers"),
|
||||||
items: data.data.cargo.passengers,
|
items: data.data.cargo.passengers,
|
||||||
css: 'cargo-row passengers',
|
css: "cargo-row passengers",
|
||||||
editableName: true,
|
editableName: true,
|
||||||
dataset: {type: 'passengers'},
|
dataset: {type: "passengers"},
|
||||||
columns: cargoColumns
|
columns: cargoColumns
|
||||||
},
|
},
|
||||||
cargo: {
|
cargo: {
|
||||||
label: game.i18n.localize('SW5E.VehicleCargo'),
|
label: game.i18n.localize("SW5E.VehicleCargo"),
|
||||||
items: [],
|
items: [],
|
||||||
dataset: {type: 'loot'},
|
dataset: {type: "loot"},
|
||||||
columns: [{
|
columns: [
|
||||||
label: game.i18n.localize('SW5E.Quantity'),
|
{
|
||||||
css: 'item-qty',
|
label: game.i18n.localize("SW5E.Quantity"),
|
||||||
property: 'data.quantity',
|
css: "item-qty",
|
||||||
editable: 'Number'
|
property: "data.quantity",
|
||||||
}, {
|
editable: "Number"
|
||||||
label: game.i18n.localize('SW5E.Price'),
|
},
|
||||||
css: 'item-price',
|
{
|
||||||
property: 'data.price',
|
label: game.i18n.localize("SW5E.Price"),
|
||||||
editable: 'Number'
|
css: "item-price",
|
||||||
}, {
|
property: "data.price",
|
||||||
label: game.i18n.localize('SW5E.Weight'),
|
editable: "Number"
|
||||||
css: 'item-weight',
|
},
|
||||||
property: 'data.weight',
|
{
|
||||||
editable: 'Number'
|
label: game.i18n.localize("SW5E.Weight"),
|
||||||
}]
|
css: "item-weight",
|
||||||
|
property: "data.weight",
|
||||||
|
editable: "Number"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -234,8 +245,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
features.equipment.items.push(item);
|
features.equipment.items.push(item);
|
||||||
break;
|
break;
|
||||||
case "feat":
|
case "feat":
|
||||||
if (!item.data.activation.type || (item.data.activation.type === "none")) features.passive.items.push(item);
|
if (!item.data.activation.type || item.data.activation.type === "none")
|
||||||
else if (item.data.activation.type === 'reaction') features.reactions.items.push(item);
|
features.passive.items.push(item);
|
||||||
|
else if (item.data.activation.type === "reaction") features.reactions.items.push(item);
|
||||||
else features.actions.items.push(item);
|
else features.actions.items.push(item);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -259,21 +271,21 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
html.find(".item-toggle").click(this._onToggleItem.bind(this));
|
||||||
html.find('.item-hp input')
|
html.find(".item-hp input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onHPChange.bind(this));
|
.change(this._onHPChange.bind(this));
|
||||||
|
|
||||||
html.find('.item:not(.cargo-row) input[data-property]')
|
html.find(".item:not(.cargo-row) input[data-property]")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onEditInSheet.bind(this));
|
.change(this._onEditInSheet.bind(this));
|
||||||
|
|
||||||
html.find('.cargo-row input')
|
html.find(".cargo-row input")
|
||||||
.click(evt => evt.target.select())
|
.click((evt) => evt.target.select())
|
||||||
.change(this._onCargoRowChange.bind(this));
|
.change(this._onCargoRowChange.bind(this));
|
||||||
|
|
||||||
if (this.actor.data.data.attributes.actions.stations) {
|
if (this.actor.data.data.attributes.actions.stations) {
|
||||||
html.find('.counter.actions, .counter.action-thresholds').hide();
|
html.find(".counter.actions, .counter.action-thresholds").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +300,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
_onCargoRowChange(event) {
|
_onCargoRowChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const row = target.closest('.item');
|
const row = target.closest(".item");
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const property = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const property = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
|
|
||||||
// Get the cargo entry
|
// Get the cargo entry
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[property]);
|
||||||
|
@ -298,10 +310,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
|
|
||||||
// Update the cargo value
|
// Update the cargo value
|
||||||
const key = target.dataset.property || 'name';
|
const key = target.dataset.property || "name";
|
||||||
const type = target.dataset.dtype;
|
const type = target.dataset.dtype;
|
||||||
let value = target.value;
|
let value = target.value;
|
||||||
if (type === 'Number') value = Number(value);
|
if (type === "Number") value = Number(value);
|
||||||
entry[key] = value;
|
entry[key] = value;
|
||||||
|
|
||||||
// Perform the Actor update
|
// Perform the Actor update
|
||||||
|
@ -318,14 +330,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onEditInSheet(event) {
|
_onEditInSheet(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const property = event.currentTarget.dataset.property;
|
const property = event.currentTarget.dataset.property;
|
||||||
const type = event.currentTarget.dataset.dtype;
|
const type = event.currentTarget.dataset.dtype;
|
||||||
let value = event.currentTarget.value;
|
let value = event.currentTarget.value;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Number': value = parseInt(value); break;
|
case "Number":
|
||||||
case 'Boolean': value = value === 'true'; break;
|
value = parseInt(value);
|
||||||
|
break;
|
||||||
|
case "Boolean":
|
||||||
|
value = value === "true";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return item.update({[`${property}`]: value});
|
return item.update({[`${property}`]: value});
|
||||||
}
|
}
|
||||||
|
@ -342,7 +358,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
const type = target.dataset.type;
|
const type = target.dataset.type;
|
||||||
if (type === 'crew' || type === 'passengers') {
|
if (type === "crew" || type === "passengers") {
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]);
|
||||||
cargo.push(this.constructor.newCargo);
|
cargo.push(this.constructor.newCargo);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
|
@ -360,10 +376,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onItemDelete(event) {
|
_onItemDelete(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const row = event.currentTarget.closest('.item');
|
const row = event.currentTarget.closest(".item");
|
||||||
if (row.classList.contains('cargo-row')) {
|
if (row.classList.contains("cargo-row")) {
|
||||||
const idx = Number(row.dataset.itemId);
|
const idx = Number(row.dataset.itemId);
|
||||||
const type = row.classList.contains('crew') ? 'crew' : 'passengers';
|
const type = row.classList.contains("crew") ? "crew" : "passengers";
|
||||||
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
const cargo = foundry.utils.deepClone(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx);
|
||||||
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
return this.actor.update({[`data.cargo.${type}`]: cargo});
|
||||||
}
|
}
|
||||||
|
@ -376,7 +392,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _onDropItemCreate(itemData) {
|
async _onDropItemCreate(itemData) {
|
||||||
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
const cargoTypes = ["weapon", "equipment", "consumable", "tool", "loot", "backpack"];
|
||||||
const isCargo = cargoTypes.includes(itemData.type) && (this._tabs[0].active === "cargo");
|
const isCargo = cargoTypes.includes(itemData.type) && this._tabs[0].active === "cargo";
|
||||||
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
foundry.utils.setProperty(itemData, "flags.sw5e.vehicleCargo", isCargo);
|
||||||
return super._onDropItemCreate(itemData);
|
return super._onDropItemCreate(itemData);
|
||||||
}
|
}
|
||||||
|
@ -391,11 +407,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onHPChange(event) {
|
_onHPChange(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
const hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max);
|
||||||
event.currentTarget.value = hp;
|
event.currentTarget.value = hp;
|
||||||
return item.update({'data.hp.value': hp});
|
return item.update({"data.hp.value": hp});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -408,9 +424,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
||||||
*/
|
*/
|
||||||
_onToggleItem(event) {
|
_onToggleItem(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemID = event.currentTarget.closest('.item').dataset.itemId;
|
const itemID = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.items.get(itemID);
|
const item = this.actor.items.get(itemID);
|
||||||
const crewed = !!item.data.data.crewed;
|
const crewed = !!item.data.data.crewed;
|
||||||
return item.update({'data.crewed': !crewed});
|
return item.update({"data.crewed": !crewed});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
|
@ -39,12 +39,15 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
// Prepare dialog form data
|
// Prepare dialog form data
|
||||||
const data = {
|
const data = {
|
||||||
item: item.data,
|
item: item.data,
|
||||||
title: game.i18n.format("SW5E.AbilityUseHint", {type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`), name: item.name}),
|
title: game.i18n.format("SW5E.AbilityUseHint", {
|
||||||
|
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`),
|
||||||
|
name: item.name
|
||||||
|
}),
|
||||||
note: this._getAbilityUseNote(item.data, uses, recharge),
|
note: this._getAbilityUseNote(item.data, uses, recharge),
|
||||||
consumePowerSlot: false,
|
consumePowerSlot: false,
|
||||||
consumeRecharge: recharges,
|
consumeRecharge: recharges,
|
||||||
consumeResource: !!itemData.consume.target,
|
consumeResource: !!itemData.consume.target,
|
||||||
consumeUses: uses.per && (uses.max > 0),
|
consumeUses: uses.per && uses.max > 0,
|
||||||
canUse: recharges ? recharge.charged : sufficientUses,
|
canUse: recharges ? recharge.charged : sufficientUses,
|
||||||
createTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget,
|
createTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget,
|
||||||
errors: []
|
errors: []
|
||||||
|
@ -65,7 +68,7 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
use: {
|
use: {
|
||||||
icon: `<i class="fas ${icon}"></i>`,
|
icon: `<i class="fas ${icon}"></i>`,
|
||||||
label: label,
|
label: label,
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
const fd = new FormDataExtended(html[0].querySelector("form"));
|
const fd = new FormDataExtended(html[0].querySelector("form"));
|
||||||
resolve(fd.toObject());
|
resolve(fd.toObject());
|
||||||
}
|
}
|
||||||
|
@ -87,10 +90,9 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getPowerData(actorData, itemData, data) {
|
static _getPowerData(actorData, itemData, data) {
|
||||||
|
|
||||||
// Determine whether the power may be up-cast
|
// Determine whether the power may be up-cast
|
||||||
const lvl = itemData.level;
|
const lvl = itemData.level;
|
||||||
const consumePowerSlot = (lvl > 0) && CONFIG.SW5E.powerUpcastModes.includes(itemData.preparation.mode);
|
const consumePowerSlot = lvl > 0 && CONFIG.SW5E.powerUpcastModes.includes(itemData.preparation.mode);
|
||||||
|
|
||||||
// If can't upcast, return early and don't bother calculating available power slots
|
// If can't upcast, return early and don't bother calculating available power slots
|
||||||
if (!consumePowerSlot) {
|
if (!consumePowerSlot) {
|
||||||
|
@ -106,67 +108,71 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
case "lgt":
|
case "lgt":
|
||||||
case "uni":
|
case "uni":
|
||||||
case "drk": {
|
case "drk": {
|
||||||
powerType = "force"
|
powerType = "force";
|
||||||
points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp;
|
points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "tec": {
|
case "tec": {
|
||||||
powerType = "tech"
|
powerType = "tech";
|
||||||
points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp;
|
points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eliminate point usage for innate casters
|
// eliminate point usage for innate casters
|
||||||
if (actorData.attributes.powercasting === 'innate') points = 999;
|
if (actorData.attributes.powercasting === "innate") points = 999;
|
||||||
|
|
||||||
|
let powerLevels;
|
||||||
let powerLevels
|
|
||||||
if (powerType === "force") {
|
if (powerType === "force") {
|
||||||
powerLevels = Array.fromRange(10).reduce((arr, i) => {
|
powerLevels = Array.fromRange(10)
|
||||||
|
.reduce((arr, i) => {
|
||||||
if (i < lvl) return arr;
|
if (i < lvl) return arr;
|
||||||
const label = CONFIG.SW5E.powerLevels[i];
|
const label = CONFIG.SW5E.powerLevels[i];
|
||||||
const l = actorData.powers["power" + i] || {fmax: 0, foverride: null};
|
const l = actorData.powers["power" + i] || {fmax: 0, foverride: null};
|
||||||
let max = parseInt(l.foverride || l.fmax || 0);
|
let max = parseInt(l.foverride || l.fmax || 0);
|
||||||
let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max);
|
let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max);
|
||||||
if (max > 0) lmax = i;
|
if (max > 0) lmax = i;
|
||||||
if ((max > 0) && (slots > 0) && (points > i)){
|
if (max > 0 && slots > 0 && points > i) {
|
||||||
arr.push({
|
arr.push({
|
||||||
level: i,
|
level: i,
|
||||||
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
|
label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label,
|
||||||
canCast: max > 0,
|
canCast: max > 0,
|
||||||
hasSlots: slots > 0
|
hasSlots: slots > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}, []).filter(sl => sl.level <= lmax);
|
}, [])
|
||||||
|
.filter((sl) => sl.level <= lmax);
|
||||||
} else if (powerType === "tech") {
|
} else if (powerType === "tech") {
|
||||||
powerLevels = Array.fromRange(10).reduce((arr, i) => {
|
powerLevels = Array.fromRange(10)
|
||||||
|
.reduce((arr, i) => {
|
||||||
if (i < lvl) return arr;
|
if (i < lvl) return arr;
|
||||||
const label = CONFIG.SW5E.powerLevels[i];
|
const label = CONFIG.SW5E.powerLevels[i];
|
||||||
const l = actorData.powers["power" + i] || {tmax: 0, toverride: null};
|
const l = actorData.powers["power" + i] || {tmax: 0, toverride: null};
|
||||||
let max = parseInt(l.override || l.tmax || 0);
|
let max = parseInt(l.override || l.tmax || 0);
|
||||||
let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max);
|
let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max);
|
||||||
if (max > 0) lmax = i;
|
if (max > 0) lmax = i;
|
||||||
if ((max > 0) && (slots > 0) && (points > i)){
|
if (max > 0 && slots > 0 && points > i) {
|
||||||
arr.push({
|
arr.push({
|
||||||
level: i,
|
level: i,
|
||||||
label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label,
|
label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label,
|
||||||
canCast: max > 0,
|
canCast: max > 0,
|
||||||
hasSlots: slots > 0
|
hasSlots: slots > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}, []).filter(sl => sl.level <= lmax);
|
}, [])
|
||||||
|
.filter((sl) => sl.level <= lmax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canCast = powerLevels.some((l) => l.hasSlots);
|
||||||
|
if (!canCast)
|
||||||
const canCast = powerLevels.some(l => l.hasSlots);
|
data.errors.push(
|
||||||
if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", {
|
game.i18n.format("SW5E.PowerCastNoSlots", {
|
||||||
level: CONFIG.SW5E.powerLevels[lvl],
|
level: CONFIG.SW5E.powerLevels[lvl],
|
||||||
name: data.item.name
|
name: data.item.name
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Merge power casting data
|
// Merge power casting data
|
||||||
return foundry.utils.mergeObject(data, {isPower: true, consumePowerSlot, powerLevels});
|
return foundry.utils.mergeObject(data, {isPower: true, consumePowerSlot, powerLevels});
|
||||||
|
@ -179,7 +185,6 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getAbilityUseNote(item, uses, recharge) {
|
static _getAbilityUseNote(item, uses, recharge) {
|
||||||
|
|
||||||
// Zero quantity
|
// Zero quantity
|
||||||
const quantity = item.data.quantity;
|
const quantity = item.data.quantity;
|
||||||
if (quantity <= 0) return game.i18n.localize("SW5E.AbilityUseUnavailableHint");
|
if (quantity <= 0) return game.i18n.localize("SW5E.AbilityUseUnavailableHint");
|
||||||
|
@ -187,8 +192,8 @@ export default class AbilityUseDialog extends Dialog {
|
||||||
// Abilities which use Recharge
|
// Abilities which use Recharge
|
||||||
if (!!recharge.value) {
|
if (!!recharge.value) {
|
||||||
return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", {
|
return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", {
|
||||||
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`),
|
type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`)
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not use any resource
|
// Does not use any resource
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get title() {
|
get title() {
|
||||||
return `${game.i18n.localize('SW5E.FlagsTitle')}: ${this.object.name}`;
|
return `${game.i18n.localize("SW5E.FlagsTitle")}: ${this.object.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -40,8 +40,10 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getClasses() {
|
_getClasses() {
|
||||||
const classes = this.object.items.filter(i => i.type === "class");
|
const classes = this.object.items.filter((i) => i.type === "class");
|
||||||
return classes.sort((a, b) => a.name.localeCompare(b.name)).reduce((obj, i) => {
|
return classes
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.reduce((obj, i) => {
|
||||||
obj[i.id] = i.name;
|
obj[i.id] = i.name;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
|
@ -63,7 +65,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
let flag = foundry.utils.deepClone(v);
|
let flag = foundry.utils.deepClone(v);
|
||||||
flag.type = v.type.name;
|
flag.type = v.type.name;
|
||||||
flag.isCheckbox = v.type === Boolean;
|
flag.isCheckbox = v.type === Boolean;
|
||||||
flag.isSelect = v.hasOwnProperty('choices');
|
flag.isSelect = v.hasOwnProperty("choices");
|
||||||
flag.value = getProperty(baseData.flags, `sw5e.${k}`);
|
flag.value = getProperty(baseData.flags, `sw5e.${k}`);
|
||||||
flags[v.section][`flags.sw5e.${k}`] = flag;
|
flags[v.section][`flags.sw5e.${k}`] = flag;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +115,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
||||||
let unset = false;
|
let unset = false;
|
||||||
const flags = updateData.flags.sw5e;
|
const flags = updateData.flags.sw5e;
|
||||||
//clone flags to dnd5e for module compatability
|
//clone flags to dnd5e for module compatability
|
||||||
updateData.flags.dnd5e = updateData.flags.sw5e
|
updateData.flags.dnd5e = updateData.flags.sw5e;
|
||||||
for (let [k, v] of Object.entries(flags)) {
|
for (let [k, v] of Object.entries(flags)) {
|
||||||
if ([undefined, null, "", false, 0].includes(v)) {
|
if ([undefined, null, "", false, 0].includes(v)) {
|
||||||
delete flags[k];
|
delete flags[k];
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Actor5e from "../actor/entity.js";
|
||||||
* @extends {FormApplication}
|
* @extends {FormApplication}
|
||||||
*/
|
*/
|
||||||
export default class ActorTypeConfig extends FormApplication {
|
export default class ActorTypeConfig extends FormApplication {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -32,11 +31,11 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
|
|
||||||
// Get current value or new default
|
// Get current value or new default
|
||||||
let attr = foundry.utils.getProperty(this.object.data.data, 'details.type');
|
let attr = foundry.utils.getProperty(this.object.data.data, "details.type");
|
||||||
if ( foundry.utils.getType(attr) !== "Object" ) attr = {
|
if (foundry.utils.getType(attr) !== "Object")
|
||||||
value: (attr in CONFIG.SW5E.creatureTypes) ? attr : "humanoid",
|
attr = {
|
||||||
|
value: attr in CONFIG.SW5E.creatureTypes ? attr : "humanoid",
|
||||||
subtype: "",
|
subtype: "",
|
||||||
swarm: "",
|
swarm: "",
|
||||||
custom: ""
|
custom: ""
|
||||||
|
@ -48,7 +47,7 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
types[k] = {
|
types[k] = {
|
||||||
label: game.i18n.localize(v),
|
label: game.i18n.localize(v),
|
||||||
chosen: attr.value === k
|
chosen: attr.value === k
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return data for rendering
|
// Return data for rendering
|
||||||
|
@ -61,12 +60,14 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
},
|
},
|
||||||
subtype: attr.subtype,
|
subtype: attr.subtype,
|
||||||
swarm: attr.swarm,
|
swarm: attr.swarm,
|
||||||
sizes: Array.from(Object.entries(CONFIG.SW5E.actorSizes)).reverse().reduce((obj, e) => {
|
sizes: Array.from(Object.entries(CONFIG.SW5E.actorSizes))
|
||||||
|
.reverse()
|
||||||
|
.reduce((obj, e) => {
|
||||||
obj[e[0]] = e[1];
|
obj[e[0]] = e[1];
|
||||||
return obj;
|
return obj;
|
||||||
}, {}),
|
}, {}),
|
||||||
preview: Actor5e.formatCreatureType(attr) || "–"
|
preview: Actor5e.formatCreatureType(attr) || "–"
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -74,7 +75,7 @@ export default class ActorTypeConfig extends FormApplication {
|
||||||
/** @override */
|
/** @override */
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
const typeObject = foundry.utils.expandObject(formData);
|
const typeObject = foundry.utils.expandObject(formData);
|
||||||
return this.object.update({ 'data.details.type': typeObject });
|
return this.object.update({"data.details.type": typeObject});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @implements {DocumentSheet}
|
* @implements {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorHitDiceConfig extends DocumentSheet {
|
export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -26,7 +25,8 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData(options) {
|
getData(options) {
|
||||||
return {
|
return {
|
||||||
classes: this.object.items.reduce((classes, item) => {
|
classes: this.object.items
|
||||||
|
.reduce((classes, item) => {
|
||||||
if (item.data.type === "class") {
|
if (item.data.type === "class") {
|
||||||
// Add the appropriate data only if this item is a "class"
|
// Add the appropriate data only if this item is a "class"
|
||||||
classes.push({
|
classes.push({
|
||||||
|
@ -35,11 +35,12 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
diceDenom: item.data.data.hitDice,
|
diceDenom: item.data.data.hitDice,
|
||||||
currentHitDice: item.data.data.levels - item.data.data.hitDiceUsed,
|
currentHitDice: item.data.data.levels - item.data.data.hitDiceUsed,
|
||||||
maxHitDice: item.data.data.levels,
|
maxHitDice: item.data.data.levels,
|
||||||
canRoll: (item.data.data.levels - item.data.data.hitDiceUsed) > 0
|
canRoll: item.data.data.levels - item.data.data.hitDiceUsed > 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
}, []).sort((a, b) => parseInt(b.diceDenom.slice(1)) - parseInt(a.diceDenom.slice(1)))
|
}, [])
|
||||||
|
.sort((a, b) => parseInt(b.diceDenom.slice(1)) - parseInt(a.diceDenom.slice(1)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
|
||||||
// Hook up -/+ buttons to adjust the current value in the form
|
// Hook up -/+ buttons to adjust the current value in the form
|
||||||
html.find("button.increment,button.decrement").click(event => {
|
html.find("button.increment,button.decrement").click((event) => {
|
||||||
const button = event.currentTarget;
|
const button = event.currentTarget;
|
||||||
const current = button.parentElement.querySelector(".current");
|
const current = button.parentElement.querySelector(".current");
|
||||||
const max = button.parentElement.querySelector(".max");
|
const max = button.parentElement.querySelector(".max");
|
||||||
|
@ -67,8 +68,8 @@ export default class ActorHitDiceConfig extends DocumentSheet {
|
||||||
async _updateObject(event, formData) {
|
async _updateObject(event, formData) {
|
||||||
const actorItems = this.object.items;
|
const actorItems = this.object.items;
|
||||||
const classUpdates = Object.entries(formData).map(([id, hd]) => ({
|
const classUpdates = Object.entries(formData).map(([id, hd]) => ({
|
||||||
_id: id,
|
"_id": id,
|
||||||
"data.hitDiceUsed": actorItems.get(id).data.data.levels - hd,
|
"data.hitDiceUsed": actorItems.get(id).data.data.levels - hd
|
||||||
}));
|
}));
|
||||||
return this.object.updateEmbeddedDocuments("Item", classUpdates);
|
return this.object.updateEmbeddedDocuments("Item", classUpdates);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class LongRestDialog extends Dialog {
|
||||||
rest: {
|
rest: {
|
||||||
icon: '<i class="fas fa-bed"></i>',
|
icon: '<i class="fas fa-bed"></i>',
|
||||||
label: game.i18n.localize("SW5E.Rest"),
|
label: game.i18n.localize("SW5E.Rest"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
let newDay = true;
|
let newDay = true;
|
||||||
if (game.settings.get("sw5e", "restVariant") !== "gritty")
|
if (game.settings.get("sw5e", "restVariant") !== "gritty")
|
||||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||||
|
@ -58,7 +58,7 @@ export default class LongRestDialog extends Dialog {
|
||||||
callback: reject
|
callback: reject
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: 'rest',
|
default: "rest",
|
||||||
close: reject
|
close: reject
|
||||||
});
|
});
|
||||||
dlg.render(true);
|
dlg.render(true);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorMovementConfig extends DocumentSheet {
|
export default class ActorMovementConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
|
|
@ -18,11 +18,11 @@ export default class SelectItemsPrompt extends Dialog {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
|
||||||
// render the item's sheet if its image is clicked
|
// render the item's sheet if its image is clicked
|
||||||
html.on('click', '.item-image', (event) => {
|
html.on("click", ".item-image", (event) => {
|
||||||
const item = this.items.find((feature) => feature.id === event.currentTarget.dataset?.itemId);
|
const item = this.items.find((feature) => feature.id === event.currentTarget.dataset?.itemId);
|
||||||
|
|
||||||
item?.sheet.render(true);
|
item?.sheet.render(true);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,29 +33,27 @@ export default class SelectItemsPrompt extends Dialog {
|
||||||
* @param {string} options.hint - Localized hint to display at the top of the prompt
|
* @param {string} options.hint - Localized hint to display at the top of the prompt
|
||||||
* @return {Promise<string[]>} - list of item ids which the user has selected
|
* @return {Promise<string[]>} - list of item ids which the user has selected
|
||||||
*/
|
*/
|
||||||
static async create(items, {
|
static async create(items, {hint}) {
|
||||||
hint
|
|
||||||
}) {
|
|
||||||
// Render the ability usage template
|
// Render the ability usage template
|
||||||
const html = await renderTemplate("systems/sw5e/templates/apps/select-items-prompt.html", {items, hint});
|
const html = await renderTemplate("systems/sw5e/templates/apps/select-items-prompt.html", {items, hint});
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const dlg = new this(items, {
|
const dlg = new this(items, {
|
||||||
title: game.i18n.localize('SW5E.SelectItemsPromptTitle'),
|
title: game.i18n.localize("SW5E.SelectItemsPromptTitle"),
|
||||||
content: html,
|
content: html,
|
||||||
buttons: {
|
buttons: {
|
||||||
apply: {
|
apply: {
|
||||||
icon: `<i class="fas fa-user-plus"></i>`,
|
icon: `<i class="fas fa-user-plus"></i>`,
|
||||||
label: game.i18n.localize('SW5E.Apply'),
|
label: game.i18n.localize("SW5E.Apply"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
const fd = new FormDataExtended(html[0].querySelector("form")).toObject();
|
const fd = new FormDataExtended(html[0].querySelector("form")).toObject();
|
||||||
const selectedIds = Object.keys(fd).filter(itemId => fd[itemId]);
|
const selectedIds = Object.keys(fd).filter((itemId) => fd[itemId]);
|
||||||
resolve(selectedIds);
|
resolve(selectedIds);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-forward"></i>',
|
icon: '<i class="fas fa-forward"></i>',
|
||||||
label: game.i18n.localize('SW5E.Skip'),
|
label: game.i18n.localize("SW5E.Skip"),
|
||||||
callback: () => resolve([])
|
callback: () => resolve([])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class ActorSensesConfig extends DocumentSheet {
|
export default class ActorSensesConfig extends DocumentSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -29,14 +28,15 @@ export default class ActorSensesConfig extends DocumentSheet {
|
||||||
const data = {
|
const data = {
|
||||||
senses: {},
|
senses: {},
|
||||||
special: senses.special ?? "",
|
special: senses.special ?? "",
|
||||||
units: senses.units, movementUnits: CONFIG.SW5E.movementUnits
|
units: senses.units,
|
||||||
|
movementUnits: CONFIG.SW5E.movementUnits
|
||||||
};
|
};
|
||||||
for (let [name, label] of Object.entries(CONFIG.SW5E.senses)) {
|
for (let [name, label] of Object.entries(CONFIG.SW5E.senses)) {
|
||||||
const v = senses[name];
|
const v = senses[name];
|
||||||
data.senses[name] = {
|
data.senses[name] = {
|
||||||
label: game.i18n.localize(label),
|
label: game.i18n.localize(label),
|
||||||
value: Number.isNumeric(v) ? v.toNearest(0.1) : 0
|
value: Number.isNumeric(v) ? v.toNearest(0.1) : 0
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ export default class ShortRestDialog extends Dialog {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
|
@ -98,7 +97,7 @@ export default class ShortRestDialog extends Dialog {
|
||||||
rest: {
|
rest: {
|
||||||
icon: '<i class="fas fa-bed"></i>',
|
icon: '<i class="fas fa-bed"></i>',
|
||||||
label: game.i18n.localize("SW5E.Rest"),
|
label: game.i18n.localize("SW5E.Rest"),
|
||||||
callback: html => {
|
callback: (html) => {
|
||||||
let newDay = false;
|
let newDay = false;
|
||||||
if (game.settings.get("sw5e", "restVariant") === "gritty")
|
if (game.settings.get("sw5e", "restVariant") === "gritty")
|
||||||
newDay = html.find('input[name="newDay"]')[0].checked;
|
newDay = html.find('input[name="newDay"]')[0].checked;
|
||||||
|
@ -127,7 +126,9 @@ export default class ShortRestDialog extends Dialog {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static async longRestDialog({actor} = {}) {
|
static async longRestDialog({actor} = {}) {
|
||||||
console.warn("WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead.");
|
console.warn(
|
||||||
|
"WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead."
|
||||||
|
);
|
||||||
return LongRestDialog.longRestDialog(...arguments);
|
return LongRestDialog.longRestDialog(...arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @extends {DocumentSheet}
|
* @extends {DocumentSheet}
|
||||||
*/
|
*/
|
||||||
export default class TraitSelector extends DocumentSheet {
|
export default class TraitSelector extends DocumentSheet {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
@ -38,22 +37,22 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
getData() {
|
getData() {
|
||||||
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
||||||
const o = this.options;
|
const o = this.options;
|
||||||
const value = (o.valueKey) ? attr[o.valueKey] ?? [] : attr;
|
const value = o.valueKey ? attr[o.valueKey] ?? [] : attr;
|
||||||
const custom = (o.customKey) ? attr[o.customKey] ?? "" : "";
|
const custom = o.customKey ? attr[o.customKey] ?? "" : "";
|
||||||
|
|
||||||
// Populate choices
|
// Populate choices
|
||||||
const choices = Object.entries(o.choices).reduce((obj, e) => {
|
const choices = Object.entries(o.choices).reduce((obj, e) => {
|
||||||
let [k, v] = e;
|
let [k, v] = e;
|
||||||
obj[k] = {label: v, chosen: attr ? value.includes(k) : false};
|
obj[k] = {label: v, chosen: attr ? value.includes(k) : false};
|
||||||
return obj;
|
return obj;
|
||||||
}, {})
|
}, {});
|
||||||
|
|
||||||
// Return data
|
// Return data
|
||||||
return {
|
return {
|
||||||
allowCustom: o.allowCustom,
|
allowCustom: o.allowCustom,
|
||||||
choices: choices,
|
choices: choices,
|
||||||
custom: custom
|
custom: custom
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -65,7 +64,7 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
// Obtain choices
|
// Obtain choices
|
||||||
const chosen = [];
|
const chosen = [];
|
||||||
for (let [k, v] of Object.entries(formData)) {
|
for (let [k, v] of Object.entries(formData)) {
|
||||||
if ( (k !== "custom") && v ) chosen.push(k);
|
if (k !== "custom" && v) chosen.push(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object including custom data
|
// Object including custom data
|
||||||
|
@ -75,10 +74,10 @@ export default class TraitSelector extends DocumentSheet {
|
||||||
if (o.allowCustom) updateData[`${this.attribute}.${o.customKey}`] = formData.custom;
|
if (o.allowCustom) updateData[`${this.attribute}.${o.customKey}`] = formData.custom;
|
||||||
|
|
||||||
// Validate the number chosen
|
// Validate the number chosen
|
||||||
if ( o.minimum && (chosen.length < o.minimum) ) {
|
if (o.minimum && chosen.length < o.minimum) {
|
||||||
return ui.notifications.error(`You must choose at least ${o.minimum} options`);
|
return ui.notifications.error(`You must choose at least ${o.minimum} options`);
|
||||||
}
|
}
|
||||||
if ( o.maximum && (chosen.length > o.maximum) ) {
|
if (o.maximum && chosen.length > o.maximum) {
|
||||||
return ui.notifications.error(`You may choose no more than ${o.maximum} options`);
|
return ui.notifications.error(`You may choose no more than ${o.maximum} options`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const measureDistances = function(segments, options={}) {
|
||||||
const d = canvas.dimensions;
|
const d = canvas.dimensions;
|
||||||
|
|
||||||
// Iterate over measured segments
|
// Iterate over measured segments
|
||||||
return segments.map(s => {
|
return segments.map((s) => {
|
||||||
let r = s.ray;
|
let r = s.ray;
|
||||||
|
|
||||||
// Determine the total distance traveled
|
// Determine the total distance traveled
|
||||||
|
@ -23,7 +23,7 @@ export const measureDistances = function(segments, options={}) {
|
||||||
// Alternative DMG Movement
|
// Alternative DMG Movement
|
||||||
if (rule === "5105") {
|
if (rule === "5105") {
|
||||||
let nd10 = Math.floor(nDiagonal / 2) - Math.floor((nDiagonal - nd) / 2);
|
let nd10 = Math.floor(nDiagonal / 2) - Math.floor((nDiagonal - nd) / 2);
|
||||||
let spaces = (nd10 * 2) + (nd - nd10) + ns;
|
let spaces = nd10 * 2 + (nd - nd10) + ns;
|
||||||
return spaces * canvas.dimensions.distance;
|
return spaces * canvas.dimensions.distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,9 @@ export default class CharacterImporter {
|
||||||
this.addSpecies(sourceCharacter.attribs.find((e) => e.name == "race").current, actor);
|
this.addSpecies(sourceCharacter.attribs.find((e) => e.name == "race").current, actor);
|
||||||
|
|
||||||
this.addPowers(
|
this.addPowers(
|
||||||
sourceCharacter.attribs.filter((e) => e.name.search(/repeating_power.+_powername/g) != -1).map((e) => e.current),
|
sourceCharacter.attribs
|
||||||
|
.filter((e) => e.name.search(/repeating_power.+_powername/g) != -1)
|
||||||
|
.map((e) => e.current),
|
||||||
actor
|
actor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight critical success or failure on d20 rolls
|
* Highlight critical success or failure on d20 rolls
|
||||||
*/
|
*/
|
||||||
|
@ -11,9 +10,9 @@ export const highlightCriticalSuccessFailure = function(message, html, data) {
|
||||||
const d = roll.dice[0];
|
const d = roll.dice[0];
|
||||||
|
|
||||||
// Ensure it is an un-modified d20 roll
|
// Ensure it is an un-modified d20 roll
|
||||||
const isD20 = (d.faces === 20) && ( d.values.length === 1 );
|
const isD20 = d.faces === 20 && d.values.length === 1;
|
||||||
if (!isD20) return;
|
if (!isD20) return;
|
||||||
const isModifiedRoll = ("success" in d.results[0]) || d.options.marginSuccess || d.options.marginFailure;
|
const isModifiedRoll = "success" in d.results[0] || d.options.marginSuccess || d.options.marginFailure;
|
||||||
if (isModifiedRoll) return;
|
if (isModifiedRoll) return;
|
||||||
|
|
||||||
// Highlight successes and failures
|
// Highlight successes and failures
|
||||||
|
@ -41,13 +40,13 @@ export const displayChatActionButtons = function(message, html, data) {
|
||||||
// If the user is the message author or the actor owner, proceed
|
// If the user is the message author or the actor owner, proceed
|
||||||
let actor = game.actors.get(data.message.speaker.actor);
|
let actor = game.actors.get(data.message.speaker.actor);
|
||||||
if (actor && actor.isOwner) return;
|
if (actor && actor.isOwner) return;
|
||||||
else if ( game.user.isGM || (data.author.id === game.user.id)) return;
|
else if (game.user.isGM || data.author.id === game.user.id) return;
|
||||||
|
|
||||||
// Otherwise conceal action buttons except for saving throw
|
// Otherwise conceal action buttons except for saving throw
|
||||||
const buttons = chatCard.find("button[data-action]");
|
const buttons = chatCard.find("button[data-action]");
|
||||||
buttons.each((i, btn) => {
|
buttons.each((i, btn) => {
|
||||||
if (btn.dataset.action === "save") return;
|
if (btn.dataset.action === "save") return;
|
||||||
btn.style.display = "none"
|
btn.style.display = "none";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -64,7 +63,7 @@ export const displayChatActionButtons = function(message, html, data) {
|
||||||
* @return {Array} The extended options Array including new context choices
|
* @return {Array} The extended options Array including new context choices
|
||||||
*/
|
*/
|
||||||
export const addChatMessageContextOptions = function (html, options) {
|
export const addChatMessageContextOptions = function (html, options) {
|
||||||
let canApply = li => {
|
let canApply = (li) => {
|
||||||
const message = game.messages.get(li.data("messageId"));
|
const message = game.messages.get(li.data("messageId"));
|
||||||
return message?.isRoll && message?.isContentVisible && canvas.tokens?.controlled.length;
|
return message?.isRoll && message?.isContentVisible && canvas.tokens?.controlled.length;
|
||||||
};
|
};
|
||||||
|
@ -73,25 +72,25 @@ export const addChatMessageContextOptions = function(html, options) {
|
||||||
name: game.i18n.localize("SW5E.ChatContextDamage"),
|
name: game.i18n.localize("SW5E.ChatContextDamage"),
|
||||||
icon: '<i class="fas fa-user-minus"></i>',
|
icon: '<i class="fas fa-user-minus"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 1)
|
callback: (li) => applyChatCardDamage(li, 1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextHealing"),
|
name: game.i18n.localize("SW5E.ChatContextHealing"),
|
||||||
icon: '<i class="fas fa-user-plus"></i>',
|
icon: '<i class="fas fa-user-plus"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, -1)
|
callback: (li) => applyChatCardDamage(li, -1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextDoubleDamage"),
|
name: game.i18n.localize("SW5E.ChatContextDoubleDamage"),
|
||||||
icon: '<i class="fas fa-user-injured"></i>',
|
icon: '<i class="fas fa-user-injured"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 2)
|
callback: (li) => applyChatCardDamage(li, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: game.i18n.localize("SW5E.ChatContextHalfDamage"),
|
name: game.i18n.localize("SW5E.ChatContextHalfDamage"),
|
||||||
icon: '<i class="fas fa-user-shield"></i>',
|
icon: '<i class="fas fa-user-shield"></i>',
|
||||||
condition: canApply,
|
condition: canApply,
|
||||||
callback: li => applyChatCardDamage(li, 0.5)
|
callback: (li) => applyChatCardDamage(li, 0.5)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return options;
|
return options;
|
||||||
|
@ -110,10 +109,12 @@ export const addChatMessageContextOptions = function(html, options) {
|
||||||
function applyChatCardDamage(li, multiplier) {
|
function applyChatCardDamage(li, multiplier) {
|
||||||
const message = game.messages.get(li.data("messageId"));
|
const message = game.messages.get(li.data("messageId"));
|
||||||
const roll = message.roll;
|
const roll = message.roll;
|
||||||
return Promise.all(canvas.tokens.controlled.map(t => {
|
return Promise.all(
|
||||||
|
canvas.tokens.controlled.map((t) => {
|
||||||
const a = t.actor;
|
const a = t.actor;
|
||||||
return a.applyDamage(roll.total, multiplier);
|
return a.applyDamage(roll.total, multiplier);
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
export const ClassFeatures = {
|
export const ClassFeatures = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the default Initiative formula to customize special behaviors of the SW5e system.
|
* Override the default Initiative formula to customize special behaviors of the SW5e system.
|
||||||
* Apply advantage, proficiency, or bonuses where appropriate
|
* Apply advantage, proficiency, or bonuses where appropriate
|
||||||
|
@ -18,10 +17,15 @@ export const _getInitiativeFormula = function() {
|
||||||
nd = 2;
|
nd = 2;
|
||||||
mods += "kh";
|
mods += "kh";
|
||||||
}
|
}
|
||||||
const parts = [`${nd}d20${mods}`, init.mod, (init.prof !== 0) ? init.prof : null, (init.bonus !== 0) ? init.bonus : null];
|
const parts = [
|
||||||
|
`${nd}d20${mods}`,
|
||||||
|
init.mod,
|
||||||
|
init.prof !== 0 ? init.prof : null,
|
||||||
|
init.bonus !== 0 ? init.bonus : null
|
||||||
|
];
|
||||||
|
|
||||||
// Optionally apply Dexterity tiebreaker
|
// Optionally apply Dexterity tiebreaker
|
||||||
const tiebreaker = game.settings.get("sw5e", "initiativeDexTiebreaker");
|
const tiebreaker = game.settings.get("sw5e", "initiativeDexTiebreaker");
|
||||||
if (tiebreaker) parts.push(actor.data.data.abilities.dex.value / 100);
|
if (tiebreaker) parts.push(actor.data.data.abilities.dex.value / 100);
|
||||||
return parts.filter(p => p !== null).join(" + ");
|
return parts.filter((p) => p !== null).join(" + ");
|
||||||
};
|
};
|
||||||
|
|
1329
module/config.js
1329
module/config.js
File diff suppressed because it is too large
Load diff
|
@ -23,14 +23,19 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
|
||||||
const constantTerms = []; // Terms that are constant, and their associated operators
|
const constantTerms = []; // Terms that are constant, and their associated operators
|
||||||
let operators = []; // Temporary storage for operators before they are moved to one of the above
|
let operators = []; // Temporary storage for operators before they are moved to one of the above
|
||||||
|
|
||||||
for (let term of terms) { // For each term
|
for (let term of terms) {
|
||||||
if (term instanceof OperatorTerm) operators.push(term); // If the term is an addition/subtraction operator, push the term into the operators array
|
// For each term
|
||||||
else { // Otherwise the term is not an operator
|
if (term instanceof OperatorTerm) operators.push(term);
|
||||||
if (term instanceof DiceTerm) { // If the term is something rollable
|
// If the term is an addition/subtraction operator, push the term into the operators array
|
||||||
|
else {
|
||||||
|
// Otherwise the term is not an operator
|
||||||
|
if (term instanceof DiceTerm) {
|
||||||
|
// If the term is something rollable
|
||||||
rollableTerms.push(...operators); // Place all the operators into the rollableTerms array
|
rollableTerms.push(...operators); // Place all the operators into the rollableTerms array
|
||||||
rollableTerms.push(term); // Then place this rollable term into it as well
|
rollableTerms.push(term); // Then place this rollable term into it as well
|
||||||
} //
|
} //
|
||||||
else { // Otherwise, this must be a constant
|
else {
|
||||||
|
// Otherwise, this must be a constant
|
||||||
constantTerms.push(...operators); // Place the operators into the constantTerms array
|
constantTerms.push(...operators); // Place the operators into the constantTerms array
|
||||||
constantTerms.push(term); // Then also add this constant term to that array.
|
constantTerms.push(term); // Then also add this constant term to that array.
|
||||||
} //
|
} //
|
||||||
|
@ -45,7 +50,7 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
|
||||||
let constantPart = undefined;
|
let constantPart = undefined;
|
||||||
if (constantFormula) {
|
if (constantFormula) {
|
||||||
try {
|
try {
|
||||||
constantPart = Roll.safeEval(constantFormula)
|
constantPart = Roll.safeEval(constantFormula);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`Unable to evaluate constant term ${constantFormula} in simplifyRollFormula`);
|
console.warn(`Unable to evaluate constant term ${constantFormula} in simplifyRollFormula`);
|
||||||
}
|
}
|
||||||
|
@ -111,12 +116,28 @@ function _isUnsupportedTerm(term) {
|
||||||
* @return {Promise<D20Roll|null>} The evaluated D20Roll, or null if the workflow was cancelled
|
* @return {Promise<D20Roll|null>} The evaluated D20Roll, or null if the workflow was cancelled
|
||||||
*/
|
*/
|
||||||
export async function d20Roll({
|
export async function d20Roll({
|
||||||
parts=[], data={}, // Roll creation
|
parts = [],
|
||||||
advantage, disadvantage, fumble=1, critical=20, targetValue, elvenAccuracy, halflingLucky, reliableTalent, // Roll customization
|
data = {}, // Roll creation
|
||||||
chooseModifier=false, fastForward=false, event, template, title, dialogOptions, // Dialog configuration
|
advantage,
|
||||||
chatMessage=true, messageData={}, rollMode, speaker, flavor // Chat Message customization
|
disadvantage,
|
||||||
|
fumble = 1,
|
||||||
|
critical = 20,
|
||||||
|
targetValue,
|
||||||
|
elvenAccuracy,
|
||||||
|
halflingLucky,
|
||||||
|
reliableTalent, // Roll customization
|
||||||
|
chooseModifier = false,
|
||||||
|
fastForward = false,
|
||||||
|
event,
|
||||||
|
template,
|
||||||
|
title,
|
||||||
|
dialogOptions, // Dialog configuration
|
||||||
|
chatMessage = true,
|
||||||
|
messageData = {},
|
||||||
|
rollMode,
|
||||||
|
speaker,
|
||||||
|
flavor // Chat Message customization
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
|
||||||
// Handle input arguments
|
// Handle input arguments
|
||||||
const formula = ["1d20"].concat(parts).join(" + ");
|
const formula = ["1d20"].concat(parts).join(" + ");
|
||||||
const {advantageMode, isFF} = _determineAdvantageMode({advantage, disadvantage, fastForward, event});
|
const {advantageMode, isFF} = _determineAdvantageMode({advantage, disadvantage, fastForward, event});
|
||||||
|
@ -138,14 +159,17 @@ export async function d20Roll({
|
||||||
|
|
||||||
// Prompt a Dialog to further configure the D20Roll
|
// Prompt a Dialog to further configure the D20Roll
|
||||||
if (!isFF) {
|
if (!isFF) {
|
||||||
const configured = await roll.configureDialog({
|
const configured = await roll.configureDialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
chooseModifier,
|
chooseModifier,
|
||||||
defaultRollMode: defaultRollMode,
|
defaultRollMode: defaultRollMode,
|
||||||
defaultAction: advantageMode,
|
defaultAction: advantageMode,
|
||||||
defaultAbility: data?.item?.ability,
|
defaultAbility: data?.item?.ability,
|
||||||
template
|
template
|
||||||
}, dialogOptions);
|
},
|
||||||
|
dialogOptions
|
||||||
|
);
|
||||||
if (configured === null) return null;
|
if (configured === null) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +178,9 @@ export async function d20Roll({
|
||||||
|
|
||||||
// Create a Chat Message
|
// Create a Chat Message
|
||||||
if (speaker) {
|
if (speaker) {
|
||||||
console.warn(`You are passing the speaker argument to the d20Roll function directly which should instead be passed as an internal key of messageData`);
|
console.warn(
|
||||||
|
`You are passing the speaker argument to the d20Roll function directly which should instead be passed as an internal key of messageData`
|
||||||
|
);
|
||||||
messageData.speaker = speaker;
|
messageData.speaker = speaker;
|
||||||
}
|
}
|
||||||
if (roll && chatMessage) await roll.toMessage(messageData);
|
if (roll && chatMessage) await roll.toMessage(messageData);
|
||||||
|
@ -171,7 +197,8 @@ function _determineAdvantageMode({event, advantage=false, disadvantage=false, fa
|
||||||
const isFF = fastForward || (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
|
const isFF = fastForward || (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
|
||||||
let advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.NORMAL;
|
let advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.NORMAL;
|
||||||
if (advantage || event?.altKey) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.ADVANTAGE;
|
if (advantage || event?.altKey) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.ADVANTAGE;
|
||||||
else if ( disadvantage || event?.ctrlKey || event?.metaKey ) advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.DISADVANTAGE;
|
else if (disadvantage || event?.ctrlKey || event?.metaKey)
|
||||||
|
advantageMode = CONFIG.Dice.D20Roll.ADV_MODE.DISADVANTAGE;
|
||||||
return {isFF, advantageMode};
|
return {isFF, advantageMode};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,12 +237,25 @@ function _determineAdvantageMode({event, advantage=false, disadvantage=false, fa
|
||||||
* @return {Promise<DamageRoll|null>} The evaluated DamageRoll, or null if the workflow was canceled
|
* @return {Promise<DamageRoll|null>} The evaluated DamageRoll, or null if the workflow was canceled
|
||||||
*/
|
*/
|
||||||
export async function damageRoll({
|
export async function damageRoll({
|
||||||
parts=[], data, // Roll creation
|
parts = [],
|
||||||
critical=false, criticalBonusDice, criticalMultiplier, multiplyNumeric, powerfulCritical, // Damage customization
|
data, // Roll creation
|
||||||
fastForward=false, event, allowCritical=true, template, title, dialogOptions, // Dialog configuration
|
critical = false,
|
||||||
chatMessage=true, messageData={}, rollMode, speaker, flavor, // Chat Message customization
|
criticalBonusDice,
|
||||||
|
criticalMultiplier,
|
||||||
|
multiplyNumeric,
|
||||||
|
powerfulCritical, // Damage customization
|
||||||
|
fastForward = false,
|
||||||
|
event,
|
||||||
|
allowCritical = true,
|
||||||
|
template,
|
||||||
|
title,
|
||||||
|
dialogOptions, // Dialog configuration
|
||||||
|
chatMessage = true,
|
||||||
|
messageData = {},
|
||||||
|
rollMode,
|
||||||
|
speaker,
|
||||||
|
flavor // Chat Message customization
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
|
||||||
// Handle input arguments
|
// Handle input arguments
|
||||||
const defaultRollMode = rollMode || game.settings.get("core", "rollMode");
|
const defaultRollMode = rollMode || game.settings.get("core", "rollMode");
|
||||||
|
|
||||||
|
@ -233,13 +273,16 @@ export async function damageRoll({
|
||||||
|
|
||||||
// Prompt a Dialog to further configure the DamageRoll
|
// Prompt a Dialog to further configure the DamageRoll
|
||||||
if (!isFF) {
|
if (!isFF) {
|
||||||
const configured = await roll.configureDialog({
|
const configured = await roll.configureDialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
defaultRollMode: defaultRollMode,
|
defaultRollMode: defaultRollMode,
|
||||||
defaultCritical: isCritical,
|
defaultCritical: isCritical,
|
||||||
template,
|
template,
|
||||||
allowCritical
|
allowCritical
|
||||||
}, dialogOptions);
|
},
|
||||||
|
dialogOptions
|
||||||
|
);
|
||||||
if (configured === null) return null;
|
if (configured === null) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +291,9 @@ export async function damageRoll({
|
||||||
|
|
||||||
// Create a Chat Message
|
// Create a Chat Message
|
||||||
if (speaker) {
|
if (speaker) {
|
||||||
console.warn(`You are passing the speaker argument to the damageRoll function directly which should instead be passed as an internal key of messageData`);
|
console.warn(
|
||||||
|
`You are passing the speaker argument to the damageRoll function directly which should instead be passed as an internal key of messageData`
|
||||||
|
);
|
||||||
messageData.speaker = speaker;
|
messageData.speaker = speaker;
|
||||||
}
|
}
|
||||||
if (roll && chatMessage) await roll.toMessage(messageData);
|
if (roll && chatMessage) await roll.toMessage(messageData);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
export default class D20Roll extends Roll {
|
export default class D20Roll extends Roll {
|
||||||
constructor(formula, data, options) {
|
constructor(formula, data, options) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
if ( !((this.terms[0] instanceof Die) && (this.terms[0].faces === 20)) ) {
|
if (!(this.terms[0] instanceof Die && this.terms[0].faces === 20)) {
|
||||||
throw new Error(`Invalid D20Roll formula provided ${this._formula}`);
|
throw new Error(`Invalid D20Roll formula provided ${this._formula}`);
|
||||||
}
|
}
|
||||||
this.configureModifiers();
|
this.configureModifiers();
|
||||||
|
@ -31,8 +31,8 @@ export default class D20Roll extends Roll {
|
||||||
static ADV_MODE = {
|
static ADV_MODE = {
|
||||||
NORMAL: 0,
|
NORMAL: 0,
|
||||||
ADVANTAGE: 1,
|
ADVANTAGE: 1,
|
||||||
DISADVANTAGE: -1,
|
DISADVANTAGE: -1
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTML template path used to configure evaluation of this Roll
|
* The HTML template path used to configure evaluation of this Roll
|
||||||
|
@ -81,13 +81,11 @@ export default class D20Roll extends Roll {
|
||||||
d20.number = this.options.elvenAccuracy ? 3 : 2;
|
d20.number = this.options.elvenAccuracy ? 3 : 2;
|
||||||
d20.modifiers.push("kh");
|
d20.modifiers.push("kh");
|
||||||
d20.options.advantage = true;
|
d20.options.advantage = true;
|
||||||
}
|
} else if (this.hasDisadvantage) {
|
||||||
else if ( this.hasDisadvantage ) {
|
|
||||||
d20.number = 2;
|
d20.number = 2;
|
||||||
d20.modifiers.push("kl");
|
d20.modifiers.push("kl");
|
||||||
d20.options.disadvantage = true;
|
d20.options.disadvantage = true;
|
||||||
}
|
} else d20.number = 1;
|
||||||
else d20.number = 1;
|
|
||||||
|
|
||||||
// Assign critical and fumble thresholds
|
// Assign critical and fumble thresholds
|
||||||
if (this.options.critical) d20.options.critical = this.options.critical;
|
if (this.options.critical) d20.options.critical = this.options.critical;
|
||||||
|
@ -102,7 +100,6 @@ export default class D20Roll extends Roll {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async toMessage(messageData = {}, options = {}) {
|
async toMessage(messageData = {}, options = {}) {
|
||||||
|
|
||||||
// Evaluate the roll now so we have the results available to determine whether reliable talent came into play
|
// Evaluate the roll now so we have the results available to determine whether reliable talent came into play
|
||||||
if (!this._evaluated) await this.evaluate({async: true});
|
if (!this._evaluated) await this.evaluate({async: true});
|
||||||
|
|
||||||
|
@ -114,7 +111,7 @@ export default class D20Roll extends Roll {
|
||||||
// Add reliable talent to the d20-term flavor text if it applied
|
// Add reliable talent to the d20-term flavor text if it applied
|
||||||
if (this.options.reliableTalent) {
|
if (this.options.reliableTalent) {
|
||||||
const d20 = this.dice[0];
|
const d20 = this.dice[0];
|
||||||
const isRT = d20.results.every(r => !r.active || (r.result < 10));
|
const isRT = d20.results.every((r) => !r.active || r.result < 10);
|
||||||
const label = `(${game.i18n.localize("SW5E.FlagsReliableTalent")})`;
|
const label = `(${game.i18n.localize("SW5E.FlagsReliableTalent")})`;
|
||||||
if (isRT) d20.options.flavor = d20.options.flavor ? `${d20.options.flavor} (${label})` : label;
|
if (isRT) d20.options.flavor = d20.options.flavor ? `${d20.options.flavor} (${label})` : label;
|
||||||
}
|
}
|
||||||
|
@ -140,8 +137,17 @@ export default class D20Roll extends Roll {
|
||||||
* @param {object} options Additional Dialog customization options
|
* @param {object} options Additional Dialog customization options
|
||||||
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
||||||
*/
|
*/
|
||||||
async configureDialog({title, defaultRollMode, defaultAction=D20Roll.ADV_MODE.NORMAL, chooseModifier=false, defaultAbility, template}={}, options={}) {
|
async configureDialog(
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
defaultRollMode,
|
||||||
|
defaultAction = D20Roll.ADV_MODE.NORMAL,
|
||||||
|
chooseModifier = false,
|
||||||
|
defaultAbility,
|
||||||
|
template
|
||||||
|
} = {},
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
// Render the Dialog inner HTML
|
// Render the Dialog inner HTML
|
||||||
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
||||||
formula: `${this.formula} + @bonus`,
|
formula: `${this.formula} + @bonus`,
|
||||||
|
@ -154,32 +160,39 @@ export default class D20Roll extends Roll {
|
||||||
|
|
||||||
let defaultButton = "normal";
|
let defaultButton = "normal";
|
||||||
switch (defaultAction) {
|
switch (defaultAction) {
|
||||||
case D20Roll.ADV_MODE.ADVANTAGE: defaultButton = "advantage"; break;
|
case D20Roll.ADV_MODE.ADVANTAGE:
|
||||||
case D20Roll.ADV_MODE.DISADVANTAGE: defaultButton = "disadvantage"; break;
|
defaultButton = "advantage";
|
||||||
|
break;
|
||||||
|
case D20Roll.ADV_MODE.DISADVANTAGE:
|
||||||
|
defaultButton = "disadvantage";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the Dialog window and await submission of the form
|
// Create the Dialog window and await submission of the form
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
new Dialog({
|
new Dialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
buttons: {
|
buttons: {
|
||||||
advantage: {
|
advantage: {
|
||||||
label: game.i18n.localize("SW5E.Advantage"),
|
label: game.i18n.localize("SW5E.Advantage"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.ADVANTAGE))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.ADVANTAGE))
|
||||||
},
|
},
|
||||||
normal: {
|
normal: {
|
||||||
label: game.i18n.localize("SW5E.Normal"),
|
label: game.i18n.localize("SW5E.Normal"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.NORMAL))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.NORMAL))
|
||||||
},
|
},
|
||||||
disadvantage: {
|
disadvantage: {
|
||||||
label: game.i18n.localize("SW5E.Disadvantage"),
|
label: game.i18n.localize("SW5E.Disadvantage"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.DISADVANTAGE))
|
callback: (html) => resolve(this._onDialogSubmit(html, D20Roll.ADV_MODE.DISADVANTAGE))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: defaultButton,
|
default: defaultButton,
|
||||||
close: () => resolve(null)
|
close: () => resolve(null)
|
||||||
}, options).render(true);
|
},
|
||||||
|
options
|
||||||
|
).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +217,7 @@ export default class D20Roll extends Roll {
|
||||||
// Customize the modifier
|
// Customize the modifier
|
||||||
if (form.ability?.value) {
|
if (form.ability?.value) {
|
||||||
const abl = this.data.abilities[form.ability.value];
|
const abl = this.data.abilities[form.ability.value];
|
||||||
this.terms.findSplice(t => t.term === "@mod", new NumericTerm({number: abl.mod}));
|
this.terms.findSplice((t) => t.term === "@mod", new NumericTerm({number: abl.mod}));
|
||||||
this.options.flavor += ` (${CONFIG.SW5E.abilities[form.ability.value]})`;
|
this.options.flavor += ` (${CONFIG.SW5E.abilities[form.ability.value]})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ export default class DamageRoll extends Roll {
|
||||||
configureDamage() {
|
configureDamage() {
|
||||||
let flatBonus = 0;
|
let flatBonus = 0;
|
||||||
for (let [i, term] of this.terms.entries()) {
|
for (let [i, term] of this.terms.entries()) {
|
||||||
|
|
||||||
// Multiply dice terms
|
// Multiply dice terms
|
||||||
if (term instanceof DiceTerm) {
|
if (term instanceof DiceTerm) {
|
||||||
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
||||||
|
@ -53,33 +52,34 @@ export default class DamageRoll extends Roll {
|
||||||
|
|
||||||
// Powerful critical - maximize damage and reduce the multiplier by 1
|
// Powerful critical - maximize damage and reduce the multiplier by 1
|
||||||
if (this.options.powerfulCritical) {
|
if (this.options.powerfulCritical) {
|
||||||
flatBonus += (term.number * term.faces);
|
flatBonus += term.number * term.faces;
|
||||||
cm = Math.max(1, cm - 1);
|
cm = Math.max(1, cm - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alter the damage term
|
// Alter the damage term
|
||||||
let cb = (this.options.criticalBonusDice && (i === 0)) ? this.options.criticalBonusDice : 0;
|
let cb = this.options.criticalBonusDice && i === 0 ? this.options.criticalBonusDice : 0;
|
||||||
term.alter(cm, cb);
|
term.alter(cm, cb);
|
||||||
term.options.critical = true;
|
term.options.critical = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiply numeric terms
|
// Multiply numeric terms
|
||||||
else if ( this.options.multiplyNumeric && (term instanceof NumericTerm) ) {
|
else if (this.options.multiplyNumeric && term instanceof NumericTerm) {
|
||||||
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
term.options.baseNumber = term.options.baseNumber ?? term.number; // Reset back
|
||||||
term.number = term.options.baseNumber;
|
term.number = term.options.baseNumber;
|
||||||
if (this.isCritical) {
|
if (this.isCritical) {
|
||||||
term.number *= (this.options.criticalMultiplier ?? 2);
|
term.number *= this.options.criticalMultiplier ?? 2;
|
||||||
term.options.critical = true;
|
term.options.critical = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add powerful critical bonus
|
// Add powerful critical bonus
|
||||||
if ( this.options.powerfulCritical && (flatBonus > 0) ) {
|
if (this.options.powerfulCritical && flatBonus > 0) {
|
||||||
this.terms.push(new OperatorTerm({operator: "+"}));
|
this.terms.push(new OperatorTerm({operator: "+"}));
|
||||||
this.terms.push(new NumericTerm({number: flatBonus}, {flavor: game.i18n.localize("SW5E.PowerfulCritical")}));
|
this.terms.push(
|
||||||
|
new NumericTerm({number: flatBonus}, {flavor: game.i18n.localize("SW5E.PowerfulCritical")})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-compile the underlying formula
|
// Re-compile the underlying formula
|
||||||
|
@ -114,34 +114,39 @@ export default class DamageRoll extends Roll {
|
||||||
* @param {object} options Additional Dialog customization options
|
* @param {object} options Additional Dialog customization options
|
||||||
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
* @returns {Promise<D20Roll|null>} A resulting D20Roll object constructed with the dialog, or null if the dialog was closed
|
||||||
*/
|
*/
|
||||||
async configureDialog({title, defaultRollMode, defaultCritical=false, template, allowCritical=true}={}, options={}) {
|
async configureDialog(
|
||||||
|
{title, defaultRollMode, defaultCritical = false, template, allowCritical = true} = {},
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
// Render the Dialog inner HTML
|
// Render the Dialog inner HTML
|
||||||
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
const content = await renderTemplate(template ?? this.constructor.EVALUATION_TEMPLATE, {
|
||||||
formula: `${this.formula} + @bonus`,
|
formula: `${this.formula} + @bonus`,
|
||||||
defaultRollMode,
|
defaultRollMode,
|
||||||
rollModes: CONFIG.Dice.rollModes,
|
rollModes: CONFIG.Dice.rollModes
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the Dialog window and await submission of the form
|
// Create the Dialog window and await submission of the form
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
new Dialog({
|
new Dialog(
|
||||||
|
{
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
buttons: {
|
buttons: {
|
||||||
critical: {
|
critical: {
|
||||||
condition: allowCritical,
|
condition: allowCritical,
|
||||||
label: game.i18n.localize("SW5E.CriticalHit"),
|
label: game.i18n.localize("SW5E.CriticalHit"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, true))
|
callback: (html) => resolve(this._onDialogSubmit(html, true))
|
||||||
},
|
},
|
||||||
normal: {
|
normal: {
|
||||||
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
|
label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"),
|
||||||
callback: html => resolve(this._onDialogSubmit(html, false))
|
callback: (html) => resolve(this._onDialogSubmit(html, false))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
default: defaultCritical ? "critical" : "normal",
|
default: defaultCritical ? "critical" : "normal",
|
||||||
close: () => resolve(null)
|
close: () => resolve(null)
|
||||||
}, options).render(true);
|
},
|
||||||
|
options
|
||||||
|
).render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
module/effects.js
vendored
15
module/effects.js
vendored
|
@ -10,13 +10,15 @@ export function onManageActiveEffect(event, owner) {
|
||||||
const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null;
|
const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null;
|
||||||
switch (a.dataset.action) {
|
switch (a.dataset.action) {
|
||||||
case "create":
|
case "create":
|
||||||
return owner.createEmbeddedDocuments("ActiveEffect", [{
|
return owner.createEmbeddedDocuments("ActiveEffect", [
|
||||||
label: game.i18n.localize("SW5E.EffectNew"),
|
{
|
||||||
icon: "icons/svg/aura.svg",
|
"label": game.i18n.localize("SW5E.EffectNew"),
|
||||||
origin: owner.uuid,
|
"icon": "icons/svg/aura.svg",
|
||||||
|
"origin": owner.uuid,
|
||||||
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
|
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
|
||||||
disabled: li.dataset.effectType === "inactive"
|
"disabled": li.dataset.effectType === "inactive"
|
||||||
}]);
|
}
|
||||||
|
]);
|
||||||
case "edit":
|
case "edit":
|
||||||
return effect.sheet.render(true);
|
return effect.sheet.render(true);
|
||||||
case "delete":
|
case "delete":
|
||||||
|
@ -32,7 +34,6 @@ export function onManageActiveEffect(event, owner) {
|
||||||
* @return {object} Data for rendering
|
* @return {object} Data for rendering
|
||||||
*/
|
*/
|
||||||
export function prepareActiveEffectCategories(effects) {
|
export function prepareActiveEffectCategories(effects) {
|
||||||
|
|
||||||
// Define effect header categories
|
// Define effect header categories
|
||||||
const categories = {
|
const categories = {
|
||||||
temporary: {
|
temporary: {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import AbilityUseDialog from "../apps/ability-use-dialog.js";
|
||||||
* @extends {Item}
|
* @extends {Item}
|
||||||
*/
|
*/
|
||||||
export default class Item5e extends Item {
|
export default class Item5e extends Item {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Item Properties */
|
/* Item Properties */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -21,7 +20,6 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Case 1 - defined directly by the item
|
// Case 1 - defined directly by the item
|
||||||
if (itemData.ability) return itemData.ability;
|
if (itemData.ability) return itemData.ability;
|
||||||
|
|
||||||
// Case 2 - inferred from a parent actor
|
// Case 2 - inferred from a parent actor
|
||||||
else if (this.actor) {
|
else if (this.actor) {
|
||||||
const actorData = this.actor.data.data;
|
const actorData = this.actor.data.data;
|
||||||
|
@ -29,18 +27,20 @@ export default class Item5e extends Item {
|
||||||
// Powers - Use Actor powercasting modifier based on power school
|
// Powers - Use Actor powercasting modifier based on power school
|
||||||
if (this.data.type === "power") {
|
if (this.data.type === "power") {
|
||||||
switch (this.data.data.school) {
|
switch (this.data.data.school) {
|
||||||
case "lgt": return "wis";
|
case "lgt":
|
||||||
case "uni": return (actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod) ? "wis" : "cha";
|
return "wis";
|
||||||
case "drk": return "cha";
|
case "uni":
|
||||||
case "tec": return "int";
|
return actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod ? "wis" : "cha";
|
||||||
|
case "drk":
|
||||||
|
return "cha";
|
||||||
|
case "tec":
|
||||||
|
return "int";
|
||||||
}
|
}
|
||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tools - default to Intelligence
|
// Tools - default to Intelligence
|
||||||
else if (this.data.type === "tool") return "int";
|
else if (this.data.type === "tool") return "int";
|
||||||
|
|
||||||
// Weapons
|
// Weapons
|
||||||
else if (this.data.type === "weapon") {
|
else if (this.data.type === "weapon") {
|
||||||
const wt = itemData.weaponType;
|
const wt = itemData.weaponType;
|
||||||
|
@ -53,7 +53,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Finesse weapons - Str or Dex (PHB pg. 147)
|
// Finesse weapons - Str or Dex (PHB pg. 147)
|
||||||
else if (itemData.properties.fin === true) {
|
else if (itemData.properties.fin === true) {
|
||||||
return (actorData.abilities["dex"].mod >= actorData.abilities["str"].mod) ? "dex" : "str";
|
return actorData.abilities["dex"].mod >= actorData.abilities["str"].mod ? "dex" : "str";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapons - Dex (PH p.194)
|
// Ranged weapons - Dex (PH p.194)
|
||||||
|
@ -63,7 +63,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 3 - unknown
|
// Case 3 - unknown
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -103,7 +103,7 @@ export default class Item5e extends Item {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
get isHealing() {
|
get isHealing() {
|
||||||
return (this.data.data.actionType === "heal") && this.data.data.damage.parts.length;
|
return this.data.data.actionType === "heal" && this.data.data.damage.parts.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -136,7 +136,7 @@ export default class Item5e extends Item {
|
||||||
*/
|
*/
|
||||||
get hasAreaTarget() {
|
get hasAreaTarget() {
|
||||||
const target = this.data.data.target;
|
const target = this.data.data.target;
|
||||||
return target && (target.type in CONFIG.SW5E.areaTargetTypes);
|
return target && target.type in CONFIG.SW5E.areaTargetTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -148,7 +148,7 @@ export default class Item5e extends Item {
|
||||||
get hasLimitedUses() {
|
get hasLimitedUses() {
|
||||||
let chg = this.data.data.recharge || {};
|
let chg = this.data.data.recharge || {};
|
||||||
let uses = this.data.data.uses || {};
|
let uses = this.data.data.uses || {};
|
||||||
return !!chg.value || (uses.per && (uses.max > 0));
|
return !!chg.value || (uses.per && uses.max > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -165,7 +165,7 @@ export default class Item5e extends Item {
|
||||||
const itemData = this.data;
|
const itemData = this.data;
|
||||||
const data = itemData.data;
|
const data = itemData.data;
|
||||||
const C = CONFIG.SW5E;
|
const C = CONFIG.SW5E;
|
||||||
const labels = this.labels = {};
|
const labels = (this.labels = {});
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
if (itemData.type === "class") {
|
if (itemData.type === "class") {
|
||||||
|
@ -188,9 +188,12 @@ export default class Item5e extends Item {
|
||||||
// Feat Items
|
// Feat Items
|
||||||
else if (itemData.type === "feat") {
|
else if (itemData.type === "feat") {
|
||||||
const act = data.activation;
|
const act = data.activation;
|
||||||
if ( act && (act.type === C.abilityActivationTypes.legendary) ) labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel");
|
if (act && act.type === C.abilityActivationTypes.legendary)
|
||||||
else if ( act && (act.type === C.abilityActivationTypes.lair) ) labels.featType = game.i18n.localize("SW5E.LairActionLabel");
|
labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel");
|
||||||
else if ( act && act.type ) labels.featType = game.i18n.localize(data.damage.length ? "SW5E.Attack" : "SW5E.Action");
|
else if (act && act.type === C.abilityActivationTypes.lair)
|
||||||
|
labels.featType = game.i18n.localize("SW5E.LairActionLabel");
|
||||||
|
else if (act && act.type)
|
||||||
|
labels.featType = game.i18n.localize(data.damage.length ? "SW5E.Attack" : "SW5E.Action");
|
||||||
else labels.featType = game.i18n.localize("SW5E.Passive");
|
else labels.featType = game.i18n.localize("SW5E.Passive");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +242,6 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Activated Items
|
// Activated Items
|
||||||
if (data.hasOwnProperty("activation")) {
|
if (data.hasOwnProperty("activation")) {
|
||||||
|
|
||||||
// Ability Activation Label
|
// Ability Activation Label
|
||||||
let act = data.activation || {};
|
let act = data.activation || {};
|
||||||
if (act) labels.activation = [act.cost, C.abilityActivationTypes[act.type]].filterJoin(" ");
|
if (act) labels.activation = [act.cost, C.abilityActivationTypes[act.type]].filterJoin(" ");
|
||||||
|
@ -268,7 +270,9 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Recharge Label
|
// Recharge Label
|
||||||
let chg = data.recharge || {};
|
let chg = data.recharge || {};
|
||||||
labels.recharge = `${game.i18n.localize("SW5E.Recharge")} [${chg.value}${parseInt(chg.value) < 6 ? "+" : ""}]`;
|
labels.recharge = `${game.i18n.localize("SW5E.Recharge")} [${chg.value}${
|
||||||
|
parseInt(chg.value) < 6 ? "+" : ""
|
||||||
|
}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item Actions
|
// Item Actions
|
||||||
|
@ -276,8 +280,11 @@ export default class Item5e extends Item {
|
||||||
// Damage
|
// Damage
|
||||||
let dam = data.damage || {};
|
let dam = data.damage || {};
|
||||||
if (dam.parts) {
|
if (dam.parts) {
|
||||||
labels.damage = dam.parts.map(d => d[0]).join(" + ").replace(/\+ -/g, "- ");
|
labels.damage = dam.parts
|
||||||
labels.damageTypes = dam.parts.map(d => C.damageTypes[d[1]]).join(", ");
|
.map((d) => d[0])
|
||||||
|
.join(" + ")
|
||||||
|
.replace(/\+ -/g, "- ");
|
||||||
|
labels.damageTypes = dam.parts.map((d) => C.damageTypes[d[1]]).join(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,10 +330,10 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
const derivedDamage = itemData.damage?.parts?.map((damagePart) => ({
|
const derivedDamage = itemData.damage?.parts?.map((damagePart) => ({
|
||||||
formula: simplifyRollFormula(damagePart[0], rollData, {constantFirst: false}),
|
formula: simplifyRollFormula(damagePart[0], rollData, {constantFirst: false}),
|
||||||
damageType: damagePart[1],
|
damageType: damagePart[1]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.labels.derivedDamage = derivedDamage
|
this.labels.derivedDamage = derivedDamage;
|
||||||
|
|
||||||
return derivedDamage;
|
return derivedDamage;
|
||||||
}
|
}
|
||||||
|
@ -396,7 +403,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Include the item's innate attack bonus as the initial value and label
|
// Include the item's innate attack bonus as the initial value and label
|
||||||
if (itemData.attackBonus) {
|
if (itemData.attackBonus) {
|
||||||
parts.push(itemData.attackBonus)
|
parts.push(itemData.attackBonus);
|
||||||
this.labels.toHit = itemData.attackBonus;
|
this.labels.toHit = itemData.attackBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,14 +423,15 @@ export default class Item5e extends Item {
|
||||||
if (actorBonus.attack) parts.push(actorBonus.attack);
|
if (actorBonus.attack) parts.push(actorBonus.attack);
|
||||||
|
|
||||||
// One-time bonus provided by consumed ammunition
|
// One-time bonus provided by consumed ammunition
|
||||||
if ( (itemData.consume?.type === 'ammo') && !!this.actor.items ) {
|
if (itemData.consume?.type === "ammo" && !!this.actor.items) {
|
||||||
const ammoItemData = this.actor.items.get(itemData.consume.target)?.data;
|
const ammoItemData = this.actor.items.get(itemData.consume.target)?.data;
|
||||||
|
|
||||||
if (ammoItemData) {
|
if (ammoItemData) {
|
||||||
const ammoItemQuantity = ammoItemData.data.quantity;
|
const ammoItemQuantity = ammoItemData.data.quantity;
|
||||||
const ammoCanBeConsumed = ammoItemQuantity && (ammoItemQuantity - (itemData.consume.amount ?? 0) >= 0);
|
const ammoCanBeConsumed = ammoItemQuantity && ammoItemQuantity - (itemData.consume.amount ?? 0) >= 0;
|
||||||
const ammoItemAttackBonus = ammoItemData.data.attackBonus;
|
const ammoItemAttackBonus = ammoItemData.data.attackBonus;
|
||||||
const ammoIsTypeConsumable = (ammoItemData.type === "consumable") && (ammoItemData.data.consumableType === "ammo")
|
const ammoIsTypeConsumable =
|
||||||
|
ammoItemData.type === "consumable" && ammoItemData.data.consumableType === "ammo";
|
||||||
if (ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable) {
|
if (ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable) {
|
||||||
parts.push("@ammo");
|
parts.push("@ammo");
|
||||||
rollData["ammo"] = ammoItemAttackBonus;
|
rollData["ammo"] = ammoItemAttackBonus;
|
||||||
|
@ -432,9 +440,9 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Condense the resulting attack bonus formula into a simplified label
|
// Condense the resulting attack bonus formula into a simplified label
|
||||||
let toHitLabel = simplifyRollFormula(parts.join('+'), rollData).trim();
|
let toHitLabel = simplifyRollFormula(parts.join("+"), rollData).trim();
|
||||||
if (toHitLabel.charAt(0) !== '-') {
|
if (toHitLabel.charAt(0) !== "-") {
|
||||||
toHitLabel = '+ ' + toHitLabel
|
toHitLabel = "+ " + toHitLabel;
|
||||||
}
|
}
|
||||||
this.labels.toHit = toHitLabel;
|
this.labels.toHit = toHitLabel;
|
||||||
|
|
||||||
|
@ -460,7 +468,7 @@ export default class Item5e extends Item {
|
||||||
max = Roll.replaceFormulaData(max, this.actor.getRollData(), {missing: 0, warn: true});
|
max = Roll.replaceFormulaData(max, this.actor.getRollData(), {missing: 0, warn: true});
|
||||||
max = Roll.safeEval(max);
|
max = Roll.safeEval(max);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Problem preparing Max uses for', this.data.name, e);
|
console.error("Problem preparing Max uses for", this.data.name, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +499,7 @@ export default class Item5e extends Item {
|
||||||
const isPower = this.type === "power"; // Does the item require a power slot?
|
const isPower = this.type === "power"; // Does the item require a power slot?
|
||||||
// TODO: Possibly Mod this to not consume slots based on class?
|
// TODO: Possibly Mod this to not consume slots based on class?
|
||||||
// We could use this for feats and architypes that let a character cast one slot every rest or so
|
// We could use this for feats and architypes that let a character cast one slot every rest or so
|
||||||
const requirePowerSlot = isPower && (id.level > 0) && CONFIG.SW5E.powerUpcastModes.includes(id.preparation.mode);
|
const requirePowerSlot = isPower && id.level > 0 && CONFIG.SW5E.powerUpcastModes.includes(id.preparation.mode);
|
||||||
|
|
||||||
// Define follow-up actions resulting from the item usage
|
// Define follow-up actions resulting from the item usage
|
||||||
let createMeasuredTemplate = hasArea; // Trigger a template creation
|
let createMeasuredTemplate = hasArea; // Trigger a template creation
|
||||||
|
@ -504,12 +512,16 @@ export default class Item5e extends Item {
|
||||||
if (requirePowerSlot) consumePowerLevel = id.preparation.mode === "pact" ? "pact" : `power${id.level}`;
|
if (requirePowerSlot) consumePowerLevel = id.preparation.mode === "pact" ? "pact" : `power${id.level}`;
|
||||||
|
|
||||||
// Display a configuration dialog to customize the usage
|
// Display a configuration dialog to customize the usage
|
||||||
const needsConfiguration = createMeasuredTemplate || consumeRecharge || (consumeResource && !['simpleB', 'martialB'].includes(id.weaponType)) || consumePowerSlot || (consumeUsage && !['simpleB', 'martialB'].includes(id.weaponType));
|
const needsConfiguration =
|
||||||
|
createMeasuredTemplate ||
|
||||||
|
consumeRecharge ||
|
||||||
|
(consumeResource && !["simpleB", "martialB"].includes(id.weaponType)) ||
|
||||||
|
consumePowerSlot ||
|
||||||
|
(consumeUsage && !["simpleB", "martialB"].includes(id.weaponType));
|
||||||
if (configureDialog && needsConfiguration) {
|
if (configureDialog && needsConfiguration) {
|
||||||
const configuration = await AbilityUseDialog.create(this);
|
const configuration = await AbilityUseDialog.create(this);
|
||||||
if (!configuration) return;
|
if (!configuration) return;
|
||||||
|
|
||||||
|
|
||||||
// Determine consumption preferences
|
// Determine consumption preferences
|
||||||
createMeasuredTemplate = Boolean(configuration.placeTemplate);
|
createMeasuredTemplate = Boolean(configuration.placeTemplate);
|
||||||
consumeUsage = Boolean(configuration.consumeUse);
|
consumeUsage = Boolean(configuration.consumeUse);
|
||||||
|
@ -531,14 +543,20 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the item can be used by testing for resource consumption
|
// Determine whether the item can be used by testing for resource consumption
|
||||||
const usage = item._getUsageUpdates({consumeRecharge, consumeResource, consumePowerLevel, consumeUsage, consumeQuantity});
|
const usage = item._getUsageUpdates({
|
||||||
|
consumeRecharge,
|
||||||
|
consumeResource,
|
||||||
|
consumePowerLevel,
|
||||||
|
consumeUsage,
|
||||||
|
consumeQuantity
|
||||||
|
});
|
||||||
if (!usage) return;
|
if (!usage) return;
|
||||||
|
|
||||||
const {actorUpdates, itemUpdates, resourceUpdates} = usage;
|
const {actorUpdates, itemUpdates, resourceUpdates} = usage;
|
||||||
|
|
||||||
// Commit pending data updates
|
// Commit pending data updates
|
||||||
if (!foundry.utils.isObjectEmpty(itemUpdates)) await item.update(itemUpdates);
|
if (!foundry.utils.isObjectEmpty(itemUpdates)) await item.update(itemUpdates);
|
||||||
if ( consumeQuantity && (item.data.data.quantity === 0) ) await item.delete();
|
if (consumeQuantity && item.data.data.quantity === 0) await item.delete();
|
||||||
if (!foundry.utils.isObjectEmpty(actorUpdates)) await actor.update(actorUpdates);
|
if (!foundry.utils.isObjectEmpty(actorUpdates)) await actor.update(actorUpdates);
|
||||||
if (!foundry.utils.isObjectEmpty(resourceUpdates)) {
|
if (!foundry.utils.isObjectEmpty(resourceUpdates)) {
|
||||||
const resource = actor.items.get(id.consume?.target);
|
const resource = actor.items.get(id.consume?.target);
|
||||||
|
@ -569,7 +587,6 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getUsageUpdates({consumeQuantity, consumeRecharge, consumeResource, consumePowerLevel, consumeUsage}) {
|
_getUsageUpdates({consumeQuantity, consumeRecharge, consumeResource, consumePowerLevel, consumeUsage}) {
|
||||||
|
|
||||||
// Reference item data
|
// Reference item data
|
||||||
const id = this.data.data;
|
const id = this.data.data;
|
||||||
const actorUpdates = {};
|
const actorUpdates = {};
|
||||||
|
@ -599,7 +616,7 @@ export default class Item5e extends Item {
|
||||||
const fp = this.actor.data.data.attributes.force.points;
|
const fp = this.actor.data.data.attributes.force.points;
|
||||||
const tp = this.actor.data.data.attributes.tech.points;
|
const tp = this.actor.data.data.attributes.tech.points;
|
||||||
const powerCost = id.level + 1;
|
const powerCost = id.level + 1;
|
||||||
const innatePower = this.actor.data.data.attributes.powercasting === 'innate';
|
const innatePower = this.actor.data.data.attributes.powercasting === "innate";
|
||||||
if (!innatePower) {
|
if (!innatePower) {
|
||||||
switch (id.school) {
|
switch (id.school) {
|
||||||
case "lgt":
|
case "lgt":
|
||||||
|
@ -608,7 +625,9 @@ export default class Item5e extends Item {
|
||||||
const powers = Number(level?.fvalue ?? 0);
|
const powers = Number(level?.fvalue ?? 0);
|
||||||
if (powers === 0) {
|
if (powers === 0) {
|
||||||
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
actorUpdates[`data.powers.${consumePowerLevel}.fvalue`] = Math.max(powers - 1, 0);
|
actorUpdates[`data.powers.${consumePowerLevel}.fvalue`] = Math.max(powers - 1, 0);
|
||||||
|
@ -624,7 +643,9 @@ export default class Item5e extends Item {
|
||||||
const powers = Number(level?.tvalue ?? 0);
|
const powers = Number(level?.tvalue ?? 0);
|
||||||
if (powers === 0) {
|
if (powers === 0) {
|
||||||
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`);
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
actorUpdates[`data.powers.${consumePowerLevel}.tvalue`] = Math.max(powers - 1, 0);
|
actorUpdates[`data.powers.${consumePowerLevel}.tvalue`] = Math.max(powers - 1, 0);
|
||||||
|
@ -640,7 +661,6 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Consume Limited Usage
|
// Consume Limited Usage
|
||||||
if (consumeUsage) {
|
if (consumeUsage) {
|
||||||
const uses = id.uses || {};
|
const uses = id.uses || {};
|
||||||
|
@ -655,7 +675,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce quantity if not reducing usages or if usages hit 0 and we are set to consumeQuantity
|
// Reduce quantity if not reducing usages or if usages hit 0 and we are set to consumeQuantity
|
||||||
if ( consumeQuantity && (!used || (remaining === 0)) ) {
|
if (consumeQuantity && (!used || remaining === 0)) {
|
||||||
const q = Number(id.quantity ?? 1);
|
const q = Number(id.quantity ?? 1);
|
||||||
if (q >= 1) {
|
if (q >= 1) {
|
||||||
used = true;
|
used = true;
|
||||||
|
@ -694,7 +714,9 @@ export default class Item5e extends Item {
|
||||||
// No consumed target
|
// No consumed target
|
||||||
const typeLabel = CONFIG.SW5E.abilityConsumptionTypes[consume.type];
|
const typeLabel = CONFIG.SW5E.abilityConsumptionTypes[consume.type];
|
||||||
if (!consume.target) {
|
if (!consume.target) {
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoResource", {name: this.name, type: typeLabel}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ConsumeWarningNoResource", {name: this.name, type: typeLabel})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,7 +755,9 @@ export default class Item5e extends Item {
|
||||||
// Verify that the required quantity is available
|
// Verify that the required quantity is available
|
||||||
let remaining = quantity - amount;
|
let remaining = quantity - amount;
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoQuantity", {name: this.name, type: typeLabel}));
|
ui.notifications.warn(
|
||||||
|
game.i18n.format("SW5E.ConsumeWarningNoQuantity", {name: this.name, type: typeLabel})
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +789,6 @@ export default class Item5e extends Item {
|
||||||
* the prepared message data (if false)
|
* the prepared message data (if false)
|
||||||
*/
|
*/
|
||||||
async displayCard({rollMode, createMessage = true} = {}) {
|
async displayCard({rollMode, createMessage = true} = {}) {
|
||||||
|
|
||||||
// Render the chat card template
|
// Render the chat card template
|
||||||
const token = this.actor.token;
|
const token = this.actor.token;
|
||||||
const templateData = {
|
const templateData = {
|
||||||
|
@ -796,7 +819,7 @@ export default class Item5e extends Item {
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the Item was destroyed in the process of displaying its card - embed the item data in the chat message
|
// If the Item was destroyed in the process of displaying its card - embed the item data in the chat message
|
||||||
if ( (this.data.type === "consumable") && !this.actor.items.has(this.id) ) {
|
if (this.data.type === "consumable" && !this.actor.items.has(this.id)) {
|
||||||
chatData.flags["sw5e.itemData"] = this.data;
|
chatData.flags["sw5e.itemData"] = this.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,10 +853,11 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Equipment properties
|
// Equipment properties
|
||||||
if (data.hasOwnProperty("equipped") && !["loot", "tool"].includes(this.data.type)) {
|
if (data.hasOwnProperty("equipped") && !["loot", "tool"].includes(this.data.type)) {
|
||||||
if ( data.attunement === CONFIG.SW5E.attunementTypes.REQUIRED ) props.push(game.i18n.localize(CONFIG.SW5E.attunements[CONFIG.SW5E.attunementTypes.REQUIRED]));
|
if (data.attunement === CONFIG.SW5E.attunementTypes.REQUIRED)
|
||||||
|
props.push(game.i18n.localize(CONFIG.SW5E.attunements[CONFIG.SW5E.attunementTypes.REQUIRED]));
|
||||||
props.push(
|
props.push(
|
||||||
game.i18n.localize(data.equipped ? "SW5E.Equipped" : "SW5E.Unequipped"),
|
game.i18n.localize(data.equipped ? "SW5E.Equipped" : "SW5E.Unequipped"),
|
||||||
game.i18n.localize(data.proficient ? "SW5E.Proficient" : "SW5E.NotProficient"),
|
game.i18n.localize(data.proficient ? "SW5E.Proficient" : "SW5E.NotProficient")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +872,7 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter properties and return
|
// Filter properties and return
|
||||||
data.properties = props.filter(p => !!p);
|
data.properties = props.filter((p) => !!p);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,9 +897,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_weaponChatData(data, labels, props) {
|
_weaponChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(CONFIG.SW5E.weaponTypes[data.weaponType]);
|
||||||
CONFIG.SW5E.weaponTypes[data.weaponType],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -899,10 +921,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_toolChatData(data, labels, props) {
|
_toolChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(CONFIG.SW5E.abilities[data.ability] || null, CONFIG.SW5E.proficiencyLevels[data.proficient || 0]);
|
||||||
CONFIG.SW5E.abilities[data.ability] || null,
|
|
||||||
CONFIG.SW5E.proficiencyLevels[data.proficient || 0]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -926,10 +945,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_powerChatData(data, labels, props) {
|
_powerChatData(data, labels, props) {
|
||||||
props.push(
|
props.push(labels.level, labels.components + (labels.materials ? ` (${labels.materials})` : ""));
|
||||||
labels.level,
|
|
||||||
labels.components + (labels.materials ? ` (${labels.materials})` : "")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -974,7 +990,7 @@ export default class Item5e extends Item {
|
||||||
if (ammo?.data) {
|
if (ammo?.data) {
|
||||||
const q = ammo.data.data.quantity;
|
const q = ammo.data.data.quantity;
|
||||||
const consumeAmount = consume.amount ?? 0;
|
const consumeAmount = consume.amount ?? 0;
|
||||||
if ( q && (q - consumeAmount >= 0) ) {
|
if (q && q - consumeAmount >= 0) {
|
||||||
this._ammo = ammo;
|
this._ammo = ammo;
|
||||||
title += ` [${ammo.name}]`;
|
title += ` [${ammo.name}]`;
|
||||||
}
|
}
|
||||||
|
@ -987,7 +1003,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose roll options
|
// Compose roll options
|
||||||
const rollConfig = mergeObject({
|
const rollConfig = mergeObject(
|
||||||
|
{
|
||||||
parts: parts,
|
parts: parts,
|
||||||
actor: this.actor,
|
actor: this.actor,
|
||||||
data: rollData,
|
data: rollData,
|
||||||
|
@ -1000,13 +1017,15 @@ export default class Item5e extends Item {
|
||||||
left: window.innerWidth - 710
|
left: window.innerWidth - 710
|
||||||
},
|
},
|
||||||
messageData: {"flags.sw5e.roll": {type: "attack", itemId: this.id}}
|
messageData: {"flags.sw5e.roll": {type: "attack", itemId: this.id}}
|
||||||
}, options);
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
rollConfig.event = options.event;
|
rollConfig.event = options.event;
|
||||||
|
|
||||||
// Expanded critical hit thresholds
|
// Expanded critical hit thresholds
|
||||||
if (( this.data.type === "weapon" ) && flags.weaponCriticalThreshold) {
|
if (this.data.type === "weapon" && flags.weaponCriticalThreshold) {
|
||||||
rollConfig.critical = parseInt(flags.weaponCriticalThreshold);
|
rollConfig.critical = parseInt(flags.weaponCriticalThreshold);
|
||||||
} else if (( this.data.type === "power" ) && flags.powerCriticalThreshold) {
|
} else if (this.data.type === "power" && flags.powerCriticalThreshold) {
|
||||||
rollConfig.critical = parseInt(flags.powerCriticalThreshold);
|
rollConfig.critical = parseInt(flags.powerCriticalThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,7 +1034,6 @@ export default class Item5e extends Item {
|
||||||
rollConfig.elvenAccuracy = true;
|
rollConfig.elvenAccuracy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Apply Halfling Lucky
|
// Apply Halfling Lucky
|
||||||
if (flags.halflingLucky) rollConfig.halflingLucky = true;
|
if (flags.halflingLucky) rollConfig.halflingLucky = true;
|
||||||
|
|
||||||
|
@ -1047,7 +1065,7 @@ export default class Item5e extends Item {
|
||||||
const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id}};
|
const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id}};
|
||||||
|
|
||||||
// Get roll data
|
// Get roll data
|
||||||
const parts = itemData.damage.parts.map(d => d[0]);
|
const parts = itemData.damage.parts.map((d) => d[0]);
|
||||||
const rollData = this.getRollData();
|
const rollData = this.getRollData();
|
||||||
if (powerLevel) rollData.item.level = powerLevel;
|
if (powerLevel) rollData.item.level = powerLevel;
|
||||||
|
|
||||||
|
@ -1079,12 +1097,12 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale damage from up-casting powers
|
// Scale damage from up-casting powers
|
||||||
if ( (this.data.type === "power") ) {
|
if (this.data.type === "power") {
|
||||||
if ( (itemData.scaling.mode === "atwill") ) {
|
if (itemData.scaling.mode === "atwill") {
|
||||||
const level = this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel;
|
const level =
|
||||||
|
this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel;
|
||||||
this._scaleAtWillDamage(parts, itemData.scaling.formula, level, rollData);
|
this._scaleAtWillDamage(parts, itemData.scaling.formula, level, rollData);
|
||||||
}
|
} else if (powerLevel && itemData.scaling.mode === "level" && itemData.scaling.formula) {
|
||||||
else if ( powerLevel && (itemData.scaling.mode === "level") && itemData.scaling.formula ) {
|
|
||||||
const scaling = itemData.scaling.formula;
|
const scaling = itemData.scaling.formula;
|
||||||
this._scalePowerDamage(parts, itemData.level, powerLevel, scaling, rollData);
|
this._scalePowerDamage(parts, itemData.level, powerLevel, scaling, rollData);
|
||||||
}
|
}
|
||||||
|
@ -1092,7 +1110,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Add damage bonus formula
|
// Add damage bonus formula
|
||||||
const actorBonus = getProperty(actorData, `bonuses.${itemData.actionType}`) || {};
|
const actorBonus = getProperty(actorData, `bonuses.${itemData.actionType}`) || {};
|
||||||
if ( actorBonus.damage && (parseInt(actorBonus.damage) !== 0) ) {
|
if (actorBonus.damage && parseInt(actorBonus.damage) !== 0) {
|
||||||
parts.push(actorBonus.damage);
|
parts.push(actorBonus.damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1100,9 +1118,9 @@ export default class Item5e extends Item {
|
||||||
const ammoData = this._ammo?.data;
|
const ammoData = this._ammo?.data;
|
||||||
|
|
||||||
// only add the ammunition damage if the ammution is a consumable with type 'ammo'
|
// only add the ammunition damage if the ammution is a consumable with type 'ammo'
|
||||||
if ( this._ammo && (ammoData.type === "consumable") && (ammoData.data.consumableType === "ammo") ) {
|
if (this._ammo && ammoData.type === "consumable" && ammoData.data.consumableType === "ammo") {
|
||||||
parts.push("@ammo");
|
parts.push("@ammo");
|
||||||
rollData["ammo"] = ammoData.data.damage.parts.map(p => p[0]).join("+");
|
rollData["ammo"] = ammoData.data.damage.parts.map((p) => p[0]).join("+");
|
||||||
rollConfig.flavor += ` [${this._ammo.name}]`;
|
rollConfig.flavor += ` [${this._ammo.name}]`;
|
||||||
delete this._ammo;
|
delete this._ammo;
|
||||||
}
|
}
|
||||||
|
@ -1164,10 +1182,10 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Attempt to simplify by combining like dice terms
|
// Attempt to simplify by combining like dice terms
|
||||||
let simplified = false;
|
let simplified = false;
|
||||||
if ( (s.terms[0] instanceof Die) && (s.terms.length === 1) ) {
|
if (s.terms[0] instanceof Die && s.terms.length === 1) {
|
||||||
const d0 = p0.terms[0];
|
const d0 = p0.terms[0];
|
||||||
const s0 = s.terms[0];
|
const s0 = s.terms[0];
|
||||||
if ( (d0 instanceof Die) && (d0.faces === s0.faces) && d0.modifiers.equals(s0.modifiers) ) {
|
if (d0 instanceof Die && d0.faces === s0.faces && d0.modifiers.equals(s0.modifiers)) {
|
||||||
d0.number += s0.number;
|
d0.number += s0.number;
|
||||||
parts[0] = p0.formula;
|
parts[0] = p0.formula;
|
||||||
simplified = true;
|
simplified = true;
|
||||||
|
@ -1225,10 +1243,14 @@ export default class Item5e extends Item {
|
||||||
const success = roll.total >= parseInt(data.recharge.value);
|
const success = roll.total >= parseInt(data.recharge.value);
|
||||||
|
|
||||||
// Display a Chat Message
|
// Display a Chat Message
|
||||||
const promises = [roll.toMessage({
|
const promises = [
|
||||||
flavor: `${game.i18n.format("SW5E.ItemRechargeCheck", {name: this.name})} - ${game.i18n.localize(success ? "SW5E.ItemRechargeSuccess" : "SW5E.ItemRechargeFailure")}`,
|
roll.toMessage({
|
||||||
|
flavor: `${game.i18n.format("SW5E.ItemRechargeCheck", {name: this.name})} - ${game.i18n.localize(
|
||||||
|
success ? "SW5E.ItemRechargeSuccess" : "SW5E.ItemRechargeFailure"
|
||||||
|
)}`,
|
||||||
speaker: ChatMessage.getSpeaker({actor: this.actor, token: this.actor.token})
|
speaker: ChatMessage.getSpeaker({actor: this.actor, token: this.actor.token})
|
||||||
})];
|
})
|
||||||
|
];
|
||||||
|
|
||||||
// Update the Item data
|
// Update the Item data
|
||||||
if (success) promises.push(this.update({"data.recharge.charged": true}));
|
if (success) promises.push(this.update({"data.recharge.charged": true}));
|
||||||
|
@ -1258,7 +1280,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose the roll data
|
// Compose the roll data
|
||||||
const rollConfig = mergeObject({
|
const rollConfig = mergeObject(
|
||||||
|
{
|
||||||
parts: parts,
|
parts: parts,
|
||||||
data: rollData,
|
data: rollData,
|
||||||
title: title,
|
title: title,
|
||||||
|
@ -1267,13 +1290,15 @@ export default class Item5e extends Item {
|
||||||
dialogOptions: {
|
dialogOptions: {
|
||||||
width: 400,
|
width: 400,
|
||||||
top: options.event ? options.event.clientY - 80 : null,
|
top: options.event ? options.event.clientY - 80 : null,
|
||||||
left: window.innerWidth - 710,
|
left: window.innerWidth - 710
|
||||||
},
|
},
|
||||||
chooseModifier: true,
|
chooseModifier: true,
|
||||||
halflingLucky: this.actor.getFlag("sw5e", "halflingLucky") || false,
|
halflingLucky: this.actor.getFlag("sw5e", "halflingLucky") || false,
|
||||||
reliableTalent: (this.data.data.proficient >= 1) && this.actor.getFlag("sw5e", "reliableTalent"),
|
reliableTalent: this.data.data.proficient >= 1 && this.actor.getFlag("sw5e", "reliableTalent"),
|
||||||
messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id}}
|
messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id}}
|
||||||
}, options);
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
rollConfig.event = options.event;
|
rollConfig.event = options.event;
|
||||||
|
|
||||||
// Call the roll helper utility
|
// Call the roll helper utility
|
||||||
|
@ -1296,13 +1321,15 @@ export default class Item5e extends Item {
|
||||||
if (abl) {
|
if (abl) {
|
||||||
const ability = rollData.abilities[abl];
|
const ability = rollData.abilities[abl];
|
||||||
if (!ability) {
|
if (!ability) {
|
||||||
console.warn(`Item ${this.name} in Actor ${this.actor.name} has an invalid item ability modifier of ${abl} defined`);
|
console.warn(
|
||||||
|
`Item ${this.name} in Actor ${this.actor.name} has an invalid item ability modifier of ${abl} defined`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
rollData["mod"] = ability?.mod || 0;
|
rollData["mod"] = ability?.mod || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include a proficiency score
|
// Include a proficiency score
|
||||||
const prof = ("proficient" in rollData.item) ? (rollData.item.proficient || 0) : 1;
|
const prof = "proficient" in rollData.item ? rollData.item.proficient || 0 : 1;
|
||||||
rollData["prof"] = Math.floor(prof * (rollData.attributes.prof || 0));
|
rollData["prof"] = Math.floor(prof * (rollData.attributes.prof || 0));
|
||||||
return rollData;
|
return rollData;
|
||||||
}
|
}
|
||||||
|
@ -1312,8 +1339,8 @@ export default class Item5e extends Item {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
static chatListeners(html) {
|
static chatListeners(html) {
|
||||||
html.on('click', '.card-buttons button', this._onChatCardAction.bind(this));
|
html.on("click", ".card-buttons button", this._onChatCardAction.bind(this));
|
||||||
html.on('click', '.item-name', this._onChatCardToggleContent.bind(this));
|
html.on("click", ".item-name", this._onChatCardToggleContent.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -1347,14 +1374,17 @@ export default class Item5e extends Item {
|
||||||
const storedData = message.getFlag("sw5e", "itemData");
|
const storedData = message.getFlag("sw5e", "itemData");
|
||||||
const item = storedData ? new this(storedData, {parent: actor}) : actor.items.get(card.dataset.itemId);
|
const item = storedData ? new this(storedData, {parent: actor}) : actor.items.get(card.dataset.itemId);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return ui.notifications.error(game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name}))
|
return ui.notifications.error(
|
||||||
|
game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const powerLevel = parseInt(card.dataset.powerLevel) || null;
|
const powerLevel = parseInt(card.dataset.powerLevel) || null;
|
||||||
|
|
||||||
// Handle different actions
|
// Handle different actions
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "attack":
|
case "attack":
|
||||||
await item.rollAttack({event}); break;
|
await item.rollAttack({event});
|
||||||
|
break;
|
||||||
case "damage":
|
case "damage":
|
||||||
case "versatile":
|
case "versatile":
|
||||||
await item.rollDamage({
|
await item.rollDamage({
|
||||||
|
@ -1365,7 +1395,8 @@ export default class Item5e extends Item {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "formula":
|
case "formula":
|
||||||
await item.rollFormula({event, powerLevel}); break;
|
await item.rollFormula({event, powerLevel});
|
||||||
|
break;
|
||||||
case "save":
|
case "save":
|
||||||
const targets = this._getChatCardTargets(card);
|
const targets = this._getChatCardTargets(card);
|
||||||
for (let token of targets) {
|
for (let token of targets) {
|
||||||
|
@ -1374,7 +1405,8 @@ export default class Item5e extends Item {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "toolCheck":
|
case "toolCheck":
|
||||||
await item.rollToolCheck({event}); break;
|
await item.rollToolCheck({event});
|
||||||
|
break;
|
||||||
case "placeTemplate":
|
case "placeTemplate":
|
||||||
const template = game.sw5e.canvas.AbilityTemplate.fromItem(item);
|
const template = game.sw5e.canvas.AbilityTemplate.fromItem(item);
|
||||||
if (template) template.drawPreview();
|
if (template) template.drawPreview();
|
||||||
|
@ -1409,7 +1441,6 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static async _getChatCardActor(card) {
|
static async _getChatCardActor(card) {
|
||||||
|
|
||||||
// Case 1 - a synthetic actor from a Token
|
// Case 1 - a synthetic actor from a Token
|
||||||
if (card.dataset.tokenId) {
|
if (card.dataset.tokenId) {
|
||||||
const token = await fromUuid(card.dataset.tokenId);
|
const token = await fromUuid(card.dataset.tokenId);
|
||||||
|
@ -1431,7 +1462,7 @@ export default class Item5e extends Item {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static _getChatCardTargets(card) {
|
static _getChatCardTargets(card) {
|
||||||
let targets = canvas.tokens.controlled.filter(t => !!t.actor);
|
let targets = canvas.tokens.controlled.filter((t) => !!t.actor);
|
||||||
if (!targets.length && game.user.character) targets = targets.concat(game.user.character.getActiveTokens());
|
if (!targets.length && game.user.character) targets = targets.concat(game.user.character.getActiveTokens());
|
||||||
if (!targets.length) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken"));
|
if (!targets.length) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken"));
|
||||||
return targets;
|
return targets;
|
||||||
|
@ -1444,7 +1475,7 @@ export default class Item5e extends Item {
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
await super._preCreate(data, options, user);
|
await super._preCreate(data, options, user);
|
||||||
if ( !this.isEmbedded || (this.parent.type === "vehicle") ) return;
|
if (!this.isEmbedded || this.parent.type === "vehicle") return;
|
||||||
const actorData = this.parent.data;
|
const actorData = this.parent.data;
|
||||||
const isNPC = this.parent.type === "npc";
|
const isNPC = this.parent.type === "npc";
|
||||||
let updates;
|
let updates;
|
||||||
|
@ -1470,7 +1501,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// The below options are only needed for character classes
|
// The below options are only needed for character classes
|
||||||
if (userId !== game.user.id) return;
|
if (userId !== game.user.id) return;
|
||||||
const isCharacterClass = this.parent && (this.parent.type !== "vehicle") && (this.type === "class");
|
const isCharacterClass = this.parent && this.parent.type !== "vehicle" && this.type === "class";
|
||||||
if (!isCharacterClass) return;
|
if (!isCharacterClass) return;
|
||||||
|
|
||||||
// Assign a new primary class
|
// Assign a new primary class
|
||||||
|
@ -1479,11 +1510,13 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Prompt to add new class features
|
// Prompt to add new class features
|
||||||
if (options.addFeatures === false) return;
|
if (options.addFeatures === false) return;
|
||||||
this.parent.getClassFeatures({
|
this.parent
|
||||||
|
.getClassFeatures({
|
||||||
className: this.name,
|
className: this.name,
|
||||||
archetypeName: this.data.data.archetype,
|
archetypeName: this.data.data.archetype,
|
||||||
level: this.data.data.levels
|
level: this.data.data.levels
|
||||||
}).then(features => {
|
})
|
||||||
|
.then((features) => {
|
||||||
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1496,17 +1529,19 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// The below options are only needed for character classes
|
// The below options are only needed for character classes
|
||||||
if (userId !== game.user.id) return;
|
if (userId !== game.user.id) return;
|
||||||
const isCharacterClass = this.parent && (this.parent.type !== "vehicle") && (this.type === "class");
|
const isCharacterClass = this.parent && this.parent.type !== "vehicle" && this.type === "class";
|
||||||
if (!isCharacterClass) return;
|
if (!isCharacterClass) return;
|
||||||
|
|
||||||
// Prompt to add new class features
|
// Prompt to add new class features
|
||||||
const addFeatures = changed["name"] || (changed.data && ["archetype", "levels"].some(k => k in changed.data));
|
const addFeatures = changed["name"] || (changed.data && ["archetype", "levels"].some((k) => k in changed.data));
|
||||||
if ( !addFeatures || (options.addFeatures === false) ) return;
|
if (!addFeatures || options.addFeatures === false) return;
|
||||||
this.parent.getClassFeatures({
|
this.parent
|
||||||
|
.getClassFeatures({
|
||||||
className: changed.name || this.name,
|
className: changed.name || this.name,
|
||||||
archetypeName: changed.data?.archetype || this.data.data.archetype,
|
archetypeName: changed.data?.archetype || this.data.data.archetype,
|
||||||
level: changed.data?.levels || this.data.data.levels
|
level: changed.data?.levels || this.data.data.levels
|
||||||
}).then(features => {
|
})
|
||||||
|
.then((features) => {
|
||||||
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
return this.parent.addEmbeddedItems(features, options.promptAddFeatures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1518,7 +1553,7 @@ export default class Item5e extends Item {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
|
|
||||||
// Assign a new primary class
|
// Assign a new primary class
|
||||||
if ( this.parent && (this.type === "class") && (userId === game.user.id) ) {
|
if (this.parent && this.type === "class" && userId === game.user.id) {
|
||||||
if (this.id !== this.parent.data.data.details.originalClass) return;
|
if (this.id !== this.parent.data.data.details.originalClass) return;
|
||||||
this.parent._assignPrimaryClass();
|
this.parent._assignPrimaryClass();
|
||||||
}
|
}
|
||||||
|
@ -1541,7 +1576,7 @@ export default class Item5e extends Item {
|
||||||
} else {
|
} else {
|
||||||
const armorProf = CONFIG.SW5E.armorProficienciesMap[data.data?.armor?.type]; // Player characters check proficiency
|
const armorProf = CONFIG.SW5E.armorProficienciesMap[data.data?.armor?.type]; // Player characters check proficiency
|
||||||
const actorArmorProfs = actorData.data.traits?.armorProf?.value || [];
|
const actorArmorProfs = actorData.data.traits?.armorProf?.value || [];
|
||||||
updates["data.proficient"] = (armorProf === true) || actorArmorProfs.includes(armorProf);
|
updates["data.proficient"] = armorProf === true || actorArmorProfs.includes(armorProf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updates;
|
return updates;
|
||||||
|
@ -1577,7 +1612,7 @@ export default class Item5e extends Item {
|
||||||
// TODO: With the changes to make weapon proficiencies more verbose, this may need revising
|
// TODO: With the changes to make weapon proficiencies more verbose, this may need revising
|
||||||
const weaponProf = CONFIG.SW5E.weaponProficienciesMap[data.data?.weaponType]; // Player characters check proficiency
|
const weaponProf = CONFIG.SW5E.weaponProficienciesMap[data.data?.weaponType]; // Player characters check proficiency
|
||||||
const actorWeaponProfs = actorData.data.traits?.weaponProf?.value || [];
|
const actorWeaponProfs = actorData.data.traits?.weaponProf?.value || [];
|
||||||
updates["data.proficient"] = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
|
updates["data.proficient"] = weaponProf === true || actorWeaponProfs.includes(weaponProf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updates;
|
return updates;
|
||||||
|
@ -1593,10 +1628,10 @@ export default class Item5e extends Item {
|
||||||
* @return {Item5e} The created scroll consumable item
|
* @return {Item5e} The created scroll consumable item
|
||||||
*/
|
*/
|
||||||
static async createScrollFromPower(power) {
|
static async createScrollFromPower(power) {
|
||||||
|
|
||||||
// Get power data
|
// Get power data
|
||||||
const itemData = power instanceof Item5e ? power.data : power;
|
const itemData = power instanceof Item5e ? power.data : power;
|
||||||
const {actionType, description, source, activation, duration, target, range, damage, save, level} = itemData.data;
|
const {actionType, description, source, activation, duration, target, range, damage, save, level} =
|
||||||
|
itemData.data;
|
||||||
|
|
||||||
// Get scroll data
|
// Get scroll data
|
||||||
const scrollUuid = `Compendium.${CONFIG.SW5E.sourcePacks.ITEMS}.${CONFIG.SW5E.powerScrollIds[level]}`;
|
const scrollUuid = `Compendium.${CONFIG.SW5E.sourcePacks.ITEMS}.${CONFIG.SW5E.powerScrollIds[level]}`;
|
||||||
|
@ -1606,7 +1641,7 @@ export default class Item5e extends Item {
|
||||||
|
|
||||||
// Split the scroll description into an intro paragraph and the remaining details
|
// Split the scroll description into an intro paragraph and the remaining details
|
||||||
const scrollDescription = scrollData.data.description.value;
|
const scrollDescription = scrollData.data.description.value;
|
||||||
const pdel = '</p>';
|
const pdel = "</p>";
|
||||||
const scrollIntroEnd = scrollDescription.indexOf(pdel);
|
const scrollIntroEnd = scrollDescription.indexOf(pdel);
|
||||||
const scrollIntro = scrollDescription.slice(0, scrollIntroEnd + pdel.length);
|
const scrollIntro = scrollDescription.slice(0, scrollIntroEnd + pdel.length);
|
||||||
const scrollDetails = scrollDescription.slice(scrollIntroEnd + pdel.length);
|
const scrollDetails = scrollDescription.slice(scrollIntroEnd + pdel.length);
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
// Attributes
|
// Attributes
|
||||||
else if (consume.type === "attribute") {
|
else if (consume.type === "attribute") {
|
||||||
const attributes = TokenDocument.getTrackedAttributes(actor.data.data);
|
const attributes = TokenDocument.getTrackedAttributes(actor.data.data);
|
||||||
attributes.bar.forEach(a => a.push("value"));
|
attributes.bar.forEach((a) => a.push("value"));
|
||||||
return attributes.bar.concat(attributes.value).reduce((obj, a) => {
|
return attributes.bar.concat(attributes.value).reduce((obj, a) => {
|
||||||
let k = a.join(".");
|
let k = a.join(".");
|
||||||
obj[k] = k;
|
obj[k] = k;
|
||||||
|
@ -136,7 +136,10 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
const label =
|
const label =
|
||||||
uses.per === "charges"
|
uses.per === "charges"
|
||||||
? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", {value: uses.value})})`
|
? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", {value: uses.value})})`
|
||||||
: ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", { max: uses.max, per: uses.per })})`;
|
: ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", {
|
||||||
|
max: uses.max,
|
||||||
|
per: uses.per
|
||||||
|
})})`;
|
||||||
obj[i.id] = i.name + label;
|
obj[i.id] = i.name + label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +286,10 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
||||||
html.find(".trait-selector.class-skills").click(this._onConfigureTraits.bind(this));
|
html.find(".trait-selector.class-skills").click(this._onConfigureTraits.bind(this));
|
||||||
html.find(".effect-control").click((ev) => {
|
html.find(".effect-control").click((ev) => {
|
||||||
if (this.item.isOwned) return ui.notifications.warn("Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update.");
|
if (this.item.isOwned)
|
||||||
|
return ui.notifications.warn(
|
||||||
|
"Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update."
|
||||||
|
);
|
||||||
onManageActiveEffect(ev, this.item);
|
onManageActiveEffect(ev, this.item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -337,14 +343,17 @@ export default class ItemSheet5e extends ItemSheet {
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (a.dataset.options) {
|
switch (a.dataset.options) {
|
||||||
case 'saves':
|
case "saves":
|
||||||
options.choices = CONFIG.SW5E.abilities;
|
options.choices = CONFIG.SW5E.abilities;
|
||||||
options.valueKey = null;
|
options.valueKey = null;
|
||||||
break;
|
break;
|
||||||
case 'skills':
|
case "skills":
|
||||||
const skills = this.item.data.data.skills;
|
const skills = this.item.data.data.skills;
|
||||||
const choiceSet = skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
|
const choiceSet =
|
||||||
options.choices = Object.fromEntries(Object.entries(CONFIG.SW5E.skills).filter(skill => choiceSet.includes(skill[0])));
|
skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
|
||||||
|
options.choices = Object.fromEntries(
|
||||||
|
Object.entries(CONFIG.SW5E.skills).filter((skill) => choiceSet.includes(skill[0]))
|
||||||
|
);
|
||||||
options.maximum = skills.number;
|
options.maximum = skills.number;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Hotbar Macros */
|
/* Hotbar Macros */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -17,7 +16,7 @@ export async function create5eMacro(data, slot) {
|
||||||
|
|
||||||
// Create the macro command
|
// Create the macro command
|
||||||
const command = `game.sw5e.rollItemMacro("${item.name}");`;
|
const command = `game.sw5e.rollItemMacro("${item.name}");`;
|
||||||
let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command));
|
let macro = game.macros.entities.find((m) => m.name === item.name && m.command === command);
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
macro = await Macro.create({
|
macro = await Macro.create({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
|
@ -46,9 +45,11 @@ export function rollItemMacro(itemName) {
|
||||||
if (!actor) actor = game.actors.get(speaker.actor);
|
if (!actor) actor = game.actors.get(speaker.actor);
|
||||||
|
|
||||||
// Get matching items
|
// Get matching items
|
||||||
const items = actor ? actor.items.filter(i => i.name === itemName) : [];
|
const items = actor ? actor.items.filter((i) => i.name === itemName) : [];
|
||||||
if (items.length > 1) {
|
if (items.length > 1) {
|
||||||
ui.notifications.warn(`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`);
|
ui.notifications.warn(
|
||||||
|
`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`
|
||||||
|
);
|
||||||
} else if (items.length === 0) {
|
} else if (items.length === 0) {
|
||||||
return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
|
return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
* @return {Promise} A Promise which resolves once the migration is completed
|
* @return {Promise} A Promise which resolves once the migration is completed
|
||||||
*/
|
*/
|
||||||
export const migrateWorld = async function () {
|
export const migrateWorld = async function () {
|
||||||
ui.notifications.info(`Applying SW5e System Migration for version ${game.system.data.version}. Please be patient and do not close your game or shut down your server.`, {permanent: true});
|
ui.notifications.info(
|
||||||
|
`Applying SW5e System Migration for version ${game.system.data.version}. Please be patient and do not close your game or shut down your server.`,
|
||||||
|
{permanent: true}
|
||||||
|
);
|
||||||
|
|
||||||
// Migrate World Actors
|
// Migrate World Actors
|
||||||
for await (let a of game.actors.contents) {
|
for await (let a of game.actors.contents) {
|
||||||
|
@ -43,7 +46,7 @@ export const migrateWorld = async function() {
|
||||||
await s.update(updateData, {enforceTypes: false});
|
await s.update(updateData, {enforceTypes: false});
|
||||||
// If we do not do this, then synthetic token actors remain in cache
|
// If we do not do this, then synthetic token actors remain in cache
|
||||||
// with the un-updated actorData.
|
// with the un-updated actorData.
|
||||||
s.tokens.contents.forEach(t => t._actor = null);
|
s.tokens.contents.forEach((t) => (t._actor = null));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
|
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
|
||||||
|
@ -102,10 +105,8 @@ export const migrateCompendium = async function(pack) {
|
||||||
// Save the entry, if data was changed
|
// Save the entry, if data was changed
|
||||||
await doc.update(updateData);
|
await doc.update(updateData);
|
||||||
console.log(`Migrated ${entity} entity ${doc.name} in Compendium ${pack.collection}`);
|
console.log(`Migrated ${entity} entity ${doc.name} in Compendium ${pack.collection}`);
|
||||||
}
|
} catch (err) {
|
||||||
|
|
||||||
// Handle migration failures
|
// Handle migration failures
|
||||||
catch(err) {
|
|
||||||
err.message = `Failed sw5e system migration for entity ${doc.name} in pack ${pack.collection}: ${err.message}`;
|
err.message = `Failed sw5e system migration for entity ${doc.name} in pack ${pack.collection}: ${err.message}`;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,8 @@ export const migrateActorData = async function(actor) {
|
||||||
|
|
||||||
// Prepared, Equipped, and Proficient for NPC actors
|
// Prepared, Equipped, and Proficient for NPC actors
|
||||||
if (actor.type === "npc") {
|
if (actor.type === "npc") {
|
||||||
if (getProperty(itemData.data, "preparation.prepared") === false) itemUpdate["data.preparation.prepared"] = true;
|
if (getProperty(itemData.data, "preparation.prepared") === false)
|
||||||
|
itemUpdate["data.preparation.prepared"] = true;
|
||||||
if (getProperty(itemData.data, "equipped") === false) itemUpdate["data.equipped"] = true;
|
if (getProperty(itemData.data, "equipped") === false) itemUpdate["data.equipped"] = true;
|
||||||
if (getProperty(itemData.data, "proficient") === false) itemUpdate["data.proficient"] = true;
|
if (getProperty(itemData.data, "proficient") === false) itemUpdate["data.proficient"] = true;
|
||||||
}
|
}
|
||||||
|
@ -178,14 +180,12 @@ export const migrateActorData = async function(actor) {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrub an Actor's system data, removing all keys which are not explicitly defined in the system template
|
* Scrub an Actor's system data, removing all keys which are not explicitly defined in the system template
|
||||||
* @param {Object} actorData The data object for an Actor
|
* @param {Object} actorData The data object for an Actor
|
||||||
* @return {Object} The scrubbed Actor data
|
* @return {Object} The scrubbed Actor data
|
||||||
*/
|
*/
|
||||||
function cleanActorData(actorData) {
|
function cleanActorData(actorData) {
|
||||||
|
|
||||||
// Scrub system data
|
// Scrub system data
|
||||||
const model = game.system.model.Actor[actorData.type];
|
const model = game.system.model.Actor[actorData.type];
|
||||||
actorData.data = filterObject(actorData.data, model);
|
actorData.data = filterObject(actorData.data, model);
|
||||||
|
@ -203,7 +203,6 @@ function cleanActorData(actorData) {
|
||||||
return actorData;
|
return actorData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,22 +242,22 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
* @return {Object} The updateData to apply
|
* @return {Object} The updateData to apply
|
||||||
*/
|
*/
|
||||||
export const migrateSceneData = async function (scene) {
|
export const migrateSceneData = async function (scene) {
|
||||||
const tokens = await Promise.all(scene.tokens.map(async token => {
|
const tokens = await Promise.all(
|
||||||
|
scene.tokens.map(async (token) => {
|
||||||
const t = token.toJSON();
|
const t = token.toJSON();
|
||||||
if (!t.actorId || t.actorLink) {
|
if (!t.actorId || t.actorLink) {
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
}
|
} else if (!game.actors.has(t.actorId)) {
|
||||||
else if (!game.actors.has(t.actorId)) {
|
|
||||||
t.actorId = null;
|
t.actorId = null;
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
} else if (!t.actorLink) {
|
} else if (!t.actorLink) {
|
||||||
const actorData = duplicate(t.actorData);
|
const actorData = duplicate(t.actorData);
|
||||||
actorData.type = token.actor?.type;
|
actorData.type = token.actor?.type;
|
||||||
const update = migrateActorData(actorData);
|
const update = migrateActorData(actorData);
|
||||||
['items', 'effects'].forEach(embeddedName => {
|
["items", "effects"].forEach((embeddedName) => {
|
||||||
if (!update[embeddedName]?.length) return;
|
if (!update[embeddedName]?.length) return;
|
||||||
const updates = new Map(update[embeddedName].map(u => [u._id, u]));
|
const updates = new Map(update[embeddedName].map((u) => [u._id, u]));
|
||||||
t.actorData[embeddedName].forEach(original => {
|
t.actorData[embeddedName].forEach((original) => {
|
||||||
const update = updates.get(original._id);
|
const update = updates.get(original._id);
|
||||||
if (update) mergeObject(original, update);
|
if (update) mergeObject(original, update);
|
||||||
});
|
});
|
||||||
|
@ -268,7 +267,8 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
mergeObject(t.actorData, update);
|
mergeObject(t.actorData, update);
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return {tokens};
|
return {tokens};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -284,7 +284,6 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
* @return {Object} The updated Actor
|
* @return {Object} The updated Actor
|
||||||
*/
|
*/
|
||||||
function _updateNPCData(actor) {
|
function _updateNPCData(actor) {
|
||||||
|
|
||||||
let actorData = actor.data;
|
let actorData = actor.data;
|
||||||
const updateData = {};
|
const updateData = {};
|
||||||
// check for flag.core, if not there is no compendium monster so exit
|
// check for flag.core, if not there is no compendium monster so exit
|
||||||
|
@ -292,13 +291,20 @@ function _updateNPCData(actor) {
|
||||||
if (!hasSource) return actor;
|
if (!hasSource) return actor;
|
||||||
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
||||||
const hasDataVersion = actor?.flags?.sw5e?.dataVersion !== undefined;
|
const hasDataVersion = actor?.flags?.sw5e?.dataVersion !== undefined;
|
||||||
if (hasDataVersion && (actor.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", actor.flags.sw5e.dataVersion))) return actor;
|
if (
|
||||||
|
hasDataVersion &&
|
||||||
|
(actor.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", actor.flags.sw5e.dataVersion))
|
||||||
|
)
|
||||||
|
return actor;
|
||||||
// Check to see what the source of NPC is
|
// Check to see what the source of NPC is
|
||||||
const sourceId = actor.flags.core.sourceId;
|
const sourceId = actor.flags.core.sourceId;
|
||||||
const coreSource = sourceId.substr(0, sourceId.length - 17);
|
const coreSource = sourceId.substr(0, sourceId.length - 17);
|
||||||
const core_id = sourceId.substr(sourceId.length - 16, 16);
|
const core_id = sourceId.substr(sourceId.length - 16, 16);
|
||||||
if (coreSource === "Compendium.sw5e.monsters") {
|
if (coreSource === "Compendium.sw5e.monsters") {
|
||||||
game.packs.get("sw5e.monsters").getEntity(core_id).then(monster => {
|
game.packs
|
||||||
|
.get("sw5e.monsters")
|
||||||
|
.getEntity(core_id)
|
||||||
|
.then((monster) => {
|
||||||
const monsterData = monster.data.data;
|
const monsterData = monster.data.data;
|
||||||
// copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel
|
// copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel
|
||||||
updateData["data.attributes.movement"] = monsterData.attributes.movement;
|
updateData["data.attributes.movement"] = monsterData.attributes.movement;
|
||||||
|
@ -314,7 +320,9 @@ function _updateNPCData(actor) {
|
||||||
const itemData = i.data;
|
const itemData = i.data;
|
||||||
if (itemData.type === "power") {
|
if (itemData.type === "power") {
|
||||||
const itemCompendium_id = itemData.flags?.core?.sourceId.split(".").slice(-1)[0];
|
const itemCompendium_id = itemData.flags?.core?.sourceId.split(".").slice(-1)[0];
|
||||||
let hasPower = !!actor.items.find(item => item.flags?.core?.sourceId.split(".").slice(-1)[0] === itemCompendium_id);
|
let hasPower = !!actor.items.find(
|
||||||
|
(item) => item.flags?.core?.sourceId.split(".").slice(-1)[0] === itemCompendium_id
|
||||||
|
);
|
||||||
if (!hasPower) {
|
if (!hasPower) {
|
||||||
// Clone power to new object. Don't know if it is technically needed, but seems to prevent some weirdness.
|
// Clone power to new object. Don't know if it is technically needed, but seems to prevent some weirdness.
|
||||||
const newPower = JSON.parse(JSON.stringify(itemData));
|
const newPower = JSON.parse(JSON.stringify(itemData));
|
||||||
|
@ -331,17 +339,15 @@ function _updateNPCData(actor) {
|
||||||
|
|
||||||
// set flag to check to see if migration has been done so we don't do it again.
|
// set flag to check to see if migration has been done so we don't do it again.
|
||||||
liveActor.setFlag("sw5e", "dataVersion", "1.2.4");
|
liveActor.setFlag("sw5e", "dataVersion", "1.2.4");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//merge object
|
//merge object
|
||||||
actorData = mergeObject(actorData, updateData);
|
actorData = mergeObject(actorData, updateData);
|
||||||
// Return the scrubbed data
|
// Return the scrubbed data
|
||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrate the actor speed string to movement object
|
* Migrate the actor speed string to movement object
|
||||||
* @private
|
* @private
|
||||||
|
@ -350,21 +356,21 @@ function _migrateActorMovement(actorData, updateData) {
|
||||||
const ad = actorData.data;
|
const ad = actorData.data;
|
||||||
|
|
||||||
// Work is needed if old data is present
|
// Work is needed if old data is present
|
||||||
const old = actorData.type === 'vehicle' ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
|
const old = actorData.type === "vehicle" ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
|
||||||
const hasOld = old !== undefined;
|
const hasOld = old !== undefined;
|
||||||
if (hasOld) {
|
if (hasOld) {
|
||||||
|
|
||||||
// If new data is not present, migrate the old data
|
// If new data is not present, migrate the old data
|
||||||
const hasNew = ad?.attributes?.movement?.walk !== undefined;
|
const hasNew = ad?.attributes?.movement?.walk !== undefined;
|
||||||
if ( !hasNew && (typeof old === "string") ) {
|
if (!hasNew && typeof old === "string") {
|
||||||
const s = (old || "").split(" ");
|
const s = (old || "").split(" ");
|
||||||
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
if (s.length > 0)
|
||||||
|
updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the old attribute
|
// Remove the old attribute
|
||||||
updateData["data.attributes.-=speed"] = null;
|
updateData["data.attributes.-=speed"] = null;
|
||||||
}
|
}
|
||||||
return updateData
|
return updateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -425,7 +431,7 @@ function _migrateActorPowers(actorData, updateData) {
|
||||||
// Remove the Power DC Bonus
|
// Remove the Power DC Bonus
|
||||||
updateData["data.bonuses.power.-=dc"] = null;
|
updateData["data.bonuses.power.-=dc"] = null;
|
||||||
|
|
||||||
return updateData
|
return updateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -479,11 +485,11 @@ function _migrateActorType(actor, updateData) {
|
||||||
|
|
||||||
// New default data structure
|
// New default data structure
|
||||||
let data = {
|
let data = {
|
||||||
"value": "",
|
value: "",
|
||||||
"subtype": "",
|
subtype: "",
|
||||||
"swarm": "",
|
swarm: "",
|
||||||
"custom": ""
|
custom: ""
|
||||||
}
|
};
|
||||||
|
|
||||||
// Specifics
|
// Specifics
|
||||||
// (Some of these have weird names, these need to be addressed individually)
|
// (Some of these have weird names, these need to be addressed individually)
|
||||||
|
@ -507,13 +513,14 @@ function _migrateActorType(actor, updateData) {
|
||||||
const pattern = /^(?:swarm of (?<size>[\w\-]+) )?(?<type>[^(]+?)(?:\((?<subtype>[^)]+)\))?$/i;
|
const pattern = /^(?:swarm of (?<size>[\w\-]+) )?(?<type>[^(]+?)(?:\((?<subtype>[^)]+)\))?$/i;
|
||||||
const match = original.trim().match(pattern);
|
const match = original.trim().match(pattern);
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
||||||
// Match a known creature type
|
// Match a known creature type
|
||||||
const typeLc = match.groups.type.trim().toLowerCase();
|
const typeLc = match.groups.type.trim().toLowerCase();
|
||||||
const typeMatch = Object.entries(CONFIG.SW5E.creatureTypes).find(([k, v]) => {
|
const typeMatch = Object.entries(CONFIG.SW5E.creatureTypes).find(([k, v]) => {
|
||||||
return (typeLc === k) ||
|
return (
|
||||||
(typeLc === game.i18n.localize(v).toLowerCase()) ||
|
typeLc === k ||
|
||||||
(typeLc === game.i18n.localize(`${v}Pl`).toLowerCase());
|
typeLc === game.i18n.localize(v).toLowerCase() ||
|
||||||
|
typeLc === game.i18n.localize(`${v}Pl`).toLowerCase()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
if (typeMatch) data.value = typeMatch[0];
|
if (typeMatch) data.value = typeMatch[0];
|
||||||
else {
|
else {
|
||||||
|
@ -527,7 +534,7 @@ function _migrateActorType(actor, updateData) {
|
||||||
if (match.groups.size || isNamedSwarm) {
|
if (match.groups.size || isNamedSwarm) {
|
||||||
const sizeLc = match.groups.size ? match.groups.size.trim().toLowerCase() : "tiny";
|
const sizeLc = match.groups.size ? match.groups.size.trim().toLowerCase() : "tiny";
|
||||||
const sizeMatch = Object.entries(CONFIG.SW5E.actorSizes).find(([k, v]) => {
|
const sizeMatch = Object.entries(CONFIG.SW5E.actorSizes).find(([k, v]) => {
|
||||||
return (sizeLc === k) || (sizeLc === game.i18n.localize(v).toLowerCase());
|
return sizeLc === k || sizeLc === game.i18n.localize(v).toLowerCase();
|
||||||
});
|
});
|
||||||
data.swarm = sizeMatch ? sizeMatch[0] : "tiny";
|
data.swarm = sizeMatch ? sizeMatch[0] : "tiny";
|
||||||
} else data.swarm = "";
|
} else data.swarm = "";
|
||||||
|
@ -560,7 +567,6 @@ function _migrateItemClassPowerCasting(item, updateData) {
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "Engineer":
|
case "Engineer":
|
||||||
|
|
||||||
updateData["data.powercasting"] = {
|
updateData["data.powercasting"] = {
|
||||||
progression: "engineer",
|
progression: "engineer",
|
||||||
ability: ""
|
ability: ""
|
||||||
|
@ -608,7 +614,11 @@ async function _migrateItemPower(item, actor, updateData) {
|
||||||
|
|
||||||
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
// shortcut out if dataVersion flag is set to 1.2.4 or higher
|
||||||
const hasDataVersion = item?.flags?.sw5e?.dataVersion !== undefined;
|
const hasDataVersion = item?.flags?.sw5e?.dataVersion !== undefined;
|
||||||
if (hasDataVersion && (item.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", item.flags.sw5e.dataVersion))) return updateData;
|
if (
|
||||||
|
hasDataVersion &&
|
||||||
|
(item.flags.sw5e.dataVersion === "1.2.4" || isNewerVersion("1.2.4", item.flags.sw5e.dataVersion))
|
||||||
|
)
|
||||||
|
return updateData;
|
||||||
|
|
||||||
// Check to see what the source of Power is
|
// Check to see what the source of Power is
|
||||||
const sourceId = item.flags.core.sourceId;
|
const sourceId = item.flags.core.sourceId;
|
||||||
|
@ -626,11 +636,10 @@ async function _migrateItemPower(item, actor, updateData) {
|
||||||
const corePowerData = corePower.data;
|
const corePowerData = corePower.data;
|
||||||
// copy Core Power Data over original Power
|
// copy Core Power Data over original Power
|
||||||
updateData["data"] = corePowerData;
|
updateData["data"] = corePowerData;
|
||||||
updateData["flags"] = {"sw5e": {"dataVersion": "1.2.4"}};
|
updateData["flags"] = {sw5e: {dataVersion: "1.2.4"}};
|
||||||
|
|
||||||
return updateData;
|
return updateData;
|
||||||
|
|
||||||
|
|
||||||
//game.packs.get(powerType).getEntity(core_id).then(corePower => {
|
//game.packs.get(powerType).getEntity(core_id).then(corePower => {
|
||||||
|
|
||||||
//})
|
//})
|
||||||
|
@ -670,10 +679,10 @@ export async function purgeFlags(pack) {
|
||||||
for (let entity of content) {
|
for (let entity of content) {
|
||||||
const update = {_id: entity.id, flags: cleanFlags(entity.data.flags)};
|
const update = {_id: entity.id, flags: cleanFlags(entity.data.flags)};
|
||||||
if (pack.entity === "Actor") {
|
if (pack.entity === "Actor") {
|
||||||
update.items = entity.data.items.map(i => {
|
update.items = entity.data.items.map((i) => {
|
||||||
i.flags = cleanFlags(i.flags);
|
i.flags = cleanFlags(i.flags);
|
||||||
return i;
|
return i;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
await pack.updateEntity(update, {recursive: false});
|
await pack.updateEntity(update, {recursive: false});
|
||||||
console.log(`Purged flags from ${entity.name}`);
|
console.log(`Purged flags from ${entity.name}`);
|
||||||
|
@ -683,7 +692,6 @@ export async function purgeFlags(pack) {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purge the data model of any inner objects which have been flagged as _deprecated.
|
* Purge the data model of any inner objects which have been flagged as _deprecated.
|
||||||
* @param {object} data The data to clean
|
* @param {object} data The data to clean
|
||||||
|
@ -695,8 +703,7 @@ export function removeDeprecatedObjects(data) {
|
||||||
if (v._deprecated === true) {
|
if (v._deprecated === true) {
|
||||||
console.log(`Deleting deprecated object key ${k}`);
|
console.log(`Deleting deprecated object key ${k}`);
|
||||||
delete data[k];
|
delete data[k];
|
||||||
}
|
} else removeDeprecatedObjects(v);
|
||||||
else removeDeprecatedObjects(v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { SW5E } from "../config.js";
|
||||||
* @extends {MeasuredTemplate}
|
* @extends {MeasuredTemplate}
|
||||||
*/
|
*/
|
||||||
export default class AbilityTemplate extends MeasuredTemplate {
|
export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory method to create an AbilityTemplate instance using provided data from an Item5e instance
|
* A factory method to create an AbilityTemplate instance using provided data from an Item5e instance
|
||||||
* @param {Item5e} item The Item object for which to construct the template
|
* @param {Item5e} item The Item object for which to construct the template
|
||||||
|
@ -84,7 +83,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
let moveTime = 0;
|
let moveTime = 0;
|
||||||
|
|
||||||
// Update placement (mouse-move)
|
// Update placement (mouse-move)
|
||||||
handlers.mm = event => {
|
handlers.mm = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
let now = Date.now(); // Apply a 20ms throttle
|
let now = Date.now(); // Apply a 20ms throttle
|
||||||
if (now - moveTime <= 20) return;
|
if (now - moveTime <= 20) return;
|
||||||
|
@ -96,7 +95,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cancel the workflow (right-click)
|
// Cancel the workflow (right-click)
|
||||||
handlers.rc = event => {
|
handlers.rc = (event) => {
|
||||||
this.layer.preview.removeChildren();
|
this.layer.preview.removeChildren();
|
||||||
canvas.stage.off("mousemove", handlers.mm);
|
canvas.stage.off("mousemove", handlers.mm);
|
||||||
canvas.stage.off("mousedown", handlers.lc);
|
canvas.stage.off("mousedown", handlers.lc);
|
||||||
|
@ -107,7 +106,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Confirm the workflow (left-click)
|
// Confirm the workflow (left-click)
|
||||||
handlers.lc = event => {
|
handlers.lc = (event) => {
|
||||||
handlers.rc(event);
|
handlers.rc(event);
|
||||||
const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2);
|
const destination = canvas.grid.getSnappedPosition(this.data.x, this.data.y, 2);
|
||||||
this.data.update(destination);
|
this.data.update(destination);
|
||||||
|
@ -115,12 +114,12 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rotate the template by 3 degree increments (mouse-wheel)
|
// Rotate the template by 3 degree increments (mouse-wheel)
|
||||||
handlers.mw = event => {
|
handlers.mw = (event) => {
|
||||||
if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window
|
if (event.ctrlKey) event.preventDefault(); // Avoid zooming the browser window
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15;
|
let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15;
|
||||||
let snap = event.shiftKey ? delta : 5;
|
let snap = event.shiftKey ? delta : 5;
|
||||||
this.data.update({direction: this.data.direction + (snap * Math.sign(event.deltaY))});
|
this.data.update({direction: this.data.direction + snap * Math.sign(event.deltaY)});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export const registerSystemSettings = function () {
|
export const registerSystemSettings = function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track the system version upon which point a migration was last applied
|
* Track the system version upon which point a migration was last applied
|
||||||
*/
|
*/
|
||||||
|
@ -22,9 +21,9 @@ export const registerSystemSettings = function() {
|
||||||
default: "normal",
|
default: "normal",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"normal": "SETTINGS.5eRestPHB",
|
normal: "SETTINGS.5eRestPHB",
|
||||||
"gritty": "SETTINGS.5eRestGritty",
|
gritty: "SETTINGS.5eRestGritty",
|
||||||
"epic": "SETTINGS.5eRestEpic",
|
epic: "SETTINGS.5eRestEpic"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,11 +38,11 @@ export const registerSystemSettings = function() {
|
||||||
default: "555",
|
default: "555",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"555": "SETTINGS.5eDiagPHB",
|
555: "SETTINGS.5eDiagPHB",
|
||||||
"5105": "SETTINGS.5eDiagDMG",
|
5105: "SETTINGS.5eDiagDMG",
|
||||||
"EUCL": "SETTINGS.5eDiagEuclidean",
|
EUCL: "SETTINGS.5eDiagEuclidean"
|
||||||
},
|
},
|
||||||
onChange: rule => canvas.grid.diagonalRule = rule
|
onChange: (rule) => (canvas.grid.diagonalRule = rule)
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +78,7 @@ export const registerSystemSettings = function() {
|
||||||
scope: "world",
|
scope: "world",
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean,
|
type: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,7 +91,7 @@ export const registerSystemSettings = function() {
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
onChange: s => {
|
onChange: (s) => {
|
||||||
ui.chat.render();
|
ui.chat.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -100,10 +99,10 @@ export const registerSystemSettings = function() {
|
||||||
/**
|
/**
|
||||||
* Option to allow GMs to restrict polymorphing to GMs only.
|
* Option to allow GMs to restrict polymorphing to GMs only.
|
||||||
*/
|
*/
|
||||||
game.settings.register('sw5e', 'allowPolymorphing', {
|
game.settings.register("sw5e", "allowPolymorphing", {
|
||||||
name: 'SETTINGS.5eAllowPolymorphingN',
|
name: "SETTINGS.5eAllowPolymorphingN",
|
||||||
hint: 'SETTINGS.5eAllowPolymorphingL',
|
hint: "SETTINGS.5eAllowPolymorphingL",
|
||||||
scope: 'world',
|
scope: "world",
|
||||||
config: true,
|
config: true,
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean
|
type: Boolean
|
||||||
|
@ -112,8 +111,8 @@ export const registerSystemSettings = function() {
|
||||||
/**
|
/**
|
||||||
* Remember last-used polymorph settings.
|
* Remember last-used polymorph settings.
|
||||||
*/
|
*/
|
||||||
game.settings.register('sw5e', 'polymorphSettings', {
|
game.settings.register("sw5e", "polymorphSettings", {
|
||||||
scope: 'client',
|
scope: "client",
|
||||||
default: {
|
default: {
|
||||||
keepPhysical: false,
|
keepPhysical: false,
|
||||||
keepMental: false,
|
keepMental: false,
|
||||||
|
@ -138,8 +137,8 @@ export const registerSystemSettings = function() {
|
||||||
default: "light",
|
default: "light",
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"light": "SETTINGS.SWColorLight",
|
light: "SETTINGS.SWColorLight",
|
||||||
"dark": "SETTINGS.SWColorDark"
|
dark: "SETTINGS.SWColorDark"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
export const preloadHandlebarsTemplates = async function () {
|
export const preloadHandlebarsTemplates = async function () {
|
||||||
return loadTemplates([
|
return loadTemplates([
|
||||||
|
|
||||||
// Shared Partials
|
// Shared Partials
|
||||||
"systems/sw5e/templates/actors/parts/active-effects.html",
|
"systems/sw5e/templates/actors/parts/active-effects.html",
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
* @extends {TokenDocument}
|
* @extends {TokenDocument}
|
||||||
*/
|
*/
|
||||||
export class TokenDocument5e extends TokenDocument {
|
export class TokenDocument5e extends TokenDocument {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
getBarAttribute(...args) {
|
getBarAttribute(...args) {
|
||||||
const data = super.getBarAttribute(...args);
|
const data = super.getBarAttribute(...args);
|
||||||
if ( data && (data.attribute === "attributes.hp") ) {
|
if (data && data.attribute === "attributes.hp") {
|
||||||
data.value += parseInt(getProperty(this.actor.data, "data.attributes.hp.temp") || 0);
|
data.value += parseInt(getProperty(this.actor.data, "data.attributes.hp.temp") || 0);
|
||||||
data.max += parseInt(getProperty(this.actor.data, "data.attributes.hp.tempmax") || 0);
|
data.max += parseInt(getProperty(this.actor.data, "data.attributes.hp.tempmax") || 0);
|
||||||
}
|
}
|
||||||
|
@ -15,16 +14,13 @@ export class TokenDocument5e extends TokenDocument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend the base Token class to implement additional system-specific logic.
|
* Extend the base Token class to implement additional system-specific logic.
|
||||||
* @extends {Token}
|
* @extends {Token}
|
||||||
*/
|
*/
|
||||||
export class Token5e extends Token {
|
export class Token5e extends Token {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_drawBar(number, bar, data) {
|
_drawBar(number, bar, data) {
|
||||||
if (data.attribute === "attributes.hp") return this._drawHPBar(number, bar, data);
|
if (data.attribute === "attributes.hp") return this._drawHPBar(number, bar, data);
|
||||||
|
@ -41,7 +37,6 @@ export class Token5e extends Token {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_drawHPBar(number, bar, data) {
|
_drawHPBar(number, bar, data) {
|
||||||
|
|
||||||
// Extract health data
|
// Extract health data
|
||||||
let {value, max, temp, tempmax} = this.document.actor.data.data.attributes.hp;
|
let {value, max, temp, tempmax} = this.document.actor.data.data.attributes.hp;
|
||||||
temp = Number(temp || 0);
|
temp = Number(temp || 0);
|
||||||
|
@ -58,42 +53,50 @@ export class Token5e extends Token {
|
||||||
|
|
||||||
// Determine colors to use
|
// Determine colors to use
|
||||||
const blk = 0x000000;
|
const blk = 0x000000;
|
||||||
const hpColor = PIXI.utils.rgb2hex([(1-(colorPct/2)), colorPct, 0]);
|
const hpColor = PIXI.utils.rgb2hex([1 - colorPct / 2, colorPct, 0]);
|
||||||
const c = CONFIG.SW5E.tokenHPColors;
|
const c = CONFIG.SW5E.tokenHPColors;
|
||||||
|
|
||||||
// Determine the container size (logic borrowed from core)
|
// Determine the container size (logic borrowed from core)
|
||||||
const w = this.w;
|
const w = this.w;
|
||||||
let h = Math.max((canvas.dimensions.size / 12), 8);
|
let h = Math.max(canvas.dimensions.size / 12, 8);
|
||||||
if (this.data.height >= 2) h *= 1.6;
|
if (this.data.height >= 2) h *= 1.6;
|
||||||
const bs = Math.clamped(h / 8, 1, 2);
|
const bs = Math.clamped(h / 8, 1, 2);
|
||||||
const bs1 = bs + 1;
|
const bs1 = bs + 1;
|
||||||
|
|
||||||
// Overall bar container
|
// Overall bar container
|
||||||
bar.clear()
|
bar.clear();
|
||||||
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
||||||
|
|
||||||
// Temporary maximum HP
|
// Temporary maximum HP
|
||||||
if (tempmax > 0) {
|
if (tempmax > 0) {
|
||||||
const pct = max / effectiveMax;
|
const pct = max / effectiveMax;
|
||||||
bar.beginFill(c.tempmax, 1.0).lineStyle(1, blk, 1.0).drawRoundedRect(pct*w, 0, (1-pct)*w, h, 2);
|
bar.beginFill(c.tempmax, 1.0)
|
||||||
|
.lineStyle(1, blk, 1.0)
|
||||||
|
.drawRoundedRect(pct * w, 0, (1 - pct) * w, h, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum HP penalty
|
// Maximum HP penalty
|
||||||
else if (tempmax < 0) {
|
else if (tempmax < 0) {
|
||||||
const pct = (max + tempmax) / max;
|
const pct = (max + tempmax) / max;
|
||||||
bar.beginFill(c.negmax, 1.0).lineStyle(1, blk, 1.0).drawRoundedRect(pct*w, 0, (1-pct)*w, h, 2);
|
bar.beginFill(c.negmax, 1.0)
|
||||||
|
.lineStyle(1, blk, 1.0)
|
||||||
|
.drawRoundedRect(pct * w, 0, (1 - pct) * w, h, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Health bar
|
// Health bar
|
||||||
bar.beginFill(hpColor, 1.0).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, valuePct*w, h, 2)
|
bar.beginFill(hpColor, 1.0)
|
||||||
|
.lineStyle(bs, blk, 1.0)
|
||||||
|
.drawRoundedRect(0, 0, valuePct * w, h, 2);
|
||||||
|
|
||||||
// Temporary hit points
|
// Temporary hit points
|
||||||
if (temp > 0) {
|
if (temp > 0) {
|
||||||
bar.beginFill(c.temp, 1.0).lineStyle(0).drawRoundedRect(bs1, bs1, (tempPct*w)-(2*bs1), h-(2*bs1), 1);
|
bar.beginFill(c.temp, 1.0)
|
||||||
|
.lineStyle(0)
|
||||||
|
.drawRoundedRect(bs1, bs1, tempPct * w - 2 * bs1, h - 2 * bs1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set position
|
// Set position
|
||||||
let posY = (number === 0) ? (this.h - h) : 0;
|
let posY = number === 0 ? this.h - h : 0;
|
||||||
bar.position.set(0, posY);
|
bar.position.set(0, posY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
135
sw5e.js
135
sw5e.js
|
@ -18,7 +18,7 @@ import { measureDistances } from "./module/canvas.js";
|
||||||
import Actor5e from "./module/actor/entity.js";
|
import Actor5e from "./module/actor/entity.js";
|
||||||
import Item5e from "./module/item/entity.js";
|
import Item5e from "./module/item/entity.js";
|
||||||
import CharacterImporter from "./module/characterImporter.js";
|
import CharacterImporter from "./module/characterImporter.js";
|
||||||
import { TokenDocument5e, Token5e } from "./module/token.js"
|
import {TokenDocument5e, Token5e} from "./module/token.js";
|
||||||
|
|
||||||
// Import Applications
|
// Import Applications
|
||||||
import AbilityTemplate from "./module/pixi/ability-template.js";
|
import AbilityTemplate from "./module/pixi/ability-template.js";
|
||||||
|
@ -46,9 +46,6 @@ import * as migrations from "./module/migration.js";
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
// Keep on while migrating to Foundry version 0.8
|
|
||||||
CONFIG.debug.hooks = true;
|
|
||||||
|
|
||||||
Hooks.once("init", function () {
|
Hooks.once("init", function () {
|
||||||
console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`);
|
console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`);
|
||||||
|
|
||||||
|
@ -77,7 +74,7 @@ Hooks.once("init", function() {
|
||||||
Actor5e,
|
Actor5e,
|
||||||
Item5e,
|
Item5e,
|
||||||
TokenDocument5e,
|
TokenDocument5e,
|
||||||
Token5e,
|
Token5e
|
||||||
},
|
},
|
||||||
macros: macros,
|
macros: macros,
|
||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
|
@ -91,11 +88,7 @@ Hooks.once("init", function() {
|
||||||
CONFIG.Token.documentClass = TokenDocument5e;
|
CONFIG.Token.documentClass = TokenDocument5e;
|
||||||
CONFIG.Token.objectClass = Token5e;
|
CONFIG.Token.objectClass = Token5e;
|
||||||
CONFIG.time.roundTime = 6;
|
CONFIG.time.roundTime = 6;
|
||||||
CONFIG.fontFamilies = [
|
CONFIG.fontFamilies = ["Engli-Besh", "Open Sans", "Russo One"];
|
||||||
"Engli-Besh",
|
|
||||||
"Open Sans",
|
|
||||||
"Russo One"
|
|
||||||
];
|
|
||||||
|
|
||||||
CONFIG.Dice.DamageRoll = dice.DamageRoll;
|
CONFIG.Dice.DamageRoll = dice.DamageRoll;
|
||||||
CONFIG.Dice.D20Roll = dice.D20Roll;
|
CONFIG.Dice.D20Roll = dice.D20Roll;
|
||||||
|
@ -145,14 +138,37 @@ Hooks.once("init", function() {
|
||||||
// makeDefault: true,
|
// makeDefault: true,
|
||||||
// label: "SW5E.SheetClassStarship"
|
// label: "SW5E.SheetClassStarship"
|
||||||
// });
|
// });
|
||||||
Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
|
Actors.registerSheet("sw5e", ActorSheet5eVehicle, {
|
||||||
types: ['vehicle'],
|
types: ["vehicle"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "SW5E.SheetClassVehicle"
|
label: "SW5E.SheetClassVehicle"
|
||||||
});
|
});
|
||||||
Items.unregisterSheet("core", ItemSheet);
|
Items.unregisterSheet("core", ItemSheet);
|
||||||
Items.registerSheet("sw5e", ItemSheet5e, {
|
Items.registerSheet("sw5e", ItemSheet5e, {
|
||||||
types: ['weapon', 'equipment', 'consumable', 'tool', 'loot', 'class', 'power', 'feat', 'species', 'backpack', 'archetype', 'classfeature', 'background', 'fightingmastery', 'fightingstyle', 'lightsaberform', 'deployment', 'deploymentfeature', 'starship', 'starshipfeature', 'starshipmod', 'venture'],
|
types: [
|
||||||
|
"weapon",
|
||||||
|
"equipment",
|
||||||
|
"consumable",
|
||||||
|
"tool",
|
||||||
|
"loot",
|
||||||
|
"class",
|
||||||
|
"power",
|
||||||
|
"feat",
|
||||||
|
"species",
|
||||||
|
"backpack",
|
||||||
|
"archetype",
|
||||||
|
"classfeature",
|
||||||
|
"background",
|
||||||
|
"fightingmastery",
|
||||||
|
"fightingstyle",
|
||||||
|
"lightsaberform",
|
||||||
|
"deployment",
|
||||||
|
"deploymentfeature",
|
||||||
|
"starship",
|
||||||
|
"starshipfeature",
|
||||||
|
"starshipmod",
|
||||||
|
"venture"
|
||||||
|
],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "SW5E.SheetClassItem"
|
label: "SW5E.SheetClassItem"
|
||||||
});
|
});
|
||||||
|
@ -161,7 +177,6 @@ Hooks.once("init", function() {
|
||||||
return preloadHandlebarsTemplates();
|
return preloadHandlebarsTemplates();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Setup */
|
/* Foundry VTT Setup */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -170,27 +185,73 @@ Hooks.once("init", function() {
|
||||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
||||||
*/
|
*/
|
||||||
Hooks.once("setup", function () {
|
Hooks.once("setup", function () {
|
||||||
|
|
||||||
// Localize CONFIG objects once up-front
|
// Localize CONFIG objects once up-front
|
||||||
const toLocalize = [
|
const toLocalize = [
|
||||||
"abilities", "abilityAbbreviations", "abilityActivationTypes", "abilityConsumptionTypes", "actorSizes", "alignments",
|
"abilities",
|
||||||
"armorProficiencies", "armorPropertiesTypes", "conditionTypes", "consumableTypes", "cover", "currencies", "damageResistanceTypes",
|
"abilityAbbreviations",
|
||||||
"damageTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes", "languages",
|
"abilityActivationTypes",
|
||||||
"limitedUsePeriods", "movementTypes", "movementUnits", "polymorphSettings", "proficiencyLevels", "senses", "skills",
|
"abilityConsumptionTypes",
|
||||||
"starshipRolessm", "starshipRolesmed", "starshipRoleslg", "starshipRoleshuge", "starshipRolesgrg", "starshipSkills",
|
"actorSizes",
|
||||||
"powerComponents", "powerLevels", "powerPreparationModes", "powerScalingModes", "powerSchools", "targetTypes",
|
"alignments",
|
||||||
"timePeriods", "toolProficiencies", "weaponProficiencies", "weaponProperties", "weaponSizes", "weaponTypes"
|
"armorProficiencies",
|
||||||
|
"armorPropertiesTypes",
|
||||||
|
"conditionTypes",
|
||||||
|
"consumableTypes",
|
||||||
|
"cover",
|
||||||
|
"currencies",
|
||||||
|
"damageResistanceTypes",
|
||||||
|
"damageTypes",
|
||||||
|
"distanceUnits",
|
||||||
|
"equipmentTypes",
|
||||||
|
"healingTypes",
|
||||||
|
"itemActionTypes",
|
||||||
|
"languages",
|
||||||
|
"limitedUsePeriods",
|
||||||
|
"movementTypes",
|
||||||
|
"movementUnits",
|
||||||
|
"polymorphSettings",
|
||||||
|
"proficiencyLevels",
|
||||||
|
"senses",
|
||||||
|
"skills",
|
||||||
|
"starshipRolessm",
|
||||||
|
"starshipRolesmed",
|
||||||
|
"starshipRoleslg",
|
||||||
|
"starshipRoleshuge",
|
||||||
|
"starshipRolesgrg",
|
||||||
|
"starshipSkills",
|
||||||
|
"powerComponents",
|
||||||
|
"powerLevels",
|
||||||
|
"powerPreparationModes",
|
||||||
|
"powerScalingModes",
|
||||||
|
"powerSchools",
|
||||||
|
"targetTypes",
|
||||||
|
"timePeriods",
|
||||||
|
"toolProficiencies",
|
||||||
|
"weaponProficiencies",
|
||||||
|
"weaponProperties",
|
||||||
|
"weaponSizes",
|
||||||
|
"weaponTypes"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Exclude some from sorting where the default order matters
|
// Exclude some from sorting where the default order matters
|
||||||
const noSort = [
|
const noSort = [
|
||||||
"abilities", "alignments", "currencies", "distanceUnits", "movementUnits", "itemActionTypes", "proficiencyLevels",
|
"abilities",
|
||||||
"limitedUsePeriods", "powerComponents", "powerLevels", "powerPreparationModes", "weaponTypes"
|
"alignments",
|
||||||
|
"currencies",
|
||||||
|
"distanceUnits",
|
||||||
|
"movementUnits",
|
||||||
|
"itemActionTypes",
|
||||||
|
"proficiencyLevels",
|
||||||
|
"limitedUsePeriods",
|
||||||
|
"powerComponents",
|
||||||
|
"powerLevels",
|
||||||
|
"powerPreparationModes",
|
||||||
|
"weaponTypes"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Localize and sort CONFIG objects
|
// Localize and sort CONFIG objects
|
||||||
for (let o of toLocalize) {
|
for (let o of toLocalize) {
|
||||||
const localized = Object.entries(CONFIG.SW5E[o]).map(e => {
|
const localized = Object.entries(CONFIG.SW5E[o]).map((e) => {
|
||||||
return [e[0], game.i18n.localize(e[1])];
|
return [e[0], game.i18n.localize(e[1])];
|
||||||
});
|
});
|
||||||
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||||
|
@ -202,7 +263,7 @@ Hooks.once("setup", function() {
|
||||||
// add DND5E translation for module compatability
|
// add DND5E translation for module compatability
|
||||||
game.i18n.translations.DND5E = game.i18n.translations.SW5E;
|
game.i18n.translations.DND5E = game.i18n.translations.SW5E;
|
||||||
// console.log(game.settings.get("sw5e", "colorTheme"));
|
// console.log(game.settings.get("sw5e", "colorTheme"));
|
||||||
let theme = game.settings.get("sw5e", "colorTheme") + '-theme';
|
let theme = game.settings.get("sw5e", "colorTheme") + "-theme";
|
||||||
document.body.classList.add(theme);
|
document.body.classList.add(theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -211,7 +272,6 @@ Hooks.once("setup", function() {
|
||||||
* Once the entire VTT framework is initialized, check to see if we should perform a data migration
|
* Once the entire VTT framework is initialized, check to see if we should perform a data migration
|
||||||
*/
|
*/
|
||||||
Hooks.once("ready", function () {
|
Hooks.once("ready", function () {
|
||||||
|
|
||||||
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
|
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
|
||||||
Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot));
|
Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot));
|
||||||
|
|
||||||
|
@ -221,8 +281,11 @@ Hooks.once("ready", function() {
|
||||||
const NEEDS_MIGRATION_VERSION = "1.3.5.R1-A6";
|
const NEEDS_MIGRATION_VERSION = "1.3.5.R1-A6";
|
||||||
// Check for R1 SW5E versions
|
// Check for R1 SW5E versions
|
||||||
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A6";
|
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A6";
|
||||||
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
const COMPATIBLE_MIGRATION_VERSION = 0.8;
|
||||||
const needsMigration = currentVersion && (isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) || isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion));
|
const needsMigration =
|
||||||
|
currentVersion &&
|
||||||
|
(isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) ||
|
||||||
|
isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion));
|
||||||
if (!needsMigration && needsMigration !== "") return;
|
if (!needsMigration && needsMigration !== "") return;
|
||||||
|
|
||||||
// Perform the migration
|
// Perform the migration
|
||||||
|
@ -243,13 +306,11 @@ Hooks.on("canvasInit", function() {
|
||||||
SquareGrid.prototype.measureDistances = measureDistances;
|
SquareGrid.prototype.measureDistances = measureDistances;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Other Hooks */
|
/* Other Hooks */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
Hooks.on("renderChatMessage", (app, html, data) => {
|
Hooks.on("renderChatMessage", (app, html, data) => {
|
||||||
|
|
||||||
// Display action buttons
|
// Display action buttons
|
||||||
chat.displayChatActionButtons(app, html, data);
|
chat.displayChatActionButtons(app, html, data);
|
||||||
|
|
||||||
|
@ -262,7 +323,7 @@ Hooks.on("renderChatMessage", (app, html, data) => {
|
||||||
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
||||||
Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html));
|
Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html));
|
||||||
Hooks.on("renderChatPopout", (app, html, data) => Item5e.chatListeners(html));
|
Hooks.on("renderChatPopout", (app, html, data) => Item5e.chatListeners(html));
|
||||||
Hooks.on('getActorDirectoryEntryContext', Actor5e.addDirectoryContextOptions);
|
Hooks.on("getActorDirectoryEntryContext", Actor5e.addDirectoryContextOptions);
|
||||||
Hooks.on("renderSceneDirectory", (app, html, data) => {
|
Hooks.on("renderSceneDirectory", (app, html, data) => {
|
||||||
//console.log(html.find("header.folder-header"));
|
//console.log(html.find("header.folder-header"));
|
||||||
setFolderBackground(html);
|
setFolderBackground(html);
|
||||||
|
@ -284,16 +345,14 @@ Hooks.on("ActorSheet5eCharacterNew", (app, html, data) => {
|
||||||
console.log("renderSwaltSheet");
|
console.log("renderSwaltSheet");
|
||||||
});
|
});
|
||||||
// FIXME: This helper is needed for the vehicle sheet. It should probably be refactored.
|
// FIXME: This helper is needed for the vehicle sheet. It should probably be refactored.
|
||||||
Handlebars.registerHelper('getProperty', function (data, property) {
|
Handlebars.registerHelper("getProperty", function (data, property) {
|
||||||
return getProperty(data, property);
|
return getProperty(data, property);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function setFolderBackground(html) {
|
function setFolderBackground(html) {
|
||||||
html.find("header.folder-header").each(function () {
|
html.find("header.folder-header").each(function () {
|
||||||
let bgColor = $(this).css("background-color");
|
let bgColor = $(this).css("background-color");
|
||||||
if(bgColor == undefined)
|
if (bgColor == undefined) bgColor = "rgb(255,255,255)";
|
||||||
bgColor = "rgb(255,255,255)";
|
$(this).closest("li").css("background-color", bgColor);
|
||||||
$(this).closest('li').css("background-color", bgColor);
|
});
|
||||||
})
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue