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,10 +165,9 @@ 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)
});
this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(abilities[this.config.roll.trait]?.label)
});
}
this.config.extraFormula = rest.extraFormula;
this.render();

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;
const model = game.system.api.models.actors[key];
const group = game.i18n.localize(model.metadata.label);
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model.metadata.type);
const 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);
if (!ignoredActorKeys.includes(key)) {
const model = game.system.api.models.actors[key];
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
// As per DHToken._getTrackedAttributesFromSchema, attributes.bar have a max version as well.
const maxAttributes = attributes.bar.map(x => [...x, 'max']);
attributes.value.push(...maxAttributes);
const group = game.i18n.localize(model.metadata.label);
const choices = CONFIG.Token.documentClass
.getTrackedAttributeChoices(attributes, model)
.map(x => ({ ...x, group: group }));
acc.push(...choices);
}
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,18 +35,15 @@ export default class DhCharacter extends BaseDataActor {
'DAGGERHEART.ACTORS.Character.maxHPBonus'
),
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.SchemaField(
{
value: new fields.NumberField({
initial: 2,
min: 0,
integer: true,
label: 'DAGGERHEART.GENERAL.hope'
}),
isReversed: new fields.BooleanField({ initial: false })
},
{ label: 'DAGGERHEART.GENERAL.hope' }
)
hope: new fields.SchemaField({
value: new fields.NumberField({
initial: 2,
min: 0,
integer: true,
label: 'DAGGERHEART.GENERAL.hope'
}),
isReversed: new fields.BooleanField({ initial: false })
})
}),
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(
{
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
max: new fields.NumberField({
initial: max,
integer: true,
label:
maxLabel ??
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
}),
isReversed: new fields.BooleanField({ initial: reverse })
},
{ label }
);
new fields.SchemaField({
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
max: new fields.NumberField({
initial: max,
integer: true,
label:
maxLabel ?? game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
}),
isReversed: new fields.BooleanField({ initial: reverse })
});
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

@ -388,7 +388,7 @@
"name": "Fumigation",
"type": "feature",
"system": {
"description": "<p>Drop a smoke bomb that fills the air within Close range with smoke, Dizzying all targets in this area. Dizzied targets have disadvantage on their next action roll, then clear the condition.</p><p>@Template[type:emanation|range:c]</p>",
"description": "<p>Drop a smoke bomb that fi lls the air within Close range with smoke, Dizzying all targets in this area. Dizzied targets have disadvantage on their next action roll, then clear the condition.</p><p>@Template[type:emanation|range:c]</p>",
"resource": null,
"actions": {
"sp7RfJRQJsEUm09m": {

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,39 +1,36 @@
#ui-left #ui-left-column-2 {
flex: 0 0 230px;
.scene-wrapper {
display: flex;
gap: 2px;
height: var(--control-size);
width: 100%;
.scene-navigation {
.scene-wrapper {
display: flex;
gap: 2px;
height: var(--control-size);
width: 100%;
> ul {
margin: 0;
padding: 0;
}
.scene-environment {
padding: 0;
.scene-environment {
padding: 0;
img {
border-radius: 4px;
img {
border-radius: 4px;
}
}
}
}
.scene {
justify-content: center;
align-content: center;
background: var(--control-bg-color);
border: 1px solid var(--control-border-color);
border-radius: 4px;
color: var(--control-icon-color);
pointer-events: all;
transition:
border 0.25s,
color 0.25s;
text-shadow: none;
width: 200px;
max-width: 200px;
.scene {
justify-content: center;
align-content: center;
background: var(--control-bg-color);
border: 1px solid var(--control-border-color);
border-radius: 4px;
color: var(--control-icon-color);
pointer-events: all;
transition:
border 0.25s,
color 0.25s;
text-shadow: none;
width: 200px;
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>
{{/if}}
<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,19 +7,17 @@
<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}}>
<span class="scene-name ellipsis">{{scene.name}}</span>
{{#if scene.users}}
<ul class="scene-players">
{{#each scene.users as |user|}}
<li class="scene-player" style="--color-bg:{{user.color}}; --color-border:{{user.border}}"
data-tooltip aria-label="{{user.name}}">{{user.letter}}</li>
{{/each}}
</ul>
{{/if}}
</li>
</ul>
<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">
{{#each scene.users as |user|}}
<li class="scene-player" style="--color-bg:{{user.color}}; --color-border:{{user.border}}"
data-tooltip aria-label="{{user.name}}">{{user.letter}}</li>
{{/each}}
</ul>
{{/if}}
</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',