Skip to content

Commit

Permalink
Merge pull request foundryvtt#3935 from foundryvtt/activity/enchant
Browse files Browse the repository at this point in the history
[foundryvtt#3914] Add `EnchantActivity` data and sheet
  • Loading branch information
arbron authored Aug 5, 2024
2 parents f344955 + f9f469b commit 4ffdc45
Show file tree
Hide file tree
Showing 26 changed files with 431 additions and 311 deletions.
112 changes: 75 additions & 37 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"TYPES.Actor.group": "Group",
"TYPES.Actor.groupPl": "Groups",

"TYPES.ActiveEffect.enchantment": "Enchantment",
"TYPES.ActiveEffect.enchantmentPl": "Enchantments",

"ITEM.TypeBackground": "Background",
"ITEM.TypeBackgroundPl": "Backgrounds",
"ITEM.TypeContainer": "Container",
Expand Down Expand Up @@ -1098,20 +1101,83 @@
"DND5E.EffectApplyWarningConcentration": "Applying an effect that is being concentrated on by another character requires GM permissions.",
"DND5E.EffectApplyWarningOwnership": "Effects cannot be applied to tokens you are not the owner of.",
"DND5E.EffectsSearch": "Search effects",
"DND5E.Enchantment": {

"DND5E.ENCHANT": {
"Title": "Enchant",
"FIELDS": {
"effects": {
"FIELDS": {
"level": {
"label": "Level Limit",
"hint": "Range of levels required to use this enchantment.",
"max": {
"label": "Maximum Level"
},
"min": {
"label": "Minimum Level"
}
},
"riders": {
"label": "Attached",
"effect": {
"label": "Additional Effects",
"hint": "These additional effects will be added to the enchanted item when this enchantment is added, and removed when the enchantment is removed."
},
"item": {
"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."
}
}
}
},
"enchant": {
"label": "Enchantment Configuration",
"identifier": {
"label": "Class Identifier",
"hint": "Identifier used to determine whether the character level or a specific class level should be used for enchantment level limits."
}
},
"restrictions": {
"label": "Restrictions",
"hint": "Restrictions on the type of item to which this enchantment can be applied.",
"allowMagical": {
"label": "Allow Magical",
"hint": "Allow items that are already magical to be enchanted."
},
"type": {
"label": "Item Type",
"hint": "Type of item to which this enchantment can be applied.",
"Any": "Any Enchantable Type"
}
}
},
"SECTIONS": {
"Enchanting": "Enchanting",
"Enchantments": "Enchantments",
"Restrictions": "Restrictions"
},
"Enchantment": {
"Action": {
"Create": "Create Enchantment",
"Delete": "Delete Enchantment"
},
"Empty": "No associated enchantments, use the button below to create one or select an existing enchantment from the control above."
}
},

"DND5E.ENCHANTMENT": {
"Action": {
"Apply": "Apply Enchantment",
"Configure": "Configure Enchantment",
"Create": "Create Enchantment",
"Delete": "Delete Enchantment",
"Disable": "Disable Enchantment",
"Edit": "Edit Enchantment",
"Enable": "Enable Enchantment",
"Remove": "Remove Enchantment"
},
}
},

"DND5E.Enchantment": {
"Category": {
"Active": "Active Enchantments",
"Empty": "No enchantments have been created, use the button above to create one.",
"General": "Enchantments",
"Inactive": "Inactive Enchantments"
},
Expand All @@ -1121,9 +1187,6 @@
"FIELDS": {
"enchantment": {
"label": "Enchantment Configuration",
"classIdentifier": {
"hint": "Identifier used to determine whether the character level or a specific class level should be used for enchantment level limits."
},
"items": {
"max": {
"label": "Item Limit",
Expand All @@ -1133,38 +1196,13 @@
"label": "Replacement Period",
"hint": "How frequently the enchantments of this type can be re-bound to different items."
}
},
"restrictions": {
"label": "Restrictions",
"hint": "Restrictions on the type of item to which this enchantment can be applied.",
"allowMagical": {
"label": "Allow Magical",
"hint": "Allow items that are already magical to be enchanted."
},
"type": {
"label": "Item Type",
"hint": "Type of item to which this enchantment can be applied."
}
}
}
},
"Label": "Enchantment",
"Level": {
"Hint": "Range of levels required to use this enchantment."
},
"Items": {
"Entry": "{item} on <em>{actor}</em>"
},
"Riders": {
"Effect": {
"Label": "Additional Effects",
"Hint": "These additional effects will be added to the enchanted item when this enchantment is added, and removed when the enchantment is removed."
},
"Item": {
"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."
}
},
"Warning": {
"ConcentrationEnded": "Cannot apply this enchantment because concentration has ended.",
"NoMagicalItems": "Items that are already magical cannot be enchanted.",
Expand Down Expand Up @@ -2119,11 +2157,11 @@
"level": {
"label": "Level Limit",
"hint": "Range of levels required to use this profile.",
"min": {
"label": "Minimum Level"
},
"max": {
"label": "Maximum Level"
},
"min": {
"label": "Minimum Level"
}
},
"name": {
Expand Down
1 change: 1 addition & 0 deletions module/applications/activity/_module.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {default as ActivitySheet} from "./activity-sheet.mjs";
export {default as AttackSheet} from "./attack-sheet.mjs";
export {default as EnchantSheet} from "./enchant-sheet.mjs";
export {default as SummonSheet} from "./summon-sheet.mjs";
export {default as UtilitySheet} from "./utility-sheet.mjs";

Expand Down
32 changes: 25 additions & 7 deletions module/applications/activity/activity-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ export default class ActivitySheet extends Application5e {
*/
#expandedSections = new Map();

get expandedSections() {
return this.#expandedSections;
}

/* -------------------------------------------- */

/**
Expand Down Expand Up @@ -334,12 +338,15 @@ export default class ActivitySheet extends Application5e {

if ( context.activity.effects ) {
const appliedEffects = new Set(context.activity.effects?.map(e => e._id) ?? []);
context.allEffects = this.item.effects.map(effect => ({
value: effect.id, label: effect.name, selected: appliedEffects.has(effect.id)
}));
context.allEffects = this.item.effects
.filter(e => e.type !== "enchantment")
.map(effect => ({
value: effect.id, label: effect.name, selected: appliedEffects.has(effect.id)
}));
context.appliedEffects = context.activity.effects.map((data, index) => {
const effect = {
data,
collapsed: this.expandedSections.get(`effects.${data._id}`) ? "" : "collapsed",
effect: data.effect,
fields: this.activity.schema.fields.effects.element.fields,
prefix: `effects.${index}.`,
Expand Down Expand Up @@ -541,14 +548,25 @@ export default class ActivitySheet extends Application5e {
* @param {HTMLElement} target Button that was clicked.
*/
static async #addEffect(event, target) {
const effectData = {
const effectData = this._addEffectData();
const [created] = await this.item.createEmbeddedDocuments("ActiveEffect", [effectData]);
this.activity.update({ effects: [...this.activity.toObject().effects, { _id: created.id }] });
}

/* -------------------------------------------- */

/**
* The data for a newly created applied effect.
* @returns {object}
* @protected
*/
_addEffectData() {
return {
name: this.item.name,
img: this.item.img,
origin: this.item.uuid,
transfer: false
};
const [created] = await this.item.createEmbeddedDocuments("ActiveEffect", [effectData]);
this.activity.update({ effects: [...this.activity.toObject().effects, { _id: created.id }] });
}

/* -------------------------------------------- */
Expand Down Expand Up @@ -646,7 +664,7 @@ export default class ActivitySheet extends Application5e {
target.classList.toggle("collapsed");
this.#expandedSections.set(
target.closest("[data-expand-id]")?.dataset.expandId,
!event.currentTarget.classList.contains("collapsed")
!target.classList.contains("collapsed")
);
}

Expand Down
104 changes: 104 additions & 0 deletions module/applications/activity/enchant-sheet.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import ActivitySheet from "./activity-sheet.mjs";

/**
* Sheet for the enchant activity.
*/
export default class EnchantSheet extends ActivitySheet {

/** @inheritDoc */
static DEFAULT_OPTIONS = {
classes: ["enchant-activity"]
};

/* -------------------------------------------- */

/** @inheritDoc */
static PARTS = {
...super.PARTS,
effect: {
template: "systems/dnd5e/templates/activity/enchant-effect.hbs",
templates: [
"systems/dnd5e/templates/activity/parts/enchant-enchantments.hbs",
"systems/dnd5e/templates/activity/parts/enchant-restrictions.hbs"
]
}
};

/* -------------------------------------------- */

/** @override */
tabGroups = {
sheet: "identity",
activation: "time",
effect: "enchantments"
};

/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */

/** @override */
_prepareAppliedEffectContext(context, effect) {
effect.effectOptions = context.allEffects.map(e => ({
...e, selected: effect.data.riders.effect.has(e.value)
}));
return effect;
}

/* -------------------------------------------- */

/** @inheritDoc */
async _prepareEffectContext(context) {
context = await super._prepareEffectContext(context);

const appliedEnchantments = new Set(context.activity.effects?.map(e => e._id) ?? []);
context.allEnchantments = this.item.effects
.filter(e => e.type === "enchantment")
.map(effect => ({
value: effect.id, label: effect.name, selected: appliedEnchantments.has(effect.id)
}));
const enchantableTypes = this.activity.enchantableTypes;
context.typeOptions = [
{ value: "", label: game.i18n.localize("DND5E.ENCHANT.FIELDS.restrictions.type.Any") },
...Object.keys(CONFIG.Item.dataModels)
.filter(t => enchantableTypes.has(t))
.map(value => ({ value, label: game.i18n.localize(CONFIG.Item.typeLabels[value]) }))
];

return context;
}

/* -------------------------------------------- */

/** @inheritDoc */
_getTabs() {
const tabs = super._getTabs();
tabs.effect.label = "DND5E.ENCHANT.SECTIONS.Enchanting";
tabs.effect.icon = "fa-solid fa-wand-sparkles";
tabs.effect.tabs = this._markTabs({
enchantments: {
id: "enchantments", group: "effect", icon: "fa-solid fa-star",
label: "DND5E.ENCHANT.SECTIONS.Enchantments"
},
restrictions: {
id: "restrictions", group: "effect", icon: "fa-solid fa-ban",
label: "DND5E.ENCHANT.SECTIONS.Restrictions"
}
});
return tabs;
}

/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */

/** @override */
_addEffectData() {
return {
type: "enchantment",
name: this.item.name,
img: this.item.img,
disabled: true
};
}
}
3 changes: 2 additions & 1 deletion module/applications/activity/summon-sheet.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ActivitySheet from "./activity-sheet.mjs";

/**
* Default sheet for activities.
* Sheet for the summon activity.
*/
export default class SummonSheet extends ActivitySheet {

Expand Down Expand Up @@ -67,6 +67,7 @@ export default class SummonSheet extends ActivitySheet {
];
context.profiles = this.activity.profiles.map((data, index) => ({
data, index,
collapsed: this.expandedSections.get(`profiles.${effect.id}`) ? "" : "collapsed",
fields: this.activity.schema.fields.profiles.element.fields,
prefix: `profiles.${index}.`,
source: context.source.profiles[index] ?? data,
Expand Down
6 changes: 3 additions & 3 deletions module/applications/components/effects.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default class EffectsElement extends HTMLElement {
if ( e.disabled ) categories.enchantmentInactive.effects.push(e);
else categories.enchantmentActive.effects.push(e);
}
else if ( e.getFlag("dnd5e", "type") === "enchantment" ) categories.enchantment.effects.push(e);
else if ( e.type === "enchantment" ) categories.enchantment.effects.push(e);
else if ( e.isSuppressed ) categories.suppressed.effects.push(e);
else if ( e.disabled ) categories.inactive.effects.push(e);
else if ( e.isTemporary ) categories.temporary.effects.push(e);
Expand Down Expand Up @@ -280,12 +280,12 @@ export default class EffectsElement extends HTMLElement {
const isActor = this.document instanceof Actor;
const isEnchantment = li.dataset.effectType.startsWith("enchantment");
return this.document.createEmbeddedDocuments("ActiveEffect", [{
type: isEnchantment ? "enchantment" : "base",
name: isActor ? game.i18n.localize("DND5E.EffectNew") : this.document.name,
icon: isActor ? "icons/svg/aura.svg" : this.document.img,
origin: isEnchantment ? undefined : this.document.uuid,
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
disabled: ["inactive", "enchantmentInactive"].includes(li.dataset.effectType),
"flags.dnd5e.type": isEnchantment ? "enchantment" : undefined
disabled: ["inactive", "enchantmentInactive"].includes(li.dataset.effectType)
}]);
}

Expand Down
Loading

0 comments on commit 4ffdc45

Please sign in to comment.