Update Core to 1.4.2

Update Core to 1.4.2 and internal Version to 1.4.2.R1-A8
This commit is contained in:
supervj 2021-08-13 15:07:03 -04:00
parent 48b6ab8e18
commit 76e4d74508
10 changed files with 146 additions and 70 deletions

View file

@ -146,11 +146,6 @@ export default class ActorSheet5e extends ActorSheet {
// Prepare warnings // Prepare warnings
data.warnings = this.actor._preparationWarnings; data.warnings = this.actor._preparationWarnings;
// Prepare property attributions
this.attribution = {
"attributes.ac": this._prepareArmorClassAttribution(actorData.data)
};
// Return data to the sheet // Return data to the sheet
return data; return data;
} }
@ -217,6 +212,30 @@ export default class ActorSheet5e extends ActorSheet {
return tags; 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 // Bonus
if (ac.bonus !== 0) if (ac.bonus !== 0) attribution.push(...this._prepareActiveEffectAttributions("data.attributes.ac.bonus"));
attribution.push({
label: game.i18n.localize("SW5E.Bonus"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.bonus
});
// Cover // Cover
if (ac.cover !== 0) if (ac.cover !== 0)
@ -1036,9 +1050,16 @@ export default class ActorSheet5e extends ActorSheet {
async _onPropertyAttribution(event) { async _onPropertyAttribution(event) {
const existingTooltip = event.currentTarget.querySelector("div.tooltip"); const existingTooltip = event.currentTarget.querySelector("div.tooltip");
const property = event.currentTarget.dataset.property; const property = event.currentTarget.dataset.property;
if (existingTooltip || !property || !this.attribution) return; if (existingTooltip || !property) return;
const data = this.actor.data.data;
let html = await new PropertyAttribution(this.object, this.attribution, property).renderTooltip(); 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]); event.currentTarget.insertAdjacentElement("beforeend", html[0]);
} }

View file

@ -138,11 +138,6 @@ export default class ActorSheet5e extends ActorSheet {
// Prepare warnings // Prepare warnings
data.warnings = this.actor._preparationWarnings; data.warnings = this.actor._preparationWarnings;
// Prepare property attributions
this.attribution = {
"attributes.ac": this._prepareArmorClassAttribution(actorData.data)
};
// Return data to the sheet // Return data to the sheet
return data; return data;
} }
@ -209,6 +204,30 @@ export default class ActorSheet5e extends ActorSheet {
return tags; 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 // Bonus
if (ac.bonus !== 0) if (ac.bonus !== 0) attribution.push(...this._prepareActiveEffectAttributions("data.attributes.ac.bonus"));
attribution.push({
label: game.i18n.localize("SW5E.Bonus"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.bonus
});
// Cover // Cover
if (ac.cover !== 0) if (ac.cover !== 0)
@ -967,9 +981,16 @@ export default class ActorSheet5e extends ActorSheet {
async _onPropertyAttribution(event) { async _onPropertyAttribution(event) {
const existingTooltip = event.currentTarget.querySelector("div.tooltip"); const existingTooltip = event.currentTarget.querySelector("div.tooltip");
const property = event.currentTarget.dataset.property; const property = event.currentTarget.dataset.property;
if (existingTooltip || !property || !this.attribution) return; if (existingTooltip || !property) return;
const data = this.actor.data.data;
let html = await new PropertyAttribution(this.object, this.attribution, property).renderTooltip(); 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]); event.currentTarget.insertAdjacentElement("beforeend", html[0]);
} }

View file

@ -16,14 +16,14 @@
*/ */
export default class PropertyAttribution extends Application { export default class PropertyAttribution extends Application {
/** /**
* @param {Document} object - Object containing the property to be attributed. * @param {Document} object The Document that owns the property being attributed.
* @param {object.<string, AttributionDescription[]>} attribution - Object containing all of the attribution data. * @param {AttributionDescription[]} attributions An array of all the attribution data.
* @param {string} property - Dot separated path to the property. * @param {string} property Dot separated path to the property.
*/ */
constructor(object, attribution, property, options = {}) { constructor(object, attributions, property, options = {}) {
super(options); super(options);
this.object = object; this.object = object;
this.attribution = attribution; this.attributions = attributions;
this.property = property; this.property = property;
} }
@ -64,7 +64,7 @@ export default class PropertyAttribution extends Application {
total = property.value; total = property.value;
} }
const sources = foundry.utils.duplicate(this.attribution[this.property]); const sources = foundry.utils.duplicate(this.attributions);
return { return {
sources: sources.map((entry) => { sources: sources.map((entry) => {
if (entry.label.startsWith("@")) { if (entry.label.startsWith("@")) {

View file

@ -70,6 +70,10 @@ export default class ItemSheet5e extends ItemSheet {
data.isCrewed = itemData.data.activation?.type === "crew"; data.isCrewed = itemData.data.activation?.type === "crew";
data.isMountable = this._isItemMountable(itemData); 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 // Prepare Active Effects
data.effects = ActiveEffect5e.prepareActiveEffectCategories(this.item.effects); data.effects = ActiveEffect5e.prepareActiveEffectCategories(this.item.effects);

View file

@ -9,7 +9,7 @@ export const migrateWorld = async function () {
); );
// Migrate World Actors // Migrate World Actors
for await (let a of game.actors.contents) { for await (let a of game.actors) {
try { try {
console.log(`Checking Actor entity ${a.name} for migration needs`); console.log(`Checking Actor entity ${a.name} for migration needs`);
const updateData = await migrateActorData(a.toObject()); const updateData = await migrateActorData(a.toObject());
@ -24,7 +24,7 @@ export const migrateWorld = async function () {
} }
// Migrate World Items // Migrate World Items
for (let i of game.items.contents) { for (let i of game.items) {
try { try {
const updateData = migrateItemData(i.toObject()); const updateData = migrateItemData(i.toObject());
if (!foundry.utils.isObjectEmpty(updateData)) { if (!foundry.utils.isObjectEmpty(updateData)) {
@ -38,7 +38,7 @@ export const migrateWorld = async function () {
} }
// Migrate Actor Override Tokens // Migrate Actor Override Tokens
for (let s of game.scenes.contents) { for (let s of game.scenes) {
try { try {
const updateData = await migrateSceneData(s.data); const updateData = await migrateSceneData(s.data);
if (!foundry.utils.isObjectEmpty(updateData)) { if (!foundry.utils.isObjectEmpty(updateData)) {
@ -46,7 +46,7 @@ export const migrateWorld = async function () {
await s.update(updateData, {enforceTypes: false}); await s.update(updateData, {enforceTypes: false});
// If we do not do this, then synthetic token actors remain in cache // If we do not do this, then synthetic token actors remain in cache
// with the un-updated actorData. // with the un-updated actorData.
s.tokens.contents.forEach((t) => (t._actor = null)); s.tokens.forEach((t) => (t._actor = null));
} }
} catch (err) { } catch (err) {
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`; err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
@ -260,6 +260,7 @@ export const migrateItemData = function (item) {
_migrateItemClassPowerCasting(item, updateData); _migrateItemClassPowerCasting(item, updateData);
_migrateItemAttunement(item, updateData); _migrateItemAttunement(item, updateData);
_migrateItemRarity(item, updateData); _migrateItemRarity(item, updateData);
_migrateArmorType(item, updateData);
return updateData; return updateData;
}; };
@ -276,6 +277,7 @@ export const migrateActorItemData = async function (item, actor) {
_migrateItemAttunement(item, updateData); _migrateItemAttunement(item, updateData);
_migrateItemRarity(item, updateData); _migrateItemRarity(item, updateData);
await _migrateItemPower(item, actor, updateData); await _migrateItemPower(item, actor, updateData);
_migrateArmorType(item, updateData);
return updateData; return updateData;
}; };
@ -601,6 +603,7 @@ function _migrateActorType(actor, updateData) {
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
* Migrate the Class item powercasting field to allow powercasting to properly calculate
* @private * @private
*/ */
function _migrateItemClassPowerCasting(item, updateData) { function _migrateItemClassPowerCasting(item, updateData) {
@ -649,9 +652,18 @@ function _migrateItemClassPowerCasting(item, updateData) {
*/ */
function _migrateActorAC(actorData, updateData) { function _migrateActorAC(actorData, updateData) {
const ac = actorData.data?.attributes?.ac; const ac = actorData.data?.attributes?.ac;
if (!Number.isNumeric(ac?.value)) return; // If the actor has a numeric ac.value, then their AC has not been migrated to the auto-calculation schema yet.
updateData["data.attributes.ac.flat"] = ac.value; if (Number.isNumeric(ac?.value)) {
updateData["data.attributes.ac.-=value"] = null; 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; return updateData;
} }
@ -742,6 +754,22 @@ function _migrateItemRarity(item, updateData) {
return 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;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**

View file

@ -18,7 +18,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
// Prepare template data // Prepare template data
const templateData = { const templateData = {
t: templateShape, t: templateShape,
user: game.user.data._id, user: game.user.id,
distance: target.value, distance: target.value,
direction: 0, direction: 0,
x: 0, x: 0,
@ -96,7 +96,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
// Cancel the workflow (right-click) // Cancel the workflow (right-click)
handlers.rc = (event) => { handlers.rc = (event) => {
this.layer.preview.removeChildren(); this.layer._onDragLeftCancel(event);
canvas.stage.off("mousemove", handlers.mm); canvas.stage.off("mousemove", handlers.mm);
canvas.stage.off("mousedown", handlers.lc); canvas.stage.off("mousedown", handlers.lc);
canvas.app.view.oncontextmenu = null; canvas.app.view.oncontextmenu = null;

View file

@ -290,7 +290,7 @@ Hooks.once("ready", function () {
// Determine whether a system migration is required and feasible // 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 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 // Check for R1 SW5E versions
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A8"; const SW5E_NEEDS_MIGRATION_VERSION = "R1-A8";
const COMPATIBLE_MIGRATION_VERSION = 0.8; const COMPATIBLE_MIGRATION_VERSION = 0.8;

View file

@ -2,7 +2,7 @@
"name": "sw5e", "name": "sw5e",
"title": "SW 5th Edition", "title": "SW 5th Edition",
"description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.", "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", "author": "Dev Team",
"scripts": [], "scripts": [],
"esmodules": ["sw5e.js"], "esmodules": ["sw5e.js"],

View file

@ -945,7 +945,7 @@
], ],
"armor": { "armor": {
"type": "light", "type": "light",
"value": 10, "value": null,
"dex": null "dex": null
}, },
"speed": { "speed": {

View file

@ -121,36 +121,38 @@
</div> </div>
{{!-- Armor Class --}} {{!-- Armor Class --}}
<div class="form-group"> {{#if hasAC}}
<label>{{ localize "SW5E.ArmorClass" }}</label> <div class="form-group">
<div class="form-fields"> <label>{{ localize "SW5E.ArmorClass" }}</label>
<input type="text" name="data.armor.value" value="{{data.armor.value}}" data-dtype="Number"/> <div class="form-fields">
<input type="text" name="data.armor.value" value="{{data.armor.value}}" data-dtype="Number"/>
</div>
</div> </div>
</div> {{/if}}
{{#unless isMountable}} {{#if isArmor}}
{{!-- Dexterity Modifier --}} {{!-- Dexterity Modifier --}}
<div class="form-group"> <div class="form-group">
<label>{{ localize "SW5E.ItemEquipmentDexMod" }}</label> <label>{{ localize "SW5E.ItemEquipmentDexMod" }}</label>
<div class="form-fields"> <div class="form-fields">
<input type="text" name="data.armor.dex" value="{{data.armor.dex}}" data-dtype="Number" placeholder="{{ localize 'SW5E.Unlimited' }}"/> <input type="text" name="data.armor.dex" value="{{data.armor.dex}}" data-dtype="Number" placeholder="{{ localize 'SW5E.Unlimited' }}"/>
</div>
</div> </div>
</div>
{{!-- Required Strength --}} {{!-- Required Strength --}}
<div class="form-group"> <div class="form-group">
<label>{{ localize "SW5E.ItemRequiredStr" }}</label> <label>{{ localize "SW5E.ItemRequiredStr" }}</label>
<div class="form-fields"> <div class="form-fields">
<input type="text" name="data.strength" value="{{data.strength}}" data-dtype="Number" placeholder="{{ localize 'SW5E.None' }}"/> <input type="text" name="data.strength" value="{{data.strength}}" data-dtype="Number" placeholder="{{ localize 'SW5E.None' }}"/>
</div>
</div> </div>
</div>
{{!-- Stealth Disadvantage --}} {{!-- Stealth Disadvantage --}}
<div class="form-group"> <div class="form-group">
<label>{{ localize "SW5E.ItemEquipmentStealthDisav" }}</label> <label>{{ localize "SW5E.ItemEquipmentStealthDisav" }}</label>
<input type="checkbox" name="data.stealth" value="1" {{checked data.stealth}}/> <input type="checkbox" name="data.stealth" value="1" {{checked data.stealth}}/>
</div> </div>
{{/unless}} {{/if}}
{{#if isMountable}} {{#if isMountable}}
{{> 'systems/sw5e/templates/items/parts/item-mountable.html'}} {{> 'systems/sw5e/templates/items/parts/item-mountable.html'}}