FEAT: BaseDataItem class

add TODO comments for future improvements
FIX: Remove effect field on template
FIX: remove unused DhpEffects file
This commit is contained in:
Joaquin Pereyra 2025-05-30 20:31:34 -03:00
parent 5a2c69d48f
commit ec7f32cdc7
14 changed files with 298 additions and 201 deletions

View file

@ -1,85 +0,0 @@
import DaggerheartAction from '../action.mjs';
export default class DhpEffects extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
effects: new fields.TypedObjectField(
new fields.SchemaField({
type: new fields.StringField({ choices: Object.keys(SYSTEM.EFFECTS.effectTypes) }),
valueType: new fields.StringField({ choices: Object.keys(SYSTEM.EFFECTS.valueTypes) }),
parseType: new fields.StringField({ choices: Object.keys(SYSTEM.EFFECTS.parseTypes) }),
initiallySelected: new fields.BooleanField({ initial: true }),
options: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
value: new fields.StringField({})
}),
{ nullable: true, initial: null }
),
dataField: new fields.StringField({}),
appliesOn: new fields.StringField(
{ choices: Object.keys(SYSTEM.EFFECTS.applyLocations) },
{ nullable: true, initial: null }
),
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
nullable: true,
initial: null
}),
valueData: new fields.SchemaField({
value: new fields.StringField({}),
fromValue: new fields.StringField({ initial: null, nullable: true }),
type: new fields.StringField({ initial: null, nullable: true }),
hopeIncrease: new fields.StringField({ initial: null, nullable: true })
})
})
),
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
// actions: new fields.SchemaField({
// damage: new fields.ArrayField(new fields.SchemaField({
// type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.extendedDamageTypes), initial: SYSTEM.GENERAL.extendedDamageTypes.physical.id }),
// value: new fields.StringField({}),
// })),
// uses: new fields.SchemaField({
// nr: new fields.StringField({}),
// refreshType: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.refreshTypes), initial: SYSTEM.GENERAL.refreshTypes.session.id }),
// refreshed: new fields.BooleanField({ initial: true }),
// }),
// }),
};
}
get effectData() {
const effectValues = Object.values(this.effects);
const effectCategories = Object.keys(SYSTEM.EFFECTS.effectTypes).reduce((acc, effectType) => {
acc[effectType] = effectValues.reduce((acc, effect) => {
if (effect.type === effectType) {
acc.push({ ...effect, valueData: this.#parseValues(effect.parseType, effect.valueData) });
}
return acc;
}, []);
return acc;
}, {});
return effectCategories;
}
#parseValues(parseType, values) {
return Object.keys(values).reduce((acc, prop) => {
acc[prop] = this.#parseValue(parseType, values[prop]);
return acc;
}, {});
}
#parseValue(parseType, value) {
switch (parseType) {
case SYSTEM.EFFECTS.parseTypes.number.id:
return Number.parseInt(value);
default:
return value;
}
}
}

View file

@ -1,11 +1,22 @@
import featuresSchema from '../interface/featuresSchema.mjs';
import BaseDataItem from './base.mjs';
export default class DHAncestry extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.ancestry",
type: "ancestry",
hasDescription: true,
});
}
export default class DHAncestry extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({}),
...super.defineSchema(),
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
abilities: featuresSchema()
};
}

View file

@ -1,16 +1,24 @@
import BaseDataItem from './base.mjs';
export default class DHArmor extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.armor",
type: "armor",
hasDescription: true,
isQuantifiable: true,
});
}
export default class DHArmor extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
equipped: new fields.BooleanField({ initial: false }),
baseScore: new fields.NumberField({ integer: true, initial: 0 }),
feature: new fields.StringField({
choices: SYSTEM.ITEM.armorFeatures,
integer: false,
blank: true
}),
feature: new fields.StringField({ choices: SYSTEM.ITEM.armorFeatures, blank: true }),
marks: new fields.SchemaField({
max: new fields.NumberField({ initial: 6, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true })
@ -19,8 +27,6 @@ export default class DHArmor extends foundry.abstract.TypeDataModel {
major: new fields.NumberField({ integer: true, initial: 0 }),
severe: new fields.NumberField({ integer: true, initial: 0 })
}),
quantity: new fields.NumberField({ integer: true, initial: 1 }),
description: new fields.HTMLField({})
};
}

View file

