[Fix] Class Feature Change (#364)

* Changed to just use a features field

* Subclass now uses a simple Features field
This commit is contained in:
WBHarry 2025-07-17 19:43:35 +02:00 committed by GitHub
parent 1d5e267285
commit 0cc1597dfe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 158 additions and 97 deletions

View file

@ -506,9 +506,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
name: this.setup.ancestryName ?? this.setup.primaryAncestry.name,
system: {
...this.setup.primaryAncestry.system,
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid],
primaryFeature: primaryAncestryFeature.uuid,
secondaryFeature: secondaryAncestryFeature.uuid
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid]
}
};

View file

@ -63,22 +63,8 @@ export default class AncestrySheet extends DHHeritageSheet {
event.stopPropagation();
const target = button.closest('.feature-item');
const feature = this.document.system[`${target.dataset.type}Feature`];
const featureExists = feature && Object.keys(feature).length > 0;
if (featureExists) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
type: game.i18n.localize(`TYPES.Item.feature`),
name: feature.name
})
},
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name })
});
if (!confirmed) return;
}
if (featureExists && target.dataset.type === 'primary') await feature.update({ 'system.primary': null });
if (feature) await feature.update({ 'system.subType': null });
await this.document.update({
'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid)
});
@ -94,15 +80,18 @@ export default class AncestrySheet extends DHHeritageSheet {
*/
async _onDrop(event) {
event.stopPropagation();
event.preventDefault();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary';
await item.update({ 'system.subType': subType });
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes[subType]) {
const error = subType === 'primary' ? 'featureNotPrimary' : 'featureNotSecondary';
ui.notifications.warn(game.i18n.localize(`DAGGERHEART.UI.Notifications.${error}`));
return;
}
await item.update({ 'system.subType': subType });
await this.document.update({
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});

View file

@ -78,6 +78,7 @@ export default class ClassSheet extends DHBaseItemSheet {
/* -------------------------------------------- */
async _onDrop(event) {
event.stopPropagation();
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
const target = event.target.closest('fieldset.drop-section');
@ -87,12 +88,24 @@ export default class ClassSheet extends DHBaseItemSheet {
});
} else if (item.type === 'feature') {
if (target.classList.contains('hope-feature')) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.hope) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotHope'));
return;
}
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.hope });
await this.document.update({
'system.hopeFeatures': [...this.document.system.hopeFeatures.map(x => x.uuid), item.uuid]
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
} else if (target.classList.contains('class-feature')) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.class) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotClass'));
return;
}
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.class });
await this.document.update({
'system.classFeatures': [...this.document.system.classFeatures.map(x => x.uuid), item.uuid]
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
}
} else if (item.type === 'weapon') {
@ -177,28 +190,25 @@ export default class ClassSheet extends DHBaseItemSheet {
doc.sheet.render({ force: true });
}
getActionPath(type) {
return type === 'hope' ? 'hopeFeatures' : 'classFeatures';
}
static async addFeature(_, target) {
const actionPath = this.getActionPath(target.dataset.type);
const feature = await game.items.documentClass.create({
type: 'feature',
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') })
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }),
system: {
subType:
target.dataset.type === 'hope'
? CONFIG.DH.ITEM.featureSubTypes.hope
: CONFIG.DH.ITEM.featureSubTypes.class
}
});
await this.document.update({
[`system.${actionPath}`]: [
...this.document.system[actionPath].filter(x => x).map(x => x.uuid),
feature.uuid
]
[`system.features`]: [...this.document.system.features.filter(x => x).map(x => x.uuid), feature.uuid]
});
}
static async editFeature(_, button) {
const target = button.closest('.feature-item');
const actionPath = this.getActionPath(button.dataset.type);
const feature = this.document.system[actionPath].find(x => x?.id === target.dataset.featureId);
const feature = this.document.system.features.find(x => x?.id === target.dataset.featureId);
if (!feature) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
return;
@ -210,10 +220,16 @@ export default class ClassSheet extends DHBaseItemSheet {
static async deleteFeature(event, button) {
event.stopPropagation();
const target = button.closest('.feature-item');
const actionPath = this.getActionPath(button.dataset.type);
const feature = this.document.system.features.find(
feature => feature && feature.id === target.dataset.featureId
);
if (feature) {
await feature.update({ 'system.subType': null });
}
await this.document.update({
[`system.${actionPath}`]: this.document.system[actionPath]
[`system.features`]: this.document.system.features
.filter(feature => feature && feature.id !== target.dataset.featureId)
.map(x => x.uuid)
});

View file

@ -40,28 +40,46 @@ export default class SubclassSheet extends DHBaseItemSheet {
static async addFeature(_, target) {
const feature = await game.items.documentClass.create({
type: 'feature',
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') })
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }),
system: {
subType:
target.dataset.type === 'foundation'
? CONFIG.DH.ITEM.featureSubTypes.foundation
: target.dataset.type === 'specialization'
? CONFIG.DH.ITEM.featureSubTypes.specialization
: CONFIG.DH.ITEM.featureSubTypes.mastery
}
});
await this.document.update({
[`system.${target.dataset.type}`]: feature.uuid
[`system.features`]: [...this.document.system.features.map(x => x.uuid), feature.uuid]
});
}
static async editFeature(_, button) {
const feature = this.document.system[button.dataset.type];
const feature = this.document.system.features.find(x => x.id === button.dataset.feature);
if (!feature) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
return;
}
if (feature) {
await feature.update({ 'system.subType': null });
}
feature.sheet.render(true);
}
static async deleteFeature(event, button) {
static async deleteFeature(event, target) {
event.stopPropagation();
const feature = this.document.system.features.find(feature => feature.id === target.dataset.feature);
if (feature) {
await feature.update({ 'system.subType': null });
}
await this.document.update({
[`system.${button.dataset.type}`]: null
[`system.features`]: this.document.system.features
.filter(feature => feature && feature.id !== target.dataset.feature)
.map(x => x.uuid)
});
}
@ -82,18 +100,45 @@ export default class SubclassSheet extends DHBaseItemSheet {
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
if (data.fromInternal) return;
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
const dropSection = event.target.closest('.drop-section');
if (this.document.system[dropSection.dataset.type]) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsFull'));
return;
}
const target = event.target.closest('fieldset.drop-section');
if (item.type === 'feature') {
if (target.dataset.type === 'foundation') {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.foundation) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotFoundation'));
return;
}
await this.document.update({ [`system.${dropSection.dataset.type}`]: item.uuid });
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.foundation });
await this.document.update({
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
} else if (target.dataset.type === 'specialization') {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.specialization) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotSpecialization'));
return;
}
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.specialization });
await this.document.update({
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
} else if (target.dataset.type === 'mastery') {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.mastery) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotMastery'));
return;
}
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.mastery });
await this.document.update({
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
}
}
}
}

