Updated to DND5e 1.3.2

Things unfinished:
 - Migration
 - The update adds new sections to the class sheet to allow some light customisation, this hasn't been included, but could be extended for the sake of dynamic classes with automatic class features and more
 - The French
 - The packs have not yet been updated, meaning due to the addition of a progression field to the class item, classes now don't set force or tech points
 - I updated the function calls in starships, but I didn't update it very thoroughly, it'll need checking
 - I only did a little testing
 - There has since been updates to DND5e that hasn't made it to release that patch bugs, those should be implemented
Things changed from base 5e:
 - Short rests and long rests were merged into one function, this needed some rewrites to account for force and tech points, and for printing the correct message
Extra Comments:
 - Unfinished code exists for automatic spell scrolls, this could be extended for single use force or tech powers
 - Weapon proficiencies probably need revising
 - Elven accuracy, halfling lucky, and reliable talent are present in the roll logic, this probably needs revising for sw5e
 - SW5e has a variant rule that permits force powers of any alignment to use either charisma or wisdom, that could be implemented
 - SW5e's version of gritty realism, [Longer Rests](https://sw5e.com/rules/variantRules/Longer%20Rests) differs from base dnd, this could be implemented
 - Extra ideas I've had while looking through the code can be found in Todos next to the ideas relevant context
This commit is contained in:
Jacob Lucas 2021-06-01 01:55:14 +01:00
parent aa07380c57
commit 2a7e1c419e
72 changed files with 3107 additions and 1359 deletions

View file

