[Fix] Itemlink Redux Revengeance (#399)

* Small random fixes

* Added use of ItemLinkFields

* Multiclass levelup fixes

* Fixed our onCreate methods unintentionally being run on all clients

* Remade apps handling

* Added for all class items and subclass

* Restored foreignDocumentUuidField

* Improved

* PR fxies

* Fixed tooltip enrichment

* .

* Reverted silly change
This commit is contained in:
WBHarry 2025-07-26 00:37:30 +02:00 committed by GitHub
parent fcba5041e9
commit 2a4777f1a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 648 additions and 489 deletions

View file

@ -37,7 +37,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
this.extraSchemas.forEach(s => {
let clsField;
if(clsField = this.getActionField(s)) schemaFields[s] = new clsField();
if ((clsField = this.getActionField(s))) schemaFields[s] = new clsField();
});
return schemaFields;
@ -117,14 +117,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
async use(event, ...args) {
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
if(this.chatDisplay) this.toChat();
if (this.chatDisplay) this.toChat();
let config = this.prepareConfig(event);
for(let i = 0; i < this.constructor.extraSchemas.length; i++) {
for (let i = 0; i < this.constructor.extraSchemas.length; i++) {
let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]);
if(clsField?.prepareConfig) {
if (clsField?.prepareConfig) {
const keep = clsField.prepareConfig.call(this, config);
if(config.isFastForward && !keep) return;
if (config.isFastForward && !keep) return;
}
}

View file

@ -4,7 +4,7 @@ export default class DHEffectAction extends DHBaseAction {
static extraSchemas = [...super.extraSchemas, 'effects', 'target'];
async trigger(event, data) {
if(this.effects.length) {
if (this.effects.length) {
const cls = getDocumentClass('ChatMessage'),
msg = {
type: 'applyEffect',

View file

@ -26,7 +26,9 @@ export default class BeastformEffect extends foundry.abstract.TypeDataModel {
};
}
async _onCreate() {
async _onCreate(_data, _options, userId) {
if (userId !== game.user.id) return;
if (this.parent.parent?.type === 'character') {
this.parent.parent.system.primaryWeapon?.update?.({ 'system.equipped': false });
this.parent.parent.system.secondayWeapon?.update?.({ 'system.equipped': false });

View file

@ -263,7 +263,8 @@ export default class DhCharacter extends BaseDataActor {
}
get tier() {
return this.levelData.level.current === 1
const currentLevel = this.levelData.level.current;
return currentLevel === 1
? 1
: Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers).find(
tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end

View file

@ -42,7 +42,9 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t))
? Array.from(game.user.targets).map(t =>
game.system.api.fields.ActionFields.TargetField.formatTarget(t)
)
: this.targets;
}
}

View file

@ -25,7 +25,9 @@ export default class DHApplyEffect extends foundry.abstract.TypeDataModel {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t))
? Array.from(game.user.targets).map(t =>
game.system.api.fields.ActionFields.TargetField.formatTarget(t)
)
: this.targets;
}

View file

@ -40,7 +40,9 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t))
? Array.from(game.user.targets).map(t =>
game.system.api.fields.ActionFields.TargetField.formatTarget(t)
)
: this.targets;
}
}

View file