@ -0,0 +1,32 @@
/**
* @typedef {Object} ItemDataModelMetadata
* @property {String} type - System type that this type data model represents
* @property {Boolean} hasDescription
*/
const fields = foundry.data.fields;
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
/** @returns {ItemDataModelMetadata}*/
static get metadata() {
return {
label: "Base Item",
type: "base",
hasDescription: false,
isQuantifiable: false
};
}
/** @inheritDoc */
static defineSchema() {
const schema = {};
if (this.metadata.hasDescription)
schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.isQuantifiable)
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
return schema;
}
}

View file

@ -1,12 +1,28 @@
import { getTier } from '../../helpers/utils.mjs';
import BaseDataItem from './base.mjs';
export default class DHClass extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.class",
type: "class",
hasDescription: true,
});
}
export default class DHClass extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
//TODO: USE SetField instead ArrayField
//set choices for this field
domains: new fields.ArrayField(new fields.StringField({})),
classItems: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
@ -15,13 +31,17 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
),
evasion: new fields.NumberField({ initial: 0, integer: true }),
features: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
uuid: new fields.StringField({})
})
),
//TODO: use SetField intead of ArrayField
subclasses: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
@ -30,6 +50,7 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
),
inventory: new fields.SchemaField({
take: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
@ -37,6 +58,7 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
})
),
choiceA: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
@ -44,6 +66,7 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
})
),
choiceB: new fields.ArrayField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
new fields.SchemaField({
name: new fields.StringField({}),
img: new fields.StringField({}),
@ -51,6 +74,7 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
})
),
extra: new fields.SchemaField(
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
{
title: new fields.StringField({}),
description: new fields.StringField({})
@ -91,18 +115,14 @@ export default class DHClass extends foundry.abstract.TypeDataModel {
},
{ initial: null, nullable: true }
),
characterDescription: new fields.SchemaField({
clothes: new fields.StringField({}),
eyes: new fields.StringField({}),
body: new fields.StringField({}),
color: new fields.StringField({}),
attitude: new fields.StringField({})
}),
//FIXME this not will work
backgroundQuestions: new fields.ArrayField(new fields.StringField({}), { initial: ['', '', ''] }),
//FIXME this not will work
connections: new fields.ArrayField(new fields.StringField({}), { initial: ['', '', ''] })
}),
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
description: new fields.HTMLField({})
};
}

View file

@ -1,11 +1,23 @@
import featuresSchema from '../interface/featuresSchema.mjs';
import BaseDataItem from './base.mjs';
export default class DHCommunity extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.community",
type: "community",
hasDescription: true,
});
}
export default class DHCommunity extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({}),
...super.defineSchema(),
//TODO: use DocumentUUIDField, DocumentIdField or create LocalDocumentField
abilities: featuresSchema()
};
}

View file

@ -1,10 +1,21 @@
export default class DHConsumable extends foundry.abstract.TypeDataModel {
import BaseDataItem from "./base.mjs";
export default class DHConsumable extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.consumable",
type: "consumable",
hasDescription: true,
isQuantifiable: true,
});
}
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({}),
quantity: new fields.NumberField({ initial: 1, integer: true }),
...super.defineSchema(),
consumeOnUse: new fields.BooleanField({ initial: false })
};
}

View file

@ -1,10 +1,21 @@
import DaggerheartAction from "../action.mjs";
import BaseDataItem from "./base.mjs";
export default class DHDomainCard extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.domainCard",
type: "domainCard",
hasDescription: true,
});
}
export default class DHDomainCard extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
domain: new fields.StringField(
{ choices: SYSTEM.DOMAIN.domains, integer: false },
{ required: true, initial: [] }
@ -16,7 +27,6 @@ export default class DHDomainCard extends foundry.abstract.TypeDataModel {
{ required: true, initial: [] }
),
foundation: new fields.BooleanField({ initial: false }),
effect: new fields.HTMLField({}),
inVault: new fields.BooleanField({ initial: false }),
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
};

View file