@ -18,9 +18,9 @@ export default class ItemSheet5e extends ItemSheet {
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
return foundry.utils.mergeObject(super.defaultOptions, {
width: 560,
height: 400,
classes: ["sw5e", "sheet", "item"],
@ -32,7 +32,7 @@ export default class ItemSheet5e extends ItemSheet {
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
get template() {
const path = "systems/sw5e/templates/items/";
return `${path}/${this.item.data.type}.html`;
@ -43,33 +43,39 @@ export default class ItemSheet5e extends ItemSheet {
/** @override */
async getData(options) {
const data = super.getData(options);
const itemData = data.data;
data.labels = this.item.labels;
data.config = CONFIG.SW5E;
// Item Type, Status, and Details
data.itemType = game.i18n.localize(`ITEM.Type${data.item.type.titleCase()}`);
data.itemStatus = this._getItemStatus(data.item);
data.itemProperties = this._getItemProperties(data.item);
data.isPhysical = data.item.data.hasOwnProperty("quantity");
data.itemStatus = this._getItemStatus(itemData);
data.itemProperties = this._getItemProperties(itemData);
data.isPhysical = itemData.data.hasOwnProperty("quantity");
// Potential consumption targets
data.abilityConsumptionTargets = this._getItemConsumptionTargets(data.item);
data.abilityConsumptionTargets = this._getItemConsumptionTargets(itemData);
// Action Detail
data.hasAttackRoll = this.item.hasAttack;
data.isHealing = data.item.data.actionType === "heal";
data.isFlatDC = getProperty(data.item.data, "save.scaling") === "flat";
data.isLine = ["line", "wall"].includes(data.item.data.target?.type);
data.isHealing = itemData.data.actionType === "heal";
data.isFlatDC = getProperty(itemData, "data.save.scaling") === "flat";
data.isLine = ["line", "wall"].includes(itemData.data.target?.type);
// Original maximum uses formula
if (this.item._data.data?.uses?.max) data.data.uses.max = this.item._data.data.uses.max;
const sourceMax = foundry.utils.getProperty(this.item.data._source, "data.uses.max");
if ( sourceMax ) itemData.data.uses.max = sourceMax;
// Vehicles
data.isCrewed = data.item.data.activation?.type === "crew";
data.isMountable = this._isItemMountable(data.item);
data.isCrewed = itemData.data.activation?.type === "crew";
data.isMountable = this._isItemMountable(itemData);
// Prepare Active Effects
data.effects = prepareActiveEffectCategories(this.entity.effects);
data.effects = prepareActiveEffectCategories(this.item.effects);
// Re-define the template data references (backwards compatible)
data.item = itemData;
data.data = itemData.data;
return data;
}
@ -102,9 +108,11 @@ export default class ItemSheet5e extends ItemSheet {
// Attributes
else if (consume.type === "attribute") {
const attributes = Object.values(CombatTrackerConfig.prototype.getAttributeChoices())[0]; // Bit of a hack
return attributes.reduce((obj, a) => {
obj[a] = a;
const attributes = TokenDocument.getTrackedAttributes(actor.data.data);
attributes.bar.forEach(a => a.push("value"));
return attributes.bar.concat(attributes.value).reduce((obj, a) => {
let k = a.join(".");
obj[k] = k;
return obj;
}, {});
}
@ -186,6 +194,7 @@ export default class ItemSheet5e extends ItemSheet {
props.push(labels.armor);
} else if (item.type === "feat") {
props.push(labels.featType);
//TODO: Work out these
} else if (item.type === "species") {
//props.push(labels.species);
} else if (item.type === "archetype") {
@ -238,7 +247,7 @@ export default class ItemSheet5e extends ItemSheet {
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
setPosition(position = {}) {
if (!(this._minimized || position.height)) {
position.height = this._tabs[0].active === "details" ? "auto" : this.options.height;
@ -250,7 +259,7 @@ export default class ItemSheet5e extends ItemSheet {
/* Form Submission */
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
_getSubmitData(updateData = {}) {
// Create the expanded update data object
const fd = new FormDataExtended(this.form, { editors: this.editors });
@ -268,17 +277,14 @@ export default class ItemSheet5e extends ItemSheet {
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
activateListeners(html) {
super.activateListeners(html);
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(".trait-selector.class-skills").click(this._onConfigureTraits.bind(this));
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."
);
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.");
onManageActiveEffect(ev, this.item);
});
}
@ -307,7 +313,7 @@ export default class ItemSheet5e extends ItemSheet {
if (a.classList.contains("delete-damage")) {
await this._onSubmit(event); // Submit any unsaved changes
const li = a.closest(".damage-part");
const damage = duplicate(this.item.data.data.damage);
const damage = foundry.utils.deepClone(this.item.data.data.damage);
damage.parts.splice(Number(li.dataset.damagePart), 1);
return this.item.update({ "data.damage.parts": damage.parts });
}
@ -316,33 +322,39 @@ export default class ItemSheet5e extends ItemSheet {
/* -------------------------------------------- */
/**
* Handle spawning the TraitSelector application which allows a checkbox of multiple trait options
* Handle spawning the TraitSelector application for selection various options.
* @param {Event} event The click event which originated the selection
* @private
*/
_onConfigureClassSkills(event) {
_onConfigureTraits(event) {
event.preventDefault();
const skills = this.item.data.data.skills;
const choices = skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
const a = event.currentTarget;
const label = a.parentElement;
// Render the Trait Selector dialog
new TraitSelector(this.item, {
const options = {
name: a.dataset.target,
title: label.innerText,
choices: Object.entries(CONFIG.SW5E.skills).reduce((obj, e) => {
if (choices.includes(e[0])) obj[e[0]] = e[1];
return obj;
}, {}),
minimum: skills.number,
maximum: skills.number
}).render(true);
title: a.parentElement.innerText,
choices: [],
allowCustom: false
};
switch(a.dataset.options) {
case 'saves':
options.choices = CONFIG.SW5E.abilities;
options.valueKey = null;
break;
case 'skills':
const skills = this.item.data.data.skills;
const choiceSet = skills.choices && skills.choices.length ? skills.choices : Object.keys(CONFIG.SW5E.skills);
options.choices = Object.fromEntries(Object.entries(CONFIG.SW5E.skills).filter(skill => choiceSet.includes(skill[0])));
options.maximum = skills.number;
break;
}
new TraitSelector(this.item, options).render(true);
}
/* -------------------------------------------- */
/** @override */
/** @inheritdoc */
async _onSubmit(...args) {
if (this._tabs[0].active === "details") this.position.height = "auto";
await super._onSubmit(...args);