[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

@ -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);
}
}