diff --git a/assets/icons/dice/adv/d10.svg b/assets/icons/dice/adv/d10.svg new file mode 100644 index 00000000..4dc6a6f9 --- /dev/null +++ b/assets/icons/dice/adv/d10.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/adv/d12.svg b/assets/icons/dice/adv/d12.svg new file mode 100644 index 00000000..f9cf1301 --- /dev/null +++ b/assets/icons/dice/adv/d12.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/adv/d20.svg b/assets/icons/dice/adv/d20.svg new file mode 100644 index 00000000..098d3131 --- /dev/null +++ b/assets/icons/dice/adv/d20.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/adv/d4.svg b/assets/icons/dice/adv/d4.svg new file mode 100644 index 00000000..e52c5f46 --- /dev/null +++ b/assets/icons/dice/adv/d4.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/adv/d6.svg b/assets/icons/dice/adv/d6.svg new file mode 100644 index 00000000..5ea7ebc1 --- /dev/null +++ b/assets/icons/dice/adv/d6.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/adv/d8.svg b/assets/icons/dice/adv/d8.svg new file mode 100644 index 00000000..aa733008 --- /dev/null +++ b/assets/icons/dice/adv/d8.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/default/d10.svg b/assets/icons/dice/default/d10.svg new file mode 100644 index 00000000..cdca9728 --- /dev/null +++ b/assets/icons/dice/default/d10.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/dice/default/d12.svg b/assets/icons/dice/default/d12.svg new file mode 100644 index 00000000..e6271a75 --- /dev/null +++ b/assets/icons/dice/default/d12.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/dice/default/d20.svg b/assets/icons/dice/default/d20.svg new file mode 100644 index 00000000..4d3ac16e --- /dev/null +++ b/assets/icons/dice/default/d20.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/icons/dice/default/d4.svg b/assets/icons/dice/default/d4.svg new file mode 100644 index 00000000..db5ad410 --- /dev/null +++ b/assets/icons/dice/default/d4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/dice/default/d6.svg b/assets/icons/dice/default/d6.svg new file mode 100644 index 00000000..6bc98c66 --- /dev/null +++ b/assets/icons/dice/default/d6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/dice/default/d8.svg b/assets/icons/dice/default/d8.svg new file mode 100644 index 00000000..edf35cff --- /dev/null +++ b/assets/icons/dice/default/d8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/dice/disadv/d10.svg b/assets/icons/dice/disadv/d10.svg new file mode 100644 index 00000000..af4d0d19 --- /dev/null +++ b/assets/icons/dice/disadv/d10.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/disadv/d12.svg b/assets/icons/dice/disadv/d12.svg new file mode 100644 index 00000000..75a7073c --- /dev/null +++ b/assets/icons/dice/disadv/d12.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/disadv/d20.svg b/assets/icons/dice/disadv/d20.svg new file mode 100644 index 00000000..e4130440 --- /dev/null +++ b/assets/icons/dice/disadv/d20.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/disadv/d4.svg b/assets/icons/dice/disadv/d4.svg new file mode 100644 index 00000000..3ec74176 --- /dev/null +++ b/assets/icons/dice/disadv/d4.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/disadv/d6.svg b/assets/icons/dice/disadv/d6.svg new file mode 100644 index 00000000..7e79e2ed --- /dev/null +++ b/assets/icons/dice/disadv/d6.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/disadv/d8.svg b/assets/icons/dice/disadv/d8.svg new file mode 100644 index 00000000..999d818f --- /dev/null +++ b/assets/icons/dice/disadv/d8.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/fear/d10.svg b/assets/icons/dice/fear/d10.svg new file mode 100644 index 00000000..80236173 --- /dev/null +++ b/assets/icons/dice/fear/d10.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/fear/d12.svg b/assets/icons/dice/fear/d12.svg new file mode 100644 index 00000000..08841af4 --- /dev/null +++ b/assets/icons/dice/fear/d12.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/fear/d20.svg b/assets/icons/dice/fear/d20.svg new file mode 100644 index 00000000..bd4e12a8 --- /dev/null +++ b/assets/icons/dice/fear/d20.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/fear/d4.svg b/assets/icons/dice/fear/d4.svg new file mode 100644 index 00000000..b641d519 --- /dev/null +++ b/assets/icons/dice/fear/d4.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/fear/d6.svg b/assets/icons/dice/fear/d6.svg new file mode 100644 index 00000000..bcc3fac2 --- /dev/null +++ b/assets/icons/dice/fear/d6.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/fear/d8.svg b/assets/icons/dice/fear/d8.svg new file mode 100644 index 00000000..9a706322 --- /dev/null +++ b/assets/icons/dice/fear/d8.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/hope/d10.svg b/assets/icons/dice/hope/d10.svg new file mode 100644 index 00000000..e5566c21 --- /dev/null +++ b/assets/icons/dice/hope/d10.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/dice/hope/d12.svg b/assets/icons/dice/hope/d12.svg new file mode 100644 index 00000000..a93766fe --- /dev/null +++ b/assets/icons/dice/hope/d12.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/hope/d20.svg b/assets/icons/dice/hope/d20.svg new file mode 100644 index 00000000..bb87a31e --- /dev/null +++ b/assets/icons/dice/hope/d20.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/hope/d4.svg b/assets/icons/dice/hope/d4.svg new file mode 100644 index 00000000..ab238828 --- /dev/null +++ b/assets/icons/dice/hope/d4.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/hope/d6.svg b/assets/icons/dice/hope/d6.svg new file mode 100644 index 00000000..26ea78ea --- /dev/null +++ b/assets/icons/dice/hope/d6.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/dice/hope/d8.svg b/assets/icons/dice/hope/d8.svg new file mode 100644 index 00000000..584b0fdc --- /dev/null +++ b/assets/icons/dice/hope/d8.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/daggerheart.mjs b/daggerheart.mjs index 81b404d5..82c56dff 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -124,7 +124,7 @@ Hooks.once('init', () => { CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.ui.resources = applications.ui.DhFearTracker; - CONFIG.ux.ContextMenu = applications.ux.ContextMenu; + CONFIG.ux.ContextMenu = applications.ux.DHContextMenu; CONFIG.ux.TooltipManager = documents.DhTooltipManager; game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent); diff --git a/lang/en.json b/lang/en.json index 4c374458..500c032a 100755 --- a/lang/en.json +++ b/lang/en.json @@ -245,7 +245,8 @@ "potentialAdversaries": "Potential Adversaries", "adversaries": "Adversaries" }, - "basics": "Basics" + "basics": "Basics", + "dualityRoll": "Duality Roll" }, "ActionType": { "passive": "Passive", diff --git a/module/applications/dialogs/costSelectionDialog.mjs b/module/applications/dialogs/costSelectionDialog.mjs index cab0d36d..026aac46 100644 --- a/module/applications/dialogs/costSelectionDialog.mjs +++ b/module/applications/dialogs/costSelectionDialog.mjs @@ -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' } }; diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index cf620e43..7eaeacb3 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -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(); } diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 84709251..442a1491 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -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' } }; diff --git a/module/applications/dialogs/damageSelectionDialog.mjs b/module/applications/dialogs/damageSelectionDialog.mjs index f4f44bc8..5248e81d 100644 --- a/module/applications/dialogs/damageSelectionDialog.mjs +++ b/module/applications/dialogs/damageSelectionDialog.mjs @@ -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' } }; diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index a48c1fa4..c12775b1 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -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' diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index e3fa7367..c74455b0 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -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 */ /* -------------------------------------------- */ diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 182a3539..2c8ad561 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -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)) ); }; @@ -156,8 +156,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo getTargetList = (event, message) => { const targetSelection = event.target - .closest('.message-content') - .querySelector('.button-target-selection.target-selected'), + .closest('.message-content') + .querySelector('.button-target-selection.target-selected'), isHit = Boolean(targetSelection.dataset.targetHit); return { isHit, @@ -229,24 +229,54 @@ 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 } + }); + + const parent = event.currentTarget.parentElement; + if (!parent) return; + + parent.querySelectorAll('.advantage').forEach(el => { + el.replaceWith(el.cloneNode(true)); + }); + } - $(event.currentTarget).siblings('.advantage').off('click'); - $(event.currentTarget).off('click'); - }; abilityUseButton = async (event, message) => { event.stopPropagation(); diff --git a/module/applications/ux/_module.mjs b/module/applications/ux/_module.mjs index 5ec0eecf..ead918fa 100644 --- a/module/applications/ux/_module.mjs +++ b/module/applications/ux/_module.mjs @@ -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'; diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index f7658d42..a913c450 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -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(); diff --git a/module/applications/ux/filter-menu.mjs b/module/applications/ux/filter-menu.mjs index ff2fb036..93d8bbf0 100644 --- a/module/applications/ux/filter-menu.mjs +++ b/module/applications/ux/filter-menu.mjs @@ -197,10 +197,12 @@ export default class FilterMenu extends foundry.applications.ux.ContextMenu { } })); + const sort = arr => game.i18n.sortObjects(arr, 'name'); + return [ - ...game.i18n.sortObjects(typesFilters, 'name'), - ...game.i18n.sortObjects(burdenFilter, 'name'), - ...game.i18n.sortObjects(damageTypeFilter, 'name') + ...sort(typesFilters, 'name'), + ...sort(burdenFilter, 'name'), + ...sort(damageTypeFilter, 'name') ]; } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index e752421c..b9faecc4 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -244,7 +244,6 @@ export default class DHBaseAction extends foundry.abstract.DataModel { } if (this.hasRoll) { - console.log(config); const rollConfig = this.prepareRoll(config); config.roll = rollConfig; config = await this.actor.diceRoll(config); diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index c90202fb..7bef3e02 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -1,4 +1,4 @@ -// import { actionsTypes } from '../action/_module.mjs'; +import { actionsTypes } from '../action/_module.mjs'; /** * Describes metadata about the item data model type @@ -55,24 +55,28 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { return data; } + /**@inheritdoc */ async _preCreate(data, options, user) { + // Skip if no initial action is required or actions already exist if (!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return; - const actionType = { - weapon: 'attack' - }[this.constructor.metadata.type], - cls = game.system.api.models.actionsTypes[actionType], - // cls = actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - type: actionType, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), - ...cls.getSourceConfig(this.parent) - }, - { - parent: this.parent - } - ); + + const metadataType = this.constructor.metadata.type; + const actionType = { weapon: "attack" }[metadataType]; + const ActionClass = actionsTypes[actionType]; + + const action = new ActionClass( + { + _id: foundry.utils.randomID(), + type: actionType, + name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), + ...ActionClass.getSourceConfig(this.parent) + }, + { + parent: this.parent + } + ); + this.updateSource({ actions: [action] }); } + } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 6200b690..99c1ec84 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -7,49 +7,11 @@ export default class RegisterHandlebarsHelpers { join: this.join, add: this.add, subtract: this.subtract, - objectSelector: this.objectSelector, includes: this.includes, - debug: this.debug, - signedNumber: this.signedNumber, - length: this.length, - switch: this.switch, case: this.case, - eq: this.eq, - ne: this.ne, - lt: this.lt, - gt: this.gt, - lte: this.lte, - gte: this.gte, - and: this.and, - or: this.or }); } - static eq(v1, v2) { - return v1 === v2; - } - static ne(v1, v2) { - return v1 !== v2; - } - static lt(v1, v2) { - return v1 < v2; - } - static gt(v1, v2) { - return v1 > v2; - } - static lte(v1, v2) { - return v1 <= v2; - } - static gte(v1, v2) { - return v1 >= v2; - } - static and() { - return Array.prototype.every.call(arguments, Boolean); - } - static or() { - return Array.prototype.slice.call(arguments, 0, -1).some(Boolean); - } - static times(nr, block) { var accum = ''; for (var i = 0; i < nr; ++i) accum += block.fn(i); @@ -72,59 +34,11 @@ export default class RegisterHandlebarsHelpers { return (Number.isNaN(aNum) ? 0 : aNum) - (Number.isNaN(bNum) ? 0 : bNum); } - static objectSelector(options) { - let { title, values, titleFontSize, ids, style } = options.hash; - - const titleLength = getWidthOfText(title, titleFontSize, true, true); - const margins = 12; - - const buttons = options.fn(); - const nrButtons = Math.max($(buttons).length - 1, 1); - const iconWidth = 26; - - const texts = values - .reduce((acc, x, index) => { - if (x) { - acc.push( - `${x}` - ); - } - - return acc; - }, []) - .join(' '); - - const html = `
-
- ${title} -
- ${texts} -
- ${buttons} -
-
- `; - - return new Handlebars.SafeString(html); - } static includes(list, item) { return list.includes(item); } - static signedNumber(number) { - return number >= 0 ? `+${number}` : number; - } - - static length(obj) { - return Object.keys(obj).length; - } - - static switch(value, options) { - this.switch_value = value; - this.switch_break = false; - return options.fn(this); - } static case(value, options) { if (value == this.switch_value) { @@ -132,9 +46,4 @@ export default class RegisterHandlebarsHelpers { return options.fn(this); } } - - static debug(a) { - console.log(a); - return a; - } } diff --git a/styles/application.less b/styles/application.less index 24b6a7d4..412d41e7 100644 --- a/styles/application.less +++ b/styles/application.less @@ -233,81 +233,6 @@ div.daggerheart.views.multiclass { } .roll-dialog-container { - .disadvantage, - .advantage { - border: 2px solid @secondaryAccent; - border-radius: 6px; - display: flex; - align-items: center; - padding: 4px; - margin-bottom: 6px; - - &.selected { - filter: drop-shadow(0px 0px 3px @mainShadow); - } - - input { - border: 0; - } - - button { - flex: 0; - border-radius: 50%; - height: 20px; - width: 20px; - display: flex; - align-items: center; - justify-content: center; - margin: 2px 0 2px 4px; - padding: 12px; - - i { - margin: 0; - } - } - } - - .roll-dialog-experience-container { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: @halfMargin; - - .roll-dialog-chip { - border: @thinBorder solid black; - border-radius: 6px; - min-width: calc(33% - 2px); - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: @halfMargin; - cursor: pointer; - padding: @fullPadding; - background: grey; - overflow: hidden; - font-weight: bold; - - &.hover { - filter: drop-shadow(0 0 3px @mainShadow); - } - - span { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - &.selected { - background: green; - - span { - filter: drop-shadow(0 0 3px @secondaryShadow); - } - } - } - } - .hope-container { display: flex; gap: @fullMargin; @@ -371,28 +296,6 @@ div.daggerheart.views.multiclass { } } } - - .roll-dialog-experience-container { - display: flex; - align-items: flex-start; - flex-wrap: wrap; - gap: @halfMargin; - flex: 1; - height: 100%; - - .experience-chip { - opacity: 0.6; - border-radius: 16px; - width: calc(50% - 4px); - white-space: nowrap; - - &.active, - &:hover { - opacity: 1; - background: var(--button-hover-background-color); - } - } - } } } diff --git a/styles/daggerheart.less b/styles/daggerheart.less index 38654759..c6d43ae8 100755 --- a/styles/daggerheart.less +++ b/styles/daggerheart.less @@ -53,6 +53,8 @@ @import './less/items/domainCard.less'; @import './less/items/class.less'; +@import './less/dialog/dice-roll/roll-selection.less'; + @import './less/utils/colors.less'; @import './less/utils/fonts.less'; diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less new file mode 100644 index 00000000..1f7653d7 --- /dev/null +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -0,0 +1,128 @@ +@import '../../utils/colors.less'; +@import '../../utils/fonts.less'; + +.application.daggerheart.dialog.dh-style.views.roll-selection { + .roll-dialog-container { + display: flex; + flex-direction: column; + gap: 12px; + + .dices-section { + display: flex; + gap: 60px; + justify-content: center; + + .dice-option { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + width: 120px; + + .dice-icon { + width: 70px; + height: 70px; + object-fit: contain; + } + .dice-select { + display: flex; + align-items: center; + gap: 10px; + height: 32px; + + .label { + font-family: @font-subtitle; + font-style: normal; + font-weight: 700; + font-size: 16px; + line-height: 19px; + + color: light-dark(@dark, @beige); + } + } + } + } + + .experience-container { + display: flex; + gap: 10px; + flex-wrap: wrap; + + .experience-chip { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 5px; + cursor: pointer; + padding: 5px; + background: light-dark(@dark-blue-10, @golden-10); + color: light-dark(@dark-blue, @golden); + + .label { + font-family: @font-body; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 17px; + } + + &.selected { + background: light-dark(@dark-blue-40, @golden-40); + } + } + } + + .modifier-container { + .advantage-chip, + .disadvantage-chip { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 5px; + cursor: pointer; + padding: 5px; + transition: all 0.3s ease; + + .label { + font-family: @font-body; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 17px; + } + } + + .advantage-chip { + background: @green-10; + color: @green; + + &.selected { + color: @beige; + background: @gradient-green; + } + } + + .disadvantage-chip { + background: @red-10; + color: @red; + + &.selected { + color: @beige; + background: @gradient-red; + } + } + } + + .formula-label { + font-family: @font-body; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 17px; + + color: light-dark(@dark, @beige); + } + } +} diff --git a/styles/less/utils/colors.less b/styles/less/utils/colors.less index 7ac572b9..57a6b018 100755 --- a/styles/less/utils/colors.less +++ b/styles/less/utils/colors.less @@ -1,22 +1,47 @@ @primary-blue: #1488cc; @secondary-blue: #2b32b2; + @golden: #f3c267; -@golden-40: #f3c26740; -@dark-blue-40: #18162e40; @golden-10: #f3c26710; -@dark-blue-10: #18162e10; -@dark-blue-50: #18162e50; +@golden-40: #f3c26740; + +@red: #e54e4e; +@red-10: #e54e4e10; +@red-40: #e54e4e40; + +@dark-red: #3c0000; +@dark-red-10: #3c000010; +@dark-red-40: #3c000040; + +@green: #40a640; +@green-10: #40a64010; +@green-40: #40a64040; + +@dark-green: #011b01; +@dark-green-10: #011b0110; +@dark-green-40: #011b0140; + @dark-blue: #18162e; +@dark-blue-10: #18162e10; +@dark-blue-40: #18162e40; +@dark-blue-50: #18162e50; +@semi-transparent-dark-blue: rgba(24, 22, 46, 0.33); + +@dark: #222; +@dark-15: #22222215; + @deep-black: #0e0d15; + @beige: #efe6d8; @beige-15: #efe6d815; @beige-50: #efe6d850; -@dark-blue: rgb(24, 22, 46); -@semi-transparent-dark-blue: rgba(24, 22, 46, 0.33); -@dark: #222; -@dark-15: #22222215; + @light-black: rgba(0, 0, 0, 0.3); @soft-shadow: rgba(0, 0, 0, 0.05); + +@gradient-green: linear-gradient(151.21deg, @green 7.21%, @dark-green 92.79%); +@gradient-red: linear-gradient(151.21deg, @red 7.21%, @dark-red 92.79%); + @gradient-hp: linear-gradient(15deg, rgb(70, 20, 10) 0%, rgb(190, 0, 0) 42%, rgb(252, 176, 69) 100%); @gradient-stress: linear-gradient(15deg, rgb(130, 59, 1) 0%, rgb(252, 142, 69) 65%, rgb(190, 0, 0) 100%); diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index c30a5535..39c2bee0 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -5,18 +5,18 @@ */ .appTheme(@darkRules, @lightRules) { // Dark theme selectors - .themed.theme-dark .application.sheet.dh-style, - .themed.theme-dark.application.sheet.dh-style, - body.theme-dark .application, - body.theme-dark.application { + .themed.theme-dark .application.daggerheart.sheet.dh-style, + .themed.theme-dark.application.daggerheart.sheet.dh-style, + body.theme-dark .application.daggerheart, + body.theme-dark.application.daggerheart { @darkRules(); } // Light theme selectors - .themed.theme-light .application.sheet.dh-style, - .themed.theme-light.application.sheet.dh-style, - body.theme-light .application, - body.theme-light.application { + .themed.theme-light .application.daggerheart.sheet.dh-style, + .themed.theme-light.application.daggerheart.sheet.dh-style, + body.theme-light .application.daggerheart, + body.theme-light.application.daggerheart { @lightRules(); } } diff --git a/templates/characterCreation/tabs/setup.hbs b/templates/characterCreation/tabs/setup.hbs index 4d6e638c..6dd04345 100644 --- a/templates/characterCreation/tabs/setup.hbs +++ b/templates/characterCreation/tabs/setup.hbs @@ -74,7 +74,7 @@ {{#each experience.values as |experience id|}}
-
{{signedNumber this.value}}
+
{{numberFormat this.value sign=true}}
{{/each}} diff --git a/templates/dialogs/costSelection.hbs b/templates/dialogs/dice-roll/costSelection.hbs similarity index 100% rename from templates/dialogs/costSelection.hbs rename to templates/dialogs/dice-roll/costSelection.hbs diff --git a/templates/dialogs/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs similarity index 100% rename from templates/dialogs/damageSelection.hbs rename to templates/dialogs/dice-roll/damageSelection.hbs diff --git a/templates/dialogs/dice-roll/header.hbs b/templates/dialogs/dice-roll/header.hbs new file mode 100644 index 00000000..462408f6 --- /dev/null +++ b/templates/dialogs/dice-roll/header.hbs @@ -0,0 +1,7 @@ +
+ {{#if rollConfig.headerTitle}} +

{{rollConfig.headerTitle}}

+ {{else}} +

{{rollConfig.title}}

+ {{/if}} +
\ No newline at end of file diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs new file mode 100644 index 00000000..fb0b2074 --- /dev/null +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -0,0 +1,126 @@ +
+ {{#if @root.hasRoll}} +
+ {{#unless @root.isLite}} +
+ {{#if (eq @root.rollType 'D20Roll')}} +
+ +
+ +
+
+ {{#if @root.advantage}} + {{#if (eq @root.advantage 1)}} +
+ +
+ Advantage +
+
+ {{else if (eq @root.advantage -1)}} +
+ +
+ Disdvantage +
+
+ {{/if}} + {{/if}} + {{/if}} + {{#if (eq @root.rollType 'DualityRoll')}} +
+ +
+ Hope + +
+
+
+ +
+ Fear + +
+
+ {{#if @root.advantage}} + {{#if (eq @root.advantage 1)}} +
+ +
+ Advantage +
+
+ {{else if (eq @root.advantage -1)}} +
+ +
+ Disdvantage +
+
+ {{/if}} + {{/if}} + {{/if}} +
+ +
+ Experiences + {{#each experiences}} + {{#if name}} +
+ {{#if (includes ../selectedExperiences id)}} + + {{else}} + + {{/if}} + {{#if (eq @root.rollType 'D20Roll')}} + {{name}} +{{modifier}} + {{else}} + {{name}} +{{value}} + {{/if}} +
+ {{/if}} + {{/each}} +
+
+ Modifiers +
+ + + {{#unless (eq @root.rollType 'D20Roll')}} + + {{/unless}} +
+ +
+ {{/unless}} + Formula: {{@root.formula}} + +
+ {{/if}} +
\ No newline at end of file diff --git a/templates/dialogs/npcRollSelection.hbs b/templates/dialogs/npcRollSelection.hbs deleted file mode 100644 index 02f5238b..00000000 --- a/templates/dialogs/npcRollSelection.hbs +++ /dev/null @@ -1,23 +0,0 @@ - -
-
-
-
- -
d20
-
-
- - -
-
-
- {{#each this.experiences as |experience|}} - - {{/each}} -
-
- -
\ No newline at end of file diff --git a/templates/dialogs/rollSelection.hbs b/templates/dialogs/rollSelection.hbs deleted file mode 100644 index 98b6c91b..00000000 --- a/templates/dialogs/rollSelection.hbs +++ /dev/null @@ -1,81 +0,0 @@ -
- {{#if @root.hasRoll}} -
-
-
- {{!-- --}} -
{{@root.formula}}
-
- {{#unless @root.isLite}} -
- {{#each experiences}} - {{#if name}} -
- {{name}} - +{{value}} -
- {{/if}} - {{/each}} -
-
- - -
- {{#if (eq @root.rollType 'D20Roll')}} -
- -
- {{/if}} - {{#if (eq @root.rollType 'DualityRoll')}} -
-
Hope Dice
- -
-
-
Fear Dice
- -
- {{#if roll.advantage}} -
-
Adv/Disadv Dice
- -
- {{/if}} - {{/if}} -
- -
- {{/unless}} - {{!-- {{#if (not isNpc)}} --}} - {{!--
- -
- -
-
-
- -
- -
-
--}} - {{!-- {{/if}} --}} -
-
- {{/if}} - -
\ No newline at end of file diff --git a/templates/levelup/tabs/selections.hbs b/templates/levelup/tabs/selections.hbs index d397fa80..9d1d00b8 100644 --- a/templates/levelup/tabs/selections.hbs +++ b/templates/levelup/tabs/selections.hbs @@ -12,7 +12,7 @@
-
{{signedNumber this.modifier}}
+
{{numberFormat this.modifier sign=true}}
{{#if this.name}}{{/if}} diff --git a/templates/levelup/tabs/summary.hbs b/templates/levelup/tabs/summary.hbs index 4ed4438f..dc7e2a47 100644 --- a/templates/levelup/tabs/summary.hbs +++ b/templates/levelup/tabs/summary.hbs @@ -48,7 +48,7 @@
{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}
{{#each this.achievements.experiences.values}} -
{{this.name}} {{signedNumber this.modifier}}
+
{{this.name}} {{numberFormat this.modifier sign=true}}
{{/each}}
@@ -125,7 +125,7 @@
{{localize "DAGGERHEART.Application.LevelUp.summary.experienceIncreases"}}
{{#each this.advancements.experiences}} -
{{this.name}} {{signedNumber this.modifier}}
+
{{this.name}} {{numberFormat this.modifier sign=true}}
{{/each}}
diff --git a/templates/sheets/actors/companion/tempMain.hbs b/templates/sheets/actors/companion/tempMain.hbs index 810d48db..f1f315ed 100644 --- a/templates/sheets/actors/companion/tempMain.hbs +++ b/templates/sheets/actors/companion/tempMain.hbs @@ -17,7 +17,7 @@ {{#each source.system.experiences as |experience key|}}
-
{{signedNumber experience.value}}
+
{{numberFormat experience.value sign=true}}
{{/each}} diff --git a/tools/create-symlink.mjs b/tools/create-symlink.mjs index 0c8804c6..fd828c73 100644 --- a/tools/create-symlink.mjs +++ b/tools/create-symlink.mjs @@ -2,8 +2,6 @@ import fs from 'fs'; import path from 'path'; import readline from 'readline'; -console.log('Reforging Symlinks'); - const askQuestion = question => { const rl = readline.createInterface({ input: process.stdin,