forked from GitHub-Mirrors/foundry-sw5e
Update Core to 1.4.1
Update Core to 1.4.1 and internal Version to 1.4.1.R1-A8
This commit is contained in:
parent
f16383841b
commit
5bb253d9c3
56 changed files with 5440 additions and 3827 deletions
94
module/apps/actor-armor.js
Normal file
94
module/apps/actor-armor.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Interface for managing a character's armor calculation.
|
||||
* @extends {DocumentSheet}
|
||||
*/
|
||||
export default class ActorArmorConfig extends DocumentSheet {
|
||||
/** @inheritdoc */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
id: "actor-armor-config",
|
||||
classes: ["sw5e", "actor-armor-config"],
|
||||
template: "systems/sw5e/templates/apps/actor-armor.html",
|
||||
width: 320,
|
||||
height: "auto"
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
get title() {
|
||||
return `${game.i18n.localize("SW5E.ArmorConfig")}: ${this.document.name}`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
async getData() {
|
||||
// Get actor AC data
|
||||
const actorData = foundry.utils.deepClone(this.object.data.data);
|
||||
const ac = foundry.utils.getProperty(actorData, "attributes.ac");
|
||||
|
||||
// Get configuration data for the calculation mode
|
||||
let cfg = CONFIG.SW5E.armorClasses[ac.calc];
|
||||
if (!cfg) {
|
||||
ac.calc = "flat";
|
||||
cfg = CONFIG.SW5E.armorClasses.flat;
|
||||
}
|
||||
|
||||
// Return context data
|
||||
return {
|
||||
ac: ac,
|
||||
calculations: CONFIG.SW5E.armorClasses,
|
||||
value: this.object._computeArmorClass(actorData).value,
|
||||
valueDisabled: !["flat", "natural"].includes(ac.calc),
|
||||
formula: ac.calc === "custom" ? ac.formula : cfg.formula,
|
||||
formulaDisabled: ac.calc !== "custom"
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
async _updateObject(event, formData) {
|
||||
const ac = foundry.utils.expandObject(formData).ac;
|
||||
return this.object.update({"data.attributes.ac": ac});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Event Listeners and Handlers */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
async _onChangeInput(event) {
|
||||
await super._onChangeInput(event);
|
||||
|
||||
// Reference actor data
|
||||
let actorData = this.object.toObject(false);
|
||||
let ac = actorData.data.attributes.ac;
|
||||
|
||||
// Reference form data
|
||||
const calc = this.form["ac.calc"].value;
|
||||
const cfg = CONFIG.SW5E.armorClasses[calc];
|
||||
const enableFlat = ["flat", "natural"].includes(calc);
|
||||
|
||||
// Handle changes to the calculation mode specifically
|
||||
let formula = this.form["ac.formula"].value;
|
||||
let flat = this.form["ac.flat"].value;
|
||||
if (event.currentTarget.name === "ac.calc") {
|
||||
formula = calc === "custom" ? ac.formula : cfg.formula;
|
||||
if (enableFlat) flat = ac.flat;
|
||||
}
|
||||
|
||||
// Recompute effective AC
|
||||
actorData = foundry.utils.mergeObject(actorData, {"data.attributes.ac": {calc, formula}});
|
||||
if (enableFlat) actorData.data.attributes.ac.flat = flat;
|
||||
ac = this.object._computeArmorClass(actorData.data);
|
||||
|
||||
// Update fields
|
||||
this.form["ac.formula"].value = ac.formula;
|
||||
this.form["ac.formula"].disabled = calc !== "custom";
|
||||
this.form["ac.flat"].value = enableFlat ? ac.flat : ac.value;
|
||||
this.form["ac.flat"].disabled = !enableFlat;
|
||||
}
|
||||
}
|
|
@ -99,7 +99,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
|||
{name: "data.bonuses.power.techDC", label: "SW5E.BonusTechPowerDC"}
|
||||
];
|
||||
for (let b of bonuses) {
|
||||
b.value = getProperty(this.object._data, b.name) || "";
|
||||
b.value = getProperty(this.object.data._source, b.name) || "";
|
||||
}
|
||||
return bonuses;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ export default class ActorSheetFlags extends DocumentSheet {
|
|||
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._source.flags, `sw5e.${k}`)) {
|
||||
unset = true;
|
||||
flags[`-=${k}`] = null;
|
||||
}
|
||||
|
|
120
module/apps/proficiency-selector.js
Normal file
120
module/apps/proficiency-selector.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
import TraitSelector from "./trait-selector.js";
|
||||
|
||||
/**
|
||||
* An application for selecting proficiencies with categories that can contain children.
|
||||
*
|
||||
* @extends {TraitSelector}
|
||||
*/
|
||||
export default class ProficiencySelector extends TraitSelector {
|
||||
/** @inheritdoc */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
title: "Actor Proficiency Selection",
|
||||
type: "",
|
||||
sortCategories: false
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
async getData() {
|
||||
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
||||
const value = this.options.valueKey ? foundry.utils.getProperty(attr, this.options.valueKey) ?? [] : attr;
|
||||
|
||||
this.options.choices = CONFIG.SW5E[`${this.options.type}Proficiencies`];
|
||||
const data = super.getData();
|
||||
|
||||
const pack = game.packs.get(CONFIG.SW5E.sourcePacks.ITEMS);
|
||||
const ids = CONFIG.SW5E[`${this.options.type}Ids`];
|
||||
const map = CONFIG.SW5E[`${this.options.type}ProficienciesMap`];
|
||||
if (ids !== undefined) {
|
||||
const typeProperty = this.options.type !== "armor" ? `${this.options.type}Type` : `armor.type`;
|
||||
for (const [key, id] of Object.entries(ids)) {
|
||||
const item = await pack.getDocument(id);
|
||||
let type = foundry.utils.getProperty(item.data.data, typeProperty);
|
||||
if (map && map[type]) type = map[type];
|
||||
const entry = {
|
||||
label: item.name,
|
||||
chosen: attr ? value.includes(key) : false
|
||||
};
|
||||
if (data.choices[type] === undefined) {
|
||||
data.choices[key] = entry;
|
||||
} else {
|
||||
if (data.choices[type].children === undefined) {
|
||||
data.choices[type].children = {};
|
||||
}
|
||||
data.choices[type].children[key] = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.type === "tool") {
|
||||
data.choices["vehicle"].children = Object.entries(CONFIG.SW5E.vehicleTypes).reduce((obj, [key, label]) => {
|
||||
obj[key] = {
|
||||
label: label,
|
||||
chosen: attr ? value.includes(key) : false
|
||||
};
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
if (this.options.sortCategories) data.choices = this._sortObject(data.choices);
|
||||
for (const category of Object.values(data.choices)) {
|
||||
if (!category.children) continue;
|
||||
category.children = this._sortObject(category.children);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Take the provided object and sort by the "label" property.
|
||||
*
|
||||
* @param {object} object Object to be sorted.
|
||||
* @return {object} Sorted object.
|
||||
* @private
|
||||
*/
|
||||
_sortObject(object) {
|
||||
return Object.fromEntries(Object.entries(object).sort((lhs, rhs) => lhs[1].label.localeCompare(rhs[1].label)));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
for (const checkbox of html[0].querySelectorAll("input[type='checkbox']")) {
|
||||
if (checkbox.checked) this._onToggleCategory(checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
async _onChangeInput(event) {
|
||||
super._onChangeInput(event);
|
||||
|
||||
if (event.target.tagName === "INPUT") this._onToggleCategory(event.target);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Enable/disable all children when a category is checked.
|
||||
*
|
||||
* @param {HTMLElement} checkbox Checkbox that was changed.
|
||||
* @private
|
||||
*/
|
||||
_onToggleCategory(checkbox) {
|
||||
const children = checkbox.closest("li")?.querySelector("ol");
|
||||
if (!children) return;
|
||||
|
||||
for (const child of children.querySelectorAll("input[type='checkbox']")) {
|
||||
child.checked = child.disabled = checkbox.checked;
|
||||
}
|
||||
}
|
||||
}
|
92
module/apps/property-attribution.js
Normal file
92
module/apps/property-attribution.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Description for a single part of a property attribution.
|
||||
*
|
||||
* @typedef {object} AttributionDescription
|
||||
* @property {string} label Descriptive label that will be displayed. If the label is in the form
|
||||
* of an @ property, the system will try to turn it into a human-readable label.
|
||||
* @property {number} mode Application mode for this step as defined in
|
||||
* [CONST.ACTIVE_EFFECT_MODES](https://foundryvtt.com/api/module-constants.html#.ACTIVE_EFFECT_MODES).
|
||||
* @property {number} value Value of this step.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for viewing what factors went into determining a specific property.
|
||||
*
|
||||
* @extends {DocumentSheet}
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
constructor(object, attribution, property, options = {}) {
|
||||
super(options);
|
||||
this.object = object;
|
||||
this.attribution = attribution;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
id: "property-attribution",
|
||||
classes: ["sw5e", "property-attribution"],
|
||||
template: "systems/sw5e/templates/apps/property-attribution.html",
|
||||
width: 320,
|
||||
height: "auto"
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Render this view as a tooltip rather than a whole window.
|
||||
* @return {jQuery} HTML of the rendered tooltip.
|
||||
*/
|
||||
async renderTooltip() {
|
||||
const data = this.getData(this.options);
|
||||
let html = await this._renderInner(data);
|
||||
html[0].classList.add("tooltip");
|
||||
return html;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
getData() {
|
||||
const property = foundry.utils.getProperty(this.object.data.data, this.property);
|
||||
let total;
|
||||
if (Number.isNumeric(property)) {
|
||||
total = property;
|
||||
} else if (typeof property === "object" && Number.isNumeric(property.value)) {
|
||||
total = property.value;
|
||||
}
|
||||
|
||||
const sources = foundry.utils.duplicate(this.attribution[this.property]);
|
||||
return {
|
||||
sources: sources.map((entry) => {
|
||||
if (entry.label.startsWith("@")) {
|
||||
entry.label = this.getPropertyLabel(entry.label.slice(1));
|
||||
}
|
||||
if (entry.mode === CONST.ACTIVE_EFFECT_MODES.ADD && entry.value < 0) {
|
||||
entry.negative = true;
|
||||
entry.value = entry.value * -1;
|
||||
}
|
||||
return entry;
|
||||
}),
|
||||
total: total
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
getPropertyLabel(property) {
|
||||
const parts = property.split(".");
|
||||
if (parts[0] === "abilities" && parts[1]) {
|
||||
return CONFIG.SW5E.abilities[parts[1]];
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,13 @@ export default class TraitSelector extends DocumentSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritdoc */
|
||||
get title() {
|
||||
return this.options.title || super.title;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Return a reference to the target attribute
|
||||
* @type {string}
|
||||
|
@ -37,8 +44,8 @@ export default class TraitSelector extends DocumentSheet {
|
|||
getData() {
|
||||
const attr = foundry.utils.getProperty(this.object.data, this.attribute);
|
||||
const o = this.options;
|
||||
const value = o.valueKey ? attr[o.valueKey] ?? [] : attr;
|
||||
const custom = o.customKey ? attr[o.customKey] ?? "" : "";
|
||||
const value = o.valueKey ? foundry.utils.getProperty(attr, o.valueKey) ?? [] : attr;
|
||||
const custom = o.customKey ? foundry.utils.getProperty(attr, o.customKey) ?? "" : "";
|
||||
|
||||
// Populate choices
|
||||
const choices = Object.entries(o.choices).reduce((obj, e) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue