Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deletion dialog when multiple sources of the same status exists on the actor #4881

Open
wants to merge 1 commit into
base: 4.2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,13 @@
"DND5E.ConCharmed": "Charmed",
"DND5E.ConDeafened": "Deafened",
"DND5E.ConDiseased": "Diseased",
"DND5E.CONDITION": {
"DeleteDialog": {
"hint": "You are under the effects of this condition from more than one source. Pick which effect to end.",
"label": "Source",
"title": "Remove Effect"
}
},
"DND5E.ConExhaustion": "Exhaustion",
"DND5E.ConFrightened": "Frightened",
"DND5E.ConGrappled": "Grappled",
Expand Down
87 changes: 81 additions & 6 deletions module/documents/active-effect.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -621,14 +621,30 @@ export default class ActiveEffect5e extends ActiveEffect {
* @param {jQuery} html The TokenHUD HTML.
*/
static onTokenHUDRender(app, html) {
if ( game.release.generation < 13 ) html = html[0];
const actor = app.object.actor;
const level = foundry.utils.getProperty(actor, "system.attributes.exhaustion");
if ( Number.isFinite(level) && (level > 0) ) {
const img = ActiveEffect5e._getExhaustionImage(level);
html.find('[data-status-id="exhaustion"]').css({
objectPosition: "-100px",
background: `url('${img}') no-repeat center / contain`
});

if ( game.release.generation < 13 ) {
html.find('[data-status-id="exhaustion"]').css({
objectPosition: "-100px",
background: `url('${img}') no-repeat center / contain`
});
} else {
const element = html.querySelector('[data-status-id="exhaustion"]');
element.style.objectPosition = "-100px";
element.style.background = `url('${img}') no-repeat center / contain`;
}
}

for ( const effect of actor.effects ) {
if ( !effect.active ) continue;
for ( const status of effect.statuses ) {
const element = html.querySelector(`[data-status-id="${status}"]`);
if ( element ) element.classList.add("active");
}
}
}

Expand Down Expand Up @@ -672,11 +688,12 @@ export default class ActiveEffect5e extends ActiveEffect {
if ( !target.classList?.contains("effect-control") ) return;

const actor = canvas.hud.token.object?.actor;
if ( !actor ) return;

const id = target.dataset?.statusId;
if ( !actor || !id ) return;

if ( id === "exhaustion" ) ActiveEffect5e._manageExhaustion(event, actor);
else if ( id === "concentrating" ) ActiveEffect5e._manageConcentration(event, actor);
else ActiveEffect5e._manageCondition(event, actor, id);
}

/* -------------------------------------------- */
Expand Down Expand Up @@ -743,6 +760,64 @@ export default class ActiveEffect5e extends ActiveEffect {

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

/**
* Manage custom condition handling when interacting with the token HUD.
* @param {PointerEvent} event The triggering event.
* @param {Actor5e} actor The actor belonging to the token.
* @param {string} status The status condition.
*/
static _manageCondition(event, actor, status) {
const effects = new Set(actor.effects.filter(effect => effect.statuses.has(status)));
if ( !effects.size ) return;

event.preventDefault();
event.stopPropagation();

if ( effects.size > 1 ) {
ActiveEffect5e.deleteConditionDialog(actor, effects);
} else {
effects.first().delete();
}
}

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

/**
* Prompt the user to delete one of several conditions.
* @param {Actor5e} actor The owner of the effects.
* @param {string|Set<ActiveEffect5e>} effects A set of effects, or the status to derive them from.
* @returns {Promise<ActiveEffect5e|null>}
*/
static async deleteConditionDialog(actor, effects) {
if ( foundry.utils.getType(effects) === "string" ) {
effects = new Set(actor.effects.filter(effect => effect.statuses.has(effects)));
}
if ( !effects.size ) return null;

const html = new foundry.data.fields.StringField({
label: game.i18n.localize("DND5E.CONDITION.DeleteDialog.label"),
hint: game.i18n.localize("DND5E.CONDITION.DeleteDialog.hint"),
required: true,
choices: Object.fromEntries(Array.from(effects).map(effect => [effect.id, effect.name]))
}).toFormGroup({}, { name: "source", sort: true }).outerHTML;

return foundry.applications.api.DialogV2.prompt({
rejectClose: false,
content: `<fieldset>${html}</fieldset>`,
window: { title: "DND5E.CONDITION.DeleteDialog.title" },
position: { width: 400 },
ok: {
label: "DND5E.Confirm",
callback: async function(event, button) {
const source = button.form.elements.source.value;
return actor.effects.get(source).delete();
}
}
});
}

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

/**
* Record another effect as a dependent of this one.
* @param {...ActiveEffect5e} dependent One or more dependent effects.
Expand Down