Skip to content

Commit

Permalink
Merge pull request foundryvtt#3907 from foundryvtt/activity/effects
Browse files Browse the repository at this point in the history
[foundryvtt#1964] Add applied effects data to activities & sheet
  • Loading branch information
arbron authored Jul 31, 2024
2 parents 72f9a9e + dac2018 commit 5f1dfd4
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 10 deletions.
19 changes: 15 additions & 4 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@
},

"DND5E.ACTIVITY": {
"Label": "Activity",
"LabelPl": "Activities",
"Title": {
"one": "Activity",
"other": "Activities"
},
"FIELDS": {
"activation": {
"label": "Activation",
Expand Down Expand Up @@ -207,6 +209,9 @@
"hint": "Value of the duration in the specified units, if applicable."
}
},
"effects": {
"label": "Applied Effects"
},
"img": {
"label": "Icon"
},
Expand Down Expand Up @@ -740,7 +745,7 @@
"DND5E.ConsumableWithoutCharges": "available units to use",
"DND5E.Consumption": {
"Action": {
"Add": "Add Consumption Target",
"Create": "Create Consumption Target",
"Delete": "Delete Consumption Target"
},
"Scaling": {
Expand Down Expand Up @@ -943,6 +948,12 @@
"DND5E.Dusk": "Dusk",
"DND5E.Effect": "Effect",
"DND5E.Effects": "Effects",
"DND5E.EFFECT": {
"Action": {
"Create": "Create Effect",
"Delete": "Delete Effect"
}
},
"DND5E.EffectsApplyTokens": "Apply to selected tokens",
"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.",
Expand Down Expand Up @@ -2137,7 +2148,7 @@
},
"Recovery": {
"Action": {
"Add": "Add Recovery Profile",
"Create": "Create Recovery Profile",
"Delete": "Delete Recovery Profile"
},
"Recharge": {
Expand Down
68 changes: 66 additions & 2 deletions module/applications/activity/activity-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ export default class ActivitySheet extends Application5e {
},
actions: {
addConsumption: ActivitySheet.#addConsumption,
addEffect: ActivitySheet.#addEffect,
addRecovery: ActivitySheet.#addRecovery,
deleteConsumption: ActivitySheet.#deleteConsumption,
deleteEffect: ActivitySheet.#deleteEffect,
deleteRecovery: ActivitySheet.#deleteRecovery
},
form: {
Expand Down Expand Up @@ -59,7 +61,10 @@ export default class ActivitySheet extends Application5e {
]
},
effect: {
template: "systems/dnd5e/templates/activity/effect.hbs"
template: "systems/dnd5e/templates/activity/effect.hbs",
templates: [
"systems/dnd5e/templates/activity/parts/activity-effects.hbs"
]
}
};

Expand Down Expand Up @@ -277,6 +282,12 @@ export default class ActivitySheet extends Application5e {
*/
async _prepareEffectContext(context) {
context.tab = context.tabs.effect;

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)
}));

return context;
}

Expand Down Expand Up @@ -358,7 +369,7 @@ export default class ActivitySheet extends Application5e {
/** @override */
_canRender(options) {
if ( !this.isVisible ) throw new Error(game.i18n.format("SHEETS.DocumentSheetPrivate", {
type: game.i18n.localize("DND5E.ACTIVITY.Label")
type: game.i18n.localize("DND5E.ACTIVITY.Title.one")
}));
}

Expand All @@ -377,6 +388,15 @@ export default class ActivitySheet extends Application5e {
this.activity.constructor._unregisterApp(this.activity, this);
}

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

/** @inheritDoc */
async _renderFrame(options) {
const frame = await super._renderFrame(options);
frame.autocomplete = "off";
return frame;
}

/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */
Expand All @@ -401,6 +421,25 @@ export default class ActivitySheet extends Application5e {

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

/**
* Handle creating a new active effect and adding it to the applied effects list.
* @this {ActivityConfig}
* @param {Event} event Triggering click event.
* @param {HTMLElement} target Button that was clicked.
*/
static async #addEffect(event, target) {
const effectData = {
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 }] });
}

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

/**
* Handle adding a new entry to the uses recovery list.
* @this {ActivityConfig}
Expand Down Expand Up @@ -437,6 +476,23 @@ export default class ActivitySheet extends Application5e {

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

/**
* Handle deleting an active effect and removing it from the applied effects list.
* @this {ActivityConfig}
* @param {Event} event Triggering click event.
* @param {HTMLElement} target Button that was clicked.
*/
static async #deleteEffect(event, target) {
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
const result = await this.item.effects.get(effectId)?.deleteDialog();
if ( result instanceof ActiveEffect ) {
const effects = this.activity.toObject().effects.filter(e => e.id !== effectId);
this.activity.update({ effects });
}
}

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

