mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-19 08:20:30 +01:00
Fix conflicts
This commit is contained in:
commit
3ff6ec4f5e
164 changed files with 2542 additions and 749 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
import { burden } from '../../config/generalConfig.mjs';
|
||||
import { createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
|
||||
import { ItemBrowser } from '../ui/itemBrowser.mjs';
|
||||
import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -20,8 +21,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
class: this.character.system.class?.value ?? {},
|
||||
subclass: this.character.system.class?.subclass ?? {},
|
||||
experiences: {
|
||||
[foundry.utils.randomID()]: { name: '', value: 2 },
|
||||
[foundry.utils.randomID()]: { name: '', value: 2 }
|
||||
[foundry.utils.randomID()]: { name: '', value: 2, core: true },
|
||||
[foundry.utils.randomID()]: { name: '', value: 2, core: true }
|
||||
},
|
||||
domainCards: {
|
||||
[foundry.utils.randomID()]: {},
|
||||
|
|
@ -42,6 +43,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
};
|
||||
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
|
||||
this.itemBrowser = null;
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -370,13 +373,18 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
);
|
||||
context.armor = {
|
||||
...this.equipment.armor,
|
||||
suggestion: { ...suggestions.armor, taken: suggestions.armor?.uuid === this.equipment.armor?.uuid },
|
||||
suggestion: {
|
||||
...suggestions.armor,
|
||||
uuid: suggestions.armor?.uuid,
|
||||
taken: suggestions.armor?.uuid === this.equipment.armor?.uuid
|
||||
},
|
||||
compendium: 'armors'
|
||||
};
|
||||
context.primaryWeapon = {
|
||||
...this.equipment.primaryWeapon,
|
||||
suggestion: {
|
||||
...suggestions.primaryWeapon,
|
||||
uuid: suggestions.primaryWeapon?.uuid,
|
||||
taken: suggestions.primaryWeapon?.uuid === this.equipment.primaryWeapon?.uuid
|
||||
},
|
||||
compendium: 'weapons'
|
||||
|
|
@ -385,6 +393,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
...this.equipment.secondaryWeapon,
|
||||
suggestion: {
|
||||
...suggestions.secondaryWeapon,
|
||||
uuid: suggestions.secondaryWeapon?.uuid,
|
||||
taken: suggestions.secondaryWeapon?.uuid === this.equipment.secondaryWeapon?.uuid
|
||||
},
|
||||
disabled: this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value,
|
||||
|
|
@ -485,8 +494,24 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
});
|
||||
}
|
||||
|
||||
static async viewCompendium(_, target) {
|
||||
(await game.packs.get(`daggerheart.${target.dataset.compendium}`))?.render(true);
|
||||
static async viewCompendium(event, target) {
|
||||
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) {
|
||||
|
|
@ -555,13 +580,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
await createEmbeddedItemWithEffects(this.character, this.setup.community);
|
||||
await createEmbeddedItemWithEffects(this.character, this.setup.class);
|
||||
await createEmbeddedItemWithEffects(this.character, this.setup.subclass);
|
||||
await this.character.createEmbeddedDocuments(
|
||||
'Item',
|
||||
Object.values(this.setup.domainCards).map(x => ({
|
||||
...x,
|
||||
effects: x.effects?.map(effect => effect.toObject())
|
||||
}))
|
||||
);
|
||||
await createEmbeddedItemsWithEffects(this.character, Object.values(this.setup.domainCards));
|
||||
|
||||
if (this.equipment.armor.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.armor, {
|
||||
|
|
@ -583,23 +602,28 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
if (this.equipment.inventory.choiceB.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceB);
|
||||
|
||||
await this.character.createEmbeddedDocuments(
|
||||
'Item',
|
||||
this.setup.class.system.inventory.take
|
||||
.filter(x => x)
|
||||
.map(x => ({
|
||||
...x,
|
||||
effects: x.effects?.map(effect => effect.toObject())
|
||||
}))
|
||||
await createEmbeddedItemsWithEffects(
|
||||
this.character,
|
||||
this.setup.class.system.inventory.take.filter(x => x)
|
||||
);
|
||||
|
||||
await this.character.update({
|
||||
system: {
|
||||
traits: this.setup.traits,
|
||||
experiences: this.setup.experiences
|
||||
}
|
||||
});
|
||||
await this.character.update(
|
||||
{
|
||||
system: {
|
||||
traits: this.setup.traits,
|
||||
experiences: {
|
||||
...this.setup.experiences,
|
||||
...Object.keys(this.character.system.experiences).reduce((acc, key) => {
|
||||
acc[`-=${key}`] = null;
|
||||
return acc;
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
},
|
||||
{ overwrite: true }
|
||||
);
|
||||
|
||||
if (this.itemBrowser) this.itemBrowser.close();
|
||||
this.close();
|
||||
}
|
||||
|
||||
|
|
@ -690,6 +714,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
return;
|
||||
}
|
||||
|
||||
if (item.system.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) {
|
||||
this.equipment.secondaryWeapon = {};
|
||||
}
|
||||
|
||||
this.equipment.primaryWeapon = { ...item, uuid: item.uuid };
|
||||
} else if (item.type === 'weapon' && event.target.closest('.secondary-weapon-card')) {
|
||||
if (this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.hasHealing ? 'healing' : 'damage'}Roll.name`);
|
||||
return game.i18n.localize(
|
||||
`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.hasHealing ? 'healing' : 'damage'}Roll.name`
|
||||
);
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,10 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
const actionItems = this.actor.items.reduce((acc, x) => {
|
||||
if (x.system.actions) {
|
||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||
if (action.uses.recovery && (action.uses.recovery === 'shortRest') === this.shortrest) {
|
||||
if (
|
||||
(action.uses.recovery && (action.uses.recovery === 'longRest') === !this.shortrest) ||
|
||||
action.uses.recovery === 'shortRest'
|
||||
) {
|
||||
acc.push({
|
||||
title: x.name,
|
||||
name: action.name,
|
||||
|
|
@ -116,7 +119,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
if (
|
||||
x.system.resource &&
|
||||
x.system.resource.type &&
|
||||
(x.system.resource.recovery === 'shortRest') === this.shortrest
|
||||
((x.system.resource.recovery === 'longRest') === !this.shortrest ||
|
||||
x.system.resource.recovery === 'shortRest')
|
||||
) {
|
||||
acc.push({
|
||||
title: game.i18n.localize(`TYPES.Item.${x.type}`),
|
||||
|
|
@ -226,14 +230,18 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
) {
|
||||
for (var data of this.refreshables.actionItems) {
|
||||
const action = await foundry.utils.fromUuid(data.uuid);
|
||||
await action.parent.parent.update({ [`system.actions.${action.id}.uses.value`]: action.uses.max ?? 1 });
|
||||
await action.parent.parent.update({ [`system.actions.${action.id}.uses.value`]: 0 });
|
||||
}
|
||||
|
||||
for (var data of this.refreshables.resourceItems) {
|
||||
const feature = await foundry.utils.fromUuid(data.uuid);
|
||||
const increasing =
|
||||
feature.system.resource.progression === CONFIG.DH.ITEM.itemResourceProgression.increasing.id;
|
||||
const resetValue = increasing ? 0 : (feature.system.resource.max ?? 0);
|
||||
const resetValue = increasing
|
||||
? 0
|
||||
: feature.system.resource.max
|
||||
? Roll.replaceFormulaData(feature.system.resource.max, this.actor)
|
||||
: 0;
|
||||
await feature.update({ 'system.resource.value': resetValue });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
|
|||
const experience = Object.keys(this.actor.system.experiences).find(
|
||||
x => x === data
|
||||
);
|
||||
return this.actor.system.experiences[experience]?.description ?? '';
|
||||
return this.actor.system.experiences[experience]?.name ?? '';
|
||||
});
|
||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
|
||||
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
|
||||
import { ItemBrowser } from '../ui/itemBrowser.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -11,6 +12,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
this.tabGroups.primary = 'advancements';
|
||||
|
||||
this.itemBrowser = null;
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -44,10 +47,12 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
|
||||
static PARTS = {
|
||||
tabs: { template: 'systems/daggerheart/templates/levelup/tabs/tab-navigation.hbs' },
|
||||
advancements: { template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs' },
|
||||
advancements: {
|
||||
template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs'
|
||||
},
|
||||
selections: {
|
||||
template: 'systems/daggerheart/templates/levelup/tabs/selections.hbs',
|
||||
scrollable: ['.selections']
|
||||
scrollable: ['.levelup-selections-container']
|
||||
},
|
||||
summary: { template: 'systems/daggerheart/templates/levelup/tabs/summary.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/levelup/tabs/footer.hbs' }
|
||||
|
|
@ -531,8 +536,30 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async viewCompendium(_, button) {
|
||||
(await game.packs.get(`daggerheart.${button.dataset.compendium}`))?.render(true);
|
||||
static async viewCompendium(event, target) {
|
||||
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) {
|
||||
|
|
@ -633,6 +660,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
}, {});
|
||||
|
||||
await this.actor.levelUp(levelupData);
|
||||
if (this.itemBrowser) this.itemBrowser.close();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
|
||||
export { default as DhAutomationSettings } from './automationSettings.mjs';
|
||||
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
|
||||
export { default as DhRangeMeasurementSettings } from './rangeMeasurementSettings.mjs';
|
||||
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
import { DhRangeMeasurement } from '../../data/settings/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhRangeMeasurementSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhRangeMeasurement(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement).toObject()
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-automation-settings',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-gears'
|
||||
},
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: 'systems/daggerheart/templates/settings/range-measurement-settings.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource(updatedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhRangeMeasurement();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement,
|
||||
this.settings.toObject()
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
|||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
|
||||
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||
|
||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||
|
||||
|
|
@ -25,7 +26,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||
toggleResourceDice: CharacterSheet.#toggleResourceDice,
|
||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||
useDowntime: this.useDowntime
|
||||
useDowntime: this.useDowntime,
|
||||
tempBrowser: CharacterSheet.#tempBrowser
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
|
|
@ -156,7 +158,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
currency: {
|
||||
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||
handfulls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfulls'),
|
||||
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||
}
|
||||
|
|
@ -178,6 +180,13 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'header':
|
||||
const { playerCanEditSheet, levelupAuto } = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||
);
|
||||
context.showSettings = game.user.isGM || !levelupAuto || (levelupAuto && playerCanEditSheet);
|
||||
break;
|
||||
case 'loadout':
|
||||
await this._prepareLoadoutContext(context, options);
|
||||
break;
|
||||
|
|
@ -188,6 +197,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
await this._prepareBiographyContext(context, options);
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -592,7 +602,16 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
*/
|
||||
static async #openPack(_event, button) {
|
||||
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 });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -707,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.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -101,8 +101,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
const item = event.currentTarget.closest('.inventory-item');
|
||||
|
||||
if (item) {
|
||||
const adversary = game.actors.find(x => x.type === 'adversary' && x.id === item.dataset.itemId);
|
||||
const adversaryData = { type: 'Actor', uuid: adversary.uuid };
|
||||
const adversaryData = { type: 'Actor', uuid: item.dataset.itemUuid };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
|
||||
event.dataTransfer.setDragImage(item, 60, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
|
||||
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||
|
||||
/**
|
||||
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
|
||||
|
|
@ -82,7 +83,9 @@ export default function DHApplicationMixin(Base) {
|
|||
toChat: DHSheetV2.#toChat,
|
||||
useItem: DHSheetV2.#useItem,
|
||||
toggleEffect: DHSheetV2.#toggleEffect,
|
||||
toggleExtended: DHSheetV2.#toggleExtended
|
||||
toggleExtended: DHSheetV2.#toggleExtended,
|
||||
addNewItem: DHSheetV2.#addNewItem,
|
||||
browseItem: DHSheetV2.#browseItem
|
||||
},
|
||||
contextMenus: [
|
||||
{
|
||||
|
|
@ -342,11 +345,25 @@ export default function DHApplicationMixin(Base) {
|
|||
icon: 'fa-solid fa-burst',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length;
|
||||
},
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
callback: async (target, event) => {
|
||||
const doc = await getDocFromElement(target),
|
||||
action = doc?.system?.attack ?? doc;
|
||||
return action && action.use(event, { byPassRoll: true });
|
||||
}
|
||||
});
|
||||
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||
icon: 'fa-solid fa-burst',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
},
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
});
|
||||
|
||||
if (toChat)
|
||||
options.push({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||
|
|
@ -423,6 +440,68 @@ export default function DHApplicationMixin(Base) {
|
|||
/* 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.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
},
|
||||
actions: {
|
||||
openSettings: DHBaseActorSheet.#openSettings,
|
||||
sendExpToChat: DHBaseActorSheet.#sendExpToChat
|
||||
sendExpToChat: DHBaseActorSheet.#sendExpToChat,
|
||||
increaseActionUses: event => DHBaseActorSheet.#modifyActionUses(event, true)
|
||||
},
|
||||
contextMenus: [
|
||||
{
|
||||
|
|
@ -70,6 +71,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
return context;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.item-button .action-uses-button').forEach(element => {
|
||||
element.addEventListener('contextmenu', DHBaseActorSheet.#modifyActionUses);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare render context for the Effect part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
|
|
@ -154,6 +164,19 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
cls.create(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static async #modifyActionUses(event, increase) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const actionId = event.target.dataset.itemUuid;
|
||||
const action = await foundry.utils.fromUuid(actionId);
|
||||
|
||||
const newValue = (action.uses.value ?? 0) + (increase ? 1 : -1);
|
||||
await action.update({ 'uses.value': Math.min(Math.max(newValue, 0), action.uses.max ?? 0) });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Drag/Drop */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import DHBaseItemSheet from '../api/base-item.mjs';
|
|||
export default class FeatureSheet extends DHBaseItemSheet {
|
||||
/** @inheritDoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'daggerheart-feature',
|
||||
classes: ['feature'],
|
||||
actions: {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class SubclassSheet extends DHBaseItemSheet {
|
|||
static DEFAULT_OPTIONS = {
|
||||
classes: ['subclass'],
|
||||
position: { width: 600 },
|
||||
window: { resizable: false }
|
||||
window: { resizable: true }
|
||||
};
|
||||
|
||||
/**@override */
|
||||
|
|
|
|||
|
|
@ -159,11 +159,12 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
event.stopPropagation();
|
||||
|
||||
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);
|
||||
if(event.currentTarget.dataset.directDamage)
|
||||
action.use(event, { byPassRoll: true })
|
||||
else
|
||||
action.use(event);
|
||||
const action =
|
||||
item.system.attack?.id === event.currentTarget.id
|
||||
? item.system.attack
|
||||
: item.system.actions.get(event.currentTarget.id);
|
||||
if (event.currentTarget.dataset.directDamage) action.use(event, { byPassRoll: true });
|
||||
else action.use(event);
|
||||
}
|
||||
|
||||
async actionUseButton(event, message) {
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
}
|
||||
}
|
||||
|
||||
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
|
||||
await this.viewed.update({
|
||||
turn: this.viewed.turn === toggleTurn ? null : toggleTurn,
|
||||
round: this.viewed.round + 1
|
||||
});
|
||||
await combatant.update(update);
|
||||
}
|
||||
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
|
|||
|
||||
const rangeMeasurementSettings = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement
|
||||
);
|
||||
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
||||
).rangeMeasurement;
|
||||
if (rangeMeasurementSettings.enabled) {
|
||||
const splitRulerText = this.ruler.text.split(' ');
|
||||
if (splitRulerText.length > 0) {
|
||||
|
|
@ -29,7 +29,7 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
|
|||
if (distance <= settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
|
||||
}
|
||||
if (distance <= settings.veryFar) {
|
||||
if (distance > settings.far) {
|
||||
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class DhpRuler extends foundry.canvas.interaction.Ruler {
|
|||
const context = super._getWaypointLabelContext(waypoint, state);
|
||||
if (!context) return;
|
||||
|
||||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement);
|
||||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
|
|
|
|||
|
|
@ -53,4 +53,40 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
this.effects.renderable = true;
|
||||
this.renderFlags.set({ refreshEffects: true });
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
_drawBar(number, bar, data) {
|
||||
const val = Number(data.value);
|
||||
const pct = Math.clamp(val, 0, data.max) / data.max;
|
||||
|
||||
// Determine sizing
|
||||
const { width, height } = this.document.getSize();
|
||||
const s = canvas.dimensions.uiScale;
|
||||
const bw = width;
|
||||
const bh = 8 * (this.document.height >= 2 ? 1.5 : 1) * s;
|
||||
|
||||
// Determine the color to use
|
||||
const fillColor =
|
||||
number === 0 ? foundry.utils.Color.fromRGB([1, 0, 0]) : foundry.utils.Color.fromString('#0032b1');
|
||||
|
||||
// Draw the bar
|
||||
const widthUnit = bw / data.max;
|
||||
bar.clear().lineStyle(s, 0x000000, 1.0);
|
||||
const sections = [...Array(data.max).keys()];
|
||||
for (let mark of sections) {
|
||||
const x = mark * widthUnit;
|
||||
const marked = mark + 1 <= data.value;
|
||||
const color = marked ? fillColor : foundry.utils.Color.fromRGB([0, 0, 0]);
|
||||
if (mark === 0 || mark === sections.length - 1) {
|
||||
bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s); // Would like drawRoundedRect, but it's very troublsome with the corners. Leaving for now.
|
||||
} else {
|
||||
bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s);
|
||||
}
|
||||
}
|
||||
|
||||
// Set position
|
||||
const posY = number === 0 ? height - bh : 0;
|
||||
bar.position.set(0, posY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.Toke
|
|||
const context = super._getWaypointLabelContext(waypoint, state);
|
||||
if (!context) return;
|
||||
|
||||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement);
|
||||
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||
|
||||
if (range.enabled) {
|
||||
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ export * as generalConfig from './generalConfig.mjs';
|
|||
export * as itemConfig from './itemConfig.mjs';
|
||||
export * as settingsConfig from './settingsConfig.mjs';
|
||||
export * as systemConfig from './system.mjs';
|
||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const ruleChoice = {
|
|||
}
|
||||
};
|
||||
|
||||
export const range = {
|
||||
export const templateRanges = {
|
||||
self: {
|
||||
id: 'self',
|
||||
short: 's',
|
||||
|
|
@ -56,7 +56,11 @@ export const range = {
|
|||
label: 'DAGGERHEART.CONFIG.Range.far.name',
|
||||
description: 'DAGGERHEART.CONFIG.Range.far.description',
|
||||
distance: 20
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export const range = {
|
||||
...templateRanges,
|
||||
veryFar: {
|
||||
id: 'veryFar',
|
||||
short: 'vf',
|
||||
|
|
@ -479,7 +483,8 @@ export const multiplierTypes = {
|
|||
cast: 'Spellcast',
|
||||
scale: 'Cost Scaling',
|
||||
result: 'Roll Result',
|
||||
flat: 'Flat'
|
||||
flat: 'Flat',
|
||||
tier: 'Tier'
|
||||
};
|
||||
|
||||
export const diceSetNumbers = {
|
||||
|
|
|
|||
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',
|
||||
cost: [
|
||||
{
|
||||
key: 'armorStack',
|
||||
key: 'armorSlot',
|
||||
value: 1
|
||||
}
|
||||
],
|
||||
|
|
@ -857,7 +857,7 @@ export const weaponFeatures = {
|
|||
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.description',
|
||||
img: 'icons/commodities/currency/coins-crown-stack-gold.webp',
|
||||
// Should cost handfull of gold,
|
||||
// Should cost handful of gold,
|
||||
effects: [
|
||||
{
|
||||
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.name',
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export const menu = {
|
|||
export const gameSettings = {
|
||||
Automation: 'Automation',
|
||||
Homebrew: 'Homebrew',
|
||||
RangeMeasurement: 'RangeMeasurement',
|
||||
appearance: 'Appearance',
|
||||
variantRules: 'VariantRules',
|
||||
Resources: {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import * as SETTINGS from './settingsConfig.mjs';
|
|||
import * as EFFECTS from './effectConfig.mjs';
|
||||
import * as ACTIONS from './actionConfig.mjs';
|
||||
import * as FLAGS from './flagsConfig.mjs';
|
||||
import * as ITEMBROWSER from './itemBrowserConfig.mjs'
|
||||
|
||||
export const SYSTEM_ID = 'daggerheart';
|
||||
|
||||
|
|
@ -18,5 +19,6 @@ export const SYSTEM = {
|
|||
SETTINGS,
|
||||
EFFECTS,
|
||||
ACTIONS,
|
||||
FLAGS
|
||||
FLAGS,
|
||||
ITEMBROWSER
|
||||
};
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
||||
|
||||
if (this.chatDisplay) await this.toChat();
|
||||
|
||||
let { byPassRoll } = options,
|
||||
config = this.prepareConfig(event, byPassRoll);
|
||||
for (let i = 0; i < this.constructor.extraSchemas.length; i++) {
|
||||
|
|
@ -221,16 +220,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const resources = config.costs
|
||||
.filter(c =>
|
||||
c.enabled !== false
|
||||
&&
|
||||
(
|
||||
(!successCost && (!c.consumeOnSuccess || config.roll?.success))
|
||||
||
|
||||
(successCost && c.consumeOnSuccess)
|
||||
)
|
||||
.filter(
|
||||
c =>
|
||||
c.enabled !== false &&
|
||||
((!successCost && (!c.consumeOnSuccess || config.roll?.success)) ||
|
||||
(successCost && c.consumeOnSuccess))
|
||||
)
|
||||
.map(c => {
|
||||
const resource = usefulResources[c.key];
|
||||
|
|
@ -243,14 +239,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
});
|
||||
|
||||
await this.actor.modifyResource(resources);
|
||||
if (config.uses?.enabled
|
||||
&&
|
||||
(
|
||||
(!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success))
|
||||
||
|
||||
(successCost && config.uses?.consumeOnSuccess)
|
||||
)
|
||||
) this.update({ 'uses.value': this.uses.value + 1 });
|
||||
if (
|
||||
config.uses?.enabled &&
|
||||
((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) ||
|
||||
(successCost && config.uses?.consumeOnSuccess))
|
||||
)
|
||||
this.update({ 'uses.value': this.uses.value + 1 });
|
||||
|
||||
if(config.roll?.success || successCost) {
|
||||
setTimeout(() => {
|
||||
|
|
@ -309,11 +303,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
async applyEffect(effect, actor) {
|
||||
const origin = effect.parent?.parent ? effect.parent.parent.uuid : effect.parent.uuid;
|
||||
// Enable an existing effect on the target if it originated from this effect
|
||||
const existingEffect = actor.effects.find(e => e.origin === origin);
|
||||
const existingEffect = actor.effects.find(e => e.origin === effect.uuid);
|
||||
if (existingEffect) {
|
||||
return existingEffect.update(
|
||||
return effect.update(
|
||||
foundry.utils.mergeObject({
|
||||
...effect.constructor.getInitialDuration(),
|
||||
disabled: false
|
||||
|
|
@ -326,7 +318,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
...effect.toObject(),
|
||||
disabled: false,
|
||||
transfer: false,
|
||||
origin: origin
|
||||
origin: effect.uuid
|
||||
});
|
||||
await ActiveEffect.implementation.create(effectData, { parent: actor });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
dialog: {},
|
||||
data: this.getRollData(),
|
||||
targetSelection: systemData.targets.length > 0
|
||||
}
|
||||
};
|
||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||
if (data.system) {
|
||||
config.source.message = data._id;
|
||||
|
|
|
|||
|
|
@ -68,12 +68,13 @@ export default class DhCharacter extends BaseDataActor {
|
|||
new fields.SchemaField({
|
||||
name: new fields.StringField(),
|
||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
description: new fields.StringField()
|
||||
description: new fields.StringField(),
|
||||
core: new fields.BooleanField({ initial: false })
|
||||
})
|
||||
),
|
||||
gold: new fields.SchemaField({
|
||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||
handfulls: new fields.NumberField({ initial: 0, integer: true }),
|
||||
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
|
|
@ -573,7 +574,10 @@ export default class DhCharacter extends BaseDataActor {
|
|||
case 'experience':
|
||||
selection.data.forEach(id => {
|
||||
const experience = this.experiences[id];
|
||||
if (experience) experience.value += selection.value;
|
||||
if (experience) {
|
||||
experience.value += selection.value;
|
||||
experience.leveledUp = true;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -620,6 +624,23 @@ export default class DhCharacter extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, userId) {
|
||||
const allowed = await super._preUpdate(changes, options, userId);
|
||||
if (allowed === false) return;
|
||||
|
||||
/* The first two experiences are always marked as core */
|
||||
if (changes.system?.experiences && Object.keys(this.experiences).length < 2) {
|
||||
const experiences = new Set(Object.keys(this.experiences));
|
||||
const changeExperiences = new Set(Object.keys(changes.system.experiences));
|
||||
const newExperiences = Array.from(changeExperiences.difference(experiences));
|
||||
|
||||
for (var i = 0; i < Math.min(newExperiences.length, 2 - experiences.size); i++) {
|
||||
const experience = newExperiences[i];
|
||||
changes.system.experiences[experience].core = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _preDelete() {
|
||||
if (this.companion) {
|
||||
this.companion.updateLevel(1);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
const targetsField = () => new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({}),
|
||||
actorId: new fields.StringField({}),
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||
evasion: new fields.NumberField({ integer: true }),
|
||||
hit: new fields.BooleanField({ initial: false }),
|
||||
saved: new fields.SchemaField({
|
||||
result: new fields.NumberField(),
|
||||
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||
const targetsField = () =>
|
||||
new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
id: new fields.StringField({}),
|
||||
actorId: new fields.StringField({}),
|
||||
name: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||
evasion: new fields.NumberField({ integer: true }),
|
||||
hit: new fields.BooleanField({ initial: false }),
|
||||
saved: new fields.SchemaField({
|
||||
result: new fields.NumberField(),
|
||||
success: new fields.BooleanField({ nullable: true, initial: null })
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||
targetHook = null;
|
||||
|
|
@ -39,27 +40,25 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
action: new fields.StringField()
|
||||
}),
|
||||
damage: new fields.ObjectField(),
|
||||
costs: new fields.ArrayField(
|
||||
new fields.ObjectField()
|
||||
),
|
||||
costs: new fields.ArrayField(new fields.ObjectField()),
|
||||
successConsumed: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
get actionActor() {
|
||||
if(!this.source.actor) return null;
|
||||
if (!this.source.actor) return null;
|
||||
return fromUuidSync(this.source.actor);
|
||||
}
|
||||
|
||||
get actionItem() {
|
||||
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);
|
||||
}
|
||||
|
||||
get action() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +73,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
|
@ -98,13 +97,13 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
if(this.targetSelection && this.targetHook !== null) {
|
||||
Hooks.off("targetToken", this.targetHook);
|
||||
this.targetHook = null;
|
||||
} else if(!this.targetSelection && this.targetHook === null) {
|
||||
this.targetHook = Hooks.on("targetToken", foundry.utils.debounce(this.updateTargets.bind(this), 50));
|
||||
} else if (!this.targetSelection && this.targetHook === null) {
|
||||
this.targetHook = Hooks.on('targetToken', foundry.utils.debounce(this.updateTargets.bind(this), 50));
|
||||
}
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
if(this.hasTarget) {
|
||||
if (this.hasTarget) {
|
||||
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
||||
this.currentTargets = this.getTargetList();
|
||||
this. registerTargetHook();
|
||||
|
|
@ -116,9 +115,9 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
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.canButtonApply = game.user.isGM;
|
||||
}
|
||||
|
|
@ -139,11 +138,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
|
||||
setPendingSaves() {
|
||||
this.pendingSaves = this.targetSelection
|
||||
? this.targets.filter(
|
||||
target => target.hit && target.saved.success === null
|
||||
).length > 0
|
||||
: this.currentTargets.filter(
|
||||
target => target.saved.success === null
|
||||
).length > 0;
|
||||
? this.targets.filter(target => target.hit && target.saved.success === null).length > 0
|
||||
: this.currentTargets.filter(target => target.saved.success === null).length > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,25 @@ export default class BeastformField extends fields.SchemaField {
|
|||
constructor(options = {}, context = {}) {
|
||||
const beastformFields = {
|
||||
tierAccess: new fields.SchemaField({
|
||||
exact: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||
exact: new fields.NumberField({
|
||||
integer: true,
|
||||
nullable: true,
|
||||
initial: null,
|
||||
choices: () => {
|
||||
const settingsTiers = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.LevelTiers
|
||||
).tiers;
|
||||
return Object.values(settingsTiers).reduce(
|
||||
(acc, tier) => {
|
||||
acc[tier.tier] = game.i18n.localize(tier.name);
|
||||
return acc;
|
||||
},
|
||||
{ 1: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') }
|
||||
);
|
||||
},
|
||||
hint: 'DAGGERHEART.ACTIONS.Config.beastform.exactHint'
|
||||
})
|
||||
})
|
||||
};
|
||||
super(beastformFields, options, context);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ export default class RangeField extends fields.StringField {
|
|||
const options = {
|
||||
choices: CONFIG.DH.GENERAL.range,
|
||||
required: false,
|
||||
blank: true
|
||||
blank: true,
|
||||
label: "DAGGERHEART.GENERAL.range"
|
||||
};
|
||||
super(options, context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
|||
static defineSchema() {
|
||||
return {
|
||||
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 }),
|
||||
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
||||
advState: new fields.StringField({
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ export default class UsesField extends fields.SchemaField {
|
|||
initial: null,
|
||||
nullable: true
|
||||
}),
|
||||
consumeOnSuccess: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label" })
|
||||
consumeOnSuccess: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label'
|
||||
})
|
||||
};
|
||||
super(usesFields, options, context);
|
||||
}
|
||||
|
|
@ -30,6 +33,7 @@ export default class UsesField extends fields.SchemaField {
|
|||
if (!uses) return null;
|
||||
return {
|
||||
...uses,
|
||||
remaining: this.remainingUses,
|
||||
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
|
||||
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||
import MappingField from './mappingField.mjs';
|
||||
|
||||
/**
|
||||
|
|
@ -164,6 +165,15 @@ export function ActionMixin(Base) {
|
|||
return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection;
|
||||
}
|
||||
|
||||
get remainingUses() {
|
||||
if (!this.uses) return null;
|
||||
|
||||
return Math.max(
|
||||
(this.uses.max ? itemAbleRollParse(this.uses.max, this.actor) : 0) - (this.uses.value ?? 0),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
static async create(data, operation = {}) {
|
||||
const { parent, renderSheet } = operation;
|
||||
let { type } = data;
|
||||
|
|
|
|||
|
|
@ -147,4 +147,8 @@ export default class DHArmor extends AttachableItem {
|
|||
const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`];
|
||||
return labels;
|
||||
}
|
||||
|
||||
get itemFeatures() {
|
||||
return this.armorFeatures;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
return this.actions;
|
||||
}
|
||||
|
||||
get itemFeatures() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
|
|
|
|||
|
|
@ -88,6 +88,26 @@ export default class DHBeastform extends BaseDataItem {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get beastformAttackData() {
|
||||
const effect = this.parent.effects.find(x => x.type === 'beastform');
|
||||
if (!effect) return null;
|
||||
|
||||
const traitBonus = effect.changes.find(x => x.key === `system.traits.${this.mainTrait}.value`)?.value ?? 0;
|
||||
const evasionBonus = effect.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
|
||||
|
||||
const damageDiceIndex = effect.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
|
||||
const damageDice = damageDiceIndex ? Object.keys(CONFIG.DH.GENERAL.diceTypes)[damageDiceIndex.value] : null;
|
||||
const damageBonus = effect.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
|
||||
|
||||
return {
|
||||
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.mainTrait].label),
|
||||
traitBonus: traitBonus ? Number(traitBonus).signedString() : '',
|
||||
evasionBonus: evasionBonus ? Number(evasionBonus).signedString() : '',
|
||||
damageDice: damageDice,
|
||||
damageBonus: damageBonus ? `${Number(damageBonus).signedString()}` : ''
|
||||
};
|
||||
}
|
||||
|
||||
async _preCreate() {
|
||||
if (!this.actor) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ export default class DHSubclass extends BaseDataItem {
|
|||
choices: CONFIG.DH.ACTOR.abilities,
|
||||
integer: false,
|
||||
nullable: true,
|
||||
initial: null
|
||||
initial: null,
|
||||
label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"
|
||||
}),
|
||||
features: new ItemLinkFields(),
|
||||
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;
|
||||
return {
|
||||
...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 }),
|
||||
|
||||
//SETTINGS
|
||||
secondary: new fields.BooleanField({ initial: false }),
|
||||
burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded' }),
|
||||
secondary: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" }),
|
||||
burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded', label: "DAGGERHEART.GENERAL.burden" }),
|
||||
weaponFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
|
|
@ -234,4 +234,8 @@ export default class DHWeapon extends AttachableItem {
|
|||
|
||||
return labels;
|
||||
}
|
||||
|
||||
get itemFeatures() {
|
||||
return this.weaponFeatures;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label'
|
||||
}),
|
||||
playerCanEditSheet: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
initial: 'Coins',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'
|
||||
}),
|
||||
handfulls: new fields.StringField({
|
||||
handfuls: new fields.StringField({
|
||||
required: true,
|
||||
initial: 'Handfulls',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.handfullName'
|
||||
initial: 'Handfuls',
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'
|
||||
}),
|
||||
bags: new fields.StringField({
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
export default class DhRangeMeasurement extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
enabled: new fields.BooleanField({ required: true, initial: true, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||
melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.CONFIG.Range.melee.name' }),
|
||||
veryClose: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 15,
|
||||
label: 'DAGGERHEART.CONFIG.Range.veryClose.name'
|
||||
}),
|
||||
close: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 30,
|
||||
label: 'DAGGERHEART.CONFIG.Range.close.name'
|
||||
}),
|
||||
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' }),
|
||||
veryFar: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 120,
|
||||
label: 'DAGGERHEART.CONFIG.Range.veryFar.name'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -17,9 +17,28 @@ export default class DhVariantRules extends foundry.abstract.DataModel {
|
|||
label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.actionTokens.tokens.label'
|
||||
})
|
||||
}),
|
||||
useCoins: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.useCoins.label'
|
||||
rangeMeasurement: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.GENERAL.enabled'
|
||||
}),
|
||||
melee: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 5,
|
||||
label: 'DAGGERHEART.CONFIG.Range.melee.name'
|
||||
}),
|
||||
veryClose: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 15,
|
||||
label: 'DAGGERHEART.CONFIG.Range.veryClose.name'
|
||||
}),
|
||||
close: new fields.NumberField({
|
||||
required: true,
|
||||
initial: 30,
|
||||
label: 'DAGGERHEART.CONFIG.Range.close.name'
|
||||
}),
|
||||
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' })
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export { default as DhAppearance } from './Appearance.mjs';
|
||||
export { default as DhAutomation } from './Automation.mjs';
|
||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||
export { default as DhRangeMeasurement } from './RangeMeasurement.mjs';
|
||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ export default class D20Roll extends DHRoll {
|
|||
static DefaultDialog = D20RollDialog;
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize(
|
||||
"DAGGERHEART.GENERAL.d20Roll"
|
||||
);
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.d20Roll');
|
||||
}
|
||||
|
||||
get d20() {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ export default class DamageRoll extends DHRoll {
|
|||
static DefaultDialog = DamageDialog;
|
||||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
if (config.evaluate !== false)
|
||||
for (const roll of config.roll) await roll.roll.evaluate();
|
||||
|
||||
if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate();
|
||||
|
||||
roll._evaluated = true;
|
||||
const parts = config.roll.map(r => this.postEvaluate(r));
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ export default class DamageRoll extends DHRoll {
|
|||
if (config.source?.message) {
|
||||
// const chatMessage = ui.chat.collection.get(config.source.message);
|
||||
chatMessage.update({ 'system.damage': config.damage });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unifyDamageRoll(rolls) {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
if (isOriginTarget && change.effect.origin) {
|
||||
change.value = change.value.replaceAll(/origin\.@/gi, '@');
|
||||
try {
|
||||
const doc = foundry.utils.fromUuidSync(change.effect.origin);
|
||||
const effect = foundry.utils.fromUuidSync(change.effect.origin);
|
||||
const doc = effect.parent?.parent;
|
||||
if (doc) parseModel = doc;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ export default class DhpActor extends Actor {
|
|||
await this.update({
|
||||
[`system.experiences.${experienceKey}`]: {
|
||||
name: experience.name,
|
||||
value: experience.modifier
|
||||
value: experience.modifier,
|
||||
core: true
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -210,7 +211,8 @@ export default class DhpActor extends Actor {
|
|||
await this.system.companion.update({
|
||||
[`system.experiences.${experienceKey}`]: {
|
||||
name: '',
|
||||
value: experience.modifier
|
||||
value: experience.modifier,
|
||||
core: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -559,8 +561,8 @@ export default class DhpActor extends Actor {
|
|||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
|
@ -606,9 +608,9 @@ export default class DhpActor extends Actor {
|
|||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
|
||||
? u.value * -1
|
||||
: u.value)
|
||||
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
|
||||
? u.value * -1
|
||||
: u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ export default class DHItem extends foundry.documents.Item {
|
|||
isInventoryItem === true
|
||||
? 'Inventory Items' //TODO localize
|
||||
: isInventoryItem === false
|
||||
? 'Character Items' //TODO localize
|
||||
: 'Other'; //TODO localize
|
||||
? 'Character Items' //TODO localize
|
||||
: 'Other'; //TODO localize
|
||||
|
||||
return { value: type, label, group };
|
||||
}
|
||||
|
|
@ -130,7 +130,6 @@ export default class DHItem extends foundry.documents.Item {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
async use(event) {
|
||||
const actions = new Set(this.system.actionsList);
|
||||
if (actions?.size) {
|
||||
|
|
@ -152,10 +151,10 @@ export default class DHItem extends foundry.documents.Item {
|
|||
this.type === 'ancestry'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
|
||||
: this.type === 'community'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||
: this.type === 'feature'
|
||||
? game.i18n.localize('TYPES.Item.feature')
|
||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||
: this.type === 'feature'
|
||||
? game.i18n.localize('TYPES.Item.feature')
|
||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||
origin: origin,
|
||||
img: this.img,
|
||||
item: {
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
for (const [index, itemValue] of pathValue.entries()) {
|
||||
const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base;
|
||||
const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item;
|
||||
const enrichedValue = await TextEditor.enrichHTML(value.description);
|
||||
const enrichedValue = await TextEditor.enrichHTML(value.system?.description ?? value.description);
|
||||
if (itemIsAction) value.enrichedDescription = enrichedValue;
|
||||
else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { range as configRange } from '../config/generalConfig.mjs';
|
||||
|
||||
export default function DhTemplateEnricher(match, _options) {
|
||||
const parts = match[1].split('|').map(x => x.trim());
|
||||
|
||||
|
|
@ -17,7 +15,7 @@ export default function DhTemplateEnricher(match, _options) {
|
|||
type = matchedType;
|
||||
break;
|
||||
case 'range':
|
||||
const matchedRange = Object.values(configRange).find(
|
||||
const matchedRange = Object.values(CONFIG.DH.GENERAL.templateRanges).find(
|
||||
x => x.id.toLowerCase() === split[1] || x.short === split[1]
|
||||
);
|
||||
range = matchedRange?.id;
|
||||
|
|
@ -55,7 +53,9 @@ export const renderMeasuredTemplate = async event => {
|
|||
? '180'
|
||||
: undefined;
|
||||
|
||||
const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement)[range];
|
||||
const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement[
|
||||
range
|
||||
];
|
||||
const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance;
|
||||
|
||||
const { width, height } = game.canvas.scene.dimensions;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
damageSymbols: this.damageSymbols,
|
||||
rollParsed: this.rollParsed,
|
||||
hasProperty: foundry.utils.hasProperty,
|
||||
getProperty: foundry.utils.getProperty,
|
||||
setVar: this.setVar,
|
||||
empty: this.empty
|
||||
});
|
||||
|
|
|
|||
|
|
@ -313,8 +313,10 @@ export const itemAbleRollParse = (value, actor, item) => {
|
|||
|
||||
const isItemTarget = value.toLowerCase().includes('item.@');
|
||||
const slicedValue = isItemTarget ? value.replaceAll(/item\.@/gi, '@') : value;
|
||||
const model = isItemTarget ? item : actor;
|
||||
|
||||
try {
|
||||
return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor);
|
||||
return Roll.replaceFormulaData(slicedValue, model?.getRollData?.() ?? model);
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -362,6 +364,7 @@ export async function createEmbeddedItemWithEffects(actor, baseData, update) {
|
|||
const [doc] = await actor.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...(update ?? data),
|
||||
...baseData,
|
||||
id: data.id,
|
||||
uuid: data.uuid,
|
||||
effects: data.effects?.map(effect => effect.toObject())
|
||||
|
|
@ -371,6 +374,21 @@ export async function createEmbeddedItemWithEffects(actor, baseData, update) {
|
|||
return doc;
|
||||
}
|
||||
|
||||
export async function createEmbeddedItemsWithEffects(actor, baseData) {
|
||||
const effectData = [];
|
||||
for (let d of baseData) {
|
||||
const data = d.uuid.startsWith('Compendium') ? await foundry.utils.fromUuid(d.uuid) : d;
|
||||
effectData.push({
|
||||
...data,
|
||||
id: data.id,
|
||||
uuid: data.uuid,
|
||||
effects: data.effects?.map(effect => effect.toObject())
|
||||
});
|
||||
}
|
||||
|
||||
await actor.createEmbeddedDocuments('Item', effectData);
|
||||
}
|
||||
|
||||
export const slugify = name => {
|
||||
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||
import DhCountdowns from '../data/countdowns.mjs';
|
||||
import {
|
||||
DhAppearance,
|
||||
DhAutomation,
|
||||
DhHomebrew,
|
||||
DhRangeMeasurement,
|
||||
DhVariantRules
|
||||
} from '../data/settings/_module.mjs';
|
||||
import { DhAppearance, DhAutomation, DhHomebrew, DhVariantRules } from '../data/settings/_module.mjs';
|
||||
import {
|
||||
DhAppearanceSettings,
|
||||
DhAutomationSettings,
|
||||
DhHomebrewSettings,
|
||||
DhRangeMeasurementSettings,
|
||||
DhVariantRuleSettings
|
||||
} from '../applications/settings/_module.mjs';
|
||||
|
||||
|
|
@ -58,12 +51,6 @@ const registerMenuSettings = () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhRangeMeasurement
|
||||
});
|
||||
};
|
||||
|
||||
const registerMenus = () => {
|
||||
|
|
@ -83,14 +70,6 @@ const registerMenus = () => {
|
|||
type: DhHomebrewSettings,
|
||||
restricted: true
|
||||
});
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Range.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.name'),
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.hint'),
|
||||
icon: CONFIG.DH.SETTINGS.menu.Range.Icon,
|
||||
type: DhRangeMeasurementSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.title'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue