import { filePath } from "../../consts.mjs"; import { GenericAppMixin } from "../mixins/GenericApp.mjs"; import { LaidOutAppMixin } from "../mixins/LaidOutAppMixin.mjs"; import { localizer } from "../../utils/Localizer.mjs"; import { editItemFromElement, deleteItemFromElement } from "../utils.mjs"; const { HandlebarsApplicationMixin } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets; const { ContextMenu, TextEditor } = foundry.applications.ux; export class BookGeistSheet extends LaidOutAppMixin( GenericAppMixin( HandlebarsApplicationMixin( ActorSheetV2, ))) { // #region Options static DEFAULT_OPTIONS = { classes: [ `ripcrypt--actor`, `BookGeistSheet`, ], position: { width: `auto`, height: `auto`, }, window: { resizable: true, }, form: { submitOnChange: true, closeOnSubmite: false, }, }; static PARTS = { layout: { root: true, template: filePath(`templates/Apps/BookGeistSheet/layout.hbs`), }, image: { template: filePath(`templates/Apps/BookGeistSheet/image.hbs`) }, header: { template: filePath(`templates/Apps/BookGeistSheet/header.hbs`) }, stats: { template: filePath(`templates/Apps/BookGeistSheet/stats.hbs`) }, items: { template: filePath(`templates/Apps/BookGeistSheet/items.hbs`) }, }; // #endregion Options // #region Lifecycle async _onRender() { await super._onRender(); new ContextMenu.implementation( this.element, `[data-ctx-menu="item"]`, [ { name: localizer(`RipCrypt.common.edit`), condition: (el) => { const itemId = el.dataset.itemId; return itemId !== ``; }, callback: editItemFromElement, }, { name: localizer(`RipCrypt.common.delete`), condition: (el) => { const itemId = el.dataset.itemId; return itemId !== ``; }, callback: deleteItemFromElement, }, ], { jQuery: false, fixed: true }, ); }; // #endregion Lifecycle // #region Data Prep async _preparePartContext(partID, ctx) { switch (partID) { case `layout`: await this._prepareLayoutContext(ctx); break; case `image`: await this._prepareImageContext(ctx); break; case `header`: await this._prepareHeaderContext(ctx); break; case `stats`: await this._prepareStatsContext(ctx); break; case `items`: await this._prepareItemsContext(ctx); break; }; return ctx; }; async _prepareLayoutContext(ctx) { ctx.imageVisible = true; }; async _prepareImageContext(ctx) { ctx.url = this.actor.img; }; async _prepareHeaderContext(ctx) { ctx.name = this.actor.name; ctx.rank = this.actor.system.level.rank; ctx.ranks = Object.values(gameTerms.Rank) .map((value, index) => ({ value, label: index })); ctx.description = await TextEditor.implementation.enrichHTML(this.actor.system.description); }; async _prepareStatsContext(ctx) { const system = this.actor.system; const fate = system.fate; if (fate) { ctx.path = { full: localizer(`RipCrypt.common.ordinals.${fate}.full`), abbv: localizer(`RipCrypt.common.ordinals.${fate}.abbv`), }; } else { ctx.path = { full: null, abbv: localizer(`RipCrypt.common.empty`), }; }; Object.assign(ctx, system.ability); ctx.guts = system.guts; ctx.speed = system.speed; }; async _prepareItemsContext(ctx) { ctx.defense = { locations: `None`, protection: 0, shield: false, }; ctx.traits = []; // Array<{name: string}> for (const item of this.actor.items) { switch (item.type) { case `weapon`: { if (!item.system.equipped) { continue }; ctx.attacks ??= []; ctx.attacks.push(this._prepareWeapon(item)); break; }; case `craft`: { ctx.crafts ??= []; ctx.crafts.push(this._prepareCraft(item)); break; }; case `trait`: { ctx.traits.push(this._prepareTrait(item)); break; }; }; }; }; _prepareWeapon(weapon) { const hasShortRange = weapon.system.range.short != null; const hasLongRange = weapon.system.range.long != null; const isRanged = hasShortRange || hasLongRange; return { uuid: weapon.uuid, name: weapon.name, damage: weapon.system.damage, isRanged, range: weapon.system.range, }; }; _prepareCraft(craft) { return { uuid: craft.uuid, name: craft.name, }; }; _prepareTrait(trait) { return { uuid: trait.uuid, name: trait.name, description: trait.system.description, }; }; // #endregion Data Prep // #region Actions // #endregion Actions };