@ -1,83 +1,91 @@
import { getTier } from '../../helpers/utils.mjs';
import DaggerheartAction from '../action.mjs';
import DhpEffects from '../interface/effects.mjs';
import BaseDataItem from './base.mjs';
export default class DHFeature extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.feature",
type: "feature",
hasDescription: true,
});
}
export default class DHFeature extends DhpEffects {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return foundry.utils.mergeObject(
{},
{
type: new fields.StringField({ choices: SYSTEM.ITEM.featureTypes }),
actionType: new fields.StringField({
choices: SYSTEM.ITEM.actionTypes,
initial: SYSTEM.ITEM.actionTypes.passive.id
console.log(SYSTEM.EFFECTS.applyLocations)
return {
...super.defineSchema(),
type: new fields.StringField({ choices: SYSTEM.ITEM.featureTypes }),
actionType: new fields.StringField({
choices: SYSTEM.ITEM.actionTypes,
initial: SYSTEM.ITEM.actionTypes.passive.id
}),
featureType: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ITEM.valueTypes,
initial: Object.keys(SYSTEM.ITEM.valueTypes).find(x => x === 'normal')
}),
featureType: new fields.SchemaField({
type: new fields.StringField({
choices: SYSTEM.ITEM.valueTypes,
initial: Object.keys(SYSTEM.ITEM.valueTypes).find(x => x === 'normal')
data: new fields.SchemaField({
value: new fields.StringField({}),
property: new fields.StringField({
choices: SYSTEM.ACTOR.featureProperties,
initial: Object.keys(SYSTEM.ACTOR.featureProperties).find(x => x === 'spellcastingTrait')
}),
data: new fields.SchemaField({
value: new fields.StringField({}),
property: new fields.StringField({
choices: SYSTEM.ACTOR.featureProperties,
initial: Object.keys(SYSTEM.ACTOR.featureProperties).find(x => x === 'spellcastingTrait')
}),
max: new fields.NumberField({ initial: 1, integer: true }),
numbers: new fields.TypedObjectField(
new fields.SchemaField({
value: new fields.NumberField({ integer: true }),
used: new fields.BooleanField({ initial: false })
})
)
})
}),
refreshData: new fields.SchemaField(
{
type: new fields.StringField({ choices: SYSTEM.GENERAL.refreshTypes }),
uses: new fields.NumberField({ initial: 1, integer: true }),
refreshed: new fields.BooleanField({ initial: true })
},
{ nullable: true, initial: null }
),
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
disabled: new fields.BooleanField({ initial: false }),
description: new fields.HTMLField({}),
effects: new fields.TypedObjectField(
new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.EFFECTS.effectTypes }),
valueType: new fields.StringField({ choices: SYSTEM.EFFECTS.valueTypes }),
parseType: new fields.StringField({ choices: SYSTEM.EFFECTS.parseTypes }),
initiallySelected: new fields.BooleanField({ initial: true }),
options: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
value: new fields.StringField({})
}),
{ nullable: true, initial: null }
),
dataField: new fields.StringField({}),
appliesOn: new fields.StringField(
{ choices: SYSTEM.EFFECTS.applyLocations },
{ nullable: true, initial: null }
),
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
nullable: true,
initial: null
}),
valueData: new fields.SchemaField({
value: new fields.StringField({}),
fromValue: new fields.StringField({ initial: null, nullable: true }),
type: new fields.StringField({ initial: null, nullable: true }),
hopeIncrease: new fields.StringField({ initial: null, nullable: true })
max: new fields.NumberField({ initial: 1, integer: true }),
numbers: new fields.TypedObjectField(
new fields.SchemaField({
value: new fields.NumberField({ integer: true }),
used: new fields.BooleanField({ initial: false })
})
)
})
}),
refreshData: new fields.SchemaField(
{
type: new fields.StringField({ choices: SYSTEM.GENERAL.refreshTypes }),
uses: new fields.NumberField({ initial: 1, integer: true }),
refreshed: new fields.BooleanField({ initial: true })
},
{ nullable: true, initial: null }
),
multiclass: new fields.NumberField({ initial: null, nullable: true, integer: true }),
disabled: new fields.BooleanField({ initial: false }),
effects: new fields.TypedObjectField(
new fields.SchemaField({
type: new fields.StringField({ choices: SYSTEM.EFFECTS.effectTypes }),
valueType: new fields.StringField({ choices: SYSTEM.EFFECTS.valueTypes }),
parseType: new fields.StringField({ choices: SYSTEM.EFFECTS.parseTypes }),
initiallySelected: new fields.BooleanField({ initial: true }),
options: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({}),
value: new fields.StringField({})
}),
{ nullable: true, initial: null }
),
dataField: new fields.StringField({}),
appliesOn: new fields.StringField({
choices: SYSTEM.EFFECTS.applyLocations,
},
{ nullable: true, initial: null }
),
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
nullable: true,
initial: null
}),
valueData: new fields.SchemaField({
value: new fields.StringField({}),
fromValue: new fields.StringField({ initial: null, nullable: true }),
type: new fields.StringField({ initial: null, nullable: true }),
hopeIncrease: new fields.StringField({ initial: null, nullable: true })
})
),
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
}
);
})
),
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
};
}
get multiclassTier() {
@ -97,4 +105,38 @@ export default class DHFeature extends DhpEffects {
}
}
}
get effectData() {
const effectValues = Object.values(this.effects);
const effectCategories = Object.keys(SYSTEM.EFFECTS.effectTypes).reduce((acc, effectType) => {
acc[effectType] = effectValues.reduce((acc, effect) => {
if (effect.type === effectType) {
acc.push({ ...effect, valueData: this.#parseValues(effect.parseType, effect.valueData) });
}
return acc;
}, []);
return acc;
}, {});
return effectCategories;
}
#parseValues(parseType, values) {
return Object.keys(values).reduce((acc, prop) => {
acc[prop] = this.#parseValue(parseType, values[prop]);
return acc;
}, {});
}
#parseValue(parseType, value) {
switch (parseType) {
case SYSTEM.EFFECTS.parseTypes.number.id:
return Number.parseInt(value);
default:
return value;
}
}
}

