diff --git a/lang/en.json b/lang/en.json index bf2f0fc3..81cb2d49 100755 --- a/lang/en.json +++ b/lang/en.json @@ -49,25 +49,32 @@ }, "TYPES": { "attack": { - "name": "Attack" + "name": "Attack", + "tooltip": "Offensive Action like weapon attack, spellcast, etc." }, "beastform": { - "name": "Beastform" + "name": "Beastform", + "tooltip": "Shapeshift the user into another form." }, "damage": { - "name": "Damage" + "name": "Damage", + "tooltip": "Direct damage without a roll." }, "effect": { - "name": "Effect" + "name": "Generic", + "tooltip": "Generic action that only display a chat message if nothing is configured." }, "healing": { - "name": "Healing" + "name": "Healing", + "tooltip": "Restore resources to the target. May have a roll configured." }, "macro": { - "name": "Macro" + "name": "Macro", + "tooltip": "Execute a macro by UUID." }, "summon": { - "name": "Summon" + "name": "Summon", + "tooltip": "Create tokens in the scene." } } }, @@ -1465,6 +1472,7 @@ "Appearance": { "FIELDS": { "displayFear": { "label": "Fear Display" }, + "dualityColorScheme": { "label": "Chat Style" }, "showGenericStatusEffects": { "label": "Show Foundry Status Effects" } }, "fearDisplay": { @@ -1545,7 +1553,7 @@ "duality": "Duality Rolls", "diceSoNice": { "title": "Dice So Nice", - "hint": "Coloration of Hope and Fear dice if the Dice So Nice module is used.", + "hint": "Coloration of Duality dice if the Dice So Nice module is used.", "foreground": "Foreground", "background": "Background", "outline": "Outline", diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs index bbffb791..f9b40f3f 100644 --- a/module/applications/dialogs/_module.mjs +++ b/module/applications/dialogs/_module.mjs @@ -7,3 +7,4 @@ export { default as DeathMove } from './deathMove.mjs'; export { default as Downtime } from './downtime.mjs'; export { default as OwnershipSelection } from './ownershipSelection.mjs'; export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs'; +export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs'; diff --git a/module/applications/dialogs/actionSelectionDialog.mjs b/module/applications/dialogs/actionSelectionDialog.mjs new file mode 100644 index 00000000..786b5329 --- /dev/null +++ b/module/applications/dialogs/actionSelectionDialog.mjs @@ -0,0 +1,87 @@ +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +export default class ActionSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(item, event, options = {}) { + super(options); + this.#item = item; + this.#event = event; + } + + /* -------------------------------------------- */ + + /** @override */ + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'dh-style', 'dialog'], + actions: { + choose: ActionSelectionDialog.#onChooseAction + }, + position: { + width: 400 + } + }; + + /* -------------------------------------------- */ + + static PARTS = { + actions: { + template: 'systems/daggerheart/templates/dialogs/actionSelect.hbs' + } + }; + + #item; + + get item() { + return this.#item; + } + + #event; + + get event() { + return this.#event; + } + + #action; + + get action() { + return this.#action ?? null; + } + + /* -------------------------------------------- */ + + /** @override */ + get title() { + return game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectAction'); + } + + /* -------------------------------------------- */ + + /** @inheritDoc */ + async _prepareContext(options) { + const actions = this.#item.system.actionsList, + itemName = this.#item.name; + return { + ...(await super._prepareContext(options)), + actions, + itemName + }; + } + + static async #onChooseAction(event, button) { + const { actionId } = button.dataset; + this.#action = this.#item.system.actionsList.find(a => a._id === actionId); + Object.defineProperty(this.#event, 'shiftKey', { + get() { + return event.shiftKey; + } + }); + this.close(); + } + + static create(item, event, options) { + return new Promise(resolve => { + const dialog = new this(item, event, options); + dialog.addEventListener('close', () => resolve(dialog.action), { once: true }); + dialog.render({ force: true }); + }); + } +} diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 163af03e..d0da7d72 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -64,8 +64,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV context.selectedActivity = this.selectedActivity; context.moveData = this.moveData; - const shortRestMovesSelected = this.#nrSelectedMoves('shortRest'); - const longRestMovesSelected = this.#nrSelectedMoves('longRest'); + const shortRestMovesSelected = this.nrSelectedMoves('shortRest'); + const longRestMovesSelected = this.nrSelectedMoves('longRest'); context.nrChoices = { ...this.nrChoices, shortRest: { @@ -89,7 +89,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV static selectMove(_, target) { const { category, move } = target.dataset; - const nrSelected = this.#nrSelectedMoves(category); + const nrSelected = this.nrSelectedMoves(category); if (nrSelected + this.nrChoices[category].taken >= this.nrChoices[category].max) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves')); @@ -176,7 +176,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV this.render(); } - #nrSelectedMoves(category) { + nrSelectedMoves(category) { return Object.values(this.moveData[category].moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); } } diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 78f067c7..a03bef9a 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -36,10 +36,37 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App } }; + /** @inheritdoc */ + static TABS = { + diceSoNice: { + tabs: [ + { id: 'hope', label: 'DAGGERHEART.GENERAL.hope' }, + { id: 'fear', label: 'DAGGERHEART.GENERAL.fear' }, + { id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }, + { id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Advantage.full' } + ], + initial: 'hope' + } + }; + + changeTab(tab, group, options) { + super.changeTab(tab, group, options); + + this.render(); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.settingFields = this.settings; + context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {}; + context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {}; + context.diceTab = { + key: this.tabGroups.diceSoNice, + source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice], + fields: this.settings.schema.fields.diceSoNice.fields[this.tabGroups.diceSoNice].fields + }; + return context; } @@ -65,4 +92,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App this.close(); } + + _getTabs(tabs) { + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.cssClass = v.active ? 'active' : ''; + } + + return tabs; + } } diff --git a/module/applications/settings/components/settingsActionsView.mjs b/module/applications/settings/components/settingsActionsView.mjs deleted file mode 100644 index f77c5fce..00000000 --- a/module/applications/settings/components/settingsActionsView.mjs +++ /dev/null @@ -1,148 +0,0 @@ -import { actionsTypes } from '../../../data/action/_module.mjs'; -import DHActionConfig from '../../sheets-configs/action-config.mjs'; - -const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; - -export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(resolve, reject, title, name, icon, img, description, actions) { - super({}); - - this.resolve = resolve; - this.reject = reject; - this.viewTitle = title; - this.name = name; - this.icon = icon; - this.img = img; - this.description = description; - this.actions = actions; - } - - get title() { - return this.viewTitle; - } - - static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'setting', 'dh-style'], - position: { width: 440, height: 'auto' }, - actions: { - editImage: this.onEditImage, - addItem: this.addItem, - editItem: this.editItem, - removeItem: this.removeItem, - resetMoves: this.resetMoves, - saveForm: this.saveForm - }, - form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' }, - main: { - template: 'systems/daggerheart/templates/settings/components/action-view.hbs' - }, - footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' } - }; - - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.name = this.name; - context.icon = this.icon; - context.img = this.img; - context.description = this.description; - context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description); - context.actions = this.actions; - - return context; - } - - static async updateData(event, element, formData) { - const { name, icon, description } = foundry.utils.expandObject(formData.object); - this.name = name; - this.icon = icon; - this.description = description; - - this.render(); - } - - static async saveForm(event) { - this.resolve({ - name: this.name, - icon: this.icon, - img: this.img, - description: this.description, - actions: this.actions - }); - this.close(true); - } - - static close(fromSave) { - if (!fromSave) { - this.reject(); - } - - super.close(); - } - - static onEditImage() { - const fp = new foundry.applications.apps.FilePicker.implementation({ - current: this.img, - type: 'image', - callback: async path => { - this.img = path; - this.render(); - }, - top: this.position.top + 40, - left: this.position.left + 10 - }); - return fp.browse(); - } - - async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/actionTypes/actionType.hbs', - { types: CONFIG.DH.ACTIONS.actionTypes } - ), - title = 'Select Action Type', - type = 'form', - data = {}; - return Dialog.prompt({ - title, - label: title, - content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - return data; - }, - rejectClose: false - }); - } - - static async addItem() { - const actionType = await this.selectActionType(); - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, - action = new cls({ - _id: foundry.utils.randomID(), - type: actionType.type, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType.type].name), - ...cls.getSourceConfig(this.document) - }); - - this.actions.push(action); - this.render(); - } - - static async editItem(_, button) { - await new DHActionConfig(this.actions[button.dataset.id]).render(true); - } - - static removeItem(event, button) { - this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id)); - this.render(); - } - - static resetMoves() {} -} diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index d2861595..2aa2660e 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,6 +1,4 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; -import DhSettingsActionView from './components/settingsActionsView.mjs'; - const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { @@ -73,23 +71,21 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli static async editItem(_, target) { const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; - new Promise((resolve, reject) => { - new DhSettingsActionView( - resolve, - reject, - game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'), - move.name, - move.icon, - move.img, - move.description, - move.actions - ).render(true); - }).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id)); + const path = `restMoves.${target.dataset.type}.moves.${target.dataset.id}`; + const editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure( + move, + path, + this.settings + ); + if (!editedMove) return; + + await this.updateAction.bind(this)(editedMove, target.dataset.type, target.dataset.id); } async updateAction(data, type, id) { await this.settings.updateSource({ [`restMoves.${type}.moves.${id}`]: { + actions: data.actions, name: data.name, icon: data.icon, img: data.img, @@ -139,7 +135,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli acc[key] = { ...move, name: game.i18n.localize(move.name), - description: game.i18n.localize(move.description) + description: game.i18n.localize(move.description), + actions: move.actions.map(action => ({ + ...action, + name: game.i18n.localize(action.name) + })) }; return acc; diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index fafb1fcf..49cc74b0 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -1,6 +1,7 @@ export { default as ActionConfig } from './action-config.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; +export { default as DowntimeConfig } from './downtimeConfig.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as DhTokenConfig } from './token-config.mjs'; diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 40c175f9..070db23f 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -2,10 +2,11 @@ import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; const { ApplicationV2 } = foundry.applications.api; export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { - constructor(action) { + constructor(action, sheetUpdate) { super({}); this.action = action; + this.sheetUpdate = sheetUpdate; this.openSection = null; } @@ -171,6 +172,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { const submitData = this._prepareSubmitData(event, formData), data = foundry.utils.mergeObject(this.action.toObject(), submitData); this.action = await this.action.update(data); + + this.sheetUpdate?.(this.action); this.render(); } @@ -236,7 +239,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { if (!this.action.effects) return; const index = button.dataset.index, effectId = this.action.effects[index]._id; - this.constructor.removeElement.bind(this)(event); + this.constructor.removeElement.bind(this)(event, button); this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]); } diff --git a/module/applications/sheets-configs/downtimeConfig.mjs b/module/applications/sheets-configs/downtimeConfig.mjs new file mode 100644 index 00000000..80aab900 --- /dev/null +++ b/module/applications/sheets-configs/downtimeConfig.mjs @@ -0,0 +1,162 @@ +import { actionsTypes } from '../../data/action/_module.mjs'; +import DHActionConfig from './action-config.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DowntimeConfig extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(move, movePath, settings, options) { + super(options); + + this.move = move; + + this.movePath = movePath; + this.actionsPath = `${movePath}.actions`; + this.settings = settings; + } + + get title() { + return game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: 440, height: 'auto' }, + window: { + icon: 'fa-solid fa-gears' + }, + actions: { + editImage: this.onEditImage, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, + saveForm: this.saveForm + }, + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/settings/downtime-config/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' }, + actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' }, + footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' } + }; + + /** @inheritdoc */ + static TABS = { + primary: { + tabs: [{ id: 'main' }, { id: 'actions' }], + initial: 'main', + labelPrefix: 'DAGGERHEART.GENERAL.Tabs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.move = this.move; + context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML( + context.move.description + ); + + return context; + } + + static async updateData(event, element, formData) { + const data = foundry.utils.expandObject(formData.object); + foundry.utils.mergeObject(this.move, data); + + this.render(); + } + + static async saveForm() { + this.close({ submitted: true }); + } + + static onEditImage() { + const fp = new foundry.applications.apps.FilePicker.implementation({ + current: this.img, + type: 'image', + callback: async path => { + this.move.img = path; + this.render(); + }, + top: this.position.top + 40, + left: this.position.left + 10 + }); + return fp.browse(); + } + + async selectActionType() { + return ( + (await foundry.applications.api.DialogV2.input({ + window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') }, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/actionTypes/actionType.hbs', + { types: CONFIG.DH.ACTIONS.actionTypes } + ), + ok: { + label: game.i18n.format('DOCUMENT.Create', { + type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single') + }) + } + })) ?? {} + ); + } + + static async addItem() { + const { type: actionType } = await this.selectActionType(); + if (!actionType) return; + + const cls = actionsTypes[actionType] ?? actionsTypes.attack, + action = new cls( + { + type: actionType, + name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + systemPath: this.actionsPath + }, + { + parent: this.settings + } + ); + + await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + + this.render(); + } + + static async editItem(_, target) { + const actionId = target.dataset.id; + const action = this.move.actions.get(actionId); + await new DHActionConfig(action, async updatedMove => { + await this.settings.updateSource({ [`${this.actionsPath}.${actionId}`]: updatedMove }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + }).render(true); + } + + static async removeItem(_, target) { + await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + } + + static resetMoves() {} + + /** @override */ + _onClose(options = {}) { + if (!options.submitted) this.move = null; + } + + static async configure(move, movePath, settings, options = {}) { + return new Promise(resolve => { + const app = new this(move, movePath, settings, options); + app.addEventListener('close', () => resolve(app.move), { once: true }); + app.render({ force: true }); + }); + } +} diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 4fe4b5e3..9d5f6a45 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -517,9 +517,10 @@ export default class CharacterSheet extends DHBaseActorSheet { const item = getDocFromElement(event.currentTarget); if (!item) return; - const max = item.system.resource.max ? itemAbleRollParse(item.system.resource.max, this.document, item) : null; + const max = event.currentTarget.max ? Number(event.currentTarget.max) : null; const value = max ? Math.min(Number(event.currentTarget.value), max) : event.currentTarget.value; await item.update({ 'system.resource.value': value }); + this.render(); } async updateItemQuantity(event) { @@ -527,6 +528,7 @@ export default class CharacterSheet extends DHBaseActorSheet { if (!item) return; await item.update({ 'system.quantity': event.currentTarget.value }); + this.render(); } async updateArmorMarks(event) { diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 5600ae72..f6fa41f6 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -2,37 +2,44 @@ export const actionTypes = { attack: { id: 'attack', name: 'DAGGERHEART.ACTIONS.TYPES.attack.name', - icon: 'fa-swords' + icon: 'fa-khanda', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.attack.tooltip' }, healing: { id: 'healing', name: 'DAGGERHEART.ACTIONS.TYPES.healing.name', - icon: 'fa-kit-medical' + icon: 'fa-kit-medical', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.healing.tooltip' }, damage: { id: 'damage', name: 'DAGGERHEART.ACTIONS.TYPES.damage.name', - icon: 'fa-bone-break' - }, - summon: { - id: 'summon', - name: 'DAGGERHEART.ACTIONS.TYPES.summon.name', - icon: 'fa-ghost' - }, - effect: { - id: 'effect', - name: 'DAGGERHEART.ACTIONS.TYPES.effect.name', - icon: 'fa-person-rays' - }, - macro: { - id: 'macro', - name: 'DAGGERHEART.ACTIONS.TYPES.macro.name', - icon: 'fa-scroll' + icon: 'fa-heart-crack', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.damage.tooltip' }, beastform: { id: 'beastform', name: 'DAGGERHEART.ACTIONS.TYPES.beastform.name', - icon: 'fa-paw' + icon: 'fa-paw', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.beastform.tooltip' + }, + summon: { + id: 'summon', + name: 'DAGGERHEART.ACTIONS.TYPES.summon.name', + icon: 'fa-ghost', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.summon.tooltip' + }, + effect: { + id: 'effect', + name: 'DAGGERHEART.ACTIONS.TYPES.effect.name', + icon: 'fa-person-rays', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.effect.tooltip' + }, + macro: { + id: 'macro', + name: 'DAGGERHEART.ACTIONS.TYPES.macro.name', + icon: 'fa-scroll', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.macro.tooltip' } }; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 390435b4..a03a0e2a 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -138,8 +138,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-bandage', img: 'icons/magic/life/cross-worn-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'), - actions: [ - { + actions: { + tendToWounds: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'), img: 'icons/magic/life/cross-worn-green.webp', @@ -154,7 +154,7 @@ export const defaultRestOptions = { } } } - ] + } }, clearStress: { id: 'clearStress', @@ -162,8 +162,8 @@ export const defaultRestOptions = { icon: 'fa-regular fa-face-surprise', img: 'icons/magic/perception/eye-ringed-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'), - actions: [ - { + actions: { + clearStress: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'), img: 'icons/magic/perception/eye-ringed-green.webp', @@ -178,7 +178,7 @@ export const defaultRestOptions = { } } } - ] + } }, repairArmor: { id: 'repairArmor', @@ -186,8 +186,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-hammer', img: 'icons/skills/trades/smithing-anvil-silver-red.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'), - actions: [ - { + actions: { + repairArmor: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', @@ -202,7 +202,7 @@ export const defaultRestOptions = { } } } - ] + } }, prepare: { id: 'prepare', @@ -210,7 +210,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'), - actions: [] + actions: {} } }), longRest: () => ({ @@ -220,7 +220,23 @@ export const defaultRestOptions = { icon: 'fa-solid fa-bandage', img: 'icons/magic/life/cross-worn-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'), - actions: [] + actions: { + tendToWounds: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.hitPoints.id, + value: { + custom: { + enabled: true, + formula: '@system.resources.hitPoints.max' + } + } + } + } + } }, clearStress: { id: 'clearStress', @@ -228,7 +244,23 @@ export const defaultRestOptions = { icon: 'fa-regular fa-face-surprise', img: 'icons/magic/perception/eye-ringed-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'), - actions: [] + actions: { + clearStress: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'), + img: 'icons/magic/perception/eye-ringed-green.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.stress.id, + value: { + custom: { + enabled: true, + formula: '@system.resources.stress.max' + } + } + } + } + } }, repairArmor: { id: 'repairArmor', @@ -236,7 +268,23 @@ export const defaultRestOptions = { icon: 'fa-solid fa-hammer', img: 'icons/skills/trades/smithing-anvil-silver-red.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'), - actions: [] + actions: { + repairArmor: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'), + img: 'icons/skills/trades/smithing-anvil-silver-red.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.armorStack.id, + value: { + custom: { + enabled: true, + formula: '@system.armorScore' + } + } + } + } + } }, prepare: { id: 'prepare', @@ -244,7 +292,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'), - actions: [] + actions: {} }, workOnAProject: { id: 'workOnAProject', @@ -252,7 +300,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-diagram-project', img: 'icons/skills/social/thumbsup-approval-like.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'), - actions: [] + actions: {} } }) }; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 9911417b..89d0caeb 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -117,6 +117,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel async use(event, ...args) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); + if (this.chatDisplay) this.toChat(); + let config = this.prepareConfig(event); for (let i = 0; i < this.constructor.extraSchemas.length; i++) { let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]); diff --git a/module/data/action/healingAction.mjs b/module/data/action/healingAction.mjs index b2cdf385..48aa4146 100644 --- a/module/data/action/healingAction.mjs +++ b/module/data/action/healingAction.mjs @@ -28,7 +28,7 @@ export default class DHHealingAction extends DHBaseAction { healing: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[this.healing.applyTo].label) }), roll: formulas, - targets: (data.system?.targets ?? data.targets).filter(t => t.hit), + targets: systemData.targets?.filter(t => t.hit), messageType: 'healing', source: systemData.source, data: this.getRollData(), diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 1e2d8da1..45f6fe39 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -1,5 +1,3 @@ -import DHBaseAction from '../../data/action/baseAction.mjs'; - const fields = foundry.data.fields; export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { @@ -44,7 +42,9 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } } diff --git a/module/data/chat-message/applyEffects.mjs b/module/data/chat-message/applyEffects.mjs index a7fdda69..41b0aaf5 100644 --- a/module/data/chat-message/applyEffects.mjs +++ b/module/data/chat-message/applyEffects.mjs @@ -1,5 +1,3 @@ -import DHBaseAction from "../action/baseAction.mjs"; - export default class DHApplyEffect extends foundry.abstract.TypeDataModel { static defineSchema() { const fields = foundry.data.fields; @@ -27,7 +25,9 @@ export default class DHApplyEffect extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } diff --git a/module/data/chat-message/damageRoll.mjs b/module/data/chat-message/damageRoll.mjs index 6d54b3f7..210cc0fe 100644 --- a/module/data/chat-message/damageRoll.mjs +++ b/module/data/chat-message/damageRoll.mjs @@ -40,7 +40,9 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } } diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 1a297ce5..dd2b4dea 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -170,7 +170,9 @@ export function ActionMixin(Base) { if (!type || !game.system.api.models.actions.actionsTypes[type]) { ({ type } = (await foundry.applications.api.DialogV2.input({ - window: { title: 'Select Action Type' }, + window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') }, + position: { width: 300 }, + classes: ['daggerheart', 'dh-style'], content: await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/actionTypes/actionType.hbs', { types: CONFIG.DH.ACTIONS.actionTypes } @@ -202,11 +204,20 @@ export function ActionMixin(Base) { } async update(updates, options = {}) { - const path = this.inCollection ? `system.${this.systemPath}.${this.id}` : `system.${this.systemPath}`, + const isSetting = !this.parent.parent; + const basePath = isSetting ? this.systemPath : `system.${this.systemPath}`; + const path = this.inCollection ? `${basePath}.${this.id}` : basePath; + let result = null; + if (isSetting) { + await this.parent.updateSource({ [path]: updates }, options); + result = this.parent; + } else { result = await this.item.update({ [path]: updates }, options); + } + return this.inCollection - ? foundry.utils.getProperty(result, `system.${this.systemPath}`).get(this.id) - : foundry.utils.getProperty(result, `system.${this.systemPath}`); + ? foundry.utils.getProperty(result, basePath).get(this.id) + : foundry.utils.getProperty(result, basePath); } delete() { diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs index 0b1113fb..5fde9394 100644 --- a/module/data/fields/actorField.mjs +++ b/module/data/fields/actorField.mjs @@ -8,7 +8,7 @@ const attributeField = label => const resourceField = (max = 0, label, reverse = false) => new fields.SchemaField({ - value: new fields.NumberField({ initial: 0, integer: true, label }), + value: new fields.NumberField({ initial: 0, min: 0, integer: true, label }), max: new fields.NumberField({ initial: max, integer: true }), isReversed: new fields.BooleanField({ initial: reverse }) }); diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index d8b4c687..ccdf3c0b 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -13,7 +13,8 @@ export default class DhAppearance extends foundry.abstract.DataModel { dualityColorScheme: new fields.StringField({ required: true, choices: DualityRollColor, - initial: DualityRollColor.normal.value + initial: DualityRollColor.normal.value, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.dualityColorScheme.label' }), diceSoNice: new fields.SchemaField({ hope: new fields.SchemaField({ diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 008cd73c..5619898e 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; +import { ActionsField } from '../fields/actionField.mjs'; export default class DhHomebrew extends foundry.abstract.DataModel { static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Homebrew']; // Doesn't work for some reason @@ -61,7 +62,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { base64: false }), description: new fields.HTMLField(), - actions: new fields.ArrayField(new fields.ObjectField()) + actions: new ActionsField() }), { initial: defaultRestOptions.longRest() } ) @@ -78,7 +79,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { base64: false }), description: new fields.HTMLField(), - actions: new fields.ArrayField(new fields.ObjectField()) + actions: new ActionsField() }), { initial: defaultRestOptions.shortRest() } ) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 2fa34b63..67a5e0b3 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -568,13 +568,7 @@ export default class DhpActor extends Actor { } convertDamageToThreshold(damage) { - return damage >= this.system.damageThresholds.severe - ? 3 - : damage >= this.system.damageThresholds.major - ? 2 - : damage >= this.system.damageThresholds.minor - ? 1 - : 0; + return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1; } convertStressDamageToHP(resources) { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 4bad4fdc..a05a7ff0 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -1,3 +1,5 @@ +import ActionSelectionDialog from '../applications/dialogs/actionSelectionDialog.mjs'; + /** * Override and extend the basic Item implementation. * @extends {foundry.documents.Item} @@ -94,46 +96,16 @@ export default class DHItem extends foundry.documents.Item { }); } - async selectActionDialog(prevEvent) { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/dialogs/actionSelect.hbs', - { - actions: this.system.actionsList, - itemName: this.name - } - ), - title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectAction'); - - return foundry.applications.api.DialogV2.prompt({ - window: { title }, - classes: ['daggerheart', 'dh-style'], - content, - ok: { - label: title, - callback: (event, button, dialog) => { - Object.defineProperty(prevEvent, 'shiftKey', { - get() { - return event.shiftKey; - } - }); - return this.system.actionsList.find(a => a._id === button.form.elements.actionId.value); - } - } - }); - } - async use(event) { const actions = new Set(this.system.actionsList); if (actions?.size) { let action = actions.first(); if (actions.size > 1 && !event?.shiftKey) { // Actions Choice Dialog - action = await this.selectActionDialog(event); + action = await ActionSelectionDialog.create(this, event); } if (action) return action.use(event); } - - return this.toChat(); } async toChat(origin) { diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 9e769d2e..751b2c38 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -48,8 +48,8 @@ export default class RegisterHandlebarsHelpers { static rollParsed(value, actor, item, numerical) { const isNumerical = typeof numerical === 'boolean' ? numerical : false; - const result = itemAbleRollParse(value, actor, item); - return isNumerical && !result ? 0 : result; + const result = itemAbleRollParse(value, actor.getRollData(), item); + return isNumerical ? (!result ? 0 : Number(result)) : result; } static setVar(name, value, context) { diff --git a/styles/less/dialog/actions/action-list.less b/styles/less/dialog/actions/action-list.less index 1f405b5d..4011d124 100644 --- a/styles/less/dialog/actions/action-list.less +++ b/styles/less/dialog/actions/action-list.less @@ -1,18 +1,62 @@ @import '../../utils/fonts.less'; .application.daggerheart.dh-style { - .actions-list { + .actions-list, + .action-buttons-list { display: flex; flex-direction: column; - gap: 10px; .action-item { display: flex; align-items: center; gap: 5px; + } + } + + .action-buttons-list { + gap: 10px; + button { + flex: 1; + display: flex; + padding: 6px 40px 6px 6px; + height: fit-content; + img { + width: 30px; + } + span { + font-family: @font-body; + flex: 1; + font-weight: bold; + } + } + } + + .actions-list { + gap: 5px; + + .action-item { + &:hover { + background-color: rgba(255, 255, 255, 0.05); + } + padding: 5px; + border-radius: 5px; + transition: background-color 0.3s ease-in-out; .label { + display: flex; + align-items: center; + gap: 10px; font-family: @font-body; + cursor: pointer; + flex: 1; + i { + text-align: center; + width: 30px; + } + } + + input[type='radio'] { + margin-left: auto; } } } diff --git a/styles/less/dialog/damage-reduction/damage-reduction-container.less b/styles/less/dialog/damage-reduction/damage-reduction-container.less index 25ca5ff9..7dab4f5d 100644 --- a/styles/less/dialog/damage-reduction/damage-reduction-container.less +++ b/styles/less/dialog/damage-reduction/damage-reduction-container.less @@ -46,10 +46,6 @@ display: flex; gap: 2px; - &:not(:last-child) { - margin-right: 8px; - } - .mark-container { cursor: pointer; border: 1px solid light-dark(@dark-blue, @golden); @@ -62,6 +58,10 @@ justify-content: center; opacity: 0.4; + &:not(:last-child) { + margin-right: 8px; + } + &.selected { opacity: 1; } diff --git a/styles/less/global/elements.css b/styles/less/global/elements.css deleted file mode 100644 index 5c9d36d1..00000000 --- a/styles/less/global/elements.css +++ /dev/null @@ -1,618 +0,0 @@ -@keyframes glow { - 0% { - box-shadow: 0 0 1px 1px #f3c267; - } - 100% { - box-shadow: 0 0 2px 2px #f3c267; - } -} -@keyframes glow-dark { - 0% { - box-shadow: 0 0 1px 1px #18162e; - } - 100% { - box-shadow: 0 0 2px 2px #18162e; - } -} -@font-face { - font-family: 'Cinzel'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/cinzel/v25/8vIU7ww63mVu7gtR-kwKxNvkNOjw-tbnTYo.ttf) format('truetype'); -} -@font-face { - font-family: 'Cinzel'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/cinzel/v25/8vIU7ww63mVu7gtR-kwKxNvkNOjw-jHgTYo.ttf) format('truetype'); -} -@font-face { - font-family: 'Cinzel Decorative'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/cinzeldecorative/v18/daaHSScvJGqLYhG8nNt8KPPswUAPniZoaelD.ttf) - format('truetype'); -} -@font-face { - font-family: 'Montserrat'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Ew-.ttf) format('truetype'); -} -@font-face { - font-family: 'Montserrat'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu170w-.ttf) format('truetype'); -} -.application.sheet.daggerheart.dh-style h1 { - font-family: 'Cinzel Decorative', serif; - margin: 0; - border: none; - font-weight: normal; -} -.application.sheet.daggerheart.dh-style h2, -.application.sheet.daggerheart.dh-style h3 { - font-family: 'Cinzel', serif; - margin: 0; - border: none; - font-weight: normal; -} -.application.sheet.daggerheart.dh-style h4 { - font-family: 'Montserrat', sans-serif; - font-size: 14px; - border: none; - font-weight: 700; - margin: 0; - text-shadow: none; - color: #f3c267; - font-weight: normal; -} -.application.sheet.daggerheart.dh-style h5 { - font-size: 14px; - color: #f3c267; - margin: 0; - font-weight: normal; -} -.application.sheet.daggerheart.dh-style p, -.application.sheet.daggerheart.dh-style span { - font-family: 'Montserrat', sans-serif; -} -.application.sheet.daggerheart.dh-style small { - font-family: 'Montserrat', sans-serif; - opacity: 0.8; -} -.application.dh-style { - border: 1px solid light-dark(#18162e, #f3c267); -} -.application.dh-style input[type='text'], -.application.dh-style input[type='number'] { - background: light-dark(transparent, transparent); - border-radius: 6px; - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); - backdrop-filter: blur(9.5px); - -webkit-backdrop-filter: blur(9.5px); - outline: none; - color: light-dark(#18162e, #f3c267); - border: 1px solid light-dark(#222, #efe6d8); -} -.application.dh-style input[type='text']:hover[type='text'], -.application.dh-style input[type='number']:hover[type='text'], -.application.dh-style input[type='text']:hover[type='number'], -.application.dh-style input[type='number']:hover[type='number'], -.application.dh-style input[type='text']:focus[type='text'], -.application.dh-style input[type='number']:focus[type='text'], -.application.dh-style input[type='text']:focus[type='number'], -.application.dh-style input[type='number']:focus[type='number'] { - background: light-dark(rgba(0, 0, 0, 0.05), rgba(24, 22, 46, 0.33)); - box-shadow: none; - outline: 2px solid light-dark(#222, #efe6d8); -} -.application.dh-style input[type='text']:disabled[type='text'], -.application.dh-style input[type='number']:disabled[type='text'], -.application.dh-style input[type='text']:disabled[type='number'], -.application.dh-style input[type='number']:disabled[type='number'] { - outline: 2px solid transparent; - cursor: not-allowed; -} -.application.dh-style input[type='text']:disabled[type='text']:hover, -.application.dh-style input[type='number']:disabled[type='text']:hover, -.application.dh-style input[type='text']:disabled[type='number']:hover, -.application.dh-style input[type='number']:disabled[type='number']:hover { - background: transparent; -} -.application.dh-style input[type='checkbox']:checked::after, -.application.dh-style input[type='radio']:checked::after { - color: light-dark(#222, #f3c267); -} -.application.dh-style input[type='checkbox']:checked::before, -.application.dh-style input[type='radio']:checked::before { - color: light-dark(#22222240, #f3c26740); -} -.application.dh-style input[type='checkbox']::before, -.application.dh-style input[type='radio']::before { - color: light-dark(#22222240, #f3c26740); -} -.application.dh-style button { - background: light-dark(transparent, #f3c267); - border: 1px solid light-dark(#18162e, #18162e); - color: light-dark(#18162e, #18162e); - outline: none; - box-shadow: none; -} -.application.dh-style button:hover { - background: light-dark(rgba(0, 0, 0, 0.3), #18162e); - color: light-dark(#18162e, #f3c267); -} -.application.dh-style button.glow { - animation: glow 0.75s infinite alternate; -} -.application.dh-style button:disabled { - background: light-dark(transparent, #f3c267); - color: light-dark(#18162e, #18162e); - opacity: 0.6; - cursor: not-allowed; -} -.application.dh-style button:disabled:hover { - background: light-dark(transparent, #f3c267); - color: light-dark(#18162e, #18162e); -} -.application.dh-style select { - background: light-dark(transparent, transparent); - color: light-dark(#222, #efe6d8); - font-family: 'Montserrat', sans-serif; - outline: 2px solid transparent; - border: 1px solid light-dark(#222, #efe6d8); -} -.application.dh-style select:focus, -.application.dh-style select:hover { - outline: 2px solid light-dark(#222, #efe6d8); - box-shadow: none; -} -.application.dh-style select option, -.application.dh-style select optgroup { - color: #efe6d8; - background-color: #18162e; - border-radius: 6px; -} -.application.dh-style select:disabled { - opacity: 0.6; - outline: 2px solid transparent; - cursor: not-allowed; -} -.application.dh-style multi-select { - position: relative; - height: 34px; -} -.application.dh-style multi-select .tags { - justify-content: flex-start; - margin: 4px; - height: inherit; -} -.application.dh-style multi-select .tags .tag { - padding: 0.3rem 0.5rem; - color: light-dark(#18162e, #f3c267); - background-color: light-dark(#18162e10, #f3c26740); - font-family: 'Montserrat', sans-serif; - border-radius: 3px; - transition: 0.13s ease-out; - gap: 0.5rem; - z-index: 1; -} -.application.dh-style multi-select .tags .tag .remove { - font-size: 10px; -} -.application.dh-style multi-select select { - position: absolute; - height: inherit; - outline: initial; -} -.application.dh-style p { - margin: 0; -} -.application.dh-style ul { - margin: 0; - padding: 0; - list-style: none; -} -.application.dh-style li { - margin: 0; -} -.application.dh-style a:hover, -.application.dh-style a.active { - font-weight: bold; - text-shadow: 0 0 8px light-dark(#18162e, #f3c267); -} -.application.dh-style fieldset { - align-items: center; - margin-top: 5px; - border-radius: 6px; - border-color: light-dark(#18162e, #f3c267); -} -.application.dh-style fieldset.glassy { - background-color: light-dark(#18162e10, #f3c26710); - border-color: transparent; -} -.application.dh-style fieldset.glassy legend { - padding: 2px 12px; - border-radius: 3px; - background-color: light-dark(#18162e, #f3c267); - color: light-dark(#efe6d8, #18162e); -} -.application.dh-style fieldset.fit-height { - height: 95%; -} -.application.dh-style fieldset.flex { - display: flex; - gap: 20px; -} -.application.dh-style fieldset.flex.wrap { - flex-wrap: wrap; - gap: 10px 20px; -} -.application.dh-style fieldset.flex .inline-child { - flex: 1; -} -.application.dh-style fieldset .list-w-img { - padding: 5px; -} -.application.dh-style fieldset .list-w-img label { - flex: 1; -} -.application.dh-style fieldset .list-w-img img { - width: 2rem; - height: 2rem; -} -.application.dh-style fieldset.one-column { - display: flex; - flex-direction: column; - align-items: start; - gap: 10px; - min-height: 64px; - flex: 1; -} -.application.dh-style fieldset.one-column > .one-column { - width: 100%; -} -.application.dh-style fieldset.two-columns { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 10px; -} -.application.dh-style fieldset.two-columns.even { - grid-template-columns: 1fr 1fr; -} -.application.dh-style fieldset.two-columns .full-width { - grid-column: span 2; -} -.application.dh-style fieldset legend { - font-family: 'Montserrat', sans-serif; - font-weight: bold; - color: light-dark(#18162e, #f3c267); -} -.application.dh-style fieldset input[type='text'], -.application.dh-style fieldset input[type='number'] { - color: light-dark(#222, #efe6d8); - font-family: 'Montserrat', sans-serif; - transition: all 0.3s ease; - outline: 2px solid transparent; -} -.application.dh-style fieldset input[type='text']:focus, -.application.dh-style fieldset input[type='number']:focus, -.application.dh-style fieldset input[type='text']:hover, -.application.dh-style fieldset input[type='number']:hover { - outline: 2px solid light-dark(#222, #efe6d8); -} -.application.dh-style fieldset[disabled], -.application.dh-style fieldset.child-disabled .form-group, -.application.dh-style fieldset select[disabled], -.application.dh-style fieldset input[disabled] { - opacity: 0.5; -} -.application.dh-style fieldset.child-disabled .form-group { - pointer-events: none; -} -.application.dh-style fieldset .nest-inputs { - display: flex; - align-items: center; - width: 100%; - gap: 5px; -} -.application.dh-style fieldset .nest-inputs .btn { - padding-top: 15px; -} -.application.dh-style fieldset .nest-inputs .image { - height: 40px; - width: 40px; - object-fit: cover; - border-radius: 6px; - border: none; -} -.application.dh-style fieldset .nest-inputs > .checkbox { - align-self: end; -} -.application.dh-style fieldset .form-group { - width: 100%; -} -.application.dh-style fieldset .form-group label { - font-family: 'Montserrat', sans-serif; - font-weight: bold; - font-size: smaller; -} -.application.dh-style fieldset .form-group.checkbox { - width: fit-content; - display: flex; - align-items: center; -} -.application.dh-style fieldset .form-group.checkbox .form-fields { - height: 32px; - align-content: center; -} -.application.dh-style fieldset:has(.list-w-img) { - gap: 0; -} -.application.dh-style .two-columns { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 10px; -} -.application.dh-style .two-columns.even { - grid-template-columns: 1fr 1fr; -} -.application.dh-style line-div { - display: block; - height: 1px; - width: 100%; - border-bottom: 1px solid light-dark(#18162e, #f3c267); - mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%); -} -.application.dh-style side-line-div { - display: block; - height: 1px; - width: 100%; - border-bottom: 1px solid light-dark(#18162e, #f3c267); - mask-image: linear-gradient(270deg, transparent 0%, black 100%); -} -.application.dh-style side-line-div.invert { - mask-image: linear-gradient(270deg, black 0%, transparent 100%); -} -.application.dh-style .item-description { - opacity: 1; - transform: translateY(0); - grid-column: 1/-1; - transition: - opacity 0.3s ease-out, - transform 0.3s ease-out; -} -.application.dh-style .item-description.invisible { - height: 0; - opacity: 0; - overflow: hidden; - transform: translateY(-20px); - transform-origin: top; -} -.application.dh-style .item-buttons { - grid-column: span 3; - display: flex; - gap: 8px; - flex-wrap: wrap; -} -.application.dh-style .item-buttons button { - white-space: nowrap; -} -.application.setting.dh-style fieldset h2, -.application.setting.dh-style fieldset h3, -.application.setting.dh-style fieldset h4 { - margin: 8px 0 4px; - text-align: center; -} -.application.setting.dh-style fieldset .title-hint { - font-size: 12px; - font-variant: small-caps; - text-align: center; -} -.application.setting.dh-style fieldset .field-section .split-section { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; -} -.application.setting.dh-style fieldset .label-container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; -} -.application.setting.dh-style fieldset .label-container label { - align-self: center; - text-align: center; -} -.application.setting.dh-style footer { - margin-top: 8px; - display: flex; - gap: 8px; -} -.application.setting.dh-style footer button { - flex: 1; -} -.application.setting.dh-style .form-group { - display: flex; - justify-content: space-between; - align-items: center; -} -.application.setting.dh-style .form-group label { - font-size: 16px; - font-family: 'Montserrat', sans-serif; -} -.application.setting.dh-style .form-group .form-fields { - display: flex; - gap: 4px; - align-items: center; -} -.system-daggerheart .tagify { - background: light-dark(transparent, transparent); - border: 1px solid light-dark(#222, #efe6d8); - height: 34px; - --tags-disabled-bg: none; - --tags-border-color: none; - --tags-hover-border-color: none; - --tags-focus-border-color: none; - --tag-border-radius: 3px; - --tag-bg: light-dark(#18162e, #f3c267); - --tag-remove-btn-color: light-dark(#18162e, #f3c267); - --tag-hover: light-dark(#18162e, #f3c267); - --tag-text-color: light-dark(#efe6d8, #222); - --tag-text-color--edit: light-dark(#efe6d8, #222); - --tag-pad: 0.3em 0.5em; - --tag-inset-shadow-size: 1.2em; - --tag-invalid-color: #d39494; - --tag-invalid-bg: rgba(211, 148, 148, 0.5); - --tag--min-width: 1ch; - --tag--max-width: 100%; - --tag-hide-transition: 0.3s; - --tag-remove-bg: light-dark(#18162e40, #f3c26740); - --tag-remove-btn-color: light-dark(#efe6d8, #222); - --tag-remove-btn-bg: none; - --tag-remove-btn-bg--hover: light-dark(#efe6d8, #222); - --input-color: inherit; - --placeholder-color: light-dark(#efe6d815, #22222215); - --placeholder-color-focus: light-dark(#efe6d815, #22222215); - --loader-size: 0.8em; - --readonly-striped: 1; - border-radius: 3px; - margin-right: 1px; -} -.system-daggerheart .tagify tag div { - display: flex; - justify-content: space-between; - align-items: center; - height: 22px; -} -.system-daggerheart .tagify tag div span { - font-weight: 400; -} -.system-daggerheart .tagify tag div img { - margin-left: 8px; - height: 20px; - width: 20px; -} -.system-daggerheart .tagify__dropdown { - border: 1px solid light-dark(#222, #efe6d8) !important; - font-family: 'Montserrat', sans-serif; - color: light-dark(#222, #efe6d8); -} -.system-daggerheart .tagify__dropdown .tagify__dropdown__wrapper { - background-image: url(../assets/parchments/dh-parchment-dark.png); - background-color: transparent; - border: 0; - color: light-dark(#222, #efe6d8); -} -.system-daggerheart .tagify__dropdown .tagify__dropdown__wrapper .tagify__dropdown__item--active { - background-color: light-dark(#222, #efe6d8); - color: light-dark(#efe6d8, #222); -} -.system-daggerheart.theme-light .tagify__dropdown .tagify__dropdown__wrapper { - background-image: url(../assets/parchments/dh-parchment-light.png); -} -.theme-light .application.sheet.dh-style button.glow { - animation: glow-dark 0.75s infinite alternate; -} -.theme-light .application .component.dh-style.card-preview-container { - background-image: url('../assets/parchments/dh-parchment-light.png'); -} -.theme-light .application .component.dh-style.card-preview-container .preview-text-container { - background-image: url(../assets/parchments/dh-parchment-dark.png); -} -.theme-light .application .component.dh-style.card-preview-container .preview-selected-icon-container { - background-image: url(../assets/parchments/dh-parchment-dark.png); - color: var(--color-light-5); -} -.application .component.dh-style.card-preview-container { - position: relative; - border-radius: 6px; - border: 2px solid var(--color-tabs-border); - display: flex; - flex-direction: column; - aspect-ratio: 0.75; - background-image: url('../assets/parchments/dh-parchment-dark.png'); -} -.application .component.dh-style.card-preview-container.selectable { - cursor: pointer; -} -.application .component.dh-style.card-preview-container.disabled { - pointer-events: none; - opacity: 0.4; -} -.application .component.dh-style.card-preview-container .preview-image-outer-container { - position: relative; - display: flex; - align-items: center; - justify-content: center; -} -.application .component.dh-style.card-preview-container .preview-image-container { - flex: 1; - border-radius: 4px 4px 0 0; -} -.application .component.dh-style.card-preview-container .preview-text-container { - flex: 1; - border-radius: 0 0 4px 4px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: 18px; - text-align: center; - color: var(--color-text-selection-bg); - background-image: url(../assets/parchments/dh-parchment-light.png); -} -.application .component.dh-style.card-preview-container .preview-empty-container { - pointer-events: none; - position: relative; - display: flex; - align-items: center; - justify-content: center; - flex: 1; -} -.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container { - width: 100%; - display: flex; - justify-content: center; -} -.application - .component.dh-style.card-preview-container - .preview-empty-container - .preview-empty-inner-container - .preview-add-icon { - font-size: 48px; -} -.application - .component.dh-style.card-preview-container - .preview-empty-container - .preview-empty-inner-container - .preview-empty-subtext { - position: absolute; - top: 10%; - font-size: 18px; - font-variant: small-caps; - text-align: center; -} -.application .component.dh-style.card-preview-container .preview-selected-icon-container { - position: absolute; - height: 54px; - width: 54px; - border-radius: 50%; - border: 2px solid; - font-size: 48px; - display: flex; - align-items: center; - justify-content: center; - background-image: url(../assets/parchments/dh-parchment-light.png); - color: var(--color-dark-5); -} -.application .component.dh-style.card-preview-container .preview-selected-icon-container i { - position: relative; - right: 2px; -} diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 4c319ed5..dda82362 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -97,6 +97,19 @@ color: light-dark(@dark-blue, @dark-blue); } } + + &.reverted { + background: light-dark(@dark-blue-10, @golden-10); + color: light-dark(@dark-blue, @golden); + border: 1px solid light-dark(@dark, transparent); + &:hover { + background: light-dark(transparent, @golden); + color: light-dark(@dark-blue, @dark-blue); + } + img { + border-radius: 3px; + } + } } select { diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 077ae165..452d997b 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -23,7 +23,7 @@ width: 100%; list-style-type: none; - &:hover { + &:not(.single-img):hover { .inventory-item-header .img-portait { .roll-img { opacity: 1; diff --git a/styles/less/global/tab-form-footer.less b/styles/less/global/tab-form-footer.less index 14794204..bd084ee8 100644 --- a/styles/less/global/tab-form-footer.less +++ b/styles/less/global/tab-form-footer.less @@ -1,7 +1,15 @@ -.sheet.daggerheart.dh-style { +.daggerheart.dh-style { .tab-form-footer { display: flex; - padding: 0 10px; + gap: 8px; + + &.spaced { + margin-top: 8px; + } + + &.padded { + padding: 0 10px; + } button { flex: 1; diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 580c79ca..0ba6f094 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -29,6 +29,7 @@ } .settings-items { + width: 100%; display: flex; flex-direction: column; gap: 8px; diff --git a/templates/actionTypes/actionType.hbs b/templates/actionTypes/actionType.hbs index 1cd912e9..5dbe63c0 100644 --- a/templates/actionTypes/actionType.hbs +++ b/templates/actionTypes/actionType.hbs @@ -5,8 +5,11 @@