RC-63 | Display skills in sheet

This commit is contained in:
Oliver-Akins 2025-01-30 21:49:16 -07:00
parent df35c00c5d
commit 1302d91469
9 changed files with 142 additions and 24 deletions

View file

@ -80,6 +80,7 @@ export class CombinedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixi
ctx = await HeroSkillsCardV1.prepareGear(ctx); ctx = await HeroSkillsCardV1.prepareGear(ctx);
ctx = await HeroSkillsCardV1.prepareAmmo(ctx); ctx = await HeroSkillsCardV1.prepareAmmo(ctx);
ctx = await HeroSkillsCardV1.prepareSkills(ctx);
Logger.debug(`Context:`, ctx); Logger.debug(`Context:`, ctx);
return ctx; return ctx;

View file

@ -1,5 +1,5 @@
import { deleteItemFromElement, editItemFromElement } from "../utils.mjs"; import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
import { filePath } from "../../consts.mjs"; import { documentSorter, filePath } from "../../consts.mjs";
import { gameTerms } from "../../gameTerms.mjs"; import { gameTerms } from "../../gameTerms.mjs";
import { GenericAppMixin } from "../GenericApp.mjs"; import { GenericAppMixin } from "../GenericApp.mjs";
import { localizer } from "../../utils/Localizer.mjs"; import { localizer } from "../../utils/Localizer.mjs";
@ -52,7 +52,7 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
} = options; } = options;
new ContextMenu( new ContextMenu(
element, element,
`[data-ctx-menu="gear"]`, `[data-ctx-menu="gear"],[data-ctx-menu="skill"]`,
[ [
{ {
name: localizer(`RipCrypt.common.edit`), name: localizer(`RipCrypt.common.edit`),
@ -81,6 +81,7 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
ctx = await HeroSkillsCardV1.prepareGear(ctx); ctx = await HeroSkillsCardV1.prepareGear(ctx);
ctx = await HeroSkillsCardV1.prepareAmmo(ctx); ctx = await HeroSkillsCardV1.prepareAmmo(ctx);
ctx = await HeroSkillsCardV1.prepareSkills(ctx);
Logger.debug(`Context:`, ctx); Logger.debug(`Context:`, ctx);
return ctx; return ctx;
@ -122,6 +123,48 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
ctx.ammo = 0; ctx.ammo = 0;
return ctx; return ctx;
}; };
static async prepareSkills(ctx) {
ctx.skills = {};
const abilities = Object.values(gameTerms.Abilities);
const heroRank = ctx.actor.system.level.rank;
const embeddedSkills = ctx.actor.itemTypes.skill.sort(documentSorter);
for (let ability of abilities) {
const skills = [];
for (const skill of embeddedSkills) {
if (skill.system.ability !== ability) { continue };
skills.push({
uuid: skill.uuid,
name: skill.name,
use: skill.system.advances[heroRank],
});
};
// Thin Glim is grouped with full glim.
if (ability === gameTerms.Abilities.THINGLIM) {
ability = gameTerms.Abilities.GLIM;
};
ctx.skills[ability] ??= [];
ctx.skills[ability].push(...skills);
};
const limit = ctx.actor.system.limit.skills;
for (const ability of abilities) {
if (ctx.skills[ability] == null) { continue };
const length = ctx.skills[ability].length;
if (length >= limit) {
ctx.skills[ability] = ctx.skills[ability].slice(0, limit);
} else {
ctx.skills[ability] = ctx.skills[ability]
.concat(Array(limit - length).fill(null))
.slice(0, limit);
};
}
return ctx;
};
// #endregion // #endregion
// #region Actions // #region Actions

View file

@ -1,8 +1,12 @@
// App imports
import { CombinedHeroSheet } from "./Apps/ActorSheets/CombinedHeroSheet.mjs"; import { CombinedHeroSheet } from "./Apps/ActorSheets/CombinedHeroSheet.mjs";
import { DicePool } from "./Apps/DicePool.mjs"; import { DicePool } from "./Apps/DicePool.mjs";
import { HeroSkillsCardV1 } from "./Apps/ActorSheets/HeroSkillsCardV1.mjs"; import { HeroSkillsCardV1 } from "./Apps/ActorSheets/HeroSkillsCardV1.mjs";
import { HeroSummaryCardV1 } from "./Apps/ActorSheets/HeroSummaryCardV1.mjs"; import { HeroSummaryCardV1 } from "./Apps/ActorSheets/HeroSummaryCardV1.mjs";
// Util imports
import { documentSorter } from "./consts.mjs";
const { deepFreeze } = foundry.utils; const { deepFreeze } = foundry.utils;
Object.defineProperty( Object.defineProperty(
@ -16,6 +20,9 @@ Object.defineProperty(
HeroSummaryCardV1, HeroSummaryCardV1,
HeroSkillsCardV1, HeroSkillsCardV1,
}, },
utils: {
documentSorter,
},
}), }),
writable: false, writable: false,
}, },

View file

@ -5,3 +5,13 @@ export function filePath(path) {
}; };
return `systems/ripcrypt/${path}`; return `systems/ripcrypt/${path}`;
}; };
// MARK: documentSorter
export function documentSorter(a, b) {
const sortDelta = b.sort - a.sort;
if (sortDelta !== 0) {
return sortDelta;
};
// TODO alphabetical sort
return 0;
};

