From e229f62fd46e85aea2542a14f43339aba94e70b4 Mon Sep 17 00:00:00 2001 From: dev7355608 Date: Fri, 19 Jul 2024 13:02:03 +0200 Subject: [PATCH 01/18] Don't register Diagonal Movement Rule setting in V12 --- module/settings.mjs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/module/settings.mjs b/module/settings.mjs index 8e94a1b4f7..8466795ccb 100644 --- a/module/settings.mjs +++ b/module/settings.mjs @@ -59,20 +59,22 @@ export function registerSystemSettings() { }); // Diagonal Movement Rule - game.settings.register("dnd5e", "diagonalMovement", { - name: "SETTINGS.5eDiagN", - hint: "SETTINGS.5eDiagL", - scope: "world", - config: true, - default: "555", - type: String, - choices: { - 555: "SETTINGS.5eDiagPHB", - 5105: "SETTINGS.5eDiagDMG", - EUCL: "SETTINGS.5eDiagEuclidean" - }, - onChange: rule => canvas.grid.diagonalRule = rule - }); + if ( game.release.generation < 12 ) { + game.settings.register("dnd5e", "diagonalMovement", { + name: "SETTINGS.5eDiagN", + hint: "SETTINGS.5eDiagL", + scope: "world", + config: true, + default: "555", + type: String, + choices: { + 555: "SETTINGS.5eDiagPHB", + 5105: "SETTINGS.5eDiagDMG", + EUCL: "SETTINGS.5eDiagEuclidean" + }, + onChange: rule => canvas.grid.diagonalRule = rule + }); + } // Allow rotating square templates game.settings.register("dnd5e", "gridAlignedSquareTemplates", { From 1137191dad194ddc9c1d8ebdd520f7c49cfb344a Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:13:43 -0700 Subject: [PATCH 02/18] Update system.json for 3.3.1 release --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 870e36381c..b0f9cbbf1e 100644 --- a/system.json +++ b/system.json @@ -2,14 +2,14 @@ "id": "dnd5e", "title": "Dungeons & Dragons Fifth Edition", "description": "A system for playing the fifth edition of the world's most popular role-playing game in the Foundry Virtual Tabletop environment.", - "version": "3.3.0", + "version": "3.3.1", "compatibility": { "minimum": "11.315", "verified": "12" }, "url": "https://github.com/foundryvtt/dnd5e/", "manifest": "https://raw.githubusercontent.com/foundryvtt/dnd5e/master/system.json", - "download": "https://github.com/foundryvtt/dnd5e/releases/download/release-3.3.0/dnd5e-release-3.3.0.zip", + "download": "https://github.com/foundryvtt/dnd5e/releases/download/release-3.3.1/dnd5e-release-3.3.1.zip", "authors": [ { "name": "Atropos", From 70eee2ccd07e999811c1d376ac7c604d870bd116 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:22:24 -0700 Subject: [PATCH 03/18] [#3839] Fix bug when spell scroll creation is canceled --- module/applications/components/inventory.mjs | 5 ++++- module/documents/item.mjs | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/module/applications/components/inventory.mjs b/module/applications/components/inventory.mjs index d1d8c1117e..c9f96d9463 100644 --- a/module/applications/components/inventory.mjs +++ b/module/applications/components/inventory.mjs @@ -207,7 +207,10 @@ export default class InventoryElement extends HTMLElement { { name: "DND5E.Scroll.CreateScroll", icon: '', - callback: async li => Item5e.create(await Item5e.createScrollFromSpell(item), { parent: this.actor }), + callback: async li => { + const scroll = await Item5e.createScrollFromSpell(item); + if ( scroll ) Item5e.create(scroll, { parent: this.actor }); + }, condition: li => (item.type === "spell") && this.actor?.isOwner, group: "action" }, diff --git a/module/documents/item.mjs b/module/documents/item.mjs index a64da99ee5..61dcaecea3 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -2677,7 +2677,8 @@ export default class Item5e extends SystemDocumentMixin(Item) { icon: '', callback: async li => { const spell = await fromUuid(makeUuid(li)); - Item5e.create(await Item5e.createScrollFromSpell(spell)); + const scroll = await Item5e.createScrollFromSpell(spell); + if ( scroll ) Item5e.create(scroll); }, condition: li => { const item = fromUuidSync(makeUuid(li)); @@ -2700,7 +2701,8 @@ export default class Item5e extends SystemDocumentMixin(Item) { icon: '', callback: async li => { const spell = game.items.get(li.data("documentId")); - Item5e.create(await Item5e.createScrollFromSpell(spell)); + const scroll = await Item5e.createScrollFromSpell(spell); + if ( scroll ) Item5e.create(scroll); }, condition: li => { const item = game.items.get(li.data("documentId")); @@ -2770,7 +2772,7 @@ export default class Item5e extends SystemDocumentMixin(Item) { * @param {Item5e|object} spell The spell or item data to be made into a scroll. * @param {object} [options] Additional options that modify the created scroll. * @param {SpellScrollConfiguration} [config={}] Configuration options for scroll creation. - * @returns {Promise} The created scroll consumable item. + * @returns {Promise} The created scroll consumable item. */ static async createScrollFromSpell(spell, options={}, config={}) { config = foundry.utils.mergeObject({ From 9d0ef837afa28ac52aa80b07889f1183851501e0 Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Fri, 19 Jul 2024 17:32:08 +0100 Subject: [PATCH 04/18] [#3850] Fix rolling initiative on the NPC sheet also showing an image popup --- module/applications/actor/npc-sheet.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/module/applications/actor/npc-sheet.mjs b/module/applications/actor/npc-sheet.mjs index 34440315e9..e2d3659339 100644 --- a/module/applications/actor/npc-sheet.mjs +++ b/module/applications/actor/npc-sheet.mjs @@ -172,6 +172,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e { return this.actor.rollDeathSave({ event }); case "rollInitiative": + event.stopPropagation(); return this.actor.rollInitiativeDialog({ event }); } } From d29b79a5158aec8bb1d53e3681b1959348047d5a Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:33:53 -0700 Subject: [PATCH 05/18] [#3827, #3834] Fix SRD typos for Longsword & Aberrant Ground --- packs/_source/items/weapon/longsword.json | 8 ++++---- .../_source/items/weapon/vicious-longsword.json | 12 ++++++------ .../_source/monsterfeatures/aberrant-ground.json | 8 ++++---- .../monsters/aberration/gibbering-mouther.json | 16 ++++++++-------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packs/_source/items/weapon/longsword.json b/packs/_source/items/weapon/longsword.json index 3f7d1a376a..a62418852a 100644 --- a/packs/_source/items/weapon/longsword.json +++ b/packs/_source/items/weapon/longsword.json @@ -5,7 +5,7 @@ "img": "icons/weapons/swords/greatsword-crossguard-steel.webp", "system": { "description": { - "value": "

