From 76e4d7450834e6e368589322d6914d33e83dc704 Mon Sep 17 00:00:00 2001 From: supervj <64861570+supervj@users.noreply.github.com> Date: Fri, 13 Aug 2021 15:07:03 -0400 Subject: [PATCH] Update Core to 1.4.2 Update Core to 1.4.2 and internal Version to 1.4.2.R1-A8 --- module/actor/sheets/newSheet/base.js | 49 ++++++++++++++++++-------- module/actor/sheets/oldSheets/base.js | 49 ++++++++++++++++++-------- module/apps/property-attribution.js | 12 +++---- module/item/sheet.js | 4 +++ module/migration.js | 42 ++++++++++++++++++---- module/pixi/ability-template.js | 4 +-- sw5e.js | 2 +- system.json | 2 +- template.json | 2 +- templates/items/equipment.html | 50 ++++++++++++++------------- 10 files changed, 146 insertions(+), 70 deletions(-) diff --git a/module/actor/sheets/newSheet/base.js b/module/actor/sheets/newSheet/base.js index a6a40142..0dc38b2a 100644 --- a/module/actor/sheets/newSheet/base.js +++ b/module/actor/sheets/newSheet/base.js @@ -146,11 +146,6 @@ export default class ActorSheet5e extends ActorSheet { // Prepare warnings data.warnings = this.actor._preparationWarnings; - // Prepare property attributions - this.attribution = { - "attributes.ac": this._prepareArmorClassAttribution(actorData.data) - }; - // Return data to the sheet return data; } @@ -217,6 +212,30 @@ export default class ActorSheet5e extends ActorSheet { return tags; } + /* --------------------------------------------- */ + + /** + * Break down all of the Active Effects affecting a given target property. + * @param {string} target The data property being targeted. + * @return {AttributionDescription[]} + * @protected + */ + _prepareActiveEffectAttributions(target) { + return this.actor.effects.reduce((arr, e) => { + let source = e.sourceName; + if (e.data.origin === this.actor.uuid) source = e.data.label; + if (!source) return arr; + const value = e.data.changes.reduce((n, change) => { + if (change.key !== target || !Number.isNumeric(change.value)) return n; + if (change.mode !== CONST.ACTIVE_EFFECT_MODES.ADD) return n; + return n + Number(change.value); + }, 0); + if (!value) return arr; + arr.push({value, label: source, mode: CONST.ACTIVE_EFFECT_MODES.ADD}); + return arr; + }, []); + } + /* -------------------------------------------- */ /** @@ -299,12 +318,7 @@ export default class ActorSheet5e extends ActorSheet { }); // Bonus - if (ac.bonus !== 0) - attribution.push({ - label: game.i18n.localize("SW5E.Bonus"), - mode: CONST.ACTIVE_EFFECT_MODES.ADD, - value: ac.bonus - }); + if (ac.bonus !== 0) attribution.push(...this._prepareActiveEffectAttributions("data.attributes.ac.bonus")); // Cover if (ac.cover !== 0) @@ -1036,9 +1050,16 @@ export default class ActorSheet5e extends ActorSheet { async _onPropertyAttribution(event) { const existingTooltip = event.currentTarget.querySelector("div.tooltip"); const property = event.currentTarget.dataset.property; - if (existingTooltip || !property || !this.attribution) return; - - let html = await new PropertyAttribution(this.object, this.attribution, property).renderTooltip(); + if (existingTooltip || !property) return; + const data = this.actor.data.data; + let attributions; + switch (property) { + case "attributes.ac": + attributions = this._prepareArmorClassAttribution(data); + break; + } + if (!attributions) return; + const html = await new PropertyAttribution(this.actor, attributions, property).renderTooltip(); event.currentTarget.insertAdjacentElement("beforeend", html[0]); } diff --git a/module/actor/sheets/oldSheets/base.js b/module/actor/sheets/oldSheets/base.js index 62e9220c..1aa7feb3 100644 --- a/module/actor/sheets/oldSheets/base.js +++ b/module/actor/sheets/oldSheets/base.js @@ -138,11 +138,6 @@ export default class ActorSheet5e extends ActorSheet { // Prepare warnings data.warnings = this.actor._preparationWarnings; - // Prepare property attributions - this.attribution = { - "attributes.ac": this._prepareArmorClassAttribution(actorData.data) - }; - // Return data to the sheet return data; } @@ -209,6 +204,30 @@ export default class ActorSheet5e extends ActorSheet { return tags; } + /* --------------------------------------------- */ + + /** + * Break down all of the Active Effects affecting a given target property. + * @param {string} target The data property being targeted. + * @return {AttributionDescription[]} + * @protected + */ + _prepareActiveEffectAttributions(target) { + return this.actor.effects.reduce((arr, e) => { + let source = e.sourceName; + if (e.data.origin === this.actor.uuid) source = e.data.label; + if (!source) return arr; + const value = e.data.changes.reduce((n, change) => { + if (change.key !== target || !Number.isNumeric(change.value)) return n; + if (change.mode !== CONST.ACTIVE_EFFECT_MODES.ADD) return n; + return n + Number(change.value); + }, 0); + if (!value) return arr; + arr.push({value, label: source, mode: CONST.ACTIVE_EFFECT_MODES.ADD}); + return arr; + }, []); + } + /* -------------------------------------------- */ /** @@ -291,12 +310,7 @@ export default class ActorSheet5e extends ActorSheet { }); // Bonus - if (ac.bonus !== 0) - attribution.push({ - label: game.i18n.localize("SW5E.Bonus"), - mode: CONST.ACTIVE_EFFECT_MODES.ADD, - value: ac.bonus - }); + if (ac.bonus !== 0) attribution.push(...this._prepareActiveEffectAttributions("data.attributes.ac.bonus")); // Cover if (ac.cover !== 0) @@ -967,9 +981,16 @@ export default class ActorSheet5e extends ActorSheet { async _onPropertyAttribution(event) { const existingTooltip = event.currentTarget.querySelector("div.tooltip"); const property = event.currentTarget.dataset.property; - if (existingTooltip || !property || !this.attribution) return; - - let html = await new PropertyAttribution(this.object, this.attribution, property).renderTooltip(); + if (existingTooltip || !property) return; + const data = this.actor.data.data; + let attributions; + switch (property) { + case "attributes.ac": + attributions = this._prepareArmorClassAttribution(data); + break; + } + if (!attributions) return; + const html = await new PropertyAttribution(this.actor, attributions, property).renderTooltip(); event.currentTarget.insertAdjacentElement("beforeend", html[0]); } diff --git a/module/apps/property-attribution.js b/module/apps/property-attribution.js index 52278d9e..7be523b0 100644 --- a/module/apps/property-attribution.js +++ b/module/apps/property-attribution.js @@ -16,14 +16,14 @@ */ export default class PropertyAttribution extends Application { /** - * @param {Document} object - Object containing the property to be attributed. - * @param {object.} attribution - Object containing all of the attribution data. - * @param {string} property - Dot separated path to the property. + * @param {Document} object The Document that owns the property being attributed. + * @param {AttributionDescription[]} attributions An array of all the attribution data. + * @param {string} property Dot separated path to the property. */ - constructor(object, attribution, property, options = {}) { + constructor(object, attributions, property, options = {}) { super(options); this.object = object; - this.attribution = attribution; + this.attributions = attributions; this.property = property; } @@ -64,7 +64,7 @@ export default class PropertyAttribution extends Application { total = property.value; } - const sources = foundry.utils.duplicate(this.attribution[this.property]); + const sources = foundry.utils.duplicate(this.attributions); return { sources: sources.map((entry) => { if (entry.label.startsWith("@")) { diff --git a/module/item/sheet.js b/module/item/sheet.js index c0143847..1bc37121 100644 --- a/module/item/sheet.js +++ b/module/item/sheet.js @@ -70,6 +70,10 @@ export default class ItemSheet5e extends ItemSheet { data.isCrewed = itemData.data.activation?.type === "crew"; data.isMountable = this._isItemMountable(itemData); + // Armor Class + data.isArmor = itemData.data.armor?.type in data.config.armorTypes; + data.hasAC = data.isArmor || data.isMountable; + // Prepare Active Effects data.effects = ActiveEffect5e.prepareActiveEffectCategories(this.item.effects); diff --git a/module/migration.js b/module/migration.js index 15b9ce13..e2b2f3a6 100644 --- a/module/migration.js +++ b/module/migration.js @@ -9,7 +9,7 @@ export const migrateWorld = async function () { ); // Migrate World Actors - for await (let a of game.actors.contents) { + for await (let a of game.actors) { try { console.log(`Checking Actor entity ${a.name} for migration needs`); const updateData = await migrateActorData(a.toObject()); @@ -24,7 +24,7 @@ export const migrateWorld = async function () { } // Migrate World Items - for (let i of game.items.contents) { + for (let i of game.items) { try { const updateData = migrateItemData(i.toObject()); if (!foundry.utils.isObjectEmpty(updateData)) { @@ -38,7 +38,7 @@ export const migrateWorld = async function () { } // Migrate Actor Override Tokens - for (let s of game.scenes.contents) { + for (let s of game.scenes) { try { const updateData = await migrateSceneData(s.data); if (!foundry.utils.isObjectEmpty(updateData)) { @@ -46,7 +46,7 @@ export const migrateWorld = async function () { await s.update(updateData, {enforceTypes: false}); // If we do not do this, then synthetic token actors remain in cache // with the un-updated actorData. - s.tokens.contents.forEach((t) => (t._actor = null)); + s.tokens.forEach((t) => (t._actor = null)); } } catch (err) { err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`; @@ -260,6 +260,7 @@ export const migrateItemData = function (item) { _migrateItemClassPowerCasting(item, updateData); _migrateItemAttunement(item, updateData); _migrateItemRarity(item, updateData); + _migrateArmorType(item, updateData); return updateData; }; @@ -276,6 +277,7 @@ export const migrateActorItemData = async function (item, actor) { _migrateItemAttunement(item, updateData); _migrateItemRarity(item, updateData); await _migrateItemPower(item, actor, updateData); + _migrateArmorType(item, updateData); return updateData; }; @@ -601,6 +603,7 @@ function _migrateActorType(actor, updateData) { /* -------------------------------------------- */ /** + * Migrate the Class item powercasting field to allow powercasting to properly calculate * @private */ function _migrateItemClassPowerCasting(item, updateData) { @@ -649,9 +652,18 @@ function _migrateItemClassPowerCasting(item, updateData) { */ function _migrateActorAC(actorData, updateData) { const ac = actorData.data?.attributes?.ac; - if (!Number.isNumeric(ac?.value)) return; - updateData["data.attributes.ac.flat"] = ac.value; - updateData["data.attributes.ac.-=value"] = null; + // If the actor has a numeric ac.value, then their AC has not been migrated to the auto-calculation schema yet. + if (Number.isNumeric(ac?.value)) { + updateData["data.attributes.ac.flat"] = ac.value; + updateData["data.attributes.ac.calc"] = actorData.type === "npc" ? "natural" : "flat"; + updateData["data.attributes.ac.-=value"] = null; + return updateData; + } + + // If the actor is already on the AC auto-calculation schema, but is using a flat value, they must now have their + // calculation updated to an appropriate value. + if (!Number.isNumeric(ac?.flat)) return updateData; + updateData["data.attributes.ac.calc"] = actorData.type === "npc" ? "natural" : "flat"; return updateData; } @@ -742,6 +754,22 @@ function _migrateItemRarity(item, updateData) { return updateData; } +/* --------------------------------------------- */ + +/** + * Convert equipment items of type 'bonus' to 'trinket'. + * + * @param {object} item Item data to migrate + * @param {object} updateData Existing update to expand upon + * @return {object} The updateData to apply + * @private + */ +function _migrateArmorType(item, updateData) { + if (item.type !== "equipment") return updateData; + if (item.data?.armor?.type === "bonus") updateData["data.armor.type"] = "trinket"; + return updateData; +} + /* -------------------------------------------- */ /** diff --git a/module/pixi/ability-template.js b/module/pixi/ability-template.js index 6799eaec..1102dbe7 100644 --- a/module/pixi/ability-template.js +++ b/module/pixi/ability-template.js @@ -18,7 +18,7 @@ export default class AbilityTemplate extends MeasuredTemplate { // Prepare template data const templateData = { t: templateShape, - user: game.user.data._id, + user: game.user.id, distance: target.value, direction: 0, x: 0, @@ -96,7 +96,7 @@ export default class AbilityTemplate extends MeasuredTemplate { // Cancel the workflow (right-click) handlers.rc = (event) => { - this.layer.preview.removeChildren(); + this.layer._onDragLeftCancel(event); canvas.stage.off("mousemove", handlers.mm); canvas.stage.off("mousedown", handlers.lc); canvas.app.view.oncontextmenu = null; diff --git a/sw5e.js b/sw5e.js index fe2a9c71..9d77cf0e 100644 --- a/sw5e.js +++ b/sw5e.js @@ -290,7 +290,7 @@ Hooks.once("ready", function () { // Determine whether a system migration is required and feasible if (!game.user.isGM) return; const currentVersion = game.settings.get("sw5e", "systemMigrationVersion"); - const NEEDS_MIGRATION_VERSION = "1.4.1.R1-A8"; + const NEEDS_MIGRATION_VERSION = "1.4.2.R1-A8"; // Check for R1 SW5E versions const SW5E_NEEDS_MIGRATION_VERSION = "R1-A8"; const COMPATIBLE_MIGRATION_VERSION = 0.8; diff --git a/system.json b/system.json index fe4d0e0f..7eceb83b 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "name": "sw5e", "title": "SW 5th Edition", "description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.", - "version": "1.4.1.R1-A8", + "version": "1.4.2.R1-A8", "author": "Dev Team", "scripts": [], "esmodules": ["sw5e.js"], diff --git a/template.json b/template.json index 71cc2b05..85c68c14 100644 --- a/template.json +++ b/template.json @@ -945,7 +945,7 @@ ], "armor": { "type": "light", - "value": 10, + "value": null, "dex": null }, "speed": { diff --git a/templates/items/equipment.html b/templates/items/equipment.html index 4c975223..426e003a 100644 --- a/templates/items/equipment.html +++ b/templates/items/equipment.html @@ -121,36 +121,38 @@ {{!-- Armor Class --}} -
- -
- + {{#if hasAC}} +
+ +
+ +
-
+ {{/if}} - {{#unless isMountable}} - {{!-- Dexterity Modifier --}} -
- -
- + {{#if isArmor}} + {{!-- Dexterity Modifier --}} +
+ +
+ +
-
- {{!-- Required Strength --}} -
- -
- + {{!-- Required Strength --}} +
+ +
+ +
-
- {{!-- Stealth Disadvantage --}} -
- - -
- {{/unless}} + {{!-- Stealth Disadvantage --}} +
+ + +
+ {{/if}} {{#if isMountable}} {{> 'systems/sw5e/templates/items/parts/item-mountable.html'}}