110 - Class Data Model (#111)

* Added PreCreate/Create/Delete logic for Class/Subclass and set it as foreignUUID fields in PC

* Moved methods into TypedModelData

* Simplified Subclass
This commit is contained in:
WBHarry 2025-06-07 20:30:12 +02:00 committed by GitHub
parent 53be047e12
commit 746e0f239a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 190 additions and 224 deletions

View file

@ -1178,6 +1178,14 @@
"Description": "Description"
}
},
"Item": {
"Errors": {
"MissingClass": "The character is missing a class",
"SubclassNotInClass": "The subclass does not belong to the character's class",
"ClassAlreadySelected": "The character already has a class",
"SubclassAlreadySelected": "The character already has a subclass for that class."
}
},
"Effects": {
"Types": {
"health": {

View file

@ -230,7 +230,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
domains:
multiclass?.system?.domains.map(key => {
const domain = domains[key];
const alreadySelected = this.actor.system.class.system.domains.includes(key);
const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
return {
...domain,
@ -480,7 +480,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const target = event.target.closest('.card-preview-container');
if (item.type === 'domainCard') {
if (
!this.actor.system.class.system.domains.includes(item.system.domain) &&
!this.actor.system.class.value.system.domains.includes(item.system.domain) &&
this.levelup.classUpgradeChoices?.multiclass?.domain !== item.system.domain
) {
ui.notifications.error(
@ -522,7 +522,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
} else if (event.target.closest('.multiclass-cards')) {
const target = event.target.closest('.multiclass-cards');
if (item.type === 'class') {
if (item.name === this.actor.system.class.name) {
if (item.name === this.actor.system.class.value.name) {
ui.notifications.error(
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.alreadySelectedClass')
);

View file

@ -27,7 +27,7 @@ export default function DhpApplicationMixin(Base) {
async _prepareContext(_options, objectPath = 'document') {
const context = await super._prepareContext(_options);
context.source = this[objectPath].toObject();
context.source = this[objectPath];
context.fields = this[objectPath].schema.fields;
context.systemFields = this[objectPath].system ? this[objectPath].system.schema.fields : {};

View file

@ -11,8 +11,8 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
actions: {
removeSubclass: this.removeSubclass,
viewSubclass: this.viewSubclass,
removeFeature: this.removeFeature,
viewFeature: this.viewFeature,
deleteFeature: this.deleteFeature,
editFeature: this.editFeature,
removeItem: this.removeItem,
viewItem: this.viewItem,
removePrimaryWeapon: this.removePrimaryWeapon,
@ -153,13 +153,13 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
subclass.sheet.render(true);
}
static async removeFeature(_, button) {
static async deleteFeature(_, button) {
await this.document.update({
'system.features': this.document.system.features.filter(x => x.uuid !== button.dataset.feature)
'system.features': this.document.system.features.map(x => x.uuid).filter(x => x !== button.dataset.feature)
});
}
static async viewFeature(_, button) {
static async editFeature(_, button) {
const feature = await fromUuid(button.dataset.feature);
feature.sheet.render(true);
}
@ -198,15 +198,11 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
const item = await fromUuid(data.uuid);
if (item.type === 'subclass') {
await this.document.update({
'system.subclasses': [
...this.document.system.subclasses, item.uuid
]
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
});
} else if (item.type === 'feature') {
await this.document.update({
'system.features': [
...this.document.system.features, item.uuid
]
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
} else if (item.type === 'weapon') {
if (event.currentTarget.classList.contains('primary-weapon-section')) {
@ -231,25 +227,19 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
if (item.type === 'miscellaneous' || item.type === 'consumable') {
if (this.document.system.inventory.choiceA.length < 2)
await this.document.update({
'system.inventory.choiceA': [
...this.document.system.inventory.choiceA, item.uuid
]
'system.inventory.choiceA': [...this.document.system.inventory.choiceA, item.uuid]
});
}
} else if (item.type === 'miscellaneous') {
if (event.currentTarget.classList.contains('take-section')) {
if (this.document.system.inventory.take.length < 3)
await this.document.update({
'system.inventory.take': [
...this.document.system.inventory.take, item.uuid
]
'system.inventory.take': [...this.document.system.inventory.take, item.uuid]
});
} else if (event.currentTarget.classList.contains('choice-b-section')) {
if (this.document.system.inventory.choiceB.length < 2)
await this.document.update({
'system.inventory.choiceB': [
...this.document.system.inventory.choiceB, item.uuid
]
'system.inventory.choiceB': [...this.document.system.inventory.choiceB, item.uuid]
});
}
}

View file

@ -215,13 +215,13 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
//FIXME:
context.domains = this.document.system.class
context.domains = this.document.system.class.value
? {
first: this.document.system.class.system.domains[0]
? SYSTEM.DOMAIN.domains[this.document.system.class.system.domains[0]].src
first: this.document.system.class.value.system.domains[0]
? SYSTEM.DOMAIN.domains[this.document.system.class.value.system.domains[0]].src
: null,
second: this.document.system.class.system.domains[1]
? SYSTEM.DOMAIN.domains[this.document.system.class.system.domains[1]].src
second: this.document.system.class.value.system.domains[1]
? SYSTEM.DOMAIN.domains[this.document.system.class.value.system.domains[1]].src
: null
}
: {};
@ -361,7 +361,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
}
mapAdvancementFeatures(actor, config) {
if (!actor.system.subclass) return { foundation: null, advancements: [] };
if (!actor.system.class.value || !actor.system.class.subclass) return { foundation: null, advancements: [] };
const { subclass, multiclassSubclass } = actor.system.subclassFeatures;
@ -370,8 +370,8 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
multiclass: false,
img: actor.system.subclass.img,
subtitle: game.i18n.localize('DAGGERHEART.Sheets.PC.DomainCard.FoundationTitle'),
domains: actor.system.class.system.domains.map(x => config.DOMAIN.domains[x].src),
className: actor.system.class.name,
domains: actor.system.class.value.system.domains.map(x => config.DOMAIN.domains[x].src),
className: actor.system.class.value.name,
subclassUuid: actor.system.subclass.uuid,
subclassName: actor.system.subclass.name,
spellcast: config.ACTOR.abilities[actor.system.subclass.system.spellcastingTrait]?.name ?? null,
@ -402,9 +402,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
: game.i18n.localize('DAGGERHEART.Sheets.PC.DomainCard.FoundationTitle'),
domains:
firstKey === 'sub'
? actor.system.class.system.domains.map(x => config.DOMAIN.domains[x].src)
? actor.system.class.value.system.domains.map(x => config.DOMAIN.domains[x].src)
: actor.system.multiclass.system.domains.map(x => config.DOMAIN.domains[x].src),
className: firstKey === 'sub' ? actor.system.class.name : actor.system.multiclass.name,
className: firstKey === 'sub' ? actor.system.class.value.name : actor.system.multiclass.name,
subclassUuid: firstBase.uuid,
subclassName: firstBase.name,
spellcast:
@ -456,9 +456,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
: game.i18n.localize('DAGGERHEART.Sheets.PC.DomainCard.FoundationTitle'),
domains:
secondKey === 'sub'
? actor.system.class.system.domains.map(x => config.DOMAIN.domains[x].src)
? actor.system.class.value.system.domains.map(x => config.DOMAIN.domains[x].src)
: actor.system.multiclass.system.domains.map(x => config.DOMAIN.domains[x].src),
className: secondKey === 'sub' ? actor.system.class.name : actor.system.multiclass.name,
className: secondKey === 'sub' ? actor.system.class.value.name : actor.system.multiclass.name,
subclassUuid: secondBase.uuid,
subclassName: secondBase.name,
spellcast:
@ -644,7 +644,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
}
openLevelUp() {
if (!this.document.system.class || !this.document.system.subclass) {
if (!this.document.system.class.value || !this.document.system.subclass) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Sheets.PC.Errors.missingClassOrSubclass'));
return;
}
@ -1144,7 +1144,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
const createdItems = [];
if (item.type === 'domainCard') {
if (!this.document.system.class) {
if (!this.document.system.class.value) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoClassSelected'));
return;
}
@ -1173,63 +1173,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
return createdItem;
} else {
if (!item.system.multiclass && ['class', 'subclass', 'ancestry', 'community'].includes(item.type)) {
const existing = this.document.items.find(x => x.type === item.type);
await existing?.delete();
}
if (item.type === 'subclass') {
if (!item.system.multiclass) {
if (!this.document.system.class) {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.Notification.Info.SelectClassBeforeSubclass')
);
return;
} else if (!this.document.system.class.system.subclasses.some(x => x.uuid === item.uuid)) {
ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.SubclassNotOfClass'));
return;
}
for (var feature of this.document.items.filter(
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.subclass.id
)) {
await feature.delete();
}
}
const features = [
itemData.system.foundationFeature,
itemData.system.specializationFeature,
itemData.system.masteryFeature
];
for (var i = 0; i < features.length; i++) {
const feature = features[i];
for (var ability of feature.abilities) {
const data = (await fromUuid(ability.uuid)).toObject();
if (i > 0) data.system.disabled = true;
data.uuid = itemData.uuid;
const abilityData = await this._onDropItemCreate(data);
ability.uuid = abilityData[0].uuid;
createdItems.push(abilityData);
}
}
} else if (item.type === 'class') {
if (!item.system.multiclass) {
for (var feature of this.document.items.filter(
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.class.id
)) {
await feature.delete();
}
}
for (var feature of item.system.features) {
const data = (await fromUuid(feature.uuid)).toObject();
const itemData = await this._onDropItemCreate(data);
createdItems.push(itemData);
}
} else if (item.type === 'ancestry') {
if (item.type === 'ancestry') {
for (var feature of this.document.items.filter(
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.ancestry.id
)) {

View file

@ -3,40 +3,39 @@
* that resolves to either the document, the index(for items in compenidums) or the UUID string.
*/
export default class ForeignDocumentUUIDField extends foundry.data.fields.DocumentUUIDField {
/**
* @param {foundry.data.types.DocumentUUIDFieldOptions} [options] Options which configure the behavior of the field
* @param {foundry.data.types.DataFieldContext} [context] Additional context which describes the field
*/
constructor(options, context) {
super(options, context);
}
/**
* @param {foundry.data.types.DocumentUUIDFieldOptions} [options] Options which configure the behavior of the field
* @param {foundry.data.types.DataFieldContext} [context] Additional context which describes the field
*/
constructor(options, context) {
super(options, context);
}
/** @inheritdoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
nullable: true,
readonly: false,
idOnly: false
});
}
/** @inheritdoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
nullable: true,
readonly: false,
idOnly: false,
});
}
/**@override */
initialize(value, _model, _options = {}) {
if (this.idOnly) return value;
return (() => {
try {
const doc = fromUuidSync(value);
return doc;
} catch (error) {
console.error(error);
return value ?? null;
}
})();
}
/**@override */
initialize(value, _model, _options = {}) {
if (this.idOnly) return value;
return () => {
try {
const doc = fromUuidSync(value);
return doc;
} catch (error) {
console.error(error);
return value ?? null;
}
};
}
/**@override */
toObject(value) {
return value?.uuid ?? value;
}
/**@override */
toObject(value) {
return value?.uuid ?? value;
}
}

View file

@ -1,4 +1,3 @@
import { getTier } from '../../helpers/utils.mjs';
import BaseDataItem from './base.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
@ -6,9 +5,9 @@ export default class DHClass extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.class",
type: "class",
hasDescription: true,
label: 'TYPES.Item.class',
type: 'class',
hasDescription: true
});
}
@ -19,15 +18,22 @@ export default class DHClass extends BaseDataItem {
...super.defineSchema(),
domains: new fields.ArrayField(new fields.StringField(), { max: 2 }),
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item" })),
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
evasion: new fields.NumberField({ initial: 0, integer: true }),
features: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item" })),
subclasses: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
features: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
subclasses: new fields.ArrayField(
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
),
inventory: new fields.SchemaField({
take: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
choiceA: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
choiceB: new fields.ArrayField(new ForeignDocumentUUIDField({ type: "Item", required: false, nullable: true, initial: undefined })),
take: new fields.ArrayField(
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
),
choiceA: new fields.ArrayField(
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
),
choiceB: new fields.ArrayField(
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
)
}),
characterGuide: new fields.SchemaField({
suggestedTraits: new fields.SchemaField({
@ -38,15 +44,45 @@ export default class DHClass extends BaseDataItem {
presence: new fields.NumberField({ initial: 0, integer: true }),
knowledge: new fields.NumberField({ initial: 0, integer: true })
}),
suggestedPrimaryWeapon: new ForeignDocumentUUIDField({ type: "Item" }),
suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: "Item" }),
suggestedArmor: new ForeignDocumentUUIDField({ type: "Item" }),
suggestedPrimaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }),
suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }),
suggestedArmor: new ForeignDocumentUUIDField({ type: 'Item' })
}),
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
isMulticlass: new fields.BooleanField({ initial: false })
};
}
get multiclassTier() {
return getTier(this.multiclass, true);
async _preCreate(data, options, user) {
const allowed = await super._preCreate(data, options, user);
if (allowed === false) return;
if (this.actor?.type === 'pc') {
const path = data.system.isMulticlass ? 'system.multiclass.value' : 'system.class.value';
if (foundry.utils.getProperty(this.actor, path)) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.ClassAlreadySelected'));
return false;
}
}
}
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (options.parent?.type === 'pc') {
const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`;
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
}
}
_onDelete(options, userId) {
super._onDelete(options, userId);
if (options.parent?.type === 'pc') {
const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`;
options.parent.update({
[`${path}.value`]: null
});
foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete();
}
}
}

View file

@ -1,13 +1,13 @@
import { getTier } from '../../helpers/utils.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import BaseDataItem from './base.mjs';
export default class DHSubclass extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.subclass",
type: "subclass",
hasDescription: true,
label: 'TYPES.Item.subclass',
type: 'subclass',
hasDescription: true
});
}
@ -22,45 +22,48 @@ export default class DHSubclass extends BaseDataItem {
nullable: true,
initial: null
}),
foundationFeature: new fields.SchemaField({
description: new fields.HTMLField({}),
abilities: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
uuid: new fields.StringField({})
})
)
}),
specializationFeature: new fields.SchemaField({
unlocked: new fields.BooleanField({ initial: false }),
tier: new fields.NumberField({ initial: null, nullable: true, integer: true }),
description: new fields.HTMLField({}),
abilities: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
uuid: new fields.StringField({})
})
)
}),
masteryFeature: new fields.SchemaField({
unlocked: new fields.BooleanField({ initial: false }),
tier: new fields.NumberField({ initial: null, nullable: true, integer: true }),
description: new fields.HTMLField({}),
abilities: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
uuid: new fields.StringField({})
})
)
}),
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true })
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
isMulticlass: new fields.BooleanField({ initial: false })
};
}
get multiclassTier() {
return getTier(this.multiclass);
async _preCreate(data, options, user) {
const allowed = await super._preCreate(data, options, user);
if (allowed === false) return;
if (this.actor?.type === 'pc') {
const path = data.system.isMulticlass ? 'system.multiclass' : 'system.class';
const classData = foundry.utils.getProperty(this.actor, path);
if (!classData.value) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.MissingClass'));
return false;
} else if (classData.subclass) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected'));
return false;
} else if (classData.value.system.subclasses.every(x => x.uuid !== `Item.${data._id}`)) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassNotInClass'));
return false;
}
}
}
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (options.parent?.type === 'pc') {
const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
}
}
_onDelete(options, userId) {
super._onDelete(options, userId);
if (options.parent?.type === 'pc') {
const path = `system.${this.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
options.parent.update({ [path]: null });
}
}
}

View file

@ -1,4 +1,5 @@
import { getPathValue } from '../helpers/utils.mjs';
import ForeignDocumentUUIDField from './fields/foreignDocumentUUIDField.mjs';
import { LevelOptionType } from './levelTier.mjs';
const fields = foundry.data.fields;
@ -96,6 +97,14 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
max: new fields.NumberField({ initial: 6, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true })
}),
class: new fields.SchemaField({
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
}),
multiclass: new fields.SchemaField({
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
}),
levelData: new fields.EmbeddedDataField(DhPCLevelData)
};
}
@ -108,14 +117,6 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
}
get class() {
return this.parent.items.find(x => x.type === 'class' && !x.system.multiclass) ?? null;
}
get multiclass() {
return this.parent.items.find(x => x.type === 'class' && x.system.multiclass) ?? null;
}
get multiclassSubclass() {
return this.parent.items.find(x => x.type === 'subclass' && x.system.multiclass) ?? null;
}

View file

@ -1,21 +1,12 @@
export default class DhpItem extends Item {
_preCreate(data, changes, user) {
super._preCreate(data, changes, user);
}
prepareData() {
super.prepareData();
if (this.type === 'class') {
// Bad. Make this better.
// this.system.domains = CONFIG.daggerheart.DOMAIN.classDomainMap[Object.keys(CONFIG.daggerheart.DOMAIN.classDomainMap).find(x => x === this.name.toLowerCase())];
}
}
/**
* @inheritdoc
* @param {object} options - Options which modify the getRollData method.
* @returns
* @returns
*/
getRollData(options = {}) {
let data;

View file

@ -9,7 +9,7 @@
<a
class='effect-control'
data-action='editFeature'
data-feature={{feature.uuid}}
data-feature='{{feature.uuid}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.openItemWorld"}}'
>
<i class="fa-solid fa-globe"></i>
@ -17,7 +17,7 @@
<a
class='effect-control'
data-action='deleteFeature'
data-feature={{feature.uuid}}
data-feature='{{feature.uuid}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.delete"}}'
>
<i class='fas fa-trash'></i>

View file

@ -5,22 +5,16 @@
>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Foundation"}}</legend>
{{#each source.system.foundationFeature.abilities as |feature key|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
{{/each}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.foundationFeature}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Specialization"}}</legend>
{{#each source.system.specializationFeature.abilities as |feature key|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
{{/each}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.specializationFeature}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Mastery"}}</legend>
{{#each source.system.masteryFeature.abilities as |feature key|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
{{/each}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.masteryFeature}}
</fieldset>
</section>

View file

@ -1,12 +1,12 @@
<fieldset class="left-main-container" style="flex: 1;">
<legend class="legend">
{{#if this.document.system.class}}
{{#if this.document.system.multiclass}}
{{#if this.document.system.class.value}}
{{#if this.document.system.multiclass.value}}
<span class="class-feature-selectable {{#if this.multiclass}}inactive{{/if}}" data-action="selectFeatureSet" data-multiclass="false">{{this.document.system.class.name}}&nbsp;{{localize "DAGGERHEART.General.Features"}}</span>
/&nbsp;
<span class="class-feature-selectable {{#if (not this.multiclass)}}inactive{{/if}}" data-action="selectFeatureSet" data-multiclass="true">{{this.document.system.multiclass.name}}&nbsp;{{localize "DAGGERHEART.General.Features"}}</span>
{{else}}
<span>{{this.document.system.class.name}}&nbsp;{{localize "DAGGERHEART.General.Features"}}</span>
<span>{{this.document.system.class.value.name}}&nbsp;{{localize "DAGGERHEART.General.Features"}}</span>
{{/if}}
{{else}}
<span>{{localize "DAGGERHEART.Sheets.PC.Features.Title"}}</span>

View file

@ -4,17 +4,17 @@
<div class="class-info">
<div class="flexrow">
<img class="portrait" src="{{document.img}}" alt="{{document.name}}" data-edit="img">
{{#if document.system.class}}
{{#if document.system.class.value}}
<div class="flexcol">
<h2 class="class-title flex0" data-action="viewObject" data-value="{{document.system.class.uuid}}" data-tab="guide">
<h2 class="class-title flex0" data-action="viewObject" data-value="{{document.system.class.value.uuid}}" data-tab="guide">
<img class="domain-image" src="{{domains.first}}" />
<span>{{document.system.class.name}}</span>
<span>{{document.system.class.value.name}}</span>
<img class="domain-image" src="{{domains.second}}" />
</h2>
<span class="domain-title flex0">
<span>{{document.system.class.system.domains.[0]}}</span>
<span>{{document.system.class.value.system.domains.[0]}}</span>
<span>and</span>
<span>{{document.system.class.system.domains.[1]}}</span>
<span>{{document.system.class.value.system.domains.[1]}}</span>
</div>
{{else}}
<div class="flexcol">
@ -56,7 +56,7 @@
<button data-action="selectAncestry" class="option-select"><i class="fa-solid fa-user-large"></i></button>
{{/objectSelector}}
{{#objectSelector title="Subclass" ids=(join document.system.subclass.uuid) values=(join document.system.subclass.name) titleFontSize=14}}
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class.value)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
{{/objectSelector}}
</div>
</div>