diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e9fbc10e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "tabWidth": 2, + "trailingComma": "none", + "bracketSpacing": false, + "arrowParens": "avoid" +} \ No newline at end of file diff --git a/module/actor/entity.js b/module/actor/entity.js index 733cf637..84e192fd 100644 --- a/module/actor/entity.js +++ b/module/actor/entity.js @@ -1,13 +1,12 @@ -import { d20Roll, damageRoll } from "../dice.js"; +import {d20Roll, damageRoll} from "../dice.js"; import ShortRestDialog from "../apps/short-rest.js"; import LongRestDialog from "../apps/long-rest.js"; -import {SW5E} from '../config.js'; +import {SW5E} from "../config.js"; /** * Extend the base Actor class to implement additional system-specific logic for SW5e. */ export default class Actor5e extends Actor { - /** * Is this Actor currently polymorphed into some other creature? * @return {boolean} @@ -20,7 +19,7 @@ export default class Actor5e extends Actor { /** @override */ prepareBaseData() { - switch ( this.data.type ) { + switch (this.data.type) { case "character": return this._prepareCharacterData(this.data); case "npc": @@ -43,8 +42,8 @@ export default class Actor5e extends Actor { let originalSaves = null; let originalSkills = null; if (this.isPolymorphed) { - const transformOptions = this.getFlag('sw5e', 'transformOptions'); - const original = game.actors?.get(this.getFlag('sw5e', 'originalActor')); + const transformOptions = this.getFlag("sw5e", "transformOptions"); + const original = game.actors?.get(this.getFlag("sw5e", "originalActor")); if (original) { if (transformOptions.mergeSaves) { originalSaves = original.data.data.abilities; @@ -84,8 +83,8 @@ export default class Actor5e extends Actor { const athlete = flags.remarkableAthlete; const joat = flags.jackOfAllTrades; init.mod = data.abilities.dex.mod; - if ( joat ) init.prof = Math.floor(0.5 * data.attributes.prof); - else if ( athlete ) init.prof = Math.ceil(0.5 * data.attributes.prof); + if (joat) init.prof = Math.floor(0.5 * data.attributes.prof); + else if (athlete) init.prof = Math.ceil(0.5 * data.attributes.prof); else init.prof = 0; init.value = init.value ?? 0; init.bonus = init.value + (flags.initiativeAlert ? 5 : 0); @@ -94,7 +93,8 @@ export default class Actor5e extends Actor { // Prepare power-casting data data.attributes.powerForceLightDC = 8 + data.abilities.wis.mod + data.attributes.prof ?? 10; data.attributes.powerForceDarkDC = 8 + data.abilities.cha.mod + data.attributes.prof ?? 10; - data.attributes.powerForceUnivDC = Math.max(data.attributes.powerForceLightDC,data.attributes.powerForceDarkDC) ?? 10; + data.attributes.powerForceUnivDC = + Math.max(data.attributes.powerForceLightDC, data.attributes.powerForceDarkDC) ?? 10; data.attributes.powerTechDC = 8 + data.abilities.int.mod + data.attributes.prof ?? 10; this._computePowercastingProgression(this.data); @@ -135,7 +135,7 @@ export default class Actor5e extends Actor { getRollData() { const data = super.getRollData(); data.classes = this.data.items.reduce((obj, i) => { - if ( i.type === "class" ) { + if (i.type === "class") { obj[i.name.slugify({strict: true})] = i.data; } return obj; @@ -154,7 +154,7 @@ export default class Actor5e extends Actor { * @param {number} priorLevel The previous level of the added class * @return {Promise} Array of Item5e entities */ - static async getClassFeatures({className="", archetypeName="", level=1, priorLevel=0}={}) { + static async getClassFeatures({className = "", archetypeName = "", level = 1, priorLevel = 0} = {}) { className = className.toLowerCase(); archetypeName = archetypeName.slugify(); @@ -164,27 +164,27 @@ export default class Actor5e extends Actor { // Acquire class features let ids = []; - for ( let [l, f] of Object.entries(clsConfig.features || {}) ) { + for (let [l, f] of Object.entries(clsConfig.features || {})) { l = parseInt(l); - if ( (l <= level) && (l > priorLevel) ) ids = ids.concat(f); + if (l <= level && l > priorLevel) ids = ids.concat(f); } // Acquire archetype features 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); - 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 const features = []; - for ( let id of ids ) { + for (let id of ids) { features.push(await fromUuid(id)); } // Class powers should always be prepared - for ( const feature of features ) { - if ( feature.type === "power" ) { + for (const feature of features) { + if (feature.type === "power") { const preparation = feature.data.data.preparation; preparation.mode = "always"; preparation.prepared = true; @@ -196,10 +196,10 @@ export default class Actor5e extends Actor { /* -------------------------------------------- */ /** @override */ - async updateEmbeddedEntity(embeddedName, data, options={}) { + async updateEmbeddedEntity(embeddedName, data, options = {}) { const createItems = embeddedName === "OwnedItem" ? await this._createClassFeatures(data) : []; let updated = await super.updateEmbeddedEntity(embeddedName, data, options); - if ( createItems.length ) await this.createEmbeddedEntity("OwnedItem", createItems); + if (createItems.length) await this.createEmbeddedEntity("OwnedItem", createItems); return updated; } @@ -213,30 +213,30 @@ export default class Actor5e extends Actor { let toCreate = []; for (let u of updated instanceof Array ? updated : [updated]) { const item = this.items.get(u._id); - if (!item || (item.data.type !== "class")) continue; + if (!item || item.data.type !== "class") continue; const updateData = expandObject(u); const config = { className: updateData.name || item.data.name, archetypeName: getProperty(updateData, "data.archetype") || item.data.data.archetype, level: getProperty(updateData, "data.levels"), priorLevel: item ? item.data.data.levels : 0 - } + }; // Get and create features for an increased class level let changed = false; - if ( config.level && (config.level > config.priorLevel)) changed = true; - if ( config.archetypeName !== item.data.data.archetype ) changed = true; + if (config.level && config.level > config.priorLevel) changed = true; + if (config.archetypeName !== item.data.data.archetype) changed = true; // Get features to create - if ( changed ) { + if (changed) { const existing = new Set(this.items.map(i => i.name)); const features = await Actor5e.getClassFeatures(config); - for ( let f of features ) { - if ( !existing.has(f.name) ) toCreate.push(f); + for (let f of features) { + if (!existing.has(f.name)) toCreate.push(f); } } } - return toCreate + return toCreate; } /* -------------------------------------------- */ @@ -250,14 +250,17 @@ export default class Actor5e extends Actor { const data = actorData.data; // Determine character level and available hit dice based on owned Class items - const [level, hd] = actorData.items.reduce((arr, item) => { - if ( item.type === "class" ) { - const classLevels = parseInt(item.data.levels) || 1; - arr[0] += classLevels; - arr[1] += classLevels - (parseInt(item.data.hitDiceUsed) || 0); - } - return arr; - }, [0, 0]); + const [level, hd] = actorData.items.reduce( + (arr, item) => { + if (item.type === "class") { + const classLevels = parseInt(item.data.levels) || 1; + arr[0] += classLevels; + arr[1] += classLevels - (parseInt(item.data.hitDiceUsed) || 0); + } + return arr; + }, + [0, 0] + ); data.details.level = level; data.attributes.hd = hd; @@ -269,7 +272,7 @@ export default class Actor5e extends Actor { xp.max = this.getLevelExp(level || 1); const prior = this.getLevelExp(level - 1 || 0); 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); } @@ -288,7 +291,7 @@ export default class Actor5e extends Actor { data.attributes.prof = Math.floor((Math.max(data.details.cr, 1) + 7) / 4); // Powercaster Level - if ( data.attributes.powercasting && !Number.isNumeric(data.details.powerLevel) ) { + if (data.attributes.powercasting && !Number.isNumeric(data.details.powerLevel)) { data.details.powerLevel = Math.max(data.details.cr, 1); } } @@ -313,7 +316,7 @@ export default class Actor5e extends Actor { * @private */ _prepareSkills(actorData, bonuses, checkBonus, originalSkills) { - if (actorData.type === 'vehicle') return; + if (actorData.type === "vehicle") return; const data = actorData.data; const flags = actorData.flags.sw5e || {}; @@ -323,24 +326,24 @@ export default class Actor5e extends Actor { const athlete = flags.remarkableAthlete; const joat = flags.jackOfAllTrades; const observant = flags.observantFeat; - const skillBonus = Number.isNumeric(bonuses.skill) ? parseInt(bonuses.skill) : 0; + const skillBonus = Number.isNumeric(bonuses.skill) ? parseInt(bonuses.skill) : 0; for (let [id, skl] of Object.entries(data.skills)) { skl.value = Math.clamped(Number(skl.value).toNearest(0.5), 0, 2) ?? 0; let round = Math.floor; // 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; round = Math.ceil; } // Jack of All Trades - if ( joat && (skl.value < 0.5) ) { + if (joat && skl.value < 0.5) { skl.value = 0.5; } // Polymorph Skill Proficiencies - if ( originalSkills ) { + if (originalSkills) { skl.value = Math.max(skl.value, originalSkills[id].value); } @@ -351,7 +354,7 @@ export default class Actor5e extends Actor { skl.total = skl.mod + skl.prof + skl.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; } } @@ -362,10 +365,10 @@ export default class Actor5e extends Actor { * Prepare data related to the power-casting capabilities of the Actor * @private */ - _computePowercastingProgression (actorData) { - if (actorData.type === 'vehicle') return; + _computePowercastingProgression(actorData) { + if (actorData.type === "vehicle") return; const powers = actorData.data.powers; - const isNPC = actorData.type === 'npc'; + const isNPC = actorData.type === "npc"; // Translate the list of classes into force and tech power-casting progression const forceProgression = { @@ -394,157 +397,161 @@ export default class Actor5e extends Actor { // Tabulate the total power-casting progression const classes = this.data.items.filter(i => i.type === "class"); let priority = 0; - for ( let cls of classes ) { + for (let cls of classes) { const d = cls.data; - if ( d.powercasting === "none" ) continue; + if (d.powercasting === "none") continue; const levels = d.levels; const prog = d.powercasting; switch (prog) { - case 'consular': + case "consular": priority = 3; forceProgression.levels += levels; - forceProgression.multi += (SW5E.powerMaxLevel['consular'][19]/9)*levels; + forceProgression.multi += (SW5E.powerMaxLevel["consular"][19] / 9) * levels; forceProgression.classes++; // see if class controls high level forcecasting - if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){ - forceProgression.maxClass = 'consular'; + if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) { + forceProgression.maxClass = "consular"; forceProgression.maxClassLevels = levels; 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 - forceProgression.powersKnown += SW5E.powersKnown['consular'][Math.clamped((levels - 1), 0, 20)]; - forceProgression.points += SW5E.powerPoints['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)]; break; - case 'engineer': - priority = 2 + case "engineer": + priority = 2; techProgression.levels += levels; - techProgression.multi += (SW5E.powerMaxLevel['engineer'][19]/9)*levels; + techProgression.multi += (SW5E.powerMaxLevel["engineer"][19] / 9) * levels; techProgression.classes++; // see if class controls high level techcasting - if ((levels >= techProgression.maxClassLevels) && (priority > techProgression.maxClassPriority)){ - techProgression.maxClass = 'engineer'; + if (levels >= techProgression.maxClassLevels && priority > techProgression.maxClassPriority) { + techProgression.maxClass = "engineer"; techProgression.maxClassLevels = levels; 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.points += SW5E.powerPoints['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)]; break; - case 'guardian': + case "guardian": priority = 1; forceProgression.levels += levels; - forceProgression.multi += (SW5E.powerMaxLevel['guardian'][19]/9)*levels; + forceProgression.multi += (SW5E.powerMaxLevel["guardian"][19] / 9) * levels; forceProgression.classes++; // see if class controls high level forcecasting - if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){ - forceProgression.maxClass = 'guardian'; + if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) { + forceProgression.maxClass = "guardian"; forceProgression.maxClassLevels = levels; 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.points += SW5E.powerPoints['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)]; break; - case 'scout': + case "scout": priority = 1; techProgression.levels += levels; - techProgression.multi += (SW5E.powerMaxLevel['scout'][19]/9)*levels; + techProgression.multi += (SW5E.powerMaxLevel["scout"][19] / 9) * levels; techProgression.classes++; // see if class controls high level techcasting - if ((levels >= techProgression.maxClassLevels) && (priority > techProgression.maxClassPriority)){ - techProgression.maxClass = 'scout'; + if (levels >= techProgression.maxClassLevels && priority > techProgression.maxClassPriority) { + techProgression.maxClass = "scout"; techProgression.maxClassLevels = levels; 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.points += SW5E.powerPoints['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)]; break; - case 'sentinel': + case "sentinel": priority = 2; forceProgression.levels += levels; - forceProgression.multi += (SW5E.powerMaxLevel['sentinel'][19]/9)*levels; + forceProgression.multi += (SW5E.powerMaxLevel["sentinel"][19] / 9) * levels; forceProgression.classes++; // see if class controls high level forcecasting - if ((levels >= forceProgression.maxClassLevels) && (priority > forceProgression.maxClassPriority)){ - forceProgression.maxClass = 'sentinel'; + if (levels >= forceProgression.maxClassLevels && priority > forceProgression.maxClassPriority) { + forceProgression.maxClass = "sentinel"; forceProgression.maxClassLevels = levels; 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; + } } // EXCEPTION: multi-classed progression uses multi rounded down rather than levels if (!isNPC && forceProgression.classes > 1) { forceProgression.levels = Math.floor(forceProgression.multi); - forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel['multi'][forceProgression.levels - 1]; + forceProgression.maxClassPowerLevel = SW5E.powerMaxLevel["multi"][forceProgression.levels - 1]; } if (!isNPC && techProgression.classes > 1) { techProgression.levels = Math.floor(techProgression.multi); - techProgression.maxClassPowerLevel = SW5E.powerMaxLevel['multi'][techProgression.levels - 1]; + techProgression.maxClassPowerLevel = SW5E.powerMaxLevel["multi"][techProgression.levels - 1]; } - + // EXCEPTION: NPC with an explicit power-caster level if (isNPC && actorData.data.details.powerForceLevel) { forceProgression.levels = actorData.data.details.powerForceLevel; actorData.data.attributes.force.level = forceProgression.levels; forceProgression.maxClass = actorData.data.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 (isNPC && actorData.data.details.powerTechLevel) { techProgression.levels = actorData.data.details.powerTechLevel; actorData.data.attributes.tech.level = techProgression.levels; techProgression.maxClass = actorData.data.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)]; } // Look up the number of slots per level from the powerLimit table - let forcePowerLimit = Array.from(SW5E.powerLimit['none']); - for (let i = 0; i < (forceProgression.maxClassPowerLevel); i++) { + let forcePowerLimit = Array.from(SW5E.powerLimit["none"]); + for (let i = 0; i < forceProgression.maxClassPowerLevel; i++) { forcePowerLimit[i] = SW5E.powerLimit[forceProgression.maxClass][i]; } - for ( let [n, lvl] of Object.entries(powers) ) { + for (let [n, lvl] of Object.entries(powers)) { let i = parseInt(n.slice(-1)); - if ( Number.isNaN(i) ) continue; - if ( Number.isNumeric(lvl.foverride) ) lvl.fmax = Math.max(parseInt(lvl.foverride), 0); - else lvl.fmax = forcePowerLimit[i-1] || 0; - if (isNPC){ - lvl.fvalue = lvl.fmax; - }else{ - lvl.fvalue = Math.min(parseInt(lvl.fvalue || lvl.value || lvl.fmax),lvl.fmax); + if (Number.isNaN(i)) continue; + if (Number.isNumeric(lvl.foverride)) lvl.fmax = Math.max(parseInt(lvl.foverride), 0); + else lvl.fmax = forcePowerLimit[i - 1] || 0; + if (isNPC) { + lvl.fvalue = lvl.fmax; + } else { + lvl.fvalue = Math.min(parseInt(lvl.fvalue || lvl.value || lvl.fmax), lvl.fmax); } } - - let techPowerLimit = Array.from(SW5E.powerLimit['none']); - for (let i = 0; i < (techProgression.maxClassPowerLevel); i++) { + + let techPowerLimit = Array.from(SW5E.powerLimit["none"]); + for (let i = 0; i < techProgression.maxClassPowerLevel; i++) { techPowerLimit[i] = SW5E.powerLimit[techProgression.maxClass][i]; } - for ( let [n, lvl] of Object.entries(powers) ) { + for (let [n, lvl] of Object.entries(powers)) { let i = parseInt(n.slice(-1)); - if ( Number.isNaN(i) ) continue; - if ( Number.isNumeric(lvl.toverride) ) lvl.tmax = Math.max(parseInt(lvl.toverride), 0); - else lvl.tmax = techPowerLimit[i-1] || 0; - if (isNPC){ + if (Number.isNaN(i)) continue; + if (Number.isNumeric(lvl.toverride)) lvl.tmax = Math.max(parseInt(lvl.toverride), 0); + else lvl.tmax = techPowerLimit[i - 1] || 0; + if (isNPC) { lvl.tvalue = lvl.tmax; - }else{ - lvl.tvalue = Math.min(parseInt(lvl.tvalue || lvl.value || lvl.tmax),lvl.tmax); + } else { + lvl.tvalue = Math.min(parseInt(lvl.tvalue || lvl.value || lvl.tmax), lvl.tmax); } } // Set Force and tech power for PC Actors - if (!isNPC && forceProgression.levels){ + if (!isNPC && forceProgression.levels) { actorData.data.attributes.force.known.max = forceProgression.powersKnown; - actorData.data.attributes.force.points.max = forceProgression.points + Math.max(actorData.data.abilities.wis.mod,actorData.data.abilities.cha.mod); + actorData.data.attributes.force.points.max = + forceProgression.points + Math.max(actorData.data.abilities.wis.mod, actorData.data.abilities.cha.mod); actorData.data.attributes.force.level = forceProgression.levels; } - if (!isNPC && techProgression.levels){ + if (!isNPC && techProgression.levels) { actorData.data.attributes.tech.known.max = techProgression.powersKnown; actorData.data.attributes.tech.points.max = techProgression.points + actorData.data.abilities.int.mod; actorData.data.attributes.tech.level = techProgression.levels; @@ -552,24 +559,24 @@ export default class Actor5e extends Actor { // Tally Powers Known and check for migration first to avoid errors let hasKnownPowers = actorData?.data?.attributes?.force?.known?.value !== undefined; - if ( hasKnownPowers ) { + if (hasKnownPowers) { const knownPowers = this.data.items.filter(i => i.type === "power"); let knownForcePowers = 0; let knownTechPowers = 0; - for ( let knownPower of knownPowers ) { + for (let knownPower of knownPowers) { const d = knownPower.data; - switch (knownPower.data.school){ + switch (knownPower.data.school) { case "lgt": case "uni": - case "drk":{ + case "drk": { knownForcePowers++; break; } - case "tec":{ + case "tec": { knownTechPowers++; break; } - } + } continue; } actorData.data.attributes.force.known.value = knownForcePowers; @@ -589,39 +596,39 @@ export default class Actor5e extends Actor { * @private */ _computeEncumbrance(actorData) { - // Get the total weight from items const physicalItems = ["weapon", "equipment", "consumable", "tool", "backpack", "loot"]; let weight = actorData.items.reduce((weight, i) => { - if ( !physicalItems.includes(i.type) ) return weight; + if (!physicalItems.includes(i.type)) return weight; const q = i.data.quantity || 0; const w = i.data.weight || 0; - return weight + (q * w); + return weight + q * w; }, 0); // [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 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; } // Determine the encumbrance size class - let mod = { - tiny: 0.5, - sm: 1, - med: 1, - lg: 2, - huge: 4, - grg: 8 - }[actorData.data.traits.size] || 1; - if ( this.getFlag("sw5e", "powerfulBuild") ) mod = Math.min(mod * 2, 8); + let mod = + { + tiny: 0.5, + sm: 1, + med: 1, + lg: 2, + huge: 4, + grg: 8 + }[actorData.data.traits.size] || 1; + if (this.getFlag("sw5e", "powerfulBuild")) mod = Math.min(mod * 2, 8); // Compute Encumbrance percentage weight = weight.toNearest(0.1); const max = actorData.data.abilities.str.value * CONFIG.SW5E.encumbrance.strMultiplier * mod; 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}; } /* -------------------------------------------- */ @@ -629,16 +636,20 @@ export default class Actor5e extends Actor { /* -------------------------------------------- */ /** @override */ - static async create(data, options={}) { + static async create(data, options = {}) { data.token = data.token || {}; - if ( data.type === "character" ) { - mergeObject(data.token, { - vision: true, - dimSight: 30, - brightSight: 0, - actorLink: true, - disposition: 1 - }, {overwrite: false}); + if (data.type === "character") { + mergeObject( + data.token, + { + vision: true, + dimSight: 30, + brightSight: 0, + actorLink: true, + disposition: 1 + }, + {overwrite: false} + ); } return super.create(data, options); } @@ -646,21 +657,20 @@ export default class Actor5e extends Actor { /* -------------------------------------------- */ /** @override */ - async update(data, options={}) { - + async update(data, options = {}) { // Apply changes in Actor size to Token width/height const newSize = getProperty(data, "data.traits.size"); - if ( newSize && (newSize !== getProperty(this.data, "data.traits.size")) ) { + if (newSize && newSize !== getProperty(this.data, "data.traits.size")) { let size = CONFIG.SW5E.tokenSizes[newSize]; - if ( this.isToken ) this.token.update({height: size, width: size}); - else if ( !data["token.width"] && !hasProperty(data, "token.width") ) { + if (this.isToken) this.token.update({height: size, width: size}); + else if (!data["token.width"] && !hasProperty(data, "token.width")) { data["token.height"] = size; data["token.width"] = size; } } // Reset death save counters - if ( (this.data.data.attributes.hp.value <= 0) && (getProperty(data, "data.attributes.hp.value") > 0) ) { + if (this.data.data.attributes.hp.value <= 0 && getProperty(data, "data.attributes.hp.value") > 0) { setProperty(data, "data.attributes.death.success", 0); setProperty(data, "data.attributes.death.failure", 0); } @@ -672,10 +682,9 @@ export default class Actor5e extends Actor { /* -------------------------------------------- */ /** @override */ - async createEmbeddedEntity(embeddedName, itemData, options={}) { - + async createEmbeddedEntity(embeddedName, itemData, options = {}) { // Pre-creation steps for owned items - if ( embeddedName === "OwnedItem" ) this._preCreateOwnedItem(itemData, options); + if (embeddedName === "OwnedItem") this._preCreateOwnedItem(itemData, options); // Standard embedded entity creation return super.createEmbeddedEntity(embeddedName, itemData, options); @@ -690,60 +699,59 @@ export default class Actor5e extends Actor { * @private */ _preCreateOwnedItem(itemData, options) { - if ( this.data.type === "vehicle" ) return; - const isNPC = this.data.type === 'npc'; + if (this.data.type === "vehicle") return; + const isNPC = this.data.type === "npc"; let initial = {}; - switch ( itemData.type ) { - + switch (itemData.type) { case "weapon": - if ( getProperty(itemData, "data.equipped") === undefined ) { - initial["data.equipped"] = isNPC; // NPCs automatically equip weapons + if (getProperty(itemData, "data.equipped") === undefined) { + initial["data.equipped"] = isNPC; // NPCs automatically equip weapons } - if ( getProperty(itemData, "data.proficient") === undefined ) { - if ( isNPC ) { - initial["data.proficient"] = true; // NPCs automatically have equipment proficiency + if (getProperty(itemData, "data.proficient") === undefined) { + if (isNPC) { + initial["data.proficient"] = true; // NPCs automatically have equipment proficiency } else { const weaponProf = { - "natural": true, - "simpleVW": "sim", - "simpleB": "sim", - "simpleLW": "sim", - "martialVW": "mar", - "martialB": "mar", - "martialLW": "mar" - }[itemData.data?.weaponType]; // Player characters check proficiency + natural: true, + simpleVW: "sim", + simpleB: "sim", + simpleLW: "sim", + martialVW: "mar", + martialB: "mar", + martialLW: "mar" + }[itemData.data?.weaponType]; // Player characters check proficiency const actorWeaponProfs = this.data.data.traits?.weaponProf?.value || []; - const hasWeaponProf = (weaponProf === true) || actorWeaponProfs.includes(weaponProf); + const hasWeaponProf = weaponProf === true || actorWeaponProfs.includes(weaponProf); initial["data.proficient"] = hasWeaponProf; } } break; case "equipment": - if ( getProperty(itemData, "data.equipped") === undefined ) { - initial["data.equipped"] = isNPC; // NPCs automatically equip equipment + if (getProperty(itemData, "data.equipped") === undefined) { + initial["data.equipped"] = isNPC; // NPCs automatically equip equipment } - if ( getProperty(itemData, "data.proficient") === undefined ) { - if ( isNPC ) { - initial["data.proficient"] = true; // NPCs automatically have equipment proficiency + if (getProperty(itemData, "data.proficient") === undefined) { + if (isNPC) { + initial["data.proficient"] = true; // NPCs automatically have equipment proficiency } else { const armorProf = { - "natural": true, - "clothing": true, - "light": "lgt", - "medium": "med", - "heavy": "hvy", - "shield": "shl" - }[itemData.data?.armor?.type]; // Player characters check proficiency + natural: true, + clothing: true, + light: "lgt", + medium: "med", + heavy: "hvy", + shield: "shl" + }[itemData.data?.armor?.type]; // Player characters check proficiency const actorArmorProfs = this.data.data.traits?.armorProf?.value || []; - const hasEquipmentProf = (armorProf === true) || actorArmorProfs.includes(armorProf); + const hasEquipmentProf = armorProf === true || actorArmorProfs.includes(armorProf); initial["data.proficient"] = hasEquipmentProf; } } break; case "power": - initial["data.prepared"] = true; // automatically prepare powers for everyone + initial["data.prepared"] = true; // automatically prepare powers for everyone break; } mergeObject(itemData, initial); @@ -755,9 +763,9 @@ export default class Actor5e extends Actor { /** @override */ async modifyTokenAttribute(attribute, value, isDelta, isBar) { - if ( attribute === "attributes.hp" ) { + if (attribute === "attributes.hp") { 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 super.modifyTokenAttribute(attribute, value, isDelta, isBar); @@ -771,7 +779,7 @@ export default class Actor5e extends Actor { * @param {number} multiplier A multiplier which allows for resistance, vulnerability, or healing * @return {Promise} A Promise which resolves once the damage has been applied */ - async applyDamage(amount=0, multiplier=1) { + async applyDamage(amount = 0, multiplier = 1) { amount = Math.floor(parseInt(amount) * multiplier); const hp = this.data.data.attributes.hp; @@ -791,12 +799,16 @@ export default class Actor5e extends Actor { // Delegate damage application to a hook // TODO replace this in the future with a better modifyTokenAttribute function in the core - const allowed = Hooks.call("modifyTokenAttribute", { - attribute: "attributes.hp", - value: amount, - isDelta: false, - isBar: true - }, updates); + const allowed = Hooks.call( + "modifyTokenAttribute", + { + attribute: "attributes.hp", + value: amount, + isDelta: false, + isBar: true + }, + updates + ); return allowed !== false ? this.update(updates) : this; } @@ -809,7 +821,7 @@ export default class Actor5e extends Actor { * @param {Object} options Options which configure how the skill check is rolled * @return {Promise} A Promise which resolves to the created Roll instance */ - rollSkill(skillId, options={}) { + rollSkill(skillId, options = {}) { const skl = this.data.data.skills[skillId]; const bonuses = getProperty(this.data.data, "bonuses.abilities") || {}; @@ -818,13 +830,13 @@ export default class Actor5e extends Actor { const data = {mod: skl.mod + skl.prof}; // Ability test bonus - if ( bonuses.check ) { + if (bonuses.check) { data["checkBonus"] = bonuses.check; parts.push("@checkBonus"); } // Skill check bonus - if ( bonuses.skill ) { + if (bonuses.skill) { data["skillBonus"] = bonuses.skill; parts.push("@skillBonus"); } @@ -835,7 +847,7 @@ export default class Actor5e extends Actor { } // 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 const rollData = mergeObject(options, { @@ -844,7 +856,7 @@ export default class Actor5e extends Actor { title: game.i18n.format("SW5E.SkillPromptTitle", {skill: CONFIG.SW5E.skills[skillId]}), halflingLucky: this.getFlag("sw5e", "halflingLucky"), reliableTalent: reliableTalent, - messageData: {"flags.sw5e.roll": {type: "skill", skillId }} + messageData: {"flags.sw5e.roll": {type: "skill", skillId}} }); rollData.speaker = options.speaker || ChatMessage.getSpeaker({actor: this}); return d20Roll(rollData); @@ -858,7 +870,7 @@ export default class Actor5e extends Actor { * @param {String}abilityId The ability id (e.g. "str") * @param {Object} options Options which configure how ability tests or saving throws are rolled */ - rollAbility(abilityId, options={}) { + rollAbility(abilityId, options = {}) { const label = CONFIG.SW5E.abilities[abilityId]; new Dialog({ title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}), @@ -885,7 +897,7 @@ export default class Actor5e extends Actor { * @param {Object} options Options which configure how ability tests are rolled * @return {Promise} A Promise which resolves to the created Roll instance */ - rollAbilityTest(abilityId, options={}) { + rollAbilityTest(abilityId, options = {}) { const label = CONFIG.SW5E.abilities[abilityId]; const abl = this.data.data.abilities[abilityId]; @@ -895,18 +907,17 @@ export default class Actor5e extends Actor { // Add feat-related proficiency bonuses const feats = this.data.flags.sw5e || {}; - if ( feats.remarkableAthlete && SW5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) { + if (feats.remarkableAthlete && SW5E.characterFlags.remarkableAthlete.abilities.includes(abilityId)) { parts.push("@proficiency"); data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof); - } - else if ( feats.jackOfAllTrades ) { + } else if (feats.jackOfAllTrades) { parts.push("@proficiency"); data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof); } // Add global actor bonus const bonuses = getProperty(this.data.data, "bonuses.abilities") || {}; - if ( bonuses.check ) { + if (bonuses.check) { parts.push("@checkBonus"); data.checkBonus = bonuses.check; } @@ -922,7 +933,7 @@ export default class Actor5e extends Actor { data: data, title: game.i18n.format("SW5E.AbilityPromptTitle", {ability: label}), halflingLucky: feats.halflingLucky, - messageData: {"flags.sw5e.roll": {type: "ability", abilityId }} + messageData: {"flags.sw5e.roll": {type: "ability", abilityId}} }); rollData.speaker = options.speaker || ChatMessage.getSpeaker({actor: this}); return d20Roll(rollData); @@ -937,7 +948,7 @@ export default class Actor5e extends Actor { * @param {Object} options Options which configure how ability tests are rolled * @return {Promise} A Promise which resolves to the created Roll instance */ - rollAbilitySave(abilityId, options={}) { + rollAbilitySave(abilityId, options = {}) { const label = CONFIG.SW5E.abilities[abilityId]; const abl = this.data.data.abilities[abilityId]; @@ -946,14 +957,14 @@ export default class Actor5e extends Actor { const data = {mod: abl.mod}; // Include proficiency bonus - if ( abl.prof > 0 ) { + if (abl.prof > 0) { parts.push("@prof"); data.prof = abl.prof; } // Include a global actor ability save bonus const bonuses = getProperty(this.data.data, "bonuses.abilities") || {}; - if ( bonuses.save ) { + if (bonuses.save) { parts.push("@saveBonus"); data.saveBonus = bonuses.save; } @@ -969,7 +980,7 @@ export default class Actor5e extends Actor { data: data, title: game.i18n.format("SW5E.SavePromptTitle", {ability: label}), halflingLucky: this.getFlag("sw5e", "halflingLucky"), - messageData: {"flags.sw5e.roll": {type: "save", abilityId }} + messageData: {"flags.sw5e.roll": {type: "save", abilityId}} }); rollData.speaker = options.speaker || ChatMessage.getSpeaker({actor: this}); return d20Roll(rollData); @@ -982,11 +993,10 @@ export default class Actor5e extends Actor { * @param {Object} options Additional options which modify the roll * @return {Promise} 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 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")); return null; } @@ -998,7 +1008,7 @@ export default class Actor5e extends Actor { // Include a global actor ability save bonus const bonuses = getProperty(this.data.data, "bonuses.abilities") || {}; - if ( bonuses.save ) { + if (bonuses.save) { parts.push("@saveBonus"); data.saveBonus = bonuses.save; } @@ -1015,28 +1025,31 @@ export default class Actor5e extends Actor { }); rollData.speaker = speaker; const roll = await d20Roll(rollData); - if ( !roll ) return null; + if (!roll) return null; // Take action depending on the result const success = roll.total >= 10; const d20 = roll.dice[0].total; // Save success - if ( success ) { + if (success) { let successes = (death.success || 0) + 1; // Critical Success = revive with 1hp - if ( d20 === 20 ) { + if (d20 === 20) { await this.update({ "data.attributes.death.success": 0, "data.attributes.death.failure": 0, "data.attributes.hp.value": 1 }); - await ChatMessage.create({content: game.i18n.format("SW5E.DeathSaveCriticalSuccess", {name: this.name}), speaker}); + await ChatMessage.create({ + content: game.i18n.format("SW5E.DeathSaveCriticalSuccess", {name: this.name}), + speaker + }); } // 3 Successes = survive and reset checks - else if ( successes === 3 ) { + else if (successes === 3) { await this.update({ "data.attributes.death.success": 0, "data.attributes.death.failure": 0 @@ -1052,7 +1065,8 @@ export default class Actor5e extends Actor { else { let failures = (death.failure || 0) + (d20 === 1 ? 2 : 1); await this.update({"data.attributes.death.failure": Math.clamped(failures, 0, 3)}); - if ( failures >= 3 ) { // 3 Failures = death + if (failures >= 3) { + // 3 Failures = death await ChatMessage.create({content: game.i18n.format("SW5E.DeathSaveFailure", {name: this.name}), speaker}); } } @@ -1070,13 +1084,12 @@ export default class Actor5e extends Actor { * @param {boolean} [dialog] Show a dialog prompt for configuring the hit die roll? * @return {Promise} 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 let cls = null; - if ( !denomination ) { + if (!denomination) { 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; } @@ -1084,12 +1097,12 @@ export default class Actor5e extends Actor { else { cls = this.items.find(i => { 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); }); } // If no class is available, display an error notification - if ( !cls ) { + if (!cls) { ui.notifications.error(game.i18n.format("SW5E.HitDiceWarn", {name: this.name, formula: denomination})); return null; } @@ -1111,7 +1124,7 @@ export default class Actor5e extends Actor { dialogOptions: {width: 350}, messageData: {"flags.sw5e.roll": {type: "hitDie"}} }); - if ( !roll ) return null; + if (!roll) return null; // Adjust actor data await cls.update({"data.hitDiceUsed": cls.data.data.hitDiceUsed + 1}); @@ -1132,8 +1145,7 @@ export default class Actor5e extends Actor { * @param {boolean} autoHDThreshold A number of missing hit points which would trigger an automatic HD roll * @return {Promise} 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 const hp = this.data.data.attributes.hp; const hd0 = this.data.data.attributes.hd; @@ -1141,19 +1153,19 @@ export default class Actor5e extends Actor { let newDay = false; // Display a Dialog for rolling hit dice - if ( dialog ) { + if (dialog) { try { newDay = await ShortRestDialog.shortRestDialog({actor: this, canRoll: hd0 > 0}); - } catch(err) { + } catch (err) { return; } } // Automatically spend hit dice - else if ( autoHD ) { - while ( (hp.value + autoHDThreshold) <= hp.max ) { + else if (autoHD) { + while (hp.value + autoHDThreshold <= hp.max) { const r = await this.rollHitDie(undefined, {dialog: false}); - if ( r === null ) break; + if (r === null) break; } } @@ -1164,11 +1176,11 @@ export default class Actor5e extends Actor { // Automatically Retore Tech Points this.update({"data.attributes.tech.points.value": this.data.data.attributes.tech.points.max}); - + // Recover character resources const updateData = {}; - for ( let [k, r] of Object.entries(this.data.data.resources) ) { - if ( r.max && r.sr ) { + for (let [k, r] of Object.entries(this.data.data.resources)) { + if (r.max && r.sr) { updateData[`data.resources.${k}.value`] = r.max; } } @@ -1185,29 +1197,34 @@ export default class Actor5e extends Actor { await this.updateEmbeddedEntity("OwnedItem", updateItems); // Display a Chat Message summarizing the rest effects - if ( chat ) { - + if (chat) { // Summarize the rest duration let restFlavor; switch (game.settings.get("sw5e", "restVariant")) { - case 'normal': restFlavor = game.i18n.localize("SW5E.ShortRestNormal"); break; - case 'gritty': restFlavor = game.i18n.localize(newDay ? "SW5E.ShortRestOvernight" : "SW5E.ShortRestGritty"); break; - case 'epic': restFlavor = game.i18n.localize("SW5E.ShortRestEpic"); break; + case "normal": + restFlavor = game.i18n.localize("SW5E.ShortRestNormal"); + break; + case "gritty": + restFlavor = game.i18n.localize(newDay ? "SW5E.ShortRestOvernight" : "SW5E.ShortRestGritty"); + break; + case "epic": + restFlavor = game.i18n.localize("SW5E.ShortRestEpic"); + break; } // Summarize the health effects let srMessage = "SW5E.ShortRestResultShort"; - if ((dhd !== 0) && (dhp !== 0)){ - if (dtp !== 0){ + if (dhd !== 0 && dhp !== 0) { + if (dtp !== 0) { srMessage = "SW5E.ShortRestResultWithTech"; - }else{ + } else { srMessage = "SW5E.ShortRestResult"; } - }else{ - if (dtp !== 0){ + } else { + if (dtp !== 0) { srMessage = "SW5E.ShortRestResultOnlyTech"; } - } + } // Create a chat message ChatMessage.create({ @@ -1226,7 +1243,7 @@ export default class Actor5e extends Actor { updateData: updateData, updateItems: updateItems, newDay: newDay - } + }; } /* -------------------------------------------- */ @@ -1238,14 +1255,14 @@ export default class Actor5e extends Actor { * @param {boolean} newDay Whether the long rest carries over to a new day * @return {Promise} A Promise which resolves once the long rest workflow has completed */ - async longRest({dialog=true, chat=true, newDay=true}={}) { + async longRest({dialog = true, chat = true, newDay = true} = {}) { const data = this.data.data; // Maybe present a confirmation dialog - if ( dialog ) { + if (dialog) { try { newDay = await LongRestDialog.longRestDialog({actor: this}); - } catch(err) { + } catch (err) { return; } } @@ -1267,65 +1284,73 @@ export default class Actor5e extends Actor { }; // Recover character resources - for ( let [k, r] of Object.entries(data.resources) ) { - if ( r.max && (r.sr || r.lr) ) { + for (let [k, r] of Object.entries(data.resources)) { + if (r.max && (r.sr || r.lr)) { updateData[`data.resources.${k}.value`] = r.max; } } // Recover power slots - for ( let [k, v] of Object.entries(data.powers) ) { - updateData[`data.powers.${k}.fvalue`] = Number.isNumeric(v.foverride) ? v.foverride : (v.fmax ?? 0); + for (let [k, v] of Object.entries(data.powers)) { + updateData[`data.powers.${k}.fvalue`] = Number.isNumeric(v.foverride) ? v.foverride : v.fmax ?? 0; } - for ( let [k, v] of Object.entries(data.powers) ) { - updateData[`data.powers.${k}.tvalue`] = Number.isNumeric(v.toverride) ? v.toverride : (v.tmax ?? 0); + for (let [k, v] of Object.entries(data.powers)) { + updateData[`data.powers.${k}.tvalue`] = Number.isNumeric(v.toverride) ? v.toverride : v.tmax ?? 0; } // Determine the number of hit dice which may be recovered let recoverHD = Math.max(Math.floor(data.details.level / 2), 1); let dhd = 0; // Sort classes which can recover HD, assuming players prefer recovering larger HD first. - const updateItems = this.items.filter(item => item.data.type === "class").sort((a, b) => { - let da = parseInt(a.data.data.hitDice.slice(1)) || 0; - let db = parseInt(b.data.data.hitDice.slice(1)) || 0; - return db - da; - }).reduce((updates, item) => { - const d = item.data.data; - if ( (recoverHD > 0) && (d.hitDiceUsed > 0) ) { - let delta = Math.min(d.hitDiceUsed || 0, recoverHD); - recoverHD -= delta; - dhd += delta; - updates.push({_id: item.id, "data.hitDiceUsed": d.hitDiceUsed - delta}); - } - return updates; - }, []); + const updateItems = this.items + .filter(item => item.data.type === "class") + .sort((a, b) => { + let da = parseInt(a.data.data.hitDice.slice(1)) || 0; + let db = parseInt(b.data.data.hitDice.slice(1)) || 0; + return db - da; + }) + .reduce((updates, item) => { + const d = item.data.data; + if (recoverHD > 0 && d.hitDiceUsed > 0) { + let delta = Math.min(d.hitDiceUsed || 0, recoverHD); + recoverHD -= delta; + dhd += delta; + updates.push({_id: item.id, "data.hitDiceUsed": d.hitDiceUsed - delta}); + } + return updates; + }, []); // Iterate over owned items, restoring uses per day and recovering Hit Dice const recovery = newDay ? ["sr", "lr", "day"] : ["sr", "lr"]; - for ( let item of this.items ) { + for (let item of this.items) { const d = item.data.data; - if ( d.uses && recovery.includes(d.uses.per) ) { + if (d.uses && recovery.includes(d.uses.per)) { updateItems.push({_id: item.id, "data.uses.value": d.uses.max}); - } - else if ( d.recharge && d.recharge.value ) { + } else if (d.recharge && d.recharge.value) { updateItems.push({_id: item.id, "data.recharge.charged": true}); } } // Perform the updates await this.update(updateData); - if ( updateItems.length ) await this.updateEmbeddedEntity("OwnedItem", updateItems); + if (updateItems.length) await this.updateEmbeddedEntity("OwnedItem", updateItems); // Display a Chat Message summarizing the rest effects let restFlavor; switch (game.settings.get("sw5e", "restVariant")) { - case 'normal': restFlavor = game.i18n.localize(newDay ? "SW5E.LongRestOvernight" : "SW5E.LongRestNormal"); break; - case 'gritty': restFlavor = game.i18n.localize("SW5E.LongRestGritty"); break; - case 'epic': restFlavor = game.i18n.localize("SW5E.LongRestEpic"); break; + case "normal": + restFlavor = game.i18n.localize(newDay ? "SW5E.LongRestOvernight" : "SW5E.LongRestNormal"); + break; + case "gritty": + restFlavor = game.i18n.localize("SW5E.LongRestGritty"); + break; + case "epic": + restFlavor = game.i18n.localize("SW5E.LongRestEpic"); + break; } // Determine the chat message to display - if ( chat ) { + if (chat) { let lrMessage = "SW5E.LongRestResult"; if (dhp !== 0) lrMessage += "HP"; if (dfp !== 0) lrMessage += "FP"; @@ -1348,12 +1373,11 @@ export default class Actor5e extends Actor { updateData: updateData, updateItems: updateItems, newDay: newDay - } + }; } /* -------------------------------------------- */ - /** * Transform this Actor into another one. * @@ -1372,13 +1396,27 @@ export default class Actor5e extends Actor { * @param {boolean} [keepVision] Keep vision * @param {boolean} [transformTokens] Transform linked tokens too */ - async transformInto(target, { 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}={}) { - + async transformInto( + target, + { + 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 const allowed = game.settings.get("sw5e", "allowPolymorphing"); - if ( !allowed && !game.user.isGM ) { + if (!allowed && !game.user.isGM) { return ui.notifications.warn(game.i18n.localize("SW5E.PolymorphWarn")); } @@ -1415,53 +1453,55 @@ export default class Actor5e extends Actor { d.data.powers = o.data.powers; // Keep power slots // Handle wildcard - if ( source.token.randomImg ) { + if (source.token.randomImg) { const images = await target.getTokenImages(); d.token.img = images[Math.floor(Math.random() * images.length)]; } // Keep Token configurations const tokenConfig = ["displayName", "vision", "actorLink", "disposition", "displayBars", "bar1", "bar2"]; - if ( keepVision ) { - tokenConfig.push(...['dimSight', 'brightSight', 'dimLight', 'brightLight', 'vision', 'sightAngle']); + if (keepVision) { + tokenConfig.push(...["dimSight", "brightSight", "dimLight", "brightLight", "vision", "sightAngle"]); } - for ( let c of tokenConfig ) { + for (let c of tokenConfig) { d.token[c] = o.token[c]; } // Transfer ability scores const abilities = d.data.abilities; - for ( let k of Object.keys(abilities) ) { + for (let k of Object.keys(abilities)) { const oa = o.data.abilities[k]; const prof = abilities[k].proficient; - if ( keepPhysical && ["str", "dex", "con"].includes(k) ) abilities[k] = oa; - else if ( keepMental && ["int", "wis", "cha"].includes(k) ) abilities[k] = oa; - if ( keepSaves ) abilities[k].proficient = oa.proficient; - else if ( mergeSaves ) abilities[k].proficient = Math.max(prof, oa.proficient); + if (keepPhysical && ["str", "dex", "con"].includes(k)) abilities[k] = oa; + else if (keepMental && ["int", "wis", "cha"].includes(k)) abilities[k] = oa; + if (keepSaves) abilities[k].proficient = oa.proficient; + else if (mergeSaves) abilities[k].proficient = Math.max(prof, oa.proficient); } // Transfer skills - if ( keepSkills ) d.data.skills = o.data.skills; - else if ( mergeSkills ) { - for ( let [k, s] of Object.entries(d.data.skills) ) { + if (keepSkills) d.data.skills = o.data.skills; + else if (mergeSkills) { + for (let [k, s] of Object.entries(d.data.skills)) { s.value = Math.max(s.value, o.data.skills[k].value); } } // Keep specific items from the original data - d.items = d.items.concat(o.items.filter(i => { - if ( i.type === "class" ) return keepClass; - else if ( i.type === "feat" ) return keepFeats; - else if ( i.type === "power" ) return keepPowers; - else return keepItems; - })); + d.items = d.items.concat( + o.items.filter(i => { + if (i.type === "class") return keepClass; + else if (i.type === "feat") return keepFeats; + else if (i.type === "power") return keepPowers; + else return keepItems; + }) + ); // Transfer classes for NPCs if (!keepClass && d.data.details.cr) { d.items.push({ - type: 'class', - name: game.i18n.localize('SW5E.PolymorphTmpClass'), - data: { levels: d.data.details.cr } + type: "class", + name: game.i18n.localize("SW5E.PolymorphTmpClass"), + data: {levels: d.data.details.cr} }); } @@ -1472,7 +1512,7 @@ export default class Actor5e extends Actor { if (keepVision) d.data.traits.senses = o.data.traits.senses; // Set new data flags - if ( !this.isPolymorphed || !d.flags.sw5e.originalActor ) d.flags.sw5e.originalActor = this.id; + if (!this.isPolymorphed || !d.flags.sw5e.originalActor) d.flags.sw5e.originalActor = this.id; d.flags.sw5e.isPolymorphed = true; // Update unlinked Tokens in place since they can simply be re-dropped from the base actor @@ -1485,18 +1525,29 @@ export default class Actor5e extends Actor { // Update regular Actors by creating a new Actor with the Polymorphed data await this.sheet.close(); - Hooks.callAll('sw5e.transformActor', this, target, d, { - keepPhysical, keepMental, keepSaves, keepSkills, mergeSaves, mergeSkills, - keepClass, keepFeats, keepPowers, keepItems, keepBio, keepVision, transformTokens + Hooks.callAll("sw5e.transformActor", this, target, d, { + keepPhysical, + keepMental, + keepSaves, + keepSkills, + mergeSaves, + mergeSkills, + keepClass, + keepFeats, + keepPowers, + keepItems, + keepBio, + keepVision, + transformTokens }); const newActor = await this.constructor.create(d, {renderSheet: true}); // Update placed Token instances - if ( !transformTokens ) return; + if (!transformTokens) return; const tokens = this.getActiveTokens(true); const updates = tokens.map(t => { const newTokenData = duplicate(d.token); - if ( !t.data.actorLink ) newTokenData.actorData = newActor.data; + if (!t.data.actorLink) newTokenData.actorData = newActor.data; newTokenData._id = t.data._id; newTokenData.actorId = newActor.id; return newTokenData; @@ -1512,13 +1563,13 @@ export default class Actor5e extends Actor { * we can safely just delete this actor. */ async revertOriginalForm() { - if ( !this.isPolymorphed ) return; - if ( !this.owner ) { + if (!this.isPolymorphed) return; + if (!this.owner) { return ui.notifications.warn(game.i18n.localize("SW5E.PolymorphRevertWarn")); } // If we are reverting an unlinked token, simply replace it with the base actor prototype - if ( this.isToken ) { + if (this.isToken) { const baseActor = game.actors.get(this.token.data.actorId); const prototypeTokenData = duplicate(baseActor.token); prototypeTokenData.actorData = null; @@ -1526,11 +1577,11 @@ export default class Actor5e extends Actor { } // Obtain a reference to the original actor - const original = game.actors.get(this.getFlag('sw5e', 'originalActor')); - if ( !original ) return; + const original = game.actors.get(this.getFlag("sw5e", "originalActor")); + if (!original) return; // Get the Tokens which represent this actor - if ( canvas.ready ) { + if (canvas.ready) { const tokens = this.getActiveTokens(true); const tokenUpdates = tokens.map(t => { const tokenData = duplicate(original.data.token); @@ -1543,7 +1594,7 @@ export default class Actor5e extends Actor { // Delete the polymorphed Actor and maybe re-render the original sheet const isRendered = this.sheet.rendered; - if ( game.user.isGM ) await this.delete(); + if (game.user.isGM) await this.delete(); original.sheet.render(isRendered); return original; } @@ -1557,16 +1608,16 @@ export default class Actor5e extends Actor { */ static addDirectoryContextOptions(html, entryOptions) { entryOptions.push({ - name: 'SW5E.PolymorphRestoreTransformation', + name: "SW5E.PolymorphRestoreTransformation", icon: '', callback: li => { - const actor = game.actors.get(li.data('entityId')); + const actor = game.actors.get(li.data("entityId")); return actor.revertOriginalForm(); }, condition: li => { const allowed = game.settings.get("sw5e", "allowPolymorphing"); - if ( !allowed && !game.user.isGM ) return false; - const actor = game.actors.get(li.data('entityId')); + if (!allowed && !game.user.isGM) return false; + const actor = game.actors.get(li.data("entityId")); return actor && actor.isPolymorphed; } }); @@ -1580,7 +1631,9 @@ export default class Actor5e extends Actor { * @deprecated since sw5e 0.97 */ 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; } @@ -1592,9 +1645,9 @@ export default class Actor5e extends Actor { * @param {Event} event The originating user interaction which triggered the cast * @deprecated since sw5e 1.2.0 */ - async usePower(item, {configureDialog=true}={}) { + async usePower(item, {configureDialog = true} = {}) { console.warn(`The Actor5e#usePower method has been deprecated in favor of Item5e#roll`); - if ( item.data.type !== "power" ) throw new Error("Wrong Item type"); + if (item.data.type !== "power") throw new Error("Wrong Item type"); return item.roll(); } } diff --git a/module/actor/sheets/newSheet/base.js b/module/actor/sheets/newSheet/base.js index 48ca3cb2..f7fc36ed 100644 --- a/module/actor/sheets/newSheet/base.js +++ b/module/actor/sheets/newSheet/base.js @@ -3,7 +3,7 @@ import TraitSelector from "../../../apps/trait-selector.js"; import ActorSheetFlags from "../../../apps/actor-flags.js"; import ActorMovementConfig from "../../../apps/movement-config.js"; import ActorSensesConfig from "../../../apps/senses-config.js"; -import {SW5E} from '../../../config.js'; +import {SW5E} from "../../../config.js"; import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js"; /** @@ -48,7 +48,8 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ 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`; } @@ -56,7 +57,6 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ getData() { - // Basic data let isOwner = this.entity.owner; const data = { @@ -67,8 +67,8 @@ export default class ActorSheet5e extends ActorSheet { cssClass: isOwner ? "editable" : "locked", isCharacter: this.entity.data.type === "character", isNPC: this.entity.data.type === "npc", - isVehicle: this.entity.data.type === 'vehicle', - config: CONFIG.SW5E, + isVehicle: this.entity.data.type === "vehicle", + config: CONFIG.SW5E }; // The Actor and its Items @@ -83,7 +83,7 @@ export default class ActorSheet5e extends ActorSheet { data.filters = this._filters; // Ability Scores - for ( let [a, abl] of Object.entries(data.actor.data.abilities)) { + for (let [a, abl] of Object.entries(data.actor.data.abilities)) { abl.icon = this._getProficiencyIcon(abl.proficient); abl.hover = CONFIG.SW5E.proficiencyLevels[abl.proficient]; abl.label = CONFIG.SW5E.abilities[a]; @@ -91,7 +91,7 @@ export default class ActorSheet5e extends ActorSheet { // Skills if (data.actor.data.skills) { - for ( let [s, skl] of Object.entries(data.actor.data.skills)) { + for (let [s, skl] of Object.entries(data.actor.data.skills)) { skl.ability = CONFIG.SW5E.abilityAbbreviations[skl.ability]; skl.icon = this._getProficiencyIcon(skl.value); skl.hover = CONFIG.SW5E.proficiencyLevels[skl.value]; @@ -115,7 +115,7 @@ export default class ActorSheet5e extends ActorSheet { data.effects = prepareActiveEffectCategories(this.entity.effects); // Return data to the sheet - return data + return data; } /* -------------------------------------------- */ @@ -127,17 +127,21 @@ export default class ActorSheet5e extends ActorSheet { * @returns {{primary: string, special: string}} * @private */ - _getMovementSpeed(actorData, largestPrimary=false) { + _getMovementSpeed(actorData, largestPrimary = false) { const movement = actorData.data.attributes.movement || {}; // Prepare an array of available movement speeds let speeds = [ [movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`], [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}`] - ] - if ( largestPrimary ) { + ]; + if (largestPrimary) { speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]); } @@ -145,12 +149,12 @@ export default class ActorSheet5e extends ActorSheet { speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]); // Case 1: Largest as primary - if ( largestPrimary ) { + if (largestPrimary) { let primary = speeds.shift(); return { primary: `${primary ? primary[1] : "0"} ${movement.units}`, special: speeds.map(s => s[1]).join(", ") - } + }; } // Case 2: Walk as primary @@ -158,7 +162,7 @@ export default class ActorSheet5e extends ActorSheet { return { primary: `${movement.walk || 0} ${movement.units}`, special: speeds.length ? speeds.map(s => s[1]).join(", ") : "" - } + }; } } @@ -167,12 +171,12 @@ export default class ActorSheet5e extends ActorSheet { _getSenses(actorData) { const senses = actorData.data.attributes.senses || {}; const tags = {}; - for ( let [k, label] of Object.entries(CONFIG.SW5E.senses) ) { - const v = senses[k] ?? 0 - if ( v === 0 ) continue; + for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) { + const v = senses[k] ?? 0; + if (v === 0) continue; tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`; } - if ( !!senses.special ) tags["special"] = senses.special; + if (!!senses.special) tags["special"] = senses.special; return tags; } @@ -185,20 +189,20 @@ export default class ActorSheet5e extends ActorSheet { */ _prepareTraits(traits) { const map = { - "dr": CONFIG.SW5E.damageResistanceTypes, - "di": CONFIG.SW5E.damageResistanceTypes, - "dv": CONFIG.SW5E.damageResistanceTypes, - "ci": CONFIG.SW5E.conditionTypes, - "languages": CONFIG.SW5E.languages, - "armorProf": CONFIG.SW5E.armorProficiencies, - "weaponProf": CONFIG.SW5E.weaponProficiencies, - "toolProf": CONFIG.SW5E.toolProficiencies + dr: CONFIG.SW5E.damageResistanceTypes, + di: CONFIG.SW5E.damageResistanceTypes, + dv: CONFIG.SW5E.damageResistanceTypes, + ci: CONFIG.SW5E.conditionTypes, + languages: CONFIG.SW5E.languages, + armorProf: CONFIG.SW5E.armorProficiencies, + weaponProf: CONFIG.SW5E.weaponProficiencies, + toolProf: CONFIG.SW5E.toolProficiencies }; - for ( let [t, choices] of Object.entries(map) ) { + for (let [t, choices] of Object.entries(map)) { const trait = traits[t]; - if ( !trait ) continue; + if (!trait) continue; let values = []; - if ( trait.value ) { + if (trait.value) { values = trait.value instanceof Array ? trait.value : [trait.value]; } trait.selected = values.reduce((obj, t) => { @@ -207,8 +211,8 @@ export default class ActorSheet5e extends ActorSheet { }, {}); // Add custom entry - if ( trait.custom ) { - trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim()); + if (trait.custom) { + trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim())); } trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive"; } @@ -229,44 +233,44 @@ export default class ActorSheet5e extends ActorSheet { // Define some mappings const sections = { - "atwill": -20, - "innate": -10, + atwill: -20, + innate: -10 }; // Label power slot uses headers const useLabels = { "-20": "-", "-10": "-", - "0": "∞" + 0: "∞" }; // Format a powerbook entry for a certain indexed level - const registerSection = (sl, i, label, {prepMode="prepared", value, max, override}={}) => { + const registerSection = (sl, i, label, {prepMode = "prepared", value, max, override} = {}) => { powerbook[i] = { order: i, label: label, usesSlots: i > 0, canCreate: owner, - canPrepare: (data.actor.type === "character") && (i >= 1), + canPrepare: data.actor.type === "character" && i >= 1, powers: [], uses: useLabels[i] || value || 0, slots: useLabels[i] || max || 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 }; }; // Determine the maximum power level which has a slot const maxLevel = Array.fromRange(10).reduce((max, i) => { - if ( i === 0 ) return max; + if (i === 0) return max; 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; }, 0); // Level-based powercasters have cantrips and leveled slots - if ( maxLevel > 0 ) { + if (maxLevel > 0) { registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]); for (let lvl = 1; lvl <= maxLevel; lvl++) { const sl = `power${lvl}`; @@ -281,9 +285,9 @@ export default class ActorSheet5e extends ActorSheet { const sl = `power${s}`; // Specialized powercasting modes (if they exist) - if ( mode in sections ) { + if (mode in sections) { s = sections[mode]; - if ( !powerbook[s] ){ + if (!powerbook[s]) { const l = levels[mode] || {}; const config = CONFIG.SW5E.powerPreparationModes[mode]; registerSection(mode, s, config, { @@ -296,7 +300,7 @@ export default class ActorSheet5e extends ActorSheet { } // Sections for higher-level powers which the caster "should not" have, but power items exist for - else if ( !powerbook[s] ) { + else if (!powerbook[s]) { registerSection(sl, s, CONFIG.SW5E.powerLevels[s], {levels: levels[sl]}); } @@ -322,28 +326,28 @@ export default class ActorSheet5e extends ActorSheet { const data = item.data; // Action usage - for ( let f of ["action", "bonus", "reaction"] ) { - if ( filters.has(f) ) { - if ((data.activation && (data.activation.type !== f))) return false; + for (let f of ["action", "bonus", "reaction"]) { + if (filters.has(f)) { + if (data.activation && data.activation.type !== f) return false; } } // Power-specific filters - if ( filters.has("ritual") ) { + if (filters.has("ritual")) { if (data.components.ritual !== true) return false; } - if ( filters.has("concentration") ) { + if (filters.has("concentration")) { if (data.components.concentration !== true) return false; } - if ( filters.has("prepared") ) { - if ( data.level === 0 || ["innate", "always"].includes(data.preparation.mode) ) return true; - if ( this.actor.data.type === "npc" ) return true; + if (filters.has("prepared")) { + if (data.level === 0 || ["innate", "always"].includes(data.preparation.mode)) return true; + if (this.actor.data.type === "npc") return true; return data.preparation.prepared; } // Equipment-specific filters - if ( filters.has("equipped") ) { - if ( data.equipped !== true ) return false; + if (filters.has("equipped")) { + if (data.equipped !== true) return false; } return true; }); @@ -374,59 +378,58 @@ export default class ActorSheet5e extends ActorSheet { * @param html {HTML} The prepared HTML object ready to be rendered into the DOM */ activateListeners(html) { - // Activate Item Filters const filterLists = html.find(".filter-list"); filterLists.each(this._initializeFilterItemList.bind(this)); filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this)); // 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)); // Editable Only Listeners - if ( this.isEditable ) { - + if (this.isEditable) { // Input focus and update const inputs = html.find("input"); inputs.focus(ev => ev.currentTarget.select()); inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this)); // Ability Proficiency - html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this)); + html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this)); // 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 - html.find('.trait-selector').click(this._onTraitSelector.bind(this)); + html.find(".trait-selector").click(this._onTraitSelector.bind(this)); // Configure Special Flags - html.find('.config-button').click(this._onConfigMenu.bind(this)); + html.find(".config-button").click(this._onConfigMenu.bind(this)); // Owned Item management - html.find('.item-create').click(this._onItemCreate.bind(this)); - html.find('.item-edit').click(this._onItemEdit.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('.slot-max-override').click(this._onPowerSlotOverride.bind(this)); + html.find(".item-create").click(this._onItemCreate.bind(this)); + html.find(".item-edit").click(this._onItemEdit.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(".slot-max-override").click(this._onPowerSlotOverride.bind(this)); // Active Effect management html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.entity)); } // Owner Only Listeners - if ( this.actor.owner ) { - + if (this.actor.owner) { // Ability Checks - html.find('.ability-name').click(this._onRollAbilityTest.bind(this)); - + html.find(".ability-name").click(this._onRollAbilityTest.bind(this)); // Roll Skill Checks - html.find('.skill-name').click(this._onRollSkillCheck.bind(this)); + html.find(".skill-name").click(this._onRollSkillCheck.bind(this)); // Item Rolling - html.find('.item .item-image').click(event => this._onItemRoll(event)); - html.find('.item .item-recharge').click(event => this._onItemRecharge(event)); + html.find(".item .item-image").click(event => this._onItemRoll(event)); + html.find(".item .item-recharge").click(event => this._onItemRecharge(event)); } // Otherwise remove rollable classes @@ -447,8 +450,8 @@ export default class ActorSheet5e extends ActorSheet { _initializeFilterItemList(i, ul) { const set = this._filters[ul.dataset.filter]; const filters = ul.querySelectorAll(".filter-item"); - for ( let li of filters ) { - if ( set.has(li.dataset.filter) ) li.classList.add("active"); + for (let li of filters) { + if (set.has(li.dataset.filter)) li.classList.add("active"); } } @@ -464,10 +467,10 @@ export default class ActorSheet5e extends ActorSheet { _onChangeInputDelta(event) { const input = event.target; const value = input.value; - if ( ["+", "-"].includes(value[0]) ) { + if (["+", "-"].includes(value[0])) { let delta = parseFloat(value); input.value = getProperty(this.actor.data, input.name) + delta; - } else if ( value[0] === "=" ) { + } else if (value[0] === "=") { input.value = value.slice(1); } } @@ -482,7 +485,7 @@ export default class ActorSheet5e extends ActorSheet { _onConfigMenu(event) { event.preventDefault(); const button = event.currentTarget; - switch ( button.dataset.action ) { + switch (button.dataset.action) { case "movement": new ActorMovementConfig(this.object).render(true); break; @@ -512,10 +515,10 @@ export default class ActorSheet5e extends ActorSheet { let idx = levels.indexOf(level); // Toggle next level - forward on click, backwards on right - if ( event.type === "click" ) { - field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]); - } else if ( event.type === "contextmenu" ) { - field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]); + if (event.type === "click") { + field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]); + } else if (event.type === "contextmenu") { + field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]); } // Update the field value and save the form @@ -526,8 +529,8 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ async _onDropActor(event, data) { - const canPolymorph = game.user.isGM || (this.actor.owner && game.settings.get('sw5e', 'allowPolymorphing')); - if ( !canPolymorph ) return false; + const canPolymorph = game.user.isGM || (this.actor.owner && game.settings.get("sw5e", "allowPolymorphing")); + if (!canPolymorph) return false; // Get the target actor let sourceActor = null; @@ -537,78 +540,82 @@ export default class ActorSheet5e extends ActorSheet { } else { sourceActor = game.actors.get(data.id); } - if ( !sourceActor ) return; + if (!sourceActor) return; // Define a function to record polymorph settings for future use const rememberOptions = html => { const options = {}; - html.find('input').each((i, el) => { + html.find("input").each((i, el) => { options[el.name] = el.checked; }); - const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options); - game.settings.set('sw5e', 'polymorphSettings', settings); + const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options); + game.settings.set("sw5e", "polymorphSettings", settings); return settings; }; // Create and render the Dialog - return new Dialog({ - title: game.i18n.localize('SW5E.PolymorphPromptTitle'), - content: { - options: game.settings.get('sw5e', 'polymorphSettings'), - i18n: SW5E.polymorphSettings, - isToken: this.actor.isToken - }, - default: 'accept', - buttons: { - accept: { - icon: '', - label: game.i18n.localize('SW5E.PolymorphAcceptSettings'), - callback: html => this.actor.transformInto(sourceActor, rememberOptions(html)) + return new Dialog( + { + title: game.i18n.localize("SW5E.PolymorphPromptTitle"), + content: { + options: game.settings.get("sw5e", "polymorphSettings"), + i18n: SW5E.polymorphSettings, + isToken: this.actor.isToken }, - wildshape: { - icon: '', - label: game.i18n.localize('SW5E.PolymorphWildShape'), - callback: html => this.actor.transformInto(sourceActor, { - keepBio: true, - keepClass: true, - keepMental: true, - mergeSaves: true, - mergeSkills: true, - transformTokens: rememberOptions(html).transformTokens - }) - }, - polymorph: { - icon: '', - label: game.i18n.localize('SW5E.Polymorph'), - callback: html => this.actor.transformInto(sourceActor, { - transformTokens: rememberOptions(html).transformTokens - }) - }, - cancel: { - icon: '', - label: game.i18n.localize('Cancel') + default: "accept", + buttons: { + accept: { + icon: '', + label: game.i18n.localize("SW5E.PolymorphAcceptSettings"), + callback: html => this.actor.transformInto(sourceActor, rememberOptions(html)) + }, + wildshape: { + icon: '', + label: game.i18n.localize("SW5E.PolymorphWildShape"), + callback: html => + this.actor.transformInto(sourceActor, { + keepBio: true, + keepClass: true, + keepMental: true, + mergeSaves: true, + mergeSkills: true, + transformTokens: rememberOptions(html).transformTokens + }) + }, + polymorph: { + icon: '', + label: game.i18n.localize("SW5E.Polymorph"), + callback: html => + this.actor.transformInto(sourceActor, { + transformTokens: rememberOptions(html).transformTokens + }) + }, + cancel: { + icon: '', + label: game.i18n.localize("Cancel") + } } + }, + { + classes: ["dialog", "sw5e"], + width: 600, + template: "systems/sw5e/templates/apps/polymorph-prompt.html" } - }, { - classes: ['dialog', 'sw5e'], - width: 600, - template: 'systems/sw5e/templates/apps/polymorph-prompt.html' - }).render(true); + ).render(true); } /* -------------------------------------------- */ /** @override */ async _onDropItemCreate(itemData) { - // 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); itemData = scroll.data; } // Ignore certain statuses - if ( itemData.data ) { + if (itemData.data) { ["attunement", "equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]); } @@ -623,7 +630,7 @@ export default class ActorSheet5e extends ActorSheet { * @param {MouseEvent} event The originating click event * @private */ - async _onPowerSlotOverride (event) { + async _onPowerSlotOverride(event) { const span = event.currentTarget.parentElement; const level = span.dataset.level; const override = this.actor.data.data.powers[level].override || span.dataset.slots; @@ -648,12 +655,12 @@ export default class ActorSheet5e extends ActorSheet { * @private */ async _onUsesChange(event) { - event.preventDefault(); - const itemId = event.currentTarget.closest(".item").dataset.itemId; - const item = this.actor.getOwnedItem(itemId); - const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max); - event.target.value = uses; - return item.update({ 'data.uses.value': uses }); + event.preventDefault(); + const itemId = event.currentTarget.closest(".item").dataset.itemId; + const item = this.actor.getOwnedItem(itemId); + const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max); + event.target.value = uses; + return item.update({"data.uses.value": uses}); } /* -------------------------------------------- */ @@ -681,7 +688,7 @@ export default class ActorSheet5e extends ActorSheet { const itemId = event.currentTarget.closest(".item").dataset.itemId; const item = this.actor.getOwnedItem(itemId); return item.rollRecharge(); - }; + } /* -------------------------------------------- */ @@ -692,11 +699,11 @@ export default class ActorSheet5e extends ActorSheet { _onItemSummary(event) { event.preventDefault(); let li = $(event.currentTarget).parents(".item"), - item = this.actor.getOwnedItem(li.data("item-id")), - chatData = item.getChatData({secrets: this.actor.owner}); + item = this.actor.getOwnedItem(li.data("item-id")), + chatData = item.getChatData({secrets: this.actor.owner}); // Toggle summary - if ( li.hasClass("expanded") ) { + if (li.hasClass("expanded")) { let summary = li.children(".item-summary"); summary.slideUp(200, () => summary.remove()); } else { @@ -808,7 +815,7 @@ export default class ActorSheet5e extends ActorSheet { const li = event.currentTarget; const set = this._filters[li.parentElement.dataset.filter]; const filter = li.dataset.filter; - if ( set.has(filter) ) set.delete(filter); + if (set.has(filter)) set.delete(filter); else set.add(filter); this.render(); } @@ -825,8 +832,8 @@ export default class ActorSheet5e extends ActorSheet { const a = event.currentTarget; const label = a.parentElement.querySelector("label"); const choices = CONFIG.SW5E[a.dataset.options]; - const options = { name: a.dataset.target, title: label.innerText, choices }; - new TraitSelector(this.actor, options).render(true) + const options = {name: a.dataset.target, title: label.innerText, choices}; + new TraitSelector(this.actor, options).render(true); } /* -------------------------------------------- */ @@ -836,13 +843,13 @@ export default class ActorSheet5e extends ActorSheet { let buttons = super._getHeaderButtons(); // Add button to revert polymorph - if ( !this.actor.isPolymorphed || this.actor.isToken ) return buttons; + if (!this.actor.isPolymorphed || this.actor.isToken) return buttons; buttons.unshift({ - label: 'SW5E.PolymorphRestoreTransformation', + label: "SW5E.PolymorphRestoreTransformation", class: "restore-transformation", icon: "fas fa-backward", onclick: ev => this.actor.revertOriginalForm() }); return buttons; } -} \ No newline at end of file +} diff --git a/module/actor/sheets/newSheet/character.js b/module/actor/sheets/newSheet/character.js index 213478b2..bb883e3c 100644 --- a/module/actor/sheets/newSheet/character.js +++ b/module/actor/sheets/newSheet/character.js @@ -7,7 +7,6 @@ import Actor5e from "../../entity.js"; * @type {ActorSheet5e} */ export default class ActorSheet5eCharacterNew extends ActorSheet5e { - get template() { 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"; @@ -16,18 +15,19 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { * Define default rendering options for the NPC sheet * @return {Object} */ - static get defaultOptions() { - + static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["swalt", "sw5e", "sheet", "actor", "character"], blockFavTab: true, subTabs: null, width: 800, - tabs: [{ - navSelector: ".root-tabs", - contentSelector: ".sheet-body", - initial: "attributes" - }], + tabs: [ + { + navSelector: ".root-tabs", + contentSelector: ".sheet-body", + initial: "attributes" + } + ] }); } @@ -48,7 +48,7 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { sheetData["resources"] = ["primary", "secondary", "tertiary"].reduce((arr, r) => { const res = sheetData.data.resources[r] || {}; res.name = r; - res.placeholder = game.i18n.localize("SW5E.Resource"+r.titleCase()); + res.placeholder = game.i18n.localize("SW5E.Resource" + r.titleCase()); if (res && res.value === 0) delete res.value; if (res && res.max === 0) delete res.max; return arr.concat([res]); @@ -57,9 +57,11 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { // Experience Tracking sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking"); sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", "); - sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => { - return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ') - }).join(', '); + sheetData["multiclassLabels"] = this.actor.itemTypes.class + .map(c => { + return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" "); + }) + .join(", "); // Return data for rendering return sheetData; @@ -72,60 +74,74 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { * @private */ _prepareItems(data) { - // Categorize items as inventory, powerbook, features, and classes const inventory = { - weapon: { label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"} }, - equipment: { label: "SW5E.ItemTypeEquipmentPl", items: [], dataset: {type: "equipment"} }, - consumable: { label: "SW5E.ItemTypeConsumablePl", items: [], dataset: {type: "consumable"} }, - tool: { label: "SW5E.ItemTypeToolPl", items: [], dataset: {type: "tool"} }, - backpack: { label: "SW5E.ItemTypeContainerPl", items: [], dataset: {type: "backpack"} }, - loot: { label: "SW5E.ItemTypeLootPl", items: [], dataset: {type: "loot"} } + weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}}, + equipment: {label: "SW5E.ItemTypeEquipmentPl", items: [], dataset: {type: "equipment"}}, + consumable: {label: "SW5E.ItemTypeConsumablePl", items: [], dataset: {type: "consumable"}}, + tool: {label: "SW5E.ItemTypeToolPl", items: [], dataset: {type: "tool"}}, + backpack: {label: "SW5E.ItemTypeContainerPl", items: [], dataset: {type: "backpack"}}, + loot: {label: "SW5E.ItemTypeLootPl", items: [], dataset: {type: "loot"}} }; // Partition items by category - let [items, forcepowers, techpowers, feats, classes, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => { + let [ + items, + forcepowers, + techpowers, + feats, + classes, + species, + archetypes, + classfeatures, + backgrounds, + fightingstyles, + fightingmasteries, + lightsaberforms + ] = data.items.reduce( + (arr, item) => { + // Item details + item.img = item.img || DEFAULT_TOKEN; + item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1; + item.attunement = { + [CONFIG.SW5E.attunementTypes.REQUIRED]: { + icon: "fa-sun", + cls: "not-attuned", + title: "SW5E.AttunementRequired" + }, + [CONFIG.SW5E.attunementTypes.ATTUNED]: { + icon: "fa-sun", + cls: "attuned", + title: "SW5E.AttunementAttuned" + } + }[item.data.attunement]; - // Item details - item.img = item.img || DEFAULT_TOKEN; - item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1); - item.attunement = { - [CONFIG.SW5E.attunementTypes.REQUIRED]: { - icon: "fa-sun", - cls: "not-attuned", - title: "SW5E.AttunementRequired" - }, - [CONFIG.SW5E.attunementTypes.ATTUNED]: { - icon: "fa-sun", - cls: "attuned", - title: "SW5E.AttunementAttuned" - } - }[item.data.attunement]; + // Item usage + 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.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0; + item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type); - // Item usage - 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.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 + this._prepareItemToggleState(item); - // Item toggle state - this._prepareItemToggleState(item); - - // Classify items into types - if ( item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school) ) arr[1].push(item); - else if ( item.type === "power" && ["tec"].includes(item.data.school) ) arr[2].push(item); - else if ( item.type === "feat" ) arr[3].push(item); - else if ( item.type === "class" ) arr[4].push(item); - else if ( item.type === "species" ) arr[5].push(item); - else if ( item.type === "archetype" ) arr[6].push(item); - else if ( item.type === "classfeature" ) arr[7].push(item); - else if ( item.type === "background" ) arr[8].push(item); - else if ( item.type === "fightingstyle" ) arr[9].push(item); - else if ( item.type === "fightingmastery" ) arr[10].push(item); - else if ( item.type === "lightsaberform" ) arr[11].push(item); - else if ( Object.keys(inventory).includes(item.type ) ) arr[0].push(item); - return arr; - }, [[], [], [], [], [], [], [], [], [], [], [], []]); + // Classify items into types + if (item.type === "power" && ["lgt", "drk", "uni"].includes(item.data.school)) arr[1].push(item); + else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[2].push(item); + else if (item.type === "feat") arr[3].push(item); + else if (item.type === "class") arr[4].push(item); + else if (item.type === "species") arr[5].push(item); + else if (item.type === "archetype") arr[6].push(item); + else if (item.type === "classfeature") arr[7].push(item); + else if (item.type === "background") arr[8].push(item); + else if (item.type === "fightingstyle") arr[9].push(item); + else if (item.type === "fightingmastery") arr[10].push(item); + else if (item.type === "lightsaberform") arr[11].push(item); + else if (Object.keys(inventory).includes(item.type)) arr[0].push(item); + return arr; + }, + [[], [], [], [], [], [], [], [], [], [], [], []] + ); // Apply active item filters items = this._filterItems(items, this._filters.inventory); @@ -134,7 +150,7 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { feats = this._filterItems(feats, this._filters.features); // Organize items - for ( let i of items ) { + for (let i of items) { i.data.quantity = i.data.quantity || 0; i.data.weight = i.data.weight || 0; i.totalWeight = Math.round(i.data.quantity * i.data.weight * 10) / 10; @@ -147,19 +163,66 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { // Organize Features const features = { - classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true }, - classfeatures: { 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"} } + classes: {label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true}, + classfeatures: { + 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"}} }; - for ( let f of feats ) { - if ( f.data.activation.type ) features.active.items.push(f); + for (let f of feats) { + if (f.data.activation.type) features.active.items.push(f); else features.passive.items.push(f); } classes.sort((a, b) => b.levels - a.levels); @@ -189,14 +252,13 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { _prepareItemToggleState(item) { if (item.type === "power") { const isAlways = getProperty(item.data, "preparation.mode") === "always"; - const isPrepared = getProperty(item.data, "preparation.prepared"); + const isPrepared = getProperty(item.data, "preparation.prepared"); item.toggleClass = isPrepared ? "active" : ""; - if ( isAlways ) item.toggleClass = "fixed"; - if ( isAlways ) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always; - else if ( isPrepared ) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared; + if (isAlways) item.toggleClass = "fixed"; + if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always; + else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared; else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared"); - } - else { + } else { const isActive = getProperty(item.data, "equipped"); item.toggleClass = isActive ? "active" : ""; item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped"); @@ -211,19 +273,19 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { * Activate event listeners using the prepared sheet HTML * @param html {HTML} The prepared HTML object ready to be rendered into the DOM */ - activateListeners(html) { + activateListeners(html) { super.activateListeners(html); - if ( !this.options.editable ) return; + if (!this.options.editable) return; // Inventory Functions // html.find(".currency-convert").click(this._onConvertCurrency.bind(this)); // 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 - html.find('.short-rest').click(this._onShortRest.bind(this)); - html.find('.long-rest').click(this._onLongRest.bind(this)); + html.find(".short-rest").click(this._onShortRest.bind(this)); + html.find(".long-rest").click(this._onLongRest.bind(this)); // Rollable sheet actions html.find(".rollable[data-action]").click(this._onSheetAction.bind(this)); @@ -263,9 +325,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { }); // Item Delete Confirmation - html.find('.item-delete').off("click"); - html.find('.item-delete').click(event => { - let li = $(event.currentTarget).parents('.item'); + html.find(".item-delete").off("click"); + html.find(".item-delete").click(event => { + let li = $(event.currentTarget).parents(".item"); let itemId = li.attr("data-item-id"); let item = this.actor.getOwnedItem(itemId); new Dialog({ @@ -274,17 +336,17 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { buttons: { Yes: { icon: '', - label: 'Yes', + label: "Yes", callback: dlg => { this.actor.deleteOwnedItem(itemId); } }, cancel: { icon: '', - label: 'No' - }, + label: "No" + } }, - default: 'cancel' + default: "cancel" }).render(true); }); } @@ -299,7 +361,7 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { _onSheetAction(event) { event.preventDefault(); const button = event.currentTarget; - switch( button.dataset.action ) { + switch (button.dataset.action) { case "rollDeathSave": return this.actor.rollDeathSave({event: event}); case "rollInitiative": @@ -309,7 +371,6 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { /* -------------------------------------------- */ - /** * Handle toggling the state of an Owned Item within the Actor * @param {Event} event The triggering click event @@ -353,14 +414,13 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e { /** @override */ async _onDropItemCreate(itemData) { - // 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); 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); - if ( next > priorLevel ) { + if (next > priorLevel) { itemData.levels = next; return cls.update({"data.levels": next}); } @@ -428,9 +488,9 @@ async function addFavorites(app, html, data) { value: data.actor.data.powers.power9.value, max: data.actor.data.powers.power9.max } - } + }; - let powerCount = 0 + let powerCount = 0; let items = data.actor.items; for (let item of items) { if (item.type == "class") continue; @@ -441,24 +501,28 @@ async function addFavorites(app, html, data) { } let isFav = item.flags.favtab.isFavourite; if (app.options.editable) { - let favBtn = $(``); + let favBtn = $( + `` + ); favBtn.click(ev => { app.actor.getOwnedItem(item._id).update({ "flags.favtab.isFavourite": !item.flags.favtab.isFavourite }); }); - html.find(`.item[data-item-id="${item._id}"]`).find('.item-controls').prepend(favBtn); + html.find(`.item[data-item-id="${item._id}"]`).find(".item-controls").prepend(favBtn); } if (isFav) { item.powerComps = ""; if (item.data.components) { let comps = item.data.components; - let v = (comps.vocal) ? "V" : ""; - let s = (comps.somatic) ? "S" : ""; - let m = (comps.material) ? "M" : ""; - let c = (comps.concentration) ? true : false; - let r = (comps.ritual) ? true : false; + let v = comps.vocal ? "V" : ""; + let s = comps.somatic ? "S" : ""; + let m = comps.material ? "M" : ""; + let c = comps.concentration ? true : false; + let r = comps.ritual ? true : false; item.powerComps = `${v}${s}${m}`; item.powerCon = c; item.powerRit = r; @@ -466,15 +530,15 @@ async function addFavorites(app, html, data) { item.editable = app.options.editable; switch (item.type) { - case 'feat': + case "feat": if (item.flags.favtab.sort === undefined) { item.flags.favtab.sort = (favFeats.count + 1) * 100000; // initial sort key if not present } favFeats.push(item); break; - case 'power': + case "power": 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) { favPowers[item.data.level].powers.push(item); @@ -500,58 +564,58 @@ async function addFavorites(app, html, data) { // html.find('.favourite .item-controls').css('flex', '0 0 22px'); // } - let tabContainer = html.find('.favtabtarget'); - 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; + let tabContainer = html.find(".favtabtarget"); + 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.favPowers = powerCount > 0 ? favPowers : false; data.editable = app.options.editable; - await loadTemplates(['systems/sw5e/templates/actors/newActor/item.hbs']); - let favtabHtml = $(await renderTemplate('systems/sw5e/templates/actors/newActor/template.hbs', data)); - favtabHtml.find('.item-name h4').click(event => app._onItemSummary(event)); + await loadTemplates(["systems/sw5e/templates/actors/newActor/item.hbs"]); + let favtabHtml = $(await renderTemplate("systems/sw5e/templates/actors/newActor/template.hbs", data)); + favtabHtml.find(".item-name h4").click(event => app._onItemSummary(event)); 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); - favtabHtml.find('.item').each((i, li) => { + favtabHtml.find(".item").each((i, li) => { if (li.classList.contains("inventory-header")) return; li.setAttribute("draggable", true); li.addEventListener("dragstart", handler, false); }); //favtabHtml.find('.item-toggle').click(event => app._onToggleItem(event)); - favtabHtml.find('.item-edit').click(ev => { - let itemId = $(ev.target).parents('.item')[0].dataset.itemId; + favtabHtml.find(".item-edit").click(ev => { + let itemId = $(ev.target).parents(".item")[0].dataset.itemId; app.actor.getOwnedItem(itemId).sheet.render(true); }); - favtabHtml.find('.item-fav').click(ev => { - let itemId = $(ev.target).parents('.item')[0].dataset.itemId; - let val = !app.actor.getOwnedItem(itemId).data.flags.favtab.isFavourite + favtabHtml.find(".item-fav").click(ev => { + let itemId = $(ev.target).parents(".item")[0].dataset.itemId; + let val = !app.actor.getOwnedItem(itemId).data.flags.favtab.isFavourite; app.actor.getOwnedItem(itemId).update({ "flags.favtab.isFavourite": val }); }); // Sorting - favtabHtml.find('.item').on('drop', ev => { + favtabHtml.find(".item").on("drop", ev => { ev.preventDefault(); 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) return; let list = null; - if (dropData.data.type === 'feat') list = favFeats; + if (dropData.data.type === "feat") list = favFeats; else list = favItems; let dragSource = list.find(i => i._id === dropData.data._id); let siblings = list.filter(i => i._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._id === targetId); if (dragTarget === undefined) return; const sortUpdates = SortingHelpers.performIntegerSort(dragSource, { target: dragTarget, siblings: siblings, - sortKey: 'flags.favtab.sort' + sortKey: "flags.favtab.sort" }); const updateData = sortUpdates.map(u => { const update = u.update; @@ -572,63 +636,62 @@ async function addFavorites(app, html, data) { //} // try { // if (game.modules.get("betterrolls5e") && game.modules.get("betterrolls5e").active) BetterRolls.addItemContent(app.object, favtabHtml, ".item .item-name h4", ".item-properties", ".item > .rollable div"); - // } + // } // catch (err) { // // Better Rolls not found! // } Hooks.callAll("renderedSwaltSheet", app, html, data); } async function addSubTabs(app, html, data) { - if(data.options.subTabs == null) { + if (data.options.subTabs == null) { //let subTabs = []; //{subgroup: '', target: '', active: false} data.options.subTabs = {}; - html.find('[data-subgroup-selection] [data-subgroup]').each((idx, el) => { - let subgroup = el.getAttribute('data-subgroup'); - let target = el.getAttribute('data-target'); - let targetObj = {target: target, active: el.classList.contains("active")} - if(data.options.subTabs.hasOwnProperty(subgroup)) { + html.find("[data-subgroup-selection] [data-subgroup]").each((idx, el) => { + let subgroup = el.getAttribute("data-subgroup"); + let target = el.getAttribute("data-target"); + let targetObj = {target: target, active: el.classList.contains("active")}; + if (data.options.subTabs.hasOwnProperty(subgroup)) { data.options.subTabs[subgroup].push(targetObj); } else { data.options.subTabs[subgroup] = []; data.options.subTabs[subgroup].push(targetObj); } - }) - } - - for(const group in data.options.subTabs) { - data.options.subTabs[group].forEach(tab => { - if(tab.active) { - html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass('active'); - } else { - html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass('active'); - } - }) + }); } - html.find('[data-subgroup-selection]').children().on('click', event => { - let subgroup = event.target.closest('[data-subgroup]').getAttribute('data-subgroup'); - let target = event.target.closest('[data-target]').getAttribute('data-target'); - html.find(`[data-subgroup=${subgroup}]`).removeClass('active'); - 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 => { - if(el.target == target) { - el.active = true; + for (const group in data.options.subTabs) { + data.options.subTabs[group].forEach(tab => { + if (tab.active) { + html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).addClass("active"); } else { - el.active = false; + html.find(`[data-subgroup=${group}][data-target=${tab.target}]`).removeClass("active"); } - return el; - }) - - }) - - + }); + } + html + .find("[data-subgroup-selection]") + .children() + .on("click", event => { + let subgroup = event.target.closest("[data-subgroup]").getAttribute("data-subgroup"); + let target = event.target.closest("[data-target]").getAttribute("data-target"); + html.find(`[data-subgroup=${subgroup}]`).removeClass("active"); + 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 => { + if (el.target == target) { + el.active = true; + } else { + el.active = false; + } + return el; + }); + }); } Hooks.on("renderActorSheet5eCharacterNew", (app, html, data) => { addFavorites(app, html, data); addSubTabs(app, html, data); -}); \ No newline at end of file +}); diff --git a/module/actor/sheets/newSheet/npc.js b/module/actor/sheets/newSheet/npc.js index 51c43006..a79728b8 100644 --- a/module/actor/sheets/newSheet/npc.js +++ b/module/actor/sheets/newSheet/npc.js @@ -6,22 +6,23 @@ import ActorSheet5e from "./base.js"; * @extends {ActorSheet5e} */ export default class ActorSheet5eNPCNew extends ActorSheet5e { - /** @override */ 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/npc-sheet.html`; } /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { classes: ["sw5e", "sheet", "actor", "npc"], width: 800, - tabs: [{ - navSelector: ".root-tabs", - contentSelector: ".sheet-body", - initial: "attributes" - }], + tabs: [ + { + navSelector: ".root-tabs", + contentSelector: ".sheet-body", + initial: "attributes" + } + ] }); } @@ -32,28 +33,40 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { * @private */ _prepareItems(data) { - // Categorize Items as Features and Powers const features = { - weapons: { 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"} }, - equipment: { label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}} + weapons: { + 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"}}, + equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}} }; // Start by classifying items into groups for rendering - let [forcepowers, techpowers, other] = data.items.reduce((arr, item) => { - item.img = item.img || DEFAULT_TOKEN; - item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1); - 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.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); - else if ( item.type === "power" && ["tec"].includes(item.data.school) ) arr[1].push(item); - else arr[2].push(item); - return arr; - }, [[], [], []]); + let [forcepowers, techpowers, other] = data.items.reduce( + (arr, item) => { + item.img = item.img || DEFAULT_TOKEN; + item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1; + 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.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); + else if (item.type === "power" && ["tec"].includes(item.data.school)) arr[1].push(item); + else arr[2].push(item); + return arr; + }, + [[], [], []] + ); // Apply item filters forcepowers = this._filterItems(forcepowers, this._filters.forcePowerbook); @@ -65,13 +78,12 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { const techPowerbook = this._preparePowerbook(data, techpowers, "tec"); // Organize Features - for ( let item of other ) { - if ( item.type === "weapon" ) features.weapons.items.push(item); - else if ( item.type === "feat" ) { - if ( item.data.activation.type ) features.actions.items.push(item); + for (let item of other) { + if (item.type === "weapon") features.weapons.items.push(item); + else if (item.type === "feat") { + if (item.data.activation.type) features.actions.items.push(item); else features.passive.items.push(item); - } - else features.equipment.items.push(item); + } else features.equipment.items.push(item); } // Assign and return @@ -80,7 +92,6 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { data.techPowerbook = techPowerbook; } - /* -------------------------------------------- */ /** @override */ @@ -100,13 +111,12 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { /** @override */ _updateObject(event, formData) { - // Format NPC Challenge Rating const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5}; let crv = "data.details.cr"; let cr = formData[crv]; cr = crs[cr] || parseFloat(cr); - if ( cr ) formData[crv] = cr < 1 ? cr : parseInt(cr); + if (cr) formData[crv] = cr < 1 ? cr : parseInt(cr); // Parent ActorSheet update steps super._updateObject(event, formData); @@ -117,7 +127,7 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { /* -------------------------------------------- */ /** @override */ - activateListeners(html) { + activateListeners(html) { super.activateListeners(html); html.find(".health .rollable").click(this._onRollHPFormula.bind(this)); } @@ -132,10 +142,9 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e { _onRollHPFormula(event) { event.preventDefault(); const formula = this.actor.data.data.attributes.hp.formula; - if ( !formula ) return; + if (!formula) return; const hp = new Roll(formula).roll().total; AudioHelper.play({src: CONFIG.sounds.dice}); this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp}); } } - diff --git a/module/actor/sheets/newSheet/vehicle.js b/module/actor/sheets/newSheet/vehicle.js index cba36ebd..7238606a 100644 --- a/module/actor/sheets/newSheet/vehicle.js +++ b/module/actor/sheets/newSheet/vehicle.js @@ -25,7 +25,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ static get newCargo() { return { - name: '', + name: "", quantity: 1 }; } @@ -40,7 +40,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _computeEncumbrance(totalWeight, actorData) { - // Compute currency weight const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0); totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight; @@ -57,7 +56,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { /* -------------------------------------------- */ /** @override */ - _getMovementSpeed(actorData, largestPrimary=true) { + _getMovementSpeed(actorData, largestPrimary = true) { return super._getMovementSpeed(actorData, largestPrimary); } @@ -69,25 +68,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _prepareCrewedItem(item) { - // Determine crewed status const isCrewed = item.data.crewed; - item.toggleClass = isCrewed ? 'active' : ''; - item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`); + item.toggleClass = isCrewed ? "active" : ""; + item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`); // 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.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`); - if (item.data.cover === .5) item.cover = '½'; - else if (item.data.cover === .75) item.cover = '¾'; - else if (item.data.cover === null) item.cover = '—'; - if (item.crew < 1 || item.crew === null) item.crew = '—'; + item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`); + if (item.data.cover === 0.5) item.cover = "½"; + else if (item.data.cover === 0.75) item.cover = "¾"; + else if (item.data.cover === null) item.cover = "—"; + if (item.crew < 1 || item.crew === null) item.crew = "—"; } // Prepare vehicle weapons - if (item.type === 'equipment' || item.type === 'weapon') { - item.threshold = item.data.hp.dt ? item.data.hp.dt : '—'; + if (item.type === "equipment" || item.type === "weapon") { + item.threshold = item.data.hp.dt ? item.data.hp.dt : "—"; } } @@ -98,128 +96,140 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _prepareItems(data) { - const cargoColumns = [{ - label: game.i18n.localize('SW5E.Quantity'), - css: 'item-qty', - property: 'quantity', - editable: 'Number' - }]; + const cargoColumns = [ + { + label: game.i18n.localize("SW5E.Quantity"), + css: "item-qty", + property: "quantity", + editable: "Number" + } + ]; - const equipmentColumns = [{ - label: game.i18n.localize('SW5E.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.HP'), - css: 'item-hp', - property: 'data.hp.value', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Threshold'), - css: 'item-threshold', - property: 'threshold' - }]; + const equipmentColumns = [ + { + label: game.i18n.localize("SW5E.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.HP"), + css: "item-hp", + property: "data.hp.value", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Threshold"), + css: "item-threshold", + property: "threshold" + } + ]; const features = { actions: { - label: game.i18n.localize('SW5E.ActionPl'), + label: game.i18n.localize("SW5E.ActionPl"), items: [], crewable: true, - dataset: {type: 'feat', 'activation.type': 'crew'}, - columns: [{ - label: game.i18n.localize('SW5E.VehicleCrew'), - css: 'item-crew', - property: 'crew' - }, { - label: game.i18n.localize('SW5E.Cover'), - css: 'item-cover', - property: 'cover' - }] + dataset: {type: "feat", "activation.type": "crew"}, + columns: [ + { + label: game.i18n.localize("SW5E.VehicleCrew"), + css: "item-crew", + property: "crew" + }, + { + label: game.i18n.localize("SW5E.Cover"), + css: "item-cover", + property: "cover" + } + ] }, equipment: { - label: game.i18n.localize('SW5E.ItemTypeEquipment'), + label: game.i18n.localize("SW5E.ItemTypeEquipment"), items: [], crewable: true, - dataset: {type: 'equipment', 'armor.type': 'vehicle'}, + dataset: {type: "equipment", "armor.type": "vehicle"}, columns: equipmentColumns }, passive: { - label: game.i18n.localize('SW5E.Features'), + label: game.i18n.localize("SW5E.Features"), items: [], - dataset: {type: 'feat'} + dataset: {type: "feat"} }, reactions: { - label: game.i18n.localize('SW5E.ReactionPl'), + label: game.i18n.localize("SW5E.ReactionPl"), items: [], - dataset: {type: 'feat', 'activation.type': 'reaction'} + dataset: {type: "feat", "activation.type": "reaction"} }, weapons: { - label: game.i18n.localize('SW5E.ItemTypeWeaponPl'), + label: game.i18n.localize("SW5E.ItemTypeWeaponPl"), items: [], crewable: true, - dataset: {type: 'weapon', 'weapon-type': 'siege'}, + dataset: {type: "weapon", "weapon-type": "siege"}, columns: equipmentColumns } }; const cargo = { crew: { - label: game.i18n.localize('SW5E.VehicleCrew'), + label: game.i18n.localize("SW5E.VehicleCrew"), items: data.data.cargo.crew, - css: 'cargo-row crew', + css: "cargo-row crew", editableName: true, - dataset: {type: 'crew'}, + dataset: {type: "crew"}, columns: cargoColumns }, passengers: { - label: game.i18n.localize('SW5E.VehiclePassengers'), + label: game.i18n.localize("SW5E.VehiclePassengers"), items: data.data.cargo.passengers, - css: 'cargo-row passengers', + css: "cargo-row passengers", editableName: true, - dataset: {type: 'passengers'}, + dataset: {type: "passengers"}, columns: cargoColumns }, cargo: { - label: game.i18n.localize('SW5E.VehicleCargo'), + label: game.i18n.localize("SW5E.VehicleCargo"), items: [], - dataset: {type: 'loot'}, - columns: [{ - label: game.i18n.localize('SW5E.Quantity'), - css: 'item-qty', - property: 'data.quantity', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Price'), - css: 'item-price', - property: 'data.price', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Weight'), - css: 'item-weight', - property: 'data.weight', - editable: 'Number' - }] + dataset: {type: "loot"}, + columns: [ + { + label: game.i18n.localize("SW5E.Quantity"), + css: "item-qty", + property: "data.quantity", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Price"), + css: "item-price", + property: "data.price", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Weight"), + css: "item-weight", + property: "data.weight", + editable: "Number" + } + ] } }; let totalWeight = 0; for (const item of data.items) { this._prepareCrewedItem(item); - if (item.type === 'weapon') features.weapons.items.push(item); - else if (item.type === 'equipment') features.equipment.items.push(item); - else if (item.type === 'loot') { + if (item.type === "weapon") features.weapons.items.push(item); + else if (item.type === "equipment") features.equipment.items.push(item); + else if (item.type === "loot") { totalWeight += (item.data.weight || 0) * item.data.quantity; cargo.cargo.items.push(item); - } - else if (item.type === 'feat') { - if (!item.data.activation.type || item.data.activation.type === 'none') { + } else if (item.type === "feat") { + if (!item.data.activation.type || item.data.activation.type === "none") { features.passive.items.push(item); - } - else if (item.data.activation.type === 'reaction') features.reactions.items.push(item); + } else if (item.data.activation.type === "reaction") features.reactions.items.push(item); else features.actions.items.push(item); } } @@ -238,21 +248,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { super.activateListeners(html); if (!this.options.editable) return; - html.find('.item-toggle').click(this._onToggleItem.bind(this)); - html.find('.item-hp input') + html.find(".item-toggle").click(this._onToggleItem.bind(this)); + html + .find(".item-hp input") .click(evt => evt.target.select()) .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()) .change(this._onEditInSheet.bind(this)); - html.find('.cargo-row input') + html + .find(".cargo-row input") .click(evt => evt.target.select()) .change(this._onCargoRowChange.bind(this)); if (this.actor.data.data.attributes.actions.stations) { - html.find('.counter.actions, .counter.action-thresholds').hide(); + html.find(".counter.actions, .counter.action-thresholds").hide(); } } @@ -267,9 +280,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { _onCargoRowChange(event) { event.preventDefault(); const target = event.currentTarget; - const row = target.closest('.item'); + const row = target.closest(".item"); 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 const cargo = duplicate(this.actor.data.data.cargo[property]); @@ -277,10 +290,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { if (!entry) return null; // Update the cargo value - const key = target.dataset.property || 'name'; + const key = target.dataset.property || "name"; const type = target.dataset.dtype; let value = target.value; - if (type === 'Number') value = Number(value); + if (type === "Number") value = Number(value); entry[key] = value; // Perform the Actor update @@ -297,14 +310,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onEditInSheet(event) { 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 property = event.currentTarget.dataset.property; const type = event.currentTarget.dataset.dtype; let value = event.currentTarget.value; switch (type) { - case 'Number': value = parseInt(value); break; - case 'Boolean': value = value === 'true'; break; + case "Number": + value = parseInt(value); + break; + case "Boolean": + value = value === "true"; + break; } return item.update({[`${property}`]: value}); } @@ -321,7 +338,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { event.preventDefault(); const target = event.currentTarget; const type = target.dataset.type; - if (type === 'crew' || type === 'passengers') { + if (type === "crew" || type === "passengers") { const cargo = duplicate(this.actor.data.data.cargo[type]); cargo.push(this.constructor.newCargo); return this.actor.update({[`data.cargo.${type}`]: cargo}); @@ -339,10 +356,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onItemDelete(event) { event.preventDefault(); - const row = event.currentTarget.closest('.item'); - if (row.classList.contains('cargo-row')) { + const row = event.currentTarget.closest(".item"); + if (row.classList.contains("cargo-row")) { const idx = Number(row.dataset.itemId); - const type = row.classList.contains('crew') ? 'crew' : 'passengers'; + const type = row.classList.contains("crew") ? "crew" : "passengers"; const cargo = duplicate(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx); return this.actor.update({[`data.cargo.${type}`]: cargo}); } @@ -360,11 +377,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onHPChange(event) { 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 hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max); event.currentTarget.value = hp; - return item.update({'data.hp.value': hp}); + return item.update({"data.hp.value": hp}); } /* -------------------------------------------- */ @@ -377,9 +394,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onToggleItem(event) { 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 crewed = !!item.data.data.crewed; - return item.update({'data.crewed': !crewed}); + return item.update({"data.crewed": !crewed}); } -}; +} diff --git a/module/actor/sheets/oldSheets/base.js b/module/actor/sheets/oldSheets/base.js index c97dd95a..a2f0dad9 100644 --- a/module/actor/sheets/oldSheets/base.js +++ b/module/actor/sheets/oldSheets/base.js @@ -3,7 +3,7 @@ import TraitSelector from "../../../apps/trait-selector.js"; import ActorSheetFlags from "../../../apps/actor-flags.js"; import ActorMovementConfig from "../../../apps/movement-config.js"; import ActorSensesConfig from "../../../apps/senses-config.js"; -import {SW5E} from '../../../config.js'; +import {SW5E} from "../../../config.js"; import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js"; /** @@ -46,7 +46,7 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ 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"; return `systems/sw5e/templates/actors/oldActor/${this.actor.data.type}-sheet.html`; } @@ -54,7 +54,6 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ getData() { - // Basic data let isOwner = this.entity.owner; const data = { @@ -65,8 +64,8 @@ export default class ActorSheet5e extends ActorSheet { cssClass: isOwner ? "editable" : "locked", isCharacter: this.entity.data.type === "character", isNPC: this.entity.data.type === "npc", - isVehicle: this.entity.data.type === 'vehicle', - config: CONFIG.SW5E, + isVehicle: this.entity.data.type === "vehicle", + config: CONFIG.SW5E }; // The Actor and its Items @@ -81,7 +80,7 @@ export default class ActorSheet5e extends ActorSheet { data.filters = this._filters; // Ability Scores - for ( let [a, abl] of Object.entries(data.actor.data.abilities)) { + for (let [a, abl] of Object.entries(data.actor.data.abilities)) { abl.icon = this._getProficiencyIcon(abl.proficient); abl.hover = CONFIG.SW5E.proficiencyLevels[abl.proficient]; abl.label = CONFIG.SW5E.abilities[a]; @@ -89,7 +88,7 @@ export default class ActorSheet5e extends ActorSheet { // Skills if (data.actor.data.skills) { - for ( let [s, skl] of Object.entries(data.actor.data.skills)) { + for (let [s, skl] of Object.entries(data.actor.data.skills)) { skl.ability = CONFIG.SW5E.abilityAbbreviations[skl.ability]; skl.icon = this._getProficiencyIcon(skl.value); skl.hover = CONFIG.SW5E.proficiencyLevels[skl.value]; @@ -113,7 +112,7 @@ export default class ActorSheet5e extends ActorSheet { data.effects = prepareActiveEffectCategories(this.entity.effects); // Return data to the sheet - return data + return data; } /* -------------------------------------------- */ @@ -125,17 +124,21 @@ export default class ActorSheet5e extends ActorSheet { * @returns {{primary: string, special: string}} * @private */ - _getMovementSpeed(actorData, largestPrimary=false) { + _getMovementSpeed(actorData, largestPrimary = false) { const movement = actorData.data.attributes.movement || {}; // Prepare an array of available movement speeds let speeds = [ [movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`], [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}`] - ] - if ( largestPrimary ) { + ]; + if (largestPrimary) { speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]); } @@ -143,12 +146,12 @@ export default class ActorSheet5e extends ActorSheet { speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]); // Case 1: Largest as primary - if ( largestPrimary ) { + if (largestPrimary) { let primary = speeds.shift(); return { primary: `${primary ? primary[1] : "0"} ${movement.units}`, special: speeds.map(s => s[1]).join(", ") - } + }; } // Case 2: Walk as primary @@ -156,7 +159,7 @@ export default class ActorSheet5e extends ActorSheet { return { primary: `${movement.walk || 0} ${movement.units}`, special: speeds.length ? speeds.map(s => s[1]).join(", ") : "" - } + }; } } @@ -165,12 +168,12 @@ export default class ActorSheet5e extends ActorSheet { _getSenses(actorData) { const senses = actorData.data.attributes.senses || {}; const tags = {}; - for ( let [k, label] of Object.entries(CONFIG.SW5E.senses) ) { - const v = senses[k] ?? 0 - if ( v === 0 ) continue; + for (let [k, label] of Object.entries(CONFIG.SW5E.senses)) { + const v = senses[k] ?? 0; + if (v === 0) continue; tags[k] = `${game.i18n.localize(label)} ${v} ${senses.units}`; } - if ( !!senses.special ) tags["special"] = senses.special; + if (!!senses.special) tags["special"] = senses.special; return tags; } @@ -183,20 +186,20 @@ export default class ActorSheet5e extends ActorSheet { */ _prepareTraits(traits) { const map = { - "dr": CONFIG.SW5E.damageResistanceTypes, - "di": CONFIG.SW5E.damageResistanceTypes, - "dv": CONFIG.SW5E.damageResistanceTypes, - "ci": CONFIG.SW5E.conditionTypes, - "languages": CONFIG.SW5E.languages, - "armorProf": CONFIG.SW5E.armorProficiencies, - "weaponProf": CONFIG.SW5E.weaponProficiencies, - "toolProf": CONFIG.SW5E.toolProficiencies + dr: CONFIG.SW5E.damageResistanceTypes, + di: CONFIG.SW5E.damageResistanceTypes, + dv: CONFIG.SW5E.damageResistanceTypes, + ci: CONFIG.SW5E.conditionTypes, + languages: CONFIG.SW5E.languages, + armorProf: CONFIG.SW5E.armorProficiencies, + weaponProf: CONFIG.SW5E.weaponProficiencies, + toolProf: CONFIG.SW5E.toolProficiencies }; - for ( let [t, choices] of Object.entries(map) ) { + for (let [t, choices] of Object.entries(map)) { const trait = traits[t]; - if ( !trait ) continue; + if (!trait) continue; let values = []; - if ( trait.value ) { + if (trait.value) { values = trait.value instanceof Array ? trait.value : [trait.value]; } trait.selected = values.reduce((obj, t) => { @@ -205,8 +208,8 @@ export default class ActorSheet5e extends ActorSheet { }, {}); // Add custom entry - if ( trait.custom ) { - trait.custom.split(";").forEach((c, i) => trait.selected[`custom${i+1}`] = c.trim()); + if (trait.custom) { + trait.custom.split(";").forEach((c, i) => (trait.selected[`custom${i + 1}`] = c.trim())); } trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive"; } @@ -227,45 +230,45 @@ export default class ActorSheet5e extends ActorSheet { // Define some mappings const sections = { - "atwill": -20, - "innate": -10, - "pact": 0.5 + atwill: -20, + innate: -10, + pact: 0.5 }; // Label power slot uses headers const useLabels = { "-20": "-", "-10": "-", - "0": "∞" + 0: "∞" }; // Format a powerbook entry for a certain indexed level - const registerSection = (sl, i, label, {prepMode="prepared", value, max, override}={}) => { + const registerSection = (sl, i, label, {prepMode = "prepared", value, max, override} = {}) => { powerbook[i] = { order: i, label: label, usesSlots: i > 0, canCreate: owner, - canPrepare: (data.actor.type === "character") && (i >= 1), + canPrepare: data.actor.type === "character" && i >= 1, powers: [], uses: useLabels[i] || value || 0, slots: useLabels[i] || max || 0, override: override || 0, - dataset: {"type": "power", "level": prepMode in sections ? 1 : i, "preparation.mode": prepMode}, + dataset: {type: "power", level: prepMode in sections ? 1 : i, "preparation.mode": prepMode}, prop: sl }; }; // Determine the maximum power level which has a slot const maxLevel = Array.fromRange(10).reduce((max, i) => { - if ( i === 0 ) return max; + if (i === 0) return max; 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; }, 0); // Level-based powercasters have cantrips and leveled slots - if ( maxLevel > 0 ) { + if (maxLevel > 0) { registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]); for (let lvl = 1; lvl <= maxLevel; lvl++) { const sl = `power${lvl}`; @@ -274,8 +277,8 @@ export default class ActorSheet5e extends ActorSheet { } // Pact magic users have cantrips and a pact magic section - if ( levels.pact && levels.pact.max ) { - if ( !powerbook["0"] ) registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]); + if (levels.pact && levels.pact.max) { + if (!powerbook["0"]) registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]); const l = levels.pact; const config = CONFIG.SW5E.powerPreparationModes.pact; registerSection("pact", sections.pact, config, { @@ -293,9 +296,9 @@ export default class ActorSheet5e extends ActorSheet { const sl = `power${s}`; // Specialized powercasting modes (if they exist) - if ( mode in sections ) { + if (mode in sections) { s = sections[mode]; - if ( !powerbook[s] ){ + if (!powerbook[s]) { const l = levels[mode] || {}; const config = CONFIG.SW5E.powerPreparationModes[mode]; registerSection(mode, s, config, { @@ -308,7 +311,7 @@ export default class ActorSheet5e extends ActorSheet { } // Sections for higher-level powers which the caster "should not" have, but power items exist for - else if ( !powerbook[s] ) { + else if (!powerbook[s]) { registerSection(sl, s, CONFIG.SW5E.powerLevels[s], {levels: levels[sl]}); } @@ -334,28 +337,28 @@ export default class ActorSheet5e extends ActorSheet { const data = item.data; // Action usage - for ( let f of ["action", "bonus", "reaction"] ) { - if ( filters.has(f) ) { - if ((data.activation && (data.activation.type !== f))) return false; + for (let f of ["action", "bonus", "reaction"]) { + if (filters.has(f)) { + if (data.activation && data.activation.type !== f) return false; } } // Power-specific filters - if ( filters.has("ritual") ) { + if (filters.has("ritual")) { if (data.components.ritual !== true) return false; } - if ( filters.has("concentration") ) { + if (filters.has("concentration")) { if (data.components.concentration !== true) return false; } - if ( filters.has("prepared") ) { - if ( data.level === 0 || ["innate", "always"].includes(data.preparation.mode) ) return true; - if ( this.actor.data.type === "npc" ) return true; + if (filters.has("prepared")) { + if (data.level === 0 || ["innate", "always"].includes(data.preparation.mode)) return true; + if (this.actor.data.type === "npc") return true; return data.preparation.prepared; } // Equipment-specific filters - if ( filters.has("equipped") ) { - if ( data.equipped !== true ) return false; + if (filters.has("equipped")) { + if (data.equipped !== true) return false; } return true; }); @@ -386,59 +389,58 @@ export default class ActorSheet5e extends ActorSheet { * @param html {HTML} The prepared HTML object ready to be rendered into the DOM */ activateListeners(html) { - // Activate Item Filters const filterLists = html.find(".filter-list"); filterLists.each(this._initializeFilterItemList.bind(this)); filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this)); // 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)); // Editable Only Listeners - if ( this.isEditable ) { - + if (this.isEditable) { // Input focus and update const inputs = html.find("input"); inputs.focus(ev => ev.currentTarget.select()); inputs.addBack().find('[data-dtype="Number"]').change(this._onChangeInputDelta.bind(this)); // Ability Proficiency - html.find('.ability-proficiency').click(this._onToggleAbilityProficiency.bind(this)); + html.find(".ability-proficiency").click(this._onToggleAbilityProficiency.bind(this)); // 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 - html.find('.trait-selector').click(this._onTraitSelector.bind(this)); + html.find(".trait-selector").click(this._onTraitSelector.bind(this)); // Configure Special Flags - html.find('.config-button').click(this._onConfigMenu.bind(this)); + html.find(".config-button").click(this._onConfigMenu.bind(this)); // Owned Item management - html.find('.item-create').click(this._onItemCreate.bind(this)); - html.find('.item-edit').click(this._onItemEdit.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('.slot-max-override').click(this._onPowerSlotOverride.bind(this)); + html.find(".item-create").click(this._onItemCreate.bind(this)); + html.find(".item-edit").click(this._onItemEdit.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(".slot-max-override").click(this._onPowerSlotOverride.bind(this)); // Active Effect management html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.entity)); } // Owner Only Listeners - if ( this.actor.owner ) { - + if (this.actor.owner) { // Ability Checks - html.find('.ability-name').click(this._onRollAbilityTest.bind(this)); - + html.find(".ability-name").click(this._onRollAbilityTest.bind(this)); // Roll Skill Checks - html.find('.skill-name').click(this._onRollSkillCheck.bind(this)); + html.find(".skill-name").click(this._onRollSkillCheck.bind(this)); // Item Rolling - html.find('.item .item-image').click(event => this._onItemRoll(event)); - html.find('.item .item-recharge').click(event => this._onItemRecharge(event)); + html.find(".item .item-image").click(event => this._onItemRoll(event)); + html.find(".item .item-recharge").click(event => this._onItemRecharge(event)); } // Otherwise remove rollable classes @@ -459,8 +461,8 @@ export default class ActorSheet5e extends ActorSheet { _initializeFilterItemList(i, ul) { const set = this._filters[ul.dataset.filter]; const filters = ul.querySelectorAll(".filter-item"); - for ( let li of filters ) { - if ( set.has(li.dataset.filter) ) li.classList.add("active"); + for (let li of filters) { + if (set.has(li.dataset.filter)) li.classList.add("active"); } } @@ -476,10 +478,10 @@ export default class ActorSheet5e extends ActorSheet { _onChangeInputDelta(event) { const input = event.target; const value = input.value; - if ( ["+", "-"].includes(value[0]) ) { + if (["+", "-"].includes(value[0])) { let delta = parseFloat(value); input.value = getProperty(this.actor.data, input.name) + delta; - } else if ( value[0] === "=" ) { + } else if (value[0] === "=") { input.value = value.slice(1); } } @@ -494,7 +496,7 @@ export default class ActorSheet5e extends ActorSheet { _onConfigMenu(event) { event.preventDefault(); const button = event.currentTarget; - switch ( button.dataset.action ) { + switch (button.dataset.action) { case "movement": new ActorMovementConfig(this.object).render(true); break; @@ -524,10 +526,10 @@ export default class ActorSheet5e extends ActorSheet { let idx = levels.indexOf(level); // Toggle next level - forward on click, backwards on right - if ( event.type === "click" ) { - field.val(levels[(idx === levels.length - 1) ? 0 : idx + 1]); - } else if ( event.type === "contextmenu" ) { - field.val(levels[(idx === 0) ? levels.length - 1 : idx - 1]); + if (event.type === "click") { + field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]); + } else if (event.type === "contextmenu") { + field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]); } // Update the field value and save the form @@ -538,8 +540,8 @@ export default class ActorSheet5e extends ActorSheet { /** @override */ async _onDropActor(event, data) { - const canPolymorph = game.user.isGM || (this.actor.owner && game.settings.get('sw5e', 'allowPolymorphing')); - if ( !canPolymorph ) return false; + const canPolymorph = game.user.isGM || (this.actor.owner && game.settings.get("sw5e", "allowPolymorphing")); + if (!canPolymorph) return false; // Get the target actor let sourceActor = null; @@ -549,78 +551,82 @@ export default class ActorSheet5e extends ActorSheet { } else { sourceActor = game.actors.get(data.id); } - if ( !sourceActor ) return; + if (!sourceActor) return; // Define a function to record polymorph settings for future use const rememberOptions = html => { const options = {}; - html.find('input').each((i, el) => { + html.find("input").each((i, el) => { options[el.name] = el.checked; }); - const settings = mergeObject(game.settings.get('sw5e', 'polymorphSettings') || {}, options); - game.settings.set('sw5e', 'polymorphSettings', settings); + const settings = mergeObject(game.settings.get("sw5e", "polymorphSettings") || {}, options); + game.settings.set("sw5e", "polymorphSettings", settings); return settings; }; // Create and render the Dialog - return new Dialog({ - title: game.i18n.localize('SW5E.PolymorphPromptTitle'), - content: { - options: game.settings.get('sw5e', 'polymorphSettings'), - i18n: SW5E.polymorphSettings, - isToken: this.actor.isToken - }, - default: 'accept', - buttons: { - accept: { - icon: '', - label: game.i18n.localize('SW5E.PolymorphAcceptSettings'), - callback: html => this.actor.transformInto(sourceActor, rememberOptions(html)) + return new Dialog( + { + title: game.i18n.localize("SW5E.PolymorphPromptTitle"), + content: { + options: game.settings.get("sw5e", "polymorphSettings"), + i18n: SW5E.polymorphSettings, + isToken: this.actor.isToken }, - wildshape: { - icon: '', - label: game.i18n.localize('SW5E.PolymorphWildShape'), - callback: html => this.actor.transformInto(sourceActor, { - keepBio: true, - keepClass: true, - keepMental: true, - mergeSaves: true, - mergeSkills: true, - transformTokens: rememberOptions(html).transformTokens - }) - }, - polymorph: { - icon: '', - label: game.i18n.localize('SW5E.Polymorph'), - callback: html => this.actor.transformInto(sourceActor, { - transformTokens: rememberOptions(html).transformTokens - }) - }, - cancel: { - icon: '', - label: game.i18n.localize('Cancel') + default: "accept", + buttons: { + accept: { + icon: '', + label: game.i18n.localize("SW5E.PolymorphAcceptSettings"), + callback: html => this.actor.transformInto(sourceActor, rememberOptions(html)) + }, + wildshape: { + icon: '', + label: game.i18n.localize("SW5E.PolymorphWildShape"), + callback: html => + this.actor.transformInto(sourceActor, { + keepBio: true, + keepClass: true, + keepMental: true, + mergeSaves: true, + mergeSkills: true, + transformTokens: rememberOptions(html).transformTokens + }) + }, + polymorph: { + icon: '', + label: game.i18n.localize("SW5E.Polymorph"), + callback: html => + this.actor.transformInto(sourceActor, { + transformTokens: rememberOptions(html).transformTokens + }) + }, + cancel: { + icon: '', + label: game.i18n.localize("Cancel") + } } + }, + { + classes: ["dialog", "sw5e"], + width: 600, + template: "systems/sw5e/templates/apps/polymorph-prompt.html" } - }, { - classes: ['dialog', 'sw5e'], - width: 600, - template: 'systems/sw5e/templates/apps/polymorph-prompt.html' - }).render(true); + ).render(true); } /* -------------------------------------------- */ /** @override */ async _onDropItemCreate(itemData) { - // 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); itemData = scroll.data; } // Ignore certain statuses - if ( itemData.data ) { + if (itemData.data) { ["attunement", "equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]); } @@ -635,7 +641,7 @@ export default class ActorSheet5e extends ActorSheet { * @param {MouseEvent} event The originating click event * @private */ - async _onPowerSlotOverride (event) { + async _onPowerSlotOverride(event) { const span = event.currentTarget.parentElement; const level = span.dataset.level; const override = this.actor.data.data.powers[level].override || span.dataset.slots; @@ -660,12 +666,12 @@ export default class ActorSheet5e extends ActorSheet { * @private */ async _onUsesChange(event) { - event.preventDefault(); - const itemId = event.currentTarget.closest(".item").dataset.itemId; - const item = this.actor.getOwnedItem(itemId); - const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max); - event.target.value = uses; - return item.update({ 'data.uses.value': uses }); + event.preventDefault(); + const itemId = event.currentTarget.closest(".item").dataset.itemId; + const item = this.actor.getOwnedItem(itemId); + const uses = Math.clamped(0, parseInt(event.target.value), item.data.data.uses.max); + event.target.value = uses; + return item.update({"data.uses.value": uses}); } /* -------------------------------------------- */ @@ -693,7 +699,7 @@ export default class ActorSheet5e extends ActorSheet { const itemId = event.currentTarget.closest(".item").dataset.itemId; const item = this.actor.getOwnedItem(itemId); return item.rollRecharge(); - }; + } /* -------------------------------------------- */ @@ -704,11 +710,11 @@ export default class ActorSheet5e extends ActorSheet { _onItemSummary(event) { event.preventDefault(); let li = $(event.currentTarget).parents(".item"), - item = this.actor.getOwnedItem(li.data("item-id")), - chatData = item.getChatData({secrets: this.actor.owner}); + item = this.actor.getOwnedItem(li.data("item-id")), + chatData = item.getChatData({secrets: this.actor.owner}); // Toggle summary - if ( li.hasClass("expanded") ) { + if (li.hasClass("expanded")) { let summary = li.children(".item-summary"); summary.slideUp(200, () => summary.remove()); } else { @@ -820,7 +826,7 @@ export default class ActorSheet5e extends ActorSheet { const li = event.currentTarget; const set = this._filters[li.parentElement.dataset.filter]; const filter = li.dataset.filter; - if ( set.has(filter) ) set.delete(filter); + if (set.has(filter)) set.delete(filter); else set.add(filter); this.render(); } @@ -837,8 +843,8 @@ export default class ActorSheet5e extends ActorSheet { const a = event.currentTarget; const label = a.parentElement.querySelector("label"); const choices = CONFIG.SW5E[a.dataset.options]; - const options = { name: a.dataset.target, title: label.innerText, choices }; - new TraitSelector(this.actor, options).render(true) + const options = {name: a.dataset.target, title: label.innerText, choices}; + new TraitSelector(this.actor, options).render(true); } /* -------------------------------------------- */ @@ -848,13 +854,13 @@ export default class ActorSheet5e extends ActorSheet { let buttons = super._getHeaderButtons(); // Add button to revert polymorph - if ( !this.actor.isPolymorphed || this.actor.isToken ) return buttons; + if (!this.actor.isPolymorphed || this.actor.isToken) return buttons; buttons.unshift({ - label: 'SW5E.PolymorphRestoreTransformation', + label: "SW5E.PolymorphRestoreTransformation", class: "restore-transformation", icon: "fas fa-backward", onclick: ev => this.actor.revertOriginalForm() }); return buttons; } -} \ No newline at end of file +} diff --git a/module/actor/sheets/oldSheets/character.js b/module/actor/sheets/oldSheets/character.js index dc28b205..d626b1a9 100644 --- a/module/actor/sheets/oldSheets/character.js +++ b/module/actor/sheets/oldSheets/character.js @@ -7,13 +7,12 @@ import Actor5e from "../../entity.js"; * @type {ActorSheet5e} */ export default class ActorSheet5eCharacter extends ActorSheet5e { - /** * Define default rendering options for the NPC sheet * @return {Object} */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { classes: ["sw5e", "sheet", "actor", "character"], width: 720, height: 736 @@ -37,7 +36,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { sheetData["resources"] = ["primary", "secondary", "tertiary"].reduce((arr, r) => { const res = sheetData.data.resources[r] || {}; res.name = r; - res.placeholder = game.i18n.localize("SW5E.Resource"+r.titleCase()); + res.placeholder = game.i18n.localize("SW5E.Resource" + r.titleCase()); if (res && res.value === 0) delete res.value; if (res && res.max === 0) delete res.max; return arr.concat([res]); @@ -46,9 +45,11 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { // Experience Tracking sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking"); sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", "); - sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => { - return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ') - }).join(', '); + sheetData["multiclassLabels"] = this.actor.itemTypes.class + .map(c => { + return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(" "); + }) + .join(", "); // Return data for rendering return sheetData; @@ -61,59 +62,72 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { * @private */ _prepareItems(data) { - // Categorize items as inventory, powerbook, features, and classes const inventory = { - weapon: { label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"} }, - equipment: { label: "SW5E.ItemTypeEquipmentPl", items: [], dataset: {type: "equipment"} }, - consumable: { label: "SW5E.ItemTypeConsumablePl", items: [], dataset: {type: "consumable"} }, - tool: { label: "SW5E.ItemTypeToolPl", items: [], dataset: {type: "tool"} }, - backpack: { label: "SW5E.ItemTypeContainerPl", items: [], dataset: {type: "backpack"} }, - loot: { label: "SW5E.ItemTypeLootPl", items: [], dataset: {type: "loot"} } + weapon: {label: "SW5E.ItemTypeWeaponPl", items: [], dataset: {type: "weapon"}}, + equipment: {label: "SW5E.ItemTypeEquipmentPl", items: [], dataset: {type: "equipment"}}, + consumable: {label: "SW5E.ItemTypeConsumablePl", items: [], dataset: {type: "consumable"}}, + tool: {label: "SW5E.ItemTypeToolPl", items: [], dataset: {type: "tool"}}, + backpack: {label: "SW5E.ItemTypeContainerPl", items: [], dataset: {type: "backpack"}}, + loot: {label: "SW5E.ItemTypeLootPl", items: [], dataset: {type: "loot"}} }; // 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.img = item.img || DEFAULT_TOKEN; + item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1; + item.attunement = { + [CONFIG.SW5E.attunementTypes.REQUIRED]: { + icon: "fa-sun", + cls: "not-attuned", + title: "SW5E.AttunementRequired" + }, + [CONFIG.SW5E.attunementTypes.ATTUNED]: { + icon: "fa-sun", + cls: "attuned", + title: "SW5E.AttunementAttuned" + } + }[item.data.attunement]; - // Item details - item.img = item.img || DEFAULT_TOKEN; - item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1); - item.attunement = { - [CONFIG.SW5E.attunementTypes.REQUIRED]: { - icon: "fa-sun", - cls: "not-attuned", - title: "SW5E.AttunementRequired" - }, - [CONFIG.SW5E.attunementTypes.ATTUNED]: { - icon: "fa-sun", - cls: "attuned", - title: "SW5E.AttunementAttuned" - } - }[item.data.attunement]; + // Item usage + 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.isDepleted = item.isOnCooldown && item.data.uses.per && item.data.uses.value > 0; + item.hasTarget = !!item.data.target && !["none", ""].includes(item.data.target.type); - // Item usage - 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.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 + this._prepareItemToggleState(item); - // Item toggle state - this._prepareItemToggleState(item); - - // Classify items into types - if ( item.type === "power" ) arr[1].push(item); - else if ( item.type === "feat" ) arr[2].push(item); - else if ( item.type === "class" ) arr[3].push(item); - else if ( item.type === "species" ) arr[4].push(item); - else if ( item.type === "archetype" ) arr[5].push(item); - else if ( item.type === "classfeature" ) arr[6].push(item); - else if ( item.type === "background" ) arr[7].push(item); - else if ( item.type === "fightingstyle" ) arr[8].push(item); - else if ( item.type === "fightingmastery" ) arr[9].push(item); - else if ( item.type === "lightsaberform" ) arr[10].push(item); - else if ( Object.keys(inventory).includes(item.type ) ) arr[0].push(item); - return arr; - }, [[], [], [], [], [], [], [], [], [], [], []]); + // Classify items into types + if (item.type === "power") arr[1].push(item); + else if (item.type === "feat") arr[2].push(item); + else if (item.type === "class") arr[3].push(item); + else if (item.type === "species") arr[4].push(item); + else if (item.type === "archetype") arr[5].push(item); + else if (item.type === "classfeature") arr[6].push(item); + else if (item.type === "background") arr[7].push(item); + else if (item.type === "fightingstyle") arr[8].push(item); + else if (item.type === "fightingmastery") arr[9].push(item); + else if (item.type === "lightsaberform") arr[10].push(item); + else if (Object.keys(inventory).includes(item.type)) arr[0].push(item); + return arr; + }, + [[], [], [], [], [], [], [], [], [], [], []] + ); // Apply active item filters items = this._filterItems(items, this._filters.inventory); @@ -121,7 +135,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { feats = this._filterItems(feats, this._filters.features); // Organize items - for ( let i of items ) { + for (let i of items) { i.data.quantity = i.data.quantity || 0; i.data.weight = i.data.weight || 0; i.totalWeight = (i.data.quantity * i.data.weight).toNearest(0.1); @@ -131,24 +145,71 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { // Organize Powerbook and count the number of prepared powers (excluding always, at will, etc...) const powerbook = this._preparePowerbook(data, powers); 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; // Organize Features const features = { - classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true }, - classfeatures: { 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"} } + classes: {label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true}, + classfeatures: { + 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"}} }; - for ( let f of feats ) { - if ( f.data.activation.type ) features.active.items.push(f); + for (let f of feats) { + if (f.data.activation.type) features.active.items.push(f); else features.passive.items.push(f); } classes.sort((a, b) => b.levels - a.levels); @@ -178,14 +239,13 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { _prepareItemToggleState(item) { if (item.type === "power") { const isAlways = getProperty(item.data, "preparation.mode") === "always"; - const isPrepared = getProperty(item.data, "preparation.prepared"); + const isPrepared = getProperty(item.data, "preparation.prepared"); item.toggleClass = isPrepared ? "active" : ""; - if ( isAlways ) item.toggleClass = "fixed"; - if ( isAlways ) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always; - else if ( isPrepared ) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared; + if (isAlways) item.toggleClass = "fixed"; + if (isAlways) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.always; + else if (isPrepared) item.toggleTitle = CONFIG.SW5E.powerPreparationModes.prepared; else item.toggleTitle = game.i18n.localize("SW5E.PowerUnprepared"); - } - else { + } else { const isActive = getProperty(item.data, "equipped"); item.toggleClass = isActive ? "active" : ""; item.toggleTitle = game.i18n.localize(isActive ? "SW5E.Equipped" : "SW5E.Unequipped"); @@ -200,16 +260,16 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { * Activate event listeners using the prepared sheet HTML * @param html {HTML} The prepared HTML object ready to be rendered into the DOM */ - activateListeners(html) { + activateListeners(html) { super.activateListeners(html); - if ( !this.options.editable ) return; + if (!this.options.editable) return; // 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 - html.find('.short-rest').click(this._onShortRest.bind(this)); - html.find('.long-rest').click(this._onLongRest.bind(this)); + html.find(".short-rest").click(this._onShortRest.bind(this)); + html.find(".long-rest").click(this._onLongRest.bind(this)); // Rollable sheet actions html.find(".rollable[data-action]").click(this._onSheetAction.bind(this)); @@ -225,7 +285,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { _onSheetAction(event) { event.preventDefault(); const button = event.currentTarget; - switch( button.dataset.action ) { + switch (button.dataset.action) { case "rollDeathSave": return this.actor.rollDeathSave({event: event}); case "rollInitiative": @@ -278,14 +338,13 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { /** @override */ async _onDropItemCreate(itemData) { - // 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); 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); - if ( next > priorLevel ) { + if (next > priorLevel) { itemData.levels = next; return cls.update({"data.levels": next}); } @@ -295,4 +354,4 @@ export default class ActorSheet5eCharacter extends ActorSheet5e { // Default drop handling if levels were not added super._onDropItemCreate(itemData); } -} \ No newline at end of file +} diff --git a/module/actor/sheets/oldSheets/npc.js b/module/actor/sheets/oldSheets/npc.js index 366dc65e..a0474f35 100644 --- a/module/actor/sheets/oldSheets/npc.js +++ b/module/actor/sheets/oldSheets/npc.js @@ -6,10 +6,9 @@ import ActorSheet5e from "./base.js"; * @extends {ActorSheet5e} */ export default class ActorSheet5eNPC extends ActorSheet5e { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { classes: ["sw5e", "sheet", "actor", "npc"], width: 600, height: 680 @@ -23,27 +22,39 @@ export default class ActorSheet5eNPC extends ActorSheet5e { * @private */ _prepareItems(data) { - // Categorize Items as Features and Powers const features = { - weapons: { 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"} }, - equipment: { label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}} + weapons: { + 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"}}, + equipment: {label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}} }; // Start by classifying items into groups for rendering - let [powers, other] = data.items.reduce((arr, item) => { - item.img = item.img || DEFAULT_TOKEN; - item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1); - 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.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); - else arr[1].push(item); - return arr; - }, [[], []]); + let [powers, other] = data.items.reduce( + (arr, item) => { + item.img = item.img || DEFAULT_TOKEN; + item.isStack = Number.isNumeric(item.data.quantity) && item.data.quantity !== 1; + 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.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); + else arr[1].push(item); + return arr; + }, + [[], []] + ); // Apply item filters powers = this._filterItems(powers, this._filters.powerbook); @@ -53,13 +64,12 @@ export default class ActorSheet5eNPC extends ActorSheet5e { const powerbook = this._preparePowerbook(data, powers); // Organize Features - for ( let item of other ) { - if ( item.type === "weapon" ) features.weapons.items.push(item); - else if ( item.type === "feat" ) { - if ( item.data.activation.type ) features.actions.items.push(item); + for (let item of other) { + if (item.type === "weapon") features.weapons.items.push(item); + else if (item.type === "feat") { + if (item.data.activation.type) features.actions.items.push(item); else features.passive.items.push(item); - } - else features.equipment.items.push(item); + } else features.equipment.items.push(item); } // Assign and return @@ -67,7 +77,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e { data.powerbook = powerbook; } - /* -------------------------------------------- */ /** @override */ @@ -87,13 +96,12 @@ export default class ActorSheet5eNPC extends ActorSheet5e { /** @override */ _updateObject(event, formData) { - // Format NPC Challenge Rating const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5}; let crv = "data.details.cr"; let cr = formData[crv]; cr = crs[cr] || parseFloat(cr); - if ( cr ) formData[crv] = cr < 1 ? cr : parseInt(cr); + if (cr) formData[crv] = cr < 1 ? cr : parseInt(cr); // Parent ActorSheet update steps super._updateObject(event, formData); @@ -104,7 +112,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e { /* -------------------------------------------- */ /** @override */ - activateListeners(html) { + activateListeners(html) { super.activateListeners(html); html.find(".health .rollable").click(this._onRollHPFormula.bind(this)); } @@ -119,7 +127,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e { _onRollHPFormula(event) { event.preventDefault(); const formula = this.actor.data.data.attributes.hp.formula; - if ( !formula ) return; + if (!formula) return; const hp = new Roll(formula).roll().total; AudioHelper.play({src: CONFIG.sounds.dice}); this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp}); diff --git a/module/actor/sheets/oldSheets/vehicle.js b/module/actor/sheets/oldSheets/vehicle.js index cba36ebd..7238606a 100644 --- a/module/actor/sheets/oldSheets/vehicle.js +++ b/module/actor/sheets/oldSheets/vehicle.js @@ -25,7 +25,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ static get newCargo() { return { - name: '', + name: "", quantity: 1 }; } @@ -40,7 +40,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _computeEncumbrance(totalWeight, actorData) { - // Compute currency weight const totalCoins = Object.values(actorData.data.currency).reduce((acc, denom) => acc + denom, 0); totalWeight += totalCoins / CONFIG.SW5E.encumbrance.currencyPerWeight; @@ -57,7 +56,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { /* -------------------------------------------- */ /** @override */ - _getMovementSpeed(actorData, largestPrimary=true) { + _getMovementSpeed(actorData, largestPrimary = true) { return super._getMovementSpeed(actorData, largestPrimary); } @@ -69,25 +68,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _prepareCrewedItem(item) { - // Determine crewed status const isCrewed = item.data.crewed; - item.toggleClass = isCrewed ? 'active' : ''; - item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? 'Crewed' : 'Uncrewed'}`); + item.toggleClass = isCrewed ? "active" : ""; + item.toggleTitle = game.i18n.localize(`SW5E.${isCrewed ? "Crewed" : "Uncrewed"}`); // 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.cover = game.i18n.localize(`SW5E.${item.data.cover ? 'CoverTotal' : 'None'}`); - if (item.data.cover === .5) item.cover = '½'; - else if (item.data.cover === .75) item.cover = '¾'; - else if (item.data.cover === null) item.cover = '—'; - if (item.crew < 1 || item.crew === null) item.crew = '—'; + item.cover = game.i18n.localize(`SW5E.${item.data.cover ? "CoverTotal" : "None"}`); + if (item.data.cover === 0.5) item.cover = "½"; + else if (item.data.cover === 0.75) item.cover = "¾"; + else if (item.data.cover === null) item.cover = "—"; + if (item.crew < 1 || item.crew === null) item.crew = "—"; } // Prepare vehicle weapons - if (item.type === 'equipment' || item.type === 'weapon') { - item.threshold = item.data.hp.dt ? item.data.hp.dt : '—'; + if (item.type === "equipment" || item.type === "weapon") { + item.threshold = item.data.hp.dt ? item.data.hp.dt : "—"; } } @@ -98,128 +96,140 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { * @private */ _prepareItems(data) { - const cargoColumns = [{ - label: game.i18n.localize('SW5E.Quantity'), - css: 'item-qty', - property: 'quantity', - editable: 'Number' - }]; + const cargoColumns = [ + { + label: game.i18n.localize("SW5E.Quantity"), + css: "item-qty", + property: "quantity", + editable: "Number" + } + ]; - const equipmentColumns = [{ - label: game.i18n.localize('SW5E.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.HP'), - css: 'item-hp', - property: 'data.hp.value', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Threshold'), - css: 'item-threshold', - property: 'threshold' - }]; + const equipmentColumns = [ + { + label: game.i18n.localize("SW5E.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.HP"), + css: "item-hp", + property: "data.hp.value", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Threshold"), + css: "item-threshold", + property: "threshold" + } + ]; const features = { actions: { - label: game.i18n.localize('SW5E.ActionPl'), + label: game.i18n.localize("SW5E.ActionPl"), items: [], crewable: true, - dataset: {type: 'feat', 'activation.type': 'crew'}, - columns: [{ - label: game.i18n.localize('SW5E.VehicleCrew'), - css: 'item-crew', - property: 'crew' - }, { - label: game.i18n.localize('SW5E.Cover'), - css: 'item-cover', - property: 'cover' - }] + dataset: {type: "feat", "activation.type": "crew"}, + columns: [ + { + label: game.i18n.localize("SW5E.VehicleCrew"), + css: "item-crew", + property: "crew" + }, + { + label: game.i18n.localize("SW5E.Cover"), + css: "item-cover", + property: "cover" + } + ] }, equipment: { - label: game.i18n.localize('SW5E.ItemTypeEquipment'), + label: game.i18n.localize("SW5E.ItemTypeEquipment"), items: [], crewable: true, - dataset: {type: 'equipment', 'armor.type': 'vehicle'}, + dataset: {type: "equipment", "armor.type": "vehicle"}, columns: equipmentColumns }, passive: { - label: game.i18n.localize('SW5E.Features'), + label: game.i18n.localize("SW5E.Features"), items: [], - dataset: {type: 'feat'} + dataset: {type: "feat"} }, reactions: { - label: game.i18n.localize('SW5E.ReactionPl'), + label: game.i18n.localize("SW5E.ReactionPl"), items: [], - dataset: {type: 'feat', 'activation.type': 'reaction'} + dataset: {type: "feat", "activation.type": "reaction"} }, weapons: { - label: game.i18n.localize('SW5E.ItemTypeWeaponPl'), + label: game.i18n.localize("SW5E.ItemTypeWeaponPl"), items: [], crewable: true, - dataset: {type: 'weapon', 'weapon-type': 'siege'}, + dataset: {type: "weapon", "weapon-type": "siege"}, columns: equipmentColumns } }; const cargo = { crew: { - label: game.i18n.localize('SW5E.VehicleCrew'), + label: game.i18n.localize("SW5E.VehicleCrew"), items: data.data.cargo.crew, - css: 'cargo-row crew', + css: "cargo-row crew", editableName: true, - dataset: {type: 'crew'}, + dataset: {type: "crew"}, columns: cargoColumns }, passengers: { - label: game.i18n.localize('SW5E.VehiclePassengers'), + label: game.i18n.localize("SW5E.VehiclePassengers"), items: data.data.cargo.passengers, - css: 'cargo-row passengers', + css: "cargo-row passengers", editableName: true, - dataset: {type: 'passengers'}, + dataset: {type: "passengers"}, columns: cargoColumns }, cargo: { - label: game.i18n.localize('SW5E.VehicleCargo'), + label: game.i18n.localize("SW5E.VehicleCargo"), items: [], - dataset: {type: 'loot'}, - columns: [{ - label: game.i18n.localize('SW5E.Quantity'), - css: 'item-qty', - property: 'data.quantity', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Price'), - css: 'item-price', - property: 'data.price', - editable: 'Number' - }, { - label: game.i18n.localize('SW5E.Weight'), - css: 'item-weight', - property: 'data.weight', - editable: 'Number' - }] + dataset: {type: "loot"}, + columns: [ + { + label: game.i18n.localize("SW5E.Quantity"), + css: "item-qty", + property: "data.quantity", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Price"), + css: "item-price", + property: "data.price", + editable: "Number" + }, + { + label: game.i18n.localize("SW5E.Weight"), + css: "item-weight", + property: "data.weight", + editable: "Number" + } + ] } }; let totalWeight = 0; for (const item of data.items) { this._prepareCrewedItem(item); - if (item.type === 'weapon') features.weapons.items.push(item); - else if (item.type === 'equipment') features.equipment.items.push(item); - else if (item.type === 'loot') { + if (item.type === "weapon") features.weapons.items.push(item); + else if (item.type === "equipment") features.equipment.items.push(item); + else if (item.type === "loot") { totalWeight += (item.data.weight || 0) * item.data.quantity; cargo.cargo.items.push(item); - } - else if (item.type === 'feat') { - if (!item.data.activation.type || item.data.activation.type === 'none') { + } else if (item.type === "feat") { + if (!item.data.activation.type || item.data.activation.type === "none") { features.passive.items.push(item); - } - else if (item.data.activation.type === 'reaction') features.reactions.items.push(item); + } else if (item.data.activation.type === "reaction") features.reactions.items.push(item); else features.actions.items.push(item); } } @@ -238,21 +248,24 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { super.activateListeners(html); if (!this.options.editable) return; - html.find('.item-toggle').click(this._onToggleItem.bind(this)); - html.find('.item-hp input') + html.find(".item-toggle").click(this._onToggleItem.bind(this)); + html + .find(".item-hp input") .click(evt => evt.target.select()) .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()) .change(this._onEditInSheet.bind(this)); - html.find('.cargo-row input') + html + .find(".cargo-row input") .click(evt => evt.target.select()) .change(this._onCargoRowChange.bind(this)); if (this.actor.data.data.attributes.actions.stations) { - html.find('.counter.actions, .counter.action-thresholds').hide(); + html.find(".counter.actions, .counter.action-thresholds").hide(); } } @@ -267,9 +280,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { _onCargoRowChange(event) { event.preventDefault(); const target = event.currentTarget; - const row = target.closest('.item'); + const row = target.closest(".item"); 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 const cargo = duplicate(this.actor.data.data.cargo[property]); @@ -277,10 +290,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { if (!entry) return null; // Update the cargo value - const key = target.dataset.property || 'name'; + const key = target.dataset.property || "name"; const type = target.dataset.dtype; let value = target.value; - if (type === 'Number') value = Number(value); + if (type === "Number") value = Number(value); entry[key] = value; // Perform the Actor update @@ -297,14 +310,18 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onEditInSheet(event) { 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 property = event.currentTarget.dataset.property; const type = event.currentTarget.dataset.dtype; let value = event.currentTarget.value; switch (type) { - case 'Number': value = parseInt(value); break; - case 'Boolean': value = value === 'true'; break; + case "Number": + value = parseInt(value); + break; + case "Boolean": + value = value === "true"; + break; } return item.update({[`${property}`]: value}); } @@ -321,7 +338,7 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { event.preventDefault(); const target = event.currentTarget; const type = target.dataset.type; - if (type === 'crew' || type === 'passengers') { + if (type === "crew" || type === "passengers") { const cargo = duplicate(this.actor.data.data.cargo[type]); cargo.push(this.constructor.newCargo); return this.actor.update({[`data.cargo.${type}`]: cargo}); @@ -339,10 +356,10 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onItemDelete(event) { event.preventDefault(); - const row = event.currentTarget.closest('.item'); - if (row.classList.contains('cargo-row')) { + const row = event.currentTarget.closest(".item"); + if (row.classList.contains("cargo-row")) { const idx = Number(row.dataset.itemId); - const type = row.classList.contains('crew') ? 'crew' : 'passengers'; + const type = row.classList.contains("crew") ? "crew" : "passengers"; const cargo = duplicate(this.actor.data.data.cargo[type]).filter((_, i) => i !== idx); return this.actor.update({[`data.cargo.${type}`]: cargo}); } @@ -360,11 +377,11 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onHPChange(event) { 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 hp = Math.clamped(0, parseInt(event.currentTarget.value), item.data.data.hp.max); event.currentTarget.value = hp; - return item.update({'data.hp.value': hp}); + return item.update({"data.hp.value": hp}); } /* -------------------------------------------- */ @@ -377,9 +394,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e { */ _onToggleItem(event) { 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 crewed = !!item.data.data.crewed; - return item.update({'data.crewed': !crewed}); + return item.update({"data.crewed": !crewed}); } -}; +} diff --git a/module/apps/ability-use-dialog.js b/module/apps/ability-use-dialog.js index bcbc9b25..82c32af3 100644 --- a/module/apps/ability-use-dialog.js +++ b/module/apps/ability-use-dialog.js @@ -3,7 +3,7 @@ * @type {Dialog} */ export default class AbilityUseDialog extends Dialog { - constructor(item, dialogData={}, options={}) { + constructor(item, dialogData = {}, options = {}) { super(dialogData, options); this.options.classes = ["sw5e", "dialog"]; @@ -25,7 +25,7 @@ export default class AbilityUseDialog extends Dialog { * @return {Promise} */ static async create(item) { - if ( !item.isOwned ) throw new Error("You cannot display an ability usage dialog for an unowned item"); + if (!item.isOwned) throw new Error("You cannot display an ability usage dialog for an unowned item"); // Prepare data const actorData = item.actor.data.data; @@ -34,7 +34,7 @@ export default class AbilityUseDialog extends Dialog { const quantity = itemData.quantity || 0; const recharge = itemData.recharge || {}; const recharges = !!recharge.value; - const sufficientUses = (quantity > 0 && !uses.value) || uses.value > 0; + const sufficientUses = (quantity > 0 && !uses.value) || uses.value > 0; // Prepare dialog form data const data = { @@ -49,7 +49,7 @@ export default class AbilityUseDialog extends Dialog { createTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget, errors: [] }; - if ( item.data.type === "power" ) this._getPowerData(actorData, itemData, data); + if (item.data.type === "power") this._getPowerData(actorData, itemData, data); // Render the ability usage template const html = await renderTemplate("systems/sw5e/templates/apps/ability-use.html", data); @@ -57,7 +57,7 @@ export default class AbilityUseDialog extends Dialog { // Create the Dialog and return data as a Promise const icon = data.isPower ? "fa-magic" : "fa-fist-raised"; const label = game.i18n.localize("SW5E.AbilityUse" + (data.isPower ? "Cast" : "Use")); - return new Promise((resolve) => { + return new Promise(resolve => { const dlg = new this(item, { title: `${item.name}: Usage Configuration`, content: html, @@ -87,14 +87,13 @@ export default class AbilityUseDialog extends Dialog { * @private */ static _getPowerData(actorData, itemData, data) { - // Determine whether the power may be up-cast 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 (!consumePowerSlot) { - mergeObject(data, { isPower: true, consumePowerSlot }); + mergeObject(data, {isPower: true, consumePowerSlot}); return; } @@ -102,74 +101,78 @@ export default class AbilityUseDialog extends Dialog { let lmax = 0; let points; let powerType; - switch (itemData.school){ + switch (itemData.school) { case "lgt": case "uni": case "drk": { - powerType = "force" + powerType = "force"; points = actorData.attributes.force.points.value + actorData.attributes.force.points.temp; break; } case "tec": { - powerType = "tech" + powerType = "tech"; points = actorData.attributes.tech.points.value + actorData.attributes.tech.points.temp; break; } } - + // eliminate point usage for innate casters - if (actorData.attributes.powercasting === 'innate') points = 999; + if (actorData.attributes.powercasting === "innate") points = 999; - - let powerLevels - if (powerType === "force"){ - powerLevels = Array.fromRange(10).reduce((arr, i) => { - if ( i < lvl ) return arr; - const label = CONFIG.SW5E.powerLevels[i]; - const l = actorData.powers["power"+i] || {fmax: 0, foverride: null}; - let max = parseInt(l.foverride || l.fmax || 0); - let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max); - if ( max > 0 ) lmax = i; - if ((max > 0) && (slots > 0) && (points > i)){ - arr.push({ - level: i, - label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label, - canCast: max > 0, - hasSlots: slots > 0 - }); - } - return arr; - }, []).filter(sl => sl.level <= lmax); - }else if (powerType === "tech"){ - powerLevels = Array.fromRange(10).reduce((arr, i) => { - if ( i < lvl ) return arr; - const label = CONFIG.SW5E.powerLevels[i]; - const l = actorData.powers["power"+i] || {tmax: 0, toverride: null}; - let max = parseInt(l.override || l.tmax || 0); - let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max); - if ( max > 0 ) lmax = i; - if ((max > 0) && (slots > 0) && (points > i)){ - arr.push({ - level: i, - label: i > 0 ? game.i18n.format('SW5E.PowerLevelSlot', {level: label, n: slots}) : label, - canCast: max > 0, - hasSlots: slots > 0 - }); - } - return arr; - }, []).filter(sl => sl.level <= lmax); + let powerLevels; + if (powerType === "force") { + powerLevels = Array.fromRange(10) + .reduce((arr, i) => { + if (i < lvl) return arr; + const label = CONFIG.SW5E.powerLevels[i]; + const l = actorData.powers["power" + i] || {fmax: 0, foverride: null}; + let max = parseInt(l.foverride || l.fmax || 0); + let slots = Math.clamped(parseInt(l.fvalue || 0), 0, max); + if (max > 0) lmax = i; + if (max > 0 && slots > 0 && points > i) { + arr.push({ + level: i, + label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label, + canCast: max > 0, + hasSlots: slots > 0 + }); + } + return arr; + }, []) + .filter(sl => sl.level <= lmax); + } else if (powerType === "tech") { + powerLevels = Array.fromRange(10) + .reduce((arr, i) => { + if (i < lvl) return arr; + const label = CONFIG.SW5E.powerLevels[i]; + const l = actorData.powers["power" + i] || {tmax: 0, toverride: null}; + let max = parseInt(l.override || l.tmax || 0); + let slots = Math.clamped(parseInt(l.tvalue || 0), 0, max); + if (max > 0) lmax = i; + if (max > 0 && slots > 0 && points > i) { + arr.push({ + level: i, + label: i > 0 ? game.i18n.format("SW5E.PowerLevelSlot", {level: label, n: slots}) : label, + canCast: max > 0, + hasSlots: slots > 0 + }); + } + return arr; + }, []) + .filter(sl => sl.level <= lmax); } - - const canCast = powerLevels.some(l => l.hasSlots); - if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", { - level: CONFIG.SW5E.powerLevels[lvl], - name: data.item.name - })); + if (!canCast) + data.errors.push( + game.i18n.format("SW5E.PowerCastNoSlots", { + level: CONFIG.SW5E.powerLevels[lvl], + name: data.item.name + }) + ); // Merge power casting data - return mergeObject(data, { isPower: true, consumePowerSlot, powerLevels }); + return mergeObject(data, {isPower: true, consumePowerSlot, powerLevels}); } /* -------------------------------------------- */ @@ -179,27 +182,26 @@ export default class AbilityUseDialog extends Dialog { * @private */ static _getAbilityUseNote(item, uses, recharge) { - // Zero quantity const quantity = item.data.quantity; - if ( quantity <= 0 ) return game.i18n.localize("SW5E.AbilityUseUnavailableHint"); + if (quantity <= 0) return game.i18n.localize("SW5E.AbilityUseUnavailableHint"); // Abilities which use Recharge - if ( !!recharge.value ) { + if (!!recharge.value) { return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", { - type: item.type, - }) + type: item.type + }); } // Does not use any resource - if ( !uses.per || !uses.max ) return ""; + if (!uses.per || !uses.max) return ""; // Consumables - if ( item.type === "consumable" ) { + if (item.type === "consumable") { let str = "SW5E.AbilityUseNormalHint"; - if ( uses.value > 1 ) str = "SW5E.AbilityUseConsumableChargeHint"; - else if ( item.data.quantity === 1 && uses.autoDestroy ) str = "SW5E.AbilityUseConsumableDestroyHint"; - else if ( item.data.quantity > 1 ) str = "SW5E.AbilityUseConsumableQuantityHint"; + if (uses.value > 1) str = "SW5E.AbilityUseConsumableChargeHint"; + else if (item.data.quantity === 1 && uses.autoDestroy) str = "SW5E.AbilityUseConsumableDestroyHint"; + else if (item.data.quantity > 1) str = "SW5E.AbilityUseConsumableQuantityHint"; return game.i18n.format(str, { type: item.data.consumableType, value: uses.value, @@ -222,7 +224,5 @@ export default class AbilityUseDialog extends Dialog { /* -------------------------------------------- */ - static _handleSubmit(formData, item) { - - } + static _handleSubmit(formData, item) {} } diff --git a/module/apps/actor-flags.js b/module/apps/actor-flags.js index 86293dab..1e82c570 100644 --- a/module/apps/actor-flags.js +++ b/module/apps/actor-flags.js @@ -7,7 +7,7 @@ export default class ActorSheetFlags extends BaseEntitySheet { const options = super.defaultOptions; return mergeObject(options, { id: "actor-flags", - classes: ["sw5e"], + classes: ["sw5e"], template: "systems/sw5e/templates/apps/actor-flags.html", width: 500, closeOnSubmit: true @@ -18,7 +18,7 @@ export default class ActorSheetFlags extends BaseEntitySheet { /** @override */ get title() { - return `${game.i18n.localize('SW5E.FlagsTitle')}: ${this.object.name}`; + return `${game.i18n.localize("SW5E.FlagsTitle")}: ${this.object.name}`; } /* -------------------------------------------- */ @@ -42,12 +42,12 @@ export default class ActorSheetFlags extends BaseEntitySheet { _getFlags() { const flags = {}; const baseData = this.entity._data; - for ( let [k, v] of Object.entries(CONFIG.SW5E.characterFlags) ) { - if ( !flags.hasOwnProperty(v.section) ) flags[v.section] = {}; + for (let [k, v] of Object.entries(CONFIG.SW5E.characterFlags)) { + if (!flags.hasOwnProperty(v.section)) flags[v.section] = {}; let flag = duplicate(v); flag.type = v.type.name; flag.isCheckbox = v.type === Boolean; - flag.isSelect = v.hasOwnProperty('choices'); + flag.isSelect = v.hasOwnProperty("choices"); flag.value = getProperty(baseData.flags, `sw5e.${k}`); flags[v.section][`flags.sw5e.${k}`] = flag; } @@ -80,7 +80,7 @@ export default class ActorSheetFlags extends BaseEntitySheet { {name: "data.bonuses.power.forceUnivDC", label: "SW5E.BonusForceUnivPowerDC"}, {name: "data.bonuses.power.techDC", label: "SW5E.BonusTechPowerDC"} ]; - for ( let b of bonuses ) { + for (let b of bonuses) { b.value = getProperty(this.object._data, b.name) || ""; } return bonuses; @@ -97,11 +97,11 @@ export default class ActorSheetFlags extends BaseEntitySheet { let unset = false; const flags = updateData.flags.sw5e; //clone flags to dnd5e for module compatability - updateData.flags.dnd5e = updateData.flags.sw5e - for ( let [k, v] of Object.entries(flags) ) { - if ( [undefined, null, "", false, 0].includes(v) ) { + updateData.flags.dnd5e = updateData.flags.sw5e; + for (let [k, v] of Object.entries(flags)) { + if ([undefined, null, "", false, 0].includes(v)) { delete flags[k]; - if ( hasProperty(actor._data.flags, `sw5e.${k}`) ) { + if (hasProperty(actor._data.flags, `sw5e.${k}`)) { unset = true; flags[`-=${k}`] = null; } @@ -109,8 +109,8 @@ export default class ActorSheetFlags extends BaseEntitySheet { } // Clear any bonuses which are whitespace only - for ( let b of Object.values(updateData.data.bonuses ) ) { - for ( let [k, v] of Object.entries(b) ) { + for (let b of Object.values(updateData.data.bonuses)) { + for (let [k, v] of Object.entries(b)) { b[k] = v.trim(); } } diff --git a/module/apps/long-rest.js b/module/apps/long-rest.js index ce7e90d4..7f9ab747 100644 --- a/module/apps/long-rest.js +++ b/module/apps/long-rest.js @@ -24,8 +24,8 @@ export default class LongRestDialog extends Dialog { getData() { const data = super.getData(); const variant = game.settings.get("sw5e", "restVariant"); - data.promptNewDay = variant !== "gritty"; // It's always a new day when resting 1 week - data.newDay = variant === "normal"; // It's probably a new day when resting normally (8 hours) + data.promptNewDay = variant !== "gritty"; // It's always a new day when resting 1 week + data.newDay = variant === "normal"; // It's probably a new day when resting normally (8 hours) return data; } @@ -37,7 +37,7 @@ export default class LongRestDialog extends Dialog { * @param {Actor5e} actor * @return {Promise} */ - static async longRestDialog({ actor } = {}) { + static async longRestDialog({actor} = {}) { return new Promise((resolve, reject) => { const dlg = new this(actor, { title: "Long Rest", @@ -49,8 +49,7 @@ export default class LongRestDialog extends Dialog { let newDay = false; if (game.settings.get("sw5e", "restVariant") === "normal") newDay = html.find('input[name="newDay"]')[0].checked; - else if(game.settings.get("sw5e", "restVariant") === "gritty") - newDay = true; + else if (game.settings.get("sw5e", "restVariant") === "gritty") newDay = true; resolve(newDay); } }, @@ -60,10 +59,10 @@ export default class LongRestDialog extends Dialog { callback: reject } }, - default: 'rest', + default: "rest", close: reject }); dlg.render(true); }); } -} \ No newline at end of file +} diff --git a/module/apps/movement-config.js b/module/apps/movement-config.js index a57de39c..635667ed 100644 --- a/module/apps/movement-config.js +++ b/module/apps/movement-config.js @@ -3,10 +3,9 @@ * @implements {BaseEntitySheet} */ export default class ActorMovementConfig extends BaseEntitySheet { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { classes: ["sw5e"], template: "systems/sw5e/templates/apps/movement-config.html", width: 300, @@ -28,9 +27,9 @@ export default class ActorMovementConfig extends BaseEntitySheet { const data = { movement: duplicate(this.entity._data.data.attributes.movement), units: CONFIG.SW5E.movementUnits - } - for ( let [k, v] of Object.entries(data.movement) ) { - if ( ["units", "hover"].includes(k) ) continue; + }; + for (let [k, v] of Object.entries(data.movement)) { + if (["units", "hover"].includes(k)) continue; data.movement[k] = Number.isNumeric(v) ? v.toNearest(0.1) : 0; } return data; diff --git a/module/apps/senses-config.js b/module/apps/senses-config.js index 71f99bf1..39e5e326 100644 --- a/module/apps/senses-config.js +++ b/module/apps/senses-config.js @@ -3,10 +3,9 @@ * @implements {BaseEntitySheet} */ export default class ActorSensesConfig extends BaseEntitySheet { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { classes: ["sw5e"], template: "systems/sw5e/templates/apps/senses-config.html", width: 300, @@ -29,14 +28,15 @@ export default class ActorSensesConfig extends BaseEntitySheet { const data = { senses: {}, 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]; data.senses[name] = { label: game.i18n.localize(label), value: Number.isNumeric(v) ? v.toNearest(0.1) : 0 - } + }; } return data; } diff --git a/module/apps/short-rest.js b/module/apps/short-rest.js index 6791cbac..a4d3725d 100644 --- a/module/apps/short-rest.js +++ b/module/apps/short-rest.js @@ -5,7 +5,7 @@ import LongRestDialog from "./long-rest.js"; * @extends {Dialog} */ export default class ShortRestDialog extends Dialog { - constructor(actor, dialogData={}, options={}) { + constructor(actor, dialogData = {}, options = {}) { super(dialogData, options); /** @@ -24,9 +24,9 @@ export default class ShortRestDialog extends Dialog { /* -------------------------------------------- */ /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - template: "systems/sw5e/templates/apps/short-rest.html", + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + template: "systems/sw5e/templates/apps/short-rest.html", classes: ["sw5e", "dialog"] }); } @@ -39,7 +39,7 @@ export default class ShortRestDialog extends Dialog { // Determine Hit Dice data.availableHD = this.actor.data.items.reduce((hd, item) => { - if ( item.type === "class" ) { + if (item.type === "class") { const d = item.data; const denom = d.hitDice || "d6"; const available = parseInt(d.levels || 1) - parseInt(d.hitDiceUsed || 0); @@ -52,14 +52,13 @@ export default class ShortRestDialog extends Dialog { // Determine rest type const variant = game.settings.get("sw5e", "restVariant"); - data.promptNewDay = variant !== "epic"; // It's never a new day when only resting 1 minute - data.newDay = false; // It may be a new day, but not by default + data.promptNewDay = variant !== "epic"; // It's never a new day when only resting 1 minute + data.newDay = false; // It may be a new day, but not by default return data; } /* -------------------------------------------- */ - /** @override */ activateListeners(html) { super.activateListeners(html); @@ -90,7 +89,7 @@ export default class ShortRestDialog extends Dialog { * @param {Actor5e} actor * @return {Promise} */ - static async shortRestDialog({actor}={}) { + static async shortRestDialog({actor} = {}) { return new Promise((resolve, reject) => { const dlg = new this(actor, { title: "Short Rest", @@ -126,8 +125,10 @@ export default class ShortRestDialog extends Dialog { * @param {Actor5e} actor * @return {Promise} */ - static async longRestDialog({actor}={}) { - console.warn("WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead."); + static async longRestDialog({actor} = {}) { + console.warn( + "WARNING! ShortRestDialog.longRestDialog has been deprecated, use LongRestDialog.longRestDialog instead." + ); return LongRestDialog.longRestDialog(...arguments); } } diff --git a/module/apps/trait-selector.js b/module/apps/trait-selector.js index 5e63d63d..848a4431 100644 --- a/module/apps/trait-selector.js +++ b/module/apps/trait-selector.js @@ -3,11 +3,10 @@ * @implements {FormApplication} */ export default class TraitSelector extends FormApplication { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - id: "trait-selector", + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + id: "trait-selector", classes: ["sw5e"], title: "Actor Trait Selection", template: "systems/sw5e/templates/apps/trait-selector.html", @@ -27,33 +26,32 @@ export default class TraitSelector extends FormApplication { * @type {String} */ get attribute() { - return this.options.name; + return this.options.name; } /* -------------------------------------------- */ /** @override */ getData() { - // Get current values let attr = getProperty(this.object._data, this.attribute); - if ( getType(attr) !== "Object" ) attr = {value: [], custom: ""}; + if (getType(attr) !== "Object") attr = {value: [], custom: ""}; - // Populate choices + // Populate choices const choices = duplicate(this.options.choices); - for ( let [k, v] of Object.entries(choices) ) { + for (let [k, v] of Object.entries(choices)) { choices[k] = { label: v, chosen: attr ? attr.value.includes(k) : false - } + }; } // Return data return { allowCustom: this.options.allowCustom, - choices: choices, + choices: choices, custom: attr ? attr.custom : "" - } + }; } /* -------------------------------------------- */ @@ -64,21 +62,21 @@ export default class TraitSelector extends FormApplication { // Obtain choices const chosen = []; - for ( let [k, v] of Object.entries(formData) ) { - if ( (k !== "custom") && v ) chosen.push(k); + for (let [k, v] of Object.entries(formData)) { + if (k !== "custom" && v) chosen.push(k); } updateData[`${this.attribute}.value`] = chosen; // Validate the number chosen - if ( this.options.minimum && (chosen.length < this.options.minimum) ) { + if (this.options.minimum && chosen.length < this.options.minimum) { return ui.notifications.error(`You must choose at least ${this.options.minimum} options`); } - if ( this.options.maximum && (chosen.length > this.options.maximum) ) { + if (this.options.maximum && chosen.length > this.options.maximum) { return ui.notifications.error(`You may choose no more than ${this.options.maximum} options`); } // Include custom - if ( this.options.allowCustom ) { + if (this.options.allowCustom) { updateData[`${this.attribute}.custom`] = formData.custom; } diff --git a/module/canvas.js b/module/canvas.js index 1767d4d8..c1535c32 100644 --- a/module/canvas.js +++ b/module/canvas.js @@ -1,6 +1,6 @@ /** @override */ -export const measureDistances = function(segments, options={}) { - if ( !options.gridSpaces ) return BaseGrid.prototype.measureDistances.call(this, segments, options); +export const measureDistances = function (segments, options = {}) { + if (!options.gridSpaces) return BaseGrid.prototype.measureDistances.call(this, segments, options); // Track the total number of diagonals let nDiagonal = 0; @@ -23,7 +23,7 @@ export const measureDistances = function(segments, options={}) { // Alternative DMG Movement if (rule === "5105") { 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; } @@ -44,9 +44,9 @@ export const measureDistances = function(segments, options={}) { * TODO: This should probably be replaced with a formal Token class extension */ const _TokenGetBarAttribute = Token.prototype.getBarAttribute; -export const getBarAttribute = function(...args) { +export const getBarAttribute = function (...args) { const data = _TokenGetBarAttribute.bind(this)(...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.max += parseInt(getProperty(this.actor.data, "data.attributes.hp.tempmax") || 0); } diff --git a/module/characterImporter.js b/module/characterImporter.js index e5af2c18..870ce233 100644 --- a/module/characterImporter.js +++ b/module/characterImporter.js @@ -1,16 +1,15 @@ export default class CharacterImporter { - // transform JSON from sw5e.com to Foundry friendly format // and insert new actor - static async transform(rawCharacter){ + static async transform(rawCharacter) { const sourceCharacter = JSON.parse(rawCharacter); //source character - + const details = { species: sourceCharacter.attribs.find(e => e.name == "race").current, background: sourceCharacter.attribs.find(e => e.name == "background").current, alignment: sourceCharacter.attribs.find(e => e.name == "alignment").current - } - + }; + const hp = { value: sourceCharacter.attribs.find(e => e.name == "hp").current, min: 0, @@ -25,28 +24,28 @@ export default class CharacterImporter { const abilities = { str: { value: sourceCharacter.attribs.find(e => e.name == "strength").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'strength_save_prof').current ? 1 : 0 + proficient: sourceCharacter.attribs.find(e => e.name == "strength_save_prof").current ? 1 : 0 }, dex: { value: sourceCharacter.attribs.find(e => e.name == "dexterity").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'dexterity_save_prof').current ? 1 : 0 + proficient: sourceCharacter.attribs.find(e => e.name == "dexterity_save_prof").current ? 1 : 0 }, con: { value: sourceCharacter.attribs.find(e => e.name == "constitution").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'constitution_save_prof').current ? 1 : 0 + proficient: sourceCharacter.attribs.find(e => e.name == "constitution_save_prof").current ? 1 : 0 }, int: { value: sourceCharacter.attribs.find(e => e.name == "intelligence").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'intelligence_save_prof').current ? 1 : 0 + proficient: sourceCharacter.attribs.find(e => e.name == "intelligence_save_prof").current ? 1 : 0 }, wis: { value: sourceCharacter.attribs.find(e => e.name == "wisdom").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'wisdom_save_prof').current ? 1 : 0 + proficient: sourceCharacter.attribs.find(e => e.name == "wisdom_save_prof").current ? 1 : 0 }, cha: { value: sourceCharacter.attribs.find(e => e.name == "charisma").current, - proficient: sourceCharacter.attribs.find(e => e.name == 'charisma_save_prof').current ? 1 : 0 - }, + proficient: sourceCharacter.attribs.find(e => e.name == "charisma_save_prof").current ? 1 : 0 + } }; const targetCharacter = { @@ -66,56 +65,59 @@ export default class CharacterImporter { const profession = sourceCharacter.attribs.find(e => e.name == "class").current; let professionLevel = sourceCharacter.attribs.find(e => e.name == "class_display").current; - professionLevel = parseInt( professionLevel.replace(/[^0-9]/g,'') ); //remove a-z, leaving only integers + professionLevel = parseInt(professionLevel.replace(/[^0-9]/g, "")); //remove a-z, leaving only integers CharacterImporter.addClasses(profession, professionLevel, actor); } - static async addClasses(profession, level, actor){ - let classes = await game.packs.get('sw5e.classes').getContent(); - let assignedClass = classes.find( c => c.name === profession ); + static async addClasses(profession, level, actor) { + let classes = await game.packs.get("sw5e.classes").getContent(); + let assignedClass = classes.find(c => c.name === profession); assignedClass.data.data.levels = level; - await actor.createEmbeddedEntity("OwnedItem", assignedClass.data, { displaySheet: false }); + await actor.createEmbeddedEntity("OwnedItem", assignedClass.data, {displaySheet: false}); } - static addImportButton(html){ + static addImportButton(html) { const header = $("#actors").find("header.directory-header"); const search = $("#actors").children().find("div.header-search"); const newImportButtonDiv = $("#actors").children().find("div.header-actions").clone(); const newSearch = search.clone(); search.remove(); - newImportButtonDiv.attr('id', 'character-sheet-import'); + newImportButtonDiv.attr("id", "character-sheet-import"); header.append(newImportButtonDiv); newImportButtonDiv.children("button").remove(); - newImportButtonDiv.append(""); + newImportButtonDiv.append( + "" + ); newSearch.appendTo(header); let characterImportButton = $("#cs-import-button"); - characterImportButton.click(ev => { - let content = '

Saved Character JSON Import

' - + ' ' - + '
' - + ''; + characterImportButton.click(ev => { + let content = + "

Saved Character JSON Import

" + + ' ' + + "
" + + ''; let importDialog = new Dialog({ title: "Import Character from SW5e.com", content: content, buttons: { - "Import": { + Import: { icon: '', label: "Import Character", - callback: (e) => { - let characterData = $('#character-json').val(); - console.log('Parsing Character JSON'); + callback: e => { + let characterData = $("#character-json").val(); + console.log("Parsing Character JSON"); CharacterImporter.transform(characterData); - } + } }, - "Cancel": { + Cancel: { icon: '', label: "Cancel", - callback: () => {}, + callback: () => {} } } - }) + }); importDialog.render(true); }); - } -} \ No newline at end of file + } +} diff --git a/module/chat.js b/module/chat.js index f8ab035f..942ff984 100644 --- a/module/chat.js +++ b/module/chat.js @@ -1,28 +1,27 @@ - /** * Highlight critical success or failure on d20 rolls */ -export const highlightCriticalSuccessFailure = function(message, html, data) { - if ( !message.isRoll || !message.isContentVisible ) return; +export const highlightCriticalSuccessFailure = function (message, html, data) { + if (!message.isRoll || !message.isContentVisible) return; // Highlight rolls where the first part is a d20 roll const roll = message.roll; - if ( !roll.dice.length ) return; + if (!roll.dice.length) return; const d = roll.dice[0]; // Ensure it is an un-modified d20 roll - const isD20 = (d.faces === 20) && ( d.values.length === 1 ); - if ( !isD20 ) return; - const isModifiedRoll = ("success" in d.results[0]) || d.options.marginSuccess || d.options.marginFailure; - if ( isModifiedRoll ) return; + const isD20 = d.faces === 20 && d.values.length === 1; + if (!isD20) return; + const isModifiedRoll = "success" in d.results[0] || d.options.marginSuccess || d.options.marginFailure; + if (isModifiedRoll) return; // Highlight successes and failures const critical = d.options.critical || 20; const fumble = d.options.fumble || 1; - if ( d.total >= critical ) html.find(".dice-total").addClass("critical"); - else if ( d.total <= fumble ) html.find(".dice-total").addClass("fumble"); - else if ( d.options.target ) { - if ( roll.total >= d.options.target ) html.find(".dice-total").addClass("success"); + if (d.total >= critical) html.find(".dice-total").addClass("critical"); + else if (d.total <= fumble) html.find(".dice-total").addClass("fumble"); + else if (d.options.target) { + if (roll.total >= d.options.target) html.find(".dice-total").addClass("success"); else html.find(".dice-total").addClass("failure"); } }; @@ -32,22 +31,22 @@ export const highlightCriticalSuccessFailure = function(message, html, data) { /** * Optionally hide the display of chat card action buttons which cannot be performed by the user */ -export const displayChatActionButtons = function(message, html, data) { +export const displayChatActionButtons = function (message, html, data) { const chatCard = html.find(".sw5e.chat-card"); - if ( chatCard.length > 0 ) { + if (chatCard.length > 0) { const flavor = html.find(".flavor-text"); - if ( flavor.text() === html.find(".item-name").text() ) flavor.remove(); + if (flavor.text() === html.find(".item-name").text()) flavor.remove(); // If the user is the message author or the actor owner, proceed let actor = game.actors.get(data.message.speaker.actor); - if ( actor && actor.owner ) return; - else if ( game.user.isGM || (data.author.id === game.user.id)) return; + if (actor && actor.owner) return; + else if (game.user.isGM || data.author.id === game.user.id) return; // Otherwise conceal action buttons except for saving throw const buttons = chatCard.find("button[data-action]"); buttons.each((i, btn) => { - if ( btn.dataset.action === "save" ) return; - btn.style.display = "none" + if (btn.dataset.action === "save") return; + btn.style.display = "none"; }); } }; @@ -63,7 +62,7 @@ export const displayChatActionButtons = function(message, html, data) { * * @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 => { const message = game.messages.get(li.data("messageId")); return message?.isRoll && message?.isContentVisible && canvas?.tokens.controlled.length; @@ -110,10 +109,12 @@ export const addChatMessageContextOptions = function(html, options) { function applyChatCardDamage(li, multiplier) { const message = game.messages.get(li.data("messageId")); const roll = message.roll; - return Promise.all(canvas.tokens.controlled.map(t => { - const a = t.actor; - return a.applyDamage(roll.total, multiplier); - })); + return Promise.all( + canvas.tokens.controlled.map(t => { + const a = t.actor; + return a.applyDamage(roll.total, multiplier); + }) + ); } /* -------------------------------------------- */ diff --git a/module/classFeatures.js b/module/classFeatures.js index 7946c252..17155d10 100644 --- a/module/classFeatures.js +++ b/module/classFeatures.js @@ -1,4 +1 @@ -export const ClassFeatures = { - -}; - +export const ClassFeatures = {}; diff --git a/module/combat.js b/module/combat.js index 8132a5ac..07f383c1 100644 --- a/module/combat.js +++ b/module/combat.js @@ -1,13 +1,12 @@ - /** * Override the default Initiative formula to customize special behaviors of the SW5e system. * Apply advantage, proficiency, or bonuses where appropriate * Apply the dexterity score as a decimal tiebreaker if requested * See Combat._getInitiativeFormula for more detail. */ -export const _getInitiativeFormula = function(combatant) { +export const _getInitiativeFormula = function (combatant) { const actor = combatant.actor; - if ( !actor ) return "1d20"; + if (!actor) return "1d20"; const init = actor.data.data.attributes.init; let nd = 1; @@ -19,10 +18,10 @@ export const _getInitiativeFormula = function(combatant) { 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 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(" + "); }; diff --git a/module/config.js b/module/config.js index 30cee69a..ede0ddf8 100644 --- a/module/config.js +++ b/module/config.js @@ -1,4 +1,4 @@ -import {ClassFeatures} from "./classFeatures.js" +import {ClassFeatures} from "./classFeatures.js"; // Namespace SW5e Configuration Values export const SW5E = {}; @@ -12,27 +12,26 @@ SW5E.ASCII = ` \\______ / \\__/\\ //______ /\\__ > \\/ \\/ \\/ \\/ `; - /** * The set of Ability Scores used within the system * @type {Object} */ SW5E.abilities = { - "str": "SW5E.AbilityStr", - "dex": "SW5E.AbilityDex", - "con": "SW5E.AbilityCon", - "int": "SW5E.AbilityInt", - "wis": "SW5E.AbilityWis", - "cha": "SW5E.AbilityCha" + str: "SW5E.AbilityStr", + dex: "SW5E.AbilityDex", + con: "SW5E.AbilityCon", + int: "SW5E.AbilityInt", + wis: "SW5E.AbilityWis", + cha: "SW5E.AbilityCha" }; SW5E.abilityAbbreviations = { - "str": "SW5E.AbilityStrAbbr", - "dex": "SW5E.AbilityDexAbbr", - "con": "SW5E.AbilityConAbbr", - "int": "SW5E.AbilityIntAbbr", - "wis": "SW5E.AbilityWisAbbr", - "cha": "SW5E.AbilityChaAbbr" + str: "SW5E.AbilityStrAbbr", + dex: "SW5E.AbilityDexAbbr", + con: "SW5E.AbilityConAbbr", + int: "SW5E.AbilityIntAbbr", + wis: "SW5E.AbilityWisAbbr", + cha: "SW5E.AbilityChaAbbr" }; /* -------------------------------------------- */ @@ -42,15 +41,15 @@ SW5E.abilityAbbreviations = { * @type {Object} */ SW5E.alignments = { - 'll': "SW5E.AlignmentLL", - 'nl': "SW5E.AlignmentNL", - 'cl': "SW5E.AlignmentCL", - 'lb': "SW5E.AlignmentLB", - 'bn': "SW5E.AlignmentBN", - 'cb': "SW5E.AlignmentCB", - 'ld': "SW5E.AlignmentLD", - 'nd': "SW5E.AlignmentND", - 'cd': "SW5E.AlignmentCD" + ll: "SW5E.AlignmentLL", + nl: "SW5E.AlignmentNL", + cl: "SW5E.AlignmentCL", + lb: "SW5E.AlignmentLB", + bn: "SW5E.AlignmentBN", + cb: "SW5E.AlignmentCB", + ld: "SW5E.AlignmentLD", + nd: "SW5E.AlignmentND", + cd: "SW5E.AlignmentCD" }; /* -------------------------------------------- */ @@ -62,8 +61,8 @@ SW5E.alignments = { SW5E.attunementTypes = { NONE: 0, REQUIRED: 1, - ATTUNED: 2, -} + ATTUNED: 2 +}; /** * An enumeration of item attunement states @@ -77,46 +76,44 @@ SW5E.attunements = { /* -------------------------------------------- */ - SW5E.weaponProficiencies = { - "sim": "SW5E.WeaponSimpleProficiency", - "mar": "SW5E.WeaponMartialProficiency" + sim: "SW5E.WeaponSimpleProficiency", + mar: "SW5E.WeaponMartialProficiency" }; SW5E.toolProficiencies = { - "armor": "SW5E.ToolArmormech", - "arms": "SW5E.ToolArmstech", - "arti": "SW5E.ToolArtificer", - "art": "SW5E.ToolArtist", - "astro": "SW5E.ToolAstrotech", - "bio": "SW5E.ToolBiotech", - "con": "SW5E.ToolConstructor", - "cyb": "SW5E.ToolCybertech", - "jew": "SW5E.ToolJeweler", - "sur": "SW5E.ToolSurveyor", - "syn": "SW5E.ToolSynthweaver", - "tin": "SW5E.ToolTinker", - "ant": "SW5E.ToolAntitoxkit", - "arc": "SW5E.ToolArchaeologistKit", - "aud": "SW5E.ToolAudiotechKit", - "bioa": "SW5E.ToolBioanalysisKit", - "brew": "SW5E.ToolBrewerKit", - "chef": "SW5E.ToolChefKit", - "demo": "SW5E.ToolDemolitionKit", - "disg": "SW5E.ToolDisguiseKit", - "forg": "SW5E.ToolForgeryKit", - "mech": "SW5E.ToolMechanicKit", - "game": "SW5E.ToolGamingSet", - "poi": "SW5E.ToolPoisonKit", - "scav": "SW5E.ToolScavengingKit", - "secur": "SW5E.ToolSecurityKit", - "slic": "SW5E.ToolSlicerKit", - "spice": "SW5E.ToolSpiceKit", - "music": "SW5E.ToolMusicalInstrument", - "vehicle": "SW5E.ToolVehicle" + armor: "SW5E.ToolArmormech", + arms: "SW5E.ToolArmstech", + arti: "SW5E.ToolArtificer", + art: "SW5E.ToolArtist", + astro: "SW5E.ToolAstrotech", + bio: "SW5E.ToolBiotech", + con: "SW5E.ToolConstructor", + cyb: "SW5E.ToolCybertech", + jew: "SW5E.ToolJeweler", + sur: "SW5E.ToolSurveyor", + syn: "SW5E.ToolSynthweaver", + tin: "SW5E.ToolTinker", + ant: "SW5E.ToolAntitoxkit", + arc: "SW5E.ToolArchaeologistKit", + aud: "SW5E.ToolAudiotechKit", + bioa: "SW5E.ToolBioanalysisKit", + brew: "SW5E.ToolBrewerKit", + chef: "SW5E.ToolChefKit", + demo: "SW5E.ToolDemolitionKit", + disg: "SW5E.ToolDisguiseKit", + forg: "SW5E.ToolForgeryKit", + mech: "SW5E.ToolMechanicKit", + game: "SW5E.ToolGamingSet", + poi: "SW5E.ToolPoisonKit", + scav: "SW5E.ToolScavengingKit", + secur: "SW5E.ToolSecurityKit", + slic: "SW5E.ToolSlicerKit", + spice: "SW5E.ToolSpiceKit", + music: "SW5E.ToolMusicalInstrument", + vehicle: "SW5E.ToolVehicle" }; - /* -------------------------------------------- */ /** @@ -124,19 +121,18 @@ SW5E.toolProficiencies = { * @type {Object} */ SW5E.timePeriods = { - "inst": "SW5E.TimeInst", - "turn": "SW5E.TimeTurn", - "round": "SW5E.TimeRound", - "minute": "SW5E.TimeMinute", - "hour": "SW5E.TimeHour", - "day": "SW5E.TimeDay", - "month": "SW5E.TimeMonth", - "year": "SW5E.TimeYear", - "perm": "SW5E.TimePerm", - "spec": "SW5E.Special" + inst: "SW5E.TimeInst", + turn: "SW5E.TimeTurn", + round: "SW5E.TimeRound", + minute: "SW5E.TimeMinute", + hour: "SW5E.TimeHour", + day: "SW5E.TimeDay", + month: "SW5E.TimeMonth", + year: "SW5E.TimeYear", + perm: "SW5E.TimePerm", + spec: "SW5E.Special" }; - /* -------------------------------------------- */ /** @@ -144,49 +140,47 @@ SW5E.timePeriods = { * @type {Object} */ SW5E.abilityActivationTypes = { - "none": "SW5E.None", - "action": "SW5E.Action", - "bonus": "SW5E.BonusAction", - "reaction": "SW5E.Reaction", - "minute": SW5E.timePeriods.minute, - "hour": SW5E.timePeriods.hour, - "day": SW5E.timePeriods.day, - "special": SW5E.timePeriods.spec, - "legendary": "SW5E.LegAct", - "lair": "SW5E.LairAct", - "crew": "SW5E.VehicleCrewAction" + none: "SW5E.None", + action: "SW5E.Action", + bonus: "SW5E.BonusAction", + reaction: "SW5E.Reaction", + minute: SW5E.timePeriods.minute, + hour: SW5E.timePeriods.hour, + day: SW5E.timePeriods.day, + special: SW5E.timePeriods.spec, + legendary: "SW5E.LegAct", + lair: "SW5E.LairAct", + crew: "SW5E.VehicleCrewAction" }; /* -------------------------------------------- */ - SW5E.abilityConsumptionTypes = { - "ammo": "SW5E.ConsumeAmmunition", - "attribute": "SW5E.ConsumeAttribute", - "material": "SW5E.ConsumeMaterial", - "charges": "SW5E.ConsumeCharges" + ammo: "SW5E.ConsumeAmmunition", + attribute: "SW5E.ConsumeAttribute", + material: "SW5E.ConsumeMaterial", + charges: "SW5E.ConsumeCharges" }; - /* -------------------------------------------- */ // Creature Sizes SW5E.actorSizes = { - "tiny": "SW5E.SizeTiny", - "sm": "SW5E.SizeSmall", - "med": "SW5E.SizeMedium", - "lg": "SW5E.SizeLarge", - "huge": "SW5E.SizeHuge", - "grg": "SW5E.SizeGargantuan" + tiny: "SW5E.SizeTiny", + sm: "SW5E.SizeSmall", + med: "SW5E.SizeMedium", + lg: "SW5E.SizeLarge", + huge: "SW5E.SizeHuge", + grg: "SW5E.SizeGargantuan" }; SW5E.tokenSizes = { - "tiny": 1, - "sm": 1, - "med": 1, - "lg": 2, - "huge": 3, - "grg": 4 + tiny: 1, + sm: 1, + med: 1, + lg: 2, + huge: 3, + grg: 4 }; /* -------------------------------------------- */ @@ -196,22 +190,22 @@ SW5E.tokenSizes = { * @type {Object} */ SW5E.itemActionTypes = { - "mwak": "SW5E.ActionMWAK", - "rwak": "SW5E.ActionRWAK", - "mpak": "SW5E.ActionMPAK", - "rpak": "SW5E.ActionRPAK", - "save": "SW5E.ActionSave", - "heal": "SW5E.ActionHeal", - "abil": "SW5E.ActionAbil", - "util": "SW5E.ActionUtil", - "other": "SW5E.ActionOther" + mwak: "SW5E.ActionMWAK", + rwak: "SW5E.ActionRWAK", + mpak: "SW5E.ActionMPAK", + rpak: "SW5E.ActionRPAK", + save: "SW5E.ActionSave", + heal: "SW5E.ActionHeal", + abil: "SW5E.ActionAbil", + util: "SW5E.ActionUtil", + other: "SW5E.ActionOther" }; /* -------------------------------------------- */ SW5E.itemCapacityTypes = { - "items": "SW5E.ItemContainerCapacityItems", - "weight": "SW5E.ItemContainerCapacityWeight" + items: "SW5E.ItemContainerCapacityItems", + weight: "SW5E.ItemContainerCapacityWeight" }; /* -------------------------------------------- */ @@ -221,13 +215,12 @@ SW5E.itemCapacityTypes = { * @type {Object} */ SW5E.limitedUsePeriods = { - "sr": "SW5E.ShortRest", - "lr": "SW5E.LongRest", - "day": "SW5E.Day", - "charges": "SW5E.Charges" + sr: "SW5E.ShortRest", + lr: "SW5E.LongRest", + day: "SW5E.Day", + charges: "SW5E.Charges" }; - /* -------------------------------------------- */ /** @@ -235,18 +228,17 @@ SW5E.limitedUsePeriods = { * @type {Object} */ SW5E.equipmentTypes = { - "light": "SW5E.EquipmentLight", - "medium": "SW5E.EquipmentMedium", - "heavy": "SW5E.EquipmentHeavy", - "bonus": "SW5E.EquipmentBonus", - "natural": "SW5E.EquipmentNatural", - "shield": "SW5E.EquipmentShield", - "clothing": "SW5E.EquipmentClothing", - "trinket": "SW5E.EquipmentTrinket", - "vehicle": "SW5E.EquipmentVehicle" + light: "SW5E.EquipmentLight", + medium: "SW5E.EquipmentMedium", + heavy: "SW5E.EquipmentHeavy", + bonus: "SW5E.EquipmentBonus", + natural: "SW5E.EquipmentNatural", + shield: "SW5E.EquipmentShield", + clothing: "SW5E.EquipmentClothing", + trinket: "SW5E.EquipmentTrinket", + vehicle: "SW5E.EquipmentVehicle" }; - /* -------------------------------------------- */ /** @@ -254,13 +246,12 @@ SW5E.equipmentTypes = { * @type {Object} */ SW5E.armorProficiencies = { - "lgt": SW5E.equipmentTypes.light, - "med": SW5E.equipmentTypes.medium, - "hvy": SW5E.equipmentTypes.heavy, - "shl": "SW5E.EquipmentShieldProficiency" + lgt: SW5E.equipmentTypes.light, + med: SW5E.equipmentTypes.medium, + hvy: SW5E.equipmentTypes.heavy, + shl: "SW5E.EquipmentShieldProficiency" }; - /* -------------------------------------------- */ /** @@ -268,16 +259,16 @@ SW5E.armorProficiencies = { * @type {Object} */ SW5E.consumableTypes = { - "adrenal": "SW5E.ConsumableAdrenal", - "poison": "SW5E.ConsumablePoison", - "explosive": "SW5E.ConsumableExplosive", - "food": "SW5E.ConsumableFood", - "medpac": "SW5E.ConsumableMedpac", - "technology": "SW5E.ConsumableTechnology", - "ammo": "SW5E.ConsumableAmmunition", - "trinket": "SW5E.ConsumableTrinket", - "force": "SW5E.ConsumableForce", - "tech": "SW5E.ConsumableTech" + adrenal: "SW5E.ConsumableAdrenal", + poison: "SW5E.ConsumablePoison", + explosive: "SW5E.ConsumableExplosive", + food: "SW5E.ConsumableFood", + medpac: "SW5E.ConsumableMedpac", + technology: "SW5E.ConsumableTechnology", + ammo: "SW5E.ConsumableAmmunition", + trinket: "SW5E.ConsumableTrinket", + force: "SW5E.ConsumableForce", + tech: "SW5E.ConsumableTech" }; /* -------------------------------------------- */ @@ -287,26 +278,25 @@ SW5E.consumableTypes = { * @type {Object} */ SW5E.currencies = { - "CR": "SW5E.CurrencyCR", - }; + CR: "SW5E.CurrencyCR" +}; /* -------------------------------------------- */ - // Damage Types SW5E.damageTypes = { - "acid": "SW5E.DamageAcid", - "cold": "SW5E.DamageCold", - "energy": "SW5E.DamageEnergy", - "fire": "SW5E.DamageFire", - "force": "SW5E.DamageForce", - "ion": "SW5E.DamageIon", - "kinetic": "SW5E.DamageKinetic", - "lightning": "SW5E.DamageLightning", - "necrotic": "SW5E.DamageNecrotic", - "poison": "SW5E.DamagePoison", - "psychic": "SW5E.DamagePsychic", - "sonic": "SW5E.DamageSonic" + acid: "SW5E.DamageAcid", + cold: "SW5E.DamageCold", + energy: "SW5E.DamageEnergy", + fire: "SW5E.DamageFire", + force: "SW5E.DamageForce", + ion: "SW5E.DamageIon", + kinetic: "SW5E.DamageKinetic", + lightning: "SW5E.DamageLightning", + necrotic: "SW5E.DamageNecrotic", + poison: "SW5E.DamagePoison", + psychic: "SW5E.DamagePsychic", + sonic: "SW5E.DamageSonic" }; // Damage Resistance Types @@ -314,39 +304,38 @@ SW5E.damageResistanceTypes = duplicate(SW5E.damageTypes); /* -------------------------------------------- */ - // armor Types SW5E.armorPropertiesTypes = { -"Absorptive": "SW5E.ArmorProperAbsorptive", -"Agile": "SW5E.ArmorProperAgile", -"Anchor": "SW5E.ArmorProperAnchor", -"Avoidant": "SW5E.ArmorProperAvoidant", -"Barbed": "SW5E.ArmorProperBarbed", -"Bulky": "SW5E.ArmorProperBulky", -"Charging": "SW5E.ArmorProperCharging", -"Concealing": "SW5E.ArmorProperConcealing", -"Cumbersome": "SW5E.ArmorProperCumbersome", -"Gauntleted": "SW5E.ArmorProperGauntleted", -"Imbalanced": "SW5E.ArmorProperImbalanced", -"Impermeable": "SW5E.ArmorProperImpermeable", -"Insulated": "SW5E.ArmorProperInsulated", -"Interlocking": "SW5E.ArmorProperInterlocking", -"Lambent": "SW5E.ArmorProperLambent", -"Lightweight": "SW5E.ArmorProperLightweight", -"Magnetic": "SW5E.ArmorProperMagnetic", -"Obscured": "SW5E.ArmorProperObscured", -"Obtrusive": "SW5E.ArmorProperObtrusive", -"Powered": "SW5E.ArmorProperPowered", -"Reactive": "SW5E.ArmorProperReactive", -"Regulated": "SW5E.ArmorProperRegulated", -"Reinforced": "SW5E.ArmorProperReinforced", -"Responsive": "SW5E.ArmorProperResponsive", -"Rigid": "SW5E.ArmorProperRigid", -"Silent": "SW5E.ArmorProperSilent", -"Spiked": "SW5E.ArmorProperSpiked", -"Strength": "SW5E.ArmorProperStrength", -"Steadfast": "SW5E.ArmorProperSteadfast", -"Versatile": "SW5E.ArmorProperVersatile" + Absorptive: "SW5E.ArmorProperAbsorptive", + Agile: "SW5E.ArmorProperAgile", + Anchor: "SW5E.ArmorProperAnchor", + Avoidant: "SW5E.ArmorProperAvoidant", + Barbed: "SW5E.ArmorProperBarbed", + Bulky: "SW5E.ArmorProperBulky", + Charging: "SW5E.ArmorProperCharging", + Concealing: "SW5E.ArmorProperConcealing", + Cumbersome: "SW5E.ArmorProperCumbersome", + Gauntleted: "SW5E.ArmorProperGauntleted", + Imbalanced: "SW5E.ArmorProperImbalanced", + Impermeable: "SW5E.ArmorProperImpermeable", + Insulated: "SW5E.ArmorProperInsulated", + Interlocking: "SW5E.ArmorProperInterlocking", + Lambent: "SW5E.ArmorProperLambent", + Lightweight: "SW5E.ArmorProperLightweight", + Magnetic: "SW5E.ArmorProperMagnetic", + Obscured: "SW5E.ArmorProperObscured", + Obtrusive: "SW5E.ArmorProperObtrusive", + Powered: "SW5E.ArmorProperPowered", + Reactive: "SW5E.ArmorProperReactive", + Regulated: "SW5E.ArmorProperRegulated", + Reinforced: "SW5E.ArmorProperReinforced", + Responsive: "SW5E.ArmorProperResponsive", + Rigid: "SW5E.ArmorProperRigid", + Silent: "SW5E.ArmorProperSilent", + Spiked: "SW5E.ArmorProperSpiked", + Strength: "SW5E.ArmorProperStrength", + Steadfast: "SW5E.ArmorProperSteadfast", + Versatile: "SW5E.ArmorProperVersatile" }; /** @@ -355,12 +344,12 @@ SW5E.armorPropertiesTypes = { * @type {Object} */ SW5E.movementTypes = { - "burrow": "SW5E.MovementBurrow", - "climb": "SW5E.MovementClimb", - "fly": "SW5E.MovementFly", - "swim": "SW5E.MovementSwim", - "walk": "SW5E.MovementWalk", -} + burrow: "SW5E.MovementBurrow", + climb: "SW5E.MovementClimb", + fly: "SW5E.MovementFly", + swim: "SW5E.MovementSwim", + walk: "SW5E.MovementWalk" +}; /** * The valid units of measure for movement distances in the game system. @@ -368,9 +357,9 @@ SW5E.movementTypes = { * @type {Object} */ SW5E.movementUnits = { - "ft": "SW5E.DistFt", - "mi": "SW5E.DistMi" -} + ft: "SW5E.DistFt", + mi: "SW5E.DistMi" +}; /** * The valid units of measure for the range of an action or effect. @@ -378,19 +367,18 @@ SW5E.movementUnits = { * @type {Object} */ SW5E.distanceUnits = { - "none": "SW5E.None", - "self": "SW5E.DistSelf", - "touch": "SW5E.DistTouch", - "spec": "SW5E.Special", - "any": "SW5E.DistAny" + none: "SW5E.None", + self: "SW5E.DistSelf", + touch: "SW5E.DistTouch", + spec: "SW5E.Special", + any: "SW5E.DistAny" }; -for ( let [k, v] of Object.entries(SW5E.movementUnits) ) { +for (let [k, v] of Object.entries(SW5E.movementUnits)) { SW5E.distanceUnits[k] = v; } /* -------------------------------------------- */ - /** * Configure aspects of encumbrance calculation so that it could be configured by modules * @type {Object} @@ -408,29 +396,27 @@ SW5E.encumbrance = { * @type {Object} */ SW5E.targetTypes = { - "none": "SW5E.None", - "self": "SW5E.TargetSelf", - "creature": "SW5E.TargetCreature", - "droid": "SW5E.TargetDroid", - "ally": "SW5E.TargetAlly", - "enemy": "SW5E.TargetEnemy", - "object": "SW5E.TargetObject", - "space": "SW5E.TargetSpace", - "radius": "SW5E.TargetRadius", - "sphere": "SW5E.TargetSphere", - "cylinder": "SW5E.TargetCylinder", - "cone": "SW5E.TargetCone", - "square": "SW5E.TargetSquare", - "cube": "SW5E.TargetCube", - "line": "SW5E.TargetLine", - "wall": "SW5E.TargetWall", - "weapon": "SW5E.TargetWeapon" + none: "SW5E.None", + self: "SW5E.TargetSelf", + creature: "SW5E.TargetCreature", + droid: "SW5E.TargetDroid", + ally: "SW5E.TargetAlly", + enemy: "SW5E.TargetEnemy", + object: "SW5E.TargetObject", + space: "SW5E.TargetSpace", + radius: "SW5E.TargetRadius", + sphere: "SW5E.TargetSphere", + cylinder: "SW5E.TargetCylinder", + cone: "SW5E.TargetCone", + square: "SW5E.TargetSquare", + cube: "SW5E.TargetCube", + line: "SW5E.TargetLine", + wall: "SW5E.TargetWall", + weapon: "SW5E.TargetWeapon" }; - /* -------------------------------------------- */ - /** * Map the subset of target types which produce a template area of effect * The keys are SW5E target types and the values are MeasuredTemplate shape types @@ -447,26 +433,22 @@ SW5E.areaTargetTypes = { wall: "ray" }; - /* -------------------------------------------- */ // Healing Types SW5E.healingTypes = { - "healing": "SW5E.Healing", - "temphp": "SW5E.HealingTemp" + healing: "SW5E.Healing", + temphp: "SW5E.HealingTemp" }; - /* -------------------------------------------- */ - /** * Enumerate the denominations of hit dice which can apply to classes in the SW5E system * @type {Array.} */ SW5E.hitDieTypes = ["d4", "d6", "d8", "d10", "d12", "d20"]; - /* -------------------------------------------- */ /** @@ -474,10 +456,10 @@ SW5E.hitDieTypes = ["d4", "d6", "d8", "d10", "d12", "d20"]; * @type {object} */ SW5E.senses = { - "blindsight": "SW5E.SenseBlindsight", - "darkvision": "SW5E.SenseDarkvision", - "tremorsense": "SW5E.SenseTremorsense", - "truesight": "SW5E.SenseTruesight" + blindsight: "SW5E.SenseBlindsight", + darkvision: "SW5E.SenseDarkvision", + tremorsense: "SW5E.SenseTremorsense", + truesight: "SW5E.SenseTruesight" }; /* -------------------------------------------- */ @@ -487,34 +469,33 @@ SW5E.senses = { * @type {Object} */ SW5E.skills = { - "acr": "SW5E.SkillAcr", - "ani": "SW5E.SkillAni", - "ath": "SW5E.SkillAth", - "dec": "SW5E.SkillDec", - "ins": "SW5E.SkillIns", - "itm": "SW5E.SkillItm", - "inv": "SW5E.SkillInv", - "lor": "SW5E.SkillLor", - "med": "SW5E.SkillMed", - "nat": "SW5E.SkillNat", - "prc": "SW5E.SkillPrc", - "prf": "SW5E.SkillPrf", - "per": "SW5E.SkillPer", - "pil": "SW5E.SkillPil", - "slt": "SW5E.SkillSlt", - "ste": "SW5E.SkillSte", - "sur": "SW5E.SkillSur", - "tec": "SW5E.SkillTec" + acr: "SW5E.SkillAcr", + ani: "SW5E.SkillAni", + ath: "SW5E.SkillAth", + dec: "SW5E.SkillDec", + ins: "SW5E.SkillIns", + itm: "SW5E.SkillItm", + inv: "SW5E.SkillInv", + lor: "SW5E.SkillLor", + med: "SW5E.SkillMed", + nat: "SW5E.SkillNat", + prc: "SW5E.SkillPrc", + prf: "SW5E.SkillPrf", + per: "SW5E.SkillPer", + pil: "SW5E.SkillPil", + slt: "SW5E.SkillSlt", + ste: "SW5E.SkillSte", + sur: "SW5E.SkillSur", + tec: "SW5E.SkillTec" }; - /* -------------------------------------------- */ SW5E.powerPreparationModes = { - "prepared": "SW5E.PowerPrepPrepared", - "always": "SW5E.PowerPrepAlways", - "atwill": "SW5E.PowerPrepAtWill", - "innate": "SW5E.PowerPrepInnate" + prepared: "SW5E.PowerPrepPrepared", + always: "SW5E.PowerPrepAlways", + atwill: "SW5E.PowerPrepAtWill", + innate: "SW5E.PowerPrepInnate" }; SW5E.powerUpcastModes = ["always", "prepared"]; @@ -525,12 +506,12 @@ SW5E.powerUpcastModes = ["always", "prepared"]; */ SW5E.powerProgression = { - "none": "SW5E.PowerNone", - "consular": "SW5E.PowerProgCns", - "engineer": "SW5E.PowerProgEng", - "guardian": "SW5E.PowerProgGrd", - "scout": "SW5E.PowerProgSct", - "sentinel": "SW5E.PowerProgSnt" + none: "SW5E.PowerNone", + consular: "SW5E.PowerProgCns", + engineer: "SW5E.PowerProgEng", + guardian: "SW5E.PowerProgGrd", + scout: "SW5E.PowerProgSct", + sentinel: "SW5E.PowerProgSnt" }; /** @@ -538,12 +519,12 @@ SW5E.powerProgression = { */ SW5E.powersKnown = { - "none": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - "consular": [9,11,13,15,17,19,21,23,25,26,28,29,31,32,34,35,37,38,39,40], - "engineer": [6,7,9,10,12,13,15,16,18,19,21,22,23,24,25,26,27,28,29,30], - "guardian": [5,7,9,10,12,13,14,15,17,18,19,20,22,23,24,25,27,28,29,30], - "scout": [0,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23], - "sentinel": [7,9,11,13,15,17,18,19,21,22,24,25,26,28,29,30,32,33,34,35] + none: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + consular: [9, 11, 13, 15, 17, 19, 21, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 39, 40], + engineer: [6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], + guardian: [5, 7, 9, 10, 12, 13, 14, 15, 17, 18, 19, 20, 22, 23, 24, 25, 27, 28, 29, 30], + scout: [0, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], + sentinel: [7, 9, 11, 13, 15, 17, 18, 19, 21, 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 35] }; /** @@ -551,14 +532,14 @@ SW5E.powersKnown = { */ SW5E.powerLimit = { - "none": [0,0,0,0,0,0,0,0,0], - "consular": [1000,1000,1000,1000,1000,1,1,1,1], - "engineer": [1000,1000,1000,1000,1000,1,1,1,1], - "guardian": [1000,1000,1000,1000,1,0,0,0,0], - "scout": [1000,1000,1000,1,1,0,0,0,0], - "sentinel": [1000,1000,1000,1000,1,1,1,0,0], - "innate": [1000,1000,1000,1000,1000,1000,1000,1000,1000], - "dual": [1000,1000,1000,1000,1000,1,1,1,1] + none: [0, 0, 0, 0, 0, 0, 0, 0, 0], + consular: [1000, 1000, 1000, 1000, 1000, 1, 1, 1, 1], + engineer: [1000, 1000, 1000, 1000, 1000, 1, 1, 1, 1], + guardian: [1000, 1000, 1000, 1000, 1, 0, 0, 0, 0], + scout: [1000, 1000, 1000, 1, 1, 0, 0, 0, 0], + sentinel: [1000, 1000, 1000, 1000, 1, 1, 1, 0, 0], + innate: [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], + dual: [1000, 1000, 1000, 1000, 1000, 1, 1, 1, 1] }; /** @@ -566,15 +547,15 @@ SW5E.powerLimit = { */ SW5E.powerMaxLevel = { - "none": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - "consular": [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,9], - "engineer": [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,9], - "guardian": [1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5], - "scout": [0,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5], - "sentinel": [1,1,2,2,2,3,3,3,4,4,5,5,5,6,6,6,7,7,7,7], - "multi": [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,9], - "innate": [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,9], - "dual": [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,9] + none: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + consular: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9], + engineer: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9], + guardian: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], + scout: [0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], + sentinel: [1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7], + multi: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9], + innate: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9], + dual: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9] }; /** @@ -582,12 +563,12 @@ SW5E.powerMaxLevel = { */ SW5E.powerPoints = { - "none": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - "consular": [4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80], - "engineer": [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40], - "guardian": [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40], - "scout": [0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], - "sentinel": [3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60] + none: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + consular: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80], + engineer: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40], + guardian: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40], + scout: [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + sentinel: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60] }; /* -------------------------------------------- */ @@ -597,32 +578,30 @@ SW5E.powerPoints = { * @type {Object} */ SW5E.powerScalingModes = { - "none": "SW5E.PowerNone", - "atwill": "SW5E.PowerAtWill", - "level": "SW5E.PowerLevel" + none: "SW5E.PowerNone", + atwill: "SW5E.PowerAtWill", + level: "SW5E.PowerLevel" }; /* -------------------------------------------- */ - /** * Define the set of types which a weapon item can take * @type {Object} */ SW5E.weaponTypes = { - "simpleVW": "SW5E.WeaponSimpleVW", - "simpleB": "SW5E.WeaponSimpleB", - "simpleLW": "SW5E.WeaponSimpleLW", - "martialVW": "SW5E.WeaponMartialVW", - "martialB": "SW5E.WeaponMartialB", - "martialLW": "SW5E.WeaponMartialLW", - "natural": "SW5E.WeaponNatural", - "improv": "SW5E.WeaponImprov", - "ammo": "SW5E.WeaponAmmo", - "siege": "SW5E.WeaponSiege" + simpleVW: "SW5E.WeaponSimpleVW", + simpleB: "SW5E.WeaponSimpleB", + simpleLW: "SW5E.WeaponSimpleLW", + martialVW: "SW5E.WeaponMartialVW", + martialB: "SW5E.WeaponMartialB", + martialLW: "SW5E.WeaponMartialLW", + natural: "SW5E.WeaponNatural", + improv: "SW5E.WeaponImprov", + ammo: "SW5E.WeaponAmmo", + siege: "SW5E.WeaponSiege" }; - /* -------------------------------------------- */ /** @@ -630,56 +609,55 @@ SW5E.weaponTypes = { * @type {Object} */ SW5E.weaponProperties = { - "amm": "SW5E.WeaponPropertiesAmm", - "aut": "SW5E.WeaponPropertiesAut", - "bur": "SW5E.WeaponPropertiesBur", - "def": "SW5E.WeaponPropertiesDef", - "dex": "SW5E.WeaponPropertiesDex", - "dir": "SW5E.WeaponPropertiesDir", - "drm": "SW5E.WeaponPropertiesDrm", - "dgd": "SW5E.WeaponPropertiesDgd", - "dis": "SW5E.WeaponPropertiesDis", - "dpt": "SW5E.WeaponPropertiesDpt", - "dou": "SW5E.WeaponPropertiesDou", - "fin": "SW5E.WeaponPropertiesFin", - "fix": "SW5E.WeaponPropertiesFix", - "foc": "SW5E.WeaponPropertiesFoc", - "hvy": "SW5E.WeaponPropertiesHvy", - "hid": "SW5E.WeaponPropertiesHid", - "ken": "SW5E.WeaponPropertiesKen", - "lgt": "SW5E.WeaponPropertiesLgt", - "lum": "SW5E.WeaponPropertiesLum", - "mig": "SW5E.WeaponPropertiesMig", - "pic": "SW5E.WeaponPropertiesPic", - "rap": "SW5E.WeaponPropertiesRap", - "rch": "SW5E.WeaponPropertiesRch", - "rel": "SW5E.WeaponPropertiesRel", - "ret": "SW5E.WeaponPropertiesRet", - "shk": "SW5E.WeaponPropertiesShk", - "sil": "SW5E.WeaponPropertiesSil", - "spc": "SW5E.WeaponPropertiesSpc", - "str": "SW5E.WeaponPropertiesStr", - "thr": "SW5E.WeaponPropertiesThr", - "two": "SW5E.WeaponPropertiesTwo", - "ver": "SW5E.WeaponPropertiesVer", - "vic": "SW5E.WeaponPropertiesVic" + amm: "SW5E.WeaponPropertiesAmm", + aut: "SW5E.WeaponPropertiesAut", + bur: "SW5E.WeaponPropertiesBur", + def: "SW5E.WeaponPropertiesDef", + dex: "SW5E.WeaponPropertiesDex", + dir: "SW5E.WeaponPropertiesDir", + drm: "SW5E.WeaponPropertiesDrm", + dgd: "SW5E.WeaponPropertiesDgd", + dis: "SW5E.WeaponPropertiesDis", + dpt: "SW5E.WeaponPropertiesDpt", + dou: "SW5E.WeaponPropertiesDou", + fin: "SW5E.WeaponPropertiesFin", + fix: "SW5E.WeaponPropertiesFix", + foc: "SW5E.WeaponPropertiesFoc", + hvy: "SW5E.WeaponPropertiesHvy", + hid: "SW5E.WeaponPropertiesHid", + ken: "SW5E.WeaponPropertiesKen", + lgt: "SW5E.WeaponPropertiesLgt", + lum: "SW5E.WeaponPropertiesLum", + mig: "SW5E.WeaponPropertiesMig", + pic: "SW5E.WeaponPropertiesPic", + rap: "SW5E.WeaponPropertiesRap", + rch: "SW5E.WeaponPropertiesRch", + rel: "SW5E.WeaponPropertiesRel", + ret: "SW5E.WeaponPropertiesRet", + shk: "SW5E.WeaponPropertiesShk", + sil: "SW5E.WeaponPropertiesSil", + spc: "SW5E.WeaponPropertiesSpc", + str: "SW5E.WeaponPropertiesStr", + thr: "SW5E.WeaponPropertiesThr", + two: "SW5E.WeaponPropertiesTwo", + ver: "SW5E.WeaponPropertiesVer", + vic: "SW5E.WeaponPropertiesVic" }; - // Power Components SW5E.powerComponents = { - "V": "SW5E.ComponentVerbal", - "S": "SW5E.ComponentSomatic", - "M": "SW5E.ComponentMaterial" + V: "SW5E.ComponentVerbal", + S: "SW5E.ComponentSomatic", + M: "SW5E.ComponentMaterial" }; // Power Schools SW5E.powerSchools = { - "lgt": "SW5E.SchoolLgt", - "uni": "SW5E.SchoolUni", - "drk": "SW5E.SchoolDrk", - "tec": "SW5E.SchoolTec", - "enh": "SW5E.SchoolEnh" + lgt: "SW5E.SchoolLgt", + uni: "SW5E.SchoolUni", + drk: "SW5E.SchoolDrk", + tec: "SW5E.SchoolTec", + enh: "SW5E.SchoolEnh" }; // Power Levels @@ -698,18 +676,18 @@ SW5E.powerLevels = { // Polymorph options. SW5E.polymorphSettings = { - keepPhysical: 'SW5E.PolymorphKeepPhysical', - keepMental: 'SW5E.PolymorphKeepMental', - keepSaves: 'SW5E.PolymorphKeepSaves', - keepSkills: 'SW5E.PolymorphKeepSkills', - mergeSaves: 'SW5E.PolymorphMergeSaves', - mergeSkills: 'SW5E.PolymorphMergeSkills', - keepClass: 'SW5E.PolymorphKeepClass', - keepFeats: 'SW5E.PolymorphKeepFeats', - keepPowers: 'SW5E.PolymorphKeepPowers', - keepItems: 'SW5E.PolymorphKeepItems', - keepBio: 'SW5E.PolymorphKeepBio', - keepVision: 'SW5E.PolymorphKeepVision' + keepPhysical: "SW5E.PolymorphKeepPhysical", + keepMental: "SW5E.PolymorphKeepMental", + keepSaves: "SW5E.PolymorphKeepSaves", + keepSkills: "SW5E.PolymorphKeepSkills", + mergeSaves: "SW5E.PolymorphMergeSaves", + mergeSkills: "SW5E.PolymorphMergeSkills", + keepClass: "SW5E.PolymorphKeepClass", + keepFeats: "SW5E.PolymorphKeepFeats", + keepPowers: "SW5E.PolymorphKeepPowers", + keepItems: "SW5E.PolymorphKeepItems", + keepBio: "SW5E.PolymorphKeepBio", + keepVision: "SW5E.PolymorphKeepVision" }; /* -------------------------------------------- */ @@ -734,157 +712,203 @@ SW5E.proficiencyLevels = { * in play, we take the highest value. */ SW5E.cover = { - 0: 'SW5E.None', - .5: 'SW5E.CoverHalf', - .75: 'SW5E.CoverThreeQuarters', - 1: 'SW5E.CoverTotal' + 0: "SW5E.None", + 0.5: "SW5E.CoverHalf", + 0.75: "SW5E.CoverThreeQuarters", + 1: "SW5E.CoverTotal" }; /* -------------------------------------------- */ - // Condition Types SW5E.conditionTypes = { - "blinded": "SW5E.ConBlinded", - "charmed": "SW5E.ConCharmed", - "deafened": "SW5E.ConDeafened", - "diseased": "SW5E.ConDiseased", - "exhaustion": "SW5E.ConExhaustion", - "frightened": "SW5E.ConFrightened", - "grappled": "SW5E.ConGrappled", - "incapacitated": "SW5E.ConIncapacitated", - "invisible": "SW5E.ConInvisible", - "paralyzed": "SW5E.ConParalyzed", - "petrified": "SW5E.ConPetrified", - "poisoned": "SW5E.ConPoisoned", - "prone": "SW5E.ConProne", - "restrained": "SW5E.ConRestrained", - "shocked": "SW5E.ConShocked", - "slowed": "SW5E.ConSlowed", - "stunned": "SW5E.ConStunned", - "unconscious": "SW5E.ConUnconscious" + blinded: "SW5E.ConBlinded", + charmed: "SW5E.ConCharmed", + deafened: "SW5E.ConDeafened", + diseased: "SW5E.ConDiseased", + exhaustion: "SW5E.ConExhaustion", + frightened: "SW5E.ConFrightened", + grappled: "SW5E.ConGrappled", + incapacitated: "SW5E.ConIncapacitated", + invisible: "SW5E.ConInvisible", + paralyzed: "SW5E.ConParalyzed", + petrified: "SW5E.ConPetrified", + poisoned: "SW5E.ConPoisoned", + prone: "SW5E.ConProne", + restrained: "SW5E.ConRestrained", + shocked: "SW5E.ConShocked", + slowed: "SW5E.ConSlowed", + stunned: "SW5E.ConStunned", + unconscious: "SW5E.ConUnconscious" }; // Languages SW5E.languages = { - "abyssin": "SW5E.LanguagesAbyssin", - "aleena": "SW5E.LanguagesAleena", - "antarian": "SW5E.LanguagesAntarian", - "anzellan": "SW5E.LanguagesAnzellan", - "aqualish": "SW5E.LanguagesAqualish", - "arconese": "SW5E.LanguagesArconese", - "ardennian": "SW5E.LanguagesArdennian", - "arkanian": "SW5E.LanguagesArkanian", - "balosur": "SW5E.LanguagesBalosur", - "barabel": "SW5E.LanguagesBarabel", - "basic": "SW5E.LanguagesBasic", - "besalisk": "SW5E.LanguagesBesalisk", - "binary": "SW5E.LanguagesBinary", - "bith": "SW5E.LanguagesBith", - "bocce": "SW5E.LanguagesBocce", - "bothese": "SW5E.LanguagesBothese", - "catharese": "SW5E.LanguagesCatharese", - "cerean": "SW5E.LanguagesCerean", + abyssin: "SW5E.LanguagesAbyssin", + aleena: "SW5E.LanguagesAleena", + antarian: "SW5E.LanguagesAntarian", + anzellan: "SW5E.LanguagesAnzellan", + aqualish: "SW5E.LanguagesAqualish", + arconese: "SW5E.LanguagesArconese", + ardennian: "SW5E.LanguagesArdennian", + arkanian: "SW5E.LanguagesArkanian", + balosur: "SW5E.LanguagesBalosur", + barabel: "SW5E.LanguagesBarabel", + basic: "SW5E.LanguagesBasic", + besalisk: "SW5E.LanguagesBesalisk", + binary: "SW5E.LanguagesBinary", + bith: "SW5E.LanguagesBith", + bocce: "SW5E.LanguagesBocce", + bothese: "SW5E.LanguagesBothese", + catharese: "SW5E.LanguagesCatharese", + cerean: "SW5E.LanguagesCerean", "chadra-fan": "SW5E.LanguagesChadra-Fan", - "chagri": "SW5E.LanguagesChagri", - "cheunh": "SW5E.LanguagesCheunh", - "chevin": "SW5E.LanguagesChevin", - "chironan": "SW5E.LanguagesChironan", - "clawdite": "SW5E.LanguagesClawdite", - "codruese": "SW5E.LanguagesCodruese", - "colicoid": "SW5E.LanguagesColicoid", - "dashadi": "SW5E.LanguagesDashadi", - "defel": "SW5E.LanguagesDefel", - "devaronese": "SW5E.LanguagesDevaronese", - "dosh": "SW5E.LanguagesDosh", - "draethos": "SW5E.LanguagesDraethos", - "durese": "SW5E.LanguagesDurese", - "dug": "SW5E.LanguagesDug", - "ewokese": "SW5E.LanguagesEwokese", - "falleen": "SW5E.LanguagesFalleen", - "felucianese": "SW5E.LanguagesFelucianese", - "gamorrese": "SW5E.LanguagesGamorrese", - "gand": "SW5E.LanguagesGand", - "geonosian": "SW5E.LanguagesGeonosian", - "givin": "SW5E.LanguagesGivin", - "gran": "SW5E.LanguagesGran", - "gungan": "SW5E.LanguagesGungan", - "hapan": "SW5E.LanguagesHapan", - "harchese": "SW5E.LanguagesHarchese", - "herglese": "SW5E.LanguagesHerglese", - "honoghran": "SW5E.LanguagesHonoghran", - "huttese": "SW5E.LanguagesHuttese", - "iktotchese": "SW5E.LanguagesIktotchese", - "ithorese": "SW5E.LanguagesIthorese", - "jawaese": "SW5E.LanguagesJawaese", - "kaleesh": "SW5E.LanguagesKaleesh", - "kaminoan": "SW5E.LanguagesKaminoan", - "karkaran": "SW5E.LanguagesKarkaran", - "keldor": "SW5E.LanguagesKelDor", - "kharan": "SW5E.LanguagesKharan", - "killik": "SW5E.LanguagesKillik", - "klatooinian": "SW5E.LanguagesKlatooinian", - "kubazian": "SW5E.LanguagesKubazian", - "kushiban": "SW5E.LanguagesKushiban", - "kyuzo": "SW5E.LanguagesKyuzo", - "lannik": "SW5E.LanguagesLannik", - "lasat": "SW5E.LanguagesLasat", - "lowickese": "SW5E.LanguagesLowickese", - "lurmese": "SW5E.LanguagesLurmese", - "mandoa": "SW5E.LanguagesMandoa", - "miralukese": "SW5E.LanguagesMiralukese", - "mirialan": "SW5E.LanguagesMirialan", - "moncal": "SW5E.LanguagesMonCal", - "mustafarian": "SW5E.LanguagesMustafarian", - "muun": "SW5E.LanguagesMuun", - "nautila": "SW5E.LanguagesNautila", - "ortolan": "SW5E.LanguagesOrtolan", - "pakpak": "SW5E.LanguagesPakPak", - "pyke": "SW5E.LanguagesPyke", - "quarrenese": "SW5E.LanguagesQuarrenese", - "rakata": "SW5E.LanguagesRakata", - "rattataki": "SW5E.LanguagesRattataki", - "rishii": "SW5E.LanguagesRishii", - "rodese": "SW5E.LanguagesRodese", - "ryn": "SW5E.LanguagesRyn", - "selkatha": "SW5E.LanguagesSelkatha", - "semblan": "SW5E.LanguagesSemblan", - "shistavanen": "SW5E.LanguagesShistavanen", - "shyriiwook": "SW5E.LanguagesShyriiwook", - "sith": "SW5E.LanguagesSith", - "squibbian": "SW5E.LanguagesSquibbian", - "sriluurian": "SW5E.LanguagesSriluurian", + chagri: "SW5E.LanguagesChagri", + cheunh: "SW5E.LanguagesCheunh", + chevin: "SW5E.LanguagesChevin", + chironan: "SW5E.LanguagesChironan", + clawdite: "SW5E.LanguagesClawdite", + codruese: "SW5E.LanguagesCodruese", + colicoid: "SW5E.LanguagesColicoid", + dashadi: "SW5E.LanguagesDashadi", + defel: "SW5E.LanguagesDefel", + devaronese: "SW5E.LanguagesDevaronese", + dosh: "SW5E.LanguagesDosh", + draethos: "SW5E.LanguagesDraethos", + durese: "SW5E.LanguagesDurese", + dug: "SW5E.LanguagesDug", + ewokese: "SW5E.LanguagesEwokese", + falleen: "SW5E.LanguagesFalleen", + felucianese: "SW5E.LanguagesFelucianese", + gamorrese: "SW5E.LanguagesGamorrese", + gand: "SW5E.LanguagesGand", + geonosian: "SW5E.LanguagesGeonosian", + givin: "SW5E.LanguagesGivin", + gran: "SW5E.LanguagesGran", + gungan: "SW5E.LanguagesGungan", + hapan: "SW5E.LanguagesHapan", + harchese: "SW5E.LanguagesHarchese", + herglese: "SW5E.LanguagesHerglese", + honoghran: "SW5E.LanguagesHonoghran", + huttese: "SW5E.LanguagesHuttese", + iktotchese: "SW5E.LanguagesIktotchese", + ithorese: "SW5E.LanguagesIthorese", + jawaese: "SW5E.LanguagesJawaese", + kaleesh: "SW5E.LanguagesKaleesh", + kaminoan: "SW5E.LanguagesKaminoan", + karkaran: "SW5E.LanguagesKarkaran", + keldor: "SW5E.LanguagesKelDor", + kharan: "SW5E.LanguagesKharan", + killik: "SW5E.LanguagesKillik", + klatooinian: "SW5E.LanguagesKlatooinian", + kubazian: "SW5E.LanguagesKubazian", + kushiban: "SW5E.LanguagesKushiban", + kyuzo: "SW5E.LanguagesKyuzo", + lannik: "SW5E.LanguagesLannik", + lasat: "SW5E.LanguagesLasat", + lowickese: "SW5E.LanguagesLowickese", + lurmese: "SW5E.LanguagesLurmese", + mandoa: "SW5E.LanguagesMandoa", + miralukese: "SW5E.LanguagesMiralukese", + mirialan: "SW5E.LanguagesMirialan", + moncal: "SW5E.LanguagesMonCal", + mustafarian: "SW5E.LanguagesMustafarian", + muun: "SW5E.LanguagesMuun", + nautila: "SW5E.LanguagesNautila", + ortolan: "SW5E.LanguagesOrtolan", + pakpak: "SW5E.LanguagesPakPak", + pyke: "SW5E.LanguagesPyke", + quarrenese: "SW5E.LanguagesQuarrenese", + rakata: "SW5E.LanguagesRakata", + rattataki: "SW5E.LanguagesRattataki", + rishii: "SW5E.LanguagesRishii", + rodese: "SW5E.LanguagesRodese", + ryn: "SW5E.LanguagesRyn", + selkatha: "SW5E.LanguagesSelkatha", + semblan: "SW5E.LanguagesSemblan", + shistavanen: "SW5E.LanguagesShistavanen", + shyriiwook: "SW5E.LanguagesShyriiwook", + sith: "SW5E.LanguagesSith", + squibbian: "SW5E.LanguagesSquibbian", + sriluurian: "SW5E.LanguagesSriluurian", "ssi-ruuvi": "SW5E.LanguagesSsi-ruuvi", - "sullustese": "SW5E.LanguagesSullustese", - "talzzi": "SW5E.LanguagesTalzzi", - "tarasinese": "SW5E.LanguagesTarasinese", - "thisspiasian": "SW5E.LanguagesThisspiasian", - "togorese": "SW5E.LanguagesTogorese", - "togruti": "SW5E.LanguagesTogruti", - "toydarian": "SW5E.LanguagesToydarian", - "tusken": "SW5E.LanguagesTusken", + sullustese: "SW5E.LanguagesSullustese", + talzzi: "SW5E.LanguagesTalzzi", + tarasinese: "SW5E.LanguagesTarasinese", + thisspiasian: "SW5E.LanguagesThisspiasian", + togorese: "SW5E.LanguagesTogorese", + togruti: "SW5E.LanguagesTogruti", + toydarian: "SW5E.LanguagesToydarian", + tusken: "SW5E.LanguagesTusken", "twi'leki": "SW5E.LanguagesTwileki", - "ugnaught": "SW5E.LanguagesUgnaught", - "umbaran": "SW5E.LanguagesUmbaran", - "utapese": "SW5E.LanguagesUtapese", - "verpine": "SW5E.LanguagesVerpine", - "vong": "SW5E.LanguagesVong", - "voss": "SW5E.LanguagesVoss", - "yevethan": "SW5E.LanguagesYevethan", - "zabraki": "SW5E.LanguagesZabraki", - "zygerrian": "SW5E.LanguagesZygerrian" + ugnaught: "SW5E.LanguagesUgnaught", + umbaran: "SW5E.LanguagesUmbaran", + utapese: "SW5E.LanguagesUtapese", + verpine: "SW5E.LanguagesVerpine", + vong: "SW5E.LanguagesVong", + voss: "SW5E.LanguagesVoss", + yevethan: "SW5E.LanguagesYevethan", + zabraki: "SW5E.LanguagesZabraki", + zygerrian: "SW5E.LanguagesZygerrian" }; // Character Level XP Requirements -SW5E.CHARACTER_EXP_LEVELS = [ - 0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, - 120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000] -; +SW5E.CHARACTER_EXP_LEVELS = [ + 0, + 300, + 900, + 2700, + 6500, + 14000, + 23000, + 34000, + 48000, + 64000, + 85000, + 100000, + 120000, + 140000, + 165000, + 195000, + 225000, + 265000, + 305000, + 355000 +]; // Challenge Rating XP Levels SW5E.CR_EXP_LEVELS = [ - 10, 200, 450, 700, 1100, 1800, 2300, 2900, 3900, 5000, 5900, 7200, 8400, 10000, 11500, 13000, 15000, 18000, - 20000, 22000, 25000, 33000, 41000, 50000, 62000, 75000, 90000, 105000, 120000, 135000, 155000 + 10, + 200, + 450, + 700, + 1100, + 1800, + 2300, + 2900, + 3900, + 5000, + 5900, + 7200, + 8400, + 10000, + 11500, + 13000, + 15000, + 18000, + 20000, + 22000, + 25000, + 33000, + 41000, + 50000, + 62000, + 75000, + 90000, + 105000, + 120000, + 135000, + 155000 ]; // Character Features Per Class And Level @@ -892,347 +916,347 @@ SW5E.classFeatures = ClassFeatures; // Configure Optional Character Flags SW5E.characterFlags = { - "adaptiveResilience": { + adaptiveResilience: { name: "SW5E.FlagsAdaptiveResilience", hint: "SW5E.FlagsAdaptiveResilienceHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "aggressive": { + aggressive: { name: "SW5E.FlagsAggressive", hint: "SW5E.FlagsAggressiveHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "amphibious": { + amphibious: { name: "SW5E.FlagsAmphibious", hint: "SW5E.FlagsAmphibiousHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "armorIntegration": { - name: "SW5E.FlagsArmorIntegration", + armorIntegration: { + name: "SW5E.FlagsArmorIntegration", hint: "SW5E.FlagsArmorIntegrationHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "businessSavvy": { + businessSavvy: { name: "SW5E.FlagsBusinessSavvy", hint: "SW5E.FlagsBusinessSavvyHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "cannibalize": { - name: "SW5E.FlagsCannibalize", + cannibalize: { + name: "SW5E.FlagsCannibalize", hint: "SW5E.FlagsCannibalizeHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "closedMind": { - name: "SW5E.FlagsClosedMind", + closedMind: { + name: "SW5E.FlagsClosedMind", hint: "SW5E.FlagsClosedMindHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "crudeWeaponSpecialists": { + crudeWeaponSpecialists: { name: "SW5E.FlagsCrudeWeaponSpecialists", hint: "SW5E.FlagsCrudeWeaponSpecialistsHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "defiant": { + defiant: { name: "SW5E.FlagsDefiant", hint: "SW5E.FlagsDefiantHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "detailOriented": { + detailOriented: { name: "SW5E.FlagsDetailOriented", hint: "SW5E.FlagsDetailOrientedHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "enthrallingPheromones": { - name: "SW5E.FlagsEnthrallingPheromones", + enthrallingPheromones: { + name: "SW5E.FlagsEnthrallingPheromones", hint: "SW5E.FlagsEnthrallingPheromonesHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "extraArms": { - name: "SW5E.FlagsExtraArms", + extraArms: { + name: "SW5E.FlagsExtraArms", hint: "SW5E.FlagsExtraArmsHint", section: "SW5E.SpeciesTraits", type: Boolean - }, - "forceContention": { - name: "SW5E.FlagsForceContention", + }, + forceContention: { + name: "SW5E.FlagsForceContention", hint: "SW5E.FlagsForceContentionHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "forceInsensitive": { + forceInsensitive: { name: "SW5E.FlagsForceInsensitive", hint: "SW5E.FlagsForceInsensitiveHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "foreignBiology": { - name: "SW5E.FlagsForeignBiology", + foreignBiology: { + name: "SW5E.FlagsForeignBiology", hint: "SW5E.FlagsForeignBiologyHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "furyOfTheSmall": { - name: "SW5E.FlagsFuryOfTheSmall", + furyOfTheSmall: { + name: "SW5E.FlagsFuryOfTheSmall", hint: "SW5E.FlagsFuryOfTheSmallHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "grovelCowerAndBeg": { + grovelCowerAndBeg: { name: "SW5E.FlagsGrovelCowerAndBeg", hint: "SW5E.FlagsGrovelCowerAndBegHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "inscrutable": { - name: "SW5E.FlagsInscrutable", + inscrutable: { + name: "SW5E.FlagsInscrutable", hint: "SW5E.FlagsInscrutableHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "keenSenses": { + keenSenses: { name: "SW5E.FlagsKeenSenses", hint: "SW5E.FlagsKeenSensesHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "longlimbed": { + longlimbed: { name: "SW5E.FlagsLongLimbed", hint: "SW5E.FlagsLongLimbedHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "maintenanceMode": { + maintenanceMode: { name: "SW5E.FlagsMaintenanceMode", hint: "SW5E.FlagsMaintenanceModeHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "maskOfTheWild": { + maskOfTheWild: { name: "SW5E.FlagsMaskOfTheWild", hint: "SW5E.FlagsMaskOfTheWildHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "multipleHearts": { - name: "SW5E.FlagsMultipleHearts", + multipleHearts: { + name: "SW5E.FlagsMultipleHearts", hint: "SW5E.FlagsMultipleHeartsHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "naturallyStealthy": { + naturallyStealthy: { name: "SW5E.FlagsNaturallyStealthy", hint: "SW5E.FlagsNaturallyStealthyHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "nimbleAgility": { + nimbleAgility: { name: "SW5E.FlagsNimbleAgility", hint: "SW5E.FlagsNimbleAgilityHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "nimbleEscape": { + nimbleEscape: { name: "SW5E.FlagsNimbleEscape", hint: "SW5E.FlagsNimbleEscapeHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "nimbleness": { + nimbleness: { name: "SW5E.FlagsNimbleness", hint: "SW5E.FlagsNimblenessHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "pintsized": { + pintsized: { name: "SW5E.FlagsPintsized", hint: "SW5E.FlagsPintsizedHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "powerfulBuild": { + powerfulBuild: { name: "SW5E.FlagsPowerfulBuild", hint: "SW5E.FlagsPowerfulBuildHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "precognition": { - name: "SW5E.FlagsPrecognition", + precognition: { + name: "SW5E.FlagsPrecognition", hint: "SW5E.FlagsPrecognitionHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "programmer": { + programmer: { name: "SW5E.FlagsProgrammer", hint: "SW5E.FlagsProgrammerHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "puny": { + puny: { name: "SW5E.FlagsPuny", - hint: "SW5E.FlagsPunyHint", - section: "SW5E.SpeciesTraits", - type: Boolean + hint: "SW5E.FlagsPunyHint", + section: "SW5E.SpeciesTraits", + type: Boolean }, - "rapidReconstruction": { + rapidReconstruction: { name: "SW5E.FlagsRapidReconstruction", hint: "SW5E.FlagsRapidReconstructionHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "rapidlyRegenerative": { - name: "SW5E.FlagsRapidlyRegenerative", + rapidlyRegenerative: { + name: "SW5E.FlagsRapidlyRegenerative", hint: "SW5E.FlagsRapidlyRegenerativeHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "regenerative": { + regenerative: { name: "SW5E.FlagsRegenerative", hint: "SW5E.FlagsRegenerativeHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "savageAttacks": { - name: "SW5E.FlagsSavageAttacks", + savageAttacks: { + name: "SW5E.FlagsSavageAttacks", hint: "SW5E.FlagsSavageAttacksHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "shapechanger": { + shapechanger: { name: "SW5E.FlagsShapechanger", hint: "SW5E.FlagsShapechangerHint", section: "SW5E.SpeciesTraits", type: Boolean - }, - "strongLegged": { - name: "SW5E.FlagsStrongLegged", + }, + strongLegged: { + name: "SW5E.FlagsStrongLegged", hint: "SW5E.FlagsStrongLeggedHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "sunlightSensitivity": { - name: "SW5E.FlagsSunlightSensitivity", + sunlightSensitivity: { + name: "SW5E.FlagsSunlightSensitivity", hint: "SW5E.FlagsSunlightSensitivityHint", - section: "SW5E.SpeciesTraits", - type: Boolean + section: "SW5E.SpeciesTraits", + type: Boolean }, - "surpriseAttack": { + surpriseAttack: { name: "SW5E.FlagsSurpriseAttack", hint: "SW5E.FlagsSurpriseAttackHint", section: "SW5E.SpeciesTraits", type: Boolean - }, - "techImpaired": { + }, + techImpaired: { name: "SW5E.FlagsTechImpaired", hint: "SW5E.FlagsTechImpairedHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "techResistance": { + techResistance: { name: "SW5E.FlagsTechResistance", hint: "SW5E.FlagsTechResistanceHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "tinker": { - name: "SW5E.FlagsTinker", + tinker: { + name: "SW5E.FlagsTinker", hint: "SW5E.FlagsTinkerHint", section: "SW5E.SpeciesTraits", - type: Boolean + type: Boolean }, - "toughness": { + toughness: { name: "SW5E.FlagsToughness", hint: "SW5E.FlagsToughnessHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "trance": { + trance: { name: "SW5E.FlagsTrance", hint: "SW5E.FlagsTranceHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "unarmedCombatant": { + unarmedCombatant: { name: "SW5E.FlagsUnarmedCombatant", hint: "SW5E.FlagsUnarmedCombatantHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "undersized": { + undersized: { name: "SW5E.FlagsUndersized", hint: "SW5E.FlagsUndersizedHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "unsettlingVisage": { + unsettlingVisage: { name: "SW5E.FlagsUnsettlingVisage", - hint: "SW5E.FlagsUnsettlingVisageHint", + hint: "SW5E.FlagsUnsettlingVisageHint", section: "SW5E.SpeciesTraits", type: Boolean }, - "initiativeAdv": { + initiativeAdv: { name: "SW5E.FlagsInitiativeAdv", hint: "SW5E.FlagsInitiativeAdvHint", section: "SW5E.Features", type: Boolean }, - "initiativeAlert": { + initiativeAlert: { name: "SW5E.FlagsAlert", hint: "SW5E.FlagsAlertHint", section: "SW5E.Features", type: Boolean }, - "jackOfAllTrades": { + jackOfAllTrades: { name: "SW5E.FlagsJOAT", hint: "SW5E.FlagsJOATHint", section: "SW5E.Features", type: Boolean }, - "observantFeat": { + observantFeat: { name: "SW5E.FlagsObservant", hint: "SW5E.FlagsObservantHint", - skills: ['prc','inv'], + skills: ["prc", "inv"], section: "SW5E.Features", type: Boolean }, - "reliableTalent": { + reliableTalent: { name: "SW5E.FlagsReliableTalent", hint: "SW5E.FlagsReliableTalentHint", section: "SW5E.Features", type: Boolean }, - "remarkableAthlete": { + remarkableAthlete: { name: "SW5E.FlagsRemarkableAthlete", hint: "SW5E.FlagsRemarkableAthleteHint", - abilities: ['str','dex','con'], + abilities: ["str", "dex", "con"], section: "SW5E.Features", type: Boolean }, - "weaponCriticalThreshold": { + weaponCriticalThreshold: { name: "SW5E.FlagsWeaponCritThreshold", hint: "SW5E.FlagsWeaponCritThresholdHint", section: "SW5E.Features", type: Number, placeholder: 20 }, - "powerCriticalThreshold": { + powerCriticalThreshold: { name: "SW5E.FlagsPowerCritThreshold", hint: "SW5E.FlagsPowerCritThresholdHint", section: "SW5E.Features", type: Number, placeholder: 20 }, - "meleeCriticalDamageDice": { + meleeCriticalDamageDice: { name: "SW5E.FlagsMeleeCriticalDice", hint: "SW5E.FlagsMeleeCriticalDiceHint", section: "SW5E.Features", diff --git a/module/dice.js b/module/dice.js index cea097fd..c945f310 100644 --- a/module/dice.js +++ b/module/dice.js @@ -18,30 +18,36 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {}) const rollableTerms = []; // Terms that are non-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 - if (["+", "-"].includes(term)) operators.push(term); // 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(term); // Then place this rollable term into it as well - } // - else { // Otherwise, this must be a constant - constantTerms.push(...operators); // Place the operators into the constantTerms array - constantTerms.push(term); // Then also add this constant term to that array. - } // - operators = []; // Finally, the operators have now all been assigend to one of the arrays, so empty this before the next iteration. + for (let term of terms) { + // For each term + if (["+", "-"].includes(term)) operators.push(term); + // 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(term); // Then place this rollable term into it as well + } // + else { + // Otherwise, this must be a constant + constantTerms.push(...operators); // Place the operators into the constantTerms array + constantTerms.push(term); // Then also add this constant term to that array. + } // + operators = []; // Finally, the operators have now all been assigend to one of the arrays, so empty this before the next iteration. } } - const constantFormula = Roll.cleanFormula(constantTerms); // Cleans up the constant terms and produces a new formula string - const rollableFormula = Roll.cleanFormula(rollableTerms); // Cleans up the non-constant terms and produces a new formula string + const constantFormula = Roll.cleanFormula(constantTerms); // Cleans up the constant terms and produces a new formula string + const rollableFormula = Roll.cleanFormula(rollableTerms); // Cleans up the non-constant terms and produces a new formula string - const constantPart = roll._safeEval(constantFormula); // Mathematically evaluate the constant formula to produce a single constant term + const constantPart = roll._safeEval(constantFormula); // Mathematically evaluate the constant formula to produce a single constant term - const parts = constantFirst ? // Order the rollable and constant terms, either constant first or second depending on the optional argumen - [constantPart, rollableFormula] : [rollableFormula, constantPart]; + const parts = constantFirst // Order the rollable and constant terms, either constant first or second depending on the optional argumen + ? [constantPart, rollableFormula] + : [rollableFormula, constantPart]; // Join the parts with a + sign, pass them to `Roll` once again to clean up the formula return new Roll(parts.filterJoin(" + ")).formula; @@ -55,11 +61,11 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {}) * @return {Boolean} True when unsupported, false if supported */ function _isUnsupportedTerm(term) { - const diceTerm = term instanceof DiceTerm; - const operator = ["+", "-"].includes(term); - const number = !isNaN(Number(term)); + const diceTerm = term instanceof DiceTerm; + const operator = ["+", "-"].includes(term); + const number = !isNaN(Number(term)); - return !(diceTerm || operator || number); + return !(diceTerm || operator || number); } /* -------------------------------------------- */ @@ -94,12 +100,28 @@ function _isUnsupportedTerm(term) { * * @return {Promise} A Promise which resolves once the roll workflow has completed */ -export async function d20Roll({parts=[], data={}, event={}, rollMode=null, template=null, title=null, speaker=null, - flavor=null, fastForward=null, dialogOptions, - advantage=null, disadvantage=null, critical=20, fumble=1, targetValue=null, - elvenAccuracy=false, halflingLucky=false, reliableTalent=false, - chatMessage=true, messageData={}}={}) { - +export async function d20Roll({ + parts = [], + data = {}, + event = {}, + rollMode = null, + template = null, + title = null, + speaker = null, + flavor = null, + fastForward = null, + dialogOptions, + advantage = null, + disadvantage = null, + critical = 20, + fumble = 1, + targetValue = null, + elvenAccuracy = false, + halflingLucky = false, + reliableTalent = false, + chatMessage = true, + messageData = {} +} = {}) { // Prepare Message Data messageData.flavor = flavor || title; messageData.speaker = speaker || ChatMessage.getSpeaker(); @@ -110,13 +132,12 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ let adv = 0; fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)); if (fastForward) { - if ( advantage ?? event.altKey ) adv = 1; - else if ( disadvantage ?? (event.ctrlKey || event.metaKey) ) adv = -1; + if (advantage ?? event.altKey) adv = 1; + else if (disadvantage ?? (event.ctrlKey || event.metaKey)) adv = -1; } // Define the inner roll function const _roll = (parts, adv, form) => { - // Determine the d20 roll and modifiers let nd = 1; let mods = halflingLucky ? "r1=1" : ""; @@ -125,7 +146,7 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ if (adv === 1) { nd = elvenAccuracy ? 3 : 2; messageData.flavor += ` (${game.i18n.localize("SW5E.Advantage")})`; - if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].advantage = true; + if ("flags.sw5e.roll" in messageData) messageData["flags.sw5e.roll"].advantage = true; mods += "kh"; } @@ -133,7 +154,7 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ else if (adv === -1) { nd = 2; messageData.flavor += ` (${game.i18n.localize("SW5E.Disadvantage")})`; - if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].disadvantage = true; + if ("flags.sw5e.roll" in messageData) messageData["flags.sw5e.roll"].disadvantage = true; mods += "kl"; } @@ -143,8 +164,8 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ parts.unshift(formula); // Optionally include a situational bonus - if ( form ) { - data['bonus'] = form.bonus.value; + if (form) { + data["bonus"] = form.bonus.value; messageOptions.rollMode = form.rollMode.value; } if (!data["bonus"]) parts.pop(); @@ -175,8 +196,8 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ if (d.faces === 20) { d.options.critical = critical; d.options.fumble = fumble; - if ( adv === 1 ) d.options.advantage = true; - else if ( adv === -1 ) d.options.disadvantage = true; + if (adv === 1) d.options.advantage = true; + else if (adv === -1) d.options.disadvantage = true; if (targetValue) d.options.target = targetValue; } } @@ -189,11 +210,20 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ }; // Create the Roll instance - const roll = fastForward ? _roll(parts, adv) : - await _d20RollDialog({template, title, parts, data, rollMode: messageOptions.rollMode, dialogOptions, roll: _roll}); + const roll = fastForward + ? _roll(parts, adv) + : await _d20RollDialog({ + template, + title, + parts, + data, + rollMode: messageOptions.rollMode, + dialogOptions, + roll: _roll + }); // Create a Chat Message - if ( roll && chatMessage ) roll.toMessage(messageData, messageOptions); + if (roll && chatMessage) roll.toMessage(messageData, messageOptions); return roll; } @@ -204,8 +234,7 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ * @return {Promise} * @private */ -async function _d20RollDialog({template, title, parts, data, rollMode, dialogOptions, roll}={}) { - +async function _d20RollDialog({template, title, parts, data, rollMode, dialogOptions, roll} = {}) { // Render modal dialog template = template || "systems/sw5e/templates/chat/roll-dialog.html"; let dialogData = { @@ -219,26 +248,29 @@ async function _d20RollDialog({template, title, parts, data, rollMode, dialogOpt // Create the Dialog window return new Promise(resolve => { - new Dialog({ - title: title, - content: html, - buttons: { - advantage: { - label: game.i18n.localize("SW5E.Advantage"), - callback: html => resolve(roll(parts, 1, html[0].querySelector("form"))) + new Dialog( + { + title: title, + content: html, + buttons: { + advantage: { + label: game.i18n.localize("SW5E.Advantage"), + callback: html => resolve(roll(parts, 1, html[0].querySelector("form"))) + }, + normal: { + label: game.i18n.localize("SW5E.Normal"), + callback: html => resolve(roll(parts, 0, html[0].querySelector("form"))) + }, + disadvantage: { + label: game.i18n.localize("SW5E.Disadvantage"), + callback: html => resolve(roll(parts, -1, html[0].querySelector("form"))) + } }, - normal: { - label: game.i18n.localize("SW5E.Normal"), - callback: html => resolve(roll(parts, 0, html[0].querySelector("form"))) - }, - disadvantage: { - label: game.i18n.localize("SW5E.Disadvantage"), - callback: html => resolve(roll(parts, -1, html[0].querySelector("form"))) - } + default: "normal", + close: () => resolve(null) }, - default: "normal", - close: () => resolve(null) - }, dialogOptions).render(true); + dialogOptions + ).render(true); }); } @@ -271,10 +303,25 @@ async function _d20RollDialog({template, title, parts, data, rollMode, dialogOpt * * @return {Promise} A Promise which resolves once the roll workflow has completed */ -export async function damageRoll({parts, actor, data, event={}, rollMode=null, template, title, speaker, flavor, - allowCritical=true, critical=false, criticalBonusDice=0, criticalMultiplier=2, fastForward=null, - dialogOptions={}, chatMessage=true, messageData={}}={}) { - +export async function damageRoll({ + parts, + actor, + data, + event = {}, + rollMode = null, + template, + title, + speaker, + flavor, + allowCritical = true, + critical = false, + criticalBonusDice = 0, + criticalMultiplier = 2, + fastForward = null, + dialogOptions = {}, + chatMessage = true, + messageData = {} +} = {}) { // Prepare Message Data messageData.flavor = flavor || title; messageData.speaker = speaker || ChatMessage.getSpeaker(); @@ -282,11 +329,10 @@ export async function damageRoll({parts, actor, data, event={}, rollMode=null, t parts = parts.concat(["@bonus"]); // Define inner roll function - const _roll = function(parts, crit, form) { - + const _roll = function (parts, crit, form) { // Optionally include a situational bonus - if ( form ) { - data['bonus'] = form.bonus.value; + if (form) { + data["bonus"] = form.bonus.value; messageOptions.rollMode = form.rollMode.value; } if (!data["bonus"]) parts.pop(); @@ -295,22 +341,23 @@ export async function damageRoll({parts, actor, data, event={}, rollMode=null, t let roll = new Roll(parts.join("+"), data); // Modify the damage formula for critical hits - if ( crit === true ) { - roll.alter(criticalMultiplier, 0); // Multiply all dice - if ( roll.terms[0] instanceof Die ) { // Add bonus dice for only the main dice term + if (crit === true) { + roll.alter(criticalMultiplier, 0); // Multiply all dice + if (roll.terms[0] instanceof Die) { + // Add bonus dice for only the main dice term roll.terms[0].alter(1, criticalBonusDice); roll._formula = roll.formula; } messageData.flavor += ` (${game.i18n.localize("SW5E.Critical")})`; - if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].critical = true; + if ("flags.sw5e.roll" in messageData) messageData["flags.sw5e.roll"].critical = true; } // Execute the roll try { - roll.evaluate() - if ( crit ) roll.dice.forEach(d => d.options.critical = true); // TODO workaround core bug which wipes Roll#options on roll + roll.evaluate(); + if (crit) roll.dice.forEach(d => (d.options.critical = true)); // TODO workaround core bug which wipes Roll#options on roll return roll; - } catch(err) { + } catch (err) { console.error(err); ui.notifications.error(`Dice roll evaluation failed: ${err.message}`); return null; @@ -318,14 +365,22 @@ export async function damageRoll({parts, actor, data, event={}, rollMode=null, t }; // Create the Roll instance - const roll = fastForward ? _roll(parts, critical) : await _damageRollDialog({ - template, title, parts, data, allowCritical, rollMode: messageOptions.rollMode, dialogOptions, roll: _roll - }); + const roll = fastForward + ? _roll(parts, critical) + : await _damageRollDialog({ + template, + title, + parts, + data, + allowCritical, + rollMode: messageOptions.rollMode, + dialogOptions, + roll: _roll + }); // Create a Chat Message - if ( roll && chatMessage ) roll.toMessage(messageData, messageOptions); + if (roll && chatMessage) roll.toMessage(messageData, messageOptions); return roll; - } /* -------------------------------------------- */ @@ -335,8 +390,7 @@ export async function damageRoll({parts, actor, data, event={}, rollMode=null, t * @return {Promise} * @private */ -async function _damageRollDialog({template, title, parts, data, allowCritical, rollMode, dialogOptions, roll}={}) { - +async function _damageRollDialog({template, title, parts, data, allowCritical, rollMode, dialogOptions, roll} = {}) { // Render modal dialog template = template || "systems/sw5e/templates/chat/roll-dialog.html"; let dialogData = { @@ -349,22 +403,25 @@ async function _damageRollDialog({template, title, parts, data, allowCritical, r // Create the Dialog window return new Promise(resolve => { - new Dialog({ - title: title, - content: html, - buttons: { - critical: { - condition: allowCritical, - label: game.i18n.localize("SW5E.CriticalHit"), - callback: html => resolve(roll(parts, true, html[0].querySelector("form"))) - }, - normal: { - label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"), - callback: html => resolve(roll(parts, false, html[0].querySelector("form"))) + new Dialog( + { + title: title, + content: html, + buttons: { + critical: { + condition: allowCritical, + label: game.i18n.localize("SW5E.CriticalHit"), + callback: html => resolve(roll(parts, true, html[0].querySelector("form"))) + }, + normal: { + label: game.i18n.localize(allowCritical ? "SW5E.Normal" : "SW5E.Roll"), + callback: html => resolve(roll(parts, false, html[0].querySelector("form"))) + } }, + default: "normal", + close: () => resolve(null) }, - default: "normal", - close: () => resolve(null) - }, dialogOptions).render(true); + dialogOptions + ).render(true); }); } diff --git a/module/effects.js b/module/effects.js index 0d957fdb..b6643f3c 100644 --- a/module/effects.js +++ b/module/effects.js @@ -8,15 +8,18 @@ export function onManageActiveEffect(event, owner) { const a = event.currentTarget; const li = a.closest("li"); const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null; - switch ( a.dataset.action ) { + switch (a.dataset.action) { case "create": - return ActiveEffect.create({ - label: "New Effect", - icon: "icons/svg/aura.svg", - origin: owner.uuid, - "duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined, - disabled: li.dataset.effectType === "inactive" - }, owner).create(); + return ActiveEffect.create( + { + label: "New Effect", + icon: "icons/svg/aura.svg", + origin: owner.uuid, + "duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined, + disabled: li.dataset.effectType === "inactive" + }, + owner + ).create(); case "edit": return effect.sheet.render(true); case "delete": @@ -32,32 +35,31 @@ export function onManageActiveEffect(event, owner) { * @return {object} Data for rendering */ export function prepareActiveEffectCategories(effects) { - - // Define effect header categories - const categories = { - temporary: { - type: "temporary", - label: "SW5E.EffectsCategoryTemporary", - effects: [] - }, - passive: { - type: "passive", - label: "SW5E.EffectsCategoryPassive", - effects: [] - }, - inactive: { - type: "inactive", - label: "SW5E.EffectsCategoryInactive", - effects: [] - } - }; - - // Iterate over active effects, classifying them into categories - for ( let e of effects ) { - e._getSourceName(); // Trigger a lookup for the source name - if ( e.data.disabled ) categories.inactive.effects.push(e); - else if ( e.isTemporary ) categories.temporary.effects.push(e); - else categories.passive.effects.push(e); + // Define effect header categories + const categories = { + temporary: { + type: "temporary", + label: "SW5E.EffectsCategoryTemporary", + effects: [] + }, + passive: { + type: "passive", + label: "SW5E.EffectsCategoryPassive", + effects: [] + }, + inactive: { + type: "inactive", + label: "SW5E.EffectsCategoryInactive", + effects: [] } - return categories; -} \ No newline at end of file + }; + + // Iterate over active effects, classifying them into categories + for (let e of effects) { + e._getSourceName(); // Trigger a lookup for the source name + if (e.data.disabled) categories.inactive.effects.push(e); + else if (e.isTemporary) categories.temporary.effects.push(e); + else categories.passive.effects.push(e); + } + return categories; +} diff --git a/module/item/entity.js b/module/item/entity.js index accded14..c02fc6eb 100644 --- a/module/item/entity.js +++ b/module/item/entity.js @@ -5,7 +5,6 @@ import AbilityUseDialog from "../apps/ability-use-dialog.js"; * Override and extend the basic :class:`Item` implementation */ export default class Item5e extends Item { - /* -------------------------------------------- */ /* Item Properties */ /* -------------------------------------------- */ @@ -20,7 +19,6 @@ export default class Item5e extends Item { // Case 1 - defined directly by the item if (itemData.ability) return itemData.ability; - // Case 2 - inferred from a parent actor else if (this.actor) { const actorData = this.actor.data.data; @@ -28,38 +26,41 @@ export default class Item5e extends Item { // Powers - Use Actor powercasting modifier based on power school if (this.data.type === "power") { switch (this.data.data.school) { - case "lgt": return "wis"; - case "uni": return (actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod) ? "wis" : "cha"; - case "drk": return "cha"; - case "tec": return "int"; + case "lgt": + return "wis"; + case "uni": + return actorData.abilities["wis"].mod >= actorData.abilities["cha"].mod ? "wis" : "cha"; + case "drk": + return "cha"; + case "tec": + return "int"; } return "none"; } - // Tools - default to Intelligence else if (this.data.type === "tool") return "int"; - // Weapons else if (this.data.type === "weapon") { const wt = itemData.weaponType; // Melee weapons - Str or Dex if Finesse (PHB pg. 147) - if ( ["simpleVW", "martialVW", "simpleLW", "martialLW"].includes(wt) ) { - if (itemData.properties.fin === true) { // Finesse weapons - return (actorData.abilities["dex"].mod >= actorData.abilities["str"].mod) ? "dex" : "str"; + if (["simpleVW", "martialVW", "simpleLW", "martialLW"].includes(wt)) { + if (itemData.properties.fin === true) { + // Finesse weapons + return actorData.abilities["dex"].mod >= actorData.abilities["str"].mod ? "dex" : "str"; } return "str"; } // Ranged weapons - Dex (PH p.194) - else if ( ["simpleB", "martialB"].includes(wt) ) return "dex"; + else if (["simpleB", "martialB"].includes(wt)) return "dex"; } return "str"; } // Case 3 - unknown - return null + return null; } /* -------------------------------------------- */ @@ -99,7 +100,7 @@ export default class Item5e extends Item { * @return {boolean} */ 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; } /* -------------------------------------------- */ @@ -121,7 +122,7 @@ export default class Item5e extends Item { */ get hasTarget() { const target = this.data.data.target; - return target && !["none",""].includes(target.type); + return target && !["none", ""].includes(target.type); } /* -------------------------------------------- */ @@ -132,7 +133,7 @@ export default class Item5e extends Item { */ get hasAreaTarget() { const target = this.data.data.target; - return target && (target.type in CONFIG.SW5E.areaTargetTypes); + return target && target.type in CONFIG.SW5E.areaTargetTypes; } /* -------------------------------------------- */ @@ -144,7 +145,7 @@ export default class Item5e extends Item { get hasLimitedUses() { let chg = this.data.data.recharge || {}; let uses = this.data.data.uses || {}; - return !!chg.value || (!!uses.per && (uses.max > 0)); + return !!chg.value || (!!uses.per && uses.max > 0); } /* -------------------------------------------- */ @@ -161,20 +162,20 @@ export default class Item5e extends Item { const itemData = this.data; const data = itemData.data; const C = CONFIG.SW5E; - const labels = this.labels = {}; + const labels = (this.labels = {}); // Classes - if ( itemData.type === "class" ) { + if (itemData.type === "class") { data.levels = Math.clamped(data.levels, 1, 20); } // Power Level, School, and Components - if ( itemData.type === "power" ) { + if (itemData.type === "power") { data.preparation.mode = data.preparation.mode || "prepared"; labels.level = C.powerLevels[data.level]; labels.school = C.powerSchools[data.school]; labels.components = Object.entries(data.components).reduce((arr, c) => { - if ( c[1] !== true ) return arr; + if (c[1] !== true) return arr; arr.push(c[0].titleCase().slice(0, 1)); return arr; }, []); @@ -182,54 +183,56 @@ export default class Item5e extends Item { } // Feat Items - else if ( itemData.type === "feat" ) { + else if (itemData.type === "feat") { const act = data.activation; - if ( act && (act.type === C.abilityActivationTypes.legendary) ) labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel"); - 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"); + if (act && act.type === C.abilityActivationTypes.legendary) + labels.featType = game.i18n.localize("SW5E.LegendaryActionLabel"); + 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"); } // Species Items - else if ( itemData.type === "species" ) { - // labels.species = C.species[data.species]; + else if (itemData.type === "species") { + // labels.species = C.species[data.species]; } // Archetype Items - else if ( itemData.type === "archetype" ) { - // labels.archetype = C.archetype[data.archetype]; + else if (itemData.type === "archetype") { + // labels.archetype = C.archetype[data.archetype]; } // Background Items - else if ( itemData.type === "background" ) { - // labels.background = C.background[data.background]; + else if (itemData.type === "background") { + // labels.background = C.background[data.background]; } // Class Feature Items - else if ( itemData.type === "classfeature" ) { - // labels.classFeature = C.classFeature[data.classFeature]; - } - // Fighting Style Items - else if ( itemData.type === "fightingstyle" ) { - // labels.fightingstyle = C.fightingstyle[data.fightingstyle]; - } - // Fighting Mastery Items - else if ( itemData.type === "fightingmastery" ) { - // labels.fightingmastery = C.fightingmastery[data.fightingmastery]; - } - // Lightsaber Form Items - else if ( itemData.type === "lightsaberform" ) { - // labels.lightsaberform = C.lightsaberform[data.lightsaberform]; - } + else if (itemData.type === "classfeature") { + // labels.classFeature = C.classFeature[data.classFeature]; + } + // Fighting Style Items + else if (itemData.type === "fightingstyle") { + // labels.fightingstyle = C.fightingstyle[data.fightingstyle]; + } + // Fighting Mastery Items + else if (itemData.type === "fightingmastery") { + // labels.fightingmastery = C.fightingmastery[data.fightingmastery]; + } + // Lightsaber Form Items + else if (itemData.type === "lightsaberform") { + // labels.lightsaberform = C.lightsaberform[data.lightsaberform]; + } // Equipment Items - else if ( itemData.type === "equipment" ) { + else if (itemData.type === "equipment") { labels.armor = data.armor.value ? `${data.armor.value} ${game.i18n.localize("SW5E.AC")}` : ""; } // Activated Items - if ( data.hasOwnProperty("activation") ) { - + if (data.hasOwnProperty("activation")) { // Ability Activation Label 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(" "); // Target Label let tgt = data.target || {}; @@ -242,7 +245,7 @@ export default class Item5e extends Item { // Range Label let rng = data.range || {}; - if (["none", "touch", "self"].includes(rng.units) || (rng.value === 0)) { + if (["none", "touch", "self"].includes(rng.units) || rng.value === 0) { rng.value = null; rng.long = null; } @@ -259,7 +262,7 @@ export default class Item5e extends Item { } // Item Actions - if ( data.hasOwnProperty("actionType") ) { + if (data.hasOwnProperty("actionType")) { // if this item is owned, we populate the label and saving throw during actor init if (!this.isOwned) { // Saving throws @@ -271,17 +274,20 @@ export default class Item5e extends Item { // Damage let dam = data.damage || {}; - if ( dam.parts ) { - labels.damage = dam.parts.map(d => d[0]).join(" + ").replace(/\+ -/g, "- "); + if (dam.parts) { + labels.damage = dam.parts + .map(d => d[0]) + .join(" + ") + .replace(/\+ -/g, "- "); labels.damageTypes = dam.parts.map(d => C.damageTypes[d[1]]).join(", "); } // Limited Uses - if ( this.isOwned && !!data.uses?.max ) { + if (this.isOwned && !!data.uses?.max) { let max = data.uses.max; - if ( !Number.isNumeric(max) ) { + if (!Number.isNumeric(max)) { max = Roll.replaceFormulaData(max, this.actor.getRollData()); - if ( Roll.MATH_PROXY.safeEval ) max = Roll.MATH_PROXY.safeEval(max); + if (Roll.MATH_PROXY.safeEval) max = Roll.MATH_PROXY.safeEval(max); } data.uses.max = Number(max); } @@ -295,11 +301,11 @@ export default class Item5e extends Item { * @returns {number|null} */ getSaveDC() { - if ( !this.hasSave ) return; + if (!this.hasSave) return; const save = this.data.data?.save; // Actor power-DC based scaling - if ( save.scaling === "power" ) { + if (save.scaling === "power") { switch (this.data.data.school) { case "lgt": { save.dc = this.isOwned ? getProperty(this.actor.data, "data.attributes.powerForceLightDC") : null; @@ -321,7 +327,7 @@ export default class Item5e extends Item { } // Ability-score based scaling - else if ( save.scaling !== "flat" ) { + else if (save.scaling !== "flat") { save.dc = this.isOwned ? getProperty(this.actor.data, `data.abilities.${save.scaling}.dc`) : null; } @@ -345,43 +351,43 @@ export default class Item5e extends Item { */ getAttackToHit() { const itemData = this.data.data; - if ( !this.hasAttack || !itemData ) return; + if (!this.hasAttack || !itemData) return; const rollData = this.getRollData(); // Define Roll bonuses const parts = []; // Include the item's innate attack bonus as the initial value and label - if ( itemData.attackBonus ) { - parts.push(itemData.attackBonus) + if (itemData.attackBonus) { + parts.push(itemData.attackBonus); this.labels.toHit = itemData.attackBonus; } // Take no further action for un-owned items - if ( !this.isOwned ) return {rollData, parts}; + if (!this.isOwned) return {rollData, parts}; // Ability score modifier parts.push(`@mod`); // Add proficiency bonus if an explicit proficiency flag is present or for non-item features - if ( !["weapon", "consumable"].includes(this.data.type) || itemData.proficient ) { + if (!["weapon", "consumable"].includes(this.data.type) || itemData.proficient) { parts.push("@prof"); } // Actor-level global bonus to attack rolls const actorBonus = this.actor.data.data.bonuses?.[itemData.actionType] || {}; - if ( actorBonus.attack ) parts.push(actorBonus.attack); + if (actorBonus.attack) parts.push(actorBonus.attack); // 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; if (ammoItemData) { 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 ammoIsTypeConsumable = (ammoItemData.type === "consumable") && (ammoItemData.data.consumableType === "ammo") - if ( ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable ) { + const ammoIsTypeConsumable = ammoItemData.type === "consumable" && ammoItemData.data.consumableType === "ammo"; + if (ammoCanBeConsumed && ammoItemAttackBonus && ammoIsTypeConsumable) { parts.push("@ammo"); rollData["ammo"] = ammoItemAttackBonus; } @@ -389,9 +395,9 @@ export default class Item5e extends Item { } // Condense the resulting attack bonus formula into a simplified label - let toHitLabel = simplifyRollFormula(parts.join('+'), rollData).trim(); - if (toHitLabel.charAt(0) !== '-') { - toHitLabel = '+ ' + toHitLabel + let toHitLabel = simplifyRollFormula(parts.join("+"), rollData).trim(); + if (toHitLabel.charAt(0) !== "-") { + toHitLabel = "+ " + toHitLabel; } this.labels.toHit = toHitLabel; @@ -409,34 +415,35 @@ export default class Item5e extends Item { * the prepared chat message data (if false). * @return {Promise} */ - async roll({configureDialog=true, rollMode, createMessage=true}={}) { + async roll({configureDialog = true, rollMode, createMessage = true} = {}) { let item = this; const actor = this.actor; // Reference aspects of the item data necessary for usage - const id = this.data.data; // Item data - const hasArea = this.hasAreaTarget; // Is the ability usage an AoE? - const resource = id.consume || {}; // Resource consumption - const recharge = id.recharge || {}; // Recharge mechanic - const uses = id?.uses ?? {}; // Limited uses - const isPower = this.type === "power"; // Does the item require a power slot? + const id = this.data.data; // Item data + const hasArea = this.hasAreaTarget; // Is the ability usage an AoE? + const resource = id.consume || {}; // Resource consumption + const recharge = id.recharge || {}; // Recharge mechanic + const uses = id?.uses ?? {}; // Limited uses + const isPower = this.type === "power"; // Does the item require a power slot? // TODO: Possibly Mod this to not consume slots based on class? - 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 - let createMeasuredTemplate = hasArea; // Trigger a template creation - let consumeRecharge = !!recharge.value; // Consume recharge - let consumeResource = !!resource.target && resource.type !== "ammo" && !['simpleB', 'martialB'].includes(id.weaponType); // Consume a linked (non-ammo) resource, ignore if use is from a blaster - let consumePowerSlot = requirePowerSlot; // Consume a power slot - let consumeUsage = !!uses.per; // Consume limited uses - let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of uses + let createMeasuredTemplate = hasArea; // Trigger a template creation + let consumeRecharge = !!recharge.value; // Consume recharge + let consumeResource = + !!resource.target && resource.type !== "ammo" && !["simpleB", "martialB"].includes(id.weaponType); // Consume a linked (non-ammo) resource, ignore if use is from a blaster + let consumePowerSlot = requirePowerSlot; // Consume a power slot + let consumeUsage = !!uses.per; // Consume limited uses + let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of uses - // Display a configuration dialog to customize the usage - const needsConfiguration = createMeasuredTemplate || consumeRecharge || consumeResource || consumePowerSlot || consumeUsage; + // Display a configuration dialog to customize the usage + const needsConfiguration = + createMeasuredTemplate || consumeRecharge || consumeResource || consumePowerSlot || consumeUsage; if (configureDialog && needsConfiguration) { const configuration = await AbilityUseDialog.create(this); if (!configuration) return; - // Determine consumption preferences createMeasuredTemplate = Boolean(configuration.placeTemplate); @@ -446,37 +453,43 @@ export default class Item5e extends Item { consumePowerSlot = Boolean(configuration.consumeSlot); // Handle power upcasting - if ( requirePowerSlot ) { + if (requirePowerSlot) { const slotLevel = configuration.level; const powerLevel = parseInt(slotLevel); - + if (powerLevel !== id.level) { const upcastData = mergeObject(this.data, {"data.level": powerLevel}, {inplace: false}); - item = this.constructor.createOwned(upcastData, actor); // Replace the item with an upcast version + item = this.constructor.createOwned(upcastData, actor); // Replace the item with an upcast version } - if ( consumePowerSlot ) consumePowerSlot = `power${powerLevel}`; + if (consumePowerSlot) consumePowerSlot = `power${powerLevel}`; } } // Determine whether the item can be used by testing for resource consumption - const usage = item._getUsageUpdates({consumeRecharge, consumeResource, consumePowerSlot, consumeUsage, consumeQuantity}); - if ( !usage ) return; - + const usage = item._getUsageUpdates({ + consumeRecharge, + consumeResource, + consumePowerSlot, + consumeUsage, + consumeQuantity + }); + if (!usage) return; + const {actorUpdates, itemUpdates, resourceUpdates} = usage; // Commit pending data updates - if ( !isObjectEmpty(itemUpdates) ) await item.update(itemUpdates); - if ( consumeQuantity && (item.data.data.quantity === 0) ) await item.delete(); - if ( !isObjectEmpty(actorUpdates) ) await actor.update(actorUpdates); - if ( !isObjectEmpty(resourceUpdates) ) { + if (!isObjectEmpty(itemUpdates)) await item.update(itemUpdates); + if (consumeQuantity && item.data.data.quantity === 0) await item.delete(); + if (!isObjectEmpty(actorUpdates)) await actor.update(actorUpdates); + if (!isObjectEmpty(resourceUpdates)) { const resource = actor.items.get(id.consume?.target); - if ( resource ) await resource.update(resourceUpdates); + if (resource) await resource.update(resourceUpdates); } // Initiate measured template creation - if ( createMeasuredTemplate ) { + if (createMeasuredTemplate) { const template = game.sw5e.canvas.AbilityTemplate.fromItem(item); - if ( template ) template.drawPreview(); + if (template) template.drawPreview(); } // Create or return the Chat Message data @@ -496,8 +509,13 @@ export default class Item5e extends Item { * @returns {object|boolean} A set of data changes to apply when the item is used, or false * @private */ - _getUsageUpdates({consumeQuantity=false, consumeRecharge=false, consumeResource=false, consumePowerSlot=false, consumeUsage=false}) { - + _getUsageUpdates({ + consumeQuantity = false, + consumeRecharge = false, + consumeResource = false, + consumePowerSlot = false, + consumeUsage = false + }) { // Reference item data const id = this.data.data; const actorUpdates = {}; @@ -505,9 +523,9 @@ export default class Item5e extends Item { const resourceUpdates = {}; // Consume Recharge - if ( consumeRecharge ) { + if (consumeRecharge) { const recharge = id.recharge || {}; - if ( recharge.charged === false ) { + if (recharge.charged === false) { ui.notifications.warn(game.i18n.format("SW5E.ItemNoUses", {name: this.name})); return false; } @@ -515,25 +533,25 @@ export default class Item5e extends Item { } // Consume Limited Resource - if ( consumeResource ) { + if (consumeResource) { const canConsume = this._handleConsumeResource(itemUpdates, actorUpdates, resourceUpdates); - if ( canConsume === false ) return false; + if (canConsume === false) return false; } // Consume Power Slots and Force/Tech Points - if ( consumePowerSlot ) { + if (consumePowerSlot) { const level = this.actor?.data.data.powers[consumePowerSlot]; const fp = this.actor.data.data.attributes.force.points; const tp = this.actor.data.data.attributes.tech.points; const powerCost = id.level + 1; - const innatePower = this.actor.data.data.attributes.powercasting === 'innate'; - if (!innatePower){ - switch (id.school){ + const innatePower = this.actor.data.data.attributes.powercasting === "innate"; + if (!innatePower) { + switch (id.school) { case "lgt": case "uni": case "drk": { const powers = Number(level?.fvalue ?? 0); - if ( powers === 0 ) { + if (powers === 0) { const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`); ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})); return false; @@ -541,7 +559,7 @@ export default class Item5e extends Item { actorUpdates[`data.powers.${consumePowerSlot}.fvalue`] = Math.max(powers - 1, 0); if (fp.temp >= powerCost) { actorUpdates["data.attributes.force.points.temp"] = fp.temp - powerCost; - }else{ + } else { actorUpdates["data.attributes.force.points.value"] = fp.value + fp.temp - powerCost; actorUpdates["data.attributes.force.points.temp"] = 0; } @@ -549,7 +567,7 @@ export default class Item5e extends Item { } case "tec": { const powers = Number(level?.tvalue ?? 0); - if ( powers === 0 ) { + if (powers === 0) { const label = game.i18n.localize(`SW5E.PowerLevel${id.level}`); ui.notifications.warn(game.i18n.format("SW5E.PowerCastNoSlots", {name: this.name, level: label})); return false; @@ -557,7 +575,7 @@ export default class Item5e extends Item { actorUpdates[`data.powers.${consumePowerSlot}.tvalue`] = Math.max(powers - 1, 0); if (tp.temp >= powerCost) { actorUpdates["data.attributes.tech.points.temp"] = tp.temp - powerCost; - }else{ + } else { actorUpdates["data.attributes.tech.points.value"] = tp.value + tp.temp - powerCost; actorUpdates["data.attributes.tech.points.temp"] = 0; } @@ -566,25 +584,24 @@ export default class Item5e extends Item { } } } - // Consume Limited Usage - if ( consumeUsage ) { + if (consumeUsage) { const uses = id.uses || {}; const available = Number(uses.value ?? 0); let used = false; // Reduce usages const remaining = Math.max(available - 1, 0); - if ( available >= 1 ) { + if (available >= 1) { used = true; itemUpdates["data.uses.value"] = remaining; } // 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); - if ( q >= 1 ) { + if (q >= 1) { used = true; itemUpdates["data.quantity"] = Math.max(q - 1, 0); itemUpdates["data.uses.value"] = uses.max ?? 1; @@ -592,7 +609,7 @@ export default class Item5e extends Item { } // If the item was not used, return a warning - if ( !used ) { + if (!used) { ui.notifications.warn(game.i18n.format("SW5E.ItemNoUses", {name: this.name})); return false; } @@ -616,11 +633,11 @@ export default class Item5e extends Item { const actor = this.actor; const itemData = this.data.data; const consume = itemData.consume || {}; - if ( !consume.type ) return; + if (!consume.type) return; // No consumed target 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})); return false; } @@ -629,7 +646,7 @@ export default class Item5e extends Item { let resource = null; let amount = Number(consume.amount ?? 1); let quantity = 0; - switch ( consume.type ) { + switch (consume.type) { case "attribute": resource = getProperty(actor.data.data, consume.target); quantity = resource || 0; @@ -641,10 +658,10 @@ export default class Item5e extends Item { break; case "charges": resource = actor.items.get(consume.target); - if ( !resource ) break; + if (!resource) break; const uses = resource.data.data.uses; - if ( uses.per && uses.max ) quantity = uses.value; - else if ( resource.data.data.recharge?.value ) { + if (uses.per && uses.max) quantity = uses.value; + else if (resource.data.data.recharge?.value) { quantity = resource.data.data.recharge.charged ? 1 : 0; amount = 1; } @@ -652,20 +669,20 @@ export default class Item5e extends Item { } // Verify that a consumed resource is available - if ( !resource ) { + if (!resource) { ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoSource", {name: this.name, type: typeLabel})); return false; } // Verify that the required quantity is available let remaining = quantity - amount; - if ( remaining < 0 ) { + if (remaining < 0) { ui.notifications.warn(game.i18n.format("SW5E.ConsumeWarningNoQuantity", {name: this.name, type: typeLabel})); return false; } // Define updates to provided data objects - switch ( consume.type ) { + switch (consume.type) { case "attribute": actorUpdates[`data.${consume.target}`] = remaining; break; @@ -676,8 +693,8 @@ export default class Item5e extends Item { case "charges": const uses = resource.data.data.uses || {}; const recharge = resource.data.data.recharge || {}; - if ( uses.per && uses.max ) resourceUpdates["data.uses.value"] = remaining; - else if ( recharge.value ) resourceUpdates["data.recharge.charged"] = false; + if (uses.per && uses.max) resourceUpdates["data.uses.value"] = remaining; + else if (recharge.value) resourceUpdates["data.recharge.charged"] = false; break; } } @@ -691,8 +708,7 @@ export default class Item5e extends Item { * @param {boolean} createMessage Whether to automatically create a ChatMessage entity (if true), or only return * the prepared message data (if false) */ - async displayCard({rollMode, createMessage=true}={}) { - + async displayCard({rollMode, createMessage = true} = {}) { // Basic template rendering data const token = this.actor.token; const templateData = { @@ -726,7 +742,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 ( (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; } @@ -746,7 +762,7 @@ export default class Item5e extends Item { * @param {Object} htmlOptions Options used by the TextEditor.enrichHTML function * @return {Object} An object of chat data to render */ - getChatData(htmlOptions={}) { + getChatData(htmlOptions = {}) { const data = duplicate(this.data.data); const labels = this.labels; @@ -756,19 +772,20 @@ export default class Item5e extends Item { // Item type specific properties const props = []; const fn = this[`_${this.data.type}ChatData`]; - if ( fn ) fn.bind(this)(data, labels, props); + if (fn) fn.bind(this)(data, labels, props); // Equipment properties - 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.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])); props.push( 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") ); } // Ability activation properties - if ( data.hasOwnProperty("activation") ) { + if (data.hasOwnProperty("activation")) { props.push( labels.activation + (data.activation?.condition ? ` (${data.activation.condition})` : ""), labels.target, @@ -803,9 +820,7 @@ export default class Item5e extends Item { * @private */ _weaponChatData(data, labels, props) { - props.push( - CONFIG.SW5E.weaponTypes[data.weaponType], - ); + props.push(CONFIG.SW5E.weaponTypes[data.weaponType]); } /* -------------------------------------------- */ @@ -829,10 +844,7 @@ export default class Item5e extends Item { * @private */ _toolChatData(data, labels, props) { - props.push( - CONFIG.SW5E.abilities[data.ability] || null, - CONFIG.SW5E.proficiencyLevels[data.proficient || 0] - ); + props.push(CONFIG.SW5E.abilities[data.ability] || null, CONFIG.SW5E.proficiencyLevels[data.proficient || 0]); } /* -------------------------------------------- */ @@ -856,10 +868,7 @@ export default class Item5e extends Item { * @private */ _powerChatData(data, labels, props) { - props.push( - labels.level, - labels.components + (labels.materials ? ` (${labels.materials})` : "") - ); + props.push(labels.level, labels.components + (labels.materials ? ` (${labels.materials})` : "")); } /* -------------------------------------------- */ @@ -883,10 +892,10 @@ export default class Item5e extends Item { * @param {object} options Roll options which are configured and provided to the d20Roll function * @return {Promise} A Promise which resolves to the created Roll instance */ - async rollAttack(options={}) { + async rollAttack(options = {}) { const itemData = this.data.data; const flags = this.actor.data.flags.sw5e || {}; - if ( !this.hasAttack ) { + if (!this.hasAttack) { throw new Error("You may not place an Attack Roll with this Item."); } let title = `${this.name} - ${game.i18n.localize("SW5E.AttackRoll")}`; @@ -899,12 +908,12 @@ export default class Item5e extends Item { let ammo = null; let ammoUpdate = null; const consume = itemData.consume; - if ( consume?.type === "ammo" ) { + if (consume?.type === "ammo") { ammo = this.actor.items.get(consume.target); if (ammo?.data) { const q = ammo.data.data.quantity; const consumeAmount = consume.amount ?? 0; - if ( q && (q - consumeAmount >= 0) ) { + if (q && q - consumeAmount >= 0) { this._ammo = ammo; title += ` [${ammo.name}]`; } @@ -912,50 +921,53 @@ export default class Item5e extends Item { // Get pending ammunition update const usage = this._getUsageUpdates({consumeResource: true}); - if ( usage === false ) return null; + if (usage === false) return null; ammoUpdate = usage.resourceUpdates || {}; } // Compose roll options - const rollConfig = mergeObject({ - parts: parts, - actor: this.actor, - data: rollData, - title: title, - flavor: title, - speaker: ChatMessage.getSpeaker({actor: this.actor}), - dialogOptions: { - width: 400, - top: options.event ? options.event.clientY - 80 : null, - left: window.innerWidth - 710 + const rollConfig = mergeObject( + { + parts: parts, + actor: this.actor, + data: rollData, + title: title, + flavor: title, + speaker: ChatMessage.getSpeaker({actor: this.actor}), + dialogOptions: { + width: 400, + top: options.event ? options.event.clientY - 80 : null, + 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; // Expanded critical hit thresholds - if (( this.data.type === "weapon" ) && flags.weaponCriticalThreshold) { + if (this.data.type === "weapon" && 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); } // Elven Accuracy - if ( ["weapon", "power"].includes(this.data.type) ) { + if (["weapon", "power"].includes(this.data.type)) { if (flags.elvenAccuracy && ["dex", "int", "wis", "cha"].includes(this.abilityMod)) { rollConfig.elvenAccuracy = true; } } // Apply Halfling Lucky - if ( flags.halflingLucky ) rollConfig.halflingLucky = true; + if (flags.halflingLucky) rollConfig.halflingLucky = true; // Invoke the d20 roll helper const roll = await d20Roll(rollConfig); - if ( roll === false ) return null; + if (roll === false) return null; // Commit ammunition consumption on attack rolls resource consumption if the attack roll was made - if ( ammo && !isObjectEmpty(ammoUpdate) ) await ammo.update(ammoUpdate); + if (ammo && !isObjectEmpty(ammoUpdate)) await ammo.update(ammoUpdate); return roll; } @@ -971,16 +983,16 @@ export default class Item5e extends Item { * @param {object} [options] Additional options passed to the damageRoll function * @return {Promise} A Promise which resolves to the created Roll instance */ - rollDamage({critical=false, event=null, powerLevel=null, versatile=false, options={}}={}) { - if ( !this.hasDamage ) throw new Error("You may not make a Damage Roll with this Item."); + rollDamage({critical = false, event = null, powerLevel = null, versatile = false, options = {}} = {}) { + if (!this.hasDamage) throw new Error("You may not make a Damage Roll with this Item."); const itemData = this.data.data; const actorData = this.actor.data.data; - const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id }}; + const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id}}; // Get roll data const parts = itemData.damage.parts.map(d => d[0]); const rollData = this.getRollData(); - if ( powerLevel ) rollData.item.level = powerLevel; + if (powerLevel) rollData.item.level = powerLevel; // Configure the damage roll const actionFlavor = game.i18n.localize(itemData.actionType === "heal" ? "SW5E.Healing" : "SW5E.DamageRoll"); @@ -1004,18 +1016,17 @@ export default class Item5e extends Item { }; // Adjust damage from versatile usage - if ( versatile && itemData.damage.versatile ) { + if (versatile && itemData.damage.versatile) { parts[0] = itemData.damage.versatile; messageData["flags.sw5e.roll"].versatile = true; } // Scale damage from up-casting powers - if ( (this.data.type === "power") ) { - if ( (itemData.scaling.mode === "atwill") ) { + if (this.data.type === "power") { + if (itemData.scaling.mode === "atwill") { const level = this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel; 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; this._scalePowerDamage(parts, itemData.level, powerLevel, scaling, rollData); } @@ -1023,7 +1034,7 @@ export default class Item5e extends Item { // Add damage bonus formula 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); } @@ -1031,7 +1042,7 @@ export default class Item5e extends Item { const ammoData = this._ammo?.data; // 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"); rollData["ammo"] = ammoData.data.damage.parts.map(p => p[0]).join("+"); rollConfig.flavor += ` [${this._ammo.name}]`; @@ -1039,7 +1050,7 @@ export default class Item5e extends Item { } // Scale melee critical hit damage - if ( itemData.actionType === "mwak" ) { + if (itemData.actionType === "mwak") { rollConfig.criticalBonusDice = this.actor.getFlag("sw5e", "meleeCriticalDamageDice") ?? 0; } @@ -1055,7 +1066,7 @@ export default class Item5e extends Item { */ _scaleAtWillDamage(parts, scale, level, rollData) { const add = Math.floor((level + 1) / 6); - if ( add === 0 ) return; + if (add === 0) return; this._scaleDamage(parts, scale || parts.join(" + "), add, rollData); } @@ -1073,7 +1084,7 @@ export default class Item5e extends Item { */ _scalePowerDamage(parts, baseLevel, powerLevel, formula, rollData) { const upcastLevels = Math.max(powerLevel - baseLevel, 0); - if ( upcastLevels === 0 ) return parts; + if (upcastLevels === 0) return parts; this._scaleDamage(parts, formula, upcastLevels, rollData); } @@ -1089,16 +1100,16 @@ export default class Item5e extends Item { * @private */ _scaleDamage(parts, scaling, times, rollData) { - if ( times <= 0 ) return parts; + if (times <= 0) return parts; const p0 = new Roll(parts[0], rollData); const s = new Roll(scaling, rollData).alter(times); // Attempt to simplify by combining like dice terms 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 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; parts[0] = p0.formula; simplified = true; @@ -1106,7 +1117,7 @@ export default class Item5e extends Item { } // Otherwise add to the first part - if ( !simplified ) { + if (!simplified) { parts[0] = `${parts[0]} + ${s.formula}`; } return parts; @@ -1120,14 +1131,14 @@ export default class Item5e extends Item { * * @return {Promise} A Promise which resolves to the created Roll instance */ - async rollFormula(options={}) { - if ( !this.data.data.formula ) { + async rollFormula(options = {}) { + if (!this.data.data.formula) { throw new Error("This Item does not have a formula to roll!"); } // Define Roll Data const rollData = this.getRollData(); - if ( options.powerLevel ) rollData.item.level = options.powerLevel; + if (options.powerLevel) rollData.item.level = options.powerLevel; const title = `${this.name} - ${game.i18n.localize("SW5E.OtherFormula")}`; // Invoke the roll and submit it to chat @@ -1136,7 +1147,7 @@ export default class Item5e extends Item { speaker: ChatMessage.getSpeaker({actor: this.actor}), flavor: title, rollMode: game.settings.get("core", "rollMode"), - messageData: {"flags.sw5e.roll": {type: "other", itemId: this.id }} + messageData: {"flags.sw5e.roll": {type: "other", itemId: this.id}} }); return roll; } @@ -1149,20 +1160,24 @@ export default class Item5e extends Item { */ async rollRecharge() { const data = this.data.data; - if ( !data.recharge.value ) return; + if (!data.recharge.value) return; // Roll the check const roll = new Roll("1d6").roll(); const success = roll.total >= parseInt(data.recharge.value); // Display a Chat Message - const promises = [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}) - })]; + const promises = [ + 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}) + }) + ]; // Update the Item data - if ( success ) promises.push(this.update({"data.recharge.charged": true})); + if (success) promises.push(this.update({"data.recharge.charged": true})); return Promise.all(promises).then(() => roll); } @@ -1173,8 +1188,8 @@ export default class Item5e extends Item { * @prarm {Object} options Roll configuration options provided to the d20Roll function * @return {Promise} A Promise which resolves to the created Roll instance */ - rollToolCheck(options={}) { - if ( this.type !== "tool" ) throw "Wrong item type!"; + rollToolCheck(options = {}) { + if (this.type !== "tool") throw "Wrong item type!"; // Prepare roll data let rollData = this.getRollData(); @@ -1182,22 +1197,25 @@ export default class Item5e extends Item { const title = `${this.name} - ${game.i18n.localize("SW5E.ToolCheck")}`; // Compose the roll data - const rollConfig = mergeObject({ - parts: parts, - data: rollData, - template: "systems/sw5e/templates/chat/tool-roll-dialog.html", - title: title, - speaker: ChatMessage.getSpeaker({actor: this.actor}), - flavor: title, - dialogOptions: { - width: 400, - top: options.event ? options.event.clientY - 80 : null, - left: window.innerWidth - 710, + const rollConfig = mergeObject( + { + parts: parts, + data: rollData, + template: "systems/sw5e/templates/chat/tool-roll-dialog.html", + title: title, + speaker: ChatMessage.getSpeaker({actor: this.actor}), + flavor: title, + dialogOptions: { + width: 400, + top: options.event ? options.event.clientY - 80 : null, + left: window.innerWidth - 710 + }, + halflingLucky: this.actor.getFlag("sw5e", "halflingLucky") || false, + reliableTalent: this.data.data.proficient >= 1 && this.actor.getFlag("sw5e", "reliableTalent"), + messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id}} }, - halflingLucky: this.actor.getFlag("sw5e", "halflingLucky" ) || false, - reliableTalent: (this.data.data.proficient >= 1) && this.actor.getFlag("sw5e", "reliableTalent"), - messageData: {"flags.sw5e.roll": {type: "tool", itemId: this.id }} - }, options); + options + ); rollConfig.event = options.event; // Call the roll helper utility @@ -1211,19 +1229,19 @@ export default class Item5e extends Item { * @private */ getRollData() { - if ( !this.actor ) return null; + if (!this.actor) return null; const rollData = this.actor.getRollData(); rollData.item = duplicate(this.data.data); // Include an ability score modifier if one exists const abl = this.abilityMod; - if ( abl ) { + if (abl) { const ability = rollData.abilities[abl]; rollData["mod"] = ability.mod || 0; } // 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)); return rollData; } @@ -1233,8 +1251,8 @@ export default class Item5e extends Item { /* -------------------------------------------- */ static chatListeners(html) { - html.on('click', '.card-buttons button', this._onChatCardAction.bind(this)); - html.on('click', '.item-name', this._onChatCardToggleContent.bind(this)); + html.on("click", ".card-buttons button", this._onChatCardAction.bind(this)); + html.on("click", ".item-name", this._onChatCardToggleContent.bind(this)); } /* -------------------------------------------- */ @@ -1253,29 +1271,32 @@ export default class Item5e extends Item { button.disabled = true; const card = button.closest(".chat-card"); const messageId = card.closest(".message").dataset.messageId; - const message = game.messages.get(messageId); + const message = game.messages.get(messageId); const action = button.dataset.action; // Validate permission to proceed with the roll const isTargetted = action === "save"; - if ( !( isTargetted || game.user.isGM || message.isAuthor ) ) return; + if (!(isTargetted || game.user.isGM || message.isAuthor)) return; // Recover the actor for the chat card const actor = this._getChatCardActor(card); - if ( !actor ) return; + if (!actor) return; // Get the Item from stored flag data or by the item ID on the Actor const storedData = message.getFlag("sw5e", "itemData"); const item = storedData ? this.createOwned(storedData, actor) : actor.getOwnedItem(card.dataset.itemId); - if ( !item ) { - return ui.notifications.error(game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name})) + if (!item) { + return ui.notifications.error( + game.i18n.format("SW5E.ActionWarningNoItem", {item: card.dataset.itemId, name: actor.name}) + ); } const powerLevel = parseInt(card.dataset.powerLevel) || null; // Handle different actions - switch ( action ) { + switch (action) { case "attack": - await item.rollAttack({event}); break; + await item.rollAttack({event}); + break; case "damage": case "versatile": await item.rollDamage({ @@ -1286,19 +1307,21 @@ export default class Item5e extends Item { }); break; case "formula": - await item.rollFormula({event, powerLevel}); break; + await item.rollFormula({event, powerLevel}); + break; case "save": const targets = this._getChatCardTargets(card); - for ( let token of targets ) { + for (let token of targets) { const speaker = ChatMessage.getSpeaker({scene: canvas.scene, token: token}); - await token.actor.rollAbilitySave(button.dataset.ability, { event, speaker }); + await token.actor.rollAbilitySave(button.dataset.ability, {event, speaker}); } break; case "toolCheck": - await item.rollToolCheck({event}); break; + await item.rollToolCheck({event}); + break; case "placeTemplate": const template = game.sw5e.canvas.AbilityTemplate.fromItem(item); - if ( template ) template.drawPreview(); + if (template) template.drawPreview(); break; } @@ -1330,7 +1353,6 @@ export default class Item5e extends Item { * @private */ static _getChatCardActor(card) { - // Case 1 - a synthetic actor from a Token const tokenKey = card.dataset.tokenId; if (tokenKey) { @@ -1358,8 +1380,8 @@ export default class Item5e extends Item { */ static _getChatCardTargets(card) { 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 ) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken")); + if (!targets.length && game.user.character) targets = targets.concat(game.user.character.getActiveTokens()); + if (!targets.length) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken")); return targets; } @@ -1374,7 +1396,6 @@ export default class Item5e extends Item { * @private */ static async createScrollFromPower(power) { - // Get power data const itemData = power instanceof Item5e ? power.data : power; const {actionType, description, source, activation, duration, target, range, damage, save, level} = itemData.data; @@ -1387,7 +1408,7 @@ export default class Item5e extends Item { // Split the scroll description into an intro paragraph and the remaining details const scrollDescription = scrollData.data.description.value; - const pdel = '

'; + const pdel = "

"; const scrollIntroEnd = scrollDescription.indexOf(pdel); const scrollIntro = scrollDescription.slice(0, scrollIntroEnd + pdel.length); const scrollDetails = scrollDescription.slice(scrollIntroEnd + pdel.length); diff --git a/module/item/sheet.js b/module/item/sheet.js index 742e4d6f..65ff9778 100644 --- a/module/item/sheet.js +++ b/module/item/sheet.js @@ -1,5 +1,5 @@ import TraitSelector from "../apps/trait-selector.js"; -import { onManageActiveEffect, prepareActiveEffectCategories } from "../effects.js"; +import {onManageActiveEffect, prepareActiveEffectCategories} from "../effects.js"; /** * Override and extend the core ItemSheet implementation to handle specific item types @@ -26,7 +26,7 @@ export default class ItemSheet5e extends ItemSheet { classes: ["sw5e", "sheet", "item"], resizable: true, scrollY: [".tab.details"], - tabs: [{ navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description" }] + tabs: [{navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description"}] }); } @@ -96,7 +96,7 @@ export default class ItemSheet5e extends ItemSheet { } return ammo; }, - { [item._id]: `${item.name} (${item.data.quantity})` } + {[item._id]: `${item.name} (${item.data.quantity})`} ); } @@ -127,8 +127,8 @@ export default class ItemSheet5e extends ItemSheet { if (uses.per && uses.max) { const label = uses.per === "charges" - ? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", { value: uses.value })})` - : ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", { max: uses.max, per: uses.per })})`; + ? ` (${game.i18n.format("SW5E.AbilityUseChargesLabel", {value: uses.value})})` + : ` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", {max: uses.max, per: uses.per})})`; obj[i.id] = i.name + label; } @@ -171,8 +171,8 @@ export default class ItemSheet5e extends ItemSheet { if (item.type === "weapon") { props.push( ...Object.entries(item.data.properties) - .filter((e) => e[1] === true) - .map((e) => CONFIG.SW5E.weaponProperties[e[0]]) + .filter(e => e[1] === true) + .map(e => CONFIG.SW5E.weaponProperties[e[0]]) ); } else if (item.type === "power") { props.push( @@ -211,7 +211,7 @@ export default class ItemSheet5e extends ItemSheet { if (item.type !== "weapon" && item.data.activation && !isObjectEmpty(item.data.activation)) { props.push(labels.activation, labels.range, labels.target, labels.duration); } - return props.filter((p) => !!p); + return props.filter(p => !!p); } /* -------------------------------------------- */ @@ -249,14 +249,14 @@ export default class ItemSheet5e extends ItemSheet { /** @override */ _getSubmitData(updateData = {}) { // Create the expanded update data object - const fd = new FormDataExtended(this.form, { editors: this.editors }); + const fd = new FormDataExtended(this.form, {editors: this.editors}); let data = fd.toObject(); if (updateData) data = mergeObject(data, updateData); else data = expandObject(data); // Handle Damage array const damage = data.data?.damage; - if (damage) damage.parts = Object.values(damage?.parts || {}).map((d) => [d[0] || "", d[1] || ""]); + if (damage) damage.parts = Object.values(damage?.parts || {}).map(d => [d[0] || "", d[1] || ""]); // Return the flattened submission data return flattenObject(data); @@ -270,7 +270,7 @@ export default class ItemSheet5e extends ItemSheet { if (this.isEditable) { html.find(".damage-control").click(this._onDamageControl.bind(this)); html.find(".trait-selector.class-skills").click(this._onConfigureClassSkills.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." @@ -296,7 +296,7 @@ export default class ItemSheet5e extends ItemSheet { if (a.classList.contains("add-damage")) { await this._onSubmit(event); // Submit any unsaved changes const damage = this.item.data.data.damage; - return this.item.update({ "data.damage.parts": damage.parts.concat([["", ""]]) }); + return this.item.update({"data.damage.parts": damage.parts.concat([["", ""]])}); } // Remove a damage component @@ -305,7 +305,7 @@ export default class ItemSheet5e extends ItemSheet { const li = a.closest(".damage-part"); const damage = duplicate(this.item.data.data.damage); damage.parts.splice(Number(li.dataset.damagePart), 1); - return this.item.update({ "data.damage.parts": damage.parts }); + return this.item.update({"data.damage.parts": damage.parts}); } } diff --git a/module/macros.js b/module/macros.js index 96bb3419..d58b7039 100644 --- a/module/macros.js +++ b/module/macros.js @@ -1,4 +1,3 @@ - /* -------------------------------------------- */ /* Hotbar Macros */ /* -------------------------------------------- */ @@ -11,14 +10,14 @@ * @returns {Promise} */ export async function create5eMacro(data, slot) { - if ( data.type !== "Item" ) return; - if (!( "data" in data ) ) return ui.notifications.warn("You can only create macro buttons for owned Items"); + if (data.type !== "Item") return; + if (!("data" in data)) return ui.notifications.warn("You can only create macro buttons for owned Items"); const item = data.data; // Create the macro command const command = `game.sw5e.rollItemMacro("${item.name}");`; - let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command)); - if ( !macro ) { + let macro = game.macros.entities.find(m => m.name === item.name && m.command === command); + if (!macro) { macro = await Macro.create({ name: item.name, type: "script", @@ -42,14 +41,16 @@ export async function create5eMacro(data, slot) { export function rollItemMacro(itemName) { const speaker = ChatMessage.getSpeaker(); let actor; - if ( speaker.token ) actor = game.actors.tokens[speaker.token]; - if ( !actor ) actor = game.actors.get(speaker.actor); + if (speaker.token) actor = game.actors.tokens[speaker.token]; + if (!actor) actor = game.actors.get(speaker.actor); // Get matching items const items = actor ? actor.items.filter(i => i.name === itemName) : []; - 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.`); - } else if ( items.length === 0 ) { + 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.` + ); + } else if (items.length === 0) { return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`); } const item = items[0]; diff --git a/module/migration.js b/module/migration.js index 60401489..1065b5af 100644 --- a/module/migration.js +++ b/module/migration.js @@ -2,56 +2,59 @@ * Perform a system migration for the entire World, applying migrations for Actors, Items, and Compendium packs * @return {Promise} A Promise which resolves once the migration is completed */ -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}); +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} + ); // Migrate World Actors - for await ( let a of game.actors.entities ) { + for await (let a of game.actors.entities) { try { console.log(`Checking Actor entity ${a.name} for migration needs`); const updateData = await migrateActorData(a.data); - if ( !isObjectEmpty(updateData) ) { + if (!isObjectEmpty(updateData)) { console.log(`Migrating Actor entity ${a.name}`); await a.update(updateData, {enforceTypes: false}); } - } catch(err) { + } catch (err) { err.message = `Failed sw5e system migration for Actor ${a.name}: ${err.message}`; console.error(err); } } // Migrate World Items - for ( let i of game.items.entities ) { + for (let i of game.items.entities) { try { const updateData = migrateItemData(i.data); - if ( !isObjectEmpty(updateData) ) { + if (!isObjectEmpty(updateData)) { console.log(`Migrating Item entity ${i.name}`); await i.update(updateData, {enforceTypes: false}); } - } catch(err) { + } catch (err) { err.message = `Failed sw5e system migration for Item ${i.name}: ${err.message}`; console.error(err); } } // Migrate Actor Override Tokens - for ( let s of game.scenes.entities ) { + for (let s of game.scenes.entities) { try { const updateData = await migrateSceneData(s.data); - if ( !isObjectEmpty(updateData) ) { + if (!isObjectEmpty(updateData)) { console.log(`Migrating Scene entity ${s.name}`); await s.update(updateData, {enforceTypes: false}); } - } catch(err) { + } catch (err) { err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`; console.error(err); } } // Migrate World Compendium Packs - for ( let p of game.packs ) { - if ( p.metadata.package !== "world" ) continue; - if ( !["Actor", "Item", "Scene"].includes(p.metadata.entity) ) continue; + for (let p of game.packs) { + if (p.metadata.package !== "world") continue; + if (!["Actor", "Item", "Scene"].includes(p.metadata.entity)) continue; await migrateCompendium(p); } @@ -67,9 +70,9 @@ export const migrateWorld = async function() { * @param pack * @return {Promise} */ -export const migrateCompendium = async function(pack) { +export const migrateCompendium = async function (pack) { const entity = pack.metadata.entity; - if ( !["Actor", "Item", "Scene"].includes(entity) ) return; + if (!["Actor", "Item", "Scene"].includes(entity)) return; // Unlock the pack for editing const wasLocked = pack.locked; @@ -80,7 +83,7 @@ export const migrateCompendium = async function(pack) { const content = await pack.getContent(); // Iterate over compendium entries - applying fine-tuned migration functions - for await ( let ent of content ) { + for await (let ent of content) { let updateData = {}; try { switch (entity) { @@ -94,16 +97,14 @@ export const migrateCompendium = async function(pack) { updateData = await migrateSceneData(ent.data); break; } - if ( isObjectEmpty(updateData) ) continue; + if (isObjectEmpty(updateData)) continue; // Save the entry, if data was changed updateData["_id"] = ent._id; await pack.updateEntity(updateData); console.log(`Migrated ${entity} entity ${ent.name} in Compendium ${pack.collection}`); - } - - // Handle migration failures - catch(err) { + } catch (err) { + // Handle migration failures err.message = `Failed sw5e system migration for entity ${ent.name} in pack ${pack.collection}: ${err.message}`; console.error(err); } @@ -124,7 +125,7 @@ export const migrateCompendium = async function(pack) { * @param {object} actor The actor data object to update * @return {Object} The updateData to apply */ -export const migrateActorData = async function(actor) { +export const migrateActorData = async function (actor) { const updateData = {}; // Actor Data Updates @@ -132,7 +133,7 @@ export const migrateActorData = async function(actor) { _migrateActorSenses(actor, updateData); // Migrate Owned Items - if ( !!actor.items ) { + if (!!actor.items) { let hasItemUpdates = false; const items = await actor.items.reduce(async (memo, i) => { const results = await memo; @@ -141,21 +142,21 @@ export const migrateActorData = async function(actor) { let itemUpdate = await migrateActorItemData(i, actor); // Prepared, Equipped, and Proficient for NPC actors - if ( actor.type === "npc" ) { + if (actor.type === "npc") { if (getProperty(i.data, "preparation.prepared") === false) itemUpdate["data.preparation.prepared"] = true; if (getProperty(i.data, "equipped") === false) itemUpdate["data.equipped"] = true; if (getProperty(i.data, "proficient") === false) itemUpdate["data.proficient"] = true; } // Update the Owned Item - if ( !isObjectEmpty(itemUpdate) ) { + if (!isObjectEmpty(itemUpdate)) { hasItemUpdates = true; console.log(`Migrating Actor ${actor.name}'s ${i.name}`); return [...results, mergeObject(i, itemUpdate, {enforceTypes: false, inplace: false})]; } else return [...results, i]; }, []); - if ( hasItemUpdates ) updateData.items = items; + if (hasItemUpdates) updateData.items = items; } // Update NPC data with new datamodel information @@ -165,20 +166,18 @@ export const migrateActorData = async function(actor) { // migrate powers last since it relies on item classes being migrated first. _migrateActorPowers(actor, updateData); - + return updateData; }; /* -------------------------------------------- */ - /** * 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 * @return {Object} The scrubbed Actor data */ function cleanActorData(actorData) { - // Scrub system data const model = game.system.model.Actor[actorData.type]; actorData.data = filterObject(actorData.data, model); @@ -188,7 +187,7 @@ function cleanActorData(actorData) { obj[f] = null; return obj; }, {}); - if ( actorData.flags.sw5e ) { + if (actorData.flags.sw5e) { actorData.flags.sw5e = filterObject(actorData.flags.sw5e, allowedFlags); } @@ -196,14 +195,13 @@ function cleanActorData(actorData) { return actorData; } - /* -------------------------------------------- */ /** * Migrate a single Item entity to incorporate latest data model changes * @param item */ -export const migrateItemData = function(item) { +export const migrateItemData = function (item) { const updateData = {}; _migrateItemClassPowerCasting(item, updateData); _migrateItemAttunement(item, updateData); @@ -217,7 +215,7 @@ export const migrateItemData = function(item) { * @param item * @param actor */ -export const migrateActorItemData = async function(item, actor) { +export const migrateActorItemData = async function (item, actor) { const updateData = {}; _migrateItemClassPowerCasting(item, updateData); _migrateItemAttunement(item, updateData); @@ -233,24 +231,26 @@ export const migrateActorItemData = async function(item, actor) { * @param {Object} scene The Scene data to Update * @return {Object} The updateData to apply */ - export const migrateSceneData = async function(scene) { +export const migrateSceneData = async function (scene) { const tokens = duplicate(scene.tokens); return { - tokens: await Promise.all(tokens.map(async (t) => { - if (!t.actorId || t.actorLink || !t.actorData.data) { - t.actorData = {}; + tokens: await Promise.all( + tokens.map(async t => { + if (!t.actorId || t.actorLink || !t.actorData.data) { + t.actorData = {}; + return t; + } + const token = new Token(t); + if (!token.actor) { + t.actorId = null; + t.actorData = {}; + } else if (!t.actorLink) { + const updateData = await migrateActorData(token.data.actorData); + t.actorData = mergeObject(token.data.actorData, updateData); + } return t; - } - const token = new Token(t); - if ( !token.actor ) { - t.actorId = null; - t.actorData = {}; - } else if ( !t.actorLink ) { - const updateData = await migrateActorData(token.data.actorData); - t.actorData = mergeObject(token.data.actorData, updateData); - } - return t; - })) + }) + ) }; }; @@ -266,7 +266,6 @@ export const migrateActorItemData = async function(item, actor) { * @return {Object} The updated Actor */ function _updateNPCData(actor) { - let actorData = actor.data; const updateData = {}; // check for flag.core, if not there is no compendium monster so exit @@ -274,29 +273,38 @@ function _updateNPCData(actor) { if (!hasSource) return actor; // shortcut out if dataVersion flag is set to 1.2.4 or higher 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 const sourceId = actor.flags.core.sourceId; - const coreSource = sourceId.substr(0,sourceId.length-17); - const core_id = sourceId.substr(sourceId.length-16,16); - if (coreSource === "Compendium.sw5e.monsters"){ - game.packs.get("sw5e.monsters").getEntity(core_id).then(monster => { - const monsterData = monster.data.data; - // copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel - updateData["data.attributes.movement"] = monsterData.attributes.movement; - updateData["data.attributes.senses"] = monsterData.attributes.senses; - updateData["data.attributes.powercasting"] = monsterData.attributes.powercasting; - updateData["data.attributes.force"] = monsterData.attributes.force; - updateData["data.attributes.tech"] = monsterData.attributes.tech; - updateData["data.details.powerForceLevel"] = monsterData.details.powerForceLevel; - updateData["data.details.powerTechLevel"] = monsterData.details.powerTechLevel; - // push missing powers onto actor - let newPowers = []; - for ( let i of monster.items ) { + const coreSource = sourceId.substr(0, sourceId.length - 17); + const core_id = sourceId.substr(sourceId.length - 16, 16); + if (coreSource === "Compendium.sw5e.monsters") { + game.packs + .get("sw5e.monsters") + .getEntity(core_id) + .then(monster => { + const monsterData = monster.data.data; + // copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel + updateData["data.attributes.movement"] = monsterData.attributes.movement; + updateData["data.attributes.senses"] = monsterData.attributes.senses; + updateData["data.attributes.powercasting"] = monsterData.attributes.powercasting; + updateData["data.attributes.force"] = monsterData.attributes.force; + updateData["data.attributes.tech"] = monsterData.attributes.tech; + updateData["data.details.powerForceLevel"] = monsterData.details.powerForceLevel; + updateData["data.details.powerTechLevel"] = monsterData.details.powerTechLevel; + // push missing powers onto actor + let newPowers = []; + for (let i of monster.items) { const itemData = i.data; - if ( itemData.type === "power" ) { + if (itemData.type === "power") { 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) { // 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)); @@ -304,26 +312,24 @@ function _updateNPCData(actor) { newPowers.push(newPower); } } - } + } - // get actor to create new powers - const liveActor = game.actors.get(actor._id); - // create the powers on the actor - liveActor.createEmbeddedEntity("OwnedItem", newPowers); + // get actor to create new powers + const liveActor = game.actors.get(actor._id); + // create the powers on the actor + liveActor.createEmbeddedEntity("OwnedItem", newPowers); - // 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"); - }) + // 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"); + }); } - //merge object actorData = mergeObject(actorData, updateData); // Return the scrubbed data return actor; } - /** * Migrate the actor speed string to movement object * @private @@ -332,21 +338,20 @@ function _migrateActorMovement(actorData, updateData) { const ad = actorData.data; // 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; - if ( hasOld ) { - + if (hasOld) { // If new data is not present, migrate the old data const hasNew = ad?.attributes?.movement?.walk !== undefined; - if ( !hasNew && (typeof old === "string") ) { + if (!hasNew && typeof old === "string") { 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 updateData["data.attributes.-=speed"] = null; } - return updateData + return updateData; } /* -------------------------------------------- */ @@ -360,7 +365,7 @@ function _migrateActorPowers(actorData, updateData) { // If new Force & Tech data is not present, create it let hasNewAttrib = ad?.attributes?.force?.level !== undefined; - if ( !hasNewAttrib ) { + if (!hasNewAttrib) { updateData["data.attributes.force.known.value"] = 0; updateData["data.attributes.force.known.max"] = 0; updateData["data.attributes.force.points.value"] = 0; @@ -381,14 +386,14 @@ function _migrateActorPowers(actorData, updateData) { // If new Power F/T split data is not present, create it const hasNewLimit = ad?.powers?.power1?.foverride !== undefined; - if ( !hasNewLimit ) { - for (let i = 1; i <= 9; i++) { - // add new - updateData["data.powers.power" + i + ".fvalue"] = getProperty(ad.powers,"power" + i + ".value"); - updateData["data.powers.power" + i + ".fmax"] = getProperty(ad.powers,"power" + i + ".max"); + if (!hasNewLimit) { + for (let i = 1; i <= 9; i++) { + // add new + updateData["data.powers.power" + i + ".fvalue"] = getProperty(ad.powers, "power" + i + ".value"); + updateData["data.powers.power" + i + ".fmax"] = getProperty(ad.powers, "power" + i + ".max"); updateData["data.powers.power" + i + ".foverride"] = null; - updateData["data.powers.power" + i + ".tvalue"] = getProperty(ad.powers,"power" + i + ".value"); - updateData["data.powers.power" + i + ".tmax"] = getProperty(ad.powers,"power" + i + ".max"); + updateData["data.powers.power" + i + ".tvalue"] = getProperty(ad.powers, "power" + i + ".value"); + updateData["data.powers.power" + i + ".tmax"] = getProperty(ad.powers, "power" + i + ".max"); updateData["data.powers.power" + i + ".toverride"] = null; //remove old updateData["data.powers.power" + i + ".-=value"] = null; @@ -397,7 +402,7 @@ function _migrateActorPowers(actorData, updateData) { } // If new Bonus Power DC data is not present, create it const hasNewBonus = ad?.bonuses?.power?.forceLightDC !== undefined; - if ( !hasNewBonus ) { + if (!hasNewBonus) { updateData["data.bonuses.power.forceLightDC"] = ""; updateData["data.bonuses.power.forceDarkDC"] = ""; updateData["data.bonuses.power.forceUnivDC"] = ""; @@ -407,7 +412,7 @@ function _migrateActorPowers(actorData, updateData) { // Remove the Power DC Bonus updateData["data.bonuses.power.-=dc"] = null; - return updateData + return updateData; } /* -------------------------------------------- */ @@ -418,7 +423,7 @@ function _migrateActorPowers(actorData, updateData) { */ function _migrateActorSenses(actor, updateData) { const ad = actor.data; - if ( ad?.traits?.senses === undefined ) return; + if (ad?.traits?.senses === undefined) return; const original = ad.traits.senses || ""; // Try to match old senses with the format like "Darkvision 60 ft, Blindsight 30 ft" @@ -426,19 +431,19 @@ function _migrateActorSenses(actor, updateData) { let wasMatched = false; // Match each comma-separated term - for ( let s of original.split(",") ) { + for (let s of original.split(",")) { s = s.trim(); const match = s.match(pattern); - if ( !match ) continue; + if (!match) continue; const type = match[1].toLowerCase(); - if ( type in CONFIG.SW5E.senses ) { + if (type in CONFIG.SW5E.senses) { updateData[`data.attributes.senses.${type}`] = Number(match[2]).toNearest(0.5); wasMatched = true; } } // If nothing was matched, but there was an old string - put the whole thing in "special" - if ( !wasMatched && !!original ) { + if (!wasMatched && !!original) { updateData["data.attributes.senses.special"] = original; } @@ -453,9 +458,9 @@ function _migrateActorSenses(actor, updateData) { * @private */ function _migrateItemClassPowerCasting(item, updateData) { - if (item.type === "class"){ - switch (item.name){ - case "Consular": + if (item.type === "class") { + switch (item.name) { + case "Consular": updateData["data.powercasting"] = "consular"; break; case "Engineer": @@ -494,32 +499,35 @@ async function _migrateItemPower(item, actor, updateData) { // shortcut out if dataVersion flag is set to 1.2.4 or higher 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 const sourceId = item.flags.core.sourceId; const coreSource = sourceId.substr(0, sourceId.length - 17); const core_id = sourceId.substr(sourceId.length - 16, 16); - + //if power type is not force or tech exit out let powerType = "none"; if (coreSource === "Compendium.sw5e.forcepowers") powerType = "sw5e.forcepowers"; if (coreSource === "Compendium.sw5e.techpowers") powerType = "sw5e.techpowers"; if (powerType === "none") return updateData; - const corePower = duplicate(await game.packs.get(powerType).getEntity(core_id)); - console.log(`Updating Actor ${actor.name}'s ${item.name} from compendium`); - const corePowerData = corePower.data; - // copy Core Power Data over original Power - updateData["data"] = corePowerData; - updateData["flags"] = {"sw5e": {"dataVersion": "1.2.4"}}; + const corePower = duplicate(await game.packs.get(powerType).getEntity(core_id)); + console.log(`Updating Actor ${actor.name}'s ${item.name} from compendium`); + const corePowerData = corePower.data; + // copy Core Power Data over original Power + updateData["data"] = corePowerData; + updateData["flags"] = {sw5e: {dataVersion: "1.2.4"}}; + + return updateData; - return updateData; - - //game.packs.get(powerType).getEntity(core_id).then(corePower => { - //}) + //}) } /* -------------------------------------------- */ @@ -529,7 +537,7 @@ async function _migrateItemPower(item, actor, updateData) { * @private */ function _migrateItemAttunement(item, updateData) { - if ( item.data.attuned === undefined ) return; + if (item.data.attuned === undefined) return; updateData["data.attunement"] = CONFIG.SW5E.attunementTypes.NONE; updateData["data.-=attuned"] = null; return updateData; @@ -543,19 +551,19 @@ function _migrateItemAttunement(item, updateData) { * @private */ export async function purgeFlags(pack) { - const cleanFlags = (flags) => { + const cleanFlags = flags => { const flags5e = flags.sw5e || null; return flags5e ? {sw5e: flags5e} : {}; }; await pack.configure({locked: false}); const content = await pack.getContent(); - for ( let entity of content ) { + for (let entity of content) { 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 => { i.flags = cleanFlags(i.flags); return i; - }) + }); } await pack.updateEntity(update, {recursive: false}); console.log(`Purged flags from ${entity.name}`); @@ -565,20 +573,18 @@ export async function purgeFlags(pack) { /* -------------------------------------------- */ - /** * Purge the data model of any inner objects which have been flagged as _deprecated. * @param {object} data The data to clean * @private */ export function removeDeprecatedObjects(data) { - for ( let [k, v] of Object.entries(data) ) { - if ( getType(v) === "Object" ) { + for (let [k, v] of Object.entries(data)) { + if (getType(v) === "Object") { if (v._deprecated === true) { console.log(`Deleting deprecated object key ${k}`); delete data[k]; - } - else removeDeprecatedObjects(v); + } else removeDeprecatedObjects(v); } } return data; diff --git a/module/pixi/ability-template.js b/module/pixi/ability-template.js index 702343b2..3f52e3e7 100644 --- a/module/pixi/ability-template.js +++ b/module/pixi/ability-template.js @@ -1,11 +1,10 @@ -import { SW5E } from "../config.js"; +import {SW5E} from "../config.js"; /** * A helper class for building MeasuredTemplates for 5e powers and abilities * @extends {MeasuredTemplate} */ export default class AbilityTemplate extends MeasuredTemplate { - /** * 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 @@ -14,7 +13,7 @@ export default class AbilityTemplate extends MeasuredTemplate { static fromItem(item) { const target = getProperty(item.data, "data.target") || {}; const templateShape = SW5E.areaTargetTypes[target.type]; - if ( !templateShape ) return null; + if (!templateShape) return null; // Prepare template data const templateData = { @@ -28,7 +27,7 @@ export default class AbilityTemplate extends MeasuredTemplate { }; // Additional type-specific data - switch ( templateShape ) { + switch (templateShape) { case "cone": templateData.angle = CONFIG.MeasuredTemplate.defaults.angle; break; @@ -65,7 +64,7 @@ export default class AbilityTemplate extends MeasuredTemplate { this.layer.preview.addChild(this); // Hide the sheet that originated the preview - if ( this.actorSheet ) this.actorSheet.minimize(); + if (this.actorSheet) this.actorSheet.minimize(); // Activate interactivity this.activatePreviewListeners(initialLayer); @@ -85,7 +84,7 @@ export default class AbilityTemplate extends MeasuredTemplate { handlers.mm = event => { event.stopPropagation(); let now = Date.now(); // Apply a 20ms throttle - if ( now - moveTime <= 20 ) return; + if (now - moveTime <= 20) return; const center = event.data.getLocalPosition(this.layer); const snapped = canvas.grid.getSnappedPosition(center.x, center.y, 2); this.data.x = snapped.x; @@ -120,11 +119,11 @@ export default class AbilityTemplate extends MeasuredTemplate { // Rotate the template by 3 degree increments (mouse-wheel) 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(); let delta = canvas.grid.type > CONST.GRID_TYPES.SQUARE ? 30 : 15; let snap = event.shiftKey ? delta : 5; - this.data.direction += (snap * Math.sign(event.deltaY)); + this.data.direction += snap * Math.sign(event.deltaY); this.refresh(); }; diff --git a/module/settings.js b/module/settings.js index 8898d8a6..a504fc7f 100644 --- a/module/settings.js +++ b/module/settings.js @@ -1,5 +1,4 @@ -export const registerSystemSettings = function() { - +export const registerSystemSettings = function () { /** * Track the system version upon which point a migration was last applied */ @@ -22,9 +21,9 @@ export const registerSystemSettings = function() { default: "normal", type: String, choices: { - "normal": "SETTINGS.5eRestPHB", - "gritty": "SETTINGS.5eRestGritty", - "epic": "SETTINGS.5eRestEpic", + normal: "SETTINGS.5eRestPHB", + gritty: "SETTINGS.5eRestGritty", + epic: "SETTINGS.5eRestEpic" } }); @@ -39,11 +38,11 @@ export const registerSystemSettings = function() { default: "555", type: String, choices: { - "555": "SETTINGS.5eDiagPHB", - "5105": "SETTINGS.5eDiagDMG", - "EUCL": "SETTINGS.5eDiagEuclidean", + 555: "SETTINGS.5eDiagPHB", + 5105: "SETTINGS.5eDiagDMG", + 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", config: true, default: false, - type: Boolean, + type: Boolean }); /** @@ -100,10 +99,10 @@ export const registerSystemSettings = function() { /** * Option to allow GMs to restrict polymorphing to GMs only. */ - game.settings.register('sw5e', 'allowPolymorphing', { - name: 'SETTINGS.5eAllowPolymorphingN', - hint: 'SETTINGS.5eAllowPolymorphingL', - scope: 'world', + game.settings.register("sw5e", "allowPolymorphing", { + name: "SETTINGS.5eAllowPolymorphingN", + hint: "SETTINGS.5eAllowPolymorphingL", + scope: "world", config: true, default: false, type: Boolean @@ -112,8 +111,8 @@ export const registerSystemSettings = function() { /** * Remember last-used polymorph settings. */ - game.settings.register('sw5e', 'polymorphSettings', { - scope: 'client', + game.settings.register("sw5e", "polymorphSettings", { + scope: "client", default: { keepPhysical: false, keepMental: false, @@ -138,8 +137,8 @@ export const registerSystemSettings = function() { default: "light", type: String, choices: { - "light": "SETTINGS.SWColorLight", - "dark": "SETTINGS.SWColorDark" + light: "SETTINGS.SWColorLight", + dark: "SETTINGS.SWColorDark" } }); }; diff --git a/module/templates.js b/module/templates.js index 764463f3..055a2f00 100644 --- a/module/templates.js +++ b/module/templates.js @@ -3,9 +3,8 @@ * Pre-loaded templates are compiled and cached for fast access when rendering * @return {Promise} */ -export const preloadHandlebarsTemplates = async function() { +export const preloadHandlebarsTemplates = async function () { return loadTemplates([ - // Shared Partials "systems/sw5e/templates/actors/parts/active-effects.html", @@ -14,18 +13,18 @@ export const preloadHandlebarsTemplates = async function() { "systems/sw5e/templates/actors/oldActor/parts/actor-inventory.html", "systems/sw5e/templates/actors/oldActor/parts/actor-features.html", "systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html", - "systems/sw5e/templates/actors/oldActor/parts/actor-notes.html", + "systems/sw5e/templates/actors/oldActor/parts/actor-notes.html", "systems/sw5e/templates/actors/newActor/parts/swalt-biography.html", "systems/sw5e/templates/actors/newActor/parts/swalt-core.html", - "systems/sw5e/templates/actors/newActor/parts/swalt-active-effects.html", + "systems/sw5e/templates/actors/newActor/parts/swalt-active-effects.html", "systems/sw5e/templates/actors/newActor/parts/swalt-features.html", "systems/sw5e/templates/actors/newActor/parts/swalt-inventory.html", "systems/sw5e/templates/actors/newActor/parts/swalt-force-powerbook.html", "systems/sw5e/templates/actors/newActor/parts/swalt-tech-powerbook.html", "systems/sw5e/templates/actors/newActor/parts/swalt-resources.html", "systems/sw5e/templates/actors/newActor/parts/swalt-traits.html", - + // Item Sheet Partials "systems/sw5e/templates/items/parts/item-action.html", "systems/sw5e/templates/items/parts/item-activation.html", diff --git a/sw5e.js b/sw5e.js index 1983130b..3ff387e4 100644 --- a/sw5e.js +++ b/sw5e.js @@ -8,11 +8,11 @@ */ // Import Modules -import { SW5E } from "./module/config.js"; -import { registerSystemSettings } from "./module/settings.js"; -import { preloadHandlebarsTemplates } from "./module/templates.js"; -import { _getInitiativeFormula } from "./module/combat.js"; -import { measureDistances, getBarAttribute } from "./module/canvas.js"; +import {SW5E} from "./module/config.js"; +import {registerSystemSettings} from "./module/settings.js"; +import {preloadHandlebarsTemplates} from "./module/templates.js"; +import {_getInitiativeFormula} from "./module/combat.js"; +import {measureDistances, getBarAttribute} from "./module/canvas.js"; // Import Entities import Actor5e from "./module/actor/entity.js"; @@ -44,7 +44,7 @@ import * as migrations from "./module/migration.js"; /* Foundry VTT Initialization */ /* -------------------------------------------- */ -Hooks.once("init", function() { +Hooks.once("init", function () { console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`); // Create a SW5E namespace within the game global @@ -69,7 +69,7 @@ Hooks.once("init", function() { dice: dice, entities: { Actor5e, - Item5e, + Item5e }, macros: macros, migrations: migrations, @@ -81,11 +81,7 @@ Hooks.once("init", function() { CONFIG.Actor.entityClass = Actor5e; CONFIG.Item.entityClass = Item5e; CONFIG.time.roundTime = 6; - CONFIG.fontFamilies = [ - "Engli-Besh", - "Open Sans", - "Russo One" - ]; + CONFIG.fontFamilies = ["Engli-Besh", "Open Sans", "Russo One"]; // 5e cone RAW should be 53.13 degrees CONFIG.MeasuredTemplate.defaults.angle = 53.13; @@ -107,12 +103,12 @@ Hooks.once("init", function() { types: ["character"], makeDefault: true, label: "SW5E.SheetClassCharacter" - }); + }); Actors.registerSheet("sw5e", ActorSheet5eCharacter, { types: ["character"], makeDefault: false, label: "SW5E.SheetClassCharacterOld" - }); + }); Actors.registerSheet("sw5e", ActorSheet5eNPCNew, { types: ["npc"], makeDefault: true, @@ -123,14 +119,31 @@ Hooks.once("init", function() { makeDefault: false, label: "SW5E.SheetClassNPCOld" }); - Actors.registerSheet('sw5e', ActorSheet5eVehicle, { - types: ['vehicle'], + Actors.registerSheet("sw5e", ActorSheet5eVehicle, { + types: ["vehicle"], makeDefault: true, label: "SW5E.SheetClassVehicle" }); Items.unregisterSheet("core", ItemSheet); Items.registerSheet("sw5e", ItemSheet5e, { - types: ['weapon', 'equipment', 'consumable', 'tool', 'loot', 'class', 'power', 'feat', 'species', 'backpack', 'archetype', 'classfeature', 'background', 'fightingmastery', 'fightingstyle', 'lightsaberform'], + types: [ + "weapon", + "equipment", + "consumable", + "tool", + "loot", + "class", + "power", + "feat", + "species", + "backpack", + "archetype", + "classfeature", + "background", + "fightingmastery", + "fightingstyle", + "lightsaberform" + ], makeDefault: true, label: "SW5E.SheetClassItem" }); @@ -139,7 +152,6 @@ Hooks.once("init", function() { preloadHandlebarsTemplates(); }); - /* -------------------------------------------- */ /* Foundry VTT Setup */ /* -------------------------------------------- */ @@ -147,30 +159,70 @@ Hooks.once("init", function() { /** * 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 const toLocalize = [ - "abilities", "abilityAbbreviations", "abilityActivationTypes", "abilityConsumptionTypes", "actorSizes", "alignments", - "armorProficiencies", "armorPropertiesTypes", "conditionTypes", "consumableTypes", "cover", "currencies", "damageResistanceTypes", - "damageTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes", "languages", - "limitedUsePeriods", "movementTypes", "movementUnits", "polymorphSettings", "proficiencyLevels", "senses", "skills", - "powerComponents", "powerLevels", "powerPreparationModes", "powerScalingModes", "powerSchools", "targetTypes", - "timePeriods", "toolProficiencies", "weaponProficiencies", "weaponProperties", "weaponTypes" + "abilities", + "abilityAbbreviations", + "abilityActivationTypes", + "abilityConsumptionTypes", + "actorSizes", + "alignments", + "armorProficiencies", + "armorPropertiesTypes", + "conditionTypes", + "consumableTypes", + "cover", + "currencies", + "damageResistanceTypes", + "damageTypes", + "distanceUnits", + "equipmentTypes", + "healingTypes", + "itemActionTypes", + "languages", + "limitedUsePeriods", + "movementTypes", + "movementUnits", + "polymorphSettings", + "proficiencyLevels", + "senses", + "skills", + "powerComponents", + "powerLevels", + "powerPreparationModes", + "powerScalingModes", + "powerSchools", + "targetTypes", + "timePeriods", + "toolProficiencies", + "weaponProficiencies", + "weaponProperties", + "weaponTypes" ]; // Exclude some from sorting where the default order matters const noSort = [ - "abilities", "alignments", "currencies", "distanceUnits", "movementUnits", "itemActionTypes", "proficiencyLevels", - "limitedUsePeriods", "powerComponents", "powerLevels", "powerPreparationModes", "weaponTypes" + "abilities", + "alignments", + "currencies", + "distanceUnits", + "movementUnits", + "itemActionTypes", + "proficiencyLevels", + "limitedUsePeriods", + "powerComponents", + "powerLevels", + "powerPreparationModes", + "weaponTypes" ]; // Localize and sort CONFIG objects - for ( let o of toLocalize ) { + for (let o of toLocalize) { const localized = Object.entries(CONFIG.SW5E[o]).map(e => { 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])); CONFIG.SW5E[o] = localized.reduce((obj, e) => { obj[e[0]] = e[1]; return obj; @@ -179,7 +231,7 @@ Hooks.once("setup", function() { // add DND5E translation for module compatability game.i18n.translations.DND5E = game.i18n.translations.SW5E; // 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); }); @@ -188,23 +240,25 @@ Hooks.once("setup", function() { /** * 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 Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot)); // Determine whether a system migration is required and feasible - if ( !game.user.isGM ) return; + if (!game.user.isGM) return; const currentVersion = game.settings.get("sw5e", "systemMigrationVersion"); const NEEDS_MIGRATION_VERSION = "1.2.4.R1-A5"; // Check for R1 SW5E versions const SW5E_NEEDS_MIGRATION_VERSION = "R1-A5"; - const COMPATIBLE_MIGRATION_VERSION = 0.80; - const needsMigration = currentVersion && (isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) || isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion)); - if ( !needsMigration ) return; + const COMPATIBLE_MIGRATION_VERSION = 0.8; + const needsMigration = + currentVersion && + (isNewerVersion(SW5E_NEEDS_MIGRATION_VERSION, currentVersion) || + isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion)); + if (!needsMigration) return; // Perform the migration - if ( currentVersion && isNewerVersion(COMPATIBLE_MIGRATION_VERSION, currentVersion) ) { + if (currentVersion && isNewerVersion(COMPATIBLE_MIGRATION_VERSION, currentVersion)) { const warning = `Your SW5e system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.`; ui.notifications.error(warning, {permanent: true}); } @@ -215,8 +269,7 @@ Hooks.once("ready", function() { /* Canvas Initialization */ /* -------------------------------------------- */ -Hooks.on("canvasInit", function() { - +Hooks.on("canvasInit", function () { // Extend Diagonal Measurement canvas.grid.diagonalRule = game.settings.get("sw5e", "diagonalMovement"); SquareGrid.prototype.measureDistances = measureDistances; @@ -225,13 +278,11 @@ Hooks.on("canvasInit", function() { Token.prototype.getBarAttribute = getBarAttribute; }); - /* -------------------------------------------- */ /* Other Hooks */ /* -------------------------------------------- */ Hooks.on("renderChatMessage", (app, html, data) => { - // Display action buttons chat.displayChatActionButtons(app, html, data); @@ -244,38 +295,36 @@ Hooks.on("renderChatMessage", (app, html, data) => { Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions); Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html)); Hooks.on("renderChatPopout", (app, html, data) => Item5e.chatListeners(html)); -Hooks.on('getActorDirectoryEntryContext', Actor5e.addDirectoryContextOptions); -Hooks.on("renderSceneDirectory", (app, html, data)=> { +Hooks.on("getActorDirectoryEntryContext", Actor5e.addDirectoryContextOptions); +Hooks.on("renderSceneDirectory", (app, html, data) => { //console.log(html.find("header.folder-header")); setFolderBackground(html); }); -Hooks.on("renderActorDirectory", (app, html, data)=> { +Hooks.on("renderActorDirectory", (app, html, data) => { setFolderBackground(html); CharacterImporter.addImportButton(html); }); -Hooks.on("renderItemDirectory", (app, html, data)=> { +Hooks.on("renderItemDirectory", (app, html, data) => { setFolderBackground(html); }); -Hooks.on("renderJournalDirectory", (app, html, data)=> { +Hooks.on("renderJournalDirectory", (app, html, data) => { setFolderBackground(html); }); -Hooks.on("renderRollTableDirectory", (app, html, data)=> { +Hooks.on("renderRollTableDirectory", (app, html, data) => { setFolderBackground(html); }); Hooks.on("ActorSheet5eCharacterNew", (app, html, data) => { console.log("renderSwaltSheet"); }); // TODO I should remove this -Handlebars.registerHelper('getProperty', function (data, property) { +Handlebars.registerHelper("getProperty", function (data, property) { return getProperty(data, property); }); - function setFolderBackground(html) { - html.find("header.folder-header").each(function() { + html.find("header.folder-header").each(function () { let bgColor = $(this).css("background-color"); - if(bgColor == undefined) - bgColor = "rgb(255,255,255)"; - $(this).closest('li').css("background-color", bgColor); - }) -} \ No newline at end of file + if (bgColor == undefined) bgColor = "rgb(255,255,255)"; + $(this).closest("li").css("background-color", bgColor); + }); +}