Merged main

This commit is contained in:
WBHarry 2026-03-13 00:25:50 +01:00
commit b87e630a0a
84 changed files with 2046 additions and 452 deletions

View file

@ -7,6 +7,7 @@ import EffectAction from './effectAction.mjs';
import HealingAction from './healingAction.mjs';
import MacroAction from './macroAction.mjs';
import SummonAction from './summonAction.mjs';
import TransformAction from './transformAction.mjs';
export const actionsTypes = {
base: BaseAction,
@ -17,5 +18,6 @@ export const actionsTypes = {
summon: SummonAction,
effect: EffectAction,
macro: MacroAction,
beastform: BeastformAction
beastform: BeastformAction,
transform: TransformAction
};

View file

@ -197,7 +197,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
async executeWorkflow(config) {
for (const [key, part] of this.workflow) {
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
if ((await part.execute(config)) === false) return;
if ((await part.execute(config)) === false) return false;
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
}
}
@ -224,7 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
// Execute the Action Worflow in order based of schema fields
await this.executeWorkflow(config);
const result = await this.executeWorkflow(config);
if (result === false) return;
await config.resourceUpdates.updateResources();
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;

View file

@ -0,0 +1,5 @@
import DHBaseAction from './baseAction.mjs';
export default class DHTransformAction extends DHBaseAction {
static extraSchemas = [...super.extraSchemas, 'transform'];
}

View file

@ -2,7 +2,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-set
import { ActionField } from '../fields/actionField.mjs';
import { commonActorRules } from './base.mjs';
import DhCreature from './creature.mjs';
import { resourceField, bonusField } from '../fields/actorField.mjs';
import { bonusField } from '../fields/actorField.mjs';
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
@ -65,10 +65,6 @@ export default class DhpAdversary extends DhCreature {
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
})
}),
resources: new fields.SchemaField({
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
}),
rules: new fields.SchemaField({
...commonActorRules()
}),
@ -191,6 +187,7 @@ export default class DhpAdversary extends DhCreature {
}
prepareDerivedData() {
super.prepareDerivedData();
this.attack.roll.isStandardAttack = true;
}

View file

@ -211,7 +211,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
const resource = changes.system.resources[key];
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
acc.push(getScrollTextData(this.resources, resource, key));
acc.push(getScrollTextData(this.parent, resource, key));
}
return acc;

View file

