Merge pull request #255 from unrealkakeman89/Core-update-1.4.2

Update Core to 1.4.2
This commit is contained in:
CK 2021-08-13 16:12:25 -04:00 committed by GitHub
commit 299b5eccd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 146 additions and 71 deletions

View file

@ -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]);
}

View file

@ -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]);
}

View file

@ -16,14 +16,14 @@
*/
export default class PropertyAttribution extends Application {
/**
* @param {Document} object - Object containing the property to be attributed.
* @param {object.<string, AttributionDescription[]>} 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("@")) {

View file

@ -400,7 +400,6 @@ SW5E.armorTypes = {
*/
SW5E.equipmentTypes = {
hyper: "SW5E.EquipmentHyperdrive",
bonus: "SW5E.EquipmentBonus",
powerc: "SW5E.EquipmentPowerCoupling",
reactor: "SW5E.EquipmentReactor",
clothing: "SW5E.EquipmentClothing",

View file

@ -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);

View file

@ -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;
}
/* -------------------------------------------- */
/**

View file

@ -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;

View file

@ -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;

View file

@ -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"],

View file

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

View file

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