diff --git a/src/module/active-effect.ts b/src/module/active-effect.ts index 8d53ba28..8a029c4d 100644 --- a/src/module/active-effect.ts +++ b/src/module/active-effect.ts @@ -4,6 +4,7 @@ import { DS4Actor } from "./actor/actor"; import { getGame } from "./helpers"; +import { DS4Item } from "./item/item"; declare global { interface DocumentClassConfig { @@ -12,6 +13,7 @@ declare global { } type PromisedType<T> = T extends Promise<infer U> ? U : T; + export class DS4ActiveEffect extends ActiveEffect { /** * A fallback icon that can be used if no icon is defined for the effect. @@ -23,6 +25,38 @@ export class DS4ActiveEffect extends ActiveEffect { */ protected source: PromisedType<ReturnType<typeof fromUuid>> | undefined = undefined; + /** + * Whether or not this effect is currently surpressed. + */ + get isSurpressed(): boolean { + const originatingItem = this.originatingItem; + if (!originatingItem) { + return false; + } + return originatingItem.isNonEquippedEuipable(); + } + + /** + * The item which this effect originates from if it has been transferred from an item to an actor. + */ + get originatingItem(): DS4Item | undefined { + if (!(this.parent instanceof DS4Actor)) { + return; + } + const [, , , itemId] = this.data.origin?.split(".") ?? []; + if (!itemId) { + return; + } + return this.parent.items.get(itemId); + } + + /** + * The number of times this effect should be applied. + */ + get factor(): number { + return this.originatingItem?.activeEffectFactor ?? 1; + } + /** @override */ apply(actor: DS4Actor, change: foundry.data.ActiveEffectData["changes"][number]): unknown { change.value = Roll.replaceFormulaData(change.value, actor.data); @@ -54,4 +88,21 @@ export class DS4ActiveEffect extends ActiveEffect { } return this.source; } + + /** + * Create a new {@link DS4ActiveEffect} using default data. + * + * @param context The context for the creation of the effect, requiring a parent {@link DS4Actor} or {@link DS4Item}. + * @returns A promise that resolved to the created effect or udifined of the creation was prevented. + */ + static async createDefault( + context: DocumentModificationContext & { parent: DS4Actor | DS4Item }, + ): Promise<DS4ActiveEffect | undefined> { + const createData = { + label: getGame().i18n.localize(`DS4.NewEffectLabel`), + icon: this.FALLBACK_ICON, + }; + + return this.create(createData, context); + } } diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index d2b12635..34051c40 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -6,7 +6,6 @@ import { ModifiableDataBaseTotal } from "../common/common-data"; import { DS4 } from "../config"; import { getGame } from "../helpers"; -import { DS4Item } from "../item/item"; import { DS4ArmorDataProperties, DS4ShieldDataProperties } from "../item/item-data-properties"; import { ItemType } from "../item/item-data-source"; import { createCheckRoll } from "../rolls/check-factory"; @@ -85,20 +84,16 @@ export class DS4Actor extends Actor { applyActiveEffectsFiltered(predicate: (change: foundry.data.ActiveEffectData["changes"][number]) => boolean): void { const overrides: Record<string, unknown> = {}; - // Organize non-disabled effects by their application priority + // Organize non-disabled and -surpressed effects by their application priority const changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[] = this.effects.reduce( (changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => { - if (e.data.disabled) return changes; - const item = this.getOriginatingItemOfActiveEffect(e); - if (item?.isNonEquippedEuipable()) return changes; - - const factor = item?.activeEffectFactor ?? 1; + if (e.data.disabled || e.isSurpressed) return changes; const newChanges = e.data.changes.filter(predicate).flatMap((c) => { const changeSource = c.toObject(); changeSource.priority = changeSource.priority ?? changeSource.mode * 10; - return Array(factor).fill({ ...changeSource, effect: e }); + return Array(e.factor).fill({ ...changeSource, effect: e }); }); return changes.concat(newChanges); @@ -117,10 +112,6 @@ export class DS4Actor extends Actor { this.overrides = foundry.utils.expandObject({ ...foundry.utils.flattenObject(this.overrides), ...overrides }); } - protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): DS4Item | undefined { - return this.items.find((item) => item.uuid === effect.data.origin); - } - /** * Apply transformations to the Actor data after effects have been applied to the base data. * @override @@ -184,8 +175,6 @@ export class DS4Actor extends Actor { ]; case "creature": return ["weapon", "armor", "shield", "equipment", "loot", "spell", "specialCreatureAbility"]; - default: - return []; } } diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts index 99e8829f..49fa1782 100644 --- a/src/module/actor/sheets/actor-sheet.ts +++ b/src/module/actor/sheets/actor-sheet.ts @@ -27,6 +27,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "values" }], dragDrop: [ { dragSelector: ".item-list .item", dropSelector: null }, + { dragSelector: ".effect-list .effect", dropSelector: null }, { dragSelector: ".ds4-check", dropSelector: null }, ], width: 650, @@ -202,12 +203,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD * @param event - The originating click event */ protected onCreateEffect(): void { - const effectData = { - label: getGame().i18n.localize(`DS4.NewEffectLabel`), - icon: "icons/svg/aura.svg", - origin: this.actor.uuid, - }; - ActiveEffect.create(effectData, { parent: this.actor }); + DS4ActiveEffect.createDefault({ parent: this.actor }); } /** diff --git a/src/module/item/item-sheet.ts b/src/module/item/item-sheet.ts index eca1d176..ee8b1346 100644 --- a/src/module/item/item-sheet.ts +++ b/src/module/item/item-sheet.ts @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: MIT +import { DS4ActiveEffect } from "../active-effect"; import { DS4 } from "../config"; import { getGame } from "../helpers"; import notifications from "../ui/notifications"; @@ -97,14 +98,8 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData> /** * Create a new ActiveEffect for the item using default data. */ - protected async createActiveEffect(): Promise<ActiveEffect | undefined> { - const createData = { - label: getGame().i18n.localize(`DS4.NewEffectLabel`), - icon: "icons/svg/aura.svg", - origin: this.item.uuid, - }; - - return ActiveEffect.create(createData, { parent: this.item }); + protected createActiveEffect(): void { + DS4ActiveEffect.createDefault({ parent: this.item }); } } diff --git a/src/templates/sheets/actor/tabs/effects.hbs b/src/templates/sheets/actor/tabs/effects.hbs index 67ed0a36..c8ff101b 100644 --- a/src/templates/sheets/actor/tabs/effects.hbs +++ b/src/templates/sheets/actor/tabs/effects.hbs @@ -7,7 +7,7 @@ SPDX-License-Identifier: MIT <div class="tab effects" data-group="primary" data-tab="effects"> {{#unless (isEmpty data.effects)}} - <ol class="ds4-embedded-document-list ds4-embedded-document-list--effect"> + <ol class="ds4-embedded-document-list ds4-embedded-document-list--effect effect-list"> {{> systems/ds4/templates/sheets/actor/components/effect-list-header.hbs}} {{#each enrichedEffects as |effectData id| }} {{> systems/ds4/templates/sheets/actor/components/effect-list-entry.hbs effectData=effectData}}