/**
* Handle removing an entry from the uses recovery list.
* @this {ActivityConfig}
Expand Down Expand Up @@ -474,6 +530,14 @@ export default class ActivitySheet extends Application5e {
*/
_prepareSubmitData(event, formData) {
const submitData = foundry.utils.expandObject(formData.object);
if ( foundry.utils.hasProperty(submitData, "appliedEffects") ) {
const effects = submitData.effects ?? this.activity.toObject().effects;
submitData.effects = effects.filter(e => submitData.appliedEffects.includes(e.id));
for ( const id of submitData.appliedEffects ) {
if ( submitData.effects.find(e => e.id === id) ) continue;
submitData.effects.push({ id });
}
}
if ( foundry.utils.hasProperty(submitData, "consumption.targets") ) {
submitData.consumption.targets = Object.values(submitData.consumption.targets);
}
Expand Down
16 changes: 16 additions & 0 deletions module/data/activity/base-activity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const {
* @property {string} scaling.formula Specific scaling formula if not automatically calculated from target's value.
*/

/**
* Data for effects that can be applied.
*
* @typedef {object} EffectApplicationData
* @property {string} effect ID of the effect to apply.
*/

/**
* Data for a recovery profile for an activity's uses.
*
Expand Down Expand Up @@ -48,6 +55,7 @@ const {
* @property {string} duration.value Scalar value for the activity's duration.
* @property {string} duration.units Units that are used for the duration.
* @property {string} duration.special Description of any special duration details.
* @property {EffectApplicationData[]} effects Linked effects that can be applied.
* @property {object} range
* @property {string} range.value Scalar value for the activity's range.
* @property {string} range.units Units that are used for the range.
Expand Down Expand Up @@ -120,6 +128,9 @@ export default class BaseActivityData extends foundry.abstract.DataModel {
units: new StringField({ initial: "inst" }),
special: new StringField()
}),
effects: new ArrayField(new SchemaField({
id: new DocumentIdField()
})),
range: new SchemaField({
value: new FormulaField({ deterministic: true }),
units: new StringField(),
Expand Down Expand Up @@ -156,6 +167,11 @@ export default class BaseActivityData extends foundry.abstract.DataModel {
prepareData() {
this.name = this.name || game.i18n.localize(this.metadata?.title);
this.img = this.img || this.metadata?.img;
const item = this.item;
this.effects.forEach(e => Object.defineProperty(e, "effect", {
get() { return item.effects.get(e.id); },
configurable: true
}));
UsesField.prepareData.call(this, this.getRollData({ deterministic: true }));
}

Expand Down
4 changes: 3 additions & 1 deletion module/documents/mixins/pseudo-document.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ export default Base => class extends Base {
* @param {RenderOptions} [options] Rendering options.
*/
render(options) {
for ( const app of this.constructor._apps.get(this.uuid) ?? [] ) app.render(options);
for ( const app of this.constructor._apps.get(this.uuid) ?? [] ) {
app.render({ window: { title: app.title }, ...options });
}
}

/* -------------------------------------------- */
Expand Down
2 changes: 1 addition & 1 deletion templates/activity/effect.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<section class="tab activity-{{ tab.id }} {{ tab.cssClass }}" data-tab="{{ tab.id }}" data-group="{{ tab.group }}">
TODO: Active effects to apply & effects from specific activity types
{{> "systems/dnd5e/templates/activity/parts/activity-effects.hbs" }}
</section>
2 changes: 1 addition & 1 deletion templates/activity/parts/activity-consumption.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
{{/each}}
</ul>
<button type="button" class="unbutton" data-action="addConsumption">
{{ localize "DND5E.Consumption.Action.Add" }}
{{ localize "DND5E.Consumption.Action.Create" }}
</button>
</fieldset>

Expand Down
20 changes: 20 additions & 0 deletions templates/activity/parts/activity-effects.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<fieldset>
<legend>{{ localize "DND5E.ACTIVITY.FIELDS.effects.label" }}</legend>
<multi-select name="appliedEffects">
{{ selectOptions allEffects }}
</multi-select>
<ul class="unlist">
{{#each activity.effects}}
<li data-index="{{ @index }}" data-effect-id="{{ id }}" class="flexrow">
<img class="gold-icon" src="{{ effect.img }}" alt="{{ effect.name }}">
<span class="name">{{{ dnd5e-linkForUuid effect.uuid }}}</span>
<button type="button" class="unbutton" data-action="deleteEffect">
{{ localize "DND5E.EFFECT.Action.Delete" }}
</button>
</li>
{{/each}}
</ul>
<button type="button" class="unbutton" data-action="addEffect">
{{ localize "DND5E.EFFECT.Action.Create" }}
</button>
</fieldset>
2 changes: 1 addition & 1 deletion templates/shared/uses-recovery.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
{{/each}}
</ul>
<button type="button" class="unbutton" data-action="addRecovery">
{{ localize "DND5E.USES.Recovery.Action.Add" }}
{{ localize "DND5E.USES.Recovery.Action.Create" }}
</button>
</fieldset>

0 comments on commit 5f1dfd4

Please sign in to comment.