mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 21:21:08 +01:00
[Feature] Compedium Browser (#707)
* Create files * Item Browser v0.5 * dialog with (temporary) * Css * initial style in compedium browser * config * fixes * Replace compendium calls * Level Up * style compedium item list * removing css files --------- Co-authored-by: Dapoolp <elcatnet@gmail.com> Co-authored-by: WBHarry <williambjrklund@gmail.com>
This commit is contained in:
parent
a19c77ae4a
commit
a25007b994
34 changed files with 1650 additions and 123 deletions
|
|
@ -2361,6 +2361,7 @@
|
||||||
"diceIsRerolled": "The dice has been rerolled (x{times})",
|
"diceIsRerolled": "The dice has been rerolled (x{times})",
|
||||||
"pendingSaves": "Pending Reaction Rolls",
|
"pendingSaves": "Pending Reaction Rolls",
|
||||||
"openSheetSettings": "Open Settings",
|
"openSheetSettings": "Open Settings",
|
||||||
|
"compendiumBrowser": "Compendium Browser",
|
||||||
"rulesOn": "Rules On",
|
"rulesOn": "Rules On",
|
||||||
"rulesOff": "Rules Off",
|
"rulesOff": "Rules Off",
|
||||||
"remainingUses": "Uses refresh on {type}"
|
"remainingUses": "Uses refresh on {type}"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { abilities } from '../../config/actorConfig.mjs';
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
import { burden } from '../../config/generalConfig.mjs';
|
import { burden } from '../../config/generalConfig.mjs';
|
||||||
|
import { ItemBrowser } from '../ui/itemBrowser.mjs';
|
||||||
import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
|
import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -42,6 +43,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
};
|
};
|
||||||
|
|
||||||
this._dragDrop = this._createDragDropHandlers();
|
this._dragDrop = this._createDragDropHandlers();
|
||||||
|
|
||||||
|
this.itemBrowser = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -491,8 +494,24 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async viewCompendium(_, target) {
|
static async viewCompendium(event, target) {
|
||||||
(await game.packs.get(`daggerheart.${target.dataset.compendium}`))?.render(true);
|
const type = target.dataset.compendium ?? target.dataset.type;
|
||||||
|
|
||||||
|
const presets = {
|
||||||
|
compendium: 'daggerheart',
|
||||||
|
folder: type,
|
||||||
|
render: {
|
||||||
|
noFolder: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == 'domains')
|
||||||
|
presets.filter = {
|
||||||
|
'level.max': { key: 'level.max', value: 1 },
|
||||||
|
'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null }
|
||||||
|
};
|
||||||
|
|
||||||
|
return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async viewItem(_, target) {
|
static async viewItem(_, target) {
|
||||||
|
|
@ -604,6 +623,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
{ overwrite: true }
|
{ overwrite: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.itemBrowser) this.itemBrowser.close();
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
|
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
|
||||||
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
|
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
|
||||||
|
import { ItemBrowser } from '../ui/itemBrowser.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -11,6 +12,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
|
|
||||||
this._dragDrop = this._createDragDropHandlers();
|
this._dragDrop = this._createDragDropHandlers();
|
||||||
this.tabGroups.primary = 'advancements';
|
this.tabGroups.primary = 'advancements';
|
||||||
|
|
||||||
|
this.itemBrowser = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -533,8 +536,30 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async viewCompendium(_, button) {
|
static async viewCompendium(event, target) {
|
||||||
(await game.packs.get(`daggerheart.${button.dataset.compendium}`))?.render(true);
|
const type = target.dataset.compendium ?? target.dataset.type;
|
||||||
|
|
||||||
|
const presets = {
|
||||||
|
compendium: 'daggerheart',
|
||||||
|
folder: type,
|
||||||
|
render: {
|
||||||
|
noFolder: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == 'domains') {
|
||||||
|
const domains = this.actor.system.domains,
|
||||||
|
multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
|
||||||
|
if (multiclassDomain) {
|
||||||
|
if (!domains.includes(x => x === multiclassDomain)) domains.push(multiclassDomain);
|
||||||
|
}
|
||||||
|
presets.filter = {
|
||||||
|
'level.max': { key: 'level.max', value: this.levelup.currentLevel },
|
||||||
|
'system.domain': { key: 'system.domain', value: domains }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async selectPreview(_, button) {
|
static async selectPreview(_, button) {
|
||||||
|
|
@ -635,6 +660,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
await this.actor.levelUp(levelupData);
|
await this.actor.levelUp(levelupData);
|
||||||
|
if (this.itemBrowser) this.itemBrowser.close();
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||||
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
|
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
|
||||||
|
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||||
|
|
||||||
|
|
@ -25,7 +26,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||||
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
||||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||||
useDowntime: this.useDowntime
|
useDowntime: this.useDowntime,
|
||||||
|
tempBrowser: CharacterSheet.#tempBrowser
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
resizable: true
|
resizable: true
|
||||||
|
|
@ -600,7 +602,16 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
*/
|
*/
|
||||||
static async #openPack(_event, button) {
|
static async #openPack(_event, button) {
|
||||||
const { key } = button.dataset;
|
const { key } = button.dataset;
|
||||||
game.packs.get(key)?.render(true);
|
|
||||||
|
const presets = {
|
||||||
|
compendium: 'daggerheart',
|
||||||
|
folder: key,
|
||||||
|
render: {
|
||||||
|
noFolder: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ItemBrowser({ presets }).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -715,6 +726,13 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temp
|
||||||
|
*/
|
||||||
|
static async #tempBrowser(_, target) {
|
||||||
|
new ItemBrowser().render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the roll values of resource dice.
|
* Handle the roll values of resource dice.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
|
import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
|
||||||
|
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
|
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
|
||||||
|
|
@ -82,7 +83,9 @@ export default function DHApplicationMixin(Base) {
|
||||||
toChat: DHSheetV2.#toChat,
|
toChat: DHSheetV2.#toChat,
|
||||||
useItem: DHSheetV2.#useItem,
|
useItem: DHSheetV2.#useItem,
|
||||||
toggleEffect: DHSheetV2.#toggleEffect,
|
toggleEffect: DHSheetV2.#toggleEffect,
|
||||||
toggleExtended: DHSheetV2.#toggleExtended
|
toggleExtended: DHSheetV2.#toggleExtended,
|
||||||
|
addNewItem: DHSheetV2.#addNewItem,
|
||||||
|
browseItem: DHSheetV2.#browseItem
|
||||||
},
|
},
|
||||||
contextMenus: [
|
contextMenus: [
|
||||||
{
|
{
|
||||||
|
|
@ -333,19 +336,19 @@ export default function DHApplicationMixin(Base) {
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target),
|
const doc = await getDocFromElement(target),
|
||||||
action = doc?.system?.attack ?? doc;
|
action = doc?.system?.attack ?? doc;
|
||||||
return action && action.use(event, { byPassRoll: true })
|
return action && action.use(event, { byPassRoll: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
options.unshift({
|
options.unshift({
|
||||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||||
icon: 'fa-solid fa-burst',
|
icon: 'fa-solid fa-burst',
|
||||||
condition: target => {
|
condition: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||||
},
|
},
|
||||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (toChat)
|
if (toChat)
|
||||||
options.push({
|
options.push({
|
||||||
|
|
@ -423,6 +426,68 @@ export default function DHApplicationMixin(Base) {
|
||||||
/* Application Clicks Actions */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
static async #addNewItem(event, target) {
|
||||||
|
const { type } = target.dataset;
|
||||||
|
|
||||||
|
const createChoice = await foundry.applications.api.DialogV2.wait({
|
||||||
|
classes: ['dh-style', 'two-big-buttons'],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: 'create',
|
||||||
|
label: 'Create Item',
|
||||||
|
icon: 'fa-solid fa-plus'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'browse',
|
||||||
|
label: 'Browse Compendium',
|
||||||
|
icon: 'fa-solid fa-book'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!createChoice) return;
|
||||||
|
|
||||||
|
if (createChoice === 'browse') return DHSheetV2.#browseItem.call(this, event, target);
|
||||||
|
else return DHSheetV2.#createDoc.call(this, event, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #browseItem(event, target) {
|
||||||
|
const type = target.dataset.compendium ?? target.dataset.type;
|
||||||
|
|
||||||
|
const presets = {};
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'loot':
|
||||||
|
case 'consumable':
|
||||||
|
case 'armor':
|
||||||
|
case 'weapon':
|
||||||
|
presets.compendium = 'daggerheart';
|
||||||
|
presets.folder = 'equipments';
|
||||||
|
presets.render = {
|
||||||
|
noFolder: true
|
||||||
|
};
|
||||||
|
presets.filter = {
|
||||||
|
type: { key: 'type', value: type, forced: true }
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'domainCard':
|
||||||
|
presets.compendium = 'daggerheart';
|
||||||
|
presets.folder = 'domains';
|
||||||
|
presets.render = {
|
||||||
|
noFolder: true
|
||||||
|
};
|
||||||
|
presets.filter = {
|
||||||
|
'level.max': { key: 'level.max', value: this.document.system.levelData.level.current },
|
||||||
|
'system.domain': { key: 'system.domain', value: this.document.system.domains }
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ItemBrowser({ presets }).render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an embedded document.
|
* Create an embedded document.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
|
||||||
|
|
@ -194,11 +194,12 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const item = await foundry.utils.fromUuid(message.system.origin);
|
const item = await foundry.utils.fromUuid(message.system.origin);
|
||||||
const action = item.system.attack?.id === event.currentTarget.id ? item.system.attack : item.system.actions.get(event.currentTarget.id);
|
const action =
|
||||||
if(event.currentTarget.dataset.directDamage)
|
item.system.attack?.id === event.currentTarget.id
|
||||||
action.use(event, { byPassRoll: true })
|
? item.system.attack
|
||||||
else
|
: item.system.actions.get(event.currentTarget.id);
|
||||||
action.use(event);
|
if (event.currentTarget.dataset.directDamage) action.use(event, { byPassRoll: true });
|
||||||
|
else action.use(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
async actionUseButton(event, message) {
|
async actionUseButton(event, message) {
|
||||||
|
|
|
||||||
425
module/applications/ui/itemBrowser.mjs
Normal file
425
module/applications/ui/itemBrowser.mjs
Normal file
|
|
@ -0,0 +1,425 @@
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UI element which displays the Users defined for this world.
|
||||||
|
* Currently active users are always displayed, while inactive users can be displayed on toggle.
|
||||||
|
*
|
||||||
|
* @extends ApplicationV2
|
||||||
|
* @mixes HandlebarsApplication
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options);
|
||||||
|
this.items = [];
|
||||||
|
this.fieldFilter = [];
|
||||||
|
this.selectedMenu = { path: [], data: null };
|
||||||
|
this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig;
|
||||||
|
this.presets = options.presets;
|
||||||
|
|
||||||
|
if(this.presets?.compendium && this.presets?.folder)
|
||||||
|
ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
id: 'itemBrowser',
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'],
|
||||||
|
tag: 'div',
|
||||||
|
// title: 'Item Browser',
|
||||||
|
window: {
|
||||||
|
frame: true,
|
||||||
|
title: 'Compendium Browser',
|
||||||
|
icon: 'fa-solid fa-book-atlas',
|
||||||
|
positioned: true,
|
||||||
|
resizable: true
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
selectFolder: this.selectFolder,
|
||||||
|
expandContent: this.expandContent,
|
||||||
|
resetFilters: this.resetFilters,
|
||||||
|
sortList: this.sortList
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
top: 330,
|
||||||
|
left: 120,
|
||||||
|
width: 800,
|
||||||
|
height: 600
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sidebar: {
|
||||||
|
template: 'systems/daggerheart/templates/ui/itemBrowser/sidebar.hbs'
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
template: 'systems/daggerheart/templates/ui/itemBrowser/itemBrowser.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Filter Tracking */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active search filter.
|
||||||
|
* @type {foundry.applications.ux.SearchFilter}
|
||||||
|
*/
|
||||||
|
#search = {};
|
||||||
|
|
||||||
|
#input = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks which item IDs are currently displayed, organized by filter type and section.
|
||||||
|
* @type {{
|
||||||
|
* inventory: {
|
||||||
|
* search: Set<string>,
|
||||||
|
* input: Set<string>
|
||||||
|
* }
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
#filteredItems = {
|
||||||
|
browser: {
|
||||||
|
search: new Set(),
|
||||||
|
input: new Set()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _preFirstRender(context, options) {
|
||||||
|
if(context.presets?.render?.noFolder || context.presets?.render?.lite)
|
||||||
|
options.position.width = 600;
|
||||||
|
|
||||||
|
await super._preFirstRender(context, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _preRender(context, options) {
|
||||||
|
|
||||||
|
if(context.presets?.render?.noFolder || context.presets?.render?.lite)
|
||||||
|
options.parts.splice(options.parts.indexOf('sidebar'), 1);
|
||||||
|
|
||||||
|
await super._preRender(context, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
this._createSearchFilter();
|
||||||
|
this._createFilterInputs();
|
||||||
|
this._createDragProcess();
|
||||||
|
|
||||||
|
if(context.presets?.render?.lite)
|
||||||
|
this.element.classList.add('lite');
|
||||||
|
|
||||||
|
if(context.presets?.render?.noFolder)
|
||||||
|
this.element.classList.add('no-folder');
|
||||||
|
|
||||||
|
if(context.presets?.render?.noFilter)
|
||||||
|
this.element.classList.add('no-filter');
|
||||||
|
|
||||||
|
if(this.presets?.filter) {
|
||||||
|
Object.entries(this.presets.filter).forEach(([k,v]) => this.fieldFilter.find(c => c.name === k).value = v.value);
|
||||||
|
await this._onInputFilterBrowser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Rendering */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.compendiums = this.getCompendiumFolders(foundry.utils.deepClone(this.config));
|
||||||
|
// context.pathTitle = this.pathTile;
|
||||||
|
context.menu = this.selectedMenu;
|
||||||
|
context.formatLabel = this.formatLabel;
|
||||||
|
context.formatChoices = this.formatChoices;
|
||||||
|
context.fieldFilter = this.fieldFilter = this._createFieldFilter();
|
||||||
|
context.items = this.items;
|
||||||
|
context.presets = this.presets;
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompendiumFolders(config, parent = null, depth = 0) {
|
||||||
|
let folders = [];
|
||||||
|
Object.values(config).forEach(c => {
|
||||||
|
const folder = {
|
||||||
|
id: c.id,
|
||||||
|
label: c.label,
|
||||||
|
selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id
|
||||||
|
};
|
||||||
|
folder.folders = c.folders
|
||||||
|
? ItemBrowser.sortBy(this.getCompendiumFolders(c.folders, folder, depth + 2), 'label')
|
||||||
|
: [];
|
||||||
|
folders.push(folder);
|
||||||
|
});
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectFolder(_, target, compend, folder) {
|
||||||
|
const config = foundry.utils.deepClone(this.config),
|
||||||
|
compendium = compend ?? target.closest('[data-compendium-id]').dataset.compendiumId,
|
||||||
|
folderId = folder ?? target.dataset.folderId,
|
||||||
|
folderPath = `${compendium}.folders.${folderId}`,
|
||||||
|
folderData = foundry.utils.getProperty(config, folderPath);
|
||||||
|
|
||||||
|
this.selectedMenu = {
|
||||||
|
path: folderPath.split('.'),
|
||||||
|
data: {
|
||||||
|
...folderData,
|
||||||
|
columns: ItemBrowser.getFolderConfig(folderData)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
for (const key of folderData.keys) {
|
||||||
|
const comp = game.packs.get(`${compendium}.${key}`);
|
||||||
|
if (!comp) return;
|
||||||
|
items = items.concat(await comp.getDocuments({ type__in: folderData.type }));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items = ItemBrowser.sortBy(items, 'name');
|
||||||
|
this.render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
static expandContent(_, target) {
|
||||||
|
const parent = target.parentElement;
|
||||||
|
parent.classList.toggle('expanded');
|
||||||
|
}
|
||||||
|
|
||||||
|
static sortBy(data, property) {
|
||||||
|
return data.sort((a, b) => (a[property] > b[property] ? 1 : -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
formatLabel(item, field) {
|
||||||
|
const property = foundry.utils.getProperty(item, field.key);
|
||||||
|
if (typeof field.format !== 'function') return property ?? '-';
|
||||||
|
return field.format(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatChoices(data) {
|
||||||
|
if (!data.field.choices) return null;
|
||||||
|
const config = {
|
||||||
|
choices: data.field.choices
|
||||||
|
};
|
||||||
|
foundry.data.fields.StringField._prepareChoiceConfig(config);
|
||||||
|
return config.options.filter(
|
||||||
|
c => data.filtered.includes(c.value) || data.filtered.includes(c.label.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createFieldFilter() {
|
||||||
|
const filters = ItemBrowser.getFolderConfig(this.selectedMenu.data, 'filters');
|
||||||
|
filters.forEach(f => {
|
||||||
|
if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field);
|
||||||
|
else if (typeof f.choices === 'function') {
|
||||||
|
f.choices = f.choices();
|
||||||
|
}
|
||||||
|
f.name ??= f.key;
|
||||||
|
f.value = this.presets?.filter?.[f.name]?.value ?? null;
|
||||||
|
});
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Search Inputs */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize search filter instances for the inventory and loadout sections.
|
||||||
|
*
|
||||||
|
* Sets up two {@link foundry.applications.ux.SearchFilter} instances:
|
||||||
|
* - One for the inventory, which filters items in the inventory grid.
|
||||||
|
* - One for the loadout, which filters items in the loadout/card grid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_createSearchFilter() {
|
||||||
|
//Filters could be a application option if needed
|
||||||
|
const filters = [
|
||||||
|
{
|
||||||
|
key: 'browser',
|
||||||
|
input: 'input[type="search"].search-input',
|
||||||
|
content: '[data-application-part="list"] .item-list',
|
||||||
|
callback: this._onSearchFilterBrowser.bind(this)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { key, input, content, callback } of filters) {
|
||||||
|
const filter = new foundry.applications.ux.SearchFilter({
|
||||||
|
inputSelector: input,
|
||||||
|
contentSelector: content,
|
||||||
|
callback
|
||||||
|
});
|
||||||
|
filter.bind(this.element);
|
||||||
|
this.#search[key] = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Filter Inputs */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
_createFilterInputs() {
|
||||||
|
const inputs = [
|
||||||
|
{
|
||||||
|
key: 'browser',
|
||||||
|
container: '[data-application-part="list"] .filter-content .wrapper',
|
||||||
|
content: '[data-application-part="list"] .item-list',
|
||||||
|
callback: this._onInputFilterBrowser.bind(this)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
inputs.forEach(m => {
|
||||||
|
const container = this.element.querySelector(m.container);
|
||||||
|
if (!container) return (this.#input[m.key] = {});
|
||||||
|
const inputs = container.querySelectorAll('input, select');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.addEventListener('change', this._onInputFilterBrowser.bind(this));
|
||||||
|
});
|
||||||
|
this.#filteredItems[m.key].input = new Set(this.items.map(i => i.id));
|
||||||
|
this.#input[m.key] = inputs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle invetory items search and filtering.
|
||||||
|
* @param {KeyboardEvent} event The keyboard input event.
|
||||||
|
* @param {string} query The input search string.
|
||||||
|
* @param {RegExp} rgx The regular expression query that should be matched against.
|
||||||
|
* @param {HTMLElement} html The container to filter items from.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onSearchFilterBrowser(event, query, rgx, html) {
|
||||||
|
this.#filteredItems.browser.search.clear();
|
||||||
|
|
||||||
|
for (const li of html.querySelectorAll('.item-container')) {
|
||||||
|
const itemUUID = li.dataset.itemUuid,
|
||||||
|
item = this.items.find(i => i.uuid === itemUUID);
|
||||||
|
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||||
|
if (matchesSearch) this.#filteredItems.browser.search.add(item.id);
|
||||||
|
const { input } = this.#filteredItems.browser;
|
||||||
|
li.hidden = !(input.has(item.id) && matchesSearch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when filters change
|
||||||
|
* @param {PointerEvent} event
|
||||||
|
* @param {HTMLElement} html
|
||||||
|
*/
|
||||||
|
async _onInputFilterBrowser(event) {
|
||||||
|
this.#filteredItems.browser.input.clear();
|
||||||
|
|
||||||
|
if(event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value;
|
||||||
|
|
||||||
|
for (const li of this.element.querySelectorAll('.item-container')) {
|
||||||
|
const itemUUID = li.dataset.itemUuid,
|
||||||
|
item = this.items.find(i => i.uuid === itemUUID);
|
||||||
|
|
||||||
|
if(!item) continue;
|
||||||
|
|
||||||
|
const matchesMenu =
|
||||||
|
this.fieldFilter.length === 0 ||
|
||||||
|
this.fieldFilter.every(f => (
|
||||||
|
!f.value && f.value !== false) ||
|
||||||
|
ItemBrowser.evaluateFilter(item, this.createFilterData(f))
|
||||||
|
);
|
||||||
|
if (matchesMenu) this.#filteredItems.browser.input.add(item.id);
|
||||||
|
|
||||||
|
const { search } = this.#filteredItems.browser;
|
||||||
|
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Foundry evaluateFilter doesn't allow you to match if filter values are included into item data
|
||||||
|
* @param {*} obj
|
||||||
|
* @param {*} filter
|
||||||
|
*/
|
||||||
|
static evaluateFilter(obj, filter) {
|
||||||
|
let docValue = foundry.utils.getProperty(obj, filter.field);
|
||||||
|
let filterValue = filter.value;
|
||||||
|
switch (filter.operator) {
|
||||||
|
case "contains2":
|
||||||
|
filterValue = Array.isArray(filterValue) ? filterValue : [filterValue];
|
||||||
|
docValue = Array.isArray(docValue) ? docValue : [docValue];
|
||||||
|
return docValue.some(dv => filterValue.includes(dv));
|
||||||
|
case "contains3":
|
||||||
|
return docValue.some(f => f.value === filterValue);
|
||||||
|
default:
|
||||||
|
return foundry.applications.ux.SearchFilter.evaluateFilter(obj, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createFilterData(filter) {
|
||||||
|
return {
|
||||||
|
field: filter.key,
|
||||||
|
value: isNaN(filter.value)
|
||||||
|
? ['true', 'false'].includes(filter.value)
|
||||||
|
? filter.value === 'true'
|
||||||
|
: filter.value
|
||||||
|
: Number(filter.value),
|
||||||
|
operator: filter.operator,
|
||||||
|
negate: filter.negate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static resetFilters() {
|
||||||
|
this.render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFolderConfig(folder, property = "columns") {
|
||||||
|
if(!folder) return [];
|
||||||
|
return folder[property] ?? CONFIG.DH.ITEMBROWSER.typeConfig[folder.listType]?.[property] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static sortList(_, target) {
|
||||||
|
const key = target.dataset.sortKey,
|
||||||
|
type = !target.dataset.sortType || target.dataset.sortType === "DESC" ? "ASC" : "DESC",
|
||||||
|
itemListContainer = target.closest(".compendium-results").querySelector(".item-list"),
|
||||||
|
itemList = itemListContainer.querySelectorAll(".item-container");
|
||||||
|
|
||||||
|
target.closest(".item-list-header").querySelectorAll('[data-sort-key]').forEach(b => b.dataset.sortType = "");
|
||||||
|
target.dataset.sortType = type;
|
||||||
|
|
||||||
|
const newOrder = [...itemList].reverse().sort((a, b) => {
|
||||||
|
const aProp = a.querySelector(`[data-item-key="${key}"]`),
|
||||||
|
bProp = b.querySelector(`[data-item-key="${key}"]`)
|
||||||
|
if(type === "DESC") {
|
||||||
|
return aProp.innerText < bProp.innerText ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return aProp.innerText > bProp.innerText ? 1 : -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
itemListContainer.replaceChildren(...newOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDragProcess() {
|
||||||
|
new foundry.applications.ux.DragDrop.implementation({
|
||||||
|
dragSelector: '.item-container',
|
||||||
|
permissions: {
|
||||||
|
dragstart: this._canDragStart.bind(this)
|
||||||
|
},
|
||||||
|
callbacks: {
|
||||||
|
dragstart: this._onDragStart.bind(this)
|
||||||
|
}
|
||||||
|
}).bind(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDragStart(event) {
|
||||||
|
const { itemUuid } = event.target.closest('[data-item-uuid]').dataset,
|
||||||
|
item = await foundry.utils.fromUuid(itemUuid),
|
||||||
|
dragData = item.toDragData();
|
||||||
|
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||||
|
}
|
||||||
|
|
||||||
|
_canDragStart() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,3 +7,4 @@ export * as generalConfig from './generalConfig.mjs';
|
||||||
export * as itemConfig from './itemConfig.mjs';
|
export * as itemConfig from './itemConfig.mjs';
|
||||||
export * as settingsConfig from './settingsConfig.mjs';
|
export * as settingsConfig from './settingsConfig.mjs';
|
||||||
export * as systemConfig from './system.mjs';
|
export * as systemConfig from './system.mjs';
|
||||||
|
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||||
|
|
|
||||||
405
module/config/itemBrowserConfig.mjs
Normal file
405
module/config/itemBrowserConfig.mjs
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
export const typeConfig = {
|
||||||
|
adversaries: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.type",
|
||||||
|
label: "Type"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.tier'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.type",
|
||||||
|
label: "Type",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.type'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.difficulty",
|
||||||
|
name: "difficulty.min",
|
||||||
|
label: "Difficulty (Min)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.difficulty",
|
||||||
|
name: "difficulty.max",
|
||||||
|
label: "Difficulty (Max)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.resources.hitPoints.max",
|
||||||
|
name: "hp.min",
|
||||||
|
label: "Hit Points (Min)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.resources.hitPoints.max",
|
||||||
|
name: "hp.max",
|
||||||
|
label: "Hit Points (Max)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.resources.stress.max",
|
||||||
|
name: "stress.min",
|
||||||
|
label: "Stress (Min)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.resources.stress.max",
|
||||||
|
name: "stress.max",
|
||||||
|
label: "Stress (Max)",
|
||||||
|
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
label: "Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.secondary",
|
||||||
|
label: "Subtype",
|
||||||
|
format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
label: "Type",
|
||||||
|
choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t }))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.secondary",
|
||||||
|
label: "Subtype",
|
||||||
|
choices: [
|
||||||
|
{ value: false, label: "Primary Weapon"},
|
||||||
|
{ value: true, label: "Secondary Weapon"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier",
|
||||||
|
choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.burden",
|
||||||
|
label: "Burden",
|
||||||
|
field: 'system.api.models.items.DHWeapon.schema.fields.burden'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.attack.roll.trait",
|
||||||
|
label: "Trait",
|
||||||
|
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.attack.range",
|
||||||
|
label: "Range",
|
||||||
|
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.baseScore",
|
||||||
|
name: "armor.min",
|
||||||
|
label: "Armor Score (Min)",
|
||||||
|
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.baseScore",
|
||||||
|
name: "armor.max",
|
||||||
|
label: "Armor Score (Max)",
|
||||||
|
field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.itemFeatures",
|
||||||
|
label: "Features",
|
||||||
|
choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})),
|
||||||
|
operator: "contains3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
columns: [
|
||||||
|
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
cards: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "system.type",
|
||||||
|
label: "Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.domain",
|
||||||
|
label: "Domain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.level",
|
||||||
|
label: "Level"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: "system.type",
|
||||||
|
label: "Type",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.type'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.domain",
|
||||||
|
label: "Domain",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.domain',
|
||||||
|
operator: "contains2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.level",
|
||||||
|
name: "level.min",
|
||||||
|
label: "Level (Min)",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.level",
|
||||||
|
name: "level.max",
|
||||||
|
label: "Level (Max)",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.level',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.recallCost",
|
||||||
|
name: "recall.min",
|
||||||
|
label: "Recall Cost (Min)",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.recallCost",
|
||||||
|
name: "recall.max",
|
||||||
|
label: "Recall Cost (Max)",
|
||||||
|
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
|
||||||
|
operator: "lte"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
classes: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "system.evasion",
|
||||||
|
label: "Evasion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.hitPoints",
|
||||||
|
label: "Hit Points"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.domains",
|
||||||
|
label: "Domains"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: "system.evasion",
|
||||||
|
name: "evasion.min",
|
||||||
|
label: "Evasion (Min)",
|
||||||
|
field: 'system.api.models.items.DHClass.schema.fields.evasion',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.evasion",
|
||||||
|
name: "evasion.max",
|
||||||
|
label: "Evasion (Max)",
|
||||||
|
field: 'system.api.models.items.DHClass.schema.fields.evasion',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.hitPoints",
|
||||||
|
name: "hp.min",
|
||||||
|
label: "Hit Points (Min)",
|
||||||
|
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
|
||||||
|
operator: "gte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.hitPoints",
|
||||||
|
name: "hp.max",
|
||||||
|
label: "Hit Points (Max)",
|
||||||
|
field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
|
||||||
|
operator: "lte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.domains",
|
||||||
|
label: "Domains",
|
||||||
|
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})),
|
||||||
|
operator: "contains2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
subclasses: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "id",
|
||||||
|
label: "Class",
|
||||||
|
format: (id) => {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.spellcastingTrait",
|
||||||
|
label: "Spellcasting Trait"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: []
|
||||||
|
},
|
||||||
|
beastforms: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.mainTrait",
|
||||||
|
label: "Main Trait"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: "system.tier",
|
||||||
|
label: "Tier",
|
||||||
|
field: 'system.api.models.items.DHBeastform.schema.fields.tier'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "system.mainTrait",
|
||||||
|
label: "Main Trait",
|
||||||
|
field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compendiumConfig = {
|
||||||
|
"daggerheart": {
|
||||||
|
id: "daggerheart",
|
||||||
|
label: "DAGGERHEART",
|
||||||
|
folders: {
|
||||||
|
"adversaries": {
|
||||||
|
id: "adversaries",
|
||||||
|
keys: ["adversaries"],
|
||||||
|
label: "Adversaries",
|
||||||
|
type: ["adversary"],
|
||||||
|
listType: "adversaries"
|
||||||
|
},
|
||||||
|
"ancestries": {
|
||||||
|
id: "ancestries",
|
||||||
|
keys: ["ancestries"],
|
||||||
|
label: "Ancestries",
|
||||||
|
type: ["ancestry"],
|
||||||
|
folders: {
|
||||||
|
"features": {
|
||||||
|
id: "features",
|
||||||
|
keys: ["ancestries"],
|
||||||
|
label: "Features",
|
||||||
|
type: ["feature"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"equipments": {
|
||||||
|
id: "equipments",
|
||||||
|
keys: ["armors", "weapons", "consumables", "loot"],
|
||||||
|
label: "Equipments",
|
||||||
|
type: ["armor", "weapon", "consumable", "loot"],
|
||||||
|
listType: "items"
|
||||||
|
},
|
||||||
|
"classes": {
|
||||||
|
id: "classes",
|
||||||
|
keys: ["classes"],
|
||||||
|
label: "Classes",
|
||||||
|
type: ["class"],
|
||||||
|
folders: {
|
||||||
|
"features": {
|
||||||
|
id: "features",
|
||||||
|
keys: ["classes"],
|
||||||
|
label: "Features",
|
||||||
|
type: ["feature"]
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
id: "items",
|
||||||
|
keys: ["classes"],
|
||||||
|
label: "Items",
|
||||||
|
type: ["armor", "weapon", "consumable", "loot"],
|
||||||
|
listType: "items"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
listType: "classes"
|
||||||
|
},
|
||||||
|
"subclasses": {
|
||||||
|
id: "subclasses",
|
||||||
|
keys: ["subclasses"],
|
||||||
|
label: "Subclasses",
|
||||||
|
type: ["subclass"],
|
||||||
|
listType: "subclasses"
|
||||||
|
},
|
||||||
|
"domains": {
|
||||||
|
id: "domains",
|
||||||
|
keys: ["domains"],
|
||||||
|
label: "Domain Cards",
|
||||||
|
type: ["domainCard"],
|
||||||
|
listType: "cards"
|
||||||
|
},
|
||||||
|
"communities": {
|
||||||
|
id: "communities",
|
||||||
|
keys: ["communities"],
|
||||||
|
label: "Communities",
|
||||||
|
type: ["community"],
|
||||||
|
folders: {
|
||||||
|
"features": {
|
||||||
|
id: "features",
|
||||||
|
keys: ["communities"],
|
||||||
|
label: "Features",
|
||||||
|
type: ["feature"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"environments": {
|
||||||
|
id: "environments",
|
||||||
|
keys: ["environments"],
|
||||||
|
label: "Environments",
|
||||||
|
type: ["environment"]
|
||||||
|
},
|
||||||
|
"beastforms": {
|
||||||
|
id: "beastforms",
|
||||||
|
keys: ["beastforms"],
|
||||||
|
label: "Beastforms",
|
||||||
|
type: ["beastform"],
|
||||||
|
listType: "beastforms",
|
||||||
|
folders: {
|
||||||
|
"features": {
|
||||||
|
id: "features",
|
||||||
|
keys: ["beastforms"],
|
||||||
|
label: "Features",
|
||||||
|
type: ["feature"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -380,7 +380,7 @@ export const armorFeatures = {
|
||||||
img: 'icons/magic/time/hourglass-brown-orange.webp',
|
img: 'icons/magic/time/hourglass-brown-orange.webp',
|
||||||
cost: [
|
cost: [
|
||||||
{
|
{
|
||||||
key: 'armorStack',
|
key: 'armorSlot',
|
||||||
value: 1
|
value: 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import * as SETTINGS from './settingsConfig.mjs';
|
||||||
import * as EFFECTS from './effectConfig.mjs';
|
import * as EFFECTS from './effectConfig.mjs';
|
||||||
import * as ACTIONS from './actionConfig.mjs';
|
import * as ACTIONS from './actionConfig.mjs';
|
||||||
import * as FLAGS from './flagsConfig.mjs';
|
import * as FLAGS from './flagsConfig.mjs';
|
||||||
|
import * as ITEMBROWSER from './itemBrowserConfig.mjs'
|
||||||
|
|
||||||
export const SYSTEM_ID = 'daggerheart';
|
export const SYSTEM_ID = 'daggerheart';
|
||||||
|
|
||||||
|
|
@ -18,5 +19,6 @@ export const SYSTEM = {
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
EFFECTS,
|
EFFECTS,
|
||||||
ACTIONS,
|
ACTIONS,
|
||||||
FLAGS
|
FLAGS,
|
||||||
|
ITEMBROWSER
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
dialog: {},
|
dialog: {},
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
targetSelection: systemData.targets.length > 0
|
targetSelection: systemData.targets.length > 0
|
||||||
}
|
};
|
||||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||||
if (data.system) {
|
if (data.system) {
|
||||||
config.source.message = data._id;
|
config.source.message = data._id;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
const targetsField = () => new fields.ArrayField(
|
const targetsField = () =>
|
||||||
new fields.SchemaField({
|
new fields.ArrayField(
|
||||||
id: new fields.StringField({}),
|
new fields.SchemaField({
|
||||||
actorId: new fields.StringField({}),
|
id: new fields.StringField({}),
|
||||||
name: new fields.StringField({}),
|
actorId: new fields.StringField({}),
|
||||||
img: new fields.StringField({}),
|
name: new fields.StringField({}),
|
||||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
img: new fields.StringField({}),
|
||||||
evasion: new fields.NumberField({ integer: true }),
|
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||||
hit: new fields.BooleanField({ initial: false }),
|
evasion: new fields.NumberField({ integer: true }),
|
||||||
saved: new fields.SchemaField({
|
hit: new fields.BooleanField({ initial: false }),
|
||||||
result: new fields.NumberField(),
|
saved: new fields.SchemaField({
|
||||||
success: new fields.BooleanField({ nullable: true, initial: null })
|
result: new fields.NumberField(),
|
||||||
|
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
);
|
||||||
)
|
|
||||||
|
|
||||||
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
targetHook = null;
|
targetHook = null;
|
||||||
|
|
@ -40,27 +41,25 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
action: new fields.StringField()
|
action: new fields.StringField()
|
||||||
}),
|
}),
|
||||||
damage: new fields.ObjectField(),
|
damage: new fields.ObjectField(),
|
||||||
costs: new fields.ArrayField(
|
costs: new fields.ArrayField(new fields.ObjectField()),
|
||||||
new fields.ObjectField()
|
|
||||||
),
|
|
||||||
successConsumed: new fields.BooleanField({ initial: false })
|
successConsumed: new fields.BooleanField({ initial: false })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get actionActor() {
|
get actionActor() {
|
||||||
if(!this.source.actor) return null;
|
if (!this.source.actor) return null;
|
||||||
return fromUuidSync(this.source.actor);
|
return fromUuidSync(this.source.actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
get actionItem() {
|
get actionItem() {
|
||||||
const actionActor = this.actionActor;
|
const actionActor = this.actionActor;
|
||||||
if(!actionActor || !this.source.item) return null;
|
if (!actionActor || !this.source.item) return null;
|
||||||
return actionActor.items.get(this.source.item);
|
return actionActor.items.get(this.source.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
get action() {
|
get action() {
|
||||||
const actionItem = this.actionItem;
|
const actionItem = this.actionItem;
|
||||||
if(!actionItem || !this.source.action) return null;
|
if (!actionItem || !this.source.action) return null;
|
||||||
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,67 +75,64 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
this.targetSelection = mode;
|
this.targetSelection = mode;
|
||||||
this.updateTargets();
|
this.updateTargets();
|
||||||
this.registerTargetHook();
|
this.registerTargetHook();
|
||||||
this.parent.update(
|
this.parent.update({
|
||||||
{
|
system: {
|
||||||
system: {
|
targetSelection: this.targetSelection,
|
||||||
targetSelection: this.targetSelection,
|
oldTargets: this.oldTargets
|
||||||
oldTargets: this.oldTargets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get hitTargets() {
|
get hitTargets() {
|
||||||
return this.currentTargets.filter(t => (t.hit || !this.hasRoll || !this.targetSelection));
|
return this.currentTargets.filter(t => t.hit || !this.hasRoll || !this.targetSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTargets() {
|
async updateTargets() {
|
||||||
this.currentTargets = this.getTargetList();
|
this.currentTargets = this.getTargetList();
|
||||||
if(!this.targetSelection) {
|
if (!this.targetSelection) {
|
||||||
this.currentTargets.forEach(ct => {
|
this.currentTargets.forEach(ct => {
|
||||||
if(this.targets.find(t => t.actorId === ct.actorId)) return;
|
if (this.targets.find(t => t.actorId === ct.actorId)) return;
|
||||||
const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId);
|
const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId);
|
||||||
if(indexTarget === -1)
|
if (indexTarget === -1) this.oldTargets.push(ct);
|
||||||
this.oldTargets.push(ct);
|
|
||||||
});
|
});
|
||||||
if(this.hasSave) this.setPendingSaves();
|
if (this.hasSave) this.setPendingSaves();
|
||||||
if(this.currentTargets.length) {
|
if (this.currentTargets.length) {
|
||||||
if(!this.parent._id) return;
|
if (!this.parent._id) return;
|
||||||
const updates = await this.parent.update(
|
const updates = await this.parent.update({
|
||||||
{
|
system: {
|
||||||
system: {
|
oldTargets: this.oldTargets
|
||||||
oldTargets: this.oldTargets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
if(!updates && ui.chat.collection.get(this.parent.id))
|
if (!updates && ui.chat.collection.get(this.parent.id)) ui.chat.updateMessage(this.parent);
|
||||||
ui.chat.updateMessage(this.parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerTargetHook() {
|
registerTargetHook() {
|
||||||
if(this.targetSelection && this.targetHook !== null) {
|
if (this.targetSelection && this.targetHook !== null) {
|
||||||
Hooks.off("targetToken", this.targetHook);
|
Hooks.off('targetToken', this.targetHook);
|
||||||
this.targetHook = null;
|
this.targetHook = null;
|
||||||
} else if(!this.targetSelection && this.targetHook === null) {
|
} else if (!this.targetSelection && this.targetHook === null) {
|
||||||
this.targetHook = Hooks.on("targetToken", foundry.utils.debounce(this.updateTargets.bind(this), 50));
|
this.targetHook = Hooks.on('targetToken', foundry.utils.debounce(this.updateTargets.bind(this), 50));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
if(this.hasTarget) {
|
if (this.hasTarget) {
|
||||||
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
||||||
this.updateTargets();
|
this.updateTargets();
|
||||||
this.registerTargetHook();
|
this.registerTargetHook();
|
||||||
if(this.targetSelection === true) {
|
if (this.targetSelection === true) {
|
||||||
this.targetShort = this.targets.reduce((a,c) => {
|
this.targetShort = this.targets.reduce(
|
||||||
if(c.hit) a.hit += 1;
|
(a, c) => {
|
||||||
else a.miss += 1;
|
if (c.hit) a.hit += 1;
|
||||||
return a;
|
else a.miss += 1;
|
||||||
}, {hit: 0, miss: 0})
|
return a;
|
||||||
|
},
|
||||||
|
{ hit: 0, miss: 0 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if(this.hasSave) this.setPendingSaves();
|
if (this.hasSave) this.setPendingSaves();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER');
|
this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER');
|
||||||
|
|
@ -144,22 +140,20 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
getTargetList() {
|
getTargetList() {
|
||||||
return this.targetSelection !== true
|
return this.targetSelection !== true
|
||||||
? Array.from(game.user.targets).map(t =>{
|
? Array.from(game.user.targets).map(t => {
|
||||||
const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t),
|
const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t),
|
||||||
oldTarget = this.targets.find(ot => ot.actorId === target.actorId) ?? this.oldTargets.find(ot => ot.actorId === target.actorId);
|
oldTarget =
|
||||||
if(oldTarget) return oldTarget;
|
this.targets.find(ot => ot.actorId === target.actorId) ??
|
||||||
return target;
|
this.oldTargets.find(ot => ot.actorId === target.actorId);
|
||||||
})
|
if (oldTarget) return oldTarget;
|
||||||
|
return target;
|
||||||
|
})
|
||||||
: this.targets;
|
: this.targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPendingSaves() {
|
setPendingSaves() {
|
||||||
this.pendingSaves = this.targetSelection
|
this.pendingSaves = this.targetSelection
|
||||||
? this.targets.filter(
|
? this.targets.filter(target => target.hit && target.saved.success === null).length > 0
|
||||||
target => target.hit && target.saved.success === null
|
: this.currentTargets.filter(target => target.saved.success === null).length > 0;
|
||||||
).length > 0
|
|
||||||
: this.currentTargets.filter(
|
|
||||||
target => target.saved.success === null
|
|
||||||
).length > 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ export default class RangeField extends fields.StringField {
|
||||||
const options = {
|
const options = {
|
||||||
choices: CONFIG.DH.GENERAL.range,
|
choices: CONFIG.DH.GENERAL.range,
|
||||||
required: false,
|
required: false,
|
||||||
blank: true
|
blank: true,
|
||||||
|
label: "DAGGERHEART.GENERAL.range"
|
||||||
};
|
};
|
||||||
super(options, context);
|
super(options, context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }),
|
type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }),
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities }),
|
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities, label: "DAGGERHEART.GENERAL.Trait.single" }),
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
||||||
advState: new fields.StringField({
|
advState: new fields.StringField({
|
||||||
|
|
|
||||||
|
|
@ -147,4 +147,8 @@ export default class DHArmor extends AttachableItem {
|
||||||
const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`];
|
const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`];
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get itemFeatures() {
|
||||||
|
return this.armorFeatures;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
return this.actions;
|
return this.actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get itemFeatures() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||||
* @param {object} [options] - Options which modify the getRollData method.
|
* @param {object} [options] - Options which modify the getRollData method.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
choices: CONFIG.DH.ACTOR.abilities,
|
choices: CONFIG.DH.ACTOR.abilities,
|
||||||
integer: false,
|
integer: false,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
initial: null
|
initial: null,
|
||||||
|
label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"
|
||||||
}),
|
}),
|
||||||
features: new ItemLinkFields(),
|
features: new ItemLinkFields(),
|
||||||
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ export default class DHWeapon extends AttachableItem {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
|
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1, label: "DAGGERHEART.GENERAL.Tiers.singular" }),
|
||||||
equipped: new fields.BooleanField({ initial: false }),
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
|
||||||
//SETTINGS
|
//SETTINGS
|
||||||
secondary: new fields.BooleanField({ initial: false }),
|
secondary: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" }),
|
||||||
burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded' }),
|
burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded', label: "DAGGERHEART.GENERAL.burden" }),
|
||||||
weaponFeatures: new fields.ArrayField(
|
weaponFeatures: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
|
|
@ -234,4 +234,8 @@ export default class DHWeapon extends AttachableItem {
|
||||||
|
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get itemFeatures() {
|
||||||
|
return this.weaponFeatures;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ export default class D20Roll extends DHRoll {
|
||||||
static DefaultDialog = D20RollDialog;
|
static DefaultDialog = D20RollDialog;
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
return game.i18n.localize(
|
return game.i18n.localize('DAGGERHEART.GENERAL.d20Roll');
|
||||||
"DAGGERHEART.GENERAL.d20Roll"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get d20() {
|
get d20() {
|
||||||
|
|
@ -147,7 +145,7 @@ export default class D20Roll extends DHRoll {
|
||||||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||||
target.hit = roll.isCritical || roll.total >= difficulty;
|
target.hit = roll.isCritical || roll.total >= difficulty;
|
||||||
});
|
});
|
||||||
data.success = config.targets.some(target => target.hit)
|
data.success = config.targets.some(target => target.hit);
|
||||||
} else if (config.roll.difficulty) {
|
} else if (config.roll.difficulty) {
|
||||||
data.difficulty = config.roll.difficulty;
|
data.difficulty = config.roll.difficulty;
|
||||||
data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
isInventoryItem === true
|
isInventoryItem === true
|
||||||
? 'Inventory Items' //TODO localize
|
? 'Inventory Items' //TODO localize
|
||||||
: isInventoryItem === false
|
: isInventoryItem === false
|
||||||
? 'Character Items' //TODO localize
|
? 'Character Items' //TODO localize
|
||||||
: 'Other'; //TODO localize
|
: 'Other'; //TODO localize
|
||||||
|
|
||||||
return { value: type, label, group };
|
return { value: type, label, group };
|
||||||
}
|
}
|
||||||
|
|
@ -130,7 +130,6 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
async use(event) {
|
async use(event) {
|
||||||
const actions = new Set(this.system.actionsList);
|
const actions = new Set(this.system.actionsList);
|
||||||
if (actions?.size) {
|
if (actions?.size) {
|
||||||
|
|
@ -152,10 +151,10 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
this.type === 'ancestry'
|
this.type === 'ancestry'
|
||||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
|
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
|
||||||
: this.type === 'community'
|
: this.type === 'community'
|
||||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||||
: this.type === 'feature'
|
: this.type === 'feature'
|
||||||
? game.i18n.localize('TYPES.Item.feature')
|
? game.i18n.localize('TYPES.Item.feature')
|
||||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||||
origin: origin,
|
origin: origin,
|
||||||
img: this.img,
|
img: this.img,
|
||||||
item: {
|
item: {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export default class RegisterHandlebarsHelpers {
|
||||||
damageSymbols: this.damageSymbols,
|
damageSymbols: this.damageSymbols,
|
||||||
rollParsed: this.rollParsed,
|
rollParsed: this.rollParsed,
|
||||||
hasProperty: foundry.utils.hasProperty,
|
hasProperty: foundry.utils.hasProperty,
|
||||||
|
getProperty: foundry.utils.getProperty,
|
||||||
setVar: this.setVar,
|
setVar: this.setVar,
|
||||||
empty: this.empty
|
empty: this.empty
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
"img": "icons/magic/time/hourglass-brown-orange.webp",
|
"img": "icons/magic/time/hourglass-brown-orange.webp",
|
||||||
"cost": [
|
"cost": [
|
||||||
{
|
{
|
||||||
"key": "armorStack",
|
"key": "armorSlot",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"keyIsID": false,
|
"keyIsID": false,
|
||||||
"scalable": false,
|
"scalable": false,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
},
|
},
|
||||||
"cost": [
|
"cost": [
|
||||||
{
|
{
|
||||||
"key": "armorStack",
|
"key": "armorSlot",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"keyIsID": false,
|
"keyIsID": false,
|
||||||
"scalable": false,
|
"scalable": false,
|
||||||
|
|
|
||||||
|
|
@ -67,4 +67,30 @@
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.two-big-buttons {
|
||||||
|
.window-content {
|
||||||
|
padding-top: 0;
|
||||||
|
|
||||||
|
.form-footer {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 1fr;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type='submit'] {
|
||||||
|
gap: 5px;
|
||||||
|
flex-direction: row;
|
||||||
|
font-family: @font-body;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: var(--font-size-14);
|
||||||
|
height: 40px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
@import './combat-sidebar/encounter-controls.less';
|
@import './combat-sidebar/encounter-controls.less';
|
||||||
@import './combat-sidebar/spotlight-control.less';
|
@import './combat-sidebar/spotlight-control.less';
|
||||||
@import './combat-sidebar/token-actions.less';
|
@import './combat-sidebar/token-actions.less';
|
||||||
|
@import './item-browser/item-browser.less';
|
||||||
|
|
||||||
@import './countdown/countdown.less';
|
@import './countdown/countdown.less';
|
||||||
@import './countdown/sheet.less';
|
@import './countdown/sheet.less';
|
||||||
|
|
|
||||||
410
styles/less/ui/item-browser/item-browser.less
Normal file
410
styles/less/ui/item-browser/item-browser.less
Normal file
|
|
@ -0,0 +1,410 @@
|
||||||
|
@import '../../utils/colors.less';
|
||||||
|
@import '../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.daggerheart.dh-style.compendium-browser {
|
||||||
|
border: initial;
|
||||||
|
.window-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-application-part='list'] {
|
||||||
|
flex: 1;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compendium-sidebar {
|
||||||
|
position: relative;
|
||||||
|
width: 200px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
background: @golden;
|
||||||
|
mask-image: linear-gradient(180deg, transparent 0%, black 50%, transparent 100%);
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compendium-container {
|
||||||
|
summary {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-family: @font-subtitle;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 2px 12px;
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&::marker, // Latest Chrome, Edge, Firefox
|
||||||
|
&::-webkit-details-marker // Safari
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .folder-list {
|
||||||
|
padding: 10px;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
&.folder-list {
|
||||||
|
> div {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compendium-results {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-path,
|
||||||
|
option,
|
||||||
|
select {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-path > :first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-path {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.item-path {
|
||||||
|
font-family: @font-body;
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
|
||||||
|
&.path-link {
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-list,
|
||||||
|
.item-list-header,
|
||||||
|
.item-header > div {
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-filter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.wrapper,
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
position: relative;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-family: @font-body;
|
||||||
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
border: none;
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
padding: 0 20px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline: 2px solid light-dark(@dark, @golden);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:placeholder {
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
align-content: center;
|
||||||
|
height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 1;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
[data-folder-id] {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
font-family: @font-body;
|
||||||
|
transition: all 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-selected,
|
||||||
|
[data-folder-id]:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: light-dark(@dark-blue-40, @golden-40);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subfolder-list {
|
||||||
|
margin: 5px 0;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
.is-selected,
|
||||||
|
[data-folder-id]:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list-header,
|
||||||
|
.item-header {
|
||||||
|
.item-info {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40px 400px repeat(auto-fit, minmax(100px, 1fr));
|
||||||
|
align-items: center;
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list-header,
|
||||||
|
.item-list {
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list-header,
|
||||||
|
.item-list [data-action='expandContent'] {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.item-list-img {
|
||||||
|
width: 40px;
|
||||||
|
flex: unset;
|
||||||
|
}
|
||||||
|
.item-list-name {
|
||||||
|
flex-grow: 3 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list-header {
|
||||||
|
align-items: center;
|
||||||
|
background-color: light-dark(@dark-15, @dark-golden-80);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 3px;
|
||||||
|
min-height: 30px;
|
||||||
|
font-family: @font-body;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-sort-key] {
|
||||||
|
&:after {
|
||||||
|
font-family: 'Font Awesome 6 Pro';
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sort-type='ASC']:after {
|
||||||
|
content: '\f0d7';
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sort-type='DESC']:after {
|
||||||
|
content: '\f0d8';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.item-container {
|
||||||
|
&:hover {
|
||||||
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-desc .wrapper {
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
font-family: @font-body;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
color: @beige;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0 0 0 1.25rem;
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-family: @font-body;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-content {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-content,
|
||||||
|
.item-desc {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 0fr;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
.wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
label {
|
||||||
|
flex: 1;
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
.form-fields {
|
||||||
|
width: 100%;
|
||||||
|
flex: 2;
|
||||||
|
|
||||||
|
input[type='number'] {
|
||||||
|
text-align: center;
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded + .extensible {
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family: @font-subtitle;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
font-family: @font-body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.lite,
|
||||||
|
&.no-folder {
|
||||||
|
.menu-path {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.lite {
|
||||||
|
.filter-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-filter {
|
||||||
|
.filter-header {
|
||||||
|
a[data-action='expandContent'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,25 +31,25 @@
|
||||||
{{#if document.system.class.value}}
|
{{#if document.system.class.value}}
|
||||||
<span data-action="editDoc" data-item-uuid="{{document.system.class.value.uuid}}">{{document.system.class.value.name}}</span>
|
<span data-action="editDoc" data-item-uuid="{{document.system.class.value.uuid}}">{{document.system.class.value.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="missing-header-feature" data-action="openPack" data-key="daggerheart.classes">{{localize 'TYPES.Item.class'}}</span>
|
<span class="missing-header-feature" data-action="openPack" data-key="classes">{{localize 'TYPES.Item.class'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
{{#if document.system.class.subclass}}
|
{{#if document.system.class.subclass}}
|
||||||
<span data-action="editDoc" data-item-uuid="{{document.system.class.subclass.uuid}}">{{document.system.class.subclass.name}}</span>
|
<span data-action="editDoc" data-item-uuid="{{document.system.class.subclass.uuid}}">{{document.system.class.subclass.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="missing-header-feature" data-action="openPack" data-key="daggerheart.subclasses">{{localize 'TYPES.Item.subclass'}}</span>
|
<span class="missing-header-feature" data-action="openPack" data-key="subclasses">{{localize 'TYPES.Item.subclass'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
{{#if document.system.community}}
|
{{#if document.system.community}}
|
||||||
<span data-action="editDoc" data-item-uuid="{{document.system.community.uuid}}">{{document.system.community.name}}</span>
|
<span data-action="editDoc" data-item-uuid="{{document.system.community.uuid}}">{{document.system.community.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="missing-header-feature" data-action="openPack" data-key="daggerheart.communities">{{localize 'TYPES.Item.community'}}</span>
|
<span class="missing-header-feature" data-action="openPack" data-key="communities">{{localize 'TYPES.Item.community'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
{{#if document.system.ancestry}}
|
{{#if document.system.ancestry}}
|
||||||
<span data-action="editDoc" data-item-uuid="{{document.system.ancestry.uuid}}">{{document.system.ancestry.name}}</span>
|
<span data-action="editDoc" data-item-uuid="{{document.system.ancestry.uuid}}">{{document.system.ancestry.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="missing-header-feature" data-action="openPack" data-key="daggerheart.ancestries">{{localize 'TYPES.Item.ancestry'}}</span>
|
<span class="missing-header-feature" data-action="openPack" data-key="ancestries">{{localize 'TYPES.Item.ancestry'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -58,13 +58,13 @@
|
||||||
{{#if document.system.multiclass.value}}
|
{{#if document.system.multiclass.value}}
|
||||||
<span data-action="editDoc"data-item-uuid="{{document.system.multiclass.value.uuid}}">{{document.system.multiclass.value.name}}</span>
|
<span data-action="editDoc"data-item-uuid="{{document.system.multiclass.value.uuid}}">{{document.system.multiclass.value.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span data-action="openPack" data-key="daggerheart.classes">{{localize 'DAGGERHEART.GENERAL.multiclass'}}</span>
|
<span data-action="openPack" data-key="classes">{{localize 'DAGGERHEART.GENERAL.multiclass'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
{{#if document.system.multiclass.subclass}}
|
{{#if document.system.multiclass.subclass}}
|
||||||
<span data-action="editDoc" data-item-uuid="{{document.system.multiclass.subclass.uuid}}">{{document.system.multiclass.subclass.name}}</span>
|
<span data-action="editDoc" data-item-uuid="{{document.system.multiclass.subclass.uuid}}">{{document.system.multiclass.subclass.name}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="missing-header-feature" data-action="openPack" data-key="daggerheart.subclasses">{{localize 'TYPES.Item.subclass'}}</span>
|
<span class="missing-header-feature" data-action="openPack" data-key="subclasses">{{localize 'TYPES.Item.subclass'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
<a class="filter-button">
|
<a class="filter-button">
|
||||||
<i class="fa-solid fa-filter"></i>
|
<i class="fa-solid fa-filter"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.compendiumBrowser'}}" data-action="tempBrowser">
|
||||||
|
<i class="fa-solid fa-book-atlas"></i>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="currency-section">
|
<div class="currency-section">
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Parameters:
|
||||||
<legend>
|
<legend>
|
||||||
{{localize title}}
|
{{localize title}}
|
||||||
{{#if canCreate}}
|
{{#if canCreate}}
|
||||||
<a data-action="createDoc" data-document-class="{{ifThen (eq type 'effect') 'ActiveEffect' 'Item' }}"
|
<a data-action="{{ifThen (or (eq type 'effect') (eq type 'feature')) 'createDoc' 'addNewItem' }}" data-document-class="{{ifThen (eq type 'effect') 'ActiveEffect' 'Item' }}"
|
||||||
data-type="{{ifThen (eq type 'effect') 'base' type}}"
|
data-type="{{ifThen (eq type 'effect') 'base' type}}"
|
||||||
{{#if inVault}}data-in-vault="{{inVault}}"{{/if}}
|
{{#if inVault}}data-in-vault="{{inVault}}"{{/if}}
|
||||||
{{#if disabled}} data-disabled="{{disabled}}"{{/if}}
|
{{#if disabled}} data-disabled="{{disabled}}"{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@
|
||||||
<fieldset class="two-columns">
|
<fieldset class="two-columns">
|
||||||
<legend>{{localize tabs.settings.label}}</legend>
|
<legend>{{localize tabs.settings.label}}</legend>
|
||||||
<span>{{localize "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"}}</span>
|
<span>{{localize "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"}}</span>
|
||||||
{{formField systemFields.spellcastingTrait value=source.system.spellcastingTrait localize=true}}
|
{{formInput systemFields.spellcastingTrait value=source.system.spellcastingTrait localize=true}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -6,15 +6,15 @@
|
||||||
<fieldset class="two-columns">
|
<fieldset class="two-columns">
|
||||||
<legend>{{localize tabs.settings.label}}</legend>
|
<legend>{{localize tabs.settings.label}}</legend>
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.Tiers.singular"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.Tiers.singular"}}</span>
|
||||||
{{formField systemFields.tier value=source.system.tier}}
|
{{formInput systemFields.tier value=source.system.tier}}
|
||||||
<span>{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}</span>
|
<span>{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}</span>
|
||||||
{{formField systemFields.secondary value=source.system.secondary}}
|
{{formInput systemFields.secondary value=source.system.secondary}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</span>
|
||||||
{{formInput systemFields.attack.fields.roll.fields.trait value=document.system.attack.roll.trait name="system.attack.roll.trait" label="DAGGERHEART.GENERAL.Trait.single" localize=true}}
|
{{formInput systemFields.attack.fields.roll.fields.trait value=document.system.attack.roll.trait name="system.attack.roll.trait" label="DAGGERHEART.GENERAL.Trait.single" localize=true}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.range"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.range"}}</span>
|
||||||
{{formInput systemFields.attack.fields.range value=document.system.attack.range label="Range" name="system.attack.range" localize=true}}
|
{{formInput systemFields.attack.fields.range value=document.system.attack.range label="Range" name="system.attack.range" localize=true}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.burden"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.burden"}}</span>
|
||||||
{{formField systemFields.burden value=source.system.burden localize=true}}
|
{{formInput systemFields.burden value=source.system.burden localize=true}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="two-columns">
|
<fieldset class="two-columns">
|
||||||
|
|
|
||||||
89
templates/ui/itemBrowser/itemBrowser.hbs
Normal file
89
templates/ui/itemBrowser/itemBrowser.hbs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<div class="compendium-results">
|
||||||
|
{{#if menu.data }}
|
||||||
|
<div class="menu-path">
|
||||||
|
{{#each menu.path}}
|
||||||
|
{{#if (eq this "folders")}}
|
||||||
|
<span class="path-link">
|
||||||
|
/
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="item-path">{{this}}</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="item-filter">
|
||||||
|
<div class="filter-header">
|
||||||
|
<div class="search-bar">
|
||||||
|
<div class="icon">
|
||||||
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
|
</div>
|
||||||
|
<input type="search" name="search" class="search-input" placeholder="Search...">
|
||||||
|
</div>
|
||||||
|
{{#if fieldFilter.length}}
|
||||||
|
<a data-tooltip="Filters" data-action="expandContent"><i class="fa-solid fa-filter"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
<a data-tooltip="Erase" data-action="resetFilters"><i class="fa-solid fa-eraser"></i></a>
|
||||||
|
</div>
|
||||||
|
<div class="filter-content extensible">
|
||||||
|
<div class="wrapper">
|
||||||
|
{{#each fieldFilter}}
|
||||||
|
{{#if choices }}
|
||||||
|
<div class="form-group"{{#with (lookup @root.presets.filter key)}}{{#if forced}} disabled{{/if}}{{/with}}>
|
||||||
|
<label>{{localize label}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<select data-key="{{key}}" name={{name}}>
|
||||||
|
{{selectOptions choices valueAttr="value" blank="" localize=true selected=value}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{#if filtered }}
|
||||||
|
{{formField field localize=true blank="" name=name choices=(@root.formatChoices this) valueAttr="value" dataset=(object key=key) value=value}}
|
||||||
|
{{else}}
|
||||||
|
{{#if field.label}}
|
||||||
|
{{formField field localize=true blank="" name=name dataset=(object key=key) value=value}}
|
||||||
|
{{else}}
|
||||||
|
{{formField field localize=true blank="" name=name dataset=(object key=key) label=label value=value}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{!-- <div class="item-list-container"> --}}
|
||||||
|
{{#if menu.data.columns.length}}
|
||||||
|
<div class="item-list-header">
|
||||||
|
<div class="item-list-img"></div>
|
||||||
|
<div class="item-list-name" data-sort-key="name" data-sort-type="ASC" data-action="sortList">Name</div>
|
||||||
|
{{#each menu.data.columns}}
|
||||||
|
<div data-sort-key="{{key}}" data-sort-type="" data-action="sortList">{{label}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="item-list">
|
||||||
|
{{#each items}}
|
||||||
|
<div class="item-container" data-item-uuid="{{uuid}}" draggable="true">
|
||||||
|
<div class="item-header">
|
||||||
|
<div class="item-info" data-action="expandContent">
|
||||||
|
<img src="{{img}}" data-item-key="img" class="item-list-img">
|
||||||
|
<div data-item-key="name" class="item-list-name">{{name}}</div>
|
||||||
|
{{#each ../menu.data.columns}}
|
||||||
|
<div data-item-key="{{key}}">{{#with (@root.formatLabel ../this this) as | label |}}{{{label}}}{{/with}}</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item-desc extensible">
|
||||||
|
<div class="wrapper">{{{system.description}}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{!-- </div> --}}
|
||||||
|
{{else}}
|
||||||
|
<div class="welcome-message">
|
||||||
|
<h2 class="title">Daggerheart Compendium Browser</h2>
|
||||||
|
<span class="hint"><i>Select a Folder in sidebar to start browsing trought the compendium</i></span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
28
templates/ui/itemBrowser/sidebar.hbs
Normal file
28
templates/ui/itemBrowser/sidebar.hbs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<div class="compendium-sidebar">
|
||||||
|
{{#each compendiums}}
|
||||||
|
<details class="compendium-container" data-compendium-id="{{id}}" open>
|
||||||
|
<summary>
|
||||||
|
{{label}}
|
||||||
|
<line-div></line-div>
|
||||||
|
</summary>
|
||||||
|
<div class="folder-list">
|
||||||
|
{{#each folders}}
|
||||||
|
<div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div>
|
||||||
|
{{!-- <div data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div> --}}
|
||||||
|
<div class="subfolder-list">
|
||||||
|
{{#each folders}}
|
||||||
|
<div
|
||||||
|
class="subfolder-item {{#if selected}} is-selected{{/if}}"
|
||||||
|
data-action="selectFolder"
|
||||||
|
data-folder-id="{{../id}}.folders.{{id}}"
|
||||||
|
>
|
||||||
|
• {{label}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<line-div></line-div>
|
||||||
|
</details>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue