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:
supervj 2021-08-06 16:38:15 -04:00
parent f16383841b
commit 5bb253d9c3
56 changed files with 5440 additions and 3827 deletions

View file

@ -1,4 +1,7 @@
import Actor5e from "../../entity.js";
import Item5e from "../../../item/entity.js";
import ProficiencySelector from "../../../apps/proficiency-selector.js";
import PropertyAttribution from "../../../apps/property-attribution.js";
import TraitSelector from "../../../apps/trait-selector.js";
import ActorSheetFlags from "../../../apps/actor-flags.js";
import ActorHitDiceConfig from "../../../apps/hit-dice-config.js";
@ -6,7 +9,7 @@ import ActorMovementConfig from "../../../apps/movement-config.js";
import ActorSensesConfig from "../../../apps/senses-config.js";
import ActorTypeConfig from "../../../apps/actor-type.js";
import {SW5E} from "../../../config.js";
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../../effects.js";
import ActiveEffect5e from "../../../active-effect.js";
/**
* Extend the basic ActorSheet class to suppose SW5e-specific logic and functionality.
@ -82,6 +85,7 @@ export default class ActorSheet5e extends ActorSheet {
// The Actor's data
const actorData = this.actor.data.toObject(false);
const source = this.actor.data._source.data;
data.actor = actorData;
data.data = actorData.data;
@ -102,6 +106,7 @@ export default class ActorSheet5e extends ActorSheet {
abl.icon = this._getProficiencyIcon(abl.proficient);
abl.hover = CONFIG.SW5E.proficiencyLevels[abl.proficient];
abl.label = CONFIG.SW5E.abilities[a];
abl.baseProf = source.abilities[a].proficient;
}
// Skills
@ -111,6 +116,7 @@ export default class ActorSheet5e extends ActorSheet {
skl.icon = this._getProficiencyIcon(skl.value);
skl.hover = CONFIG.SW5E.proficiencyLevels[skl.value];
skl.label = CONFIG.SW5E.skills[s];
skl.baseValue = source.skills[s].value;
}
}
@ -127,7 +133,15 @@ export default class ActorSheet5e extends ActorSheet {
this._prepareItems(data);
// Prepare active effects
data.effects = prepareActiveEffectCategories(this.actor.effects);
data.effects = ActiveEffect5e.prepareActiveEffectCategories(this.actor.effects);
// 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;
@ -197,6 +211,105 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Produce a list of armor class attribution objects.
* @param {object} data Actor data to determine the attributions from.
* @return {AttributionDescription[]} List of attribution descriptions.
*/
_prepareArmorClassAttribution(data) {
const ac = data.attributes.ac;
const cfg = CONFIG.SW5E.armorClasses[ac.calc];
const attribution = [];
// Base AC Attribution
switch (ac.calc) {
// Flat AC
case "flat":
return [
{
label: game.i18n.localize("SW5E.ArmorClassFlat"),
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: ac.flat
}
];
// Natural armor
case "natural":
attribution.push({
label: game.i18n.localize("SW5E.ArmorClassNatural"),
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: ac.flat
});
break;
// Equipment-based AC
case "default":
const hasArmor = !!this.actor.armor;
attribution.push({
label: hasArmor ? this.actor.armor.name : game.i18n.localize("SW5E.ArmorClassUnarmored"),
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: hasArmor ? this.actor.armor.data.data.armor.value : 10
});
if (ac.dex !== 0) {
attribution.push({
label: game.i18n.localize("SW5E.AbilityDex"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.dex
});
}
break;
// Other AC formula
default:
const formula = ac.calc === "custom" ? ac.formula : cfg.formula;
let base = ac.base;
const dataRgx = new RegExp(/@([a-z.0-9_\-]+)/gi);
for (const [match, term] of formula.matchAll(dataRgx)) {
const value = foundry.utils.getProperty(data, term);
if (term === "attributes.ac.base" || value === 0) continue;
if (Number.isNumeric(value)) base -= Number(value);
attribution.push({
label: match,
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: foundry.utils.getProperty(data, term)
});
}
attribution.unshift({
label: game.i18n.localize("SW5E.PropertyBase"),
mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE,
value: base
});
break;
}
// Shield
if (ac.shield !== 0)
attribution.push({
label: this.actor.shield?.name ?? game.i18n.localize("SW5E.EquipmentShield"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.shield
});
// Bonus
if (ac.bonus !== 0)
attribution.push({
label: game.i18n.localize("SW5E.Bonus"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.bonus
});
// Cover
if (ac.cover !== 0)
attribution.push({
label: game.i18n.localize("SW5E.Cover"),
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: ac.cover
});
return attribution;
}
/* -------------------------------------------- */
/**
* Prepare the data structure for traits data like languages, resistances & vulnerabilities, and proficiencies
* @param {object} traits The raw traits data object from the actor data
@ -208,10 +321,7 @@ export default class ActorSheet5e extends ActorSheet {
di: CONFIG.SW5E.damageResistanceTypes,
dv: CONFIG.SW5E.damageResistanceTypes,
ci: CONFIG.SW5E.conditionTypes,
languages: CONFIG.SW5E.languages,
armorProf: CONFIG.SW5E.armorProficiencies,
weaponProf: CONFIG.SW5E.weaponProficiencies,
toolProf: CONFIG.SW5E.toolProficiencies
languages: CONFIG.SW5E.languages
};
for (let [t, choices] of Object.entries(map)) {
const trait = traits[t];
@ -231,6 +341,14 @@ export default class ActorSheet5e extends ActorSheet {
}
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
}
// Populate and localize proficiencies
for (const t of ["armor", "weapon", "tool"]) {
const trait = traits[`${t}Prof`];
if (!trait) continue;
Actor5e.prepareProficiencies(trait, t);
trait.cssClass = !isObjectEmpty(trait.selected) ? "" : "inactive";
}
}
/* -------------------------------------------- */
@ -418,6 +536,9 @@ export default class ActorSheet5e extends ActorSheet {
// View Item Sheets
html.find(".item-edit").click(this._onItemEdit.bind(this));
// Property attributions
html.find(".attributable").mouseover(this._onPropertyAttribution.bind(this));
// Editable Only Listeners
if (this.isEditable) {
// Input focus and update
@ -432,6 +553,7 @@ export default class ActorSheet5e extends ActorSheet {
html.find(".skill-proficiency").on("click contextmenu", this._onCycleSkillProficiency.bind(this));
// Trait Selector
html.find(".proficiency-selector").click(this._onProficiencySelector.bind(this));
html.find(".trait-selector").click(this._onTraitSelector.bind(this));
// Configure Special Flags
@ -446,7 +568,7 @@ export default class ActorSheet5e extends ActorSheet {
html.find(".slot-max-override").click(this._onPowerSlotOverride.bind(this));
// Active Effect management
html.find(".effect-control").click((ev) => onManageActiveEffect(ev, this.actor));
html.find(".effect-control").click((ev) => ActiveEffect5e.onManageActiveEffect(ev, this.actor));
}
// Owner Only Listeners
@ -474,7 +596,7 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Iinitialize Item list filters by activating the set of filters which are currently applied
* Initialize Item list filters by activating the set of filters which are currently applied
* @private
*/
_initializeFilterItemList(i, ul) {
@ -517,6 +639,9 @@ export default class ActorSheet5e extends ActorSheet {
const button = event.currentTarget;
let app;
switch (button.dataset.action) {
case "armor":
app = new ActorArmorConfig(this.object);
break;
case "hit-dice":
app = new ActorHitDiceConfig(this.object);
break;
@ -530,7 +655,7 @@ export default class ActorSheet5e extends ActorSheet {
app = new ActorSensesConfig(this.object);
break;
case "type":
new ActorTypeConfig(this.object).render(true);
app = new ActorTypeConfig(this.object);
break;
}
app?.render(true);
@ -545,22 +670,19 @@ export default class ActorSheet5e extends ActorSheet {
*/
_onCycleSkillProficiency(event) {
event.preventDefault();
const field = $(event.currentTarget).siblings('input[type="hidden"]');
const field = event.currentTarget.previousElementSibling;
const skillName = field.parentElement.dataset.skill;
const source = this.actor.data._source.data.skills[skillName];
if (!source) return;
// Get the current level and the array of levels
const level = parseFloat(field.val());
// Cycle to the next or previous skill level
const levels = [0, 1, 0.5, 2];
let idx = levels.indexOf(level);
// Toggle next level - forward on click, backwards on right
if (event.type === "click") {
field.val(levels[idx === levels.length - 1 ? 0 : idx + 1]);
} else if (event.type === "contextmenu") {
field.val(levels[idx === 0 ? levels.length - 1 : idx - 1]);
}
let idx = levels.indexOf(source.value);
const next = idx + (event.type === "click" ? 1 : 3);
field.value = levels[next % 4];
// Update the field value and save the form
this._onSubmit(event);
return this._onSubmit(event);
}
/* -------------------------------------------- */
@ -675,7 +797,12 @@ export default class ActorSheet5e extends ActorSheet {
if (itemData.type === "consumable" && itemData.flags.core?.sourceId) {
const similarItem = this.actor.items.find((i) => {
const sourceId = i.getFlag("core", "sourceId");
return sourceId && sourceId === itemData.flags.core?.sourceId && i.type === "consumable";
return (
sourceId &&
sourceId === itemData.flags.core?.sourceId &&
i.type === "consumable" &&
i.name === itemData.name
);
});
if (similarItem) {
return similarItem.update({
@ -832,6 +959,22 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Handle displaying the property attribution tooltip when a property is hovered over.
* @param {Event} event The originating mouse event.
* @private
*/
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();
event.currentTarget.insertAdjacentElement("beforeend", html[0]);
}
/* -------------------------------------------- */
/**
* Handle rolling an Ability check, either a test or a saving throw
* @param {Event} event The originating click event
@ -888,6 +1031,22 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Handle spawning the ProficiencySelector application to configure armor, weapon, and tool proficiencies.
* @param {Event} event The click event which originated the selection
* @private
*/
_onProficiencySelector(event) {
event.preventDefault();
const a = event.currentTarget;
const label = a.parentElement.querySelector("label");
const options = {name: a.dataset.target, title: label.innerText, type: a.dataset.type};
if (options.type === "tool") options.sortCategories = true;
return new ProficiencySelector(this.actor, options).render(true);
}
/* -------------------------------------------- */
/**
* Handle spawning the TraitSelector application which allows a checkbox of multiple trait options
* @param {Event} event The click event which originated the selection