View file

@ -132,6 +132,7 @@ export class HeroData extends foundry.abstract.TypeDataModel {
this.limit = { this.limit = {
weapons: 4, weapons: 4,
equipment: 12, equipment: 12,
skills: 4,
}; };
}; };

View file

@ -2,19 +2,17 @@ import { gameTerms } from "../../gameTerms.mjs";
const { fields } = foundry.data; const { fields } = foundry.data;
const abilityPaths = [`grit`, `gait`, `grip`, `glim`, `thin-glim`];
export class SkillData extends foundry.abstract.TypeDataModel { export class SkillData extends foundry.abstract.TypeDataModel {
// MARK: Schema // MARK: Schema
static defineSchema() { static defineSchema() {
const schema = { const schema = {
ability: new fields.StringField({ ability: new fields.StringField({
initial: abilityPaths[0], initial: gameTerms.Abilities.GRIT,
blank: true, blank: true,
trim: true, trim: true,
nullable: false, nullable: false,
required: true, required: true,
choices: () => abilityPaths, choices: () => Object.values(gameTerms.Abilities),
}), }),
}; };
@ -53,7 +51,7 @@ export class SkillData extends foundry.abstract.TypeDataModel {
label: `RipCrypt.common.ability`, label: `RipCrypt.common.ability`,
path: `system.ability`, path: `system.ability`,
value: this.ability, value: this.ability,
options: abilityPaths.map(ability => ({ options: Object.values(gameTerms.Abilities).map(ability => ({
label: `RipCrypt.common.abilities.${ability}`, label: `RipCrypt.common.abilities.${ability}`,
value: ability, value: ability,
})), })),

View file

@ -1,4 +1,11 @@
export const gameTerms = Object.preventExtensions({ export const gameTerms = Object.preventExtensions({
Abilities: Object.freeze({
GRIT: `grit`,
GRIP: `grip`,
GAIT: `gait`,
GLIM: `glim`,
THINGLIM: `thin-glim`,
}),
FatePath: [ FatePath: [
`North`, `North`,
`East`, `East`,

View file

@ -4,10 +4,22 @@
<span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span> <span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span>
</div> </div>
<ol class="num-before skill-list even grit-skills"> <ol class="num-before skill-list even grit-skills">
{{#each skills.grit as | skill |}}
{{#if skill}}
<li data-item-id="{{skill.uuid}}" data-ctx-menu="skill">
<span class="name">{{ skill.name }}</span>
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ skill.use }}"
data-tooltip-direction="UP"
></rc-icon>
</li>
{{else}}
<li></li> <li></li>
<li></li> {{/if}}
<li></li> {{/each}}
<li></li>
</ol> </ol>
<div class="label col-header list-header gait-skills-header"> <div class="label col-header list-header gait-skills-header">
@ -15,10 +27,22 @@
<span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span> <span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span>
</div> </div>
<ol class="num-before skill-list even gait-skills"> <ol class="num-before skill-list even gait-skills">
{{#each skills.gait as | skill |}}
{{#if skill}}
<li data-item-id="{{skill.uuid}}" data-ctx-menu="skill">
<span class="name">{{ skill.name }}</span>
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ skill.use }}"
data-tooltip-direction="UP"
></rc-icon>
</li>
{{else}}
<li></li> <li></li>
<li></li> {{/if}}
<li></li> {{/each}}
<li></li>
</ol> </ol>
<div class="label col-header list-header grip-skills-header"> <div class="label col-header list-header grip-skills-header">
@ -26,10 +50,22 @@
<span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span> <span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span>
</div> </div>
<ol class="num-before skill-list odd grip-skills"> <ol class="num-before skill-list odd grip-skills">
{{#each skills.grip as | skill |}}
{{#if skill}}
<li data-item-id="{{skill.uuid}}" data-ctx-menu="skill">
<span class="name">{{ skill.name }}</span>
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ skill.use }}"
data-tooltip-direction="UP"
></rc-icon>
</li>
{{else}}
<li></li> <li></li>
<li></li> {{/if}}
<li></li> {{/each}}
<li></li>
</ol> </ol>
<div class="label col-header list-header glim-skills-header"> <div class="label col-header list-header glim-skills-header">
@ -37,10 +73,22 @@
<span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span> <span class="small">{{ rc-i18n "RipCrypt.common.rank" }}</span>
</div> </div>
<ol class="num-before skill-list odd glim-skills"> <ol class="num-before skill-list odd glim-skills">
{{#each skills.glim as | skill |}}
{{#if skill}}
<li data-item-id="{{skill.uuid}}" data-ctx-menu="skill">
<span class="name">{{ skill.name }}</span>
<rc-icon
name="icons/info-circle"
var:size="16px"
var:fill="currentColor"
data-tooltip="{{ skill.use }}"
data-tooltip-direction="UP"
></rc-icon>
</li>
{{else}}
<li></li> <li></li>
<li></li> {{/if}}
<li></li> {{/each}}
<li></li>
</ol> </ol>
<div class="label col-header list-header"> <div class="label col-header list-header">

View file

@ -41,7 +41,10 @@
grid-template-rows: subgrid; grid-template-rows: subgrid;
& > li { & > li {
padding-left: 4px; padding: 0 4px;
.name {
flex-grow: 1;
}
} }
&.even > :nth-child(even), &.even > :nth-child(even),