diff --git a/lang/en.json b/lang/en.json
index 8096036c81..1046518012 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -2306,6 +2306,10 @@
"label": "Additional Items",
"hint": "These additional items will be added to the creature when one of its items is enchanted, and will be removed if the enchantment is ever removed."
}
+ },
+ "static": {
+ "label": "Static",
+ "hint": "Prevent this enchantment from being removed via the item context menu."
}
},
"enchant": {
diff --git a/module/applications/components/inventory.mjs b/module/applications/components/inventory.mjs
index bfe44a249d..58f6a438b6 100644
--- a/module/applications/components/inventory.mjs
+++ b/module/applications/components/inventory.mjs
@@ -375,9 +375,16 @@ export default class InventoryElement extends (foundry.applications.elements.Ado
}, {
label: "DND5E.ConcentrationBreak",
icon: '',
- group: "state",
+ group: "destructive",
visible: () => this.actor?.concentration?.items.has(item),
onClick: () => this.actor?.endConcentration(item)
+ }, {
+ label: "DND5E.ENCHANTMENT.Action.Remove",
+ icon: '',
+ group: "destructive",
+ visible: () => item.isOwner && !compendiumLocked
+ && item.effects.some(e => e.isAppliedEnchantment && !e.getFlag("dnd5e", "static")),
+ onClick: (event, target) => this._onAction(target, "removeEnchantment", { event })
}, {
label: `DND5E.ContextMenuAction${item.system.attuned ? "Unattune" : "Attune"}`,
icon: '',
@@ -479,6 +486,7 @@ export default class InventoryElement extends (foundry.applications.elements.Ado
case "identify": return this._onToggleIdentify(item);
case "prepare": return this._onTogglePrepared(item);
case "recharge": return this._onRollRecharge(activity ?? item, { event });
+ case "removeEnchantment": return this._onRemoveEnchantment(item);
case "toggleCharge": return this._onToggleCharge(item);
case "toggleExpand": return this._onToggleExpand(target, { item });
case "toggleFavorite": return this._onToggleFavorite(item);
@@ -630,6 +638,21 @@ export default class InventoryElement extends (foundry.applications.elements.Ado
/* -------------------------------------------- */
+ /**
+ * Handle removing all applied enchantments from an item.
+ * @param {Item5e} item The item.
+ * @returns {Promise}
+ * @protected
+ */
+ _onRemoveEnchantment(item) {
+ const ids = item.effects
+ .filter(e => e.isAppliedEnchantment && !e.getFlag("dnd5e", "static"))
+ .map(e => e.id);
+ return item.deleteEmbeddedDocuments("ActiveEffect", ids);
+ }
+
+ /* -------------------------------------------- */
+
/**
* Handle recharging an item.
* @param {Item5e|Activity} entry The entity being recharged.
diff --git a/module/data/activity/enchant-data.mjs b/module/data/activity/enchant-data.mjs
index 56649995a6..1f36ee43fa 100644
--- a/module/data/activity/enchant-data.mjs
+++ b/module/data/activity/enchant-data.mjs
@@ -24,7 +24,8 @@ export default class BaseEnchantActivityData extends BaseActivityData {
activity: new SetField(new DocumentIdField()),
effect: new SetField(new DocumentIdField()),
item: new SetField(new DocumentUUIDField({ type: "Item" }))
- })
+ }),
+ static: new BooleanField()
})),
enchant: new SchemaField({
self: new BooleanField()
diff --git a/module/documents/activity/enchant.mjs b/module/documents/activity/enchant.mjs
index e13fd45d09..e7e004381a 100644
--- a/module/documents/activity/enchant.mjs
+++ b/module/documents/activity/enchant.mjs
@@ -172,6 +172,7 @@ export default class EnchantActivity extends ActivityMixin(BaseEnchantActivityDa
const flags = { enchantmentProfile: profile };
if ( concentration ) flags.dependentOn = concentration.uuid;
+ if ( this.effects.find(e => e._id === profile)?.static ) flags.static = true;
const enchantmentData = effect.clone({ origin: this.uuid, "flags.dnd5e": flags }).toObject();
/**
diff --git a/templates/activity/parts/enchant-enchantments.hbs b/templates/activity/parts/enchant-enchantments.hbs
index 948bad98eb..b5cca01147 100644
--- a/templates/activity/parts/enchant-enchantments.hbs
+++ b/templates/activity/parts/enchant-enchantments.hbs
@@ -61,6 +61,8 @@
{{ localize "DND5E.ENCHANT.FIELDS.effects.element.level.hint" }}
+ {{ formField fields.static name=(concat prefix "static") value=data.static
+ input=@root.inputs.createCheckboxInput rootId=@root.partId }}
{{#with fields.riders.fields as |fields|}}
{{ formField fields.activity name=(concat ../prefix "riders.activity") options=../activityOptions
rootId=@root.partId }}