@ -7,4 +7,4 @@ export { default as SaveField } from './saveField.mjs';
export { default as BeastformField } from './beastformField.mjs';
export { default as DamageField } from './damageField.mjs';
export { default as HealingField } from './healingField.mjs';
export { default as RollField } from './rollField.mjs';
export { default as RollField } from './rollField.mjs';

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class BeastformField extends fields.SchemaField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const beastformFields = {
tierAccess: new fields.SchemaField({
exact: new fields.NumberField({ integer: true, nullable: true, initial: null })
@ -9,4 +9,4 @@ export default class BeastformField extends fields.SchemaField {
};
super(beastformFields, options, context);
}
}
}

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class CostField extends fields.ArrayField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const element = new fields.SchemaField({
key: new fields.StringField({
nullable: false,
@ -20,7 +20,7 @@ export default class CostField extends fields.ArrayField {
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
config.costs = CostField.calcCosts.call(this, costs);
const hasCost = CostField.hasCost.call(this, config.costs);
if(config.isFastForward && !hasCost)
if (config.isFastForward && !hasCost)
return ui.notifications.warn("You don't have the resources to use that action.");
return hasCost;
}
@ -79,4 +79,4 @@ export default class CostField extends fields.ArrayField {
const realCosts = costs?.length ? costs.filter(c => c.enabled) : [];
return realCosts;
}
}
}

View file

@ -1,4 +1,4 @@
import FormulaField from "../formulaField.mjs";
import FormulaField from '../formulaField.mjs';
const fields = foundry.data.fields;
@ -81,4 +81,4 @@ export class DHDamageData extends DHResourceData {
)
};
}
}
}

View file

@ -1,11 +1,11 @@
const fields = foundry.data.fields;
export default class EffectsField extends fields.ArrayField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const element = new fields.SchemaField({
_id: new fields.DocumentIdField(),
onSave: new fields.BooleanField({ initial: false })
});
super(element, options, context);
}
}
}

View file

@ -1,4 +1,4 @@
import { DHDamageData } from "./damageField.mjs";
import { DHDamageData } from './damageField.mjs';
const fields = foundry.data.fields;
@ -6,4 +6,4 @@ export default class HealingField extends fields.EmbeddedDataField {
constructor(options, context = {}) {
super(DHDamageData, options, context);
}
}
}

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class RangeField extends fields.StringField {
constructor(context={}) {
constructor(context = {}) {
const options = {
choices: CONFIG.DH.GENERAL.range,
required: false,
@ -13,4 +13,4 @@ export default class RangeField extends fields.StringField {
static prepareConfig(config) {
return true;
}
}
}

View file

@ -55,4 +55,4 @@ export default class RollField extends fields.EmbeddedDataField {
constructor(options, context = {}) {
super(DHActionRollData, options, context);
}
}
}

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class SaveField extends fields.SchemaField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const saveFields = {
trait: new fields.StringField({
nullable: true,
@ -16,4 +16,4 @@ export default class SaveField extends fields.SchemaField {
};
super(saveFields, options, context);
}
}
}

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class TargetField extends fields.SchemaField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const targetFields = {
type: new fields.StringField({
choices: CONFIG.DH.ACTIONS.targetTypes,
@ -26,13 +26,13 @@ export default class TargetField extends fields.SchemaField {
}
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);
if(config.isFastForward && !hasTargets)
if (config.isFastForward && !hasTargets)
return ui.notifications.warn('Too many targets selected for that actions.');
return hasTargets;
}
static checkTargets(amount, targets) {
return true
return true;
// return !amount || (targets.length > amount);
}
@ -59,4 +59,4 @@ export default class TargetField extends fields.SchemaField {
evasion: actor.actor.system.evasion
};
}
}
}

View file

