diff --git a/daggerheart.mjs b/daggerheart.mjs
index f75ff1da..49ed7049 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -242,6 +242,38 @@ Hooks.on('setup', () => {
systemEffect: true
}))
];
+
+ const actorCommon = {
+ bar: ['resources.stress'],
+ value: []
+ };
+ const damageThresholds = ['damageThresholds.major', 'damageThresholds.severe'];
+ const traits = Object.keys(game.system.api.data.actors.DhCharacter.schema.fields.traits.fields).map(
+ trait => `traits.${trait}.value`
+ );
+ CONFIG.Actor.trackableAttributes = {
+ character: {
+ bar: [...actorCommon.bar, 'resources.hitPoints', 'resources.hope'],
+ value: [
+ ...actorCommon.value,
+ ...traits,
+ ...damageThresholds,
+ 'proficiency',
+ 'evasion',
+ 'armorScore',
+ 'scars',
+ 'levelData.level.current'
+ ]
+ },
+ adversary: {
+ bar: [...actorCommon.bar, 'resources.hitPoints'],
+ value: [...actorCommon.value, ...damageThresholds, 'criticalThreshold']
+ },
+ companion: {
+ bar: [...actorCommon.bar],
+ value: [...actorCommon.value, 'evasion', 'levelData.level.current']
+ }
+ };
});
Hooks.on('ready', async () => {
diff --git a/lang/en.json b/lang/en.json
index 0186ae3e..86b4323c 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -2152,6 +2152,7 @@
"continue": "Continue",
"criticalSuccess": "Critical Success",
"criticalShort": "Critical",
+ "currentLevel": "Current Level",
"custom": "Custom",
"d20Roll": "D20 Roll",
"damage": "Damage",
diff --git a/module/applications/dialogs/attributionDialog.mjs b/module/applications/dialogs/attributionDialog.mjs
index 99ff261a..7f3f8bb2 100644
--- a/module/applications/dialogs/attributionDialog.mjs
+++ b/module/applications/dialogs/attributionDialog.mjs
@@ -54,7 +54,11 @@ export default class AttributionDialog extends HandlebarsApplicationMixin(Applic
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
- element.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`;
+ element.innerHTML =
+ `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll(
+ ' ',
+ ' '
+ );
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}
diff --git a/module/applications/dialogs/group-roll-dialog.mjs b/module/applications/dialogs/group-roll-dialog.mjs
index 2cb79563..8a3c43d6 100644
--- a/module/applications/dialogs/group-roll-dialog.mjs
+++ b/module/applications/dialogs/group-roll-dialog.mjs
@@ -70,7 +70,11 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
element.appendChild(img);
const label = document.createElement('span');
- label.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`;
+ label.innerHTML =
+ `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll(
+ ' ',
+ ' '
+ );
element.appendChild(label);
return element;
@@ -119,7 +123,11 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
element.appendChild(img);
const label = document.createElement('span');
- label.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`;
+ label.innerHTML =
+ `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll(
+ ' ',
+ ' '
+ );
element.appendChild(label);
return element;
diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs
index d7b1b536..9b8edd8a 100644
--- a/module/applications/sheets-configs/activeEffectConfig.mjs
+++ b/module/applications/sheets-configs/activeEffectConfig.mjs
@@ -4,20 +4,34 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
constructor(options) {
super(options);
- const ignoredActorKeys = ['config', 'DhEnvironment'];
+ const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
- if (!ignoredActorKeys.includes(key)) {
- const model = game.system.api.models.actors[key];
- const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
- // As per DHToken._getTrackedAttributesFromSchema, attributes.bar have a max version as well.
- const maxAttributes = attributes.bar.map(x => [...x, 'max']);
- attributes.value.push(...maxAttributes);
- const group = game.i18n.localize(model.metadata.label);
- const choices = CONFIG.Token.documentClass
- .getTrackedAttributeChoices(attributes, model)
- .map(x => ({ ...x, group: group }));
- acc.push(...choices);
- }
+ if (ignoredActorKeys.includes(key)) return acc;
+
+ const model = game.system.api.models.actors[key];
+ const group = game.i18n.localize(model.metadata.label);
+ const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model.metadata.type);
+
+ const getLabel = path => {
+ const label = model.schema.getField(path)?.label;
+ return label ? game.i18n.localize(label) : path;
+ };
+
+ const bars = attributes.bar.flatMap(x => {
+ const joined = `${x.join('.')}.max`;
+ const label =
+ joined === 'resources.hope.max'
+ ? 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxHope.label'
+ : getLabel(joined);
+ return { value: joined, label, group };
+ });
+ const values = attributes.value.flatMap(x => {
+ const joined = x.join('.');
+ return { value: joined, label: getLabel(joined), group };
+ });
+
+ acc.push(...bars, ...values);
+
return acc;
}, []);
}
@@ -68,14 +82,18 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
},
render: function (item, search) {
const label = game.i18n.localize(item.label);
- const matchIndex = label.toLowerCase().indexOf(search);
+ const matchIndex = label.toLowerCase().indexOf(search.toLowerCase());
const beforeText = label.slice(0, matchIndex);
const matchText = label.slice(matchIndex, matchIndex + search.length);
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
- element.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`;
+ element.innerHTML =
+ `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll(
+ ' ',
+ ' '
+ );
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}
diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs
index ca0d56e3..fe36e37f 100644
--- a/module/applications/sheets-configs/setting-active-effect-config.mjs
+++ b/module/applications/sheets-configs/setting-active-effect-config.mjs
@@ -103,7 +103,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
- element.innerHTML = `${beforeText}${matchText ? `${matchText}` : ''}${after}`;
+ element.innerHTML =
+ `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll(
+ ' ',
+ ' '
+ );
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}
diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs
index 16e7e37a..f2c38090 100644
--- a/module/data/actor/adversary.mjs
+++ b/module/data/actor/adversary.mjs
@@ -40,7 +40,14 @@ export default class DhpAdversary extends BaseDataActor {
integer: true,
label: 'DAGGERHEART.GENERAL.hordeHp'
}),
- criticalThreshold: new fields.NumberField({ required: true, integer: true, min: 1, max: 20, initial: 20 }),
+ criticalThreshold: new fields.NumberField({
+ required: true,
+ integer: true,
+ min: 1,
+ max: 20,
+ initial: 20,
+ label: 'DAGGERHEART.ACTIONS.Settings.criticalThreshold'
+ }),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({
required: true,
diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs
index 8af4c74c..3913d426 100644
--- a/module/data/actor/character.mjs
+++ b/module/data/actor/character.mjs
@@ -35,15 +35,18 @@ export default class DhCharacter extends BaseDataActor {
'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 })
- })
+ 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'),
diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs
index f9eeeb90..db1faad4 100644
--- a/module/data/fields/actorField.mjs
+++ b/module/data/fields/actorField.mjs
@@ -7,16 +7,20 @@ const attributeField = label =>
});
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 })
- });
+ 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({
diff --git a/module/data/levelData.mjs b/module/data/levelData.mjs
index 669077ee..4f55d9ee 100644
--- a/module/data/levelData.mjs
+++ b/module/data/levelData.mjs
@@ -6,7 +6,12 @@ export default class DhLevelData extends foundry.abstract.DataModel {
return {
level: new fields.SchemaField({
- current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
+ current: new fields.NumberField({
+ required: true,
+ integer: true,
+ initial: 1,
+ label: 'DAGGERHEART.GENERAL.currentLevel'
+ }),
changed: new fields.NumberField({ required: true, integer: true, initial: 1 }),
bonuses: new fields.TypedObjectField(new fields.NumberField({ integer: true, nullable: false }))
}),
diff --git a/module/documents/token.mjs b/module/documents/token.mjs
index b9507c2f..6fd931d6 100644
--- a/module/documents/token.mjs
+++ b/module/documents/token.mjs
@@ -1,78 +1,30 @@
export default class DHToken extends CONFIG.Token.documentClass {
- /**
- * Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
- * @param {object} attributes The tracked attributes which can be chosen from
- * @returns {object} A nested object of attribute choices to display
- */
- static getTrackedAttributeChoices(attributes, model) {
+ /**@inheritdoc */
+ static getTrackedAttributeChoices(attributes, typeKey) {
attributes = attributes || this.getTrackedAttributes();
const barGroup = game.i18n.localize('TOKEN.BarAttributes');
const valueGroup = game.i18n.localize('TOKEN.BarValues');
+ const actorModel = typeKey ? game.system.api.data.actors[`Dh${typeKey.capitalize()}`] : null;
+ const getLabel = path => {
+ const label = actorModel.schema.getField(path)?.label;
+ return label ? game.i18n.localize(label) : path;
+ };
const bars = attributes.bar.map(v => {
const a = v.join('.');
- const modelLabel = model ? game.i18n.localize(model.schema.getField(`${a}.value`).label) : null;
- return { group: barGroup, value: a, label: modelLabel ? modelLabel : a };
+ return { group: barGroup, value: a, label: getLabel(a) };
});
- bars.sort((a, b) => a.label.compare(b.label));
+ bars.sort((a, b) => a.value.compare(b.value));
- const invalidAttributes = [
- 'gold',
- 'levelData',
- 'actions',
- 'biography',
- 'class',
- 'multiclass',
- 'companion',
- 'notes',
- 'partner',
- 'description',
- 'impulses',
- 'tier',
- 'type'
- ];
- const values = attributes.value.reduce((acc, v) => {
+ const values = attributes.value.map(v => {
const a = v.join('.');
- if (invalidAttributes.some(x => a.startsWith(x))) return acc;
-
- const field = model ? model.schema.getField(a) : null;
- const modelLabel = field ? game.i18n.localize(field.label) : null;
- const hint = field ? game.i18n.localize(field.hint) : null;
- acc.push({ group: valueGroup, value: a, label: modelLabel ? modelLabel : a, hint: hint });
-
- return acc;
- }, []);
- values.sort((a, b) => a.label.compare(b.label));
+ return { group: valueGroup, value: a, label: getLabel(a) };
+ });
+ values.sort((a, b) => a.value.compare(b.value));
return bars.concat(values);
}
- static _getTrackedAttributesFromSchema(schema, _path = []) {
- const attributes = { bar: [], value: [] };
- for (const [name, field] of Object.entries(schema.fields)) {
- const p = _path.concat([name]);
- if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
- if (field instanceof foundry.data.fields.BooleanField && field.options.isAttributeChoice)
- attributes.value.push(p);
- if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
- if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
- const isSchema = field instanceof foundry.data.fields.SchemaField;
- const isModel = field instanceof foundry.data.fields.EmbeddedDataField;
-
- if (isSchema || isModel) {
- const schema = isModel ? field.model.schema : field;
- const isBar = schema.has && schema.has('value') && schema.has('max');
- if (isBar) attributes.bar.push(p);
- else {
- const inner = this.getTrackedAttributes(schema, p);
- attributes.bar.push(...inner.bar);
- attributes.value.push(...inner.value);
- }
- }
- }
- return attributes;
- }
-
_shouldRecordMovementHistory() {
return false;
}
diff --git a/styles/less/ux/autocomplete/autocomplete.less b/styles/less/ux/autocomplete/autocomplete.less
index 08854a53..7f799449 100644
--- a/styles/less/ux/autocomplete/autocomplete.less
+++ b/styles/less/ux/autocomplete/autocomplete.less
@@ -32,7 +32,6 @@
li[role='option'] {
display: flex;
align-items: center;
- gap: 10px;
font-size: var(--font-size-14);
padding: 0 10px;
cursor: pointer;