A lengthy stright blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", + "value": "

A lengthy straight blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", "chat": "" }, "source": { @@ -62,7 +62,7 @@ "amount": null, "scale": false }, - "ability": null, + "ability": "", "actionType": "mwak", "chatFlavor": "", "critical": { @@ -123,10 +123,10 @@ "flags": {}, "_stats": { "systemId": "dnd5e", - "systemVersion": "3.2.0", + "systemVersion": "3.3.1", "coreVersion": "11.315", "createdTime": 1661787233436, - "modifiedTime": 1715374570344, + "modifiedTime": 1721406272799, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!items!10ZP2Bu3vnCuYMIB" diff --git a/packs/_source/items/weapon/vicious-longsword.json b/packs/_source/items/weapon/vicious-longsword.json index 5deb1d10d9..e5a5d4f9c1 100644 --- a/packs/_source/items/weapon/vicious-longsword.json +++ b/packs/_source/items/weapon/vicious-longsword.json @@ -5,7 +5,7 @@ "img": "icons/weapons/swords/greatsword-crossguard-steel.webp", "system": { "description": { - "value": "

A lengthy stright blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

\n

When you roll a 20 on your attack roll with this magic weapon, your critical hit deals an extra 2d6 damage of the weapon's type.

", + "value": "

A lengthy straight blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

When you roll a 20 on your attack roll with this magic weapon, your critical hit deals an extra 2d6 damage of the weapon's type.

", "chat": "" }, "source": { @@ -62,7 +62,7 @@ "amount": null, "scale": false }, - "ability": null, + "ability": "", "actionType": "mwak", "chatFlavor": "", "critical": { @@ -94,8 +94,8 @@ "conditions": "" }, "properties": [ - "ver", - "mgc" + "mgc", + "ver" ], "proficient": null, "type": { @@ -124,10 +124,10 @@ "flags": {}, "_stats": { "systemId": "dnd5e", - "systemVersion": "3.2.0", + "systemVersion": "3.3.1", "coreVersion": "11.315", "createdTime": 1661787233875, - "modifiedTime": 1715374645990, + "modifiedTime": 1721406287697, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!items!pC3202gDTy8G5i4r" diff --git a/packs/_source/monsterfeatures/aberrant-ground.json b/packs/_source/monsterfeatures/aberrant-ground.json index 48e7e58d0a..d37c37e9a6 100644 --- a/packs/_source/monsterfeatures/aberrant-ground.json +++ b/packs/_source/monsterfeatures/aberrant-ground.json @@ -7,7 +7,7 @@ "type": "feat", "system": { "description": { - "value": "
\n

The ground in a 10-foot radius around the [[lookup @name lowercase]] is doughlike difficult terrain.

Each creature that starts its turn in that area must succeed on a DC 10 Strength saving throw or have its speed reduced to 0 until the start of its next turn.

\n
\n

The ground in a 10-foot radius around the [[lookup @name lowercase]] is doughlike difficult terrain. make a Strength saving throw.

", + "value": "

The ground in a 10-foot radius around the [[lookup @name lowercase]] is doughlike difficult terrain.

Each creature that starts its turn in that area must succeed on a DC 10 Strength saving throw or have its speed reduced to 0 until the start of its next turn.

The ground in a 10-foot radius around the [[lookup @name lowercase]] is doughlike difficult terrain. Make a Strength saving throw.

", "chat": "", "unidentified": "" }, @@ -84,10 +84,10 @@ "sort": 0, "_stats": { "systemId": "dnd5e", - "systemVersion": "2.1.0", - "coreVersion": "10.291", + "systemVersion": "3.3.1", + "coreVersion": "11.315", "createdTime": 1661787234532, - "modifiedTime": 1672335459239, + "modifiedTime": 1721406351309, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!items!KoBGbIkb2tMZv0ch" diff --git a/packs/_source/monsters/aberration/gibbering-mouther.json b/packs/_source/monsters/aberration/gibbering-mouther.json index 275e94b68d..16caf55e3a 100644 --- a/packs/_source/monsters/aberration/gibbering-mouther.json +++ b/packs/_source/monsters/aberration/gibbering-mouther.json @@ -470,7 +470,7 @@ "img": "icons/magic/air/wind-tornado-funnel-damage-blue.webp", "system": { "description": { - "value": "

The ground in a 10-foot radius around the mouther is doughlike difficult terrain.

Each creature that starts its turn in that area must succeed on a DC 10 Strength saving throw or have its speed reduced to 0 until the start of its next turn.

The ground in a 10-foot radius around the mouther is doughlike difficult terrain. make a Strength saving throw.

", + "value": "

The ground in a 10-foot radius around the mouther is doughlike difficult terrain.

Each creature that starts its turn in that area must succeed on a DC 10 Strength saving throw or have its speed reduced to 0 until the start of its next turn.

The ground in a 10-foot radius around the mouther is doughlike difficult terrain. Make a Strength saving throw.

", "chat": "" }, "source": { @@ -556,12 +556,12 @@ } }, "_stats": { - "systemId": null, - "systemVersion": null, - "coreVersion": null, + "systemId": "dnd5e", + "systemVersion": "3.3.1", + "coreVersion": "11.315", "createdTime": null, - "modifiedTime": null, - "lastModifiedBy": null + "modifiedTime": 1721406369726, + "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors.items!8pX2JhWUpTNNRBVx.yxlzyXTpsEf4QiI9" }, @@ -1003,10 +1003,10 @@ "flags": {}, "_stats": { "systemId": "dnd5e", - "systemVersion": "3.0.0", + "systemVersion": "3.3.1", "coreVersion": "11.315", "createdTime": 1661787232636, - "modifiedTime": 1704847043023, + "modifiedTime": 1721406369726, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors!8pX2JhWUpTNNRBVx" From faa33e31015f22c4d39eb5791c6ce7d434f27fe5 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:35:56 -0700 Subject: [PATCH 06/18] [#3827] Fix longsword typo in starter heroes --- .../_source/heroes/krusk-half-orc-paladin.json | 18 +++++++++--------- packs/_source/heroes/randal-human-fighter.json | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packs/_source/heroes/krusk-half-orc-paladin.json b/packs/_source/heroes/krusk-half-orc-paladin.json index 97865fc9b5..91d18586c0 100644 --- a/packs/_source/heroes/krusk-half-orc-paladin.json +++ b/packs/_source/heroes/krusk-half-orc-paladin.json @@ -490,7 +490,7 @@ "attenuation": 0.5 }, "texture": { - "src": null, + "src": "systems/dnd5e/tokens/heroes/PaladinHammer.webp", "tint": null, "scaleX": 1, "scaleY": 1, @@ -1310,7 +1310,7 @@ "type": "weapon", "system": { "description": { - "value": "

A lengthy stright blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", + "value": "

A lengthy straight blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", "chat": "" }, "source": { @@ -1423,12 +1423,12 @@ "default": 0 }, "_stats": { - "systemId": null, - "systemVersion": null, - "coreVersion": null, + "systemId": "dnd5e", + "systemVersion": "3.3.1", + "coreVersion": "11.315", "createdTime": null, - "modifiedTime": null, - "lastModifiedBy": null + "modifiedTime": 1721406909938, + "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors.items!Dh1AA6w104V17V6w.JVygpNYgNviQlQDL" }, @@ -3369,10 +3369,10 @@ "_id": "Dh1AA6w104V17V6w", "_stats": { "systemId": "dnd5e", - "systemVersion": "3.0.2", + "systemVersion": "3.3.1", "coreVersion": "11.315", "createdTime": 1661787232161, - "modifiedTime": 1707505942110, + "modifiedTime": 1721406909938, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors!Dh1AA6w104V17V6w" diff --git a/packs/_source/heroes/randal-human-fighter.json b/packs/_source/heroes/randal-human-fighter.json index 026d454fb0..4cce13768d 100644 --- a/packs/_source/heroes/randal-human-fighter.json +++ b/packs/_source/heroes/randal-human-fighter.json @@ -469,7 +469,7 @@ "attenuation": 0.5 }, "texture": { - "src": null, + "src": "systems/dnd5e/tokens/heroes/FighterShield.webp", "tint": null, "scaleX": 1, "scaleY": 1, @@ -1009,7 +1009,7 @@ "type": "weapon", "system": { "description": { - "value": "

A lengthy stright blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", + "value": "

A lengthy straight blade designed for slashing foes, the longsword is a highly versatile weapon that may also be wielded with two hands for more punishing strikes.

", "chat": "" }, "source": { @@ -1122,12 +1122,12 @@ "default": 0 }, "_stats": { - "systemId": null, - "systemVersion": null, - "coreVersion": null, + "systemId": "dnd5e", + "systemVersion": "3.3.1", + "coreVersion": "11.315", "createdTime": null, - "modifiedTime": null, - "lastModifiedBy": null + "modifiedTime": 1721406924911, + "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors.items!2Pdtnswo8Nj2nafY.3d2b3iqnC0t116JB" }, @@ -3724,10 +3724,10 @@ "_id": "2Pdtnswo8Nj2nafY", "_stats": { "systemId": "dnd5e", - "systemVersion": "3.0.0", + "systemVersion": "3.3.1", "coreVersion": "11.315", "createdTime": 1661787232150, - "modifiedTime": 1706819091699, + "modifiedTime": 1721406924911, "lastModifiedBy": "dnd5ebuilder0000" }, "_key": "!actors!2Pdtnswo8Nj2nafY" From cf17c8e80654058470fecb20865b1b9ce87c0ad6 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:38:04 -0700 Subject: [PATCH 07/18] [#3844] Fix NPC ideal being displayed in flaw textbox --- templates/actors/tabs/npc-biography.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/actors/tabs/npc-biography.hbs b/templates/actors/tabs/npc-biography.hbs index 4c3588fd90..00d99ea7c5 100644 --- a/templates/actors/tabs/npc-biography.hbs +++ b/templates/actors/tabs/npc-biography.hbs @@ -16,7 +16,7 @@ {{~> "dnd5e.biography-textbox" name="system.details.bond" value=system.details.bond label="DND5E.Bonds" icon="fas fa-link" ~}} - {{~> "dnd5e.biography-textbox" name="system.details.flaw" value=system.details.ideal label="DND5E.Flaws" + {{~> "dnd5e.biography-textbox" name="system.details.flaw" value=system.details.flaw label="DND5E.Flaws" icon="fas fa-heart-crack" ~}} From 94a7d1e120df9629eecf830211011cbedbf63740 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:40:05 -0700 Subject: [PATCH 08/18] [#3852] Fixed error on armor & shield getters on group actors --- module/documents/actor/actor.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/documents/actor/actor.mjs b/module/documents/actor/actor.mjs index 41a4ef2880..c9d0495fbb 100644 --- a/module/documents/actor/actor.mjs +++ b/module/documents/actor/actor.mjs @@ -103,7 +103,7 @@ export default class Actor5e extends SystemDocumentMixin(Actor) { * @type {Item5e|null} */ get armor() { - return this.system.attributes.ac.equippedArmor ?? null; + return this.system.attributes?.ac?.equippedArmor ?? null; } /* -------------------------------------------- */ @@ -113,7 +113,7 @@ export default class Actor5e extends SystemDocumentMixin(Actor) { * @type {Item5e|null} */ get shield() { - return this.system.attributes.ac.equippedShield ?? null; + return this.system.attributes?.ac?.equippedShield ?? null; } /* -------------------------------------------- */ From e0cb31c600ed0ae886d3b87909ec7c5d45be5ec9 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:52:14 -0700 Subject: [PATCH 09/18] [#3849] Remove Expand context menu option for concept items --- .../applications/actor/character-sheet-2.mjs | 11 ++++++++++- module/applications/actor/sheet-v2-mixin.mjs | 13 +++++++++++++ module/applications/components/inventory.mjs | 18 ++++++++++-------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/module/applications/actor/character-sheet-2.mjs b/module/applications/actor/character-sheet-2.mjs index a3b2ea5a6e..ae92595ca8 100644 --- a/module/applications/actor/character-sheet-2.mjs +++ b/module/applications/actor/character-sheet-2.mjs @@ -1,6 +1,6 @@ import CharacterData from "../../data/actor/character.mjs"; import * as Trait from "../../documents/actor/trait.mjs"; -import { simplifyBonus, staticID } from "../../utils.mjs"; +import { simplifyBonus } from "../../utils.mjs"; import CompendiumBrowser from "../compendium-browser.mjs"; import ContextMenu5e from "../context-menu.mjs"; import SheetConfig5e from "../sheet-config.mjs"; @@ -717,4 +717,13 @@ export default class ActorSheet5eCharacter2 extends ActorSheetV2Mixin(ActorSheet return { img, title, subtitle, modifier: total, passive, reference }; } } + + /* -------------------------------------------- */ + /* Helpers */ + /* -------------------------------------------- */ + + /** @inheritDoc */ + canExpand(item) { + return !["background", "race"].includes(item.type) && super.canExpand(item); + } } diff --git a/module/applications/actor/sheet-v2-mixin.mjs b/module/applications/actor/sheet-v2-mixin.mjs index f2050be7b8..8e24f109ab 100644 --- a/module/applications/actor/sheet-v2-mixin.mjs +++ b/module/applications/actor/sheet-v2-mixin.mjs @@ -726,5 +726,18 @@ export default function ActorSheetV2Mixin(Base) {
`; } + + /* -------------------------------------------- */ + /* Helpers */ + /* -------------------------------------------- */ + + /** + * Can an item be expanded on the sheet? + * @param {Item5e} item Item on the sheet. + * @returns {boolean} + */ + canExpand(item) { + return !["class", "subclass"].includes(item.type); + } }; } diff --git a/module/applications/components/inventory.mjs b/module/applications/components/inventory.mjs index c9f96d9463..b26245330c 100644 --- a/module/applications/components/inventory.mjs +++ b/module/applications/components/inventory.mjs @@ -285,14 +285,16 @@ export default class InventoryElement extends HTMLElement { }); } - // Toggle collapsed state. - const expanded = this._app._expanded.has(item.id); - options.push({ - name: expanded ? "Collapse" : "Expand", - icon: ``, - callback: () => element.closest("[data-item-id]")?.querySelector("[data-toggle-description]")?.click(), - group: "collapsible" - }); + // Toggle Collapsed State + if ( this._app.canExpand?.(item) ) { + const expanded = this._app._expanded.has(item.id); + options.push({ + name: expanded ? "Collapse" : "Expand", + icon: ``, + callback: () => element.closest("[data-item-id]")?.querySelector("[data-toggle-description]")?.click(), + group: "collapsible" + }); + } return options; } From 483109d259a615d0589ea0fcf0a3c57ab149b425 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 09:58:02 -0700 Subject: [PATCH 10/18] [#3846] Fix disabling damage fields by enchantments --- module/applications/item/item-sheet.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/item/item-sheet.mjs b/module/applications/item/item-sheet.mjs index 3c332128a7..520cf108ad 100644 --- a/module/applications/item/item-sheet.mjs +++ b/module/applications/item/item-sheet.mjs @@ -378,7 +378,7 @@ export default class ItemSheet5e extends ItemSheet { } if ( ("damage" in this.item.system) && foundry.utils.getProperty(this.item.overrides, "system.damage.parts") ) { overrides.push("damage-control"); - Array.fromRange(2).forEach(index => overrides.push( + Array.fromRange(this.item.system.damage.parts.length).forEach(index => overrides.push( `system.damage.parts.${index}.0`, `system.damage.parts.${index}.1` )); } From 2312dd649d584b2a3b8bf66970ce7130ff6c8192 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 10:14:46 -0700 Subject: [PATCH 11/18] [#3842] Fix tabs not filtering properly when changing compendium browser mode --- module/applications/compendium-browser.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/module/applications/compendium-browser.mjs b/module/applications/compendium-browser.mjs index 5453b5a256..b5f332ba42 100644 --- a/module/applications/compendium-browser.mjs +++ b/module/applications/compendium-browser.mjs @@ -776,7 +776,7 @@ export default class CompendiumBrowser extends HandlebarsApplicationMixin(Applic try { const { type } = foundry.utils.parseUuid(uuid); event.dataTransfer.setData("text/plain", JSON.stringify({ type, uuid })); - } catch (e) { + } catch(e) { console.error(e); } } @@ -966,8 +966,11 @@ export default class CompendiumBrowser extends HandlebarsApplicationMixin(Applic static #onToggleMode(event, target) { // TODO: Consider persisting this choice in a client setting. this._mode = target.checked ? this.constructor.MODES.ADVANCED : this.constructor.MODES.BASIC; - const types = target.checked ? [] : ["class"]; + const tabs = foundry.utils.deepClone(this.constructor.TABS.filter(t => !!t.advanced === target.checked)); + const activeTab = tabs.find(t => t.tab === this.tabGroups.primary) ?? tabs[0]; + const types = target.checked ? [] : (activeTab?.types ?? ["class"]); this._applyModeFilters(this._mode); + this._applyTabFilters(activeTab?.tab); this.render({ parts: ["results", "filters", "types", "tabs"], dnd5e: { browser: { types } } }); } From 6d2307594c2430cf61ead5704d2a947ee2de4b57 Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Fri, 19 Jul 2024 10:39:08 -0700 Subject: [PATCH 12/18] [#3815] Fix rider effects for enchantments on destroyed items --- module/documents/active-effect.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/module/documents/active-effect.mjs b/module/documents/active-effect.mjs index 4c615b2ddd..8c74535e85 100644 --- a/module/documents/active-effect.mjs +++ b/module/documents/active-effect.mjs @@ -403,9 +403,11 @@ export default class ActiveEffect5e extends ActiveEffect { /** * Create additional effects that are applied separately from an enchantment. + * @param {object} options Options passed to the effect creation. */ - async createRiderEnchantments() { - const origin = await fromUuid(this.origin); + async createRiderEnchantments(options) { + const origin = await fromUuid(this.origin)?? game.messages.get(options?.chatMessageOrigin)?.getAssociatedItem(); + if ( !origin ) return; // Create Effects const riderEffects = (this.getFlag("dnd5e", "enchantment.riders.effect") ?? []).map(id => { @@ -469,7 +471,7 @@ export default class ActiveEffect5e extends ActiveEffect { super._onCreate(data, options, userId); if ( userId === game.userId ) { if ( this.active && (this.parent instanceof Actor) ) await this.createRiderConditions(); - if ( this.isAppliedEnchantment ) await this.createRiderEnchantments(); + if ( this.isAppliedEnchantment ) await this.createRiderEnchantments(options); } if ( options.chatMessageOrigin ) { document.body.querySelectorAll(`[data-message-id="${options.chatMessageOrigin}"] enchantment-application`) From 653d9d11856d36f17ab9437b171596260b2ac40d Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Fri, 19 Jul 2024 19:34:34 +0100 Subject: [PATCH 13/18] [#3855] Show creature XP value on NPC sheet --- lang/en.json | 1 + less/v2/npc.less | 13 ++++++-- module/applications/actor/npc-sheet-2.mjs | 40 ++++++++++++++--------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/lang/en.json b/lang/en.json index 09160d4948..faf662c996 100644 --- a/lang/en.json +++ b/lang/en.json @@ -842,6 +842,7 @@ "DND5E.ExperiencePoints": "Experience Points", "DND5E.ExperiencePointsAbbr": "XP", "DND5E.ExperiencePointsCurrent": "Current XP", +"DND5E.ExperiencePointsFormat": "{value} XP", "DND5E.ExperiencePointsLabel": "Progress to next level", "DND5E.ExperiencePointsMax": "XP for Next Level", "DND5E.ExperiencePointsMin": "Minimum XP for This Level", diff --git a/less/v2/npc.less b/less/v2/npc.less index abbd05367c..552883b423 100644 --- a/less/v2/npc.less +++ b/less/v2/npc.less @@ -11,7 +11,7 @@ &.minimized { box-shadow: 0 0 20px var(--color-shadow-dark); - .window-header .source-book { display: none; } + .window-header .header-elements { display: none; } } /* ---------------------------------- */ @@ -20,7 +20,7 @@ > header slide-toggle { --slide-toggle-track-color-unchecked: var(--dnd5e-background-25); } - .window-header .source-book { + .window-header .header-elements { flex: none; color: var(--color-text-dark-primary); font-family: var(--dnd5e-font-roboto-condensed); @@ -30,6 +30,14 @@ margin-top: 7px; gap: 4px; + > div:has(> span:not(:empty)) + div::before { content: " • "; } + + .source-book { + display: flex; + align-items: center; + gap: 4px; + } + a { margin: 0; display: grid; @@ -40,6 +48,7 @@ i { font-size: var(--font-size-11); color: var(--color-text-light-6); + margin: 0; } } diff --git a/module/applications/actor/npc-sheet-2.mjs b/module/applications/actor/npc-sheet-2.mjs index a73911289d..3a499891ef 100644 --- a/module/applications/actor/npc-sheet-2.mjs +++ b/module/applications/actor/npc-sheet-2.mjs @@ -47,17 +47,20 @@ export default class ActorSheet5eNPC2 extends ActorSheetV2Mixin(ActorSheet5eNPC) /** @inheritDoc */ async _renderOuter() { const html = await super._renderOuter(); - const source = document.createElement("div"); - source.classList.add("source-book"); - source.innerHTML = ` - - - - + const elements = document.createElement("div"); + elements.classList.add("header-elements"); + elements.innerHTML = ` +
+ + + + +
+
`; - html[0].querySelector(".window-title")?.insertAdjacentElement("afterend", source); - source.querySelector(".config-button").addEventListener("click", this._onConfigMenu.bind(this)); + html[0].querySelector(".window-title")?.insertAdjacentElement("afterend", elements); + elements.querySelector(".config-button").addEventListener("click", this._onConfigMenu.bind(this)); return html; } @@ -66,11 +69,18 @@ export default class ActorSheet5eNPC2 extends ActorSheetV2Mixin(ActorSheet5eNPC) /** @inheritDoc */ async _render(force=false, options={}) { await super._render(force, options); - const [source] = this.element.find(".source-book"); - if ( !source ) return; - const sourceEditable = this.isEditable && (this._mode === this.constructor.MODES.EDIT); - source.querySelector(".config-button")?.toggleAttribute("hidden", !sourceEditable); - source.querySelector(":scope > span").innerText = this.actor.system.details.source.label; + const [elements] = this.element.find(".header-elements"); + if ( !elements ) return; + const { details } = this.actor.system; + const editable = this.isEditable && (this._mode === this.constructor.MODES.EDIT); + const sourceLabel = details.source.label; + elements.querySelector(".config-button")?.toggleAttribute("hidden", !editable); + elements.querySelector(".source-book > span").innerText = editable + ? (sourceLabel || game.i18n.localize("DND5E.Source")) + : sourceLabel; + elements.querySelector(".cr-xp").innerText = game.i18n.format("DND5E.ExperiencePointsFormat", { + value: new Intl.NumberFormat(game.i18n.lang).format(details.xp.value) + }); } /* -------------------------------------------- */ From d6fd0238eae02916f72d9272fa6cf8f47645469e Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Fri, 19 Jul 2024 19:49:33 +0100 Subject: [PATCH 14/18] [#3854] Add temporary HP to hit points config. --- lang/en.json | 1 + templates/apps/hit-points-config.hbs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lang/en.json b/lang/en.json index faf662c996..39f808e17c 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1029,6 +1029,7 @@ "DND5E.HitPointsMin": "Minimum Hit Points", "DND5E.HitPointsOverride": "Maximum Hit Points Override", "DND5E.HitPointsTemp": "Temporary Hit Points", +"DND5E.HitPointsTempShort": "Temporary HP", "DND5E.HitPointsTempMax": "Temporary Maximum Hit Points", "DND5E.HitPointsTempMaxShort": "Temp Max HP", "DND5E.HP": "HP", diff --git a/templates/apps/hit-points-config.hbs b/templates/apps/hit-points-config.hbs index 64a55b4536..d5203b2768 100644 --- a/templates/apps/hit-points-config.hbs +++ b/templates/apps/hit-points-config.hbs @@ -33,6 +33,13 @@
+
+ +
+ {{ numberInput source.temp name="hp.temp" min=0 step=1 }} +
+
+
From 270904ccca1dbb983cc709718a286b3ee3a4225a Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Fri, 19 Jul 2024 20:02:20 +0100 Subject: [PATCH 15/18] [#3606] Fix issue when re-rendering NPC sheet with filters active --- module/applications/actor/npc-sheet.mjs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/module/applications/actor/npc-sheet.mjs b/module/applications/actor/npc-sheet.mjs index e2d3659339..d99596a975 100644 --- a/module/applications/actor/npc-sheet.mjs +++ b/module/applications/actor/npc-sheet.mjs @@ -55,7 +55,7 @@ export default class ActorSheet5eNPC extends ActorSheet5e { // Start by classifying items into groups for rendering const maxLevelDelta = CONFIG.DND5E.maxLevel - (this.actor.system.details.level ?? 0); - let [spells, other] = context.items.reduce((arr, item) => { + const [spells, other] = context.items.reduce((arr, item) => { const {quantity, uses, target} = item.system; const ctx = context.itemContext[item.id] ??= {}; ctx.isStack = Number.isNumeric(quantity) && (quantity !== 1); @@ -79,10 +79,6 @@ export default class ActorSheet5eNPC extends ActorSheet5e { return arr; }, [[], []]); - // Apply item filters - spells = this._filterItems(spells, this._filters.spellbook.properties); - other = this._filterItems(other, this._filters.features.properties); - // Organize Spellbook const spellbook = this._prepareSpellbook(context, spells); From 8e58f9c4e02f75333d526ae80ca8234fc1685a48 Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Mon, 22 Jul 2024 13:38:47 +0100 Subject: [PATCH 16/18] [#3863] Fix broken portrait images rendering at 0x0 in NPC sheets --- less/v2/npc.less | 1 + 1 file changed, 1 insertion(+) diff --git a/less/v2/npc.less b/less/v2/npc.less index 552883b423..a3534eb88b 100644 --- a/less/v2/npc.less +++ b/less/v2/npc.less @@ -89,6 +89,7 @@ object-fit: cover; object-position: top; margin: 0; + display: block; } &.token > img { From 2f14e7fc8429d2ca9af89cd6ba460d913d81ed75 Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Mon, 22 Jul 2024 16:30:19 +0100 Subject: [PATCH 17/18] [#3725] Add preparation warnings to v2 sheets. --- less/v2/actors.less | 36 +++++++++++++ less/v2/apps.less | 2 +- module/applications/actor/sheet-v2-mixin.mjs | 50 ++++++++++++++++++- module/utils.mjs | 1 + templates/actors/character-sheet-2.hbs | 4 ++ templates/actors/npc-sheet-2.hbs | 3 ++ .../actors/parts/actor-warnings-dialog.hbs | 11 ++++ 7 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 templates/actors/parts/actor-warnings-dialog.hbs diff --git a/less/v2/actors.less b/less/v2/actors.less index ee9c7ef7f1..8adfd83e9b 100644 --- a/less/v2/actors.less +++ b/less/v2/actors.less @@ -352,6 +352,42 @@ form:is(.tab-inventory, .tab-features, .tab-spells, .tab-effects) .create-child { display: block; } + /* ---------------------------------- */ + /* Warnings */ + /* ---------------------------------- */ + + dialog.warnings:is(#tooltip, .locked-tooltip) { /* :is used here to override specificity of base tooltip styles */ + position: fixed; + width: 300px; + max-width: unset; + max-height: unset; + margin: 0; + outline: none; + padding: 4px 8px; + font-family: var(--dnd5e-font-roboto-condensed); + + li { + padding: 6px 8px; + border-bottom: 1px dotted var(--dnd5e-color-gold); + &:last-child { border: none; } + + a:hover { + text-shadow: none; + text-decoration: underline dotted; + } + + &.warning::before, &.error::before { + font-family: var(--font-awesome); + font-weight: bold; + color: var(--color-text-dark-5); + margin-right: 2px; + } + + &.warning::before { content: "\f071"; } + &.error::before { content: "\f06a"; } + } + } + /* ---------------------------------- */ /* Minimized */ /* ---------------------------------- */ diff --git a/less/v2/apps.less b/less/v2/apps.less index 7dc39de7fd..9167a23711 100644 --- a/less/v2/apps.less +++ b/less/v2/apps.less @@ -19,9 +19,9 @@ height: 18px; aspect-ratio: 1; line-height: unset; - display: grid; place-content: center; + &:not([hidden]) { display: grid; } > i { margin: 0 } } diff --git a/module/applications/actor/sheet-v2-mixin.mjs b/module/applications/actor/sheet-v2-mixin.mjs index 8e24f109ab..450103a297 100644 --- a/module/applications/actor/sheet-v2-mixin.mjs +++ b/module/applications/actor/sheet-v2-mixin.mjs @@ -88,14 +88,24 @@ export default function ActorSheetV2Mixin(Base) { header.insertAdjacentElement("afterbegin", toggle); } + // Document UUID link. + const firstButton = header.querySelector(".header-button"); const idLink = header.querySelector(".document-id-link"); if ( idLink ) { - const firstButton = header.querySelector(".header-button"); firstButton?.insertAdjacentElement("beforebegin", idLink); idLink.classList.add("header-button"); idLink.dataset.tooltipDirection = "DOWN"; } + // Preparation warnings. + const warnings = document.createElement("a"); + warnings.classList.add("header-button", "preparation-warnings"); + warnings.dataset.tooltip = "Warnings"; + warnings.setAttribute("aria-label", game.i18n.localize("Warnings")); + warnings.innerHTML = ''; + warnings.addEventListener("click", this._onOpenWarnings.bind(this)); + firstButton?.insertAdjacentElement("beforebegin", warnings); + // Render tabs. const nav = document.createElement("nav"); nav.classList.add("tabs", "tabs-right"); @@ -123,6 +133,15 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** @inheritDoc */ + async _render(force=false, options={}) { + await super._render(force, options); + const [warnings] = this.element.find(".header-button.preparation-warnings"); + warnings?.toggleAttribute("hidden", !this.actor._preparationWarnings?.length); + } + + /* -------------------------------------------- */ + /** @inheritDoc */ async getData(options) { this._concentration = this.actor.concentration; // Cache concentration so it's not called for every item. @@ -430,6 +449,7 @@ export default function ActorSheetV2Mixin(Base) { html.find(".sidebar-collapser").on("click", this._onToggleSidebar.bind(this)); html.find("[data-item-id][data-action]").on("click", this._onItemAction.bind(this)); html.find("[data-toggle-description]").on("click", this._onToggleDescription.bind(this)); + html.find("dialog.warnings").on("click", this._onCloseWarnings.bind(this)); this.form.querySelectorAll(".item-tooltip").forEach(this._applyItemTooltips.bind(this)); this.form.querySelectorAll("[data-reference-tooltip]").forEach(this._applyReferenceTooltips.bind(this)); @@ -488,6 +508,18 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** + * Handle closing the warnings dialog. + * @param {PointerEvent} event The triggering event. + * @protected + */ + _onCloseWarnings(event) { + if ( event.target instanceof HTMLDialogElement ) event.target.close(); + if ( event.target instanceof HTMLAnchorElement ) event.target.closest("dialog")?.close(); + } + + /* -------------------------------------------- */ + /** * Handle creating a new embedded child. * @returns {ActiveEffect5e|Item5e|void} @@ -548,6 +580,22 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** + * Handle opening the warnings dialog. + * @param {PointerEvent} event The triggering event. + * @protected + */ + _onOpenWarnings(event) { + event.stopImmediatePropagation(); + const { top, left, height } = event.target.getBoundingClientRect(); + const { clientWidth } = document.documentElement; + const dialog = this.form.querySelector("dialog.warnings"); + Object.assign(dialog.style, { top: `${top + height}px`, left: `${Math.min(left - 16, clientWidth - 300)}px` }); + dialog.showModal(); + } + + /* -------------------------------------------- */ + /** * Toggle editing hit points. * @param {PointerEvent} event The triggering event. diff --git a/module/utils.mjs b/module/utils.mjs index 40788e594b..9111c7613b 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -327,6 +327,7 @@ export async function preloadHandlebarsTemplates() { "systems/dnd5e/templates/actors/parts/actor-inventory.hbs", "systems/dnd5e/templates/actors/parts/actor-spellbook.hbs", "systems/dnd5e/templates/actors/parts/actor-warnings.hbs", + "systems/dnd5e/templates/actors/parts/actor-warnings-dialog.hbs", "systems/dnd5e/templates/actors/parts/biography-textbox.hbs", "systems/dnd5e/templates/actors/tabs/character-biography.hbs", "systems/dnd5e/templates/actors/tabs/character-details.hbs", diff --git a/templates/actors/character-sheet-2.hbs b/templates/actors/character-sheet-2.hbs index c2bec0fc6c..0b7f007229 100644 --- a/templates/actors/character-sheet-2.hbs +++ b/templates/actors/character-sheet-2.hbs @@ -518,4 +518,8 @@ aria-label="{{ "SIDEBAR.Create" type=(localize "DOCUMENT.Item") }}"> + + {{!-- Warnings --}} + {{> "dnd5e.actor-warnings-dialog" }} + diff --git a/templates/actors/npc-sheet-2.hbs b/templates/actors/npc-sheet-2.hbs index be83a18307..0feb71e050 100644 --- a/templates/actors/npc-sheet-2.hbs +++ b/templates/actors/npc-sheet-2.hbs @@ -527,4 +527,7 @@ + {{!-- Warnings --}} + {{> "dnd5e.actor-warnings-dialog" }} + diff --git a/templates/actors/parts/actor-warnings-dialog.hbs b/templates/actors/parts/actor-warnings-dialog.hbs new file mode 100644 index 0000000000..63e89e3293 --- /dev/null +++ b/templates/actors/parts/actor-warnings-dialog.hbs @@ -0,0 +1,11 @@ + + + From b6ca06aba690ac8e492d60394d67dc81e7055aab Mon Sep 17 00:00:00 2001 From: Paul Basov Date: Mon, 22 Jul 2024 20:43:29 +0200 Subject: [PATCH 18/18] [#3156] Add setting to show attack roll result (AC check) card to players (#3157) Co-authored-by: Pavel Basov Co-authored-by: Kim Mantas --- lang/en.json | 7 +++++++ module/documents/chat-message.mjs | 12 ++++++++++-- module/settings.mjs | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index 39f808e17c..b61a261d97 100644 --- a/lang/en.json +++ b/lang/en.json @@ -2119,6 +2119,13 @@ "SETTINGS.5eAutoCollapseCardN": "Collapse Item Cards in Chat", "SETTINGS.5eAutoSpellTemplateL": "When a spell is cast, defaults to begin the process to create the corresponding Measured Template if any (requires TRUSTED or higher player role)", "SETTINGS.5eAutoSpellTemplateN": "Always place Spell Template", +"SETTINGS.5eAttackRollVisibility": { + "Name": "Attack Result Visibility", + "Hint": "Control visibility of attack roll results in chat cards for players.", + "All": "Show results & target ACs", + "HideAC": "Show only results", + "None": "Hide all" +}, "SETTINGS.5eChallengeVisibility": { "Name": "Challenge Visibility", "Hint": "Control what roll DCs are visible to the players and whether successes/failures are highlighted.", diff --git a/module/documents/chat-message.mjs b/module/documents/chat-message.mjs index e17b27b554..93036ad32a 100644 --- a/module/documents/chat-message.mjs +++ b/module/documents/chat-message.mjs @@ -172,6 +172,7 @@ export default class ChatMessage5e extends ChatMessage { if ( !this.isContentVisible || !this.rolls.length ) return; const originatingMessage = game.messages.get(this.getFlag("dnd5e", "originatingMessage")) ?? this; const displayChallenge = originatingMessage?.shouldDisplayChallenge; + const displayAttackResult = game.user.isGM || (game.settings.get("dnd5e", "attackRollVisibility") !== "none"); /** * Create an icon to indicate success or failure. @@ -202,7 +203,9 @@ export default class ChatMessage5e extends ChatMessage { if ( !total ) continue; // Only attack rolls and death saves can crit or fumble. const canCrit = ["attack", "death"].includes(this.getFlag("dnd5e", "roll.type")); - if ( d.options.target && displayChallenge ) { + const isAttack = this.getFlag("dnd5e", "roll.type") === "attack"; + const showResult = isAttack ? displayAttackResult : displayChallenge; + if ( d.options.target && showResult ) { if ( d20Roll.total >= d.options.target ) total.classList.add("success"); else total.classList.add("failure"); } @@ -359,7 +362,9 @@ export default class ChatMessage5e extends ChatMessage { _enrichAttackTargets(html) { const attackRoll = this.rolls[0]; const targets = this.getFlag("dnd5e", "targets"); - if ( !game.user.isGM || !(attackRoll instanceof dnd5e.dice.D20Roll) || !targets?.length ) return; + const visibility = game.settings.get("dnd5e", "attackRollVisibility"); + const isVisible = game.user.isGM || (visibility !== "none"); + if ( !isVisible || !(attackRoll instanceof dnd5e.dice.D20Roll) || !targets?.length ) return; const tray = document.createElement("div"); tray.classList.add("dnd5e2"); tray.innerHTML = ` @@ -376,15 +381,18 @@ export default class ChatMessage5e extends ChatMessage { `; const evaluation = tray.querySelector("ul"); evaluation.innerHTML = targets.map(({ name, ac, uuid }) => { + if ( !game.user.isGM && (visibility !== "all") ) ac = ""; const isMiss = !attackRoll.isCritical && ((attackRoll.total < ac) || attackRoll.isFumble); return [`
  • ${name}
    + ${ac ? `
    ${ac}
    + ` : ""}
  • `, isMiss]; }).sort((a, b) => (a[1] === b[1]) ? 0 : a[1] ? 1 : -1).reduce((str, [li]) => str + li, ""); diff --git a/module/settings.mjs b/module/settings.mjs index 8466795ccb..13428a1c9e 100644 --- a/module/settings.mjs +++ b/module/settings.mjs @@ -28,6 +28,20 @@ export function registerSystemSettings() { } }); + game.settings.register("dnd5e", "attackRollVisibility", { + name: "SETTINGS.5eAttackRollVisibility.Name", + hint: "SETTINGS.5eAttackRollVisibility.Hint", + scope: "world", + config: true, + default: "none", + type: String, + choices: { + all: "SETTINGS.5eAttackRollVisibility.All", + hideAC: "SETTINGS.5eAttackRollVisibility.HideAC", + none: "SETTINGS.5eAttackRollVisibility.None" + } + }); + // Encumbrance tracking game.settings.register("dnd5e", "encumbrance", { name: "SETTINGS.5eEncumbrance.Name",