chore: reformat with 2 spaces
This commit is contained in:
parent
d659e4bed9
commit
7670d7f808
1577 changed files with 70010 additions and 70042 deletions
|
@ -3,36 +3,36 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
export class DS4ActiveEffectConfig extends ActiveEffectConfig {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs",
|
||||
});
|
||||
}
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {JQuery} html
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]');
|
||||
checkbox?.addEventListener("change", () => this.#toggleItemEffectConfig(checkbox.checked));
|
||||
}
|
||||
/**
|
||||
* @override
|
||||
* @param {JQuery} html
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]');
|
||||
checkbox?.addEventListener("change", () => this.#toggleItemEffectConfig(checkbox.checked));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the visibility of the item effect config section
|
||||
* @param {boolean} active The target state
|
||||
*/
|
||||
#toggleItemEffectConfig(active) {
|
||||
const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config");
|
||||
elements?.forEach((element) => {
|
||||
if (active) {
|
||||
element.classList.remove("ds4-hidden");
|
||||
} else {
|
||||
element.classList.add("ds4-hidden");
|
||||
}
|
||||
});
|
||||
this.setPosition({ height: "auto" });
|
||||
}
|
||||
/**
|
||||
* Toggle the visibility of the item effect config section
|
||||
* @param {boolean} active The target state
|
||||
*/
|
||||
#toggleItemEffectConfig(active) {
|
||||
const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config");
|
||||
elements?.forEach((element) => {
|
||||
if (active) {
|
||||
element.classList.remove("ds4-hidden");
|
||||
} else {
|
||||
element.classList.add("ds4-hidden");
|
||||
}
|
||||
});
|
||||
this.setPosition({ height: "auto" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,487 +17,481 @@ import { disableOverriddenFields } from "../sheet-helpers";
|
|||
* The base sheet class for all {@link DS4Actor}s.
|
||||
*/
|
||||
export class DS4ActorSheet extends ActorSheet {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet"],
|
||||
height: 645,
|
||||
scrollY: [".ds4-sheet-body"],
|
||||
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "values" }],
|
||||
dragDrop: [
|
||||
{ dragSelector: ".item-list .item", dropSelector: null },
|
||||
{ dragSelector: ".effect-list .effect", dropSelector: null },
|
||||
{ dragSelector: ".ds4-check", dropSelector: null },
|
||||
],
|
||||
width: 650,
|
||||
});
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet"],
|
||||
height: 645,
|
||||
scrollY: [".ds4-sheet-body"],
|
||||
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "values" }],
|
||||
dragDrop: [
|
||||
{ dragSelector: ".item-list .item", dropSelector: null },
|
||||
{ dragSelector: ".effect-list .effect", dropSelector: null },
|
||||
{ dragSelector: ".ds4-check", dropSelector: null },
|
||||
],
|
||||
width: 650,
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
get template() {
|
||||
const basePath = "systems/ds4/templates/sheets/actor";
|
||||
if (!getGame().user?.isGM && this.actor.limited) return `${basePath}/limited-sheet.hbs`;
|
||||
return `${basePath}/${this.actor.type}-sheet.hbs`;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const itemsByType = Object.fromEntries(
|
||||
Object.entries(this.actor.itemTypes).map(([itemType, items]) => {
|
||||
return [itemType, [...items].sort((a, b) => (a.sort || 0) - (b.sort || 0))];
|
||||
}),
|
||||
);
|
||||
|
||||
const enrichedEffects = [...this.actor.allApplicableEffects()].map((effect) => {
|
||||
return {
|
||||
...effect.toObject(),
|
||||
sourceName: effect.parent instanceof Item ? effect.parent.name : effect.sourceName,
|
||||
factor: effect.factor,
|
||||
active: effect.active,
|
||||
uuid: effect.uuid,
|
||||
};
|
||||
});
|
||||
|
||||
const context = {
|
||||
...this.addTooltipsToData(await super.getData(options)),
|
||||
config: DS4,
|
||||
itemsByType,
|
||||
enrichedEffects,
|
||||
settings: getDS4Settings(),
|
||||
};
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tooltips to the attributes, traits, and combatValues of the given context object.
|
||||
* @param {object} context
|
||||
* @protected
|
||||
*/
|
||||
addTooltipsToData(context) {
|
||||
const valueGroups = [context.data.system.attributes, context.data.system.traits, context.data.system.combatValues];
|
||||
|
||||
valueGroups.forEach((valueGroup) => {
|
||||
Object.values(valueGroup).forEach((attribute) => {
|
||||
attribute.tooltip = this.getTooltipForValue(attribute);
|
||||
});
|
||||
});
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a tooltip for a given attribute, trait, or combatValue.
|
||||
* @param {import("../../documents/common/common-data").ModifiableDataBaseTotal<number>} value The value to get a tooltip for
|
||||
* @returns {string} The tooltip
|
||||
* @protected
|
||||
*/
|
||||
getTooltipForValue(value) {
|
||||
return `${value.base} (${getGame().i18n.localize("DS4.TooltipBaseValue")}) + ${
|
||||
value.mod
|
||||
} (${getGame().i18n.localize("DS4.TooltipModifier")}) ➞ ${getGame().i18n.localize("DS4.TooltipEffects")} ➞ ${
|
||||
value.total
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JQuery} html
|
||||
* @override
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!this.options.editable) return;
|
||||
|
||||
html.find(".control-item").on("click", this.onControlItem.bind(this));
|
||||
html.find(".change-item").on("change", this.onChangeItem.bind(this));
|
||||
|
||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||
html.find(".change-effect").on("change", this.onChangeEffect.bind(this));
|
||||
|
||||
html.find(".rollable-item").on("click", this.onRollItem.bind(this));
|
||||
html.find(".rollable-check").on("click", this.onRollCheck.bind(this));
|
||||
|
||||
html.find(".sort-items").on("click", this.onSortItems.bind(this));
|
||||
|
||||
disableOverriddenFields(this.form, this.actor.overrides, (key) => `[name="${key}"]`);
|
||||
for (const item of this.actor.items) {
|
||||
disableOverriddenFields(
|
||||
this.form,
|
||||
item.overrides,
|
||||
(key) => `[data-item-uuid="${item.uuid}"] .change-item[data-property="${key}"]`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
get template() {
|
||||
const basePath = "systems/ds4/templates/sheets/actor";
|
||||
if (!getGame().user?.isGM && this.actor.limited) return `${basePath}/limited-sheet.hbs`;
|
||||
return `${basePath}/${this.actor.type}-sheet.hbs`;
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded item of the actor corresponding to this sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlItem(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateItem(event);
|
||||
case "edit":
|
||||
return this.onEditItem(event);
|
||||
case "delete":
|
||||
return this.onDeleteItem(event);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const itemsByType = Object.fromEntries(
|
||||
Object.entries(this.actor.itemTypes).map(([itemType, items]) => {
|
||||
return [itemType, [...items].sort((a, b) => (a.sort || 0) - (b.sort || 0))];
|
||||
}),
|
||||
);
|
||||
/**
|
||||
* Creates a new embedded item using the initial data defined in the HTML dataset of the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onCreateItem(event) {
|
||||
const { type } = foundry.utils.deepClone(event.currentTarget.dataset);
|
||||
const name = getGame().i18n.localize(`DS4.New${type.capitalize()}Name`);
|
||||
const itemData = { name, type };
|
||||
Item.create(itemData, { parent: this.actor });
|
||||
}
|
||||
|
||||
const enrichedEffects = [...this.actor.allApplicableEffects()].map((effect) => {
|
||||
return {
|
||||
...effect.toObject(),
|
||||
sourceName: effect.parent instanceof Item ? effect.parent.name : effect.sourceName,
|
||||
factor: effect.factor,
|
||||
active: effect.active,
|
||||
uuid: effect.uuid,
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Opens the sheet of the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onEditItem(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.sheet?.render(true);
|
||||
}
|
||||
|
||||
const context = {
|
||||
...this.addTooltipsToData(await super.getData(options)),
|
||||
config: DS4,
|
||||
itemsByType,
|
||||
enrichedEffects,
|
||||
settings: getDS4Settings(),
|
||||
};
|
||||
return context;
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onDeleteItem(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.delete();
|
||||
$(li).slideUp(200, () => this.render(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded item depending on the `data-property` attribute of the
|
||||
* {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onChangeItem(event) {
|
||||
return this.onChangeEmbeddedDocument(event, "Item");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded effect of the actor corresponding to this
|
||||
* sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlEffect(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateEffect();
|
||||
case "edit":
|
||||
return this.onEditEffect(event);
|
||||
case "delete":
|
||||
return this.onDeleteEffect(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new embedded effect.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onCreateEffect() {
|
||||
DS4ActiveEffect.createDefault(this.actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet of the embedded effect corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onEditEffect(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.ActiveEffect.uuidDataAttribute];
|
||||
const effect = await fromUuid(uuid);
|
||||
enforce(
|
||||
effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
effect.sheet?.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onDeleteEffect(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.ActiveEffect.uuidDataAttribute];
|
||||
const effect = await fromUuid(uuid);
|
||||
enforce(
|
||||
effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
effect.delete();
|
||||
$(li).slideUp(200, () => this.render(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded effect depending on the `data-property` attribute of the
|
||||
* {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onChangeEffect(event) {
|
||||
return this.onChangeEmbeddedDocument(event, "ActiveEffect");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded document of the actor belonging to this sheet. The change depends
|
||||
* on the `data-property` attribute of the {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ChangeEvent} event The originating click event
|
||||
* @param {"Item" | "ActiveEffect"} documentName The name of the embedded document to be changed.
|
||||
* @protected
|
||||
*/
|
||||
async onChangeEmbeddedDocument(event, documentName) {
|
||||
event.preventDefault();
|
||||
const element = $(event.currentTarget).get(0);
|
||||
if (element.disabled) return;
|
||||
|
||||
const documentElement = element.closest(embeddedDocumentListEntryProperties[documentName].selector);
|
||||
const uuid = documentElement.dataset[embeddedDocumentListEntryProperties[documentName].uuidDataAttribute];
|
||||
const property = element.dataset["property"];
|
||||
enforce(property !== undefined, TypeError("HTML element does not provide 'data-property' attribute"));
|
||||
|
||||
const newValue = this.parseValue(element);
|
||||
|
||||
const document = await fromUuid(uuid);
|
||||
|
||||
if (documentName === "Item") {
|
||||
enforce(
|
||||
document && document.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
} else {
|
||||
enforce(
|
||||
document && (document.parent === this.actor || document.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
}
|
||||
document.update({ [property]: newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the value of the given {@link HTMLInputElement} depending on the element's type
|
||||
* The value is parsed to:
|
||||
* - checkbox: `boolean`, if the attribute `data-inverted` is set to a truthy value, the parsed value is inverted
|
||||
* - text input: `string`
|
||||
* - number: `number`
|
||||
*
|
||||
* @param {HTMLInputElement} element The input element to parse the value from
|
||||
* @returns {boolean | string | number} The parsed data
|
||||
* @protected
|
||||
*/
|
||||
parseValue(element) {
|
||||
switch (element.type) {
|
||||
case "checkbox": {
|
||||
const inverted = Boolean(element.dataset["inverted"]);
|
||||
const value = element.checked;
|
||||
return inverted ? !value : value;
|
||||
}
|
||||
case "text": {
|
||||
const value = element.value;
|
||||
return value;
|
||||
}
|
||||
case "number": {
|
||||
const value = Number(element.value.trim());
|
||||
return value;
|
||||
}
|
||||
default: {
|
||||
throw new TypeError("Binding of item property to this type of HTML element not supported; given: " + element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clickable item rolls.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onRollItem(event) {
|
||||
event.preventDefault();
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.roll().catch((e) => notifications.error(e, { log: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clickable check rolls.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onRollCheck(event) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.blur();
|
||||
const check = event.currentTarget.dataset["check"];
|
||||
this.actor.rollCheck(check).catch((e) => notifications.error(e, { log: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @override
|
||||
*/
|
||||
_onDragStart(event) {
|
||||
const target = event.currentTarget;
|
||||
if (!(target instanceof HTMLElement)) return super._onDragStart(event);
|
||||
|
||||
const check = target.dataset.check;
|
||||
if (!check) return super._onDragStart(event);
|
||||
|
||||
enforce(isCheck(check), getGame().i18n.format("DS4.ErrorCannotDragMissingCheck", { check }));
|
||||
|
||||
const dragData = {
|
||||
actorId: this.actor.id,
|
||||
sceneId: this.actor.isToken ? getCanvas().scene?.id : null,
|
||||
tokenId: this.actor.isToken ? this.actor.token?.id : null,
|
||||
type: "Check",
|
||||
data: check,
|
||||
};
|
||||
|
||||
event.dataTransfer?.setData("text/plain", JSON.stringify(dragData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort items according to the item list header that has been clicked.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onSortItems(event) {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget;
|
||||
const type = target.parentElement?.dataset["type"];
|
||||
enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`);
|
||||
const dataPath = target.dataset["dataPath"];
|
||||
enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`);
|
||||
const dataPath2 = target.dataset["dataPath2"];
|
||||
/** @type {import("../../documents/item/item").DS4Item[]}*/
|
||||
const items = this.actor.items.filter((item) => item.type === type);
|
||||
items.sort((a, b) => a.sort - b.sort);
|
||||
|
||||
/**
|
||||
* Adds tooltips to the attributes, traits, and combatValues of the given context object.
|
||||
* @param {object} context
|
||||
* @protected
|
||||
* @param {boolean} invert Whether or not to inverse the sort order
|
||||
* @returns {(a: import("../../documents/item/item").DS4Item, b: import("../../documents/item/item").DS4Item) => number} A function for sorting items
|
||||
*/
|
||||
addTooltipsToData(context) {
|
||||
const valueGroups = [
|
||||
context.data.system.attributes,
|
||||
context.data.system.traits,
|
||||
context.data.system.combatValues,
|
||||
];
|
||||
const sortFunction = (invert) => (a, b) => {
|
||||
const propertyA = getProperty(a, dataPath);
|
||||
const propertyB = getProperty(b, dataPath);
|
||||
const comparison =
|
||||
typeof propertyA === "string" || typeof propertyB === "string"
|
||||
? compareAsStrings(propertyA, propertyB, invert)
|
||||
: compareAsNumbers(propertyA, propertyB, invert);
|
||||
|
||||
valueGroups.forEach((valueGroup) => {
|
||||
Object.values(valueGroup).forEach((attribute) => {
|
||||
attribute.tooltip = this.getTooltipForValue(attribute);
|
||||
});
|
||||
});
|
||||
return context;
|
||||
if (comparison === 0 && dataPath2 !== undefined) {
|
||||
const propertyA = getProperty(a, dataPath);
|
||||
const propertyB = getProperty(b, dataPath);
|
||||
return typeof propertyA === "string" || typeof propertyB === "string"
|
||||
? compareAsStrings(propertyA, propertyB, invert)
|
||||
: compareAsNumbers(propertyA, propertyB, invert);
|
||||
}
|
||||
|
||||
return comparison;
|
||||
};
|
||||
|
||||
const sortedItems = [...items].sort(sortFunction(false));
|
||||
const wasSortedAlready = !sortedItems.find((item, index) => item !== items[index]);
|
||||
|
||||
if (wasSortedAlready) {
|
||||
sortedItems.sort(sortFunction(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a tooltip for a given attribute, trait, or combatValue.
|
||||
* @param {import("../../documents/common/common-data").ModifiableDataBaseTotal<number>} value The value to get a tooltip for
|
||||
* @returns {string} The tooltip
|
||||
* @protected
|
||||
*/
|
||||
getTooltipForValue(value) {
|
||||
return `${value.base} (${getGame().i18n.localize("DS4.TooltipBaseValue")}) + ${
|
||||
value.mod
|
||||
} (${getGame().i18n.localize("DS4.TooltipModifier")}) ➞ ${getGame().i18n.localize("DS4.TooltipEffects")} ➞ ${
|
||||
value.total
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JQuery} html
|
||||
* @override
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!this.options.editable) return;
|
||||
|
||||
html.find(".control-item").on("click", this.onControlItem.bind(this));
|
||||
html.find(".change-item").on("change", this.onChangeItem.bind(this));
|
||||
|
||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||
html.find(".change-effect").on("change", this.onChangeEffect.bind(this));
|
||||
|
||||
html.find(".rollable-item").on("click", this.onRollItem.bind(this));
|
||||
html.find(".rollable-check").on("click", this.onRollCheck.bind(this));
|
||||
|
||||
html.find(".sort-items").on("click", this.onSortItems.bind(this));
|
||||
|
||||
disableOverriddenFields(this.form, this.actor.overrides, (key) => `[name="${key}"]`);
|
||||
for (const item of this.actor.items) {
|
||||
disableOverriddenFields(
|
||||
this.form,
|
||||
item.overrides,
|
||||
(key) => `[data-item-uuid="${item.uuid}"] .change-item[data-property="${key}"]`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded item of the actor corresponding to this sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlItem(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateItem(event);
|
||||
case "edit":
|
||||
return this.onEditItem(event);
|
||||
case "delete":
|
||||
return this.onDeleteItem(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new embedded item using the initial data defined in the HTML dataset of the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onCreateItem(event) {
|
||||
const { type } = foundry.utils.deepClone(event.currentTarget.dataset);
|
||||
const name = getGame().i18n.localize(`DS4.New${type.capitalize()}Name`);
|
||||
const itemData = { name, type };
|
||||
Item.create(itemData, { parent: this.actor });
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet of the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onEditItem(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.sheet?.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onDeleteItem(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.delete();
|
||||
$(li).slideUp(200, () => this.render(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded item depending on the `data-property` attribute of the
|
||||
* {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onChangeItem(event) {
|
||||
return this.onChangeEmbeddedDocument(event, "Item");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded effect of the actor corresponding to this
|
||||
* sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlEffect(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateEffect();
|
||||
case "edit":
|
||||
return this.onEditEffect(event);
|
||||
case "delete":
|
||||
return this.onDeleteEffect(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new embedded effect.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onCreateEffect() {
|
||||
DS4ActiveEffect.createDefault(this.actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet of the embedded effect corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onEditEffect(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.ActiveEffect.uuidDataAttribute];
|
||||
const effect = await fromUuid(uuid);
|
||||
enforce(
|
||||
effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
effect.sheet?.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onDeleteEffect(event) {
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.ActiveEffect.uuidDataAttribute];
|
||||
const effect = await fromUuid(uuid);
|
||||
enforce(
|
||||
effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
effect.delete();
|
||||
$(li).slideUp(200, () => this.render(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded effect depending on the `data-property` attribute of the
|
||||
* {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onChangeEffect(event) {
|
||||
return this.onChangeEmbeddedDocument(event, "ActiveEffect");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a change to a property of an embedded document of the actor belonging to this sheet. The change depends
|
||||
* on the `data-property` attribute of the {@link HTMLInputElement} that has been changed and its new value.
|
||||
*
|
||||
* @param {JQuery.ChangeEvent} event The originating click event
|
||||
* @param {"Item" | "ActiveEffect"} documentName The name of the embedded document to be changed.
|
||||
* @protected
|
||||
*/
|
||||
async onChangeEmbeddedDocument(event, documentName) {
|
||||
event.preventDefault();
|
||||
const element = $(event.currentTarget).get(0);
|
||||
if (element.disabled) return;
|
||||
|
||||
const documentElement = element.closest(embeddedDocumentListEntryProperties[documentName].selector);
|
||||
const uuid = documentElement.dataset[embeddedDocumentListEntryProperties[documentName].uuidDataAttribute];
|
||||
const property = element.dataset["property"];
|
||||
enforce(property !== undefined, TypeError("HTML element does not provide 'data-property' attribute"));
|
||||
|
||||
const newValue = this.parseValue(element);
|
||||
|
||||
const document = await fromUuid(uuid);
|
||||
|
||||
if (documentName === "Item") {
|
||||
enforce(
|
||||
document && document.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
} else {
|
||||
enforce(
|
||||
document && (document.parent === this.actor || document.parent.parent === this.actor),
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
}
|
||||
document.update({ [property]: newValue });
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the value of the given {@link HTMLInputElement} depending on the element's type
|
||||
* The value is parsed to:
|
||||
* - checkbox: `boolean`, if the attribute `data-inverted` is set to a truthy value, the parsed value is inverted
|
||||
* - text input: `string`
|
||||
* - number: `number`
|
||||
*
|
||||
* @param {HTMLInputElement} element The input element to parse the value from
|
||||
* @returns {boolean | string | number} The parsed data
|
||||
* @protected
|
||||
*/
|
||||
parseValue(element) {
|
||||
switch (element.type) {
|
||||
case "checkbox": {
|
||||
const inverted = Boolean(element.dataset["inverted"]);
|
||||
const value = element.checked;
|
||||
return inverted ? !value : value;
|
||||
}
|
||||
case "text": {
|
||||
const value = element.value;
|
||||
return value;
|
||||
}
|
||||
case "number": {
|
||||
const value = Number(element.value.trim());
|
||||
return value;
|
||||
}
|
||||
default: {
|
||||
throw new TypeError(
|
||||
"Binding of item property to this type of HTML element not supported; given: " + element,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clickable item rolls.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
async onRollItem(event) {
|
||||
event.preventDefault();
|
||||
const li = event.currentTarget.closest(embeddedDocumentListEntryProperties.Item.selector);
|
||||
const uuid = li.dataset[embeddedDocumentListEntryProperties.Item.uuidDataAttribute];
|
||||
const item = await fromUuid(uuid);
|
||||
enforce(
|
||||
item && item.parent === this.actor,
|
||||
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }),
|
||||
);
|
||||
item.roll().catch((e) => notifications.error(e, { log: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clickable check rolls.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onRollCheck(event) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.blur();
|
||||
const check = event.currentTarget.dataset["check"];
|
||||
this.actor.rollCheck(check).catch((e) => notifications.error(e, { log: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @override
|
||||
*/
|
||||
_onDragStart(event) {
|
||||
const target = event.currentTarget;
|
||||
if (!(target instanceof HTMLElement)) return super._onDragStart(event);
|
||||
|
||||
const check = target.dataset.check;
|
||||
if (!check) return super._onDragStart(event);
|
||||
|
||||
enforce(isCheck(check), getGame().i18n.format("DS4.ErrorCannotDragMissingCheck", { check }));
|
||||
|
||||
const dragData = {
|
||||
actorId: this.actor.id,
|
||||
sceneId: this.actor.isToken ? getCanvas().scene?.id : null,
|
||||
tokenId: this.actor.isToken ? this.actor.token?.id : null,
|
||||
type: "Check",
|
||||
data: check,
|
||||
};
|
||||
|
||||
event.dataTransfer?.setData("text/plain", JSON.stringify(dragData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort items according to the item list header that has been clicked.
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onSortItems(event) {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget;
|
||||
const type = target.parentElement?.dataset["type"];
|
||||
enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`);
|
||||
const dataPath = target.dataset["dataPath"];
|
||||
enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`);
|
||||
const dataPath2 = target.dataset["dataPath2"];
|
||||
/** @type {import("../../documents/item/item").DS4Item[]}*/
|
||||
const items = this.actor.items.filter((item) => item.type === type);
|
||||
items.sort((a, b) => a.sort - b.sort);
|
||||
|
||||
/**
|
||||
* @param {boolean} invert Whether or not to inverse the sort order
|
||||
* @returns {(a: import("../../documents/item/item").DS4Item, b: import("../../documents/item/item").DS4Item) => number} A function for sorting items
|
||||
*/
|
||||
const sortFunction = (invert) => (a, b) => {
|
||||
const propertyA = getProperty(a, dataPath);
|
||||
const propertyB = getProperty(b, dataPath);
|
||||
const comparison =
|
||||
typeof propertyA === "string" || typeof propertyB === "string"
|
||||
? compareAsStrings(propertyA, propertyB, invert)
|
||||
: compareAsNumbers(propertyA, propertyB, invert);
|
||||
|
||||
if (comparison === 0 && dataPath2 !== undefined) {
|
||||
const propertyA = getProperty(a, dataPath);
|
||||
const propertyB = getProperty(b, dataPath);
|
||||
return typeof propertyA === "string" || typeof propertyB === "string"
|
||||
? compareAsStrings(propertyA, propertyB, invert)
|
||||
: compareAsNumbers(propertyA, propertyB, invert);
|
||||
}
|
||||
|
||||
return comparison;
|
||||
};
|
||||
|
||||
const sortedItems = [...items].sort(sortFunction(false));
|
||||
const wasSortedAlready = !sortedItems.find((item, index) => item !== items[index]);
|
||||
|
||||
if (wasSortedAlready) {
|
||||
sortedItems.sort(sortFunction(true));
|
||||
}
|
||||
|
||||
const updates = sortedItems.map((item, i) => ({
|
||||
_id: item.id,
|
||||
sort: (i + 1) * CONST.SORT_INTEGER_DENSITY,
|
||||
}));
|
||||
|
||||
this.actor.updateEmbeddedDocuments("Item", updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @param {object} data
|
||||
* @override
|
||||
*/
|
||||
async _onDropItem(event, data) {
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
if (item && !this.actor.canOwnItemType(item.type)) {
|
||||
notifications.warn(
|
||||
getGame().i18n.format("DS4.WarningActorCannotOwnItem", {
|
||||
actorName: this.actor.name,
|
||||
actorType: this.actor.type,
|
||||
itemName: item.name,
|
||||
itemType: item.type,
|
||||
}),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return super._onDropItem(event, data);
|
||||
const updates = sortedItems.map((item, i) => ({
|
||||
_id: item.id,
|
||||
sort: (i + 1) * CONST.SORT_INTEGER_DENSITY,
|
||||
}));
|
||||
|
||||
this.actor.updateEmbeddedDocuments("Item", updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @param {object} data
|
||||
* @override
|
||||
*/
|
||||
async _onDropItem(event, data) {
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
if (item && !this.actor.canOwnItemType(item.type)) {
|
||||
notifications.warn(
|
||||
getGame().i18n.format("DS4.WarningActorCannotOwnItem", {
|
||||
actorName: this.actor.name,
|
||||
actorType: this.actor.type,
|
||||
itemName: item.name,
|
||||
itemType: item.type,
|
||||
}),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return super._onDropItem(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This object contains information about specific properties embedded document list entries for each different type.
|
||||
*/
|
||||
const embeddedDocumentListEntryProperties = Object.freeze({
|
||||
ActiveEffect: {
|
||||
selector: ".effect",
|
||||
uuidDataAttribute: "effectUuid",
|
||||
},
|
||||
Item: {
|
||||
selector: ".item",
|
||||
uuidDataAttribute: "itemUuid",
|
||||
},
|
||||
ActiveEffect: {
|
||||
selector: ".effect",
|
||||
uuidDataAttribute: "effectUuid",
|
||||
},
|
||||
Item: {
|
||||
selector: ".item",
|
||||
uuidDataAttribute: "itemUuid",
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -508,7 +502,7 @@ const embeddedDocumentListEntryProperties = Object.freeze({
|
|||
* @return {number} A number that indicates the result of the comparison
|
||||
*/
|
||||
const compareAsStrings = (a, b, invert) => {
|
||||
return invert ? b.toString().localeCompare(a.toString()) : a.toString().localeCompare(b.toString());
|
||||
return invert ? b.toString().localeCompare(a.toString()) : a.toString().localeCompare(b.toString());
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -519,5 +513,5 @@ const compareAsStrings = (a, b, invert) => {
|
|||
* @return {number} A number that indicates the result of the comparison
|
||||
*/
|
||||
const compareAsNumbers = (a, b, invert) => {
|
||||
return invert ? b - a : a - b;
|
||||
return invert ? b - a : a - b;
|
||||
};
|
||||
|
|
|
@ -8,18 +8,18 @@ import { DS4ActorSheet } from "./base-sheet";
|
|||
* The Sheet class for DS4 Character Actors
|
||||
*/
|
||||
export class DS4CharacterActorSheet extends DS4ActorSheet {
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"],
|
||||
});
|
||||
}
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"],
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const context = await super.getData(options);
|
||||
context.data.system.profile.biography = await TextEditor.enrichHTML(context.data.system.profile.biography, {
|
||||
async: true,
|
||||
});
|
||||
return context;
|
||||
}
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const context = await super.getData(options);
|
||||
context.data.system.profile.biography = await TextEditor.enrichHTML(context.data.system.profile.biography, {
|
||||
async: true,
|
||||
});
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,19 +8,18 @@ import { DS4ActorSheet } from "./base-sheet";
|
|||
* The Sheet class for DS4 Creature Actors
|
||||
*/
|
||||
export class DS4CreatureActorSheet extends DS4ActorSheet {
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet"],
|
||||
});
|
||||
}
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet"],
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const context = await super.getData(options);
|
||||
context.data.system.baseInfo.description = await TextEditor.enrichHTML(
|
||||
context.data.system.baseInfo.description,
|
||||
{ async: true },
|
||||
);
|
||||
return context;
|
||||
}
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const context = await super.getData(options);
|
||||
context.data.system.baseInfo.description = await TextEditor.enrichHTML(context.data.system.baseInfo.description, {
|
||||
async: true,
|
||||
});
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
* A simple extension to the {@link Dialog} class that allows attaching additional listeners.
|
||||
*/
|
||||
export class DialogWithListeners extends Dialog {
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
if (this.options.activateAdditionalListeners !== undefined) {
|
||||
this.options.activateAdditionalListeners(html, this);
|
||||
}
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
if (this.options.activateAdditionalListeners !== undefined) {
|
||||
this.options.activateAdditionalListeners(html, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,138 +13,138 @@ import { disableOverriddenFields } from "./sheet-helpers";
|
|||
* The Sheet class for DS4 Items
|
||||
*/
|
||||
export class DS4ItemSheet extends ItemSheet {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-item-sheet"],
|
||||
height: 400,
|
||||
scrollY: [".ds4-sheet-body"],
|
||||
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "description" }],
|
||||
width: 540,
|
||||
});
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["sheet", "ds4-item-sheet"],
|
||||
height: 400,
|
||||
scrollY: [".ds4-sheet-body"],
|
||||
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "description" }],
|
||||
width: 540,
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
get template() {
|
||||
const basePath = "systems/ds4/templates/sheets/item";
|
||||
return `${basePath}/${this.item.type}-sheet.hbs`;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const superContext = await super.getData(options);
|
||||
superContext.data.system.description = await TextEditor.enrichHTML(superContext.data.system.description, {
|
||||
async: true,
|
||||
});
|
||||
const context = {
|
||||
...superContext,
|
||||
config: DS4,
|
||||
isOwned: this.item.isOwned,
|
||||
actor: this.item.actor,
|
||||
};
|
||||
return context;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_getSubmitData(updateData = {}) {
|
||||
const data = super._getSubmitData(updateData);
|
||||
// Prevent submitting overridden values
|
||||
const overrides = foundry.utils.flattenObject(this.item.overrides);
|
||||
for (const k of Object.keys(overrides)) {
|
||||
delete data[k];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
setPosition(options = {}) {
|
||||
const position = super.setPosition(options);
|
||||
if (position) {
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
const bodyHeight = position.height - 192;
|
||||
sheetBody.css("height", bodyHeight);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
get template() {
|
||||
const basePath = "systems/ds4/templates/sheets/item";
|
||||
return `${basePath}/${this.item.type}-sheet.hbs`;
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {JQuery} html
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!this.options.editable) return;
|
||||
|
||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||
|
||||
disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded effect of the item corresponding to this
|
||||
* sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlEffect(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateEffect();
|
||||
case "edit":
|
||||
return this.onEditEffect(event);
|
||||
case "delete":
|
||||
return this.onDeleteEffect(event);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const superContext = await super.getData(options);
|
||||
superContext.data.system.description = await TextEditor.enrichHTML(superContext.data.system.description, {
|
||||
async: true,
|
||||
});
|
||||
const context = {
|
||||
...superContext,
|
||||
config: DS4,
|
||||
isOwned: this.item.isOwned,
|
||||
actor: this.item.actor,
|
||||
};
|
||||
return context;
|
||||
}
|
||||
/**
|
||||
* Creates a new embedded effect.
|
||||
* @protected
|
||||
*/
|
||||
onCreateEffect() {
|
||||
DS4ActiveEffect.createDefault(this.item);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_getSubmitData(updateData = {}) {
|
||||
const data = super._getSubmitData(updateData);
|
||||
// Prevent submitting overridden values
|
||||
const overrides = foundry.utils.flattenObject(this.item.overrides);
|
||||
for (const k of Object.keys(overrides)) {
|
||||
delete data[k];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Opens the sheet of the embedded effect corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @porotected
|
||||
*/
|
||||
onEditEffect(event) {
|
||||
const id = $(event.currentTarget)
|
||||
.parents(embeddedDocumentListEntryProperties.ActiveEffect.selector)
|
||||
.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
|
||||
const effect = this.item.effects.get(id);
|
||||
enforce(effect, getGame().i18n.format("DS4.ErrorItemDoesNotHaveEffect", { id, item: this.item.name }));
|
||||
effect.sheet?.render(true);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
setPosition(options = {}) {
|
||||
const position = super.setPosition(options);
|
||||
if (position) {
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
const bodyHeight = position.height - 192;
|
||||
sheetBody.css("height", bodyHeight);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {JQuery} html
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!this.options.editable) return;
|
||||
|
||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||
|
||||
disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on an element of this sheet to control an embedded effect of the item corresponding to this
|
||||
* sheet.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onControlEffect(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
switch (a.dataset["action"]) {
|
||||
case "create":
|
||||
return this.onCreateEffect();
|
||||
case "edit":
|
||||
return this.onEditEffect(event);
|
||||
case "delete":
|
||||
return this.onDeleteEffect(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new embedded effect.
|
||||
* @protected
|
||||
*/
|
||||
onCreateEffect() {
|
||||
DS4ActiveEffect.createDefault(this.item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sheet of the embedded effect corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @porotected
|
||||
*/
|
||||
onEditEffect(event) {
|
||||
const id = $(event.currentTarget)
|
||||
.parents(embeddedDocumentListEntryProperties.ActiveEffect.selector)
|
||||
.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
|
||||
const effect = this.item.effects.get(id);
|
||||
enforce(effect, getGame().i18n.format("DS4.ErrorItemDoesNotHaveEffect", { id, item: this.item.name }));
|
||||
effect.sheet?.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onDeleteEffect(event) {
|
||||
const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
|
||||
this.item.deleteEmbeddedDocuments("ActiveEffect", [id]);
|
||||
li.slideUp(200, () => this.render(false));
|
||||
}
|
||||
/**
|
||||
* Deletes the embedded item corresponding to the clicked element.
|
||||
*
|
||||
* @param {JQuery.ClickEvent} event The originating click event
|
||||
* @protected
|
||||
*/
|
||||
onDeleteEffect(event) {
|
||||
const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector);
|
||||
const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
|
||||
this.item.deleteEmbeddedDocuments("ActiveEffect", [id]);
|
||||
li.slideUp(200, () => this.render(false));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This object contains information about specific properties embedded document list entries for each different type.
|
||||
*/
|
||||
const embeddedDocumentListEntryProperties = Object.freeze({
|
||||
ActiveEffect: {
|
||||
selector: ".effect",
|
||||
idDataAttribute: "effectId",
|
||||
},
|
||||
ActiveEffect: {
|
||||
selector: ".effect",
|
||||
idDataAttribute: "effectId",
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,18 +11,18 @@ import { getGame } from "../utils/utils";
|
|||
* @param {(key: string) => string} selector A function that generates a selector, based on a property key
|
||||
*/
|
||||
export function disableOverriddenFields(form, overrides, selector) {
|
||||
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"];
|
||||
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`;
|
||||
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"];
|
||||
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`;
|
||||
|
||||
for (const key of Object.keys(foundry.utils.flattenObject(overrides))) {
|
||||
const elements = form?.querySelectorAll(selector(key));
|
||||
elements?.forEach((element) => {
|
||||
if (inputs.includes(element.tagName) && !element.hasAttribute("disabled")) {
|
||||
element.setAttribute("disabled", "");
|
||||
const title = element.getAttribute("title");
|
||||
const newTitle = title === null ? titleAddition : `${title} ${titleAddition}`;
|
||||
element.setAttribute("title", newTitle);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (const key of Object.keys(foundry.utils.flattenObject(overrides))) {
|
||||
const elements = form?.querySelectorAll(selector(key));
|
||||
elements?.forEach((element) => {
|
||||
if (inputs.includes(element.tagName) && !element.hasAttribute("disabled")) {
|
||||
element.setAttribute("disabled", "");
|
||||
const title = element.getAttribute("title");
|
||||
const newTitle = title === null ? titleAddition : `${title} ${titleAddition}`;
|
||||
element.setAttribute("title", newTitle);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue