Merged with main

This commit is contained in:
WBHarry 2025-07-05 21:40:38 +02:00
commit b7c2d40d7f
62 changed files with 990 additions and 514 deletions

View file

@ -30,7 +30,7 @@ export default class CostSelectionDialog extends HandlebarsApplicationMixin(Appl
static PARTS = {
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/costSelection.hbs'
template: 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'
}
};

View file

@ -20,10 +20,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'roll-selection',
classes: ['daggerheart', 'views', 'roll-selection'],
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'roll-selection'],
position: {
width: 400,
height: 'auto'
width: 550
},
window: {
icon: 'fa-solid fa-dice'
},
actions: {
updateIsAdvantage: this.updateIsAdvantage,
@ -37,20 +39,29 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}
};
get title() {
return this.config.title;
}
/** @override */
static PARTS = {
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/costSelection.hbs'
header: {
id: 'header',
template: 'systems/daggerheart/templates/dialogs/dice-roll/header.hbs'
},
rollSelection: {
id: 'rollSelection',
template: 'systems/daggerheart/templates/dialogs/rollSelection.hbs'
template: 'systems/daggerheart/templates/dialogs/dice-roll/rollSelection.hbs'
},
costSelection: {
id: 'costSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.rollConfig = this.config;
context.hasRoll = !!this.config.roll;
context.roll = this.roll;
context.rollType = this.roll?.constructor.name;
@ -60,6 +71,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}));
context.selectedExperiences = this.config.experiences;
context.advantage = this.config.roll?.advantage;
context.disadvantage = this.config.roll?.disadvantage;
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
context.canRoll = true;
context.isLite = this.config.roll?.lite;
@ -75,7 +87,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
return context;
}
@ -96,6 +107,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
static updateIsAdvantage(_, button) {
const advantage = Number(button.dataset.advantage);
this.advantage = advantage === 1;
this.disadvantage = advantage === -1;
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
this.render();
}

View file

@ -30,7 +30,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/damageSelection.hbs'
template: 'systems/daggerheart/templates/dialogs/dice-roll/damageSelection.hbs'
}
};

View file

@ -44,7 +44,7 @@ export default class DamageSelectionDialog extends HandlebarsApplicationMixin(Ap
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/damageSelection.hbs'
template: 'systems/daggerheart/templates/dialogs/dice-roll/damageSelection.hbs'
}
};

View file

@ -85,7 +85,8 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
static async reactionRoll(event) {
const config = {
event: event,
title: `${this.actor.name} - Reaction Roll`,
title: `Reaction Roll: ${this.actor.name}`,
headerTitle: 'Adversary Reaction Roll',
roll: {
// modifier: null,
type: 'reaction'

View file

@ -490,6 +490,19 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
}
}
static async rollAttribute(event, button) {
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
const config = {
event: event,
title: `${game.i18n.localize('DAGGERHEART.General.dualityRoll')}: ${this.actor.name}`,
headerTitle: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', { ability: abilityLabel }),
roll: {
trait: button.dataset.attribute
}
};
this.document.diceRoll(config);
}
/* -------------------------------------------- */
/* Filter Menus */
/* -------------------------------------------- */

View file

@ -50,13 +50,13 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
element.addEventListener('mouseenter', this.hoverAdvantage)
);
html.querySelectorAll('.advantage').forEach(element =>
element.addEventListener('click', event => this.selectAdvantage.bind(this)(event, data.message))
element.addEventListener('click', event => this.selectAdvantage.call(this, event, data.message))
);
html.querySelectorAll('.ability-use-button').forEach(element =>
element.addEventListener('click', event => this.abilityUseButton.bind(this)(event, data.message))
element.addEventListener('click', event => this.abilityUseButton.call(this, event, data.message))
);
html.querySelectorAll('.action-use-button').forEach(element =>
element.addEventListener('click', event => this.actionUseButton.bind(this)(event, data.message))
element.addEventListener('click', event => this.actionUseButton.call(this, event, data.message))
);
};
@ -229,24 +229,52 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
};
onToggleTargets = async event => {
/**
* Toggle visibility of target containers.
* @param {MouseEvent} event
*/
onToggleTargets(event) {
event.stopPropagation();
$($(event.currentTarget).parent()).find('.target-container').toggleClass('hidden');
};
event.currentTarget.parentElement
?.querySelectorAll('.target-container')
.forEach(el => el.classList.toggle('hidden'));
}
hoverAdvantage = event => {
$(event.currentTarget).siblings('.advantage').toggleClass('unused');
};
/**
* Highlight advantage icons on hover.
* @param {MouseEvent} event
*/
hoverAdvantage(event) {
const parent = event.currentTarget.parentElement;
if (!parent) return;
selectAdvantage = async (event, message) => {
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 } });
await updateMessage?.update({
system: { advantageSelected: event.currentTarget.id === 'hope' ? 1 : 2 }
});
$(event.currentTarget).siblings('.advantage').off('click');
$(event.currentTarget).off('click');
};
const parent = event.currentTarget.parentElement;
if (!parent) return;
parent.querySelectorAll('.advantage').forEach(el => {
el.replaceWith(el.cloneNode(true));
});
}
abilityUseButton = async (event, message) => {
event.stopPropagation();

View file

@ -1,2 +1,2 @@
export { default as FilterMenu } from './filter-menu.mjs';
export { default as ContextMenu } from './contextMenu.mjs';
export { default as DHContextMenu } from './contextMenu.mjs';

View file

@ -1,4 +1,63 @@
export default class DhContextMenu extends foundry.applications.ux.ContextMenu.implementation {
/**
* @typedef ContextMenuEntry
* @property {string} name The context menu label. Can be localized.
* @property {string} [icon] A string containing an HTML icon element for the menu item.
* @property {string} [classes] Additional CSS classes to apply to this menu item.
* @property {string} [group] An identifier for a group this entry belongs to.
* @property {ContextMenuJQueryCallback} callback The function to call when the menu item is clicked.
* @property {ContextMenuCondition|boolean} [condition] A function to call or boolean value to determine if this entry
* appears in the menu.
*/
/**
* @callback ContextMenuCondition
* @param {jQuery|HTMLElement} html The element of the context menu entry.
* @returns {boolean} Whether the entry should be rendered in the context menu.
*/
/**
* @callback ContextMenuCallback
* @param {HTMLElement} target The element that the context menu has been triggered for.
* @returns {unknown}
*/
/**
* @callback ContextMenuJQueryCallback
* @param {HTMLElement|jQuery} target The element that the context menu has been triggered for. Will
* either be a jQuery object or an HTMLElement instance, depending
* on how the ContextMenu was configured.
* @returns {unknown}
*/
/**
* @typedef ContextMenuOptions
* @property {string} [eventName="contextmenu"] Optionally override the triggering event which can spawn the menu. If
* the menu is using fixed positioning, this event must be a MouseEvent.
* @property {ContextMenuCallback} [onOpen] A function to call when the context menu is opened.
* @property {ContextMenuCallback} [onClose] A function to call when the context menu is closed.
* @property {boolean} [fixed=false] If true, the context menu is given a fixed position rather than being
* injected into the target.
* @property {boolean} [jQuery=true] If true, callbacks will be passed jQuery objects instead of HTMLElement
* instances.
*/
/**
* @typedef ContextMenuRenderOptions
* @property {Event} [event] The event that triggered the context menu opening.
* @property {boolean} [animate=true] Animate the context menu opening.
*/
/**
* A subclass of ContextMenu.
* @extends {foundry.applications.ux.ContextMenu}
*/
export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
/**
* @param {HTMLElement|jQuery} container - The HTML element that contains the context menu targets.
* @param {string} selector - A CSS selector which activates the context menu.
* @param {ContextMenuEntry[]} menuItems - An Array of entries to display in the menu
* @param {ContextMenuOptions} [options] - Additional options to configure the context menu.
*/
constructor(container, selector, menuItems, options) {
super(container, selector, menuItems, options);
@ -6,12 +65,21 @@ export default class DhContextMenu extends foundry.applications.ux.ContextMenu.i
this.#jQuery = options.jQuery;
}
/**
* Whether to pass jQuery objects or HTMLElement instances to callback.
* @type {boolean}
*/
#jQuery;
/**@inheritdoc */
activateListeners(menu) {
menu.addEventListener('click', this.#onClickItem.bind(this));
}
/**
* Handle click events on context menu items.
* @param {PointerEvent} event The click event
*/
#onClickItem(event) {
event.preventDefault();
event.stopPropagation();
@ -22,6 +90,12 @@ export default class DhContextMenu extends foundry.applications.ux.ContextMenu.i
this.close();
}
/* -------------------------------------------- */
/**
* Trigger a context menu event in response to a normal click on a additional options button.
* @param {PointerEvent} event
*/
static triggerContextMenu(event) {
event.preventDefault();
event.stopPropagation();

View file

@ -197,11 +197,9 @@ export default class FilterMenu extends foundry.applications.ux.ContextMenu {
}
}));
return [
...game.i18n.sortObjects(typesFilters, 'name'),
...game.i18n.sortObjects(burdenFilter, 'name'),
...game.i18n.sortObjects(damageTypeFilter, 'name')
];
const sort = arr => game.i18n.sortObjects(arr, 'name');
return [...sort(typesFilters, 'name'), ...sort(burdenFilter, 'name'), ...sort(damageTypeFilter, 'name')];
}
/**