@ -1,7 +1,7 @@
const fields = foundry.data.fields;
export default class UsesField extends fields.SchemaField {
constructor(options={}, context={}) {
constructor(options = {}, context = {}) {
const usesFields = {
value: new fields.NumberField({ nullable: true, initial: null }),
max: new fields.NumberField({ nullable: true, initial: null }),
@ -19,8 +19,7 @@ export default class UsesField extends fields.SchemaField {
if (uses && !uses.value) uses.value = 0;
config.uses = uses;
const hasUses = UsesField.hasUses.call(this, config.uses);
if(config.isFastForward && !hasUses)
return ui.notifications.warn("That action doesn't have remaining uses.");
if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses.");
return hasUses;
}
@ -36,4 +35,4 @@ export default class UsesField extends fields.SchemaField {
if (!uses) return true;
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
}
}
}

View file

@ -0,0 +1,19 @@
import ForeignDocumentUUIDField from './foreignDocumentUUIDField.mjs';
export default class ItemLinkFields extends foundry.data.fields.ArrayField {
constructor(options, context) {
super(new ItemLinkField(), options, context);
}
}
class ItemLinkField extends foundry.data.fields.SchemaField {
constructor(context) {
super(
{
type: new foundry.data.fields.StringField({ choices: CONFIG.DH.ITEM.featureSubTypes, nullable: true }),
item: new ForeignDocumentUUIDField({ type: 'Item' })
},
context
);
}
}

View file

@ -9,120 +9,120 @@
* by `options.initialKeys`?
*/
export default class MappingField extends foundry.data.fields.ObjectField {
constructor(model, options) {
if ( !(model instanceof foundry.data.fields.DataField) ) {
throw new Error("MappingField must have a DataField as its contained element");
constructor(model, options) {
if (!(model instanceof foundry.data.fields.DataField)) {
throw new Error('MappingField must have a DataField as its contained element');
}
super(options);
/**
* The embedded DataField definition which is contained in this field.
* @type {DataField}
*/
this.model = model;
model.parent = this;
}
super(options);
/* -------------------------------------------- */
/** @inheritDoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
initialKeys: null,
initialValue: null,
initialKeysOnly: false
});
}
/* -------------------------------------------- */
/** @inheritDoc */
_cleanType(value, options) {
Object.entries(value).forEach(([k, v]) => {
if (k.startsWith('-=')) return;
value[k] = this.model.clean(v, options);
});
return value;
}
/* -------------------------------------------- */
/** @inheritDoc */
getInitialValue(data) {
let keys = this.initialKeys;
const initial = super.getInitialValue(data);
if (!keys || !foundry.utils.isEmpty(initial)) return initial;
if (!(keys instanceof Array)) keys = Object.keys(keys);
for (const key of keys) initial[key] = this._getInitialValueForKey(key);
return initial;
}
/* -------------------------------------------- */
/**
* The embedded DataField definition which is contained in this field.
* @type {DataField}
* Get the initial value for the provided key.
* @param {string} key Key within the object being built.
* @param {object} [object] Any existing mapping data.
* @returns {*} Initial value based on provided field type.
*/
this.model = model;
model.parent = this;
}
/* -------------------------------------------- */
/** @inheritDoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
initialKeys: null,
initialValue: null,
initialKeysOnly: false
});
}
/* -------------------------------------------- */
/** @inheritDoc */
_cleanType(value, options) {
Object.entries(value).forEach(([k, v]) => {
if ( k.startsWith("-=") ) return;
value[k] = this.model.clean(v, options);
});
return value;
}
/* -------------------------------------------- */
/** @inheritDoc */
getInitialValue(data) {
let keys = this.initialKeys;
const initial = super.getInitialValue(data);
if ( !keys || !foundry.utils.isEmpty(initial) ) return initial;
if ( !(keys instanceof Array) ) keys = Object.keys(keys);
for ( const key of keys ) initial[key] = this._getInitialValueForKey(key);
return initial;
}
/* -------------------------------------------- */
/**
* Get the initial value for the provided key.
* @param {string} key Key within the object being built.
* @param {object} [object] Any existing mapping data.
* @returns {*} Initial value based on provided field type.
*/
_getInitialValueForKey(key, object) {
const initial = this.model.getInitialValue();
return this.initialValue?.(key, initial, object) ?? initial;
}
/* -------------------------------------------- */
/** @override */
_validateType(value, options={}) {
if ( foundry.utils.getType(value) !== "Object" ) throw new Error("must be an Object");
const errors = this._validateValues(value, options);
if ( !foundry.utils.isEmpty(errors) ) {
const failure = new foundry.data.validation.DataModelValidationFailure();
failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure }));
throw failure.asError();
_getInitialValueForKey(key, object) {
const initial = this.model.getInitialValue();
return this.initialValue?.(key, initial, object) ?? initial;
}
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/**
* Validate each value of the object.
* @param {object} value The object to validate.
* @param {object} options Validation options.
* @returns {Record<string, Error>} An object of value-specific errors by key.
*/
_validateValues(value, options) {
const errors = {};
for ( const [k, v] of Object.entries(value) ) {
if ( k.startsWith("-=") ) continue;
const error = this.model.validate(v, options);
if ( error ) errors[k] = error;
/** @override */
_validateType(value, options = {}) {
if (foundry.utils.getType(value) !== 'Object') throw new Error('must be an Object');
const errors = this._validateValues(value, options);
if (!foundry.utils.isEmpty(errors)) {
const failure = new foundry.data.validation.DataModelValidationFailure();
failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure }));
throw failure.asError();
}
}
return errors;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @override */
initialize(value, model, options={}) {
if ( !value ) return value;
const obj = {};
const initialKeys = (this.initialKeys instanceof Array) ? this.initialKeys : Object.keys(this.initialKeys ?? {});
const keys = this.initialKeysOnly ? initialKeys : Object.keys(value);
for ( const key of keys ) {
const data = value[key] ?? this._getInitialValueForKey(key, value);
obj[key] = this.model.initialize(data, model, options);
/**
* Validate each value of the object.
* @param {object} value The object to validate.
* @param {object} options Validation options.
* @returns {Record<string, Error>} An object of value-specific errors by key.
*/
_validateValues(value, options) {
const errors = {};
for (const [k, v] of Object.entries(value)) {
if (k.startsWith('-=')) continue;
const error = this.model.validate(v, options);
if (error) errors[k] = error;
}
return errors;
}
return obj;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @inheritDoc */
_getField(path) {
if ( path.length === 0 ) return this;
else if ( path.length === 1 ) return this.model;
path.shift();
return this.model._getField(path);
}
}
/** @override */
initialize(value, model, options = {}) {
if (!value) return value;
const obj = {};
const initialKeys = this.initialKeys instanceof Array ? this.initialKeys : Object.keys(this.initialKeys ?? {});
const keys = this.initialKeysOnly ? initialKeys : Object.keys(value);
for (const key of keys) {
const data = value[key] ?? this._getInitialValueForKey(key, value);
obj[key] = this.model.initialize(data, model, options);
}
return obj;
}
/* -------------------------------------------- */
/** @inheritDoc */
_getField(path) {
if (path.length === 0) return this;
else if (path.length === 1) return this.model;
path.shift();
return this.model._getField(path);
}
}

View file

@ -1,5 +1,5 @@
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import BaseDataItem from './base.mjs';
import ItemLinkFields from '../../data/fields/itemLinkFields.mjs';
export default class DHAncestry extends BaseDataItem {
/** @inheritDoc */
@ -15,23 +15,15 @@ export default class DHAncestry extends BaseDataItem {
static defineSchema() {
return {
...super.defineSchema(),
features: new ForeignDocumentUUIDArrayField({ type: 'Item' })
features: new ItemLinkFields()
};
}
get primaryFeature() {
return (
this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.primary) ??
(this.features.filter(x => !x).length > 0 ? {} : null)
);
return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.primary)?.item;
}
get secondaryFeature() {
return (
this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.secondary) ??
(this.features.filter(x => !x || x.system.subType === CONFIG.DH.ITEM.featureSubTypes.primary).length > 1
? {}
: null)
);
return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.secondary)?.item;
}
}

