Merged with main

This commit is contained in:
WBHarry 2025-08-24 21:31:57 +02:00
commit 990c73987e
65 changed files with 567 additions and 424 deletions

View file

@ -432,12 +432,17 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
}
};
if (type == 'domains')
if (type === 'domains')
presets.filter = {
'level.max': { key: 'level.max', value: 1 },
'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null }
};
if (type === 'subclasses')
presets.filter = {
'system.linkedClass.uuid': { key: 'system.linkedClass.uuid', value: this.setup.class?.uuid }
};
if (equipment.includes(type))
presets.filter = {
'system.tier': { key: 'system.tier', value: 1 },

View file

@ -119,6 +119,15 @@ export default class ClassSheet extends DHBaseItemSheet {
const itemType = data.data ? data.type : item.type;
const target = event.target.closest('fieldset.drop-section');
if (itemType === 'subclass') {
if (item.system.linkedClass) {
return ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.subclassAlreadyLinked', {
name: item.name,
class: this.document.name
})
);
}
await item.update({ 'system.linkedClass': this.document.uuid });
await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
});
@ -181,6 +190,12 @@ export default class ClassSheet extends DHBaseItemSheet {
static async #removeItemFromCollection(_event, element) {
const { uuid, target } = element.dataset;
const prop = foundry.utils.getProperty(this.document.system, target);
if (target === 'subclasses') {
const subclass = await foundry.utils.fromUuid(uuid);
await subclass.update({ 'system.linkedClass': null });
}
await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid).map(x => x.uuid) });
}

View file

