diff --git a/.editorconfig b/.editorconfig index aa391e00..6cfef2fc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,5 @@ [*] indent_size = 4 indent_style = spaces -end_of_line = lf [*.yml] indent_size = 2 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 56ce8818..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -* text=auto eol=lf -*.json text eol=lf diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4ffcc64d..553a1a17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -37,7 +37,6 @@ jobs: url: https://github.com/${{github.repository}} manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip - flags.hotReload: false # Create a zip file with all files required by the module to add to the release - run: zip -r ./system.zip system.json README.md LICENSE build/daggerheart.js build/tagify.css styles/daggerheart.css assets/ templates/ packs/ lang/ diff --git a/.gitignore b/.gitignore index e41a67d3..9a22c0ce 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ Build build foundry styles/daggerheart.css -styles/daggerheart.css.map diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..6de9e5d0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "trailingComma": "none", + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "consistent", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 120, + "endOfLine": "lf", + "bracketSameLine": true +} diff --git a/README.md b/README.md index ac3666b3..f59143fd 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,6 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart Looking to contribute to the project? Look no further, check out our [contributing guide](CONTRIBUTING.md), and keep the [Code of Conduct](coc.md) in mind when working on things. -## AI Policy - -The Foundryborne Daggerheart system does not make use of AI (generative or otherwise) for any area of its implementation. We expect all contributors to follow this same policy when contributing with a pull request; contributions made using AI will be rejected outright. - ## Disclaimer: **Daggerheart System** diff --git a/assets/icons/documents/actors/drama-masks.svg b/assets/icons/documents/actors/drama-masks.svg deleted file mode 100644 index 84307da0..00000000 --- a/assets/icons/documents/actors/drama-masks.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/daggerheart.d.ts b/daggerheart.d.ts index 7ff7fd59..ab754b17 100644 --- a/daggerheart.d.ts +++ b/daggerheart.d.ts @@ -1,11 +1,8 @@ import '@client/global.mjs'; -import '@common/global.mjs'; -import '@common/primitives/global.mjs'; import Canvas from '@client/canvas/board.mjs'; // Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such // This declare global hopefully fixes that -// Note: eslint is not aware of these, whatever is added here should go in the eslint's globals list declare global { /** * A simple event framework used throughout Foundry Virtual Tabletop. @@ -15,28 +12,9 @@ declare global { class Hooks extends foundry.helpers.Hooks {} const fromUuid = foundry.utils.fromUuid; const fromUuidSync = foundry.utils.fromUuidSync; - /** - * A representation of a color in hexadecimal format. - * This class provides methods for transformations and manipulations of colors. - */ - class Color extends foundry.utils.Color {} + /** * The singleton game canvas */ const canvas: Canvas; - - const ActiveEffect: foundry.documents.ActiveEffect; - const Actor: foundry.documents.Actor; - const BaseScene: foundry.documents.BaseScene; - const ChatMessage: foundry.documents.ChatMessage; - const Combat: foundry.documents.Combat; - const Combatant: foundry.documents.Combatant; - const Item: foundry.documents.Item; - const Macro: foundry.documents.Macro; - const Scene: foundry.documents.Scene; - const TokenDocument: foundry.documents.TokenDocument; - - const Collection: foundry.utils.Collection; - const FormDataExtended: foundry.applications.ux.FormDataExtended; - const TextEditor: foundry.applications.ux.TextEditor; } diff --git a/daggerheart.mjs b/daggerheart.mjs index 7bfdf874..363430be 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -196,11 +196,6 @@ Hooks.once('init', () => { makeDefault: true, label: sheetLabel('TYPES.Actor.environment') }); - Actors.registerSheet(SYSTEM.id, applications.sheets.actors.NPC, { - types: ['npc'], - makeDefault: true, - label: sheetLabel('TYPES.Actor.npc') - }); Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, { types: ['party'], makeDefault: true, @@ -446,33 +441,3 @@ Hooks.on('canvasTearDown', canvas => { Hooks.on('canvasReady', canas => { game.system.registeredTriggers.registerSceneTriggers(canvas.scene); }); - -/** Make the user to select a document type, instead of having a default doc type for them to accidentally keep */ -Hooks.on('renderDialogV2', (_dialog, html) => { - if (!html.classList.contains('dialog')) return; - const cls = html.classList.contains('item-create') - ? documents.DHItem.implementation - : html.classList.contains('actor-create') - ? documents.DhpActor.implementation - : null; - if (!cls) return; - - const form = html.querySelector('form'); - const submit = html.querySelector('button[type=submit]'); - const select = html.querySelector('select[name=type]'); - const nameInput = html.querySelector('input[name=name]'); - if (!form || !select || !submit || !nameInput) return; - - nameInput.placeholder = cls.defaultName({}); - const emptyOption = document.createElement('option'); - emptyOption.value = ''; - emptyOption.selected = true; - select.required = true; - select.prepend(emptyOption); - submit.addEventListener('click', event => { - if (!form.reportValidity()) { - event.preventDefault(); - event.stopPropagation(); - } - }); -}); diff --git a/eslint.config.mjs b/eslint.config.mjs index 3c9b8fd9..ce2bb86f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,101 +1,14 @@ import globals from 'globals'; -import { defineConfig, globalIgnores } from 'eslint/config'; -import tseslint from 'typescript-eslint'; -import js from '@eslint/js'; -import stylistic from '@stylistic/eslint-plugin'; - -/** @type {Partial} */ -export const stylisticRules = { - '@stylistic/indent': [ - 'error', - 4, - { - SwitchCase: 1 - } - ], - '@stylistic/max-len': ['error', { - code: 120, - ignoreComments: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - ignoreRegExpLiterals: true - }], - '@stylistic/quotes': ['error', 'single', { allowTemplateLiterals: 'always' }], - '@stylistic/arrow-parens': ['error', 'as-needed'], - '@stylistic/quote-props': ['error', 'as-needed'], - '@stylistic/array-bracket-newline': ['error', 'consistent'], - '@stylistic/key-spacing': 'error', - '@stylistic/comma-dangle': ['error', 'never'], - '@stylistic/space-in-parens': ['error', 'never'], - '@stylistic/space-infix-ops': 2, - '@stylistic/keyword-spacing': 2, - '@stylistic/semi-spacing': 2, - '@stylistic/no-multi-spaces': 2, - '@stylistic/no-extra-semi': 2, - '@stylistic/no-whitespace-before-property': 2, - '@stylistic/space-unary-ops': 2 -}; +import { defineConfig } from 'eslint/config'; +import prettier from 'eslint-plugin-prettier'; export default defineConfig([ - globalIgnores(['foundry/**/*', 'build/**/*']), - { - files: ['gulpfile.js', 'postcss.config.js'], - languageOptions: { globals: globals.node } - }, + { files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.browser } }, + { plugins: { prettier } }, { files: ['**/*.{js,mjs,cjs}'], - plugins: { - '@stylistic': stylistic - }, - languageOptions: { - globals: { - ...globals.browser, - CONFIG: 'readonly', - CONST: 'readonly', - // Global classes - Color: 'readonly', - Handlebars: 'readonly', - Hooks: 'readonly', - PIXI: 'readonly', - ProseMirror: 'readonly', - Roll: 'readonly', - // global namespaces - canvas: 'readonly', - foundry: 'readonly', - game: 'readonly', - ui: 'readonly', - // global functions - fromUuid: 'readonly', - fromUuidSync: 'readonly', - getDocumentClass: 'readonly', - _del: 'readonly', - _replace: 'readonly', - _loc: 'readonly', - // Documents - ActiveEffect: 'readonly', - Actor: 'readonly', - BaseScene: 'readonly', - ChatMessage: 'readonly', - Combat: 'readonly', - Combatant: 'readonly', - Item: 'readonly', - Macro: 'readonly', - Scene: 'readonly', - TokenDocument: 'readonly', - // Other - Collection: 'readonly', - FormDataExtended: 'readonly', - TextEditor: 'readonly' - } - }, rules: { - 'no-undef': 'error', - // 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], - ...stylisticRules + 'prettier/prettier': 'error' } - }, - { - files: ['**/*.ts'], - extends: [js.configs.recommended, tseslint.configs.recommended] } ]); diff --git a/gulpfile.js b/gulpfile.js index 2e873ced..1a81f5ae 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,15 +1,9 @@ // Less configuration var gulp = require('gulp'); var less = require('gulp-less'); -var sourcemaps = require('gulp-sourcemaps'); gulp.task('less', function (cb) { - gulp.src('styles/daggerheart.less') - .pipe(sourcemaps.init()) - .pipe(less()) - .on('error', console.error.bind(console)) - .pipe(sourcemaps.write('.')) - .pipe(gulp.dest('styles')); + gulp.src('styles/daggerheart.less').pipe(less()).on('error', console.error.bind(console)).pipe(gulp.dest('styles')); cb(); }); diff --git a/jsconfig.json b/jsconfig.json index a0d51d0b..00bab1f5 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "module": "es2022", - "target": "es2022", + "module": "ES6", + "target": "ES6", "paths": { "@client/*": ["./foundry/client/*"], "@common/*": ["./foundry/common/*"] diff --git a/lang/en.json b/lang/en.json index 3a1340e0..b4b1410e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -23,7 +23,6 @@ "companion": "Companion", "adversary": "Adversary", "environment": "Environment", - "npc": "NPC", "party": "Party" } }, @@ -334,11 +333,6 @@ }, "newAdversary": "New Adversary" }, - "NPC": { - "FIELDS": { - "motives": { "label": "Motives" } - } - }, "Party": { "Subtitle": { "character": "{community} {ancestry} | {subclass} {class}", @@ -412,11 +406,7 @@ "giveSpotlight": "Give The Spotlight", "requestingSpotlight": "Requesting The Spotlight", "requestSpotlight": "Request The Spotlight", - "openCountdowns": "Countdowns", - "adversaryCategories": { - "friendly": "Friendly", - "adversaries": "Adversaries" - } + "openCountdowns": "Countdowns" }, "CompendiumBrowserSettings": { "title": "Enable Compendiums", @@ -711,13 +701,19 @@ }, "PendingReactionsDialog": { "title": "Pending Reaction Rolls Found", - "unfinishedRolls": "Some Tokens have not finished their Reaction Rolls.", - "warning": "Unfinished reaction rolls will be considered as failed.", - "confirmation": "Are you sure you want to continue?" + "unfinishedRolls": "Some Tokens still need to roll their Reaction Roll.", + "confirmation": "Are you sure you want to continue ?", + "warning": "Undone reaction rolls will be considered as failed" }, "ReactionRoll": { "title": "Reaction Roll: {trait}" }, + "RerollDialog": { + "title": "Reroll", + "damageTitle": "Reroll Damage", + "deselectDiceNotification": "Deselect one of the selected dice first", + "acceptCurrentRolls": "Accept Current Rolls" + }, "ResourceDice": { "title": "{name} Resource", "rerollDice": "Reroll Dice" @@ -778,9 +774,7 @@ "title": "Group Roll" }, "TokenConfig": { - "actorSizeUsed": "Actor size is set, determining the dimensions", - "tokenSize": "Token Size", - "sizeCategory": "Size Category" + "actorSizeUsed": "Actor size is set, determining the dimensions" } }, "CLASS": { @@ -2448,7 +2442,6 @@ "single": "Miss", "plural": "Miss" }, - "missingX": "Missing {x}", "maxWithThing": "Max {thing}", "missingDragDropThing": "Drop {thing} here", "multiclass": "Multiclass", @@ -2539,9 +2532,6 @@ "recovery": { "label": "Recovery" }, "type": { "label": "Type" }, "value": { "label": "Value" } - }, - "identifier": { - "label": "Identifier" } }, "Ancestry": { @@ -2567,11 +2557,10 @@ "tokenImg": { "label": "Token Image" }, "tokenRingImg": { "label": "Subject Texture" }, "tokenSize": { - "placeholder": "Token Size", - "disabledPlaceholder": "Token Size", + "placeholder": "Using character dimensions", + "disabledPlaceholder": "Set by character size", "height": { "label": "Height" }, "width": { "label": "Width" }, - "depth": { "label": "Depth" }, "scale": { "label": "Token Scale" } }, "evolved": { @@ -2833,15 +2822,6 @@ "hideObserverPermissionInChat": { "label": "Hide Chat Info From Players", "hint": "Information such as hit/miss on attack rolls against adversaries will be hidden" - }, - "hidePartyStats": { - "label": "Hide Party Stats", - "hint": "Resources and stats in the party sheet's member list will be hidden to the following users, even if the user is part of the same party", - "choices": { - "never": "Never, always show", - "players": "Hide From Players", - "always": "Hide from Everyone" - } } } }, @@ -3097,7 +3077,6 @@ } }, "ChatLog": { - "rerollActionRoll": "Reroll Action", "rerollDamage": "Reroll Damage", "assignTagRoll": "Assign as Tag Roll" }, @@ -3231,6 +3210,7 @@ "subclassesAlreadyPresent": "You already have a class and multiclass subclass", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice", "gmMenuRefresh": "You refreshed all actions and resources {types}", + "subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class.", "gmRequired": "This action requires an online GM", "gmOnly": "This can only be accessed by the GM", "noActorOwnership": "You do not have permissions for this character", @@ -3240,8 +3220,7 @@ "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used", "knowTheTide": "Know The Tide gained a token", "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}", - "noTokenTargeted": "No token is targeted", - "behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM" + "noTokenTargeted": "No token is targeted" }, "Progress": { "migrationLabel": "Performing system migration. Please wait and do not close Foundry." diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index 517f95da..936bb79d 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -154,8 +154,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : this.tabGroups.primary !== 'equipment' - ? v.active - : false; + ? v.active + : false; v.cssClass = v.active ? 'active' : ''; switch (v.id) { @@ -211,9 +211,9 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl context.suggestedTraits = this.setup.class.system ? Object.keys(this.setup.class.system.characterGuide.suggestedTraits).map(traitKey => { - const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey]; - return `${game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`; - }) + const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey]; + return `${game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`; + }) : []; context.traits = { values: Object.keys(this.setup.traits).map(traitKey => { @@ -439,18 +439,15 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl 'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null } }; - if (type === 'subclasses') { - const classItem = this.setup.class; - const uuid = classItem?._stats.compendiumSource ?? classItem?.uuid; + if (type === 'subclasses') presets.filter = { - 'system.linkedClass': { key: 'system.linkedClass', value: uuid } + 'system.linkedClass.uuid': { key: 'system.linkedClass.uuid', value: this.setup.class?.uuid } }; - } if (equipment.includes(type)) presets.filter = { 'system.tier': { key: 'system.tier', value: 1 }, - type: { key: 'type', value: type } + 'type': { key: 'type', value: type } }; ui.compendiumBrowser.open(presets); @@ -613,8 +610,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl [foundry.utils.randomID()]: {} }; } else if (item.type === 'subclass' && event.target.closest('.subclass-card')) { - const classSubclasses = await this.setup.class.system.fetchSubclasses(); - if (classSubclasses.every(subclass => subclass.uuid !== item.uuid)) { + if (this.setup.class.system.subclasses.every(subclass => subclass.uuid !== item.uuid)) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass')); return; } diff --git a/module/applications/dialogs/CompendiumBrowserSettings.mjs b/module/applications/dialogs/CompendiumBrowserSettings.mjs index b0d83fb3..bef54a6f 100644 --- a/module/applications/dialogs/CompendiumBrowserSettings.mjs +++ b/module/applications/dialogs/CompendiumBrowserSettings.mjs @@ -50,7 +50,7 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi const excludedSourceData = this.browserSettings.excludedSources; const excludedPackData = this.browserSettings.excludedPacks; context.typePackCollections = game.packs.reduce((acc, pack) => { - const { type, label, packageType, packageName: basePackageName, name, id } = pack.metadata; + const { type, label, packageType, packageName: basePackageName, id } = pack.metadata; if (!CompendiumBrowserSettings.#browserPackTypes.includes(type)) return acc; const isWorldPack = packageType === 'world'; @@ -68,15 +68,13 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi if (!acc[type].sources[packageName]) acc[type].sources[packageName] = { label: sourceLabel, checked: sourceChecked, packs: [] }; - const included = - !excludedPackData[packageName] || - !excludedPackData[packageName][name]?.excludedDocumentTypes.includes(type); + const checked = !excludedPackData[id] || !excludedPackData[id].excludedDocumentTypes.includes(type); acc[type].sources[packageName].packs.push({ - name, + pack: id, type, label: id === game.system.id ? game.system.title : game.i18n.localize(label), - checked: included + checked: checked }); return acc; @@ -108,16 +106,16 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi toggleTypedPack(event) { event.stopPropagation(); - const { type, source, packName } = event.target.dataset; - const currentlyExcluded = this.browserSettings.excludedPacks[source]?.[packName] - ? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.includes(type) + const { type, pack } = event.target.dataset; + const currentlyExcluded = this.browserSettings.excludedPacks[pack] + ? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.includes(type) : false; - this.browserSettings.excludedPacks[source] ??= {}; - this.browserSettings.excludedPacks[source][packName] ??= { excludedDocumentTypes: [] }; - this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes = currentlyExcluded - ? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.filter(x => x !== type) - : [...(this.browserSettings.excludedPacks[source][packName]?.excludedDocumentTypes ?? []), type]; + if (!this.browserSettings.excludedPacks[pack]) + this.browserSettings.excludedPacks[pack] = { excludedDocumentTypes: [] }; + this.browserSettings.excludedPacks[pack].excludedDocumentTypes = currentlyExcluded + ? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.filter(x => x !== type) + : [...(this.browserSettings.excludedPacks[pack]?.excludedDocumentTypes ?? []), type]; this.render(); } diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs index fc5169b2..c866f1cd 100644 --- a/module/applications/dialogs/_module.mjs +++ b/module/applications/dialogs/_module.mjs @@ -10,6 +10,7 @@ export { default as ImageSelectDialog } from './imageSelectDialog.mjs'; export { default as ItemTransferDialog } from './itemTransfer.mjs'; export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs'; export { default as OwnershipSelection } from './ownershipSelection.mjs'; +export { default as RerollDamageDialog } from './rerollDamageDialog.mjs'; export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs'; export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs'; export { default as TagTeamDialog } from './tagTeamDialog.mjs'; diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 9a98b197..067aa473 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -175,14 +175,14 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.disadvantage = advantage === -1; this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage; - if (this.config.roll.advantage === 0) return this.render(); - const defaultFaces = - this.config.roll.advantage === 1 - ? this.config.data.rules.roll.defaultAdvantageDice - : this.config.data.rules.roll.defaultDisadvantageDice; - const faces = Number.parseInt(defaultFaces); - this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; + if (this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) { + const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice); + this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; + } else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) { + const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice); + this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; + } this.render(); } @@ -196,14 +196,14 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1 ? this.config.costs.filter(x => x.extKey !== button.dataset.key) : [ - ...this.config.costs, - { - extKey: button.dataset.key, - key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope', - value: 1, - name: this.config.data?.system.experiences?.[button.dataset.key]?.name - } - ]; + ...this.config.costs, + { + extKey: button.dataset.key, + key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope', + value: 1, + name: this.config.data?.system.experiences?.[button.dataset.key]?.name + } + ]; this.render(); } @@ -213,8 +213,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config.actionType = this.reactionOverride ? 'reaction' : this.config.actionType === 'reaction' - ? 'action' - : this.config.actionType; + ? 'action' + : this.config.actionType; this.render(); } } diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index e5108e34..b916a5de 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -138,13 +138,13 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap const stressReductionStress = this.availableStressReductions ? stressReductions.reduce((acc, red) => acc + red.cost, 0) : 0; - const stress = this.actor.system.resources.stress; context.stress = selectedStressMarks.length > 0 || this.availableStressReductions ? { - value: stress.value + selectedStressMarks.length + stressReductionStress, - max: stress.max - } + value: + this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress, + max: this.actor.system.resources.stress.max + } : null; context.maxArmorUsed = maxArmorUsed; diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs index 4a949b99..69ff758e 100644 --- a/module/applications/dialogs/deathMove.mjs +++ b/module/applications/dialogs/deathMove.mjs @@ -57,7 +57,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV let returnMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar'); if (config.roll.fate.value <= this.actor.system.levelData.level.current) { - const maxHope = this.actor.system.resources.hope.max + this.actor.system.scars; const newScarAmount = this.actor.system.scars + 1; await this.actor.update({ system: { @@ -65,7 +64,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV } }); - if (newScarAmount >= maxHope) { + if (newScarAmount >= this.actor.system.resources.hope.max) { await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id); return game.i18n.format('DAGGERHEART.UI.Chat.deathMove.journeysEnd', { scars: newScarAmount }); } diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index e209cc3b..989e4625 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -259,10 +259,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV const resetValue = increasing ? 0 : feature.system.resource.max - ? new Roll( - Roll.replaceFormulaData(feature.system.resource.max, this.actor.getRollData()) - ).evaluateSync().total - : 0; + ? new Roll(Roll.replaceFormulaData(feature.system.resource.max, this.actor)).evaluateSync().total + : 0; await feature.update({ 'system.resource.value': resetValue }); } diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs index 7196d848..48110e4c 100644 --- a/module/applications/dialogs/groupRollDialog.mjs +++ b/module/applications/dialogs/groupRollDialog.mjs @@ -1,5 +1,5 @@ import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; -import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -106,12 +106,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat const context = await super._prepareContext(_options); context.isGM = game.user.isGM; - context.isEditable = - game.user.isGM || - this.party.system.partyMembers.some(actor => { - const selected = Boolean(this.party.system.groupRoll.participants[actor.id]); - return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER); - }); + context.isEditable = this.getIsEditable(); context.fields = this.party.system.schema.fields.groupRoll.fields; context.data = this.party.system.groupRoll; context.traitOptions = CONFIG.DH.ACTOR.abilities; @@ -167,8 +162,8 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat partContext.groupRoll = { totalLabel: leader?.rollData ? game.i18n.format('DAGGERHEART.GENERAL.withThing', { - thing: leader.roll.totalLabel - }) + thing: leader.roll.totalLabel + }) : null, totalDualityClass: leader?.roll?.isCritical ? 'critical' : leader?.roll?.withHope ? 'hope' : 'fear', total: leaderTotal + modifierTotal, @@ -247,7 +242,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat }); }; - await emitGMUpdate( + await emitAsGM( GMUpdateEvent.UpdateDocument, gmUpdate, update, @@ -270,6 +265,13 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat ]; } + getIsEditable() { + return this.party.system.partyMembers.some(actor => { + const selected = Boolean(this.party.system.groupRoll.participants[actor.id]); + return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER); + }); + } + groupRollRefresh = ({ refreshType, action, parts }) => { if (refreshType !== RefreshType.GroupRoll) return; @@ -356,6 +358,8 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat }); if (!result) return; + // todo: move logic to actor.rollTrait() or actor.diceRoll() + if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); const rollData = result.messageRoll.toJSON(); delete rollData.options.messageRoll; diff --git a/module/applications/dialogs/rerollDamageDialog.mjs b/module/applications/dialogs/rerollDamageDialog.mjs new file mode 100644 index 00000000..b821bd24 --- /dev/null +++ b/module/applications/dialogs/rerollDamageDialog.mjs @@ -0,0 +1,280 @@ +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(message, options = {}) { + super(options); + + this.message = message; + this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => { + const type = message.system.damage[typeKey]; + acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => { + const part = type.parts[partKey]; + acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => { + const dice = part.dice[diceKey]; + const activeResults = dice.results.filter(x => x.active); + acc[diceKey] = { + dice: dice.dice, + selectedResults: activeResults.length, + maxSelected: activeResults.length, + results: activeResults.map(x => ({ ...x, selected: true })) + }; + + return acc; + }, {}); + + return acc; + }, {}); + + return acc; + }, {}); + } + + static DEFAULT_OPTIONS = { + id: 'reroll-dialog', + classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'], + window: { + icon: 'fa-solid fa-dice' + }, + actions: { + toggleResult: RerollDamageDialog.#toggleResult, + selectRoll: RerollDamageDialog.#selectRoll, + doReroll: RerollDamageDialog.#doReroll, + save: RerollDamageDialog.#save + } + }; + + /** @override */ + static PARTS = { + main: { + id: 'main', + template: 'systems/daggerheart/templates/dialogs/rerollDialog/damage/main.hbs' + }, + footer: { + id: 'footer', + template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs' + } + }; + + get title() { + return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.damageTitle'); + } + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + htmlElement.querySelectorAll('.to-reroll-input').forEach(element => { + element.addEventListener('change', this.toggleDice.bind(this)); + }); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.damage = this.damage; + context.disabledReroll = !this.getRerollDice().length; + context.saveDisabled = !this.isSelectionDone(); + + return context; + } + + static async #save() { + const update = { + 'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => { + const type = this.damage[typeKey]; + let typeTotal = 0; + const messageType = this.message.system.damage[typeKey]; + const parts = Object.keys(type).map(partKey => { + const part = type[partKey]; + const messagePart = messageType.parts[partKey]; + let partTotal = messagePart.modifierTotal; + const dice = Object.keys(part).map(diceKey => { + const dice = part[diceKey]; + const total = dice.results.reduce((acc, result) => { + if (result.active) acc += result.result; + return acc; + }, 0); + partTotal += total; + const messageDice = messagePart.dice[diceKey]; + return { + ...messageDice, + total: total, + results: dice.results.map(x => ({ + ...x, + hasRerolls: dice.results.length > 1 + })) + }; + }); + + typeTotal += partTotal; + return { + ...messagePart, + total: partTotal, + dice: dice + }; + }); + + acc[typeKey] = { + ...messageType, + total: typeTotal, + parts: parts + }; + + return acc; + }, {}) + }; + + await this.message.update(update); + await this.close(); + } + + getRerollDice() { + const rerollDice = []; + Object.keys(this.damage).forEach(typeKey => { + const type = this.damage[typeKey]; + Object.keys(type).forEach(partKey => { + const part = type[partKey]; + Object.keys(part).forEach(diceKey => { + const dice = part[diceKey]; + Object.keys(dice.results).forEach(resultKey => { + const result = dice.results[resultKey]; + if (result.toReroll) { + rerollDice.push({ + ...result, + dice: dice.dice, + type: typeKey, + part: partKey, + dice: diceKey, + result: resultKey + }); + } + }); + }); + }); + }); + + return rerollDice; + } + + isSelectionDone() { + const diceFinishedData = []; + Object.keys(this.damage).forEach(typeKey => { + const type = this.damage[typeKey]; + Object.keys(type).forEach(partKey => { + const part = type[partKey]; + Object.keys(part).forEach(diceKey => { + const dice = part[diceKey]; + const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0); + diceFinishedData.push(selected === dice.maxSelected); + }); + }); + }); + + return diceFinishedData.every(x => x); + } + + toggleDice(event) { + const target = event.target; + const { type, part, dice } = target.dataset; + const toggleDice = this.damage[type][part][dice]; + + const existingDiceRerolls = this.getRerollDice().filter( + x => x.type === type && x.part === part && x.dice === dice + ); + + const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length; + + toggleDice.toReroll = !allRerolled; + toggleDice.results.forEach(result => { + if (result.active) { + result.toReroll = !allRerolled; + } + }); + + this.render(); + } + + static #toggleResult(event) { + event.stopPropagation(); + + const target = event.target.closest('.to-reroll-result'); + const { type, part, dice, result } = target.dataset; + const toggleDice = this.damage[type][part][dice]; + const toggleResult = toggleDice.results[result]; + toggleResult.toReroll = !toggleResult.toReroll; + + const existingDiceRerolls = this.getRerollDice().filter( + x => x.type === type && x.part === part && x.dice === dice + ); + + const allToReroll = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length; + toggleDice.toReroll = allToReroll; + + this.render(); + } + + static async #selectRoll(_, button) { + const { type, part, dice, result } = button.dataset; + + const diceVal = this.damage[type][part][dice]; + const diceResult = diceVal.results[result]; + if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) { + return ui.notifications.warn( + game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification') + ); + } + + if (diceResult.active) { + diceVal.toReroll = false; + diceResult.toReroll = false; + } + + diceVal.selectedResults += diceResult.active ? -1 : 1; + diceResult.active = !diceResult.active; + + this.render(); + } + + static async #doReroll() { + const toReroll = this.getRerollDice().map(x => { + const { type, part, dice, result } = x; + const diceData = this.damage[type][part][dice].results[result]; + return { + ...diceData, + dice: this.damage[type][part][dice].dice, + typeKey: type, + partKey: part, + diceKey: dice, + resultsIndex: result + }; + }); + + const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate(); + + if (game.modules.get('dice-so-nice')?.active) { + const diceSoNiceRoll = { + _evaluated: true, + dice: roll.dice, + options: { appearance: {} } + }; + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); + } + + toReroll.forEach((data, index) => { + const { typeKey, partKey, diceKey, resultsIndex } = data; + const rerolledDice = roll.dice[index]; + + const dice = this.damage[typeKey][partKey][diceKey]; + dice.toReroll = false; + dice.results[resultsIndex].active = false; + dice.results[resultsIndex].discarded = true; + dice.results[resultsIndex].toReroll = false; + dice.results.splice(dice.results.length, 0, { + ...rerolledDice.results[0], + toReroll: false, + selected: true + }); + }); + + this.render(); + } +} diff --git a/module/applications/dialogs/rerollDialog.mjs b/module/applications/dialogs/rerollDialog.mjs new file mode 100644 index 00000000..cae4e53a --- /dev/null +++ b/module/applications/dialogs/rerollDialog.mjs @@ -0,0 +1,279 @@ +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +export default class RerollDialog extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(message, options = {}) { + super(options); + + this.message = message; + this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => { + const type = message.system.damage[typeKey]; + acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => { + const part = type.parts[partKey]; + acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => { + const dice = part.dice[diceKey]; + const activeResults = dice.results.filter(x => x.active); + acc[diceKey] = { + dice: dice.dice, + selectedResults: activeResults.length, + maxSelected: activeResults.length, + results: activeResults.map(x => ({ ...x, selected: true })) + }; + + return acc; + }, {}); + + return acc; + }, {}); + + return acc; + }, {}); + } + + static DEFAULT_OPTIONS = { + id: 'reroll-dialog', + classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'], + window: { + icon: 'fa-solid fa-dice' + }, + actions: { + toggleResult: RerollDialog.#toggleResult, + selectRoll: RerollDialog.#selectRoll, + doReroll: RerollDialog.#doReroll, + save: RerollDialog.#save + } + }; + + /** @override */ + static PARTS = { + main: { + id: 'main', + template: 'systems/daggerheart/templates/dialogs/rerollDialog/main.hbs' + }, + footer: { + id: 'footer', + template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs' + } + }; + + get title() { + return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.title'); + } + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + htmlElement.querySelectorAll('.to-reroll-input').forEach(element => { + element.addEventListener('change', this.toggleDice.bind(this)); + }); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.damage = this.damage; + context.disabledReroll = !this.getRerollDice().length; + context.saveDisabled = !this.isSelectionDone(); + + return context; + } + + static async #save() { + const update = { + 'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => { + const type = this.damage[typeKey]; + let typeTotal = 0; + const messageType = this.message.system.damage[typeKey]; + const parts = Object.keys(type).map(partKey => { + const part = type[partKey]; + const messagePart = messageType.parts[partKey]; + let partTotal = messagePart.modifierTotal; + const dice = Object.keys(part).map(diceKey => { + const dice = part[diceKey]; + const total = dice.results.reduce((acc, result) => { + if (result.active) acc += result.result; + return acc; + }, 0); + partTotal += total; + const messageDice = messagePart.dice[diceKey]; + return { + ...messageDice, + total: total, + results: dice.results.map(x => ({ + ...x, + hasRerolls: dice.results.length > 1 + })) + }; + }); + + typeTotal += partTotal; + return { + ...messagePart, + total: partTotal, + dice: dice + }; + }); + + acc[typeKey] = { + ...messageType, + total: typeTotal, + parts: parts + }; + + return acc; + }, {}) + }; + await this.message.update(update); + await this.close(); + } + + getRerollDice() { + const rerollDice = []; + Object.keys(this.damage).forEach(typeKey => { + const type = this.damage[typeKey]; + Object.keys(type).forEach(partKey => { + const part = type[partKey]; + Object.keys(part).forEach(diceKey => { + const dice = part[diceKey]; + Object.keys(dice.results).forEach(resultKey => { + const result = dice.results[resultKey]; + if (result.toReroll) { + rerollDice.push({ + ...result, + dice: dice.dice, + type: typeKey, + part: partKey, + dice: diceKey, + result: resultKey + }); + } + }); + }); + }); + }); + + return rerollDice; + } + + isSelectionDone() { + const diceFinishedData = []; + Object.keys(this.damage).forEach(typeKey => { + const type = this.damage[typeKey]; + Object.keys(type).forEach(partKey => { + const part = type[partKey]; + Object.keys(part).forEach(diceKey => { + const dice = part[diceKey]; + const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0); + diceFinishedData.push(selected === dice.maxSelected); + }); + }); + }); + + return diceFinishedData.every(x => x); + } + + toggleDice(event) { + const target = event.target; + const { type, part, dice } = target.dataset; + const toggleDice = this.damage[type][part][dice]; + + const existingDiceRerolls = this.getRerollDice().filter( + x => x.type === type && x.part === part && x.dice === dice + ); + + const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length; + + toggleDice.toReroll = !allRerolled; + toggleDice.results.forEach(result => { + if (result.active) { + result.toReroll = !allRerolled; + } + }); + + this.render(); + } + + static #toggleResult(event) { + event.stopPropagation(); + + const target = event.target.closest('.to-reroll-result'); + const { type, part, dice, result } = target.dataset; + const toggleDice = this.damage[type][part][dice]; + const toggleResult = toggleDice.results[result]; + toggleResult.toReroll = !toggleResult.toReroll; + + const existingDiceRerolls = this.getRerollDice().filter( + x => x.type === type && x.part === part && x.dice === dice + ); + + const allToReroll = existingDiceRerolls.length === toggleDice.results.length; + toggleDice.toReroll = allToReroll; + + this.render(); + } + + static async #selectRoll(_, button) { + const { type, part, dice, result } = button.dataset; + + const diceVal = this.damage[type][part][dice]; + const diceResult = diceVal.results[result]; + if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) { + return ui.notifications.warn( + game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification') + ); + } + + if (diceResult.active) { + diceVal.toReroll = false; + diceResult.toReroll = false; + } + + diceVal.selectedResults += diceResult.active ? -1 : 1; + diceResult.active = !diceResult.active; + + this.render(); + } + + static async #doReroll() { + const toReroll = this.getRerollDice().map(x => { + const { type, part, dice, result } = x; + const diceData = this.damage[type][part][dice].results[result]; + return { + ...diceData, + dice: this.damage[type][part][dice].dice, + typeKey: type, + partKey: part, + diceKey: dice, + resultsIndex: result + }; + }); + + const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate(); + + if (game.modules.get('dice-so-nice')?.active) { + const diceSoNiceRoll = { + _evaluated: true, + dice: roll.dice, + options: { appearance: {} } + }; + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); + } + + toReroll.forEach((data, index) => { + const { typeKey, partKey, diceKey, resultsIndex } = data; + const rerolledDice = roll.dice[index]; + + const dice = this.damage[typeKey][partKey][diceKey]; + dice.toReroll = false; + dice.results[resultsIndex].active = false; + dice.results[resultsIndex].discarded = true; + dice.results[resultsIndex].toReroll = false; + dice.results.splice(dice.results.length, 0, { + ...rerolledDice.results[0], + toReroll: false, + selected: true + }); + }); + + this.render(); + } +} diff --git a/module/applications/dialogs/resourceDiceDialog.mjs b/module/applications/dialogs/resourceDiceDialog.mjs index 8394538c..32e1e5d8 100644 --- a/module/applications/dialogs/resourceDiceDialog.mjs +++ b/module/applications/dialogs/resourceDiceDialog.mjs @@ -1,4 +1,4 @@ -import { itemAbleRollParse, triggerChatRollFx } from '../../helpers/utils.mjs'; +import { itemAbleRollParse } from '../../helpers/utils.mjs'; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; @@ -69,7 +69,7 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item); const diceFormula = `${max}${this.item.system.resource.dieFaces}`; const roll = await new Roll(diceFormula).evaluate(); - await triggerChatRollFx([roll]); + if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true); this.rollValues = roll.terms[0].results.map(x => ({ value: x.result, used: false })); this.resetUsed = true; diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index b2ce0258..325cc445 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,6 +1,6 @@ import { MemberData } from '../../data/tagTeamData.mjs'; import { getCritDamageBonus } from '../../helpers/utils.mjs'; -import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -38,9 +38,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio tag: 'form', classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'tag-team-dialog'], position: { width: 550, height: 'auto' }, - window: { - icon: 'fa-solid fa-user-group' - }, actions: { toggleSelectMember: TagTeamDialog.#toggleSelectMember, startTagTeamRoll: TagTeamDialog.#startTagTeamRoll, @@ -116,12 +113,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.isEditable = - game.user.isGM || - this.party.system.partyMembers.some(actor => { - const selected = Boolean(this.party.system.tagTeam.members[actor.id]); - return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER); - }); + 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; @@ -184,56 +176,57 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } if (Object.keys(this.party.system.tagTeam.members).includes(partId)) { - const data = await this.#prepareMemberContext(partId); - partContext.hasDamage |= Boolean(data?.damage); - partContext.members[partId] = data; + 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, + roll: data.roll, + 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; } - async #prepareMemberContext(partId) { - const data = this.party.system.tagTeam.members[partId] ?? {}; - const actor = game.actors.get(partId); - if (!actor) console.error(`Failed to get actor ${partId}`); - - const rollOptions = []; - const damageRollOptions = []; - for (const item of actor?.items ?? []) { - if (!item.system.metadata.hasActions) continue; - const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])]; - for (const action of actions) { - if (action.hasRoll) { - const collection = action.hasDamage ? damageRollOptions : rollOptions; - collection.push({ - value: action.uuid, - label: action.name, - group: item.name, - baseAction: action.baseAction - }); - } - } - } - - 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; - - return { - ...data, - roll: data.roll, - 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: await this.getCriticalDamage(damage), - useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) - }; - } - getUpdatingParts(target) { const { initialization, rollSelection, result } = this.constructor.PARTS; const isInitialization = this.tabGroups.application === initialization.id; @@ -266,7 +259,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio }); }; - await emitGMUpdate( + await emitAsGM( GMUpdateEvent.UpdateDocument, gmUpdate, update, @@ -277,6 +270,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio ); } + getIsEditable() { + return this.party.system.partyMembers.some(actor => { + const selected = Boolean(this.party.system.tagTeam.members[actor.id]); + return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER); + }); + } + tagTeamRefresh = ({ refreshType, action, parts }) => { if (refreshType !== RefreshType.TagTeamRoll) return; @@ -431,6 +431,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio if (!result) return; + if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); + const rollData = result.messageRoll.toJSON(); delete rollData.options.messageRoll; this.updatePartyData( @@ -646,50 +648,42 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } async getJoinedRoll({ overrideIsCritical, displayVersion } = {}) { - try { - const memberValues = Object.values(this.party.system.tagTeam.members); - const selectedRoll = memberValues.find(x => x.selected); - const baseMainRoll = selectedRoll ?? memberValues[0]; - const baseSecondaryRoll = selectedRoll - ? memberValues.find(x => !x.selected) - : memberValues.length > 1 - ? memberValues[1] - : null; + const memberValues = Object.values(this.party.system.tagTeam.members); + const selectedRoll = memberValues.find(x => x.selected); + let baseMainRoll = selectedRoll ?? memberValues[0]; + let baseSecondaryRoll = selectedRoll + ? memberValues.find(x => !x.selected) + : memberValues.length > 1 + ? memberValues[1] + : null; - if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null; + if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null; - const mainRoll = new MemberData(baseMainRoll.toObject()); - const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData; - const systemData = mainRoll.rollData.options; - const isCritical = overrideIsCritical ?? systemData.roll.isCritical; - if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage); + const mainRoll = new MemberData(baseMainRoll.toObject()); + const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData; + const systemData = mainRoll.rollData.options; + const isCritical = overrideIsCritical ?? systemData.roll.isCritical; + if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage); - if (secondaryRollData?.options.hasDamage) { - const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) - ? await this.getCriticalDamage(secondaryRollData.options.damage) - : secondaryRollData.options.damage; - if (systemData.damage) { - for (const [key, damage] of Object.entries(secondaryDamage ?? {})) { - if (key in systemData.damage) { - systemData.damage[key].formula = [systemData.damage[key]?.formula, damage.formula] - .filter(x => x) - .join(' + '); - systemData.damage[key].total += damage.total; - systemData.damage[key].parts.push(...damage.parts); - } else { - systemData.damage[key] = damage; - } - } - } else { - systemData.damage = secondaryDamage; + if (secondaryRollData?.options.hasDamage) { + const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) + ? await this.getCriticalDamage(secondaryRollData.options.damage) + : secondaryRollData.options.damage; + if (systemData.damage) { + for (const key in secondaryDamage) { + const damage = secondaryDamage[key]; + systemData.damage[key].formula = [systemData.damage[key].formula, damage.formula] + .filter(x => x) + .join(' + '); + systemData.damage[key].total += damage.total; + systemData.damage[key].parts.push(...damage.parts); } + } else { + systemData.damage = secondaryDamage; } - - return mainRoll; - } catch (err) { - console.error(err); - return null; } + + return mainRoll; } static async #onCancelRoll(_event, _button, options = { confirm: true }) { diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 4805cd9e..943f3506 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -50,11 +50,11 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { ).showGenericStatusEffects; context.genericStatusEffects = useGeneric ? Object.keys(context.statusEffects).reduce((acc, key) => { - const effect = context.statusEffects[key]; - if (!effect.systemEffect) acc[key] = effect; + const effect = context.statusEffects[key]; + if (!effect.systemEffect) acc[key] = effect; - return acc; - }, {}) + return acc; + }, {}) : null; context.hasCompanion = this.actor.system.companion; @@ -68,11 +68,11 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { const warning = tokensWithoutActors.length === 1 ? game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorMissing', { - name: tokensWithoutActors[0].name - }) + name: tokensWithoutActors[0].name + }) : game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorsMissing', { - names: tokensWithoutActors.map(x => x.name).join(', ') - }); + names: tokensWithoutActors.map(x => x.name).join(', ') + }); const tokens = canvas.tokens.controlled .filter(t => t.actor && !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) @@ -124,9 +124,7 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { const animationDuration = 500; const scene = game.scenes.get(game.user.viewedScene); /* getDependentTokens returns already removed tokens with id = null. Need to filter that until it's potentially fixed from Foundry */ - const activeTokens = actors.flatMap(member => - member.getDependentTokens({ scenes: scene }).filter(x => x._id && !x._destroyed) - ); + const activeTokens = actors.flatMap(member => member.getDependentTokens({ scenes: scene }).filter(x => x._id)); const { x: actorX, y: actorY } = this.document; if (activeTokens.length > 0) { for (let token of activeTokens) { @@ -174,8 +172,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { nonZeroIndex === sideMiddle ? 0 : nonZeroIndex < sideMiddle - ? -nonZeroIndex - : nonZeroIndex - sideMiddle; + ? -nonZeroIndex + : nonZeroIndex - sideMiddle; return { x: actorX - sizeX * distance, y: actorY - sizeY * distanceCoefficient }; } else if (index < side + inbetween) { const inbetweenIndex = nonZeroIndex - side; @@ -183,8 +181,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { inbetweenIndex === inbetweenMiddle ? 0 : inbetweenIndex < inbetweenMiddle - ? -inbetweenIndex - : inbetweenIndex - inbetweenMiddle; + ? -inbetweenIndex + : inbetweenIndex - inbetweenMiddle; return { x: actorX + sizeX * distanceCoefficient, y: actorY + sizeY * distance }; } else if (index < 2 * side + inbetween) { const sideIndex = nonZeroIndex - side - inbetween; @@ -192,8 +190,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { sideIndex === sideMiddle ? 0 : sideIndex < sideMiddle - ? sideIndex - : -(sideIndex - sideMiddle); + ? sideIndex + : -(sideIndex - sideMiddle); return { x: actorX + sizeX * distance, y: actorY + sizeY * distanceCoefficient }; } else { const inbetweenIndex = nonZeroIndex - 2 * side - inbetween; @@ -201,8 +199,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { inbetweenIndex === inbetweenMiddle ? 0 : inbetweenIndex < inbetweenMiddle - ? inbetweenIndex - : -(inbetweenIndex - inbetweenMiddle); + ? inbetweenIndex + : -(inbetweenIndex - inbetweenMiddle); return { x: actorX - sizeX * distanceCoefficient, y: actorY + sizeY * distance }; } }) diff --git a/module/applications/levelup/characterLevelup.mjs b/module/applications/levelup/characterLevelup.mjs index a2df63c1..f7ef2ffa 100644 --- a/module/applications/levelup/characterLevelup.mjs +++ b/module/applications/levelup/characterLevelup.mjs @@ -156,7 +156,6 @@ export default class DhCharacterLevelUp extends LevelUpBase { if (multiclasses?.[0]) { const data = multiclasses[0]; const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {}; - const subclasses = (await multiclass?.system?.fetchSubclasses()) ?? []; context.multiclass = { ...data, @@ -176,12 +175,13 @@ export default class DhCharacterLevelUp extends LevelUpBase { alreadySelected }; }) ?? [], - subclasses: subclasses.map(subclass => ({ - ...subclass, - uuid: subclass.uuid, - selected: data.secondaryData.subclass === subclass.uuid, - disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid - })), + subclasses: + multiclass?.system?.subclasses.map(subclass => ({ + ...subclass, + uuid: subclass.uuid, + selected: data.secondaryData.subclass === subclass.uuid, + disabled: data.secondaryData.subclass && data.secondaryData.subclass !== subclass.uuid + })) ?? [], compendium: 'classes', limit: 1 }; @@ -210,9 +210,9 @@ export default class DhCharacterLevelUp extends LevelUpBase { achievementExperiences = level.achievements.experiences ? Object.values(level.achievements.experiences).reduce((acc, experience) => { - if (experience.name) acc.push(experience); - return acc; - }, []) + if (experience.name) acc.push(experience); + return acc; + }, []) : []; } @@ -315,15 +315,15 @@ export default class DhCharacterLevelUp extends LevelUpBase { : null; advancement[choiceKey] = multiclassItem ? { - ...multiclassItem.toObject(), - domain: checkbox.secondaryData.domain - ? game.i18n.localize( - CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain] - .label - ) - : null, - subclass: subclass ? subclass.name : null - } + ...multiclassItem.toObject(), + domain: checkbox.secondaryData.domain + ? game.i18n.localize( + CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain] + .label + ) + : null, + subclass: subclass ? subclass.name : null + } : {}; break; } diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index 92cf3050..d6bf2d78 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -77,9 +77,9 @@ export default class DhCompanionLevelUp extends BaseLevelUp { achievementExperiences = level.achievements.experiences ? Object.values(level.achievements.experiences).reduce((acc, experience) => { - if (experience.name) acc.push(experience); - return acc; - }, []) + if (experience.name) acc.push(experience); + return acc; + }, []) : []; } context.achievements = { @@ -155,15 +155,15 @@ export default class DhCompanionLevelUp extends BaseLevelUp { vicious: { damage: advancement.vicious?.damage ? { - old: actorDamageDice, - new: advancement.vicious.damage - } + old: actorDamageDice, + new: advancement.vicious.damage + } : null, range: advancement.vicious?.range ? { - old: game.i18n.localize(`DAGGERHEART.CONFIG.Range.${actorRange}.name`), - new: game.i18n.localize(advancement.vicious.range.label) - } + old: game.i18n.localize(`DAGGERHEART.CONFIG.Range.${actorRange}.name`), + new: game.i18n.localize(advancement.vicious.range.label) + } : null }, simple: advancement.simple ?? {} diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index cafc5c89..c4616d9a 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -135,6 +135,192 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections }; context.showTabs = this.tabGroups.primary !== 'summary'; break; + + const actorArmor = this.actor.system.armor; + const levelKeys = Object.keys(this.levelup.levels); + let achivementProficiency = 0; + const achievementCards = []; + let achievementExperiences = []; + for (var levelKey of levelKeys) { + const level = this.levelup.levels[levelKey]; + if (Number(levelKey) < this.levelup.startLevel) continue; + + achivementProficiency += level.achievements.proficiency ?? 0; + const cards = level.achievements.domainCards ? Object.values(level.achievements.domainCards) : null; + if (cards) { + for (var card of cards) { + const itemCard = await foundry.utils.fromUuid(card.uuid); + achievementCards.push(itemCard); + } + } + + achievementExperiences = level.achievements.experiences + ? Object.values(level.achievements.experiences).reduce((acc, experience) => { + if (experience.name) acc.push(experience); + return acc; + }, []) + : []; + } + + context.achievements = { + proficiency: { + old: this.actor.system.proficiency, + new: this.actor.system.proficiency + achivementProficiency, + shown: achivementProficiency > 0 + }, + damageThresholds: { + major: { + old: this.actor.system.damageThresholds.major, + new: this.actor.system.damageThresholds.major + changedActorLevel - currentActorLevel + }, + severe: { + old: this.actor.system.damageThresholds.severe, + new: + this.actor.system.damageThresholds.severe + + (actorArmor + ? changedActorLevel - currentActorLevel + : (changedActorLevel - currentActorLevel) * 2) + }, + unarmored: !actorArmor + }, + domainCards: { + values: achievementCards, + shown: achievementCards.length > 0 + }, + experiences: { + values: achievementExperiences + } + }; + + const advancement = {}; + for (var levelKey of levelKeys) { + const level = this.levelup.levels[levelKey]; + if (Number(levelKey) < this.levelup.startLevel) continue; + + for (var choiceKey of Object.keys(level.choices)) { + const choice = level.choices[choiceKey]; + for (var checkbox of Object.values(choice)) { + switch (choiceKey) { + case 'proficiency': + case 'hitPoint': + case 'stress': + case 'evasion': + advancement[choiceKey] = advancement[choiceKey] + ? advancement[choiceKey] + Number(checkbox.value) + : Number(checkbox.value); + break; + case 'trait': + if (!advancement[choiceKey]) advancement[choiceKey] = {}; + for (var traitKey of checkbox.data) { + if (!advancement[choiceKey][traitKey]) advancement[choiceKey][traitKey] = 0; + advancement[choiceKey][traitKey] += 1; + } + break; + case 'domainCard': + if (!advancement[choiceKey]) advancement[choiceKey] = []; + if (checkbox.data.length === 1) { + const choiceItem = await foundry.utils.fromUuid(checkbox.data[0]); + advancement[choiceKey].push(choiceItem.toObject()); + } + break; + case 'experience': + if (!advancement[choiceKey]) advancement[choiceKey] = []; + const data = checkbox.data.map(data => { + const experience = Object.keys(this.actor.system.experiences).find( + x => x === data + ); + return this.actor.system.experiences[experience]?.description ?? ''; + }); + advancement[choiceKey].push({ data: data, value: checkbox.value }); + break; + case 'subclass': + if (checkbox.data[0]) { + const subclassItem = await foundry.utils.fromUuid(checkbox.data[0]); + if (!advancement[choiceKey]) advancement[choiceKey] = []; + advancement[choiceKey].push({ + ...subclassItem.toObject(), + featureLabel: game.i18n.localize( + subclassFeatureLabels[Number(checkbox.secondaryData.featureState)] + ) + }); + } + break; + case 'multiclass': + const multiclassItem = await foundry.utils.fromUuid(checkbox.data[0]); + const subclass = multiclassItem + ? await foundry.utils.fromUuid(checkbox.secondaryData.subclass) + : null; + advancement[choiceKey] = multiclassItem + ? { + ...multiclassItem.toObject(), + domain: checkbox.secondaryData.domain + ? game.i18n.localize( + CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain] + .label + ) + : null, + subclass: subclass ? subclass.name : null + } + : {}; + break; + } + } + } + } + + context.advancements = { + statistics: { + proficiency: { + old: context.achievements.proficiency.new, + new: context.achievements.proficiency.new + (advancement.proficiency ?? 0) + }, + hitPoints: { + old: this.actor.system.resources.hitPoints.max, + new: this.actor.system.resources.hitPoints.max + (advancement.hitPoint ?? 0) + }, + stress: { + old: this.actor.system.resources.stress.max, + new: this.actor.system.resources.stress.max + (advancement.stress ?? 0) + }, + evasion: { + old: this.actor.system.evasion, + new: this.actor.system.evasion + (advancement.evasion ?? 0) + } + }, + traits: Object.keys(this.actor.system.traits).reduce((acc, traitKey) => { + if (advancement.trait?.[traitKey]) { + if (!acc) acc = {}; + acc[traitKey] = { + label: game.i18n.localize(abilities[traitKey].label), + old: this.actor.system.traits[traitKey].value, + new: this.actor.system.traits[traitKey].value + advancement.trait[traitKey] + }; + } + return acc; + }, null), + domainCards: advancement.domainCard ?? [], + experiences: + advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ?? + [], + multiclass: advancement.multiclass, + subclass: advancement.subclass + }; + + context.advancements.statistics.proficiency.shown = + context.advancements.statistics.proficiency.new > context.advancements.statistics.proficiency.old; + context.advancements.statistics.hitPoints.shown = + context.advancements.statistics.hitPoints.new > context.advancements.statistics.hitPoints.old; + context.advancements.statistics.stress.shown = + context.advancements.statistics.stress.new > context.advancements.statistics.stress.old; + context.advancements.statistics.evasion.shown = + context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old; + context.advancements.statistics.shown = + context.advancements.statistics.proficiency.shown || + context.advancements.statistics.hitPoints.shown || + context.advancements.statistics.stress.shown || + context.advancements.statistics.evasion.shown; + + break; } return context; @@ -172,14 +358,14 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const experienceIncreaseTagify = htmlElement.querySelector('.levelup-experience-increases'); if (experienceIncreaseTagify) { const allExperiences = { + ...this.actor.system.experiences, ...Object.values(this.levelup.levels).reduce((acc, level) => { for (const key of Object.keys(level.achievements.experiences)) { acc[key] = level.achievements.experiences[key]; } return acc; - }, {}), - ...this.actor.system.experiences + }, {}) }; tagifyElement( experienceIncreaseTagify, @@ -198,35 +384,37 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) this._dragDrop.forEach(d => d.bind(htmlElement)); } - tagifyUpdate = type => async (_, { option, removed }) => { - const updatePath = Object.keys(this.levelup.levels[this.levelup.currentLevel].choices).reduce( - (acc, choiceKey) => { - const choice = this.levelup.levels[this.levelup.currentLevel].choices[choiceKey]; - Object.keys(choice).forEach(checkboxNr => { - const checkbox = choice[checkboxNr]; - if ( - choiceKey === type && - (removed ? checkbox.data.includes(option) : checkbox.data.length < checkbox.amount) - ) { - acc = `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}.data`; - } - }); + tagifyUpdate = + type => + async (_, { option, removed }) => { + const updatePath = Object.keys(this.levelup.levels[this.levelup.currentLevel].choices).reduce( + (acc, choiceKey) => { + const choice = this.levelup.levels[this.levelup.currentLevel].choices[choiceKey]; + Object.keys(choice).forEach(checkboxNr => { + const checkbox = choice[checkboxNr]; + if ( + choiceKey === type && + (removed ? checkbox.data.includes(option) : checkbox.data.length < checkbox.amount) + ) { + acc = `levels.${this.levelup.currentLevel}.choices.${choiceKey}.${checkboxNr}.data`; + } + }); - return acc; - }, - null - ); + return acc; + }, + null + ); - if (!updatePath) { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectionsLeft')); - return; - } + if (!updatePath) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectionsLeft')); + return; + } - const currentData = foundry.utils.getProperty(this.levelup, updatePath); - const updatedData = removed ? currentData.filter(x => x !== option) : [...currentData, option]; - await this.levelup.updateSource({ [updatePath]: updatedData }); - this.render(); - }; + const currentData = foundry.utils.getProperty(this.levelup, updatePath); + const updatedData = removed ? currentData.filter(x => x !== option) : [...currentData, option]; + await this.levelup.updateSource({ [updatePath]: updatedData }); + this.render(); + }; static async updateForm(event, _, formData) { const { levelup } = foundry.utils.expandObject(formData.object); @@ -405,10 +593,10 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const domainCards = this.levelup.levels[this.levelup.currentLevel].achievements.domainCards; const illegalDomainCards = option.secondaryData.domain ? Object.keys(domainCards) - .map(key => ({ ...domainCards[key], key })) - .filter( - x => x.uuid && foundry.utils.fromUuidSync(x.uuid).system.domain === option.secondaryData.domain - ) + .map(key => ({ ...domainCards[key], key })) + .filter( + x => x.uuid && foundry.utils.fromUuidSync(x.uuid).system.domain === option.secondaryData.domain + ) : []; illegalDomainCards.forEach(card => { update[`levels.${this.levelup.currentLevel}.achievements.domainCards.${card.key}.uuid`] = null; diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index 0a999506..ce312a45 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -120,6 +120,12 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S foundry.utils.fromUuidSync(x) ); + for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) { + if (!submitData.flags.daggerheart.sceneEnvironments[key]) { + submitData.flags.daggerheart.sceneEnvironments[key] = _del; + } + } + super._processSubmitData(event, form, submitData, options); } } diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index c4dfc397..9f0b22c4 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,5 +1,6 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; import { Resource } from '../../data/settings/Homebrew.mjs'; +import { slugify } from '../../helpers/utils.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -111,7 +112,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli switch (partId) { case 'domains': - const selectedDomain = this.settings.domains[this.selected.domain] ?? null; + const selectedDomain = this.selected.domain ? this.settings.domains[this.selected.domain] : null; const enrichedDescription = selectedDomain ? await foundry.applications.ux.TextEditor.implementation.enrichHTML(selectedDomain.description) : null; @@ -251,8 +252,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli const configTitle = isDowntime ? game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMove') : type === 'armorFeatures' - ? game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.armorFeature') - : game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.weaponFeature'); + ? game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.armorFeature') + : game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.weaponFeature'); const editedBase = await game.system.api.applications.sheetConfigs.SettingFeatureConfig.configure( configTitle, @@ -402,12 +403,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli const domainName = button.form.elements.domainName.value; if (!domainName) return; - const newSlug = domainName.slugify(); + const newSlug = slugify(domainName); const existingDomains = [ ...Object.values(this.settings.domains), ...Object.values(CONFIG.DH.DOMAIN.domains) ]; - if (existingDomains.find(x => x.id === newSlug)) { + if (existingDomains.find(x => slugify(game.i18n.localize(x.label)) === newSlug)) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.domains.duplicateDomain')); return; } @@ -528,7 +529,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli const identifier = button.form.elements.identifier.value; if (!identifier) return; - const sluggedIdentifier = identifier.slugify(); + const sluggedIdentifier = slugify(identifier); await this.settings.updateSource({ [`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier) diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index 9528a424..4b83a042 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -2,7 +2,6 @@ export { default as ActionConfig } from './action-config.mjs'; export { default as ActionSettingsConfig } from './action-settings-config.mjs'; export { default as CharacterSettings } from './character-settings.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; -export { default as NPCSettings } from './npc-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; export { default as SettingFeatureConfig } from './setting-feature-config.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index b65e1cdf..7406b084 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -204,7 +204,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) }; } - if (this.action.parent.metadata?.isInventoryItem) { + if (this.action.parent.metadata?.isQuantifiable) { options.quantity = { label: 'DAGGERHEART.GENERAL.itemQuantity', group: 'Global' diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 2d9eafc3..834a57a8 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -41,7 +41,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac * @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]} */ static getChangeChoices() { - const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty', 'DhNPC']; + const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty']; const getAllLeaves = (root, group, parentPath = '') => { const leaves = []; @@ -175,7 +175,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac const partContext = await super._preparePartContext(partId, context); switch (partId) { case 'details': - partContext.isItemEffect = partContext.isItemEffect || this.options.isSetting; const useGeneric = game.settings.get( CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index 57405675..6593f23d 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -110,7 +110,6 @@ export default class DHAdversarySettings extends DHBaseActorSettings { } async _onDrop(event) { - event.stopPropagation(); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const item = await fromUuid(data.uuid); diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 6d74f9c6..bc0efef2 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -121,7 +121,6 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { } async _onDrop(event) { - event.stopPropagation(); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const item = await fromUuid(data.uuid); if (data.fromInternal && item?.parent?.uuid === this.actor.uuid) return; @@ -139,4 +138,8 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { this.render(); } } + + async _onDropItem(event, item) { + console.log(item); + } } diff --git a/module/applications/sheets-configs/npc-settings.mjs b/module/applications/sheets-configs/npc-settings.mjs deleted file mode 100644 index c187877c..00000000 --- a/module/applications/sheets-configs/npc-settings.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import DHBaseActorSettings from '../sheets/api/actor-setting.mjs'; - -/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ - -export default class DHNPCSettings extends DHBaseActorSettings { - /**@inheritdoc */ - static DEFAULT_OPTIONS = { - classes: ['npc-settings'], - position: { width: 455, height: 'auto' }, - actions: {}, - dragDrop: [ - { dragSelector: null, dropSelector: '.tab.features' }, - { dragSelector: '.feature-item', dropSelector: null } - ] - }; - - /**@override */ - static PARTS = { - header: { - id: 'header', - template: 'systems/daggerheart/templates/sheets-settings/npc-settings/header.hbs' - }, - tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, - details: { - id: 'details', - template: 'systems/daggerheart/templates/sheets-settings/npc-settings/details.hbs' - }, - features: { - id: 'features', - template: 'systems/daggerheart/templates/sheets-settings/npc-settings/features.hbs' - } - }; - - /** @override */ - static TABS = { - primary: { - tabs: [{ id: 'details' }, { id: 'features' }], - initial: 'details', - labelPrefix: 'DAGGERHEART.GENERAL.Tabs' - } - }; - - async _prepareContext(options) { - const context = await super._prepareContext(options); - - const featureForms = ['passive', 'action', 'reaction']; - context.features = context.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - - return context; - } - - /* -------------------------------------------- */ - - async _onDragStart(event) { - const featureItem = event.currentTarget.closest('.feature-item'); - - if (featureItem) { - const feature = this.actor.items.get(featureItem.id); - const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true }; - event.dataTransfer.setData('text/plain', JSON.stringify(featureData)); - event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0); - } - } - - async _onDrop(event) { - event.stopPropagation(); - const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - - const item = await fromUuid(data.uuid); - if (item?.type === 'feature') { - if (data.fromInternal && item.parent?.uuid === this.actor.uuid) { - return; - } - - const itemData = item.toObject(); - delete itemData._id; - - await this.actor.createEmbeddedDocuments('Item', [itemData]); - } - } -} diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index 531158cd..a5bcc4f9 100644 --- a/module/applications/sheets-configs/setting-feature-config.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -168,8 +168,8 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App updatedEffects = deleteEffect ? currentEffects.filter(x => x.id !== effectData.id) : existingEffectIndex === -1 - ? [...currentEffects, effectData] - : currentEffects.with(existingEffectIndex, effectData); + ? [...currentEffects, effectData] + : currentEffects.with(existingEffectIndex, effectData); await this.updateMove({ [`${this.movePath}.effects`]: updatedEffects }); @@ -235,9 +235,9 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App return this.hasEffects ? tabs : Object.keys(tabs).reduce((acc, key) => { - if (key !== 'effects') acc[key] = tabs[key]; - return acc; - }, {}); + if (key !== 'effects') acc[key] = tabs[key]; + return acc; + }, {}); } /** @override */ diff --git a/module/applications/sheets/actors/_module.mjs b/module/applications/sheets/actors/_module.mjs index 1a2bebfb..c4ea2d94 100644 --- a/module/applications/sheets/actors/_module.mjs +++ b/module/applications/sheets/actors/_module.mjs @@ -2,5 +2,4 @@ export { default as Adversary } from './adversary.mjs'; export { default as Character } from './character.mjs'; export { default as Companion } from './companion.mjs'; export { default as Environment } from './environment.mjs'; -export { default as NPC } from './npc.mjs'; export { default as Party } from './party.mjs'; diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 06dd4a0f..04be3efb 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -31,16 +31,6 @@ export default class AdversarySheet extends DHBaseActorSheet { dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]', dropSelector: null } - ], - contextMenus: [ - { - handler: DHBaseActorSheet.getBaseAttackContextOptions, - selector: '[data-item-uuid][data-type="attack"]', - options: { - parentClassHooks: false, - fixed: true - } - } ] }; diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index bc2cdb41..513ad3f3 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -57,22 +57,6 @@ export default class CharacterSheet extends DHBaseActorSheet { } ], contextMenus: [ - { - handler: CharacterSheet.#getCreationMainContextOptions, - selector: '.character-details [data-action="editDoc"]', - options: { - parentClassHooks: false, - fixed: true - } - }, - { - handler: DHBaseActorSheet.getBaseAttackContextOptions, - selector: '[data-item-uuid][data-type="attack"]', - options: { - parentClassHooks: false, - fixed: true - } - }, { handler: CharacterSheet.#getDomainCardContextOptions, selector: '[data-item-uuid][data-type="domainCard"]', @@ -192,9 +176,6 @@ export default class CharacterSheet extends DHBaseActorSheet { for (const input of form.querySelectorAll('input:not([type=search]), .editor.prosemirror')) { input.disabled = disabled; } - for (const element of form.querySelectorAll('.input[contenteditable]')) { - element.classList.toggle('disabled', disabled); - } } /** @inheritDoc */ @@ -228,9 +209,8 @@ export default class CharacterSheet extends DHBaseActorSheet { context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => { acc[key] = { ...this.document.system.traits[key], - label: _loc(CONFIG.DH.ACTOR.abilities[key].label), - verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)), - isSpellcasting: this.document.system.spellcastModifierTrait?.key === key + name: game.i18n.localize(CONFIG.DH.ACTOR.abilities[key].name), + verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)) }; return acc; @@ -247,7 +227,7 @@ export default class CharacterSheet extends DHBaseActorSheet { context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; context.equippedItems = sortBy( - this.document.items.filter(i => i.system.equipped && (i.type === 'weapon' || i.usable)), + this.document.items.filter(i => i.system.equipped), i => (i.type === 'weapon' ? (i.system.secondary ? 1 : 0) : 2) ); @@ -338,56 +318,6 @@ export default class CharacterSheet extends DHBaseActorSheet { /* Context Menu */ /* -------------------------------------------- */ - static #getCreationMainContextOptions() { - /** Returns true if the item is managed by the level up wizard. Such items shouldn't allow things like manual removal */ - function isItemWizardManaged(item) { - const actor = item?.actor; - if (!actor) return false; - - // If levelup automation is off in general or for this character, all items are unmanaged - // This is disabled until we have proper granted feature removal, for now this feature is to correct errors - // const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto; - // if (!levelupAuto) return false; - - // Core items aren't part of levelup data. TODO: add some way to flag a specific character as no auto leveling - const classPair = actor.system.class; - const coreItems = [actor.system.ancestry, actor.system.community, classPair?.value, classPair?.subclass]; - if (coreItems.includes(item)) return true; - - const levelups = Object.values(actor.system.levelData?.levelups) ?? []; - const uuid = item.uuid; - const sourceUuid = item._stats.compendiumSource; // on older characters this may be missing - return levelups.some(data => { - if (item.type === 'subclass') { - const selectedSubclasses = data.selections.map(s => s.secondaryData?.subclass).filter(s => !!s); - return sourceUuid - ? selectedSubclasses.includes(sourceUuid) - : selectedSubclasses.length && item.system.isMulticlass; - } - - const matchesCard = data.achievements.domainCards.some(i => i.itemUuid === uuid); - const matchesSelection = data.selections.some(s => s.itemUuid === uuid); - return matchesCard || matchesSelection; - }); - } - - return [ - { - label: 'CONTROLS.CommonDelete', - icon: 'fa-solid fa-trash', - visible: target => { - const doc = getDocFromElementSync(target); - return doc?.isOwner && !isItemWizardManaged(doc); - }, - onClick: async (event, target) => { - const doc = await getDocFromElement(target); - if (event.shiftKey) return doc.delete(); - else return doc.deleteDialog(); - } - } - ]; - } - /** * Get the set of ContextMenu options for DomainCards. * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance @@ -404,7 +334,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const doc = getDocFromElementSync(target); return doc?.isOwner && doc.system.inVault; }, - onClick: async (_, target) => { + callback: async target => { const doc = await getDocFromElement(target); const actorLoadout = doc.actor.system.loadoutSlot; if (actorLoadout.available) return doc.update({ 'system.inVault': false }); @@ -418,7 +348,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const doc = getDocFromElementSync(target); return doc?.isOwner && doc.system.inVault; }, - onClick: async (event, target) => { + callback: async (target, event) => { const doc = await getDocFromElement(target); const actorLoadout = doc.actor.system.loadoutSlot; if (!actorLoadout.available) { @@ -457,7 +387,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const doc = getDocFromElementSync(target); return doc?.isOwner && !doc.system.inVault; }, - onClick: async (_, target) => (await getDocFromElement(target)).update({ 'system.inVault': true }) + callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true }) } ].map(option => ({ ...option, @@ -483,7 +413,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const doc = getDocFromElementSync(target); return doc.isOwner && doc && !doc.system.equipped; }, - onClick: (event, target) => CharacterSheet.#toggleEquipItem.call(this, event, target) + callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) }, { label: 'unequip', @@ -492,7 +422,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const doc = getDocFromElementSync(target); return doc.isOwner && doc && doc.system.equipped; }, - onClick: (event, target) => CharacterSheet.#toggleEquipItem.call(this, event, target) + callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) } ].map(option => ({ ...option, @@ -785,11 +715,11 @@ export default class CharacterSheet extends DHBaseActorSheet { filter: key === 'subclasses' ? { - 'system.linkedClass.uuid': { - key: 'system.linkedClass.uuid', - value: this.document.system.class.value?._stats.compendiumSource - } - } + 'system.linkedClass.uuid': { + key: 'system.linkedClass.uuid', + value: this.document.system.class.value._stats.compendiumSource + } + } : undefined, render: { noFolder: true @@ -1053,7 +983,7 @@ export default class CharacterSheet extends DHBaseActorSheet { game.tooltip.activate(target, { html, locked: true, - cssClass: 'bordered-tooltip dh-style', + cssClass: 'bordered-tooltip', direction: 'DOWN' }); @@ -1149,7 +1079,7 @@ export default class CharacterSheet extends DHBaseActorSheet { game.tooltip.activate(target, { html, locked: true, - cssClass: 'bordered-tooltip dh-style', + cssClass: 'bordered-tooltip', direction: 'DOWN', noOffset: true }); diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index a01b4a64..b30b9c07 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -11,17 +11,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet { toggleStress: DhCompanionSheet.#toggleStress, actionRoll: DhCompanionSheet.#actionRoll, levelManagement: DhCompanionSheet.#levelManagement - }, - contextMenus: [ - { - handler: DHBaseActorSheet.getBaseAttackContextOptions, - selector: '[data-item-uuid][data-type="attack"]', - options: { - parentClassHooks: false, - fixed: true - } - } - ] + } }; static PARTS = { diff --git a/module/applications/sheets/actors/npc.mjs b/module/applications/sheets/actors/npc.mjs deleted file mode 100644 index 8c9048c2..00000000 --- a/module/applications/sheets/actors/npc.mjs +++ /dev/null @@ -1,136 +0,0 @@ -import DHBaseActorSheet from '../api/base-actor.mjs'; - -export default class NPCSheet extends DHBaseActorSheet { - /** @inheritDoc */ - static DEFAULT_OPTIONS = { - classes: ['npc'], - position: { width: 660, height: 600 }, - window: { resizable: true }, - actions: {}, - window: { - resizable: true, - controls: [ - { - icon: 'fa-solid fa-signature', - label: 'DAGGERHEART.UI.Tooltip.configureAttribution', - action: 'editAttribution' - } - ] - }, - dragDrop: [ - { - dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]', - dropSelector: null - } - ] - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/sheets/actors/npc/header.hbs' }, - tabs: { template: 'systems/daggerheart/templates/sheets/actors/npc/navigation.hbs' }, - features: { - template: 'systems/daggerheart/templates/sheets/actors/npc/features.hbs', - scrollable: ['.feature-section'] - }, - notes: { - template: 'systems/daggerheart/templates/sheets/actors/npc/notes.hbs' - } - }; - - /** @inheritdoc */ - static TABS = { - primary: { - tabs: [{ id: 'notes' }, { id: 'features' }], - initial: 'notes', - labelPrefix: 'DAGGERHEART.GENERAL.Tabs' - } - }; - - /** @inheritdoc */ - _prepareTabs(group) { - const result = super._prepareTabs(group); - if (group === 'primary') { - result.features.empty = this.document.system.features.length === 0; - } - return result; - } - - /** @inheritdoc */ - async _preparePartContext(partId, context, options) { - context = await super._preparePartContext(partId, context, options); - switch (partId) { - case 'header': - await this._prepareHeaderContext(context, options); - break; - case 'features': - await this._prepareFeaturesContext(context, options); - break; - case 'notes': - await this._prepareNotesContext(context, options); - break; - } - - return context; - } - - /** - * Prepare render context for the Header part. - * @param {ApplicationRenderContext} context - * @param {ApplicationRenderOptions} options - * @returns {Promise} - * @protected - */ - async _prepareHeaderContext(context, _options) { - const { system } = this.document; - const { TextEditor } = foundry.applications.ux; - - context.description = await TextEditor.implementation.enrichHTML(system.description, { - secrets: this.document.isOwner, - relativeTo: this.document - }); - } - - /** - * Prepare render context for the Features part. - * @param {ApplicationRenderContext} context - * @param {ApplicationRenderOptions} options - * @returns {Promise} - * @protected - */ - async _prepareFeaturesContext(context, _options) { - const featureForms = ['passive', 'action', 'reaction']; - context.features = this.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - } - - /** - * Prepare render context for the Biography part. - * @param {ApplicationRenderContext} context - * @param {ApplicationRenderOptions} options - * @returns {Promise} - * @protected - */ - async _prepareNotesContext(context, _options) { - const { system } = this.document; - const { TextEditor } = foundry.applications.ux; - - const paths = { - notes: 'notes' - }; - - for (const [key, path] of Object.entries(paths)) { - const value = foundry.utils.getProperty(system, path); - context[key] = { - field: system.schema.getField(path), - value, - enriched: await TextEditor.implementation.enrichHTML(value, { - secrets: this.document.isOwner, - relativeTo: this.document - }) - }; - } - } -} diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index 3af8ea5f..a7eeccdf 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -26,6 +26,7 @@ export default class Party extends DHBaseActorSheet { actions: { openDocument: Party.#openDocument, deletePartyMember: Party.#deletePartyMember, + deleteItem: Party.#deleteItem, toggleHope: Party.#toggleHope, toggleHitPoints: Party.#toggleHitPoints, toggleStress: Party.#toggleStress, @@ -43,10 +44,12 @@ export default class Party extends DHBaseActorSheet { static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/actors/party/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, - partyMembers: { - template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs', - scrollable: [''] - }, + partyMembers: { template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs' }, + /* NOT YET IMPLEMENTED */ + // projects: { + // template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs', + // scrollable: [''] + // }, inventory: { template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs', scrollable: ['.tab.inventory .items-section'] @@ -57,13 +60,19 @@ export default class Party extends DHBaseActorSheet { /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'partyMembers' }, { id: 'inventory' }, { id: 'notes' }], + tabs: [ + { id: 'partyMembers' }, + /* NOT YET IMPLEMENTED */ + // { id: 'projects' }, + { id: 'inventory' }, + { id: 'notes' } + ], initial: 'partyMembers', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; - static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary', 'npc']; + static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary']; static DICE_ROLL_ACTOR_TYPES = ['character']; async _onRender(context, options) { @@ -76,14 +85,6 @@ export default class Party extends DHBaseActorSheet { /* Prepare Context */ /* -------------------------------------------- */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming); - context.showStats = - settings.hidePartyStats === 'never' || (settings.hidePartyStats === 'players' && game.user.isGM); - return context; - } - async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); switch (partId) { @@ -162,9 +163,9 @@ export default class Party extends DHBaseActorSheet { difficulty: actor.system.difficulty, traits: actor.system.traits ? traits.map(t => ({ - label: game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${t}.short`), - value: actor.system.traits[t].value - })) + label: game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${t}.short`), + value: actor.system.traits[t].value + })) : null, weapons }); @@ -306,7 +307,7 @@ export default class Party extends DHBaseActorSheet { static async downtimeMoveQuery({ actorId, downtimeType }) { const actor = await foundry.utils.fromUuid(actorId); - if (!actor || !actor?.isOwner) return; + if (!actor || !actor?.isOwner) reject(); new game.system.api.applications.dialogs.Downtime(actor, downtimeType === 'shortRest').render({ force: true }); @@ -497,4 +498,23 @@ export default class Party extends DHBaseActorSheet { const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid); await this.document.update({ 'system.partyMembers': newMembersList }); } + + static async #deleteItem(event, target) { + const doc = await getDocFromElement(target.closest('.inventory-item')); + if (!event.shiftKey) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize('TYPES.Actor.party'), + name: doc.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name }) + }); + + if (!confirmed) return; + } + + this.document.deleteEmbeddedDocuments('Item', [doc.id]); + } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 63bbb536..36477821 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -89,7 +89,7 @@ export default function DHApplicationMixin(Base) { classes: ['daggerheart', 'sheet', 'dh-style'], actions: { triggerContextMenu: DHSheetV2.#triggerContextMenu, - createDoc: DHSheetV2.#onCreateDoc, + createDoc: DHSheetV2.#createDoc, editDoc: DHSheetV2.#editDoc, deleteDoc: DHSheetV2.#deleteDoc, toChat: DHSheetV2.#toChat, @@ -97,8 +97,8 @@ export default function DHApplicationMixin(Base) { viewItem: DHSheetV2.#viewItem, toggleEffect: DHSheetV2.#toggleEffect, toggleExtended: DHSheetV2.#toggleExtended, - addNewItem: DHSheetV2.#onAddNewItem, - browseItem: DHSheetV2.#onBrowseItem, + addNewItem: DHSheetV2.#addNewItem, + browseItem: DHSheetV2.#browseItem, editAttribution: DHSheetV2.#editAttribution }, contextMenus: [ @@ -424,7 +424,7 @@ export default function DHApplicationMixin(Base) { const target = element.closest('[data-item-uuid]'); return !target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, - onClick: async (_, target) => (await getDocFromElement(target)).update({ disabled: true }) + callback: async target => (await getDocFromElement(target)).update({ disabled: true }) }, { label: 'enableEffect', @@ -433,7 +433,7 @@ export default function DHApplicationMixin(Base) { const target = element.closest('[data-item-uuid]'); return target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, - onClick: async (_, target) => (await getDocFromElement(target)).update({ disabled: false }) + callback: async target => (await getDocFromElement(target)).update({ disabled: false }) } ].map(option => ({ ...option, @@ -478,9 +478,7 @@ export default function DHApplicationMixin(Base) { (doc?.isOwner && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection)) ); }, - onClick: async (_, target) => { - return (await getDocFromElement(target)).sheet.render({ force: true }); - } + callback: async target => (await getDocFromElement(target)).sheet.render({ force: true }) } ]; @@ -495,7 +493,7 @@ export default function DHApplicationMixin(Base) { !foundry.utils.isEmpty(doc?.damage?.parts); return doc?.isOwner && hasDamage; }, - onClick: async (event, target) => { + callback: async (target, event) => { const doc = await getDocFromElement(target), action = doc?.system?.attack ?? doc; const config = action.prepareConfig(event); @@ -515,7 +513,7 @@ export default function DHApplicationMixin(Base) { const doc = getDocFromElementSync(target); return doc?.isOwner && !(doc.type === 'domainCard' && doc.system.inVault); }, - onClick: async (event, target) => (await getDocFromElement(target)).use(event) + callback: async (target, event) => (await getDocFromElement(target)).use(event) }); } @@ -523,7 +521,7 @@ export default function DHApplicationMixin(Base) { options.push({ label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', icon: 'fa-solid fa-message', - onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid) + callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid) }); if (deletable) @@ -535,7 +533,7 @@ export default function DHApplicationMixin(Base) { const doc = getDocFromElementSync(target); return doc?.isOwner !== false && target.dataset.itemType !== 'beastform'; }, - onClick: async (event, target) => { + callback: async (target, event) => { const doc = await getDocFromElement(target); if (event.shiftKey) return doc.delete(); else return doc.deleteDialog(); @@ -641,7 +639,7 @@ export default function DHApplicationMixin(Base) { /* Application Clicks Actions */ /* -------------------------------------------- */ - static async #onAddNewItem(event, target) { + static async #addNewItem(event, target) { const createChoice = await foundry.applications.api.DialogV2.wait({ classes: ['dh-style', 'two-big-buttons'], buttons: [ @@ -660,11 +658,11 @@ export default function DHApplicationMixin(Base) { if (!createChoice) return; - if (createChoice === 'browse') return DHSheetV2.#onBrowseItem.call(this, event, target); - else return DHSheetV2.#onCreateDoc.call(this, event, target); + if (createChoice === 'browse') return DHSheetV2.#browseItem.call(this, event, target); + else return DHSheetV2.#createDoc.call(this, event, target); } - static async #onBrowseItem(_event, target) { + static async #browseItem(event, target) { const type = target.dataset.compendium ?? target.dataset.type; const presets = { @@ -715,19 +713,19 @@ export default function DHApplicationMixin(Base) { * Create an embedded document. * @type {ApplicationClickAction} */ - static async #onCreateDoc(event, target) { + static async #createDoc(event, target) { const { documentClass, type, inVault, disabled } = target.dataset; const parentIsItem = this.document.documentName === 'Item'; const featureOnCharacter = this.document.parent?.type === 'character' && type === 'feature'; const parent = featureOnCharacter ? this.document.parent : parentIsItem && documentClass === 'Item' - ? type === 'action' - ? this.document.system - : null - : this.document; + ? type === 'action' + ? this.document.system + : null + : this.document; - let systemData = {}; + let systemData = null; if (featureOnCharacter) { systemData = { originItemType: this.document.type, @@ -740,18 +738,15 @@ export default function DHApplicationMixin(Base) { const data = { name: cls.defaultName({ type, parent }), - type, - system: systemData + type }; - if (disabled) data.disabled = true; - if (type === 'domainCard') { - if (parent?.system.domains?.length) data.system.domain = parent.system.domains[0]; - if (inVault) data.system.inVault = true; - } else if (type === 'weapon') { - // Passing an empty system object to weapon causes validation failure due to attack action initialization - // todo: determine why, fix it at its source, then remove this fallback - delete data.system; + if (systemData) data.system = systemData; + + if (inVault) data['system.inVault'] = true; + if (disabled) data.disabled = true; + if (type === 'domainCard' && parent?.system.domains?.length) { + data.system.domain = parent.system.domains[0]; } const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey }); diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 812ad311..e23a4426 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -166,15 +166,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { } } - /** Add support for input content editables */ - _toggleDisabled(disabled) { - super._toggleDisabled(disabled); - const form = this.form; - for (const element of form.querySelectorAll('.input[contenteditable]')) { - element.classList.toggle('disabled', disabled); - } - } - /* -------------------------------------------- */ /* Context Menu */ /* -------------------------------------------- */ @@ -189,43 +180,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true }); } - /** - * Get the set of ContextMenu options for the base attack. - * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance - * @this {CharacterSheet} - * @protected - */ - static getBaseAttackContextOptions() { - /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ - return [ - { - label: 'DAGGERHEART.CONFIG.RollTypes.attack.name', - icon: 'fa-solid fa-burst', - onClick: async (event, target) => (await getDocFromElement(target)).use(event) - }, - { - label: 'DAGGERHEART.GENERAL.damage', - icon: 'fa-solid fa-explosion', - onClick: async (event, target) => { - const doc = await getDocFromElement(target), - action = doc?.system?.attack ?? doc; - const config = action.prepareConfig(event); - config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects( - this.document, - doc - ); - config.hasRoll = false; - return action && action.workflow.get('damage').execute(config, null, true); - } - }, - { - label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', - icon: 'fa-solid fa-message', - onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid) - } - ]; - } - /* -------------------------------------------- */ /* Application Listener Actions */ /* -------------------------------------------- */ @@ -377,7 +331,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { action: 'update', documentName: 'Item', parent: targetActor, - updates: [{ _id: existing.id, 'system.quantity': existing.system.quantity + quantity }] + updates: [{ '_id': existing.id, 'system.quantity': existing.system.quantity + quantity }] }); } else { const itemsToCreate = []; @@ -410,7 +364,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { action: 'update', documentName: 'Item', parent: originActor, - updates: [{ _id: item.id, 'system.quantity': item.system.quantity - quantity }] + updates: [{ '_id': item.id, 'system.quantity': item.system.quantity - quantity }] }); } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 1e08fc05..e3568b23 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -126,7 +126,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { options.push({ name: 'CONTROLS.CommonDelete', icon: '', - onClick: async (_, target) => { + callback: async target => { const feature = await getDocFromElement(target); if (!feature) return; const confirmed = await foundry.applications.api.DialogV2.confirm({ diff --git a/module/applications/sheets/api/item-attachment-sheet.mjs b/module/applications/sheets/api/item-attachment-sheet.mjs index ea4d5352..bcf2fc3c 100644 --- a/module/applications/sheets/api/item-attachment-sheet.mjs +++ b/module/applications/sheets/api/item-attachment-sheet.mjs @@ -29,6 +29,16 @@ export default function ItemAttachmentSheet(Base) { } }; + async _preparePartContext(partId, context) { + await super._preparePartContext(partId, context); + + if (partId === 'attachments') { + context.attachedItems = await prepareAttachmentContext(this.document); + } + + return context; + } + async _onDrop(event) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 25c631fe..05bb0229 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -104,10 +104,9 @@ export default class ClassSheet extends DHBaseItemSheet { } /**@inheritdoc */ - async _prepareContext(options) { - const context = await super._prepareContext(options); + async _prepareContext(_options) { + const context = await super._prepareContext(_options); context.domains = this.document.system.domains; - context.subclasses = await this.document.system.fetchSubclasses(); return context; } @@ -129,8 +128,20 @@ export default class ClassSheet extends DHBaseItemSheet { const item = await fromUuid(data.uuid); const itemType = data.type === 'ActiveEffect' ? data.type : item.type; const target = event.target.closest('fieldset.drop-section'); - - if (['feature', 'ActiveEffect'].includes(itemType)) { + if (itemType === 'subclass') { + if (item.system.linkedClass) { + return ui.notifications.warn( + game.i18n.format('DAGGERHEART.UI.Notifications.subclassAlreadyLinked', { + name: item.name, + class: this.document.name + }) + ); + } + await item.update({ 'system.linkedClass': this.document.uuid }); + await this.document.update({ + 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] + }); + } else if (['feature', 'ActiveEffect'].includes(itemType)) { super._onDrop(event); } else if (this.document.parent?.type !== 'character') { if (itemType === 'weapon') { @@ -189,6 +200,12 @@ export default class ClassSheet extends DHBaseItemSheet { static async #removeItemFromCollection(_event, element) { const { uuid, target } = element.dataset; const prop = foundry.utils.getProperty(this.document.system, target); + + if (target === 'subclasses') { + const subclass = await foundry.utils.fromUuid(uuid); + await subclass?.update({ 'system.linkedClass': null }); + } + await this.document.update({ [`system.${target}`]: prop.filter(i => i && i.uuid !== uuid).map(x => x.uuid) }); } diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index e9d8370e..5c731777 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -40,36 +40,4 @@ export default class SubclassSheet extends DHBaseItemSheet { get relatedDocs() { return this.document.system.features.map(x => x.item); } - - async _prepareContext(options) { - const context = await super._prepareContext(options); - if (this.document.system.linkedClass) { - const classData = await fromUuid(this.document.system.linkedClass); - context.class = classData ?? { - name: _loc('DAGGERHEART.GENERAL.missingX', { x: _loc('TYPES.Item.class') }), - missing: true - }; - } - return context; - } - - async _onDrop(event) { - event.stopPropagation(); - const data = TextEditor.getDragEventData(event); - const item = await fromUuid(data.uuid); - const itemType = data.type === 'ActiveEffect' ? data.type : item.type; - if (itemType === 'class') { - const uuid = item._stats.compendiumSource ?? item.uuid; - if (this.document.system.linkedClass !== uuid) { - await this.document.update({ 'system.linkedClass': uuid }); - // Re-render all class sheets for instant feedback - for (const app of foundry.applications.instances.values()) { - if (app.document?.type === 'class') app.render(); - } - } - return; - } - - return super._onDrop(event); - } } diff --git a/module/applications/sidebar/tabs/actorDirectory.mjs b/module/applications/sidebar/tabs/actorDirectory.mjs index a6f48b54..89da1426 100644 --- a/module/applications/sidebar/tabs/actorDirectory.mjs +++ b/module/applications/sidebar/tabs/actorDirectory.mjs @@ -13,8 +13,8 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs. return document.type === 'adversary' ? game.i18n.localize(adversaryTypes[document.system.type]?.label ?? 'TYPES.Actor.adversary') : document.type === 'environment' - ? game.i18n.localize(environmentTypes[document.system.type]?.label ?? 'TYPES.Actor.environment') - : null; + ? game.i18n.localize(environmentTypes[document.system.type]?.label ?? 'TYPES.Actor.environment') + : null; }; } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 199ee87d..34b25591 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -41,8 +41,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const advantage = rollCommand.advantage ? CONFIG.DH.ACTIONS.advantageState.advantage.value : rollCommand.disadvantage - ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value - : undefined; + ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value + : undefined; const difficulty = rollCommand.difficulty; const grantResources = rollCommand.grantResources; @@ -50,8 +50,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const title = (flavor ?? traitValue) ? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: game.i18n.localize(CONFIG.DH.ACTOR.abilities[traitValue].label) - }) + ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label) + }) : game.i18n.localize('DAGGERHEART.GENERAL.duality'); enrichedDualityRoll({ @@ -103,19 +103,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo _getEntryContextOptions() { return [ ...super._getEntryContextOptions(), - { - label: 'DAGGERHEART.UI.ChatLog.rerollActionRoll', - icon: '', - visible: li => { - const message = game.messages.get(li.dataset.messageId); - return message.system.hasRoll && (game.user.isGM || message.isAuthor); - }, - callback: async li => { - const message = game.messages.get(li.dataset.messageId); - const reroll = await message.rolls[0].reroll({ liveRoll: true }); - message.update({ rolls: [reroll] }); - } - }, { label: 'DAGGERHEART.UI.ChatLog.rerollDamage', icon: '', @@ -126,10 +113,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo : false; return (game.user.isGM || message.isAuthor) && hasRolledDamage; }, - callback: async li => { + callback: li => { const message = game.messages.get(li.dataset.messageId); - const update = await message.system.getRerolledDamage(); - message.update(update); + new game.system.api.applications.dialogs.RerollDamageDialog(message).render({ force: true }); } } ]; diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 25f3e06b..fb19a17e 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -56,9 +56,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C async _prepareTrackerContext(context, options) { await super._prepareTrackerContext(context, options); - const npcs = context.turns?.filter(x => x.isNPC) ?? []; - const adversaries = npcs.filter(x => x.disposition !== CONST.TOKEN_DISPOSITIONS.FRIENDLY); - const friendlies = npcs.filter(x => x.disposition === CONST.TOKEN_DISPOSITIONS.FRIENDLY); + const adversaries = context.turns?.filter(x => x.isNPC) ?? []; const characters = context.turns?.filter(x => !x.isNPC) ?? []; const spotlightQueueEnabled = game.settings.get( CONFIG.DH.id, @@ -77,56 +75,25 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C Object.assign(context, { actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens, adversaries, - friendlies, allCharacters: characters, characters: characters.filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0), spotlightRequests }); } - /** - * Open the dialog used to edit the name of the currently viewed Combat encounter. - * @this {CombatTracker} - * @returns {Promise} - */ - static async #onEditName() { - const combat = this.viewed; - if (!combat || !game.user.isGM) return null; - const field = combat.schema.fields.name; - const inputHTML = field.toFormGroup({}, { name: 'name', value: combat.name, autofocus: true }).outerHTML; - const formData = await foundry.applications.api.DialogV2.input({ - window: { icon: 'fa-solid fa-tag', title: 'COMBAT.ACTIONS.EditNameTitle' }, - position: { width: 480 }, - content: inputHTML - }); - await combat.update({ name: formData.name || '' }); - } - _getCombatContextOptions() { return [ { - label: 'COMBAT.ACTIONS.EditName', - icon: 'fa-solid fa-tag', + label: 'COMBAT.ClearMovementHistories', + icon: '', + visible: () => game.user.isGM && this.viewed?.combatants.size > 0, + callback: () => this.viewed.clearMovementHistories() + }, + { + label: 'COMBAT.Delete', + icon: '', visible: () => game.user.isGM && !!this.viewed, - onClick: () => DhCombatTracker.#onEditName.call(this) - }, - { - label: 'COMBAT.ACTIONS.LinkToScene', - icon: '', - visible: () => game.user.isGM && !this.scene, - onClick: () => this.viewed.toggleSceneLink() - }, - { - label: 'COMBAT.ACTIONS.UnlinkFromScene', - icon: '', - visible: () => game.user.isGM && !!this.scene, - onClick: () => this.viewed.toggleSceneLink() - }, - { - label: 'COMBAT.End', - icon: 'fa-solid fa-xmark', - visible: () => game.user.isGM && !!this.viewed, - onClick: () => this.viewed.endCombat() + callback: () => this.viewed.endCombat() } ]; } @@ -162,8 +129,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C active: index === combat.turn, canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'), type: combatant.actor?.system?.type, - img: await this._getCombatantThumbnail(combatant), - disposition: combatant.token?.disposition + img: await this._getCombatantThumbnail(combatant) }; turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin( diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 5974e1f2..8bb9fc1d 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -1,6 +1,6 @@ import { DhCountdown } from '../../data/countdowns.mjs'; import { waitForDiceSoNice } from '../../helpers/utils.mjs'; -import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -56,8 +56,8 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio ? countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.increasing.id ? 'DAGGERHEART.UI.Countdowns.increasingLoop' : countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.decreasing.id - ? 'DAGGERHEART.UI.Countdowns.decreasingLoop' - : 'DAGGERHEART.UI.Countdowns.loop' + ? 'DAGGERHEART.UI.Countdowns.decreasingLoop' + : 'DAGGERHEART.UI.Countdowns.loop' : null; const randomizeValid = !new Roll(countdown.progress.startFormula ?? '').isDeterministic; acc[key] = { @@ -114,7 +114,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio } await this.data.updateSource(update); - await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, this.gmSetSetting.bind(this.data), this.data, null, { + await emitAsGM(GMUpdateEvent.UpdateCountdowns, this.gmSetSetting.bind(this.data), this.data, null, { refreshType: RefreshType.Countdown }); @@ -148,11 +148,11 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio } async gmSetSetting(data) { - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.Countdown } - }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data), + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); } diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 2a2a113e..79a59a07 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -1,5 +1,5 @@ import { waitForDiceSoNice } from '../../helpers/utils.mjs'; -import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -21,19 +21,19 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application static DEFAULT_OPTIONS = { id: 'countdowns', tag: 'div', - classes: ['daggerheart', 'dh-style', 'countdowns'], + classes: ['daggerheart', 'dh-style', 'countdowns', 'faded-ui'], window: { icon: 'fa-solid fa-clock-rotate-left', - frame: false, + frame: true, title: 'DAGGERHEART.UI.Countdowns.title', positioned: false, resizable: false, minimizable: false }, actions: { - toggleViewMode: DhCountdowns.#onToggleViewMode, - editCountdowns: DhCountdowns.#onEditCountdowns, - loopCountdown: DhCountdowns.#onLoopCountdown, + toggleViewMode: DhCountdowns.#toggleViewMode, + editCountdowns: DhCountdowns.#editCountdowns, + loopCountdown: DhCountdowns.#loopCountdown, decreaseCountdown: (_, target) => this.editCountdown(false, target), increaseCountdown: (_, target) => this.editCountdown(true, target) }, @@ -62,6 +62,20 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application if (iconOnly) frame.classList.add('icon-only'); else frame.classList.remove('icon-only'); + const header = frame.querySelector('.window-header'); + header.querySelector('button[data-action="close"]').remove(); + header.querySelector('button[data-action="toggleControls"]').remove(); + + if (game.user.isGM) { + const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle'); + const editButton = ``; + header.insertAdjacentHTML('beforeEnd', editButton); + } + + const minimizeTooltip = game.i18n.localize('DAGGERHEART.UI.Countdowns.toggleIconMode'); + const minimizeButton = ``; + header.insertAdjacentHTML('beforeEnd', minimizeButton); + return frame; } @@ -101,8 +115,8 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application ? countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.increasing.id ? 'DAGGERHEART.UI.Countdowns.increasingLoop' : countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.decreasing.id - ? 'DAGGERHEART.UI.Countdowns.decreasingLoop' - : 'DAGGERHEART.UI.Countdowns.loop' + ? 'DAGGERHEART.UI.Countdowns.decreasingLoop' + : 'DAGGERHEART.UI.Countdowns.loop' : null; const loopDisabled = !countdownEditable || @@ -147,7 +161,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return true; } - static async #onToggleViewMode() { + static async #toggleViewMode() { const currentMode = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode); const appMode = CONFIG.DH.GENERAL.countdownAppMode; const newMode = currentMode === appMode.textIcon ? appMode.iconOnly : appMode.textIcon; @@ -158,16 +172,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application this.render(); } - static async #onEditCountdowns() { + static async #editCountdowns() { new game.system.api.applications.ui.CountdownEdit().render(true); } - static async #onLoopCountdown(_, target) { + static async #loopCountdown(_, target) { if (!DhCountdowns.canPerformEdit()) return; const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdownId = target.closest('[data-countdown]').dataset.countdown; - const countdown = settings.countdowns[countdownId]; + const countdown = settings.countdowns[target.id]; let progressMax = countdown.progress.start; let message = null; @@ -181,17 +194,17 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.increasing.id ? Number(progressMax) + 1 : countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.decreasing.id - ? Math.max(Number(progressMax) - 1, 0) - : progressMax; + ? Math.max(Number(progressMax) - 1, 0) + : progressMax; await waitForDiceSoNice(message); await settings.updateSource({ - [`countdowns.${countdownId}.progress`]: { + [`countdowns.${target.id}.progress`]: { current: newMax, start: newMax } }); - await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { + await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { refreshType: RefreshType.Countdown }); } @@ -200,23 +213,22 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application if (!DhCountdowns.canPerformEdit()) return; const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdownId = target.closest('[data-countdown]').dataset.countdown; - const countdown = settings.countdowns[countdownId]; + const countdown = settings.countdowns[target.id]; const newCurrent = increase ? Math.min(countdown.progress.current + 1, countdown.progress.start) : Math.max(countdown.progress.current - 1, 0); - await settings.updateSource({ [`countdowns.${countdownId}.progress.current`]: newCurrent }); - await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { + await settings.updateSource({ [`countdowns.${target.id}.progress.current`]: newCurrent }); + await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { refreshType: RefreshType.Countdown }); } static async gmSetSetting(data) { - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.Countdown } - }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data), + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); } @@ -265,7 +277,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return acc; }, {}) }; - await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { + await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { refreshType: RefreshType.Countdown }); } diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index a64b1b22..035041e1 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -67,10 +67,10 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica const actor = token ? token.actor : canvas.tokens.controlled.length === 0 - ? !game.user.isGM - ? game.user.character - : null - : canvas.tokens.controlled[0].actor; + ? !game.user.isGM + ? game.user.character + : null + : canvas.tokens.controlled[0].actor; return getIconVisibleActiveEffects(actor?.getActiveEffects() ?? []); }; diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index 8c247f79..4e5e1132 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -1,4 +1,4 @@ -import { emitGMUpdate, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -104,7 +104,7 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV } async updateFear(value) { - return emitGMUpdate( + return emitAsGM( GMUpdateEvent.UpdateFear, game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 8f38918a..67a16f6a 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -1,4 +1,3 @@ -import { getDocFromElement } from '../../helpers/utils.mjs'; import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -48,8 +47,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { expandContent: this.expandContent, resetFilters: this.resetFilters, sortList: this.sortList, - openSettings: this.openSettings, - viewSheet: this.#onViewSheet + openSettings: this.openSettings }, position: { left: 100, @@ -111,8 +109,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.compendiumBrowserTypeKey}`].position ); + options.position = userPresetPosition ?? ItemBrowser.DEFAULT_OPTIONS.position; - delete options.position.zIndex; if (!userPresetPosition) { const width = noFolder === true || lite === true ? 600 : 850; @@ -279,7 +277,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { (await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description)); } - this.fieldFilter = await this._createFieldFilter(); + this.fieldFilter = this._createFieldFilter(); if (this.presets?.filter) { Object.entries(this.presets.filter).forEach(([k, v]) => { @@ -308,8 +306,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { { items: this.items, menu: this.selectedMenu, - formatLabel: this.formatLabel, - viewSheet: this.items[0] instanceof Actor + formatLabel: this.formatLabel } ); @@ -358,12 +355,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { ); } - async _createFieldFilter() { + _createFieldFilter() { const filters = ItemBrowser.getFolderConfig(this.selectedMenu.data, 'filters'); - for (const f of filters) { + filters.forEach(f => { if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field); else if (typeof f.choices === 'function') { - f.choices = await f.choices(this.items); + f.choices = f.choices(this.items); } // Clear field label so template uses our custom label parameter @@ -373,8 +370,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { f.name ??= f.key; f.value = this.presets?.filter?.[f.name]?.value ?? null; - } - + }); return filters; } @@ -571,11 +567,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { } } - static async #onViewSheet(_, target) { - const document = await getDocFromElement(target); - document?.sheet?.render(true); - } - _createDragProcess() { new foundry.applications.ux.DragDrop.implementation({ dragSelector: '.item-container', @@ -614,16 +605,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { items: { folder: 'equipments', render: { - folders: [ - 'equipments', - 'ancestries', - 'classes', - 'subclasses', - 'domains', - 'communities', - 'beastforms' - // excluded: features - ] + noFolder: true } }, compendium: {} diff --git a/module/applications/ui/sceneNavigation.mjs b/module/applications/ui/sceneNavigation.mjs index 982063e7..a0005fc7 100644 --- a/module/applications/ui/sceneNavigation.mjs +++ b/module/applications/ui/sceneNavigation.mjs @@ -1,4 +1,4 @@ -import { emitGMUpdate, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; export default class DhSceneNavigation extends foundry.applications.ui.SceneNavigation { /** @inheritdoc */ @@ -68,7 +68,7 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi 1 )[0]; newEnvironments.unshift(newFirst); - emitGMUpdate( + emitAsGM( GMUpdateEvent.UpdateDocument, scene.update.bind(scene), { 'flags.daggerheart.sceneEnvironments': newEnvironments }, diff --git a/module/canvas/placeables/regionLayer.mjs b/module/canvas/placeables/regionLayer.mjs index 684fdd5e..c53cf782 100644 --- a/module/canvas/placeables/regionLayer.mjs +++ b/module/canvas/placeables/regionLayer.mjs @@ -57,14 +57,14 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer { } async placeRegion(data, options = {}) { - const preConfirm = data => { - const shape = data.document.shapes[0]; + const preConfirm = ({ _event, document, _create, _options }) => { + const shape = document.shapes[0]; const isEmanation = shape.type === 'emanation'; if (isEmanation) { const token = this.#findTokenInBounds(shape.base.origin); - if (!token) return options.preConfirm?.(data) ?? true; + if (!token) return options.preConfirm?.() ?? true; const shapeData = shape.toObject(); - data.document.updateSource({ + document.updateSource({ shapes: [ { ...shapeData, @@ -80,10 +80,10 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer { }); } - return options?.preConfirm?.(data) ?? true; + return options?.preConfirm?.() ?? true; }; - return await super.placeRegion(data, { ...options, preConfirm }); + super.placeRegion(data, { ...options, preConfirm }); } /** Searches for token at origin point, returning null if there are no tokens or multiple overlapping tokens */ diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 02eed7db..77c178d6 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -155,15 +155,15 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { const targetEdge = this.#getEdgeBoundary(targetBounds, originPoint, targetPoint); const adjustedOriginPoint = originEdge ? canvas.grid.getTopLeftPoint({ - x: originEdge.x + Math.sign(originPoint.x - originEdge.x), - y: originEdge.y + Math.sign(originPoint.y - originEdge.y) - }) + x: originEdge.x + Math.sign(originPoint.x - originEdge.x), + y: originEdge.y + Math.sign(originPoint.y - originEdge.y) + }) : originPoint; const adjustDestinationPoint = targetEdge ? canvas.grid.getTopLeftPoint({ - x: targetEdge.x + Math.sign(targetPoint.x - targetEdge.x), - y: targetEdge.y + Math.sign(targetPoint.y - targetEdge.y) - }) + x: targetEdge.x + Math.sign(targetPoint.x - targetEdge.x), + y: targetEdge.y + Math.sign(targetPoint.y - targetEdge.y) + }) : targetPoint; const distance = canvas.grid.measurePath([ { ...adjustedOriginPoint, elevation: 0 }, @@ -249,6 +249,9 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { /** @inheritDoc */ _drawBar(number, bar, data) { + const val = Number(data.value); + const pct = Math.clamp(val, 0, data.max) / data.max; + // Determine sizing const { width, height } = this.document.getSize(); const s = canvas.dimensions.uiScale; @@ -256,19 +259,17 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { const bh = 8 * (this.document.height >= 2 ? 1.5 : 1) * s; // Determine the color to use - const Color = foundry.utils.Color; - const fillColor = number === 0 ? Color.fromRGB([1, 0, 0]) : Color.fromString('#0032b1'); - const emptyColor = Color.fromRGB([0, 0, 0]); + const fillColor = + number === 0 ? foundry.utils.Color.fromRGB([1, 0, 0]) : foundry.utils.Color.fromString('#0032b1'); - // Draw the bar (accounting floating point numbers from bar animations) - const widthUnit = bw / Math.ceil(data.max); + // Draw the bar + const widthUnit = bw / data.max; bar.clear().lineStyle(s, 0x000000, 1.0); - const sections = [...Array(Math.ceil(data.max)).keys()]; - for (const mark of sections) { + const sections = [...Array(data.max).keys()]; + for (let mark of sections) { const x = mark * widthUnit; - const marked = mark < Math.ceil(data.value); - const remainder = mark === Math.ceil(data.value) - 1 ? data.value % 1 : 0; - const color = !marked ? emptyColor : remainder ? emptyColor.mix(fillColor, remainder) : fillColor; + const marked = mark + 1 <= data.value; + const color = marked ? fillColor : foundry.utils.Color.fromRGB([0, 0, 0]); if (mark === 0 || mark === sections.length - 1) { bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s); // Would like drawRoundedRect, but it's very troublsome with the corners. Leaving for now. } else { diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 83572dc0..04b387cb 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -70,49 +70,6 @@ export const typeConfig = { } ] }, - environments: { - columns: [ - { - key: 'system.tier', - label: 'DAGGERHEART.GENERAL.Tiers.singular' - }, - { - key: 'system.type', - label: 'DAGGERHEART.GENERAL.type', - format: type => { - if (!type) return '-'; - - return CONFIG.DH.ACTOR.environmentTypes[type].label; - } - } - ], - filters: [ - { - key: 'system.tier', - label: 'DAGGERHEART.GENERAL.Tiers.singular', - field: 'system.api.models.actors.DhEnvironment.schema.fields.tier' - }, - { - key: 'system.type', - label: 'DAGGERHEART.GENERAL.type', - field: 'system.api.models.actors.DhEnvironment.schema.fields.type' - }, - { - key: 'system.difficulty', - name: 'difficulty.min', - label: 'DAGGERHEART.UI.ItemBrowser.difficultyMin', - field: 'system.api.models.actors.DhEnvironment.schema.fields.difficulty', - operator: 'gte' - }, - { - key: 'system.difficulty', - name: 'difficulty.max', - label: 'DAGGERHEART.UI.ItemBrowser.difficultyMax', - field: 'system.api.models.actors.DhEnvironment.schema.fields.difficulty', - operator: 'lte' - } - ] - }, items: { columns: [ { @@ -127,8 +84,8 @@ export const typeConfig = { isSecondary ? 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.short' : isSecondary === false - ? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short' - : '-' + ? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short' + : '-' }, { key: 'system.tier', @@ -426,8 +383,7 @@ export const typeConfig = { { key: 'system.linkedClass', label: 'TYPES.Item.class', - format: linkedClass => - foundry.utils.fromUuidSync(linkedClass)?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' + format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' }, { key: 'system.spellcastingTrait', @@ -437,20 +393,15 @@ export const typeConfig = { ], filters: [ { - key: 'system.linkedClass', + key: 'system.linkedClass.uuid', label: 'TYPES.Item.class', - choices: async items => { - const list = []; - for (const item of items.filter(item => item.system.linkedClass)) { - const linkedClass = await foundry.utils.fromUuid(item.system.linkedClass); - if (linkedClass) { - list.push({ - value: linkedClass.uuid, - label: linkedClass.name - }); - } - } - + choices: items => { + const list = items + .filter(item => item.system.linkedClass) + .map(item => ({ + value: item.system.linkedClass.uuid, + label: item.system.linkedClass.name + })); return list.reduce((a, c) => { if (!a.find(i => i.value === c.value)) a.push(c); return a; @@ -604,8 +555,7 @@ export const compendiumConfig = { id: 'environments', keys: ['environments'], label: 'DAGGERHEART.UI.ItemBrowser.folders.environments', - type: ['environment'], - listType: 'environments' + type: ['environment'] }, beastforms: { id: 'beastforms', diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 1988b1d8..c4d07c25 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -75,12 +75,7 @@ export default class DHAttackAction extends DHDamageAction { const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active; for (const { value, valueAlt, type } of damage.parts) { const usedValue = useAltDamage ? valueAlt : value; - const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {}); - const str = damageString - ? damageString - : game.i18n.format('DAGGERHEART.GENERAL.missingX', { - x: game.i18n.localize('DAGGERHEART.GENERAL.damage') - }); + const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {}); const icons = Array.from(type) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index c71f5ef9..b3775dc9 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -144,18 +144,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return this.item instanceof DhpActor ? this.item : this.item?.parent instanceof DhpActor - ? this.item.parent - : null; + ? this.item.parent + : null; } - /** - * Returns true if the action is usable. - * An action is usable on any actor type. For example, an adversary might have a base attack action. - */ + /** Returns true if the action is usable */ get usable() { const actor = this.actor; - const pack = actor?.pack ? game.packs.get(actor.pack) : null; - return !pack?.locked && this.isOwner; + return this.isOwner && actor?.type === 'character'; } static getRollType(parent) { @@ -223,7 +219,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @returns {object} */ async use(event, configOptions = {}) { - if (!this.actor) throw new Error('An Action can\'t be used outside of an Actor context.'); + if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); let config = this.prepareConfig(event, configOptions); if (!config) return; @@ -300,17 +296,17 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel const groupAttackTokens = this.damage.groupAttack ? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens( - this.actor.id, - this.damage.groupAttack - ) + this.actor.id, + this.damage.groupAttack + ) : null; config.damageOptions = { groupAttack: this.damage.groupAttack ? { - numAttackers: Math.max(groupAttackTokens.length, 1), - range: this.damage.groupAttack - } + numAttackers: Math.max(groupAttackTokens.length, 1), + range: this.damage.groupAttack + } : null }; } diff --git a/module/data/action/countdownAction.mjs b/module/data/action/countdownAction.mjs index cb141637..abcc6b40 100644 --- a/module/data/action/countdownAction.mjs +++ b/module/data/action/countdownAction.mjs @@ -36,7 +36,7 @@ export default class DhCountdownAction extends DHBaseAction { /** @inheritDoc */ static migrateData(source) { - for (const countdown of Object.values(source.countdown)) { + for (const countdown of source.countdown) { if (countdown.progress.max) { countdown.progress.startFormula = countdown.progress.max; countdown.progress.start = 1; diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 7e037f5b..128c0c52 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -35,7 +35,6 @@ export default class BeastformEffect extends BaseEffect { static migrateData(source) { if (!source.characterTokenData.tokenSize.height) source.characterTokenData.tokenSize.height = 1; if (!source.characterTokenData.tokenSize.width) source.characterTokenData.tokenSize.width = 1; - if (!source.characterTokenData.tokenSize.depth) source.characterTokenData.tokenSize.depth = 1; return super.migrateData(source); } @@ -53,8 +52,7 @@ export default class BeastformEffect extends BaseEffect { if (this.parent.parent.type === 'character') { const baseUpdate = { height: this.characterTokenData.tokenSize.height, - width: this.characterTokenData.tokenSize.width, - depth: this.characterTokenData.tokenSize.depth + width: this.characterTokenData.tokenSize.width }; const update = { ...baseUpdate, @@ -90,13 +88,13 @@ export default class BeastformEffect extends BaseEffect { ...baseUpdate, x, y, - texture: { + 'texture': { enabled: this.characterTokenData.usesDynamicToken, src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg, scaleX: this.characterTokenData.tokenSize.scale, scaleY: this.characterTokenData.tokenSize.scale }, - ring: { + 'ring': { subject: { texture: token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg diff --git a/module/data/activeEffect/changeTypes/armor.mjs b/module/data/activeEffect/changeTypes/armor.mjs index 0c226513..217ff9dd 100644 --- a/module/data/activeEffect/changeTypes/armor.mjs +++ b/module/data/activeEffect/changeTypes/armor.mjs @@ -166,10 +166,10 @@ export default class ArmorChange extends foundry.abstract.DataModel { value: change.type === 'armor' ? { - ...change.value, - current: Math.min(change.value.current, newMax), - max: newMax - } + ...change.value, + current: Math.min(change.value.current, newMax), + max: newMax + } : change.value })) ]; diff --git a/module/data/actor/_module.mjs b/module/data/actor/_module.mjs index 1fe1ef3f..99577620 100644 --- a/module/data/actor/_module.mjs +++ b/module/data/actor/_module.mjs @@ -1,17 +1,15 @@ import DhCharacter from './character.mjs'; import DhCompanion from './companion.mjs'; import DhAdversary from './adversary.mjs'; -import DhNPC from './npc.mjs'; import DhEnvironment from './environment.mjs'; import DhParty from './party.mjs'; -export { DhCharacter, DhCompanion, DhAdversary, DhNPC, DhEnvironment, DhParty }; +export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment, DhParty }; export const config = { character: DhCharacter, companion: DhCompanion, adversary: DhAdversary, - npc: DhNPC, environment: DhEnvironment, party: DhParty }; diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index d6d0dcdf..d69e17ad 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -3,7 +3,8 @@ import { ActionField } from '../fields/actionField.mjs'; import { commonActorRules } from './base.mjs'; import DhCreature from './creature.mjs'; import { bonusField } from '../fields/actorField.mjs'; -import { getTierAdjustedAdversary } from './tierAdjustment.mjs'; +import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs'; +import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs'; export default class DhpAdversary extends DhCreature { static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary']; @@ -205,6 +206,205 @@ export default class DhpAdversary extends DhCreature { /** Returns source data for this actor adjusted to a new tier, which can be used to create a new actor. */ adjustForTier(tier) { const source = this.parent.toObject(true); - return getTierAdjustedAdversary(source, tier); + + /** @type {(2 | 3 | 4)[]} */ + const tiers = new Array(Math.abs(tier - this.tier)) + .fill(0) + .map((_, idx) => idx + Math.min(tier, this.tier) + 1); + if (tier < this.tier) tiers.reverse(); + const typeData = adversaryScalingData[source.system.type] ?? adversaryScalingData[source.system.standard]; + const tierEntries = tiers.map(t => ({ tier: t, ...typeData[t] })); + + // Apply simple tier changes + const scale = tier > this.tier ? 1 : -1; + for (const entry of tierEntries) { + source.system.difficulty += scale * entry.difficulty; + source.system.damageThresholds.major += scale * entry.majorThreshold; + source.system.damageThresholds.severe += scale * entry.severeThreshold; + source.system.resources.hitPoints.max += scale * entry.hp; + source.system.resources.stress.max += scale * entry.stress; + source.system.attack.roll.bonus += scale * entry.attack; + } + + // Get the mean and standard deviation of expected damage in the previous and new tier + // The data we have is for attack scaling, but we reuse this for action scaling later + const expectedDamageData = adversaryExpectedDamage[source.system.type] ?? adversaryExpectedDamage.basic; + const damageMeta = { + currentDamageRange: { tier: source.system.tier, ...expectedDamageData[source.system.tier] }, + newDamageRange: { tier, ...expectedDamageData[tier] }, + type: 'attack' + }; + + // Update damage of base attack + try { + this.#adjustActionDamage(source.system.attack, damageMeta); + } catch (err) { + ui.notifications.warn('Failed to convert attack damage of adversary'); + console.error(err); + } + + // Update damage of each item action, making sure to also update the description if possible + const damageRegex = /@Damage\[([^\[\]]*)\]({[^}]*})?/g; + for (const item of source.items) { + // Replace damage inlines with new formulas + for (const withDescription of [item.system, ...Object.values(item.system.actions)]) { + withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => { + const { value: formula } = parseInlineParams(inner); + if (!formula || !type) return match; + + try { + const adjusted = this.#calculateAdjustedDamage(formula, { ...damageMeta, type: 'action' }); + const newFormula = [ + adjusted.diceQuantity ? `${adjusted.diceQuantity}d${adjusted.faces}` : null, + adjusted.bonus + ] + .filter(p => !!p) + .join('+'); + return match.replace(formula, newFormula); + } catch { + return match; + } + }); + } + + // Update damage in item actions + // Parse damage, and convert all formula matches in the descriptions to the new damage + for (const action of Object.values(item.system.actions)) { + try { + const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' }); + if (!result) continue; + + for (const { previousFormula, formula } of Object.values(result)) { + const oldFormulaRegexp = new RegExp( + previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?') + ); + item.system.description = item.system.description.replace(oldFormulaRegexp, formula); + action.description = action.description.replace(oldFormulaRegexp, formula); + } + } catch (err) { + ui.notifications.warn(`Failed to convert action damage for item ${item.name}`); + console.error(err); + } + } + } + + // Finally set the tier of the source data, now that everything is complete + source.system.tier = tier; + return source; + } + + /** + * Converts a damage object to a new damage range + * @returns {{ diceQuantity: number; faces: number; bonus: number }} the adjusted result as a combined term + * @throws error if the formula is the wrong type + */ + #calculateAdjustedDamage(formula, { currentDamageRange, newDamageRange, type }) { + const terms = parseTermsFromSimpleFormula(formula); + const flatTerms = terms.filter(t => t.diceQuantity === 0); + const diceTerms = terms.filter(t => t.diceQuantity > 0); + if (flatTerms.length > 1 || diceTerms.length > 1) { + throw new Error('invalid formula for conversion'); + } + const value = { + ...(diceTerms[0] ?? { diceQuantity: 0, faces: 1 }), + bonus: flatTerms[0]?.bonus ?? 0 + }; + const previousExpected = calculateExpectedValue(value); + if (previousExpected === 0) return value; // nothing to do + + const dieSizes = [4, 6, 8, 10, 12, 20]; + const steps = newDamageRange.tier - currentDamageRange.tier; + const increasing = steps > 0; + const deviation = (previousExpected - currentDamageRange.mean) / currentDamageRange.deviation; + const expected = Math.max(1, newDamageRange.mean + newDamageRange.deviation * deviation); + + // If this was just a flat number, convert to the expected damage and exit + if (value.diceQuantity === 0) { + value.bonus = Math.round(expected); + return value; + } + + const getExpectedDie = () => calculateExpectedValue({ diceQuantity: 1, faces: value.faces }) || 1; + const getBaseAverage = () => calculateExpectedValue({ ...value, bonus: 0 }); + + // Check the number of base overages over the expected die. In the end, if the bonus inflates too much, we add a die + const baseOverages = Math.floor(value.bonus / getExpectedDie()); + + // Prestep. Change number of dice for attacks, bump up/down for actions + // We never bump up to d20, though we might bump down from it + if (type === 'attack') { + const minimum = increasing ? value.diceQuantity : 0; + value.diceQuantity = Math.max(minimum, newDamageRange.tier); + } else { + const currentIdx = dieSizes.indexOf(value.faces); + value.faces = dieSizes[Math.clamp(currentIdx + steps, 0, 4)]; + } + + value.bonus = Math.round(expected - getBaseAverage()); + + // Attempt to handle negative values. + // If we can do it with only step downs, do so. Otherwise remove tier dice, and try again + if (value.bonus < 0) { + let stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity); + const currentIdx = dieSizes.indexOf(value.faces); + + // If step downs alone don't suffice, change the flat modifier, then calculate steps required again + // If this isn't sufficient, the result will be slightly off. This is unlikely to happen + if (type !== 'attack' && stepsRequired > currentIdx && value.diceQuantity > 0) { + value.diceQuantity -= increasing ? 1 : Math.abs(steps); + value.bonus = Math.round(expected - getBaseAverage()); + if (value.bonus >= 0) return value; // complete + } + + stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity); + value.faces = dieSizes[Math.max(0, currentIdx - stepsRequired)]; + value.bonus = Math.max(0, Math.round(expected - getBaseAverage())); + } + + // If value is really high, we add a number of dice based on the number of overages + // This attempts to preserve a similar amount of variance when increasing an action + const overagesToRemove = Math.floor(value.bonus / getExpectedDie()) - baseOverages; + if (type !== 'attack' && increasing && overagesToRemove > 0) { + value.diceQuantity += overagesToRemove; + value.bonus = Math.round(expected - getBaseAverage()); + } + + return value; + } + + /** + * Updates damage to reflect a specific value. + * @throws if damage structure is invalid for conversion + * @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage + */ + #adjustActionDamage(action, damageMeta) { + if (!action.damage?.parts.hitPoints) return null; + + const result = {}; + for (const property of ['value', 'valueAlt']) { + const data = action.damage.parts.hitPoints[property]; + const previousFormula = data.custom.enabled + ? data.custom.formula + : [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0] + .filter(p => !!p) + .join('+'); + const value = this.#calculateAdjustedDamage(previousFormula, damageMeta); + const formula = [value.diceQuantity ? `${value.diceQuantity}d${value.faces}` : null, value.bonus] + .filter(p => !!p) + .join('+'); + if (value.diceQuantity) { + data.custom.enabled = false; + data.bonus = value.bonus; + data.dice = `d${value.faces}`; + data.flatMultiplier = value.diceQuantity; + } else if (!value.diceQuantity) { + data.custom.enabled = true; + data.custom.formula = formula; + } + + result[property] = { previousFormula, formula, value }; + } + + return result; } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index aed27650..d42f2f64 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -315,8 +315,8 @@ export default class DhCharacter extends DhCreature { return currentLevel === 1 ? 1 : Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers).find( - tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end - ).tier; + tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end + ).tier; } get ancestry() { @@ -520,20 +520,20 @@ export default class DhCharacter extends DhCreature { if (armorSource.type === 'armor') { armorUpdates[armorSource.parent.id].updates.push({ - _id: armorSource.id, + '_id': armorSource.id, 'system.armor.current': armorSource.system.armor.current + usedArmorChange }); } else { effectUpdates[armorSource.parent.id].updates.push({ - _id: armorSource.id, + '_id': armorSource.id, 'system.changes': armorSource.system.changes.map(change => ({ ...change, value: change.type === 'armor' ? { - ...change.value, - current: armorSource.system.armorChange.value.current + usedArmorChange - } + ...change.value, + current: armorSource.system.armorChange.value.current + usedArmorChange + } : change.value })) }); @@ -577,8 +577,6 @@ export default class DhCharacter extends DhCreature { communityFeatures = [], classFeatures = [], subclassFeatures = [], - multiclassFeatures = [], - multiclassSubclassFeatures = [], companionFeatures = [], features = []; @@ -588,9 +586,9 @@ export default class DhCharacter extends DhCreature { } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.community.id) { communityFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.class.id) { - (item.system.multiclassOrigin ? multiclassFeatures : classFeatures).push(item); + classFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { - (item.system.multiclassOrigin ? multiclassSubclassFeatures : subclassFeatures).push(item); + subclassFeatures.push(item); } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.companion.id) { companionFeatures.push(item); } else if (item.type === 'feature' && !item.system.type) { @@ -619,24 +617,6 @@ export default class DhCharacter extends DhCreature { type: 'subclass', values: subclassFeatures }, - ...(multiclassFeatures.length - ? { - multiclassFeatures: { - title: `${game.i18n.localize('DAGGERHEART.GENERAL.multiclass')} - ${this.multiclass.value?.name}`, - type: 'multiclass', - values: multiclassFeatures - } - } - : {}), - ...(multiclassSubclassFeatures.length - ? { - multiclassSubclassFeatures: { - title: `${game.i18n.localize('DAGGERHEART.GENERAL.multiclass')} ${game.i18n.localize('TYPES.Item.subclass')} - ${this.multiclass.subclass?.name}`, - type: 'multiclassSubclass', - values: multiclassSubclassFeatures - } - } - : {}), companionFeatures: { title: game.i18n.localize('DAGGERHEART.ACTORS.Character.companionFeatures'), type: 'companion', @@ -659,8 +639,8 @@ export default class DhCharacter extends DhCreature { (this.primaryWeapon && this.secondaryWeapon) ? burden.twoHanded.value : this.primaryWeapon || this.secondaryWeapon - ? burden.oneHanded.value - : null; + ? burden.oneHanded.value + : null; } get deathMoveViable() { @@ -726,8 +706,8 @@ export default class DhCharacter extends DhCreature { currentLevel === 1 ? null : Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers).find( - tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end - ).tier; + tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end + ).tier; if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto) { for (let levelKey in this.levelData.levelups) { const level = this.levelData.levelups[levelKey]; @@ -856,17 +836,16 @@ export default class DhCharacter extends DhCreature { /* Scars can alter the amount of current hope */ if (changes.system?.scars) { - const diff = this.scars - changes.system.scars; - const newHopeMax = this.resources.hope.max + diff; - const newHopeValue = Math.min(newHopeMax, this.resources.hope.value); - if (newHopeValue != this.resources.hope.value) { - changes.system = foundry.utils.mergeObject(changes.system ?? {}, { - resources: { - hope: { - value: (changes.system?.resources?.hope?.value ?? 0) + newHopeMax - } - } - }); + const diff = this.system.scars - changes.system.scars; + const newHopeMax = this.system.resources.hope.max + diff; + const newHopeValue = Math.min(newHopeMax, this.system.resources.hope.value); + if (newHopeValue != this.system.resources.hope.value) { + if (!changes.system.resources.hope) changes.system.resources.hope = { value: 0 }; + + changes.system.resources.hope = { + ...changes.system.resources.hope, + value: changes.system.resources.hope.value + newHopeValue + }; } } diff --git a/module/data/actor/npc.mjs b/module/data/actor/npc.mjs deleted file mode 100644 index 2ccaf926..00000000 --- a/module/data/actor/npc.mjs +++ /dev/null @@ -1,43 +0,0 @@ -import DHNPCSettings from '../../applications/sheets-configs/npc-settings.mjs'; -import BaseDataActor from './base.mjs'; - -export default class DhpNPC extends BaseDataActor { - static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.NPC']; - - static get metadata() { - return foundry.utils.mergeObject(super.metadata, { - label: 'TYPES.Actor.npc', - type: 'npc', - settingSheet: DHNPCSettings, - hasResistances: false, - hasAttribution: true - }); - } - - static defineSchema() { - const fields = foundry.data.fields; - return { - ...super.defineSchema(), - difficulty: new fields.NumberField({ - nullable: true, - initial: null, - integer: true, - label: 'DAGGERHEART.GENERAL.difficulty' - }), - description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' }), - motives: new fields.StringField(), - notes: new fields.HTMLField() - }; - } - - /**@inheritdoc */ - static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/drama-masks.svg'; - - get features() { - return this.parent.items.filter(x => x.type === 'feature'); - } - - isItemValid(source) { - return super.isItemValid(source) || source.type === 'feature'; - } -} diff --git a/module/data/actor/tierAdjustment.mjs b/module/data/actor/tierAdjustment.mjs deleted file mode 100644 index bc6ad176..00000000 --- a/module/data/actor/tierAdjustment.mjs +++ /dev/null @@ -1,219 +0,0 @@ -import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs'; -import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs'; -import { parseInlineParams } from '../../enrichers/parser.mjs'; - -export function getTierAdjustedAdversary(source, tier) { - const currentTier = source.tier ?? 1; - - /** @type {(2 | 3 | 4)[]} */ - const tiers = new Array(Math.abs(tier - currentTier)) - .fill(0) - .map((_, idx) => idx + Math.min(tier, currentTier) + 1); - if (tier < currentTier) tiers.reverse(); - const typeData = adversaryScalingData[source.system.type] ?? adversaryScalingData[source.system.standard]; - const tierEntries = tiers.map(t => ({ tier: t, ...typeData[t] })); - - // Apply simple tier changes - const scale = tier > currentTier ? 1 : -1; - for (const entry of tierEntries) { - source.system.difficulty += scale * entry.difficulty; - source.system.damageThresholds.major += scale * entry.majorThreshold; - source.system.damageThresholds.severe += scale * entry.severeThreshold; - source.system.resources.hitPoints.max += scale * entry.hp; - source.system.resources.stress.max += scale * entry.stress; - source.system.attack.roll.bonus += scale * entry.attack; - } - - // Get the mean and standard deviation of expected damage in the previous and new tier - // The data we have is for attack scaling, but we reuse this for action scaling later - const expectedDamageData = adversaryExpectedDamage[source.system.type] ?? adversaryExpectedDamage.basic; - const damageMeta = { - currentDamageRange: { tier: source.system.tier, ...expectedDamageData[source.system.tier] }, - newDamageRange: { tier, ...expectedDamageData[tier] } - }; - - // Store initial attack damage for abilities that have you deal a "standard attack" - const initialAttack = { - type: source.system.attack.damage?.parts.hitPoints?.type?.toSorted(), - value: getDamagePartsFormula(source.system.attack.damage?.parts.hitPoints?.value) - }; - - // Update damage of base attack. - try { - const damage = source.system.attack.damage; - if (!damage?.parts.hitPoints) throw new Error('Unexpected missing attack in adversary'); - - for (const property of ['value', 'valueAlt']) { - const data = damage.parts.hitPoints[property]; - const previousFormula = getDamagePartsFormula(data); - const { value, formula } = calculateAdjustedDamage(previousFormula, 'attack', damageMeta); - applyAdjustedDamage(data, value, formula); - } - } catch (err) { - ui.notifications.warn('Failed to convert attack damage of adversary'); - console.error(err); - } - - // Update damage of each item action, making sure to also update the description if possible - const damageRegex = /@Damage\[([^\[\]]*)\]({[^}]*})?/g; - for (const item of source.items) { - // Replace damage inlines with new formulas. Keep a record for a specific check later - const descriptionFormulas = []; - for (const withDescription of [item.system, ...Object.values(item.system.actions)]) { - withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => { - const { value: formula } = parseInlineParams(inner, { first: 'value' }); - if (!formula) return match; - - try { - const newFormula = calculateAdjustedDamage(formula, 'action', damageMeta)?.formula; - descriptionFormulas.push(formula); - return match.replace(formula, newFormula); - } catch { - return match; - } - }); - } - - // Update damage in item actions and convert all formula matches in the descriptions to the new damage - for (const action of Object.values(item.system.actions)) { - if (!action.damage?.parts.hitPoints) continue; - try { - // Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead - const result = []; - for (const property of ['value', 'valueAlt']) { - const { [property]: data, type: damageType } = action.damage.parts.hitPoints; - const previousFormula = getDamagePartsFormula(data); - const isActuallyAttack = - previousFormula === initialAttack.value && - foundry.utils.equals(damageType.toSorted(), initialAttack.type) && - !descriptionFormulas.includes(previousFormula); - const type = isActuallyAttack ? 'attack' : 'action'; - const { value, formula } = calculateAdjustedDamage(previousFormula, type, damageMeta); - applyAdjustedDamage(data, value, formula); - result.push({ previousFormula, formula }); - } - - // Override text in the description with those values - for (const { previousFormula, formula } of Object.values(result)) { - const oldFormulaRegexp = new RegExp( - previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?') - ); - item.system.description = item.system.description.replace(oldFormulaRegexp, formula); - action.description = action.description.replace(oldFormulaRegexp, formula); - } - } catch (err) { - ui.notifications.warn(`Failed to convert action damage for item ${item.name}`); - console.error(err); - } - } - } - - // Finally set the tier of the source data, now that everything is complete - source.system.tier = tier; - return source; -} - -/** - * Converts a damage object to a new damage range - * @returns {{ diceQuantity: number; faces: number; bonus: number }} the adjusted result as a combined term - * @throws error if the formula is the wrong type - */ -function calculateAdjustedDamage(formula, type, { currentDamageRange, newDamageRange }) { - const terms = parseTermsFromSimpleFormula(formula); - const flatTerms = terms.filter(t => t.diceQuantity === 0); - const diceTerms = terms.filter(t => t.diceQuantity > 0); - if (flatTerms.length > 1 || diceTerms.length > 1) { - throw new Error('invalid formula for conversion'); - } - const value = { - ...(diceTerms[0] ?? { diceQuantity: 0, faces: 1 }), - bonus: flatTerms[0]?.bonus ?? 0 - }; - const previousExpected = calculateExpectedValue(value); - if (previousExpected === 0) return value; // nothing to do - - const dieSizes = [4, 6, 8, 10, 12, 20]; - const steps = newDamageRange.tier - currentDamageRange.tier; - const increasing = steps > 0; - const deviation = (previousExpected - currentDamageRange.mean) / currentDamageRange.deviation; - const expected = Math.max(1, newDamageRange.mean + newDamageRange.deviation * deviation); - - // If this was just a flat number, convert to the expected damage and exit - if (value.diceQuantity === 0) { - value.bonus = Math.round(expected); - return value; - } - - const getExpectedDie = () => calculateExpectedValue({ diceQuantity: 1, faces: value.faces }) || 1; - const getBaseAverage = () => calculateExpectedValue({ ...value, bonus: 0 }); - - // Check the number of base overages over the expected die. In the end, if the bonus inflates too much, we add a die - const baseOverages = Math.floor(value.bonus / getExpectedDie()); - - // Prestep. Change number of dice for attacks, bump up/down for actions - // We never bump up to d20, though we might bump down from it - if (type === 'attack') { - const minimum = increasing ? value.diceQuantity : 0; - value.diceQuantity = Math.max(minimum, newDamageRange.tier); - } else { - const currentIdx = dieSizes.indexOf(value.faces); - value.faces = dieSizes[Math.clamp(currentIdx + steps, 0, 4)]; - } - - value.bonus = Math.round(expected - getBaseAverage()); - - // Attempt to handle negative values. - // If we can do it with only step downs, do so. Otherwise remove tier dice, and try again - if (value.bonus < 0) { - let stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity); - const currentIdx = dieSizes.indexOf(value.faces); - - // If step downs alone don't suffice, change the flat modifier, then calculate steps required again - // If this isn't sufficient, the result will be slightly off. This is unlikely to happen - if (type !== 'attack' && stepsRequired > currentIdx && value.diceQuantity > 0) { - value.diceQuantity -= increasing ? 1 : Math.abs(steps); - value.bonus = Math.round(expected - getBaseAverage()); - if (value.bonus >= 0) return value; // complete - } - - stepsRequired = Math.ceil(Math.abs(value.bonus) / value.diceQuantity); - value.faces = dieSizes[Math.max(0, currentIdx - stepsRequired)]; - value.bonus = Math.max(0, Math.round(expected - getBaseAverage())); - } - - // If value is really high, we add a number of dice based on the number of overages - // This attempts to preserve a similar amount of variance when increasing an action - const overagesToRemove = Math.floor(value.bonus / getExpectedDie()) - baseOverages; - if (type !== 'attack' && increasing && overagesToRemove > 0) { - value.diceQuantity += overagesToRemove; - value.bonus = Math.round(expected - getBaseAverage()); - } - - const newFormula = [value.diceQuantity ? `${value.diceQuantity}d${value.faces}` : null, value.bonus] - .filter(p => !!p) - .join('+'); - return { value, formula: newFormula }; -} - -function getDamagePartsFormula(data) { - return data.custom.enabled - ? data.custom.formula - : [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0].filter(p => !!p).join('+'); -} - -/** - * Updates damage to reflect a specific value. - * @throws if damage structure is invalid for conversion - * @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage - */ -function applyAdjustedDamage(diceData, value, formula) { - if (value.diceQuantity) { - diceData.custom.enabled = false; - diceData.bonus = value.bonus; - diceData.dice = `d${value.faces}`; - diceData.flatMultiplier = value.diceQuantity; - } else if (!value.diceQuantity) { - diceData.custom.enabled = true; - diceData.custom.formula = formula; - } -} diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index ccfe25ea..eaa1cdc2 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -1,5 +1,3 @@ -import { triggerChatRollFx } from '../../helpers/utils.mjs'; - const fields = foundry.data.fields; const targetsField = () => @@ -132,35 +130,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { }); } - /* TODO: Change how damage data is stored somehow to enable better rerolling */ - async getRerolledDamage() { - if (!this.damage) return; - - const rerolls = []; - const update = { system: { damage: {} } }; - for (const partKey in this.damage) { - const part = this.damage[partKey]; - const testRoll = Roll.fromData(part.parts[0].roll); - const rerolled = await testRoll.reroll(); - rerolls.push(rerolled); - - if (!update.system.damage[partKey]) update.system.damage[partKey] = { parts: [part.parts[0]] }; - const partData = update.system.damage[partKey].parts[0]; - update.system.damage[partKey].total = rerolled.total; - partData.modifierTotal = rerolled.terms.reduce((acc, x) => { - if (x.isDeterministic && !x.operator) acc += x.total; - return acc; - }, 0); - partData.dice = rerolled.dice.map(d => ({ ...d.toJSON(), dice: d.denomination })); - partData.total = rerolled.total; - partData.roll = rerolled.toJSON(); - } - - await triggerChatRollFx(rerolls); - - return update; - } - registerTargetHook() { if (!this.parent.isAuthor || !this.hasTarget) return; if (this.targetMode && this.parent.targetHook !== null) { diff --git a/module/data/companionLevelup.mjs b/module/data/companionLevelup.mjs index e24820de..7ab61210 100644 --- a/module/data/companionLevelup.mjs +++ b/module/data/companionLevelup.mjs @@ -22,12 +22,12 @@ export class DhCompanionLevelup extends foundry.abstract.DataModel { const initialAchievements = i === tier.levels.start ? tier.initialAchievements : {}; const experiences = initialAchievements.experience ? [...Array(initialAchievements.experience.nr).keys()].reduce((acc, _) => { - acc[foundry.utils.randomID()] = { - name: '', - modifier: initialAchievements.experience.modifier - }; - return acc; - }, {}) + acc[foundry.utils.randomID()] = { + name: '', + modifier: initialAchievements.experience.modifier + }; + return acc; + }, {}) : {}; const currentChoices = pcLevelData.levelups[i]?.selections?.length; @@ -302,9 +302,9 @@ export class DhLevelupLevel extends foundry.abstract.DataModel { experiences: levelData.achievements?.experiences ?? achievements.experiences ?? {}, domainCards: levelData.achievements?.domainCards ? levelData.achievements.domainCards.reduce((acc, card, index) => { - acc[index] = { ...card }; - return acc; - }, {}) + acc[index] = { ...card }; + return acc; + }, {}) : (achievements.domainCards ?? {}), proficiency: levelData.achievements?.proficiency ?? achievements.proficiency ?? null }, diff --git a/module/data/compendiumBrowserSettings.mjs b/module/data/compendiumBrowserSettings.mjs index ed70c88f..ea71c439 100644 --- a/module/data/compendiumBrowserSettings.mjs +++ b/module/data/compendiumBrowserSettings.mjs @@ -11,13 +11,11 @@ export default class CompendiumBrowserSettings extends foundry.abstract.DataMode }) ), excludedPacks: new fields.TypedObjectField( - new fields.TypedObjectField( - new fields.SchemaField({ - excludedDocumentTypes: new fields.ArrayField( - new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES }) - ) - }) - ) + new fields.SchemaField({ + excludedDocumentTypes: new fields.ArrayField( + new fields.StringField({ required: true, choices: CONST.SYSTEM_SPECIFIC_COMPENDIUM_TYPES }) + ) + }) ) }; } @@ -30,7 +28,7 @@ export default class CompendiumBrowserSettings extends foundry.abstract.DataMode const excludedSourceData = this.excludedSources[packageName]; if (excludedSourceData && excludedSourceData.excludedDocumentTypes.includes(pack.metadata.type)) return true; - const excludedPackData = this.excludedPacks[packageName]?.[pack.metadata.name]; + const excludedPackData = this.excludedPacks[item.pack]; if (excludedPackData && excludedPackData.excludedDocumentTypes.includes(pack.metadata.type)) return true; return false; diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index 54971d34..7d27197d 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -77,11 +77,11 @@ export class DhCountdown extends foundry.abstract.DataModel { static defaultCountdown(type, playerHidden) { const ownership = playerHidden ? game.users.reduce((acc, user) => { - if (!user.isGM) { - acc[user.id] = CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE; - } - return acc; - }, {}) + if (!user.isGM) { + acc[user.id] = CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE; + } + return acc; + }, {}) : undefined; return { @@ -102,8 +102,8 @@ export class DhCountdown extends foundry.abstract.DataModel { value: user.isGM ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER : this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1 - ? this.ownership.players[user.id].type - : this.ownership.default, + ? this.ownership.players[user.id].type + : this.ownership.default, isGM: user.isGM }; diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index 0eeb95c2..e3be9937 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -105,8 +105,8 @@ export default class BeastformField extends fields.SchemaField { baseSize === 'custom' ? 'custom' : (Object.keys(CONFIG.DH.ACTOR.tokenSize).find( - x => CONFIG.DH.ACTOR.tokenSize[x].value === CONFIG.DH.ACTOR.tokenSize[baseSize].value + 1 - ) ?? baseSize); + x => CONFIG.DH.ACTOR.tokenSize[x].value === CONFIG.DH.ACTOR.tokenSize[baseSize].value + 1 + ) ?? baseSize); formData.system.tokenSize = { ...evolvedData.form.system.tokenSize, size: evolvedSize diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 82cfcd23..9271f6b0 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -103,7 +103,7 @@ export default class CostField extends fields.ArrayField { static calcCosts(costs) { const resources = CostField.getResources.call(this, costs); let filteredCosts = costs; - if (this.parent?.isInventoryItem && this.parent.consumeOnUse === false) { + if (this.parent?.metadata.isQuantifiable && this.parent.consumeOnUse === false) { filteredCosts = filteredCosts.filter(c => c.key !== 'quantity'); } @@ -116,8 +116,8 @@ export default class CostField extends fields.ArrayField { c.key === 'fear' ? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) : resources[c.key].isReversed - ? resources[c.key].max - resources[c.key].value - : resources[c.key].value; + ? resources[c.key].max - resources[c.key].value + : resources[c.key].value; if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step); return c; }); @@ -149,8 +149,8 @@ export default class CostField extends fields.ArrayField { !resources[c.key] ? a : a && resources[c.key].isReversed - ? resources[c.key].value + (c.total ?? c.value) <= resources[c.key].max - : resources[c.key]?.value >= (c.total ?? c.value), + ? resources[c.key].value + (c.total ?? c.value) <= resources[c.key].max + : resources[c.key]?.value >= (c.total ?? c.value), true ); } diff --git a/module/data/fields/action/countdownField.mjs b/module/data/fields/action/countdownField.mjs index 96d9dd91..719ca749 100644 --- a/module/data/fields/action/countdownField.mjs +++ b/module/data/fields/action/countdownField.mjs @@ -1,4 +1,4 @@ -import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../../systemRegistration/socket.mjs'; const fields = foundry.data.fields; @@ -78,7 +78,7 @@ export default class CountdownField extends fields.ArrayField { ); } - await emitGMUpdate( + await emitAsGM( GMUpdateEvent.UpdateCountdowns, async () => { const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); @@ -87,11 +87,11 @@ export default class CountdownField extends fields.ArrayField { CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSetting.toObject() - ); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.Countdown } - }); + ), + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); }, data, diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 9b21d3ba..30a5ad7c 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -72,6 +72,9 @@ export default class DamageField extends fields.SchemaField { damageConfig.source.message = messageId; damageConfig.directDamage = !!damageConfig.source?.message; + // if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active) + // await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message); + const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); if (!damageResult) return false; if (damageResult.actionChatMessageHandled) config.actionChatMessageHandled = true; diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index e943d63d..9a4ffc31 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -1,3 +1,5 @@ +import { emitAsGM, GMUpdateEvent } from '../../../systemRegistration/socket.mjs'; + const fields = foundry.data.fields; export default class EffectsField extends fields.ArrayField { @@ -32,7 +34,8 @@ export default class EffectsField extends fields.ArrayField { } if (EffectsField.getAutomation() || force) { targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit); - EffectsField.applyEffects.call(this, targets); + await emitAsGM(GMUpdateEvent.UpdateEffect, EffectsField.applyEffects.bind(this), targets, this.uuid); + // EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit)); } } @@ -56,16 +59,16 @@ export default class EffectsField extends fields.ArrayField { if (!token) return; const messageToken = token.document ?? token; - const conditionImmunities = messageToken.actor.system.rules?.conditionImmunities ?? {}; + const conditionImmunities = messageToken.actor.system.rules.conditionImmunities ?? {}; messageTargets.push({ token: messageToken, conditionImmunities: Object.values(conditionImmunities).some(x => x) ? game.i18n.format('DAGGERHEART.UI.Chat.effectSummary.immunityTo', { - immunities: Object.keys(conditionImmunities) - .filter(x => conditionImmunities[x]) - .map(x => game.i18n.localize(conditions[x].name)) - .join(', ') - }) + immunities: Object.keys(conditionImmunities) + .filter(x => conditionImmunities[x]) + .map(x => game.i18n.localize(conditions[x].name)) + .join(', ') + }) : null }); diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index 7343ab85..0629353e 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -69,11 +69,11 @@ export default class SaveField extends fields.SchemaField { game.user === actor.owner ? SaveField.rollSave.call(this, actor, event) : actor.owner.query('reactionRoll', { - actionId: this.uuid, - actorId: actor.uuid, - event, - message - }); + actionId: this.uuid, + actorId: actor.uuid, + event, + message + }); const result = await rollSave; await SaveField.updateSaveMessage.call(this, result, message, target.id); subResolve(); @@ -97,8 +97,8 @@ export default class SaveField extends fields.SchemaField { const title = actor.isNPC ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { - ability: game.i18n.localize(abilities[this.save.trait]?.label) - }), + ability: game.i18n.localize(abilities[this.save.trait]?.label) + }), rollConfig = { event, title, diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index a2275fa5..ec7881f7 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,4 +1,4 @@ -import { itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs'; +import { itemAbleRollParse } from '../../../helpers/utils.mjs'; import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; @@ -40,7 +40,7 @@ export default class DHSummonField extends fields.ArrayField { const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item)); await roll.evaluate(); const count = roll.total; - if (!roll.isDeterministic) rolls.push(roll); + if (!roll.isDeterministic && game.modules.get('dice-so-nice')?.active) rolls.push(roll); const actor = await DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); /* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */ @@ -56,7 +56,7 @@ export default class DHSummonField extends fields.ArrayField { } } - if (rolls.length) await triggerChatRollFx(rolls); + if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true))); this.actor.sheet?.minimize(); DHSummonField.handleSummon(summonData, this.actor); diff --git a/module/data/item/ancestry.mjs b/module/data/item/ancestry.mjs index eae1136c..b9253a3c 100644 --- a/module/data/item/ancestry.mjs +++ b/module/data/item/ancestry.mjs @@ -1,6 +1,6 @@ import BaseDataItem from './base.mjs'; import ItemLinkFields from '../../data/fields/itemLinkFields.mjs'; -import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import { getFeaturesHTMLData } from '../../helpers/utils.mjs'; export default class DHAncestry extends BaseDataItem { /** @inheritDoc */ @@ -45,10 +45,6 @@ export default class DHAncestry extends BaseDataItem { /**@inheritdoc */ async getDescriptionData() { - // Preload all ancestry features for acquisition from the cache - // todo: make feature acquisition async and replace feature helpers for methods - await fromUuids(this._source.features.map(f => f.item)); - const baseDescription = this.description; const features = await getFeaturesHTMLData(this.features); diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 21c56f9a..b0e4847f 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -141,6 +141,16 @@ export default class DHArmor extends AttachableItem { } } + _onUpdate(a, b, c) { + super._onUpdate(a, b, c); + + if (this.actor?.type === 'character') { + for (const party of this.actor.parties) { + party.render(); + } + } + } + /** @inheritDoc */ static migrateDocumentData(source) { if (!source.system.armor) { diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index ba114fda..aebf33bf 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -200,7 +200,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { const features = []; for (let f of this.features) { const fBase = f.item ?? f; - const feature = fBase.pack ? await foundry.utils.fromUuid(fBase.uuid) : fBase; + const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); features.push( foundry.utils.mergeObject( feature.toObject(), diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs index ba274cc7..d30d6046 100644 --- a/module/data/item/beastform.mjs +++ b/module/data/item/beastform.mjs @@ -51,8 +51,7 @@ export default class DHBeastform extends BaseDataItem { }), scale: new fields.NumberField({ nullable: false, min: 0.2, max: 3, step: 0.05, initial: 1 }), height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }), - width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }), - depth: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }) + width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }) }), mainTrait: new fields.StringField({ required: true, @@ -193,8 +192,7 @@ export default class DHBeastform extends BaseDataItem { tokenSize: { scale: this.parent.parent.prototypeToken.texture.scaleX, height: this.parent.parent.prototypeToken.height, - width: this.parent.parent.prototypeToken.width, - depth: this.parent.parent.prototypeToken.depth + width: this.parent.parent.prototypeToken.width } }, advantageOn: this.advantageOn, @@ -208,17 +206,15 @@ export default class DHBeastform extends BaseDataItem { const autoTokenSize = this.tokenSize.size !== 'custom' ? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes[ - this.tokenSize.size - ] + this.tokenSize.size + ] : null; const width = autoTokenSize ?? this.tokenSize.width; const height = autoTokenSize ?? this.tokenSize.height; - const depth = autoTokenSize ?? this.tokenSize.depth; const prototypeTokenUpdate = { height, width, - depth, texture: { src: this.tokenImg, scaleX: this.tokenSize.scale, diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 470a1e3c..d3738318 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import ItemLinkFields from '../fields/itemLinkFields.mjs'; -import { addLinkedItemsDiff, fromUuids, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs'; +import { addLinkedItemsDiff, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs'; export default class DHClass extends BaseDataItem { /** @inheritDoc */ @@ -30,6 +30,7 @@ export default class DHClass extends BaseDataItem { }), evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }), features: new ItemLinkFields(), + subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), inventory: new fields.SchemaField({ take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), choiceA: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), @@ -69,25 +70,6 @@ export default class DHClass extends BaseDataItem { return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.class).map(x => x.item); } - async fetchSubclasses() { - const uuids = [this.parent.uuid, this.parent._stats?.compendiumSource].filter(u => !!u); - const subclasses = game.items.filter(x => x.type === 'subclass' && uuids.includes(x.system.linkedClass)); - for (const pack of game.packs) { - const packIds = []; - const indexes = await pack.getIndex({ fields: ['system.linkedClass'] }); - for (const index of indexes) { - if (index.type !== 'subclass') continue; - if (!uuids.includes(index.system?.linkedClass)) continue; - if (subclasses.find(x => x.uuid === index.uuid)) continue; - packIds.push(index._id); - } - - if (packIds.length > 0) subclasses.push(...(await pack.getDocuments({ _id__in: packIds }))); - } - - return subclasses; - } - async _preCreate(data, options, user) { if (this.actor?.type === 'character') { const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto; @@ -217,10 +199,6 @@ export default class DHClass extends BaseDataItem { classItems.push(contentLink.outerHTML); } - // Preload all class features for acquisition from the cache - // todo: make feature acquisition async and replace feature helpers for methods - await fromUuids(this._source.features.map(f => f.item)); - const hopeFeatures = await getFeaturesHTMLData(this.hopeFeatures); const classFeatures = await getFeaturesHTMLData(this.classFeatures); diff --git a/module/data/item/community.mjs b/module/data/item/community.mjs index 6f4470b8..6d054976 100644 --- a/module/data/item/community.mjs +++ b/module/data/item/community.mjs @@ -1,4 +1,4 @@ -import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import { getFeaturesHTMLData } from '../../helpers/utils.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import BaseDataItem from './base.mjs'; @@ -27,10 +27,6 @@ export default class DHCommunity extends BaseDataItem { /**@inheritdoc */ async getDescriptionData() { - // Preload all community features for acquisition from the cache - // todo: make feature acquisition async and replace feature helpers for methods - await fromUuids(this._source.features); - const baseDescription = this.description; const features = await getFeaturesHTMLData(this.features); diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index 934b55d3..d421cc6d 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -1,4 +1,5 @@ -import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import { getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ItemLinkFields from '../fields/itemLinkFields.mjs'; import BaseDataItem from './base.mjs'; @@ -27,7 +28,7 @@ export default class DHSubclass extends BaseDataItem { features: new ItemLinkFields(), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), isMulticlass: new fields.BooleanField({ initial: false }), - linkedClass: new fields.DocumentUUIDField({ type: 'Item', nullable: true, initial: null }) + linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null }) }; } @@ -55,30 +56,37 @@ export default class DHSubclass extends BaseDataItem { if (allowed === false) return; if (this.actor?.type === 'character') { - const { value: actorClass, subclass: existingSubclass } = this.actor.system.class; - const { value: multiclass, subclass: existingMultisubclass } = this.actor.system.multiclass; - if (!actorClass && !multiclass) { - ui.notifications.warn('DAGGERHEART.UI.Notifications.missingClass', { localize: true }); - return false; - } - if (existingSubclass && existingMultisubclass) { - ui.notifications.warn('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent', { localize: true }); - return false; - } - if (existingSubclass && !multiclass) { - ui.notifications.warn('DAGGERHEART.UI.Notifications.missingMulticlass', { localize: true }); - return false; - } + const dataUuid = data.uuid ?? data._stats.compendiumSource ?? `Item.${data._id}`; + if (this.actor.system.class.subclass) { + if (this.actor.system.multiclass.subclass) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent')); + return false; + } else { + const multiclass = this.actor.items.find(x => x.type === 'class' && x.system.isMulticlass); + if (!multiclass) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingMulticlass')); + return false; + } - const match = [multiclass, actorClass].find( - c => c && (c._stats.compendiumSource ?? c.uuid) === this.linkedClass - ); - if (!match) { - const key = multiclass ? 'subclassNotInMulticlass' : 'subclassNotInClass'; - ui.notifications.warn(`DAGGERHEART.UI.Notifications.${key}`, { localize: true }); - return false; - } else if (match.system.isMulticlass) { - await this.updateSource({ isMulticlass: true }); + if (multiclass.system.subclasses.every(x => x.uuid !== dataUuid)) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInMulticlass') + ); + return false; + } + + await this.updateSource({ isMulticlass: true }); + } + } else { + const actorClass = this.actor.items.find(x => x.type === 'class' && !x.system.isMulticlass); + if (!actorClass) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass')); + return false; + } + if (actorClass.system.subclasses.every(x => x.uuid !== dataUuid)) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass')); + return false; + } } } } @@ -90,11 +98,6 @@ export default class DHSubclass extends BaseDataItem { const spellcastTrait = this.spellcastingTrait ? game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.spellcastingTrait].label) : null; - - // Preload all subclass features for acquisition from the cache - // todo: make feature acquisition async and replace feature helpers for methods - await fromUuids(this._source.features.map(f => f.item)); - const foundationFeatures = await getFeaturesHTMLData(this.foundationFeatures); const specializationFeatures = await getFeaturesHTMLData(this.specializationFeatures); const masteryFeatures = await getFeaturesHTMLData(this.masteryFeatures); diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index e30bf52d..4dc1c058 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -19,12 +19,12 @@ export class DhLevelup extends foundry.abstract.DataModel { const initialAchievements = i === tier.levels.start ? tier.initialAchievements : {}; const experiences = initialAchievements.experience ? [...Array(initialAchievements.experience.nr).keys()].reduce((acc, _) => { - acc[foundry.utils.randomID()] = { - name: '', - modifier: initialAchievements.experience.modifier - }; - return acc; - }, {}) + acc[foundry.utils.randomID()] = { + name: '', + modifier: initialAchievements.experience.modifier + }; + return acc; + }, {}) : {}; const domainCards = [...Array(tier.domainCardByLevel).keys()].reduce((acc, _) => { @@ -298,9 +298,9 @@ export class DhLevelupLevel extends foundry.abstract.DataModel { experiences: levelData.achievements?.experiences ?? achievements.experiences ?? {}, domainCards: levelData.achievements?.domainCards ? levelData.achievements.domainCards.reduce((acc, card, index) => { - acc[index] = { ...card }; - return acc; - }, {}) + acc[index] = { ...card }; + return acc; + }, {}) : (achievements.domainCards ?? {}), proficiency: levelData.achievements?.proficiency ?? achievements.proficiency ?? null }, diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 31247458..b5e02675 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -1,5 +1,4 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; -import { resetAndRerenderActors } from '../../helpers/utils.mjs'; import { ActionsField } from '../fields/actionField.mjs'; const currencyField = (initial, label, icon) => @@ -210,7 +209,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { } this.refreshConfig(); - resetAndRerenderActors(); + this.#resetActors(); } /** Update config values based on homebrew data. Make sure the references don't change */ @@ -231,6 +230,29 @@ export default class DhHomebrew extends foundry.abstract.DataModel { }); } } + + /** + * Triggers a reset and non-forced re-render on all given actors (if given) + * or all world actors and actors in all scenes to show immediate results for a changed setting. + */ + #resetActors() { + const actors = new Set( + [ + game.actors.contents, + game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? []) + ].flat() + ); + for (const actor of actors) { + for (const app of Object.values(actor.apps)) { + for (const element of app.element?.querySelectorAll('prose-mirror.active')) { + element.open = false; // This triggers a save + } + } + + actor.reset(); + actor.render(); + } + } } export class Resource extends foundry.abstract.DataModel { diff --git a/module/data/settings/Metagaming.mjs b/module/data/settings/Metagaming.mjs index 3694e09e..2bb5afdf 100644 --- a/module/data/settings/Metagaming.mjs +++ b/module/data/settings/Metagaming.mjs @@ -1,5 +1,3 @@ -import { resetAndRerenderActors } from '../../helpers/utils.mjs'; - export default class DhMetagaming extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -8,24 +6,7 @@ export default class DhMetagaming extends foundry.abstract.DataModel { initial: false, label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label', hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint' - }), - hidePartyStats: new fields.StringField({ - initial: 'never', - label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hidePartyStats.label', - hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hidePartyStats.hint', - required: true, - nullable: false, - choices: { - never: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hidePartyStats.choices.never', - players: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hidePartyStats.choices.players', - always: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hidePartyStats.choices.always' - } }) }; } - - /** Invoked by the setting when data changes */ - handleChange() { - resetAndRerenderActors(); - } } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index b1d3bd0b..509f5d69 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -1,5 +1,4 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; -import { triggerChatRollFx } from '../helpers/utils.mjs'; import DHRoll from './dhRoll.mjs'; export default class D20Roll extends DHRoll { @@ -225,15 +224,4 @@ export default class D20Roll extends DHRoll { resetFormula() { return (this._formula = this.constructor.getFormula(this.terms)); } - - async reroll(options) { - const result = await super.reroll(options); - if (this instanceof game.system.api.dice.DualityRoll) return result; - - if (options?.liveRoll) { - await triggerChatRollFx([result]); - } - - return result; - } } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index ef810ed7..98fd8401 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -1,5 +1,5 @@ import DamageDialog from '../applications/dialogs/damageDialog.mjs'; -import { parseRallyDice, triggerChatRollFx } from '../helpers/utils.mjs'; +import { parseRallyDice } from '../helpers/utils.mjs'; import DHRoll from './dhRoll.mjs'; export default class DamageRoll extends DHRoll { @@ -18,12 +18,7 @@ export default class DamageRoll extends DHRoll { if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate(); roll._evaluated = true; - - const parts = []; - for (const roll of config.roll) { - parts.push(this.postEvaluate(roll)); - roll.roll = JSON.stringify(roll.roll.toJSON()); - } + const parts = config.roll.map(r => this.postEvaluate(r)); config.damage = this.unifyDamageRoll(parts); } @@ -43,24 +38,25 @@ export default class DamageRoll extends DHRoll { const chatMessage = config.source?.message ? ui.chat.collection.get(config.source.message) : getDocumentClass('ChatMessage').applyMode({}, config.rollMode ?? 'public'); - - const diceRolls = []; if (game.modules.get('dice-so-nice')?.active) { - config.mute = true; const pool = foundry.dice.terms.PoolTerm.fromRolls( - Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) + Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) + ), + diceRoll = Roll.fromTerms([pool]); + await game.dice3d.showForRoll( + diceRoll, + game.user, + true, + chatMessage.whisper?.length > 0 ? chatMessage.whisper : null, + chatMessage.blind ); - diceRolls.push(Roll.fromTerms([pool])); + config.mute = true; } - - await triggerChatRollFx(diceRolls, { - whisper: chatMessage.whisper?.length > 0 ? chatMessage.whisper : null, - blind: chatMessage.blind - }); await super.buildPost(roll, config, message); - if (config.source?.message) { chatMessage.update({ 'system.damage': config.damage }); + + if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); } } @@ -323,10 +319,9 @@ export default class DamageRoll extends DHRoll { const newIndex = parsedDiceTerms[dice].results.length; await term.reroll(`/r1=${termResult.result}`); - const diceRolls = []; if (game.modules.get('dice-so-nice')?.active) { const newResult = parsedDiceTerms[dice].results[newIndex]; - diceRolls.push({ + const diceSoNiceRoll = { _evaluated: true, dice: [ new foundry.dice.terms.Die({ @@ -337,10 +332,11 @@ export default class DamageRoll extends DHRoll { }) ], options: { appearance: {} } - }); + }; + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); } - await triggerChatRollFx(diceRolls); await parsedRoll.evaluate(); const results = parsedRoll.dice[dice].results.map(result => ({ diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index c28db98f..83dbbaf2 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -1,5 +1,4 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; -import { triggerChatRollFx } from '../helpers/utils.mjs'; export default class DHRoll extends Roll { baseTerms = []; @@ -37,7 +36,6 @@ export default class DHRoll extends Roll { static async buildConfigure(config = {}, message = {}) { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; - config.damageOptions ??= {}; for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; @@ -77,7 +75,9 @@ export default class DHRoll extends Roll { } if (config.skips?.createMessage) { - await triggerChatRollFx([roll]); + if (game.modules.get('dice-so-nice')?.active) { + await game.dice3d.showForRoll(roll, game.user, true); + } } else if (!config.source?.message) { config.message = await this.toMessage(roll, config); } @@ -85,7 +85,6 @@ export default class DHRoll extends Roll { static postEvaluate(roll, config = {}) { return { - ...roll.options.roll, total: roll.total, formula: roll.formula, dice: roll.dice.map(d => ({ @@ -104,9 +103,9 @@ export default class DHRoll extends Roll { if (action?.chatDisplay) { actionDescription = action ? await foundry.applications.ux.TextEditor.implementation.enrichHTML(action.description, { - relativeTo: config.data, - rollData: config.data.getRollData?.() ?? {} - }) + relativeTo: config.data, + rollData: config.data.getRollData?.() ?? {} + }) : null; config.actionChatMessageHandled = true; } @@ -142,8 +141,8 @@ export default class DHRoll extends Roll { const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming); const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options }); return foundry.applications.handlebars.renderTemplate(template, { - roll: this, ...chatData, + roll: this, parent: chatData.parent, targetMode: chatData.targetMode, areas: chatData.action?.areas, diff --git a/module/dice/die/dualityDie.mjs b/module/dice/die/dualityDie.mjs index cc7ee75e..83229425 100644 --- a/module/dice/die/dualityDie.mjs +++ b/module/dice/die/dualityDie.mjs @@ -1,4 +1,4 @@ -import { updateResourcesForDualityReroll } from '../helpers.mjs'; +import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; export default class DualityDie extends foundry.dice.terms.Die { constructor(options) { @@ -12,6 +12,24 @@ export default class DualityDie extends foundry.dice.terms.Die { return roll.withHope ? 1 : roll.withFear ? -1 : 0; } + #updateResources(oldDuality, newDuality, actor) { + const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return; + + const updates = []; + const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0); + const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0); + const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0); + + if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true }); + if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true }); + if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true }); + + const resourceUpdates = new ResourceUpdateMap(actor); + resourceUpdates.addResources(updates); + resourceUpdates.updateResources(); + } + async reroll(modifier, options) { const oldDuality = this.#getDualityState(options.liveRoll.roll); await super.reroll(modifier, options); @@ -39,7 +57,7 @@ export default class DualityDie extends foundry.dice.terms.Die { if (options.liveRoll.isReaction) return; const newDuality = this.#getDualityState(options.liveRoll.roll); - updateResourcesForDualityReroll(oldDuality, newDuality, options.liveRoll.actor); + this.#updateResources(oldDuality, newDuality, options.liveRoll.actor); } } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 1cfed094..5e03a680 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -1,8 +1,6 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20Roll from './d20Roll.mjs'; import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; -import { getDiceSoNicePresets } from '../config/generalConfig.mjs'; -import { updateResourcesForDualityReroll } from './helpers.mjs'; export default class DualityRoll extends D20Roll { _advantageNumber = 1; @@ -109,10 +107,10 @@ export default class DualityRoll extends D20Roll { const label = this.guaranteedCritical ? 'DAGGERHEART.GENERAL.guaranteedCriticalSuccess' : this.isCritical - ? 'DAGGERHEART.GENERAL.criticalSuccess' - : this.withHope - ? 'DAGGERHEART.GENERAL.hope' - : 'DAGGERHEART.GENERAL.fear'; + ? 'DAGGERHEART.GENERAL.criticalSuccess' + : this.withHope + ? 'DAGGERHEART.GENERAL.hope' + : 'DAGGERHEART.GENERAL.fear'; return game.i18n.localize(label); } @@ -132,34 +130,32 @@ export default class DualityRoll extends D20Roll { } createBaseDice() { - this.terms = [this.terms[0], this.terms[1], this.terms[2]]; + if ( + this.dice[0] instanceof game.system.api.dice.diceTypes.HopeDie && + this.dice[1] instanceof game.system.api.dice.diceTypes.FearDie + ) { + this.terms = [this.terms[0], this.terms[1], this.terms[2]]; + return; + } this.terms[0] = new game.system.api.dice.diceTypes.HopeDie({ - faces: this.terms[0]?.faces ?? this.data.rules.dualityRoll?.defaultHopeDice ?? 12 + faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12 }); this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' }); this.terms[2] = new game.system.api.dice.diceTypes.FearDie({ - faces: this.terms[2]?.faces ?? this.data.rules.dualityRoll?.defaultFearDice ?? 12 + faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12 }); } applyAdvantage() { - const advDieClass = this.hasAdvantage - ? game.system.api.dice.diceTypes.AdvantageDie - : this.hasDisadvantage - ? game.system.api.dice.diceTypes.DisadvantageDie - : null; - if (advDieClass) { - const advDie = new advDieClass({ faces: this.advantageFaces, number: this.advantageNumber }); - if (this.terms.length < 4) { - if (this.advantageNumber > 1) advDie.modifiers = ['kh']; - this.terms.push( - new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), - advDie - ); - } else { - this.terms[4] = advDie; - } + if (this.hasAdvantage || this.hasDisadvantage) { + const dieFaces = this.advantageFaces, + advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber }); + if (this.advantageNumber > 1) advDie.modifiers = ['kh']; + this.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), + advDie + ); } if (this.rallyFaces) this.terms.push( @@ -384,40 +380,4 @@ export default class DualityRoll extends D20Roll { if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id); } } - - async reroll(options) { - const oldDuality = this.withHope ? 1 : this.withFear ? -1 : 0; - const rerolled = DualityRoll.fromData((await super.reroll(options)).toJSON()); - - if (options?.liveRoll) { - if (game.modules.get('dice-so-nice')?.active) { - const diceAppearance = await getDiceSoNicePresets( - rerolled, - rerolled.dHope.denomination, - rerolled.dFear.denomination - ); - rerolled.dHope.options.appearance = diceAppearance.hope.appearance; - rerolled.dFear.options.appearance = diceAppearance.fear.appearance; - if (rerolled.dAdvantage) rerolled.dAdvantage.options.appearance = diceAppearance.advantage.appearance; - if (rerolled.dDisadvantage) - rerolled.dDisadvantage.options.appearance = diceAppearance.disadvantage.appearance; - - await game.dice3d.showForRoll(rerolled, game.user, true); - } else { - foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); - } - - if (this.options.actionType === 'reaction') return; - - const newDuality = rerolled.withHope ? 1 : rerolled.withFear ? -1 : 0; - const actor = await foundry.utils.fromUuid(this.options.source.actor); - updateResourcesForDualityReroll(oldDuality, newDuality, actor); - } - - return rerolled; - } - - fromJSON(json) { - return super.fromJSON(json); - } } diff --git a/module/dice/helpers.mjs b/module/dice/helpers.mjs deleted file mode 100644 index 35adb8b7..00000000 --- a/module/dice/helpers.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { ResourceUpdateMap } from '../data/action/baseAction.mjs'; - -export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) { - const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); - if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return; - - const updates = []; - const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0); - const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0); - const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0); - - if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true }); - if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true }); - if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true }); - - const resourceUpdates = new ResourceUpdateMap(actor); - resourceUpdates.addResources(updates); - resourceUpdates.updateResources(); -} diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 3518210b..f9239a90 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -175,8 +175,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { return model instanceof documentClass ? model : model.parent - ? this.#resolveParentDocument(model.parent, documentClass) - : null; + ? this.#resolveParentDocument(model.parent, documentClass) + : null; } static getChangeValue(model, change, effect) { diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index fb10435f..eb57a186 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,4 +1,4 @@ -import { emitGMUpdate, GMUpdateEvent } from '../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; import { createScrollText, damageKeyToNumber, getDamageKey, createShallowProxy } from '../helpers/utils.mjs'; @@ -65,11 +65,6 @@ export default class DhpActor extends Actor { }; } - static createDialog(data, createOptions, options, renderOptions) { - options.classes = [options.classes ?? [], 'actor-create'].flat(); // handled in hook - return super.createDialog(data, createOptions, options, renderOptions); - } - /* -------------------------------------------- */ /** @inheritDoc */ @@ -114,33 +109,13 @@ export default class DhpActor extends Actor { }); } - if (this.type === 'npc') { - Object.assign(update, { - prototypeToken: { - disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY - } - }); - } - this.updateSource(update); } - /** Perform a render, debounced in order to prevent overloading repeat render requests */ - renderDebounced = foundry.utils.debounce(options => { - return this.render(options); - }, 10); - - _onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) { - super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId); - for (const party of this.parties) { - party.renderDebounced({ parts: ['partyMembers'] }); - } - } - _onUpdate(changes, options, userId) { super._onUpdate(changes, options, userId); for (const party of this.parties) { - party.renderDebounced({ parts: ['partyMembers'] }); + party.render({ parts: ['partyMembers'] }); } } @@ -159,20 +134,17 @@ export default class DhpActor extends Actor { _onDelete(options, userId) { super._onDelete(options, userId); for (const party of this.parties) { - party.renderDebounced({ parts: ['partyMembers'] }); + party.render({ parts: ['partyMembers'] }); } } async updateLevel(newLevel) { if (!['character', 'companion'].includes(this.type) || newLevel === this.system.levelData.level.changed) return; - const tiers = Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers); - const maxLevel = tiers.reduce((acc, tier) => Math.max(acc, tier.levels.end), 0); - const multiclassMinLevel = Math.min( - maxLevel, - ...tiers.filter(t => t.options.multiclass).map(t => t.levels.start) - ); if (newLevel > this.system.levelData.level.current) { + const maxLevel = Object.values( + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers + ).reduce((acc, tier) => Math.max(acc, tier.levels.end), 0); if (newLevel > maxLevel) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.tooHighLevel')); } @@ -247,19 +219,18 @@ export default class DhpActor extends Actor { this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass }); } - // Remove multiclass if we're removing a multiclass feature or if we're below the multiclass minimum level - // Multclasses cannot be manually removed on the sheet, so this allows recovering in the case of errors - if (multiclass || newLevel < multiclassMinLevel) { - const multiclassItems = this.items.filter( - x => - x.uuid === multiclass?.itemUuid || - x.system.isMulticlass || - (['class', 'subclass'].includes(x.system.originItemType) && x.system.multiclassOrigin) + if (multiclass) { + const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); + const multiclassFeatures = this.items.filter( + x => x.system.originItemType === 'class' && x.system.multiclassOrigin + ); + const subclassFeatures = this.items.filter( + x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin ); this.deleteEmbeddedDocuments( 'Item', - multiclassItems.map(x => x.id) + [multiclassItem, ...multiclassFeatures, ...subclassFeatures].map(x => x.id) ); this.update({ @@ -298,7 +269,6 @@ export default class DhpActor extends Actor { async levelUp(levelupData) { const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto; - const getStatsWithSource = document => ({ ...(document._stats ?? {}), compendiumSource: document.uuid }); const levelups = {}; for (var levelKey of Object.keys(levelupData)) { @@ -411,8 +381,8 @@ export default class DhpActor extends Actor { const embeddedItem = await this.createEmbeddedDocuments('Item', [ { ...multiclassData, - uuid: multiclassItem.uuid, // todo: replace with setting an id and using keepId - _stats: getStatsWithSource(multiclassItem), + uuid: multiclassItem.uuid, + _stats: multiclassItem._stats, system: { ...multiclassData.system, features: multiclassData.system.features.filter(x => x.type !== 'hope'), @@ -425,8 +395,8 @@ export default class DhpActor extends Actor { await this.createEmbeddedDocuments('Item', [ { ...subclassData, - uuid: subclassItem.uuid, // todo: replace with setting an id and using keepId - _stats: getStatsWithSource(subclassItem), + uuid: subclassItem.uuid, + _stats: subclassItem._stats, system: { ...subclassData.system, isMulticlass: true @@ -446,8 +416,8 @@ export default class DhpActor extends Actor { const embeddedItem = await this.createEmbeddedDocuments('Item', [ { ...cardData, - uuid: cardItem.uuid, // todo: replace with setting an id and using keepId - _stats: getStatsWithSource(cardItem), + uuid: cardItem.uuid, + _stats: cardItem._stats, system: { ...cardData.system, inVault: true @@ -468,7 +438,8 @@ export default class DhpActor extends Actor { const embeddedItem = await this.createEmbeddedDocuments('Item', [ { ...cardData, - _stats: getStatsWithSource(cardItem), + uuid: cardItem.uuid, + _stats: cardItem._stats, system: { ...cardData.system, inVault: true @@ -856,7 +827,7 @@ export default class DhpActor extends Actor { const u = updates[key]; if (key === 'items') { Object.values(u).forEach(async item => { - await emitGMUpdate( + await emitAsGM( GMUpdateEvent.UpdateDocument, item.target.update.bind(item.target), item.resources, @@ -865,7 +836,7 @@ export default class DhpActor extends Actor { }); } else { if (Object.keys(u.resources).length > 0) { - await emitGMUpdate( + await emitAsGM( GMUpdateEvent.UpdateDocument, u.target.update.bind(u.target), u.resources, diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 480f8c69..2e20fb87 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,4 +1,4 @@ -import { emitGMUpdate, emitGMCreate, GMUpdateEvent } from '../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; export default class DhpChatMessage extends foundry.documents.ChatMessage { targetHook = null; @@ -9,9 +9,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { actor && this.isContentVisible ? actor : { - img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg', - name: '' - }; + img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg', + name: '' + }; /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ const html = await super.renderHTML({ actor: actorData, author: this.author }); @@ -183,11 +183,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (pendingingSaves.length) { const confirm = await foundry.applications.api.DialogV2.confirm({ window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') }, - content: ` -

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}

-

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}

-

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}

- ` + content: `

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}

` }); if (!confirm) return; } @@ -218,7 +214,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { const action = this.system.action; if (!action || !action?.hasSave) return; game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result => - emitGMUpdate( + emitAsGM( GMUpdateEvent.UpdateSaveMessage, game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind( action, @@ -251,24 +247,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { const targets = this.filterPermTargets(this.system.hitTargets), config = foundry.utils.deepClone(this.system); config.event = event; - if (targets.length === 0) - return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); - else if (config.hasSave) { - const pendingingSaves = targets.filter(t => t.saved.success === null); - if (pendingingSaves.length) { - const confirm = await foundry.applications.api.DialogV2.confirm({ - window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') }, - content: ` -

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}

-

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}

-

${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}

- ` - }); - if (!confirm) return; - } - } - + ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); this.consumeOnSuccess(); this.system.action?.workflow.get('effects')?.execute(config, targets, true); } @@ -279,47 +259,27 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { const { shape: type, size: range } = selectedArea; const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range }); - const scene = game.scenes.get(game.user.viewedScene); - const level = scene.levels.find(x => x.isView); - - const regionData = { - name: selectedArea.name, - levels: level ? [level.id] : [], - shapes: [shapeData], - restriction: { enabled: false, type: 'move', priority: 0 }, - behaviors: - effects.length > 0 - ? [ - { - name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), - type: 'applyActiveEffect', - system: { - effects: effects - } + await canvas.regions.placeRegion( + { + name: selectedArea.name, + shapes: [shapeData], + restriction: { enabled: false, type: 'move', priority: 0 }, + behaviors: [ + { + name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), + type: 'applyActiveEffect', + system: { + effects: effects } - ] - : [], - displayMeasurements: true, - locked: false, - ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE }, - visibility: CONST.REGION_VISIBILITY.ALWAYS - }; - const placeRegion = data => { - canvas.regions.placeRegion(data, { create: true }); - }; - - // Regions with effects must be placed by the GM - if (effects.length > 0 && !game.user.isGM) { - if (!game.users.activeGM) - return ui.notifications.error( - game.i18n.localize('DAGGERHEART.UI.Notifications.behaviorRegionRequiresGM') - ); - - const region = await canvas.regions.placeRegion(regionData, { create: false }); - emitGMCreate('Region', placeRegion, region, scene.id); - } else { - placeRegion(regionData); - } + } + ], + displayMeasurements: true, + locked: false, + ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE }, + visibility: CONST.REGION_VISIBILITY.ALWAYS + }, + { create: true } + ); }; if (this.system.action.areas.length === 1) createArea(this.system.action.areas[0]); diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 4716068d..93aa3b28 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -73,16 +73,13 @@ export default class DHItem extends foundry.documents.Item { /** Returns true if the item can be used */ get usable() { const actor = this.actor; - const pack = actor?.pack ? game.packs.get(actor.pack) : null; - const hasActions = this.system.actionsList?.size || this.system.actionsList?.length; - const isValidType = actor?.type === 'character' || this.type === 'feature'; - return !pack?.locked && this.isOwner && isValidType && hasActions; + const actionsList = this.system.actionsList; + return this.isOwner && actor?.type === 'character' && (actionsList?.size || actionsList?.length); } /** @inheritdoc */ static async createDialog(data = {}, createOptions = {}, options = {}) { const { folders, types, template, context = {}, ...dialogOptions } = options; - dialogOptions.classes = [options.classes ?? [], 'item-create'].flat(); // handled in hook if (types?.length === 0) { throw new Error('The array of sub-types to restrict to must not be empty.'); @@ -98,8 +95,8 @@ export default class DHItem extends foundry.documents.Item { isInventoryItem === true ? 'Inventory Items' //TODO localize : isInventoryItem === false - ? 'Character Items' //TODO localize - : 'Other'; //TODO localize + ? 'Character Items' //TODO localize + : 'Other'; //TODO localize return { value: type, label, group }; } diff --git a/module/documents/scene.mjs b/module/documents/scene.mjs index bf700610..59b8091f 100644 --- a/module/documents/scene.mjs +++ b/module/documents/scene.mjs @@ -20,7 +20,7 @@ export default class DhScene extends Scene { const prototype = tokenDoc.actor?.prototypeToken ?? tokenDoc; this.#sizeSyncBatch.set(tokenDoc.id, { size: tokenSize, - prototypeSize: { width: prototype.width, height: prototype.height, depth: prototype.depth }, + prototypeSize: { width: prototype.width, height: prototype.height }, position: { x: tokenDoc.x, y: tokenDoc.y, elevation: tokenDoc.elevation } }); this.#processSyncBatch(); @@ -36,13 +36,11 @@ export default class DhScene extends Scene { const tokenSize = tokenSizes[size]; const width = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.width; const height = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.height; - const depth = size !== CONFIG.DH.ACTOR.tokenSize.custom.id ? tokenSize : prototypeSize.depth; const updatedPosition = DHToken.getSnappedPositionInSquareGrid(this.grid, position, width, height); return { _id, width, height, - depth, ...updatedPosition }; }); diff --git a/module/documents/token.mjs b/module/documents/token.mjs index 8e91d4f0..4ee7ce05 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -66,8 +66,7 @@ export default class DHToken extends CONFIG.Token.documentClass { if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) { document.updateSource({ width: tokenSize, - height: tokenSize, - depth: tokenSize + height: tokenSize }); } } @@ -91,7 +90,7 @@ export default class DHToken extends CONFIG.Token.documentClass { ) { const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; const tokenSize = tokenSizes[update.system.size]; - if (tokenSize !== this.width || tokenSize !== this.height || tokenSize !== this.depth) { + if (tokenSize !== this.width || tokenSize !== this.height) { this.parent?.syncTokenDimensions(this, update.system.size); } } @@ -324,7 +323,7 @@ export default class DHToken extends CONFIG.Token.documentClass { } let x = 0.5 * bottom; let y = 0.25; - for (let k = width - bottom; k--;) { + for (let k = width - bottom; k--; ) { points.push(x, y); x += 0.5; y -= 0.25; @@ -333,7 +332,7 @@ export default class DHToken extends CONFIG.Token.documentClass { y += 0.25; } points.push(x, y); - for (let k = bottom; k--;) { + for (let k = bottom; k--; ) { y += 0.5; points.push(x, y); x += 0.5; @@ -341,14 +340,14 @@ export default class DHToken extends CONFIG.Token.documentClass { points.push(x, y); } y += 0.5; - for (let k = top; k--;) { + for (let k = top; k--; ) { points.push(x, y); x -= 0.5; y += 0.25; points.push(x, y); y += 0.5; } - for (let k = width - top; k--;) { + for (let k = width - top; k--; ) { points.push(x, y); x -= 0.5; y += 0.25; @@ -357,7 +356,7 @@ export default class DHToken extends CONFIG.Token.documentClass { y -= 0.25; } points.push(x, y); - for (let k = top; k--;) { + for (let k = top; k--; ) { y -= 0.5; points.push(x, y); x -= 0.5; @@ -365,7 +364,7 @@ export default class DHToken extends CONFIG.Token.documentClass { points.push(x, y); } y -= 0.5; - for (let k = bottom; k--;) { + for (let k = bottom; k--; ) { points.push(x, y); x += 0.5; y -= 0.25; diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs index 7678d2c7..3ccff4e2 100644 --- a/module/documents/tokenManager.mjs +++ b/module/documents/tokenManager.mjs @@ -15,7 +15,6 @@ export default class DhTokenManager { if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) { tokenData.width = tokenSize; tokenData.height = tokenSize; - tokenData.depth = tokenSize; } } diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 3e3f4a16..e10dc5fa 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -3,6 +3,7 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager { #wide = false; #bordered = false; + #active = false; async activate(element, options = {}) { const { TextEditor } = foundry.applications.ux; @@ -349,9 +350,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti async getBattlepointHTML(combatId) { const combat = game.combats.get(combatId); const adversaries = - combat.turns - ?.filter(x => x.actor?.isNPC && x.token.disposition === CONST.TOKEN_DISPOSITIONS.HOSTILE) - ?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; + combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; const characters = combat.turns?.filter(x => !x.isNPC && x.actor) ?? []; const nrCharacters = characters.length; diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs index db0e8729..e3f9c42a 100644 --- a/module/enrichers/DamageEnricher.mjs +++ b/module/enrichers/DamageEnricher.mjs @@ -1,7 +1,7 @@ import { parseInlineParams } from './parser.mjs'; export default function DhDamageEnricher(match, _options) { - const { value, type, inline } = parseInlineParams(match[1], { first: 'value' }); + const { value, type, inline } = parseInlineParams(match[1]); if (!value || !type) return match[0]; return getDamageMessage(value, type, inline, match[0]); } @@ -59,7 +59,7 @@ export const renderDamageButton = async event => { { formula: value, applyTo: CONFIG.DH.GENERAL.healingTypes.hitPoints.id, - damageTypes: type + type: type } ] }; diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs index a7db01a4..5b66179f 100644 --- a/module/enrichers/DualityRollEnricher.mjs +++ b/module/enrichers/DualityRollEnricher.mjs @@ -15,8 +15,8 @@ function getDualityMessage(roll, flavor) { (roll?.trait ? game.i18n.format('DAGGERHEART.GENERAL.rollWith', { roll: trait }) : roll?.reaction - ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') - : game.i18n.localize('DAGGERHEART.GENERAL.duality')); + ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') + : game.i18n.localize('DAGGERHEART.GENERAL.duality')); const dataLabel = trait ? game.i18n.localize(abilities[roll.trait].label) @@ -25,14 +25,14 @@ function getDualityMessage(roll, flavor) { const advantage = roll?.advantage ? CONFIG.DH.ACTIONS.advantageState.advantage.value : roll?.disadvantage - ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value - : undefined; + ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value + : undefined; const advantageLabel = advantage === CONFIG.DH.ACTIONS.advantageState.advantage.value ? 'Advantage' : advantage === CONFIG.DH.ACTIONS.advantageState.disadvantage.value - ? 'Disadvantage' - : undefined; + ? 'Disadvantage' + : undefined; const dualityElement = document.createElement('span'); dualityElement.innerHTML = ` diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs index bbe93b17..8db3ec14 100644 --- a/module/enrichers/TemplateEnricher.mjs +++ b/module/enrichers/TemplateEnricher.mjs @@ -8,8 +8,8 @@ export default function DhTemplateEnricher(match, _options) { const range = params.range && Number.isNaN(Number(params.range)) ? Object.values(CONFIG.DH.GENERAL.templateRanges).find( - x => x.id.toLowerCase() === params.range || x.short === params.range - )?.id + x => x.id.toLowerCase() === params.range || x.short === params.range + )?.id : params.range; if (!CONFIG.DH.GENERAL.templateTypes[type] || !range) return match[0]; diff --git a/module/enrichers/parser.mjs b/module/enrichers/parser.mjs index 76ea0b73..365caec9 100644 --- a/module/enrichers/parser.mjs +++ b/module/enrichers/parser.mjs @@ -8,7 +8,7 @@ export function parseInlineParams(paramString, { first } = {}) { const parts = paramString.split('|').map(x => x.trim()); const params = {}; for (const [idx, param] of parts.entries()) { - if (first && idx === 0 && !param.includes(':')) { + if (first && idx === 0) { params[first] = param; } else { const parts = param.split(':'); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index af6c2777..faa046ff 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -108,9 +108,9 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {} const options = Array.isArray(baseOptions) ? baseOptions : Object.keys(baseOptions).map(optionKey => ({ - ...baseOptions[optionKey], - id: optionKey - })); + ...baseOptions[optionKey], + id: optionKey + })); const tagifyElement = new Tagify(element, { tagTextProp: 'name', @@ -449,9 +449,14 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) { effects: data.effects?.map(effect => effect.toObject()) }); } + await actor.createEmbeddedDocuments('Item', effectData); } +export const slugify = name => { + return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', ''); +}; + export function shuffleArray(array) { let currentIndex = array.length; while (currentIndex != 0) { @@ -605,8 +610,8 @@ export function calculateExpectedValue(formulaOrTerms) { const terms = Array.isArray(formulaOrTerms) ? formulaOrTerms : typeof formulaOrTerms === 'string' - ? parseTermsFromSimpleFormula(formulaOrTerms) - : [formulaOrTerms]; + ? parseTermsFromSimpleFormula(formulaOrTerms) + : [formulaOrTerms]; return terms.reduce((r, t) => r + (t.bonus ?? 0) + (t.diceQuantity ? (t.diceQuantity * (t.faces + 1)) / 2 : 0), 0); } @@ -656,8 +661,8 @@ export async function RefreshFeatures( 'resource.value': increasing ? 0 : game.system.api.documents.DhActiveEffect.effectSafeEval( - Roll.replaceFormulaData(item.system.resource.max, actor.getRollData()) - ) + Roll.replaceFormulaData(item.system.resource.max, actor.getRollData()) + ) }; } if (item.system.metadata?.hasActions) { @@ -788,26 +793,6 @@ export function getArmorSources(actor) { }); } -/** - * Triggers a reset and non-forced re-render on all given actors (if given) - * or all world actors and actors in all scenes to show immediate results for a changed setting. - */ -export function resetAndRerenderActors() { - const actors = new Set( - [game.actors.contents, game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])].flat() - ); - for (const actor of actors) { - for (const app of Object.values(actor.apps)) { - for (const element of app.element?.querySelectorAll('prose-mirror.active')) { - element.open = false; // This triggers a save - } - } - - actor.reset(); - actor.render(); - } -} - /** * Returns an array sorted by a function that returns a thing to compare, or an array to compare in order * Similar to lodash's sortBy function. @@ -856,66 +841,3 @@ export function createShallowProxy(obj) { } }); } - -export function camelize(str) { - return str - .replace(/(?:^\w|[A-Z]|\b\w)/g, (part, index) => { - return index === 0 ? part.toLowerCase() : part.toUpperCase(); - }) - .replace(/\s+/g, ''); -} - -/** Bulk load a list of documents using uuids. Returns the documents in the same order */ -export async function fromUuids(uuids) { - // Set up base entries. Each step works on a sublist of these objects - const entries = uuids.map(uuid => ({ - uuid, - parsed: foundry.utils.parseUuid(uuid), - value: foundry.utils.fromUuidSync(uuid) - })); - - // Handle missing uuids for embedded documents first - // A value may be index data, so we check if its a document - const packEmbeddedEntries = entries.filter( - e => - !(e.value instanceof Document) && - e.parsed && - e.parsed.collection instanceof foundry.documents.collections.CompendiumCollection && - e.parsed.embedded.length > 0 - ); - await Promise.all( - packEmbeddedEntries.map(async e => { - e.value = await fromUuid(e.uuid); - return true; - }) - ); - - // Handle missing top level pack stuff, by batching per pack - const missingTopLevel = entries.filter(e => !(e.value instanceof Document) && e.value?.pack); - for (const packGroup of Object.values(Object.groupBy(missingTopLevel, e => e.value.pack))) { - const pack = game.packs.get(packGroup[0].value.pack); - if (!pack) continue; - - const ids = packGroup.map(p => p.parsed?.id).filter(id => !!id); - const documents = await pack.getDocuments({ _id__in: ids }); - for (const p of packGroup) { - p.value = documents.find(d => d.id === p.parsed.id) ?? p.value; - } - } - - return entries.map(e => e.value); -} -/** - * Triggers DiceSoNice rolls or dice roll audio for rolls. Not used for duality rolls. - * @param { Roll[] } rolls - * @return { void } - */ -export async function triggerChatRollFx(rolls, options = { whisper: false, blind: false }) { - const { whisper, blind } = options; - if (game.modules.get('dice-so-nice')?.active) { - const rerollPromises = rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true, whisper, blind)); - await Promise.allSettled(rerollPromises); - } else { - foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); - } -} diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index b718a127..b4c446b2 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -24,8 +24,8 @@ export async function runMigrations() { const { originItemType, isMulticlass, identifier } = item.system; const base = originItemType ? actor.items.find( - x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass) - ) + x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass) + ) : null; if (base) { const feature = base.system.features.find(x => x.item && x.item.uuid === item.uuid); diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index a66323c7..41cab011 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -91,10 +91,7 @@ const registerMenuSettings = () => { game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, { scope: 'world', config: false, - type: DhMetagaming, - onChange: value => { - value.handleChange(); - } + type: DhMetagaming }); game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, { diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index de9bf00c..8fed346d 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -6,9 +6,6 @@ export function handleSocketEvent({ action = null, data = {} } = {}) { case socketEvent.GMUpdate: Hooks.callAll(socketEvent.GMUpdate, data); break; - case socketEvent.GMCreate: - Hooks.callAll(socketEvent.GMCreate, data); - break; case socketEvent.DhpFearUpdate: Hooks.callAll(socketEvent.DhpFearUpdate); break; @@ -28,7 +25,6 @@ export function handleSocketEvent({ action = null, data = {} } = {}) { export const socketEvent = { GMUpdate: 'DhGMUpdate', - GMCreate: 'DhGMCreate', Refresh: 'DhRefresh', DhpFearUpdate: 'DhFearUpdate', DowntimeTrigger: 'DowntimeTrigger', @@ -60,14 +56,14 @@ export const registerSocketHooks = () => { const document = data.uuid ? await fromUuid(data.uuid) : null; switch (data.action) { case GMUpdateEvent.UpdateDocument: - if (document && data.data) await document.update(data.data); + if (document && data.update) await document.update(data.update); break; case GMUpdateEvent.UpdateEffect: - if (document && data.data) - await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.data); + if (document && data.update) + await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.update); break; case GMUpdateEvent.UpdateSetting: - await game.settings.set(CONFIG.DH.id, data.uuid, data.data); + await game.settings.set(CONFIG.DH.id, data.uuid, data.update); break; case GMUpdateEvent.UpdateFear: await game.settings.set( @@ -77,22 +73,22 @@ export const registerSocketHooks = () => { 0, Math.min( game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - data.data + data.update ) ) ); break; case GMUpdateEvent.UpdateCountdowns: - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data.data); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data.update); Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); break; case GMUpdateEvent.UpdateSaveMessage: - const message = game.messages.get(data.data.message); + const message = game.messages.get(data.update.message); if (!message) return; game.system.api.fields.ActionFields.SaveField.updateSaveMessage( - data.data.result, + data.update.result, message, - data.data.token + data.update.token ); break; } @@ -106,17 +102,6 @@ export const registerSocketHooks = () => { } } }); - - Hooks.on(socketEvent.GMCreate, async ({ data, documentType, scene }) => { - if (!game.user.isGM) return; - - switch (documentType) { - default: - const cls = getDocumentClass(documentType); - cls.create(data, { parent: game.scenes.get(scene) }); - break; - } - }); }; export const registerUserQueries = () => { @@ -124,21 +109,18 @@ export const registerUserQueries = () => { CONFIG.queries.reactionRoll = game.system.api.fields.ActionFields.SaveField.rollSaveQuery; }; -export const emitGMUpdate = async (eventName, callback, update, uuid = null, refresh = null) => { - return await emitAsGM(socketEvent.GMUpdate, { action: eventName, callback, data: update, uuid, refresh }); -}; - -export const emitGMCreate = async (documentType, callback, data, scene) => { - return await emitAsGM(socketEvent.GMCreate, { documentType, callback, data, scene }); -}; - -export const emitAsGM = async (event, data = { callback: () => {}, data: {} }) => { +export const emitAsGM = async (eventName, callback, update, uuid = null, refresh = null) => { if (!game.user.isGM) { return await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: event, - data: data + action: socketEvent.GMUpdate, + data: { + action: eventName, + uuid, + update, + refresh + } }); - } else return data.callback(data.data); + } else return callback(update); }; export const emitAsOwner = (eventName, userId, args) => { diff --git a/package-lock.json b/package-lock.json index dee096eb..47c5dede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,21 @@ "autocompleter": "^9.3.2", "gulp": "^5.0.0", "gulp-less": "^5.0.0", - "gulp-sourcemaps": "^3.0.0", "rollup": "^4.40.0" }, "devDependencies": { - "@eslint/js": "^10.0.1", "@foundryvtt/foundryvtt-cli": "^1.0.2", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", - "@stylistic/eslint-plugin": "^5.10.0", "concurrently": "^8.2.2", "eslint": "^10.2.1", + "eslint-plugin-prettier": "^5.5.5", "globals": "^17.5.0", "husky": "^9.1.7", "lint-staged": "^16.4.0", "postcss": "^8.4.32", - "rollup-plugin-postcss": "^4.0.2", - "typescript": "^6.0.3", - "typescript-eslint": "^8.60.1" + "prettier": "^3.5.3", + "rollup-plugin-postcss": "^4.0.2" } }, "node_modules/@babel/runtime": { @@ -160,27 +157,6 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, "node_modules/@eslint/object-schema": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", @@ -226,132 +202,6 @@ "node": ">17.0.0" } }, - "node_modules/@gulp-sourcemaps/identity-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", - "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", - "license": "MIT", - "dependencies": { - "acorn": "^6.4.1", - "normalize-path": "^3.0.0", - "postcss": "^7.0.16", - "source-map": "^0.6.0", - "through2": "^3.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "license": "ISC" - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "node_modules/@gulp-sourcemaps/map-sources": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", - "license": "MIT", - "dependencies": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "license": "MIT", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -443,6 +293,19 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "25.0.8", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", @@ -771,58 +634,6 @@ "util": "^0.12.4" } }, - "node_modules/@stylistic/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/types": "^8.56.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "estraverse": "^5.3.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": "^9.0.0 || ^10.0.0" - } - }, - "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@stylistic/eslint-plugin/node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -857,288 +668,6 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", - "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/type-utils": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.60.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", - "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", - "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.60.1", - "@typescript-eslint/types": "^8.60.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", - "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", - "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", - "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", - "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", - "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.60.1", - "@typescript-eslint/tsconfig-utils": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", - "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", - "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@yaireo/tagify": { "version": "4.35.1", "resolved": "https://registry.npmjs.org/@yaireo/tagify/-/tagify-4.35.1.tgz", @@ -1365,18 +894,6 @@ "node": ">= 10.13.0" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/autocompleter": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-9.3.2.tgz", @@ -1965,12 +1482,6 @@ "node": ">= 10.13.0" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1985,17 +1496,6 @@ "node": ">= 8" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -2167,19 +1667,6 @@ "node": ">=8.0.0" } }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -2197,11 +1684,10 @@ } }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -2214,35 +1700,6 @@ } } }, - "node_modules/debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", - "license": "MIT", - "dependencies": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" - } - }, - "node_modules/debug-fabulous/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2284,15 +1741,6 @@ "node": ">=0.10.0" } }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -2457,58 +1905,6 @@ "node": ">= 0.4" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2586,6 +1982,37 @@ } } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", @@ -2679,21 +2106,6 @@ "node": ">=6" } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", @@ -2764,16 +2176,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", @@ -2792,15 +2194,6 @@ "node": ">=0.10.0" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2825,6 +2218,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -2861,24 +2261,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3438,86 +2820,6 @@ "node": ">=6" } }, - "node_modules/gulp-sourcemaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", - "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", - "license": "ISC", - "dependencies": { - "@gulp-sourcemaps/identity-map": "^2.0.1", - "@gulp-sourcemaps/map-sources": "^1.0.0", - "acorn": "^6.4.1", - "convert-source-map": "^1.0.0", - "css": "^3.0.0", - "debug-fabulous": "^1.0.0", - "detect-newline": "^2.0.0", - "graceful-fs": "^4.0.0", - "source-map": "^0.6.0", - "strip-bom-string": "^1.0.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gulp-sourcemaps/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/gulp-sourcemaps/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, - "node_modules/gulp-sourcemaps/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-sourcemaps/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/gulp-sourcemaps/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/gulp-sourcemaps/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, "node_modules/gulplog": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-2.2.0.tgz", @@ -3946,12 +3248,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "license": "MIT" - }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -4037,12 +3333,6 @@ "node": ">=0.10.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4357,15 +3647,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -4411,25 +3692,6 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, - "node_modules/memoizee": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", - "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "es5-ext": "^0.10.64", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -4517,7 +3779,8 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/mute-stdout": { "version": "2.0.0", @@ -4583,12 +3846,6 @@ "node": ">= 4.4.x" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "license": "ISC" - }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -5542,11 +4799,33 @@ "node": ">= 0.8.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } }, "node_modules/promise.series": { "version": "0.2.0", @@ -6097,6 +5376,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -6110,17 +5390,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "license": "MIT", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, "node_modules/sparkles": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", @@ -6246,15 +5515,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/style-inject": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", @@ -6350,6 +5610,22 @@ "node": ">= 10" } }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -6374,19 +5650,6 @@ "readable-stream": "3" } }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/tinyexec": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", @@ -6397,23 +5660,6 @@ "node": ">=18" } }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6445,30 +5691,11 @@ "tree-kill": "cli.js" } }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "license": "ISC" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6482,44 +5709,6 @@ "node": ">= 0.8.0" } }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz", - "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.60.1", - "@typescript-eslint/parser": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -6800,15 +5989,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index ede90401..52bb4ce7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "autocompleter": "^9.3.2", "gulp": "^5.0.0", "gulp-less": "^5.0.0", - "gulp-sourcemaps": "^3.0.0", "rollup": "^4.40.0" }, "scripts": { @@ -24,20 +23,18 @@ "prepare": "husky" }, "devDependencies": { - "@eslint/js": "^10.0.1", "@foundryvtt/foundryvtt-cli": "^1.0.2", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", - "@stylistic/eslint-plugin": "^5.10.0", "concurrently": "^8.2.2", "eslint": "^10.2.1", + "eslint-plugin-prettier": "^5.5.5", "globals": "^17.5.0", "husky": "^9.1.7", "lint-staged": "^16.4.0", "postcss": "^8.4.32", - "rollup-plugin-postcss": "^4.0.2", - "typescript": "^6.0.3", - "typescript-eslint": "^8.60.1" + "prettier": "^3.5.3", + "rollup-plugin-postcss": "^4.0.2" }, "lint-staged": { "**/*": "eslint --fix" diff --git a/styles/daggerheart.less b/styles/daggerheart.less index 4da2e043..187402fb 100755 --- a/styles/daggerheart.less +++ b/styles/daggerheart.less @@ -1,11 +1,19 @@ @import './less/sheets/index.less'; @import './less/sheets-settings/index.less'; + @import './less/dialog/index.less'; -@import './less/hud/index.less'; -@import './less/utils/index.less'; + +@import './less//hud/index.less'; + +@import './less/utils/colors.less'; +@import './less/utils/fonts.less'; + @import './less/global/index.less'; + @import './less/ui/index.less'; + @import './less/ux/index.less'; @import '../build/tagify.css'; +@import './less/utils/mixin.less'; diff --git a/styles/less/dialog/actions/index.less b/styles/less/dialog/actions/index.less deleted file mode 100644 index e9cc0401..00000000 --- a/styles/less/dialog/actions/index.less +++ /dev/null @@ -1 +0,0 @@ -@import "./action-list.less"; \ No newline at end of file diff --git a/styles/less/dialog/attribution/index.less b/styles/less/dialog/attribution/index.less deleted file mode 100644 index 2f8eaf45..00000000 --- a/styles/less/dialog/attribution/index.less +++ /dev/null @@ -1 +0,0 @@ -@import "./sheet.less"; \ No newline at end of file diff --git a/styles/less/dialog/beastform/index.less b/styles/less/dialog/beastform/index.less deleted file mode 100644 index 2f8eaf45..00000000 --- a/styles/less/dialog/beastform/index.less +++ /dev/null @@ -1 +0,0 @@ -@import "./sheet.less"; \ No newline at end of file diff --git a/styles/less/dialog/beastform/sheet.less b/styles/less/dialog/beastform/sheet.less index 6d1a8a2a..0e1fe746 100644 --- a/styles/less/dialog/beastform/sheet.less +++ b/styles/less/dialog/beastform/sheet.less @@ -43,7 +43,7 @@ text-align: center; font-size: var(--font-size-16); margin: 0 4px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url('../assets/parchments/dh-parchment-dark.png'); @@ -58,7 +58,7 @@ position: relative; display: flex; justify-content: center; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; cursor: pointer; width: 120px; @@ -164,7 +164,7 @@ .hybrid-data { padding: 0 2px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url('../assets/parchments/dh-parchment-dark.png'); @@ -191,7 +191,7 @@ flex-direction: column; gap: 4px; padding: 0 4px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url('../assets/parchments/dh-parchment-dark.png'); @@ -226,7 +226,7 @@ gap: 4px; .trait-card { - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; padding: 2px; opacity: 0.4; diff --git a/styles/less/dialog/character-creation/index.less b/styles/less/dialog/character-creation/index.less deleted file mode 100644 index adf8d57a..00000000 --- a/styles/less/dialog/character-creation/index.less +++ /dev/null @@ -1,4 +0,0 @@ -@import "./sheet.less"; -@import "./creation-action-footer.less"; -@import "./selections-container.less"; -@import "./tab-navigation.less"; \ No newline at end of file diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index ebf12eda..2bbac484 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -79,7 +79,7 @@ font-weight: bold; padding: 0 2px; background-image: url(../assets/parchments/dh-parchment-light.png); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@beige, @dark); opacity: 0.4; @@ -114,6 +114,9 @@ .card-preview-container { flex: 1; + } + + .card-preview-container { border-color: light-dark(@dark-blue, @golden); } @@ -203,7 +206,7 @@ height: 16px; width: 110px; min-height: unset; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); color: light-dark(@beige, @beige); background-color: light-dark(var(--color-warm-3), var(--color-warm-3)); @@ -230,7 +233,7 @@ .suggested-trait-container { width: 56px; white-space: nowrap; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@beige, @dark); background-image: url('../assets/parchments/dh-parchment-light.png'); @@ -345,7 +348,7 @@ display: flex; justify-content: center; position: relative; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; .nav-section-text { @@ -383,7 +386,7 @@ width: 56px; text-align: center; line-height: 1; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@beige, @dark); background-image: url(../assets/parchments/dh-parchment-light.png); @@ -447,7 +450,7 @@ height: 100%; .simple-equipment { - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 8px; position: relative; display: flex; @@ -466,7 +469,7 @@ top: -8px; font-size: var(--font-size-12); white-space: nowrap; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: @dark; background-image: url('../assets/parchments/dh-parchment-light.png'); diff --git a/styles/less/dialog/character-creation/tab-navigation.less b/styles/less/dialog/character-creation/tab-navigation.less index 85541db7..36b89d5a 100644 --- a/styles/less/dialog/character-creation/tab-navigation.less +++ b/styles/less/dialog/character-creation/tab-navigation.less @@ -7,7 +7,7 @@ border-top: 0; a { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &[disabled] { opacity: 0.4; diff --git a/styles/less/dialog/character-reset/index.less b/styles/less/dialog/character-reset/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/character-reset/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/compendiumBrowserPackDialog/index.less b/styles/less/dialog/compendiumBrowserPackDialog/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/compendiumBrowserPackDialog/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/compendiumBrowserPackDialog/sheet.less b/styles/less/dialog/compendiumBrowserPackDialog/sheet.less index b16f1086..dfe375b5 100644 --- a/styles/less/dialog/compendiumBrowserPackDialog/sheet.less +++ b/styles/less/dialog/compendiumBrowserPackDialog/sheet.less @@ -67,6 +67,7 @@ i { font-size: 18px; + // color: light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/dialog/damage-reduction/damage-reduction-container.less b/styles/less/dialog/damage-reduction/damage-reduction-container.less index 6f7ffb51..e8242bdd 100644 --- a/styles/less/dialog/damage-reduction/damage-reduction-container.less +++ b/styles/less/dialog/damage-reduction/damage-reduction-container.less @@ -81,7 +81,7 @@ .mark-container { cursor: pointer; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; height: 26px; padding: 0 1px; @@ -126,7 +126,7 @@ width: 100%; .chip-inner-container { - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; height: 26px; padding: 0 4px; diff --git a/styles/less/dialog/damage-reduction/index.less b/styles/less/dialog/damage-reduction/index.less deleted file mode 100644 index 0b8e94a8..00000000 --- a/styles/less/dialog/damage-reduction/index.less +++ /dev/null @@ -1,2 +0,0 @@ -@import './sheets.less'; -@import './damage-reduction-container.less'; \ No newline at end of file diff --git a/styles/less/dialog/damage-selection/index.less b/styles/less/dialog/damage-selection/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/damage-selection/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/death-move/index.less b/styles/less/dialog/death-move/index.less deleted file mode 100644 index 8a8a16c4..00000000 --- a/styles/less/dialog/death-move/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './death-move-container.less'; \ No newline at end of file diff --git a/styles/less/dialog/dice-roll/index.less b/styles/less/dialog/dice-roll/index.less deleted file mode 100644 index 8e0af6e0..00000000 --- a/styles/less/dialog/dice-roll/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './roll-selection.less'; \ No newline at end of file diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less index e3551902..a1a01e6b 100644 --- a/styles/less/dialog/dice-roll/roll-selection.less +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -56,7 +56,7 @@ cursor: pointer; padding: 5px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .label { font-style: normal; @@ -129,7 +129,7 @@ cursor: pointer; padding: 5px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .label { font-style: normal; diff --git a/styles/less/dialog/downtime/downtime-container.less b/styles/less/dialog/downtime/downtime-container.less index 33d153fd..eb615ef0 100644 --- a/styles/less/dialog/downtime/downtime-container.less +++ b/styles/less/dialog/downtime/downtime-container.less @@ -37,7 +37,7 @@ .activity-marker { font-size: 0.5rem; flex: none; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); margin-right: 4px; } @@ -55,7 +55,7 @@ .activity-selected-marker { font-size: var(--font-size-14); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url(../assets/parchments/dh-parchment-dark.png); @@ -78,7 +78,7 @@ } .refreshable-container { - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url('../assets/parchments/dh-parchment-dark.png'); diff --git a/styles/less/dialog/downtime/index.less b/styles/less/dialog/downtime/index.less deleted file mode 100644 index 09cc2dfe..00000000 --- a/styles/less/dialog/downtime/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './downtime-container.less'; \ No newline at end of file diff --git a/styles/less/dialog/group-roll-dialog/_common.less b/styles/less/dialog/group-roll-dialog/_common.less new file mode 100644 index 00000000..41573718 --- /dev/null +++ b/styles/less/dialog/group-roll-dialog/_common.less @@ -0,0 +1,46 @@ +h1 { + color: light-dark(@dark-blue, @golden); + font-family: var(--dh-font-subtitle); + font-size: var(--font-size-24); + text-align: center; + font-weight: 700; +} + +header { + --bar-color: light-dark(@dark-blue, @golden); + color: light-dark(@dark, @beige); + display: flex; + justify-content: center; + align-items: center; + + &:not(:first-child) { + margin-top: var(--spacer-8); + } + + span { + padding: 0 10px; + } + + &:before { + content: ' '; + flex: 1; + height: 1px; + background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bar-color) 100%); + } + + &:after { + content: ' '; + flex: 1; + height: 1px; + background: linear-gradient(90deg, var(--bar-color) 0%, rgba(0, 0, 0, 0) 100%); + } +} + +img.portrait { + border-radius: 50%; + border: none; + object-fit: cover; + object-position: center top; + width: 2.5rem; + height: 2.5rem; +} diff --git a/styles/less/dialog/group-roll-dialog/index.less b/styles/less/dialog/group-roll-dialog/index.less index f90b57dc..27925fa2 100644 --- a/styles/less/dialog/group-roll-dialog/index.less +++ b/styles/less/dialog/group-roll-dialog/index.less @@ -1,3 +1,8 @@ -@import './sheet.less'; -@import './initialization.less'; -@import './main.less'; +.daggerheart.dialog.dh-style.views.group-roll-dialog { + .window-content { + @import "./_common.less"; + } +} + +@import "./initialization.less"; +@import "./main.less"; diff --git a/styles/less/dialog/group-roll-dialog/main.less b/styles/less/dialog/group-roll-dialog/main.less index e30f4e29..f266dcc7 100644 --- a/styles/less/dialog/group-roll-dialog/main.less +++ b/styles/less/dialog/group-roll-dialog/main.less @@ -110,7 +110,7 @@ display: flex; flex-direction: row; button { - --button-text-color: @color-text-primary; + --button-text-color: var(--color-text-primary); --button-size: 1.5em; padding: 0 var(--spacer-4); img { diff --git a/styles/less/dialog/group-roll-dialog/sheet.less b/styles/less/dialog/group-roll-dialog/sheet.less deleted file mode 100644 index 938710c9..00000000 --- a/styles/less/dialog/group-roll-dialog/sheet.less +++ /dev/null @@ -1,48 +0,0 @@ -.daggerheart.dialog.dh-style.views.group-roll-dialog { - .window-content { - h1 { - color: @color-text-emphatic; - font: 700 var(--font-size-24) var(--dh-font-subtitle); - text-align: center; - } - - header { - --bar-color: light-dark(@dark-blue, @golden); - color: light-dark(@dark, @beige); - display: flex; - justify-content: center; - align-items: center; - - &:not(:first-child) { - margin-top: var(--spacer-8); - } - - span { - padding: 0 10px; - } - - &:before { - content: ' '; - flex: 1; - height: 1px; - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bar-color) 100%); - } - - &:after { - content: ' '; - flex: 1; - height: 1px; - background: linear-gradient(90deg, var(--bar-color) 0%, rgba(0, 0, 0, 0) 100%); - } - } - - img.portrait { - border-radius: 50%; - border: none; - object-fit: cover; - object-position: center top; - width: 2.5rem; - height: 2.5rem; - } - } -} \ No newline at end of file diff --git a/styles/less/dialog/image-select/index.less b/styles/less/dialog/image-select/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/image-select/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/image-select/sheet.less b/styles/less/dialog/image-select/sheet.less index 7a3a8468..3ed4f583 100644 --- a/styles/less/dialog/image-select/sheet.less +++ b/styles/less/dialog/image-select/sheet.less @@ -12,7 +12,7 @@ img { width: 136px; height: 136px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; opacity: 0.4; diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less index 4ce4834e..eb882eeb 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -1,20 +1,49 @@ -@import './actions/index.less'; -@import './attribution/index.less'; -@import './beastform/index.less'; -@import './character-creation/index.less'; -@import './character-reset/index.less'; -@import './compendiumBrowserPackDialog/index.less'; -@import './damage-reduction/index.less'; -@import './damage-selection/index.less'; -@import './death-move/index.less'; -@import './dice-roll/index.less'; -@import './downtime/index.less'; +@import './attribution/sheet.less'; +@import './level-up/navigation-container.less'; +@import './level-up/selections-container.less'; +@import './level-up/sheet.less'; +@import './level-up/summary-container.less'; +@import './level-up/tiers-container.less'; +@import './level-up/footer.less'; + +@import './resource-dice/sheet.less'; + +@import './actions/action-list.less'; + +@import './damage-selection/sheet.less'; + +@import './downtime/downtime-container.less'; + +@import './death-move/death-move-container.less'; + +@import './beastform/sheet.less'; + +@import './character-creation/creation-action-footer.less'; +@import './character-creation/selections-container.less'; +@import './character-creation/sheet.less'; +@import './character-creation/tab-navigation.less'; + +@import './dice-roll/roll-selection.less'; +@import './damage-reduction/damage-reduction-container.less'; +@import './damage-reduction/sheets.less'; + +@import './multiclass-choice/sheet.less'; + +@import './reroll-dialog/sheet.less'; + +@import './tag-team-dialog/initialization.less'; +@import './tag-team-dialog/sheet.less'; + @import './group-roll-dialog/index.less'; -@import './level-up/index.less'; -@import './resource-dice/index.less'; -@import './multiclass-choice/index.less'; -@import './tag-team-dialog/index.less'; -@import './image-select/index.less'; -@import './item-transfer/index.less'; -@import './settings/index.less'; -@import './risk-it-all/index.less'; \ No newline at end of file + +@import './image-select/sheet.less'; + +@import './item-transfer/sheet.less'; + +@import './settings/change-currency-icon.less'; + +@import './risk-it-all/sheet.less'; + +@import './character-reset/sheet.less'; + +@import './compendiumBrowserPackDialog/sheet.less'; diff --git a/styles/less/dialog/item-transfer/index.less b/styles/less/dialog/item-transfer/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/item-transfer/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/level-up/index.less b/styles/less/dialog/level-up/index.less deleted file mode 100644 index 849a4d36..00000000 --- a/styles/less/dialog/level-up/index.less +++ /dev/null @@ -1,6 +0,0 @@ -@import './navigation-container.less'; -@import './selections-container.less'; -@import './summary-container.less'; -@import './tiers-container.less'; -@import './footer.less'; -@import './sheet.less'; diff --git a/styles/less/dialog/level-up/navigation-container.less b/styles/less/dialog/level-up/navigation-container.less index 6bf80d7c..282d683f 100644 --- a/styles/less/dialog/level-up/navigation-container.less +++ b/styles/less/dialog/level-up/navigation-container.less @@ -19,7 +19,7 @@ a, span { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/dialog/level-up/selections-container.less b/styles/less/dialog/level-up/selections-container.less index 8c0dbaec..6a551865 100644 --- a/styles/less/dialog/level-up/selections-container.less +++ b/styles/less/dialog/level-up/selections-container.less @@ -3,7 +3,12 @@ .daggerheart.levelup { .levelup-selections-container { + overflow: auto; padding: 10px 0; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + max-height: 500px; + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); .achievement-experience-cards { display: flex; @@ -40,22 +45,20 @@ .levelup-card-selection { display: flex; + flex-wrap: wrap; justify-content: center; gap: 40px; height: 190px; - align-items: stretch; .card-preview-container { - height: 190px; + height: 100%; max-width: 200px; } .levelup-domains-selection-container { - display: grid; - grid-auto-flow: column; - grid-template-rows: repeat(2, minmax(0, 1fr)); - height: 100%; - gap: 4px; + display: flex; + flex-direction: column; + gap: 8px; .levelup-domain-selection-container { display: flex; @@ -63,8 +66,6 @@ align-items: center; position: relative; cursor: pointer; - overflow: hidden; - width: 93px; &.disabled { pointer-events: none; @@ -73,20 +74,16 @@ .levelup-domain-label { position: absolute; - left: 0; - right: 0; - bottom: 0; text-align: center; + top: 4px; background: grey; - padding: 2px 12px; + padding: 0 12px; + border-radius: 6px; z-index: 2; - line-height: 1; } img { - object-fit: cover; - width: auto; - height: auto; + height: 124px; &.svg { filter: @beige-filter; @@ -95,18 +92,17 @@ .levelup-domain-selected { position: absolute; - height: 40px; - width: 40px; + height: 54px; + width: 54px; border-radius: 50%; - border: 2px solid @golden; - font-size: var(--font-size-24); + border: 2px solid; + font-size: var(--font-size-48); display: flex; align-items: center; justify-content: center; - background: @dark-golden; - color: @golden; - top: 10px; - z-index: 2; + background-image: url(../assets/parchments/dh-parchment-light.png); + color: var(--color-dark-5); + top: calc(50% - 29px); i { position: relative; diff --git a/styles/less/dialog/level-up/sheet.less b/styles/less/dialog/level-up/sheet.less index 9ebd9331..ade7c8a9 100644 --- a/styles/less/dialog/level-up/sheet.less +++ b/styles/less/dialog/level-up/sheet.less @@ -11,10 +11,9 @@ }); .daggerheart.levelup { - .tab.active { - flex: 1; + .window-content { + max-height: 960px; overflow: auto; - .with-scroll-shadows(); } div[data-application-part='form'] { @@ -23,13 +22,15 @@ gap: 8px; } - .section-container { - display: flex; - flex-direction: row; - justify-content: center; - gap: 20px 8px; - margin-top: 8px; - flex-wrap: wrap; + section { + .section-container { + display: flex; + flex-direction: row; + justify-content: center; + gap: 20px 8px; + margin-top: 8px; + flex-wrap: wrap; + } } .levelup-footer { diff --git a/styles/less/dialog/level-up/summary-container.less b/styles/less/dialog/level-up/summary-container.less index de7c9f4a..d67abff6 100644 --- a/styles/less/dialog/level-up/summary-container.less +++ b/styles/less/dialog/level-up/summary-container.less @@ -17,8 +17,10 @@ .levelup-summary-container { overflow: auto; padding: 10px 0; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; max-height: 700px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); .level-achievements-container, .level-advancements-container { diff --git a/styles/less/dialog/multiclass-choice/index.less b/styles/less/dialog/multiclass-choice/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/multiclass-choice/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/multiclass-choice/sheet.less b/styles/less/dialog/multiclass-choice/sheet.less index 0c487cbc..1f38449a 100644 --- a/styles/less/dialog/multiclass-choice/sheet.less +++ b/styles/less/dialog/multiclass-choice/sheet.less @@ -35,7 +35,7 @@ width: 120px; height: 120px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.selected { background: light-dark(@dark-blue-40, @golden-40); @@ -57,7 +57,7 @@ display: flex; flex-wrap: wrap; font-style: italic; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; padding: 4px 4px; } diff --git a/styles/less/dialog/reroll-dialog/sheet.less b/styles/less/dialog/reroll-dialog/sheet.less new file mode 100644 index 00000000..71c94d80 --- /dev/null +++ b/styles/less/dialog/reroll-dialog/sheet.less @@ -0,0 +1,125 @@ +.daggerheart.dialog.dh-style.views.reroll-dialog { + .window-content { + max-width: 648px; + } + + .reroll-outer-container { + h2 { + margin: 0; + } + + .dices-container { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + + .dice-outer-container { + width: 300px; + + legend { + display: flex; + align-items: center; + gap: 4px; + + i { + margin-right: 4px; + } + } + + .dice-container { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; + + .result-container { + position: relative; + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.375rem; + opacity: 0.8; + + &.selected { + opacity: 1; + border: 1px solid; + border-radius: 6px; + border-color: light-dark(@dark-blue, @golden); + filter: drop-shadow(0 0 3px @golden); + } + + &:before { + content: ' '; + position: absolute; + width: 100%; + height: 100%; + z-index: -1; + mask: var(--svg-die) no-repeat center; + mask-size: contain; + background: linear-gradient(139.01deg, #efe6d8 3.51%, #372e1f 96.49%); + } + + &.d4:before { + --svg-die: url(../assets/icons/dice/default/d4.svg); + } + &.d6:before { + --svg-die: url(../assets/icons/dice/default/d6.svg); + } + &.d8:before { + --svg-die: url(../assets/icons/dice/default/d8.svg); + } + &.d10:before { + --svg-die: url(../assets/icons/dice/default/d10.svg); + } + &.d12:before { + --svg-die: url('../assets/icons/dice/default/d12.svg'); + } + &.d20:before { + --svg-die: url(../assets/icons/dice/default/d20.svg); + } + + .to-reroll-result { + position: absolute; + bottom: -7px; + gap: 2px; + border: 1px solid; + border-radius: 6px; + background-image: url(../assets/parchments/dh-parchment-dark.png); + display: flex; + align-items: center; + padding: 2px 6px; + + input { + margin: 0; + height: 12px; + line-height: 0px; + position: relative; + top: 1px; + + &:before, + &:after { + line-height: 12px; + font-size: var(--font-size-12); + } + } + + i { + font-size: var(--font-size-10); + } + } + } + } + } + } + + footer { + margin-top: 8px; + display: flex; + justify-content: space-between; + + .controls { + display: flex; + gap: 8px; + } + } +} diff --git a/styles/less/dialog/resource-dice/index.less b/styles/less/dialog/resource-dice/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/resource-dice/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/risk-it-all/index.less b/styles/less/dialog/risk-it-all/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/dialog/risk-it-all/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/dialog/settings/index.less b/styles/less/dialog/settings/index.less deleted file mode 100644 index 235f3b9c..00000000 --- a/styles/less/dialog/settings/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './change-currency-icon.less'; \ No newline at end of file diff --git a/styles/less/dialog/tag-team-dialog/index.less b/styles/less/dialog/tag-team-dialog/index.less deleted file mode 100644 index 8bf56824..00000000 --- a/styles/less/dialog/tag-team-dialog/index.less +++ /dev/null @@ -1,2 +0,0 @@ -@import './sheet.less'; -@import './initialization.less'; \ No newline at end of file diff --git a/styles/less/dialog/tag-team-dialog/initialization.less b/styles/less/dialog/tag-team-dialog/initialization.less index d6f7ad29..8557d231 100644 --- a/styles/less/dialog/tag-team-dialog/initialization.less +++ b/styles/less/dialog/tag-team-dialog/initialization.less @@ -1,5 +1,3 @@ -@import '../../utils/mixin.less'; - .theme-light .daggerheart.dialog.dh-style.views.tag-team-dialog { .initialization-container .members-container .member-container { .member-name { @@ -9,75 +7,39 @@ } .daggerheart.dialog.dh-style.views.tag-team-dialog { - .initialization-container.active { - display: flex; - flex-direction: column; - gap: var(--spacer-4); - + .initialization-container { h2 { text-align: center; } .members-container { - display: flex; - flex-wrap: wrap; - justify-content: center; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; - // Force 3 columns for 5 -> 6 players - &:has(> :nth-child(5)):not(:has(> :nth-child(7))) { - padding-left: 10%; - padding-right: 10%; - } - .member-container { position: relative; display: flex; - align-items: stretch; justify-content: center; - border-radius: 6px; - border: 1px solid @color-border; - overflow: hidden; - height: 11.5rem; - width: 122px; &.inactive { - border-color: light-dark(@dark-blue-40, @golden-40); - img { - opacity: 0.4; - } + opacity: 0.4; } .member-name { - --shadow-color: light-dark(white, black); position: absolute; - bottom: 0; - left: 0; - right: 0; - - display: flex; - flex-direction: column; - justify-content: flex-end; - min-height: 4rem; - padding: 5rem 4px var(--spacer-8) 4px; + padding: 0 2px; + border: 1px solid; + border-radius: 6px; + margin-top: 4px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); text-align: center; - - color: @color-text-primary; - text-shadow: 1px 1px 2px var(--shadow-color), 0 0 10px var(--shadow-color); - .smooth-gradient-ease-in-out(background-image, to bottom, var(--shadow-color), 100%); } img { - object-fit: cover; - object-position: top center; - flex: 1; - } - - .leader-mark { - position: absolute; - top: 4px; - right: 4px; - text-shadow: var(--shadow-text-stroke), 0 0 20px black; + border-radius: 6px; + border: 1px solid light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/dialog/tag-team-dialog/sheet.less b/styles/less/dialog/tag-team-dialog/sheet.less index 82bc0270..dc8f16dc 100644 --- a/styles/less/dialog/tag-team-dialog/sheet.less +++ b/styles/less/dialog/tag-team-dialog/sheet.less @@ -1,10 +1,4 @@ -.daggerheart.dialog.dh-style.views.tag-team-dialog .window-content { - h1 { - color: @color-text-emphatic; - font: 700 var(--font-size-24) var(--dh-font-subtitle); - text-align: center; - } - +.daggerheart.dialog.dh-style.views.tag-team-dialog { .team-container { display: flex; gap: 16px; @@ -42,7 +36,7 @@ img { height: 64px; border-radius: 6px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); } .member-name { @@ -64,7 +58,7 @@ .roll-title { font-size: var(--font-size-20); font-weight: bold; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); text-align: center; display: flex; align-items: center; @@ -72,7 +66,7 @@ &::before, &::after { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); content: ''; flex: 1; height: 2px; @@ -194,7 +188,6 @@ .roll-selection-container { display: flex; - gap: 16px; .select-roll-button { margin-top: 8px; @@ -203,7 +196,7 @@ justify-content: center; i { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); font-size: 48px; &.inactive { diff --git a/styles/less/global/dialog.less b/styles/less/global/dialog.less index 1313d68b..a3400700 100644 --- a/styles/less/global/dialog.less +++ b/styles/less/global/dialog.less @@ -36,8 +36,8 @@ padding: 0; &:hover { - border: 1px solid @color-border; - color: @color-text-emphatic; + border: 1px solid light-dark(@dark-blue, @golden); + color: light-dark(@dark-blue, @golden); } } } @@ -81,7 +81,7 @@ cursor: pointer; padding: 5px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .label { font-style: normal; diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index e57ba50d..afd56057 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -2,22 +2,24 @@ @import '../utils/fonts.less'; .dh-style { + border: 1px solid light-dark(@dark-blue, @golden); + input[type='text'], input[type='number'], textarea, - file-picker, .input[contenteditable] { background: light-dark(transparent, transparent); border-radius: 6px; box-shadow: 0 4px 30px @soft-shadow; backdrop-filter: blur(9.5px); + -webkit-backdrop-filter: blur(9.5px); outline: 2px solid transparent; - color: @input-color-text; - border: 1px solid @input-color-border; + color: light-dark(@dark-blue, @golden); + border: 1px solid light-dark(@dark, @beige); transition: all 0.3s ease; &::placeholder { - color: @color-text-subtle; + color: light-dark(@dark-40, @beige-50); } &:hover, @@ -28,7 +30,7 @@ &:focus[type='number'] { background: light-dark(@soft-shadow, @semi-transparent-dark-blue); box-shadow: none; - outline: 2px solid @input-color-border; + outline: 2px solid light-dark(@dark, @beige); } &:disabled[type='text'], @@ -45,7 +47,7 @@ .input[contenteditable] { cursor: var(--cursor-text); &:empty:before { - color: @color-text-subtle; + color: light-dark(@dark-40, @beige-50); content: attr(placeholder); } } @@ -94,9 +96,11 @@ textarea { color: light-dark(@dark, @beige); + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } - button:where(:not(.plain, color-picker *, file-picker *)) { + button:where(:not(.plain)) { background: light-dark(transparent, @golden); border: 1px solid light-dark(@dark-blue, @dark-blue); color: light-dark(@dark-blue, @dark-blue); @@ -105,7 +109,7 @@ &:hover { background: light-dark(@light-black, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } &.glow { @@ -126,7 +130,7 @@ &.reverted { background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); border: 1px solid light-dark(@dark, transparent); &:hover { background: light-dark(transparent, @golden); @@ -173,7 +177,7 @@ height: inherit; .tag { padding: 0.3rem 0.5rem; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); background-color: light-dark(@dark-blue-10, @golden-40); border-radius: 3px; transition: 0.13s ease-out; @@ -247,23 +251,15 @@ a:hover, a.active { - text-shadow: 0 0 1px currentColor, 0 0 1px currentColor, 0 0 8px light-dark(@dark-blue, @golden); - } - - file-picker, color-picker { - > input[type=text] { - background: transparent; - border: none; - outline: none; - backdrop-filter: unset; - } + font-weight: bold; + text-shadow: 0 0 8px light-dark(@dark-blue, @golden); } fieldset { align-items: center; margin-top: 5px; border-radius: 6px; - border-color: @color-fieldset-border; + border-color: light-dark(@dark-blue, @golden); &.glassy { background-color: light-dark(@dark-blue-10, @golden-10); @@ -272,9 +268,8 @@ legend { padding: 2px 12px; border-radius: 3px; - background-color: @color-fieldset-border; + background-color: light-dark(@dark-blue, @golden); color: light-dark(@beige, @dark-blue); - margin-bottom: var(--spacer-4); } } @@ -283,7 +278,7 @@ } &.fit-height { - flex: 1; + height: 95%; } &.flex { @@ -351,7 +346,7 @@ legend { font-weight: bold; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.with-icon { display: flex; @@ -363,6 +358,18 @@ } } + input[type='text'], + input[type='number'] { + color: light-dark(@dark, @beige); + transition: all 0.3s ease; + outline: 2px solid transparent; + + &:focus, + &:hover { + outline: 2px solid light-dark(@dark, @beige); + } + } + &[disabled], &.child-disabled .form-group, select[disabled], @@ -500,7 +507,7 @@ display: block; height: 1px; width: 100%; - border-bottom: 1px solid @color-border; + border-bottom: 1px solid light-dark(@dark-blue, @golden); mask-image: linear-gradient(270deg, transparent 0%, black 50%, transparent 100%); } @@ -508,7 +515,7 @@ display: block; height: 1px; width: 100%; - border-bottom: 1px solid @color-border; + border-bottom: 1px solid light-dark(@dark-blue, @golden); mask-image: linear-gradient(270deg, transparent 0%, black 100%); &.invert { @@ -557,7 +564,7 @@ border: 0; &:hover { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } &:not(:first-child) { @@ -592,6 +599,59 @@ } } +.application.setting.dh-style { + h2, + h3, + h4 { + margin: 8px 0 4px; + text-align: center; + } + + footer { + margin-top: 8px; + display: flex; + gap: 8px; + + button { + flex: 1; + } + } + + .form-group { + display: flex; + justify-content: space-between; + align-items: center; + gap: 0.25rem 0.5rem; + flex-wrap: wrap; + + label { + font-size: var(--font-size-14); + font-weight: normal; + } + + .form-fields { + display: flex; + gap: 4px; + align-items: center; + } + + &.setting-two-values { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.25rem 0.5rem; + + .form-group { + justify-content: end; + flex-wrap: nowrap; + } + + .hint { + grid-column: 1 / -1; + } + } + } +} + .system-daggerheart { .tagify { background: light-dark(transparent, transparent); @@ -744,7 +804,6 @@ .preview-image-container { width: 100%; - min-height: 0; flex-grow: 1; object-fit: cover; border-radius: 4px 4px 0 0; diff --git a/styles/less/global/feature-section.less b/styles/less/global/feature-section.less index 2fd4e20f..13feb92c 100644 --- a/styles/less/global/feature-section.less +++ b/styles/less/global/feature-section.less @@ -5,6 +5,8 @@ .tab.features { padding: 0 10px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; .feature-list { display: flex; flex-direction: column; @@ -17,36 +19,28 @@ &:last-child { margin-bottom: 0px; } - } - .feature-line { - display: grid; - align-items: center; - grid-template-columns: 1fr 4fr 1fr; - h4 { - font-weight: lighter; - color: light-dark(@dark, @beige); - } - .image { - height: 40px; - width: 40px; - object-fit: cover; - border-radius: 6px; - border: none; - } - .image-icon { - font-size: 26px; - width: 40px; - height: 40px; - display: flex; - justify-content: center; + .feature-line { + display: grid; align-items: center; - } - .controls { - display: flex; - justify-content: center; - gap: 10px; - a { - text-shadow: none; + grid-template-columns: 1fr 4fr 1fr; + h4 { + font-weight: lighter; + color: light-dark(@dark, @beige); + } + .image { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 6px; + border: none; + } + .controls { + display: flex; + justify-content: center; + gap: 10px; + a { + text-shadow: none; + } } } } diff --git a/styles/less/global/filter-menu.less b/styles/less/global/filter-menu.less index a0545950..65a184f8 100644 --- a/styles/less/global/filter-menu.less +++ b/styles/less/global/filter-menu.less @@ -13,7 +13,7 @@ legend { font-weight: bold; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); font-size: var(--font-size-12); } @@ -25,7 +25,7 @@ button { background: light-dark(@light-black, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); outline: none; box-shadow: none; border: 1px solid light-dark(@dark-blue, @dark-blue); diff --git a/styles/less/global/global.less b/styles/less/global/global.less index c0e7f3fc..b9af67c0 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -12,11 +12,6 @@ } .daggerheart.dh-style { - * { - scrollbar-width: thin; - scrollbar-color: light-dark(@dark-blue, @golden) transparent; - } - .hint { flex: 0 0 100%; margin: 0; @@ -38,7 +33,7 @@ } &:before { - font-family: var(--font-awesome); + font-family: 'Font Awesome 6 Pro'; content: '\f110'; position: absolute; height: 100%; diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 3a5a9321..a2b9ebd8 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -287,7 +287,7 @@ position: relative; height: 120px; width: 98px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; cursor: pointer; diff --git a/styles/less/global/prose-mirror.less b/styles/less/global/prose-mirror.less index e4b1249f..8a663e28 100644 --- a/styles/less/global/prose-mirror.less +++ b/styles/less/global/prose-mirror.less @@ -10,6 +10,8 @@ background-color: transparent; } .editor-content { + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; h1 { font-size: var(--font-size-32); } @@ -40,11 +42,6 @@ ul { list-style: disc; } - } - // Fixes centering and makes it not render over scrollbar - &:hover button.toggle:enabled { - display: flex; - right: 12px; } } } diff --git a/styles/less/global/resource-bar.less b/styles/less/global/resource-bar.less index d06b43a8..be9bc68b 100644 --- a/styles/less/global/resource-bar.less +++ b/styles/less/global/resource-bar.less @@ -50,16 +50,16 @@ flex-wrap: wrap; gap: 5px; padding: 5px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); width: fit-content; .slot { width: 15px; height: 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); background: light-dark(@dark-blue-10, @golden-10); border-radius: 3px; transition: all 0.3s ease; @@ -148,7 +148,7 @@ appearance: none; width: 100px; height: 40px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index e3072da1..0c400564 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -4,8 +4,8 @@ // Theme handling .appTheme({ - background: @dark-blue-c0; - backdrop-filter: blur(7px); + background: @dark-blue-90; + backdrop-filter: blur(8px); }, { background: url('../assets/parchments/dh-parchment-light.png') no-repeat center; }); @@ -43,7 +43,7 @@ body.game:is(.performance-low, .noblur) { &:hover { border-color: light-dark(@dark-blue, @golden); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/global/tab-navigation.less b/styles/less/global/tab-navigation.less index 3d143b4c..3f8844f2 100755 --- a/styles/less/global/tab-navigation.less +++ b/styles/less/global/tab-navigation.less @@ -3,7 +3,8 @@ .daggerheart.dh-style { .tab-navigation { - margin: 5px 0 10px 0; + margin: 5px 0; + height: 40px; width: 100%; .navigation-container { @@ -19,11 +20,7 @@ white-space: nowrap; a { - color: @color-text-emphatic; - - &.empty:not(.active) { - opacity: 0.4; - } + color: light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/hud/index.less b/styles/less/hud/index.less index f1f4602e..459f8fd7 100644 --- a/styles/less/hud/index.less +++ b/styles/less/hud/index.less @@ -1 +1 @@ -@import './token-hud/index.less'; +@import './token-hud/token-hud.less'; diff --git a/styles/less/hud/token-hud/index.less b/styles/less/hud/token-hud/index.less deleted file mode 100644 index c86d0939..00000000 --- a/styles/less/hud/token-hud/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './token-hud.less'; \ No newline at end of file diff --git a/styles/less/hud/token-hud/token-hud.less b/styles/less/hud/token-hud/token-hud.less index 3b998f4e..e31ede4a 100644 --- a/styles/less/hud/token-hud/token-hud.less +++ b/styles/less/hud/token-hud/token-hud.less @@ -24,7 +24,7 @@ .palette-category-title { grid-column: span var(--effect-columns); font-weight: bold; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } @@ -38,9 +38,6 @@ } .status-effects { - // TODO: Remove when the issue https://github.com/foundryvtt/foundryvtt/issues/14410 is resolved and Foundry handles it cleanly themselves. - grid-template-rows: min-content; - .effect-control-container { position: relative; diff --git a/styles/less/sheets-settings/adversary-settings/features.less b/styles/less/sheets-settings/adversary-settings/features.less index 15b1fa18..4e0f6a8f 100644 --- a/styles/less/sheets-settings/adversary-settings/features.less +++ b/styles/less/sheets-settings/adversary-settings/features.less @@ -5,6 +5,8 @@ .tab.features { max-height: 450px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; .add-feature-btn { width: 100%; diff --git a/styles/less/sheets-settings/adversary-settings/index.less b/styles/less/sheets-settings/adversary-settings/index.less deleted file mode 100644 index 5968577d..00000000 --- a/styles/less/sheets-settings/adversary-settings/index.less +++ /dev/null @@ -1,3 +0,0 @@ -@import './sheet.less'; -@import './experiences.less'; -@import './features.less'; \ No newline at end of file diff --git a/styles/less/sheets-settings/adversary-settings/sheet.less b/styles/less/sheets-settings/adversary-settings/sheet.less index e6eb8d0b..b4b0683b 100644 --- a/styles/less/sheets-settings/adversary-settings/sheet.less +++ b/styles/less/sheets-settings/adversary-settings/sheet.less @@ -7,7 +7,7 @@ &.attack.active { display: flex; flex-direction: column; - gap: 12px; + gap: 16px; } .fieldsets-section { diff --git a/styles/less/sheets-settings/character-settings/index.less b/styles/less/sheets-settings/character-settings/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/sheets-settings/character-settings/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/sheets-settings/character-settings/sheet.less b/styles/less/sheets-settings/character-settings/sheet.less index 37906712..eab29436 100644 --- a/styles/less/sheets-settings/character-settings/sheet.less +++ b/styles/less/sheets-settings/character-settings/sheet.less @@ -1,19 +1,17 @@ @import '../../utils/colors.less'; -.appTheme({}, { - &.dialog.character-settings { - .tab.details { - .traits-inner-container .trait-container { - background: url('../assets/svg/trait-shield-light.svg') no-repeat; +.theme-light .application.daggerheart.dh-style.dialog { + .tab.details { + .traits-inner-container .trait-container { + background: url('../assets/svg/trait-shield-light.svg') no-repeat; - div { - filter: drop-shadow(0 0 3px @beige); - text-shadow: 0 0 3px @beige; - } + div { + filter: drop-shadow(0 0 3px @beige); + text-shadow: 0 0 3px @beige; } } } -}); +} .application.daggerheart.dh-style.dialog { .tab.details { diff --git a/styles/less/sheets-settings/environment-settings/adversaries.less b/styles/less/sheets-settings/environment-settings/adversaries.less index 2ce4819a..1a27eaca 100644 --- a/styles/less/sheets-settings/environment-settings/adversaries.less +++ b/styles/less/sheets-settings/environment-settings/adversaries.less @@ -5,6 +5,8 @@ .tab.adversaries { max-height: 450px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; .add-action-btn { width: 100%; diff --git a/styles/less/sheets-settings/environment-settings/features.less b/styles/less/sheets-settings/environment-settings/features.less index db6b544d..d907837a 100644 --- a/styles/less/sheets-settings/environment-settings/features.less +++ b/styles/less/sheets-settings/environment-settings/features.less @@ -5,6 +5,8 @@ .tab.features { max-height: 450px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; .add-feature-btn { width: 100%; diff --git a/styles/less/sheets-settings/environment-settings/index.less b/styles/less/sheets-settings/environment-settings/index.less deleted file mode 100644 index 1e6ee34d..00000000 --- a/styles/less/sheets-settings/environment-settings/index.less +++ /dev/null @@ -1,2 +0,0 @@ -@import './adversaries.less'; -@import './features.less'; \ No newline at end of file diff --git a/styles/less/sheets-settings/header.less b/styles/less/sheets-settings/header.less index 04e2fa90..82f3c488 100644 --- a/styles/less/sheets-settings/header.less +++ b/styles/less/sheets-settings/header.less @@ -13,7 +13,7 @@ font-size: var(--font-size-24); margin: 0; text-align: center; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/sheets-settings/index.less b/styles/less/sheets-settings/index.less index 53a03868..f575f848 100644 --- a/styles/less/sheets-settings/index.less +++ b/styles/less/sheets-settings/index.less @@ -1,4 +1,8 @@ @import './header.less'; -@import './adversary-settings/index.less'; -@import './character-settings/index.less'; -@import './environment-settings/index.less'; +@import './adversary-settings/sheet.less'; +@import './adversary-settings/experiences.less'; +@import './adversary-settings/features.less'; +@import './character-settings/sheet.less'; + +@import './environment-settings/features.less'; +@import './environment-settings/adversaries.less'; diff --git a/styles/less/sheets/actions/index.less b/styles/less/sheets/actions/index.less deleted file mode 100644 index 29ef8645..00000000 --- a/styles/less/sheets/actions/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './actions.less'; \ No newline at end of file diff --git a/styles/less/sheets/activeEffects/index.less b/styles/less/sheets/activeEffects/index.less deleted file mode 100644 index 19f8a3a7..00000000 --- a/styles/less/sheets/activeEffects/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './activeEffects.less'; \ No newline at end of file diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index b3eb0469..bd82ef83 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -34,27 +34,15 @@ .attribution-header-label { font-style: italic; font-family: @font-body; - color: @color-text-subtle; + color: light-dark(@chat-blue-bg, @beige-50); } .tab.inventory { - .gold-section { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; + .search-section { + display: flex; gap: 10px; - padding: 10px 10px 0; - - .input { - color: light-dark(@dark, @beige); - } + align-items: center; } - } - - .search-section { - display: flex; - gap: 10px; - align-items: center; - justify-content: space-between; .search-bar { position: relative; color: light-dark(@dark-blue-50, @beige-50); @@ -84,11 +72,22 @@ height: 32px; position: absolute; right: 20px; - font-size: var(--font-size-16); + font-size: 16px; z-index: 1; color: light-dark(@dark-blue-50, @beige-50); } } + + .gold-section { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 10px; + padding: 10px 10px 0; + + .input { + color: light-dark(@dark, @beige); + } + } } &.limited { @@ -128,7 +127,7 @@ .title-name { text-align: start; font-size: var(--font-size-28); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); text-align: center; } } @@ -181,9 +180,9 @@ display: flex; gap: 10px; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 5px 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; align-items: center; width: fit-content; @@ -195,7 +194,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } .domain { @@ -207,7 +206,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } img { @@ -231,7 +230,7 @@ padding: 10px; border-radius: 5px; min-width: 90px; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); background-color: light-dark(@dark-blue-10, @golden-40); } } diff --git a/styles/less/sheets/actors/adversary/features.less b/styles/less/sheets/actors/adversary/actions.less similarity index 65% rename from styles/less/sheets/actors/adversary/features.less rename to styles/less/sheets/actors/adversary/actions.less index 447d050e..00395ebd 100644 --- a/styles/less/sheets/actors/adversary/features.less +++ b/styles/less/sheets/actors/adversary/actions.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.adversary { .tab.features { @@ -9,8 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); padding-bottom: 20px; - .with-scroll-shadows(); + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/adversary/effects.less b/styles/less/sheets/actors/adversary/effects.less index 4aa44e51..4afe2454 100644 --- a/styles/less/sheets/actors/adversary/effects.less +++ b/styles/less/sheets/actors/adversary/effects.less @@ -1,5 +1,4 @@ @import '../../../utils/colors.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.adversary { .tab.effects { @@ -8,8 +7,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); padding-bottom: 20px; - .with-scroll-shadows(); + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/adversary/header.less b/styles/less/sheets/actors/adversary/header.less index 1e5e4fa5..8bd3fcee 100644 --- a/styles/less/sheets/actors/adversary/header.less +++ b/styles/less/sheets/actors/adversary/header.less @@ -35,7 +35,7 @@ .tags { display: flex; gap: 10px; - padding-bottom: 8px; + padding-bottom: 16px; .tag { display: flex; @@ -67,5 +67,11 @@ gap: 12px; padding: 16px 0; } + + .adversary-navigation { + display: flex; + gap: 8px; + align-items: center; + } } } diff --git a/styles/less/sheets/actors/adversary/index.less b/styles/less/sheets/actors/adversary/index.less deleted file mode 100644 index 28ff9d22..00000000 --- a/styles/less/sheets/actors/adversary/index.less +++ /dev/null @@ -1,7 +0,0 @@ -@import './features.less'; -@import './header.less'; -@import './sheet.less'; -@import './sidebar.less'; -@import './effects.less'; -@import './notes.less'; - diff --git a/styles/less/sheets/actors/adversary/notes.less b/styles/less/sheets/actors/adversary/notes.less deleted file mode 100644 index a95d070e..00000000 --- a/styles/less/sheets/actors/adversary/notes.less +++ /dev/null @@ -1,3 +0,0 @@ -.application.sheet.daggerheart.actor.dh-style.adversary .tab.notes.active { - padding-bottom: 20px; -} diff --git a/styles/less/sheets/actors/adversary/sidebar.less b/styles/less/sheets/actors/adversary/sidebar.less index 5db9f5e9..4e7535c1 100644 --- a/styles/less/sheets/actors/adversary/sidebar.less +++ b/styles/less/sheets/actors/adversary/sidebar.less @@ -65,9 +65,9 @@ display: flex; gap: 10px; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 5px 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; align-items: center; width: fit-content; @@ -77,7 +77,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.threshold-value { color: light-dark(@dark, @beige); @@ -191,7 +191,7 @@ appearance: none; width: 100px; height: 40px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; @@ -237,7 +237,7 @@ display: flex; width: 50px; height: 30px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-bottom: none; border-radius: 6px 6px 0 0; padding: 0 6px; @@ -286,11 +286,13 @@ overflow-y: hidden; padding-top: 10px; padding-bottom: 20px; + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); + scrollbar-width: thin; scrollbar-gutter: stable; - .with-scroll-shadows(); &:hover { overflow-y: auto; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } diff --git a/styles/less/sheets/actors/character/biography.less b/styles/less/sheets/actors/character/biography.less index 8548a2fb..12a662ff 100644 --- a/styles/less/sheets/actors/character/biography.less +++ b/styles/less/sheets/actors/character/biography.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.character { .tab.biography { @@ -10,15 +9,12 @@ gap: 10px; height: 100%; overflow-y: auto; - padding-top: 8px; - padding-bottom: 20px; + mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%); + padding-bottom: 10px; height: 100%; - .with-scroll-shadows(); - } - .characteristics-section { - gap: 20px; - padding: 0 10px; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } .biography-section { diff --git a/styles/less/sheets/actors/character/effects.less b/styles/less/sheets/actors/character/effects.less index 0ab1007d..ceadd05e 100644 --- a/styles/less/sheets/actors/character/effects.less +++ b/styles/less/sheets/actors/character/effects.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.character { .tab.effects { @@ -9,8 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); padding-bottom: 20px; - .with-scroll-shadows(); + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/character/features.less b/styles/less/sheets/actors/character/features.less index 52b41826..6a6438ff 100644 --- a/styles/less/sheets/actors/character/features.less +++ b/styles/less/sheets/actors/character/features.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.character { .tab.features { @@ -9,8 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); padding-bottom: 20px; - .with-scroll-shadows(); + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 91b3545a..31fd4256 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -5,12 +5,20 @@ // Theme header backgrounds .appTheme({ .character-header-sheet { + .trait { + background: url(../assets/svg/trait-shield.svg) no-repeat; + } + .character-row .domains-section img { filter: @golden-filter; } } }, { .character-header-sheet { + .trait { + background: url('../assets/svg/trait-shield-light.svg') no-repeat; + } + .character-row .domains-section img { filter: brightness(0) saturate(100%); } @@ -25,19 +33,13 @@ .name-row { display: flex; - gap: 6px; + gap: 5px; align-items: start; justify-content: space-between; padding: 0; padding-top: 5px; flex: 1; - [contenteditable], - input { - border: 1px solid @soft-shadow; - background-color: light-dark(@dark-15, @soft-white-shadow); - } - h1 { display: flex; flex: 1; @@ -63,16 +65,14 @@ .label { display: flex; - align-items: baseline; + align-items: center; gap: 4px; } input { - border: none; width: 40px; padding: 0; text-align: center; - font-weight: 600; } .level-button { @@ -101,9 +101,9 @@ display: flex; justify-content: space-between; padding: 5px 0; - margin-bottom: 8px; + margin-bottom: 10px; font-size: var(--font-size-12); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .missing-header-feature { opacity: 0.5; @@ -131,7 +131,7 @@ display: flex; align-items: center; padding: 0; - margin-bottom: 12px; + margin-bottom: 15px; .resource-section { display: flex; @@ -168,11 +168,11 @@ .domains-section { position: relative; display: flex; - gap: 4px; + gap: 10px; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 5px 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; align-items: center; width: fit-content; @@ -182,8 +182,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; - margin-right: 4px; + color: light-dark(@dark-blue, @golden); } .domain { @@ -195,7 +194,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } img { @@ -218,110 +217,37 @@ .character-traits { display: flex; + justify-content: space-between; padding: 0; margin-bottom: 15px; - justify-content: space-between; - max-width: 38.5rem; - gap: 0.5rem; - padding-left: 0.5rem; .trait { + height: 60px; + width: 60px; cursor: pointer; - position: relative; - - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - min-width: 4.375rem; .trait-name { - position: relative; - background-color: @trait-color-bg; - border: 1px solid @trait-color-border; - border-radius: 3px; - color: @color-text-emphatic; - font-size: var(--font-size-12); - font-weight: 600; - height: 1rem; - line-height: 1rem; - white-space: nowrap; - width: 100%; - padding: 0 0.1876px 0 0.375rem; - margin-right: 0.125rem; /* makes it center SLIGHTLY */ - text-shadow: 1px 1px 3px @color-text-shadow-contrast; - display: flex; align-items: center; + padding-top: 5px; + color: light-dark(@dark-blue, @golden); + font-size: var(--font-size-14); + font-weight: 600; + align-items: center; justify-content: center; + gap: 3px; - .tier-mark { - position: absolute; - background-color: @dark-blue; - border: 1px solid @color-border; - border-radius: 50%; - width: 1rem; - height: 1rem; - right: calc(100% - 0.4375rem); - display: flex; - justify-content: center; - align-items: center; - &.marked::before { - content: ' '; - position: absolute; - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; - background-color: @golden; - } + i { + line-height: 17px; + font-size: var(--font-size-10); } } - .trait-value-area { - --color-border: @trait-color-border; - --background: light-dark(#e8e6e3, @dark-blue); - display: flex; - position: relative; - .trait-value { - position: absolute; - inset: 0; - display: flex; - align-items: center; - - justify-content: center; - font-style: normal; - font-weight: 600; - font-size: var(--font-size-20); - text-align: center; - margin-bottom: 0.375rem; - } - - .spellcasting-mark { - position: absolute; - border: 1px solid @color-border; - color: @golden; - left: 0; - right: 0; - bottom: -0.375rem; - margin-inline: auto; - border-radius: 50%; - width: 1.125rem; - height: 1.125rem; - background: radial-gradient(190.63% 190.63% at 50% -80.63%, #18152E 70%, #4D4494 80%, #A0837E 90%, var(--color-border) 100%); - font-size: var(--font-size-9); - text-shadow: 0 0 2px @light-black; - - display: flex; - align-items: center; - justify-content: center; - } - } - - &:hover { - .trait-name { - color: light-dark(@dark, @beige); - text-shadow: 0 0 8px light-dark(@dark-80, @beige-80); - } + .trait-value { + font-style: normal; + font-weight: 400; + font-size: var(--font-size-20); + text-align: center; } } } diff --git a/styles/less/sheets/actors/character/index.less b/styles/less/sheets/actors/character/index.less deleted file mode 100644 index edefe0a1..00000000 --- a/styles/less/sheets/actors/character/index.less +++ /dev/null @@ -1,8 +0,0 @@ -@import './biography.less'; -@import './effects.less'; -@import './features.less'; -@import './header.less'; -@import './inventory.less'; -@import './loadout.less'; -@import './sheet.less'; -@import './sidebar.less'; diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index fcfbbee9..12f63753 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.character { .tab.inventory { @@ -9,9 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); + padding: 20px 0; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less index fa3e0176..eba55890 100644 --- a/styles/less/sheets/actors/character/loadout.less +++ b/styles/less/sheets/actors/character/loadout.less @@ -1,13 +1,51 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.character { .tab.loadout { .search-section { + display: flex; + align-items: center; + justify-content: space-between; + + .search-bar { + position: relative; + color: light-dark(@dark-blue-50, @beige-50); + width: 80%; + padding-top: 5px; + + input { + border-radius: 50px; + background: light-dark(@dark-blue-10, @golden-10); + border: none; + outline: 2px solid transparent; + transition: all 0.3s ease; + padding: 0 20px; + + &:hover { + outline: 2px solid light-dark(@dark, @golden); + } + + &::-webkit-search-cancel-button { + -webkit-appearance: none; + display: none; + } + } + + .icon { + align-content: center; + height: 32px; + position: absolute; + right: 20px; + font-size: var(--font-size-16); + z-index: 1; + color: light-dark(@dark-blue-50, @beige-50); + } + } + .btn-toggle-view { background: light-dark(@dark-blue-10, @dark-blue); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 15px; padding: 0; gap: 0; @@ -16,7 +54,7 @@ span { margin: 1px; width: 26px; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.list-icon { i { @@ -52,9 +90,11 @@ gap: 10px; height: 100%; overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%); + padding: 20px 0; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index c76ee9ff..b159a8e8 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -40,7 +40,7 @@ .application.sheet.dh-style .character-sidebar-sheet { width: 275px; min-width: 275px; - border-right: 1px solid @color-border; + border-right: 1px solid light-dark(@dark-blue, @golden); .portrait { position: relative; @@ -74,6 +74,62 @@ .death-roll-btn { display: none; } + + .icons-list { + position: absolute; + display: flex; + flex-direction: column; + gap: 5px; + align-items: end; + justify-content: center; + top: 45px; + right: 10px; + + .spellcast-icon { + display: flex; + align-items: center; + justify-content: end; + text-align: center; + padding-right: 8px; + max-width: 50px; + height: 50px; + font-size: 1.2rem; + background: light-dark(@dark-blue-60, @dark-golden-80); + backdrop-filter: blur(8px); + border: 4px double light-dark(@beige, @golden); + color: light-dark(@beige, @golden); + border-radius: 999px; + transition: all 0.3s ease; + + .spellcast-label { + font-size: var(--font-size-14); + opacity: 0; + margin-right: 0.3rem; + transition: all 0.3s ease; + } + + i { + height: 24px; + width: 24px; + align-content: center; + margin-right: 3px; + } + + &:not(.no-label):hover { + max-width: 300px; + padding: 0 10px; + border-radius: 60px; + + .spellcast-label { + opacity: 1; + } + + i { + margin-right: 0px; + } + } + } + } } .info-section { @@ -168,7 +224,7 @@ appearance: none; width: 100px; height: 40px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; @@ -282,11 +338,11 @@ &:hover { background: light-dark(@light-black, @dark-blue); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); h4, i { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } @@ -309,12 +365,12 @@ flex-wrap: wrap; gap: 4px; padding: 5px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; justify-content: center; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .armor-slot { cursor: pointer; @@ -343,12 +399,12 @@ &:hover { background: light-dark(@light-black, @dark-blue); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); .label, .value, i { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } @@ -375,7 +431,7 @@ text-align: center; z-index: 2; color: light-dark(@dark-blue, @beige); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-bottom: none; border-radius: 6px 6px 0 0; @@ -411,7 +467,7 @@ appearance: none; width: 80px; height: 30px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: light-dark(transparent, @dark-blue); @@ -450,7 +506,7 @@ display: flex; width: 50px; height: 30px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-bottom: none; border-radius: 6px 6px 0 0; padding: 0 6px; @@ -513,9 +569,9 @@ align-self: center; gap: 10px; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 5px 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; align-items: center; width: fit-content; @@ -525,7 +581,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.threshold-value { color: light-dark(@dark, @beige); @@ -549,11 +605,13 @@ overflow-y: hidden; padding-top: 10px; padding-bottom: 20px; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); scrollbar-gutter: stable; - .with-scroll-shadows(); - + scrollbar-width: thin; + &:hover { overflow-y: auto; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } diff --git a/styles/less/sheets/actors/companion/effects.less b/styles/less/sheets/actors/companion/effects.less index c0cac669..12e1d847 100644 --- a/styles/less/sheets/actors/companion/effects.less +++ b/styles/less/sheets/actors/companion/effects.less @@ -7,8 +7,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%); padding-bottom: 20px; - .with-scroll-shadows(); + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index aca789a6..2a162a25 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -11,7 +11,7 @@ .profile { height: 235px; cursor: pointer; - .smooth-gradient-ease-in-out(mask-image, to top, black, 2.25rem); + mask-image: linear-gradient(0deg, transparent 0%, black 10%); } .actor-name { @@ -24,20 +24,15 @@ margin-bottom: -30px; input[type='text'] { - backdrop-filter: none; - border: none; - font-family: @font-title; font-size: var(--font-size-24); - outline: 2px solid transparent; - box-shadow: unset; - text-shadow: 0 0 4px @color-text-shadow-contrast, 0 0 8px @color-text-shadow-contrast, 0 0 14px @color-text-shadow-contrast; - - height: 2rem; + height: 32px; text-align: center; + border: 1px solid transparent; + outline: 2px solid transparent; transition: all 0.3s ease; &:hover { - outline: 2px solid @color-border; + outline: 2px solid light-dark(@dark, @golden); } } } @@ -68,7 +63,7 @@ display: flex; width: 50px; height: 40px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-bottom: none; border-radius: 6px 6px 0 0; padding: 0 6px; @@ -100,6 +95,104 @@ } } + // .status-bar { + // display: flex; + // justify-content: center; + // position: relative; + // width: 100px; + // height: 40px; + + // .status-label { + // position: relative; + // top: 40px; + // height: 22px; + // width: 79px; + // clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z'); + // background: light-dark(@dark-blue, @golden); + + // h4 { + // font-weight: bold; + // text-align: center; + // line-height: 18px; + // color: light-dark(@beige, @dark-blue); + // } + // } + // .status-value { + // position: absolute; + // display: flex; + // padding: 0 6px; + // font-size: 1.5rem; + // align-items: center; + // width: 100px; + // height: 40px; + // justify-content: center; + // text-align: center; + // z-index: 2; + // color: @beige; + + // input[type='number'] { + // background: transparent; + // font-size: 1.5rem; + // width: 40px; + // height: 30px; + // text-align: center; + // border: none; + // outline: 2px solid transparent; + // color: @beige; + + // &.bar-input { + // padding: 0; + // color: @beige; + // backdrop-filter: none; + // background: transparent; + // transition: all 0.3s ease; + + // &:hover, + // &:focus { + // background: @semi-transparent-dark-blue; + // backdrop-filter: blur(9.5px); + // } + // } + // } + + // .bar-label { + // width: 40px; + // } + // } + // .progress-bar { + // position: absolute; + // appearance: none; + // width: 100px; + // height: 40px; + // border: 1px solid light-dark(@dark-blue, @golden); + // border-radius: 6px; + // z-index: 1; + // background: @dark-blue; + + // &::-webkit-progress-bar { + // border: none; + // background: @dark-blue; + // border-radius: 6px; + // } + // &::-webkit-progress-value { + // background: @gradient-hp; + // border-radius: 6px; + // } + // &.stress-color::-webkit-progress-value { + // background: @gradient-stress; + // border-radius: 6px; + // } + // &::-moz-progress-bar { + // background: @gradient-hp; + // border-radius: 6px; + // } + // &.stress-color::-moz-progress-bar { + // background: @gradient-stress; + // border-radius: 6px; + // } + // } + // } + .level-div { white-space: nowrap; display: flex; @@ -148,8 +241,10 @@ } .companion-navigation { + display: flex; + gap: 8px; + align-items: center; width: 100%; - padding: 0 10px; } } } diff --git a/styles/less/sheets/actors/companion/index.less b/styles/less/sheets/actors/companion/index.less deleted file mode 100644 index c4931814..00000000 --- a/styles/less/sheets/actors/companion/index.less +++ /dev/null @@ -1,4 +0,0 @@ -@import './details.less'; -@import './header.less'; -@import './sheet.less'; -@import './effects.less'; diff --git a/styles/less/sheets/actors/companion/sheet.less b/styles/less/sheets/actors/companion/sheet.less index 8bf8a0b9..f31679ba 100644 --- a/styles/less/sheets/actors/companion/sheet.less +++ b/styles/less/sheets/actors/companion/sheet.less @@ -10,16 +10,3 @@ background: url('../assets/parchments/dh-parchment-light.png'); } }); - -.application.sheet.daggerheart.actor.dh-style.companion { - .window-content { - display: flex; - } - - .tab.active { - flex: 1; - overflow: hidden; - display: flex; - flex-direction: column; - } -} diff --git a/styles/less/sheets/actors/environment/features.less b/styles/less/sheets/actors/environment/actions.less similarity index 59% rename from styles/less/sheets/actors/environment/features.less rename to styles/less/sheets/actors/environment/actions.less index 84cf26f8..51385322 100644 --- a/styles/less/sheets/actors/environment/features.less +++ b/styles/less/sheets/actors/environment/actions.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.environment { .tab.features { @@ -9,8 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 5%); + padding-bottom: 20px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/environment/header.less b/styles/less/sheets/actors/environment/header.less index da6954e0..670f6c92 100644 --- a/styles/less/sheets/actors/environment/header.less +++ b/styles/less/sheets/actors/environment/header.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.environment { .environment-header-sheet { @@ -11,82 +10,60 @@ .profile { height: 235px; + mask-image: linear-gradient(0deg, transparent 0%, black 10%); cursor: pointer; - .smooth-gradient-ease-in-out(mask-image, to top, black, 3.5rem); } .item-container { - display: grid; - grid-auto-flow: row; - grid-template-columns: 1fr min-content; - + display: flex; align-items: center; position: relative; - top: -36px; - gap: 0 var(--spacer-12); + top: -45px; + gap: 20px; padding: 0 20px; - margin-bottom: -26px; + margin-bottom: -30px; - .item-name input[type='text'] { - backdrop-filter: none; - border: none; - font-family: @font-title; - font-size: var(--font-size-32); - text-align: start; - transition: all 0.3s ease; - outline: 2px solid transparent; - box-shadow: none; - text-shadow: 0 0 4px @color-text-shadow-contrast, 0 0 8px @color-text-shadow-contrast, 0 0 14px @color-text-shadow-contrast; - padding-left: 0; - height: 2.625rem; - - &:hover[type='text'], - &:focus[type='text'] { - box-shadow: none; - outline: 2px solid @color-border; - } - } - - .flexrow { - align-items: baseline; - grid-column: span 2; - } - - .tags { + .item-info { display: flex; - gap: 10px; - padding-bottom: 0; - flex: 0; + flex-direction: column; + gap: 8px; - .tag { + .tags { display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - padding: 3px 5px; - font-size: var(--font-size-12); - font: @font-body; - white-space: nowrap; + gap: 10px; + padding-bottom: 0; - background: light-dark(@dark-15, @beige-15); - border: 1px solid light-dark(@dark, @beige); - border-radius: 3px; + .tag { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 3px 5px; + font-size: var(--font-size-12); + font: @font-body; + + background: light-dark(@dark-15, @beige-15); + border: 1px solid light-dark(@dark, @beige); + border-radius: 3px; + } + + .label { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font-size: var(--font-size-12); + } } - .label { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - font-size: var(--font-size-12); + .attribution-header-label { + text-align: left; + position: relative; + top: 4px; + margin-bottom: -6px; } } - .attribution-header-label { - text-align: right; - position: relative; - } - .status-number { display: flex; align-items: center; @@ -97,14 +74,14 @@ display: flex; width: 50px; height: 30px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-bottom: none; border-radius: 6px 6px 0 0; padding: 0 6px; font-size: 1.2rem; align-items: center; justify-content: center; - background: light-dark(#e8e6e3, @dark-blue); + background: light-dark(transparent, @dark-blue); z-index: 2; &.armor-slots { @@ -116,7 +93,7 @@ .status-label { padding: 2px 10px; width: 100%; - border-radius: 0 0 3px 3px; + border-radius: 3px; background: light-dark(@dark-blue, @golden); h4 { @@ -128,21 +105,37 @@ } } } + + .item-name { + input[type='text'] { + font-size: var(--font-size-32); + height: 42px; + text-align: start; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } + } + } } .environment-info { display: flex; flex-direction: column; - gap: var(--spacer-8); + gap: 12px; padding: 10px 20px; } .environment-navigation { + display: flex; + gap: 20px; + align-items: center; padding: 0 20px; - - .tab-navigation { - margin-top: 0; - } } } } diff --git a/styles/less/sheets/actors/environment/index.less b/styles/less/sheets/actors/environment/index.less deleted file mode 100644 index 211c8e60..00000000 --- a/styles/less/sheets/actors/environment/index.less +++ /dev/null @@ -1,4 +0,0 @@ -@import './features.less'; -@import './header.less'; -@import './potentialAdversaries.less'; -@import './sheet.less'; diff --git a/styles/less/sheets/actors/environment/potentialAdversaries.less b/styles/less/sheets/actors/environment/potentialAdversaries.less index f112c0d2..274362a5 100644 --- a/styles/less/sheets/actors/environment/potentialAdversaries.less +++ b/styles/less/sheets/actors/environment/potentialAdversaries.less @@ -7,8 +7,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 5%); + padding-bottom: 20px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/environment/sheet.less b/styles/less/sheets/actors/environment/sheet.less index 2d9cc188..3ea14bc7 100644 --- a/styles/less/sheets/actors/environment/sheet.less +++ b/styles/less/sheets/actors/environment/sheet.less @@ -5,6 +5,10 @@ .appTheme({ &.environment { background-image: url('../assets/parchments/dh-parchment-dark.png'); + + .attribution-header-label { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } } }, { &.environment { @@ -16,6 +20,8 @@ .tab { flex: 1; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; &.active { overflow: hidden; diff --git a/styles/less/sheets/actors/index.less b/styles/less/sheets/actors/index.less deleted file mode 100644 index 959bc0f5..00000000 --- a/styles/less/sheets/actors/index.less +++ /dev/null @@ -1,7 +0,0 @@ -@import './actor-sheet-shared.less'; -@import './adversary/index.less'; -@import './character/index.less'; -@import './companion/index.less'; -@import './environment/index.less'; -@import './npc/index.less'; -@import './party/index.less'; \ No newline at end of file diff --git a/styles/less/sheets/actors/npc/features.less b/styles/less/sheets/actors/npc/features.less deleted file mode 100644 index a579d9f8..00000000 --- a/styles/less/sheets/actors/npc/features.less +++ /dev/null @@ -1,18 +0,0 @@ -.application.sheet.daggerheart.actor.dh-style.npc { - .tab.features { - &.active { - overflow: hidden; - display: flex; - flex-direction: column; - } - - .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); - } - } -} diff --git a/styles/less/sheets/actors/npc/header.less b/styles/less/sheets/actors/npc/header.less deleted file mode 100644 index 086a254c..00000000 --- a/styles/less/sheets/actors/npc/header.less +++ /dev/null @@ -1,83 +0,0 @@ -.application.sheet.daggerheart.actor.dh-style.npc { - .npc-header-sheet { - width: 100%; - display: flex; - - .portrait { - cursor: pointer; - max-width: 275px; - - img { - height: 275px; - } - } - - .tags { - display: flex; - gap: 10px; - padding-bottom: 8px; - - .tag { - display: flex; - flex-direction: row; - gap: 4px; - justify-content: center; - align-items: center; - padding: 3px 5px; - font-size: var(--font-size-12); - font: @font-body; - - background: light-dark(@dark-15, @beige-15); - border: 1px solid light-dark(@dark, @beige); - border-radius: 3px; - } - - .label { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - font-size: var(--font-size-12); - } - } - - .info-section { - flex: 1; - padding: 0 15px; - padding-top: var(--header-height); - display: flex; - flex-direction: column; - - .name-row { - display: flex; - gap: 5px; - align-items: center; - justify-content: space-between; - padding: 8px 0; - - h1 { - display: flex; - flex: 1; - padding: 6px 0 0 0; - font-size: var(--font-size-32); - text-align: start; - border: 1px solid transparent; - outline: 2px solid transparent; - transition: all 0.3s ease; - word-break: break-word; - - &:hover { - outline: 2px solid light-dark(@dark, @golden); - } - } - } - - .npc-info { - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px 0; - } - } - } -} \ No newline at end of file diff --git a/styles/less/sheets/actors/npc/index.less b/styles/less/sheets/actors/npc/index.less deleted file mode 100644 index 2d7d54e3..00000000 --- a/styles/less/sheets/actors/npc/index.less +++ /dev/null @@ -1,3 +0,0 @@ -@import './sheet.less'; -@import './header.less'; -@import './features.less'; \ No newline at end of file diff --git a/styles/less/sheets/actors/npc/sheet.less b/styles/less/sheets/actors/npc/sheet.less deleted file mode 100644 index 8ba3b7a9..00000000 --- a/styles/less/sheets/actors/npc/sheet.less +++ /dev/null @@ -1,10 +0,0 @@ -.application.sheet.daggerheart.actor.dh-style.npc { - .window-content { - display: grid; - grid-template-rows: auto auto 1fr; - } - - .npc-navigation { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/styles/less/sheets/actors/party/header.less b/styles/less/sheets/actors/party/header.less index 58e3bc31..9a2c7350 100644 --- a/styles/less/sheets/actors/party/header.less +++ b/styles/less/sheets/actors/party/header.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .party-header-sheet { display: flex; @@ -10,31 +9,26 @@ .profile { height: 235px; + mask-image: linear-gradient(0deg, transparent 0%, black 10%); cursor: pointer; - .smooth-gradient-ease-in-out(mask-image, to top, black, 3.5rem); } .item-container { - margin-top: -2rem; - z-index: 1; - input.item-name[type='text'] { - backdrop-filter: none; - border: none; - color: @color-text-emphatic; - font-family: @font-title; - font-size: var(--font-size-32); - outline: 2px solid transparent; - box-shadow: unset; - text-shadow: 0 0 4px @color-text-shadow-contrast, 0 0 8px @color-text-shadow-contrast, 0 0 14px @color-text-shadow-contrast; + .item-name { + padding: 0 20px; + input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; - text-align: center; - transition: all 0.3s ease; - width: calc(100% - 40px); - height: 2.625rem; - - &:hover[type='text'], - &:focus[type='text'] { - outline: 2px solid @color-border; + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } } } diff --git a/styles/less/sheets/actors/party/index.less b/styles/less/sheets/actors/party/index.less deleted file mode 100644 index 56f7a457..00000000 --- a/styles/less/sheets/actors/party/index.less +++ /dev/null @@ -1,4 +0,0 @@ -@import './header.less'; -@import './party-members.less'; -@import './sheet.less'; -@import './inventory.less'; diff --git a/styles/less/sheets/actors/party/inventory.less b/styles/less/sheets/actors/party/inventory.less index 444c6a57..ac59e1de 100644 --- a/styles/less/sheets/actors/party/inventory.less +++ b/styles/less/sheets/actors/party/inventory.less @@ -1,6 +1,5 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.party { .tab.inventory { @@ -9,9 +8,11 @@ flex-direction: column; gap: 10px; overflow-y: auto; - margin-top: 20px; - padding-bottom: 4px; - .with-scroll-shadows(); + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); + padding: 20px 0; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } } diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index dc464291..2e2f4cf8 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -2,6 +2,16 @@ @import '../../../utils/fonts.less'; @import '../../../utils/mixin.less'; +body.game:is(.performance-low, .noblur) { + .application.sheet.daggerheart.actor.dh-style.party .tab.resources .actors-list .actor-resources { + background: light-dark(@dark-blue, @dark-golden); + + .actor-name { + background: light-dark(@dark-blue, @dark-golden); + } + } +} + .application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { overflow: auto; @@ -48,7 +58,7 @@ border-radius: 50%; width: 24px; height: 24px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); object-fit: cover; } } @@ -79,9 +89,9 @@ display: flex; gap: 4px; background-color: light-dark(var(--color-light-1), @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 4px 6px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 3px; align-items: baseline; width: fit-content; @@ -93,7 +103,7 @@ &.threshold-label { font-size: var(--font-size-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } &.threshold-value { @@ -116,7 +126,7 @@ width: 100%; z-index: 1; font-size: var(--font-size-20); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); font-weight: bold; } @@ -132,9 +142,9 @@ .hope-section { display: flex; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 3px 6px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 3px; align-items: center; width: fit-content; @@ -144,7 +154,7 @@ font-size: var(--font-size-12); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); margin-right: 3px; } @@ -212,9 +222,9 @@ gap: 4px; background-color: light-dark(@dark-blue-10, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 2px 5px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 0 6px 6px 0; width: fit-content; min-height: 22px; @@ -232,7 +242,7 @@ .slot { width: 16px; height: 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); background: light-dark(@dark-blue-10, @golden-10); border-radius: 3px; transition: all 0.3s ease; @@ -248,7 +258,7 @@ .traits { background-color: light-dark(@dark-blue-10, @dark-blue); - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; display: grid; grid-template-columns: 1fr 1fr; @@ -260,7 +270,7 @@ justify-content: space-between; gap: 3px; .label { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } .value { font-weight: 600; @@ -270,17 +280,6 @@ } } - .actors-list.limited { - .actor-resources { - display: flex; - align-items: center; - } - .actor-img-frame { - width: 3rem; - height: 3rem; - } - } - .actors-dragger { display: flex; align-items: center; diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less index 852b6cfc..6b51de53 100644 --- a/styles/less/sheets/actors/party/sheet.less +++ b/styles/less/sheets/actors/party/sheet.less @@ -20,6 +20,8 @@ .tab { flex: 1; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; scrollbar-gutter: stable; &.active { diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index 451ae03a..7d595614 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -1,5 +1,45 @@ -@import './activeEffects/index.less'; -@import './actions/index.less'; -@import './actors/index.less'; -@import './items/index.less'; -@import './rollTables/index.less'; \ No newline at end of file +@import './actions/actions.less'; + +@import './actors/actor-sheet-shared.less'; + +@import './actors/adversary/actions.less'; +@import './actors/adversary/header.less'; +@import './actors/adversary/sheet.less'; +@import './actors/adversary/sidebar.less'; +@import './actors/adversary/effects.less'; + +@import './actors/character/biography.less'; +@import './actors/character/effects.less'; +@import './actors/character/features.less'; +@import './actors/character/header.less'; +@import './actors/character/inventory.less'; +@import './actors/character/loadout.less'; +@import './actors/character/sheet.less'; +@import './actors/character/sidebar.less'; + +@import './actors/companion/details.less'; +@import './actors/companion/header.less'; +@import './actors/companion/sheet.less'; +@import './actors/companion/effects.less'; + +@import './actors/environment/actions.less'; +@import './actors/environment/header.less'; +@import './actors/environment/potentialAdversaries.less'; +@import './actors/environment/sheet.less'; + +@import './actors/party/header.less'; +@import './actors/party/party-members.less'; +@import './actors/party/sheet.less'; +@import './actors/party/inventory.less'; + +@import './items/beastform.less'; +@import './items/class.less'; +@import './items/domain-card.less'; +@import './items/feature.less'; +@import './items/heritage.less'; +@import './items/item-sheet-shared.less'; + +@import './rollTables/sheet.less'; +@import './actions/actions.less'; + +@import './activeEffects/activeEffects.less'; diff --git a/styles/less/sheets/items/domain-card.less b/styles/less/sheets/items/domain-card.less index 54378fd0..a784b3a2 100644 --- a/styles/less/sheets/items/domain-card.less +++ b/styles/less/sheets/items/domain-card.less @@ -5,5 +5,7 @@ section.tab { height: 400px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } diff --git a/styles/less/sheets/items/feature.less b/styles/less/sheets/items/feature.less index f3c7cd49..b7493f15 100644 --- a/styles/less/sheets/items/feature.less +++ b/styles/less/sheets/items/feature.less @@ -14,5 +14,7 @@ section.tab { height: 400px; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } } diff --git a/styles/less/sheets/items/index.less b/styles/less/sheets/items/index.less deleted file mode 100644 index 7c40a2e3..00000000 --- a/styles/less/sheets/items/index.less +++ /dev/null @@ -1,6 +0,0 @@ -@import './beastform.less'; -@import './class.less'; -@import './domain-card.less'; -@import './feature.less'; -@import './heritage.less'; -@import './item-sheet-shared.less'; \ No newline at end of file diff --git a/styles/less/sheets/items/item-sheet-shared.less b/styles/less/sheets/items/item-sheet-shared.less index 5155ad70..d0a8cc48 100644 --- a/styles/less/sheets/items/item-sheet-shared.less +++ b/styles/less/sheets/items/item-sheet-shared.less @@ -10,8 +10,4 @@ font-family: @font-body; color: light-dark(@chat-blue-bg, @beige-50); } - - button.plain.inline-control { - flex: 0 0 auto; - } } diff --git a/styles/less/sheets/rollTables/index.less b/styles/less/sheets/rollTables/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/sheets/rollTables/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/ui/chat/group-roll.less b/styles/less/ui/chat/group-roll.less index 98f0cfac..9ed87220 100644 --- a/styles/less/ui/chat/group-roll.less +++ b/styles/less/ui/chat/group-roll.less @@ -31,7 +31,7 @@ } i { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } } } @@ -71,7 +71,7 @@ align-items: center; justify-content: center; gap: 10px; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .main-value { font-size: var(--font-size-24); @@ -153,7 +153,7 @@ cursor: pointer; padding: 5px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); &.finished { background-color: initial; diff --git a/styles/less/ui/chat/index.less b/styles/less/ui/chat/index.less deleted file mode 100644 index 9cadbd04..00000000 --- a/styles/less/ui/chat/index.less +++ /dev/null @@ -1,10 +0,0 @@ -@import './sheet.less'; -@import './ability-use.less'; -@import './action.less'; -@import './chat.less'; -@import './damage-summary.less'; -@import './deathmoves.less'; -@import './downtime.less'; -@import './effect-summary.less'; -@import './group-roll.less'; -@import './refresh-message.less'; \ No newline at end of file diff --git a/styles/less/ui/chat/sheet.less b/styles/less/ui/chat/sheet.less index bdf22364..66b539b4 100644 --- a/styles/less/ui/chat/sheet.less +++ b/styles/less/ui/chat/sheet.less @@ -93,7 +93,7 @@ } a[href] { - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } a[href]:hover, @@ -195,7 +195,7 @@ fieldset.daggerheart.chat { &:before, &:after { content: '\f0d8'; - font-family: var(--font-awesome); + font-family: 'Font Awesome 6 Pro'; } } &.expanded { diff --git a/styles/less/ui/combat-sidebar/index.less b/styles/less/ui/combat-sidebar/index.less deleted file mode 100644 index 786815ef..00000000 --- a/styles/less/ui/combat-sidebar/index.less +++ /dev/null @@ -1,5 +0,0 @@ -@import './combat-sidebar.less'; -@import './combatant-controls.less'; -@import './encounter-controls.less'; -@import './spotlight-control.less'; -@import './token-actions.less'; \ No newline at end of file diff --git a/styles/less/ui/countdown/countdown-edit.less b/styles/less/ui/countdown/countdown-edit.less index 78ad3a06..d6c4da93 100644 --- a/styles/less/ui/countdown/countdown-edit.less +++ b/styles/less/ui/countdown/countdown-edit.less @@ -60,6 +60,8 @@ overflow-y: auto; overflow-x: hidden; max-height: 500px; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; .countdown-edit-outer-container { display: flex; diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 63e539ba..47f06eb7 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -1,47 +1,28 @@ @import '../../utils/colors.less'; @import '../../utils/fonts.less'; -#interface.theme-dark { +.theme-dark { .daggerheart.dh-style.countdowns { - --background: url(../assets/parchments/dh-parchment-dark.png); + background-image: url(../assets/parchments/dh-parchment-dark.png); + + .window-header { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } } } -#interface.theme-light { - .daggerheart.dh-style.countdowns { - --background: url('../assets/parchments/dh-parchment-light.png') no-repeat center; - } -} - -.daggerheart.dh-style.countdowns { +.daggerheart.dh-style.countdowns { position: relative; border: 0; + border-radius: 4px; box-shadow: none; - color: @color-text-primary; - width: 18.75rem; + width: 300px; pointer-events: all; align-self: flex-end; transition: 0.3s right ease-in-out; - display: flex; - flex-direction: column; - - &::before { - content: ' '; - position: absolute; - inset: 0; - background: var(--background); - border-radius: 4px; - opacity: var(--ui-fade-opacity); - transition: opacity var(--ui-fade-duration); - } - - &:not(.performance-low, .noblur) { - backdrop-filter: blur(5px); - } - - &:hover::before { - opacity: 1; + .window-title { + font-family: @font-body; } #ui-right:has(#effects-display .effect-container) & { @@ -49,139 +30,127 @@ } &.icon-only { - width: 12rem; + width: 180px; + min-width: 180px; } - .countdowns-header, - .countdowns-container { - position: relative; // allow rendering over the background + .window-header { + cursor: default; + border-bottom: 0; } - .countdowns-header { - display: flex; - align-items: center; - gap: 0.25rem; - flex: 0 0 36px; - padding: 0 0.5rem; - overflow: hidden; - font-size: var(--font-size-13); - .window-title { - font-family: @font-body; - flex: 1; - } - .header-control + .header-control { - margin-left: 8px; - } - } - - .countdowns-container { - display: flex; - flex-direction: column; - gap: 8px; - padding: 4px var(--spacer-16) var(--spacer-16) var(--spacer-16); + .window-content { + padding-top: 4px; + padding-bottom: 16px; overflow: auto; max-height: 312px; - .countdown-container { + .countdowns-container { display: flex; - width: 100%; + flex-direction: column; + gap: 8px; - &.icon-only { - gap: 8px; + .countdown-container { + display: flex; + width: 100%; - .countdown-main-container { - .countdown-content { - justify-content: center; + &.icon-only { + gap: 8px; - .countdown-tools { - gap: 8px; + .countdown-main-container { + .countdown-content { + justify-content: center; + + .countdown-tools { + gap: 8px; + } } } } - } - .countdown-main-container { - width: 100%; - display: flex; - align-items: center; - gap: 16px; - - img { - width: 2.75rem; - height: 2.75rem; - border-radius: 6px; - } - - .countdown-content { - flex: 1; + .countdown-main-container { + width: 100%; display: flex; - flex-direction: column; - justify-content: space-between; + align-items: center; + gap: 16px; - .countdown-tools { + img { + width: 44px; + height: 44px; + border-radius: 6px; + } + + .countdown-content { + flex: 1; display: flex; - align-items: center; + flex-direction: column; justify-content: space-between; - .countdown-tool-controls { + .countdown-tools { display: flex; align-items: center; - gap: var(--spacer-12); - } + justify-content: space-between; - .progress-tag { - border: 1px solid; - border-radius: 4px; - padding: 2px 4px; - background-color: light-dark(@beige, @dark-blue); - } + .countdown-tool-controls { + display: flex; + align-items: center; + gap: 16px; + } - .countdown-tool-icons { - display: flex; - align-items: center; - gap: 8px; - - .looping-container { - position: relative; - border: 1px solid light-dark(@dark-blue, white); - border-radius: 6px; + .progress-tag { + border: 1px solid; + border-radius: 4px; padding: 2px 4px; + background-color: light-dark(@beige, @dark-blue); + } - &.should-loop { - background: light-dark(@golden, @golden); + .countdown-tool-icons { + display: flex; + align-items: center; + gap: 8px; - .loop-marker { - color: light-dark(@dark-blue, @dark-blue); + .looping-container { + position: relative; + border: 1px solid light-dark(@dark-blue, white); + border-radius: 6px; + padding: 2px 4px; + + &.should-loop { + background: light-dark(@golden, @golden); + + .loop-marker { + color: light-dark(@dark-blue, @dark-blue); + } } - } - .direction-marker { - position: absolute; - font-size: 10px; - filter: drop-shadow(0 0 3px black); - top: -3px; + .direction-marker { + position: absolute; + font-size: 10px; + filter: drop-shadow(0 0 3px black); + top: -3px; + } } } } } } - } - /* Keep incase we want to reintroduce the player pips */ - // .countdown-access-container { - // display: grid; - // grid-template-columns: 1fr 1fr 1fr; - // grid-auto-rows: min-content; - // width: 38px; - // gap: 4px; + /* Keep incase we want to reintroduce the player pips */ + // .countdown-access-container { + // display: grid; + // grid-template-columns: 1fr 1fr 1fr; + // grid-auto-rows: min-content; + // width: 38px; + // gap: 4px; - // .countdown-access { - // height: 10px; - // width: 10px; - // border-radius: 50%; - // border: 1px solid light-dark(@dark-blue, @beige-80); - // content: ''; - // } - // } + // .countdown-access { + // height: 10px; + // width: 10px; + // border-radius: 50%; + // border: 1px solid light-dark(@dark-blue, @beige-80); + // content: ''; + // } + // } + } } } } diff --git a/styles/less/ui/countdown/index.less b/styles/less/ui/countdown/index.less deleted file mode 100644 index 45ecda26..00000000 --- a/styles/less/ui/countdown/index.less +++ /dev/null @@ -1,3 +0,0 @@ -@import './sheet.less'; -@import './countdown-edit.less'; -@import './countdown.less'; \ No newline at end of file diff --git a/styles/less/ui/effects-display/index.less b/styles/less/ui/effects-display/index.less deleted file mode 100644 index 1c574e81..00000000 --- a/styles/less/ui/effects-display/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './sheet.less'; \ No newline at end of file diff --git a/styles/less/ui/effects-display/sheet.less b/styles/less/ui/effects-display/sheet.less index 80ad0d65..17d9889f 100644 --- a/styles/less/ui/effects-display/sheet.less +++ b/styles/less/ui/effects-display/sheet.less @@ -20,7 +20,7 @@ .effect-container { position: relative; pointer-events: all; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 3px; img { diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 53a71b9b..31ea8955 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -1,11 +1,40 @@ -@import './chat/index.less'; -@import './combat-sidebar/index.less'; -@import './countdown/index.less'; -@import './effects-display/index.less'; -@import './item-browser/index.less'; -@import './ownership-selection/index.less'; -@import './resources/index.less'; -@import './scene-config/index.less'; -@import './scene-navigation/index.less'; -@import './settings/index.less'; -@import './sidebar/index.less'; \ No newline at end of file +@import './chat/ability-use.less'; +@import './chat/action.less'; +@import './chat/chat.less'; +@import './chat/damage-summary.less'; +@import './chat/downtime.less'; +@import './chat/effect-summary.less'; +@import './chat/group-roll.less'; +@import './chat/refresh-message.less'; +@import './chat/deathmoves.less'; +@import './chat/sheet.less'; + +@import './combat-sidebar/combat-sidebar.less'; +@import './combat-sidebar/combatant-controls.less'; +@import './combat-sidebar/encounter-controls.less'; +@import './combat-sidebar/spotlight-control.less'; +@import './combat-sidebar/token-actions.less'; +@import './item-browser/item-browser.less'; + +@import './countdown/countdown.less'; +@import './countdown/countdown-edit.less'; +@import './countdown/sheet.less'; + +@import './ownership-selection/ownership-selection.less'; + +@import './resources/resources.less'; + +@import './settings/settings.less'; +@import './settings/homebrew-settings/domains.less'; +@import './settings/homebrew-settings/types.less'; +@import './settings/homebrew-settings/resources.less'; +@import './settings/appearance-settings/diceSoNice.less'; + +@import './sidebar/tabs.less'; +@import './sidebar/daggerheartMenu.less'; + +@import './scene-config/scene-config.less'; + +@import './effects-display/sheet.less'; + +@import './scene-navigation/scene-navigation.less'; diff --git a/styles/less/ui/item-browser/index.less b/styles/less/ui/item-browser/index.less deleted file mode 100644 index 842f716b..00000000 --- a/styles/less/ui/item-browser/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './item-browser.less'; \ No newline at end of file diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index aac63d7a..f558a0ba 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -1,6 +1,5 @@ @import '../../utils/colors.less'; @import '../../utils/fonts.less'; -@import '../../utils/mixin.less'; .application.daggerheart.dh-style.compendium-browser { border: initial; @@ -126,7 +125,6 @@ .form-group { white-space: nowrap; - gap: 0; } .filter-header { @@ -170,10 +168,6 @@ } } } - - .filter-content { - margin-top: 8px; - } } .folder-list { @@ -201,7 +195,7 @@ font-weight: bold; border-radius: 3px; background-color: light-dark(@dark-blue-40, @golden-40); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } .subfolder-list { @@ -219,7 +213,7 @@ font-weight: bold; border-radius: 3px; background-color: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); } .wrapper { @@ -243,11 +237,12 @@ .compendium-sidebar > .folder-list { overflow-y: auto; scrollbar-gutter: stable; - .with-scroll-shadows(); + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; } .item-list-header, - .item-list .item-info[data-action] { + .item-list [data-action='expandContent'] { display: flex; > * { @@ -267,8 +262,8 @@ .item-list-header { align-items: center; background-color: light-dark(@dark-15, @dark-golden-80); - color: @color-text-emphatic; - border: 1px solid @color-border; + color: light-dark(@dark-blue, @golden); + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 3px; min-height: 30px; font-weight: bold; @@ -279,7 +274,7 @@ div[data-sort-key] { &:after { - font-family: var(--font-awesome); + font-family: 'Font Awesome 6 Pro'; margin-left: 5px; } diff --git a/styles/less/ui/ownership-selection/index.less b/styles/less/ui/ownership-selection/index.less deleted file mode 100644 index 9646670a..00000000 --- a/styles/less/ui/ownership-selection/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './ownership-selection.less'; \ No newline at end of file diff --git a/styles/less/ui/resources/index.less b/styles/less/ui/resources/index.less deleted file mode 100644 index a7d08785..00000000 --- a/styles/less/ui/resources/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './resources.less'; \ No newline at end of file diff --git a/styles/less/ui/scene-config/index.less b/styles/less/ui/scene-config/index.less deleted file mode 100644 index 4e3af363..00000000 --- a/styles/less/ui/scene-config/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './scene-config.less'; \ No newline at end of file diff --git a/styles/less/ui/scene-navigation/index.less b/styles/less/ui/scene-navigation/index.less deleted file mode 100644 index c0765ae7..00000000 --- a/styles/less/ui/scene-navigation/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './scene-navigation.less'; \ No newline at end of file diff --git a/styles/less/ui/settings/appearance-settings/index.less b/styles/less/ui/settings/appearance-settings/index.less deleted file mode 100644 index 8b1c109a..00000000 --- a/styles/less/ui/settings/appearance-settings/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './diceSoNice.less'; \ No newline at end of file diff --git a/styles/less/ui/settings/homebrew-settings/domains.less b/styles/less/ui/settings/homebrew-settings/domains.less index 6314cc66..da258fcd 100644 --- a/styles/less/ui/settings/homebrew-settings/domains.less +++ b/styles/less/ui/settings/homebrew-settings/domains.less @@ -55,12 +55,14 @@ gap: 8px; max-height: 184px; overflow: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(#18162e, #f3c267) transparent; .domain-container { position: relative; display: flex; justify-content: center; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; &.selectable { @@ -76,7 +78,7 @@ .domain-label { position: absolute; top: 4px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; padding: 0 2px; color: light-dark(@dark, @beige); diff --git a/styles/less/ui/settings/homebrew-settings/index.less b/styles/less/ui/settings/homebrew-settings/index.less deleted file mode 100644 index f0a8bfc1..00000000 --- a/styles/less/ui/settings/homebrew-settings/index.less +++ /dev/null @@ -1,3 +0,0 @@ -@import './domains.less'; -@import './resources.less'; -@import './types.less'; \ No newline at end of file diff --git a/styles/less/ui/settings/homebrew-settings/resources.less b/styles/less/ui/settings/homebrew-settings/resources.less index 9d562756..5333e54d 100644 --- a/styles/less/ui/settings/homebrew-settings/resources.less +++ b/styles/less/ui/settings/homebrew-settings/resources.less @@ -24,7 +24,7 @@ .resource-icons-container { display: flex; justify-content: space-between; - gap: 10px; + gap: 8px; width: 100%; .resource-icon-container { @@ -61,7 +61,7 @@ display: flex; align-items: center; gap: 4px; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); i { font-size: 14px; diff --git a/styles/less/ui/settings/homebrew-settings/types.less b/styles/less/ui/settings/homebrew-settings/types.less index 1d568853..d09431f7 100644 --- a/styles/less/ui/settings/homebrew-settings/types.less +++ b/styles/less/ui/settings/homebrew-settings/types.less @@ -21,7 +21,7 @@ border: 1px solid; border-radius: 6px; padding: 0 8px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); color: light-dark(@dark, @beige); background-image: url('../assets/parchments/dh-parchment-dark.png'); cursor: pointer; diff --git a/styles/less/ui/settings/index.less b/styles/less/ui/settings/index.less deleted file mode 100644 index 4e1aa798..00000000 --- a/styles/less/ui/settings/index.less +++ /dev/null @@ -1,3 +0,0 @@ -@import './settings.less'; -@import './appearance-settings/index.less'; -@import './homebrew-settings/index.less'; \ No newline at end of file diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 35c48480..34f17d53 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -1,67 +1,6 @@ @import '../../utils/colors.less'; .daggerheart.dh-style.setting { - --color-form-label: @color-text-primary; - - h2, - h3, - h4 { - margin: 8px 0 4px; - text-align: center; - } - - footer { - margin-top: 8px; - display: flex; - gap: 8px; - - button { - flex: 1; - } - } - - .standard-form { - gap: var(--spacer-8); - .form-group .form-fields { - width: unset; - } - } - - .form-group { - display: flex; - justify-content: space-between; - align-items: center; - gap: 0.25rem 0.5rem; - flex-wrap: wrap; - - label { - font-size: var(--font-size-14); - font-weight: normal; - line-height: var(--input-height); - } - - .form-fields { - display: flex; - gap: 4px; - align-items: center; - } - - &.setting-two-values { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 0.25rem 0.5rem; - - .form-group { - justify-content: end; - flex-wrap: nowrap; - } - - .hint { - grid-column: 1 / -1; - } - } - } - fieldset { display: flex; flex-direction: column; @@ -80,10 +19,7 @@ &.three-columns { display: grid; grid-template-columns: 1fr 1fr 1fr; - gap: 4px; - .form-group label { - line-height: unset; - } + gap: 2px; } &.six-columns { diff --git a/styles/less/ui/sidebar/daggerheartMenu.less b/styles/less/ui/sidebar/daggerheartMenu.less index 280d5ad3..88b139c5 100644 --- a/styles/less/ui/sidebar/daggerheartMenu.less +++ b/styles/less/ui/sidebar/daggerheartMenu.less @@ -34,7 +34,7 @@ cursor: pointer; padding: 5px; background: light-dark(@dark-blue-10, @golden-10); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); .label { font-style: normal; diff --git a/styles/less/ui/sidebar/index.less b/styles/less/ui/sidebar/index.less deleted file mode 100644 index b4961b41..00000000 --- a/styles/less/ui/sidebar/index.less +++ /dev/null @@ -1,2 +0,0 @@ -@import './daggerheartMenu.less'; -@import './tabs.less'; \ No newline at end of file diff --git a/styles/less/utils/colors.less b/styles/less/utils/colors.less index bb219ebb..8f6418bf 100755 --- a/styles/less/utils/colors.less +++ b/styles/less/utils/colors.less @@ -5,7 +5,6 @@ --golden: #f3c267; --golden-10: #f3c26710; --golden-40: #f3c26740; - --golden-60: #f3c26760; --golden-90: #f3c26790; --golden-bg: #f3c2671a; --golden-secondary: #eaaf42; @@ -51,7 +50,6 @@ --dark-blue-50: #18162e50; --dark-blue-60: #18162e60; --dark-blue-90: #18162e90; - --dark-blue-c0: #18162ec0; --semi-transparent-dark-blue: rgba(24, 22, 46, 0.33); --dark: #222222; @@ -71,7 +69,6 @@ --beige-filter: brightness(0) saturate(100%) invert(87%) sepia(25%) saturate(164%) hue-rotate(339deg) brightness(106%) contrast(87%); --bright-beige-filter: brightness(0) saturate(100%) invert(87%) sepia(15%) saturate(343%) hue-rotate(333deg) brightness(110%) contrast(87%); - --light-white: rgba(255, 255, 255, 0.3); --soft-white-shadow: rgba(255, 255, 255, 0.05); --light-black: rgba(0, 0, 0, 0.3); @@ -86,54 +83,12 @@ --primary-color-fear: rgba(9, 71, 179, 0.75); } -/** - * Override core foundry color variables in theme scopes, and add some new theme specific variants. - * These are intended to be more representative of its use case, so avoid naming these after the color. - * Some foundry variables are heavily scoped and we need to redefine them (https://github.com/foundryvtt/foundryvtt/issues/12893) - * Convention for the redefined ones is --dh-{element}-color-{thing} - */ -@scope (.theme-light) to (.themed) { - .dh-style :scope, - :scope.dh-style, - .dh-style, - .duality { - --color-border: @dark-blue; - --color-fieldset-border: @dark-blue; - --color-text-emphatic: @dark-blue; - --color-text-subtle: #555; - - --dh-color-text-shadow-contrast: #ffffffa0; - --dh-input-color-border: @dark; - --dh-input-color-text: @dark; - --dh-trait-color-bg: #b1afb6; - --dh-trait-color-border: #8e8d96; - } -} -@scope (.theme-dark) to (.themed) { - .dh-style :scope, - :scope.dh-style, - .dh-style, - .duality { - --color-border: @golden; - --color-fieldset-border: @golden; - --color-text-emphatic: @golden; - --color-text-subtle: #a29086; - - --dh-color-text-shadow-contrast: @dark-80; - --dh-input-color-border: @beige; - --dh-input-color-text: @beige; - --dh-trait-color-bg: #50433F; - --dh-trait-color-border: #927952; - } -} - @primary-blue: var(--primary-blue, #1488cc); @secondary-blue: var(--secondary-blue, #2b32b2); @golden: var(--golden, #f3c267); @golden-10: var(--golden-10, #f3c26710); @golden-40: var(--golden-40, #f3c26740); -@golden-60: var(--golden-60, #f3c26760); @golden-90: var(--golden-90, #f3c26790); @golden-bg: var(--golden-bg, #f3c2671a); @golden-secondary: var(--golden-secondary, #eaaf42); @@ -179,7 +134,6 @@ @dark-blue-50: var(--dark-blue-50, #18162e50); @dark-blue-60: var(--dark-blue-60, #18162e60); @dark-blue-90: var(--dark-blue-90, #18162e90); -@dark-blue-c0: var(--dark-blue-c0, #18162ec0); @semi-transparent-dark-blue: var(--semi-transparent-dark-blue, rgba(24, 22, 46, 0.33)); @dark: var(--dark, #222222); @@ -201,7 +155,6 @@ @soft-white-shadow: var(--soft-white-shadow, rgba(255, 255, 255, 0.05)); -@light-white: var(--light-white, rgba(255, 255, 255, 0.3)); @light-black: var(--light-black, rgba(0, 0, 0, 0.3)); @soft-shadow: var(--soft-shadow, rgba(0, 0, 0, 0.05)); @@ -233,15 +186,3 @@ box-shadow: 0 0 2px 2px @dark-blue; } } - -// LESS variable versions of core foundry color variables -@color-border: var(--color-border); -@color-fieldset-border: var(--color-fieldset-border); -@color-text-emphatic: var(--color-text-emphatic); -@color-text-primary: var(--color-text-primary); -@color-text-subtle: var(--color-text-subtle); -@color-text-shadow-contrast: var(--dh-color-text-shadow-contrast); -@input-color-border: var(--dh-input-color-border); -@input-color-text: var(--dh-input-color-text); -@trait-color-bg: var(--dh-trait-color-bg); -@trait-color-border: var(--dh-trait-color-border); \ No newline at end of file diff --git a/styles/less/utils/fonts.less b/styles/less/utils/fonts.less index 201ea356..5c1e597a 100755 --- a/styles/less/utils/fonts.less +++ b/styles/less/utils/fonts.less @@ -5,11 +5,6 @@ --dh-font-title: 'Cinzel Decorative'; --dh-font-subtitle: 'Cinzel'; --dh-font-body: 'Montserrat'; - - /* Include missing font sizes */ - --font-size-8: 0.5rem; - --font-size-9: 0.5625rem; - --font-size-22: 1.375rem; } @font-title: ~"var(--dh-font-title, 'Cinzel Decorative'), serif"; diff --git a/styles/less/utils/index.less b/styles/less/utils/index.less deleted file mode 100644 index 37b096d3..00000000 --- a/styles/less/utils/index.less +++ /dev/null @@ -1,4 +0,0 @@ -@import './colors.less'; -@import './fonts.less'; -@import './mixin.less'; -@import './spacing.less'; \ No newline at end of file diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index 18b1f9a6..49e97a1f 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -5,16 +5,16 @@ */ .appTheme(@darkRules, @lightRules) { // Dark theme selectors - .themed.theme-dark .application.daggerheart.dh-style, - .themed.theme-dark.application.daggerheart.dh-style, + .themed.theme-dark .application.daggerheart.sheet.dh-style, + .themed.theme-dark.application.daggerheart.sheet.dh-style, body.theme-dark .application.daggerheart, body.theme-dark.application.daggerheart { @darkRules(); } // Light theme selectors - .themed.theme-light .application.daggerheart.dh-style, - .themed.theme-light.application.daggerheart.dh-style, + .themed.theme-light .application.daggerheart.sheet.dh-style, + .themed.theme-light.application.daggerheart.sheet.dh-style, body.theme-light .application.daggerheart, body.theme-light.application.daggerheart { @lightRules(); @@ -50,15 +50,14 @@ */ .dh-typography() { h1 { - --dh-input-color-text: @color-text-emphatic; font-family: @font-title; margin: 0; border: none; font-weight: normal; - input[type='text'], - .input[contenteditable] { - font-family: @font-title; - } + } + + h1 input[type='text'] { + font-family: @font-title; } h2, @@ -115,92 +114,3 @@ font-family: @font-body; } } - - -// A simple ease-out mask -.smooth-gradient-ease-out(@param, @to, @destination, @length) { - @{param}+: linear-gradient( - @to, - transparent 0%, - rgb(from @destination r g b / ~"calc(alpha * 0.013)") calc(0.008 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.049)") calc(0.029 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.104)") calc(0.064 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.259)") calc(0.166 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.45)") calc(0.304 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.741)") calc(0.554 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.825)") calc(0.644 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.896)") calc(0.735 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.951)") calc(0.825 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.987)") calc(0.914 * @length), - @destination @length - ); -} - -/** - * A simple ease in and out mask. - * @param - the parameter to add to - * @param - direction, such as "to top" - * @destination - the color at the destination. The origin is always transparent - * @length - the value at the destination - */ -.smooth-gradient-ease-in-out(@param, @to, @destination, @length: 100%) { - @{param}+: linear-gradient( - @to, - transparent 0%, - rgb(from @destination r g b / ~"calc(alpha * 0.013)") calc(0.081 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.049)") calc(0.155 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.104)") calc(0.225 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.259)") calc(0.353 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.45)") calc(0.471 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.741)") calc(0.647 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.825)") calc(0.71 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.896)") calc(0.775 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.951)") calc(0.845 * @length), - rgb(from @destination r g b / ~"calc(alpha * 0.987)") calc(0.914 * @length), - @destination @length - ); -} - -// Scroll shadows, but only if the browser supports. At the time of writing, this doesn't work on firefox -@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%)) { - @property --fade-start { - syntax: ""; - inherits: false; - initial-value: 0; - } - - @property --fade-end { - syntax: ""; - inherits: false; - initial-value: 0; - } - - @keyframes scrollfade { - 0% { - --fade-start: 0; - } - 10%, 100% { - --fade-start: 12px; - } - 0%, 90% { - --fade-end: 12px; - } - 100% { - --fade-end: 0; - } - } -} - -.with-scroll-shadows() { - animation: scrollfade; - animation-timeline: --scrollfade; - animation-range: entry 0% exit 100%; - scroll-timeline: --scrollfade y; - mask-image: linear-gradient( - 0deg, - transparent 0%, - black var(--fade-end), - black calc(100% - var(--fade-start)), - transparent 100% - ); -} diff --git a/styles/less/ux/autocomplete/autocomplete.less b/styles/less/ux/autocomplete/autocomplete.less index e778f0da..7f799449 100644 --- a/styles/less/ux/autocomplete/autocomplete.less +++ b/styles/less/ux/autocomplete/autocomplete.less @@ -21,6 +21,8 @@ flex-direction: column; gap: 2px; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + .group { font-weight: bold; font-size: var(--font-size-14); diff --git a/styles/less/ux/autocomplete/index.less b/styles/less/ux/autocomplete/index.less deleted file mode 100644 index 91007146..00000000 --- a/styles/less/ux/autocomplete/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './autocomplete.less'; \ No newline at end of file diff --git a/styles/less/ux/index.less b/styles/less/ux/index.less index b6c9a2e7..a73f2d1c 100644 --- a/styles/less/ux/index.less +++ b/styles/less/ux/index.less @@ -1,2 +1,10 @@ -@import './autocomplete/index.less'; -@import './tooltip/index.less'; \ No newline at end of file +@import './tooltip/sheet.less'; +@import './tooltip/tooltip.less'; +@import './tooltip/armorManagement.less'; +@import './tooltip/battlepoints.less'; +@import './tooltip/bordered-tooltip.less'; +@import './tooltip/domain-cards.less'; + +@import './autocomplete/autocomplete.less'; + +@import './tooltip/resource-management.less'; diff --git a/styles/less/ux/tooltip/armorManagement.less b/styles/less/ux/tooltip/armorManagement.less index 497df4f5..e1ac6bb9 100644 --- a/styles/less/ux/tooltip/armorManagement.less +++ b/styles/less/ux/tooltip/armorManagement.less @@ -83,7 +83,7 @@ appearance: none; width: 100%; height: 30px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; @@ -118,13 +118,13 @@ flex-wrap: wrap; gap: 4px; padding: 5px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; z-index: 1; background: @dark-blue; align-items: center; justify-content: center; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); min-height: 30px; width: 100%; diff --git a/styles/less/ux/tooltip/index.less b/styles/less/ux/tooltip/index.less deleted file mode 100644 index eeec9354..00000000 --- a/styles/less/ux/tooltip/index.less +++ /dev/null @@ -1,7 +0,0 @@ -@import './sheet.less'; -@import './armorManagement.less'; -@import './battlepoints.less'; -@import './bordered-tooltip.less'; -@import './domain-cards.less'; -@import './resource-management.less'; -@import './tooltip.less'; \ No newline at end of file diff --git a/styles/less/ux/tooltip/resource-management.less b/styles/less/ux/tooltip/resource-management.less index 6e7e7851..ff1f4dd2 100644 --- a/styles/less/ux/tooltip/resource-management.less +++ b/styles/less/ux/tooltip/resource-management.less @@ -9,9 +9,9 @@ display: flex; gap: 10px; background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); padding: 5px 10px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; align-items: center; width: fit-content; @@ -22,7 +22,7 @@ font-size: var(--font-size-14); font-weight: bold; text-transform: uppercase; - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); margin: 0; } diff --git a/styles/less/ux/tooltip/sheet.less b/styles/less/ux/tooltip/sheet.less index cc4166da..59e4e638 100644 --- a/styles/less/ux/tooltip/sheet.less +++ b/styles/less/ux/tooltip/sheet.less @@ -3,7 +3,7 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip), #tooltip.bordered-tooltip { .tooltip-title { font-size: var(--font-size-20); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); font-weight: 700; } @@ -48,6 +48,9 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip), overflow-y: auto; position: relative; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + .tooltip-tag { display: flex; gap: 10px; diff --git a/styles/less/ux/tooltip/tooltip.less b/styles/less/ux/tooltip/tooltip.less index f02499e2..1566059f 100644 --- a/styles/less/ux/tooltip/tooltip.less +++ b/styles/less/ux/tooltip/tooltip.less @@ -132,7 +132,7 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip.card-style) { border-radius: 3px; padding: 3px; background: light-dark(@dark-blue-60, @rustic-brown-80); - color: @color-text-emphatic; + color: light-dark(@dark-blue, @golden); font-size: 12px; margin-bottom: 10px; } @@ -238,7 +238,7 @@ aside[role='tooltip'].locked-tooltip:has(div.daggerheart.dh-style.tooltip.card-s .tooltip-chip { font-size: var(--font-size-18); padding: 2px 4px; - border: 1px solid @color-border; + border: 1px solid light-dark(@dark-blue, @golden); border-radius: 6px; color: light-dark(@dark, @beige); background-image: url(../assets/parchments/dh-parchment-dark.png); diff --git a/system.json b/system.json index ed14a17b..deb30b53 100644 --- a/system.json +++ b/system.json @@ -2,24 +2,19 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.3.2", + "version": "2.2.2", "compatibility": { - "minimum": "14.361", - "verified": "14.363", + "minimum": "14.359", + "verified": "14.360", "maximum": "14" }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.2/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.2/system.zip", "authors": [ { "name": "WBHarry" }, - { - "name": "Supe", - "url": "https://github.com/CarlosFdez", - "discord": "supe" - }, { "name": "cptn-cosmo", "url": "https://github.com/cptn-cosmo", @@ -244,14 +239,11 @@ "adversary": { "htmlFields": ["notes", "description"] }, - "npc": { - "htmlFields": ["notes"] - }, "environment": { "htmlFields": ["notes", "description"] }, "party": { - "htmlFields": ["notes", "description"] + "htmlFields": ["notes"] } }, "Item": { @@ -306,11 +298,5 @@ }, "background": "systems/daggerheart/assets/logos/FoundrybornBackgroundLogo.png", "primaryTokenAttribute": "resources.hitPoints", - "secondaryTokenAttribute": "resources.stress", - "flags": { - "hotReload": { - "extensions": ["css", "hbs", "json"], - "paths": ["styles/daggerheart.css", "templates", "lang"] - } - } + "secondaryTokenAttribute": "resources.stress" } diff --git a/templates/dialogs/compendiumBrowserSettingsDialog/packs.hbs b/templates/dialogs/compendiumBrowserSettingsDialog/packs.hbs index ca2739b2..dcda8108 100644 --- a/templates/dialogs/compendiumBrowserSettingsDialog/packs.hbs +++ b/templates/dialogs/compendiumBrowserSettingsDialog/packs.hbs @@ -22,7 +22,7 @@
{{#each source.packs as |pack|}}
- +
{{/each}} diff --git a/templates/dialogs/rerollDialog/damage/main.hbs b/templates/dialogs/rerollDialog/damage/main.hbs new file mode 100644 index 00000000..5b994bf6 --- /dev/null +++ b/templates/dialogs/rerollDialog/damage/main.hbs @@ -0,0 +1,35 @@ +
+ {{#each damage}} +

{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' @key '.name')}}

+ {{#each this}} +
+ {{#each this}} +
+ + + + {{this.selectedResults}}/{{this.maxSelected}} Selected + + +
+ {{#each this.results}} +
+ {{this.result}} + {{#if this.active}} + + + + + {{/if}} +
+ {{/each}} +
+
+ {{/each}} +
+ {{/each}} + {{/each}} +
\ No newline at end of file diff --git a/templates/dialogs/rerollDialog/footer.hbs b/templates/dialogs/rerollDialog/footer.hbs new file mode 100644 index 00000000..5d4ae2b2 --- /dev/null +++ b/templates/dialogs/rerollDialog/footer.hbs @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/templates/dialogs/rerollDialog/main.hbs b/templates/dialogs/rerollDialog/main.hbs new file mode 100644 index 00000000..6f10ce33 --- /dev/null +++ b/templates/dialogs/rerollDialog/main.hbs @@ -0,0 +1,35 @@ +
+ {{#each damage}} +

{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' @key '.name')}}

+ {{#each this}} +
+ {{#each this}} +
+ + + + {{this.selectedResults}}/{{this.results.length}} Selected + + +
+ {{#each this.results}} +
+ {{this.result}} + {{#if this.active}} + + + + + {{/if}} +
+ {{/each}} +
+
+ {{/each}} +
+ {{/each}} + {{/each}} +
\ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/initialization.hbs b/templates/dialogs/tagTeamDialog/initialization.hbs index 0b92e68e..7ccdf566 100644 --- a/templates/dialogs/tagTeamDialog/initialization.hbs +++ b/templates/dialogs/tagTeamDialog/initialization.hbs @@ -1,18 +1,13 @@
-

{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.selectParticipants"}}

+

{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.selectParticipants"}}

@@ -35,13 +30,10 @@
+
{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.openDialogForAll"}}
-
\ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs index 2a366269..49fc8f4f 100644 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs @@ -16,7 +16,7 @@ {{/unless}} {{/each}} {{#if part.modifierTotal}} - {{#if part.dice.length}}{{#if (gte part.modifierTotal 0)}}+{{else}}-{{/if}}{{/if}} + {{#if (gte part.modifierTotal 0)}}+{{else}}-{{/if}} {{positive part.modifierTotal}} {{/if}} diff --git a/templates/dialogs/tagTeamDialog/result.hbs b/templates/dialogs/tagTeamDialog/result.hbs index 151c8c87..79b5138e 100644 --- a/templates/dialogs/tagTeamDialog/result.hbs +++ b/templates/dialogs/tagTeamDialog/result.hbs @@ -32,10 +32,7 @@
- +
\ No newline at end of file diff --git a/templates/hud/tokenHUD.hbs b/templates/hud/tokenHUD.hbs index cf43e15d..ab0872bf 100644 --- a/templates/hud/tokenHUD.hbs +++ b/templates/hud/tokenHUD.hbs @@ -4,6 +4,10 @@ + + {{#if canChangeLevel}} - {{#if hasCompanion}} {{/if}} - {{#if isGM}} - - {{/if}} - {{#if canConfigure}} - {{/if}} @@ -56,9 +49,8 @@
{{#if isGM}} - {{/if}} @@ -127,15 +119,13 @@ {{/each}}
- {{#if canToggleCombat}} - {{/if}} diff --git a/templates/levelup/tabs/selections.hbs b/templates/levelup/tabs/selections.hbs index deb7f6c0..7ebe32bb 100644 --- a/templates/levelup/tabs/selections.hbs +++ b/templates/levelup/tabs/selections.hbs @@ -43,6 +43,45 @@ {{/if}} + {{#if (gt this.domainCards.length 0)}} +
+
+ +

{{localize "DAGGERHEART.APPLICATIONS.Levelup.summary.domainCards"}}

+ +
+
+ +
+ +
+ {{#each this.domainCards}} + {{#> "systems/daggerheart/templates/components/card-preview.hbs" this }} + {{#each this.emptySubtexts}} +
{{this}}
+ {{/each}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} + {{/each}} +
+
+ {{/if}} + + {{#if (gt this.subclassCards.length 0)}} +
+
+ +

{{localize "DAGGERHEART.APPLICATIONS.Levelup.summary.subclass"}}

+ +
+ +
+ {{#each this.subclassCards}} + {{> "systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs" img=this.img header=this.featureLabel name=this.name path=this.path selected=this.selected uuid=this.uuid isMulticlass=this.isMulticlass featureState=this.featureState disabled=this.disabled }} + {{/each}} +
+
+ {{/if}} + {{#if this.multiclass}}
@@ -89,45 +128,6 @@
{{/if}} - {{#if (gt this.domainCards.length 0)}} -
-
- -

{{localize "DAGGERHEART.APPLICATIONS.Levelup.summary.domainCards"}}

- -
-
- -
- -
- {{#each this.domainCards}} - {{#> "systems/daggerheart/templates/components/card-preview.hbs" this }} - {{#each this.emptySubtexts}} -
{{this}}
- {{/each}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} - {{/each}} -
-
- {{/if}} - - {{#if (gt this.subclassCards.length 0)}} -
-
- -

{{localize "DAGGERHEART.APPLICATIONS.Levelup.summary.subclass"}}

- -
- -
- {{#each this.subclassCards}} - {{> "systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs" img=this.img header=this.featureLabel name=this.name path=this.path selected=this.selected uuid=this.uuid isMulticlass=this.isMulticlass featureState=this.featureState disabled=this.disabled }} - {{/each}} -
-
- {{/if}} - {{#if this.vicious}}

{{localize "DAGGERHEART.APPLICATIONS.Levelup.summary.vicious"}}

diff --git a/templates/settings/metagaming-settings/general.hbs b/templates/settings/metagaming-settings/general.hbs index 9f5bd514..7f36715e 100644 --- a/templates/settings/metagaming-settings/general.hbs +++ b/templates/settings/metagaming-settings/general.hbs @@ -1,4 +1,3 @@ -
+
{{formGroup settingFields.schema.fields.hideObserverPermissionInChat value=settingFields._source.hideObserverPermissionInChat localize=true}} - {{formGroup settingFields.schema.fields.hidePartyStats value=settingFields._source.hidePartyStats localize=true}} -
+
\ No newline at end of file diff --git a/templates/sheets-settings/adversary-settings/details.hbs b/templates/sheets-settings/adversary-settings/details.hbs index 3160fbb9..dc2fd386 100644 --- a/templates/sheets-settings/adversary-settings/details.hbs +++ b/templates/sheets-settings/adversary-settings/details.hbs @@ -18,12 +18,6 @@ {{formField systemFields.motivesAndTactics value=document._source.system.motivesAndTactics label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.motivesAndTactics.label")}} -
- {{localize "DAGGERHEART.GENERAL.DamageThresholds.title"}} - {{formGroup systemFields.damageThresholds.fields.major value=document._source.system.damageThresholds.major label=(localize "DAGGERHEART.GENERAL.DamageThresholds.majorThreshold")}} - {{formGroup systemFields.damageThresholds.fields.severe value=document._source.system.damageThresholds.severe label=(localize "DAGGERHEART.GENERAL.DamageThresholds.severeThreshold")}} -
-
{{localize "DAGGERHEART.GENERAL.Resource.plural"}} @@ -32,4 +26,10 @@ {{/each}}
+ +
+ {{localize "DAGGERHEART.GENERAL.DamageThresholds.title"}} + {{formGroup systemFields.damageThresholds.fields.major value=document._source.system.damageThresholds.major label=(localize "DAGGERHEART.GENERAL.DamageThresholds.majorThreshold")}} + {{formGroup systemFields.damageThresholds.fields.severe value=document._source.system.damageThresholds.severe label=(localize "DAGGERHEART.GENERAL.DamageThresholds.severeThreshold")}} +
diff --git a/templates/sheets-settings/npc-settings/details.hbs b/templates/sheets-settings/npc-settings/details.hbs deleted file mode 100644 index 0e18b488..00000000 --- a/templates/sheets-settings/npc-settings/details.hbs +++ /dev/null @@ -1,13 +0,0 @@ -
-
- {{localize "DAGGERHEART.GENERAL.description"}} - {{formInput systemFields.description value=document._source.system.description}} -
- - {{formGroup systemFields.motives value=document._source.system.motives}} - {{formGroup systemFields.difficulty value=document._source.system.difficulty localize=true}} -
diff --git a/templates/sheets-settings/npc-settings/features.hbs b/templates/sheets-settings/npc-settings/features.hbs deleted file mode 100644 index 2f2f5f47..00000000 --- a/templates/sheets-settings/npc-settings/features.hbs +++ /dev/null @@ -1,29 +0,0 @@ -
- -
- {{localize tabs.features.label}} -
    - {{#each @root.features as |feature|}} -
  • - -
    - {{feature.name}} -
    -
    - - -
    -
  • - {{/each}} -
-
- {{localize "DAGGERHEART.GENERAL.dropFeaturesHere"}} -
-
-
\ No newline at end of file diff --git a/templates/sheets-settings/npc-settings/header.hbs b/templates/sheets-settings/npc-settings/header.hbs deleted file mode 100644 index c9cb60fe..00000000 --- a/templates/sheets-settings/npc-settings/header.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
-

{{document.name}}

-
\ No newline at end of file diff --git a/templates/sheets-settings/token-config/appearance.hbs b/templates/sheets-settings/token-config/appearance.hbs index 76ba255d..abdb49c2 100644 --- a/templates/sheets-settings/token-config/appearance.hbs +++ b/templates/sheets-settings/token-config/appearance.hbs @@ -1,43 +1,20 @@
-
- {{formGroup fields.texture.fields.src value=source.texture.src rootId=rootId}} - {{#if randomImgEnabled}} - {{formGroup fields.randomImg value=source.randomImg classes="slim" rootId=rootId}} - {{else if hasAlternates}} -
- - -
- {{/if}} - - {{#if imagePreview}} -
- {{#if imagePreview.isVideo}} - - {{else}} - {{localize - {{/if}} -
- - {{imagePreview.current}} / {{imagePreview.total}} - -
-
- {{/if}} + {{formGroup fields.texture.fields.src value=source.texture.src rootId=rootId}} + {{#if randomImgEnabled}} + {{formGroup fields.randomImg value=source.randomImg classes="slim" rootId=rootId}} + {{else if hasAlternates}} +
+ +
- + {{/if}}
- {{localize "DAGGERHEART.APPLICATIONS.TokenConfig.tokenSize"}} + {{localize "Token Size"}} {{#if usesActorSize}} -
- +
+ +
+
+ {{/if}} + + {{#if isItemEffect}} + {{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=(localize "DAGGERHEART.EFFECTS.Attachments.transferHint")}} {{/if}} {{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}} diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index 3b495e74..d320b0d8 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -6,7 +6,6 @@ type='feature' collection=@root.features hideContextMenu=true - hideModifyControls=true canCreate=@root.editable showActions=@root.editable }} diff --git a/templates/sheets/actors/adversary/header.hbs b/templates/sheets/actors/adversary/header.hbs index 5adc235a..5bdfa421 100644 --- a/templates/sheets/actors/adversary/header.hbs +++ b/templates/sheets/actors/adversary/header.hbs @@ -1,14 +1,7 @@
-

{{source.name}}

+

{{source.name}}

@@ -44,9 +37,10 @@
- {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
+ {{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} - {{/ 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
\ No newline at end of file diff --git a/templates/sheets/actors/character/biography.hbs b/templates/sheets/actors/character/biography.hbs index 7da920e0..6f2fe8c1 100644 --- a/templates/sheets/actors/character/biography.hbs +++ b/templates/sheets/actors/character/biography.hbs @@ -4,7 +4,9 @@ data-group='{{tabs.biography.group}}' >
-
+
+ {{localize 'DAGGERHEART.ACTORS.Character.story.characteristics'}} +
{{localize 'DAGGERHEART.ACTORS.Character.pronouns'}} {{formInput systemFields.biography.fields.characteristics.fields.pronouns value=source.system.biography.characteristics.pronouns enriched=source.system.biography.characteristics.pronouns localize=true toggled=true}} @@ -19,14 +21,14 @@ {{localize 'DAGGERHEART.ACTORS.Character.faith'}} {{formInput systemFields.biography.fields.characteristics.fields.faith value=source.system.biography.characteristics.faith enriched=source.system.biography.characteristics.faith localize=true toggled=true}}
-
+
-
+
{{localize 'DAGGERHEART.ACTORS.Character.story.backgroundTitle'}} {{formInput background.field value=background.value enriched=background.enriched toggled=true}}
-
+
{{localize 'DAGGERHEART.ACTORS.Character.story.connectionsTitle'}} {{formInput connections.field value=connections.value enriched=connections.enriched toggled=true}}
diff --git a/templates/sheets/actors/character/features.hbs b/templates/sheets/actors/character/features.hbs index b2760900..70544683 100644 --- a/templates/sheets/actors/character/features.hbs +++ b/templates/sheets/actors/character/features.hbs @@ -2,25 +2,26 @@ data-group='{{tabs.features.group}}'>
{{#each document.system.sheetLists as |category|}} - {{#if (eq category.type 'feature' )}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=@root.editable - showActions=@root.editable - }} - {{else if category.values}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=false - showActions=@root.editable - }} - {{/if}} + {{#if (eq category.type 'feature' )}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=@root.editable + showActions=@root.editable + }} + {{else if category.values}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=false + showActions=@root.editable + }} + + {{/if}} {{/each}}
\ No newline at end of file diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 459911af..a75b2c2f 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -1,14 +1,7 @@
-

{{source.name}}

+

{{source.name}}

{{#if @root.editable}} @@ -64,7 +57,7 @@ {{/if}}

- {{#if (or document.system.multiclass.value document.system.multiclass.subclass)}} + {{#if document.system.multiclass.value}}
{{#if document.system.multiclass.value}} {{document.system.multiclass.value.name}} @@ -132,27 +125,20 @@
{{#each this.attributes as |attribute key|}} -
-
-
- {{attribute.label}} +
+
+ {{localize (concat 'DAGGERHEART.CONFIG.Traits.' key '.short')}} + {{#if attribute.tierMarked}} + + {{else}} + + {{/if}}
-
- - - - -
- {{#if (gt attribute.value 0)}} - +{{attribute.value}} - {{else}} - {{attribute.value}} - {{/if}} -
- {{#if isSpellcasting}} -
- -
+
+ {{#if (gt attribute.value 0)}} + +{{attribute.value}} + {{else}} + {{attribute.value}} {{/if}}
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 313c81f9..a0f93121 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -1,6 +1,17 @@
{{else}}
- + / {{document.system.armorScore.max}}
diff --git a/templates/sheets/actors/companion/header.hbs b/templates/sheets/actors/companion/header.hbs index 9c324709..8b63e396 100644 --- a/templates/sheets/actors/companion/header.hbs +++ b/templates/sheets/actors/companion/header.hbs @@ -6,8 +6,6 @@ name='name' value='{{document.name}}' placeholder='{{localize "DAGGERHEART.GENERAL.actorName"}}' - autocomplete="off" - spellcheck="false" /> {{#if useResourcePips}} @@ -50,10 +48,9 @@
- {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} - - {{/ 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} + {{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
\ No newline at end of file diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 35fcb038..3fd512da 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -9,7 +9,6 @@ type='feature' collection=@root.features hideContextMenu=true - hideModifyControls=true canCreate=@root.editable showActions=@root.editable }} diff --git a/templates/sheets/actors/environment/header.hbs b/templates/sheets/actors/environment/header.hbs index 1b4073c7..b7eab3db 100644 --- a/templates/sheets/actors/environment/header.hbs +++ b/templates/sheets/actors/environment/header.hbs @@ -1,7 +1,28 @@
-

+
+

+
+
+
+ + {{localize (concat 'DAGGERHEART.GENERAL.Tiers.' source.system.tier)}} + +
+ {{#if source.system.type}} +
+ + {{localize (concat 'DAGGERHEART.CONFIG.EnvironmentType.' source.system.type '.label')}} + +
+ {{/if}} +
+ {{#if (and showAttribution document.system.attributionLabel)}} + + {{/if}} +
+
{{#if source.system.difficulty}} @@ -14,25 +35,6 @@

{{localize "DAGGERHEART.GENERAL.difficulty"}}

-
-
-
- - {{localize (concat 'DAGGERHEART.GENERAL.Tiers.' source.system.tier)}} - -
- {{#if source.system.type}} -
- - {{localize (concat 'DAGGERHEART.CONFIG.EnvironmentType.' source.system.type '.label')}} - -
- {{/if}} -
- {{#if (and showAttribution document.system.attributionLabel)}} - - {{/if}} -
@@ -44,10 +46,9 @@
- {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} - - {{/ 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} + {{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
\ No newline at end of file diff --git a/templates/sheets/actors/npc/features.hbs b/templates/sheets/actors/npc/features.hbs deleted file mode 100644 index 3b495e74..00000000 --- a/templates/sheets/actors/npc/features.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
-
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} -
-
\ No newline at end of file diff --git a/templates/sheets/actors/npc/header.hbs b/templates/sheets/actors/npc/header.hbs deleted file mode 100644 index fce300da..00000000 --- a/templates/sheets/actors/npc/header.hbs +++ /dev/null @@ -1,42 +0,0 @@ -
-
- {{source.name}} -
-
- - -
-

{{source.name}}

-
- - {{#if source.system.difficulty}} -
-
- {{localize "DAGGERHEART.GENERAL.difficulty"}} - {{source.system.difficulty}} -
-
- {{/if}} - - - -
- - {{{description}}} - - {{#if source.system.motives}} -
- {{localize 'DAGGERHEART.ACTORS.NPC.FIELDS.motives.label'}}: - {{source.system.motives}} -
- {{/if}} -
-
-
\ No newline at end of file diff --git a/templates/sheets/actors/npc/navigation.hbs b/templates/sheets/actors/npc/navigation.hbs deleted file mode 100644 index ae684f0d..00000000 --- a/templates/sheets/actors/npc/navigation.hbs +++ /dev/null @@ -1,7 +0,0 @@ -
- {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} - - {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} -
\ No newline at end of file diff --git a/templates/sheets/actors/npc/notes.hbs b/templates/sheets/actors/npc/notes.hbs deleted file mode 100644 index bc9ac3cf..00000000 --- a/templates/sheets/actors/npc/notes.hbs +++ /dev/null @@ -1,11 +0,0 @@ -
- {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} - - {{#if (and showAttribution document.system.attribution.artist)}} - - {{/if}} -
\ No newline at end of file diff --git a/templates/sheets/actors/party/header.hbs b/templates/sheets/actors/party/header.hbs index efa6e5b8..3fdb137d 100644 --- a/templates/sheets/actors/party/header.hbs +++ b/templates/sheets/actors/party/header.hbs @@ -2,7 +2,7 @@
- +

\ No newline at end of file diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs index 55cacb79..aa41aeaa 100644 --- a/templates/sheets/actors/party/party-members.hbs +++ b/templates/sheets/actors/party/party-members.hbs @@ -23,166 +23,143 @@
- {{#if @root.showStats}} -
    - {{#each partyMembers as |member id|}} -
  • -
    - - {{#if member.weapons}} -
    - {{#each member.weapons as |weapon|}} - - {{/each}} -
    - {{/if}} - {{#if member.evasion includeZero=true}} -
    {{member.evasion}}
    - {{/if}} - {{#if member.difficulty includeZero=true}} -
    {{member.difficulty}}
    - {{/if}} - {{#unless (or (eq member.type 'companion') (eq member.type 'npc'))}} -
    -

    {{localize "DAGGERHEART.ACTORS.Party.Thresholds.minor"}}

    -

    {{member.damageThresholds.major}}

    -

    {{localize "DAGGERHEART.ACTORS.Party.Thresholds.major"}}

    -

    {{member.damageThresholds.severe}}

    -

    {{localize "DAGGERHEART.ACTORS.Party.Thresholds.severe"}}

    +
      + {{#each partyMembers as |member id|}} +
    • +
      + + {{#if member.weapons}} +
      + {{#each member.weapons as |weapon|}} + + {{/each}} +
      + {{/if}} + {{#if member.evasion includeZero=true}} +
      {{member.evasion}}
      + {{/if}} + {{#if member.difficulty includeZero=true}} +
      {{member.difficulty}}
      + {{/if}} + {{#unless (eq member.type 'companion')}} +
      +

      {{localize "DAGGERHEART.ACTORS.Party.Thresholds.minor"}}

      +

      {{member.damageThresholds.major}}

      +

      {{localize "DAGGERHEART.ACTORS.Party.Thresholds.major"}}

      +

      {{member.damageThresholds.severe}}

      +

      {{localize "DAGGERHEART.ACTORS.Party.Thresholds.severe"}}

      +
      + {{/unless}} +
      +
      +

      + {{member.name}} + +

      +
      + {{#unless (or (eq member.type 'companion') (eq member.type 'adversary')) }} +
      +

      {{localize "DAGGERHEART.GENERAL.hope"}}

      + {{#times member.resources.hope.max}} + + {{#if (gte member.resources.hope.value (add this 1))}} + + {{else}} + + {{/if}} + + {{/times}}
      {{/unless}}
      -
      -

      - {{member.name}} - -

      -
      - {{#unless (or (eq member.type 'companion') (eq member.type 'adversary') (eq member.type 'npc')) }} -
      -

      {{localize "DAGGERHEART.GENERAL.hope"}}

      - {{#times member.resources.hope.max}} - - {{#if (gte member.resources.hope.value (add this 1))}} - - {{else}} - - {{/if}} + {{#if member.subtitle}} + {{member.subtitle}} + {{/if}} +
      +
      +
      + {{#unless (eq member.type 'companion') }} +
      +
      + + + + + {{member.resources.hitPoints.value}} + / + {{member.resources.hitPoints.max}} + +
      +
      + {{#times member.resources.hitPoints.max}} + {{/times}}
      - {{/unless}} -
      - {{#if member.subtitle}} - {{member.subtitle}} - {{/if}} -
      -
      -
      - {{#unless (or (eq member.type 'companion') (eq member.type 'npc')) }} -
      -
      - - - - - {{member.resources.hitPoints.value}} - / - {{member.resources.hitPoints.max}} - -
      -
      - {{#times member.resources.hitPoints.max}} - - - {{/times}} -
      -
      - {{/unless}} +
    + {{/unless}} - {{#unless (eq member.type 'npc')}} -
    -
    - - - - - {{member.resources.stress.value}} - / - {{member.resources.stress.max}} - -
    -
    - {{#times member.resources.stress.max}} - - - {{/times}} -
    -
    - {{/unless}} - - {{#if member.armorScore.max}} -
    -
    - - - - - {{member.armorScore.value}} - / - {{member.armorScore.max}} - -
    - -
    - {{/if}} - - {{#if member.traits}} -
    - {{#each member.traits as |trait|}} - - {{trait.label}} - {{trait.value}} +
    +
    + + + + + {{member.resources.stress.value}} + / + {{member.resources.stress.max}} + +
    +
    + {{#times member.resources.stress.max}} + - {{/each}} + {{/times}} +
    +
    + + {{#if member.armorScore.max}} +
    +
    + + + + + {{member.armorScore.value}} + / + {{member.armorScore.max}} + +
    +
    {{/if}} -
  • - {{/each}} -
- {{else}} -
    - {{#each partyMembers as |member id|}} -
  • -
    - -
    -
    -

    - {{member.name}} - -

    - {{#if member.subtitle}} - {{member.subtitle}} - {{/if}} -
    -
  • - {{/each}} -
- {{/if}} + {{#if member.traits}} +
+ {{#each member.traits as |trait|}} + + {{trait.label}} + {{trait.value}} + + {{/each}} +
+ {{/if}} + + + {{/each}} + {{#unless document.system.partyMembers.length}}
{{localize "DAGGERHEART.GENERAL.dropActorsHere"}} diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs index db2fb6b7..3f58b80b 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -19,8 +19,6 @@ Parameters: - showLabels {boolean} : If true, show label-tags else show simple tags. - hideTooltip {boolean} : If true, disables the tooltip on the item image. - hideControls {boolean} : If true, hides the controls inside inventory-item partials. -- hideContextMenu {boolean}: If true, hides the context menu dropdown button -- hideModifyControls {boolean}: If true, hides the edit and delete options - hideDescription {boolean} : If true, hides the item's description. - hideResources {boolean} : If true, hides the item's resources. - showActions {boolean} : If true show feature's actions. @@ -61,7 +59,6 @@ Parameters: actorType=(ifThen ../actorType ../actorType @root.document.type) hideControls=../hideControls hideContextMenu=../hideContextMenu - hideModifyControls=../hideModifyControls isActor=../isActor categoryAdversary=../categoryAdversary hideTooltip=../hideTooltip diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index 523e9304..d76f2897 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -12,8 +12,6 @@ Parameters: - hideTags {boolean} : If true, hide simple-tags else show simple-tags. - hideTooltip {boolean} : If true, disables the tooltip on the item image. - hideControls {boolean} : If true, hides the controls inside inventory-item partials. -- hideContextMenu {boolean}: If true, hides the context menu dropdown button -- hideModifyControls {boolean}: If true, hides the edit and delete options (todo: swap to show, only party cares to show this) - hideDescription {boolean} : If true, hides the item's description. - hideResources {boolean} : If true, hides the item's resources. - showActions {boolean} : If true show feature's actions. @@ -114,12 +112,12 @@ Parameters: - {{else if (and @root.editable (not hideModifyControls))}} + {{else if @root.editable}} {{#if (not isActor)}} - + {{else if (eq type 'adversary')}} diff --git a/templates/sheets/global/partials/inventory-item-compact.hbs b/templates/sheets/global/partials/inventory-item-compact.hbs index 78eaf087..daba6721 100644 --- a/templates/sheets/global/partials/inventory-item-compact.hbs +++ b/templates/sheets/global/partials/inventory-item-compact.hbs @@ -5,7 +5,7 @@ (hasProperty item "toChat" ) "toChat" "editDoc" ) }}' {{#unless hideTooltip}} {{#if (eq type 'attack' )}} data-tooltip="#attack#{{item.actor.uuid}}" {{else}} data-tooltip="#item#{{item.uuid}}" {{/if}} {{/unless}}> - {{#if item.usable}} + {{#if (or item.system.actionsList.size item.system.actionsList.length item.actionType)}} {{#if @root.isNPC}} d20 {{else}} diff --git a/templates/sheets/global/tabs/tab-navigation.hbs b/templates/sheets/global/tabs/tab-navigation.hbs index 8af1f140..f9a31d3e 100755 --- a/templates/sheets/global/tabs/tab-navigation.hbs +++ b/templates/sheets/global/tabs/tab-navigation.hbs @@ -4,7 +4,7 @@
{{formGroup systemFields.tokenSize.fields.scale value=source.system.tokenSize.scale localize=true }} diff --git a/templates/sheets/items/class/features.hbs b/templates/sheets/items/class/features.hbs index 279ff52c..9d037b65 100644 --- a/templates/sheets/items/class/features.hbs +++ b/templates/sheets/items/class/features.hbs @@ -27,7 +27,10 @@
{{localize "TYPES.Item.subclass"}}
- {{#each subclasses as |subclass index|}} + {{#unless source.system.subclasses}} +
{{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.subclasses")}}
+ {{/unless}} + {{#each source.system.subclasses as |subclass index|}}
  • @@ -41,7 +44,16 @@ data-item-uuid={{subclass.uuid}} data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openItemWorld"}}' > - + + + +
  • diff --git a/templates/sheets/items/class/header.hbs b/templates/sheets/items/class/header.hbs index 019825f8..6b60ada6 100644 --- a/templates/sheets/items/class/header.hbs +++ b/templates/sheets/items/class/header.hbs @@ -3,6 +3,7 @@

    +

    {{localize 'TYPES.Item.class'}}

    {{localize "DAGGERHEART.GENERAL.Domain.plural"}} diff --git a/templates/sheets/items/subclass/description.hbs b/templates/sheets/items/subclass/description.hbs index 0267eb9b..4591bd1a 100644 --- a/templates/sheets/items/subclass/description.hbs +++ b/templates/sheets/items/subclass/description.hbs @@ -1,10 +1,8 @@
    - {{#if spellcastTrait}} -
    -

    {{localize "DAGGERHEART.ITEMS.Subclass.spellcastTrait"}}

    - {{spellcastTrait}} -
    - {{/if}} +
    +

    {{localize "DAGGERHEART.ITEMS.Subclass.spellcastTrait"}}

    + {{spellcastTrait}} +

    {{localize "DAGGERHEART.ITEMS.Subclass.foundationFeatures"}}

    {{#each foundationFeatures as | feature |}} diff --git a/templates/sheets/items/subclass/features.hbs b/templates/sheets/items/subclass/features.hbs index c54e702e..1a75974e 100644 --- a/templates/sheets/items/subclass/features.hbs +++ b/templates/sheets/items/subclass/features.hbs @@ -3,35 +3,6 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > -
    - {{localize "TYPES.Item.class"}} - {{#if class}} -
    -
  • - {{#if class.missing}} - - {{class.name}} - {{else}} - - {{class.name}} -
    - - - -
    - {{/if}} -
  • -
    - {{else}} -
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "TYPES.Item.class")}}
    - {{/if}} -
    -
    {{localize "DAGGERHEART.GENERAL.Tabs.foundation"}} diff --git a/templates/ui/chat/parts/roll-part.hbs b/templates/ui/chat/parts/roll-part.hbs index cfee735f..78f4dcd9 100644 --- a/templates/ui/chat/parts/roll-part.hbs +++ b/templates/ui/chat/parts/roll-part.hbs @@ -6,15 +6,19 @@ {{#if roll.isCritical}} {{localize "DAGGERHEART.GENERAL.criticalShort"}} {{else}} - {{#if (and roll.dHope (not (eq roll.options.roll.type "reaction")))}} + {{#if (and roll.dHope (not (eq roll.type "reaction")))}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}} {{/if}} {{/if}}
    - {{#if roll.options.roll.difficulty}} - - {{localize "DAGGERHEART.GENERAL.difficulty"}} {{roll.options.roll.difficulty}} + {{#if roll.difficulty}} + + {{!-- {{#if canViewSecret}} --}} + difficulty {{roll.difficulty}} + {{!-- {{else}} + {{localize (ifThen roll.success "DAGGERHEART.GENERAL.success" "DAGGERHEART.GENERAL.failure")}} + {{/if}} --}} {{/if}}
    diff --git a/templates/ui/combatTracker/combatTracker.hbs b/templates/ui/combatTracker/combatTracker.hbs index 4ec37134..91f7786b 100644 --- a/templates/ui/combatTracker/combatTracker.hbs +++ b/templates/ui/combatTracker/combatTracker.hbs @@ -5,10 +5,7 @@ {{#if (gt this.characters.length 0)}} {{> 'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.GENERAL.Character.plural") turns=this.characters}} {{/if}} - {{#if (gt this.friendlies.length 0)}} - {{> 'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.APPLICATIONS.CombatTracker.adversaryCategories.friendly") turns=this.friendlies}} - {{/if}} {{#if (gt this.adversaries.length 0)}} - {{> 'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.APPLICATIONS.CombatTracker.adversaryCategories.adversaries") turns=this.adversaries}} + {{> 'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs' this title=(localize "DAGGERHEART.GENERAL.Adversary.plural") turns=this.adversaries}} {{/if}}

    \ No newline at end of file diff --git a/templates/ui/combatTracker/combatTrackerHeader.hbs b/templates/ui/combatTracker/combatTrackerHeader.hbs index 9f5a7561..803286ab 100644 --- a/templates/ui/combatTracker/combatTrackerHeader.hbs +++ b/templates/ui/combatTracker/combatTrackerHeader.hbs @@ -50,11 +50,6 @@ {{/if}} - {{!-- Encounter Name --}} - {{#if combat.name}} -

    {{ combat.name }}

    - {{/if}} -
    {{!-- Combat Status --}} diff --git a/templates/ui/combatTracker/combatTrackerSection.hbs b/templates/ui/combatTracker/combatTrackerSection.hbs index ac759dfe..cf3ca422 100644 --- a/templates/ui/combatTracker/combatTrackerSection.hbs +++ b/templates/ui/combatTracker/combatTrackerSection.hbs @@ -13,20 +13,20 @@
    {{#if @root.user.isGM}} + data-action="toggleHidden" data-tooltip aria-label="{{ localize "COMBAT.ToggleVis" }}"> + aria-label="{{ localize "COMBAT.ToggleDead" }}"> {{/if}} {{#if canPing}} + aria-label="{{ localize "COMBAT.PingCombatant" }}"> {{/if}} {{#unless @root.user.isGM}} + aria-label="{{ localize "COMBAT.PanToCombatant" }}"> {{/unless}}
    diff --git a/templates/ui/countdowns.hbs b/templates/ui/countdowns.hbs index faaffdc5..18694e49 100644 --- a/templates/ui/countdowns.hbs +++ b/templates/ui/countdowns.hbs @@ -1,30 +1,18 @@
    -
    - - {{localize "DAGGERHEART.UI.Countdowns.title"}} - - - - - - -
    {{#each countdowns as | countdown id |}} -
    +
    - {{#unless ../iconOnly}} -
    {{countdown.name}}
    - {{/unless}} + {{#unless ../iconOnly}}{{/unless}}
    - {{#if countdown.editable}}{{/if}} + {{#if countdown.editable}}{{/if}}
    {{countdown.progress.current}}/{{countdown.progress.start}}
    - {{#if countdown.editable}}{{/if}} + {{#if countdown.editable}}{{/if}}
    {{#if (not ../iconOnly)}} @@ -33,7 +21,7 @@ {{/if}} {{#unless (eq countdown.progress.looping "noLooping")}} - + {{#if (eq countdown.progress.looping "increasing")}} diff --git a/templates/ui/itemBrowser/itemContainer.hbs b/templates/ui/itemBrowser/itemContainer.hbs index a5f067e8..8dd75156 100644 --- a/templates/ui/itemBrowser/itemContainer.hbs +++ b/templates/ui/itemBrowser/itemContainer.hbs @@ -1,7 +1,7 @@ {{#each items}}
    -
    +
    {{name}} {{#each ../menu.data.columns}} @@ -9,13 +9,11 @@ {{/each}}
    - {{#unless viewSheet}} -
    - - {{{system.enrichedTags}}} - {{{system.enrichedDescription}}} - -
    - {{/unless}} +
    + + {{{system.enrichedTags}}} + {{{system.enrichedDescription}}} + +
    {{/each}} \ No newline at end of file diff --git a/templates/ui/sceneNavigation/scene-navigation.hbs b/templates/ui/sceneNavigation/scene-navigation.hbs index 1b65c289..5bdbbdad 100644 --- a/templates/ui/sceneNavigation/scene-navigation.hbs +++ b/templates/ui/sceneNavigation/scene-navigation.hbs @@ -12,20 +12,15 @@ {{/with}} {{#if scenes.levels}} - + {{#each scenes.levels}}
  • + {{#with button}} + + {{/with}}
    {{ name }} - {{#if users}} -
      - {{#each users}} -
    • {{ letter }}
    • - {{/each}} -
    - {{/if}}
  • {{/each}} @@ -45,8 +40,7 @@ {{#*inline ".scene"}}
  • -
    +
    {{ name }} {{#if users}}
      diff --git a/templates/ui/sidebar/actor-document-partial.hbs b/templates/ui/sidebar/actor-document-partial.hbs index 1bd3ff9a..2a9f47fa 100644 --- a/templates/ui/sidebar/actor-document-partial.hbs +++ b/templates/ui/sidebar/actor-document-partial.hbs @@ -6,8 +6,6 @@ {{name}} {{#if (or (eq type "adversary") (eq type "environment"))}} {{localize "DAGGERHEART.UI.Sidebar.actorDirectory.tier" tier=system.tier type=(@root.getTypeLabel this)}} - {{else if (eq type "npc")}} - {{localize "TYPES.Actor.npc"}} {{else if (eq type "character")}} {{localize "DAGGERHEART.UI.Sidebar.actorDirectory.character" level=system.levelData.level.current}} {{else if (eq type "companion")}} diff --git a/tools/analyze-damage.mjs b/tools/analyze-damage.mjs index 31c254ce..7b3fb9e5 100644 --- a/tools/analyze-damage.mjs +++ b/tools/analyze-damage.mjs @@ -82,7 +82,7 @@ function getMean(numbers) { } function getMedianAverageDeviation(numbers, { median }) { - const residuals = numbers.map(d => Math.abs(d - median)); + const residuals = allDamage.map(d => Math.abs(d - median)); return getMedian(residuals); } @@ -98,8 +98,8 @@ function parseDamage(damage) { p.value.custom.enabled ? p.value.custom.formula : [p.value.flatMultiplier ? `${p.value.flatMultiplier}${p.value.dice}` : 0, p.value.bonus ?? 0] - .filter(p => !!p) - .join('+') + .filter(p => !!p) + .join('+') ) .join('+'); return getExpectedDamage(formula); diff --git a/tools/create-symlink.mjs b/tools/create-symlink.mjs index 4e14d5df..fd828c73 100644 --- a/tools/create-symlink.mjs +++ b/tools/create-symlink.mjs @@ -2,8 +2,6 @@ import fs from 'fs'; import path from 'path'; import readline from 'readline'; -console.log('Creates a foundry symlink in the base folder for type purposes\n'); - const askQuestion = question => { const rl = readline.createInterface({ input: process.stdin, diff --git a/tools/eslint.config.mjs b/tools/eslint.config.mjs deleted file mode 100644 index 36a5174a..00000000 --- a/tools/eslint.config.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import globals from 'globals'; -import { defineConfig, globalIgnores } from 'eslint/config'; -import { stylisticRules } from '../eslint.config.mjs'; -import stylistic from '@stylistic/eslint-plugin'; - -export default defineConfig([ - globalIgnores(['foundry/**/*']), - { - files: ['**/*.{js,mjs,cjs}'], - plugins: { - '@stylistic': stylistic - }, - languageOptions: { globals: globals.node }, - rules: { - 'no-undef': 'error', - 'no-unused-vars': 0, - ...stylisticRules - } - } -]);