View file

@ -8,7 +8,8 @@
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
*/
import { ActionsField } from "../fields/actionField.mjs";
import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs';
import { ActionsField } from '../fields/actionField.mjs';
const fields = foundry.data.fields;
@ -72,8 +73,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
if (this.metadata.isQuantifiable)
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
if (this.metadata.hasActions)
schema.actions = new ActionsField()
if (this.metadata.hasActions) schema.actions = new ActionsField();
return schema;
}
@ -124,18 +124,21 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
}
}
_onCreate(data) {
_onCreate(data, _, userId) {
if (userId !== game.user.id) return;
if (!this.actor || this.actor.type !== 'character' || !this.features) return;
this.actor.createEmbeddedDocuments(
'Item',
this.features.map(feature => ({
...feature,
...(feature.item ?? feature),
system: {
...feature.system,
...(feature.item?.system ?? feature.system),
originItemType: this.parent.type,
originId: data._id,
identifier: feature.identifier
identifier: feature.identifier,
subType: feature.item ? feature.type : undefined
}
}))
);
@ -151,4 +154,17 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
items.map(x => x.id)
);
}
async _preUpdate(changed, options, userId) {
const allowed = await super._preUpdate(changed, options, userId);
if (allowed === false) return false;
addLinkedItemsDiff(changed.system?.features, this.features, options);
}
_onUpdate(changed, options, userId) {
super._onUpdate(changed, options, userId);
updateLinkedItemApps(options, this.parent.sheet);
}
}