@ -124,11 +124,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
htmlElement
.querySelectorAll('[data-action="selectFolder"]')
.forEach(element => element.addEventListener("contextmenu", (event) => {
htmlElement.querySelectorAll('[data-action="selectFolder"]').forEach(element =>
element.addEventListener('contextmenu', event => {
event.target.classList.toggle('expanded');
}))
})
);
}
/* -------------------------------------------- */
@ -195,8 +195,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
this.items = ItemBrowser.sortBy(items, 'name');
if(target) {
target.closest('.compendium-sidebar').querySelectorAll('[data-action="selectFolder"]').forEach(element => element.classList.remove("is-selected"))
if (target) {
target
.closest('.compendium-sidebar')
.querySelectorAll('[data-action="selectFolder"]')
.forEach(element => element.classList.remove('is-selected'));
target.classList.add('is-selected');
}
@ -204,7 +207,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
}
_replaceHTML(result, content, options) {
if(!options.isFirstRender) delete result.sidebar;
if (!options.isFirstRender) delete result.sidebar;
super._replaceHTML(result, content, options);
}
@ -240,14 +243,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
filters.forEach(f => {
if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field);
else if (typeof f.choices === 'function') {
f.choices = f.choices();
f.choices = f.choices(this.items);
}
// Clear field label so template uses our custom label parameter
if (f.field && f.label) {
f.field.label = undefined;
}
f.name ??= f.key;
f.value = this.presets?.filter?.[f.name]?.value ?? null;
});
@ -259,11 +262,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
/* -------------------------------------------- */
/**
* Create and initialize search filter instances for the inventory and loadout sections.
* Create and initialize search filter instance.
*
* Sets up two {@link foundry.applications.ux.SearchFilter} instances:
* - One for the inventory, which filters items in the inventory grid.
* - One for the loadout, which filters items in the loadout/card grid.
* @private
*/
_createSearchFilter() {

View file

@ -2,270 +2,278 @@ export const typeConfig = {
adversaries: {
columns: [
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular"
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular'
},
{
key: "system.type",
label: "DAGGERHEART.GENERAL.type"
key: 'system.type',
label: 'DAGGERHEART.GENERAL.type'
}
],
filters: [
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular",
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular',
field: 'system.api.models.actors.DhAdversary.schema.fields.tier'
},
{
key: "system.type",
label: "DAGGERHEART.GENERAL.type",
key: 'system.type',
label: 'DAGGERHEART.GENERAL.type',
field: 'system.api.models.actors.DhAdversary.schema.fields.type'
},
{
key: "system.difficulty",
name: "difficulty.min",
label: "DAGGERHEART.UI.ItemBrowser.difficultyMin",
key: 'system.difficulty',
name: 'difficulty.min',
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "gte"
operator: 'gte'
},
{
key: "system.difficulty",
name: "difficulty.max",
label: "DAGGERHEART.UI.ItemBrowser.difficultyMax",
key: 'system.difficulty',
name: 'difficulty.max',
label: 'DAGGERHEART.UI.ItemBrowser.difficultyMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "lte"
operator: 'lte'
},
{
key: "system.resources.hitPoints.max",
name: "hp.min",
label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin",
key: 'system.resources.hitPoints.max',
name: 'hp.min',
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "gte"
operator: 'gte'
},
{
key: "system.resources.hitPoints.max",
name: "hp.max",
label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax",
key: 'system.resources.hitPoints.max',
name: 'hp.max',
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "lte"
operator: 'lte'
},
{
key: "system.resources.stress.max",
name: "stress.min",
label: "DAGGERHEART.UI.ItemBrowser.stressMin",
key: 'system.resources.stress.max',
name: 'stress.min',
label: 'DAGGERHEART.UI.ItemBrowser.stressMin',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "gte"
operator: 'gte'
},
{
key: "system.resources.stress.max",
name: "stress.max",
label: "DAGGERHEART.UI.ItemBrowser.stressMax",
key: 'system.resources.stress.max',
name: 'stress.max',
label: 'DAGGERHEART.UI.ItemBrowser.stressMax',
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "lte"
},
operator: 'lte'
}
]
},
items: {
columns: [
{
key: "type",
label: "DAGGERHEART.GENERAL.type"
key: 'type',
label: 'DAGGERHEART.GENERAL.type'
},
{
key: "system.secondary",
label: "DAGGERHEART.UI.ItemBrowser.subtype",
format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-')
key: 'system.secondary',
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-')
},
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular"
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular'
}
],
filters: [
{
key: "type",
label: "DAGGERHEART.GENERAL.type",
choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t }))
key: 'type',
label: 'DAGGERHEART.GENERAL.type',
choices: () =>
CONFIG.Item.documentClass.TYPES.filter(t =>
['armor', 'weapon', 'consumable', 'loot'].includes(t)
).map(t => ({ value: t, label: t }))
},
{
key: "system.secondary",
label: "DAGGERHEART.UI.ItemBrowser.subtype",
key: 'system.secondary',
label: 'DAGGERHEART.UI.ItemBrowser.subtype',
choices: [
{ value: false, label: "DAGGERHEART.ITEMS.Weapon.primaryWeapon" },
{ value: true, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" }
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' },
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }
]
},
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular",
choices: [{ value: "1", label: "1" }, { value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }]
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular',
choices: [
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
{ value: '4', label: '4' }
]
},
{
key: "system.burden",
label: "DAGGERHEART.GENERAL.burden",
key: 'system.burden',
label: 'DAGGERHEART.GENERAL.burden',
field: 'system.api.models.items.DHWeapon.schema.fields.burden'
},
{
key: "system.attack.roll.trait",
label: "DAGGERHEART.GENERAL.Trait.single",
key: 'system.attack.roll.trait',
label: 'DAGGERHEART.GENERAL.Trait.single',
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait'
},
{
key: "system.attack.range",
label: "DAGGERHEART.GENERAL.range",
key: 'system.attack.range',
label: 'DAGGERHEART.GENERAL.range',
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range'
},
{
key: "system.baseScore",
name: "armor.min",
label: "DAGGERHEART.UI.ItemBrowser.armorScoreMin",
key: 'system.baseScore',
name: 'armor.min',
label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMin',
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "gte"
operator: 'gte'
},
{
key: "system.baseScore",
name: "armor.max",
label: "DAGGERHEART.UI.ItemBrowser.armorScoreMax",
key: 'system.baseScore',
name: 'armor.max',
label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMax',
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "lte"
operator: 'lte'
},
{
key: "system.itemFeatures",
label: "DAGGERHEART.GENERAL.features",
choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k, v]) => ({ value: k, label: v.label })),
operator: "contains3"
key: 'system.itemFeatures',
label: 'DAGGERHEART.GENERAL.features',
choices: () =>
[
...Object.entries(CONFIG.DH.ITEM.weaponFeatures),
...Object.entries(CONFIG.DH.ITEM.armorFeatures)
].map(([k, v]) => ({ value: k, label: v.label })),
operator: 'contains3'
}
]
},
features: {
columns: [
],
filters: [
]
columns: [],
filters: []
},
cards: {
columns: [
{
key: "system.type",
label: "DAGGERHEART.GENERAL.type"
key: 'system.type',
label: 'DAGGERHEART.GENERAL.type'
},
{
key: "system.domain",
label: "DAGGERHEART.GENERAL.Domain.single"
key: 'system.domain',
label: 'DAGGERHEART.GENERAL.Domain.single'
},
{
key: "system.level",
label: "DAGGERHEART.GENERAL.level"
key: 'system.level',
label: 'DAGGERHEART.GENERAL.level'
}
],
filters: [
{
key: "system.type",
label: "DAGGERHEART.GENERAL.type",
key: 'system.type',
label: 'DAGGERHEART.GENERAL.type',
field: 'system.api.models.items.DHDomainCard.schema.fields.type'
},
{
key: "system.domain",
label: "DAGGERHEART.GENERAL.Domain.single",
key: 'system.domain',
label: 'DAGGERHEART.GENERAL.Domain.single',
field: 'system.api.models.items.DHDomainCard.schema.fields.domain',
operator: "contains2"
operator: 'contains2'
},
{
key: "system.level",
name: "level.min",
label: "DAGGERHEART.UI.ItemBrowser.levelMin",
key: 'system.level',
name: 'level.min',
label: 'DAGGERHEART.UI.ItemBrowser.levelMin',
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "gte"
operator: 'gte'
},
{
key: "system.level",
name: "level.max",
label: "DAGGERHEART.UI.ItemBrowser.levelMax",
key: 'system.level',
name: 'level.max',
label: 'DAGGERHEART.UI.ItemBrowser.levelMax',
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "lte"
operator: 'lte'
},
{
key: "system.recallCost",
name: "recall.min",
label: "DAGGERHEART.UI.ItemBrowser.recallCostMin",
key: 'system.recallCost',
name: 'recall.min',
label: 'DAGGERHEART.UI.ItemBrowser.recallCostMin',
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "gte"
operator: 'gte'
},
{
key: "system.recallCost",
name: "recall.max",
label: "DAGGERHEART.UI.ItemBrowser.recallCostMax",
key: 'system.recallCost',
name: 'recall.max',
label: 'DAGGERHEART.UI.ItemBrowser.recallCostMax',
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "lte"
operator: 'lte'
}
]
},
classes: {
columns: [
{
key: "system.evasion",
label: "DAGGERHEART.GENERAL.evasion"
key: 'system.evasion',
label: 'DAGGERHEART.GENERAL.evasion'
},
{
key: "system.hitPoints",
label: "DAGGERHEART.GENERAL.HitPoints.plural"
key: 'system.hitPoints',
label: 'DAGGERHEART.GENERAL.HitPoints.plural'
},
{
key: "system.domains",
label: "DAGGERHEART.GENERAL.Domain.plural"
key: 'system.domains',
label: 'DAGGERHEART.GENERAL.Domain.plural'
}
],
filters: [
{
key: "system.evasion",
name: "evasion.min",
label: "DAGGERHEART.UI.ItemBrowser.evasionMin",
key: 'system.evasion',
name: 'evasion.min',
label: 'DAGGERHEART.UI.ItemBrowser.evasionMin',
field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "gte"
operator: 'gte'
},
{
key: "system.evasion",
name: "evasion.max",
label: "DAGGERHEART.UI.ItemBrowser.evasionMax",
key: 'system.evasion',
name: 'evasion.max',
label: 'DAGGERHEART.UI.ItemBrowser.evasionMax',
field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "lte"
operator: 'lte'
},
{
key: "system.hitPoints",
name: "hp.min",
label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin",
key: 'system.hitPoints',
name: 'hp.min',
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMin',
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "gte"
operator: 'gte'
},
{
key: "system.hitPoints",
name: "hp.max",
label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax",
key: 'system.hitPoints',
name: 'hp.max',
label: 'DAGGERHEART.UI.ItemBrowser.hitPointsMax',
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "lte"
operator: 'lte'
},
{
key: "system.domains",
label: "DAGGERHEART.GENERAL.Domain.plural",
key: 'system.domains',
label: 'DAGGERHEART.GENERAL.Domain.plural',
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })),
operator: "contains2"
operator: 'contains2'
}
]
},
subclasses: {
columns: [
{
key: "id",
label: "TYPES.Item.class",
format: (id) => {
return "";
key: 'id',
label: 'TYPES.Item.class',
format: id => {
return '';
}
},
{
key: "system.spellcastingTrait",
label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"
key: 'system.spellcastingTrait',
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
}
],
filters: []
@ -273,133 +281,133 @@ export const typeConfig = {
beastforms: {
columns: [
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular"
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular'
},
{
key: "system.mainTrait",
label: "DAGGERHEART.GENERAL.Trait.single"
key: 'system.mainTrait',
label: 'DAGGERHEART.GENERAL.Trait.single'
}
],
filters: [
{
key: "system.tier",
label: "DAGGERHEART.GENERAL.Tiers.singular",
key: 'system.tier',
label: 'DAGGERHEART.GENERAL.Tiers.singular',
field: 'system.api.models.items.DHBeastform.schema.fields.tier'
},
{
key: "system.mainTrait",
label: "DAGGERHEART.GENERAL.Trait.single",
key: 'system.mainTrait',
label: 'DAGGERHEART.GENERAL.Trait.single',
field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait'
}
]
}
}
};
export const compendiumConfig = {
"daggerheart": {
id: "daggerheart",
label: "DAGGERHEART",
daggerheart: {
id: 'daggerheart',
label: 'DAGGERHEART',
folders: {
"adversaries": {
id: "adversaries",
keys: ["adversaries"],
label: "DAGGERHEART.UI.ItemBrowser.folders.adversaries",
type: ["adversary"],
listType: "adversaries"
adversaries: {
id: 'adversaries',
keys: ['adversaries'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.adversaries',
type: ['adversary'],
listType: 'adversaries'
},
"ancestries": {
id: "ancestries",
keys: ["ancestries"],
label: "DAGGERHEART.UI.ItemBrowser.folders.ancestries",
type: ["ancestry"],
ancestries: {
id: 'ancestries',
keys: ['ancestries'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries',
type: ['ancestry'],
folders: {
"features": {
id: "features",
keys: ["ancestries"],
label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"]
features: {
id: 'features',
keys: ['ancestries'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ['feature']
}
}
},
"equipments": {
id: "equipments",
keys: ["armors", "weapons", "consumables", "loot"],
label: "DAGGERHEART.UI.ItemBrowser.folders.equipment",
type: ["armor", "weapon", "consumable", "loot"],
listType: "items"
equipments: {
id: 'equipments',
keys: ['armors', 'weapons', 'consumables', 'loot'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment',
type: ['armor', 'weapon', 'consumable', 'loot'],
listType: 'items'
},
"classes": {
id: "classes",
keys: ["classes"],
label: "DAGGERHEART.UI.ItemBrowser.folders.classes",
type: ["class"],
classes: {
id: 'classes',
keys: ['classes'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.classes',
type: ['class'],
folders: {
"features": {
id: "features",
keys: ["classes"],
label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"]
features: {
id: 'features',
keys: ['classes'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ['feature']
},
"items": {
id: "items",
keys: ["classes"],
label: "DAGGERHEART.UI.ItemBrowser.folders.items",
type: ["armor", "weapon", "consumable", "loot"],
listType: "items"
items: {
id: 'items',
keys: ['classes'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.items',
type: ['armor', 'weapon', 'consumable', 'loot'],
listType: 'items'
}
},
listType: "classes"
listType: 'classes'
},
"subclasses": {
id: "subclasses",
keys: ["subclasses"],
label: "DAGGERHEART.UI.ItemBrowser.folders.subclasses",
type: ["subclass"],
listType: "subclasses"
subclasses: {
id: 'subclasses',
keys: ['subclasses'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.subclasses',
type: ['subclass'],
listType: 'subclasses'
},
"domains": {
id: "domains",
keys: ["domains"],
label: "DAGGERHEART.UI.ItemBrowser.folders.domainCards",
type: ["domainCard"],
listType: "cards"
domains: {
id: 'domains',
keys: ['domains'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.domainCards',
type: ['domainCard'],
listType: 'cards'
},
"communities": {
id: "communities",
keys: ["communities"],
label: "DAGGERHEART.UI.ItemBrowser.folders.communities",
type: ["community"],
communities: {
id: 'communities',
keys: ['communities'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.communities',
type: ['community'],
folders: {
"features": {
id: "features",
keys: ["communities"],
label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"]
features: {
id: 'features',
keys: ['communities'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ['feature']
}
}
},
"environments": {
id: "environments",
keys: ["environments"],
label: "DAGGERHEART.UI.ItemBrowser.folders.environments",
type: ["environment"]
environments: {
id: 'environments',
keys: ['environments'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.environments',
type: ['environment']
},
"beastforms": {
id: "beastforms",
keys: ["beastforms"],
label: "DAGGERHEART.UI.ItemBrowser.folders.beastforms",
type: ["beastform"],
listType: "beastforms",
beastforms: {
id: 'beastforms',
keys: ['beastforms'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms',
type: ['beastform'],
listType: 'beastforms',
folders: {
"features": {
id: "features",
keys: ["beastforms"],
label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"]
features: {
id: 'features',
keys: ['beastforms'],
label: 'DAGGERHEART.UI.ItemBrowser.folders.features',
type: ['feature']
}
}
}
}
}
}
};

View file

@ -51,11 +51,13 @@ export default class DHAttackAction extends DHDamageAction {
const labels = [];
const { roll, range, damage } = this;
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`))
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`));
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
for (const { value, type } of damage.parts) {
const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {});
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
for (const { value, valueAlt, type } of damage.parts) {
const usedValue = useAltDamage ? valueAlt : value;
const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
const icons = Array.from(type)
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)

View file

@ -172,7 +172,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
dialog: {
configure: hasRoll
},
type: this.type,
type: this.roll?.type ?? this.type,
hasRoll: hasRoll,
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
hasHealing: this.damage?.parts?.length && this.type === 'healing',

View file

@ -130,7 +130,7 @@ export default class DhpAdversary extends BaseDataActor {
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Automation
).hordeDamage;
if (autoHordeDamage && changes.system?.resources?.hitPoints?.value) {
if (autoHordeDamage && changes.system?.resources?.hitPoints?.value !== undefined) {
const hordeActiveEffect = this.parent.effects.find(x => x.type === 'horde');
if (hordeActiveEffect) {
const halfHP = Math.ceil(this.resources.hitPoints.max / 2);

View file

@ -130,11 +130,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
const typeForDefeated = ['character', 'adversary', 'companion'].find(x => x === this.parent.type);
if (defeatedSettings.enabled && typeForDefeated) {
const resource = typeForDefeated === 'companion' ? 'stress' : 'hitPoints';
if (changes.system.resources[resource]) {
const becameMax = changes.system.resources[resource].value === this.resources[resource].max;
const resourceValue = changes.system.resources[resource];
if (
resourceValue &&
this.resources[resource].max &&
resourceValue.value !== this.resources[resource].value
) {
const becameMax = resourceValue.value === this.resources[resource].max;
const wasMax =
this.resources[resource].value === this.resources[resource].max &&
this.resources[resource].value !== changes.system.resources[resource].value;
this.resources[resource].value !== resourceValue.value;
if (becameMax) {
this.parent.toggleDefeated(true);
} else if (wasMax) {

View file

@ -317,7 +317,7 @@ export default class DhCharacter extends BaseDataActor {
}
get multiclass() {
const value = this.parent.items.find(x => x.type === 'Class' && x.system.isMulticlass);
const value = this.parent.items.find(x => x.type === 'class' && x.system.isMulticlass);
const subclass = this.parent.items.find(x => x.type === 'subclass' && x.system.isMulticlass);
return {
@ -443,7 +443,9 @@ export default class DhCharacter extends BaseDataActor {
classFeatures.push(item);
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
if (this.class.subclass) {
const subclassState = this.class.subclass.system.featureState;
const prop = item.system.multiclassOrigin ? 'multiclass' : 'class';
const subclassState = this[prop].subclass?.system?.featureState;
if (!subclassState) continue;
if (
item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||

View file

@ -113,7 +113,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
this.currentTargets = this.getTargetList();
// this.registerTargetHook();
if (this.targetMode === true && this.hasRoll) {
if (this.hasRoll) {
this.targetShort = this.targets.reduce(
(a, c) => {
if (c.hit) a.hit += 1;
@ -127,7 +127,8 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
}
this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER');
this.canButtonApply = game.user.isGM;
this.canButtonApply = game.user.isGM; //temp
this.isGM = game.user.isGM; //temp
}
getTargetList() {

View file

@ -162,7 +162,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
for (let f of this.features) {
const fBase = f.item ?? f;
const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid);
const multiclass = this.isMulticlass ? 'multiclass' : null;
features.push(
foundry.utils.mergeObject(
feature.toObject(),
@ -170,7 +169,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
_stats: { compendiumSource: fBase.uuid },
system: {
originItemType: this.parent.type,
identifier: multiclass ?? (f.item ? f.type : null)
identifier: f.item ? f.type : null,
multiclassOrigin: this.isMulticlass
}
},
{ inplace: false }

View file

@ -29,6 +29,7 @@ export default class DHFeature extends BaseDataItem {
nullable: true,
initial: null
}),
multiclassOrigin: new fields.BooleanField({ initial: false }),
identifier: new fields.StringField()
};
}

View file

@ -1,3 +1,4 @@
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ItemLinkFields from '../fields/itemLinkFields.mjs';
import BaseDataItem from './base.mjs';
@ -25,7 +26,8 @@ export default class DHSubclass extends BaseDataItem {
}),
features: new ItemLinkFields(),
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
isMulticlass: new fields.BooleanField({ initial: false })
isMulticlass: new fields.BooleanField({ initial: false }),
linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null })
};
}

View file

@ -167,13 +167,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
if (subclass) {
const featureState = subclass.system.featureState;
const featureType = subclass
? (subclass.system.features.find(x => x.item?.uuid === this.parent.uuid)?.type ?? null)
: null;
if (
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && featureState < 2) ||
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
featureState < 2) ||
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
) {
this.transfer = false;
}

View file

@ -1,7 +1,7 @@
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import { LevelOptionType } from '../data/levelTier.mjs';
import DHFeature from '../data/item/feature.mjs';
import { createScrollText, damageKeyToNumber, versionCompare } from '../helpers/utils.mjs';
import { createScrollText, damageKeyToNumber } from '../helpers/utils.mjs';
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
export default class DhpActor extends Actor {
@ -167,10 +167,10 @@ export default class DhpActor extends Actor {
if (multiclass) {
const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid);
const multiclassFeatures = this.items.filter(
x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass'
x => x.system.originItemType === 'class' && x.system.multiclassOrigin
);
const subclassFeatures = this.items.filter(
x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass'
x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin
);
this.deleteEmbeddedDocuments(
@ -782,7 +782,7 @@ export default class DhpActor extends Actor {
}
const parsedJSON = JSON.parse(json);
if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) {
if (foundry.utils.isNewerVersion('1.1.0', parsedJSON._stats.systemVersion)) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle')

View file

@ -2,7 +2,8 @@ export default function DhDamageEnricher(match, _options) {
const parts = match[1].split('|').map(x => x.trim());
let value = null,
type = null;
type = null,
inline = false;
parts.forEach(part => {
const split = part.split(':').map(x => x.toLowerCase().trim());
@ -14,16 +15,19 @@ export default function DhDamageEnricher(match, _options) {
case 'type':
type = split[1];
break;
case 'inline':
inline = true;
break;
}
}
});
if (!value || !value) return match[0];
return getDamageMessage(value, type, match[0]);
return getDamageMessage(value, type, inline, match[0]);
}
function getDamageMessage(damage, type, defaultElement) {
function getDamageMessage(damage, type, inline, defaultElement) {
const typeIcons = type
.replace('[', '')
.replace(']', '')
@ -40,7 +44,7 @@ function getDamageMessage(damage, type, defaultElement) {
const dualityElement = document.createElement('span');
dualityElement.innerHTML = `
<button class="enriched-damage-button"
<button class="enriched-damage-button${inline ? ' inline' : ''}"
data-value="${damage}"
data-type="${type}"
data-tooltip="${game.i18n.localize('DAGGERHEART.GENERAL.damage')}"

View file

@ -36,7 +36,7 @@ function getDualityMessage(roll, flavor) {
const dualityElement = document.createElement('span');
dualityElement.innerHTML = `
<button class="duality-roll-button"
<button class="duality-roll-button${roll.inline ? ' inline' : ''}"
data-title="${label}"
data-label="${dataLabel}"
data-reaction="${roll.reaction ? 'true' : 'false'}"

View file

@ -2,7 +2,8 @@ export default function DhTemplateEnricher(match, _options) {
const parts = match[1].split('|').map(x => x.trim());
let type = null,
range = null;
range = null,
inline = false;
parts.forEach(part => {
const split = part.split(':').map(x => x.toLowerCase().trim());
@ -20,6 +21,9 @@ export default function DhTemplateEnricher(match, _options) {
);
range = matchedRange?.id;
break;
case 'inline':
inline = true;
break;
}
}
});
@ -30,7 +34,7 @@ export default function DhTemplateEnricher(match, _options) {
const templateElement = document.createElement('span');
templateElement.innerHTML = `
<button class="measured-template-button" data-type="${type}" data-range="${range}">
<button class="measured-template-button${inline ? ' inline' : ''}" data-type="${type}" data-range="${range}">
${label} - ${game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.name`)}
</button>
`;

View file

@ -418,14 +418,3 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
export const slugify = name => {
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
};
export const versionCompare = (current, target) => {
const currentSplit = current.split('.').map(x => Number.parseInt(x));
const targetSplit = target.split('.').map(x => Number.parseInt(x));
for (var i = 0; i < currentSplit.length; i++) {
if (currentSplit[i] < targetSplit[i]) return true;
if (currentSplit[i] > targetSplit[i]) return false;
}
return false;
};

View file

@ -1,12 +1,15 @@
import { versionCompare } from '../helpers/utils.mjs';
export async function runMigrations() {
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
if (!lastMigrationVersion) lastMigrationVersion = '1.0.6';
if (versionCompare(lastMigrationVersion, '1.1.0')) {
if (foundry.utils.isNewerVersion('1.1.0', lastMigrationVersion)) {
const lockedPacks = [];
const compendiumActors = [];
for (let pack of game.packs) {
if (pack.locked) {
lockedPacks.push(pack.collection);
await pack.configure({ locked: false });
}
const documents = await pack.getDocuments();
compendiumActors.push(...documents.filter(x => x.type === 'character'));
}
@ -34,8 +37,64 @@ export async function runMigrations() {
actor.updateEmbeddedDocuments('Item', items);
});
for (let packId of lockedPacks) {
const pack = game.packs.get(packId);
await pack.configure({ locked: true });
}
lastMigrationVersion = '1.1.0';
}
if (foundry.utils.isNewerVersion('1.1.1', lastMigrationVersion)) {
const lockedPacks = [];
const compendiumClasses = [];
const compendiumActors = [];
for (let pack of game.packs) {
if (pack.locked) {
lockedPacks.push(pack.collection);
await pack.configure({ locked: false });
}
const documents = await pack.getDocuments();
compendiumClasses.push(...documents.filter(x => x.type === 'class'));
compendiumActors.push(...documents.filter(x => x.type === 'character'));
}
[...compendiumActors, ...game.actors.filter(x => x.type === 'character')].forEach(char => {
const multiclass = char.items.find(x => x.type === 'class' && x.system.isMulticlass);
const multiclassSubclass =
multiclass?.system?.subclasses?.length > 0 ? multiclass.system.subclasses[0] : null;
char.items.forEach(item => {
if (item.type === 'feature' && item.system.identifier === 'multiclass') {
const base = item.system.originItemType === 'class' ? multiclass : multiclassSubclass;
if (base) {
const baseFeature = base.system.features.find(x => x.item.name === item.name);
if (baseFeature) {
item.update({
system: {
multiclassOrigin: true,
identifier: baseFeature.type
}
});
}
}
}
});
});
const worldClasses = game.items.filter(x => x.type === 'class');
for (let classVal of [...compendiumClasses, ...worldClasses]) {
for (let subclass of classVal.system.subclasses) {
await subclass.update({ 'system.linkedClass': classVal.uuid });
}
}
for (let packId of lockedPacks) {
const pack = game.packs.get(packId);
await pack.configure({ locked: true });
}
lastMigrationVersion = '1.1.1';
}
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
}