Merged with main

This commit is contained in:
WBHarry 2025-07-24 19:00:39 +02:00
commit 679edf341e
45 changed files with 1147 additions and 340 deletions

View file

@ -1,5 +1,4 @@
export { default as BeastformDialog } from './beastformDialog.mjs';
export { default as costSelectionDialog } from './costSelectionDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs';
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';

View file

@ -1,9 +1,11 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class BeastformDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(configData) {
constructor(configData, item) {
super();
this.item = item;
this.configData = configData;
this.selected = null;
this.evolved = { form: null };
@ -14,11 +16,14 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'dh-style', 'beastform-selection'],
classes: ['daggerheart', 'views', 'dialog', 'dh-style', 'beastform-selection'],
position: {
width: 600,
height: 'auto'
},
window: {
icon: 'fa-solid fa-paw'
},
actions: {
selectBeastform: this.selectBeastform,
toggleHybridFeature: this.toggleHybridFeature,
@ -34,11 +39,12 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
};
get title() {
return game.i18n.localize('DAGGERHEART.ITEMS.Beastform.dialogTitle');
return this.item.name;
}
/** @override */
static PARTS = {
header: { template: 'systems/daggerheart/templates/dialogs/beastform/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/dialogs/beastform/tabs.hbs' },
beastformTier: { template: 'systems/daggerheart/templates/dialogs/beastform/beastformTier.hbs' },
advanced: { template: 'systems/daggerheart/templates/dialogs/beastform/advanced.hbs' },
@ -262,12 +268,13 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
if (!options.submitted) this.selected = null;
}
static async configure(configData) {
static async configure(configData, item) {
return new Promise(resolve => {
const app = new this(configData);
const app = new this(configData, item);
const featureItem = item;
app.addEventListener(
'close',
() => resolve({ selected: app.selected, evolved: app.evolved, hybrid: app.hybrid }),
() => resolve({ selected: app.selected, evolved: app.evolved, hybrid: app.hybrid, item: featureItem }),
{ once: true }
);
app.render({ force: true });

View file

@ -1,66 +0,0 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class CostSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(costs, uses, action, resolve) {
super({});
this.costs = costs;
this.uses = uses;
this.action = action;
this.resolve = resolve;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
sendCost: this.sendCost
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return `Cost Options`;
}
async _prepareContext(_options) {
const updatedCosts = this.action.calcCosts(this.costs),
updatedUses = this.action.calcUses(this.uses);
return {
costs: updatedCosts,
uses: updatedUses,
canUse: this.action.hasCost(updatedCosts) && this.action.hasUses(updatedUses)
};
}
static async updateForm(event, _, formData) {
const data = foundry.utils.expandObject(formData.object);
this.costs = foundry.utils.mergeObject(this.costs, data.costs);
this.uses = foundry.utils.mergeObject(this.uses, data.uses);
this.render(true);
}
static sendCost(event) {
event.preventDefault();
this.resolve({ costs: this.action.getRealCosts(this.costs), uses: this.uses });
this.close();
}
}

View file

@ -22,7 +22,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
id: 'roll-selection',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'roll-selection'],
position: {
width: 550
width: 'auto'
},
window: {
icon: 'fa-solid fa-dice'
@ -52,10 +52,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
rollSelection: {
id: 'rollSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/rollSelection.hbs'
},
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'
}
};

View file

@ -12,14 +12,17 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.name');
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-appearance-settings',
classes: ['daggerheart', 'setting', 'dh-style'],
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save

View file

@ -12,14 +12,17 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.automation.name');
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-automation-settings',
classes: ['daggerheart', 'setting', 'dh-style'],
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save

View file

@ -11,14 +11,17 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name');
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-homebrew-settings',
classes: ['daggerheart', 'setting', 'dh-style'],
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
addItem: this.addItem,
editItem: this.editItem,

View file

@ -12,14 +12,17 @@ export default class DhRangeMeasurementSettings extends HandlebarsApplicationMix
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.automation.name');
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-automation-settings',
classes: ['daggerheart', 'setting', 'dh-style'],
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save

View file

@ -12,14 +12,17 @@ export default class DHVariantRuleSettings extends HandlebarsApplicationMixin(Ap
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.variantRules.name');
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-appearance-settings',
classes: ['daggerheart', 'setting', 'dh-style'],
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save

View file

@ -441,9 +441,13 @@ export default function DHApplicationMixin(Base) {
const { type: actionType } =
(await foundry.applications.api.DialogV2.input({
window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
classes: ['daggerheart', 'dh-style'],
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs',
{ types: CONFIG.DH.ACTIONS.actionTypes }
{
types: CONFIG.DH.ACTIONS.actionTypes,
itemName: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectAction')
}
),
ok: {
label: game.i18n.format('DOCUMENT.Create', {
@ -581,7 +585,7 @@ export default function DHApplicationMixin(Base) {
const { actionId } = target.closest('[data-action-id]').dataset;
const { actions, attack } = doc.system;
const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId);
await action.use(event);
await action.use(event, doc);
}
/**

View file

@ -46,17 +46,14 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.target-indicator').forEach(element =>
element.addEventListener('click', this.onToggleTargets)
);
html.querySelectorAll('.advantage').forEach(element =>
element.addEventListener('mouseenter', this.hoverAdvantage)
);
html.querySelectorAll('.advantage').forEach(element =>
element.addEventListener('click', event => this.selectAdvantage.call(this, event, data.message))
);
html.querySelectorAll('.ability-use-button').forEach(element =>
element.addEventListener('click', event => this.abilityUseButton.call(this, event, data.message))
element.addEventListener('click', event => this.abilityUseButton(this, event, data.message))
);
html.querySelectorAll('.action-use-button').forEach(element =>
element.addEventListener('click', event => this.actionUseButton.call(this, event, data.message))
element.addEventListener('click', event => this.actionUseButton(this, event, data.message))
);
html.querySelectorAll('.reroll-button').forEach(element =>
element.addEventListener('click', event => this.rerollEvent(this, event, data.message))
);
};
@ -70,7 +67,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
async getActor(id) {
// return game.actors.get(id);
return await fromUuid(id);
}
@ -85,7 +81,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return action;
}
onRollDamage = async (event, message) => {
async onRollDamage(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (game.user.character?.id !== actor.id && !game.user.isGM) return true;
@ -94,9 +90,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (!action || !action?.rollDamage) return;
await action.rollDamage(event, message);
}
};
}
onRollHealing = async (event, message) => {
async onRollHealing(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
@ -105,9 +101,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (!action || !action?.rollHealing) return;
await action.rollHealing(event, message);
}
};
}
onRollSave = async (event, message) => {
async onRollSave(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor),
tokenId = event.target.closest('[data-token]')?.dataset.token,
@ -118,9 +114,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (!action || !action?.hasSave) return;
action.rollSave(token, event, message);
}
};
}
onRollAllSave = async (event, message) => {
onRollAllSave(event, _message) {
event.stopPropagation();
const targets = event.target.parentElement.querySelectorAll(
'.target-section > [data-token] .target-save-container'
@ -128,9 +124,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
targets.forEach(el => {
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
});
};
}
onApplyEffect = async (event, message) => {
async onApplyEffect(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
@ -142,9 +138,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
await action.applyEffects(event, message, targets);
}
};
}
onTargetSelection = async (event, message) => {
onTargetSelection(event, message) {
event.stopPropagation();
const targetSelection = Boolean(event.target.dataset.targetHit),
msg = ui.chat.collection.get(message._id);
@ -154,9 +150,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
msg.system.targetSelection = targetSelection;
msg.system.prepareDerivedData();
ui.chat.updateMessage(msg);
};
}
getTargetList = (event, message) => {
getTargetList(event, message) {
const targetSelection = event.target
.closest('.message-content')
.querySelector('.button-target-selection.target-selected'),
@ -167,20 +163,20 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
: Array.from(game.user.targets)
};
};
}
hoverTarget = event => {
hoverTarget(event) {
event.stopPropagation();
const token = canvas.tokens.get(event.currentTarget.dataset.token);
if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true });
};
}
unhoverTarget = event => {
unhoverTarget(event) {
const token = canvas.tokens.get(event.currentTarget.dataset.token);
if (!token?.controlled) token._onHoverOut(event);
};
}
clickTarget = event => {
clickTarget(event) {
event.stopPropagation();
const token = canvas.tokens.get(event.currentTarget.dataset.token);
if (!token) {
@ -188,9 +184,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return;
}
game.canvas.pan(token);
};
}
onDamage = async (event, message) => {
async onDamage(event, message) {
event.stopPropagation();
const { isHit, targets } = this.getTargetList(event, message);
@ -228,9 +224,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
target.actor.takeDamage(damages);
}
};
}
onHealing = async (event, message) => {
async onHealing(event, message) {
event.stopPropagation();
const targets = Array.from(game.user.targets);
@ -240,7 +236,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
for (var target of targets) {
target.actor.takeHealing(message.system.roll);
}
};
}
/**
* Toggle visibility of target containers.
@ -253,51 +249,15 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
.forEach(el => el.classList.toggle('hidden'));
}
/**
* Highlight advantage icons on hover.
* @param {MouseEvent} event
*/
hoverAdvantage(event) {
const parent = event.currentTarget.parentElement;
if (!parent) return;
parent.querySelectorAll('.advantage').forEach(el => {
if (el !== event.currentTarget) {
el.classList.toggle('unused');
}
});
}
/**
* Handle selecting an advantage and disable further selection.
* @param {MouseEvent} event
* @param {object} message
*/
async selectAdvantage(event, message) {
event.stopPropagation();
const updateMessage = game.messages.get(message._id);
await updateMessage?.update({
system: { advantageSelected: event.currentTarget.id === 'hope' ? 1 : 2 }
});
const parent = event.currentTarget.parentElement;
if (!parent) return;
parent.querySelectorAll('.advantage').forEach(el => {
el.replaceWith(el.cloneNode(true));
});
}
abilityUseButton = async (event, message) => {
async abilityUseButton(event, message) {
event.stopPropagation();
const action = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)];
const actor = game.actors.get(message.system.source.actor);
await actor.useAction(action);
};
}
actionUseButton = async (event, message) => {
async actionUseButton(event, message) {
const { moveIndex, actionIndex } = event.currentTarget.dataset;
const parent = await foundry.utils.fromUuid(message.system.actor);
const actionType = message.system.moves[moveIndex].actions[actionIndex];
@ -308,5 +268,30 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
);
action.use(event);
};
}
async rerollEvent(event, message) {
if (!event.shiftKey) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmTitle')
},
content: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmText')
});
if (!confirmed) return;
}
const target = event.target.closest('button[data-die-index]');
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
const rollClass =
game.system.api.dice[
message.type === 'dualityRoll' ? 'DualityRoll' : target.dataset.type === 'damage' ? 'DHRoll' : 'D20Roll'
];
const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message);
await game.messages.get(message._id).update({
'system.roll': newRoll,
'rolls': [parsedRoll]
});
}
}

View file

@ -10,7 +10,9 @@ export default class DhBeastformAction extends DHBaseAction {
const abort = await this.handleActiveTransformations();
if (abort) return;
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig);
const item = args[0];
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, item);
if (!selected) return;
await this.transform(selected, evolved, hybrid);

View file

@ -6,7 +6,7 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
title: new fields.StringField(),
roll: new fields.DataField(),
roll: new fields.ObjectField(),
targets: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({}),

View file

@ -26,10 +26,7 @@ const stressDamageReductionRule = localizationPath =>
const bonusField = label =>
new fields.SchemaField({
bonus: new fields.NumberField({ integer: true, initial: 0, label: `${game.i18n.localize(label)} Value` }),
dice: new fields.ArrayField(
new fields.StringField(),
{ label: `${game.i18n.localize(label)} Dice` }
)
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
});
export { attributeField, resourceField, stressDamageReductionRule, bonusField };

View file

@ -147,7 +147,10 @@ export default class D20Roll extends DHRoll {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
target.hit = this.isCritical || roll.total >= difficulty;
});
} else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty;
} else if (config.roll.difficulty) {
data.difficulty = config.roll.difficulty;
data.success = roll.isCritical || roll.total >= config.roll.difficulty;
}
data.advantage = {
type: config.roll.advantage,
dice: roll.dAdvantage?.denomination,
@ -169,4 +172,22 @@ export default class D20Roll extends DHRoll {
resetFormula() {
return (this._formula = this.constructor.getFormula(this.terms));
}
static async reroll(rollString, _target, message) {
let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString);
parsedRoll = await parsedRoll.reroll();
const newRoll = game.system.api.dice.D20Roll.postEvaluate(parsedRoll, {
targets: message.system.targets,
roll: {
advantage: message.system.roll.advantage?.type,
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
}
});
if (game.modules.get('dice-so-nice')?.active) {
await game.dice3d.showForRoll(parsedRoll, game.user, true);
}
return { newRoll, parsedRoll };
}
}

View file

@ -1,6 +1,7 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import D20Roll from './d20Roll.mjs';
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
export default class DualityRoll extends D20Roll {
_advantageFaces = 6;
@ -110,6 +111,13 @@ export default class DualityRoll extends D20Roll {
return [...(hooks ?? []), 'Duality'];
}
/** @inheritDoc */
static fromData(data) {
data.terms[0].class = game.system.api.dice.DualityDie.name;
data.terms[2].class = game.system.api.dice.DualityDie.name;
return super.fromData(data);
}
createBaseDice() {
if (
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
@ -186,4 +194,44 @@ export default class DualityRoll extends D20Roll {
return data;
}
static async reroll(rollString, target, message) {
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false });
const term = parsedRoll.terms[target.dataset.dieIndex];
await term.reroll(`/r1=${term.total}`);
if (game.modules.get('dice-so-nice')?.active) {
const diceSoNiceRoll = {
_evaluated: true,
dice: [
new foundry.dice.terms.Die({
...term,
faces: term._faces,
results: term.results.filter(x => !x.rerolled)
})
],
options: { appearance: {} }
};
const diceSoNicePresets = getDiceSoNicePresets();
const type = target.dataset.type;
if (diceSoNicePresets[type]) {
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets[type] };
}
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
}
await parsedRoll.evaluate();
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
targets: message.system.targets,
roll: {
advantage: message.system.roll.advantage?.type,
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
}
});
newRoll.extra = newRoll.extra.slice(2);
return { newRoll, parsedRoll };
}
}