View file

@ -149,7 +149,9 @@ export default class DHBeastform extends BaseDataItem {
return false;
}
_onCreate() {
_onCreate(_data, _options, userId) {
if (userId !== game.user.id) return;
this.parent.createEmbeddedDocuments('ActiveEffect', [
{
type: 'beastform',

View file

@ -1,6 +1,8 @@
import BaseDataItem from './base.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import ItemLinkFields from '../fields/itemLinkFields.mjs';
import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs';
export default class DHClass extends BaseDataItem {
/** @inheritDoc */
@ -27,7 +29,7 @@ export default class DHClass extends BaseDataItem {
label: 'DAGGERHEART.GENERAL.HitPoints.plural'
}),
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
features: new ItemLinkFields(),
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
inventory: new fields.SchemaField({
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
@ -52,17 +54,11 @@ export default class DHClass extends BaseDataItem {
}
get hopeFeatures() {
return (
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.hope) ??
(this.features.filter(x => !x).length > 0 ? {} : null)
);
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.hope).map(x => x.item);
}
get classFeatures() {
return (
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.class) ??
(this.features.filter(x => !x).length > 0 ? {} : null)
);
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.class).map(x => x.item);
}
async _preCreate(data, options, user) {
@ -80,6 +76,9 @@ export default class DHClass extends BaseDataItem {
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (userId !== game.user.id) return;
if (options.parent?.type === 'character') {
const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`;
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
@ -98,4 +97,33 @@ export default class DHClass extends BaseDataItem {
foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete();
}
}
async _preUpdate(changed, options, userId) {
const allowed = await super._preUpdate(changed, options, userId);
if (allowed === false) return false;
const paths = [
'subclasses',
'characterGuide.suggestedPrimaryWeapon',
'characterGuide.suggestedSecondaryWeapon',
'characterGuide.suggestedArmor',
'inventory.take',
'inventory.choiceA',
'inventory.choiceB'
];
for (let path of paths) {
const currentItems = [].concat(foundry.utils.getProperty(this, path) ?? []);
const changedItems = [].concat(foundry.utils.getProperty(changed, `system.${path}`) ?? []);
if (!changedItems.length) continue;
addLinkedItemsDiff(changedItems, currentItems, options);
}
}
_onUpdate(changed, options, userId) {
super._onUpdate(changed, options, userId);
updateLinkedItemApps(options, this.parent.sheet);
}
}

View file

@ -1,4 +1,4 @@
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import ItemLinkFields from '../fields/itemLinkFields.mjs';
import BaseDataItem from './base.mjs';
export default class DHSubclass extends BaseDataItem {
@ -22,22 +22,22 @@ export default class DHSubclass extends BaseDataItem {
nullable: true,
initial: null
}),
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
features: new ItemLinkFields(),
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
isMulticlass: new fields.BooleanField({ initial: false })
};
}
get foundationFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.foundation);
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.foundation).map(x => x.item);
}
get specializationFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.specialization);
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.specialization).map(x => x.item);
}
get masteryFeatures() {
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.mastery);
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.mastery).map(x => x.item);
}
async _preCreate(data, options, user) {
@ -67,6 +67,8 @@ export default class DHSubclass extends BaseDataItem {
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (userId !== game.user.id) return;
if (options.parent?.type === 'character') {
const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });