Add crafts into the Combined Hero sheet

This commit is contained in:
Oliver-Akins 2025-02-09 16:48:32 -07:00
parent 1535e07f77
commit f6ff3247e3
6 changed files with 269 additions and 12 deletions

View file

@ -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,12 +68,25 @@ 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);
switch (partId) {
case `summary`: {
ctx = await HeroSummaryCardV1.prepareGuts(ctx);
ctx = await HeroSummaryCardV1.prepareWeapons(ctx);
ctx = await HeroSummaryCardV1.prepareArmor(ctx);
@ -77,12 +94,21 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi
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;
};
};
Logger.debug(`Context:`, ctx);
Logger.debug(`Context keys:`, Object.keys(ctx));
return ctx;
};
// #endregion

View file

@ -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
};

View file

@ -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;
}
}

View file

@ -0,0 +1,67 @@
<div class="crafts-summary">
<div class="label col-header">Focus</div>
<ol class="craft-list num-before">
{{#each craft.focus as | craft |}}
{{#if craft}}
<li data-item-id="{{craft.uuid}}" data-ctx-menu="craft">
<span class="name">{{ craft.name }}</span>
{{#if craft.use}}
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ craft.use }}"
data-tooltip-direction="UP"
></rc-icon>
{{/if}}
</li>
{{else}}
<li></li>
{{/if}}
{{/each}}
</ol>
<div class="label col-header">Fract</div>
<ol class="craft-list num-before">
{{#each craft.fract as | craft |}}
{{#if craft}}
<li data-item-id="{{craft.uuid}}" data-ctx-menu="craft">
<span class="name">{{ craft.name }}</span>
{{#if craft.use}}
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ craft.use }}"
data-tooltip-direction="UP"
></rc-icon>
{{/if}}
</li>
{{else}}
<li></li>
{{/if}}
{{/each}}
</ol>
<div class="label col-header">Flect</div>
<ol class="craft-list num-before">
{{#each craft.flect as | craft |}}
{{#if craft}}
<li data-item-id="{{craft.uuid}}" data-ctx-menu="craft">
<span class="name">{{ craft.name }}</span>
{{#if craft.use}}
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ craft.use }}"
data-tooltip-direction="UP"
></rc-icon>
{{/if}}
</li>
{{else}}
<li></li>
{{/if}}
{{/each}}
</ol>
</div>

View file

@ -1,3 +1,5 @@
@import url("./crafts.css");
.ripcrypt.ripcrypt--CombinedHeroSheet {
> .window-content {
gap: 4px;

View file

@ -13,6 +13,7 @@
align-items: center;
counter-increment: list-index 1;
position: relative;
padding: 0 4px;
}
&.num-before > li::before,