diff --git a/module/Apps/ActorSheets/CombinedHeroSheet.mjs b/module/Apps/ActorSheets/CombinedHeroSheet.mjs index 974269b..5bad10a 100644 --- a/module/Apps/ActorSheets/CombinedHeroSheet.mjs +++ b/module/Apps/ActorSheets/CombinedHeroSheet.mjs @@ -3,6 +3,7 @@ import { GenericAppMixin } from "../GenericApp.mjs"; import { HeroSkillsCardV1 } from "./HeroSkillsCardV1.mjs"; import { HeroSummaryCardV1 } from "./HeroSummaryCardV1.mjs"; import { Logger } from "../../utils/Logger.mjs"; +import { HeroCraftCardV1 } from "./HeroCraftCardV1.mjs"; const { HandlebarsApplicationMixin } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets; @@ -38,6 +39,9 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi skills: { template: filePath(`templates/Apps/HeroSkillsCardV1/content.hbs`), }, + craft: { + template: filePath(`templates/Apps/CombinedHeroSheet/crafts.hbs`), + }, }; // #endregion @@ -64,25 +68,47 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi isEditable: this.isEditable, }, ); + + const craftsElement = this.element.querySelector(`.crafts-summary`); + HeroCraftCardV1._onRender.bind(this)( + context, + { + ...options, + element: craftsElement, + isEditable: this.isEditable, + }, + ); }; async _preparePartContext(partId, ctx, opts) { ctx = await super._preparePartContext(partId, ctx, opts); ctx.actor = this.document; + Logger.debug(`partID:`, partId); - ctx = await HeroSummaryCardV1.prepareGuts(ctx); - ctx = await HeroSummaryCardV1.prepareWeapons(ctx); - ctx = await HeroSummaryCardV1.prepareArmor(ctx); - ctx = await HeroSummaryCardV1.prepareFatePath(ctx); - ctx = await HeroSummaryCardV1.prepareAbilityRow(ctx); - ctx = await HeroSummaryCardV1.prepareSpeed(ctx); - ctx = await HeroSummaryCardV1.prepareLevelData(ctx); + switch (partId) { + case `summary`: { + ctx = await HeroSummaryCardV1.prepareGuts(ctx); + ctx = await HeroSummaryCardV1.prepareWeapons(ctx); + ctx = await HeroSummaryCardV1.prepareArmor(ctx); + ctx = await HeroSummaryCardV1.prepareFatePath(ctx); + ctx = await HeroSummaryCardV1.prepareAbilityRow(ctx); + ctx = await HeroSummaryCardV1.prepareSpeed(ctx); + ctx = await HeroSummaryCardV1.prepareLevelData(ctx); + break; + }; + case `skills`: { + ctx = await HeroSkillsCardV1.prepareGear(ctx); + ctx = await HeroSkillsCardV1.prepareAmmo(ctx); + ctx = await HeroSkillsCardV1.prepareSkills(ctx); + break; + }; + case `craft`: { + ctx = await HeroCraftCardV1.prepareCraft(ctx); + break; + }; + }; - ctx = await HeroSkillsCardV1.prepareGear(ctx); - ctx = await HeroSkillsCardV1.prepareAmmo(ctx); - ctx = await HeroSkillsCardV1.prepareSkills(ctx); - - Logger.debug(`Context:`, ctx); + Logger.debug(`Context keys:`, Object.keys(ctx)); return ctx; }; // #endregion diff --git a/module/Apps/ActorSheets/HeroCraftCardV1.mjs b/module/Apps/ActorSheets/HeroCraftCardV1.mjs new file mode 100644 index 0000000..7d78029 --- /dev/null +++ b/module/Apps/ActorSheets/HeroCraftCardV1.mjs @@ -0,0 +1,125 @@ +import { deleteItemFromElement, editItemFromElement } from "../utils.mjs"; +import { documentSorter, filePath } from "../../consts.mjs"; +import { gameTerms } from "../../gameTerms.mjs"; +import { GenericAppMixin } from "../GenericApp.mjs"; +import { localizer } from "../../utils/Localizer.mjs"; +import { Logger } from "../../utils/Logger.mjs"; + +const { HandlebarsApplicationMixin } = foundry.applications.api; +const { ActorSheetV2 } = foundry.applications.sheets; +const { ContextMenu } = foundry.applications.ui; + +export class HeroCraftCardV1 extends GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2)) { + + // #region Options + static DEFAULT_OPTIONS = { + classes: [ + `ripcrypt--actor`, + `ripcrypt--HeroCraftCardV1`, + ], + position: { + width: `auto`, + height: `auto`, + }, + window: { + resizable: false, + }, + actions: { + }, + form: { + submitOnChange: true, + closeOnSubmit: false, + }, + }; + + static PARTS = { + content: { + template: filePath(`templates/Apps/HeroCraftCardV1/content.hbs`), + }, + }; + // #endregion + + // #region Lifecycle + async _onRender(context, options) { + await super._onRender(context, options); + HeroCraftCardV1._onRender.bind(this)(context, options); + }; + + static async _onRender(_context, options) { + const { + element = this.element, + isEditable = this.isEditable, + } = options; + new ContextMenu( + element, + `[data-ctx-menu="craft"]`, + [ + { + name: localizer(`RipCrypt.common.edit`), + condition: (el) => { + const itemId = el.dataset.itemId; + return isEditable && itemId !== ``; + }, + callback: editItemFromElement, + }, + { + name: localizer(`RipCrypt.common.delete`), + condition: (el) => { + const itemId = el.dataset.itemId; + return isEditable && itemId !== ``; + }, + callback: deleteItemFromElement, + }, + ], + { jQuery: false, fixed: true }, + ); + }; + + async _preparePartContext(partId, ctx, opts) { + ctx = await super._preparePartContext(partId, ctx, opts); + ctx.actor = this.document; + + ctx = await HeroCraftCardV1.prepareCraft(ctx); + + Logger.debug(`Context:`, ctx); + return ctx; + }; + + static async prepareCraft(ctx) { + ctx.craft = {}; + const aspects = Object.values(gameTerms.Aspects); + const heroRank = ctx.actor.system.level.rank; + const embeddedCrafts = ctx.actor.itemTypes.craft; + const limit = 4; + + for (const aspect of aspects) { + let crafts = []; + for (const craft of embeddedCrafts) { + if (craft.system.aspect !== aspect) { continue }; + crafts.push({ + uuid: craft.uuid, + name: craft.name, + sort: craft.sort, + use: craft.system.advances[heroRank], + }); + }; + + // Ensure limit isn't surpassed + const length = crafts.length; + if (length >= limit) { + crafts = crafts.slice(0, limit); + } else { + crafts = crafts + .concat(Array(limit - length).fill(null)) + .slice(0, limit); + }; + + ctx.craft[aspect] = crafts.sort(documentSorter); + } + return ctx; + }; + // #endregion + + // #region Actions + // #endregion +}; diff --git a/templates/Apps/CombinedHeroSheet/crafts.css b/templates/Apps/CombinedHeroSheet/crafts.css new file mode 100644 index 0000000..d2d89d1 --- /dev/null +++ b/templates/Apps/CombinedHeroSheet/crafts.css @@ -0,0 +1,36 @@ +.ripcrypt.ripcrypt--CombinedHeroSheet .crafts-summary { + display: grid; + column-gap: var(--col-gap); + grid-template-columns: repeat(3, minmax(0, 3fr)); + grid-template-rows: repeat(5, minmax(0, 1fr)); + grid-auto-flow: column; + + .col-header { + background: var(--section-header-background); + color: var(--section-header-text); + } + + label, .label { + box-sizing: border-box; + padding: 2px 4px; + text-transform: uppercase; + font-size: var(--font-size-14); + overflow: hidden; + text-overflow: ellipsis; + font-weight: bold; + } + + .craft-list { + grid-row: span 4; + display: contents; + + > :nth-child(odd) { + background: var(--alt-row-background); + color: var(--alt-row-text); + } + } + + span.name { + flex-grow: 1; + } +} diff --git a/templates/Apps/CombinedHeroSheet/crafts.hbs b/templates/Apps/CombinedHeroSheet/crafts.hbs new file mode 100644 index 0000000..e0c08ab --- /dev/null +++ b/templates/Apps/CombinedHeroSheet/crafts.hbs @@ -0,0 +1,67 @@ +