View file

@ -1,10 +1,10 @@
export default class DhpChatMessage extends foundry.documents.ChatMessage {
async renderHTML() {
if (this.system.messageTemplate)
this.content = await foundry.applications.handlebars.renderTemplate(
this.system.messageTemplate,
this.system
);
this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, {
...this.system,
_source: this.system._source
});
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
const html = await super.renderHTML();

View file

@ -21,19 +21,34 @@ function getDualityMessage(roll) {
? game.i18n.localize(abilities[roll.trait].label)
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
const advantage = roll.advantage
? CONFIG.DH.ACTIONS.advandtageState.advantage.value
: roll.disadvantage
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
: undefined;
const advantageLabel =
advantage === CONFIG.DH.ACTIONS.advandtageState.advantage.value
? 'Advantage'
: advantage === CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
? 'Disadvantage'
: undefined;
const dualityElement = document.createElement('span');
dualityElement.innerHTML = `
<button class="duality-roll-button"
data-title="${label}"
data-label="${dataLabel}"
data-hope="${roll.hope ?? 'd12'}"
data-fear="${roll.fear ?? 'd12'}"
data-fear="${roll.fear ?? 'd12'}"
${advantage ? `data-advantage="${advantage}"` : ''}
${roll.difficulty !== undefined ? `data-difficulty="${roll.difficulty}"` : ''}
${roll.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
${roll.advantage ? 'data-advantage="true"' : ''}
${roll.disadvantage ? 'data-disadvantage="true"' : ''}
>
<i class="fa-solid fa-circle-half-stroke"></i>
${label}
${roll.difficulty || advantageLabel ? `(${[roll.difficulty, advantageLabel ? game.i18n.localize(`DAGGERHEART.GENERAL.${advantageLabel}.short`) : null].filter(x => x).join(' ')})` : ''}
</button>
`;
@ -43,16 +58,39 @@ function getDualityMessage(roll) {
export const renderDualityButton = async event => {
const button = event.currentTarget,
traitValue = button.dataset.trait?.toLowerCase(),
target = getCommandTarget();
target = getCommandTarget(),
difficulty = button.dataset.difficulty,
advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined;
await enrichedDualityRoll(
{
traitValue,
target,
difficulty,
title: button.dataset.title,
label: button.dataset.label,
actionType: button.dataset.actionType,
advantage
},
event
);
};
export const enrichedDualityRoll = async (
{ traitValue, target, difficulty, title, label, actionType, advantage },
event
) => {
if (!target) return;
const config = {
event: event,
title: button.dataset.title,
event: event ?? {},
title: title,
roll: {
modifier: traitValue ? target.system.traits[traitValue].value : null,
label: button.dataset.label,
type: button.dataset.actionType ?? null // Need check
label: label,
difficulty: difficulty,
advantage,
type: actionType ?? null // Need check,
},
chatMessage: {
template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs'

View file

@ -30,6 +30,7 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs',
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
'systems/daggerheart/templates/dialogs/downtime/activities.hbs'
'systems/daggerheart/templates/dialogs/downtime/activities.hbs',
'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'
]);
};