@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import DhLevelData from '../levelData.mjs';
import { commonActorRules } from './base.mjs';
import DhCreature from './creature.mjs';
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
import { ActionField } from '../fields/actionField.mjs';
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
@ -27,28 +27,6 @@ export default class DhCharacter extends DhCreature {
return {
...super.defineSchema(),
resources: new fields.SchemaField({
hitPoints: resourceField(
0,
0,
'DAGGERHEART.GENERAL.HitPoints.plural',
true,
'DAGGERHEART.ACTORS.Character.maxHPBonus'
),
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.SchemaField(
{
value: new fields.NumberField({
initial: 2,
min: 0,
integer: true,
label: 'DAGGERHEART.GENERAL.hope'
}),
isReversed: new fields.BooleanField({ initial: false })
},
{ label: 'DAGGERHEART.GENERAL.hope' }
)
}),
traits: new fields.SchemaField({
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
@ -609,6 +587,7 @@ export default class DhCharacter extends DhCreature {
}
prepareBaseData() {
super.prepareBaseData();
this.evasion += this.class.value?.system?.evasion ?? 0;
const currentLevel = this.levelData.level.current;
@ -680,6 +659,7 @@ export default class DhCharacter extends DhCreature {
}
prepareDerivedData() {
super.prepareDerivedData();
let baseHope = this.resources.hope.value;
if (this.companion) {
for (let levelKey in this.companion.system.levelData.levelups) {
@ -699,6 +679,7 @@ export default class DhCharacter extends DhCreature {
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
this.resources.armor = {
label: 'DAGGERHEART.GENERAL.armor',
value: this.armor?.system?.marks?.value ?? 0,
max: this.armorScore,
isReversed: true
@ -740,7 +721,8 @@ export default class DhCharacter extends DhCreature {
const newHopeMax = this.system.resources.hope.max + diff;
const newHopeValue = Math.min(newHopeMax, this.system.resources.hope.value);
if (newHopeValue != this.system.resources.hope.value) {
if (!changes.system.resources) changes.system.resources = { hope: { value: 0 } };
if (!changes.system.resources.hope) changes.system.resources.hope = { value: 0 };
changes.system.resources.hope = {
...changes.system.resources.hope,
value: changes.system.resources.hope.value + newHopeValue

View file

@ -4,7 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import { ActionField } from '../fields/actionField.mjs';
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
import { resourceField, bonusField } from '../fields/actorField.mjs';
import { bonusField } from '../fields/actorField.mjs';
export default class DhCompanion extends DhCreature {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
@ -26,10 +26,6 @@ export default class DhCompanion extends DhCreature {
return {
...super.defineSchema(),
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
resources: new fields.SchemaField({
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
}),
evasion: new fields.NumberField({
required: true,
min: 1,
@ -127,6 +123,7 @@ export default class DhCompanion extends DhCreature {
}
prepareBaseData() {
super.prepareBaseData();
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
for (let levelKey in this.levelData.levelups) {
@ -163,6 +160,7 @@ export default class DhCompanion extends DhCreature {
}
prepareDerivedData() {
super.prepareDerivedData();
/* Partner Related Setup */
if (this.partner) {
this.levelData.level.changed = this.partner.system.levelData.level.current;

View file

@ -1,3 +1,4 @@
import { ResourcesField } from '../fields/actorField.mjs';
import BaseDataActor from './base.mjs';
export default class DhCreature extends BaseDataActor {
@ -7,6 +8,7 @@ export default class DhCreature extends BaseDataActor {
return {
...super.defineSchema(),
resources: new ResourcesField(this.metadata.type),
advantageSources: new fields.ArrayField(new fields.StringField(), {
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'

View file

@ -10,3 +10,4 @@ export { default as DamageField } from './damageField.mjs';
export { default as RollField } from './rollField.mjs';
export { default as MacroField } from './macroField.mjs';
export { default as SummonField } from './summonField.mjs';
export { default as TransformField } from './transformField.mjs';

View file

@ -0,0 +1,103 @@
const fields = foundry.data.fields;
export default class DHSummonField extends fields.SchemaField {
/**
* Action Workflow order
*/
static order = 130;
constructor(options = {}, context = {}) {
const transformFields = {
actorUUID: new fields.DocumentUUIDField({
type: 'Actor',
required: true
}),
resourceRefresh: new fields.SchemaField({
hitPoints: new fields.BooleanField({ initial: true }),
stress: new fields.BooleanField({ initial: true })
})
};
super(transformFields, options, context);
}
static async execute() {
if (!this.transform.actorUUID) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.noTransformActor'));
return false;
}
const baseActor = await foundry.utils.fromUuid(this.transform.actorUUID);
if (!baseActor) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.transformActorMissing'));
return false;
}
if (!canvas.scene) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.canvasError'));
return false;
}
if (this.actor.prototypeToken.actorLink) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.actorLinkError'));
return false;
}
if (!this.actor.token) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.prototypeError'));
return false;
}
const actor = await DHSummonField.getWorldActor(baseActor);
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
const tokenSize = actor?.system.metadata.usesSize ? tokenSizes[actor.system.size] : actor.prototypeToken.width;
await this.actor.token.update(
{ ...actor.prototypeToken.toJSON(), actorId: actor.id, width: tokenSize, height: tokenSize },
{ diff: false, recursive: false, noHook: true }
);
if (this.actor.token.combatant) {
this.actor.token.combatant.update({ actorId: actor.id, img: actor.prototypeToken.texture.src });
}
const marks = { hitPoints: 0, stress: 0 };
if (!this.transform.resourceRefresh.hitPoints) {
marks.hitPoints = Math.min(
this.actor.system.resources.hitPoints.value,
this.actor.token.actor.system.resources.hitPoints.max - 1
);
}
if (!this.transform.resourceRefresh.stress) {
marks.stress = Math.min(
this.actor.system.resources.stress.value,
this.actor.token.actor.system.resources.stress.max - 1
);
}
if (marks.hitPoints || marks.stress) {
this.actor.token.actor.update({
'system.resources': {
hitPoints: { value: marks.hitPoints },
stress: { value: marks.stress }
}
});
}
const prevPosition = { ...this.actor.sheet.position };
this.actor.sheet.close();
this.actor.token.actor.sheet.render({ force: true, position: prevPosition });
}
/* Check for any available instances of the actor present in the world, or create a world actor based on compendium */
static async getWorldActor(baseActor) {
if (!baseActor.inCompendium) return baseActor;
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
if (dataType && baseActor.img === dataType.DEFAULT_ICON) {
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
if (worldActorCopy) return worldActorCopy;
}
const worldActor = await game.system.api.documents.DhpActor.create(baseActor.toObject());
return worldActor;
}
}

View file

@ -6,22 +6,6 @@ const attributeField = label =>
tierMarked: new fields.BooleanField({ initial: false })
});
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
new fields.SchemaField(
{
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
max: new fields.NumberField({
initial: max,
integer: true,
label:
maxLabel ??
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
}),
isReversed: new fields.BooleanField({ initial: reverse })
},
{ label }
);
const stressDamageReductionRule = localizationPath =>
new fields.SchemaField({
cost: new fields.NumberField({
@ -37,4 +21,67 @@ const bonusField = label =>
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
});
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
/**
* Field used for actor resources. It is a resource that validates dynamically based on the config.
* Because "max" may be defined during runtime, we don't attempt to clamp the maximum value.
*/
class ResourcesField extends fields.TypedObjectField {
constructor(actorType) {
super(
new fields.SchemaField({
value: new fields.NumberField({ min: 0, initial: 0, integer: true }),
// Some resources allow changing max. A null max means its the default
max: new fields.NumberField({ initial: null, integer: true, nullable: true })
})
);
this.actorType = actorType;
}
getInitialValue() {
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
return Object.values(resources).reduce((result, resource) => {
result[resource.id] = {
value: resource.initial,
max: null
};
return result;
}, {});
}
_validateKey(key) {
return key in CONFIG.DH.RESOURCE[this.actorType].all;
}
_cleanType(value, options) {
value = super._cleanType(value, options);
// If not partial, ensure all data exists
if (!options.partial) {
value = foundry.utils.mergeObject(this.getInitialValue(), value);
}
return value;
}
/** Initializes the original source data, returning prepared data */
initialize(...args) {
const data = super.initialize(...args);
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
for (const [key, value] of Object.entries(data)) {
// TypedObjectField only calls _validateKey when persisting, so we also call it here
if (!this._validateKey(key)) {
delete value[key];
continue;
}
// Add basic prepared data.
const resource = resources[key];
value.label = resource.label;
value.isReversed = resources[key].reverse;
value.max = typeof resource.max === 'number' ? (value.max ?? resource.max) : null;
}
return data;
}
}
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };

View file

@ -224,7 +224,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
const armorChanged =
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
const armorData = getScrollTextData(this.parent.parent, changed.system.marks, 'armor');
options.scrollingTextData = [armorData];
}

View file

@ -112,24 +112,14 @@ export default class DHWeapon extends AttachableItem {
async getDescriptionData() {
const baseDescription = this.description;
const tier = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`);
const trait = game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.attack.roll.trait].label);
const range = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${this.attack.range}.name`);
const damage = Roll.replaceFormulaData(this.attack.damageFormula, this.parent.parent ?? this.parent);
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
const prefix = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
{
features,
tier,
trait,
range,
damage,
burden
item: this,
features
}
);

View file

@ -1,6 +1,16 @@
export default class DhAppearance extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
static sfxSchema = () =>
new foundry.data.fields.SchemaField({
class: new foundry.data.fields.StringField({
nullable: true,
initial: null,
blank: true,
choices: CONFIG.DH.GENERAL.diceSoNiceSFXClasses
})
});
static defineSchema() {
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
@ -15,7 +25,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
material: new StringField({ initial: 'metal', required: true, blank: false }),
system: new StringField({ initial: 'standard', required: true, blank: false }),
font: new StringField({ initial: 'auto', required: true, blank: false })
font: new StringField({ initial: 'auto', required: true, blank: false }),
sfx: new SchemaField({
higher: DhAppearance.sfxSchema()
})
});
return {
@ -30,7 +43,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }),
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' })
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' }),
sfx: new SchemaField({
critical: DhAppearance.sfxSchema()
})
}),
extendCharacterDescriptions: new BooleanField(),
extendAdversaryDescriptions: new BooleanField(),
@ -65,4 +81,48 @@ export default class DhAppearance extends foundry.abstract.DataModel {
showGenericStatusEffects: new BooleanField({ initial: true })
};
}
get diceSoNiceData() {
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
const getSFX = (baseClientData, overrideKey) => {
if (!globalOverrides.diceSoNice.sfx.overrideEnabled) return baseClientData;
const overrideData = globalOverrides.diceSoNice.sfx[overrideKey];
const clientData = foundry.utils.deepClone(baseClientData);
return Object.keys(clientData).reduce((acc, key) => {
const data = clientData[key];
acc[key] = Object.keys(data).reduce((acc, dataKey) => {
const value = data[dataKey];
acc[dataKey] = value ? value : overrideData[key][dataKey];
return acc;
}, {});
return acc;
}, {});
};
return {
...this.diceSoNice,
sfx: getSFX(this.diceSoNice.sfx, 'global'),
hope: {
...this.diceSoNice.hope,
sfx: getSFX(this.diceSoNice.hope.sfx, 'hope')
},
fear: {
...this.diceSoNice.fear,
sfx: getSFX(this.diceSoNice.fear.sfx, 'fear')
}
};
}
/** Invoked by the setting when data changes */
handleChange() {
if (this.displayFear) {
if (ui.resources) {
if (this.displayFear === 'hide') ui.resources.close({ allowed: true });
else ui.resources.render({ force: true });
}
}
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
globalOverrides.diceSoNiceSFXUpdate(this);
}
}

View file

@ -0,0 +1,55 @@
import DhAppearance from './Appearance.mjs';
/**
* A setting to handle cases where we want to allow the GM to set a global default for client settings.
*/
export default class DhGlobalOverrides extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
diceSoNice: new fields.SchemaField({
sfx: new fields.SchemaField({
overrideEnabled: new fields.BooleanField(),
global: new fields.SchemaField({
critical: DhAppearance.sfxSchema()
}),
hope: new fields.SchemaField({
higher: DhAppearance.sfxSchema()
}),
fear: new fields.SchemaField({
higher: DhAppearance.sfxSchema()
})
})
})
};
}
async diceSoNiceSFXUpdate(appearanceSettings, enabled) {
if (!game.user.isGM) return;
const newEnabled = enabled !== undefined ? enabled : this.diceSoNice.sfx.overrideEnabled;
if (newEnabled) {
const newOverrides = foundry.utils.mergeObject(this.toObject(), {
diceSoNice: {
sfx: {
overrideEnabled: true,
global: appearanceSettings.diceSoNice.sfx,
hope: appearanceSettings.diceSoNice.hope.sfx,
fear: appearanceSettings.diceSoNice.fear.sfx
}
}
});
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
} else {
const newOverrides = {
...this.toObject(),
diceSoNice: {
sfx: {
overrideEnabled: false
}
}
};
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
}
}
}

View file

@ -145,6 +145,16 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
description: new fields.StringField()
})
),
resources: new fields.TypedObjectField(
new fields.SchemaField({
resources: new fields.TypedObjectField(new fields.EmbeddedDataField(Resource))
}),
{
initial: {
character: { resources: {} }
}
}
),
itemFeatures: new fields.SchemaField({
weaponFeatures: new fields.TypedObjectField(
new fields.SchemaField({
@ -185,4 +195,117 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
}
return source;
}
/** Invoked by the setting when data changes */
handleChange() {
if (this.maxFear) {
if (ui.resources) ui.resources.render({ force: true });
}
this.refreshConfig();
this.#resetActors();
}
/** Update config values based on homebrew data. Make sure the references don't change */
refreshConfig() {
for (const [actorType, actorData] of Object.entries(this.resources)) {
const config = CONFIG.DH.RESOURCE[actorType];
for (const key of Object.keys(config.all)) {
delete config.all[key];
}
Object.assign(config.all, {
...Object.entries(actorData.resources).reduce((result, [key, value]) => {
result[key] = value.toObject();
result[key].id = key;
return result;
}, {}),
...config.custom,
...config.base
});
}
}
/**
* Triggers a reset and non-forced re-render on all given actors (if given)
* or all world actors and actors in all scenes to show immediate results for a changed setting.
*/
#resetActors() {
const actors = new Set(
[
game.actors.contents,
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
].flat()
);
for (const actor of actors) {
for (const app of Object.values(actor.apps)) {
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
element.open = false; // This triggers a save
}
}
actor.reset();
actor.render();
}
}
}
export class Resource extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
initial: new fields.NumberField({
required: true,
integer: true,
initial: 0,
min: 0,
label: 'DAGGERHEART.GENERAL.initial'
}),
max: new fields.NumberField({
nullable: true,
initial: null,
min: 0,
label: 'DAGGERHEART.GENERAL.max'
}),
label: new fields.StringField({ label: 'DAGGERHEART.GENERAL.label' }),
images: new fields.SchemaField({
full: imageIconField('fa solid fa-circle'),
empty: imageIconField('fa-regular fa-circle')
})
};
}
static getDefaultResourceData = label => {
const images = Resource.schema.fields.images.getInitialValue();
return {
initial: 0,
max: 0,
label: label ?? '',
images
};
};
static getDefaultImageData = imageKey => {
return Resource.schema.fields.images.fields[imageKey].getInitialValue();
};
}
const imageIconField = defaultValue =>
new foundry.data.fields.SchemaField(
{
value: new foundry.data.fields.StringField({
initial: defaultValue,
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.value.label'
}),
isIcon: new foundry.data.fields.BooleanField({
required: true,
initial: true,
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.isIcon.label'
}),
noColorFilter: new foundry.data.fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.noColorFilter.label'
})
},
{ required: true }
);

View file

@ -0,0 +1,12 @@
export default class DhMetagaming extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
hideObserverPermissionInChat: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label',
hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint'
})
};
}
}

View file

@ -1,4 +1,6 @@
export { default as DhAppearance } from './Appearance.mjs';
export { default as DhAutomation } from './Automation.mjs';
export { default as DhHomebrew } from './Homebrew.mjs';
export { default as DhMetagaming } from './Metagaming.mjs';
export { default as DhVariantRules } from './VariantRules.mjs';
export { default as DhGlobalOverrides } from './GlobalOverrides.mjs';