View file

@ -1,10 +1,21 @@
export default class DHMiscellaneous extends foundry.abstract.TypeDataModel {
import BaseDataItem from './base.mjs';
export default class DHMiscellaneous extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.miscellaneous",
type: "miscellaneous",
hasDescription: true,
isQuantifiable: true,
});
}
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({}),
quantity: new fields.NumberField({ initial: 1, integer: true })
...super.defineSchema(),
};
}
}

View file

@ -1,11 +1,21 @@
import { getTier } from '../../helpers/utils.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,
});
}
export default class DHSubclass extends foundry.abstract.TypeDataModel {
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({}),
...super.defineSchema(),
spellcastingTrait: new fields.StringField({
choices: SYSTEM.ACTOR.abilities,
integer: false,

View file

@ -1,25 +1,45 @@
export default class DHWeapon extends foundry.abstract.TypeDataModel {
import BaseDataItem from "./base.mjs";
export default class DHWeapon extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.weapon",
type: "weapon",
hasDescription: true,
isQuantifiable: true,
});
}
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
equipped: new fields.BooleanField({ initial: false }),
//TODO: NEED REVISION
//It makes more sense to control the inventory from the actor
inventoryWeapon: new fields.NumberField({ initial: null, nullable: true, integer: true }),
//SETTINGS
secondary: new fields.BooleanField({ initial: false }),
trait: new fields.StringField({ choices: SYSTEM.ACTOR.abilities, integer: false, initial: 'agility' }),
range: new fields.StringField({ choices: SYSTEM.GENERAL.range, integer: false, initial: 'melee' }),
trait: new fields.StringField({ required: true, choices: SYSTEM.ACTOR.abilities, initial: 'agility' }),
range: new fields.StringField({ required: true, choices: SYSTEM.GENERAL.range, initial: 'melee' }),
burden: new fields.StringField({ required: true, choices: SYSTEM.GENERAL.burden, initial: 'oneHanded' }),
//DAMAGE
damage: new fields.SchemaField({
//TODO: CREATE FORMULA FIELD
value: new fields.StringField({ initial: 'd6' }),
type: new fields.StringField({
required: true,
choices: SYSTEM.GENERAL.damageTypes,
integer: false,
initial: 'physical'
})
}),
burden: new fields.StringField({ choices: SYSTEM.GENERAL.burden, integer: false, initial: 'oneHanded' }),
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, integer: false, blank: true }),
quantity: new fields.NumberField({ initial: 1, integer: true }),
description: new fields.HTMLField({})
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
};
}
@ -31,9 +51,10 @@ export default class DHWeapon extends foundry.abstract.TypeDataModel {
applyEffects() {
const effects = this.parent.parent.system.effects;
for (var key in effects) {
for (const key in effects) {
console.log
const effectType = effects[key];
for (var effect of effectType) {
for (const effect of effectType) {
switch (key) {
case SYSTEM.EFFECTS.effectTypes.reach.id:
if (
@ -44,10 +65,6 @@ export default class DHWeapon extends foundry.abstract.TypeDataModel {
}
break;
// case SYSTEM.EFFECTS.effectTypes.damage.id:
// if(this.damage.type === 'physical') this.damage.value = (`${this.damage.value} + ${this.parent.parent.system.levelData.currentLevel}`);
// break;
}
}
}

View file

@ -348,6 +348,7 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
}
//Should not be done in data?
//TODO: REMOVE THIS
#weaponData(weapon) {
return weapon
? {