mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 23:49:02 +01:00
Merge branch 'main' into feature/416-reaction-roll-query
This commit is contained in:
commit
36959156d0
247 changed files with 13334 additions and 825 deletions
|
|
@ -123,13 +123,15 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
);
|
||||
|
||||
const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments();
|
||||
const beastformTiers = [...(compendiumBeastforms ? compendiumBeastforms : []), ...game.items].reduce(
|
||||
const beastformTiers = [...game.items, ...(compendiumBeastforms ? compendiumBeastforms : [])].reduce(
|
||||
(acc, x) => {
|
||||
const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
|
||||
if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc;
|
||||
|
||||
if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} };
|
||||
|
||||
if (Object.values(acc[tier.id].values).find(existing => existing.value.name === x.name)) return acc;
|
||||
|
||||
acc[tier.id].values[x.uuid] = {
|
||||
selected: this.selected?.uuid == x.uuid,
|
||||
value: x,
|
||||
|
|
|
|||
|
|
@ -91,9 +91,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
context.roll = this.roll;
|
||||
context.rollType = this.roll?.constructor.name;
|
||||
context.rallyDie = this.roll.rallyChoices;
|
||||
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||
const experiences = this.config.data?.experiences || {};
|
||||
context.experiences = Object.keys(experiences).map(id => ({
|
||||
id,
|
||||
...this.config.data.experiences[id]
|
||||
...experiences[id]
|
||||
}));
|
||||
context.selectedExperiences = this.config.experiences;
|
||||
context.advantage = this.config.roll?.advantage;
|
||||
|
|
|
|||
|
|
@ -38,17 +38,15 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.isHealing ? 'healing' : 'damage'}Roll.name`);
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.config = CONFIG.DH;
|
||||
context.title = this.config.title
|
||||
? this.config.title
|
||||
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
// context.extraFormula = this.config.extraFormula;
|
||||
context.title = this.config.title ?? this.title;
|
||||
context.formula = this.roll.constructFormula(this.config);
|
||||
context.isHealing = this.config.isHealing;
|
||||
context.directDamage = this.config.directDamage;
|
||||
context.selectedRollMode = this.config.selectedRollMode;
|
||||
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// TO DELETE ?
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
|
||||
{ id: 'fear', label: 'DAGGERHEART.GENERAL.fear' },
|
||||
{ id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' },
|
||||
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }
|
||||
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Disadvantage.full' }
|
||||
],
|
||||
initial: 'hope'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,10 +136,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
...move,
|
||||
name: game.i18n.localize(move.name),
|
||||
description: game.i18n.localize(move.description),
|
||||
actions: move.actions.map(action => ({
|
||||
...action,
|
||||
name: game.i18n.localize(action.name)
|
||||
}))
|
||||
actions: move.actions.reduce((acc, key) => {
|
||||
const action = move.actions[key];
|
||||
acc[key] = {
|
||||
...action,
|
||||
name: game.i18n.localize(action.name)
|
||||
};
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
return acc;
|
||||
|
|
@ -165,8 +169,18 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
}
|
||||
|
||||
static async reset() {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationTitle')
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationText', {
|
||||
settings: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name')
|
||||
})
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
const resetSettings = new DhHomebrew();
|
||||
let localizedSettings = this.localizeObject(resetSettings);
|
||||
let localizedSettings = this.localizeObject(resetSettings.toObject());
|
||||
this.settings.updateSource(localizedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
context.costOptions = this.getCostOptions();
|
||||
context.disableOption = this.disableOption.bind(this);
|
||||
context.isNPC = this.action.actor?.isNPC;
|
||||
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
||||
context.hasRoll = this.action.hasRoll;
|
||||
|
||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
||||
|
|
@ -117,7 +118,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
|
||||
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
|
||||
];
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
|||
* @returns
|
||||
*/
|
||||
static async #deleteAdversary(_event, target) {
|
||||
const doc = getDocFromElement(target);
|
||||
const doc = await getDocFromElement(target);
|
||||
const { category } = target.dataset;
|
||||
const path = `system.potentialAdversaries.${category}.adversaries`;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs';
|
|||
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
|
||||
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
|
||||
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||
import { getDocFromElement, itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
|
||||
|
||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
},
|
||||
{
|
||||
handler: CharacterSheet.#getItemContextOptions,
|
||||
selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="miscellaneous"]',
|
||||
selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="loot"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
|
|
@ -258,19 +258,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
{
|
||||
name: 'toLoadout',
|
||||
icon: 'fa-solid fa-arrow-up',
|
||||
condition: target => getDocFromElement(target).system.inVault,
|
||||
callback: target => {
|
||||
const doc = getDocFromElement(target),
|
||||
actorLoadout = doc.actor.system.loadoutSlot;
|
||||
if(actorLoadout.available) return doc.update({ 'system.inVault': false });
|
||||
ui.notifications.warn(game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max }))
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && system.inVault;
|
||||
},
|
||||
callback: async target => {
|
||||
const doc = await getDocFromElement(target);
|
||||
const actorLoadout = doc.actor.system.loadoutSlot;
|
||||
if (actorLoadout.available) return doc.update({ 'system.inVault': false });
|
||||
ui.notifications.warn(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max })
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'toVault',
|
||||
icon: 'fa-solid fa-arrow-down',
|
||||
condition: target => !getDocFromElement(target).system.inVault,
|
||||
callback: target => getDocFromElement(target).update({ 'system.inVault': true })
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !doc.system.inVault;
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
|
||||
}
|
||||
].map(option => ({
|
||||
...option,
|
||||
|
|
@ -292,13 +300,19 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
{
|
||||
name: 'equip',
|
||||
icon: 'fa-solid fa-hands',
|
||||
condition: target => !getDocFromElement(target).system.equipped,
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !doc.system.equipped;
|
||||
},
|
||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
},
|
||||
{
|
||||
name: 'unequip',
|
||||
icon: 'fa-solid fa-hands',
|
||||
condition: target => getDocFromElement(target).system.equipped,
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && doc.system.equipped;
|
||||
},
|
||||
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
|
||||
}
|
||||
].map(option => ({
|
||||
|
|
@ -311,7 +325,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options for Consumable and Miscellaneous.
|
||||
* Get the set of ContextMenu options for Consumable and Loot.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {CharacterSheet}
|
||||
* @protected
|
||||
|
|
@ -407,11 +421,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @param {HTMLElement} html The container to filter items from.
|
||||
* @protected
|
||||
*/
|
||||
_onSearchFilterInventory(event, query, rgx, html) {
|
||||
async _onSearchFilterInventory(_event, query, rgx, html) {
|
||||
this.#filteredItems.inventory.search.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||
const item = getDocFromElement(li);
|
||||
const item = await getDocFromElement(li);
|
||||
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||
if (matchesSearch) this.#filteredItems.inventory.search.add(item.id);
|
||||
const { menu } = this.#filteredItems.inventory;
|
||||
|
|
@ -427,11 +441,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @param {HTMLElement} html The container to filter items from.
|
||||
* @protected
|
||||
*/
|
||||
_onSearchFilterCard(event, query, rgx, html) {
|
||||
async _onSearchFilterCard(_event, query, rgx, html) {
|
||||
this.#filteredItems.loadout.search.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
|
||||
const item = getDocFromElement(li);
|
||||
const item = await getDocFromElement(li);
|
||||
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||
if (matchesSearch) this.#filteredItems.loadout.search.add(item.id);
|
||||
const { menu } = this.#filteredItems.loadout;
|
||||
|
|
@ -478,11 +492,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @param {HTMLElement} html
|
||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||
*/
|
||||
_onMenuFilterInventory(event, html, filters) {
|
||||
async _onMenuFilterInventory(_event, html, filters) {
|
||||
this.#filteredItems.inventory.menu.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||
const item = getDocFromElement(li);
|
||||
const item = await getDocFromElement(li);
|
||||
|
||||
const matchesMenu =
|
||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||
|
|
@ -499,11 +513,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @param {HTMLElement} html
|
||||
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||
*/
|
||||
_onMenuFilterLoadout(event, html, filters) {
|
||||
async _onMenuFilterLoadout(_event, html, filters) {
|
||||
this.#filteredItems.loadout.menu.clear();
|
||||
|
||||
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
|
||||
const item = getDocFromElement(li);
|
||||
const item = await getDocFromElement(li);
|
||||
|
||||
const matchesMenu =
|
||||
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||
|
|
@ -519,7 +533,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
async updateItemResource(event) {
|
||||
const item = getDocFromElement(event.currentTarget);
|
||||
const item = await getDocFromElement(event.currentTarget);
|
||||
if (!item) return;
|
||||
|
||||
const max = event.currentTarget.max ? Number(event.currentTarget.max) : null;
|
||||
|
|
@ -529,7 +543,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
async updateItemQuantity(event) {
|
||||
const item = getDocFromElement(event.currentTarget);
|
||||
const item = await getDocFromElement(event.currentTarget);
|
||||
if (!item) return;
|
||||
|
||||
await item.update({ 'system.quantity': event.currentTarget.value });
|
||||
|
|
@ -609,7 +623,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleEquipItem(_event, button) {
|
||||
const item = getDocFromElement(button);
|
||||
const item = await getDocFromElement(button);
|
||||
if (!item) return;
|
||||
if (item.system.equipped) {
|
||||
await item.update({ 'system.equipped': false });
|
||||
|
|
@ -664,7 +678,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleVault(_event, button) {
|
||||
const doc = getDocFromElement(button);
|
||||
const doc = await getDocFromElement(button);
|
||||
await doc?.update({ 'system.inVault': !doc.system.inVault });
|
||||
}
|
||||
|
||||
|
|
@ -673,7 +687,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleResourceDice(event, target) {
|
||||
const item = getDocFromElement(target);
|
||||
const item = await getDocFromElement(target);
|
||||
|
||||
const { dice } = event.target.closest('.item-resource').dataset;
|
||||
const diceState = item.system.resource.diceStates[dice];
|
||||
|
|
@ -688,7 +702,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #handleResourceDice(_, target) {
|
||||
const item = getDocFromElement(target);
|
||||
const item = await getDocFromElement(target);
|
||||
if (!item) return;
|
||||
|
||||
const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document);
|
||||
|
|
@ -709,7 +723,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = getDocFromElement(event.target);
|
||||
const item = await getDocFromElement(event.target);
|
||||
|
||||
const dragData = {
|
||||
type: item.documentName,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
import { getDocFromElement, tagifyElement } from '../../../helpers/utils.mjs';
|
||||
import DHActionConfig from '../../sheets-configs/action-config.mjs';
|
||||
import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
|
||||
|
||||
/**
|
||||
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
|
||||
|
|
@ -124,7 +123,14 @@ export default function DHApplicationMixin(Base) {
|
|||
/**@inheritdoc */
|
||||
async _onFirstRender(context, options) {
|
||||
await super._onFirstRender(context, options);
|
||||
this.relatedDocs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
|
||||
|
||||
const docs = [];
|
||||
for (var docData of this.relatedDocs) {
|
||||
const doc = await foundry.utils.fromUuid(docData.uuid);
|
||||
docs.push(doc);
|
||||
}
|
||||
|
||||
docs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
|
||||
|
||||
if (!!this.options.contextMenus.length) this._createContextMenus();
|
||||
}
|
||||
|
|
@ -259,14 +265,20 @@ export default function DHApplicationMixin(Base) {
|
|||
{
|
||||
name: 'disableEffect',
|
||||
icon: 'fa-solid fa-lightbulb',
|
||||
condition: target => !getDocFromElement(target).disabled,
|
||||
callback: target => getDocFromElement(target).update({ disabled: true })
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !doc.disabled;
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
|
||||
},
|
||||
{
|
||||
name: 'enableEffect',
|
||||
icon: 'fa-regular fa-lightbulb',
|
||||
condition: target => getDocFromElement(target).disabled,
|
||||
callback: target => getDocFromElement(target).update({ disabled: false })
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && doc.disabled;
|
||||
},
|
||||
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
|
||||
}
|
||||
].map(option => ({
|
||||
...option,
|
||||
|
|
@ -299,10 +311,10 @@ export default function DHApplicationMixin(Base) {
|
|||
name: 'CONTROLS.CommonEdit',
|
||||
icon: 'fa-solid fa-pen-to-square',
|
||||
condition: target => {
|
||||
const doc = getDocFromElement(target);
|
||||
return !doc.hasOwnProperty('systemPath') || doc.inCollection;
|
||||
const doc = getDocFromElementSync(target);
|
||||
return !doc || !doc.hasOwnProperty('systemPath') || doc.inCollection;
|
||||
},
|
||||
callback: target => getDocFromElement(target).sheet.render({ force: true })
|
||||
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -311,25 +323,25 @@ export default function DHApplicationMixin(Base) {
|
|||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||
icon: 'fa-solid fa-burst',
|
||||
condition: target => {
|
||||
const doc = getDocFromElement(target);
|
||||
return !(doc.type === 'domainCard' && doc.system.inVault)
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
},
|
||||
callback: (target, event) => getDocFromElement(target).use(event)
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
});
|
||||
|
||||
if (toChat)
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||
icon: 'fa-solid fa-message',
|
||||
callback: target => getDocFromElement(target).toChat(this.document.id)
|
||||
callback: async target => (await getDocFromElement(target)).toChat(this.document.id)
|
||||
});
|
||||
|
||||
if (deletable)
|
||||
options.push({
|
||||
name: 'CONTROLS.CommonDelete',
|
||||
icon: 'fa-solid fa-trash',
|
||||
callback: (target, event) => {
|
||||
const doc = getDocFromElement(target);
|
||||
callback: async (target, event) => {
|
||||
const doc = await getDocFromElement(target);
|
||||
if (event.shiftKey) return doc.delete();
|
||||
else return doc.deleteDialog();
|
||||
}
|
||||
|
|
@ -371,7 +383,7 @@ export default function DHApplicationMixin(Base) {
|
|||
if (!actionId && !itemUuid) return;
|
||||
|
||||
const doc = itemUuid
|
||||
? getDocFromElement(extensibleElement)
|
||||
? await getDocFromElement(extensibleElement)
|
||||
: this.document.system.attack?.id === actionId
|
||||
? this.document.system.attack
|
||||
: this.document.system.actions?.get(actionId);
|
||||
|
|
@ -429,8 +441,8 @@ export default function DHApplicationMixin(Base) {
|
|||
* Renders an embedded document.
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static #editDoc(_event, target) {
|
||||
const doc = getDocFromElement(target);
|
||||
static async #editDoc(_event, target) {
|
||||
const doc = await getDocFromElement(target);
|
||||
if (doc) return doc.sheet.render({ force: true });
|
||||
}
|
||||
|
||||
|
|
@ -439,7 +451,7 @@ export default function DHApplicationMixin(Base) {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #deleteDoc(event, target) {
|
||||
const doc = getDocFromElement(target);
|
||||
const doc = await getDocFromElement(target);
|
||||
if (doc) {
|
||||
if (event.shiftKey) return doc.delete();
|
||||
else return await doc.deleteDialog();
|
||||
|
|
@ -451,7 +463,7 @@ export default function DHApplicationMixin(Base) {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toChat(_event, target) {
|
||||
let doc = getDocFromElement(target);
|
||||
let doc = await getDocFromElement(target);
|
||||
return doc.toChat(this.document.id);
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +472,7 @@ export default function DHApplicationMixin(Base) {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #useItem(event, target) {
|
||||
let doc = getDocFromElement(target);
|
||||
let doc = await getDocFromElement(target);
|
||||
await doc.use(event);
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +481,7 @@ export default function DHApplicationMixin(Base) {
|
|||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async #toggleEffect(_, target) {
|
||||
const doc = getDocFromElement(target);
|
||||
const doc = await getDocFromElement(target);
|
||||
await doc.update({ disabled: !doc.disabled });
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +504,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const t = extensible?.classList.toggle('extended');
|
||||
|
||||
const descriptionElement = extensible?.querySelector('.invetory-description');
|
||||
if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement);
|
||||
if (t && !!descriptionElement) await this.#prepareInventoryDescription(extensible, descriptionElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
name: 'CONTROLS.CommonDelete',
|
||||
icon: '<i class="fa-solid fa-trash"></i>',
|
||||
callback: async target => {
|
||||
const feature = getDocFromElement(target);
|
||||
const feature = await getDocFromElement(target);
|
||||
if (!feature) return;
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
|
|
@ -168,7 +168,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
*/
|
||||
static async #deleteFeature(_, element) {
|
||||
const target = element.closest('[data-item-uuid]');
|
||||
const feature = getDocFromElement(target);
|
||||
const feature = await getDocFromElement(target);
|
||||
if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features
|
||||
|
|
@ -249,12 +249,20 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
const target = event.target.closest('fieldset.drop-section');
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item?.type === 'feature') {
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(x => ({
|
||||
...x,
|
||||
item: x.item?.uuid
|
||||
}))
|
||||
});
|
||||
if (target.dataset.type) {
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(
|
||||
x => ({
|
||||
...x,
|
||||
item: x.item?.uuid
|
||||
})
|
||||
)
|
||||
});
|
||||
} else {
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features, item].map(x => x.uuid)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ export { default as Community } from './community.mjs';
|
|||
export { default as Consumable } from './consumable.mjs';
|
||||
export { default as DomainCard } from './domainCard.mjs';
|
||||
export { default as Feature } from './feature.mjs';
|
||||
export { default as Miscellaneous } from './miscellaneous.mjs';
|
||||
export { default as Loot } from './loot.mjs';
|
||||
export { default as Subclass } from './subclass.mjs';
|
||||
export { default as Weapon } from './weapon.mjs';
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
|||
case 'effects':
|
||||
context.effects.actives = context.effects.actives.map(effect => {
|
||||
const data = effect.toObject();
|
||||
data.uuid = effect.uuid;
|
||||
data.id = effect.id;
|
||||
if (effect.type === 'beastform') data.mandatory = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -132,14 +132,14 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
});
|
||||
}
|
||||
} else if (target.classList.contains('choice-a-section')) {
|
||||
if (item.type === 'miscellaneous' || item.type === 'consumable') {
|
||||
if (item.type === 'loot' || item.type === 'consumable') {
|
||||
const filteredChoiceA = this.document.system.inventory.choiceA;
|
||||
if (filteredChoiceA.length < 2)
|
||||
await this.document.update({
|
||||
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'miscellaneous') {
|
||||
} else if (item.type === 'loot') {
|
||||
if (target.classList.contains('take-section')) {
|
||||
const filteredTake = this.document.system.inventory.take.filter(x => x);
|
||||
if (filteredTake.length < 3)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||
|
||||
export default class MiscellaneousSheet extends DHBaseItemSheet {
|
||||
export default class LootSheet extends DHBaseItemSheet {
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['miscellaneous'],
|
||||
classes: ['loot'],
|
||||
position: { width: 550 }
|
||||
};
|
||||
|
||||
/**@override */
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' },
|
||||
header: { template: 'systems/daggerheart/templates/sheets/items/loot/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||
actions: {
|
||||
|
|
@ -17,7 +17,7 @@ export default class MiscellaneousSheet extends DHBaseItemSheet {
|
|||
scrollable: ['.actions']
|
||||
},
|
||||
settings: {
|
||||
template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs',
|
||||
template: 'systems/daggerheart/templates/sheets/items/loot/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
},
|
||||
effects: {
|
||||
|
|
@ -17,9 +17,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.duality-action-healing').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollHealing(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.target-save-container').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollSave(event, data.message))
|
||||
);
|
||||
|
|
@ -66,8 +63,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
super.close(options);
|
||||
}
|
||||
|
||||
async getActor(id) {
|
||||
return await fromUuid(id);
|
||||
async getActor(uuid) {
|
||||
return await foundry.utils.fromUuid(uuid);
|
||||
}
|
||||
|
||||
getAction(actor, itemId, actionId) {
|
||||
|
|
@ -92,17 +89,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
}
|
||||
|
||||
async onRollHealing(event, message) {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor);
|
||||
if (!actor || !game.user.isGM) return true;
|
||||
if (message.system.source.item && message.system.source.action) {
|
||||
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
||||
if (!action || !action?.rollHealing) return;
|
||||
await action.rollHealing(event, message);
|
||||
}
|
||||
}
|
||||
|
||||
async onRollSave(event, message) {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor),
|
||||
|
|
@ -204,7 +190,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
return {
|
||||
isHit,
|
||||
targets: isHit
|
||||
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
|
||||
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
|
||||
: Array.from(game.user.targets)
|
||||
};
|
||||
}
|
||||
|
|
@ -266,19 +252,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
});
|
||||
}
|
||||
|
||||
target.actor.takeDamage(damages);
|
||||
}
|
||||
}
|
||||
|
||||
async onHealing(event, message) {
|
||||
event.stopPropagation();
|
||||
const targets = Array.from(game.user.targets);
|
||||
|
||||
if (targets.length === 0)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||
|
||||
for (var target of targets) {
|
||||
target.actor.takeHealing(message.system.roll);
|
||||
if(message.system.hasHealing)
|
||||
target.actor.takeHealing(damages);
|
||||
else
|
||||
target.actor.takeDamage(damages);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export const diceCompare = {
|
|||
}
|
||||
};
|
||||
|
||||
export const advandtageState = {
|
||||
export const advantageState = {
|
||||
disadvantage: {
|
||||
label: 'DAGGERHEART.GENERAL.Disadvantage.full',
|
||||
value: -1
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
tendToWounds: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.shortRest.moves.tendToWounds.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -166,6 +167,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
clearStress: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.shortRest.moves.clearStress.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -191,6 +193,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
repairArmor: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.shortRest.moves.repairArmor.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -226,6 +229,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
tendToWounds: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.longRest.moves.tendToWounds.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -251,6 +255,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
clearStress: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.longRest.moves.clearStress.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -276,6 +281,7 @@ export const defaultRestOptions = {
|
|||
actions: {
|
||||
repairArmor: {
|
||||
type: 'healing',
|
||||
systemPath: 'restMoves.longRest.moves.repairArmor.actions',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
actionType: 'action',
|
||||
|
|
@ -433,7 +439,7 @@ export const abilityCosts = {
|
|||
},
|
||||
armor: {
|
||||
id: 'armor',
|
||||
label: 'Armor Stack',
|
||||
label: 'Armor Slot',
|
||||
group: 'TYPES.Actor.character'
|
||||
},
|
||||
fear: {
|
||||
|
|
|
|||
|
|
@ -1301,9 +1301,9 @@ export const featureTypes = {
|
|||
id: 'consumable',
|
||||
label: 'TYPES.Item.consumable'
|
||||
},
|
||||
miscellaneous: {
|
||||
id: 'miscellaneous',
|
||||
label: 'TYPES.Item.miscellaneous'
|
||||
loot: {
|
||||
id: 'loot',
|
||||
label: 'TYPES.Item.loot'
|
||||
},
|
||||
beastform: {
|
||||
if: 'beastform',
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
name: new fields.StringField({ initial: undefined }),
|
||||
description: new fields.HTMLField(),
|
||||
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
|
||||
chatDisplay: new fields.BooleanField({ initial: true, label: 'Display in chat' }),
|
||||
chatDisplay: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.ACTIONS.Config.displayInChat' }),
|
||||
actionType: new fields.StringField({
|
||||
choices: CONFIG.DH.ITEM.actionTypes,
|
||||
initial: 'action',
|
||||
|
|
@ -164,12 +164,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
title: this.item.name,
|
||||
source: {
|
||||
item: this.item._id,
|
||||
action: this._id
|
||||
action: this._id,
|
||||
actor: this.actor.uuid
|
||||
},
|
||||
dialog: {},
|
||||
type: this.type,
|
||||
hasDamage: !!this.damage?.parts?.length,
|
||||
hasHealing: !!this.healing,
|
||||
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
|
||||
hasHealing: this.damage?.parts?.length && this.type === 'healing',
|
||||
hasEffect: !!this.effects?.length,
|
||||
hasSave: this.hasSave,
|
||||
selectedRollMode: game.settings.get('core', 'rollMode'),
|
||||
|
|
@ -191,7 +192,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
difficulty: this.roll?.difficulty,
|
||||
formula: this.roll.getFormula(),
|
||||
bonus: this.roll.bonus,
|
||||
advantage: CONFIG.DH.ACTIONS.advandtageState[this.roll.advState].value
|
||||
advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value
|
||||
};
|
||||
if (this.roll?.type === 'diceSet') roll.lite = true;
|
||||
|
||||
|
|
@ -256,6 +257,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
/* EFFECTS */
|
||||
async applyEffects(event, data, targets) {
|
||||
targets ??= data.system.targets;
|
||||
const force = true; /* Where should this come from? */
|
||||
if (!this.effects?.length || !targets.length) return;
|
||||
let effects = this.effects;
|
||||
targets.forEach(async token => {
|
||||
|
|
@ -305,7 +307,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
title: 'Roll Save',
|
||||
roll: {
|
||||
trait: this.save.trait,
|
||||
difficulty: this.save.difficulty,
|
||||
difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty,
|
||||
type: 'reaction'
|
||||
},
|
||||
data: actor.getRollData()
|
||||
|
|
|
|||
|
|
@ -4,15 +4,13 @@ import DHBaseAction from './baseAction.mjs';
|
|||
export default class DhBeastformAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'beastform'];
|
||||
|
||||
async use(event, ...args) {
|
||||
async use(_event, ...args) {
|
||||
const beastformConfig = this.prepareBeastformConfig();
|
||||
|
||||
const abort = await this.handleActiveTransformations();
|
||||
if (abort) return;
|
||||
|
||||
const item = args[0];
|
||||
|
||||
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, item);
|
||||
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, this.item);
|
||||
if (!selected) return;
|
||||
|
||||
await this.transform(selected, evolved, hybrid);
|
||||
|
|
|
|||
|
|
@ -47,11 +47,12 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
formulas = this.formatFormulas(formulas, systemData);
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }),
|
||||
title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }),
|
||||
roll: formulas,
|
||||
targets: systemData.targets?.filter(t => t.hit) ?? data.targets,
|
||||
hasSave: this.hasSave,
|
||||
isCritical: systemData.roll?.isCritical ?? false,
|
||||
isHealing: this.type === 'healing',
|
||||
source: systemData.source,
|
||||
data: this.getRollData(),
|
||||
event
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
import DHDamageAction from './damageAction.mjs';
|
||||
|
||||
export default class DHHealingAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
|
||||
export default class DHHealingAction extends DHDamageAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'roll'];
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'spellcast';
|
||||
}
|
||||
|
||||
/* static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'spellcast';
|
||||
|
|
@ -44,5 +51,5 @@ export default class DHHealingAction extends DHBaseAction {
|
|||
|
||||
get modifiers() {
|
||||
return [];
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||
|
||||
const resistanceField = reductionLabel =>
|
||||
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
resistance: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
immunity: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0, label: reductionLabel })
|
||||
resistance: new foundry.data.fields.BooleanField({
|
||||
initial: false,
|
||||
label: `${resistanceLabel}.label`,
|
||||
hint: `${resistanceLabel}.hint`
|
||||
}),
|
||||
immunity: new foundry.data.fields.BooleanField({
|
||||
initial: false,
|
||||
label: `${immunityLabel}.label`,
|
||||
hint: `${immunityLabel}.hint`
|
||||
}),
|
||||
reduction: new foundry.data.fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: `${reductionLabel}.label`,
|
||||
hint: `${reductionLabel}.hint`
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -40,8 +53,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if (this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
physical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.physicalReduction'),
|
||||
magical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.magicalReduction')
|
||||
physical: resistanceField(
|
||||
'DAGGERHEART.GENERAL.DamageResistance.physicalResistance',
|
||||
'DAGGERHEART.GENERAL.DamageResistance.physicalImmunity',
|
||||
'DAGGERHEART.GENERAL.DamageResistance.physicalReduction'
|
||||
),
|
||||
magical: resistanceField(
|
||||
'DAGGERHEART.GENERAL.DamageResistance.magicalResistance',
|
||||
'DAGGERHEART.GENERAL.DamageResistance.magicalImmunity',
|
||||
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
|
||||
)
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ export default class DhCharacter extends BaseDataActor {
|
|||
severe: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
}),
|
||||
major: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
|
||||
})
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
|
|
@ -112,7 +112,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.rules.attack.damage.value'
|
||||
formula: '@profd4'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
})
|
||||
})
|
||||
}),
|
||||
maxLoadout : new fields.NumberField({
|
||||
maxLoadout: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Bonuses.maxLoadout.label'
|
||||
|
|
@ -244,10 +244,28 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}),
|
||||
attack: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
diceIndex: new fields.NumberField({
|
||||
integer: true,
|
||||
min: 0,
|
||||
max: 5,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.hint'
|
||||
}),
|
||||
bonus: new fields.NumberField({
|
||||
required: true,
|
||||
initial: '@profd4',
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.value.label'
|
||||
initial: 0,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.bonus.label'
|
||||
})
|
||||
}),
|
||||
roll: new fields.SchemaField({
|
||||
trait: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ACTOR.abilities,
|
||||
nullable: true,
|
||||
initial: null,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label'
|
||||
})
|
||||
})
|
||||
}),
|
||||
|
|
@ -329,13 +347,15 @@ export default class DhCharacter extends BaseDataActor {
|
|||
|
||||
get loadoutSlot() {
|
||||
const loadoutCount = this.domainCards.loadout?.length ?? 0,
|
||||
max = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout + this.bonuses.maxLoadout;
|
||||
max =
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout +
|
||||
this.bonuses.maxLoadout;
|
||||
|
||||
return {
|
||||
current: loadoutCount,
|
||||
available: Math.max(max - loadoutCount, 0),
|
||||
max
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get armor() {
|
||||
|
|
@ -451,6 +471,12 @@ export default class DhCharacter extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
get basicAttackDamageDice() {
|
||||
const diceTypes = Object.keys(CONFIG.DH.GENERAL.diceTypes);
|
||||
const attackDiceIndex = Math.max(Math.min(this.rules.attack.damage.diceIndex, 5), 0);
|
||||
return diceTypes[attackDiceIndex];
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const primary = this.primaryWeapon,
|
||||
secondary = this.secondaryWeapon;
|
||||
|
|
@ -535,12 +561,17 @@ export default class DhCharacter extends BaseDataActor {
|
|||
prepareDerivedData() {
|
||||
const baseHope = this.resources.hope.value + (this.companion?.system?.resources?.hope ?? 0);
|
||||
this.resources.hope.value = Math.min(baseHope, this.resources.hope.max);
|
||||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
|
||||
this.attack.damage.parts[0].value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
|
||||
}
|
||||
|
||||
getRollData() {
|
||||
const data = super.getRollData();
|
||||
|
||||
return {
|
||||
...data,
|
||||
basicAttackDamageDice: this.basicAttackDamageDice,
|
||||
tier: this.tier,
|
||||
level: this.levelData.level.current
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
|||
),
|
||||
targetSelection: new fields.BooleanField({ initial: true }),
|
||||
hasSave: new fields.BooleanField({ initial: false }),
|
||||
isHealing: new fields.BooleanField({ initial: false }),
|
||||
onSave: new fields.StringField(),
|
||||
source: new fields.SchemaField({
|
||||
actor: new fields.StringField(),
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ import { DHDamageData } from './damageField.mjs';
|
|||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class HealingField extends fields.EmbeddedDataField {
|
||||
export default class HealingField extends fields.SchemaField {
|
||||
constructor(options, context = {}) {
|
||||
super(DHDamageData, options, context);
|
||||
const healingFields = {
|
||||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
||||
};
|
||||
super(healingFields, options, context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,38 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
|||
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities }),
|
||||
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({ choices: CONFIG.DH.ACTIONS.advandtageState, initial: 'neutral' }),
|
||||
advState: new fields.StringField({
|
||||
choices: CONFIG.DH.ACTIONS.advantageState,
|
||||
initial: 'neutral'
|
||||
}),
|
||||
diceRolling: new fields.SchemaField({
|
||||
multiplier: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.diceSetNumbers,
|
||||
initial: 'prof',
|
||||
label: 'Dice Number'
|
||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier'
|
||||
}),
|
||||
flatMultiplier: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: 1,
|
||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.flatMultiplier'
|
||||
}),
|
||||
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
|
||||
dice: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.diceTypes,
|
||||
initial: 'd6',
|
||||
label: 'Dice Type'
|
||||
initial: CONFIG.DH.GENERAL.diceTypes.d6,
|
||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice'
|
||||
}),
|
||||
compare: new fields.StringField({
|
||||
choices: CONFIG.DH.ACTIONS.diceCompare,
|
||||
initial: 'above',
|
||||
label: 'Should be'
|
||||
nullable: true,
|
||||
initial: null,
|
||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.compare'
|
||||
}),
|
||||
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' })
|
||||
treshold: new fields.NumberField({
|
||||
integer: true,
|
||||
nullable: true,
|
||||
initial: null,
|
||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.threshold'
|
||||
})
|
||||
}),
|
||||
useDefault: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
|
|
@ -41,7 +54,11 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
|||
this.diceRolling.multiplier === 'flat'
|
||||
? this.diceRolling.flatMultiplier
|
||||
: `@${this.diceRolling.multiplier}`;
|
||||
formula = `${multiplier}${this.diceRolling.dice}cs${CONFIG.DH.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
|
||||
if (this.diceRolling.compare && this.diceRolling.threshold) {
|
||||
formula = `${multiplier}${this.diceRolling.dice}cs${CONFIG.DH.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
|
||||
} else {
|
||||
formula = `${multiplier}${this.diceRolling.dice}`;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
formula = '';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default class SaveField extends fields.SchemaField {
|
|||
initial: null,
|
||||
choices: CONFIG.DH.ACTOR.abilities
|
||||
}),
|
||||
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
|
||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||
damageMod: new fields.StringField({
|
||||
initial: CONFIG.DH.ACTIONS.damageOnSave.none.id,
|
||||
choices: CONFIG.DH.ACTIONS.damageOnSave
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ export default class TargetField extends fields.SchemaField {
|
|||
type: new fields.StringField({
|
||||
choices: CONFIG.DH.ACTIONS.targetTypes,
|
||||
initial: CONFIG.DH.ACTIONS.targetTypes.any.id,
|
||||
nullable: true,
|
||||
initial: null
|
||||
nullable: true
|
||||
}),
|
||||
amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
||||
};
|
||||
|
|
@ -18,11 +17,13 @@ export default class TargetField extends fields.SchemaField {
|
|||
if (!this.target?.type) return [];
|
||||
let targets;
|
||||
if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id)
|
||||
targets = TargetField.formatTarget.call(this, this.actor.token ?? this.actor.prototypeToken);
|
||||
targets = Array.from(game.user.targets);
|
||||
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
|
||||
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
|
||||
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
||||
targets = [this.actor.token ?? this.actor.prototypeToken];
|
||||
else {
|
||||
targets = Array.from(game.user.targets);
|
||||
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
|
||||
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
|
||||
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
||||
}
|
||||
}
|
||||
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
|
||||
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ export function ActionMixin(Base) {
|
|||
}
|
||||
);
|
||||
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
|
||||
const newAction = parent.actions.get(action.id);
|
||||
const newAction = created.system.actions.get(action.id);
|
||||
if (!newAction) return null;
|
||||
if (renderSheet) newAction.sheet.render({ force: true });
|
||||
return newAction;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,13 @@ export default class FormulaField extends foundry.data.fields.StringField {
|
|||
|
||||
/** @inheritDoc */
|
||||
_validateType(value) {
|
||||
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1'));
|
||||
/* A bit suss, but seems to work */
|
||||
let roll = null;
|
||||
try {
|
||||
roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1'));
|
||||
} catch (_) {
|
||||
roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, 'd6'));
|
||||
}
|
||||
roll.evaluateSync({ strict: false });
|
||||
if (this.options.deterministic && !roll.isDeterministic)
|
||||
throw new Error(`must not contain dice terms: ${value}`);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import DHCommunity from './community.mjs';
|
|||
import DHConsumable from './consumable.mjs';
|
||||
import DHDomainCard from './domainCard.mjs';
|
||||
import DHFeature from './feature.mjs';
|
||||
import DHMiscellaneous from './miscellaneous.mjs';
|
||||
import DHLoot from './loot.mjs';
|
||||
import DHSubclass from './subclass.mjs';
|
||||
import DHWeapon from './weapon.mjs';
|
||||
import DHBeastform from './beastform.mjs';
|
||||
|
|
@ -20,7 +20,7 @@ export {
|
|||
DHConsumable,
|
||||
DHDomainCard,
|
||||
DHFeature,
|
||||
DHMiscellaneous,
|
||||
DHLoot,
|
||||
DHSubclass,
|
||||
DHWeapon,
|
||||
DHBeastform
|
||||
|
|
@ -35,7 +35,7 @@ export const config = {
|
|||
consumable: DHConsumable,
|
||||
domainCard: DHDomainCard,
|
||||
feature: DHFeature,
|
||||
miscellaneous: DHMiscellaneous,
|
||||
loot: DHLoot,
|
||||
subclass: DHSubclass,
|
||||
weapon: DHWeapon,
|
||||
beastform: DHBeastform
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import AttachableItem from './attachableItem.mjs';
|
||||
import { ActionsField } from '../fields/actionField.mjs';
|
||||
import { armorFeatures } from '../../config/itemConfig.mjs';
|
||||
|
||||
export default class DHArmor extends AttachableItem {
|
||||
|
|
|
|||
|
|
@ -94,10 +94,13 @@ export default class DHBeastform extends BaseDataItem {
|
|||
return false;
|
||||
}
|
||||
|
||||
const features = await this.parent.parent.createEmbeddedDocuments(
|
||||
'Item',
|
||||
this.features.map(x => x.toObject())
|
||||
);
|
||||
const beastformFeatures = [];
|
||||
for (let featureData of this.features) {
|
||||
const feature = await foundry.utils.fromUuid(featureData.uuid);
|
||||
beastformFeatures.push(feature.toObject());
|
||||
}
|
||||
|
||||
const features = await this.parent.parent.createEmbeddedDocuments('Item', beastformFeatures);
|
||||
|
||||
const extraEffects = await this.parent.parent.createEmbeddedDocuments(
|
||||
'ActiveEffect',
|
||||
|
|
@ -152,12 +155,14 @@ export default class DHBeastform extends BaseDataItem {
|
|||
_onCreate(_data, _options, userId) {
|
||||
if (userId !== game.user.id) return;
|
||||
|
||||
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||
{
|
||||
type: 'beastform',
|
||||
name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
|
||||
img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
|
||||
}
|
||||
]);
|
||||
if (!this.parent.effects.find(x => x.type === 'beastform')) {
|
||||
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||
{
|
||||
type: 'beastform',
|
||||
name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
|
||||
img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import { ActionField } from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHMiscellaneous extends BaseDataItem {
|
||||
export default class DHLoot extends BaseDataItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Item.miscellaneous',
|
||||
type: 'miscellaneous',
|
||||
label: 'TYPES.Item.loot',
|
||||
type: 'loot',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isInventoryItem: true,
|
||||
|
|
@ -63,6 +63,19 @@ export default class DHWeapon extends AttachableItem {
|
|||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
rules: new fields.SchemaField({
|
||||
attack: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
trait: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ACTOR.abilities,
|
||||
nullable: true,
|
||||
initial: null,
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label'
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
@ -77,6 +90,10 @@ export default class DHWeapon extends AttachableItem {
|
|||
);
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
|
|
|||
|
|
@ -83,18 +83,20 @@ export default class DamageRoll extends DHRoll {
|
|||
|
||||
applyBaseBonus(part) {
|
||||
const modifiers = [],
|
||||
type = this.options.messageType ?? 'damage',
|
||||
type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
|
||||
options = part ?? this.options;
|
||||
|
||||
|
||||
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||
options.damageTypes?.forEach(t => {
|
||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||
});
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
if(!this.options.isHealing) {
|
||||
options.damageTypes?.forEach(t => {
|
||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||
});
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ export default class DhActiveEffect extends ActiveEffect {
|
|||
}
|
||||
|
||||
static applyField(model, change, field) {
|
||||
change.value = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent));
|
||||
const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent));
|
||||
change.value = evalValue ?? change.value;
|
||||
super.applyField(model, change, field);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -384,6 +384,10 @@ export default class DhpActor extends Actor {
|
|||
return CONFIG.Dice.daggerheart[['character', 'companion'].includes(this.type) ? 'DualityRoll' : 'D20Roll'];
|
||||
}
|
||||
|
||||
get baseSaveDifficulty() {
|
||||
return this.system.difficulty ?? 10;
|
||||
}
|
||||
|
||||
getRollData() {
|
||||
const rollData = super.getRollData();
|
||||
rollData.system = this.system.getRollData();
|
||||
|
|
@ -470,7 +474,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
await this.modifyResource(updates);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damages) === false) return null;
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, updates) === false) return null;
|
||||
}
|
||||
|
||||
calculateDamage(baseDamage, type) {
|
||||
|
|
@ -497,14 +501,28 @@ export default class DhpActor extends Actor {
|
|||
return reduction === Infinity ? 0 : reduction;
|
||||
}
|
||||
|
||||
async takeHealing(resources) {
|
||||
const updates = Object.entries(resources).map(([key, value]) => ({
|
||||
key: key,
|
||||
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false)
|
||||
? value.total * -1
|
||||
: value.total
|
||||
}));
|
||||
async takeHealing(healings) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null;
|
||||
|
||||
const updates = [];
|
||||
Object.entries(healings).forEach(([key, healing]) => {
|
||||
healing.parts.forEach(part => {
|
||||
const update = updates.find(u => u.key === key);
|
||||
if (update)
|
||||
update.value += part.total;
|
||||
else updates.push({ value: part.total, key });
|
||||
});
|
||||
});
|
||||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
!(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
|
||||
}
|
||||
|
||||
async modifyResource(resources) {
|
||||
|
|
@ -546,6 +564,7 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(updates).forEach(async key => {
|
||||
const u = updates[key];
|
||||
if (key === 'items') {
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ function getDualityMessage(roll) {
|
|||
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
||||
|
||||
const advantage = roll.advantage
|
||||
? CONFIG.DH.ACTIONS.advandtageState.advantage.value
|
||||
? CONFIG.DH.ACTIONS.advantageState.advantage.value
|
||||
: roll.disadvantage
|
||||
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
|
||||
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
||||
: undefined;
|
||||
const advantageLabel =
|
||||
advantage === CONFIG.DH.ACTIONS.advandtageState.advantage.value
|
||||
advantage === CONFIG.DH.ACTIONS.advantageState.advantage.value
|
||||
? 'Advantage'
|
||||
: advantage === CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
|
||||
: advantage === CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
||||
? 'Disadvantage'
|
||||
: undefined;
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ function getDualityMessage(roll) {
|
|||
export const renderDualityButton = async event => {
|
||||
const button = event.currentTarget,
|
||||
traitValue = button.dataset.trait?.toLowerCase(),
|
||||
target = getCommandTarget(),
|
||||
target = getCommandTarget({ allowNull: true }),
|
||||
difficulty = button.dataset.difficulty,
|
||||
advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined;
|
||||
|
||||
|
|
@ -80,13 +80,11 @@ export const enrichedDualityRoll = async (
|
|||
{ traitValue, target, difficulty, title, label, actionType, advantage },
|
||||
event
|
||||
) => {
|
||||
if (!target) return;
|
||||
|
||||
const config = {
|
||||
event: event ?? {},
|
||||
title: title,
|
||||
roll: {
|
||||
modifier: traitValue ? target.system.traits[traitValue].value : null,
|
||||
trait: traitValue && target ? traitValue : null,
|
||||
label: label,
|
||||
difficulty: difficulty,
|
||||
advantage,
|
||||
|
|
@ -96,5 +94,13 @@ export const enrichedDualityRoll = async (
|
|||
template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs'
|
||||
}
|
||||
};
|
||||
await target.diceRoll(config);
|
||||
|
||||
if (target) {
|
||||
await target.diceRoll(config);
|
||||
} else {
|
||||
// For no target, call DualityRoll directly with basic data
|
||||
config.data = { experiences: {}, traits: {} };
|
||||
config.source = { actor: null };
|
||||
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,21 +31,24 @@ export function rollCommandToJSON(text) {
|
|||
return Object.keys(result).length > 0 ? result : null;
|
||||
}
|
||||
|
||||
export const getCommandTarget = () => {
|
||||
export const getCommandTarget = (options = {}) => {
|
||||
const { allowNull = false } = options;
|
||||
let target = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0].actor : null;
|
||||
if (!game.user.isGM) {
|
||||
target = game.user.character;
|
||||
if (!target) {
|
||||
if (!target && !allowNull) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noAssignedPlayerCharacter'));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!target) {
|
||||
if (!target && !allowNull) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken'));
|
||||
return null;
|
||||
}
|
||||
if (target.type !== 'character') {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyUseableByPC'));
|
||||
if (target && target.type !== 'character') {
|
||||
if (!allowNull) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyUseableByPC'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +56,7 @@ export const getCommandTarget = () => {
|
|||
};
|
||||
|
||||
export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
|
||||
if (!game.modules.get('dice-so-nice')?.active) return;
|
||||
const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
|
||||
rollResult.dice[0].options = diceSoNicePresets.hope;
|
||||
rollResult.dice[1].options = diceSoNicePresets.fear;
|
||||
|
|
@ -235,9 +239,25 @@ export const updateActorTokens = async (actor, update) => {
|
|||
* @param {HTMLElement} element - The DOM element to start the search from.
|
||||
* @returns {foundry.abstract.Document|null} The resolved document, or null if not found or invalid.
|
||||
*/
|
||||
export function getDocFromElement(element) {
|
||||
export async function getDocFromElement(element) {
|
||||
const target = element.closest('[data-item-uuid]');
|
||||
return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null;
|
||||
return (await foundry.utils.fromUuid(target.dataset.itemUuid)) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a Foundry document associated with the nearest ancestor element
|
||||
* that has a `data-item-uuid` attribute.
|
||||
* @param {HTMLElement} element - The DOM element to start the search from.
|
||||
* @returns {foundry.abstract.Document|null} The resolved document, or null if not found, invalid
|
||||
* or in embedded compendium collection.
|
||||
*/
|
||||
export function getDocFromElementSync(element) {
|
||||
const target = element.closest('[data-item-uuid]');
|
||||
try {
|
||||
return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -255,14 +275,14 @@ export function addLinkedItemsDiff(changedItems, currentItems, options) {
|
|||
newItems
|
||||
.difference(prevItems)
|
||||
.map(item => item?.item ?? item)
|
||||
.filter(x => (typeof x === 'object' ? x.item : x))
|
||||
.filter(x => (typeof x === 'object' ? x?.item : x))
|
||||
);
|
||||
|
||||
options.toUnlink = Array.from(
|
||||
prevItems
|
||||
.difference(newItems)
|
||||
.map(item => item?.item?.uuid ?? item?.uuid ?? item)
|
||||
.filter(x => (typeof x === 'object' ? x.item : x))
|
||||
.filter(x => (typeof x === 'object' ? x?.item : x))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue