mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-07 14:36:13 +01:00
Merged with main
This commit is contained in:
commit
063ff3d999
57 changed files with 1074 additions and 285 deletions
136
module/applications/dialogs/CompendiumBrowserSettings.mjs
Normal file
136
module/applications/dialogs/CompendiumBrowserSettings.mjs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -16,3 +16,4 @@ 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';
|
||||
|
|
|
|||
|
|
@ -54,7 +54,11 @@ export default class AttributionDialog extends HandlebarsApplicationMixin(Applic
|
|||
const after = label.slice(matchIndex + search.length, label.length);
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
element.innerHTML =
|
||||
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
|
||||
' ',
|
||||
' '
|
||||
);
|
||||
if (item.hint) {
|
||||
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,9 +165,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
if (rest.hasOwnProperty('trait')) {
|
||||
this.config.roll.trait = rest.trait;
|
||||
this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: game.i18n.localize(abilities[this.config.roll.trait]?.label)
|
||||
});
|
||||
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.extraFormula = rest.extraFormula;
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -70,7 +70,11 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
element.appendChild(img);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
label.innerHTML =
|
||||
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
|
||||
' ',
|
||||
' '
|
||||
);
|
||||
element.appendChild(label);
|
||||
|
||||
return element;
|
||||
|
|
@ -119,7 +123,11 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
element.appendChild(img);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
label.innerHTML =
|
||||
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
|
||||
' ',
|
||||
' '
|
||||
);
|
||||
element.appendChild(label);
|
||||
|
||||
return element;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,12 @@ 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;
|
||||
|
|
@ -225,6 +231,15 @@ 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}`;
|
||||
|
|
|
|||
|
|
@ -4,20 +4,55 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||
if (!ignoredActorKeys.includes(key)) {
|
||||
const model = game.system.api.models.actors[key];
|
||||
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
||||
// As per DHToken._getTrackedAttributesFromSchema, attributes.bar have a max version as well.
|
||||
const maxAttributes = attributes.bar.map(x => [...x, 'max']);
|
||||
attributes.value.push(...maxAttributes);
|
||||
const group = game.i18n.localize(model.metadata.label);
|
||||
const choices = CONFIG.Token.documentClass
|
||||
.getTrackedAttributeChoices(attributes, model)
|
||||
.map(x => ({ ...x, group: group }));
|
||||
acc.push(...choices);
|
||||
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;
|
||||
};
|
||||
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 getLabel = path => {
|
||||
const label = model.schema.getField(path)?.label;
|
||||
return label ? game.i18n.localize(label) : path;
|
||||
};
|
||||
|
||||
const bars = attributes.bar.flatMap(x => {
|
||||
const joined = `${x.join('.')}.max`;
|
||||
const label =
|
||||
joined === 'resources.hope.max'
|
||||
? 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxHope.label'
|
||||
: getLabel(joined);
|
||||
return { value: joined, label, group };
|
||||
});
|
||||
const values = attributes.value.flatMap(x => {
|
||||
const joined = x.join('.');
|
||||
return { value: joined, label: getLabel(joined), group };
|
||||
});
|
||||
|
||||
const bonuses = getAllLeaves(model.schema.fields.bonuses, group);
|
||||
const rules = getAllLeaves(model.schema.fields.rules, group);
|
||||
|
||||
acc.push(...bars, ...values, ...rules, ...bonuses);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
|
@ -69,14 +104,18 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
},
|
||||
render: function (item, search) {
|
||||
const label = game.i18n.localize(item.label);
|
||||
const matchIndex = label.toLowerCase().indexOf(search);
|
||||
const matchIndex = label.toLowerCase().indexOf(search.toLowerCase());
|
||||
|
||||
const beforeText = label.slice(0, matchIndex);
|
||||
const matchText = label.slice(matchIndex, matchIndex + search.length);
|
||||
const after = label.slice(matchIndex + search.length, label.length);
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
element.innerHTML =
|
||||
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
|
||||
' ',
|
||||
' '
|
||||
);
|
||||
if (item.hint) {
|
||||
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
const after = label.slice(matchIndex + search.length, label.length);
|
||||
|
||||
const element = document.createElement('li');
|
||||
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||
element.innerHTML =
|
||||
`${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`.replaceAll(
|
||||
' ',
|
||||
' '
|
||||
);
|
||||
if (item.hint) {
|
||||
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ export default function DHApplicationMixin(Base) {
|
|||
icon: 'fa-solid fa-lightbulb',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !doc.disabled;
|
||||
return doc && !doc.disabled && doc.type !== 'beastform';
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
||||
},
|
||||
|
|
@ -415,7 +415,7 @@ export default function DHApplicationMixin(Base) {
|
|||
icon: 'fa-regular fa-lightbulb',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && doc.disabled;
|
||||
return doc && doc.disabled && doc.type !== 'beastform';
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
|
||||
}
|
||||
|
|
@ -509,6 +509,10 @@ 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();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
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' }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
/**
|
||||
|
|
@ -17,6 +19,15 @@ 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 */
|
||||
|
|
@ -35,7 +46,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
selectFolder: this.selectFolder,
|
||||
expandContent: this.expandContent,
|
||||
resetFilters: this.resetFilters,
|
||||
sortList: this.sortList
|
||||
sortList: this.sortList,
|
||||
openSettings: this.openSettings
|
||||
},
|
||||
position: {
|
||||
left: 100,
|
||||
|
|
@ -157,6 +169,8 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +228,10 @@ 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 => {
|
||||
|
|
@ -227,7 +245,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
Promise.all(promises).then(async result => {
|
||||
this.items = ItemBrowser.sortBy(
|
||||
result.flatMap(r => r),
|
||||
result.flatMap(r => r).filter(r => !browserSettings.isEntryExcluded.bind(browserSettings)(r)),
|
||||
'name'
|
||||
);
|
||||
|
||||
|
|
@ -512,6 +530,22 @@ 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',
|
||||
|
|
@ -571,4 +605,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
headerActions.append(button);
|
||||
}
|
||||
}
|
||||
|
||||
async close(options = {}) {
|
||||
Hooks.off(socketEvent.Refresh, this.setupHooks);
|
||||
await super.close(options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,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 distance - originRadius - targetRadius + canvas.grid.distance;
|
||||
return Math.floor(distance - originRadius - targetRadius + canvas.grid.distance);
|
||||
}
|
||||
|
||||
// Compute what the closest grid space of each token is, then compute that distance
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ export const defaultRestOptions = {
|
|||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
target: {
|
||||
amount: 1,
|
||||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
|
|
@ -308,6 +309,7 @@ export const defaultRestOptions = {
|
|||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
target: {
|
||||
amount: 1,
|
||||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
|
|
@ -333,7 +335,56 @@ 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: {},
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
effects: []
|
||||
}
|
||||
}),
|
||||
|
|
@ -353,6 +404,7 @@ export const defaultRestOptions = {
|
|||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
target: {
|
||||
amount: 1,
|
||||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
|
|
@ -421,6 +473,7 @@ export const defaultRestOptions = {
|
|||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
target: {
|
||||
amount: 1,
|
||||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
|
|
@ -446,7 +499,56 @@ 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: {},
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
effects: []
|
||||
},
|
||||
workOnAProject: {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export const gameSettings = {
|
|||
LastMigrationVersion: 'LastMigrationVersion',
|
||||
TagTeamRoll: 'TagTeamRoll',
|
||||
SpotlightRequestQueue: 'SpotlightRequestQueue',
|
||||
CompendiumBrowserSettings: 'CompendiumBrowserSettings'
|
||||
};
|
||||
|
||||
export const actionAutomationChoices = {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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';
|
||||
|
|
|
|||
|
|
@ -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) await this.toChat();
|
||||
if (this.chatDisplay && !config.actionChatMessageHandled) await this.toChat();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
|
@ -240,9 +240,13 @@ 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: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
|
||||
title: `${itemTitle}${actionTitle}`,
|
||||
source: {
|
||||
item: this.item._id,
|
||||
originItem: this.originItem,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,14 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hordeHp'
|
||||
}),
|
||||
criticalThreshold: new fields.NumberField({ required: true, integer: true, min: 1, max: 20, initial: 20 }),
|
||||
criticalThreshold: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 1,
|
||||
max: 20,
|
||||
initial: 20,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.criticalThreshold'
|
||||
}),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
major: new fields.NumberField({
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -29,17 +29,40 @@ 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 }),
|
||||
restrained: new fields.BooleanField({ initial: false }),
|
||||
vulnerable: new fields.BooleanField({ initial: false })
|
||||
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'
|
||||
})
|
||||
}),
|
||||
damageReduction: new fields.SchemaField({
|
||||
thresholdImmunities: new fields.SchemaField({
|
||||
minor: new fields.BooleanField({ initial: false })
|
||||
minor: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.thresholdImmunities.minor.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.thresholdImmunities.minor.hint'
|
||||
})
|
||||
}),
|
||||
reduceSeverity: new fields.SchemaField({
|
||||
magical: new fields.NumberField({ initial: 0, min: 0 }),
|
||||
physical: new fields.NumberField({ initial: 0, min: 0 })
|
||||
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'
|
||||
})
|
||||
}),
|
||||
...(extendedData.damageReduction ?? {})
|
||||
}),
|
||||
|
|
@ -49,12 +72,16 @@ export const commonActorRules = (extendedData = { damageReduction: {}, attack: {
|
|||
hpDamageMultiplier: new fields.NumberField({
|
||||
required: true,
|
||||
nullable: false,
|
||||
initial: 1
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.GENERAL.Attack.hpDamageMultiplier.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Attack.hpDamageMultiplier.hint'
|
||||
}),
|
||||
hpDamageTakenMultiplier: new fields.NumberField({
|
||||
required: true,
|
||||
nullable: false,
|
||||
initial: 1
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.GENERAL.Attack.hpDamageTakenMultiplier.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Attack.hpDamageTakenMultiplier.hint'
|
||||
}),
|
||||
...(extendedData.attack?.damage ?? {})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,15 +35,18 @@ export default class DhCharacter extends BaseDataActor {
|
|||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
),
|
||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.SchemaField({
|
||||
value: new fields.NumberField({
|
||||
initial: 2,
|
||||
min: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: false })
|
||||
})
|
||||
hope: new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({
|
||||
initial: 2,
|
||||
min: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: false })
|
||||
},
|
||||
{ label: 'DAGGERHEART.GENERAL.hope' }
|
||||
)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
|
|
@ -222,8 +225,16 @@ export default class DhCharacter extends BaseDataActor {
|
|||
rules: new fields.SchemaField({
|
||||
...commonActorRules({
|
||||
damageReduction: {
|
||||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false }),
|
||||
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'
|
||||
}),
|
||||
maxArmorMarked: new fields.SchemaField({
|
||||
value: new fields.NumberField({
|
||||
required: true,
|
||||
|
|
@ -253,7 +264,10 @@ 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 })
|
||||
disabledArmor: new fields.BooleanField({
|
||||
intial: false,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.disabledArmor.label'
|
||||
})
|
||||
},
|
||||
attack: {
|
||||
damage: {
|
||||
|
|
@ -301,12 +315,14 @@ 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()
|
||||
ignore: new fields.BooleanField({ label: 'DAGGERHEART.ACTORS.Character.burden.ignore.label' })
|
||||
}),
|
||||
roll: new fields.SchemaField({
|
||||
guaranteedCritical: new fields.BooleanField()
|
||||
guaranteedCritical: new fields.BooleanField({
|
||||
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
|
||||
})
|
||||
})
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,9 +53,18 @@ export default class DhCompanion extends BaseDataActor {
|
|||
),
|
||||
rules: new fields.SchemaField({
|
||||
conditionImmunities: new fields.SchemaField({
|
||||
hidden: new fields.BooleanField({ initial: false }),
|
||||
restrained: new fields.BooleanField({ initial: false }),
|
||||
vulnerable: new fields.BooleanField({ initial: false })
|
||||
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'
|
||||
})
|
||||
})
|
||||
}),
|
||||
attack: new ActionField({
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ 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 }),
|
||||
|
|
|
|||
35
module/data/compendiumBrowserSettings.mjs
Normal file
35
module/data/compendiumBrowserSettings.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +68,8 @@ 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;
|
||||
}
|
||||
|
|
@ -107,8 +109,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);
|
||||
|
|
|
|||
|
|
@ -7,16 +7,20 @@ const attributeField = label =>
|
|||
});
|
||||
|
||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
label:
|
||||
maxLabel ?? game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
});
|
||||
new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
label:
|
||||
maxLabel ??
|
||||
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
},
|
||||
{ label }
|
||||
);
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
|
|
|
|||
|
|
@ -254,4 +254,20 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ export default class DhLevelData extends foundry.abstract.DataModel {
|
|||
|
||||
return {
|
||||
level: new fields.SchemaField({
|
||||
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
current: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.GENERAL.currentLevel'
|
||||
}),
|
||||
changed: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
bonuses: new fields.TypedObjectField(new fields.NumberField({ integer: true, nullable: false }))
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
extendEnvironmentDescriptions: new BooleanField(),
|
||||
extendItemDescriptions: new BooleanField(),
|
||||
expandRollMessage: new SchemaField({
|
||||
desc: new BooleanField(),
|
||||
desc: new BooleanField({ initial: true }),
|
||||
roll: new BooleanField(),
|
||||
damage: new BooleanField(),
|
||||
target: new BooleanField()
|
||||
|
|
|
|||
|
|
@ -96,6 +96,19 @@ 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,
|
||||
|
|
@ -103,7 +116,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,
|
||||
system: { ...config, actionDescription },
|
||||
rolls: [roll]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -92,14 +92,15 @@ 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 =
|
||||
data.statuses?.filter(
|
||||
statuses.filter(
|
||||
status =>
|
||||
this.parent.system.rules?.conditionImmunities &&
|
||||
this.parent.system.rules.conditionImmunities[status]
|
||||
) ?? [];
|
||||
if (immuneStatuses.length > 0) {
|
||||
update.statuses = data.statuses.filter(x => !immuneStatuses.includes(x));
|
||||
update.statuses = statuses.filter(x => !immuneStatuses.includes(x));
|
||||
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||
const scrollingTexts = immuneStatuses.map(status => ({
|
||||
text: game.i18n.format('DAGGERHEART.ACTIVEEFFECT.immuneStatusText', {
|
||||
|
|
@ -146,6 +147,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
super.applyChangeField(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 key = change.value.toString();
|
||||
const isOriginTarget = key.toLowerCase().includes('origin.@');
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ 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', '');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +1,30 @@
|
|||
export default class DHToken extends CONFIG.Token.documentClass {
|
||||
/**
|
||||
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
|
||||
* @param {object} attributes The tracked attributes which can be chosen from
|
||||
* @returns {object} A nested object of attribute choices to display
|
||||
*/
|
||||
static getTrackedAttributeChoices(attributes, model) {
|
||||
/**@inheritdoc */
|
||||
static getTrackedAttributeChoices(attributes, typeKey) {
|
||||
attributes = attributes || this.getTrackedAttributes();
|
||||
const barGroup = game.i18n.localize('TOKEN.BarAttributes');
|
||||
const valueGroup = game.i18n.localize('TOKEN.BarValues');
|
||||
const actorModel = typeKey ? game.system.api.data.actors[`Dh${typeKey.capitalize()}`] : null;
|
||||
const getLabel = path => {
|
||||
const label = actorModel.schema.getField(path)?.label;
|
||||
return label ? game.i18n.localize(label) : path;
|
||||
};
|
||||
|
||||
const bars = attributes.bar.map(v => {
|
||||
const a = v.join('.');
|
||||
const modelLabel = model ? game.i18n.localize(model.schema.getField(`${a}.value`).label) : null;
|
||||
return { group: barGroup, value: a, label: modelLabel ? modelLabel : a };
|
||||
return { group: barGroup, value: a, label: getLabel(a) };
|
||||
});
|
||||
bars.sort((a, b) => a.label.compare(b.label));
|
||||
bars.sort((a, b) => a.value.compare(b.value));
|
||||
|
||||
const invalidAttributes = [
|
||||
'gold',
|
||||
'levelData',
|
||||
'actions',
|
||||
'biography',
|
||||
'class',
|
||||
'multiclass',
|
||||
'companion',
|
||||
'notes',
|
||||
'partner',
|
||||
'description',
|
||||
'impulses',
|
||||
'tier',
|
||||
'type'
|
||||
];
|
||||
const values = attributes.value.reduce((acc, v) => {
|
||||
const values = attributes.value.map(v => {
|
||||
const a = v.join('.');
|
||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||
|
||||
const field = model ? model.schema.getField(a) : null;
|
||||
const modelLabel = field ? game.i18n.localize(field.label) : null;
|
||||
const hint = field ? game.i18n.localize(field.hint) : null;
|
||||
acc.push({ group: valueGroup, value: a, label: modelLabel ? modelLabel : a, hint: hint });
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
values.sort((a, b) => a.label.compare(b.label));
|
||||
return { group: valueGroup, value: a, label: getLabel(a) };
|
||||
});
|
||||
|
||||
values.sort((a, b) => a.value.compare(b.value));
|
||||
return bars.concat(values);
|
||||
}
|
||||
|
||||
static _getTrackedAttributesFromSchema(schema, _path = []) {
|
||||
const attributes = { bar: [], value: [] };
|
||||
for (const [name, field] of Object.entries(schema.fields)) {
|
||||
const p = _path.concat([name]);
|
||||
if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.BooleanField && field.options.isAttributeChoice)
|
||||
attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
|
||||
const isSchema = field instanceof foundry.data.fields.SchemaField;
|
||||
const isModel = field instanceof foundry.data.fields.EmbeddedDataField;
|
||||
|
||||
if (isSchema || isModel) {
|
||||
const schema = isModel ? field.model.schema : field;
|
||||
const isBar = schema.has && schema.has('value') && schema.has('max');
|
||||
if (isBar) attributes.bar.push(p);
|
||||
else {
|
||||
const inner = this.getTrackedAttributes(schema, p);
|
||||
attributes.bar.push(...inner.bar);
|
||||
attributes.value.push(...inner.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
_shouldRecordMovementHistory() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ 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',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
DhHomebrewSettings,
|
||||
DhVariantRuleSettings
|
||||
} from '../applications/settings/_module.mjs';
|
||||
import { DhTagTeamRoll } from '../data/_module.mjs';
|
||||
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
|
||||
|
||||
export const registerDHSettings = () => {
|
||||
registerMenuSettings();
|
||||
|
|
@ -142,6 +142,12 @@ const registerNonConfigSettings = () => {
|
|||
config: false,
|
||||
type: DhTagTeamRoll
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: CompendiumBrowserSettings
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ export const RefreshType = {
|
|||
Countdown: 'DhCoundownRefresh',
|
||||
TagTeamRoll: 'DhTagTeamRollRefresh',
|
||||
EffectsDisplay: 'DhEffectsDisplayRefresh',
|
||||
Scene: 'DhSceneRefresh'
|
||||
Scene: 'DhSceneRefresh',
|
||||
CompendiumBrowser: 'DhCompendiumBrowserRefresh'
|
||||
};
|
||||
|
||||
export const registerSocketHooks = () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue