diff --git a/.editorconfig b/.editorconfig index ba4931db..12b53f83 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,6 @@ root = true end_of_line = lf insert_final_newline = true indent_style = space -indent_size = 4 +indent_size = 2 charset = utf-8 trim_trailing_whitespace = true diff --git a/lang/de.json b/lang/de.json index 3f7208c1..c5176813 100644 --- a/lang/de.json +++ b/lang/de.json @@ -184,7 +184,9 @@ "DS4.EffectFactor": "Faktor (wie oft der Effekt angewendet wird)", "DS4.EffectFactorAbbr": "F", "DS4.ActorName": "Name", + "DS4.ActorSheet": "Aktorbogen", "DS4.ActorImageAltText": "Bild des Aktors", + "DS4.ItemSheet": "Itembogen", "DS4.ActorTypeCharacter": "Charakter", "DS4.ActorTypeCreature": "Kreatur", "DS4.Attribute": "Attribut", diff --git a/lang/en.json b/lang/en.json index e58b6caf..8b032343 100644 --- a/lang/en.json +++ b/lang/en.json @@ -184,7 +184,9 @@ "DS4.EffectFactor": "Factor (the number of times the effect is being applied)", "DS4.EffectFactorAbbr": "F", "DS4.ActorName": "Name", + "DS4.ActorSheet": "Actor Sheet", "DS4.ActorImageAltText": "Image of the Actor", + "DS4.ItemSheet": "Item Sheet", "DS4.ActorTypeCharacter": "Character", "DS4.ActorTypeCreature": "Creature", "DS4.Attribute": "Attribute", diff --git a/scss/components/shared/_ruler.scss b/scss/components/shared/_ruler.scss index 3fdb0065..ebbb188d 100644 --- a/scss/components/shared/_ruler.scss +++ b/scss/components/shared/_ruler.scss @@ -53,7 +53,8 @@ /* Icon animations */ @keyframes pulse { - 0%, 100% { + 0%, + 100% { opacity: 1; } 50% { diff --git a/scss/utils/_variables.scss b/scss/utils/_variables.scss index f01f73be..9909c4ed 100644 --- a/scss/utils/_variables.scss +++ b/scss/utils/_variables.scss @@ -6,7 +6,6 @@ @use "./colors"; - $padding-sm: 5px; $padding-md: 10px; $padding-lg: 20px; diff --git a/src/apps/actor/base-sheet.js b/src/apps/actor/base-sheet.js index 55e2163a..285a023a 100644 --- a/src/apps/actor/base-sheet.js +++ b/src/apps/actor/base-sheet.js @@ -6,12 +6,12 @@ // // SPDX-License-Identifier: MIT - - /** * The base sheet class for DS4 Actor Sheets */ -export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { +export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicationMixin( + foundry.applications.sheets.ActorSheetV2, +) { static DEFAULT_OPTIONS = { classes: ["sheet", "ds4-actor-sheet", "themed"], tag: "form", @@ -41,14 +41,7 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio changeEffect: DS4ActorSheet.prototype._onChangeEffect, sortItems: DS4ActorSheet.prototype._onSortItems, changeTab: DS4ActorSheet.prototype._onChangeTab, - edititem: DS4ActorSheet.prototype._onEditItem, - deleteitem: DS4ActorSheet.prototype._onDeleteItem, - createitem: DS4ActorSheet.prototype._onCreateItem, - editeffect: DS4ActorSheet.prototype._onEditEffect, - deleteeffect: DS4ActorSheet.prototype._onDeleteEffect, - createeffect: DS4ActorSheet.prototype._onCreateEffect, - editImage: DS4ActorSheet._onEditImage, - + editImage: DS4ActorSheet.prototype._onEditImage, }, }; @@ -65,14 +58,14 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio /** @override */ get template() { - const templatePath = !game.user?.isGM && this.document.limited - ? "systems/ds4/templates/sheets/actor/limited-sheet.hbs" - : `systems/ds4/templates/sheets/actor/${this.document.type}-sheet.hbs`; + const templatePath = + !game.user?.isGM && this.document.limited + ? "systems/ds4/templates/sheets/actor/limited-sheet.hbs" + : `systems/ds4/templates/sheets/actor/${this.document.type}-sheet.hbs`; return templatePath; } - /** @override */ async _renderHTML(context) { return await foundry.applications.handlebars.renderTemplate(this.template, context); @@ -83,8 +76,6 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio content.innerHTML = result; } - - /** @override */ async _prepareContext(options) { const context = await super._prepareContext(options); @@ -94,8 +85,6 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio throw new Error("Document not available for sheet rendering"); } - - // Add document data context.data = this.document; context.system = this.document.system; @@ -111,8 +100,6 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio showSlayerPoints: game.settings.get("ds4", "showSlayerPoints") || false, }; - - // Add items organized by type context.itemsByType = {}; if (this.document.items && this.document.items.size > 0) { @@ -352,7 +339,6 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio } } - /** * Handle changing an item property * @param {Event} event - The triggering event @@ -484,8 +470,6 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio } } - - /** * Handle sorting items * @param {Event} event - The triggering event @@ -497,7 +481,7 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio if (!dataPath || !itemType) return; - const items = this.document.items.filter(item => item.type === itemType); + const items = this.document.items.filter((item) => item.type === itemType); const sortedItems = this.sortItems(items, dataPath); const updates = sortedItems.map((item, index) => ({ @@ -549,13 +533,13 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio if (!nav || !sheet) return; // Update navigation active state - nav.querySelectorAll(".ds4-sheet-tab-nav__item").forEach(item => { + nav.querySelectorAll(".ds4-sheet-tab-nav__item").forEach((item) => { item.classList.remove("active"); }); target.classList.add("active"); // Update tab content visibility - sheet.querySelectorAll(".ds4-sheet-tab").forEach(tabContent => { + sheet.querySelectorAll(".ds4-sheet-tab").forEach((tabContent) => { tabContent.classList.remove("active"); }); @@ -589,8 +573,8 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio const tabContents = sheet.querySelectorAll(".ds4-sheet-tab"); // Remove active class from all items first - navItems.forEach(item => item.classList.remove("active")); - tabContents.forEach(content => content.classList.remove("active")); + navItems.forEach((item) => item.classList.remove("active")); + tabContents.forEach((content) => content.classList.remove("active")); // Find the currently active tab or default to first let targetTab = this.activeTab; @@ -626,7 +610,7 @@ export class DS4ActorSheet extends foundry.applications.api.HandlebarsApplicatio const fp = new foundry.applications.apps.FilePicker({ type: "image", current: current, - callback: (path) => this.document.update({ [field]: path }) + callback: (path) => this.document.update({ [field]: path }), }); return fp.browse(); diff --git a/src/apps/actor/character-sheet.js b/src/apps/actor/character-sheet.js index 1e25b487..715b345d 100644 --- a/src/apps/actor/character-sheet.js +++ b/src/apps/actor/character-sheet.js @@ -25,7 +25,7 @@ export class DS4CharacterActorSheet extends DS4ActorSheet { { async: true, relativeTo: this.document, - } + }, ); } diff --git a/src/apps/actor/creature-sheet.js b/src/apps/actor/creature-sheet.js index 9bd8cd1f..3c68de8e 100644 --- a/src/apps/actor/creature-sheet.js +++ b/src/apps/actor/creature-sheet.js @@ -25,7 +25,7 @@ export class DS4CreatureActorSheet extends DS4ActorSheet { { async: true, relativeTo: this.document, - } + }, ); } diff --git a/src/apps/dialog-with-listeners.js b/src/apps/dialog-with-listeners.js index 5eba545d..b495b867 100644 --- a/src/apps/dialog-with-listeners.js +++ b/src/apps/dialog-with-listeners.js @@ -25,7 +25,7 @@ export class DialogWithListeners extends foundry.applications.api.DialogV2 { await super._onRender(context, options); // Attach additional listeners if provided - if (this.activateAdditionalListeners && typeof this.activateAdditionalListeners === 'function') { + if (this.activateAdditionalListeners && typeof this.activateAdditionalListeners === "function") { this.activateAdditionalListeners(this.element, this); } } diff --git a/src/apps/item-sheet.js b/src/apps/item-sheet.js index 004b3ab9..8ff85ee7 100644 --- a/src/apps/item-sheet.js +++ b/src/apps/item-sheet.js @@ -8,7 +8,9 @@ /** * The Sheet class for DS4 Items */ -export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) { +export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplicationMixin( + foundry.applications.sheets.ItemSheetV2, +) { static DEFAULT_OPTIONS = { classes: ["sheet", "ds4-item-sheet", "themed"], tag: "form", @@ -26,18 +28,13 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication actions: { controlEffect: DS4ItemSheet.prototype._onControlEffect, createEffect: DS4ItemSheet.prototype._onCreateEffect, - createeffect: DS4ItemSheet.prototype._onCreateEffect, editEffect: DS4ItemSheet.prototype._onEditEffect, - editeffect: DS4ItemSheet.prototype._onEditEffect, deleteEffect: DS4ItemSheet.prototype._onDeleteEffect, - deleteeffect: DS4ItemSheet.prototype._onDeleteEffect, changeTab: DS4ItemSheet.prototype._onChangeTab, editImage: DS4ItemSheet.prototype._onEditImage, }, }; - - static TABS = {}; constructor(options = {}) { @@ -105,14 +102,13 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication this.item.system.description, { secrets: this.item.isOwner, - relativeTo: this.item - } + relativeTo: this.item, + }, ); } else { context.enrichedDescription = ""; } - return context; } @@ -183,10 +179,12 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication const effect = this.item.effects.get(effectId); if (!effect) { - throw new Error(game.i18n.format("DS4.ErrorItemDoesNotHaveEffect", { - id: effectId, - item: this.item.name - })); + throw new Error( + game.i18n.format("DS4.ErrorItemDoesNotHaveEffect", { + id: effectId, + item: this.item.name, + }), + ); } await effect.sheet.render(true); @@ -235,13 +233,13 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication if (!nav || !sheet) return; // Update navigation active state - nav.querySelectorAll(".ds4-sheet-tab-nav__item").forEach(item => { + nav.querySelectorAll(".ds4-sheet-tab-nav__item").forEach((item) => { item.classList.remove("active"); }); target.classList.add("active"); // Update tab content visibility - sheet.querySelectorAll(".ds4-sheet-tab").forEach(tabContent => { + sheet.querySelectorAll(".ds4-sheet-tab").forEach((tabContent) => { tabContent.classList.remove("active"); }); @@ -263,7 +261,7 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication const fp = new foundry.applications.apps.FilePicker({ type: "image", current: current, - callback: (path) => this.item.update({ [field]: path }) + callback: (path) => this.item.update({ [field]: path }), }); return fp.browse(); } @@ -276,8 +274,6 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication this._initializeTabs(); } - - /** @override */ async _onClose(options) { await super._onClose(options); @@ -299,8 +295,8 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication const tabContents = sheet.querySelectorAll(".ds4-sheet-tab"); // Remove active class from all items first - navItems.forEach(item => item.classList.remove("active")); - tabContents.forEach(content => content.classList.remove("active")); + navItems.forEach((item) => item.classList.remove("active")); + tabContents.forEach((content) => content.classList.remove("active")); // Find the currently active tab or default to first let targetTab = this.activeTab; @@ -312,8 +308,6 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication targetTab = targetNavItem?.dataset.tab; } - - // Set target tab navigation as active if (targetNavItem && targetTab) { targetNavItem.classList.add("active"); diff --git a/src/apps/ruler/token-ruler.js b/src/apps/ruler/token-ruler.js index 3d48362a..467ffa0d 100644 --- a/src/apps/ruler/token-ruler.js +++ b/src/apps/ruler/token-ruler.js @@ -7,41 +7,38 @@ * Based on actor movement combat value */ export class DS4TokenRuler extends foundry.canvas.placeables.tokens.TokenRuler { + static WAYPOINT_LABEL_TEMPLATE = "systems/ds4/templates/partials/waypoint-label.hbs"; - static WAYPOINT_LABEL_TEMPLATE = "systems/ds4/templates/partials/waypoint-label.hbs"; + /** + * Enhance waypoint label context with movement range information + * @param {object} waypoint - The waypoint data + * @param {object} state - The current ruler state + * @returns {object} Enhanced context with range class + */ + _getWaypointLabelContext(waypoint, state) { + const context = super._getWaypointLabelContext(waypoint, state); + // Only apply movement coloring for distance measurements in meters + if (context?.cost?.units === "m" || context?.distance?.units === "m") { + const movement = this.token?.actor?.system?.combatValues?.movement?.total; + if (movement) { + const total = Number(context.cost?.total || context.distance?.total || 0); - /** - * Enhance waypoint label context with movement range information - * @param {object} waypoint - The waypoint data - * @param {object} state - The current ruler state - * @returns {object} Enhanced context with range class - */ - _getWaypointLabelContext(waypoint, state) { - const context = super._getWaypointLabelContext(waypoint, state); - - // Only apply movement coloring for distance measurements in meters - if (context?.cost?.units === "m" || context?.distance?.units === "m") { - const movement = this.token?.actor?.system?.combatValues?.movement?.total; - - if (movement) { - const total = Number(context.cost?.total || context.distance?.total || 0); - - // DS4 movement rules: - // - Normal movement: up to movement value - // - Dash: up to 2x movement value (requires action) - // - Beyond 2x: impossible in single turn - if (total > 2 * movement) { - context.rangeClass = "out-of-range"; - } else if (total <= movement) { - context.rangeClass = "move-range"; - } else { - context.rangeClass = "dash-range"; - } - } + // DS4 movement rules: + // - Normal movement: up to movement value + // - Dash: up to 2x movement value (requires action) + // - Beyond 2x: impossible in single turn + if (total > 2 * movement) { + context.rangeClass = "out-of-range"; + } else if (total <= movement) { + context.rangeClass = "move-range"; + } else { + context.rangeClass = "dash-range"; } - - return context; + } } + + return context; + } } diff --git a/src/dice/check-factory.js b/src/dice/check-factory.js index d5a64c10..250dcd83 100644 --- a/src/dice/check-factory.js +++ b/src/dice/check-factory.js @@ -212,7 +212,10 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp if (checkModifierSelect) { checkModifierSelect.addEventListener("change", (event) => { - if (event.currentTarget.value === "custom" && checkModifierCustomFormGroup?.classList.contains("ds4-hidden")) { + if ( + event.currentTarget.value === "custom" && + checkModifierCustomFormGroup?.classList.contains("ds4-hidden") + ) { checkModifierCustomFormGroup.classList.remove("ds4-hidden"); app.setPosition({ height: "auto" }); } else if (checkModifierCustomFormGroup && !checkModifierCustomFormGroup.classList.contains("ds4-hidden")) { diff --git a/src/documents/actor/actor.js b/src/documents/actor/actor.js index a7d0b2c3..0725a444 100644 --- a/src/documents/actor/actor.js +++ b/src/documents/actor/actor.js @@ -466,30 +466,33 @@ export class DS4Actor extends Actor { const traitIdentifier = "attribute-trait-selection-trait"; return foundry.applications.api.DialogV2.prompt({ window: { title: getGame().i18n.localize("DS4.DialogAttributeTraitSelection") }, - content: await foundry.applications.handlebars.renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { - selects: [ - { - label: getGame().i18n.localize("DS4.Attribute"), - identifier: attributeIdentifier, - options: Object.fromEntries( - Object.entries(DS4.i18n.attributes).map(([attribute, translation]) => [ - attribute, - `${translation} (${this.system.attributes[attribute].total})`, - ]), - ), - }, - { - label: getGame().i18n.localize("DS4.Trait"), - identifier: traitIdentifier, - options: Object.fromEntries( - Object.entries(DS4.i18n.traits).map(([trait, translation]) => [ - trait, - `${translation} (${this.system.traits[trait].total})`, - ]), - ), - }, - ], - }), + content: await foundry.applications.handlebars.renderTemplate( + "systems/ds4/templates/dialogs/simple-select-form.hbs", + { + selects: [ + { + label: getGame().i18n.localize("DS4.Attribute"), + identifier: attributeIdentifier, + options: Object.fromEntries( + Object.entries(DS4.i18n.attributes).map(([attribute, translation]) => [ + attribute, + `${translation} (${this.system.attributes[attribute].total})`, + ]), + ), + }, + { + label: getGame().i18n.localize("DS4.Trait"), + identifier: traitIdentifier, + options: Object.fromEntries( + Object.entries(DS4.i18n.traits).map(([trait, translation]) => [ + trait, + `${translation} (${this.system.traits[trait].total})`, + ]), + ), + }, + ], + }, + ), ok: { label: getGame().i18n.localize("DS4.GenericOkButton"), callback: (_event, button) => { diff --git a/src/documents/item/weapon/weapon.js b/src/documents/item/weapon/weapon.js index b125966f..3a96ddac 100644 --- a/src/documents/item/weapon/weapon.js +++ b/src/documents/item/weapon/weapon.js @@ -86,18 +86,21 @@ export class DS4Weapon extends DS4Item { const identifier = `attack-type-selection-${foundry.utils.randomID()}`; return foundry.applications.api.DialogV2.prompt({ window: { title: getGame().i18n.localize("DS4.DialogAttackTypeSelection") }, - content: await foundry.applications.handlebars.renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { - selects: [ - { - label: getGame().i18n.localize("DS4.AttackType"), - identifier, - options: { melee, ranged }, - }, - ], - }), + content: await foundry.applications.handlebars.renderTemplate( + "systems/ds4/templates/dialogs/simple-select-form.hbs", + { + selects: [ + { + label: getGame().i18n.localize("DS4.AttackType"), + identifier, + options: { melee, ranged }, + }, + ], + }, + ), ok: { label: getGame().i18n.localize("DS4.GenericOkButton"), - callback: (_event, button, _dialog) => { + callback: (_event, button) => { const selectedAttackType = button.form.elements[identifier].value; if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") { throw new Error( diff --git a/src/handlebars/handlebars-helpers.ts b/src/handlebars/handlebars-helpers.ts index 015064a4..5f9dcbc9 100644 --- a/src/handlebars/handlebars-helpers.ts +++ b/src/handlebars/handlebars-helpers.ts @@ -14,6 +14,11 @@ const helpers = { isEmpty: (input: Array | null | undefined): boolean => (input?.length ?? 0) === 0, + capitalize: (str: string): string => { + if (typeof str !== "string") return ""; + return str.charAt(0).toUpperCase() + str.slice(1); + }, + toRomanNumerals, }; diff --git a/src/hooks/hooks.ts b/src/hooks/hooks.ts index 684a04db..b69fbb1f 100644 --- a/src/hooks/hooks.ts +++ b/src/hooks/hooks.ts @@ -9,8 +9,6 @@ import { registerForReadyHook } from "./ready"; import { registerForRenderHooks } from "./render"; import { registerForSetupHook } from "./setup"; - - export function registerForHooks(): void { registerForHotbarDropHook(); registerForPreCreateItemHook(); diff --git a/src/hooks/render.js b/src/hooks/render.js index 1c8c580a..ad5224c3 100644 --- a/src/hooks/render.js +++ b/src/hooks/render.js @@ -21,12 +21,12 @@ export function registerForRenderHooks() { */ function selectTargetInputOnFocus(app, element) { // V13: element is always a plain DOM element - if (!element || typeof element.querySelectorAll !== 'function') { + if (!element || typeof element.querySelectorAll !== "function") { return; } const inputs = element.querySelectorAll("input"); - inputs.forEach(input => { + inputs.forEach((input) => { input.addEventListener("focus", (ev) => { ev.currentTarget.select(); }); diff --git a/src/utils/utils.js b/src/utils/utils.js index 1849eb6e..b7979b64 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -30,7 +30,10 @@ export function enforce(value, message) { * @returns {Canvas} */ export function getCanvas() { - enforce(canvas instanceof foundry.canvas.Canvas && canvas.ready, getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized")); + enforce( + canvas instanceof foundry.canvas.Canvas && canvas.ready, + getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized"), + ); return canvas; } diff --git a/templates/sheets/actor/creature-sheet.hbs b/templates/sheets/actor/creature-sheet.hbs index afa35ba0..cfa20a7e 100644 --- a/templates/sheets/actor/creature-sheet.hbs +++ b/templates/sheets/actor/creature-sheet.hbs @@ -12,7 +12,6 @@ SPDX-License-Identifier: MIT {{> systems/ds4/templates/sheets/actor/components/creature-properties.hbs}} {{/systems/ds4/templates/sheets/actor/components/actor-header.hbs}} - {{!-- Sheet Tab Navigation --}}