View file

@ -411,7 +411,7 @@ export const levelupData = {
};
export const subclassFeatureLabels = {
1: 'DAGGERHEART.ITEMS.DomainCard.foundation',
1: 'DAGGERHEART.ITEMS.DomainCard.foundationTitle',
2: 'DAGGERHEART.ITEMS.DomainCard.specializationTitle',
3: 'DAGGERHEART.ITEMS.DomainCard.masteryTitle'
};

View file

@ -1322,7 +1322,12 @@ export const featureTypes = {
export const featureSubTypes = {
primary: 'primary',
secondary: 'secondary'
secondary: 'secondary',
hope: 'hope',
class: 'class',
foundation: 'foundation',
specialization: 'specialization',
mastery: 'mastery'
};
export const actionTypes = {

View file

@ -124,12 +124,9 @@ export default class DhCharacter extends BaseDataActor {
label: 'DAGGERHEART.GENERAL.Range.other'
})
}),
rally: new fields.ArrayField(
new fields.StringField(),
{
label: 'DAGGERHEART.CLASS.Feature.rallyDice'
}
)
rally: new fields.ArrayField(new fields.StringField(), {
label: 'DAGGERHEART.CLASS.Feature.rallyDice'
})
}),
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
rules: new fields.SchemaField({
@ -260,11 +257,11 @@ export default class DhCharacter extends BaseDataActor {
classFeatures.push(item);
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
const subclassState = this.class.subclass.system.featureState;
const identifier = item.system.identifier;
const subType = item.system.subType;
if (
identifier === 'foundationFeature' ||
(identifier === 'specializationFeature' && subclassState >= 2) ||
(identifier === 'masterFeature' && subclassState >= 3)
subType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
(subType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
(subType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
) {
subclassFeatures.push(item);
}

View file

@ -27,8 +27,7 @@ export default class DHClass extends BaseDataItem {
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
}),
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
hopeFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
classFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
inventory: new fields.SchemaField({
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
@ -52,12 +51,18 @@ export default class DHClass extends BaseDataItem {
};
}
get hopeFeature() {
return this.hopeFeatures.length > 0 ? this.hopeFeatures[0] : null;
get hopeFeatures() {
return (
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.hope) ??
(this.features.filter(x => !x).length > 0 ? {} : null)
);
}
get features() {
return [...this.hopeFeatures.filter(x => x), ...this.classFeatures.filter(x => x)];
get classFeatures() {
return (
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.class) ??
(this.features.filter(x => !x).length > 0 ? {} : null)
);
}
async _preCreate(data, options, user) {

View file

@ -29,7 +29,6 @@ export default class DHDomainCard extends BaseDataItem {
required: true,
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
}),
foundation: new fields.BooleanField({ initial: false }),
inVault: new fields.BooleanField({ initial: false }),
actions: new fields.ArrayField(new ActionField())
};

View file

@ -1,4 +1,4 @@
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import BaseDataItem from './base.mjs';
export default class DHSubclass extends BaseDataItem {
@ -22,20 +22,22 @@ export default class DHSubclass extends BaseDataItem {
nullable: true,
initial: null
}),
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
isMulticlass: new fields.BooleanField({ initial: false })
};
}
get features() {
return [
{ ...this.foundationFeature?.toObject(), identifier: 'foundationFeature' },
{ ...this.specializationFeature?.toObject(), identifier: 'specializationFeature' },
{ ...this.masteryFeature?.toObject(), identifier: 'masteryFeature' }
];
get foundationFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.foundation);
}
get specializationFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.specialization);
}
get masteryFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.mastery);
}
async _preCreate(data, options, user) {