Compare commits

..

No commits in common. "6cbe7708801c0795d567fb1a544d19d0702cb95e" and "c8d1ea14603a2ceab3a40a6a262b5c2f528f18ce" have entirely different histories.

66 changed files with 333 additions and 1143 deletions

View file

@ -1,3 +0,0 @@
[*]
indent_size = 4
indent_style = spaces

View file

@ -242,41 +242,6 @@ Hooks.on('setup', () => {
systemEffect: true
}))
];
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`
);
const resistance = Object.values(game.system.api.data.actors.DhCharacter.schema.fields.resistance.fields).flatMap(
type => Object.keys(type.fields).map(x => `resistance.${type.name}.${x}`)
);
const actorCommon = {
bar: ['resources.stress'],
value: [...resistance]
};
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 () => {

View file

@ -192,9 +192,6 @@
},
"age": "Age",
"backgroundQuestions": "Backgrounds",
"burden": {
"ignore": { "label": "Burden: Ignore", "hint": "Ignore burden rules" }
},
"companionFeatures": "Companion Features",
"connections": "Connections",
"contextMenu": {
@ -217,12 +214,6 @@
"maxEvasionBonus": "Max Evasion Increase",
"maxHPBonus": "Max HP Increase",
"pronouns": "Pronouns",
"roll": {
"guaranteedCritical": {
"label": "Guaranteed Critical",
"hint": "Set to 1 to always roll a critical"
}
},
"story": {
"backgroundTitle": "Background",
"characteristics": "Characteristics",
@ -352,11 +343,6 @@
"requestSpotlight": "Request The Spotlight",
"openCountdowns": "Countdowns"
},
"CompendiumBrowserSettings": {
"title": "Enable Compendiums",
"enableSource": "Enable Source",
"disableSource": "Disable Source"
},
"ContextMenu": {
"disableEffect": "Disable Effect",
"enableEffect": "Enable Effect",
@ -457,13 +443,9 @@
"name": "Clear Stress"
},
"prepare": {
"description": "Describe how you are preparing for the next day's adventure, then gain a Hope.",
"description": "Describe how you are preparing for the next day's adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope.",
"name": "Prepare"
},
"prepareWithFriends": {
"description": "You prepare with one or more members of your party, and you each gain 2 Hope.",
"name": "Prepare (together)"
},
"repairArmor": {
"description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally's armor instead.",
"name": "Repair Armor"
@ -494,11 +476,7 @@
},
"prepare": {
"name": "Prepare",
"description": "Describe how you prepare yourself for the path ahead, then gain a Hope."
},
"prepareWithFriends": {
"name": "Prepare (together)",
"description": "You prepare with one or more members of your party, and you each gain 2 Hope."
"description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope."
}
},
"refreshable": {
@ -1862,16 +1840,6 @@
"singular": "Adversary",
"plural": "Adversaries"
},
"Attack": {
"hpDamageMultiplier": {
"label": "HP Damage Multiplier",
"hint": "Multiply any damage you deal by this number"
},
"hpDamageTakenMultiplier": {
"label": "HP Damage Taken Multiplier",
"hint": "Multiply any damage dealt to you by this number"
}
},
"Bonuses": {
"rest": {
"downtimeAction": "Downtime Action",
@ -2056,40 +2024,16 @@
"reaction": "Reaction Roll"
},
"Rules": {
"conditionImmunities": {
"hidden": "Condition Immunity: Hidden",
"restrained": "Condition Immunity: Restrained",
"vulnerable": "Condition Immunity: Vulnerable"
},
"damageReduction": {
"disabledArmor": { "label": "Disabled Armorslots" },
"increasePerArmorMark": {
"label": "Damage Reduction per Armor Slot",
"hint": "A used armor slot normally reduces damage by one step. This value increases the number of steps damage is reduced by."
},
"magical": {
"label": "Daamge Reduction: Only Magical",
"hint": "Armor can only be used to reduce magical damage"
},
"maxArmorMarkedBonus": "Max Armor Used",
"maxArmorMarkedStress": {
"label": "Max Armor Used With Stress",
"hint": "If this value is set you can use up to that much stress to spend additional Armor Marks beyond your normal maximum."
},
"reduceSeverity": {
"magical": {
"label": "Reduce Damage Severity: Magical",
"hint": "Lowers any magical damage received by the set amount of severity degrees"
},
"physical": {
"label": "Reduce Damage Severity: Physical",
"hint": "Lowers any physical damage received by the set amount of severity degrees"
}
},
"physical": {
"label": "Damage Reduction: Only Physical",
"hint": "Armor can only be used to reduce physical damage"
},
"stress": {
"any": {
"label": "Stress Damage Reduction: Any",
@ -2107,12 +2051,6 @@
"label": "Stress Damage Reduction: Minor",
"hint": "The cost in stress you can pay to reduce minor damage to none."
}
},
"thresholdImmunities": {
"minor": {
"label": "Threshold Immunities: Minor",
"hint": "Automatically ignores minor damage when set to 1"
}
}
},
"attack": {
@ -2182,9 +2120,7 @@
"configuration": "Configuration",
"base": "Base",
"triggers": "Triggers",
"deathMoves": "Deathmoves",
"sources": "Sources",
"packs": "Packs"
"deathMoves": "Deathmoves"
},
"Tiers": {
"singular": "Tier",
@ -2216,7 +2152,6 @@
"continue": "Continue",
"criticalSuccess": "Critical Success",
"criticalShort": "Critical",
"currentLevel": "Current Level",
"custom": "Custom",
"d20Roll": "D20 Roll",
"damage": "Damage",
@ -2620,8 +2555,6 @@
"resetMovesTitle": "Reset {type} Downtime Moves",
"resetItemFeaturesTitle": "Reset {type}",
"resetMovesText": "Are you sure you want to reset?",
"deleteItemTitle": "Delete Homebrew Item",
"deleteItemText": "Are you sure you want to delete the item?",
"FIELDS": {
"maxFear": { "label": "Max Fear" },
"maxHope": { "label": "Max Hope" },
@ -2853,7 +2786,6 @@
"ItemBrowser": {
"title": "Daggerheart Compendium Browser",
"hint": "Select a Folder in sidebar to start browsing through the compendium",
"browserSettings": "Browser Settings",
"searchPlaceholder": "Search...",
"columnName": "Name",
"tooltipFilters": "Filters",
@ -3010,7 +2942,7 @@
"rulesOn": "Rules On",
"rulesOff": "Rules Off",
"remainingUses": "Uses refresh on {type}",
"rightClickExtend": "Right-Click to extend",
"rightClickExtand": "Right-Click to extand",
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
"configureAttribution": "Configure Attribution",
"deleteItem": "Delete Item",

View file

@ -1,136 +0,0 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class CompendiumBrowserSettings extends HandlebarsApplicationMixin(ApplicationV2) {
constructor() {
super();
this.browserSettings = game.settings
.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings)
.toObject();
}
static DEFAULT_OPTIONS = {
tag: 'div',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'compendium-brower-settings'],
window: {
icon: 'fa-solid fa-book',
title: 'DAGGERHEART.APPLICATIONS.CompendiumBrowserSettings.title'
},
position: {
width: 500
},
actions: {
toggleSource: CompendiumBrowserSettings.#toggleSource,
finish: CompendiumBrowserSettings.#finish
}
};
/** @override */
static PARTS = {
packs: {
id: 'packs',
template: 'systems/daggerheart/templates/dialogs/compendiumBrowserSettingsDialog/packs.hbs'
},
footer: { template: 'systems/daggerheart/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs' }
};
static #browserPackTypes = ['Actor', 'Item'];
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
for (const element of htmlElement.querySelectorAll('.pack-checkbox'))
element.addEventListener('change', this.toggleTypedPack.bind(this));
}
/**@inheritdoc */
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
const excludedSourceData = this.browserSettings.excludedSources;
const excludedPackData = this.browserSettings.excludedPacks;
context.typePackCollections = game.packs.reduce((acc, pack) => {
const { type, label, packageType, packageName, id } = pack.metadata;
if (packageType === 'world' || !CompendiumBrowserSettings.#browserPackTypes.includes(type)) return acc;
const sourceChecked =
!excludedSourceData[packageName] ||
!excludedSourceData[packageName].excludedDocumentTypes.includes(type);
const sourceLabel = game.modules.get(packageName)?.title ?? game.system.title;
if (!acc[type]) acc[type] = { label: game.i18n.localize(`DOCUMENT.${type}s`), sources: {} };
if (!acc[type].sources[packageName])
acc[type].sources[packageName] = { label: sourceLabel, checked: sourceChecked, packs: [] };
const checked = !excludedPackData[id] || !excludedPackData[id].excludedDocumentTypes.includes(type);
acc[type].sources[packageName].packs.push({
pack: id,
type,
label: id === game.system.id ? game.system.title : game.i18n.localize(label),
checked: checked
});
return acc;
}, {});
return context;
}
static #toggleSource(event, button) {
event.stopPropagation();
const { type, source } = button.dataset;
const currentlyExcluded = this.browserSettings.excludedSources[source]
? this.browserSettings.excludedSources[source].excludedDocumentTypes.includes(type)
: false;
if (!this.browserSettings.excludedSources[source])
this.browserSettings.excludedSources[source] = { excludedDocumentTypes: [] };
this.browserSettings.excludedSources[source].excludedDocumentTypes = currentlyExcluded
? this.browserSettings.excludedSources[source].excludedDocumentTypes.filter(x => x !== type)
: [...(this.browserSettings.excludedSources[source]?.excludedDocumentTypes ?? []), type];
const toggleIcon = button.querySelector('a > i');
toggleIcon.classList.toggle('fa-toggle-off');
toggleIcon.classList.toggle('fa-toggle-on');
button.closest('.source-container').querySelector('.checks-container').classList.toggle('collapsed');
}
toggleTypedPack(event) {
event.stopPropagation();
const { type, pack } = event.target.dataset;
const currentlyExcluded = this.browserSettings.excludedPacks[pack]
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.includes(type)
: false;
if (!this.browserSettings.excludedPacks[pack])
this.browserSettings.excludedPacks[pack] = { excludedDocumentTypes: [] };
this.browserSettings.excludedPacks[pack].excludedDocumentTypes = currentlyExcluded
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.filter(x => x !== type)
: [...(this.browserSettings.excludedPacks[pack]?.excludedDocumentTypes ?? []), type];
this.render();
}
static async #finish() {
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings);
await settings.updateSource(this.browserSettings);
await game.settings.set(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings,
settings.toObject()
);
this.updated = true;
this.close();
}
static async configure() {
return new Promise(resolve => {
const app = new this();
app.addEventListener('close', () => resolve(app.updated), { once: true });
app.render({ force: true });
});
}
}

View file

@ -16,4 +16,3 @@ export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
export { default as GroupRollDialog } from './group-roll-dialog.mjs';
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
export { default as RiskItAllDialog } from './riskItAllDialog.mjs';
export { default as CompendiumBrowserSettingsDialog } from './CompendiumBrowserSettings.mjs';

View file

@ -54,11 +54,7 @@ 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 ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
' ',
'&nbsp;'
);
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}

View file

@ -165,7 +165,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}
if (rest.hasOwnProperty('trait')) {
this.config.roll.trait = rest.trait;
if (!this.config.source.item)
this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(abilities[this.config.roll.trait]?.label)
});

View file

@ -200,6 +200,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
description: game.i18n.localize(this.selectedMove.description),
result: result,
open: autoExpandDescription ? 'open' : '',
chevron: autoExpandDescription ? 'fa-chevron-up' : 'fa-chevron-down',
showRiskItAllButton: this.showRiskItAllButton,
riskItAllButtonLabel: this.riskItAllButtonLabel,
riskItAllHope: this.riskItAllHope

View file

@ -196,9 +196,6 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
.filter(x => x.uuid !== this.actor.uuid);
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
const cls = getDocumentClass('ChatMessage');
const msg = {
user: game.user.id,
@ -219,8 +216,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
actor: { name: this.actor.name, img: this.actor.img },
moves: moves,
characters: characters,
selfId: this.actor.uuid,
open: autoExpandDescription ? 'open' : ''
selfId: this.actor.uuid
}
),
flags: {

View file

@ -70,11 +70,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
element.appendChild(img);
const label = document.createElement('span');
label.innerHTML =
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
' ',
'&nbsp;'
);
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
element.appendChild(label);
return element;
@ -123,11 +119,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
element.appendChild(img);
const label = document.createElement('span');
label.innerHTML =
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
' ',
'&nbsp;'
);
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
element.appendChild(label);
return element;

View file

@ -103,12 +103,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
: null;
break;
case 'downtime':
context.restOptions = {
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
longRest: CONFIG.DH.GENERAL.defaultRestOptions.longRest()
};
break;
}
return context;
@ -231,15 +225,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
}
static async removeItem(_, target) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize(`DAGGERHEART.SETTINGS.Homebrew.deleteItemTitle`)
},
content: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.deleteItemText')
});
if (!confirmed) return;
const { type, id } = target.dataset;
const isDowntime = ['shortRest', 'longRest'].includes(type);
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;

View file

@ -4,60 +4,20 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
constructor(options) {
super(options);
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
const getAllLeaves = (root, group, parentPath = '') => {
const leaves = [];
const rootKey = `${parentPath ? `${parentPath}.` : ''}${root.name}`;
for (const field of Object.values(root.fields)) {
if (field instanceof foundry.data.fields.SchemaField)
leaves.push(...getAllLeaves(field, group, rootKey));
else
leaves.push({
value: `${rootKey}.${field.name}`,
label: game.i18n.localize(field.label),
hint: game.i18n.localize(field.hint),
group
});
}
return leaves;
};
const ignoredActorKeys = ['config', 'DhEnvironment'];
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
if (ignoredActorKeys.includes(key)) return acc;
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 attributes = CONFIG.Token.documentClass.getTrackedAttributes(model.metadata.type);
const getTranslations = path => {
if (path === 'resources.hope.max')
return {
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxHope.label'),
hint: ''
};
const field = model.schema.getField(path);
return {
label: field ? game.i18n.localize(field.label) : path,
hint: field ? game.i18n.localize(field.hint) : ''
};
};
const bars = attributes.bar.flatMap(x => {
const joined = `${x.join('.')}.max`;
return { value: joined, ...getTranslations(joined), group };
});
const values = attributes.value.flatMap(x => {
const joined = x.join('.');
return { value: joined, ...getTranslations(joined), group };
});
const bonuses = getAllLeaves(model.schema.fields.bonuses, group);
const rules = getAllLeaves(model.schema.fields.rules, group);
acc.push(...bars, ...values, ...rules, ...bonuses);
const choices = CONFIG.Token.documentClass
.getTrackedAttributeChoices(attributes, model)
.map(x => ({ ...x, group: group }));
acc.push(...choices);
}
return acc;
}, []);
}
@ -108,18 +68,14 @@ 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.toLowerCase());
const matchIndex = label.toLowerCase().indexOf(search);
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 ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
' ',
'&nbsp;'
);
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}

View file

@ -103,11 +103,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
element.innerHTML =
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
' ',
'&nbsp;'
);
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}

View file

@ -433,7 +433,7 @@ export default function DHApplicationMixin(Base) {
icon: 'fa-solid fa-lightbulb',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.disabled && doc.type !== 'beastform';
return doc && !doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
},
@ -442,7 +442,7 @@ export default function DHApplicationMixin(Base) {
icon: 'fa-regular fa-lightbulb',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.disabled && doc.type !== 'beastform';
return doc && doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
}
@ -536,10 +536,6 @@ export default function DHApplicationMixin(Base) {
options.push({
name: 'CONTROLS.CommonDelete',
icon: 'fa-solid fa-trash',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.type !== 'beastform';
},
callback: async (target, event) => {
const doc = await getDocFromElement(target);
if (event.shiftKey) return doc.delete();

View file

@ -1,6 +1,7 @@
export default function ItemAttachmentSheet(Base) {
return class extends Base {
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
dragDrop: [
...(super.DEFAULT_OPTIONS.dragDrop || []),
{ dragSelector: null, dropSelector: '.attachments-section' }

View file

@ -1,5 +1,3 @@
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
/**
@ -19,15 +17,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig;
this.presets = {};
this.compendiumBrowserTypeKey = 'compendiumBrowserDefault';
this.setupHooks = Hooks.on(socketEvent.Refresh, ({ refreshType }) => {
if (refreshType === RefreshType.CompendiumBrowser) {
if (this.rendered) {
this.render();
this.loadItems();
}
}
});
}
/** @inheritDoc */
@ -46,8 +35,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
selectFolder: this.selectFolder,
expandContent: this.expandContent,
resetFilters: this.resetFilters,
sortList: this.sortList,
openSettings: this.openSettings
sortList: this.sortList
},
position: {
left: 100,
@ -169,8 +157,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
context.formatChoices = this.formatChoices;
context.items = this.items;
context.presets = this.presets;
context.isGM = game.user.isGM;
return context;
}
@ -228,10 +214,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
loadItems() {
let loadTimeout = this.toggleLoader(true);
const browserSettings = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings
);
const promises = [];
game.packs.forEach(pack => {
@ -245,7 +227,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
Promise.all(promises).then(async result => {
this.items = ItemBrowser.sortBy(
result.flatMap(r => r).filter(r => !browserSettings.isEntryExcluded.bind(browserSettings)(r)),
result.flatMap(r => r),
'name'
);
@ -530,22 +512,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
itemListContainer.replaceChildren(...newOrder);
}
static async openSettings() {
const settingsUpdated = await game.system.api.applications.dialogs.CompendiumBrowserSettingsDialog.configure();
if (settingsUpdated) {
if (this.rendered) {
this.render();
this.loadItems();
}
await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data: {
refreshType: RefreshType.CompendiumBrowser
}
});
}
}
_createDragProcess() {
new foundry.applications.ux.DragDrop.implementation({
dragSelector: '.item-container',
@ -605,9 +571,4 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
headerActions.append(button);
}
}
async close(options = {}) {
Hooks.off(socketEvent.Refresh, this.setupHooks);
await super.close(options);
}
}

View file

@ -1,4 +1,4 @@
import DhMeasuredTemplate from './measuredTemplate.mjs';
import DhMeasuredTemplate from "./measuredTemplate.mjs";
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
/** @inheritdoc */
@ -63,7 +63,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
const originRadius = (this.bounds.width * boundsCorrection) / 2;
const targetRadius = (target.bounds.width * boundsCorrection) / 2;
const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance;
return Math.floor(distance - originRadius - targetRadius + canvas.grid.distance);
return distance - originRadius - targetRadius + canvas.grid.distance;
}
// Compute what the closest grid space of each token is, then compute that distance
@ -85,7 +85,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
// Check if the setting is enabled
const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).showTokenDistance;
if (setting === 'never' || (setting === 'encounters' && !game.combat?.started)) return;
if (setting === "never" || (setting === "encounters" && !game.combat?.started)) return;
// Check if this token isn't invisible and is actually being hovered
const isTokenValid =

View file

@ -236,7 +236,6 @@ export const defaultRestOptions = {
actionType: 'action',
chatDisplay: false,
target: {
amount: 1,
type: 'friendly'
},
damage: {
@ -305,7 +304,6 @@ export const defaultRestOptions = {
actionType: 'action',
chatDisplay: false,
target: {
amount: 1,
type: 'friendly'
},
damage: {
@ -331,56 +329,7 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-dumbbell',
img: 'icons/skills/trades/academics-merchant-scribe.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'),
actions: {
prepare: {
type: 'healing',
systemPath: 'restMoves.shortRest.moves.prepare.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.name'),
img: 'icons/skills/trades/academics-merchant-scribe.webp',
actionType: 'action',
chatDisplay: false,
target: {
type: 'self'
},
damage: {
parts: [
{
applyTo: healingTypes.hope.id,
value: {
custom: {
enabled: true,
formula: '1'
}
}
}
]
}
},
prepareWithFriends: {
type: 'healing',
systemPath: 'restMoves.shortRest.moves.prepare.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepareWithFriends.name'),
img: 'icons/skills/trades/academics-merchant-scribe.webp',
actionType: 'action',
chatDisplay: false,
target: {
type: 'self'
},
damage: {
parts: [
{
applyTo: healingTypes.hope.id,
value: {
custom: {
enabled: true,
formula: '2'
}
}
}
]
}
}
},
actions: {},
effects: []
}
}),
@ -400,7 +349,6 @@ export const defaultRestOptions = {
actionType: 'action',
chatDisplay: false,
target: {
amount: 1,
type: 'friendly'
},
damage: {
@ -469,7 +417,6 @@ export const defaultRestOptions = {
actionType: 'action',
chatDisplay: false,
target: {
amount: 1,
type: 'friendly'
},
damage: {
@ -495,56 +442,7 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-dumbbell',
img: 'icons/skills/trades/academics-merchant-scribe.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'),
actions: {
prepare: {
type: 'healing',
systemPath: 'restMoves.longRest.moves.prepare.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.name'),
img: 'icons/skills/trades/academics-merchant-scribe.webp',
actionType: 'action',
chatDisplay: false,
target: {
type: 'self'
},
damage: {
parts: [
{
applyTo: healingTypes.hope.id,
value: {
custom: {
enabled: true,
formula: '1'
}
}
}
]
}
},
prepareWithFriends: {
type: 'healing',
systemPath: 'restMoves.longRest.moves.prepare.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepareWithFriends.name'),
img: 'icons/skills/trades/academics-merchant-scribe.webp',
actionType: 'action',
chatDisplay: false,
target: {
type: 'self'
},
damage: {
parts: [
{
applyTo: healingTypes.hope.id,
value: {
custom: {
enabled: true,
formula: '2'
}
}
}
]
}
}
},
actions: {},
effects: []
},
workOnAProject: {

View file

@ -30,7 +30,6 @@ export const gameSettings = {
LastMigrationVersion: 'LastMigrationVersion',
TagTeamRoll: 'TagTeamRoll',
SpotlightRequestQueue: 'SpotlightRequestQueue',
CompendiumBrowserSettings: 'CompendiumBrowserSettings'
};
export const actionAutomationChoices = {

View file

@ -3,7 +3,6 @@ export { default as DhCombatant } from './combatant.mjs';
export { default as DhTagTeamRoll } from './tagTeamRoll.mjs';
export { default as DhRollTable } from './rollTable.mjs';
export { default as RegisteredTriggers } from './registeredTriggers.mjs';
export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs';
export * as countdowns from './countdowns.mjs';
export * as actions from './action/_module.mjs';

View file

@ -229,7 +229,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
if (this.chatDisplay && !config.actionChatMessageHandled) await this.toChat();
if (this.chatDisplay) await this.toChat();
return config;
}
@ -240,13 +240,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
* @returns {object}
*/
prepareBaseConfig(event) {
const isActor = this.item instanceof CONFIG.Actor.documentClass;
const actionTitle = game.i18n.localize(this.name);
const itemTitle = isActor || this.item.name === actionTitle ? '' : `${this.item.name} - `;
const config = {
event,
title: `${itemTitle}${actionTitle}`,
title: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
source: {
item: this.item._id,
originItem: this.originItem,

View file

@ -40,14 +40,7 @@ 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,
label: 'DAGGERHEART.ACTIONS.Settings.criticalThreshold'
}),
criticalThreshold: new fields.NumberField({ required: true, integer: true, min: 1, max: 20, initial: 20 }),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({
required: true,

View file

@ -29,40 +29,17 @@ const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
/* Common rules applying to Characters and Adversaries */
export const commonActorRules = (extendedData = { damageReduction: {}, attack: { damage: {} } }) => ({
conditionImmunities: new fields.SchemaField({
hidden: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.hidden'
}),
restrained: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.restrained'
}),
vulnerable: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
})
hidden: new fields.BooleanField({ initial: false }),
restrained: new fields.BooleanField({ initial: false }),
vulnerable: new fields.BooleanField({ initial: false })
}),
damageReduction: new fields.SchemaField({
thresholdImmunities: new fields.SchemaField({
minor: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.thresholdImmunities.minor.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.thresholdImmunities.minor.hint'
})
minor: new fields.BooleanField({ initial: false })
}),
reduceSeverity: new fields.SchemaField({
magical: new fields.NumberField({
initial: 0,
min: 0,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.reduceSeverity.magical.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.reduceSeverity.magical.hint'
}),
physical: new fields.NumberField({
initial: 0,
min: 0,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.reduceSeverity.physical.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.reduceSeverity.physical.hint'
})
magical: new fields.NumberField({ initial: 0, min: 0 }),
physical: new fields.NumberField({ initial: 0, min: 0 })
}),
...(extendedData.damageReduction ?? {})
}),
@ -72,16 +49,12 @@ export const commonActorRules = (extendedData = { damageReduction: {}, attack: {
hpDamageMultiplier: new fields.NumberField({
required: true,
nullable: false,
initial: 1,
label: 'DAGGERHEART.GENERAL.Attack.hpDamageMultiplier.label',
hint: 'DAGGERHEART.GENERAL.Attack.hpDamageMultiplier.hint'
initial: 1
}),
hpDamageTakenMultiplier: new fields.NumberField({
required: true,
nullable: false,
initial: 1,
label: 'DAGGERHEART.GENERAL.Attack.hpDamageTakenMultiplier.label',
hint: 'DAGGERHEART.GENERAL.Attack.hpDamageTakenMultiplier.hint'
initial: 1
}),
...(extendedData.attack?.damage ?? {})
})

View file

@ -35,8 +35,7 @@ export default class DhCharacter extends BaseDataActor {
'DAGGERHEART.ACTORS.Character.maxHPBonus'
),
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.SchemaField(
{
hope: new fields.SchemaField({
value: new fields.NumberField({
initial: 2,
min: 0,
@ -44,9 +43,7 @@ export default class DhCharacter extends BaseDataActor {
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'),
@ -225,16 +222,8 @@ export default class DhCharacter extends BaseDataActor {
rules: new fields.SchemaField({
...commonActorRules({
damageReduction: {
magical: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.magical.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.magical.hint'
}),
physical: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.physical.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.physical.hint'
}),
magical: new fields.BooleanField({ initial: false }),
physical: new fields.BooleanField({ initial: false }),
maxArmorMarked: new fields.SchemaField({
value: new fields.NumberField({
required: true,
@ -264,10 +253,7 @@ export default class DhCharacter extends BaseDataActor {
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
}),
disabledArmor: new fields.BooleanField({
intial: false,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.disabledArmor.label'
})
disabledArmor: new fields.BooleanField({ intial: false })
},
attack: {
damage: {
@ -315,14 +301,12 @@ export default class DhCharacter extends BaseDataActor {
label: 'DAGGERHEART.ACTORS.Character.defaultFearDice'
})
}),
runeWard: new fields.BooleanField({ initial: false }),
burden: new fields.SchemaField({
ignore: new fields.BooleanField({ label: 'DAGGERHEART.ACTORS.Character.burden.ignore.label' })
ignore: new fields.BooleanField()
}),
roll: new fields.SchemaField({
guaranteedCritical: new fields.BooleanField({
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
})
guaranteedCritical: new fields.BooleanField()
})
})
};

View file

@ -53,18 +53,9 @@ export default class DhCompanion extends BaseDataActor {
),
rules: new fields.SchemaField({
conditionImmunities: new fields.SchemaField({
hidden: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.hidden'
}),
restrained: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.restrained'
}),
vulnerable: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
})
hidden: new fields.BooleanField({ initial: false }),
restrained: new fields.BooleanField({ initial: false }),
vulnerable: new fields.BooleanField({ initial: false })
})
}),
attack: new ActionField({

View file

@ -31,7 +31,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
title: new fields.StringField(),
actionDescription: new fields.HTMLField(),
roll: new fields.ObjectField(),
targets: targetsField(),
hasRoll: new fields.BooleanField({ initial: false }),

View file

@ -1,35 +0,0 @@
export default class CompendiumBrowserSettings extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
excludedSources: new fields.TypedObjectField(
new fields.SchemaField({
excludedDocumentTypes: new fields.ArrayField(
new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES })
)
})
),
excludedPacks: new fields.TypedObjectField(
new fields.SchemaField({
excludedDocumentTypes: new fields.ArrayField(
new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES })
)
})
)
};
}
isEntryExcluded(item) {
const pack = game.packs.get(item.pack);
if (!pack) return false;
const excludedSourceData = this.excludedSources[pack.metadata.packageName];
if (excludedSourceData && excludedSourceData.excludedDocumentTypes.includes(pack.metadata.type)) return true;
const excludedPackData = this.excludedPacks[item.pack];
if (excludedPackData && excludedPackData.excludedDocumentTypes.includes(pack.metadata.type)) return true;
return false;
}
}

View file

@ -68,8 +68,6 @@ export default class DamageField extends fields.SchemaField {
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
if (!damageResult) return false;
if (damageResult.actionChatMessageHandled) config.actionChatMessageHandled = true;
config.damage = damageResult.damage;
config.message ??= damageConfig.message;
}
@ -109,8 +107,8 @@ export default class DamageField extends fields.SchemaField {
);
else {
const configDamage = foundry.utils.deepClone(config.damage);
const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
const hpDamageMultiplier = config.actionActor?.system.rules.attack.damage.hpDamageMultiplier ?? 1;
const hpDamageTakenMultiplier = actor.system.rules.attack.damage.hpDamageTakenMultiplier;
if (configDamage.hitPoints) {
for (const part of configDamage.hitPoints.parts) {
part.total = Math.ceil(part.total * hpDamageMultiplier * hpDamageTakenMultiplier);

View file

@ -262,9 +262,6 @@ export function ActionMixin(Base) {
}
async toChat(origin) {
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
@ -293,7 +290,7 @@ export function ActionMixin(Base) {
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/action.hbs',
{ ...systemData, open: autoExpandDescription ? 'open' : '' }
systemData
),
flags: {
daggerheart: {

View file

@ -7,20 +7,16 @@ const attributeField = label =>
});
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
new fields.SchemaField(
{
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) })
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({

View file

@ -253,20 +253,4 @@ export default class DHBeastform extends BaseDataItem {
return false;
}
_onCreate(_data, _options, userId) {
if (!this.actor && game.user.id === userId) {
const hasBeastformEffect = this.parent.effects.some(x => x.type === 'beastform');
if (!hasBeastformEffect)
this.parent.createEmbeddedDocuments('ActiveEffect', [
{
type: 'beastform',
name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
}
]);
return;
}
}
}

View file

@ -6,12 +6,7 @@ export default class DhLevelData extends foundry.abstract.DataModel {
return {
level: new fields.SchemaField({
current: new fields.NumberField({
required: true,
integer: true,
initial: 1,
label: 'DAGGERHEART.GENERAL.currentLevel'
}),
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
changed: new fields.NumberField({ required: true, integer: true, initial: 1 }),
bonuses: new fields.TypedObjectField(new fields.NumberField({ integer: true, nullable: false }))
}),

View file

@ -37,7 +37,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
extendEnvironmentDescriptions: new BooleanField(),
extendItemDescriptions: new BooleanField(),
expandRollMessage: new SchemaField({
desc: new BooleanField({ initial: true }),
desc: new BooleanField(),
roll: new BooleanField(),
damage: new BooleanField(),
target: new BooleanField()

View file

@ -96,19 +96,6 @@ export default class DHRoll extends Roll {
}
static async toMessage(roll, config) {
const item = config.data.parent?.items?.get?.(config.source.item) ?? null;
const action = item ? item.system.actions.get(config.source.action) : null;
let actionDescription = null;
if (action?.chatDisplay) {
actionDescription = action
? await foundry.applications.ux.TextEditor.implementation.enrichHTML(action.description, {
relativeTo: config.data,
rollData: config.data.getRollData?.() ?? {}
})
: null;
config.actionChatMessageHandled = true;
}
const cls = getDocumentClass('ChatMessage'),
msgData = {
type: this.messageType,
@ -116,7 +103,7 @@ export default class DHRoll extends Roll {
title: roll.title,
speaker: cls.getSpeaker({ actor: roll.data?.parent }),
sound: config.mute ? null : CONFIG.sounds.dice,
system: { ...config, actionDescription },
system: config,
rolls: [roll]
};

View file

@ -61,15 +61,14 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
update.img = 'icons/magic/life/heart-cross-blue.webp';
}
const statuses = Object.keys(data.statuses ?? {});
const immuneStatuses =
statuses.filter(
data.statuses?.filter(
status =>
this.parent.system.rules?.conditionImmunities &&
this.parent.system.rules.conditionImmunities[status]
) ?? [];
if (immuneStatuses.length > 0) {
update.statuses = statuses.filter(x => !immuneStatuses.includes(x));
update.statuses = data.statuses.filter(x => !immuneStatuses.includes(x));
const conditions = CONFIG.DH.GENERAL.conditions();
const scrollingTexts = immuneStatuses.map(status => ({
text: game.i18n.format('DAGGERHEART.ACTIVEEFFECT.immuneStatusText', {
@ -114,11 +113,6 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
super.applyField(model, change, field);
}
_applyLegacy(actor, change, changes) {
change.value = DhActiveEffect.getChangeValue(actor, change, change.effect);
super._applyLegacy(actor, change, changes);
}
/** */
static getChangeValue(model, change, effect) {
let value = change.value;

View file

@ -110,8 +110,6 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
} else if (s.classList.contains('damage-section'))
s.classList.toggle('expanded', autoExpandRoll.damage);
else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
else if (s.classList.contains('description-section'))
s.classList.toggle('expanded', autoExpandRoll.desc);
});
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
}

View file

@ -1,30 +1,78 @@
export default class DHToken extends CONFIG.Token.documentClass {
/**@inheritdoc */
static getTrackedAttributeChoices(attributes, typeKey) {
/**
* 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) {
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('.');
return { group: barGroup, value: a, label: getLabel(a) };
const modelLabel = model ? game.i18n.localize(model.schema.getField(`${a}.value`).label) : null;
return { group: barGroup, value: a, label: modelLabel ? modelLabel : a };
});
bars.sort((a, b) => a.value.compare(b.value));
bars.sort((a, b) => a.label.compare(b.label));
const values = attributes.value.map(v => {
const invalidAttributes = [
'gold',
'levelData',
'actions',
'biography',
'class',
'multiclass',
'companion',
'notes',
'partner',
'description',
'impulses',
'tier',
'type'
];
const values = attributes.value.reduce((acc, v) => {
const a = v.join('.');
return { group: valueGroup, value: a, label: getLabel(a) };
});
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));
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;
}

View file

@ -39,7 +39,6 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs',
'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/description-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',

View file

@ -7,7 +7,7 @@ import {
DhHomebrewSettings,
DhVariantRuleSettings
} from '../applications/settings/_module.mjs';
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
import { DhTagTeamRoll } from '../data/_module.mjs';
export const registerDHSettings = () => {
registerMenuSettings();
@ -142,12 +142,6 @@ const registerNonConfigSettings = () => {
config: false,
type: DhTagTeamRoll
});
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
scope: 'client',
config: false,
type: CompendiumBrowserSettings
});
};
/**

View file

@ -38,8 +38,7 @@ export const RefreshType = {
Countdown: 'DhCoundownRefresh',
TagTeamRoll: 'DhTagTeamRollRefresh',
EffectsDisplay: 'DhEffectsDisplayRefresh',
Scene: 'DhSceneRefresh',
CompendiumBrowser: 'DhCompendiumBrowserRefresh'
Scene: 'DhSceneRefresh'
};
export const registerSocketHooks = () => {

View file

@ -110,7 +110,7 @@
"startRound": null,
"startTurn": null
},
"description": "<p class=\"Body-Foundation\">Add your Strength to the presence roll.</p>",
"description": "<p class=\"Body-Foundation\">Add your Strength to the presence roll roll.</p>",
"tint": "#ffffff",
"statuses": [],
"sort": 0,

View file

@ -170,8 +170,7 @@
"value": 1,
"recovery": "shortRest",
"max": "1",
"icon": "",
"progression": "decreasing"
"icon": ""
},
"attribution": {
"source": "Daggerheart SRD",

View file

@ -1,105 +0,0 @@
.daggerheart.dialog.dh-style.views.compendium-brower-settings {
--text-color: light-dark(@dark-blue, @beige);
color: var(--text-color);
.window-content {
justify-content: space-between;
> div {
overflow: auto;
display: flex;
flex-direction: column;
max-height: 440px;
}
}
.types-container {
display: flex;
flex-direction: column;
gap: 8px;
.type-container {
display: flex;
flex-direction: column;
gap: 8px;
> label {
display: flex;
align-items: center;
font-size: var(--font-size-16);
font-family: @font-subtitle;
font-weight: bold;
&::before {
content: '';
flex: 1;
height: 2px;
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%);
margin-right: 8px;
}
&::after {
content: '';
flex: 1;
height: 2px;
background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%);
margin-left: 8px;
}
}
.sources-container {
display: flex;
flex-direction: column;
gap: 8px;
.source-container {
display: flex;
flex-direction: column;
gap: 2px;
.source-inner-container {
display: flex;
justify-content: space-between;
.source-inner-label-container {
width: 100%;
display: flex;
gap: 8px;
i {
font-size: 18px;
// color: light-dark(@dark-blue, @golden);
}
}
}
}
}
}
}
.checks-container {
padding-left: 24px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
transition: height 0.4s ease-in-out;
overflow: hidden;
&.collapsed {
height: 0px;
}
.check-container {
display: flex;
align-items: center;
}
}
footer {
margin-top: 8px;
display: flex;
button {
flex: 1;
}
}
}

View file

@ -17,9 +17,7 @@
.dialog-header-inner {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
justify-content: center;
}
h1 {
@ -47,29 +45,6 @@
}
}
.reaction-chip {
display: flex;
align-items: center;
border-radius: 5px;
width: fit-content;
gap: 5px;
cursor: pointer;
padding: 5px;
background: light-dark(@dark-blue-10, @golden-10);
color: light-dark(@dark-blue, @golden);
.label {
font-style: normal;
font-weight: 400;
font-size: var(--font-size-14);
line-height: 17px;
}
&.selected {
background: light-dark(@dark-blue-40, @golden-40);
}
}
.tag-team-controller {
display: flex;
align-items: center;

View file

@ -43,5 +43,3 @@
@import './risk-it-all/sheet.less';
@import './character-reset/sheet.less';
@import './compendiumBrowserPackDialog/sheet.less';

View file

@ -52,14 +52,6 @@
}
}
input[type='checkbox'] {
&:indeterminate {
&::before {
content: '\f0fe';
}
}
}
input[type='checkbox'],
input[type='radio'] {
height: 20px;

View file

@ -38,6 +38,124 @@
flex-direction: column;
align-items: center;
details[open] {
.fa-chevron-down {
transform: rotate(180deg);
transition: all 0.3s ease;
}
}
.action-move {
width: 100%;
.fa-chevron-down {
transition: all 0.3s ease;
margin-left: auto;
}
.action-section {
display: flex;
flex-direction: row;
align-items: center;
margin: 8px 8px 0;
padding-bottom: 5px;
width: -webkit-fill-available;
gap: 5px;
border-bottom: 1px solid @golden;
&:hover {
background: @golden-10;
cursor: pointer;
transition: all 0.3s ease;
}
.action-img {
width: 40px;
height: 40px;
border-radius: 3px;
object-fit: cover;
}
.action-header {
display: flex;
flex-direction: column;
gap: 5px;
color: @beige;
.title {
font-size: var(--font-size-20);
color: @golden;
font-weight: 700;
}
.label {
font-size: var(--font-size-12);
color: @beige;
margin: 0;
}
}
}
}
.description {
padding: 8px;
.summons-header {
font-size: var(--font-size-14);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
span {
width: 100%;
}
&:before,
&:after {
content: ' ';
height: 1px;
width: 100%;
}
&:before {
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%);
}
&:after {
background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%);
}
}
.summons-container {
display: flex;
flex-direction: column;
gap: 4px;
.summon-container {
display: flex;
align-items: center;
justify-content: space-between;
.summon-label-container {
flex: 1;
display: flex;
align-items: center;
gap: 4px;
img {
height: 32px;
}
label {
display: flex;
flex-wrap: wrap;
}
}
}
}
}
.ability-card-footer {
display: flex;
flex-wrap: wrap;

View file

@ -228,15 +228,6 @@
font-size: var(--font-size-12);
padding: 0 20px;
.roll-part-title {
text-align: center;
font-family: @font-subtitle;
font-size: var(--font-size-18);
font-weight: bold;
color: light-dark(@dark-blue, var(--text-color));
margin-bottom: -2px;
}
> .roll-part-header {
font-size: var(--font-size-14);
}
@ -295,7 +286,6 @@
> :first-child:not(.target-selector) {
margin-top: 5px;
text-align: center;
}
> :last-child {
@ -583,30 +573,6 @@
}
}
.chat-roll .description-section {
.roll-part-content {
.dice-tooltip {
.wrapper {
i {
margin: 0;
:first-child {
margin-top: 0;
}
:last-child {
margin-bottom: 0;
}
}
> :first-child:not(.target-selector) {
margin: 0;
}
}
}
}
}
.roll-buttons {
display: flex;
gap: 5px;
@ -624,124 +590,5 @@
.dice-roll .dice-tooltip fieldset {
margin-bottom: 5px;
}
details[open] {
.fa-chevron-down {
transform: rotate(180deg);
transition: all 0.3s ease;
}
}
.action-move {
width: 100%;
.fa-chevron-down {
transition: all 0.3s ease;
margin-left: auto;
}
.action-section {
display: flex;
flex-direction: row;
align-items: center;
margin: 8px 8px 0;
padding-bottom: 5px;
width: -webkit-fill-available;
gap: 5px;
border-bottom: 1px solid @golden;
&:hover {
background: @golden-10;
cursor: pointer;
transition: all 0.3s ease;
}
.action-img {
width: 40px;
height: 40px;
border-radius: 3px;
object-fit: cover;
}
.action-header {
display: flex;
flex-direction: column;
gap: 5px;
color: @beige;
.title {
font-size: var(--font-size-20);
color: @golden;
font-weight: 700;
margin: 0;
}
.label {
font-size: var(--font-size-12);
color: @beige;
margin: 0;
}
}
}
}
.description {
padding: 8px;
.summons-header {
font-size: var(--font-size-14);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
span {
width: 100%;
}
&:before,
&:after {
content: ' ';
height: 1px;
width: 100%;
}
&:before {
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%);
}
&:after {
background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%);
}
}
.summons-container {
display: flex;
flex-direction: column;
gap: 4px;
.summon-container {
display: flex;
align-items: center;
justify-content: space-between;
.summon-label-container {
flex: 1;
display: flex;
align-items: center;
gap: 4px;
img {
height: 32px;
}
label {
display: flex;
flex-wrap: wrap;
}
}
}
}
}
}
}

View file

@ -1,17 +1,13 @@
#ui-left #ui-left-column-2 {
flex: 0 0 230px;
.scene-navigation {
.scene-wrapper {
display: flex;
gap: 2px;
height: var(--control-size);
width: 100%;
> ul {
margin: 0;
padding: 0;
}
.scene-environment {
padding: 0;
@ -37,3 +33,4 @@
max-width: 200px;
}
}
}

View file

@ -32,6 +32,7 @@
li[role='option'] {
display: flex;
align-items: center;
gap: 10px;
font-size: var(--font-size-14);
padding: 0 10px;
cursor: pointer;

View file

@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
"version": "1.7.1",
"version": "1.6.4",
"compatibility": {
"minimum": "13.346",
"verified": "13.351",

View file

@ -1,3 +0,0 @@
<footer>
<button data-action="finish">{{localize "Save Settings"}}</button>
</footer>

View file

@ -1,36 +0,0 @@
<div>
<div class="types-container">
{{#each typePackCollections as |type|}}
<div class="type-container">
<label>{{type.label}}</label>
<div class="sources-container">
{{#each type.sources as |source|}}
<div class="source-container">
<div class="source-inner-container">
<div class="source-inner-label-container">
<a
data-action="toggleSource" data-source="{{@key}}" data-type="{{@../key}}"
data-tooltip="{{#if source.checked}}{{localize "DAGGERHEART.APPLICATIONS.CompendiumBrowserSettings.disableSource"}}{{else}}{{localize "DAGGERHEART.APPLICATIONS.CompendiumBrowserSettings.enableSource"}}{{/if}}"
>
<i class="fa-solid {{#if source.checked}}fa-toggle-on{{else}}fa-toggle-off{{/if}}"></i>
</a>
<label>{{source.label}}</label>
</div>
</div>
<div class="checks-container {{#unless source.checked}}collapsed{{/unless}}">
{{#each source.packs as |pack|}}
<div class="check-container">
<input type="checkbox" class="pack-checkbox" data-type="{{pack.type}}" data-pack="{{pack.pack}}" {{checked pack.checked}} />
<label>{{pack.label}}</label>
</div>
{{/each}}
</div>
</div>
{{/each}}
</div>
</div>
{{/each}}
</div>
</div>

View file

@ -1,12 +1,17 @@
<header class="dialog-header">
<div class="dialog-header-inner">
<h1>{{ifThen rollConfig.headerTitle rollConfig.headerTitle rollConfig.title}}</h1>
{{#if showReaction}}
<div class="reaction-chip {{#if reactionOverride}}selected{{/if}}" data-action="toggleReaction">
<span><i class="{{ifThen reactionOverride "fa-solid" "fa-regular"}} fa-circle"></i></span>
<span class="label">{{localize "DAGGERHEART.GENERAL.reactionRoll"}}</span>
</div>
<h1>
{{#if reactionOverride}}
{{localize "DAGGERHEART.CONFIG.FeatureForm.reaction"}}
{{else}}
{{ifThen rollConfig.headerTitle rollConfig.headerTitle rollConfig.title}}
{{/if}}
{{#if showReaction}}
<button class="reaction-roll-controller {{#if reactionOverride}}active{{/if}}" data-action="toggleReaction" data-tooltip-text="{{localize "DAGGERHEART.GENERAL.reactionRoll"}}">
<i class="fa-solid fa-reply"></i>
</button>
{{/if}}
</h1>
</div>
{{#if (and @root.hasRoll @root.activeTagTeamRoll)}}
<div class="tag-team-controller {{#if @root.tagTeamSelected}}selected{{/if}}" data-action="toggleTagTeamRoll">

View file

@ -118,7 +118,7 @@
{{#if name}}
<div class="experience-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}" data-tooltip="{{this.description}}">
<span><i class="{{ifThen (includes ../selectedExperiences id) "fa-solid" "fa-regular"}} fa-circle"></i></span>
<span class="label">{{name}} {{numberFormat value sign=true}}</span>
<span class="label">{{name}} +{{value}}</span>
</div>
{{/if}}
{{/each}}

View file

@ -145,7 +145,7 @@
{{#each document.system.experiences as |experience id|}}
<div class="experience-row" data-tooltip-text="{{experience.description}}">
<span class="experience-value">
{{numberFormat experience.value sign=true}}
+{{experience.value}}
</span>
<span class="experience-name">{{experience.name}}</span>
<div class="controls">

View file

@ -113,7 +113,7 @@ Parameters:
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.inVault 'sendToLoadout' 'sendToVault' }}">
<i class="fa-solid {{ifThen item.system.inVault 'fa-arrow-up' 'fa-arrow-down'}}"></i>
</a>
{{else if (and (eq type 'effect') (not (eq item.type 'beastform')))}}
{{else if (eq type 'effect')}}
<a data-action="toggleEffect"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.disabled 'enableEffect' 'disableEffect' }}">
<i class="{{ifThen item.disabled 'fa-solid fa-toggle-off' 'fa-solid fa-toggle-on'}}"></i>

View file

@ -1,5 +1,5 @@
<div class="daggerheart chat action">
<details class="action-move" {{this.open}}>
<details class="action-move">
<summary class="action-section">
<img class="action-img" src="{{action.img}}" />
<div class="action-header">

View file

@ -7,7 +7,7 @@
<h2 class="title">{{this.title}}</h2>
<span class="label">{{localize 'DAGGERHEART.UI.Chat.deathMove.title'}}</span>
</div>
<i class="fa-solid fa-chevron-down"></i>
<i class="fa-solid {{this.chevron}}"></i>
</summary>
<div class="description">
{{{this.description}}}

View file

@ -1,7 +1,7 @@
<div class="daggerheart chat downtime">
<ul class="downtime-moves-list">
{{#each moves as | move index |}}
<details class="downtime-move" {{@root.open}}>
<details class="downtime-move">
<summary class="downtime-label">
<img class="downtime-image" src="{{move.img}}" />
<div class="header-label">

View file

@ -1,11 +0,0 @@
<div class="roll-part dice-roll description-section" data-action="expandRoll">
<div class="roll-part-header"><div><span>{{localize "DAGGERHEART.GENERAL.description"}}</span></div></div>
<div class="roll-part-content description-content">
<div class="dice-tooltip">
<div class="wrapper">
<i>{{{actionDescription}}}</i>
</div>
</div>
</div>
</div>

View file

@ -1,10 +1,6 @@
<div class="chat-roll">
<div class="roll-part-title"><span>{{title}}</span></div>
{{#if actionDescription}}{{> 'systems/daggerheart/templates/ui/chat/parts/description-part.hbs'}}{{/if}}
{{#if hasRoll}}
<div class="roll-part-header"><span>{{localize "Result"}}</span></div>
{{> 'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs'}}
{{/if}}
<div class="roll-part-header"><span>{{title}}</span></div>
{{#if hasRoll}}{{> 'systems/daggerheart/templates/ui/chat/parts/roll-part.hbs'}}{{/if}}
{{#if (or hasDamage hasHealing)}}{{> 'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs'}}{{/if}}
{{#if hasTarget}}{{> 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs'}}{{/if}}
<div class="roll-part-header"><div></div></div>

View file

@ -1,8 +1,7 @@
<div class="compendium-sidebar">
{{#if isGM}}<button data-action="openSettings"><i class="fa-solid fa-gear"></i> {{localize "DAGGERHEART.UI.ItemBrowser.browserSettings"}}</button>{{/if}}
<div class="folder-list">
{{#each compendiums}}
<div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{id}}" {{#if folders.length}}data-tooltip="DAGGERHEART.UI.Tooltip.rightClickExtend" data-tooltip-direction="RIGHT"{{/if}}>{{label}}</div>
<div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{id}}" {{#if folders.length}}data-tooltip="DAGGERHEART.UI.Tooltip.rightClickExtand" data-tooltip-direction="RIGHT"{{/if}}>{{label}}</div>
{{#if folders.length}}
<div class="subfolder-list">
<div class="wrapper">

View file

@ -7,8 +7,7 @@
<menu id="scene-navigation-active" class="scene-navigation-menu flexcol">
{{#each scenes.active as |scene|}}
<li class="scene-wrapper">
<ul>
<li class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<div class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<span class="scene-name ellipsis">{{scene.name}}</span>
{{#if scene.users}}
<ul class="scene-players">
@ -18,8 +17,7 @@
{{/each}}
</ul>
{{/if}}
</li>
</ul>
</div>
{{#if scene.hasEnvironments}}
<button class="ui-control scene-environment {{#if (gt scene.environments.length 1)}}many-environments{{/if}}" data-action="openSceneEnvironment" data-scene-id="{{scene.id}}"><img src="{{scene.environmentImage}}" /> </button>
{{/if}}
@ -29,11 +27,9 @@
<menu id="scene-navigation-inactive" class="scene-navigation-menu flexcol">
{{#each scenes.inactive as |scene|}}
<li class="scene-wrapper">
<ul>
<li class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<div class="ui-control scene {{scene.cssClass}}" data-scene-id="{{scene.id}}" data-action="viewScene" {{#if scene.tooltip}}data-tooltip-text="{{scene.tooltip}}"{{/if}}>
<span class="scene-name ellipsis">{{scene.name}}</span>
</li>
</ul>
</div>
</li>
{{/each}}
</menu>

View file

@ -18,7 +18,7 @@ const foundryPath = process.env.FOUNDRY_MAIN_PATH || '../../../../FoundryDev/mai
const dataPath = process.env.FOUNDRY_DATA_PATH || '../../../';
// Run the original command with proper environment
const args = ['rollup -c --watch', `node "\"${foundryPath}\"" --dataPath="${dataPath}" --noupnp`, 'gulp'];
const args = ['rollup -c --watch', `node "${foundryPath}" --dataPath="${dataPath}" --noupnp`, 'gulp'];
spawn('npx', ['concurrently', ...args.map(arg => `"${arg}"`)], {
stdio: 'inherit',