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/.env.example b/.env.example index 3d62d699..730309d3 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,2 @@ FOUNDRY_MAIN_PATH=/path/to/foundry/resources/app/main.js -FOUNDRY_DATA_PATH=/path/to/foundry/ \ No newline at end of file +FOUNDRY_DATA_PATH=/path/to/foundry/data \ No newline at end of file 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/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b472f383..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "js/ts.preferGoToSourceDefinition": true -} \ No newline at end of file diff --git a/README.md b/README.md index 177636c7..f59143fd 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,11 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart 3. **Configure your Foundry paths:** - Copy `.env.example` to `.env` and set up the foundry path and data path. The foundry path must point to main.js, and the data path must point the folder containing the Data folder itself (but not the `Data` folder itself). + ```bash + npm run setup:dev -- --foundry-path="/path/to/foundry/main.js" --data-path="/path/to/data" + ``` -4. **Setup symlinks:** - - In a shell with elevated privilages, run `npm run createSymlink`. It will add a foundry folder for types, and add a symlink to the daggerheart folder into your foundry data. - -5. **Start developing:** +4. **Start developing:** ```bash npm start ``` @@ -53,7 +51,7 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart - `npm start` - Start development with file watching and Foundry launching - `npm run build` - One-time build -- `npm run createSymlink` - Setup types and output symlinks +- `npm run setup:dev -- --foundry-path="" --data-path=""` - Configure development environment ### Notes @@ -68,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/capybara.svg b/assets/icons/documents/actors/capybara.svg index 8fbe2c06..90deb64a 100644 --- a/assets/icons/documents/actors/capybara.svg +++ b/assets/icons/documents/actors/capybara.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/dark-squad.svg b/assets/icons/documents/actors/dark-squad.svg index 39ca794a..f21b4c5b 100644 --- a/assets/icons/documents/actors/dark-squad.svg +++ b/assets/icons/documents/actors/dark-squad.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/dragon-head.svg b/assets/icons/documents/actors/dragon-head.svg index 324ffbbb..d9e008f5 100644 --- a/assets/icons/documents/actors/dragon-head.svg +++ b/assets/icons/documents/actors/dragon-head.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/drama-masks.svg b/assets/icons/documents/actors/drama-masks.svg index 98b9b68b..84307da0 100644 --- a/assets/icons/documents/actors/drama-masks.svg +++ b/assets/icons/documents/actors/drama-masks.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/forest.svg b/assets/icons/documents/actors/forest.svg index fcdb8a9d..8f7117e8 100644 --- a/assets/icons/documents/actors/forest.svg +++ b/assets/icons/documents/actors/forest.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/battered-axe.svg b/assets/icons/documents/items/battered-axe.svg index 436884bd..5d7be27d 100644 --- a/assets/icons/documents/items/battered-axe.svg +++ b/assets/icons/documents/items/battered-axe.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/card-play.svg b/assets/icons/documents/items/card-play.svg index 5c29fc43..587cb1c1 100644 --- a/assets/icons/documents/items/card-play.svg +++ b/assets/icons/documents/items/card-play.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/chest-armor.svg b/assets/icons/documents/items/chest-armor.svg index 03b82d48..2cef80a6 100644 --- a/assets/icons/documents/items/chest-armor.svg +++ b/assets/icons/documents/items/chest-armor.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/family-tree.svg b/assets/icons/documents/items/family-tree.svg index 031f23da..d95c935d 100644 --- a/assets/icons/documents/items/family-tree.svg +++ b/assets/icons/documents/items/family-tree.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/laurel-crown.svg b/assets/icons/documents/items/laurel-crown.svg index 38dbcd04..34a54d2a 100644 --- a/assets/icons/documents/items/laurel-crown.svg +++ b/assets/icons/documents/items/laurel-crown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/laurels.svg b/assets/icons/documents/items/laurels.svg index ae7398ec..2c3cdf63 100644 --- a/assets/icons/documents/items/laurels.svg +++ b/assets/icons/documents/items/laurels.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/open-treasure-chest.svg b/assets/icons/documents/items/open-treasure-chest.svg index d66399f4..172a8003 100644 --- a/assets/icons/documents/items/open-treasure-chest.svg +++ b/assets/icons/documents/items/open-treasure-chest.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/round-potion.svg b/assets/icons/documents/items/round-potion.svg index 0c235732..7f981914 100644 --- a/assets/icons/documents/items/round-potion.svg +++ b/assets/icons/documents/items/round-potion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/stars-stack.svg b/assets/icons/documents/items/stars-stack.svg index 2d446683..19c197f6 100644 --- a/assets/icons/documents/items/stars-stack.svg +++ b/assets/icons/documents/items/stars-stack.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/village.svg b/assets/icons/documents/items/village.svg index b7c776a1..c28d742b 100644 --- a/assets/icons/documents/items/village.svg +++ b/assets/icons/documents/items/village.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/wolf-head.svg b/assets/icons/documents/items/wolf-head.svg index 7e57bbad..2be500c1 100644 --- a/assets/icons/documents/items/wolf-head.svg +++ b/assets/icons/documents/items/wolf-head.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/daggerheart.d.ts b/daggerheart.d.ts index 1641faa8..ab754b17 100644 --- a/daggerheart.d.ts +++ b/daggerheart.d.ts @@ -1,12 +1,8 @@ import '@client/global.mjs'; -import '@common/global.mjs'; -import '@common/primitives/global.mjs'; import Canvas from '@client/canvas/board.mjs'; -import { ResourceUpdateMap } from './module/data/action/baseAction.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. @@ -16,67 +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; - - /** - * Data used to build rolls such as duality rolls. The definition is incomplete and likely incorrect. - * Objects will often accept a Partial and spit out a non-partial. Those that are not guaranteed should be marked optional. - */ - interface RollConfig { - // unverified, check which ones are used and optional/not optional - event: Event; - title: string; - roll: { - modifier: number; - simple: boolean; - type: string; - difficulty: number; - }; - hasDamage: boolean; - hasEffect: boolean; - hasRoll: boolean; - chatMessage: { - template: string; - mute: boolean; - }; - targets: object; - costs: object; - - // verified - source?: { - /** uuid of the actor this roll is coming from */ - actor: string; - }; - /** Roll data associated with the actor or item */ - data: object; - resourceUpdates: ResourceUpdateMap; - hooks: string[]; - dialog: { - configure: boolean; - }; - damageOptions: object; - } } diff --git a/daggerheart.mjs b/daggerheart.mjs index b92be2a2..25c41ced 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -77,20 +77,6 @@ CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenCon CONFIG.Token.objectClass = placeables.DhTokenPlaceable; CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.Token.hudClass = applications.hud.DHTokenHUD; -CONFIG.Token.barConfig = { - bar1: { - colors: { - full: Color.fromRGB([1, 0, 0]), - empty: Color.fromRGB([0, 0, 0]) - } - }, - bar2: { - colors: { - full: Color.fromString('#0032b1'), - empty: Color.fromRGB([0, 0, 0]) - } - } -}; CONFIG.ui.combat = applications.ui.DhCombatTracker; CONFIG.ui.nav = applications.ui.DhSceneNavigation; @@ -102,7 +88,6 @@ CONFIG.ui.actors = applications.sidebar.DhActorDirectory; CONFIG.ui.daggerheartMenu = applications.sidebar.DaggerheartMenu; CONFIG.ui.resources = applications.ui.DhFearTracker; CONFIG.ui.countdowns = applications.ui.DhCountdowns; -CONFIG.ui.pause = applications.ui.DhGamePause; CONFIG.ux.ContextMenu = applications.ux.DHContextMenu; CONFIG.ux.TooltipManager = documents.DhTooltipManager; CONFIG.ux.TokenManager = new TokenManager(); @@ -468,8 +453,8 @@ Hooks.on('renderDialogV2', (_dialog, html) => { const cls = html.classList.contains('item-create') ? documents.DHItem.implementation : html.classList.contains('actor-create') - ? documents.DhpActor.implementation - : null; + ? documents.DhpActor.implementation + : null; if (!cls) return; const form = html.querySelector('form'); diff --git a/eslint.config.mjs b/eslint.config.mjs index 74141cc8..ce2bb86f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,115 +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', - { - args: 'none', - destructuredArrayIgnorePattern: '^_', - varsIgnorePattern: '^_[A-Z]', - ignoreRestSiblings: true - } - ], - ...stylisticRules - } - }, - { - files: ['**/*.ts'], - extends: [js.configs.recommended, tseslint.configs.recommended], - plugins: { - '@stylistic': stylistic - }, - rules: { - ...stylisticRules + 'prettier/prettier': 'error' } } ]); diff --git a/jsconfig.json b/jsconfig.json index 42c4b404..00bab1f5 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,14 +1,14 @@ { "compilerOptions": { - "module": "es2022", - "target": "es2022", + "module": "ES6", + "target": "ES6", "paths": { "@client/*": ["./foundry/client/*"], "@common/*": ["./foundry/common/*"] } }, "exclude": ["node_modules", "**/node_modules/*"], - "include": ["daggerheart.mjs", "foundry/client/client.mjs", "daggerheart.d.ts", "module/**/*.d.ts"], + "include": ["daggerheart.mjs", "foundry/client/client.mjs", "daggerheart.d.ts"], "typeAcquisition": { "include": ["jquery"] } diff --git a/lang/en.json b/lang/en.json index 368dd54d..a853d5f9 100755 --- a/lang/en.json +++ b/lang/en.json @@ -484,6 +484,7 @@ "startFormula": "Randomized Start Value Formula", "currentCountdownCurrent": "Current: {value}", "currentCountdownStart": "Start: {value}", + "category": "Category", "progressionType": "Progression", "decreasing": "Decreasing", "looping": "Looping", @@ -1161,7 +1162,7 @@ "autoAppliedByLabel": "Max Stress" } }, - "CountdownProgressType": { + "CountdownType": { "actionRoll": "Action Roll", "characterAttack": "Character Attack", "characterSpotlight": "Character Spotlight", @@ -1169,10 +1170,6 @@ "fear": "Fear", "spotlight": "Spotlight" }, - "CountdownType": { - "encounter": { "label": "Short Term", "shortLabel": "Short" }, - "narrative": { "label": "Long Term", "shortLabel": "Long" } - }, "DaggerheartDiceAnimationEvents": { "critical": { "name": "Critical" }, "higher": { "name": "Highest Roll" } @@ -1458,7 +1455,7 @@ }, "triggerType": "Trigger Type", "triggeringActorType": "Triggering Actor Type", - "triggerError": "{trigger} trigger on {item}({itemUuid}) failed for {actor}. It's probably configured wrong." + "triggerError": "{trigger} trigger failed for {actor}. It's probably configured wrong." }, "WeaponFeature": { "barrier": { @@ -2017,8 +2014,7 @@ "Attachments": { "attachHint": "Drop items here to attach them", "transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one." - }, - "OriginTag": "Origin: {name}" + } }, "GENERAL": { "Ability": { @@ -2900,9 +2896,7 @@ "iconName": "Icon Name", "iconNameHint": "Icons are from fontawesome", "bagName": "Bag Name", - "chestName": "Chest Name", - "denomination": "Denomination", - "initialAmount": "Starting Amount" + "chestName": "Chest Name" }, "domains": { "domainsTitle": "Base Domains", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index 9d27fbb7..82ca9ccb 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -1,5 +1,6 @@ import { abilities } from '../../config/actorConfig.mjs'; import { burden } from '../../config/generalConfig.mjs'; +import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -153,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) { @@ -210,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 => { @@ -440,15 +441,16 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl if (type === 'subclasses') { const classItem = this.setup.class; + const uuid = classItem?._stats.compendiumSource ?? classItem?.uuid; presets.filter = { - 'system.linkedClass': { key: 'system.linkedClass', value: classItem?.sourceUuid } + 'system.linkedClass': { key: 'system.linkedClass', value: 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); @@ -522,52 +524,37 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl } }; - // Inner function to create the base item data - async function createEmbeddedItemData(baseData) { - const uuid = baseData.uuid ?? baseData._uuid - const data = baseData instanceof Item ? baseData : await foundry.utils.fromUuid(baseData.uuid) ?? baseData; - return { - ...baseData, - id: data.id, - uuid: uuid, - _uuid: uuid, - effects: data.effects?.map(effect => effect.toObject()), - flags: baseData.flags ?? data.flags, - _stats: { - ...data._stats, - compendiumSource: uuid.startsWith('Compendium.') ? uuid : null, - duplicateSource: uuid && !uuid.startsWith('Compendium.') ? uuid : null - } - }; - } + await createEmbeddedItemWithEffects(this.character, ancestry); + await createEmbeddedItemWithEffects(this.character, this.setup.community); + await createEmbeddedItemWithEffects(this.character, this.setup.class); + await createEmbeddedItemWithEffects(this.character, this.setup.subclass); + await createEmbeddedItemsWithEffects(this.character, Object.values(this.setup.domainCards)); - // Add the class first. All other items validate it during pre creation - await this.character.createEmbeddedDocuments('Item', [await createEmbeddedItemData(this.setup.class)]); - - // Add the remaining items - const newItems = [ - await createEmbeddedItemData(ancestry), - await createEmbeddedItemData(this.setup.community), - await createEmbeddedItemData(this.setup.subclass), - ...(await Promise.all( - Object.values(this.setup.domainCards).map(d => createEmbeddedItemData(d)) - )) - ]; if (this.equipment.armor.uuid) - newItems.push(await createEmbeddedItemData(this.equipment.armor)); + await createEmbeddedItemWithEffects(this.character, this.equipment.armor, { + ...this.equipment.armor, + system: { ...this.equipment.armor.system, equipped: true } + }); if (this.equipment.primaryWeapon.uuid) - newItems.push(await createEmbeddedItemData(this.equipment.primaryWeapon)); + await createEmbeddedItemWithEffects(this.character, this.equipment.primaryWeapon, { + ...this.equipment.primaryWeapon, + system: { ...this.equipment.primaryWeapon.system, equipped: true } + }); if (this.equipment.secondaryWeapon.uuid) - newItems.push(await createEmbeddedItemData(this.equipment.secondaryWeapon)); + await createEmbeddedItemWithEffects(this.character, this.equipment.secondaryWeapon, { + ...this.equipment.secondaryWeapon, + system: { ...this.equipment.secondaryWeapon.system, equipped: true } + }); if (this.equipment.inventory.choiceA.uuid) - newItems.push(await createEmbeddedItemData(this.equipment.inventory.choiceA)); + await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceA); if (this.equipment.inventory.choiceB.uuid) - newItems.push(await createEmbeddedItemData(this.equipment.inventory.choiceB)); - for (const item of this.setup.class.system.inventory.take.filter(x => x)) { - newItems.push(await createEmbeddedItemData(item)); - } + await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceB); + + await createEmbeddedItemsWithEffects( + this.character, + this.setup.class.system.inventory.take.filter(x => x) + ); - await this.character.createEmbeddedDocuments('Item', newItems); await this.character.update( { system: { @@ -600,14 +587,26 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl const item = await foundry.utils.fromUuid(data.uuid); if (item.type === 'ancestry' && event.target.closest('.primary-ancestry-card')) { this.setup.ancestryName.primary = item.name; - this.setup.primaryAncestry = item; + this.setup.primaryAncestry = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; } else if (item.type === 'ancestry' && event.target.closest('.secondary-ancestry-card')) { this.setup.ancestryName.secondary = item.name; - this.setup.secondaryAncestry = item; + this.setup.secondaryAncestry = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; } else if (item.type === 'community' && event.target.closest('.community-card')) { - this.setup.community = item; + this.setup.community = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; } else if (item.type === 'class' && event.target.closest('.class-card')) { - this.setup.class = item; + this.setup.class = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), uuid: item.uuid }; this.setup.subclass = {}; this.setup.domainCards = { [foundry.utils.randomID()]: {}, @@ -620,7 +619,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl return; } - this.setup.subclass = item; + this.setup.subclass = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; } else if (item.type === 'domainCard' && event.target.closest('.domain-card')) { if (!this.setup.class.uuid) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass')); @@ -642,14 +645,14 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl return; } - this.setup.domainCards[event.target.closest('.domain-card').dataset.card] = item; + this.setup.domainCards[event.target.closest('.domain-card').dataset.card] = { ...item, uuid: item.uuid }; } else if (item.type === 'armor' && event.target.closest('.armor-card')) { if (item.system.tier > 1) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.itemTooHighTier')); return; } - this.equipment.armor = item; + this.equipment.armor = { ...item, uuid: item.uuid }; } else if (item.type === 'weapon' && event.target.closest('.primary-weapon-card')) { if (item.system.secondary) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.notPrimary')); @@ -665,7 +668,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.equipment.secondaryWeapon = {}; } - this.equipment.primaryWeapon = item; + this.equipment.primaryWeapon = { ...item, uuid: item.uuid }; } else if (item.type === 'weapon' && event.target.closest('.secondary-weapon-card')) { if (this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.primaryIsTwoHanded')); @@ -682,7 +685,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl return; } - this.equipment.secondaryWeapon = item; + this.equipment.secondaryWeapon = { ...item, uuid: item.uuid }; } else { return; } diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 048797d8..76b2e751 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -160,7 +160,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } if (rest.hasOwnProperty('trait')) { this.config.roll.trait = rest.trait; - if (!this.config.source.item && this.config.roll.trait) + if (!this.config.source.item) this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: game.i18n.localize(abilities[this.config.roll.trait]?.label) }); @@ -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(); } } @@ -224,7 +224,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.render(); } - static async submitRoll(event) { + static async submitRoll() { await this.close({ submitted: true }); } diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index 8cb026f0..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; @@ -176,6 +176,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } static updateData(event, _, formData) { + const form = foundry.utils.expandObject(formData.object); this.render(true); } diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index e209cc3b..367540bf 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -259,10 +259,10 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV const resetValue = increasing ? 0 : feature.system.resource.max - ? new Roll( + ? new Roll( Roll.replaceFormulaData(feature.system.resource.max, this.actor.getRollData()) ).evaluateSync().total - : 0; + : 0; await feature.update({ 'system.resource.value': resetValue }); } diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs index ebc80d39..dd504b4b 100644 --- a/module/applications/dialogs/groupRollDialog.mjs +++ b/module/applications/dialogs/groupRollDialog.mjs @@ -1,5 +1,4 @@ import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; -import { shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; @@ -168,8 +167,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, @@ -481,23 +480,20 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat await cls.create(msgData); - /* Handle resource updates for the finished GroupRoll */ - if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) { - const resourceMap = new ResourceUpdateMap(actor); - if (totalRoll.isCritical) { - resourceMap.addResources([ - { key: 'stress', value: -1 }, - { key: 'hope', value: 1 } - ]); - } else if (totalRoll.withHope) { - resourceMap.addResources([{ key: 'hope', value: 1 }]); - } else { - resourceMap.addResources([{ key: 'fear', value: 1 }]); - } - - resourceMap.updateResources(); + const resourceMap = new ResourceUpdateMap(actor); + if (totalRoll.isCritical) { + resourceMap.addResources([ + { key: 'stress', value: -1, total: 1 }, + { key: 'hope', value: 1, total: 1 } + ]); + } else if (totalRoll.withHope) { + resourceMap.addResources([{ key: 'hope', value: 1, total: 1 }]); + } else { + resourceMap.addResources([{ key: 'fear', value: 1, total: 1 }]); } + resourceMap.updateResources(); + /* Fin */ this.cancelRoll({ confirm: false }); } diff --git a/module/applications/dialogs/itemTransfer.mjs b/module/applications/dialogs/itemTransfer.mjs index 46ceafbd..42e3a727 100644 --- a/module/applications/dialogs/itemTransfer.mjs +++ b/module/applications/dialogs/itemTransfer.mjs @@ -39,7 +39,6 @@ export default class ItemTransferDialog extends HandlebarsApplicationMixin(Appli const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew; const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null; const max = item?.system.quantity ?? originActor.system.gold[currency] ?? 0; - const isQuantifiable = targetActor.system.metadata.quantifiable?.includes(item?.type); return { originActor, @@ -47,7 +46,7 @@ export default class ItemTransferDialog extends HandlebarsApplicationMixin(Appli itemImage: item?.img, currencyIcon: currencySetting?.icon, max, - initial: isQuantifiable || !!currencySetting ? max : 1, + initial: targetActor.system.metadata.quantifiable?.includes(item.type) ? max : 1, title: item?.name ?? currencySetting?.label }; } diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 19869a00..3dc6b0fc 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,6 +1,5 @@ -import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; import { MemberData } from '../../data/tagTeamData.mjs'; -import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; +import { getCritDamageBonus } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; @@ -10,7 +9,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio constructor(party) { super({ id: `TagTeamDialog-${party.id}` }); - this.usesTagTeamHopeCost = true; this.party = party; this.partyMembers = party.system.partyMembers .filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type)) @@ -22,9 +20,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) })); - this.initiator = { cost: - this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial - }; + this.initiator = { cost: 3 }; this.openForAllPlayers = true; this.tabGroups.application = Object.keys(party.system.tagTeam.members).length @@ -98,13 +94,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio ?.addEventListener('input', this.updateInitiatorMemberField.bind(this)); htmlElement - .querySelector('.initiator-cost-input') + .querySelector('.initiator-cost-field') ?.addEventListener('input', this.updateInitiatorCostField.bind(this)); - htmlElement - .querySelector('.initiator-cost-enabled-checkbox') - ?.addEventListener('change', this.toggleInitiatorCostEnabled.bind(this)); - htmlElement .querySelector('.openforall-field') ?.addEventListener('change', this.updateOpenForAllField.bind(this)); @@ -164,7 +156,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio .map(x => ({ value: x.id, label: x.name })); partContext.initiatorDisabled = !selectedMembers.length; partContext.openForAllPlayers = this.openForAllPlayers; - partContext.usesTagTeamHopeCost = this.usesTagTeamHopeCost; break; case 'tagTeamRoll': @@ -406,13 +397,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio this.render(); } - toggleInitiatorCostEnabled(_event) { - this.usesTagTeamHopeCost = !this.usesTagTeamHopeCost; - this.initiator.cost = this.usesTagTeamHopeCost ? - this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial : 0; - this.render(); - } - updateOpenForAllField(event) { this.openForAllPlayers = event.target.checked; this.render(); @@ -669,8 +653,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const baseSecondaryRoll = selectedRoll ? memberValues.find(x => !x.selected) : memberValues.length > 1 - ? memberValues[1] - : null; + ? memberValues[1] + : null; if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null; @@ -768,37 +752,32 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio /* Handle resource updates from the finished TagTeamRoll */ const tagTeamData = this.party.system.tagTeam; - - const actorResourceMaps = Object.keys(tagTeamData.members).reduce((acc, key) => { - acc[key] = new ResourceUpdateMap(game.actors.get(key)); - return acc; - }, {}); - - if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) { - const fearResourceMap = actorResourceMaps[tagTeamData.initiator.memberId]; - for (const memberId in tagTeamData.members) { - const resourceMap = actorResourceMaps[memberId]; - if (finalRoll.isCritical) { - resourceMap.addResources([ - { key: 'stress', value: -1, enabled: true }, - { key: 'hope', value: 1, enabled: true } - ]); - } else if (finalRoll.withHope) { - resourceMap.addResources([{ key: 'hope', value: 1, enabled: true }]); - } else if (finalRoll.withFear) { - fearResourceMap.addResources([{ key: 'fear', value: 1, enabled: true }]); - } + const fearUpdate = { key: 'fear', value: null, total: null, enabled: true }; + for (let memberId in tagTeamData.members) { + const resourceUpdates = []; + const rollGivesHope = finalRoll.isCritical || finalRoll.withHope; + if (memberId === tagTeamData.initiator.memberId) { + const value = tagTeamData.initiator.cost + ? rollGivesHope + ? 1 - tagTeamData.initiator.cost + : -tagTeamData.initiator.cost + : 1; + resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true }); + } else if (rollGivesHope) { + resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true }); + } + if (finalRoll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true }); + if (finalRoll.withFear) { + fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1; + fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1; } - } - /* Even with Hope/Fear automation off, the hope cost of performing the TagTeamRoll can still optionally be subtracted */ - if (tagTeamData.initiator.cost) { - const resourceMap = actorResourceMaps[tagTeamData.initiator.memberId]; - resourceMap.addResources([{ key: 'hope', value: -tagTeamData.initiator.cost, enabled: true }]); + game.actors.get(memberId).modifyResource(resourceUpdates); } - for (const resourceMap of Object.values(actorResourceMaps)) - resourceMap.updateResources(); + if (fearUpdate.value) { + mainActor.modifyResource([fearUpdate]); + } /* Fin */ this.cancelRoll({ confirm: false }); diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 4805cd9e..671b01a1 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)) @@ -174,8 +174,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 +183,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 +192,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 +201,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..e8d6cf1c 100644 --- a/module/applications/levelup/characterLevelup.mjs +++ b/module/applications/levelup/characterLevelup.mjs @@ -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 f40609a4..c4616d9a 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -1,3 +1,4 @@ +import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs'; import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -134,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; @@ -171,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, @@ -197,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); @@ -404,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/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index f2defffb..9de9e752 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -1,13 +1,12 @@ +import DhAppearance from '../../data/settings/Appearance.mjs'; import { getDiceSoNicePreset } from '../../config/generalConfig.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; /** - * @import DhAppearance from '../../data/settings/Appearance.mjs'; * @import {ApplicationClickAction} from "@client/applications/_types.mjs" */ -/** Settings menu for appearance settings */ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(ApplicationV2) { /**@inheritdoc */ static DEFAULT_OPTIONS = { diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index c4dfc397..40ea0301 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -111,7 +111,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 +251,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, diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 958d502e..33f4d9cb 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -1,4 +1,4 @@ -import { AltOutcome } from '../../data/action/altOutcome.mjs'; +import { AltDamageOutcome } from '../../data/fields/action/damageField.mjs'; import { getUnusedDamageTypes } from '../../helpers/utils.mjs'; import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; @@ -10,6 +10,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.action = action; this.openSection = null; this.openTrigger = this.action.triggers.length > 0 ? 0 : null; + + this.outcomeTabs = DHActionBaseConfig.getOutcomeTabs(action); } get title() { @@ -123,7 +125,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static getOutcomeTabs(action) { const outcomeKeys = [ 'default', - ...Object.keys(action.altOutcomes ?? {}).filter(key => action.altOutcomes[key]) + ...Object.keys(action.damage?.altOutcomes ?? {}).filter(key => action.damage.altOutcomes[key]) ]; return outcomeKeys.reduce((acc, key, index) => { acc[key] = { @@ -132,10 +134,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) group: 'outcomes', id: key, icon: null, - label: game.i18n.localize(CONFIG.DH.ACTIONS.outcomeTypes[key].label), - source: key === 'default' ? action._source : action._source.altOutcomes[key], - fields: key === 'default' ? action.schema.fields : action.schema.fields.altOutcomes.fields[key].fields, - getBasePath: path => (key === 'default' ? path : ['altOutcomes', key, path].join('.')) + label: game.i18n.localize(CONFIG.DH.ACTIONS.outcomeTypes[key].label) }; return acc; }, {}); @@ -144,7 +143,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) /* Needs to consider effect altOutcomes aswell */ static selectOutcome(action, callback) { const choices = Object.entries(CONFIG.DH.ACTIONS.outcomeTypes).reduce((acc, [key, value]) => { - if (action.altOutcomes[key] === null) acc.push({ id: key, label: game.i18n.localize(value.label) }); + if (action.damage.altOutcomes[key] === null) acc.push({ id: key, label: game.i18n.localize(value.label) }); return acc; }, []); @@ -238,8 +237,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.openSection = this.openSection; context.tabs = this._getTabs(this.constructor.TABS); - context.outcomeTabs = this._getTabs(DHActionBaseConfig.getOutcomeTabs(this.action)); - context.allOutcomesAssigned = Object.keys(context.outcomeTabs).length >= 4; + context.outcomeTabs = this._getTabs(this.outcomeTabs); + context.allOutcomesAssigned = Object.keys(this.outcomeTabs).length >= 4; context.config = CONFIG.DH; if (this.action.damage) { @@ -290,7 +289,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) }; } - if (this.action.parent.metadata?.isInventoryItem) { + if (this.action.parent.metadata.isInventoryItem) { options.quantity = { label: 'DAGGERHEART.GENERAL.itemQuantity', group: 'Global' @@ -389,8 +388,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) if (!this.action.damage.parts) return; const outcome = button.dataset.outcome; - const outcomeData = outcome === 'default' ? this.action._source : this.action._source.altOutcomes[outcome]; - const choices = getUnusedDamageTypes(outcomeData.damage.parts); + const source = this.action._source; + const outcomeParts = outcome === 'default' ? source.damage.parts : source.damage.altOutcomes[outcome].parts; + const choices = getUnusedDamageTypes(outcomeParts); const content = new foundry.data.fields.StringField({ label: game.i18n.localize('Damage Type'), choices, @@ -413,8 +413,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial; } - if (outcome !== 'default') data.altOutcomes[outcome] ??= new AltOutcome(); - (outcome === 'default' ? data : data.altOutcomes[outcome]).damage.parts[type] = part; + if (outcome !== 'default') data.damage.altOutcomes[outcome] ??= new AltDamageOutcome(); + (outcome === 'default' ? data.damage : data.damage.altOutcomes[outcome]).parts[type] = part; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }; @@ -444,7 +444,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) if (!this.action.damage.parts) return; const data = this.action.toObject(); const { key, outcome } = button.dataset; - (outcome === 'default' ? data : data.altOutcomes[outcome]).damage.parts[key] = _del; + const parts = outcome === 'default' ? data.damage.parts : data.damage.altOutcomes[outcome].parts; + parts[key] = _del; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 444a60fe..a4b0a6c4 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -1,4 +1,4 @@ -import { AltOutcome } from '../../data/action/altOutcome.mjs'; +import { AltDamageOutcome } from '../../data/fields/action/damageField.mjs'; import DHActionBaseConfig from './action-base-config.mjs'; export default class DHActionConfig extends DHActionBaseConfig { @@ -27,7 +27,9 @@ export default class DHActionConfig extends DHActionBaseConfig { DHActionBaseConfig.selectOutcome(this.action, key => { if (!key) return; - data.altOutcomes[key] = new AltOutcome(); + + data.damage.altOutcomes[key] = new AltDamageOutcome(); + this.outcomeTabs = DHActionBaseConfig.getOutcomeTabs(data); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }); } @@ -35,7 +37,10 @@ export default class DHActionConfig extends DHActionBaseConfig { static onRemoveOutcome(_event, button) { const { outcome } = button.dataset; const data = this.action.toObject(); - data.altOutcomes[outcome] = null; + + data.damage.altOutcomes[outcome] = null; + this.outcomeTabs = DHActionBaseConfig.getOutcomeTabs(data); + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index ff3f3039..57405675 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -10,7 +10,11 @@ export default class DHAdversarySettings extends DHBaseActorSettings { actions: { addExperience: DHAdversarySettings.#addExperience, removeExperience: DHAdversarySettings.#removeExperience - } + }, + dragDrop: [ + { dragSelector: null, dropSelector: '.tab.features' }, + { dragSelector: '.feature-item', dropSelector: null } + ] }; /**@override */ @@ -34,8 +38,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { }, features: { id: 'features', - template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/features.hbs', - scrollable: [''] + template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/features.hbs' } }; @@ -51,16 +54,12 @@ export default class DHAdversarySettings extends DHBaseActorSettings { async _prepareContext(options) { const context = await super._prepareContext(options); - // Get feature groups. Uncategorized go to actions - const featureFormsTypes = ['passive', 'action', 'reaction']; - const features = this.document.system.features.sort((a, b) => a.sort - b.sort); - const featureGroups = featureFormsTypes.map(t => ({ - featureForm: t, - label: _loc(CONFIG.DH.ITEM.featureForm[t]), - features: features.filter(f => f.system.featureForm === t) - })); - featureGroups[1].features.push(...features.filter(f => !featureFormsTypes.includes(f.system.featureForm))); - context.featureGroups = featureGroups; + 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; } @@ -98,4 +97,32 @@ export default class DHAdversarySettings extends DHBaseActorSettings { await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); } + + 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/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 954d6381..6d74f9c6 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -15,7 +15,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { dragDrop: [ { dragSelector: null, dropSelector: '.category-container' }, { dragSelector: null, dropSelector: '.tab.features' }, - { dragSelector: '.feature-item, .inventory-item[data-type="adversary"]', dropSelector: null } + { dragSelector: '.feature-item', dropSelector: null } ] }; @@ -32,13 +32,11 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { }, features: { id: 'features', - template: 'systems/daggerheart/templates/sheets-settings/environment-settings/features.hbs', - scrollable: [''] + template: 'systems/daggerheart/templates/sheets-settings/environment-settings/features.hbs' }, adversaries: { id: 'adversaries', - template: 'systems/daggerheart/templates/sheets-settings/environment-settings/adversaries.hbs', - scrollable: [''] + template: 'systems/daggerheart/templates/sheets-settings/environment-settings/adversaries.hbs' } }; @@ -54,16 +52,12 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { async _prepareContext(options) { const context = await super._prepareContext(options); - // Get feature groups. Uncategorized go to actions - const featureFormsTypes = ['passive', 'action', 'reaction']; - const features = this.document.system.features.sort((a, b) => a.sort - b.sort); - const featureGroups = featureFormsTypes.map(t => ({ - featureForm: t, - label: _loc(CONFIG.DH.ITEM.featureForm[t]), - features: features.filter(f => f.system.featureForm === t) - })); - featureGroups[1].features.push(...features.filter(f => !featureFormsTypes.includes(f.system.featureForm))); - context.featureGroups = featureGroups; + 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; } @@ -116,30 +110,33 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { } async _onDragStart(event) { - const element = event.currentTarget.closest('.inventory-item[data-type=adversary]'); - if (element) { - const adversaryData = { type: 'Actor', uuid: element.dataset.itemUuid }; - event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData)); - event.dataTransfer.setDragImage(element, 60, 0); - } else { - return super._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 doc = await fromUuid(data.uuid); - if (doc?.type === 'adversary' && event.target.closest('.category-container')) { + const item = await fromUuid(data.uuid); + if (data.fromInternal && item?.parent?.uuid === this.actor.uuid) return; + + if (item.type === 'adversary' && event.target.closest('.category-container')) { const target = event.target.closest('.category-container'); const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid); - if (!current.includes(doc.uuid)) { - await this.actor.update({ [path]: [...current, doc.uuid] }); - } - return; + await this.actor.update({ + [path]: [...current, item.uuid] + }); + this.render(); + } else if (item.type === 'feature' && event.target.closest('.tab.features')) { + await this.actor.createEmbeddedDocuments('Item', [item]); + this.render(); } - - return super._onDrop(event); } } diff --git a/module/applications/sheets-configs/npc-settings.mjs b/module/applications/sheets-configs/npc-settings.mjs index 33cbc270..c187877c 100644 --- a/module/applications/sheets-configs/npc-settings.mjs +++ b/module/applications/sheets-configs/npc-settings.mjs @@ -27,8 +27,7 @@ export default class DHNPCSettings extends DHBaseActorSettings { }, features: { id: 'features', - template: 'systems/daggerheart/templates/sheets-settings/npc-settings/features.hbs', - scrollable: [''] + template: 'systems/daggerheart/templates/sheets-settings/npc-settings/features.hbs' } }; @@ -44,17 +43,43 @@ export default class DHNPCSettings extends DHBaseActorSettings { async _prepareContext(options) { const context = await super._prepareContext(options); - // Get feature groups. Uncategorized go to actions - const featureFormsTypes = ['passive', 'action', 'reaction']; - const features = this.document.system.features.sort((a, b) => a.sort - b.sort); - const featureGroups = featureFormsTypes.map(t => ({ - featureForm: t, - label: _loc(CONFIG.DH.ITEM.featureForm[t]), - features: features.filter(f => f.system.featureForm === t) - })); - featureGroups[1].features.push(...features.filter(f => !featureFormsTypes.includes(f.system.featureForm))); - context.featureGroups = featureGroups; + 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/_types.d.ts b/module/applications/sheets/actors/_types.d.ts deleted file mode 100644 index 89adc57b..00000000 --- a/module/applications/sheets/actors/_types.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import DhCompanion from '../../../data/actor/companion.mjs'; -import DhpActor from '../../../documents/actor.mjs'; - -declare module './companion.mjs' { - export default interface DhCompanionSheet { - actor: DhpActor; - } -} diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 85380392..06dd4a0f 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -103,7 +103,7 @@ export default class AdversarySheet extends DHBaseActorSheet { context.resources.stress.emptyPips = context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; - const featureForms = Object.keys(CONFIG.DH.ITEM.featureForm); + 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) @@ -131,15 +131,6 @@ export default class AdversarySheet extends DHBaseActorSheet { return context; } - /** @inheritdoc */ - _prepareTabs(group) { - const result = super._prepareTabs(group); - if (group === 'primary') { - result.notes.empty = !this.document.system.notes?.trim(); - } - return result; - } - /**@inheritdoc */ _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); @@ -195,6 +186,15 @@ export default class AdversarySheet extends DHBaseActorSheet { }); } + /** @inheritdoc */ + async _onDragStart(event) { + const inventoryItem = event.currentTarget.closest('.inventory-item'); + if (inventoryItem) { + event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); + } + super._onDragStart(event); + } + /* -------------------------------------------- */ /* Application Clicks Actions */ /* -------------------------------------------- */ diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 3a60e7ca..5d0e7144 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -356,7 +356,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const levelups = Object.values(actor.system.levelData?.levelups) ?? []; const uuid = item.uuid; - const sourceUuid = item.sourceUuid; // on older characters this may be missing + 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); @@ -785,11 +785,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 @@ -809,7 +809,7 @@ export default class CharacterSheet extends DHBaseActorSheet { /* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */ const costResources = - result.costs?.filter(x => x.enabled).map(cost => ({ ...cost, value: -cost.value })) || + result.costs?.filter(x => x.enabled).map(cost => ({ ...cost, value: -cost.value, total: -cost.total })) || {}; result.resourceUpdates.addResources(costResources); await result.resourceUpdates.updateResources(); @@ -1193,6 +1193,15 @@ export default class CharacterSheet extends DHBaseActorSheet { }); } + /** @inheritdoc */ + async _onDragStart(event) { + const inventoryItem = event.currentTarget.closest('.inventory-item'); + if (inventoryItem) { + event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); + } + super._onDragStart(event); + } + async _onDropItem(event, item) { const setupCriticalItemTypes = ['class', 'subclass', 'ancestry', 'community']; if (this.document.system.needsCharacterSetup && setupCriticalItemTypes.includes(item.type)) { diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 4b5b5a56..a01b4a64 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -62,7 +62,9 @@ export default class DhCompanionSheet extends DHBaseActorSheet { await this.document.update({ 'system.resources.stress.value': newValue }); } - /** @this {DhCompanionSheet} **/ + /** + * + */ static async #actionRoll(event) { const partner = this.actor.system.partner; const config = { @@ -78,7 +80,6 @@ export default class DhCompanionSheet extends DHBaseActorSheet { const result = await partner.diceRoll(config); this.consumeResource(result?.costs); - result?.resourceUpdates.updateResources(); } // Remove when Action Refactor part #2 done diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 60fb9071..f8ff74a6 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -72,22 +72,13 @@ export default class DhpEnvironment extends DHBaseActorSheet { return applicationOptions; } - /** @inheritdoc */ - _prepareTabs(group) { - const result = super._prepareTabs(group); - if (group === 'primary') { - result.potentialAdversaries.empty = foundry.utils.isEmpty(this.document.system.potentialAdversaries); - result.notes.empty = !this.document.system.notes?.trim(); - } - 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); diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index 3af8ea5f..927a8810 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -162,9 +162,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 +306,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 }); diff --git a/module/applications/sheets/api/actor-setting.mjs b/module/applications/sheets/api/actor-setting.mjs index fa434034..738f7002 100644 --- a/module/applications/sheets/api/actor-setting.mjs +++ b/module/applications/sheets/api/actor-setting.mjs @@ -1,15 +1,13 @@ import DHApplicationMixin from './application-mixin.mjs'; -const { ActorSheetV2 } = foundry.applications.sheets; +const { DocumentSheetV2 } = foundry.applications.api; -/** - * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction - */ +/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /** * Base settings sheet for Daggerheart actors. - * @extends {DHApplicationMixin} + * @extends {DHApplicationMixin} */ -export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2) { +export default class DHBaseActorSettings extends DHApplicationMixin(DocumentSheetV2) { /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['dialog'], @@ -25,7 +23,7 @@ export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2 }, dragDrop: [ { dragSelector: null, dropSelector: '.tab.features' }, - { dragSelector: '.feature-item, .inventory-item[data-type="feature"]', dropSelector: null } + { dragSelector: '.feature-item', dropSelector: null } ] }; @@ -36,7 +34,7 @@ export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2 return options; } - /** @returns {foundry.documents.Actor} */ + /**@returns {foundry.documents.Actor} */ get actor() { return this.document; } @@ -75,32 +73,4 @@ export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2 return context; } - - async _onDragStart(event) { - const featureItemEl = event.currentTarget.closest('.feature-item, .inventory-item[data-type="feature"]'); - const feature = this.actor.items.get(featureItemEl?.dataset.itemId); - if (feature && event.target.closest('.tab.features')) { - const featureData = { ...feature.toDragData(), fromInternal: true }; - event.dataTransfer.setData('text/plain', JSON.stringify(featureData)); - event.dataTransfer.setDragImage(featureItemEl.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 super._onDrop(event); - } - - const itemData = item.toObject(); - delete itemData._id; - await this.actor.createEmbeddedDocuments('Item', [itemData]); - } - } - - /** Setting sheets do not auto extend */ - async _autoExpandDescriptions() {} } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 0168f46d..2b0c3e55 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -4,7 +4,6 @@ import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../. const typeSettingsMap = { character: 'extendCharacterDescriptions', adversary: 'extendAdversaryDescriptions', - npc: 'extendAdversaryDescriptions', environment: 'extendEnvironmentDescriptions', ancestry: 'extendItemDescriptions', community: 'extendItemDescriptions', @@ -263,7 +262,7 @@ export default function DHApplicationMixin(Base) { if (!!this.options.contextMenus.length) this._createContextMenus(); - this._autoExpandDescriptions(context); + this.#autoExtendDescriptions(context); } /** @inheritDoc */ @@ -362,17 +361,18 @@ export default function DHApplicationMixin(Base) { */ async _onDragStart(event) { const inventoryItem = event.currentTarget.closest('.inventory-item'); - if (!inventoryItem) return; - - const { type, itemUuid } = inventoryItem.dataset; - const effect = type === 'effect' ? await foundry.utils.fromUuid(itemUuid) : null; - if (effect) { - const effectData = { - ...effect.toDragData(), - fromInternal: this.document.uuid - }; - event.dataTransfer.setData('text/plain', JSON.stringify(effectData)); - event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); + if (inventoryItem) { + const { type, itemUuid } = inventoryItem.dataset; + if (type === 'effect') { + const effect = await foundry.utils.fromUuid(itemUuid); + const effectData = { + type: 'ActiveEffect', + data: { ...effect.toObject(), _id: null }, + fromInternal: this.document.uuid + }; + event.dataTransfer.setData('text/plain', JSON.stringify(effectData)); + event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); + } } } @@ -382,33 +382,14 @@ export default function DHApplicationMixin(Base) { * @protected */ _onDrop(event) { - // Potentially handle subclasses that dont descend from actor/item sheet. event.stopPropagation(); - return super._onDrop?.(event); - } - - /** @inheritdoc */ - _onSortItem(event, item) { - // If we are dragging a feature past its allowed feature form, put it in the front or in the back - const doc = this.actor.items.get(item.id); - const dropTargetEl = event.target.closest('[data-item-id]'); - const dropTarget = this.actor.items.get(dropTargetEl?.dataset.itemId); - if (doc?.type === 'feature' && dropTarget?.type === 'feature' && doc.system.featureForm !== dropTarget.system.featureForm) { - const siblings = this.actor.itemTypes.feature - .filter(f => f.system.featureForm === doc.system.featureForm) - .sort((a, b) => a.sort - b.sort); - if (siblings.length > 1) { - const featureForms = Object.keys(CONFIG.DH.ITEM.featureForm); - const thisFeatureIdx = featureForms.indexOf(doc.system.featureForm); - const targetFeatureIdx = featureForms.indexOf(dropTarget.system.featureForm); - const target = targetFeatureIdx < thisFeatureIdx ? siblings[0] : siblings.at(-1); - const sortUpdates = foundry.utils.performIntegerSort(doc, { target, siblings }); - const updateData = sortUpdates.map(u => ({ ...u.update, _id: u.target._id })); - return this.actor.updateEmbeddedDocuments('Item', updateData); - } + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + if (data.type === 'ActiveEffect' && data.fromInternal !== this.document.uuid) { + this.document.createEmbeddedDocuments('ActiveEffect', [data.data]); + } else { + // Fallback to super, but note that item sheets do not have this function + return super._onDrop?.(event); } - - return super._onSortItem?.(event, item); } /* -------------------------------------------- */ @@ -518,7 +499,7 @@ export default function DHApplicationMixin(Base) { 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.getActionRelevantEffects( + config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects( this.document, doc ); @@ -603,7 +584,7 @@ export default function DHApplicationMixin(Base) { const doc = await fromUuid(itemUuid); //get inventory-item description element - const descriptionElement = el.querySelector('.inventory-description'); + const descriptionElement = el.querySelector('.invetory-description'); if (!doc || !descriptionElement) continue; // localize the description (idk if it's still necessary) @@ -631,9 +612,8 @@ export default function DHApplicationMixin(Base) { /** * Extend inventory description when enabled in settings. * @returns {Promise} - * @protected */ - async _autoExpandDescriptions(context) { + async #autoExtendDescriptions(context) { const inventoryItems = this.element.querySelectorAll('.inventory-item[data-item-uuid]'); for (const el of inventoryItems) { // Get the doc uuid from the element @@ -736,16 +716,16 @@ export default function DHApplicationMixin(Base) { * @type {ApplicationClickAction} */ static async #onCreateDoc(event, target) { - const { documentClass, type, inVault, disabled, featureForm } = target.dataset; + 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 = {}; if (featureOnCharacter) { @@ -754,7 +734,6 @@ export default function DHApplicationMixin(Base) { identifier: this.document.system.isMulticlass ? 'multiclass' : null }; } - if (featureForm) systemData.featureForm = featureForm; const cls = type === 'action' ? game.system.api.models.actions.actionsTypes.base : getDocumentClass(documentClass); diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 007b641b..7b820822 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -1,12 +1,10 @@ import { getDocFromElement, itemIsIdentical } from '../../../helpers/utils.mjs'; +import DHBaseActorSettings from './actor-setting.mjs'; import DHApplicationMixin from './application-mixin.mjs'; const { ActorSheetV2 } = foundry.applications.sheets; -/** - * @import DHBaseActorSettings from './actor-setting.mjs'; - * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction - */ +/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /** * A base actor sheet extending {@link ActorSheetV2} via {@link DHApplicationMixin} @@ -212,7 +210,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { 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.getActionRelevantEffects( + config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects( this.document, doc ); @@ -379,7 +377,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 = []; @@ -412,7 +410,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 }] }); } @@ -449,15 +447,13 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { const item = await getDocFromElement(event.target); if (item) { - const inventoryItem = event.currentTarget.closest('.inventory-item'); const dragData = { - ...item.toDragData(), originActor: this.document.uuid, - originId: item.id + originId: item.id, + type: item.documentName, + uuid: item.uuid }; event.dataTransfer.setData('text/plain', JSON.stringify(dragData)); - if (inventoryItem) event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); - return; } super._onDragStart(event); 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/subclass.mjs b/module/applications/sheets/items/subclass.mjs index 650d2088..e9d8370e 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -59,7 +59,7 @@ export default class SubclassSheet extends DHBaseItemSheet { const item = await fromUuid(data.uuid); const itemType = data.type === 'ActiveEffect' ? data.type : item.type; if (itemType === 'class') { - const uuid = item.sourceUuid; + 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 diff --git a/module/applications/sidebar/tabs/actorDirectory.mjs b/module/applications/sidebar/tabs/actorDirectory.mjs index 4d00afa2..89da1426 100644 --- a/module/applications/sidebar/tabs/actorDirectory.mjs +++ b/module/applications/sidebar/tabs/actorDirectory.mjs @@ -1,6 +1,6 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs.ActorDirectory { static DEFAULT_OPTIONS = { - renderUpdateKeys: ['system.levelData.level.current', 'system.partner', 'system.tier', 'system.type'] + renderUpdateKeys: ['system.levelData.level.current', 'system.partner', 'system.tier'] }; static _entryPartial = 'systems/daggerheart/templates/ui/sidebar/actor-document-partial.hbs'; @@ -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/_module.mjs b/module/applications/ui/_module.mjs index e30950b5..80d3ebe4 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -8,4 +8,3 @@ export { default as DhHotbar } from './hotbar.mjs'; export { default as DhSceneNavigation } from './sceneNavigation.mjs'; export { ItemBrowser } from './itemBrowser.mjs'; export { default as DhProgress } from './progress.mjs'; -export { default as DhGamePause } from './gamePause.mjs'; diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 199ee87d..7036a5df 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({ diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 1dbc56be..b418107c 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -35,7 +35,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio static PARTS = { countdowns: { - template: 'systems/daggerheart/templates/ui/countdowns/countdown-edit.hbs', + template: 'systems/daggerheart/templates/ui/countdown-edit.hbs', scrollable: ['.expanded-view', '.edit-content'] } }; @@ -45,7 +45,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio context.isGM = game.user.isGM; context.ownershipDefaultOptions = CONFIG.DH.GENERAL.basicOwnershiplevels; context.defaultOwnership = this.data.defaultOwnership; - context.countdownTypes = CONFIG.DH.GENERAL.countdownTypes; + context.countdownBaseTypes = CONFIG.DH.GENERAL.countdownBaseTypes; context.countdownProgressionTypes = CONFIG.DH.GENERAL.countdownProgressionTypes; context.countdownLoopingTypes = CONFIG.DH.GENERAL.countdownLoopingTypes; context.hideNewCountdowns = this.hideNewCountdowns; @@ -56,13 +56,13 @@ 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] = { ...countdown, - typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownTypes[countdown.type].label), + typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownBaseTypes[countdown.type].label), progress: { ...countdown.progress, typeName: game.i18n.localize( @@ -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 5cf79100..76e2b399 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -11,14 +11,9 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; */ export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) { - previousCountdownData = null; - changedCountdownsForAnimation = new Set(); - constructor(options = {}) { super(options); - this.previousCountdownData = - game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns; this.setupHooks(); } @@ -36,10 +31,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application minimizable: false }, actions: { - toggleViewMode: DhCountdowns.#onToggleViewMode, - toggleCountdownTypes: DhCountdowns.#onToggleCountdownTypes, - 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) }, @@ -54,20 +48,11 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application static PARTS = { resources: { root: true, - template: 'systems/daggerheart/templates/ui/countdowns/countdowns-view.hbs' + template: 'systems/daggerheart/templates/ui/countdowns.hbs' } }; - /** - * Returns all visible countdown types - * @returns {string[]} - */ - get visibleCountdownTypes() { - const { encounter, narrative } = CONFIG.DH.GENERAL.countdownTypes; - return game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes) - ?? [encounter.id, narrative.id]; - } - + /**@inheritdoc */ async _renderFrame(options) { const frame = await super._renderFrame(options); @@ -80,57 +65,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return frame; } - - async _onRender(context, options) { - await super._onRender(context, options); - - /* Handle rendering/hiding/positioning of the countdown UI */ - this.element.hidden = !game.user.isGM && this.#getCountdowns().length === 0; - if (options?.force) { - document.getElementById('ui-right-column-1')?.appendChild(this.element); - } - - this.previousCountdownData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns) - .countdowns; - - /* Handle animations to draw attention to countdown values changing */ - const typesToAnimate = new Set(); - for (const countdownKey of this.changedCountdownsForAnimation) { - const shimmerAnimation = [ - { backgroundPositionX: '98%' }, - { backgroundPositionX: '0%' } - ]; - const shimmerTiming = { - duration: 1000, - iterations: 1 - }; - - const element = this.element.querySelector(`.countdown-container[data-countdown="${countdownKey}"]`); - element?.animate(shimmerAnimation, shimmerTiming); - - const countdown = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns) - .countdowns[countdownKey]; - if (!this.visibleCountdownTypes.includes(countdown?.type)) - typesToAnimate.add(countdown.type); - } - - for (const type of typesToAnimate) { - const pulseAnimation = [ - { boxShadow: '0 0 1px 1px var(--golden)' }, - { boxShadow: '0 0 2px 2px var(--golden)' } - ]; - const pulseTiming = { - duration: 1000, - iterations: 3 - }; - - const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`); - element?.animate(pulseAnimation, pulseTiming); - } - - this.changedCountdownsForAnimation.clear(); - } - /** Returns countdown data filtered by ownership */ #getCountdowns() { const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); @@ -142,10 +76,16 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE); } - _getCountdownData() { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + /** @override */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.isGM = game.user.isGM; - return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { + context.iconOnly = + game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === + CONFIG.DH.GENERAL.countdownAppMode.iconOnly; + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + context.countdowns = this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { const playersWithAccess = game.users.reduce((acc, user) => { const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown); if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { @@ -161,14 +101,14 @@ 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 || (isLooping && (countdown.progress.current > 0 || countdown.progress.start === '0')); - acc[countdown.type][key] = { + acc[key] = { ...countdown, editable: countdownEditable, noPlayerAccess: nonGmPlayers.length && playersWithAccess.length === 0, @@ -177,38 +117,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application loopTooltip: isLooping && game.i18n.localize(loopTooltip) }; return acc; - }, Object.keys(CONFIG.DH.GENERAL.countdownTypes).reduce((acc, key) => { - acc[key] = {}; - return acc; - }, {})); - } - - /** @override */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - context.isGM = game.user.isGM; - - context.iconOnly = - game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) - === CONFIG.DH.GENERAL.countdownAppMode.iconOnly; - - context.userCountdownTypes = this.visibleCountdownTypes; - - context.typeToggles = - Object.values(CONFIG.DH.GENERAL.countdownTypes).map(type => ({ - type: type.id, - label: game.i18n.localize(type.shortLabel), - active: context.userCountdownTypes.includes(type.id) - })); - - context.countdowns = this._getCountdownData(); - context.countdownTypesWithVisibleEntries = this.#getCountdowns().reduce((acc, data) => { - if (context.userCountdownTypes.includes(data.countdown.type) && !acc.includes(data.countdown.type)) - acc.push(data.countdown.type); - - return acc; - }, []); - + }, {}); return context; } @@ -238,8 +147,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return true; } - /** @this {DhCountdowns} */ - 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; @@ -250,30 +158,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application this.render(); } - /** @this {DhCountdowns} */ - static async #onToggleCountdownTypes(event, target) { - const currentTypes = this.visibleCountdownTypes; - const { type } = target.dataset; - const newTypes = event.shiftKey ? - [type] : - currentTypes.includes(type) ? currentTypes.filter(x => x !== type) : [...currentTypes, type]; - await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes, newTypes); - - this.render(); - } - - /** @this {DhCountdowns} */ - static async #onEditCountdowns() { + static async #editCountdowns() { new game.system.api.applications.ui.CountdownEdit().render(true); } - /** @this {DhCountdowns} */ - 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; @@ -287,12 +180,12 @@ 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 } @@ -306,23 +199,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 settings.updateSource({ [`countdowns.${target.id}.progress.current`]: newCurrent }); await emitGMUpdate(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 }); } @@ -342,31 +234,29 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application * Sends updates of the countdowns to the GM player. Since this is asynchronous, be sure to * update all the countdowns at the same time. * - * @param {...(string | { type: string; undo?: boolean })} progressTypes Countdowns to be updated + * @param {...any} progressTypes Countdowns to be updated */ static async updateCountdowns(...progressTypes) { - progressTypes = progressTypes.map(p => typeof p === 'string' ? { type: p } : p); const { countdownAutomation } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); if (!countdownAutomation) return; const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); const updatedCountdowns = Object.keys(countdownSetting.countdowns).reduce((acc, key) => { const countdown = countdownSetting.countdowns[key]; - const progressData = progressTypes.find(x => x.type === countdown.progress.type); - if (progressData && countdown.progress.current > 0) { - acc[key] = { value: progressData.undo ? 1 : -1 }; + if (progressTypes.indexOf(countdown.progress.type) !== -1 && countdown.progress.current > 0) { + acc.push(key); } return acc; - }, {}); + }, []); const countdownData = countdownSetting.toObject(); const settings = { ...countdownData, countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { const countdown = foundry.utils.deepClone(countdownData.countdowns[key]); - if (updatedCountdowns[key]) { - countdown.progress.current += updatedCountdowns[key].value; + if (updatedCountdowns.includes(key)) { + countdown.progress.current -= 1; } acc[key] = countdown; @@ -377,4 +267,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application refreshType: RefreshType.Countdown }); } + + async _onRender(context, options) { + await super._onRender(context, options); + this.element.hidden = !game.user.isGM && this.#getCountdowns().length === 0; + if (options?.force) { + document.getElementById('ui-right-column-1')?.appendChild(this.element); + } + } } 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/gamePause.mjs b/module/applications/ui/gamePause.mjs deleted file mode 100644 index 8047f1dc..00000000 --- a/module/applications/ui/gamePause.mjs +++ /dev/null @@ -1,23 +0,0 @@ -export default class DhGamePause extends foundry.applications.ui.GamePause { - async _onRender(context, options) { - await super._onRender(context, options); - - /* Avoid altering the styling if a module has subscribed to the renderGamePause hook */ - if (!Hooks.events.renderGamePause?.length) { - this.element.classList.add('dh-style'); - } - } - - /** @override */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - - /* Avoid altering the gamepause context if a module has subscribed to the renderGamePause hook */ - if (!Hooks.events.renderGamePause?.length) { - context.spin = options.spin ?? false; - context.icon = options.icon ?? 'systems/daggerheart/assets/logos/compatible_with_DH_logos-10.png'; - } - - return context; - } -} \ No newline at end of file diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 7ae6dc37..68e325c2 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 }, @@ -248,7 +248,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { } /** @inheritDoc */ - _drawBar(index, bar, data) { + _drawBar(number, bar, data) { // Determine sizing const { width, height } = this.document.getSize(); const s = canvas.dimensions.uiScale; @@ -256,7 +256,9 @@ 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 { full: fullColor, empty: emptyColor } = this._getBarColors(index, data); + const Color = foundry.utils.Color; + const fillColor = number === 0 ? Color.fromRGB([1, 0, 0]) : Color.fromString('#0032b1'); + const emptyColor = Color.fromRGB([0, 0, 0]); // Draw the bar (accounting floating point numbers from bar animations) const widthUnit = bw / Math.ceil(data.max); @@ -266,7 +268,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { 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(fullColor, remainder) : fullColor; + const color = !marked ? emptyColor : remainder ? emptyColor.mix(fillColor, remainder) : fillColor; 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 { @@ -275,7 +277,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { } // Set position - const posY = index === 0 ? height - bh : 0; + const posY = number === 0 ? height - bh : 0; bar.position.set(0, posY); return true; } diff --git a/module/config/flagsConfig.mjs b/module/config/flagsConfig.mjs index cb05bd72..817ac89d 100644 --- a/module/config/flagsConfig.mjs +++ b/module/config/flagsConfig.mjs @@ -24,8 +24,7 @@ export const itemAttachmentSource = 'attachmentSource'; export const userFlags = { welcomeMessage: 'welcome-message', - countdownMode: 'countdown-mode', - countdownTypeModes: 'countdown-type-modes' + countdownMode: 'countdown-mode' }; export const combatToggle = 'combat-toggle-origin'; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 188efafb..6fc85ec5 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -867,27 +867,27 @@ export const abilityCosts = { export const countdownProgressionTypes = { actionRoll: { id: 'actionRoll', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.actionRoll' + label: 'DAGGERHEART.CONFIG.CountdownType.actionRoll' }, characterAttack: { id: 'characterAttack', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.characterAttack' + label: 'DAGGERHEART.CONFIG.CountdownType.characterAttack' }, characterSpotlight: { id: 'characterSpotlight', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.characterSpotlight' + label: 'DAGGERHEART.CONFIG.CountdownType.characterSpotlight' }, custom: { id: 'custom', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.custom' + label: 'DAGGERHEART.CONFIG.CountdownType.custom' }, fear: { id: 'fear', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.fear' + label: 'DAGGERHEART.CONFIG.CountdownType.fear' }, spotlight: { id: 'spotlight', - label: 'DAGGERHEART.CONFIG.CountdownProgressType.spotlight' + label: 'DAGGERHEART.CONFIG.CountdownType.spotlight' } }; export const rollTypes = { @@ -935,6 +935,17 @@ export const simpleOwnershiplevels = { ...basicOwnershiplevels }; +export const countdownBaseTypes = { + narrative: { + id: 'narrative', + label: 'DAGGERHEART.APPLICATIONS.Countdown.types.narrative' + }, + encounter: { + id: 'encounter', + label: 'DAGGERHEART.APPLICATIONS.Countdown.types.encounter' + } +}; + export const countdownLoopingTypes = { noLooping: { id: 'noLooping', @@ -959,19 +970,6 @@ export const countdownAppMode = { iconOnly: 'icon-only' }; -export const countdownTypes = { - encounter: { - id: 'encounter', - label: 'DAGGERHEART.CONFIG.CountdownType.encounter.label', - shortLabel: 'DAGGERHEART.CONFIG.CountdownType.encounter.shortLabel' - }, - narrative: { - id: 'narrative', - label: 'DAGGERHEART.CONFIG.CountdownType.narrative.label', - shortLabel: 'DAGGERHEART.CONFIG.CountdownType.narrative.shortLabel' - } -}; - export const sceneRangeMeasurementSetting = { disable: { id: 'disable', diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 83572dc0..ae5fa71b 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -127,8 +127,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', diff --git a/module/data/action/altOutcome.mjs b/module/data/action/altOutcome.mjs deleted file mode 100644 index 95f75f73..00000000 --- a/module/data/action/altOutcome.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { getDamageBaseFields } from '../fields/action/damageField.mjs'; - -const fields = foundry.data.fields; - -export class AltOutcome extends foundry.abstract.DataModel { - static defineSchema() { - return { - damage: new fields.SchemaField(getDamageBaseFields()) - // todo: add effects - }; - } - - get data() { - return { - ...this.parent, - ...this - }; - } -} diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 1988b1d8..1f7e1c92 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -79,8 +79,8 @@ export default class DHAttackAction extends DHDamageAction { const str = damageString ? damageString : game.i18n.format('DAGGERHEART.GENERAL.missingX', { - x: game.i18n.localize('DAGGERHEART.GENERAL.damage') - }); + x: game.i18n.localize('DAGGERHEART.GENERAL.damage') + }); 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 58be672b..acd104a7 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -54,10 +54,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return {}; } - get hasDescription() { - return Boolean(this.description); - } - /** * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. * @@ -148,8 +144,8 @@ 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; } /** @@ -227,13 +223,12 @@ 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; - config.effects = - await game.system.api.data.actions.actionsTypes.base.getActionRelevantEffects(this.actor, this.item); + config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(this.actor, this.item); if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return; @@ -305,17 +300,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 }; } @@ -338,45 +333,27 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } /** - * Get the all potentially applicable effects on the actor for the action's RollDialog + * Get the all potentially applicable effects on the actor * @param {DHActor} actor The actor performing the action * @param {DHItem|DhActor} effectParent The parent of the effect * @returns {DhActiveEffect[]} */ - static async getActionRelevantEffects(actor, effectParent) { + static async getEffects(actor, effectParent) { if (!actor) return []; - // Changes on weapon effects are not typically only applicable to show in the roll dialog for the weapon itself - // The exemptions to this rule are listed below - const weaponTransferredEffectKeys = [ - 'system.bonuses.roll.spellcast.bonus' - ]; - - const results = []; - const applicableEffects = await actor.allApplicableEffects({ noTransferArmor: true, noSelfArmor: true }); - for (const effect of [...applicableEffects].filter(e => !e.isSuppressed)) { - if (effect.parent.type === 'weapon') { - // Effects on weapons only ever apply for the weapon itself (with a few exceptions) - const restricted = - effect.parent.system.secondary - // Secondary applies only to other primary weapons - ? effectParent?.type !== 'weapon' || effectParent?.system.secondary - // Primary only applies to itself - : effectParent?.id !== effect.parent.id; - if (restricted) { - const sourceChanges = effect._source.system.changes; - const changes = sourceChanges.filter(x => weaponTransferredEffectKeys.includes(x.key)); - if (changes.length) { - results.push(effect.clone({ 'system.changes': changes })); - } - continue; + return Array.from(await actor.allApplicableEffects({ noTransferArmor: true, noSelfArmor: true })).filter( + effect => { + /* Effects on weapons only ever apply for the weapon itself */ + if (effect.parent.type === 'weapon') { + /* Unless they're secondary - then they apply only to other primary weapons */ + if (effect.parent.system.secondary) { + if (effectParent?.type !== 'weapon' || effectParent?.system.secondary) return false; + } else if (effectParent?.id !== effect.parent.id) return false; } - } - - results.push(effect); - } - return results; + return !effect.isSuppressed; + } + ); } /** @@ -451,15 +428,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel static migrateData(source) { if (source.damage?.parts && Array.isArray(source.damage.parts)) { - let hitPointsExists = source.damage.parts.some(x => x.applyTo === 'hitPoints'); source.damage.parts = source.damage.parts.reduce((acc, part) => { - if (!part.applyTo && hitPointsExists) return acc; - - if (!part.applyTo) { - hitPointsExists = true; - part.applyTo = 'hitPoints'; - } - acc[part.applyTo] = part; return acc; }, {}); @@ -490,7 +459,8 @@ export class ResourceUpdateMap extends Map { } else if (!existing?.clear) { this.set(resource.key, { ...existing, - value: existing.value + (resource.value ?? 0) + value: existing.value + (resource.value ?? 0), + total: existing.total + (resource.total ?? 0) }); } } diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index f7814181..51735543 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -1,23 +1,8 @@ -import { AltOutcome } from './altOutcome.mjs'; import DHBaseAction from './baseAction.mjs'; -const fields = foundry.data.fields; - export default class DHDamageAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'damage', 'target', 'effects']; - static defineSchema() { - return { - ...super.defineSchema(), - altOutcomes: new fields.SchemaField({ - successHope: new fields.EmbeddedDataField(AltOutcome, { nullable: true, initial: null }), - successFear: new fields.EmbeddedDataField(AltOutcome, { nullable: true, initial: null }), - failureHope: new fields.EmbeddedDataField(AltOutcome, { nullable: true, initial: null }), - failureFear: new fields.EmbeddedDataField(AltOutcome, { nullable: true, initial: null }) - }) - }; - } - /** * Return a display ready damage formula string * @returns Formula string diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 7e037f5b..0fbea122 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -90,13 +90,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/_types.d.ts b/module/data/actor/_types.d.ts deleted file mode 100644 index fec6e638..00000000 --- a/module/data/actor/_types.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import DhpActor from '../../documents/actor.mjs' -import DhCharacter from './character.mjs'; - -declare module './base.mjs' { - export default interface BaseDataActor { - parent: DhpActor; - } -} - -declare module './companion.mjs' { - export default interface DhCompanion { - partner: DhpActor; - } -} diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index ae17c128..d6d0dcdf 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -87,7 +87,6 @@ export default class DhpAdversary extends DhCreature { parts: { hitPoints: { type: ['physical'], - applyTo: 'hitPoints', value: { multiplier: 'flat' } diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 995708eb..9efbe7d7 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -1,13 +1,9 @@ +import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; +import DHItem from '../../documents/item.mjs'; import { createShallowProxy, getScrollTextData } from '../../helpers/utils.mjs'; const fields = foundry.data.fields; -/** - * @import DHItem from '../../documents/item.mjs'; - * @import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; - */ - -/** Function to generate resistance fields for damage types */ const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => new fields.SchemaField({ resistance: new fields.BooleanField({ @@ -100,8 +96,6 @@ export const commonActorRules = (extendedData = { damageReduction: {}, attack: { * @property {Boolean} isNPC - This data model represents a NPC? * @property {typeof DHBaseActorSettings} settingSheet - The sheet class used to render the settings UI for this actor type. */ - -/** Base actor type data model for all actors in Daggerheart */ export default class BaseDataActor extends foundry.abstract.TypeDataModel { /** @returns {ActorDataModelMetadata}*/ static get metadata() { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index b39c64aa..10d53c13 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -64,18 +64,7 @@ export default class DhCharacter extends DhCreature { core: new fields.BooleanField({ initial: false }) }) ), - gold: new GoldField({ - initial: () => { - const homebrew = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew); - const { coins, handfuls, bags, chests } = homebrew.currency; - return { - coins: coins.enabled ? coins.initialAmount : 0, - handfuls: handfuls.enabled ? handfuls.initialAmount : 0, - bags: bags.enabled ? bags.initialAmount : 0, - chests: chests.enabled ? chests.initialAmount : 0 - }; - } - }), + gold: new GoldField(), scars: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.scars' }), biography: new fields.SchemaField({ background: new fields.HTMLField(), @@ -107,7 +96,6 @@ export default class DhCharacter extends DhCreature { parts: { hitPoints: { type: ['physical'], - applyTo: 'hitPoints', value: { custom: { enabled: true, @@ -316,12 +304,7 @@ export default class DhCharacter extends DhCreature { label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' }) }) - }), - /** Accumulated armor score from all sources */ - armorScore: new fields.SchemaField({ - value: new fields.NumberField(), - max: new fields.NumberField() - }, { persisted: false }) + }) }; } @@ -332,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() { @@ -417,8 +400,8 @@ export default class DhCharacter extends DhCreature { get domainCards() { const domainCards = this.parent.items.filter(x => x.type === 'domainCard'); - const loadout = domainCards.filter(x => !x.system.inVault).sort((a, b) => a.sort - b.sort); - const vault = domainCards.filter(x => x.system.inVault).sort((a, b) => a.sort - b.sort); + const loadout = domainCards.filter(x => !x.system.inVault); + const vault = domainCards.filter(x => x.system.inVault); return { loadout: loadout, @@ -537,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 })) }); @@ -638,21 +621,21 @@ export default class DhCharacter extends DhCreature { }, ...(multiclassFeatures.length ? { - multiclassFeatures: { - title: `${game.i18n.localize('DAGGERHEART.GENERAL.multiclass')} - ${this.multiclass.value?.name}`, - type: 'multiclass', - values: multiclassFeatures - } - } + 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 - } - } + 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'), @@ -676,8 +659,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() { @@ -743,8 +726,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]; diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 2ca7fd5b..300bd698 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -102,7 +102,6 @@ export default class DhCompanion extends DhCreature { parts: { hitPoints: { type: ['physical'], - applyTo: 'hitPoints', value: { dice: 'd6', multiplier: 'prof' diff --git a/module/data/actor/tierAdjustment.mjs b/module/data/actor/tierAdjustment.mjs index bc6ad176..785eec2b 100644 --- a/module/data/actor/tierAdjustment.mjs +++ b/module/data/actor/tierAdjustment.mjs @@ -1,6 +1,5 @@ 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; @@ -61,8 +60,8 @@ export function getTierAdjustedAdversary(source, tier) { 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; + const { value: formula } = parseInlineParams(inner); + if (!formula || !type) return match; try { const newFormula = calculateAdjustedDamage(formula, 'action', damageMeta)?.formula; 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/countdowns.mjs b/module/data/countdowns.mjs index ffe4d26b..7d27197d 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -13,54 +13,6 @@ export default class DhCountdowns extends foundry.abstract.DataModel { }) }; } - - handleChange() { - const previousCountdowns = foundry.ui.countdowns.previousCountdownData; - const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => { - const previousCountdown = previousCountdowns[key]; - if (!previousCountdown || (previousCountdown.progress.current !== countdown.progress.current)) { - acc.push(key); - } - - return acc; - }, []); - - for (const countdownKey of changedCountdowns) - foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey); - } - - static migrateData(source) { - const migrateOldCountdowns = (data, type) => { - for (const key of Object.keys(data.countdowns)) { - const countdown = data.countdowns[key]; - source.countdowns[key] = { - ...countdown, - type: type, - ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { - acc[key] = - countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; - return acc; - }, {}), - progress: { - ...countdown.progress, - type: countdown.progress.type.value - } - }; - } - - source[type] = null; - }; - - if (source.narrative) { - migrateOldCountdowns(source.narrative, 'narrative'); - } - - if (source.encounter) { - migrateOldCountdowns(source.encounter, 'encounter'); - } - - return super.migrateData(source); - } } export class DhCountdown extends foundry.abstract.DataModel { @@ -69,8 +21,7 @@ export class DhCountdown extends foundry.abstract.DataModel { return { type: new fields.StringField({ required: true, - choices: CONFIG.DH.GENERAL.countdownTypes, - initial: CONFIG.DH.GENERAL.countdownTypes.encounter.id, + choices: CONFIG.DH.GENERAL.countdownBaseTypes, label: 'DAGGERHEART.GENERAL.type' }), name: new fields.StringField({ @@ -126,15 +77,15 @@ 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 { - type: type ?? CONFIG.DH.GENERAL.countdownTypes.encounter.id, + type: type ?? CONFIG.DH.GENERAL.countdownBaseTypes.narrative.id, name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.newCountdown'), img: 'icons/magic/time/hourglass-yellow-green.webp', ownership: ownership, @@ -151,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..1928af41 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -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 53e132c9..990f8ef1 100644 --- a/module/data/fields/action/countdownField.mjs +++ b/module/data/fields/action/countdownField.mjs @@ -8,8 +8,8 @@ export default class CountdownField extends fields.ArrayField { ...game.system.api.data.countdowns.DhCountdown.defineSchema(), type: new fields.StringField({ required: true, - choices: CONFIG.DH.GENERAL.countdownTypes, - initial: CONFIG.DH.GENERAL.countdownTypes.encounter.id, + choices: CONFIG.DH.GENERAL.countdownBaseTypes, + initial: CONFIG.DH.GENERAL.countdownBaseTypes.encounter.id, label: 'DAGGERHEART.GENERAL.type' }), name: new fields.StringField({ @@ -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 b7bd2332..e4d919fe 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -4,7 +4,7 @@ import IterableTypedObjectField from '../iterableTypedObjectField.mjs'; const fields = foundry.data.fields; -export const getDamageBaseFields = () => ({ +const getDamageBaseFields = () => ({ parts: new IterableTypedObjectField(DHDamageData), includeBase: new fields.BooleanField({ initial: false, @@ -23,6 +23,12 @@ export default class DamageField extends fields.SchemaField { constructor(options, context = {}) { const damageFields = { ...getDamageBaseFields(), + altOutcomes: new fields.SchemaField({ + successHope: new fields.EmbeddedDataField(AltDamageOutcome, { nullable: true, initial: null }), + successFear: new fields.EmbeddedDataField(AltDamageOutcome, { nullable: true, initial: null }), + failureHope: new fields.EmbeddedDataField(AltDamageOutcome, { nullable: true, initial: null }), + failureFear: new fields.EmbeddedDataField(AltDamageOutcome, { nullable: true, initial: null }) + }), groupAttack: new fields.StringField({ choices: CONFIG.DH.GENERAL.groupAttackRange, blank: true, @@ -333,3 +339,18 @@ export class DHDamageData extends DHResourceData { }; } } + +export class AltDamageOutcome extends foundry.abstract.DataModel { + static defineSchema() { + return { + ...getDamageBaseFields() + }; + } + + get data() { + return { + ...this.parent, + ...this + }; + } +} diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index e943d63d..d2ee1682 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -61,11 +61,11 @@ export default class EffectsField extends fields.ArrayField { 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 6845d2ba..a2275fa5 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,4 +1,4 @@ -import { getWorldActor, itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs'; +import { itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs'; import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; @@ -42,7 +42,7 @@ export default class DHSummonField extends fields.ArrayField { const count = roll.total; if (!roll.isDeterministic) rolls.push(roll); - const actor = await getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); + 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. */ summon.actor = actor.toObject(); @@ -62,6 +62,19 @@ export default class DHSummonField extends fields.ArrayField { DHSummonField.handleSummon(summonData, this.actor); } + /* Check for any available instances of the actor present in the world if we're missing artwork in the compendium. If none exists, create one. */ + static async getWorldActor(baseActor) { + const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`]; + if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) { + const worldActorCopy = game.actors.find(x => x.name === baseActor.name); + if (worldActorCopy) return worldActorCopy; + + return await game.system.api.documents.DhpActor.create(baseActor.toObject()); + } + + return baseActor; + } + static async handleSummon(summonData, actionActor) { await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation }); diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs index e165f5cc..a5f6f379 100644 --- a/module/data/fields/actorField.mjs +++ b/module/data/fields/actorField.mjs @@ -116,16 +116,13 @@ class ResourcesField extends fields.TypedObjectField { } class GoldField extends fields.SchemaField { - constructor(options = {}) { - super( - { - coins: new fields.NumberField({ initial: 0, integer: true }), - handfuls: new fields.NumberField({ initial: 1, integer: true }), - bags: new fields.NumberField({ initial: 0, integer: true }), - chests: new fields.NumberField({ initial: 0, integer: true }) - }, - options - ); + constructor() { + super({ + coins: new fields.NumberField({ initial: 0, integer: true }), + handfuls: new fields.NumberField({ initial: 1, integer: true }), + bags: new fields.NumberField({ initial: 0, integer: true }), + chests: new fields.NumberField({ initial: 0, integer: true }) + }); } } diff --git a/module/data/fields/formulaField.mjs b/module/data/fields/formulaField.mjs index c465bec3..85922f1f 100644 --- a/module/data/fields/formulaField.mjs +++ b/module/data/fields/formulaField.mjs @@ -39,7 +39,7 @@ export default class FormulaField extends foundry.data.fields.StringField { let roll = null; try { roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1')); - } catch { + } catch (_) { roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, 'd6')); } roll.evaluateSync({ strict: false }); diff --git a/module/data/item/_types.d.ts b/module/data/item/_types.d.ts deleted file mode 100644 index 6004e13b..00000000 --- a/module/data/item/_types.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import DHItem from '../../documents/item.mjs'; - -declare module './base.mjs' { - export default interface BaseDataItem { - parent: DHItem; - actor: DhpActor; - } -} \ No newline at end of file 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 15bb620d..21c56f9a 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -52,10 +52,6 @@ export default class DHArmor extends AttachableItem { ); } - get itemFeatures() { - return this.armorFeatures; - } - /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -173,4 +169,8 @@ export default class DHArmor extends AttachableItem { const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armor.max}`]; return labels; } + + get itemFeatures() { + return this.armorFeatures; + } } diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs index ba274cc7..ee9d9839 100644 --- a/module/data/item/beastform.mjs +++ b/module/data/item/beastform.mjs @@ -208,8 +208,8 @@ 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; diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index b9cda264..470a1e3c 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -70,14 +70,14 @@ export default class DHClass extends BaseDataItem { } async fetchSubclasses() { - const sourceUuid = this.parent.sourceUuid; - const subclasses = game.items.filter(x => x.type === 'subclass' && x.system.linkedClass === sourceUuid); + 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 (index.system?.linkedClass !== sourceUuid) continue; + if (!uuids.includes(index.system?.linkedClass)) continue; if (subclasses.find(x => x.uuid === index.uuid)) continue; packIds.push(index._id); } 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 cbf73bc3..55b078c2 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -70,7 +70,9 @@ export default class DHSubclass extends BaseDataItem { return false; } - const match = [multiclass, actorClass].find(c => c && c.sourceUuid === this.linkedClass); + 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 }); @@ -89,7 +91,7 @@ export default class DHSubclass extends BaseDataItem { ? game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.spellcastingTrait].label) : null; - // Preload all subclass features for acquisition from the cache + // 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)); diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 39c0fc8e..84e4de7f 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -113,10 +113,6 @@ export default class DHWeapon extends AttachableItem { ); } - get itemFeatures() { - return this.weaponFeatures; - } - /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -273,4 +269,8 @@ export default class DHWeapon extends AttachableItem { return labels; } + + get itemFeatures() { + return this.weaponFeatures; + } } 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/registeredTriggers.mjs b/module/data/registeredTriggers.mjs index 64e7f93e..ab86351c 100644 --- a/module/data/registeredTriggers.mjs +++ b/module/data/registeredTriggers.mjs @@ -149,15 +149,12 @@ export default class RegisteredTriggers extends Map { const result = await command(...args); if (result?.updates?.length) updates.push(...result.updates); - } catch { - const item = await foundry.utils.fromUuid(itemUuid); + } catch (_) { const triggerName = game.i18n.localize(triggerData.label); - console.error( + ui.notifications.error( game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerError', { trigger: triggerName, - actor: currentActor?.name, - item: item?.name ?? '', - itemUuid: item?.uuid ?? '' + actor: currentActor?.name }) ); } diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index a0761e20..31247458 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -2,7 +2,7 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; import { resetAndRerenderActors } from '../../helpers/utils.mjs'; import { ActionsField } from '../fields/actionField.mjs'; -const currencyField = (initial, label, icon, initialAmount = 0) => +const currencyField = (initial, label, icon) => new foundry.data.fields.SchemaField({ enabled: new foundry.data.fields.BooleanField({ required: true, initial: true }), label: new foundry.data.fields.StringField({ @@ -10,14 +10,7 @@ const currencyField = (initial, label, icon, initialAmount = 0) => initial, label }), - icon: new foundry.data.fields.StringField({ required: true, nullable: false, blank: true, initial: icon }), - initialAmount: new foundry.data.fields.NumberField({ - required: true, - integer: true, - min: 0, - initial: initialAmount, - label: 'DAGGERHEART.SETTINGS.Homebrew.currency.initialAmount' - }) + icon: new foundry.data.fields.StringField({ required: true, nullable: false, blank: true, initial: icon }) }); const restMoveField = () => @@ -115,8 +108,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { handfuls: currencyField( 'Handfuls', 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName', - 'fa-solid fa-coins', - 1 + 'fa-solid fa-coins' ), bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName', 'fa-solid fa-sack'), chests: currencyField( @@ -201,7 +193,6 @@ export default class DhHomebrew extends foundry.abstract.DataModel { for (const type of ['coins', 'handfuls', 'bags', 'chests']) { const initial = this.schema.fields.currency.fields[type].getInitialValue(); source.currency[type] = foundry.utils.mergeObject(initial, source.currency[type], { inplace: false }); - source.currency[type].initialAmount ??= 0; } return source; } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index e9c447e4..b1d3bd0b 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -100,19 +100,16 @@ export default class D20Roll extends DHRoll { this.options.roll.modifiers = this.applyBaseBonus(); - let actorExperiences = this.options.data.system?.experiences ?? {}; - if (this.options.roll.companionRoll) { - const companion = typeof this.options.data.companion === 'string' ? - foundry.utils.fromUuidSync(this.options.data.companion) : - this.options.data.companion; - actorExperiences = companion?.system?.experiences ?? {}; - } - for (const m of this.options.experiences?.filter(m => !!actorExperiences[m]) ?? []) { - this.options.roll.modifiers.push({ - label: actorExperiences[m].name, - value: actorExperiences[m].value - }); - } + const actorExperiences = this.options.roll.companionRoll + ? (this.options.data?.companion?.system.experiences ?? {}) + : (this.options.data.system?.experiences ?? {}); + this.options.experiences?.forEach(m => { + if (actorExperiences[m]) + this.options.roll.modifiers.push({ + label: actorExperiences[m].name, + value: actorExperiences[m].value + }); + }); this.addModifiers(); if (this.options.extraFormula) { diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 1c6ec829..02c4ab24 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -23,10 +23,6 @@ export default class DHRoll extends Roll { static DefaultDialog = D20RollDialog; - /** - * @param {Partial} config - * @returns {Promise} - */ static async build(config = {}, message = {}) { const roll = await this.buildConfigure(config, message); if (!roll) return; @@ -38,14 +34,9 @@ export default class DHRoll extends Roll { return config; } - /** - * @param {Partial} config - * @returns {Promise} - */ 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; @@ -112,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; } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index e2d967a3..1d2d556a 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -1,6 +1,6 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20Roll from './d20Roll.mjs'; -import { parseRallyDice, setDiceSoNiceForDualityRoll, shouldUseHopeFearAutomation } from '../helpers/utils.mjs'; +import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; import { getDiceSoNicePresets } from '../config/generalConfig.mjs'; import { updateResourcesForDualityReroll } from './helpers.mjs'; @@ -109,10 +109,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); } @@ -147,8 +147,8 @@ export default class DualityRoll extends D20Roll { const advDieClass = this.hasAdvantage ? game.system.api.dice.diceTypes.AdvantageDie : this.hasDisadvantage - ? game.system.api.dice.diceTypes.DisadvantageDie - : null; + ? game.system.api.dice.diceTypes.DisadvantageDie + : null; if (advDieClass) { const advDie = new advDieClass({ faces: this.advantageFaces, number: this.advantageNumber }); if (this.terms.length < 4) { @@ -312,9 +312,11 @@ export default class DualityRoll extends D20Roll { } static async addDualityResourceUpdates(config) { + const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + const hopeFearAutomation = automationSettings.hopeFear; if ( !config.source?.actor || - !shouldUseHopeFearAutomation() || + (game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) || config.actionType === 'reaction' || config.skips?.resources ) @@ -332,15 +334,15 @@ export default class DualityRoll extends D20Roll { const fear = (config.roll.result.duality === -1 ? 1 : 0) - (config.rerolledRoll.result.duality === -1 ? 1 : 0); - if (hope !== 0) updates.push({ key: 'hope', value: hope, enabled: true }); - if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, enabled: true }); - if (fear !== 0) updates.push({ key: 'fear', value: fear, enabled: true }); + 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 }); } } else { if (config.roll.isCritical || config.roll.result.duality === 1) - updates.push({ key: 'hope', value: 1, enabled: true }); - if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, enabled: true }); - if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, enabled: true }); + updates.push({ key: 'hope', value: 1, total: -1, enabled: true }); + if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true }); + if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true }); } if (updates.length) { diff --git a/module/dice/helpers.mjs b/module/dice/helpers.mjs index d03970d0..33519949 100644 --- a/module/dice/helpers.mjs +++ b/module/dice/helpers.mjs @@ -1,28 +1,17 @@ -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); - const { hopeFear, countdownAutomation } = - game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + 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 }); - if (game.user.isGM ? hopeFear.gm : hopeFear.players) { - const updates = []; - if (hope !== 0) updates.push({ key: 'hope', value: hope, enabled: true }); - if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, enabled: true }); - if (fear !== 0) updates.push({ key: 'fear', value: fear, enabled: true }) - - const resourceUpdates = new ResourceUpdateMap(actor); - resourceUpdates.addResources(updates); - resourceUpdates.updateResources(); - } - - if (countdownAutomation && fear !== 0) { - game.system.api.applications.ui.DhCountdowns.updateCountdowns({ - type: CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id, - undo: fear === 1 ? false : true - }); - } + const resourceUpdates = new ResourceUpdateMap(actor); + resourceUpdates.addResources(updates); + resourceUpdates.updateResources(); } diff --git a/module/documents/_types.d.ts b/module/documents/_types.d.ts deleted file mode 100644 index a94d6395..00000000 --- a/module/documents/_types.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import BaseDataActor from '../data/actor/base.mjs' -import DHItem from './item.mjs'; -import BaseDataItem from '../data/item/base.mjs'; -import DhActiveEffect from './activeEffect.mjs'; -import EmbeddedCollection from '@common/abstract/embedded-collection.mjs'; - -declare module './actor.mjs' { - export default interface DhpActor { - system: T; - items: EmbeddedCollection; - effects: EmbeddedCollection; - } -} - -declare module './item.mjs' { - export default interface DHItem { - parent: DhpActor; - actor: DhpActor; - system: T; - effects: EmbeddedCollection; - } -} \ No newline at end of file diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 4a9f3cc4..f9239a90 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -65,10 +65,6 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { ); } - get hasDescription() { - return Boolean(this.description); - } - /* -------------------------------------------- */ /* Event Handlers */ /* -------------------------------------------- */ @@ -179,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) { @@ -216,7 +212,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { try { const evl = new Function('sandbox', `with (sandbox) { return ${expression}}`); result = evl(Roll.MATH_PROXY); - } catch { + } catch (err) { return expression; } @@ -228,13 +224,12 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { * @returns {string[]} An array of localized tag strings. */ _getTags() { - const tags = []; - const originActor = DhActiveEffect.#resolveParentDocument(fromUuidSync(this.origin, { strict: false }), Actor); - if (originActor && originActor !== this.actor) { - tags.push(_loc('DAGGERHEART.EFFECTS.OriginTag', { name: originActor.name })); - } else if (!(this.parent instanceof Actor)) { - tags.push(`${_loc(this.parent.system.metadata.label)}: ${this.parent.name}`); - } + const tags = [ + `${game.i18n.localize(this.parent.system.metadata.label)}: ${this.parent.name}`, + game.i18n.localize( + this.isTemporary ? 'DAGGERHEART.EFFECTS.Duration.temporary' : 'DAGGERHEART.EFFECTS.Duration.passive' + ) + ]; for (const statusId of this.statuses) { const status = CONFIG.statusEffects.find(s => s.id === statusId); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 8ef64f65..fb10435f 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -531,7 +531,21 @@ export default class DhpActor extends Actor { } /** - * @param {Partial} config + * @param {object} config + * @param {Event} config.event + * @param {string} config.title + * @param {object} config.roll + * @param {number} config.roll.modifier + * @param {boolean} [config.roll.simple=false] + * @param {string} [config.roll.type] + * @param {number} [config.roll.difficulty] + * @param {boolean} [config.hasDamage] + * @param {boolean} [config.hasEffect] + * @param {object} [config.chatMessage] + * @param {string} config.chatMessage.template + * @param {boolean} [config.chatMessage.mute] + * @param {object} [config.targets] + * @param {object} [config.costs] */ async diceRoll(config) { config.source = { ...(config.source ?? {}), actor: this.uuid }; @@ -549,7 +563,7 @@ export default class DhpActor extends Actor { headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: abilityLabel }), - effects: await game.system.api.data.actions.actionsTypes.base.getActionRelevantEffects(this), + effects: await game.system.api.data.actions.actionsTypes.base.getEffects(this), roll: { trait: trait, type: 'trait' diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index b555dfca..893e6e5c 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -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 }); @@ -167,7 +167,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (this.system.action) { const actor = await foundry.utils.fromUuid(config.source.actor); const item = actor?.items.get(config.source.item) ?? null; - config.effects = await game.system.api.data.actions.actionsTypes.base.getActionRelevantEffects(actor, item); + config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(actor, item); await this.system.action.workflow.get('damage')?.execute(config, this._id, true); } } @@ -290,14 +290,14 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { behaviors: effects.length > 0 ? [ - { - name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), - type: 'applyActiveEffect', - system: { - effects: effects - } - } - ] + { + name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), + type: 'applyActiveEffect', + system: { + effects: effects + } + } + ] : [], displayMeasurements: true, locked: false, diff --git a/module/documents/combat.mjs b/module/documents/combat.mjs index e74127e9..20996b77 100644 --- a/module/documents/combat.mjs +++ b/module/documents/combat.mjs @@ -46,9 +46,7 @@ export default class DhpCombat extends Combat { for (let actor of actors) { await actor.createEmbeddedDocuments( 'ActiveEffect', - effects - .filter(x => x.effectTargetTypes.includes(actor.type)) - .map(x => foundry.utils.deepClone(x)) + effects.filter(x => x.effectTargetTypes.includes(actor.type)) ); } } else { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 14717538..f46e24e6 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -5,16 +5,6 @@ import ActionSelectionDialog from '../applications/dialogs/actionSelectionDialog * @extends {foundry.documents.Item} */ export default class DHItem extends foundry.documents.Item { - /** - * Returns the uuid of the original item this item was derived from, - * or its own uuid if its a compendium item or not derived from a source item. - * @returns {string} - */ - get sourceUuid() { - const isCompendium = this._id && this.pack && !this.isEmbedded; - return isCompendium ? this.uuid : this._stats.duplicateSource ?? this._stats.compendiumSource ?? this.uuid; - } - /** @inheritDoc */ prepareEmbeddedDocuments() { super.prepareEmbeddedDocuments(); @@ -89,10 +79,6 @@ export default class DHItem extends foundry.documents.Item { return !pack?.locked && this.isOwner && isValidType && hasActions; } - get hasDescription() { - return Boolean(this.system.description) || Boolean(this.system.itemFeatures?.length); - } - /** @inheritdoc */ static async createDialog(data = {}, createOptions = {}, options = {}) { const { folders, types, template, context = {}, ...dialogOptions } = options; @@ -112,8 +98,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/token.mjs b/module/documents/token.mjs index 8e91d4f0..30862724 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -324,7 +324,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 +333,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 +341,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 +357,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 +365,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/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/FateRollEnricher.mjs b/module/enrichers/FateRollEnricher.mjs index 8513ed94..c82bbcb2 100644 --- a/module/enrichers/FateRollEnricher.mjs +++ b/module/enrichers/FateRollEnricher.mjs @@ -49,7 +49,7 @@ export const renderFateButton = async event => { const fateTypeData = getFateTypeData(button.dataset?.fatetype); if (!fateTypeData) ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); - const { value: fateType } = fateTypeData; + const { value: fateType, label: fateTypeLabel } = fateTypeData; await enrichedFateRoll( { 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 cb79a76b..2f20175b 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', @@ -318,7 +318,7 @@ export function getDocFromElementSync(element) { const target = element.closest('[data-item-uuid]'); try { return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null; - } catch { + } catch (_) { return null; } } @@ -377,7 +377,7 @@ export const itemAbleRollParse = (value, actor, item) => { try { return Roll.replaceFormulaData(slicedValue, rollData); - } catch { + } catch (_) { return ''; } }; @@ -418,6 +418,40 @@ export function createScrollText(actor, data) { } } +export async function createEmbeddedItemWithEffects(actor, baseData, update) { + const data = baseData.uuid.startsWith('Compendium') ? await foundry.utils.fromUuid(baseData.uuid) : baseData; + const [doc] = await actor.createEmbeddedDocuments('Item', [ + { + ...(update ?? data), + ...baseData, + id: data.id, + uuid: data.uuid, + _uuid: data.uuid, + effects: data.effects?.map(effect => effect.toObject()), + _stats: { + ...data._stats, + compendiumSource: data.pack ? `Compendium.${data.pack}.Item.${data.id}` : null + } + } + ]); + + return doc; +} + +export async function createEmbeddedItemsWithEffects(actor, baseData) { + const effectData = []; + for (let d of baseData) { + const data = d.uuid.startsWith('Compendium') ? await foundry.utils.fromUuid(d.uuid) : d; + effectData.push({ + ...data, + id: data.id, + uuid: data.uuid, + effects: data.effects?.map(effect => effect.toObject()) + }); + } + await actor.createEmbeddedDocuments('Item', effectData); +} + export function shuffleArray(array) { let currentIndex = array.length; while (currentIndex != 0) { @@ -571,8 +605,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); } @@ -622,8 +656,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) { @@ -845,7 +879,6 @@ export async function fromUuids(uuids) { const packEmbeddedEntries = entries.filter( e => !(e.value instanceof Document) && - e.parsed && e.parsed.collection instanceof foundry.documents.collections.CompendiumCollection && e.parsed.embedded.length > 0 ); @@ -862,7 +895,7 @@ export async function fromUuids(uuids) { const pack = game.packs.get(packGroup[0].value.pack); if (!pack) continue; - const ids = packGroup.map(p => p.parsed?.id).filter(id => !!id); + const ids = packGroup.map(p => p.parsed.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; @@ -885,31 +918,3 @@ export async function triggerChatRollFx(rolls, options = { whisper: false, blind foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); } } - -export function shouldUseHopeFearAutomation(options = { gmAsPlayer: true }) { - const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); - return (!game.user.isGM || options.gmAsPlayer) ? hopeFear.players : hopeFear.gm; -} - -export async function getWorldActor(baseActor) { - if (baseActor.inCompendium) { - const worldActorCopy = game.actors.find(x => - x._stats.compendiumSource === baseActor.uuid && - (!x.prototypeToken.actorLink || x.name === baseActor.name) - ); - - if (worldActorCopy) - return worldActorCopy; - - const baseActorData = baseActor; - return await game.system.api.documents.DhpActor.create({ - ...baseActorData, - _stats: { - ...baseActorData._stats, - compendiumSource: baseActor.uuid - } - }); - } - - return baseActor; -} \ No newline at end of file diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index bf315358..1b08e0ad 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -50,7 +50,6 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', - 'systems/daggerheart/templates/ui/countdowns/parts/countdowns.hbs', 'systems/daggerheart/templates/scene/dh-config.hbs', 'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs', 'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs' diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 6971c34c..b4c446b2 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; +import { RefreshType, socketEvent } from './socket.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -23,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); @@ -152,26 +153,61 @@ export async function runMigrations() { await pack.configure({ locked: true }); } + /* Migrate old countdown structure */ + const countdownSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + const getCountdowns = (data, type) => { + return Object.keys(data.countdowns).reduce((acc, key) => { + const countdown = data.countdowns[key]; + acc[key] = { + ...countdown, + type: type, + ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { + acc[key] = + countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; + return acc; + }, {}), + progress: { + ...countdown.progress, + type: countdown.progress.type.value + } + }; + + return acc; + }, {}); + }; + + await countdownSettings.updateSource({ + countdowns: { + ...getCountdowns(countdownSettings.narrative, CONFIG.DH.GENERAL.countdownBaseTypes.narrative.id), + ...getCountdowns(countdownSettings.encounter, CONFIG.DH.GENERAL.countdownBaseTypes.encounter.id) + } + }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings); + + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); + lastMigrationVersion = '1.2.0'; } if (foundry.utils.isNewerVersion('1.2.7', lastMigrationVersion)) { - try { - const tagTeam = game.settings.get(CONFIG.DH.id, 'TagTeamRoll'); - const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); - const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { - if (!game.actors.some(actor => actor.id === id)) { - acc[id] = _del; - } - return acc; - }, {}); + const tagTeam = game.settings.get(CONFIG.DH.id, 'TagTeamRoll'); + const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); + const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { + if (!game.actors.some(actor => actor.id === id)) { + acc[id] = _del; + } + return acc; + }, {}); - await tagTeam.updateSource({ - initiator: initatorMissing ? null : tagTeam.initiator, - members: missingMembers - }); - await game.settings.set(CONFIG.DH.id, 'TagTeamRoll', tagTeam); - } catch { } + await tagTeam.updateSource({ + initiator: initatorMissing ? null : tagTeam.initiator, + members: missingMembers + }); + await game.settings.set(CONFIG.DH.id, 'TagTeamRoll', tagTeam); lastMigrationVersion = '1.2.7'; } @@ -267,8 +303,6 @@ export async function runMigrations() { /* Migrate existing effects modifying armor, creating new Armor Effects instead */ const migrateEffects = async entity => { - if (!entity?.effects) return; - for (const effect of entity.effects) { if (effect.system.changes.every(x => x.key !== 'system.armorScore')) continue; diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 1a985274..a66323c7 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -199,10 +199,7 @@ const registerNonConfigSettings = () => { game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, { scope: 'world', config: false, - type: DhCountdowns, - onChange: value => { - value.handleChange(); - } + type: DhCountdowns }); game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, { diff --git a/package-lock.json b/package-lock.json index dee096eb..28223032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,20 +13,18 @@ "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 +158,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", @@ -443,6 +420,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 +761,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 +795,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", @@ -2197,11 +1853,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" }, @@ -2586,6 +2241,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", @@ -2825,6 +2511,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 +2554,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", @@ -5542,6 +5217,34 @@ "node": ">= 0.8.0" } }, + "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/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6350,6 +6053,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", @@ -6397,23 +6116,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,19 +6147,6 @@ "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", @@ -6482,44 +6171,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", diff --git a/package.json b/package.json index 533d250a..73a7fe99 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "scripts": { "start": "node ./tools/run-start.mjs", + "start-test": "node ./resources/app/main.js --dataPath=./ && rollup -c --watch && gulp", "build": "npm run rollup && npm run gulp", "rollup": "rollup -c", "gulp": "gulp less", @@ -17,25 +18,24 @@ "pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs", "pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDB.mjs --build", "createSymlink": "node ./tools/create-symlink.mjs", + "setup:dev": "node ./tools/dev-setup.mjs", "lint": "eslint", "lint:fix": "eslint --fix", "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/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-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/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/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..f74ab8a0 --- /dev/null +++ b/styles/less/dialog/group-roll-dialog/_common.less @@ -0,0 +1,44 @@ +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; +} 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/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/index.less b/styles/less/dialog/index.less index 4ce4834e..e8f61318 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -1,20 +1,42 @@ -@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 './group-roll-dialog/index.less'; +@import './attribution/sheet.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 './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 './tag-team-dialog/initialization.less'; +@import './tag-team-dialog/sheet.less'; + +@import './group-roll-dialog/index.less'; + +@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/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/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 e79ed65c..d6f7ad29 100644 --- a/styles/less/dialog/tag-team-dialog/initialization.less +++ b/styles/less/dialog/tag-team-dialog/initialization.less @@ -88,21 +88,9 @@ grid-template-columns: 1fr 1fr; gap: 8px; - .inactive { + &.inactive { opacity: 0.4; } - - .initiator-cost-fields { - display: flex; - flex-direction: column; - align-items: flex-start; - - .initiator-cost-inputs { - display: grid; - grid-template-columns: auto 1fr; - align-items: center; - } - } } footer { diff --git a/styles/less/global/global.less b/styles/less/global/global.less index 19a9e519..c0e7f3fc 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -12,9 +12,6 @@ } .daggerheart.dh-style { - /** Not an actual scrollbar width (it can't be configured on all browsers) but actually a compensation value for scrollbar gutter purposes */ - --scrollbar-width: 10px; - * { scrollbar-width: thin; scrollbar-color: light-dark(@dark-blue, @golden) transparent; diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index fc73ba95..3a5a9321 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -43,19 +43,16 @@ } } - .item-main { - border-radius: 5px; - padding: 2px; - margin: -2px; - } - &:hover { .inventory-item-header .item-label .item-name .expanded-icon { margin-left: 10px; display: inline-block; } - .item-main { - background: light-dark(@dark-blue-40, @golden-40); + &:has(.inventory-item-content.extensible) { + .inventory-item-header, + .inventory-item-content { + background: light-dark(@dark-blue-40, @golden-40); + } } &:has(.inventory-item-content.extended) { .inventory-item-header .item-label .item-name .expanded-icon { @@ -63,6 +60,19 @@ } } } + + &:has(.inventory-item-content.extensible) { + .inventory-item-header { + border-radius: 5px 5px 0 0; + } + .inventory-item-content { + border-radius: 0 0 5px 5px; + } + } + + &:not(:has(.inventory-item-content.extensible)) .inventory-item-header { + border-radius: 5px; + } } .inventory-item-header, @@ -161,7 +171,7 @@ grid-template-rows: 1fr; padding-top: 4px; } - .inventory-description { + .invetory-description { overflow: hidden; h1 { diff --git a/styles/less/global/prose-mirror.less b/styles/less/global/prose-mirror.less index 27048ddf..e4b1249f 100644 --- a/styles/less/global/prose-mirror.less +++ b/styles/less/global/prose-mirror.less @@ -1,11 +1,8 @@ @import '../utils/colors.less'; @import '../utils/fonts.less'; -@import '../utils/mixin.less'; .application.daggerheart { prose-mirror { - --menu-padding: 4px 0px; - --menu-height: calc(var(--menu-button-height) + 8px); height: 100% !important; width: 100%; @@ -13,7 +10,6 @@ background-color: transparent; } .editor-content { - .with-scroll-shadows(); h1 { font-size: var(--font-size-32); } @@ -44,7 +40,7 @@ ul { list-style: disc; } - } + } // Fixes centering and makes it not render over scrollbar &:hover button.toggle:enabled { display: flex; 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/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/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/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/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 5eb5b43c..b3eb0469 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -37,10 +37,6 @@ color: @color-text-subtle; } - .window-header > .attribution-header-label { - margin-right: var(--spacer-4); - } - .tab.inventory { .gold-section { display: grid; @@ -54,37 +50,6 @@ } } - .tab.notes.active { - padding: 0; - margin: 0; - margin-top: -10px; // will be removed once tab-navigation bottom margin is removed on all actor sheets - scrollbar-gutter: unset; - - // Add padding around top level level prosemirrors used for note tabs - > prose-mirror { - @right-padding: calc(16px - var(--scrollbar-width)); - .editor-content { - scrollbar-gutter: stable; - padding-right: @right-padding; - } - &.inactive { - button.toggle { - top: 16px; - } - .editor-content { - padding: 16px @right-padding 4px 16px; - } - } - &.active { - padding: 8px 0 4px 16px; - } - } - - .artist-attribution { - padding-left: 16px; - } - } - .search-section { display: flex; gap: 10px; diff --git a/styles/less/sheets/actors/adversary/header.less b/styles/less/sheets/actors/adversary/header.less index 549a219d..1e5e4fa5 100644 --- a/styles/less/sheets/actors/adversary/header.less +++ b/styles/less/sheets/actors/adversary/header.less @@ -3,21 +3,16 @@ .application.sheet.daggerheart.actor.dh-style.adversary { .adversary-header-sheet { + padding: 0 15px; padding-top: var(--header-height); width: 100%; - > *:not(line-div, .tab-navigation) { - padding-left: 15px; - padding-right: 15px; - } - .name-row { display: flex; gap: 5px; align-items: center; justify-content: space-between; - padding-top: var(--spacer-4); - padding-bottom: var(--spacer-4); + padding: 8px 0; flex: 1; h1 { @@ -39,8 +34,8 @@ .tags { display: flex; - gap: 8px; - padding-bottom: var(--spacer-12); + gap: 10px; + padding-bottom: 8px; .tag { display: flex; @@ -49,7 +44,8 @@ justify-content: center; align-items: center; padding: 3px 5px; - font: var(--font-size-12) @font-body; + font-size: var(--font-size-12); + font: @font-body; background: light-dark(@dark-15, @beige-15); border: 1px solid light-dark(@dark, @beige); @@ -68,16 +64,8 @@ .adversary-info { display: flex; flex-direction: column; - gap: var(--spacer-8); - padding-top: var(--spacer-12); - padding-bottom: var(--spacer-12); - } - - .tab-navigation { - margin-top: 0; - button[data-action="openSettings"] { - margin-right: 12px; - } + gap: 12px; + padding: 16px 0; } } } 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/index.less b/styles/less/sheets/index.less index 451ae03a..4312f755 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -1,5 +1,22 @@ -@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/index.less'; +@import './actors/character/index.less'; +@import './actors/companion/index.less'; +@import './actors/environment/index.less'; +@import './actors/npc/index.less'; +@import './actors/party/index.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/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/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/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/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.less b/styles/less/ui/countdown/countdown.less index 96e01ffd..66a6c88a 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -18,10 +18,11 @@ border: 0; 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; @@ -35,7 +36,7 @@ transition: opacity var(--ui-fade-duration); } - &:not(.performance-low, .noblur) { + :not(.performance-low, .noblur) { backdrop-filter: blur(5px); } @@ -48,7 +49,8 @@ } &.icon-only { - width: 12rem; + width: 180px; + min-width: 180px; } .countdowns-header, @@ -64,31 +66,12 @@ padding: 0 0.5rem; overflow: hidden; font-size: var(--font-size-13); - .window-title { font-family: @font-body; - } - - - .header-type-toggles { flex: 1; - display: flex; - align-items: center; - gap: 4px; - - .header-type { - flex: 1; - border: 1px solid @beige; - border-radius: 12px; - padding: 2px 4px; - text-align: center; - background-color: @dark-blue; - color: @beige; - - &.inactive { - opacity: 0.4; - } - } + } + .header-control + .header-control { + margin-left: 8px; } } @@ -100,23 +83,9 @@ overflow: auto; max-height: 312px; - .countdown-category-container { - display: flex; - flex-direction: column; - gap: 8px; - - &.hidden { - display: none; - } - } - .countdown-container { display: flex; width: 100%; - border-radius: 6px; - background: linear-gradient(-45deg, transparent 30%, light-dark(@dark-blue-40, @golden-40) 35%, transparent 40%); - background-size: 300%; - background-position-x: 100%; &.icon-only { gap: 8px; @@ -137,11 +106,10 @@ display: flex; align-items: center; gap: 16px; - border-radius: 6px; img { - width: 2.75rem; - height: 2.75rem; + width: 44px; + height: 44px; border-radius: 6px; } @@ -159,7 +127,7 @@ .countdown-tool-controls { display: flex; align-items: center; - gap: var(--spacer-12); + gap: 16px; } .progress-tag { 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/game-pause/game-pause.less b/styles/less/ui/game-pause/game-pause.less deleted file mode 100644 index daf9a1e4..00000000 --- a/styles/less/ui/game-pause/game-pause.less +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../utils/mixin.less'; - -#pause.dh-style { - animation: none; - - img { - width: 125px; - height: 125px; - } - - figcaption { - --base-shadow: drop-shadow(2px 2px 2px black); - position: absolute; - margin-top: 24px; - animation: pause-pulse 3s ease-in-out infinite; - font-size: var(--font-size-30); - letter-spacing: 0.18em; - filter: var(--base-shadow); - } - - @keyframes pause-pulse { - 0% { - filter: var(--base-shadow) drop-shadow(0 0 5px @secondary-blue); - } - - 50% { - filter: var(--base-shadow) drop-shadow(0 0 5px @golden-secondary); - } - - 100% { - filter: var(--base-shadow) drop-shadow(0 0 5px @secondary-blue); - } - } -} \ No newline at end of file diff --git a/styles/less/ui/game-pause/index.less b/styles/less/ui/game-pause/index.less deleted file mode 100644 index ad28855c..00000000 --- a/styles/less/ui/game-pause/index.less +++ /dev/null @@ -1 +0,0 @@ -@import './game-pause.less'; \ No newline at end of file diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index af1614e4..31ea8955 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -1,12 +1,40 @@ -@import './chat/index.less'; -@import './combat-sidebar/index.less'; -@import './countdown/index.less'; -@import './effects-display/index.less'; -@import './game-pause/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/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/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/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 f2ab99e7..35c48480 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -201,23 +201,6 @@ } } - .currency-rows { - display: grid; - grid-template-columns: auto 1fr 1fr auto; - gap: 4px; - align-items: center; - width: 100%; - - .currency-header-label { - text-align: center; - font-size: var(--font-size-14); - } - - input[type='checkbox'] { - justify-self: center; - } - } - .settings-hint { width: 100%; display: flex; 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/fonts.less b/styles/less/utils/fonts.less index 72ee1a85..201ea356 100755 --- a/styles/less/utils/fonts.less +++ b/styles/less/utils/fonts.less @@ -1,22 +1,25 @@ -@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Cinzel+Decorative:wght@700&family=Montserrat:wght@400;600&display=swap'); -@import './mixin.less'; - -:root { - --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-size-30: 1.875rem; -} - -@font-title: ~"var(--dh-font-title, 'Cinzel Decorative'), serif"; -@font-subtitle: ~"var(--dh-font-subtitle, 'Cinzel'), serif"; -@font-body: ~"var(--dh-font-body, 'Montserrat'), sans-serif"; - -.dh-style { - .dh-typography(); -} +@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Cinzel+Decorative:wght@700&family=Montserrat:wght@400;600&display=swap'); +@import './mixin.less'; + +:root { + --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"; +@font-subtitle: ~"var(--dh-font-subtitle, 'Cinzel'), serif"; +@font-body: ~"var(--dh-font-body, 'Montserrat'), sans-serif"; + +.dh-style { + .dh-typography(); +} + +.dh-style fieldset { + .dh-typography(); +} 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 e2ef85ef..18b1f9a6 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -49,15 +49,16 @@ * Apply default typography styles. */ .dh-typography() { - --font-body: @font-body; - font-family: @font-body; - 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; + } } h2, @@ -85,17 +86,22 @@ font-weight: normal; } - // todo: determine if we can remove these or replace with font-family: inherit + p, + span, + textarea, + label, + select, multi-select .tags .tag, table, - .tagify__dropdown { + fieldset legend, + input[type='text'], + input[type='number'], + input[type='search'], + .tagify__dropdown, + li { font-family: @font-body; } - textarea { - font-family: inherit; - } - button span { font-weight: bold; } 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/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/system.json b/system.json index 0ecc2e75..588ceafe 100644 --- a/system.json +++ b/system.json @@ -2,15 +2,15 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.4.2", + "version": "2.3.1", "compatibility": { - "minimum": "14.364", - "verified": "14.364", + "minimum": "14.361", + "verified": "14.363", "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.4.2/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.1/system.zip", "authors": [ { "name": "WBHarry" diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 83715564..fef6591e 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -38,15 +38,15 @@
{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path basePath=../basePath}} + {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path outcomePath=../outcomePath}}
{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} - {{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path basePath=../basePath}} + {{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path outcomePath=../outcomePath}}
{{else}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path basePath=../basePath}} + {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path outcomePath=../outcomePath}} {{/if}} {{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}} @@ -72,21 +72,21 @@ {{#*inline "formula"}} {{#unless dmg.base}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path basePath ".parts." key "." target ".custom.enabled") classes="checkbox" localize=true}} + {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path outcomePath ".parts." key "." target ".custom.enabled") classes="checkbox" localize=true}} {{/unless}} {{#if source.custom.enabled}} - {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path basePath ".parts." key "." target ".custom.formula") localize=true}} + {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path outcomePath ".parts." key "." target ".custom.formula") localize=true}} {{else}}
{{#unless @root.isNPC}} - {{formField fields.multiplier value=source.multiplier name=(concat path basePath ".parts." key "." target ".multiplier") localize=true}} + {{formField fields.multiplier value=source.multiplier name=(concat path outcomePath ".parts." key "." target ".multiplier") localize=true}} {{/unless}} - {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path basePath ".parts." key "." target ".flatMultiplier") localize=true }}{{/if}} - {{formField fields.dice value=source.dice name=(concat path basePath ".parts." key "." target ".dice") localize=true}} - {{formField fields.bonus value=source.bonus name=(concat path basePath ".parts." key "." target ".bonus") localize=true}} + {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path outcomePath ".parts." key "." target ".flatMultiplier") localize=true }}{{/if}} + {{formField fields.dice value=source.dice name=(concat path outcomePath ".parts." key "." target ".dice") localize=true}} + {{formField fields.bonus value=source.bonus name=(concat path outcomePath ".parts." key "." target ".bonus") localize=true}}
{{/if}} {{#if @root.isNPC}} - + {{/if}} {{/inline}} \ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/initialization.hbs b/templates/dialogs/tagTeamDialog/initialization.hbs index 40491e1b..0b92e68e 100644 --- a/templates/dialogs/tagTeamDialog/initialization.hbs +++ b/templates/dialogs/tagTeamDialog/initialization.hbs @@ -26,16 +26,11 @@ -
+
-
- - -
+
+ +
diff --git a/templates/hud/tokenHUD.hbs b/templates/hud/tokenHUD.hbs index 91c82f13..cf43e15d 100644 --- a/templates/hud/tokenHUD.hbs +++ b/templates/hud/tokenHUD.hbs @@ -103,7 +103,7 @@ {{/if}} -
+
{{#each movementActions as |action|}} - + {{#if action.img}} {{ action.label }} diff --git a/templates/settings/homebrew-settings/settings.hbs b/templates/settings/homebrew-settings/settings.hbs index 1fd449a9..4b6e7d85 100644 --- a/templates/settings/homebrew-settings/settings.hbs +++ b/templates/settings/homebrew-settings/settings.hbs @@ -42,35 +42,29 @@
- {{localize "DAGGERHEART.SETTINGS.Homebrew.currency.title"}} + {{localize "DAGGERHEART.SETTINGS.Homebrew.currency.title"}}
{{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}}
-
- - {{localize "DAGGERHEART.SETTINGS.Homebrew.currency.denomination"}} - {{localize "DAGGERHEART.SETTINGS.Homebrew.currency.initialAmount"}} - - +
- {{formInput settingFields.schema.fields.currency.fields.coins.fields.label value=settingFields._source.currency.coins.label name="currency.coins.label"}} - + {{formGroup settingFields.schema.fields.currency.fields.coins.fields.label value=settingFields._source.currency.coins.label localize=true}} - +
+
- {{formInput settingFields.schema.fields.currency.fields.handfuls.fields.label value=settingFields._source.currency.handfuls.label name="currency.handfuls.label"}} - + {{formGroup settingFields.schema.fields.currency.fields.handfuls.fields.label value=settingFields._source.currency.handfuls.label localize=true}} - +
+
- {{formInput settingFields.schema.fields.currency.fields.bags.fields.label value=settingFields._source.currency.bags.label name="currency.bags.label"}} - + {{formGroup settingFields.schema.fields.currency.fields.bags.fields.label value=settingFields._source.currency.bags.label localize=true}} - +
+
- {{formInput settingFields.schema.fields.currency.fields.chests.fields.label value=settingFields._source.currency.chests.label name="currency.chests.label"}} - + {{formGroup settingFields.schema.fields.currency.fields.chests.fields.label value=settingFields._source.currency.chests.label localize=true}}
diff --git a/templates/sheets-settings/action-settings/outcomes.hbs b/templates/sheets-settings/action-settings/outcomes.hbs index fb15bd06..442333a4 100644 --- a/templates/sheets-settings/action-settings/outcomes.hbs +++ b/templates/sheets-settings/action-settings/outcomes.hbs @@ -21,15 +21,14 @@ data-group="outcomes" data-tab="{{tab.id}}" > - {{#if tab.fields.damage}} - {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' - outcome=tab.id - fields=tab.fields.damage.fields.parts.element.fields - source=tab.source.damage - baseFields=tab.fields.damage.fields - isDefaultDamage="true" - basePath=(tab.getBasePath "damage") - }} + {{#if ../fields.damage}} + {{#if (eq tab.id 'default')}} + {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=../fields.damage.fields.parts.element.fields source=../source.damage baseFields=../fields.damage.fields outcome=tab.id outcomePath="damage" isDefaultDamage="true" }} + {{else}} + {{#with (lookup ../fields.damage.fields.altOutcomes.fields tab.id) as |field|}} + {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=field.fields.parts.element.fields source=(lookup ../../source.damage.altOutcomes ../id) baseFields=field.fields outcome=../id outcomePath=(concat "damage.altOutcomes." tab.id) }} + {{/with }} + {{/if}} {{/if}} diff --git a/templates/sheets-settings/adversary-settings/features.hbs b/templates/sheets-settings/adversary-settings/features.hbs index 1cab8cfe..2f2f5f47 100644 --- a/templates/sheets-settings/adversary-settings/features.hbs +++ b/templates/sheets-settings/adversary-settings/features.hbs @@ -3,31 +3,27 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > - {{#each featureGroups as |group|}} -
- - {{group.label}} - - - - -
    - {{#each group.features as |feature|}} - {{> 'daggerheart.inventory-item' - item=feature - type='feature' - actorType=@root.document.type - hideTags=true - hideContextMenu=true - hideResources=true - showActions=false - hideTooltip=true - }} - {{/each}} -
-
- {{/each}} -
- {{localize "DAGGERHEART.GENERAL.dropFeaturesHere"}} -
+ +
+ {{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/environment-settings/features.hbs b/templates/sheets-settings/environment-settings/features.hbs index 1cab8cfe..579fe74e 100644 --- a/templates/sheets-settings/environment-settings/features.hbs +++ b/templates/sheets-settings/environment-settings/features.hbs @@ -3,31 +3,24 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > - {{#each featureGroups as |group|}} -
- - {{group.label}} - - - - -
    - {{#each group.features as |feature|}} - {{> 'daggerheart.inventory-item' - item=feature - type='feature' - actorType=@root.document.type - hideTags=true - hideContextMenu=true - hideResources=true - showActions=false - hideTooltip=true - }} - {{/each}} -
-
- {{/each}} -
- {{localize "DAGGERHEART.GENERAL.dropFeaturesHere"}} -
+ +
+ {{localize tabs.features.label}} +
    + {{#each @root.features as |feature|}} +
  • + +
    + {{feature.name}} +
    +
    + + +
    +
  • + {{/each}} +
+
\ No newline at end of file diff --git a/templates/sheets-settings/npc-settings/features.hbs b/templates/sheets-settings/npc-settings/features.hbs index 1cab8cfe..2f2f5f47 100644 --- a/templates/sheets-settings/npc-settings/features.hbs +++ b/templates/sheets-settings/npc-settings/features.hbs @@ -3,31 +3,27 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > - {{#each featureGroups as |group|}} -
- - {{group.label}} - - - - -
    - {{#each group.features as |feature|}} - {{> 'daggerheart.inventory-item' - item=feature - type='feature' - actorType=@root.document.type - hideTags=true - hideContextMenu=true - hideResources=true - showActions=false - hideTooltip=true - }} - {{/each}} -
-
- {{/each}} -
- {{localize "DAGGERHEART.GENERAL.dropFeaturesHere"}} -
+ +
+ {{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/actors/adversary/notes.hbs b/templates/sheets/actors/adversary/notes.hbs index d329d318..a5c3f706 100644 --- a/templates/sheets/actors/adversary/notes.hbs +++ b/templates/sheets/actors/adversary/notes.hbs @@ -1,9 +1,13 @@
- {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} +
+ {{localize tabs.notes.label}} + {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} +
+ {{#if (and showAttribution document.system.attribution.artist)}} {{/if}} diff --git a/templates/sheets/actors/environment/notes.hbs b/templates/sheets/actors/environment/notes.hbs index 1acf0e93..4f6b131e 100644 --- a/templates/sheets/actors/environment/notes.hbs +++ b/templates/sheets/actors/environment/notes.hbs @@ -3,7 +3,10 @@ data-tab='{{tabs.notes.id}}' data-group='{{tabs.notes.group}}' > - {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} +
+ {{localize tabs.notes.label}} + {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} +
{{#if (and showAttribution document.system.attribution.artist)}} diff --git a/templates/sheets/actors/npc/notes.hbs b/templates/sheets/actors/npc/notes.hbs index 7dd5432d..bc9ac3cf 100644 --- a/templates/sheets/actors/npc/notes.hbs +++ b/templates/sheets/actors/npc/notes.hbs @@ -1,7 +1,7 @@
{{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} diff --git a/templates/sheets/actors/party/notes.hbs b/templates/sheets/actors/party/notes.hbs index af21b29f..663a484a 100644 --- a/templates/sheets/actors/party/notes.hbs +++ b/templates/sheets/actors/party/notes.hbs @@ -1,7 +1,10 @@
- {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} +
+ {{localize tabs.notes.label}} + {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} +
\ No newline at end of file diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs index ca584e57..54e44e64 100644 --- a/templates/sheets/global/partials/domain-card-item.hbs +++ b/templates/sheets/global/partials/domain-card-item.hbs @@ -1,4 +1,4 @@ -
  • +
  • {{item.system.recallCost}} diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index 775690d4..523e9304 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -25,146 +25,146 @@ Parameters: data-type="{{type}}" data-item-type="{{item.type}}" data-item-uuid="{{item.uuid}}" data-no-compendium-edit="{{noCompendiumEdit}}" > -
    -
    - {{!-- Image --}} -
    - - {{#if (and item.usable (ne showActions false))}} - {{#if @root.isNPC}} - d20 - {{else}} - 2d12 +
    + {{!-- Image --}} +
    + + {{#if item.usable}} + {{#if @root.isNPC}} + d20 + {{else}} + 2d12 + {{/if}} + {{/if}} +
    + + {{!-- Name & Tags --}} +
    + {{!-- Item Name --}} + {{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}{{/unless}} + + {{!-- Tags Start --}} + {{#if (not ../hideTags)}} + {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item}} + {{#if (eq ../type 'feature')}} + {{#if (and system.featureForm (or (eq @root.document.type "adversary") (eq @root.document.type "environment")))}} +
    + {{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}} +
    {{/if}} {{/if}} -
    + {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} + {{/if}} - {{!-- Name & Tags --}} -
    - {{!-- Item Name --}} - {{localize item.name}} {{#unless (or noExtensible (not item.hasDescription))}}{{/unless}} + {{!--Tags End --}} +
    - {{!-- Tags Start --}} - {{#if (not hideTags)}} - {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item}} - {{#if (and (eq ../type 'feature') system.featureForm (ne @root.document.type "character"))}} -
    - {{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}} -
    - {{/if}} - {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} + {{!-- Simple Resource --}} + {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} + {{/if}} + {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}} +
    + +
    + {{/if}} + + {{!-- Controls --}} + {{#unless hideControls}} +
    + {{!-- Toggle/Equip buttons --}} + {{#if @root.editable}} + {{#if (and (eq actorType 'character') (eq type 'weapon'))}} + + + + {{/if}} + {{#if (and (eq actorType 'character') (eq type 'armor'))}} + + + + {{/if}} + {{#if (and (eq type 'domainCard'))}} + + + + {{/if}} + {{#if (and (and (eq type 'effect') (not (eq item.type 'beastform'))))}} + + + {{/if}} - - {{!--Tags End --}} -
    - - {{!-- Simple Resource --}} - {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}} -
    - -
    {{/if}} - {{!-- Controls --}} - {{#unless hideControls}} -
    - {{!-- Toggle/Equip buttons --}} - {{#if @root.editable}} - {{#if (and (eq actorType 'character') (eq type 'weapon'))}} - - - - {{/if}} - {{#if (and (eq actorType 'character') (eq type 'armor'))}} - - - - {{/if}} - {{#if (and (eq type 'domainCard'))}} - - - - {{/if}} - {{#if (and (and (eq type 'effect') (not (eq item.type 'beastform'))))}} - - - - {{/if}} - {{/if}} - - {{!-- Send to Chat --}} - {{#if (hasProperty item "toChat")}} - - - - {{/if}} - - {{!-- Document management buttons or context menu --}} - {{#if (and (not isActor) (not hideContextMenu))}} - - - - {{else if (and @root.editable (not hideModifyControls))}} - - - - {{#if (not isActor)}} - - - - {{else if (eq type 'adversary')}} - - - - {{/if}} - {{/if}} -
    - {{/unless}} -
    - {{#unless hideDescription}} -
    - {{!-- Description --}} -
    -
    - {{/unless}} -
    - {{!-- Dice Resource --}} - {{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{!-- Actions Buttons --}} - {{#if (and showActions item.system.actions.size)}} -
    - {{#each item.system.actions as | action |}} -
    - {{#if (and (eq action.type 'beastform') @root.beastformActive)}} - - {{else}} - + {{!-- Send to Chat --}} + {{#if (hasProperty item "toChat")}} + + + {{/if}} - {{#if action.uses.max}} -
    - + + {{!-- Document management buttons or context menu --}} + {{#if (and (not isActor) (not hideContextMenu))}} + + + + {{else if (and @root.editable (not hideModifyControls))}} + + + + {{#if (not isActor)}} + + + + {{else if (eq type 'adversary')}} + + + + {{/if}} {{/if}}
    - {{/each}} + {{/unless}} +
    +
    + {{!-- Description --}} + {{#unless hideDescription}} +
    + {{/unless}} +
    + {{!-- Dice Resource --}} + {{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} + {{/if}} + {{!-- Actions Buttons --}} + {{#if (and showActions item.system.actions.size)}} +
    + {{#each item.system.actions as | action |}} +
    + {{#if (and (eq action.type 'beastform') @root.beastformActive)}} + + {{else}} + + {{/if}} + {{#if action.uses.max}} +
    + + {{/if}}
    - {{/if}} -
  • + {{/each}} +
    + {{/if}} + \ No newline at end of file diff --git a/templates/ui/countdowns/countdown-edit.hbs b/templates/ui/countdown-edit.hbs similarity index 97% rename from templates/ui/countdowns/countdown-edit.hbs rename to templates/ui/countdown-edit.hbs index eebefe11..6b7d22d4 100644 --- a/templates/ui/countdowns/countdown-edit.hbs +++ b/templates/ui/countdown-edit.hbs @@ -78,9 +78,9 @@
    - +
    diff --git a/templates/ui/countdowns/parts/countdowns.hbs b/templates/ui/countdowns.hbs similarity index 65% rename from templates/ui/countdowns/parts/countdowns.hbs rename to templates/ui/countdowns.hbs index 4f0ee086..95067826 100644 --- a/templates/ui/countdowns/parts/countdowns.hbs +++ b/templates/ui/countdowns.hbs @@ -1,29 +1,37 @@ -{{#each countdowns as | category key |}} -
    - {{#each category as |countdown id|}} -
    +
    +
    + + {{localize "DAGGERHEART.UI.Countdowns.title"}} + + + + + + +
    +
    + {{#each countdowns as | countdown id |}} +
    - +
    - {{#unless @root.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 @root.iconOnly)}} + {{#if (not ../iconOnly)}} {{#if (and countdown.noPlayerAccess @root.isGM)}} {{/if}} {{#unless (eq countdown.progress.looping "noLooping")}} - + {{#if (eq countdown.progress.looping "increasing")}} @@ -41,4 +49,4 @@
    {{/each}}
    -{{/each}} \ No newline at end of file +
    \ No newline at end of file diff --git a/templates/ui/countdowns/countdowns-view.hbs b/templates/ui/countdowns/countdowns-view.hbs deleted file mode 100644 index 7727c4b6..00000000 --- a/templates/ui/countdowns/countdowns-view.hbs +++ /dev/null @@ -1,24 +0,0 @@ -
    -
    - - {{#unless iconOnly}} - {{localize "DAGGERHEART.UI.Countdowns.title"}} - {{/unless}} - - {{#if isGM}} - - - - {{/if}} - - - -
    -
    - {{> "systems/daggerheart/templates/ui/countdowns/parts/countdowns.hbs" }} -
    -
    \ No newline at end of file diff --git a/templates/ui/itemBrowser/filterContainer.hbs b/templates/ui/itemBrowser/filterContainer.hbs index 64784bf1..da8e8cfe 100644 --- a/templates/ui/itemBrowser/filterContainer.hbs +++ b/templates/ui/itemBrowser/filterContainer.hbs @@ -9,15 +9,13 @@
    {{else}} - {{#if field}} - {{#if filtered }} - {{formField field localize=true blank="" name=name choices=(@root.formatChoices this) valueAttr="value" dataset=(object key=key) value=value}} + {{#if filtered }} + {{formField field localize=true blank="" name=name choices=(@root.formatChoices this) valueAttr="value" dataset=(object key=key) value=value}} + {{else}} + {{#if field.label}} + {{formField field localize=true blank="" name=name dataset=(object key=key) value=value}} {{else}} - {{#if field.label}} - {{formField field localize=true blank="" name=name dataset=(object key=key) value=value}} - {{else}} - {{formField field localize=true blank="" name=name dataset=(object key=key) label=label value=value}} - {{/if}} + {{formField field localize=true blank="" name=name dataset=(object key=key) label=label value=value}} {{/if}} {{/if}} {{/if}} 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 a9a121b0..fd828c73 100644 --- a/tools/create-symlink.mjs +++ b/tools/create-symlink.mjs @@ -1,70 +1,45 @@ import fs from 'fs'; import path from 'path'; import readline from 'readline'; -import { isContainedPath, readEnvFile } from './util.mjs'; -const projectRoot = path.resolve(import.meta.dirname, '../') -const { foundryRoot, dataPath } = readEnvFile(); +const askQuestion = question => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); -async function createFoundrySymlink() { - // If foundry already exists, exit and inform the user. This operation can't complete correctly otherwise. - // If the folder is empty, its fine. It may have failed due to perms - const foundryDestPath = path.join(projectRoot, 'foundry'); - if (fs.existsSync(foundryDestPath) && fs.readdirSync(foundryDestPath).length) { - console.log('"foundry" folder already exists in this project'); - return; - } + return new Promise(resolve => + rl.question(question, answer => { + rl.close(); + resolve(answer); + }) + ); +}; - console.log('Creating "foundry" symlinks for types'); +const installPath = await askQuestion('Enter your Foundry install path: '); + +// Determine if it's an Electron install (nested structure) +const nested = fs.existsSync(path.join(installPath, 'resources', 'app')); +const fileRoot = nested ? path.join(installPath, 'resources', 'app') : installPath; + +try { + await fs.promises.mkdir('foundry'); +} catch (e) { + if (e.code !== 'EEXIST') throw e; +} + +// JavaScript files +for (const p of ['client', 'common', 'tsconfig.json']) { try { - await fs.promises.mkdir(foundryDestPath); - console.log('Root foundry folder created'); + await fs.promises.symlink(path.join(fileRoot, p), path.join('foundry', p)); } catch (e) { if (e.code !== 'EEXIST') throw e; } - - // JavaScript files - for (const p of ['client', 'common', 'tsconfig.json']) { - try { - await fs.promises.symlink(path.join(foundryRoot, p), path.join(foundryDestPath, p)); - console.log(`${p} folder created`); - } catch (e) { - if (e.code !== 'EEXIST') throw e; - } - } - - // Language files - try { - await fs.promises.symlink(path.join(foundryRoot, 'public', 'lang'), path.join(foundryDestPath, 'lang')); - console.log(`lang folder created`); - } catch (e) { - if (e.code !== 'EEXIST') throw e; - console.log(`lang folder already exists`); - } } -async function createDaggerheartSymlink() { - if (isContainedPath(dataPath, projectRoot)) { - console.log('The Daggerheart project repo is in foundry data, so a symlink won\'t be created'); - return; - } - - const destination = path.join(dataPath, 'Data', 'systems', 'daggerheart'); - if (fs.existsSync(destination)) { - console.log('A Daggerheart folder already exists in Foundry data'); - return; - } - - console.log('Creating Daggerheart symlink in the foundry systems folder') - try { - await fs.promises.symlink(projectRoot, destination); - console.log('Daggerheart system folder symlink created'); - } catch (e) { - if (e.code !== 'EEXIST') throw e; - console.log(`Daggerheart system folder already exists`); - } +// Language files +try { + await fs.promises.symlink(path.join(fileRoot, 'public', 'lang'), path.join('foundry', 'lang')); +} catch (e) { + if (e.code !== 'EEXIST') throw e; } - -await createFoundrySymlink(); -console.log(); // Add empty newline -await createDaggerheartSymlink(); diff --git a/tools/dev-setup.mjs b/tools/dev-setup.mjs new file mode 100644 index 00000000..f232f5a8 --- /dev/null +++ b/tools/dev-setup.mjs @@ -0,0 +1,18 @@ +#!/usr/bin/env node +import fs from 'fs'; + +const args = process.argv.slice(2); +const foundryPath = args.find(arg => arg.startsWith('--foundry-path='))?.split('=')[1]; +const dataPath = args.find(arg => arg.startsWith('--data-path='))?.split('=')[1]; + +if (!foundryPath || !dataPath) { + console.log('Usage: npm run setup:dev -- --foundry-path="/path/to/foundry/main.js" --data-path="/path/to/data"'); + process.exit(1); +} + +const envContent = `FOUNDRY_MAIN_PATH=${foundryPath} +FOUNDRY_DATA_PATH=${dataPath} +`; + +fs.writeFileSync('.env', envContent); +console.log(`✅ Development environment configured: ${foundryPath}, ${dataPath}`); 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 - } - } -]); diff --git a/tools/run-start.mjs b/tools/run-start.mjs index 379e174e..3f6b25cb 100644 --- a/tools/run-start.mjs +++ b/tools/run-start.mjs @@ -1,13 +1,24 @@ #!/usr/bin/env node import { spawn } from 'child_process'; -import { readEnvFile } from './util.mjs'; import fs from 'fs'; -// Load .env file params -const { foundryPath, dataPath } = readEnvFile(); +// Load .env file if it exists +if (fs.existsSync('.env')) { + const envFile = fs.readFileSync('.env', 'utf8'); + envFile.split('\n').forEach(line => { + const [key, value] = line.split('='); + if (key && value) { + process.env[key] = value; + } + }); +} + +// Set defaults if not in environment +const foundryPath = process.env.FOUNDRY_MAIN_PATH || '../../../../FoundryDev/main.js'; +const dataPath = process.env.FOUNDRY_DATA_PATH || '../../../'; // Run the original command with proper environment -const args = ['rollup -c --watch', 'gulp', `node "\"${foundryPath}\"" --dataPath="${dataPath}" --noupnp`]; +const args = ['rollup -c --watch', `node "\"${foundryPath}\"" --dataPath="${dataPath}" --noupnp`, 'gulp']; spawn('npx', ['concurrently', ...args.map(arg => `"${arg}"`)], { stdio: 'inherit', diff --git a/tools/util.mjs b/tools/util.mjs deleted file mode 100644 index ae3fedc5..00000000 --- a/tools/util.mjs +++ /dev/null @@ -1,40 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -export function readEnvFile() { - if (!fs.existsSync('.env')) { - console.error('No configured .env file. Copy .env.example to .env and configure it.'); - process.exit(); - } - - const envFile = fs.readFileSync('.env', 'utf8'); - envFile.split('\n').forEach(line => { - const [key, value] = line.split('='); - if (key && value) { - process.env[key] = value; - } - }); - - // Determine foundry path, handling if its an electron install (nested structure) - const foundryPath = path.normalize(process.env.FOUNDRY_MAIN_PATH).trimEnd(); - const dataPath = path.normalize(process.env.FOUNDRY_DATA_PATH).trimEnd(); - if (!foundryPath.endsWith('main.js')) { - console.error('Configured FOUNDRY_MAIN_PATH is invalid, it must end with main.js'); - process.exit(); - } - if (path.basename(dataPath) === 'Data' || !fs.existsSync(path.join(dataPath, 'Data'))) { - console.error('Configured FOUNDRY_DATA_PATH is incorrect. This must be a folder that contains "Data"'); - process.exit(); - } - - return { - foundryPath, - foundryRoot: path.dirname(foundryPath), - dataPath - }; -} - -export function isContainedPath(parent, child) { - const relative = path.relative(parent, child); - return relative && !relative.startsWith('..') && !path.isAbsolute(relative); -} \ No newline at end of file