diff --git a/lang/en.json b/lang/en.json index aa379c6f..85d5f997 100755 --- a/lang/en.json +++ b/lang/en.json @@ -156,7 +156,8 @@ }, "conditional": { "title": "Conditional Application" - } + }, + "stacking": { "title": "Stacking" } }, "RangeDependance": { "hint": "Settings for an optional distance at which this effect should activate", @@ -2541,8 +2542,7 @@ "featuresLabel": "Community Feature" }, "Consumable": { - "consumeOnUse": "Consume On Use", - "destroyOnEmpty": "Destroy On Empty" + "consumeOnUse": "Consume On Use" }, "DomainCard": { "type": "Type", @@ -3008,6 +3008,8 @@ }, "EffectsDisplay": { "removeThing": "[Right Click] Remove {thing}", + "increaseStacks": "[Left Click] Increment Stacks", + "decreaseStacks": "[Right Click] Decrement Stacks", "appliedBy": "Applied By: {by}" }, "ItemBrowser": { diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 0fdb1896..64fa168a 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -70,8 +70,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.rollConfig = this.config; context.hasRoll = !!this.config.roll; context.canRoll = true; - context.selectedRollMode = this.config.selectedRollMode ?? game.settings.get('core', 'rollMode'); - context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({ + context.selectedMessageMode = this.config.selectedMessageMode ?? game.settings.get('core', 'messageMode'); + context.rollModes = Object.entries(CONFIG.ChatMessage.modes).map(([action, { label, icon }]) => ({ action, label, icon @@ -142,10 +142,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio })); } - static updateRollConfiguration(event, _, formData) { + static updateRollConfiguration(_event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); - this.config.selectedRollMode = rest.selectedRollMode; + this.config.selectedMessageMode = rest.selectedMessageMode; if (this.config.costs) { this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index b24570cc..d00a744d 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -52,8 +52,8 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application context.formula = this.roll.constructFormula(this.config); context.hasHealing = this.config.hasHealing; context.directDamage = this.config.directDamage; - context.selectedRollMode = this.config.selectedRollMode; - context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({ + context.selectedMessageMode = this.config.selectedMessageMode; + context.rollModes = Object.entries(CONFIG.ChatMessage.modes).map(([action, { label, icon }]) => ({ action, label, icon @@ -69,7 +69,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application const { ...rest } = foundry.utils.expandObject(formData.object); foundry.utils.mergeObject(this.config.roll, rest.roll); foundry.utils.mergeObject(this.config.modifiers, rest.modifiers); - this.config.selectedRollMode = rest.selectedRollMode; + this.config.selectedMessageMode = rest.selectedMessageMode; this.render(); } diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index ddaabcb4..27003162 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -58,6 +58,10 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio id: 'initialization', template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/initialization.hbs' }, + rollSelection: { + id: 'rollSelection', + template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/rollSelection.hbs' + }, tagTeamRoll: { id: 'tagTeamRoll', template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs' @@ -78,15 +82,52 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio element.addEventListener('change', this.updateRollType.bind(this)); } + _configureRenderParts(options) { + const { initialization, rollSelection, tagTeamRoll } = super._configureRenderParts(options); + const augmentedParts = { initialization }; + for (const memberKey of Object.keys(this.party.system.tagTeam.members)) { + augmentedParts[memberKey] = { + id: memberKey, + template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamMember.hbs' + }; + } + augmentedParts.rollSelection = rollSelection; + augmentedParts.tagTeamRoll = tagTeamRoll; + + return augmentedParts; + } + + /**@inheritdoc */ + async _onRender(context, options) { + await super._onRender(context, options); + + if (this.element.querySelector('.team-container')) return; + const initializationPart = this.element.querySelector('.initialization-container'); + initializationPart.insertAdjacentHTML('afterend', '
'); + const teamContainer = this.element.querySelector('.team-container'); + for (const memberContainer of this.element.querySelectorAll('.team-member-container')) + teamContainer.appendChild(memberContainer); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.isEditable = this.getIsEditable(); + context.fields = this.party.system.schema.fields.tagTeam.fields; + context.data = this.party.system.tagTeam; + context.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes; + context.traitOptions = CONFIG.DH.ACTOR.abilities; + context.members = {}; + context.allHaveRolled = Object.keys(this.party.system.tagTeam.members).every(key => { + const data = this.party.system.tagTeam.members[key]; + return Boolean(data.rollData); + }); return context; } async _preparePartContext(partId, context, options) { const partContext = await super._preparePartContext(partId, context, options); + partContext.partId = partId; switch (partId) { case 'initialization': partContext.tagTeamFields = this.party.system.schema.fields.tagTeam.fields; @@ -100,66 +141,20 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio partContext.initiatorDisabled = !selectedMembers.length; partContext.openForAllPlayers = this.openForAllPlayers; + break; + case 'rollSelection': + partContext.members = Object.keys(this.party.system.tagTeam.members).reduce((acc, key) => { + const member = this.party.system.tagTeam.members[key]; + acc[key] = { selected: member.selected }; + return acc; + }, {}); break; case 'tagTeamRoll': - partContext.fields = this.party.system.schema.fields.tagTeam.fields; - partContext.data = this.party.system.tagTeam; - partContext.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes; - partContext.traitOptions = CONFIG.DH.ACTOR.abilities; - const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); - partContext.members = {}; - for (const actorId in this.party.system.tagTeam.members) { - const data = this.party.system.tagTeam.members[actorId]; - const actor = game.actors.get(actorId); - - const rollOptions = []; - const damageRollOptions = []; - for (const item of actor.items) { - if (item.system.metadata.hasActions) { - const actions = [ - ...item.system.actions, - ...(item.system.attack ? [item.system.attack] : []) - ]; - for (const action of actions) { - if (action.hasRoll) { - const actionItem = { - value: action.uuid, - label: action.name, - group: item.name, - baseAction: action.baseAction - }; - - if (action.hasDamage) damageRollOptions.push(actionItem); - else rollOptions.push(actionItem); - } - } - } - } - - const damage = data.rollData?.options?.damage; - partContext.hasDamage |= Boolean(damage); - const critHitPointsDamage = await this.getCriticalDamage(damage); - - partContext.members[actorId] = { - ...data, - isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER), - key: actorId, - readyToRoll: Boolean(data.rollChoice), - hasRolled: Boolean(data.rollData), - rollOptions, - damageRollOptions, - damage: damage, - critDamage: critHitPointsDamage, - useCritDamage: - critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) - }; - } - partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members); partContext.joinedRoll = await this.getJoinedRoll({ overrideIsCritical: critSelected, @@ -169,24 +164,85 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio break; } + if (Object.keys(this.party.system.tagTeam.members).includes(partId)) { + const data = this.party.system.tagTeam.members[partId]; + const actor = game.actors.get(partId); + + const rollOptions = []; + const damageRollOptions = []; + for (const item of actor.items) { + if (item.system.metadata.hasActions) { + const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])]; + for (const action of actions) { + if (action.hasRoll) { + const actionItem = { + value: action.uuid, + label: action.name, + group: item.name, + baseAction: action.baseAction + }; + + if (action.hasDamage) damageRollOptions.push(actionItem); + else rollOptions.push(actionItem); + } + } + } + } + + const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); + const critSelected = !selectedRoll + ? undefined + : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); + + const damage = data.rollData?.options?.damage; + partContext.hasDamage |= Boolean(damage); + const critHitPointsDamage = await this.getCriticalDamage(damage); + + partContext.members[partId] = { + ...data, + isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER), + key: partId, + readyToRoll: Boolean(data.rollChoice), + hasRolled: Boolean(data.rollData), + rollOptions, + damageRollOptions, + damage: damage, + critDamage: critHitPointsDamage, + useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) + }; + } + return partContext; } - static async updateData(_event, _, formData) { + getUpdatingParts(target) { + const { initialization, rollSelection, tagTeamRoll } = this.constructor.PARTS; + const isInitialization = this.tabGroups.application === initialization.id; + const updatingMember = target.closest('.team-member-container')?.dataset?.memberKey; + + return [ + ...(isInitialization ? [initialization.id] : []), + ...(updatingMember ? [updatingMember] : []), + ...(!isInitialization ? [rollSelection.id] : []), + ...(!isInitialization ? [tagTeamRoll.id] : []) + ]; + } + + static async updateData(event, _, formData) { const { initiator, openForAllPlayers, ...partyData } = foundry.utils.expandObject(formData.object); this.initiator = initiator; this.openForAllPlayers = openForAllPlayers !== undefined ? openForAllPlayers : this.openForAllPlayers; - this.updatePartyData(partyData); + this.updatePartyData(partyData, this.getUpdatingParts(event.target)); } - async updatePartyData(update, options = { render: true }) { + async updatePartyData(update, updatingParts, options = { render: true }) { const gmUpdate = async update => { await this.party.update(update); - this.render(); + this.render({ parts: updatingParts }); game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.Refresh, - data: { refreshType: RefreshType.TagTeamRoll, action: 'refresh' } + data: { refreshType: RefreshType.TagTeamRoll, action: 'refresh', parts: updatingParts } }); }; @@ -195,7 +251,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio gmUpdate, update, this.party.uuid, - options.render ? { refreshType: RefreshType.TagTeamRoll, action: 'refresh' } : undefined + options.render + ? { refreshType: RefreshType.TagTeamRoll, action: 'refresh', parts: updatingParts } + : undefined ); } @@ -206,7 +264,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio }); } - tagTeamRefresh = ({ refreshType, action }) => { + tagTeamRefresh = ({ refreshType, action, parts }) => { if (refreshType !== RefreshType.TagTeamRoll) return; switch (action) { @@ -214,7 +272,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio this.tabGroups.application = 'tagTeamRoll'; break; case 'refresh': - this.render(); + this.render({ parts }); break; case 'close': this.close(); @@ -304,22 +362,28 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } async updateRollType(event) { - this.updatePartyData({ - [`system.tagTeam.members.${event.target.dataset.member}`]: { - rollType: event.target.value, - rollChoice: null - } - }); + this.updatePartyData( + { + [`system.tagTeam.members.${event.target.dataset.member}`]: { + rollType: event.target.value, + rollChoice: null + } + }, + this.getUpdatingParts(event.target) + ); } static async #removeRoll(_, button) { - this.updatePartyData({ - [`system.tagTeam.members.${button.dataset.member}`]: { - rollData: null, - rollChoice: null, - selected: false - } - }); + this.updatePartyData( + { + [`system.tagTeam.members.${button.dataset.member}`]: { + rollData: null, + rollChoice: null, + selected: false + } + }, + this.getUpdatingParts(button) + ); } static async #makeRoll(event, button) { @@ -342,9 +406,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const rollData = result.messageRoll.toJSON(); delete rollData.options.messageRoll; - this.updatePartyData({ - [`system.tagTeam.members.${member}.rollData`]: rollData - }); + this.updatePartyData( + { + [`system.tagTeam.members.${member}.rollData`]: rollData + }, + this.getUpdatingParts(button) + ); } async makeTraitRoll(memberKey) { @@ -389,15 +456,18 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio diceType ); const rollData = parsedRoll.toJSON(); - this.updatePartyData({ - [`system.tagTeam.members.${member}.rollData`]: { - ...rollData, - options: { - ...rollData.options, - roll: newRoll + this.updatePartyData( + { + [`system.tagTeam.members.${member}.rollData`]: { + ...rollData, + options: { + ...rollData.options, + roll: newRoll + } } - } - }); + }, + this.getUpdatingParts(button) + ); } static async #makeDamageRoll(event, button) { @@ -423,29 +493,35 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio if (!config.damage) return; const current = this.party.system.tagTeam.members[memberKey].rollData; - await this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: config.damage + await this.updatePartyData( + { + [`system.tagTeam.members.${memberKey}.rollData`]: { + ...current, + options: { + ...current.options, + damage: config.damage + } } - } - }); + }, + this.getUpdatingParts(button) + ); } static async #removeDamageRoll(_, button) { const { memberKey } = button.dataset; const current = this.party.system.tagTeam.members[memberKey].rollData; - this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: null + this.updatePartyData( + { + [`system.tagTeam.members.${memberKey}.rollData`]: { + ...current, + options: { + ...current.options, + damage: null + } } - } - }); + }, + this.getUpdatingParts(button) + ); } static async #rerollDamageDice(_, button) { @@ -476,9 +552,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio return acc; }, 0); - this.updatePartyData({ - [`system.tagTeam.members.${memberKey}.rollData`]: rollData - }); + this.updatePartyData( + { + [`system.tagTeam.members.${memberKey}.rollData`]: rollData + }, + this.getUpdatingParts(button) + ); } async getCriticalDamage(damage) { @@ -529,15 +608,18 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio static async #selectRoll(_, button) { const { memberKey } = button.dataset; - this.updatePartyData({ - [`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce( - (acc, [key, member]) => { - acc[key] = { selected: key === memberKey ? !member.selected : false }; - return acc; - }, - {} - ) - }); + this.updatePartyData( + { + [`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce( + (acc, [key, member]) => { + acc[key] = { selected: key === memberKey ? !member.selected : false }; + return acc; + }, + {} + ) + }, + this.getUpdatingParts(button) + ); } async getJoinedRoll({ overrideIsCritical, displayVersion } = {}) { @@ -602,6 +684,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio members: _replace({}) } }, + [], { render: false } ); diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 53c57ec5..05a3177d 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -154,8 +154,13 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.openSection = this.openSection; context.tabs = this._getTabs(this.constructor.TABS); context.config = CONFIG.DH; - if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack') - context.hasBaseDamage = !!this.action.parent.attack; + if (this.action.hasDamage) { + context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length; + + if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack') + context.hasBaseDamage = !!this.action.parent.attack; + } + context.costOptions = this.getCostOptions(); context.getRollTypeOptions = this.getRollTypeOptions(); context.disableOption = this.disableOption.bind(this); @@ -173,7 +178,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) revealed: this.openTrigger === index }; }); - context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length; const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; context.tierOptions = [ @@ -312,8 +316,11 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) const callback = (_, button) => { const data = this.action.toObject(); const type = choices[button.form.elements.type.value].value; - const part = { applyTo: type }; - if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' }; + const part = this.action.schema.fields.damage.fields.parts.element.getInitialValue(); + part.applyTo = type; + if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) + part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial; + data.damage.parts[type] = part; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }; diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index e75e16ab..e15b0391 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -24,15 +24,13 @@ export default class DHActionConfig extends DHActionBaseConfig { const effectData = this._addEffectData.bind(this)(); const data = this.action.toObject(); - const created = await game.system.api.documents.DhActiveEffect.createDialog(effectData, { - parent: this.action.item, - render: false - }); - if (!created) return; + const created = await this.action.item.createEmbeddedDocuments('ActiveEffect', [ + game.system.api.data.activeEffects.BaseEffect.getDefaultObject() + ]); - data.effects.push({ _id: created._id }); + data.effects.push({ _id: created[0]._id }); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); - this.action.item.effects.get(created._id).sheet.render(true); + this.action.item.effects.get(created[0]._id).sheet.render(true); } /** diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 3a993da0..1c738bdb 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -172,6 +172,10 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac }); }); + htmlElement + .querySelector('.stacking-change-checkbox') + ?.addEventListener('change', this.stackingChangeToggle.bind(this)); + htmlElement .querySelector('.armor-change-checkbox') ?.addEventListener('change', this.armorChangeToggle.bind(this)); @@ -244,6 +248,16 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac return partContext; } + stackingChangeToggle(event) { + const stackingFields = this.document.system.schema.fields.stacking.fields; + const systemData = { + stacking: event.target.checked + ? { value: stackingFields.value.initial, max: stackingFields.max.initial } + : null + }; + return this.submit({ updateData: { system: systemData } }); + } + armorChangeToggle(event) { if (event.target.checked) { this.addArmorChange(); diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index 3bc5e716..035041e1 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -49,11 +49,9 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); - - if (this.element) { - this.element.querySelectorAll('.effect-container a').forEach(element => { - element.addEventListener('contextmenu', this.removeEffect.bind(this)); - }); + for (const element of this.element?.querySelectorAll('.effect-container a') ?? []) { + element.addEventListener('click', e => this.#onClickEffect(e)); + element.addEventListener('contextmenu', e => this.#onClickEffect(e, -1)); } } @@ -87,11 +85,21 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica this.render(); } - async removeEffect(event) { + async #onClickEffect(event, delta = 1) { const element = event.target.closest('.effect-container'); const effects = DhEffectsDisplay.getTokenEffects(); const effect = effects.find(x => x.id === element.dataset.effectId); - await effect.delete(); + if (!effect || (delta >= 0 && !effect.system.stacking)) { + return; + } + + const maxValue = effect.system.stacking?.max ?? Infinity; + const newValue = Math.clamp((effect.system.stacking?.value ?? 1) + delta, 0, maxValue); + if (newValue > 0) { + await effect.update({ 'system.stacking.value': newValue }); + } else { + await effect.delete(); + } this.render(); } diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index 9a308667..4e4ec6a4 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -1,56 +1,3 @@ -/** - * @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 { /** * Trigger a context menu event in response to a normal click on a additional options button. diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 58157a85..83fe3f41 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -30,8 +30,8 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { if (!effect.img) continue; const promise = effect === overlayEffect - ? this._drawOverlay(effect.img, effect.tint) - : this._drawEffect(effect.img, effect.tint); + ? this._drawOverlay(effect.img, effect.tint, effect) + : this._drawEffect(effect.img, effect.tint, effect); promises.push( promise.then(e => { if (e) e.zIndex = i; @@ -45,6 +45,39 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { this.renderFlags.set({ refreshEffects: true }); } + /**@inheritdoc */ + async _drawEffect(src, tint, effect) { + if (!src) return; + const tex = await foundry.canvas.loadTexture(src, { fallback: 'icons/svg/hazard.svg' }); + const icon = new PIXI.Sprite(tex); + icon.tint = tint ?? 0xffffff; + + if (effect.system.stacking?.value > 1) { + const stackOverlay = new PIXI.Text(effect.system.stacking.value, { + fill: '#f3c267', + stroke: '#000000', + fontSize: 96, + strokeThickness: 4 + }); + const nrDigits = Math.floor(Math.log10(effect.system.stacking.value)) + 1; + stackOverlay.y = -8; + /* This does not account for 1:s being much less wide than other digits. I don't think it's desired however as it makes it look jumpy */ + stackOverlay.x = icon.width - 8 - nrDigits * 56; + stackOverlay.anchor.set(0, 0); + + icon.addChild(stackOverlay); + } + + return this.effects.addChild(icon); + } + + async _drawOverlay(src, tint, effect) { + const icon = await this._drawEffect(src, tint, effect); + if (icon) icon.alpha = 0.8; + this.effects.overlay = icon ?? null; + return icon; + } + /** * Returns the distance from this token to another token object. * This value is corrected to handle alternate token sizes and other grid types diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 01139b30..1f75d382 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -264,12 +264,20 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel hasSave: this.hasSave, onSave: this.save?.damageMod, isDirect: !!this.damage?.direct, - selectedRollMode: game.settings.get('core', 'rollMode'), + selectedMessageMode: game.settings.get('core', 'messageMode'), data: this.getRollData(), evaluate: this.hasRoll, resourceUpdates: new ResourceUpdateMap(this.actor), targetUuid: this.targetUuid, - ...configOptions + ...configOptions, + skips: { + resources: false, + triggers: false, + createMessage: false, + updateCountdowns: false, + reaction: false, + ...(configOptions.skips ?? {}) + } }; DHBaseAction.applyKeybindings(config); @@ -329,6 +337,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @param {boolean} successCost */ async consume(config, successCost = false) { + config.resourceUpdates = new ResourceUpdateMap(config.actionActor); await this.workflow.get('cost')?.execute(config, successCost); await this.workflow.get('uses')?.execute(config, successCost); diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index 82e96776..4296874c 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -83,7 +83,20 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { initial: CONFIG.DH.GENERAL.range.melee.id, label: 'DAGGERHEART.GENERAL.range' }) - }) + }), + stacking: new fields.SchemaField( + { + value: new fields.NumberField({ + initial: 1, + min: 1, + integer: true, + nullable: false, + label: 'DAGGERHEART.GENERAL.value' + }), + max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' }) + }, + { nullable: true, initial: null } + ) }; } @@ -158,8 +171,10 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { return acc; }, this.parent.actor.system.armor?.system?.armor?.current ?? 0); - const armorData = getScrollTextData(this.parent.actor, { value: newArmorTotal }, 'armor'); - options.scrollingTextData = [armorData]; + if (newArmorTotal !== this.parent.actor.system.armorScore.value) { + const armorData = getScrollTextData(this.parent.actor, { value: newArmorTotal }, 'armor'); + options.scrollingTextData = [armorData]; + } } } diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 1a003e2b..9a4ffc31 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -106,22 +106,11 @@ export default class EffectsField extends fields.ArrayField { } /** - * Apply an Effect to a target or enable it if already on it + * Apply an Effect to a target * @param {object} effect Effect object containing ActiveEffect UUID * @param {object} actor Actor Document */ static async applyEffect(effect, actor) { - const existingEffect = actor.effects.find(e => e.origin === effect.uuid); - if (existingEffect) { - return effect.update( - foundry.utils.mergeObject({ - ...effect.constructor.getInitialDuration(), - disabled: false - }) - ); - } - - // Otherwise, create a new effect on the target const effectData = foundry.utils.mergeObject({ ...(effect.toObject?.() ?? effect), disabled: false, diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 20e4d6f0..ffdc25cd 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -89,6 +89,7 @@ export class ActionField extends foundry.data.fields.ObjectField { /** @override */ _cleanType(value, options, _state) { if (!(typeof value === 'object')) value = {}; + value = super._cleanType(value, options, _state); const cls = this.getModel(value); if (cls) return cls.cleanData(value, options, _state); return value; @@ -309,7 +310,7 @@ export function ActionMixin(Base) { } }; - ChatMessage.applyRollMode(msg, game.settings.get('core', 'rollMode')); + ChatMessage.applyMode(msg, game.settings.get('core', 'messageMode')); cls.create(msg); } } diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs index 5a50525a..ab527967 100644 --- a/module/data/item/consumable.mjs +++ b/module/data/item/consumable.mjs @@ -18,8 +18,7 @@ export default class DHConsumable extends BaseDataItem { const fields = foundry.data.fields; return { ...super.defineSchema(), - consumeOnUse: new fields.BooleanField({ initial: true }), - destroyOnEmpty: new fields.BooleanField({ initial: true }) + consumeOnUse: new fields.BooleanField({ initial: true }) }; } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 8cd3caac..58c0cd02 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -33,7 +33,7 @@ export default class DamageRoll extends DHRoll { static async buildPost(roll, config, message) { const chatMessage = config.source?.message ? ui.chat.collection.get(config.source.message) - : getDocumentClass('ChatMessage').applyRollMode({}, config.rollMode ?? CONST.DICE_ROLL_MODES.PUBLIC); + : getDocumentClass('ChatMessage').applyMode({}, config.rollMode ?? 'public'); if (game.modules.get('dice-so-nice')?.active) { const pool = foundry.dice.terms.PoolTerm.fromRolls( Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index b74adaf3..a5d95cd1 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -117,10 +117,10 @@ export default class DHRoll extends Roll { rolls: [roll] }; - config.selectedRollMode ??= game.settings.get('core', 'rollMode'); + config.selectedMessageMode ??= game.settings.get('core', 'messageMode'); if (roll._evaluated) { - const message = await cls.create(msgData, { rollMode: config.selectedRollMode }); + const message = await cls.create(msgData, { messageMode: config.selectedMessageMode }); if (config.tagTeamSelected) { game.system.api.applications.dialogs.TagTeamDialog.assignRoll(message.speakerActor, message); diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 355dcf32..c52e4cc0 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -111,6 +111,18 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { update.img = 'icons/magic/life/heart-cross-blue.webp'; } + const existingEffect = this.actor.effects.find(x => x.origin === data.origin); + const stacks = Boolean(data.system?.stacking); + if (existingEffect && !stacks) return false; + + if (existingEffect && stacks) { + const incrementedValue = existingEffect.system.stacking.value + 1; + await existingEffect.update({ + 'system.stacking.value': Math.min(incrementedValue, existingEffect.system.stacking.max ?? Infinity) + }); + return false; + } + const statuses = Object.keys(data.statuses ?? {}); const immuneStatuses = statuses.filter( @@ -187,7 +199,10 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { } catch (_) {} } - const evalValue = this.effectSafeEval(itemAbleRollParse(key, parseModel, effect.parent)); + const stackingParsedValue = effect.system.stacking + ? Roll.replaceFormulaData(key, { stacks: effect.system.stacking.value }) + : key; + const evalValue = itemAbleRollParse(stackingParsedValue, parseModel, effect.parent); return evalValue ?? key; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 023beaa0..8105471b 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -122,6 +122,14 @@ export default class DhpActor extends Actor { } } + _onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) { + if (collection === 'effects') { + ui.effectsDisplay.render(); + } + + super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId); + } + async updateLevel(newLevel) { if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return; @@ -771,20 +779,10 @@ export default class DhpActor extends Actor { resources.forEach(r => { if (r.itemId) { const { path, value } = game.system.api.fields.ActionFields.CostField.getItemIdCostUpdate(r); - - if ( - r.key === 'quantity' && - r.target.type === 'consumable' && - value === 0 && - r.target.system.destroyOnEmpty - ) { - r.target.delete(); - } else { - updates.items[r.key] = { - target: r.target, - resources: { [path]: value } - }; - } + updates.items[r.key] = { + target: r.target, + resources: { [path]: value } + }; } else { const valueFunc = (base, resource, baseMax) => { if (resource.clear) return baseMax && base.inverted ? baseMax : 0; diff --git a/module/documents/rollTable.mjs b/module/documents/rollTable.mjs index 50b8fe63..59652f44 100644 --- a/module/documents/rollTable.mjs +++ b/module/documents/rollTable.mjs @@ -76,7 +76,7 @@ export default class DhRollTable extends foundry.documents.RollTable { } async toMessage(results, { roll, messageData = {}, messageOptions = {} } = {}) { - messageOptions.rollMode ??= game.settings.get('core', 'rollMode'); + messageOptions.rollMode ??= game.settings.get('core', 'messageMode'); // Construct chat data messageData = foundry.utils.mergeObject( diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 64e136ee..bf107a42 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -169,70 +169,19 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti } } - this.baseActivate(element, { ...options, html: html }); + this.noOffset = options.noOffset; + super.activate(element, { ...options, html: html }); } - /* Need to pass more options to _setAnchor, so have to copy whole foundry method >_< */ - async baseActivate(element, options) { - let { text, direction, cssClass, locked = false, html, content } = options; - if (content && !html) { - foundry.utils.logCompatibilityWarning( - 'The content option has been deprecated in favor of the html option', - { since: 13, until: 15, once: true } - ); - html = content; - } - - // Deactivate currently active element - this.deactivate(); - // Check if the element still exists in the DOM. - if (!document.body.contains(element)) return; - // Mark the new element as active - this.#active = true; - this.element = element; - element.setAttribute('aria-describedby', 'tooltip'); - html ||= element.dataset.tooltipHtml; - if (html) { - if (typeof html === 'string') this.tooltip.innerHTML = foundry.utils.cleanHTML(html); - else { - this.tooltip.innerHTML = ''; // Clear existing HTML - this.tooltip.appendChild(html); - } - } else { - text ||= element.dataset.tooltipText; - if (text) this.tooltip.textContent = text; - else { - text = element.dataset.tooltip; - // Localized message should be safe - if (game.i18n.has(text)) this.tooltip.innerHTML = game.i18n.localize(text); - else this.tooltip.innerHTML = foundry.utils.cleanHTML(text); - } - } - - // Activate display of the tooltip - this.tooltip.removeAttribute('class'); - this.tooltip.classList.add('active', 'themed', 'theme-dark'); - this.tooltip.showPopover(); - cssClass ??= element.closest('[data-tooltip-class]')?.dataset.tooltipClass; - if (cssClass) this.tooltip.classList.add(...cssClass.split(' ')); - - // Set tooltip position - direction ??= element.closest('[data-tooltip-direction]')?.dataset.tooltipDirection; - if (!direction) direction = this._determineDirection(); - this._setAnchor(direction, options); - - if (locked || element.dataset.hasOwnProperty('locked')) this.lockTooltip(); - } - - _setAnchor(direction, options = {}) { + _setAnchor(direction) { const directions = this.constructor.TOOLTIP_DIRECTIONS; const pad = this.constructor.TOOLTIP_MARGIN_PX; const pos = this.element.getBoundingClientRect(); const { innerHeight, innerWidth } = this.tooltip.ownerDocument.defaultView; const tooltipPadding = 16; - const horizontalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetWidth / 2 - pos.width / 2; - const verticalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetHeight / 2 - pos.height / 2; + const horizontalOffset = this.noOffset ? tooltipPadding : this.tooltip.offsetWidth / 2 - pos.width / 2; + const verticalOffset = this.noOffset ? tooltipPadding : this.tooltip.offsetHeight / 2 - pos.height / 2; const style = {}; switch (direction) { diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 8b9be60c..37293863 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -771,8 +771,7 @@ export function getArmorSources(actor) { const data = rawArmorSources.map(doc => { // Get the origin item. Since the actor is already loaded, it should already be cached // Consider the relative function versions if this causes an issue - const isItem = doc instanceof Item; - const origin = isItem ? doc : doc.origin ? foundry.utils.fromUuidSync(doc.origin) : doc.parent; + const origin = doc.origin ? foundry.utils.fromUuidSync(doc.origin) : doc; return { origin, name: origin.name, diff --git a/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json b/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json index 71018bc9..bd9bfffb 100644 --- a/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json +++ b/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json @@ -29,8 +29,7 @@ "Compendium.daggerheart.beastforms.Item.QFg1hNCEoKVDd9Zo" ], "evolved": { - "mainTraitBonus": 0, - "maximumTier": 1 + "mainTraitBonus": 0 }, "hybrid": { "beastformOptions": 2, diff --git a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json index b5239242..5b770e5d 100644 --- a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json +++ b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json @@ -71,13 +71,17 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "value": 1, + "type": "add", + "value": "@stacks", "priority": null, - "type": "add" + "phase": "initial" } ], "duration": { "type": "shortRest" + }, + "stacking": { + "max": null } }, "disabled": false, diff --git a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json index 0f31f491..b886b079 100644 --- a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json +++ b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json @@ -69,14 +69,18 @@ "changes": [ { "key": "system.evasion", - "value": 2, + "type": "add", + "value": "2 * @stacks", "priority": null, - "type": "add" + "phase": "initial" } ], "duration": { "type": "temporary", "description": "Until the next time an attack succeeds against you.
" + }, + "stacking": { + "max": null } }, "disabled": false, diff --git a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json index 01d88111..6a039bbf 100644 --- a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json +++ b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json @@ -127,7 +127,7 @@ "sort": 3400000, "effects": [ { - "name": "Corroded (1 stack)", + "name": "Corroded", "img": "icons/magic/acid/dissolve-bone-white.webp", "origin": "Compendium.daggerheart.domains.Item.qJaSNTuDfbPVr8Lb", "transfer": false, @@ -139,27 +139,31 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.difficulty", + "type": "add", + "value": "-@stack", + "priority": null, + "phase": "initial" + } + ], + "stacking": { + "max": null + }, + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.difficulty", - "mode": 2, - "value": "-1", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "While a target is Corroded, they gain a −1 penalty to their Difficulty for every 2 Stress you spent. This condition can stack.
", + "description": "While a target is Corroded, they gain a −1 penalty to their Difficulty for every 2 Stress you spent. This condition can stack.
", "tint": "#ffffff", "statuses": [ "corrode" @@ -169,6 +173,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!qJaSNTuDfbPVr8Lb.zB95bjSSdVlApQnR" } ], diff --git a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json index 6f8b481d..c3493aea 100644 --- a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json +++ b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json @@ -42,44 +42,8 @@ "type": "self", "amount": null }, - "name": "Mark 1 Stress", - "img": "icons/magic/control/silhouette-aura-energy.webp", - "range": "self" - }, - "fKY9NcYBwCFwMsgV": { - "type": "effect", - "_id": "fKY9NcYBwCFwMsgV", - "systemPath": "actions", - "description": "You can mark a Stress to gain a bonus to your damage roll equal to twice your Strength.
", - "chatDisplay": true, - "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "stress", - "value": 2, - "step": null, - "consumeOnSuccess": false - } - ], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "effects": [ - { - "_id": "t6SIjQxB6UBUJ98f", - "onSave": false - } - ], - "target": { - "type": "self", - "amount": null - }, - "name": "Mark 2 Stress", - "img": "icons/magic/control/silhouette-aura-energy.webp", + "name": "Mark Stress", + "img": "icons/skills/wounds/injury-face-impact-orange.webp", "range": "self" } }, @@ -94,8 +58,8 @@ "sort": 3400000, "effects": [ { - "name": "Rage Up (1)", - "img": "systems/daggerheart/assets/icons/domains/domain-card/blade.png", + "name": "Rage Up", + "img": "icons/skills/wounds/injury-face-impact-orange.webp", "origin": "Compendium.daggerheart.domains.Item.GRL0cvs96vrTDckZ", "transfer": false, "_id": "bq1MhcmoP6Wo5CXF", @@ -106,33 +70,38 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.bonuses.damage.magical.bonus", + "type": "add", + "value": "2*@system.traits.strength.value*@stacks", + "priority": 21, + "phase": "initial" + }, + { + "key": "system.bonuses.damage.physical.bonus", + "type": "add", + "value": "2*@system.traits.strength.value*@stacks", + "priority": 21, + "phase": "initial" + } + ], + "stacking": { + "max": 2 + }, + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.bonuses.damage.magical.bonus", - "mode": 2, - "value": "2*@system.traits.strength.value", - "priority": 21 - }, - { - "key": "system.bonuses.damage.physical.bonus", - "mode": 2, - "value": "2*@system.traits.strength.value", - "priority": 21 - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "For your next attack you have a bonus to your damage roll equal to twice your Strength.
", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -140,6 +109,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!GRL0cvs96vrTDckZ.bq1MhcmoP6Wo5CXF" }, { @@ -155,31 +134,28 @@ "type": "withinRange", "target": "hostile", "range": "melee" - } - }, - "changes": [ - { - "key": "system.bonuses.damage.magical.bonus", - "mode": 2, - "value": "4*@system.traits.strength.value", - "priority": 21 }, - { - "key": "system.bonuses.damage.physical.bonus", - "mode": 2, - "value": "4*@system.traits.strength.value", - "priority": 21 - } - ], + "changes": [ + { + "key": "system.bonuses.damage.magical.bonus", + "value": "4*@system.traits.strength.value", + "priority": 21, + "type": "add" + }, + { + "key": "system.bonuses.damage.physical.bonus", + "value": "4*@system.traits.strength.value", + "priority": 21, + "type": "add" + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "", "tint": "#ffffff", @@ -189,6 +165,9 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!GRL0cvs96vrTDckZ.t6SIjQxB6UBUJ98f" } ], diff --git a/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json b/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json index 7c2d113d..c1833e87 100644 --- a/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json +++ b/src/packs/items/consumables/consumable_Acidpaste_cfVFmS8vT9dbq9s1.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json index 58efc3cc..d78986cf 100644 --- a/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json +++ b/src/packs/items/consumables/consumable_Armor_Stitcher_VlbsCjvvLNfTzNXb.json @@ -48,7 +48,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json index f19c55c3..e034976a 100644 --- a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json +++ b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json b/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json index 9c6403e1..1282ceeb 100644 --- a/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json +++ b/src/packs/items/consumables/consumable_Blinding_Orb_eAXHdzA5qNPldOpn.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json index 6a84b89b..df40e563 100644 --- a/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json +++ b/src/packs/items/consumables/consumable_Blood_of_the_Yorgi_pDGzmczoTlKGmKgd.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json index c6241a9c..421acdc3 100644 --- a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json +++ b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json b/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json index 4240ae2e..04532b27 100644 --- a/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json +++ b/src/packs/items/consumables/consumable_Bonding_Honey_PfQvqopXgvroBklL.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json b/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json index a80481d5..b81e7f36 100644 --- a/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json +++ b/src/packs/items/consumables/consumable_Bridge_Seed_RrIasiMCt6mqVTps.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json b/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json index f4ec4476..28a13044 100644 --- a/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json +++ b/src/packs/items/consumables/consumable_Channelstone_IKMVQ6VwtapwoUim.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json index 48303854..f1d7b058 100644 --- a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json +++ b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json b/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json index 159a1381..7c93342e 100644 --- a/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json +++ b/src/packs/items/consumables/consumable_Circle_of_the_Void_elsyP6VhHw1JjGSl.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json index 9523cdce..2c6b9a93 100644 --- a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json +++ b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json index 1a7ae1a5..5fbae976 100644 --- a/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json +++ b/src/packs/items/consumables/consumable_Death_Tea_xDnJeF1grkmKck8Q.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json index 2a4c6ac9..5e877f6d 100644 --- a/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json +++ b/src/packs/items/consumables/consumable_Dragonbloom_Tea_wM18PWWW2Ami4fBG.json @@ -89,7 +89,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json b/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json index c37b8bb4..cb2c7997 100644 --- a/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json +++ b/src/packs/items/consumables/consumable_Dripfang_Poison_eU8VpbWB2NHIL47n.json @@ -71,7 +71,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json index 02ffa6d9..bff70126 100644 --- a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json +++ b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json b/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json index b23f091b..159e0442 100644 --- a/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json +++ b/src/packs/items/consumables/consumable_Feast_of_Xuria_aX6NyxkNzu0LcJpt.json @@ -134,7 +134,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json index dbf5b19a..9b5bd0df 100644 --- a/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json +++ b/src/packs/items/consumables/consumable_Featherbone_DpxEMpwfasEBpORU.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json index e69c440c..890c61e4 100644 --- a/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json +++ b/src/packs/items/consumables/consumable_Gill_Salve_Nvbb9mze6o5D0AEg.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json index 75d19c32..70291404 100644 --- a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json +++ b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json b/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json index 44114455..833e7055 100644 --- a/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json +++ b/src/packs/items/consumables/consumable_Growing_Potion_fl2f3ees8RFMze9t.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json b/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json index bc82daa2..7365b375 100644 --- a/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json +++ b/src/packs/items/consumables/consumable_Health_Potion_Aruc2NLutWuVIjP1.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json b/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json index 8ee6f171..00eb03f1 100644 --- a/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json +++ b/src/packs/items/consumables/consumable_Homet_s_Secret_Potion_VSwa1LpQ9PjZKsWF.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json b/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json index 809ad08e..2d83ed52 100644 --- a/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json +++ b/src/packs/items/consumables/consumable_Hopehold_Flare_EhaQCPJ8oiqpRIwB.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json b/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json index b39db9b5..707a395f 100644 --- a/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json +++ b/src/packs/items/consumables/consumable_Improved_Arcane_Shard_nQTo6mNoPTEVBtkm.json @@ -91,7 +91,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json index 904c4675..560644b3 100644 --- a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json +++ b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json b/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json index 3e716230..c8e6e75b 100644 --- a/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json +++ b/src/packs/items/consumables/consumable_Jar_of_Lost_Voices_yUol6M5b8jsbk9za.json @@ -71,7 +71,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json b/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json index dec72da8..c1b5eee1 100644 --- a/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json +++ b/src/packs/items/consumables/consumable_Jumping_Root_c2putn9apuurJhWX.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json b/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json index 19f273ed..9685a44f 100644 --- a/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json +++ b/src/packs/items/consumables/consumable_Knowledge_Stone_nL9IALzm9BNi5oSt.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json b/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json index 25cb8fb3..5e95fe66 100644 --- a/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json +++ b/src/packs/items/consumables/consumable_Major_Arcane_Shard_AA7bmiwv00lshPrC.json @@ -89,7 +89,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json index 6090c631..b27fee91 100644 --- a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json +++ b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json index 98329c78..95cd6c92 100644 --- a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json +++ b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json index aebb9435..c7e22aeb 100644 --- a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json +++ b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json index 76d4ea3c..1dabf6c6 100644 --- a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json +++ b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json index 9bbd4290..5a9a2d28 100644 --- a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json +++ b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json b/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json index 843249de..67be0b08 100644 --- a/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json +++ b/src/packs/items/consumables/consumable_Major_Health_Potion_cM7pHe8bBAxSZ2xR.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json b/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json index ebe07a1f..703c2cbe 100644 --- a/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json +++ b/src/packs/items/consumables/consumable_Major_Stamina_Potion_I4cQ03xbxnc81EGa.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json b/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json index cfcee96b..dbfe2dcc 100644 --- a/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json +++ b/src/packs/items/consumables/consumable_Major_Stride_Potion_yK6eEDUrsPbZA8G0.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json b/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json index bac0f425..fe86bf95 100644 --- a/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json +++ b/src/packs/items/consumables/consumable_Minor_Health_Potion_tPfKtKRRjv8qdSqy.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json b/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json index 79a29e8b..625167e1 100644 --- a/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json +++ b/src/packs/items/consumables/consumable_Minor_Stamina_Potion_b6vGSPFWOlzZZDLO.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json b/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json index ba3a3f17..7adaf3e6 100644 --- a/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json +++ b/src/packs/items/consumables/consumable_Mirror_of_Marigold_UFQVwgYOUZ88UxcH.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json b/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json index 68965de6..90dd4fdc 100644 --- a/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json +++ b/src/packs/items/consumables/consumable_Morphing_Clay_f1NHVSIHJJCIOaBl.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json index e281399e..342bf60b 100644 --- a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json +++ b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json b/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json index fdd98249..59e51e80 100644 --- a/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json +++ b/src/packs/items/consumables/consumable_Ogre_Musk_qr1bosjFcUfuwq4B.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json index ba2f2b30..ddff33f0 100644 --- a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json +++ b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json index 654470db..69edf2df 100644 --- a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json +++ b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json b/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json index c2176ca0..702d10ca 100644 --- a/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json +++ b/src/packs/items/consumables/consumable_Replication_Parchment_yJkwz4AP6yhGo8Vj.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json b/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json index ed0e233e..a31a13f1 100644 --- a/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json +++ b/src/packs/items/consumables/consumable_Shrinking_Potion_HGixKenQwhyRAYNk.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json b/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json index 942f449e..d66ff42b 100644 --- a/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json +++ b/src/packs/items/consumables/consumable_Sleeping_Sap_XZavUVlHEvE2srEt.json @@ -85,7 +85,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json b/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json index 1ae34797..506cb1cf 100644 --- a/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json +++ b/src/packs/items/consumables/consumable_Snap_Powder_cg6VtQ0eVZjDdcK0.json @@ -85,7 +85,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json b/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json index 3affdb8e..f464ee61 100644 --- a/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json +++ b/src/packs/items/consumables/consumable_Stamina_Potion_hf3k1POoVSooJyN2.json @@ -84,7 +84,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json index be977305..3351c24f 100644 --- a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json +++ b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json @@ -71,7 +71,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json index 94baa901..76d43d33 100644 --- a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json +++ b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json b/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json index 2669a49b..cfe0a63c 100644 --- a/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json +++ b/src/packs/items/consumables/consumable_Sun_Tree_Sap_kwexUzdM9wm1Qums.json @@ -64,7 +64,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json b/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json index c17d6514..84663fb4 100644 --- a/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json +++ b/src/packs/items/consumables/consumable_Sweet_Moss_GrDrRqWgv7gvl9vn.json @@ -159,7 +159,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 62, diff --git a/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json b/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json index a5890ab0..b6f18539 100644 --- a/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json +++ b/src/packs/items/consumables/consumable_Unstable_Arcane_Shard_mUepnLbkvFk0ha4Z.json @@ -91,7 +91,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json b/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json index 27e83aa4..7e2f0c08 100644 --- a/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json +++ b/src/packs/items/consumables/consumable_Varik_Leaves_hvy5BkG3F6iOIXTx.json @@ -85,7 +85,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json b/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json index 9de41467..b956477d 100644 --- a/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json +++ b/src/packs/items/consumables/consumable_Vial_of_Darksmoke_Nwv5ydGf0MWnzq1n.json @@ -40,7 +40,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json b/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json index 195a46de..3fc572fd 100644 --- a/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json +++ b/src/packs/items/consumables/consumable_Vial_of_Moondrip_VqEX5YwK5oL3r1t6.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 60, diff --git a/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json b/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json index 7196b296..1bdd4bc0 100644 --- a/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json +++ b/src/packs/items/consumables/consumable_Wingsprout_n10vozlmosVR6lo4.json @@ -45,7 +45,6 @@ } }, "consumeOnUse": true, - "destroyOnEmpty": true, "attribution": { "source": "Daggerheart SRD", "page": 61, diff --git a/styles/less/dialog/tag-team-dialog/sheet.less b/styles/less/dialog/tag-team-dialog/sheet.less index e72b4956..dc8f16dc 100644 --- a/styles/less/dialog/tag-team-dialog/sheet.less +++ b/styles/less/dialog/tag-team-dialog/sheet.less @@ -1,4 +1,212 @@ .daggerheart.dialog.dh-style.views.tag-team-dialog { + .team-container { + display: flex; + gap: 16px; + margin-bottom: 16px; + + .team-member-container { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 8px; + flex: 1; + + &.select-padding { + padding-bottom: 64px; + } + + &.inactive { + opacity: 0.4; + pointer-events: none; + } + + .data-container { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; + } + + .member-info { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + + img { + height: 64px; + border-radius: 6px; + border: 1px solid light-dark(@dark-blue, @golden); + } + + .member-name { + flex: 1; + text-align: center; + font-size: var(--font-size-18); + } + } + + .roll-setup { + width: 100%; + } + + .roll-container { + display: flex; + flex-direction: column; + } + + .roll-title { + font-size: var(--font-size-20); + font-weight: bold; + color: light-dark(@dark-blue, @golden); + text-align: center; + display: flex; + align-items: center; + gap: 8px; + + &::before, + &::after { + color: light-dark(@dark-blue, @golden); + content: ''; + flex: 1; + height: 2px; + } + + &::before { + background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%); + } + + &::after { + background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%); + } + } + + .roll-tools { + display: flex; + gap: 4px; + align-items: center; + + img { + height: 16px; + } + + a { + display: flex; + font-size: 16px; + + &:hover { + text-shadow: none; + filter: drop-shadow(0 0 8px var(--golden)); + } + } + } + + .roll-data { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + + &.hope { + --text-color: @golden; + --bg-color: @golden-40; + } + + &.fear { + --text-color: @chat-blue; + --bg-color: @chat-blue-40; + } + + &.critical { + --text-color: @chat-purple; + --bg-color: @chat-purple-40; + } + + .duality-label { + color: var(--text-color); + font-size: var(--font-size-20); + font-weight: bold; + text-align: center; + + .unused-damage { + text-decoration: line-through; + } + } + + .roll-dice-container { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 8px; + + .roll-dice { + position: relative; + display: flex; + align-items: center; + justify-content: center; + + .dice-label { + position: absolute; + color: white; + font-size: 1rem; + paint-order: stroke fill; + -webkit-text-stroke: 2px black; + } + + img { + height: 32px; + } + } + + .roll-operator { + font-size: var(--font-size-24); + } + + .roll-value { + font-size: 18px; + } + } + + .roll-total { + background: var(--bg-color); + color: var(--text-color); + border-radius: 4px; + padding: 3px; + } + } + } + } + + .roll-selection { + position: relative; + top: -80px; + + &.rendered { + margin-bottom: -56px; + } + + .roll-selection-container { + display: flex; + + .select-roll-button { + margin-top: 8px; + flex: 1; + display: flex; + justify-content: center; + + i { + color: light-dark(@dark-blue, @golden); + font-size: 48px; + + &.inactive { + opacity: 0.4; + } + } + } + } + } + .tag-team-roll-container { display: flex; flex-direction: column; @@ -9,193 +217,6 @@ pointer-events: none; } - .team-container { - display: flex; - gap: 16px; - - .member-container { - display: flex; - flex-direction: column; - justify-content: space-between; - gap: 8px; - flex: 1; - - &.inactive { - opacity: 0.4; - pointer-events: none; - } - - .data-container { - display: flex; - flex-direction: column; - gap: 8px; - width: 100%; - } - - .member-info { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - - img { - height: 64px; - border-radius: 6px; - border: 1px solid light-dark(@dark-blue, @golden); - } - - .member-name { - flex: 1; - text-align: center; - font-size: var(--font-size-18); - } - } - - .roll-setup { - width: 100%; - } - - .roll-container { - display: flex; - flex-direction: column; - } - - .roll-title { - font-size: var(--font-size-20); - font-weight: bold; - color: light-dark(@dark-blue, @golden); - text-align: center; - display: flex; - align-items: center; - gap: 8px; - - &::before, - &::after { - color: light-dark(@dark-blue, @golden); - content: ''; - flex: 1; - height: 2px; - } - - &::before { - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%); - } - - &::after { - background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%); - } - } - - .roll-tools { - display: flex; - gap: 4px; - align-items: center; - - img { - height: 16px; - } - - a { - display: flex; - font-size: 16px; - - &:hover { - text-shadow: none; - filter: drop-shadow(0 0 8px var(--golden)); - } - } - } - - .roll-data { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - - &.hope { - --text-color: @golden; - --bg-color: @golden-40; - } - - &.fear { - --text-color: @chat-blue; - --bg-color: @chat-blue-40; - } - - &.critical { - --text-color: @chat-purple; - --bg-color: @chat-purple-40; - } - - .duality-label { - color: var(--text-color); - font-size: var(--font-size-20); - font-weight: bold; - text-align: center; - - .unused-damage { - text-decoration: line-through; - } - } - - .roll-dice-container { - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - gap: 8px; - - .roll-dice { - position: relative; - display: flex; - align-items: center; - justify-content: center; - - .dice-label { - position: absolute; - color: white; - font-size: 1rem; - paint-order: stroke fill; - -webkit-text-stroke: 2px black; - } - - img { - height: 32px; - } - } - - .roll-operator { - font-size: var(--font-size-24); - } - - .roll-value { - font-size: 18px; - } - } - - .roll-total { - background: var(--bg-color); - color: var(--text-color); - border-radius: 4px; - padding: 3px; - } - } - - .select-roll-button { - margin-top: 8px; - - i { - color: light-dark(@dark-blue, @golden); - font-size: 48px; - - &.inactive { - opacity: 0.4; - } - } - } - } - } - .results-container { display: flex; flex-direction: column; @@ -243,9 +264,9 @@ grid-column: span 2; } } + } - .hint { - text-align: center; - } + .hint { + text-align: center; } } diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index c33557b1..793c8164 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -293,6 +293,20 @@ } } + &.optional, + &.one-column.optional { + padding-top: 0; + padding-bottom: 4px; + min-height: auto; + row-gap: 0; + + legend { + display: flex; + align-items: center; + padding-left: 3px; + } + } + .list-w-img { padding: 5px; label { @@ -469,6 +483,10 @@ &.even { grid-template-columns: 1fr 1fr; } + + &.full-width { + width: 100%; + } } .three-columns { diff --git a/styles/less/sheets/activeEffects/activeEffects.less b/styles/less/sheets/activeEffects/activeEffects.less index b6fa98db..afa23b72 100644 --- a/styles/less/sheets/activeEffects/activeEffects.less +++ b/styles/less/sheets/activeEffects/activeEffects.less @@ -46,16 +46,6 @@ } .armor-change-container { - padding-top: 0; - padding-bottom: 4px; - row-gap: 0; - - legend { - display: flex; - align-items: center; - padding-left: 3px; - } - header { padding: 0; left: -0.25rem; // TODO: Find why this header is offset 0.25rem to the right so this can be removed. diff --git a/styles/less/ui/effects-display/sheet.less b/styles/less/ui/effects-display/sheet.less index 1331b094..17d9889f 100644 --- a/styles/less/ui/effects-display/sheet.less +++ b/styles/less/ui/effects-display/sheet.less @@ -35,6 +35,21 @@ color: @golden; filter: drop-shadow(0 0 3px black); } + + .stacking-value { + position: absolute; + top: 4px; + right: 4px; + font-size: 16px; + font-weight: bold; + font-variant-numeric: tabular-nums; + color: @golden; + background: black; + padding: 1px; + border-radius: 6px; + border: 1px solid @golden; + pointer-events: none; + } } } } diff --git a/styles/less/ux/tooltip/bordered-tooltip.less b/styles/less/ux/tooltip/bordered-tooltip.less index b3a5ed29..abec93b7 100644 --- a/styles/less/ux/tooltip/bordered-tooltip.less +++ b/styles/less/ux/tooltip/bordered-tooltip.less @@ -6,6 +6,7 @@ .daggerheart.dh-style.tooltip { display: flex; flex-direction: column; + align-items: start; text-align: start; width: 100%; gap: 5px; @@ -13,6 +14,7 @@ border-radius: 3px; .tooltip-header { + width: 100%; display: flex; flex-direction: column; align-items: center; @@ -35,12 +37,48 @@ } } - .close-hint { - border-radius: 3px; - padding: 3px; - background: @rustic-brown-80; - color: @golden; - font-size: 12px; + .effect-stacks-outer-container { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + + .effect-stacks-title { + font-size: var(--font-size-20); + font-weight: bold; + text-align: center; + } + + .effect-stacks-container { + display: flex; + justify-content: space-between; + + .effect-stacks-inner-container { + display: flex; + flex-direction: column; + gap: 2px; + + .effect-stack-title { + font-weight: bold; + } + } + } + } + + .close-hints { + margin-top: 0.5rem; + display: flex; + flex-direction: column; + gap: 4px; + + .close-hint { + border-radius: 3px; + padding: 3px; + background: @rustic-brown-80; + color: @golden; + font-size: 12px; + margin: 0; + } } .duration-container { diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 6f159a2d..9e7c2884 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -16,50 +16,52 @@ {{formField directField value=source.direct name=(concat path "damage.direct") localize=true classes="checkbox"}} {{/unless}} - {{#each source.parts as |dmg key|}} + + {{!-- Handlebars uses Symbol.Iterator to produce index|key. This isn't compatible with our parts object, so we instead use applyTo, which is the same value --}} + {{#each source.parts as |dmg|}}