RC-65 | Gear | Display Data

This commit is contained in:
Oliver-Akins 2025-01-25 23:49:51 -07:00
parent 9a3b82ef6a
commit 1e416ff9bf
10 changed files with 172 additions and 39 deletions

View file

@ -17,6 +17,8 @@
"HeroSkillsCardV1": "Hero Skill Card" "HeroSkillsCardV1": "Hero Skill Card"
}, },
"common": { "common": {
"edit": "Edit",
"delete": "Delete",
"empty": "---", "empty": "---",
"move": "Move", "move": "Move",
"run": "Run", "run": "Run",

View file

@ -1,5 +1,6 @@
import { filePath } from "../../consts.mjs"; import { filePath } from "../../consts.mjs";
import { GenericAppMixin } from "../GenericApp.mjs"; import { GenericAppMixin } from "../GenericApp.mjs";
import { HeroSkillsCardV1 } from "./HeroSkillsCardV1.mjs";
import { HeroSummaryCardV1 } from "./HeroSummaryCardV1.mjs"; import { HeroSummaryCardV1 } from "./HeroSummaryCardV1.mjs";
import { Logger } from "../../utils/Logger.mjs"; import { Logger } from "../../utils/Logger.mjs";
@ -43,7 +44,26 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi
// #region Lifecycle // #region Lifecycle
async _onRender(context, options) { async _onRender(context, options) {
await super._onRender(context, options); await super._onRender(context, options);
HeroSummaryCardV1._onRender.bind(this)(context, options);
const summaryElement = this.element.querySelector(`.HeroSummaryCardV1`);
HeroSummaryCardV1._onRender(
context,
{
...options,
element: summaryElement,
isEditable: this.isEditable,
},
);
const skillsElement = this.element.querySelector(`.HeroSkillsCardV1`);
HeroSkillsCardV1._onRender.bind(this)(
context,
{
...options,
element: skillsElement,
isEditable: this.isEditable,
},
);
}; };
async _preparePartContext(partId, ctx, opts) { async _preparePartContext(partId, ctx, opts) {
@ -58,6 +78,8 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi
ctx = await HeroSummaryCardV1.prepareSpeed(ctx); ctx = await HeroSummaryCardV1.prepareSpeed(ctx);
ctx = await HeroSummaryCardV1.prepareLevelData(ctx); ctx = await HeroSummaryCardV1.prepareLevelData(ctx);
ctx = await HeroSkillsCardV1.prepareGear(ctx);
Logger.debug(`Context:`, ctx); Logger.debug(`Context:`, ctx);
return ctx; return ctx;
}; };

View file

@ -1,3 +1,4 @@
import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
import { filePath } from "../../consts.mjs"; import { filePath } from "../../consts.mjs";
import { gameTerms } from "../../gameTerms.mjs"; import { gameTerms } from "../../gameTerms.mjs";
import { GenericAppMixin } from "../GenericApp.mjs"; import { GenericAppMixin } from "../GenericApp.mjs";
@ -6,6 +7,7 @@ import { Logger } from "../../utils/Logger.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api; const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets; const { ActorSheetV2 } = foundry.applications.sheets;
const { ContextMenu } = foundry.applications.ui;
export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2)) { export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2)) {
@ -38,13 +40,79 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
// #endregion // #endregion
// #region Lifecycle // #region Lifecycle
async _onRender(context, options) {
await super._onRender(context, options);
HeroSkillsCardV1._onRender.bind(this)(context, options);
};
static async _onRender(_context, options) {
const {
element = this.element,
isEditable = this.isEditable,
} = options;
new ContextMenu(
element,
`[data-ctx-menu="gear"]`,
[
{
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) { async _preparePartContext(partId, ctx, opts) {
ctx = await super._preparePartContext(partId, ctx, opts); ctx = await super._preparePartContext(partId, ctx, opts);
ctx.actor = this.document; ctx.actor = this.document;
ctx = await HeroSkillsCardV1.prepareGear(ctx);
Logger.debug(`Context:`, ctx); Logger.debug(`Context:`, ctx);
return ctx; return ctx;
}; };
static async prepareGear(ctx) {
const limit = ctx.actor.system.limit.equipment;
ctx.gear = [];
const items = [...ctx.actor.items];
for (const item of items) {
if (!gameTerms.gearItemTypes.has(item.type)) { continue };
if ( `equipped` in item.system && !item.system.equipped) { continue };
ctx.gear.push({
index: ctx.gear.length,
uuid: item.uuid,
name: item.name,
empty: false,
});
if (ctx.gear.length >= limit) { break };
};
if (ctx.gear.length < limit) {
for (let i = ctx.gear.length - 1; i <= limit; i++) {
ctx.gear.push({
index: ctx.gear.length,
uuid: ``, name: ``, empty: true });
};
};
return ctx;
};
// #endregion // #endregion
// #region Actions // #region Actions

View file

@ -1,3 +1,4 @@
import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
import { filePath } from "../../consts.mjs"; import { filePath } from "../../consts.mjs";
import { gameTerms } from "../../gameTerms.mjs"; import { gameTerms } from "../../gameTerms.mjs";
import { GenericAppMixin } from "../GenericApp.mjs"; import { GenericAppMixin } from "../GenericApp.mjs";
@ -24,7 +25,6 @@ export class HeroSummaryCardV1 extends GenericAppMixin(HandlebarsApplicationMixi
resizable: false, resizable: false,
}, },
actions: { actions: {
editItem: (_event, target) => this._editItem(target),
}, },
form: { form: {
submitOnChange: true, submitOnChange: true,
@ -45,32 +45,30 @@ export class HeroSummaryCardV1 extends GenericAppMixin(HandlebarsApplicationMixi
HeroSummaryCardV1._onRender.bind(this)(context, options); HeroSummaryCardV1._onRender.bind(this)(context, options);
}; };
static async _onRender() { static async _onRender(context, options) {
const {
element = this.element,
isEditable = this.isEditable,
} = options;
new ContextMenu( new ContextMenu(
this.element, element,
`[data-ctx-menu="weapon"],[data-ctx-menu="armour"]`, `[data-ctx-menu="weapon"],[data-ctx-menu="armour"]`,
[ [
{ {
name: `Edit`, name: localizer(`RipCrypt.common.edit`),
condition: (el) => { condition: (el) => {
const itemId = el.dataset.itemId; const itemId = el.dataset.itemId;
return this.isEditable && itemId !== ``; return isEditable && itemId !== ``;
}, },
callback: HeroSummaryCardV1._editItem, callback: editItemFromElement,
}, },
{ {
name: `Delete`, name: localizer(`RipCrypt.common.delete`),
condition: (el) => { condition: (el) => {
const itemId = el.dataset.itemId; const itemId = el.dataset.itemId;
return this.isEditable && itemId !== ``; return isEditable && itemId !== ``;
},
callback: async (el) => {
const itemEl = el.closest(`[data-item-id]`);
if (!itemEl) { return };
const itemId = itemEl.dataset.itemId;
const item = await fromUuid(itemId);
await item.delete();
}, },
callback: deleteItemFromElement,
}, },
], ],
{ jQuery: false, fixed: true }, { jQuery: false, fixed: true },
@ -200,13 +198,5 @@ export class HeroSummaryCardV1 extends GenericAppMixin(HandlebarsApplicationMixi
// #endregion // #endregion
// #region Actions // #region Actions
static async _editItem(target) {
const itemEl = target.closest(`[data-item-id]`);
if (!itemEl) { return };
const itemId = itemEl.dataset.itemId;
if (!itemId) { return };
const item = await fromUuid(itemId);
item.sheet.render({ force: true });
};
// #endregion // #endregion
}; };

View file

@ -1,3 +1,4 @@
import { deleteItemFromElement, editItemFromElement } from "./utils.mjs";
import { DicePool } from "./DicePool.mjs"; import { DicePool } from "./DicePool.mjs";
/** /**
@ -12,7 +13,9 @@ export function GenericAppMixin(HandlebarsApp) {
`ripcrypt`, `ripcrypt`,
], ],
actions: { actions: {
roll: this.rollDice, roll: this._rollDice,
editItem: (_event, target) => editItemFromElement(target),
deleteItem: (_event, target) => deleteItemFromElement(target),
}, },
}; };
@ -22,6 +25,19 @@ export function GenericAppMixin(HandlebarsApp) {
// #endregion // #endregion
// #region Lifecycle // #region Lifecycle
/**
* @override
* Making it so that if the app is already open, it's brought to
* top after being re-rendered as normal
*/
async render(options = {}, _options = {}) {
super.render(options, _options);
const instance = foundry.applications.instances.get(this.id);
if (instance !== undefined && !options.noBringToFront) {
instance.bringToFront();
};
};
async _preparePartContext(partId, ctx, opts) { async _preparePartContext(partId, ctx, opts) {
ctx = await super._preparePartContext(partId, ctx, opts); ctx = await super._preparePartContext(partId, ctx, opts);
delete ctx.document; delete ctx.document;
@ -41,7 +57,7 @@ export function GenericAppMixin(HandlebarsApp) {
// #region Actions // #region Actions
/** @this {GenericRipCryptApp} */ /** @this {GenericRipCryptApp} */
static async rollDice(_$e, el) { static async _rollDice(_$e, el) {
const data = el.dataset; const data = el.dataset;
const diceCount = parseInt(data.diceCount); const diceCount = parseInt(data.diceCount);
const flavor = data.flavor; const flavor = data.flavor;

27
module/Apps/utils.mjs Normal file
View file

@ -0,0 +1,27 @@
/*
This file contains utilities used by Applications in order to be DRYer
*/
/**
* @param {HTMLElement} target The element that gets
*/
export async function editItemFromElement(target) {
const itemEl = target.closest(`[data-item-id]`);
if (!itemEl) { return };
const itemId = itemEl.dataset.itemId;
if (!itemId) { return };
const item = await fromUuid(itemId);
item.sheet.render({ force: true });
};
/**
* @param {HTMLElement} target The element that gets
*/
export async function deleteItemFromElement(target) {
const itemEl = target.closest(`[data-item-id]`);
if (!itemEl) { return };
const itemId = itemEl.dataset.itemId;
if (!itemId) { return };
const item = await fromUuid(itemId);
item.delete();
};

View file

@ -46,7 +46,7 @@ export class ProtectorData extends foundry.abstract.TypeDataModel {
// #endregion // #endregion
// #region Sheet Data // #region Sheet Data
getFormFields(ctx) { getFormFields(_ctx) {
const fields = [ const fields = [
{ {
id: `location`, id: `location`,

View file

@ -23,4 +23,10 @@ export const gameTerms = Object.preventExtensions({
ARMS: `arms`, ARMS: `arms`,
LEGS: `legs`, LEGS: `legs`,
}), }),
/** The types of items that contribute to the gear limit */
gearItemTypes: new Set([
`armour`,
`weapon`,
`shield`,
]),
}); });

View file

@ -48,18 +48,16 @@
<span class="small">{{ rc-i18n "RipCrypt.common.slot" }}</span> <span class="small">{{ rc-i18n "RipCrypt.common.slot" }}</span>
</div> </div>
<ol class="num-after gear-list"> <ol class="num-after gear-list">
<li></li> {{#each gear as | itemInSlot |}}
<li></li> <li
<li></li> data-slot="{{itemInSlot.index}}"
<li></li> data-ctx-menu="gear"
<li></li> data-slot-empty="{{ itemInSlot.empty }}"
<li></li> data-item-id="{{ itemInSlot.uuid }}"
<li></li> >
<li></li> {{itemInSlot.name}}
<li></li> </li>
<li></li> {{/each}}
<li></li>
<li></li>
</ol> </ol>
<div class="currencies"> <div class="currencies">

View file

@ -85,6 +85,10 @@
grid-template-rows: subgrid; grid-template-rows: subgrid;
list-style-type: none; list-style-type: none;
> li {
padding: 0 4px;
}
> :nth-child(even) { > :nth-child(even) {
background: var(--alt-row-background); background: var(--alt-row-background);
color: var(--alt-row-text); color: var(--alt-row-text);